Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CustomPosters v4.0.4
BepInEx/plugins/CustomPosters/CustomPosters.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; 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.Security; using System.Security.Permissions; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using CustomPosters.Data; using CustomPosters.Data.PosterLayouts; using CustomPosters.Data.PosterLayouts.Legacy; using CustomPosters.Networking; using CustomPosters.Patches; using CustomPosters.Utils; using HarmonyLib; using LethalNetworkAPI; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; using UnityEngine.Video; [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("CustomPosters")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Replaces the default posters in the ship with custom posters")] [assembly: AssemblyFileVersion("4.0.4.0")] [assembly: AssemblyInformationalVersion("4.0.4+779ef258841132444bf56259880ed810a1832f15")] [assembly: AssemblyProduct("CustomPosters")] [assembly: AssemblyTitle("CustomPosters")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("4.0.4.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } 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 CustomPosters { internal static class AssetManager { public static AssetBundle? Bundle { get; private set; } public static GameObject? PosterPrefab { get; private set; } public static GameObject? Poster5Prefab { get; private set; } public static GameObject? TipsPrefab { get; private set; } public static void LoadAssets() { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string directoryName = Path.GetDirectoryName(executingAssembly.Location); string text = Path.Combine(directoryName, "customposters"); Bundle = AssetBundle.LoadFromFile(text); if ((Object)(object)Bundle == (Object)null) { Plugin.Log.LogError((object)"Failed to load AssetBundle."); return; } PosterPrefab = Bundle.LoadAsset<GameObject>("CustomPosterPrefab"); Poster5Prefab = Bundle.LoadAsset<GameObject>("Poster5"); TipsPrefab = Bundle.LoadAsset<GameObject>("Tips"); if ((Object)(object)PosterPrefab == (Object)null) { Plugin.Log.LogError((object)"Failed to load prefab from the AssetBundle."); } else { Plugin.Log.LogInfo((object)"Loaded assetbundle."); } if ((Object)(object)Poster5Prefab == (Object)null) { Plugin.Log.LogError((object)"Failed to load Poster5 prefab from the AssetBundle."); } if ((Object)(object)TipsPrefab == (Object)null) { Plugin.Log.LogError((object)"Failed to load Tips prefab from the AssetBundle."); } } } internal static class Constants { public static readonly string[] ValidImageExtensions = new string[4] { ".png", ".jpg", ".jpeg", ".bmp" }; public static readonly string[] ValidVideoExtensions = new string[1] { ".mp4" }; public static readonly string[] AllValidExtensions = new string[5] { ".png", ".jpg", ".jpeg", ".bmp", ".mp4" }; public const string PluginsFolderName = "plugins"; public const int DefaultVideoVolume = 10; public const float DefaultVideoMaxDistance = 3.5f; public static readonly string[] ExcludedModFolderNames = new string[3] { "CustomPosters", "seechela-CustomPosters", "plugins" }; public const string BiggerShipGUID = "BiggerShip"; public const string ShipWindowsGUID = "TestAccount666.ShipWindows"; public const string WiderShipModGUID = "mborsh.WiderShipMod"; public const string TwoStoryShipGUID = "MelanieMelicious.2StoryShip"; public const string PosterNamePoster1 = "Poster1"; public const string PosterNamePoster2 = "Poster2"; public const string PosterNamePoster3 = "Poster3"; public const string PosterNamePoster4 = "Poster4"; public const string PosterNamePoster5 = "Poster5"; public const string PosterNameCustomTips = "CustomTips"; public const string WiderShipSideLeft = "Left"; public const string WiderShipSideRight = "Right"; public const string WiderShipSideBoth = "Both"; public const string SinglePackFolderName = "CustomPosters"; public const string PackSyncIdentifier = "CustomPosters_SyncPack"; public const string SeedSyncIdentifier = "CustomPosters_SyncSeed"; public const string VideoRequestIdentifier = "CustomPosters_RequestVideoTime"; public const string VideoSyncIdentifier = "CustomPosters_SyncVideoTime"; public const string Es3SelectedPackKey = "CustomPosters_SelectedPack"; public const int PosterRenderTextureWidth = 512; public const int PosterRenderTextureHeight = 512; public const int PosterRenderTextureDepth = 16; } [BepInPlugin("CustomPosters", "CustomPosters", "4.0.4")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private readonly Harmony _harmony = new Harmony("CustomPosters"); public static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } public static PosterService Service { get; private set; } public static PosterConfig ModConfig { get; private set; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"Initializing CustomPosters"); AssetManager.LoadAssets(); ModConfig = new PosterConfig(((BaseUnityPlugin)this).Config); Service = new PosterService(); ModConfig.Initialize(); Log.LogInfo((object)"Applying patches"); _harmony.PatchAll(typeof(StartOfRoundPatch)); _harmony.PatchAll(typeof(GameNetworkManagerPatch)); Log.LogInfo((object)"CustomPosters is loaded!"); } } internal class FileConfig { public ConfigEntry<bool> Enabled { get; } public ConfigEntry<int> Chance { get; } public ConfigEntry<int>? Volume { get; set; } public ConfigEntry<float>? MaxDistance { get; set; } public ConfigEntry<PosterConfig.VideoAspectRatio>? AspectRatio { get; set; } public FileConfig(ConfigEntry<bool> enabled, ConfigEntry<int> chance) { Enabled = enabled; Chance = chance; } } public class PosterConfig { [Serializable] public enum RandomizerMode { PerPack, PerPoster } [Serializable] public enum VideoAspectRatio { Stretch, FitInside, FitOutside, NoScaling } [Serializable] public enum KeepFor { Lobby, Session, SaveSlot } [Flags] public enum VanillaModelOption { None = 0, Poster5 = 1, Tips = 2, Both = 3 } private readonly Dictionary<string, ConfigEntry<bool>> _packEnabledEntries = new Dictionary<string, ConfigEntry<bool>>(); private readonly Dictionary<string, ConfigEntry<int>> _packChanceEntries = new Dictionary<string, ConfigEntry<int>>(); private readonly Dictionary<string, FileConfig> _fileConfigs = new Dictionary<string, FileConfig>(); private readonly ConfigFile _configFile; public ConfigEntry<bool> EnableNetworking { get; private set; } public ConfigEntry<RandomizerMode> RandomizerModeSetting { get; private set; } public ConfigEntry<KeepFor> KeepPackFor { get; private set; } public ConfigEntry<bool> EnableTextureCaching { get; private set; } public ConfigEntry<bool> EnableVideoAudio { get; private set; } public ConfigEntry<VanillaModelOption> VanillaModelSelection { get; private set; } public bool UsePoster5VanillaModel => VanillaModelSelection.Value.HasFlag(VanillaModelOption.Poster5); public bool UseTipsVanillaModel => VanillaModelSelection.Value.HasFlag(VanillaModelOption.Tips); public PosterConfig(ConfigFile config) { _configFile = config; } public void Initialize() { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Expected O, but got Unknown //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Expected O, but got Unknown //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02dc: Expected O, but got Unknown //IL_0321: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Expected O, but got Unknown _configFile.SaveOnConfigSet = false; EnableNetworking = _configFile.Bind<bool>("1. Settings", "Enable Networking", true, "Sync posters with all players (requires all to have the mod); false = client-side, makes compatible with vanilla lobbies."); RandomizerModeSetting = _configFile.Bind<RandomizerMode>("1. Settings", "RandomizerMode", RandomizerMode.PerPack, "Controls randomization: PerPack selects one pack for all posters; PerPoster randomizes each poster from enabled packs."); KeepPackFor = _configFile.Bind<KeepFor>("1. Settings", "Keep pack for", KeepFor.Lobby, "Keeping selection: Lobby = reroll each lobby; Session = until game restart; Save slot = per save file."); VanillaModelSelection = _configFile.Bind<VanillaModelOption>("1. Settings", "Vanilla Model", VanillaModelOption.Both, new ConfigDescription("Choose vanilla LC mesh: None (use quads), Poster5, Tips, or Both.", (AcceptableValueBase)null, Array.Empty<object>())); EnableVideoAudio = _configFile.Bind<bool>("1. Settings", "EnableVideoAudio", false, "Enable audio for .mp4 poster videos; disable to mute."); EnableTextureCaching = _configFile.Bind<bool>("1. Settings", "EnableTextureCaching", false, "Cache textures/videos in memory to improve performance; disable to reduce memory usage."); int num = 2; foreach (string posterFolder in Plugin.Service.PosterFolders) { try { string text = PackName(posterFolder); if (string.IsNullOrEmpty(text)) { continue; } string text2 = $"{num}. {text}"; string text3 = $"{num}. {text} - Chances"; ConfigEntry<bool> value = _configFile.Bind<bool>(text2, "Enabled", true, "Enable or disable the " + text + " pack"); _packEnabledEntries[text] = value; ConfigEntry<int> value2 = _configFile.Bind<int>(text3, "Global chance", 0, new ConfigDescription("Chance of selecting the " + text + " pack in PerPack mode; 0 = equal probability with other packs.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); _packChanceEntries[text] = value2; IEnumerable<string> filesFromPack = GetFilesFromPack(posterFolder); foreach (string item in filesFromPack) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(item); string text4 = Path.GetExtension(item).TrimStart('.').ToUpper(); string fileName = Path.GetFileName(item); string text5 = fileNameWithoutExtension + "-" + text4; ConfigEntry<bool> enabled = _configFile.Bind<bool>(text2, text5, true, "Enable or disable poster file '" + fileName + "' in pack '" + text + "'"); ConfigEntry<int> chance = _configFile.Bind<int>(text3, text5 + " Chance", 0, new ConfigDescription("Chance of selecting poster '" + fileName + "' in PerPoster mode; 0 = equal probability with other posters.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); FileConfig fileConfig = new FileConfig(enabled, chance); if (text4 == "MP4") { fileConfig.Volume = _configFile.Bind<int>(text2, text5 + " Volume", 10, new ConfigDescription("Volume for video '" + fileName + "'.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); fileConfig.MaxDistance = _configFile.Bind<float>(text2, text5 + " MaxDistance", 3.5f, new ConfigDescription("Maximum distance for audio playback of video '" + fileName + "'", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>())); fileConfig.AspectRatio = _configFile.Bind<VideoAspectRatio>(text2, text5 + " AspectRatio", VideoAspectRatio.Stretch, "Aspect ratio for video '" + fileName + "': Stretch (fill), FitInside (no crop), FitOutside (may crop), NoScaling (original size)."); } _fileConfigs[item] = fileConfig; } num++; } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to generate config for pack at " + PathUtils.GetPrettyPath(posterFolder) + ": " + ex.Message)); } } ClearOrphanedEntries(); _configFile.Save(); _configFile.SaveOnConfigSet = true; Plugin.Log.LogInfo((object)$"Found {_packEnabledEntries.Count} packs and {_fileConfigs.Count} poster files"); } public bool IsPackEnabled(string packPath) { string key = PackName(packPath); if (_packEnabledEntries.TryGetValue(key, out ConfigEntry<bool> value)) { return value.Value; } return true; } public int GetPackChance(string packPath) { string key = PackName(packPath); if (_packChanceEntries.TryGetValue(key, out ConfigEntry<int> value)) { return value.Value; } return 0; } public bool IsFileEnabled(string filePath) { if (_fileConfigs.TryGetValue(Path.GetFullPath(filePath), out FileConfig value)) { return value.Enabled.Value; } return true; } public int GetFileChance(string filePath) { if (_fileConfigs.TryGetValue(Path.GetFullPath(filePath), out FileConfig value)) { return value.Chance.Value; } return 0; } public (int volume, float maxDistance, VideoAspectRatio aspectRatio) GetFileAudioSettings(string filePath) { if (_fileConfigs.TryGetValue(Path.GetFullPath(filePath), out FileConfig value)) { int item = value.Volume?.Value ?? 10; float item2 = value.MaxDistance?.Value ?? 3.5f; VideoAspectRatio item3 = value.AspectRatio?.Value ?? VideoAspectRatio.Stretch; return (item, item2, item3); } return (10, 3.5f, VideoAspectRatio.Stretch); } private static string PackName(string packPath) { return PathUtils.GetDisplayPackName(packPath); } private static IEnumerable<string> GetFilesFromPack(string packPath) { List<string> list = new List<string>(); string path = Path.Combine(packPath, "posters"); if (Directory.Exists(path)) { list.AddRange(from f in Directory.GetFiles(path) where Constants.AllValidExtensions.Contains(Path.GetExtension(f).ToLowerInvariant()) select f); } string path2 = Path.Combine(packPath, "tips"); if (Directory.Exists(path2)) { list.AddRange(from f in Directory.GetFiles(path2) where Constants.AllValidExtensions.Contains(Path.GetExtension(f).ToLowerInvariant()) select f); } string text = Path.Combine(packPath, "CustomTips.png"); if (File.Exists(text)) { list.Add(text); } list.AddRange(from f in Directory.GetFiles(packPath) where Constants.AllValidExtensions.Contains(Path.GetExtension(f).ToLowerInvariant()) select f); return from f in list.Distinct<string>(StringComparer.OrdinalIgnoreCase) select Path.GetFullPath(f); } private void ClearOrphanedEntries() { try { PropertyInfo propertyInfo = AccessTools.Property(typeof(ConfigFile), "OrphanedEntries"); if (propertyInfo != null && propertyInfo.GetValue(_configFile) is Dictionary<ConfigDefinition, string> dictionary) { dictionary.Clear(); Plugin.Log.LogDebug((object)"Cleared orphaned config entries"); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not clear orphaned config entries: " + ex.Message)); } } } internal static class PosterManager { [CompilerGenerated] private sealed class <>c__DisplayClass33_0 { public bool anyPosterLoaded; internal void <CreateCustomPostersAsync>b__0(bool loaded) { anyPosterLoaded = loaded; } } [CompilerGenerated] private sealed class <>c__DisplayClass43_0 { public (Texture2D? texture, string? filePath) result; public Task loadTask; internal void <LoadTextureToDictionary>b__0((Texture2D? texture, string? filePath) r) { result = r; } internal bool <LoadTextureToDictionary>b__1() { return loadTask.IsCompleted; } } [CompilerGenerated] private sealed class <CreateCustomPostersAsync>d__33 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private <>c__DisplayClass33_0 <>8__1; private GameObject <hangarShip>5__2; private GameObject <postersParent>5__3; private GameObject <posterPlane>5__4; private PosterData[] <posterData>5__5; private Dictionary<string, List<(Texture2D texture, string filePath)>> <allTextures>5__6; private Dictionary<string, List<string>> <allVideos>5__7; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <CreateCustomPostersAsync>d__33(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; <hangarShip>5__2 = null; <postersParent>5__3 = null; <posterPlane>5__4 = null; <posterData>5__5 = null; <allTextures>5__6 = null; <allVideos>5__7 = null; <>1__state = -2; } private bool MoveNext() { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: { <>1__state = -1; <>8__1 = new <>c__DisplayClass33_0(); CleanUpPosters(); <hangarShip>5__2 = GameObject.Find("Environment/HangarShip"); if ((Object)(object)<hangarShip>5__2 == (Object)null) { return false; } <postersParent>5__3 = new GameObject("CustomPosters"); <postersParent>5__3.transform.SetParent(<hangarShip>5__2.transform); <postersParent>5__3.transform.localPosition = Vector3.zero; <posterPlane>5__4 = GameObject.Find("Environment/HangarShip/Plane.001"); <posterData>5__5 = PosterLayoutProvider.GetLayout(); if (!TryResolvePacksToUse(<posterPlane>5__4, out List<string> packsToUse)) { return false; } <allTextures>5__6 = new Dictionary<string, List<(Texture2D, string)>>(); <allVideos>5__7 = new Dictionary<string, List<string>>(); <>2__current = LoadContentFromPacks(packsToUse, <allTextures>5__6, <allVideos>5__7); <>1__state = 1; return true; } case 1: { <>1__state = -1; if (<allTextures>5__6.Count == 0 && <allVideos>5__7.Count == 0) { Plugin.Log.LogWarning((object)"No textures found in enabled packs"); if ((Object)(object)<posterPlane>5__4 != (Object)null) { <posterPlane>5__4.SetActive(true); } return false; } Dictionary<string, (Texture2D, string, bool)> prioritizedContent = BuildPrioritizedContent(<allTextures>5__6, <allVideos>5__7); <>8__1.anyPosterLoaded = false; <>2__current = SpawnPostersFromLayout(<posterData>5__5, <postersParent>5__3, prioritizedContent, delegate(bool loaded) { <>8__1.anyPosterLoaded = loaded; }); <>1__state = 2; return true; } case 2: <>1__state = -1; FinalizeRender(<>8__1.anyPosterLoaded, <hangarShip>5__2, <posterPlane>5__4); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <DelayedUpdateMaterialsAsync>d__50 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public StartOfRound instance; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DelayedUpdateMaterialsAsync>d__50(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (_isUpdating) { return false; } _isUpdating = true; <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 1; return true; case 1: <>1__state = -1; goto IL_004c; case 2: { <>1__state = -1; if (!_needsReUpdate) { _isUpdating = false; return false; } goto IL_004c; } IL_004c: _needsReUpdate = false; HideVanillaPosterPlane(); <>2__current = ((MonoBehaviour)instance).StartCoroutine(CreateCustomPostersAsync()); <>1__state = 2; return true; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <LoadContentFromPacks>d__40 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public List<string> packsToUse; public Dictionary<string, List<string>> allVideos; public Dictionary<string, List<(Texture2D texture, string filePath)>> allTextures; private List<string>.Enumerator <>7__wrap1; private List<string> <filesToLoad>5__3; private int <i>5__4; private List<string>.Enumerator <>7__wrap4; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <LoadContentFromPacks>d__40(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if ((uint)(num - -4) <= 1u || (uint)(num - 1) <= 1u) { try { if (num == -4 || num == 1) { try { } finally { <>m__Finally2(); } } } finally { <>m__Finally1(); } } <>7__wrap1 = default(List<string>.Enumerator); <filesToLoad>5__3 = null; <>7__wrap4 = default(List<string>.Enumerator); <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>7__wrap1 = packsToUse.GetEnumerator(); <>1__state = -3; goto IL_014e; case 1: <>1__state = -4; goto IL_00ea; case 2: { <>1__state = -3; <i>5__4 += 5; goto IL_0131; } IL_014e: if (<>7__wrap1.MoveNext()) { string current = <>7__wrap1.Current; <filesToLoad>5__3 = GatherValidFilesForPack(current); <i>5__4 = 0; goto IL_0131; } <>m__Finally1(); <>7__wrap1 = default(List<string>.Enumerator); return false; IL_00ea: while (<>7__wrap4.MoveNext()) { string current2 = <>7__wrap4.Current; if (current2.IsVideo()) { RegisterVideo(current2, allVideos); continue; } <>2__current = LoadTextureToDictionary(current2, allTextures); <>1__state = 1; return true; } <>m__Finally2(); <>7__wrap4 = default(List<string>.Enumerator); <>2__current = null; <>1__state = 2; return true; IL_0131: if (<i>5__4 < <filesToLoad>5__3.Count) { List<string> list = <filesToLoad>5__3.Skip(<i>5__4).Take(5).ToList(); <>7__wrap4 = list.GetEnumerator(); <>1__state = -4; goto IL_00ea; } <filesToLoad>5__3 = null; goto IL_014e; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; ((IDisposable)<>7__wrap1).Dispose(); } private void <>m__Finally2() { <>1__state = -3; ((IDisposable)<>7__wrap4).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <LoadTextureToDictionary>d__43 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string file; private <>c__DisplayClass43_0 <>8__1; public Dictionary<string, List<(Texture2D texture, string filePath)>> allTextures; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <LoadTextureToDictionary>d__43(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; <>1__state = -2; } private bool MoveNext() { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass43_0(); <>8__1.result = (null, null); <>8__1.loadTask = LoadTextureAsync(file, delegate((Texture2D? texture, string? filePath) r) { <>8__1.result = r; }); <>2__current = (object)new WaitUntil((Func<bool>)(() => <>8__1.loadTask.IsCompleted)); <>1__state = 1; return true; case 1: { <>1__state = -1; if ((Object)(object)<>8__1.result.texture == (Object)null || <>8__1.result.filePath == null) { Plugin.Log.LogWarning((object)("Failed to load texture from " + file)); return false; } string posterName = <>8__1.result.filePath.GetPosterName(); if (!allTextures.ContainsKey(posterName)) { allTextures[posterName] = new List<(Texture2D, string)>(); } allTextures[posterName].Add((<>8__1.result.texture, <>8__1.result.filePath)); return false; } } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <SpawnPostersFromLayout>d__45 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public PosterData[] posterData; public GameObject postersParent; public Dictionary<string, (Texture2D? texture, string filePath, bool isVideo)> prioritizedContent; public Action<bool> onCompleted; private bool <anyPosterLoaded>5__2; private int <i>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <SpawnPostersFromLayout>d__45(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_00b6; } <>1__state = -1; <anyPosterLoaded>5__2 = false; <i>5__3 = 0; goto IL_00c6; IL_00b6: <i>5__3++; goto IL_00c6; IL_00c6: if (<i>5__3 < posterData.Length) { GameObject val = CreatePoster(posterData[<i>5__3].Name); if (!((Object)(object)val == (Object)null)) { ApplyPosterTransform(val, posterData[<i>5__3], postersParent.transform); if (TryAssignPosterContent(val, posterData[<i>5__3].Name, prioritizedContent)) { <anyPosterLoaded>5__2 = true; } <>2__current = null; <>1__state = 1; return true; } goto IL_00b6; } onCompleted(<anyPosterLoaded>5__2); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static bool _isUpdating = false; private static bool _needsReUpdate = false; internal static string? _selectedPack = null; private static bool _clientSeedReceived = false; private static int _currentHostSeed = 0; private static readonly List<GameObject> CreatedPosters = new List<GameObject>(); private static int _sessionMapSeed = 0; private static bool _sessionSeedInitialized = false; public static string? SelectedPack => _selectedPack; public static int CurrentHostSeed => _currentHostSeed; public static bool IsNewLobby { get; set; } = true; private static bool ShouldActAsHost { get { if (Plugin.ModConfig.EnableNetworking.Value) { if ((Object)(object)NetworkManager.Singleton != (Object)null) { return NetworkManager.Singleton.IsHost; } return false; } return true; } } public static void ResetSession() { _sessionSeedInitialized = false; _sessionMapSeed = 0; _selectedPack = null; _clientSeedReceived = false; _currentHostSeed = 0; _isUpdating = false; _needsReUpdate = false; Plugin.Log.LogDebug((object)"Session randomization reset."); } public static void OnSceneAwake() { _isUpdating = false; _needsReUpdate = false; if (Plugin.ModConfig.EnableNetworking.Value && !((Object)(object)NetworkManager.Singleton == (Object)null)) { if (NetworkManager.Singleton.IsHost) { _currentHostSeed = 0; return; } _clientSeedReceived = false; _selectedPack = null; _currentHostSeed = 0; Plugin.Log.LogDebug((object)"Client per lobby state reset, waiting for host seed."); } } public static void SetPackForClients(string packName) { if (!Plugin.ModConfig.EnableNetworking.Value || (Object)(object)NetworkManager.Singleton == (Object)null || NetworkManager.Singleton.IsHost) { return; } Plugin.Log.LogDebug((object)("Client received selected pack from host - " + PathUtils.GetPrettyPath(packName))); _selectedPack = LocalPackPath(packName); if (string.IsNullOrEmpty(_selectedPack)) { Plugin.Log.LogWarning((object)"Could not resolve host selected pack on client. Keeping vanilla posters."); return; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null && instance.inShipPhase) { if (_isUpdating) { _needsReUpdate = true; } else { ((MonoBehaviour)instance).StartCoroutine(DelayedUpdateMaterialsAsync(instance)); } } } public static void SetSeedForClients(int seed) { if (!Plugin.ModConfig.EnableNetworking.Value || (Object)(object)NetworkManager.Singleton == (Object)null || NetworkManager.Singleton.IsHost) { return; } Plugin.Log.LogDebug((object)$"Client received seed from host: {seed}"); Plugin.Service.SetRandomSeed(seed); _clientSeedReceived = true; StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null && instance.inShipPhase) { if (_isUpdating) { _needsReUpdate = true; } else { ((MonoBehaviour)instance).StartCoroutine(DelayedUpdateMaterialsAsync(instance)); } } } private static string? LocalPackPath(string hostPackIdentifier) { try { string displayPackName = PathUtils.GetDisplayPackName(hostPackIdentifier); string fileName = Path.GetFileName(hostPackIdentifier.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); foreach (string posterFolder in Plugin.Service.PosterFolders) { if (string.Equals(PathUtils.GetDisplayPackName(posterFolder), displayPackName, StringComparison.OrdinalIgnoreCase)) { return posterFolder; } } foreach (string posterFolder2 in Plugin.Service.PosterFolders) { if (string.Equals(Path.GetFileName(posterFolder2), fileName, StringComparison.OrdinalIgnoreCase)) { return posterFolder2; } } } catch (Exception ex) { Plugin.Log.LogDebug((object)("LocalPackPath error: " + ex.Message)); } return null; } public static void OnRoundStart(StartOfRound instance) { _isUpdating = false; _needsReUpdate = false; if (IsNewLobby && ShouldActAsHost) { InitializeSessionSeedIfNeeded(); int num = ComputeSeedAndMaybeLoadSavePack(Plugin.ModConfig.KeepPackFor.Value); Plugin.Service.SetRandomSeed(num); _currentHostSeed = num; PosterSyncManager.SendSeed(num); } if (instance.inShipPhase) { ((MonoBehaviour)instance).StartCoroutine(DelayedUpdateMaterialsAsync(instance)); } IsNewLobby = false; } private static void InitializeSessionSeedIfNeeded() { PosterConfig.KeepFor value = Plugin.ModConfig.KeepPackFor.Value; if (!_sessionSeedInitialized) { _sessionMapSeed = ((value == PosterConfig.KeepFor.Session) ? StartOfRound.Instance.randomMapSeed : Environment.TickCount); _sessionSeedInitialized = true; Plugin.Log.LogDebug((object)$"Seed: {_sessionMapSeed}"); } } private static int ComputeSeedAndMaybeLoadSavePack(PosterConfig.KeepFor mode) { switch (mode) { case PosterConfig.KeepFor.Session: return _sessionMapSeed; case PosterConfig.KeepFor.SaveSlot: { string text = null; try { text = SavePersistenceManager.TryGetCurrentSaveId(); } catch (Exception) { } int result = ((!string.IsNullOrEmpty(text)) ? HashUtils.DeterministicHash(text) : Environment.TickCount); TryLoadSavedPack(); return result; } default: _selectedPack = null; return Environment.TickCount; } } private static void TryLoadSavedPack() { try { string text = SavePersistenceManager.TryGetCurrentSaveId(); string text2 = null; if (!string.IsNullOrEmpty(text)) { text2 = SavePersistenceManager.TryLoadSelectedPack(text); } if (!string.IsNullOrEmpty(text2)) { _selectedPack = text2; Plugin.Log.LogInfo((object)("Loaded saved pack for save '" + text + "': " + PathUtils.GetPrettyPath(_selectedPack))); } else { _selectedPack = null; } } catch (Exception ex) { _selectedPack = null; Plugin.Log.LogDebug((object)("SaveSlot load skipped: " + ex.Message)); } } private static async Task LoadTextureAsync(string filePath, Action<(Texture2D? texture, string? filePath)> onComplete) { try { if (!File.Exists(filePath)) { Plugin.Log.LogError((object)("File not found: " + PathUtils.GetPrettyPath(filePath))); onComplete?.Invoke((null, null)); return; } Texture2D cachedTexture = Plugin.Service.GetCachedTexture(filePath); if ((Object)(object)cachedTexture != (Object)null) { onComplete?.Invoke((cachedTexture, filePath)); return; } byte[] array = await File.ReadAllBytesAsync(filePath); Texture2D val = new Texture2D(2, 2); if (!ImageConversion.LoadImage(val, array)) { Plugin.Log.LogError((object)("Failed to load texture from " + PathUtils.GetPrettyPath(filePath))); onComplete?.Invoke((null, null)); } else { ((Texture)val).filterMode = (FilterMode)0; Plugin.Service.CacheTexture(filePath, val); onComplete?.Invoke((val, filePath)); } } catch (Exception ex) { Plugin.Log.LogError((object)("Error loading file " + PathUtils.GetPrettyPath(filePath) + ": " + ex.Message)); onComplete?.Invoke((null, null)); } } private static void HideVanillaPosterPlane() { GameObject val = GameObject.Find("Environment/HangarShip/Plane.001 (Old)") ?? GameObject.Find("Environment/HangarShip/Plane.001"); if (val != null) { val.SetActive(false); } } private static void CleanUpPosters() { foreach (GameObject createdPoster in CreatedPosters) { if (!((Object)(object)createdPoster == (Object)null)) { PosterRenderer component = createdPoster.GetComponent<PosterRenderer>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } Object.Destroy((Object)(object)createdPoster); } } CreatedPosters.Clear(); } private static GameObject? CreatePoster(string posterName) { GameObject val = null; val = ((posterName == "CustomTips") ? (Plugin.ModConfig.UseTipsVanillaModel ? AssetManager.TipsPrefab : AssetManager.PosterPrefab) : ((!(posterName == "Poster5")) ? AssetManager.PosterPrefab : (Plugin.ModConfig.UsePoster5VanillaModel ? AssetManager.Poster5Prefab : AssetManager.PosterPrefab))); if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)("Failed to find prefab for " + posterName + ". Falling back to default poster prefab.")); val = AssetManager.PosterPrefab; if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)"Default poster prefab is also null!"); return null; } } return Object.Instantiate<GameObject>(val); } public static double? GetVideoTimeForPoster(string posterName) { string posterName2 = posterName; GameObject val = ((IEnumerable<GameObject>)CreatedPosters).FirstOrDefault((Func<GameObject, bool>)((GameObject p) => ((Object)p).name == posterName2)); if (val == null) { return null; } return val.GetComponent<PosterRenderer>()?.GetCurrentVideoTime(); } public static void SetVideoTimeForPoster(string posterName, double time) { string posterName2 = posterName; GameObject val = ((IEnumerable<GameObject>)CreatedPosters).FirstOrDefault((Func<GameObject, bool>)((GameObject p) => ((Object)p).name == posterName2)); if (val != null) { val.GetComponent<PosterRenderer>()?.SetVideoTime(time); } } [IteratorStateMachine(typeof(<CreateCustomPostersAsync>d__33))] private static IEnumerator CreateCustomPostersAsync() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <CreateCustomPostersAsync>d__33(0); } private static bool TryResolvePacksToUse(GameObject? posterPlane, out List<string> packsToUse) { packsToUse = new List<string>(); List<string> list = Plugin.Service.PosterFolders.Where((string folder) => Plugin.ModConfig.IsPackEnabled(folder)).ToList(); bool flag = Plugin.ModConfig.EnableNetworking.Value && !ShouldActAsHost && !string.IsNullOrEmpty(_selectedPack); if (list.Count == 0 && !flag) { Plugin.Log.LogWarning((object)"No enabled packs found"); if ((Object)(object)posterPlane != (Object)null) { posterPlane.SetActive(true); } return false; } if (Plugin.ModConfig.RandomizerModeSetting.Value == PosterConfig.RandomizerMode.PerPack) { return TryResolvePerPackSelection(list, posterPlane, out packsToUse); } if (Plugin.ModConfig.EnableNetworking.Value && !ShouldActAsHost && !_clientSeedReceived) { Plugin.Log.LogInfo((object)"Client is waiting for host to send seed..."); if ((Object)(object)posterPlane != (Object)null) { posterPlane.SetActive(true); } return false; } Plugin.Log.LogInfo((object)"PerPoster mode enabled."); packsToUse = list; return true; } private static bool TryResolvePerPackSelection(List<string> enabledPacks, GameObject? posterPlane, out List<string> packsToUse) { packsToUse = new List<string>(); if (ShouldActAsHost) { SelectHostPack(enabledPacks); BroadcastSelectedPackIfNeeded(); } if (string.IsNullOrEmpty(_selectedPack)) { Plugin.Log.LogInfo((object)(Plugin.ModConfig.EnableNetworking.Value ? "Client is waiting for host to select a pack..." : "No valid pack found, falling back to vanilla posters")); if ((Object)(object)posterPlane != (Object)null) { posterPlane.SetActive(true); } return false; } Plugin.Log.LogInfo((object)("Using pack: " + PathUtils.GetDisplayPackName(_selectedPack))); packsToUse = new List<string> { _selectedPack }; return true; } private static void SelectHostPack(List<string> enabledPacks) { PosterConfig.KeepFor value = Plugin.ModConfig.KeepPackFor.Value; if (value == PosterConfig.KeepFor.SaveSlot && string.IsNullOrEmpty(SavePersistenceManager.TryGetCurrentSaveId())) { _selectedPack = null; } if (value switch { PosterConfig.KeepFor.Lobby => true, PosterConfig.KeepFor.Session => _selectedPack == null || !enabledPacks.Contains(_selectedPack), PosterConfig.KeepFor.SaveSlot => _selectedPack == null || !enabledPacks.Contains(_selectedPack), _ => true, }) { _selectedPack = PickFirstValidPack(enabledPacks); if (!string.IsNullOrEmpty(_selectedPack) && value == PosterConfig.KeepFor.SaveSlot) { PersistSelectionForSaveSlot(_selectedPack); } } } private static string? PickFirstValidPack(List<string> candidates) { List<string> list = new List<string>(candidates); while (list.Count > 0) { string text = PackSelector.SelectPackByChance(list); if (PackHasValidFiles(text)) { return text; } list.Remove(text); } return null; } private static void PersistSelectionForSaveSlot(string selectedPack) { try { string text = SavePersistenceManager.TryGetCurrentSaveId(); if (!string.IsNullOrEmpty(text)) { SavePersistenceManager.SaveSelectedPack(text, selectedPack); Plugin.Log.LogInfo((object)("Saved selected pack for save '" + text + "'")); } else { Plugin.Log.LogDebug((object)"PerSave: No valid saveId yet; not persisting selection."); } } catch (Exception ex) { Plugin.Log.LogDebug((object)("SaveSlot save skipped: " + ex.Message)); } } private static void BroadcastSelectedPackIfNeeded() { if (!string.IsNullOrEmpty(_selectedPack) && Plugin.ModConfig.EnableNetworking.Value) { PosterSyncManager.SendPacket(_selectedPack); } } [IteratorStateMachine(typeof(<LoadContentFromPacks>d__40))] private static IEnumerator LoadContentFromPacks(List<string> packsToUse, Dictionary<string, List<(Texture2D texture, string filePath)>> allTextures, Dictionary<string, List<string>> allVideos) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <LoadContentFromPacks>d__40(0) { packsToUse = packsToUse, allTextures = allTextures, allVideos = allVideos }; } private static List<string> GatherValidFilesForPack(string pack) { string[] source = new string[4] { Path.Combine(pack, "posters"), Path.Combine(pack, "tips"), Path.Combine(pack, "CustomPosters", "posters"), Path.Combine(pack, "CustomPosters", "tips") }; IEnumerable<string> enumerable = (from p in source.Where(Directory.Exists) select Path.GetFullPath(p).NormalizePath()).Distinct<string>(StringComparer.OrdinalIgnoreCase); List<string> list = new List<string>(); foreach (string item in enumerable) { list.AddRange(from f in Directory.GetFiles(item) where f.IsValidPosterFile() && Plugin.ModConfig.IsFileEnabled(f) select Path.GetFullPath(f).NormalizePath()); } return list.Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList(); } private static void RegisterVideo(string file, Dictionary<string, List<string>> allVideos) { string posterName = file.GetPosterName(); if (!allVideos.ContainsKey(posterName)) { allVideos[posterName] = new List<string>(); } allVideos[posterName].Add(file); Plugin.Service.CacheVideo(file); } [IteratorStateMachine(typeof(<LoadTextureToDictionary>d__43))] private static IEnumerator LoadTextureToDictionary(string file, Dictionary<string, List<(Texture2D texture, string filePath)>> allTextures) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <LoadTextureToDictionary>d__43(0) { file = file, allTextures = allTextures }; } private static Dictionary<string, (Texture2D? texture, string filePath, bool isVideo)> BuildPrioritizedContent(Dictionary<string, List<(Texture2D texture, string filePath)>> allTextures, Dictionary<string, List<string>> allVideos) { Dictionary<string, (Texture2D, string, bool)> dictionary = new Dictionary<string, (Texture2D, string, bool)>(); IEnumerable<string> enumerable = allTextures.Keys.Union(allVideos.Keys); foreach (string item in enumerable) { List<(Texture2D, string, bool)> list = new List<(Texture2D, string, bool)>(); if (allTextures.TryGetValue(item, out List<(Texture2D, string)> value)) { list.AddRange(value.Select<(Texture2D, string), (Texture2D, string, bool)>(((Texture2D texture, string filePath) t) => (t.texture, t.filePath, false))); } if (allVideos.TryGetValue(item, out List<string> value2)) { list.AddRange(((IEnumerable<string>)value2).Select((Func<string, (Texture2D, string, bool)>)((string v) => (null, v, true)))); } if (list.Count != 0) { dictionary[item] = PackSelector.SelectContentByChance<(Texture2D, string, bool)>(list, ((Texture2D texture, string filePath, bool isVideo) item) => Plugin.ModConfig.GetFileChance(item.filePath), ((Texture2D texture, string filePath, bool isVideo) item) => Plugin.Service.GetFilePriority(item.filePath)); } } return dictionary; } [IteratorStateMachine(typeof(<SpawnPostersFromLayout>d__45))] private static IEnumerator SpawnPostersFromLayout(PosterData[] posterData, GameObject postersParent, Dictionary<string, (Texture2D? texture, string filePath, bool isVideo)> prioritizedContent, Action<bool> onCompleted) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <SpawnPostersFromLayout>d__45(0) { posterData = posterData, postersParent = postersParent, prioritizedContent = prioritizedContent, onCompleted = onCompleted }; } private static void ApplyPosterTransform(GameObject poster, PosterData data, Transform parent) { //IL_001f: 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_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) ((Object)poster).name = data.Name; poster.transform.SetParent(parent); poster.transform.position = data.Position; poster.transform.rotation = Quaternion.Euler(data.Rotation); poster.transform.localScale = data.Scale; } private static bool TryAssignPosterContent(GameObject poster, string posterName, Dictionary<string, (Texture2D? texture, string filePath, bool isVideo)> prioritizedContent) { string key = posterName.ToLower(); if (!prioritizedContent.TryGetValue(key, out (Texture2D, string, bool) value) || !Plugin.ModConfig.IsFileEnabled(value.Item2)) { Plugin.Log.LogWarning((object)("No enabled texture found for " + posterName + ". Destroying the poster")); Object.Destroy((Object)(object)poster); return false; } PosterRenderer posterRenderer = poster.AddComponent<PosterRenderer>(); MeshRenderer component = poster.GetComponent<MeshRenderer>(); posterRenderer.Initialize(value.Item1, value.Item3 ? value.Item2 : null, (component != null) ? ((Renderer)component).material : null); CreatedPosters.Add(poster); return true; } private static void FinalizeRender(bool anyPosterLoaded, GameObject hangarShip, GameObject? posterPlane) { if (anyPosterLoaded) { if ((Object)(object)posterPlane != (Object)null) { Object.Destroy((Object)(object)posterPlane); } Transform obj = hangarShip.transform.Find("Plane"); GameObject val = ((obj != null) ? ((Component)obj).gameObject : null); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } Plugin.Log.LogInfo((object)"Posters created successfully!"); } else if ((Object)(object)posterPlane != (Object)null) { posterPlane.SetActive(true); Plugin.Log.LogWarning((object)"Re-enabled vanilla Plane.001 poster due to no textures loaded"); } } private static bool PackHasValidFiles(string pack) { try { string text = Path.Combine(pack, "posters"); string text2 = Path.Combine(pack, "tips"); string text3 = Path.Combine(pack, "CustomPosters", "posters"); string text4 = Path.Combine(pack, "CustomPosters", "tips"); List<string> list = new string[4] { text, text2, text3, text4 }.Where((string p) => Directory.Exists(p)).ToList(); foreach (string item in list) { if (Directory.EnumerateFiles(item).Any((string f) => f.IsValidPosterFile())) { return true; } } if (File.Exists(Path.Combine(pack, "CustomTips.png"))) { return true; } } catch (Exception ex) { Plugin.Log.LogDebug((object)("PackHasValidFiles error for " + pack + ": " + ex.Message)); } return false; } [IteratorStateMachine(typeof(<DelayedUpdateMaterialsAsync>d__50))] private static IEnumerator DelayedUpdateMaterialsAsync(StartOfRound instance) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DelayedUpdateMaterialsAsync>d__50(0) { instance = instance }; } public static void ChangePosterPack(string packName) { if (string.IsNullOrEmpty(packName)) { List<string> enabledPackNames = Plugin.Service.GetEnabledPackNames(); if (enabledPackNames.Count == 0) { return; } int num = enabledPackNames.FindIndex((string p) => p.Equals(_selectedPack, StringComparison.OrdinalIgnoreCase)); _selectedPack = enabledPackNames[(num + 1) % enabledPackNames.Count]; } else { if (!Plugin.Service.GetEnabledPackNames().Contains<string>(packName, StringComparer.OrdinalIgnoreCase)) { Plugin.Log.LogWarning((object)("Attempted to select invalid pack: " + packName)); return; } _selectedPack = packName; } Plugin.Service.SetRandomSeed(Environment.TickCount); Plugin.Log.LogInfo((object)("Changed poster pack to - " + _selectedPack)); StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null && instance.inShipPhase) { if (_isUpdating) { _needsReUpdate = true; } else { ((MonoBehaviour)instance).StartCoroutine(DelayedUpdateMaterialsAsync(instance)); } } } } public class PosterRenderer : MonoBehaviour { private VideoPlayer? _videoPlayer; private AudioSource? _audioSource; private RenderTexture? _renderTexture; private float _originalVolume; private static VideoAspectRatio ConvertAspectRatio(PosterConfig.VideoAspectRatio aspectRatio) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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) return (VideoAspectRatio)(aspectRatio switch { PosterConfig.VideoAspectRatio.Stretch => 5, PosterConfig.VideoAspectRatio.FitInside => 3, PosterConfig.VideoAspectRatio.FitOutside => 4, PosterConfig.VideoAspectRatio.NoScaling => 0, _ => 5, }); } public void Initialize(Texture2D? texture, string? videoPath, Material? materialTemplate) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Expected O, but got Unknown //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Expected O, but got Unknown //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Expected O, but got Unknown string videoPath2 = videoPath; if ((Object)(object)materialTemplate == (Object)null) { Plugin.Log.LogError((object)"Cannot initialize poster, material template is null."); Object.Destroy((Object)(object)((Component)this).gameObject); return; } MeshRenderer component = ((Component)this).GetComponent<MeshRenderer>(); Material val2 = (((Renderer)component).material = new Material(materialTemplate)); if (videoPath2 != null && Path.GetExtension(videoPath2).ToLower() == ".mp4") { _renderTexture = new RenderTexture(512, 512, 16); val2.SetTexture("_BaseColorMap", (Texture)(object)_renderTexture); _videoPlayer = ((Component)this).gameObject.AddComponent<VideoPlayer>(); _videoPlayer.url = "file://" + videoPath2; _videoPlayer.renderMode = (VideoRenderMode)2; _videoPlayer.targetTexture = _renderTexture; var (num, maxDistance, aspectRatio) = Plugin.ModConfig.GetFileAudioSettings(videoPath2); _videoPlayer.aspectRatio = ConvertAspectRatio(aspectRatio); _videoPlayer.isLooping = true; _videoPlayer.playOnAwake = false; _audioSource = ((Component)this).gameObject.AddComponent<AudioSource>(); _audioSource.spatialBlend = 1f; _audioSource.spatialize = false; _audioSource.rolloffMode = (AudioRolloffMode)2; AnimationCurve val3 = new AnimationCurve((Keyframe[])(object)new Keyframe[6] { new Keyframe(0f, 1f), new Keyframe(0.157f, 0.547f), new Keyframe(0.517f, 0.278f), new Keyframe(1.259f, 0.102f), new Keyframe(2.69f, 0.033f), new Keyframe(4f, 0f) }); _audioSource.SetCustomCurve((AudioSourceCurveType)0, val3); _audioSource.maxDistance = maxDistance; _videoPlayer.Stop(); _videoPlayer.errorReceived += (ErrorEventHandler)delegate(VideoPlayer player, string message) { Plugin.Log.LogError((object)("VideoPlayer error for " + PathUtils.GetPrettyPath(videoPath2) + ": " + message)); }; _videoPlayer.prepareCompleted += (EventHandler)delegate(VideoPlayer player) { player.Play(); if (Plugin.ModConfig.EnableNetworking.Value && (Object)(object)NetworkManager.Singleton != (Object)null && !NetworkManager.Singleton.IsHost) { PosterSyncManager.RequestVideoTimeFromServer(((Object)((Component)this).gameObject).name); } }; _originalVolume = (float)num / 100f; if (Plugin.ModConfig.EnableVideoAudio.Value) { _videoPlayer.audioOutputMode = (VideoAudioOutputMode)1; _videoPlayer.SetTargetAudioSource((ushort)0, _audioSource); _audioSource.volume = _originalVolume; } else { _videoPlayer.audioOutputMode = (VideoAudioOutputMode)0; _audioSource.volume = 0f; } _videoPlayer.Prepare(); } else if ((Object)(object)texture != (Object)null) { val2.SetTexture("_BaseColorMap", (Texture)(object)texture); } else { Plugin.Log.LogError((object)("No valid texture for poster: " + ((Object)((Component)this).gameObject).name)); Object.Destroy((Object)(object)((Component)this).gameObject); } } public void SetVideoTime(double time) { if ((Object)(object)_videoPlayer != (Object)null && _videoPlayer.isPrepared) { _videoPlayer.time = time; } } public double? GetCurrentVideoTime() { if ((Object)(object)_videoPlayer != (Object)null && _videoPlayer.isPlaying) { return _videoPlayer.time; } return null; } private void Update() { if ((Object)(object)_audioSource != (Object)null && !Plugin.ModConfig.EnableVideoAudio.Value) { if (_audioSource.isPlaying) { _audioSource.Stop(); } _audioSource.volume = 0f; _audioSource.mute = true; } } private void OnDestroy() { if ((Object)(object)_renderTexture != (Object)null) { _renderTexture.Release(); Object.Destroy((Object)(object)_renderTexture); } if ((Object)(object)_videoPlayer != (Object)null && _videoPlayer.isPlaying) { _videoPlayer.Stop(); } } } public class PosterService { private readonly List<string> _posterFolders = new List<string>(); private readonly Dictionary<string, Texture2D> _textureCache = new Dictionary<string, Texture2D>(); private readonly Dictionary<string, string> _videoCache = new Dictionary<string, string>(); private Random _rand; public IReadOnlyList<string> PosterFolders => _posterFolders.AsReadOnly(); public bool IsBiggerShipInstalled { get; private set; } public bool IsShipWindowsInstalled { get; private set; } public bool IsRightWindowEnabled { get; private set; } public bool IsWiderShipModInstalled { get; private set; } public string WiderShipExtendedSide { get; private set; } = "Both"; public bool Is2StoryShipModInstalled { get; private set; } public bool EnableRightWindows { get; private set; } public bool EnableLeftWindows { get; private set; } public string TwoStoryShipLayout { get; private set; } = "Default"; public Random Rand => _rand; public PosterService() { DiscoverPosterPacks(); LogDiscoveredPacks(); InitializeBiggerShip(); InitializeShipWindows(); InitializeWiderShipMod(); Initialize2StoryShipMod(); SetRandomSeed(Environment.TickCount); } private void DiscoverPosterPacks() { try { string pluginPath = Paths.PluginPath; HashSet<string> hashSet = new HashSet<string>(Constants.ExcludedModFolderNames, StringComparer.OrdinalIgnoreCase); string[] directories = Directory.GetDirectories(pluginPath); foreach (string text in directories) { string fileName = Path.GetFileName(text); if (hashSet.Contains(fileName)) { continue; } string text2 = Path.Combine(text, "CustomPosters"); if (Directory.Exists(text2) && IsValidPosterPack(text2)) { _posterFolders.Add(text2); Plugin.Log.LogDebug((object)("Added single pack: " + PathUtils.GetPrettyPath(text2))); continue; } string[] directories2 = Directory.GetDirectories(text); bool flag = false; string[] array = directories2; foreach (string text3 in array) { try { if (IsValidPosterPack(text3)) { _posterFolders.Add(text3); Plugin.Log.LogDebug((object)("Added child pack: " + PathUtils.GetPrettyPath(text3))); flag = true; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Error while checking child folder " + text3 + ": " + ex.Message)); } } if (!flag && IsValidPosterPack(text)) { _posterFolders.Add(text); Plugin.Log.LogDebug((object)("Added pack from root folder: " + PathUtils.GetPrettyPath(text))); } } } catch (Exception ex2) { Plugin.Log.LogError((object)("Error scanning for poster packs: " + ex2.Message)); } } private void LogDiscoveredPacks() { try { if (_posterFolders.Count != 0) { List<string> values = _posterFolders.Select((string pack) => $"{PathUtils.GetDisplayPackName(pack)}={CountValidFiles(pack)}").ToList(); Plugin.Log.LogDebug((object)string.Format("Poster packs discovered: {0}; files per pack: {1}", _posterFolders.Count, string.Join(", ", values))); } } catch { } } private static int CountValidFiles(string pack) { try { int num = 0; string path = Path.Combine(pack, "posters"); if (Directory.Exists(path)) { num += Directory.GetFiles(path).Count((string f) => Constants.AllValidExtensions.Contains(Path.GetExtension(f).ToLowerInvariant())); } string path2 = Path.Combine(pack, "tips"); if (Directory.Exists(path2)) { num += Directory.GetFiles(path2).Count((string f) => Constants.AllValidExtensions.Contains(Path.GetExtension(f).ToLowerInvariant())); } return num + Directory.GetFiles(pack).Count((string f) => Constants.AllValidExtensions.Contains(Path.GetExtension(f).ToLowerInvariant())); } catch { return 0; } } private bool IsValidPosterPack(string folderPath) { try { string path = Path.Combine(folderPath, "posters"); if (Directory.Exists(path) && Directory.EnumerateFiles(path, "*.*", SearchOption.TopDirectoryOnly).Any((string file) => Constants.ValidImageExtensions.Contains(Path.GetExtension(file).ToLowerInvariant()) || Constants.ValidVideoExtensions.Contains(Path.GetExtension(file).ToLowerInvariant()))) { return true; } string text = Path.Combine(folderPath, "tips"); if (Directory.Exists(text) && File.Exists(Path.Combine(text, "CustomTips.png"))) { return true; } if (File.Exists(Path.Combine(folderPath, "CustomTips.png"))) { return true; } } catch (Exception ex) { Plugin.Log.LogDebug((object)("IsValidPosterPack error for " + PathUtils.GetPrettyPath(folderPath) + ": " + ex.Message)); } return false; } private void InitializeBiggerShip() { IsBiggerShipInstalled = Chainloader.PluginInfos.ContainsKey("BiggerShip"); if (IsBiggerShipInstalled) { Plugin.Log.LogInfo((object)"Detected BiggerShip"); } } private void InitializeShipWindows() { IsShipWindowsInstalled = Chainloader.PluginInfos.ContainsKey("TestAccount666.ShipWindows"); if (IsShipWindowsInstalled) { string configPath = Path.Combine(Paths.ConfigPath, "TestAccount666.ShipWindows.cfg"); IsRightWindowEnabled = ConfigFileReader.ReadBoolFromSection(configPath, "Right Window (SideRight)", "1. Enabled = ", defaultValue: false); Plugin.Log.LogInfo((object)$"Detected ShipWindows, RW - {IsRightWindowEnabled}"); } } private void InitializeWiderShipMod() { IsWiderShipModInstalled = Chainloader.PluginInfos.ContainsKey("mborsh.WiderShipMod"); if (!IsWiderShipModInstalled) { return; } try { FieldInfo fieldInfo = Type.GetType("WiderShipMod")?.GetField("ExtendedSide", BindingFlags.Static | BindingFlags.Public); if (fieldInfo != null) { WiderShipExtendedSide = (string)fieldInfo.GetValue(null); } else { ReadWiderShipConfigFile(); } } catch { ReadWiderShipConfigFile(); } Plugin.Log.LogInfo((object)("Detected WiderShip, ES - " + WiderShipExtendedSide)); } private void ReadWiderShipConfigFile() { string configPath = Path.Combine(Paths.ConfigPath, "mborsh.WiderShipMod.cfg"); WiderShipExtendedSide = ConfigFileReader.ReadStringValue(configPath, "Extended Side = ", "Both"); } private void Initialize2StoryShipMod() { Is2StoryShipModInstalled = Chainloader.PluginInfos.ContainsKey("MelanieMelicious.2StoryShip"); if (!Is2StoryShipModInstalled) { return; } try { Type type = Type.GetType("2StoryShip"); if (type != null) { FieldInfo field = type.GetField("EnableRightWindows", BindingFlags.Static | BindingFlags.Public); FieldInfo field2 = type.GetField("EnableLeftWindows", BindingFlags.Static | BindingFlags.Public); if (field != null && field2 != null) { EnableRightWindows = (bool)field.GetValue(null); EnableLeftWindows = (bool)field2.GetValue(null); } else { Read2StoryShipConfigFile(); } } else { Read2StoryShipConfigFile(); } } catch { Read2StoryShipConfigFile(); } Plugin.Log.LogInfo((object)$"Detected 2StoryShipMod, RW - {EnableRightWindows}, LW - {EnableLeftWindows}"); } private void Read2StoryShipConfigFile() { string configPath = Path.Combine(Paths.ConfigPath, "MelanieMelicious.2StoryShip.cfg"); Dictionary<string, string> dictionary = ConfigFileReader.ReadMultipleValues(configPath, "Enable Right Windows = ", "Enable Left Windows = "); EnableRightWindows = !dictionary.TryGetValue("Enable Right Windows = ", out var value) || bool.Parse(value); EnableLeftWindows = !dictionary.TryGetValue("Enable Left Windows = ", out var value2) || bool.Parse(value2); string text = ConfigFileReader.ReadStringFromSection(configPath, "Wider + 2-Story Exclusive", "Ship Layout = ", "Default"); if (!string.IsNullOrEmpty(text)) { TwoStoryShipLayout = text.Trim(); } } public void SetRandomSeed(int seed) { _rand = new Random(seed); } public Texture2D? GetCachedTexture(string filePath) { if (!Plugin.ModConfig.EnableTextureCaching.Value) { return null; } if (_textureCache.TryGetValue(filePath, out Texture2D value)) { Plugin.Log.LogDebug((object)("Retrieved cached texture: " + PathUtils.GetPrettyPath(filePath))); return value; } return null; } public void CacheTexture(string filePath, Texture2D texture) { if (Plugin.ModConfig.EnableTextureCaching.Value && !_textureCache.ContainsKey(filePath)) { _textureCache[filePath] = texture; } } public string? GetCachedVideo(string filePath) { if (!Plugin.ModConfig.EnableTextureCaching.Value) { return null; } if (_videoCache.TryGetValue(filePath, out string value)) { Plugin.Log.LogDebug((object)("Retrieved cached video: " + PathUtils.GetPrettyPath(filePath))); return value; } return null; } public void CacheVideo(string filePath) { if (Plugin.ModConfig.EnableTextureCaching.Value && !_videoCache.ContainsKey(filePath)) { if (!File.Exists(filePath)) { Plugin.Log.LogError((object)("Cannot cache video, file does not exist: " + PathUtils.GetPrettyPath(filePath))); return; } _videoCache[filePath] = filePath; Plugin.Log.LogDebug((object)("Cached video: " + PathUtils.GetPrettyPath(filePath))); } } public void ClearCache() { foreach (Texture2D value in _textureCache.Values) { if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } } _textureCache.Clear(); _videoCache.Clear(); Plugin.Log.LogInfo((object)"Cleared texture and video cache"); } public List<string> GetEnabledPackNames() { List<string> list = (from f in PosterFolders where Plugin.ModConfig.IsPackEnabled(f) select Path.GetFullPath(f).NormalizePath()).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList(); List<string> values = list.Select(Path.GetFileName).ToList(); Plugin.Log.LogDebug((object)("Enabled pack names: " + string.Join(", ", values))); return list; } public int GetFilePriority(string filePath) { return Path.GetExtension(filePath).ToLower() switch { ".png" => 1, ".jpg" => 2, ".jpeg" => 3, ".bmp" => 4, ".mp4" => 5, _ => int.MaxValue, } * 1000 + Rand.Next(0, 1000); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "CustomPosters"; public const string PLUGIN_NAME = "CustomPosters"; public const string PLUGIN_VERSION = "4.0.4"; } } namespace CustomPosters.Utils { internal static class ConfigFileReader { public static bool ReadBoolValue(string configPath, string key, bool defaultValue = true) { if (!File.Exists(configPath)) { return defaultValue; } try { string[] array = File.ReadAllLines(configPath); string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); if (text2.StartsWith(key, StringComparison.OrdinalIgnoreCase)) { string value = text2.Substring(key.Length).Trim(); if (bool.TryParse(value, out var result)) { return result; } } } } catch (Exception ex) { Plugin.Log.LogError((object)("Error reading config file " + configPath + ": " + ex.Message)); } return defaultValue; } public static string ReadStringValue(string configPath, string key, string defaultValue = "") { if (!File.Exists(configPath)) { return defaultValue; } try { string[] array = File.ReadAllLines(configPath); string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); if (text2.StartsWith(key, StringComparison.OrdinalIgnoreCase)) { return text2.Substring(key.Length).Trim(); } } } catch (Exception ex) { Plugin.Log.LogError((object)("Error reading config file " + configPath + ": " + ex.Message)); } return defaultValue; } public static bool ReadBoolFromSection(string configPath, string sectionHeader, string key, bool defaultValue = true) { if (!File.Exists(configPath)) { return defaultValue; } try { string[] array = File.ReadAllLines(configPath); bool flag = false; string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); if (text2.StartsWith('[') && text2.EndsWith(']')) { flag = text2.Equals("[" + sectionHeader + "]", StringComparison.OrdinalIgnoreCase); } else if (flag && text2.StartsWith(key, StringComparison.OrdinalIgnoreCase)) { string value = text2.Substring(key.Length).Trim(); if (bool.TryParse(value, out var result)) { return result; } } } } catch (Exception ex) { Plugin.Log.LogError((object)("Error reading config file " + configPath + ": " + ex.Message)); } return defaultValue; } public static string ReadStringFromSection(string configPath, string sectionHeader, string key, string defaultValue = "") { if (!File.Exists(configPath)) { return defaultValue; } try { bool flag = false; string[] array = File.ReadAllLines(configPath); foreach (string text in array) { string text2 = text.Trim(); if (string.IsNullOrWhiteSpace(text2)) { continue; } if (text2.StartsWith('[') && text2.EndsWith(']')) { string a = text2.Substring(1, text2.Length - 2).Trim(); flag = string.Equals(a, sectionHeader, StringComparison.OrdinalIgnoreCase); } else if (flag && text2.StartsWith(key, StringComparison.OrdinalIgnoreCase)) { int num = text2.IndexOf('='); if (num >= 0 && num + 1 < text2.Length) { return text2.Substring(num + 1).Trim(); } } } } catch (Exception ex) { try { Plugin.Log.LogDebug((object)("ReadStringFromSection error for '" + sectionHeader + ":" + key + "': " + ex.Message)); } catch { } } return defaultValue; } public static Dictionary<string, string> ReadMultipleValues(string configPath, params string[] keys) { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); if (!File.Exists(configPath)) { return dictionary; } try { string[] array = File.ReadAllLines(configPath); HashSet<string> hashSet = new HashSet<string>(keys, StringComparer.OrdinalIgnoreCase); string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); foreach (string item in hashSet) { if (text2.StartsWith(item, StringComparison.OrdinalIgnoreCase)) { string value = text2.Substring(item.Length).Trim(); dictionary[item] = value; if (dictionary.Count == keys.Length) { return dictionary; } break; } } } } catch (Exception ex) { Plugin.Log.LogError((object)("Error reading config file " + configPath + ": " + ex.Message)); } return dictionary; } } internal static class Extensions { public static bool IsImage(this string filePath) { if (string.IsNullOrEmpty(filePath)) { return false; } string value = Path.GetExtension(filePath).ToLowerInvariant(); return Constants.ValidImageExtensions.Contains(value); } public static bool IsVideo(this string filePath) { if (string.IsNullOrEmpty(filePath)) { return false; } string value = Path.GetExtension(filePath).ToLowerInvariant(); return Constants.ValidVideoExtensions.Contains(value); } public static bool IsValidPosterFile(this string filePath) { if (string.IsNullOrEmpty(filePath)) { return false; } string value = Path.GetExtension(filePath).ToLowerInvariant(); return Constants.AllValidExtensions.Contains(value); } public static string NormalizePath(this string path) { return path?.Replace('\\', '/') ?? string.Empty; } public static string GetPosterName(this string filePath) { return Path.GetFileNameWithoutExtension(filePath)?.ToLower() ?? string.Empty; } public static T? GetComponentSafe<T>(this GameObject? gameObject) where T : Component { if (!((Object)(object)gameObject != (Object)null)) { return default(T); } return gameObject.GetComponent<T>(); } public static void AddRangeDistinct<T>(this List<T> list, IEnumerable<T> items, IEqualityComparer<T>? comparer = null) { if (comparer == null) { comparer = EqualityComparer<T>.Default; } HashSet<T> hashSet = new HashSet<T>(list, comparer); foreach (T item in items) { if (hashSet.Add(item)) { list.Add(item); } } } } internal static class HashUtils { private const int HashSeed = 23; private const int HashMul = 31; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int DeterministicHash(string input) { int num = 23; for (int i = 0; i < input.Length; i++) { num = num * 31 + input[i]; } return num; } } internal static class PackSelector { public static string SelectPackByChance(List<string> enabledPacks) { if (enabledPacks == null || enabledPacks.Count == 0) { return null; } if (enabledPacks.Count == 1) { return enabledPacks[0]; } List<int> list = enabledPacks.Select((string p) => Plugin.ModConfig.GetPackChance(p)).ToList(); if (list.All((int c) => c == 0)) { return enabledPacks[Plugin.Service.Rand.Next(enabledPacks.Count)]; } return SelectByWeightedChance(enabledPacks, list); } public static T SelectContentByChance<T>(List<T> items, Func<T, int> getChance, Func<T, int> getPriority) { if (items == null || items.Count == 0) { return default(T); } if (items.Count == 1) { return items[0]; } List<int> list = items.Select(getChance).ToList(); if (list.All((int c) => c == 0)) { return items.OrderBy(getPriority).First(); } return SelectByWeightedChance(items, list); } private static T SelectByWeightedChance<T>(List<T> items, List<int> chances) { int num = chances.Sum(); if (num <= 0) { return items[0]; } double num2 = Plugin.Service.Rand.NextDouble() * (double)num; double num3 = 0.0; for (int i = 0; i < items.Count; i++) { num3 += (double)chances[i]; if (num2 <= num3) { return items[i]; } } return items[0]; } } internal static class PathUtils { public static string GetPrettyPath(string fullPath) { if (string.IsNullOrEmpty(fullPath)) { return string.Empty; } int num = fullPath.IndexOf("plugins", StringComparison.OrdinalIgnoreCase); if (num != -1) { return fullPath.Substring(num + "plugins".Length + 1); } return fullPath; } public static string GetPackName(string fullPackName) { if (string.IsNullOrEmpty(fullPackName)) { return string.Empty; } int num = fullPackName.IndexOf('-'); if (num > 0 && num < fullPackName.Length - 1) { return fullPackName.Substring(num + 1); } return fullPackName; } public static string GetDisplayPackName(string packPath) { if (string.IsNullOrEmpty(packPath)) { return string.Empty; } string fileName = Path.GetFileName(packPath); string directoryName = Path.GetDirectoryName(packPath); if (!string.IsNullOrEmpty(directoryName)) { string fileName2 = Path.GetFileName(directoryName); if (string.Equals(fileName, "CustomPosters", StringComparison.OrdinalIgnoreCase)) { return GetPackName(fileName2); } string.IsNullOrEmpty(fileName2); return fileName; } return fileName; } } internal static class SavePersistenceManager { private const string Es3Key = "CustomPosters_SelectedPack"; public static string? TryGetCurrentSaveId() { try { GameNetworkManager instance = GameNetworkManager.Instance; if ((Object)(object)instance != (Object)null) { Type type = ((object)instance).GetType(); FieldInfo field = type.GetField("currentSaveFileName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); PropertyInfo property = type.GetProperty("currentSaveFileName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); string text = (field?.GetValue(instance) as string) ?? (property?.GetValue(instance) as string); if (!string.IsNullOrEmpty(text)) { return text; } } } catch (Exception) { } return null; } public static string? TryLoadSelectedPack(string saveId) { try { if (string.IsNullOrEmpty(saveId)) { return null; } if (ES3.KeyExists("CustomPosters_SelectedPack", saveId)) { string text = ES3.Load<string>("CustomPosters_SelectedPack", saveId, ""); return string.IsNullOrEmpty(text) ? null : text; } } catch (Exception ex) { Plugin.Log.LogDebug((object)("Failed to load per save selection from ES3: " + ex.Message)); } return null; } public static void SaveSelectedPack(string saveId, string packPath) { try { if (!string.IsNullOrEmpty(saveId)) { ES3.Save<string>("CustomPosters_SelectedPack", packPath, saveId); } } catch (Exception ex) { Plugin.Log.LogDebug((object)("Failed to save per save selection to ES3: " + ex.Message)); } } } } namespace CustomPosters.Patches { [HarmonyPatch(typeof(GameNetworkManager))] internal class GameNetworkManagerPatch { [HarmonyPostfix] [HarmonyPatch("Start")] private static void OnStartPatch() { PosterManager.ResetSession(); if (Plugin.ModConfig.EnableNetworking.Value) { if ((Object)(object)NetworkManager.Singleton != (Object)null) { Plugin.Log.LogInfo((object)"Networking is enabled."); NetworkManager.Singleton.OnClientConnectedCallback += PosterSyncManager.OnClientConnected; } else { Plugin.Log.LogWarning((object)"NetworkManager.Singleton is null, cannot register callbacks."); } } else { Plugin.Log.LogInfo((object)"Networking is disabled."); } } [HarmonyPostfix] [HarmonyPatch("StartHost")] private static void OnStartHostPatch() { PosterManager.IsNewLobby = true; } [HarmonyPostfix] [HarmonyPatch("JoinLobby")] private static void OnJoinLobbyPatch() { PosterManager.ResetSession(); PosterManager.IsNewLobby = true; } } [HarmonyPatch(typeof(StartOfRound))] internal class StartOfRoundPatch { [HarmonyPostfix] [HarmonyPatch("Awake")] private static void OnAwakePatch() { PosterManager.OnSceneAwake(); } [HarmonyPostfix] [HarmonyPatch("Start")] private static void OnRoundStartPatch(StartOfRound __instance) { PosterManager.OnRoundStart(__instance); } } } namespace CustomPosters.Networking { internal static class PosterSyncManager { private static readonly LNetworkMessage<string> SyncPackMessage = LNetworkMessage<string>.Connect("CustomPosters_SyncPack", (Action<string, ulong>)null, (Action<string>)PosterManager.SetPackForClients, (Action<string, ulong>)null); private static readonly LNetworkMessage<int> SyncSeedMessage = LNetworkMessage<int>.Connect("CustomPosters_SyncSeed", (Action<int, ulong>)null, (Action<int>)PosterManager.SetSeedForClients, (Action<int, ulong>)null); private static readonly LNetworkMessage<string> RequestVideoTimeMessage = LNetworkMessage<string>.Connect("CustomPosters_RequestVideoTime", (Action<string, ulong>)OnVideoTimeRequested, (Action<string>)null, (Action<string, ulong>)null); private static readonly LNetworkMessage<VideoSyncData> SyncVideoTimeMessage = LNetworkMessage<VideoSyncData>.Connect("CustomPosters_SyncVideoTime", (Action<VideoSyncData, ulong>)null, (Action<VideoSyncData>)OnVideoTimeReceived, (Action<VideoSyncData, ulong>)null); public static void SendPacket(string packName) { if (!Plugin.ModConfig.EnableNetworking.Value) { Plugin.Log.LogDebug((object)"Networking disabled."); } else if ((Object)(object)NetworkManager.Singleton == (Object)null) { Plugin.Log.LogWarning((object)"NetworkManager.Singleton is null, cannot send pack sync"); } else if (NetworkManager.Singleton.IsHost) { Plugin.Log.LogDebug((object)("Sending selected pack to all clients: " + PathUtils.GetPrettyPath(packName))); SyncPackMessage.SendClients(packName); } } public static void SendSeed(int seed) { if (Plugin.ModConfig.EnableNetworking.Value && !((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.IsHost) { Plugin.Log.LogDebug((object)$"Sending seed to all clients - {seed}"); SyncSeedMessage.SendClients(seed); } } public static void OnClientConnected(ulong clientId) { if (!Plugin.ModConfig.EnableNetworking.Value) { return; } if ((Object)(object)NetworkManager.Singleton == (Object)null) { Plugin.Log.LogWarning((object)"NetworkManager.Singleton is null in OnClientConnected"); } else if (NetworkManager.Singleton.IsHost && clientId != NetworkManager.Singleton.LocalClientId) { if (!string.IsNullOrEmpty(PosterManager.SelectedPack)) { Plugin.Log.LogDebug((object)("New client joined, sending pack: " + PathUtils.GetPrettyPath(PosterManager.SelectedPack))); SyncPackMessage.SendClient(PosterManager.SelectedPack, clientId); } if (PosterManager.CurrentHostSeed != 0) { Plugin.Log.LogDebug((object)$"New client joined, sending seed - {PosterManager.CurrentHostSeed}"); SyncSeedMessage.SendClient(PosterManager.CurrentHostSeed, clientId); } } } public static void RequestVideoTimeFromServer(string posterName) { if (!Plugin.ModConfig.EnableNetworking.Value) { Plugin.Log.LogDebug((object)"Networking disabled, skipping video time sync request"); } else if (!((Object)(object)NetworkManager.Singleton == (Object)null) && !NetworkManager.Singleton.IsHost) { Plugin.Log.LogDebug((object)("Requesting video time for poster: " + posterName)); RequestVideoTimeMessage.SendServer(posterName); } } private static void OnVideoTimeRequested(string posterName, ulong clientId) { double? videoTimeForPoster = PosterManager.GetVideoTimeForPoster(posterName); if (videoTimeForPoster.HasValue) { Plugin.Log.LogDebug((object)$"Host received request for '{posterName}'. Sending time: {videoTimeForPoster.Value} to client {clientId}"); VideoSyncData videoSyncData = default(VideoSyncData); videoSyncData.PosterName = posterName; videoSyncData.VideoTime = videoTimeForPoster.Value; VideoSyncData videoSyncData2 = videoSyncData; SyncVideoTimeMessage.SendClient(videoSyncData2, clientId); } } private static void OnVideoTimeReceived(VideoSyncData syncData) { Plugin.Log.LogDebug((object)$"Client received sync time for '{syncData.PosterName}': {syncData.VideoTime}"); PosterManager.SetVideoTimeForPoster(syncData.PosterName, syncData.VideoTime); } } public struct VideoSyncData { public string PosterName; public double VideoTime; } } namespace CustomPosters.Data { public struct PosterData { public Vector3 Position; public Vector3 Rotation; public Vector3 Scale; public string Name; } internal static class PosterHelper { public static PosterData Poster5Vanilla(Vector3 pos, Vector3 rot, Vector3 scale) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) PosterData result = default(PosterData); result.Position = pos; result.Rotation = rot; result.Scale = scale; result.Name = "Poster5"; return result; } public static PosterData Poster5Quad(Vector3 pos, Vector3 rot, Vector3 scale) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) PosterData result = default(PosterData); result.Position = pos; result.Rotation = rot; result.Scale = scale; result.Name = "Poster5"; return result; } public static PosterData TipsVanilla(Vector3 pos, Vector3 rot, Vector3 scale) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) PosterData result = default(PosterData); result.Position = pos; result.Rotation = rot; result.Scale = scale; result.Name = "CustomTips"; return result; } public static PosterData TipsQuad(Vector3 pos, Vector3 rot, Vector3 scale) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) PosterData result = default(PosterData); result.Position = pos; result.Rotation = rot; result.Scale = scale; result.Name = "CustomTips"; return result; } } internal static class PosterLayoutProvider { public static PosterData[] GetLayout() { bool isShipWindowsInstalled = Plugin.Service.IsShipWindowsInstalled; bool isRightWindowEnabled = Plugin.Service.IsRightWindowEnabled; bool isWiderShipModInstalled = Plugin.Service.IsWiderShipModInstalled; string widerShipExtendedSide = Plugin.Service.WiderShipExtendedSide; bool is2StoryShipModInstalled = Plugin.Service.Is2StoryShipModInstalled; bool enableRightWindows = Plugin.Service.EnableRightWindows; bool enableLeftWindows = Plugin.Service.EnableLeftWindows; bool legacy = is2StoryShipModInstalled && string.Equals(Plugin.Service.TwoStoryShipLayout, "Legacy", StringComparison.OrdinalIgnoreCase); if (Plugin.Service.IsBiggerShipInstalled) { Plugin.Log.LogInfo((object)"Choosing layout: BiggerShip"); return BiggerShip.Get(); } if (is2StoryShipModInstalled) { return GetTwoStoryLayout(legacy, isWiderShipModInstalled, isShipWindowsInstalled, isRightWindowEnabled, enableRightWindows, enableLeftWindows); } if (isShipWindowsInstalled && isRightWindowEnabled && isWiderShipModInstalled && widerShipExtendedSide == "Left") { Plugin.Log.LogInfo((object)"Choosing layout: ShipWindows + WiderShip (Left)"); return ShipWindows_WiderShip_Left.Get(); } if (isWiderShipModInstalled) { Plugin.Log.LogInfo((object)("Choosing layout: WiderShip - " + widerShipExtendedSide)); switch (widerShipExtendedSide) { case "Both": return WiderShip_Both.Get(); case "Right": return WiderShip_Right.Get(); case "Left": return WiderShip_Left.Get(); } } if (isShipWindowsInstalled && isRightWindowEnabled) { Plugin.Log.LogInfo((object)"Choosing layout: ShipWindows"); return ShipWindows.Get(); } Plugin.Log.LogInfo((object)"Choosing layout: Vanilla"); return Vanilla.Get(); } private static PosterData[] GetTwoStoryLayout(bool legacy, bool widerShip, bool shipWindows, bool rightWindow, bool enableRightWindows, bool enableLeftWindows) { if (legacy && widerShip) { Plugin.Log.LogInfo((object)"Choosing layout: 2 Story Ship (Legacy) + WiderShip"); return CustomPosters.Data.PosterLayouts.Legacy.TwoStoryShip_WiderShip.Get(); } if (widerShip) { Plugin.Log.LogInfo((object)("Choosing layout: 2 Story Ship + WiderShip" + GetWiderShipDetail(shipWindows, rightWindow, enableLeftWindows))); return CustomPosters.Data.PosterLayouts.TwoStoryShip_WiderShip.Get(); } Plugin.Log.LogInfo((object)("Choosing layout: 2 Story Ship" + GetTwoStorySuffix(shipWindows, enableRightWindows, enableLeftWindows))); return TwoStoryShip.Get(); } private static string GetWiderShipDetail(bool shipWindows, bool rightWindow, bool enableLeftWindows) { if (shipWindows && rightWindow) { return " + ShipWindows"; } if (shipWindows) { return " + ShipWindows (No right window)"; } if (!enableLeftWindows) { return " (No left window)"; } return ""; } private static string GetTwoStorySuffix(bool shipWindows, bool enableRightWindows, bool enableLeftWindows) { if (shipWindows) { return " + ShipWindows"; } if (!enableRightWindows && !enableLeftWindows) { return " (No both windows)"; } if (enableRightWindows && enableLeftWindows) { return " (All windows)"; } if (!enableRightWindows) { return " (No right window)"; } return " (No left window)"; } } } namespace CustomPosters.Data.PosterLayouts { internal static class BiggerShip { public static PosterData[] Get() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) bool usePoster5VanillaModel = Plugin.ModConfig.UsePoster5VanillaModel; bool useTipsVanillaModel = Plugin.ModConfig.UseTipsVanillaModel; return new PosterData[6] { new PosterData { Position = new Vector3(4.1886f, 2.8918f, -17.8606f), Rotation = new Vector3(0f, 169.6929f, 0f), Scale = new Vector3(0.6391f, 0.4882f, 2f), Name = "Poster1" }, new PosterData { Position = new Vector3(6.4202f, 2.4776f, -10.9064f), Rotation = new Vector3(0f, 0f, 0f), Scale = new Vector3(0.7296f, 0.4896f, 1f), Name = "Poster2" }, new PosterData { Position = new Vector3(9.9186f, 2.8591f, -17.5587f), Rotation = new Vector3(0f, 180f, 356.3345f), Scale = new Vector3(0.7487f, 1.0539f, 1f), Name = "Poster3" }, new PosterData { Position = new Vector3(5.2187f, 2.5963f, -10.782f), Rotation = new Vector3(0f, 10.7715f, 2.68f), Scale = new Vector3(0.7289f, 0.9989f, 1f), Name = "Poster4" }, usePoster5VanillaModel ? PosterHelper.Poster5Vanilla(new Vector3(5.5286f, 2.5882f, -17.6077f), new Vector3(1.3609f, 329.1297f, 182.4321f), new Vector3(0.465f, 0.71f, 1f)) : PosterHelper.Poster5Quad(new Vector3(5.5286f, 2.5882f, -17.6184f), new Vector3(0f, 169.7645f, 359.8f), new Vector3(0.5516f, 0.769f, 1f)), useTipsVanillaModel ? PosterHelper.TipsVanilla(new Vector3(8.147f, 2.399426f, -20.6869f), new Vector3(-270.38f, -321.026f, 219.239f), new Vector3(46.75954f, 100f, 70.89838f)) : PosterHelper.TipsQuad(new Vector3(3.0647f, 2.8174f, -10.4842f), new Vector3(0f, 0f, 0f), new Vector3(0.8596f, 1.2194f, 1f)) }; } } internal static class ShipWindows { public static PosterData[] Get() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02dd: Unknown result type (might be