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 DeepNorthShaderFix v1.0.3
plugins/DeepNorthShaderFix.dll
Decompiled a day 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.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExpandWorldSize; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using ServerSync; using SoftReferenceableAssets; using TMPro; using UnityEngine; [assembly: AssemblyFileVersion("1.0.3")] [assembly: Guid("6E0B7F3C-4D2A-4B8E-9C61-2F5A7D9E1B43")] [assembly: ComVisible(false)] [assembly: AssemblyTrademark("")] [assembly: AssemblyCopyright("Copyright © 2026 Radamanto")] [assembly: AssemblyProduct("DeepNorthShaderFix")] [assembly: AssemblyCompany("Radamanto")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyDescription("")] [assembly: AssemblyTitle("DeepNorthShaderFix")] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.3.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [<db20378a-7ba8-4987-8fcf-7e82b019061a>Embedded] internal sealed class <db20378a-7ba8-4987-8fcf-7e82b019061a>EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] [<db20378a-7ba8-4987-8fcf-7e82b019061a>Embedded] [CompilerGenerated] internal sealed class <9adb7d9b-56fb-4c20-9988-0b5764d74a79>NullableAttribute : Attribute { public readonly byte[] NullableFlags; public <9adb7d9b-56fb-4c20-9988-0b5764d74a79>NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public <9adb7d9b-56fb-4c20-9988-0b5764d74a79>NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] [<db20378a-7ba8-4987-8fcf-7e82b019061a>Embedded] [CompilerGenerated] internal sealed class <cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContextAttribute : Attribute { public readonly byte Flag; public <cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace radamanto.DeepNorthShaderFix { [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] [BepInPlugin("radamanto.DeepNorthShaderFix", "DeepNorthShaderFix", "1.0.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] public class DeepNorthShaderFixPlugin : BaseUnityPlugin { [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(0)] private sealed class ConfigurationManagerAttributes { public bool? Browsable = false; } public const string ModGUID = "radamanto.DeepNorthShaderFix"; public const string ModName = "DeepNorthShaderFix"; public const string ModVersion = "1.0.3"; private const string ExpandWorldSizeGUID = "expand_world_size"; private const float MinRadius = 1000f; private const float MaxRadius = 100000f; private readonly Harmony _harmony = new Harmony("radamanto.DeepNorthShaderFix"); internal static ManualLogSource Log; internal static ConfigEntry<float> WorldRadius; private const float ConfigReloadQuietSeconds = 0.75f; [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(2)] internal static ConfigSync ConfigSync; [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(2)] private static ConfigEntry<bool> _serverConfigLocked; [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(2)] private FileSystemWatcher _configWatcher; private DateTime _lastCfgWriteUtc = DateTime.MinValue; private DateTime _ignoreEventsUntilUtc = DateTime.MinValue; private bool _reloadScheduled; private void Awake() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; bool syncReady = BeginConfigSetup(); WorldRadius = Cfg("1 - General", "World radius", 10000f, new ConfigDescription("Must be the same value as 'World radius' in expand_world_size.cfg. If 'Stretch world' is not 1, use World radius divided by Stretch world. 10000 is vanilla and keeps the original terrain shader.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1000f, 100000f), Array.Empty<object>())); EndConfigSetup(syncReady); WorldRadius.SettingChanged += [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(0)] (object _, EventArgs _) => { PoleShaders.Request(WorldRadius.Value); }; _harmony.PatchAll(); PoleShaders.Request(WorldRadius.Value); } private void Update() { PoleShaders.Tick((MonoBehaviour)(object)this); } private void OnDestroy() { DisposeConfigWatcher(); _harmony.UnpatchSelf(); } private bool BeginConfigSetup() { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { ConfigSync = new ConfigSync("radamanto.DeepNorthShaderFix") { DisplayName = "DeepNorthShaderFix", CurrentVersion = "1.0.3", MinimumRequiredVersion = "1.0.3", ModRequired = true }; _serverConfigLocked = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Lock Configuration", true, new ConfigDescription("If true, the server locks synchronized configs.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Browsable = false } })); ConfigSync.AddLockingConfigEntry<bool>(_serverConfigLocked); return true; } catch (Exception ex) { Log.LogError((object)("[ConfigSync] " + ex)); return false; } } private void EndConfigSetup(bool syncReady) { if (syncReady) { ((BaseUnityPlugin)this).Config.Save(); } ((BaseUnityPlugin)this).Config.SaveOnConfigSet = true; if (syncReady) { SetupConfigWatcher(); } } private ConfigEntry<T> Cfg<[<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(2)] T>(string section, string key, T defaultValue, ConfigDescription description, bool synced = true) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown ConfigDescription val = new ConfigDescription(description.Description + (synced ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags); ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(section, key, defaultValue, val); SyncedConfigEntry<T> syncedConfigEntry = ConfigSync?.AddConfigEntry<T>(val2); if (syncedConfigEntry != null) { syncedConfigEntry.SynchronizedConfig = synced; } return val2; } private void SetupConfigWatcher() { DisposeConfigWatcher(); try { _configWatcher = new FileSystemWatcher(Paths.ConfigPath, Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath)) { IncludeSubdirectories = false, NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime), SynchronizingObject = ThreadingHelper.SynchronizingObject }; _configWatcher.Changed += OnConfigFileChanged; _configWatcher.Created += OnConfigFileChanged; _configWatcher.Renamed += OnConfigFileChanged; _configWatcher.EnableRaisingEvents = true; } catch (Exception ex) { Log.LogError((object)("[ConfigWatcher] " + ex)); _configWatcher = null; } } private void DisposeConfigWatcher() { if (_configWatcher != null) { _configWatcher.EnableRaisingEvents = false; _configWatcher.Dispose(); _configWatcher = null; } _reloadScheduled = false; } private void OnConfigFileChanged(object sender, FileSystemEventArgs e) { if (!(DateTime.UtcNow < _ignoreEventsUntilUtc)) { _lastCfgWriteUtc = DateTime.UtcNow; if (!_reloadScheduled) { _reloadScheduled = true; ((MonoBehaviour)this).StartCoroutine(ReloadConfigAfterQuietPeriod()); } } } private IEnumerator ReloadConfigAfterQuietPeriod() { while ((DateTime.UtcNow - _lastCfgWriteUtc).TotalSeconds < 0.75) { yield return null; } _reloadScheduled = false; FileSystemWatcher watcher = _configWatcher; if (watcher == null || ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer())) { yield break; } watcher.EnableRaisingEvents = false; try { ((BaseUnityPlugin)this).Config.Reload(); _ignoreEventsUntilUtc = DateTime.UtcNow.AddSeconds(1.0); } catch (Exception ex) { Log.LogError((object)("[ConfigReload] " + ex)); } finally { if (_configWatcher == watcher) { watcher.EnableRaisingEvents = true; } } } } [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] internal static class PoleShaders { [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(2)] [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] private sealed class ShaderTarget { [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(1)] public readonly string Name; [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(1)] public readonly string AssetPath; public Shader Vanilla; public Shader Patched; public AssetBundle Bundle; public string File; [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] public ShaderTarget(string name, string assetPath) { Name = name; AssetPath = assetPath; } } [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] private readonly struct PendingShader { public readonly AssetBundle Bundle; public readonly string File; public readonly Shader Shader; public PendingShader(AssetBundle bundle, Shader shader, string file) { File = file; Bundle = bundle; Shader = shader; } } [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] private sealed class BuildResult { public readonly Dictionary<ShaderTarget, string> Files = new Dictionary<ShaderTarget, string>(); public readonly List<string> Errors = new List<string>(); } private const string SoftRefFolder = "SoftRef"; private const string BundlesFolder = "Bundles"; private const string ManifestFile = "manifest_extended"; private const string BundlePrefix = "bundle:"; private const string PathPrefix = "path in bundle:"; private const string TemplateAssetPath = "Assets/Shaders/Heightmap.shader"; private const string CacheExtension = ".bundle"; private const string TempExtension = ".tmp"; private const int CacheFormat = 2; private const float RescanInterval = 0.5f; private static readonly int DepthProperty = Shader.PropertyToID("_depth"); private static readonly ShaderTarget[] Targets = new ShaderTarget[5] { new ShaderTarget("Custom/Heightmap", "Assets/Shaders/Heightmap.shader"), new ShaderTarget("Custom/Vegetation", "Assets/Shaders/Vegetation.shader"), new ShaderTarget("Custom/Piece", "Assets/Shaders/Piece.shader"), new ShaderTarget("Custom/StaticRock", "Assets/Shaders/StaticRock.shader"), new ShaderTarget("Custom/Water", "Assets/Shaders/Water.shader") }; private static readonly Dictionary<string, ShaderTarget> TargetsByName = CreateLookup(); private static readonly Dictionary<Shader, ShaderTarget> KnownShaders = new Dictionary<Shader, ShaderTarget>(); private static readonly HashSet<Shader> IgnoredShaders = new HashSet<Shader>(); private static readonly HashSet<Shader> OwnShaders = new HashSet<Shader>(); private static readonly List<string> RetiredFiles = new List<string>(); private static float _requestedRadius = 10000f; private static float _appliedRadius = 10000f; private static bool _busy; private static bool _rescanRequested; private static float _nextRescan; public static void Request(float radius) { _requestedRadius = radius; } public static void RequestRescan() { _rescanRequested = true; } public static void Tick(MonoBehaviour host) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 if (_busy || (int)SystemInfo.graphicsDeviceType != 2) { return; } if (_requestedRadius != _appliedRadius) { _busy = true; host.StartCoroutine(Apply(_requestedRadius)); } else if (_rescanRequested && Time.unscaledTime >= _nextRescan) { _rescanRequested = false; _nextRescan = Time.unscaledTime + 0.5f; if (HasPatched()) { SwapMaterials(); } } } [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(2)] public static void Ensure(Material material) { if ((Object)(object)material == (Object)null) { return; } Shader shader = material.shader; ShaderTarget shaderTarget = Classify(shader); if (shaderTarget != null) { Shader val = Desired(shaderTarget); if ((Object)(object)val != (Object)null && (Object)(object)shader != (Object)(object)val) { Assign(material, val); } } } private static IEnumerator Apply(float radius) { Dictionary<ShaderTarget, PendingShader> loaded = new Dictionary<ShaderTarget, PendingShader>(); if (radius != 10000f) { string softRef = Path.Combine(Application.streamingAssetsPath, "SoftRef"); string cacheFolder = Path.Combine(Paths.CachePath, "DeepNorthShaderFix"); string[] inUse = FilesInUse(); Task<BuildResult> build = Task.Run([<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(0)] () => Build(radius, softRef, cacheFolder, inUse)); while (!build.IsCompleted) { yield return null; } BuildResult result = build.Result; foreach (string error in result.Errors) { DeepNorthShaderFixPlugin.Log.LogError((object)(error + " (keeping the vanilla shader)")); } foreach (KeyValuePair<ShaderTarget, string> file in result.Files) { AssetBundleCreateRequest bundleRequest = AssetBundle.LoadFromFileAsync(file.Value); yield return bundleRequest; AssetBundle bundle = bundleRequest.assetBundle; if ((Object)(object)bundle == (Object)null) { DeepNorthShaderFixPlugin.Log.LogError((object)(file.Key.Name + ": Unity refused to load the patched shader bundle (keeping the vanilla shader)")); continue; } AssetBundleRequest assetRequest = bundle.LoadAllAssetsAsync<Shader>(); yield return assetRequest; Shader shader = null; Object[] allAssets = assetRequest.allAssets; foreach (Object asset in allAssets) { Shader candidate = (Shader)(object)((asset is Shader) ? asset : null); if (candidate != null && ((Object)candidate).name == file.Key.Name) { shader = candidate; } } if ((Object)(object)shader == (Object)null || !shader.isSupported) { DeepNorthShaderFixPlugin.Log.LogError((object)(file.Key.Name + ": patched shader is missing or not supported by this GPU (keeping the vanilla shader)")); bundle.Unload(true); } else { loaded[file.Key] = new PendingShader(bundle, shader, file.Value); } } } Swap(loaded); _appliedRadius = radius; _busy = false; } private static void Swap(Dictionary<ShaderTarget, PendingShader> loaded) { List<PendingShader> list = new List<PendingShader>(); ShaderTarget[] targets = Targets; foreach (ShaderTarget shaderTarget in targets) { if ((Object)(object)shaderTarget.Bundle != (Object)null && (Object)(object)shaderTarget.Patched != (Object)null && shaderTarget.File != null) { list.Add(new PendingShader(shaderTarget.Bundle, shaderTarget.Patched, shaderTarget.File)); } shaderTarget.Patched = null; shaderTarget.Bundle = null; shaderTarget.File = null; if (loaded.TryGetValue(shaderTarget, out var value)) { shaderTarget.Patched = value.Shader; shaderTarget.Bundle = value.Bundle; shaderTarget.File = value.File; KnownShaders[value.Shader] = shaderTarget; OwnShaders.Add(value.Shader); } } HashSet<Shader> hashSet = SwapMaterials(); foreach (PendingShader item in list) { if (hashSet.Contains(item.Shader)) { DeepNorthShaderFixPlugin.Log.LogError((object)(((Object)item.Shader).name + ": the vanilla shader is not available, keeping the patched one loaded")); RetiredFiles.Add(item.File); } else { KnownShaders.Remove(item.Shader); OwnShaders.Remove(item.Shader); item.Bundle.Unload(true); } } } private static HashSet<Shader> SwapMaterials() { ShaderTarget[] targets = Targets; foreach (ShaderTarget shaderTarget in targets) { if ((Object)(object)shaderTarget.Patched == (Object)null && (Object)(object)shaderTarget.Vanilla == (Object)null) { shaderTarget.Vanilla = FindVanilla(shaderTarget); } } HashSet<Shader> hashSet = new HashSet<Shader>(); Material[] array = Resources.FindObjectsOfTypeAll<Material>(); foreach (Material val in array) { Shader shader = val.shader; ShaderTarget shaderTarget2 = Classify(shader); if (shaderTarget2 != null) { Shader val2 = Desired(shaderTarget2); if ((Object)(object)val2 == (Object)null) { hashSet.Add(shader); } else if ((Object)(object)shader != (Object)(object)val2) { Assign(val, val2); } } } return hashSet; } [return: <9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(2)] private static Shader Desired(ShaderTarget target) { if ((Object)(object)target.Patched != (Object)null) { return target.Patched; } if ((Object)(object)target.Vanilla != (Object)null) { return target.Vanilla; } return null; } [return: <9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(2)] private static Shader FindVanilla(ShaderTarget target) { Shader[] array = Resources.FindObjectsOfTypeAll<Shader>(); foreach (Shader val in array) { if (!OwnShaders.Contains(val) && !(((Object)val).name != target.Name)) { KnownShaders[val] = target; return val; } } return null; } [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(2)] private static ShaderTarget Classify(Shader shader) { if ((Object)(object)shader == (Object)null) { return null; } if (KnownShaders.TryGetValue(shader, out var value)) { return value; } if (IgnoredShaders.Contains(shader)) { return null; } if (!TargetsByName.TryGetValue(((Object)shader).name, out var value2)) { IgnoredShaders.Add(shader); return null; } value2.Vanilla = shader; KnownShaders[shader] = value2; return value2; } private static void Assign(Material material, Shader shader) { int renderQueue = material.renderQueue; float[] floatArray = material.GetFloatArray(DepthProperty); material.shader = shader; material.renderQueue = renderQueue; if (floatArray != null && floatArray.Length != 0) { material.SetFloatArray(DepthProperty, floatArray); } } private static string[] FilesInUse() { List<string> list = new List<string>(RetiredFiles); ShaderTarget[] targets = Targets; foreach (ShaderTarget shaderTarget in targets) { if (shaderTarget.File != null) { list.Add(shaderTarget.File); } } return list.ToArray(); } private static bool HasPatched() { ShaderTarget[] targets = Targets; foreach (ShaderTarget shaderTarget in targets) { if ((Object)(object)shaderTarget.Patched != (Object)null) { return true; } } return false; } private static BuildResult Build(float radius, string softRef, string cacheFolder, string[] inUse) { BuildResult buildResult = new BuildResult(); try { string path = Path.Combine(softRef, "Bundles"); Dictionary<string, string> bundleNames = ReadBundleNames(Path.Combine(softRef, "manifest_extended")); string text = Path.Combine(path, GetBundle(bundleNames, "Assets/Shaders/Heightmap.shader")); Directory.CreateDirectory(cacheFolder); ShaderTemplate shaderTemplate = null; HashSet<string> hashSet = new HashSet<string>(inUse, StringComparer.OrdinalIgnoreCase); ShaderTarget[] targets = Targets; foreach (ShaderTarget shaderTarget in targets) { try { string text2 = Path.Combine(path, GetBundle(bundleNames, shaderTarget.AssetPath)); string text3 = Path.Combine(cacheFolder, CacheName(shaderTarget, text2, text, radius)); hashSet.Add(text3); if (File.Exists(text3)) { buildResult.Files[shaderTarget] = text3; continue; } if (shaderTemplate == null) { shaderTemplate = ShaderTemplate.Read(text); } ShaderSource source = ShaderSource.Read(text2, shaderTarget.AssetPath, shaderTarget.Name); byte[] bytes = ShaderPatcher.CreatePatchedBundle(shaderTemplate, source, radius); WriteCache(text3, bytes); buildResult.Files[shaderTarget] = text3; } catch (Exception ex) { buildResult.Errors.Add(shaderTarget.Name + ": " + ex.Message); } } RemoveStaleCache(cacheFolder, hashSet); } catch (Exception ex2) { buildResult.Errors.Add("Could not prepare the patched shaders: " + ex2.Message); } return buildResult; } private static string CacheName(ShaderTarget target, string sourcePath, string templatePath, float radius) { FileInfo fileInfo = new FileInfo(sourcePath); FileInfo fileInfo2 = new FileInfo(templatePath); string s = string.Join("|", target.Name, fileInfo.Length.ToString(), fileInfo.LastWriteTimeUtc.Ticks.ToString(), fileInfo2.Length.ToString(), fileInfo2.LastWriteTimeUtc.Ticks.ToString(), radius.ToString("R"), "1.0.3", 2.ToString()); using MD5 mD = MD5.Create(); byte[] array = mD.ComputeHash(Encoding.UTF8.GetBytes(s)); StringBuilder stringBuilder = new StringBuilder(target.Name.Substring(target.Name.IndexOf('/') + 1)); stringBuilder.Append('_'); for (int i = 0; i < 8; i++) { stringBuilder.Append(array[i].ToString("x2")); } return stringBuilder.Append(".bundle").ToString(); } private static void WriteCache(string path, byte[] bytes) { string text = path + ".tmp"; File.WriteAllBytes(text, bytes); if (File.Exists(path)) { File.Delete(path); } File.Move(text, path); } private static void RemoveStaleCache(string folder, HashSet<string> keep) { string[] files = Directory.GetFiles(folder); foreach (string text in files) { if (!keep.Contains(text)) { try { File.Delete(text); } catch (IOException) { } catch (UnauthorizedAccessException) { } } } } private static Dictionary<string, string> ReadBundleNames(string manifestPath) { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); string[] array = File.ReadAllLines(manifestPath); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (!text.StartsWith("path in bundle:", StringComparison.Ordinal)) { continue; } string key = text.Substring("path in bundle:".Length).Trim(); int num = i - 1; while (num >= 0 && num >= i - 3) { string text2 = array[num].Trim(); if (text2.StartsWith("bundle:", StringComparison.Ordinal)) { dictionary[key] = text2.Substring("bundle:".Length).Trim(); break; } num--; } } return dictionary; } private static string GetBundle(Dictionary<string, string> bundleNames, string assetPath) { if (bundleNames.TryGetValue(assetPath, out var value)) { return value; } throw new InvalidDataException(assetPath + " not found in manifest_extended"); } private static Dictionary<string, ShaderTarget> CreateLookup() { Dictionary<string, ShaderTarget> dictionary = new Dictionary<string, ShaderTarget>(); ShaderTarget[] targets = Targets; foreach (ShaderTarget shaderTarget in targets) { dictionary[shaderTarget.Name] = shaderTarget; } return dictionary; } } [HarmonyPatch(typeof(Heightmap), "Initialize")] internal static class HeightmapInitializePatch { [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] private static void Prefix(Heightmap __instance) { PoleShaders.Ensure(__instance.m_material); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class ZNetSceneAwakePatch { private static void Postfix() { PoleShaders.RequestRescan(); } } [HarmonyPatch(typeof(AssetLoader), "InvokeCallbacks")] internal static class AssetLoaderInvokeCallbacksPatch { private static void Postfix(LoadResult result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 if ((int)result == 0) { PoleShaders.RequestRescan(); } } } [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] [HarmonyPatch(typeof(WorldGenerator), "DeepNorthWaveFade")] [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] internal static class DeepNorthWaveFadePatch { private const double VanillaYOffset = 4000.0; private const double VanillaMinDistance = 12000.0; private const int ExpectedReplacements = 2; private static readonly MethodInfo YOffsetSingle = SymbolExtensions.GetMethodInfo((Expression<Action>)(() => YOffset())); private static readonly MethodInfo YOffsetDouble = SymbolExtensions.GetMethodInfo((Expression<Action>)(() => YOffsetWide())); private static readonly MethodInfo MinDistanceSingle = SymbolExtensions.GetMethodInfo((Expression<Action>)(() => MinDistanceNarrow())); private static readonly MethodInfo MinDistanceDouble = SymbolExtensions.GetMethodInfo((Expression<Action>)(() => MinDistance())); public static float YOffset() { return DeepNorthShaderFixPlugin.WorldRadius.Value * 2f / 5f; } public static double YOffsetWide() { return (double)DeepNorthShaderFixPlugin.WorldRadius.Value * 2.0 / 5.0; } public static float MinDistanceNarrow() { return DeepNorthShaderFixPlugin.WorldRadius.Value * 6f / 5f; } public static double MinDistance() { return (double)DeepNorthShaderFixPlugin.WorldRadius.Value * 6.0 / 5.0; } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = new List<CodeInstruction>(instructions); int num = 0; foreach (CodeInstruction item in list) { MethodInfo replacement = GetReplacement(item); if (!(replacement == null)) { item.opcode = OpCodes.Call; item.operand = replacement; num++; } } if (num != 2) { DeepNorthShaderFixPlugin.Log.LogWarning((object)$"DeepNorthWaveFade: expected {2} constants, replaced {num}"); } return list; } [return: <9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(2)] private static MethodInfo GetReplacement(CodeInstruction instruction) { if (instruction.opcode == OpCodes.Ldc_R4 && instruction.operand is float num) { if (num == 4000f) { return YOffsetSingle; } if (num == 12000f) { return MinDistanceSingle; } } else if (instruction.opcode == OpCodes.Ldc_R8 && instruction.operand is double num2) { if (num2 == 4000.0) { return YOffsetDouble; } if (num2 == 12000.0) { return MinDistanceDouble; } } return null; } } [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] internal static class MapDiagnostics { private const float ProbeDistance = 15000f; private static readonly MethodBase BiomeHeightMethod = AccessTools.Method(typeof(WorldGenerator), "GetBiomeHeight", new Type[6] { typeof(Biome), typeof(float), typeof(float), typeof(Color).MakeByRefType(), typeof(bool), typeof(bool) }, (Type[])null); private static readonly MethodBase BaseHeightMethod = AccessTools.Method(typeof(WorldGenerator), "GetBaseHeight", (Type[])null, (Type[])null); public static void Log(string message) { DeepNorthShaderFixPlugin.Log.LogWarning((object)$"[MapDiag {Time.realtimeSinceStartup:0.00}s] {message}"); } public static string Describe() { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) string text = (((Object)(object)Minimap.instance == (Object)null) ? "minimap=null" : $"textureSize={Minimap.instance.m_textureSize} pixelSize={Minimap.instance.m_pixelSize} maxZoom={Minimap.instance.m_maxZoom} mapTexture={(((Object)(object)Minimap.instance.m_mapTexture != (Object)null) ? ((Texture)Minimap.instance.m_mapTexture).width : (-1))}"); WorldGenerator instance = WorldGenerator.instance; if (instance == null) { return text + " generator=null"; } Biome biome = instance.GetBiome(0f, 15000f, 0.02f, false); Color val = default(Color); float biomeHeight = instance.GetBiomeHeight(biome, 0f, 15000f, ref val, false, true); return $"{text} world={instance.m_world?.m_name} menu={instance.m_world?.m_menu} biome@15k={biome} height@15k={biomeHeight:0.###} biomeHeightPatch=[{Patches(BiomeHeightMethod)}] baseHeightPatch=[{Patches(BaseHeightMethod)}]"; } private static string Patches(MethodBase method) { Patches patchInfo = Harmony.GetPatchInfo(method); if (patchInfo == null) { return "none"; } return string.Format("prefix {0} postfix {1} transpiler {2}: {3}", patchInfo.Prefixes.Count, patchInfo.Postfixes.Count, patchInfo.Transpilers.Count, string.Join(",", patchInfo.Owners)); } } [HarmonyPatch(typeof(WorldGenerator), "Initialize")] internal static class MapDiagnosticsInitializePatch { [HarmonyPriority(0)] [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] private static void Postfix(World world) { MapDiagnostics.Log($"WorldGenerator.Initialize world={world.m_name} menu={world.m_menu} -> {MapDiagnostics.Describe()}"); } } [HarmonyPatch(typeof(Minimap), "Awake")] internal static class MapDiagnosticsAwakePatch { [HarmonyPriority(0)] private static void Postfix() { MapDiagnostics.Log("Minimap.Awake -> " + MapDiagnostics.Describe()); } } [HarmonyPatch(typeof(Minimap), "GenerateWorldMap")] internal static class MapDiagnosticsGeneratePatch { [HarmonyPriority(800)] private static void Prefix() { MapDiagnostics.Log("Minimap.GenerateWorldMap called -> " + MapDiagnostics.Describe()); } } [HarmonyPatch(typeof(Minimap), "SetMapData")] [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] internal static class MinimapLoadExplorationPatch { [HarmonyPriority(800)] [HarmonyBefore(new string[] { "expand_world_size" })] private static void Prefix(Minimap __instance, byte[] data, out int __state) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown ZPackage val = new ZPackage(data); if (val.ReadInt() >= 7) { val = val.ReadCompressedPackage(); } int num = val.ReadInt(); if (num <= 0 || num > SystemInfo.maxTextureSize) { throw new InvalidDataException($"Unsupported saved minimap size: {num}"); } __state = num; MapGeneration.UpdateTextureSize(__instance, num); } private static void Postfix(Minimap __instance, int __state) { MinimapAwake.Refresh(__instance); MapDiagnostics.Log($"Loaded profile exploration: savedSize={__state} finalSize={__instance.m_textureSize} pixelSize={__instance.m_pixelSize}"); } } [HarmonyPatch(typeof(MapGeneration), "ConfigReady")] internal static class MinimapConfigReadyPatch { private static void Postfix(bool __result) { if (__result && !((Object)(object)Minimap.instance == (Object)null) && MinimapAwake.Refresh(Minimap.instance)) { MapDiagnostics.Log("Applied EWS minimap dimensions before generation -> " + MapDiagnostics.Describe()); } } } [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] internal sealed class ShaderBlob { public int[] Platforms = Array.Empty<int>(); public uint[][] Offsets = Array.Empty<uint[]>(); public uint[][] CompressedLengths = Array.Empty<uint[]>(); public uint[][] DecompressedLengths = Array.Empty<uint[]>(); public int BlobStart; public int BlobLength; public int ArraysStart; public int SuffixStart => Align4(BlobStart + BlobLength); public static ShaderBlob Find(byte[] data) { for (int i = 0; i + 16 <= data.Length; i += 4) { ShaderBlob shaderBlob = TryRead(data, i, data.Length); if (shaderBlob != null) { return shaderBlob; } } throw new InvalidDataException("Shader program blob not found"); } [return: <9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(2)] private static ShaderBlob TryRead(byte[] b, int p, int end) { int num = ReadInt(b, p); if (num < 1 || num > 8) { return null; } int pos = p + 4; if (pos + 4 * num > end) { return null; } int[] array = new int[num]; for (int i = 0; i < num; i++) { array[i] = ReadInt(b, pos); if (array[i] < 0 || array[i] > 40) { return null; } pos += 4; } int arraysStart = pos; uint[][] array2 = ReadArrays(b, ref pos, end, num, null); if (array2 == null) { return null; } uint[][] array3 = ReadArrays(b, ref pos, end, num, array2); if (array3 == null) { return null; } uint[][] array4 = ReadArrays(b, ref pos, end, num, array2); if (array4 == null) { return null; } if (pos + 4 > end) { return null; } int num2 = ReadInt(b, pos); int num3 = pos + 4; if (num2 <= 0 || (long)num3 + (long)num2 > end) { return null; } for (int j = 0; j < num; j++) { for (int k = 0; k < array2[j].Length; k++) { if ((long)array2[j][k] + (long)array3[j][k] > num2 || array3[j][k] > array4[j][k] + array4[j][k] / 255 + 16) { return null; } } } return new ShaderBlob { Platforms = array, Offsets = array2, CompressedLengths = array3, DecompressedLengths = array4, BlobStart = num3, BlobLength = num2, ArraysStart = arraysStart }; } [return: <9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(new byte[] { 2, 1 })] private static uint[][] ReadArrays(byte[] b, ref int pos, int end, int count, [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(new byte[] { 2, 1 })] uint[][] shape) { if (pos + 4 > end || ReadInt(b, pos) != count) { return null; } pos += 4; uint[][] array = new uint[count][]; for (int i = 0; i < count; i++) { if (pos + 4 > end) { return null; } int num = ReadInt(b, pos); if (num < 1 || num > 100000 || pos + 4 + 4L * (long)num > end) { return null; } if (shape != null && shape[i].Length != num) { return null; } pos += 4; array[i] = new uint[num]; for (int j = 0; j < num; j++) { array[i][j] = (uint)ReadInt(b, pos); pos += 4; } } return array; } private static int Align4(int value) { return (value + 3) & -4; } private static int ReadInt(byte[] b, int p) { return BitConverter.ToInt32(b, p); } } [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] internal sealed class ShaderSource { public string Name = ""; public byte[] Object = Array.Empty<byte>(); public List<string> Externals = new List<string>(); public static ShaderSource Read(string bundlePath, string assetPath, string name) { using FileStream stream = File.OpenRead(bundlePath); using BundleReader bundleReader = new BundleReader(stream); BundleNode serializedNode = bundleReader.GetSerializedNode(); byte[] header = bundleReader.Read(serializedNode.Offset, 48); SerializedFile serializedFile = SerializedFile.Parse(bundleReader.Read(serializedNode.Offset, SerializedFile.MetadataEnd(header))); SerializedObject single = serializedFile.GetSingle(142); byte[] assetBundle = bundleReader.Read(serializedNode.Offset + serializedFile.DataOffset + single.ByteStart, (int)single.ByteSize); SerializedObject byPathId = serializedFile.GetByPathId(SerializedFile.FindContainerPathId(assetBundle, assetPath)); if (byPathId.ClassId != 48) { throw new InvalidDataException(assetPath + " is not a shader"); } return new ShaderSource { Name = name, Object = bundleReader.Read(serializedNode.Offset + serializedFile.DataOffset + byPathId.ByteStart, (int)byPathId.ByteSize), Externals = serializedFile.Externals }; } } [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] internal sealed class ShaderTemplate { public BundleFile Bundle = new BundleFile(); public byte[] Serialized = Array.Empty<byte>(); public SerializedFile File = new SerializedFile(); public SerializedObject Shader = new SerializedObject(); public static ShaderTemplate Read(string bundlePath) { using FileStream stream = System.IO.File.OpenRead(bundlePath); using BundleReader reader = new BundleReader(stream); BundleFile bundleFile = BundleFile.Read(reader); if (bundleFile.Nodes.Count != 1) { throw new InvalidDataException("Unexpected template layout with " + bundleFile.Nodes.Count + " files"); } BundleNode bundleNode = bundleFile.Nodes[0]; byte[] array = new byte[bundleNode.Size]; Buffer.BlockCopy(bundleFile.Data, (int)bundleNode.Offset, array, 0, (int)bundleNode.Size); SerializedFile serializedFile = SerializedFile.Parse(array); return new ShaderTemplate { Bundle = bundleFile, Serialized = array, File = serializedFile, Shader = serializedFile.GetSingle(48) }; } } [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] internal static class ShaderPatcher { [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(0)] private sealed class Values { private const float Tolerance = 50f; private readonly float _distance; private readonly float _offset; public Values(float radius) { _distance = radius * 1.2f; _offset = radius * 0.4f; } [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] public int ReplaceNear(byte[] t, int p) { float num = BitConverter.ToSingle(t, p); int num2 = Classify(num); float num3; switch (num2) { case 0: return 0; default: num3 = ((num > 0f) ? (_offset + (num - 4000f)) : (0f - _offset + (num + 4000f))); break; case 1: num3 = _distance + (num - 12000f); break; } float value = num3; byte[] bytes = BitConverter.GetBytes(value); Buffer.BlockCopy(bytes, 0, t, p, 4); return num2; } private static int Classify(float value) { if (Math.Abs(value - 12000f) <= 50f) { return 1; } if (Math.Abs(Math.Abs(value) - 4000f) <= 50f) { return 2; } return 0; } } public const float VanillaRadius = 10000f; private const float VanillaYOffset = 4000f; private const float VanillaMinDistance = 12000f; private const float YOffsetFactor = 0.4f; private const float MinDistanceFactor = 1.2f; private const int PlatformD3D11 = 4; private const uint ImmediateScalarToken = 16385u; private const uint ImmediateVectorToken = 16386u; private const int PPtrSize = 12; public static byte[] CreatePatchedBundle(ShaderTemplate template, ShaderSource source, float radius) { byte[] array = PatchObject(source.Object, radius); RemapDependencies(array, source.Externals, template.File.Externals); byte[] array2 = SerializedFile.ReplaceObject(template.Serialized, template.File, template.Shader, array); BundleNode bundleNode = template.Bundle.Nodes[0]; BundleFile bundleFile = new BundleFile { Version = template.Bundle.Version, UnityVersion = template.Bundle.UnityVersion, UnityRevision = template.Bundle.UnityRevision, Flags = template.Bundle.Flags, Data = array2 }; bundleFile.Nodes.Add(new BundleNode { Offset = 0L, Size = array2.Length, Flags = bundleNode.Flags, Path = CreateCabName(source.Name, radius) }); return bundleFile.Write(); } private static byte[] PatchObject(byte[] shaderObject, float radius) { ShaderBlob shaderBlob = ShaderBlob.Find(shaderObject); Values values = new Values(radius); bool flag = false; byte[][][] array = new byte[shaderBlob.Platforms.Length][][]; for (int i = 0; i < shaderBlob.Platforms.Length; i++) { int num = shaderBlob.Platforms[i]; array[i] = new byte[shaderBlob.Offsets[i].Length][]; for (int j = 0; j < shaderBlob.Offsets[i].Length; j++) { int num2 = (int)shaderBlob.CompressedLengths[i][j]; int num3 = (int)shaderBlob.DecompressedLengths[i][j]; byte[] array2 = new byte[num2]; Buffer.BlockCopy(shaderObject, shaderBlob.BlobStart + (int)shaderBlob.Offsets[i][j], array2, 0, num2); array[i][j] = array2; if (num == 4) { byte[] array3 = new byte[num3]; if (Lz4.Decompress(array2, 0, num2, array3, 0, num3) != num3) { throw new InvalidDataException("Shader program size mismatch"); } int num4 = PatchDirect3D(array3, values); if (num4 <= 0) { throw new InvalidDataException("Direct3D 11 program could not be patched (code " + num4 + ")"); } array[i][j] = EncodeVerified(array3); flag = true; } } } if (!flag) { throw new InvalidDataException("Shader has no Direct3D 11 program"); } return RebuildObject(shaderObject, shaderBlob, array); } private static byte[] RebuildObject(byte[] shaderObject, ShaderBlob blob, byte[][][] chunks) { List<byte> list = new List<byte>(); uint num = 0u; uint[][] array = new uint[chunks.Length][]; uint[][] array2 = new uint[chunks.Length][]; for (int i = 0; i < chunks.Length; i++) { array[i] = new uint[chunks[i].Length]; array2[i] = new uint[chunks[i].Length]; for (int j = 0; j < chunks[i].Length; j++) { array[i][j] = num; array2[i][j] = (uint)chunks[i][j].Length; num += array2[i][j]; } } WriteArrays(list, array); WriteArrays(list, array2); WriteArrays(list, blob.DecompressedLengths); list.AddRange(BitConverter.GetBytes((int)num)); foreach (byte[][] array3 in chunks) { byte[][] array4 = array3; foreach (byte[] collection in array4) { list.AddRange(collection); } } while (((blob.ArraysStart + list.Count) & 3) != 0) { list.Add(0); } int num2 = shaderObject.Length - blob.SuffixStart; byte[] array5 = new byte[blob.ArraysStart + list.Count + num2]; Buffer.BlockCopy(shaderObject, 0, array5, 0, blob.ArraysStart); list.CopyTo(array5, blob.ArraysStart); Buffer.BlockCopy(shaderObject, blob.SuffixStart, array5, blob.ArraysStart + list.Count, num2); return array5; } private static void RemapDependencies(byte[] shaderObject, List<string> sourceExternals, List<string> targetExternals) { ShaderBlob shaderBlob = ShaderBlob.Find(shaderObject); int suffixStart = shaderBlob.SuffixStart; int num = BitConverter.ToInt32(shaderObject, suffixStart); suffixStart += 4 + num * 4; int num2 = BitConverter.ToInt32(shaderObject, suffixStart); suffixStart += 4; int num3 = 0; while (num3 < num2) { int num4 = BitConverter.ToInt32(shaderObject, suffixStart); if (num4 == 0) { throw new InvalidDataException("Shader depends on an object inside its own bundle"); } if (num4 > sourceExternals.Count) { throw new InvalidDataException("Invalid shader dependency file " + num4); } int num5 = targetExternals.IndexOf(sourceExternals[num4 - 1]); if (num5 < 0) { throw new InvalidDataException("Shader dependency not available: " + sourceExternals[num4 - 1]); } Buffer.BlockCopy(BitConverter.GetBytes(num5 + 1), 0, shaderObject, suffixStart, 4); num3++; suffixStart += 12; } if (BitConverter.ToInt32(shaderObject, suffixStart) != 0) { throw new InvalidDataException("Shader textures are not supported"); } } private static byte[] EncodeVerified(byte[] program) { byte[] array = Lz4.Encode(program); byte[] array2 = new byte[program.Length]; if (Lz4.Decompress(array, 0, array.Length, array2, 0, array2.Length) != program.Length) { throw new InvalidDataException("LZ4 encode size mismatch"); } for (int i = 0; i < program.Length; i++) { if (array2[i] != program[i]) { throw new InvalidDataException("LZ4 encode mismatch"); } } return array; } private static void WriteArrays(List<byte> list, uint[][] arrays) { list.AddRange(BitConverter.GetBytes(arrays.Length)); foreach (uint[] array in arrays) { list.AddRange(BitConverter.GetBytes(array.Length)); uint[] array2 = array; foreach (uint value in array2) { list.AddRange(BitConverter.GetBytes(value)); } } } private static int PatchDirect3D(byte[] t, Values values) { int distanceCount = 0; int offsetCount = 0; for (int i = 0; i + 32 <= t.Length; i++) { if (t[i] != 68 || t[i + 1] != 88 || t[i + 2] != 66 || t[i + 3] != 67) { continue; } int num = BitConverter.ToInt32(t, i + 24); if (num < 32 || (long)i + (long)num > t.Length) { continue; } if (!DxbcChecksum.Matches(t, i, num)) { return -2; } int num2 = 0; int num3 = BitConverter.ToInt32(t, i + 28); for (int j = 0; j < num3; j++) { int num4 = i + BitConverter.ToInt32(t, i + 32 + 4 * j); if (IsShaderCode(t, num4)) { int val = num4 + 8 + BitConverter.ToInt32(t, num4 + 4); num2 += PatchImmediates(t, num4 + 16, Math.Min(val, i + num), values, ref distanceCount, ref offsetCount); } } if (num2 > 0) { DxbcChecksum.Write(t, i, num); } i += num - 1; } if (distanceCount == 0 || offsetCount == 0) { return -3; } return distanceCount + offsetCount; } private static bool IsShaderCode(byte[] t, int chunk) { if (t[chunk] != 83 || t[chunk + 1] != 72) { return false; } return (t[chunk + 2] == 69 && t[chunk + 3] == 88) || (t[chunk + 2] == 68 && t[chunk + 3] == 82); } private static int PatchImmediates(byte[] t, int start, int end, Values values, ref int distanceCount, ref int offsetCount) { int num = 0; int num2 = start; while (num2 + 4 <= end) { int num3 = BitConverter.ToUInt32(t, num2) switch { 16386u => 4, 16385u => 1, _ => 0, }; num2 += 4; if (num3 == 0 || num2 + 4 * num3 > end) { continue; } int num4 = 0; while (num4 < num3) { int num5 = values.ReplaceNear(t, num2); switch (num5) { case 1: distanceCount++; break; case 2: offsetCount++; break; } if (num5 != 0) { num++; } num4++; num2 += 4; } } return num; } private static string CreateCabName(string name, float radius) { using MD5 mD = MD5.Create(); byte[] array = mD.ComputeHash(Encoding.UTF8.GetBytes(name + "|DeepNorthShaderFix|" + radius.ToString("R"))); StringBuilder stringBuilder = new StringBuilder("CAB-"); byte[] array2 = array; foreach (byte b in array2) { stringBuilder.Append(b.ToString("x2")); } return stringBuilder.ToString(); } } [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] internal static class DxbcChecksum { public static bool Matches(byte[] data, int offset, int size) { uint[] array = Compute(data, offset, size); for (int i = 0; i < 4; i++) { if (BitConverter.ToUInt32(data, offset + 4 + 4 * i) != array[i]) { return false; } } return true; } public static void Write(byte[] data, int offset, int size) { uint[] array = Compute(data, offset, size); for (int i = 0; i < 4; i++) { Buffer.BlockCopy(BitConverter.GetBytes(array[i]), 0, data, offset + 4 + 4 * i, 4); } } private static uint[] Compute(byte[] data, int offset, int size) { uint[] array = new uint[4] { 1732584193u, 4023233417u, 2562383102u, 271733878u }; uint[] array2 = new uint[16]; int num = offset + 20; int num2 = size - 20; int num3 = num2 / 64; int num4 = num2 % 64; for (int i = 0; i < num3; i++) { Load(data, num + i * 64, array2); Md5.Transform(array, array2); } uint num5 = (uint)(num2 * 8); int srcOffset = num + num3 * 64; byte[] array3 = new byte[64]; if (num4 >= 56) { Buffer.BlockCopy(data, srcOffset, array3, 0, num4); array3[num4] = 128; Load(array3, 0, array2); Md5.Transform(array, array2); Array.Clear(array2, 0, 16); array2[0] = num5; array2[15] = (num5 >> 2) | 1; Md5.Transform(array, array2); } else { Buffer.BlockCopy(BitConverter.GetBytes(num5), 0, array3, 0, 4); Buffer.BlockCopy(data, srcOffset, array3, 4, num4); array3[4 + num4] = 128; Buffer.BlockCopy(BitConverter.GetBytes((num5 >> 2) | 1), 0, array3, 60, 4); Load(array3, 0, array2); Md5.Transform(array, array2); } return array; } private static void Load(byte[] data, int offset, uint[] block) { for (int i = 0; i < 16; i++) { block[i] = BitConverter.ToUInt32(data, offset + 4 * i); } } } [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] internal static class Md5 { private static readonly int[] Shifts = new int[64] { 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 }; private static readonly uint[] Constants = CreateConstants(); private static uint[] CreateConstants() { uint[] array = new uint[64]; for (int i = 0; i < 64; i++) { array[i] = (uint)(long)Math.Floor(Math.Abs(Math.Sin(i + 1)) * 4294967296.0); } return array; } public static void Transform(uint[] state, uint[] m) { uint num = state[0]; uint num2 = state[1]; uint num3 = state[2]; uint num4 = state[3]; for (int i = 0; i < 64; i++) { uint num5; int num6; if (i < 16) { num5 = (num2 & num3) | (~num2 & num4); num6 = i; } else if (i < 32) { num5 = (num4 & num2) | (~num4 & num3); num6 = (5 * i + 1) & 0xF; } else if (i < 48) { num5 = num2 ^ num3 ^ num4; num6 = (3 * i + 5) & 0xF; } else { num5 = num3 ^ (num2 | ~num4); num6 = (7 * i) & 0xF; } uint num7 = num4; num4 = num3; num3 = num2; uint num8 = num + num5 + Constants[i] + m[num6]; num2 += (num8 << Shifts[i]) | (num8 >> 32 - Shifts[i]); num = num7; } state[0] += num; state[1] += num2; state[2] += num3; state[3] += num4; } } internal sealed class SerializedObject { public long PathId; public long ByteStart; public uint ByteSize; public int TypeIndex; public int ClassId; public int EntryPos; } [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] internal sealed class SerializedFile { public const int HeaderSize = 48; public const int ShaderClassId = 48; public const int AssetBundleClassId = 142; private const int MinVersion = 22; private const int MonoBehaviourClassId = 114; public uint Version; public long DataOffset; public List<SerializedObject> Objects = new List<SerializedObject>(); public List<string> Externals = new List<string>(); public static int MetadataEnd(byte[] header) { int pos = 20; return (int)(48 + BundleReader.ReadUInt32BE(header, ref pos)); } public static SerializedFile Parse(byte[] b) { int pos = 8; SerializedFile serializedFile = new SerializedFile { Version = BundleReader.ReadUInt32BE(b, ref pos) }; if (serializedFile.Version < 22) { throw new InvalidDataException("Unsupported serialized file version " + serializedFile.Version); } if (b[16] != 0) { throw new InvalidDataException("Big endian serialized file is not supported"); } pos = 32; serializedFile.DataOffset = BundleReader.ReadInt64BE(b, ref pos); pos = 48; BundleReader.ReadString(b, ref pos); pos += 4; bool flag = b[pos++] != 0; int num = ReadInt32(b, ref pos); int[] array = new int[num]; for (int i = 0; i < num; i++) { array[i] = ReadInt32(b, ref pos); pos += 3; if (array[i] == 114) { pos += 16; } pos += 16; if (flag) { int num2 = ReadInt32(b, ref pos); int num3 = ReadInt32(b, ref pos); pos += num2 * 32 + num3; int num4 = ReadInt32(b, ref pos); pos += num4 * 4; } } int num5 = ReadInt32(b, ref pos); for (int j = 0; j < num5; j++) { pos = Align4(pos); SerializedObject serializedObject = new SerializedObject { EntryPos = pos }; serializedObject.PathId = ReadInt64(b, ref pos); serializedObject.ByteStart = ReadInt64(b, ref pos); serializedObject.ByteSize = (uint)ReadInt32(b, ref pos); serializedObject.TypeIndex = ReadInt32(b, ref pos); if (serializedObject.TypeIndex < 0 || serializedObject.TypeIndex >= num) { throw new InvalidDataException("Invalid object type index"); } serializedObject.ClassId = array[serializedObject.TypeIndex]; serializedFile.Objects.Add(serializedObject); } int num6 = ReadInt32(b, ref pos); for (int k = 0; k < num6; k++) { pos += 4; pos = Align4(pos); pos += 8; } int num7 = ReadInt32(b, ref pos); for (int l = 0; l < num7; l++) { BundleReader.ReadString(b, ref pos); pos += 20; serializedFile.Externals.Add(BundleReader.ReadString(b, ref pos)); } return serializedFile; } public SerializedObject GetSingle(int classId) { SerializedObject serializedObject = null; foreach (SerializedObject @object in Objects) { if (@object.ClassId == classId) { if (serializedObject != null) { throw new InvalidDataException("More than one object of class " + classId); } serializedObject = @object; } } return serializedObject ?? throw new InvalidDataException("No object of class " + classId); } public SerializedObject GetByPathId(long pathId) { foreach (SerializedObject @object in Objects) { if (@object.PathId == pathId) { return @object; } } throw new InvalidDataException("Object " + pathId + " not found"); } public static long FindContainerPathId(byte[] assetBundle, string assetPath) { int pos = 0; ReadAlignedString(assetBundle, ref pos); int num = ReadInt32(assetBundle, ref pos); pos += num * 12; int num2 = ReadInt32(assetBundle, ref pos); for (int i = 0; i < num2; i++) { string a = ReadAlignedString(assetBundle, ref pos); pos += 8; int num3 = ReadInt32(assetBundle, ref pos); long result = ReadInt64(assetBundle, ref pos); if (num3 == 0 && string.Equals(a, assetPath, StringComparison.OrdinalIgnoreCase)) { return result; } } throw new InvalidDataException(assetPath + " not found in the bundle container"); } public static byte[] ReplaceObject(byte[] original, SerializedFile file, SerializedObject target, byte[] newObject) { long num = file.DataOffset + target.ByteStart; long num2 = num + target.ByteSize; long num3 = newObject.Length - target.ByteSize; long num4 = (num3 + 7) & -8; byte[] array = new byte[original.Length + num4]; Buffer.BlockCopy(original, 0, array, 0, (int)num); Buffer.BlockCopy(newObject, 0, array, (int)num, newObject.Length); Buffer.BlockCopy(original, (int)num2, array, (int)(num2 + num4), (int)(original.Length - num2)); foreach (SerializedObject @object in file.Objects) { if (@object == target) { WriteInt32(array, @object.EntryPos + 16, newObject.Length); } else if (@object.ByteStart > target.ByteStart) { WriteInt64(array, @object.EntryPos + 8, @object.ByteStart + num4); } } int pos = 24; long num5 = BundleReader.ReadInt64BE(original, ref pos); WriteInt64BE(array, 24, num5 + num4); int pos2 = 4; uint num6 = BundleReader.ReadUInt32BE(original, ref pos2); if (num6 != 0) { WriteUInt32BE(array, 4, (uint)(num6 + num4)); } return array; } private static string ReadAlignedString(byte[] b, ref int pos) { int num = ReadInt32(b, ref pos); string result = Encoding.UTF8.GetString(b, pos, num); pos = Align4(pos + num); return result; } private static int Align4(int pos) { return (pos + 3) & -4; } private static int ReadInt32(byte[] b, ref int pos) { int result = BitConverter.ToInt32(b, pos); pos += 4; return result; } private static long ReadInt64(byte[] b, ref int pos) { long result = BitConverter.ToInt64(b, pos); pos += 8; return result; } private static void WriteInt32(byte[] b, int pos, int v) { Buffer.BlockCopy(BitConverter.GetBytes(v), 0, b, pos, 4); } private static void WriteInt64(byte[] b, int pos, long v) { Buffer.BlockCopy(BitConverter.GetBytes(v), 0, b, pos, 8); } private static void WriteUInt32BE(byte[] b, int pos, uint v) { for (int i = 0; i < 4; i++) { b[pos + i] = (byte)(v >> 24 - 8 * i); } } private static void WriteInt64BE(byte[] b, int pos, long v) { for (int i = 0; i < 8; i++) { b[pos + i] = (byte)(v >> 56 - 8 * i); } } } internal sealed class BundleNode { public long Offset; public long Size; public uint Flags; [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(1)] public string Path = ""; } [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] internal sealed class BundleReader : IDisposable { private const uint CompressionMask = 63u; private const uint BlocksInfoAtEnd = 128u; private const uint BlockInfoPaddingAtStart = 512u; private const uint SerializedFileFlag = 4u; private const int HeaderReadSize = 256; private readonly Stream _stream; private readonly long[] _compressedOffsets; private readonly long[] _uncompressedOffsets; private readonly uint[] _compressedSizes; private readonly uint[] _uncompressedSizes; private readonly ushort[] _blockFlags; private int _cachedBlock = -1; private byte[] _cachedData = Array.Empty<byte>(); public uint Version { get; } public string UnityVersion { get; } public string UnityRevision { get; } public uint Flags { get; } public long TotalSize { get; } public List<BundleNode> Nodes { get; } = new List<BundleNode>(); public BundleReader(Stream stream) { _stream = stream; byte[] b = ReadAt(0L, (int)Math.Min(256L, stream.Length)); int pos = 0; string text = ReadString(b, ref pos); if (text != "UnityFS") { throw new InvalidDataException("Not a UnityFS bundle: " + text); } Version = ReadUInt32BE(b, ref pos); UnityVersion = ReadString(b, ref pos); UnityRevision = ReadString(b, ref pos); ReadInt64BE(b, ref pos); int num = (int)ReadUInt32BE(b, ref pos); int uncompressedSize = (int)ReadUInt32BE(b, ref pos); Flags = ReadUInt32BE(b, ref pos); if (Version >= 7) { pos = Align16(pos); } long num2 = (((Flags & 0x80) != 0) ? (stream.Length - num) : pos); byte[] b2 = Decompress(ReadAt(num2, num), uncompressedSize, Flags & 0x3F); long num3 = (((Flags & 0x80) != 0) ? pos : (num2 + num)); if ((Flags & 0x200) != 0) { num3 = (num3 + 15) & -16; } int pos2 = 16; int num4 = (int)ReadUInt32BE(b2, ref pos2); _compressedOffsets = new long[num4]; _uncompressedOffsets = new long[num4]; _compressedSizes = new uint[num4]; _uncompressedSizes = new uint[num4]; _blockFlags = new ushort[num4]; long num5 = num3; long num6 = 0L; for (int i = 0; i < num4; i++) { _uncompressedSizes[i] = ReadUInt32BE(b2, ref pos2); _compressedSizes[i] = ReadUInt32BE(b2, ref pos2); _blockFlags[i] = ReadUInt16BE(b2, ref pos2); _compressedOffsets[i] = num5; _uncompressedOffsets[i] = num6; num5 += _compressedSizes[i]; num6 += _uncompressedSizes[i]; } TotalSize = num6; int num7 = (int)ReadUInt32BE(b2, ref pos2); for (int j = 0; j < num7; j++) { Nodes.Add(new BundleNode { Offset = ReadInt64BE(b2, ref pos2), Size = ReadInt64BE(b2, ref pos2), Flags = ReadUInt32BE(b2, ref pos2), Path = ReadString(b2, ref pos2) }); } } public BundleNode GetSerializedNode() { foreach (BundleNode node in Nodes) { if ((node.Flags & 4) != 0) { return node; } } throw new InvalidDataException("Bundle has no serialized file"); } public byte[] Read(long offset, int length) { if (offset < 0 || offset + length > TotalSize) { throw new InvalidDataException("Bundle read out of range"); } byte[] array = new byte[length]; int num = FindBlock(offset); int num2 = 0; while (num2 < length) { byte[] block = GetBlock(num); long num3 = _uncompressedOffsets[num]; int num4 = (int)(offset + num2 - num3); int num5 = Math.Min(block.Length - num4, length - num2); Buffer.BlockCopy(block, num4, array, num2, num5); num2 += num5; num++; } return array; } public void Dispose() { _stream.Dispose(); } private int FindBlock(long offset) { int num = 0; int num2 = _uncompressedOffsets.Length - 1; while (num < num2) { int num3 = (num + num2 + 1) / 2; if (_uncompressedOffsets[num3] <= offset) { num = num3; } else { num2 = num3 - 1; } } return num; } private byte[] GetBlock(int index) { if (index == _cachedBlock) { return _cachedData; } byte[] src = ReadAt(_compressedOffsets[index], (int)_compressedSizes[index]); _cachedData = Decompress(src, (int)_uncompressedSizes[index], (uint)(_blockFlags[index] & 0x3F)); _cachedBlock = index; return _cachedData; } private byte[] ReadAt(long position, int length) { byte[] array = new byte[length]; _stream.Position = position; int num; for (int i = 0; i < length; i += num) { num = _stream.Read(array, i, length - i); if (num <= 0) { throw new EndOfStreamException(); } } return array; } private static byte[] Decompress(byte[] src, int uncompressedSize, uint compression) { switch (compression) { case 0u: return src; default: if (compression != 3) { throw new InvalidDataException("Unsupported bundle compression " + compression); } break; case 2u: break; } byte[] array = new byte[uncompressedSize]; if (Lz4.Decompress(src, 0, src.Length, array, 0, uncompressedSize) != uncompressedSize) { throw new InvalidDataException("LZ4 size mismatch"); } return array; } private static int Align16(int pos) { return (pos + 15) & -16; } internal static string ReadString(byte[] b, ref int pos) { int num = pos; while (b[pos] != 0) { pos++; } string result = Encoding.UTF8.GetString(b, num, pos - num); pos++; return result; } internal static uint ReadUInt32BE(byte[] b, ref int pos) { uint result = (uint)((b[pos] << 24) | (b[pos + 1] << 16) | (b[pos + 2] << 8) | b[pos + 3]); pos += 4; return result; } private static ushort ReadUInt16BE(byte[] b, ref int pos) { ushort result = (ushort)((b[pos] << 8) | b[pos + 1]); pos += 2; return result; } internal static long ReadInt64BE(byte[] b, ref int pos) { long num = 0L; for (int i = 0; i < 8; i++) { num = (num << 8) | b[pos + i]; } pos += 8; return num; } } [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] internal sealed class BundleFile { private const uint CompressionMask = 63u; private const uint BlocksInfoAtEnd = 128u; private const uint BlocksAndDirectoryCombined = 64u; private const uint BlockInfoPaddingAtStart = 512u; public uint Version; public string UnityVersion = ""; public string UnityRevision = ""; public uint Flags; public List<BundleNode> Nodes = new List<BundleNode>(); public byte[] Data = Array.Empty<byte>(); public static BundleFile Read(BundleReader reader) { BundleFile bundleFile = new BundleFile { Version = reader.Version, UnityVersion = reader.UnityVersion, UnityRevision = reader.UnityRevision, Flags = reader.Flags, Data = reader.Read(0L, (int)reader.TotalSize) }; bundleFile.Nodes.AddRange(reader.Nodes); return bundleFile; } public byte[] Write() { uint num = (Flags & 0xFFFFFF40u) | 0x40; List<byte> list = new List<byte>(); list.AddRange(new byte[16]); WriteUInt32BE(list, 1u); WriteUInt32BE(list, (uint)Data.Length); WriteUInt32BE(list, (uint)Data.Length); WriteUInt16BE(list, 0); WriteUInt32BE(list, (uint)Nodes.Count); foreach (BundleNode node in Nodes) { WriteInt64BE(list, node.Offset); WriteInt64BE(list, node.Size); WriteUInt32BE(list, node.Flags); WriteString(list, node.Path); } List<byte> list2 = new List<byte>(); WriteString(list2, "UnityFS"); WriteUInt32BE(list2, Version); WriteString(list2, UnityVersion); WriteString(list2, UnityRevision); int count = list2.Count; WriteInt64BE(list2, 0L); WriteUInt32BE(list2, (uint)list.Count); WriteUInt32BE(list2, (uint)list.Count); WriteUInt32BE(list2, num); if (Version >= 7) { Pad16(list2); } list2.AddRange(list); if ((num & 0x200) != 0) { Pad16(list2); } long num2 = (long)list2.Count + (long)Data.Length; byte[] array = new byte[num2]; list2.CopyTo(array); for (int i = 0; i < 8; i++) { array[count + i] = (byte)(num2 >> 56 - 8 * i); } Buffer.BlockCopy(Data, 0, array, list2.Count, Data.Length); return array; } private static void Pad16(List<byte> list) { while ((list.Count & 0xF) != 0) { list.Add(0); } } private static void WriteString(List<byte> list, string s) { list.AddRange(Encoding.UTF8.GetBytes(s)); list.Add(0); } private static void WriteUInt32BE(List<byte> list, uint v) { list.Add((byte)(v >> 24)); list.Add((byte)(v >> 16)); list.Add((byte)(v >> 8)); list.Add((byte)v); } private static void WriteUInt16BE(List<byte> list, ushort v) { list.Add((byte)(v >> 8)); list.Add((byte)v); } private static void WriteInt64BE(List<byte> list, long v) { for (int i = 0; i < 8; i++) { list.Add((byte)(v >> 56 - 8 * i)); } } } [<cebcbe21-7092-4bc7-8bb8-eca43f57f185>NullableContext(1)] [<9adb7d9b-56fb-4c20-9988-0b5764d74a79>Nullable(0)] internal static class Lz4 { private const int MinMatch = 4; private const int HashBits = 16; private const int MaxOffset = 65535; private const int LastLiterals = 5; private const int MatchSearchLimit = 12; public static byte[] Encode(byte[] data) { int num = data.Length; byte[] output = new byte[num + num / 255 + 16]; int[] array = new int[65536]; for (int i = 0; i < array.Length; i++) { array[i] = -1; } int o = 0; int num2 = 0; int num3 = 0; int num4 = num - 12; while (num3 < num4) { uint num5 = BitConverter.ToUInt32(data, num3); int num6 = (int)num5 * -1640531535 >>> 16; int num7 = array[num6]; array[num6] = num3; if (num7 < 0 || num3 - num7 > 65535 || BitConverter.ToUInt32(data, num7) != num5) { num3++; continue; } int j = 4; for (int num8 = num - 5 - num3; j < num8 && data[num7 + j] == data[num3 + j]; j++) { } o = WriteSequence(output, o, data, num2, num3 - num2, num3 - num7, j); num3 += j; num2 = num3; } return Finish(output, o, data, num2, num - num2); } private static int WriteSequence(byte[] output, int o, byte[] data, int literalStart, int literalLength, int offset, int matchLength) { int num = o++; int num2 = matchLength - 4; int num3 = (Math.Min(literalLength, 15) << 4) | Math.Min(num2, 15); output[num] = (byte)num3; o = WriteLength(output, o, literalLength); Buffer.BlockCopy(data, literalStart, output, o, literalLength); o += literalLength; output[o++] = (byte)offset; output[o++] = (byte)(offset >> 8); return WriteLength(output, o, num2); } private static byte[] Finish(byte[] output, int o, byte[] data, int literalStart, int literalLength) { output[o++] = (byte)(Math.Min(literalLength, 15) << 4); o = WriteLength(output, o, literalLength); Buffer.BlockCopy(data, literalStart, output, o, literalLength); o += literalLength; byte[] array = new byte[o]; Buffer.BlockCopy(output, 0, array, 0, o); return array; } private static int WriteLength(byte[] output, int o, int length) { if (length < 15) { return o; } int num; for (num = length - 15; num >= 255; num -= 255) { output[o++] = byte.MaxValue; } output[o++] = (byte)num; return o; } public static int Decompress(byte[] src, int srcOffset, int srcLength, byte[] dst, int dstOffset, int dstLength) { int num = srcOffset; int num2 = srcOffset + srcLength; int num3 = dstOffset; int num4 = dstOffset + dstLength; while (num < num2) { int num5 = src[num++]; int num6 = num5 >> 4; if (num6 == 15) { int num7; do { num7 = src[num++]; num6 += num7; } while (num7 == 255); } if (num + num6 > num2 || num3 + num6 > num4) { throw new InvalidDataException("LZ4 literal out of range"); } Buffer.BlockCopy(src, num, dst, num3, num6); num += num6; num3 += num6; if (num >= num2) { break; } int num8 = src[num] | (src[num + 1] << 8); num += 2; int num9 = num5 & 0xF; if (num9 == 15) { int num10; do { num10 = src[num++]; num9 += num10; } while (num10 == 255); } num9 += 4; int num11 = num3 - num8; if (num8 == 0 || num11 < dstOffset || num3 + num9 > num4) { throw new InvalidDataException("LZ4 match out of range"); } for (int i = 0; i < num9; i++) { dst[num3++] = dst[num11++]; } } return num3 - dstOffset; } } } namespace Microsoft.CodeAnalysis { [Embedded] [CompilerGenerated] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] [Embedded] [CompilerGenerated] 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] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] [Embedded] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] [Embedded] [CompilerGenerated] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ServerSync { [PublicAPI] internal abstract class OwnConfigEntryBase { public object? LocalBaseValue; public bool SynchronizedConfig = true; public abstract ConfigEntryBase BaseConfig { get; } } [PublicAPI] internal class SyncedConfigEntry<T>(ConfigEntry<T> sourceConfig) : OwnConfigEntryBase() { public readonly ConfigEntry<T> SourceConfig = sourceConfig; public override ConfigEntryBase BaseConfig => (ConfigEntryBase)(object)SourceConfig; public T Value { get { return SourceConfig.Value; } set { SourceConfig.Value = value; } } public void AssignLocalValue(T value) { if (LocalBaseValue == null) { Value = value; } else { LocalBaseValue = value; } } } internal abstract class CustomSyncedValueBase { public object? LocalBaseValue; public readonly string Identifier; public readonly Type Type; private object? boxedValue; protected bool localIsOwner; public readonly int Priority; public object? BoxedValue { get { return boxedValue; } set { boxedValue = value; this.ValueChanged?.Invoke(); } } public event Action? ValueChanged; protected CustomSyncedValueBase(ConfigSync configSync, string identifier, Type type, int priority) { Priority = priority; Identifier = identifier; Type = type; configSync.AddCustomValue(this); localIsOwner = configSync.IsSourceOfTruth; configSync.SourceOfTruthChanged += delegate(bool truth) { localIsOwner = truth; }; } } [PublicAPI] internal sealed class CustomSyncedValue<T> : CustomSyncedValueBase { public T Value { get { return (T)base.BoxedValue; } set { base.BoxedValue = value; } } public CustomSyncedValue(ConfigSync configSync, string identifier, T value = default(T), int priority = 0) : base(configSync, identifier, typeof(T), priority) { Value = value; } public void AssignLocalValue(T value) { if (localIsOwner) { Value = value; } else { LocalBaseValue = value; } } } internal class ConfigurationManagerAttributes { [UsedImplicitly] public bool? ReadOnly = false; } [PublicAPI] internal class ConfigSync { [HarmonyPatch(typeof(ZRpc), "HandlePackage")] private static class SnatchCurrentlyHandlingRPC { public static ZRpc? currentRpc; [HarmonyPrefix] private static void Prefix(ZRpc __instance) { currentRpc = __instance; } } [HarmonyPatch(typeof(ZNet), "Awake")] internal static class RegisterRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance) { isServer = __instance.IsServer(); foreach (ConfigSync configSync2 in configSyncs) { ZRoutedRpc.instance.Register<ZPackage>(configSync2.Name + " ConfigSync", (Action<long, ZPackage>)configSync2.RPC_FromOtherClientConfigSync); if (isServer) { configSync2.InitialSyncDone = true; Debug.Log((object)("Registered '" + configSync2.Name + " ConfigSync' RPC - waiting for incoming connections")); } } if (isServer) { ((MonoBehaviour)__instance).StartCoroutine(WatchAdminListChanges()); } static void SendAdmin(List<ZNetPeer> peers, bool isAdmin) { ZPackage package = ConfigsToPackage(null, null, new PackageEntry[1] { new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = isAdmin } }); ConfigSync configSync = configSyncs.First(); if (configSync != null) { ((MonoBehaviour)ZNet.instance).StartCoroutine(configSync.sendZPackage(peers, package)); } } static IEnumerator WatchAdminListChanges() { MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); List<string> CurrentList = new List<string>(adminList.GetList()); while (true) { yield return (object)new WaitForSeconds(30f); if (!adminList.GetList().SequenceEqual(CurrentList)) { CurrentList = new List<string>(adminList.GetList()); List<ZNetPeer> adminPeer = ZNet.instance.GetPeers().Where(delegate(ZNetPeer p) { string hostName = p.m_rpc.GetSocket().GetHostName(); return ((object)listContainsId == null) ? adminList.Contains(hostName) : ((bool)listContainsId.Invoke(ZNet.instance, new object[2] { adminList, hostName })); }).ToList(); List<ZNetPeer> nonAdminPeer = ZNet.instance.GetPeers().Except(adminPeer).ToList(); SendAdmin(nonAdminPeer, isAdmin: false); SendAdmin(adminPeer, isAdmin: true); } } } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class RegisterClientRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance, ZNetPeer peer) { if (__instance.IsServer()) { return; } foreach (ConfigSync configSync in configSyncs) { peer.m_rpc.Register<ZPackage>(configSync.Name + " ConfigSync", (Action<ZRpc, ZPackage>)configSync.RPC_FromServerConfigSync); } } } private class ParsedConfigs { public readonly Dictionary<OwnConfigEntryBase, object?> configValues = new Dictionary<OwnConfigEntryBase, object>(); public readonly Dictionary<CustomSyncedValueBase, object?> customValues = new Dictionary<CustomSyncedValueBase, object>(); } [HarmonyPatch(typeof(ZNet), "Shutdown")] private class ResetConfigsOnShutdown { [HarmonyPostfix] private static void Postfix() { ProcessingServerUpdate = true; foreach (ConfigSync configSync in configSyncs) { configSync.resetConfigsFromServer(); configSync.IsSourceOfTruth = true; configSync.InitialSyncDone = false; } ProcessingServerUpdate = false; } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private class SendConfigsAfterLogin { private class BufferingSocket : ZPlayFabSocket, ISocket { public volatile bool finished = false; public volatile int versionMatchQueued = -1; public readonly List<ZPackage> Package = new List<ZPackage>(); public readonly ISocket Original; public BufferingSocket(ISocket original) { Original = original; ((ZPlayFabSocket)this)..ctor(); } public bool IsConnected() { return Original.IsConnected(); } public ZPackage Recv() { return Original.Recv(); } public int GetSendQueueSize() { return Original.GetSendQueueSize(); } public int GetCurrentSendRate() { return Original.GetCurrentSendRate(); } public bool IsHost() { return Original.IsHost(); } public void Dispose() { Original.Dispose(); } public bool GotNewData() { return Original.GotNewData(); } public void Close() { Original.Close(); } public string GetEndPointString() { return Original.GetEndPointString(); } public void GetAndResetStats(out int totalSent, out int totalRecv) { Original.GetAndResetStats(ref totalSent, ref totalRecv); } public void GetConnectionQuality(out float localQuality, out float remoteQuality, out int ping, out float outByteSec, out float inByteSec) { Original.GetConnectionQuality(ref localQuality, ref remoteQuality, ref ping, ref outByteSec, ref inByteSec); } public ISocket Accept() { return Original.Accept(); } public int GetHostPort() { return Original.GetHostPort(); } public bool Flush() { return Original.Flush(); } public string GetHostName() { return Original.GetHostName(); } public void VersionMatch() { if (finished) { Original.VersionMatch(); } else { versionMatchQueued = Package.Count; } } public void Send(ZPackage pkg) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown int pos = pkg.GetPos(); pkg.SetPos(0); int num = pkg.ReadInt(); if ((num == StringExtensionMethods.GetStableHashCode("PeerInfo") || num == StringExtensionMethods.GetStableHashCode("RoutedRPC") || num == StringExtensionMethods.GetStableHashCode("ZDOData")) && !finished) { ZPackage val = new ZPackage(pkg.GetArray()); val.SetPos(pos); Package.Add(val); } else { pkg.SetPos(pos); Original.Send(pkg); } } } [HarmonyPriority(800)] [HarmonyPrefix] private static void Prefix(ref Dictionary<Assembly, BufferingSocket>? __state, ZNet __instance, ZRpc rpc) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Invalid comparison between Unknown and I4 if (!__instance.IsServer()) { return; } BufferingSocket bufferingSocket = new BufferingSocket(rpc.GetSocket()); AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, bufferingSocket); object? obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); ZNetPeer val = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (val != null && (int)ZNet.m_onlineBackend > 0) { FieldInfo fieldInfo = AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket"); object? value = fieldInfo.GetValue(val); ZPlayFabSocket val2 = (ZPlayFabSocket)((value is ZPlayFabSocket) ? value : null); if (val2 != null) { typeof(ZPlayFabSocket).GetField("m_remotePlayerId").SetValue(bufferingSocket, val2.m_remotePlayerId); } fieldInfo.SetValue(val, bufferingSocket); } if (__state == null) { __state = new Dictionary<Assembly, BufferingSocket>(); } __state[Assembly.GetExecutingAssembly()] = bufferingSocket; } [HarmonyPostfix] private static void Postfix(Dictionary<Assembly, BufferingSocket> __state, ZNet __instance, ZRpc rpc) { ZNetPeer peer; if (__instance.IsServer()) { object obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); peer = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (peer == null) { SendBufferedData(); } else { ((MonoBehaviour)__instance).StartCoroutine(sendAsync()); } } void SendBufferedData() { if (rpc.GetSocket() is BufferingSocket bufferingSocket) { AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, bufferingSocket.Original); object? obj2 = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); ZNetPeer val = (ZNetPeer)((obj2 is ZNetPeer) ? obj2 : null); if (val != null) { AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket").SetValue(val, bufferingSocket.Original); } } BufferingSocket bufferingSocket2 = __state[Assembly.GetExecutingAssembly()]; bufferingSocket2.finished = true; for (int i = 0; i < bufferingSocket2.Package.Count; i++) { if (i == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } bufferingSocket2.Original.Send(bufferingSocket2.Package[i]); } if (bufferingSocket2.Package.Count == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } } IEnumerator sendAsync() { foreach (ConfigSync configSync in configSyncs) { List<PackageEntry> entries = new List<PackageEntry>(); if (configSync.CurrentVersion != null) { entries.Add(new PackageEntry { section = "Internal", key = "serverversion", type = typeof(string), value = configSync.CurrentVersion }); } MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); entries.Add(new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = (((object)listContainsId == null) ? ((object)adminList.Contains(rpc.GetSocket().GetHostName())) : listContainsId.Invoke(ZNet.instance, new object[2] { adminList, rpc.GetSocket().GetHostName() })) }); ZPackage package = ConfigsToPackage(configSync.allConfigs.Select((OwnConfigEntryBase c) => c.BaseConfig), configSync.allCustomValues, entries, partial: false); yield return ((MonoBehaviour)__instance).StartCoroutine(configSync.sendZPackage(new List<ZNetPeer> { peer }, package)); } SendBufferedData(); } } } private class PackageEntry { public string section = null; public string key = null; public Type type = null; public object? value; } [HarmonyPatch(typeof(ConfigEntryBase), "GetSerializedValue")] private static class PreventSavingServerInfo { [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, ref string __result) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || isWritableConfig(ownConfigEntryBase)) { return true; } __result = TomlTypeConverter.ConvertToString(ownConfigEntryBase.LocalBaseValue, __instance.SettingType); return false; } } [HarmonyPatch(typeof(ConfigEntryBase), "SetSerializedValue")] private static class PreventConfigRereadChangingValues { [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, string value) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || ownConfigEntryBase.LocalBaseValue == null) { return true; } try { ownConfigEntryBase.LocalBaseValue = TomlTypeConverter.ConvertToValue(value, __instance.SettingType); } catch (Exception ex) { Debug.LogWarning((object)$"Config value of setting \"{__instance.Definition}\" could not be parsed and will be ignored. Reason: {ex.Message}; Value: {value}"); } return false; } } private class InvalidDeserializationTypeException : Exception { public string expected = null; public string received = null; public string field = ""; } public static bool ProcessingServerUpdate; public readonly string Name; public string? DisplayName; public string? CurrentVersion; public string? MinimumRequiredVersion; public bool ModRequired = false; private bool? forceConfigLocking; private bool isSourceOfTruth = true; private static readonly HashSet<ConfigSync> configSyncs; private readonly HashSet<OwnConfigEntryBase> allConfigs = new HashSet<OwnConfigEntryBase>(); private HashSet<CustomSyncedValueBase> allCustomValues = new HashSet<CustomSyncedValueBase>(); private static bool isServer; private static bool lockExempt; private OwnConfigEntryBase? lockedConfig = null; private const byte PARTIAL_CONFIGS = 1; private const byte FRAGMENTED_CONFIG = 2; private const byte COMPRESSED_CONFIG = 4; private readonly Dictionary<string, SortedDictionary<int, byte[]>> configValueCache = new Dictionary<string, SortedDictionary<int, byte[]>>(); private readonly List<KeyValuePair<long, string>> cacheExpirations = new List<KeyValuePair<long, string>>(); private static long packageCounter; public bool IsLocked { get { bool? flag = forceConfigLocking; bool num; if (!flag.HasValue) { if (lockedConfig == null) { goto IL_0052; } num = ((IConvertible)lockedConfig.BaseConfig.BoxedValue).ToInt32(CultureInfo.InvariantCulture) != 0; } else { num = flag == true; } if (!num) { goto IL_0052; } int result = ((!lockExempt) ? 1 : 0); goto IL_0053; IL_0052: result = 0; goto IL_0053; IL_0053: return (byte)result != 0; } set { forceConfigLocking = value; } } public bool IsAdmin => lockExempt || isSourceOfTruth; public bool IsSourceOfTruth { get { return isSourceOfTruth; } private set { if (value != isSourceOfTruth) { isSourceOfTruth = value; this.SourceOfTruthChanged?.Invoke(value); } } } public bool InitialSyncDone { get; private set; } = false; public event Action<bool>? SourceOfTruthChanged; private event Action? lockedConfigChanged; static ConfigSync() { ProcessingServerUpdate = false; configSyncs = new HashSet<ConfigSync>(); lockExempt = false; packageCounter = 0L; RuntimeHelpers.RunClassConstructor(typeof(VersionCheck).TypeHandle); } public ConfigSync(string name) { Name = name; configSyncs.Add(this); new VersionCheck(this); } public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigEntry<T> configEntry) { OwnConfigEntryBase ownConfigEntryBase = configData((ConfigEntryBase)(object)configEntry); SyncedConfigEntry<T> syncedEntry = ownConfigEntryBase as SyncedConfigEntry<T>; if (syncedEntry == null) { syncedEntry = new SyncedConfigEntry<T>(configEntry); AccessTools.DeclaredField(typeof(ConfigDescription), "<Tags>k__BackingField").SetValue(((ConfigEntryBase)configEntry).Description, new object[1] { new ConfigurationManagerAttributes() }.Concat(((ConfigEntryBase)configEntry).Description.Tags ?? Array.Empty<object>()).Concat(new SyncedConfigEntry<T>[1] { syncedEntry }).ToArray()); configEntry.SettingChanged += delegate { if (!ProcessingServerUpdate && syncedEntry.SynchronizedConfig) { Broadcast(0L, (ConfigEntryBase)configEntry); } }; allConfigs.Add(syncedEntry); } return syncedEntry; } public SyncedConfigEntry<T> AddLockingConfigEntry<T>(ConfigEntry<T> lockingConfig) where T : IConvertible { if (lockedConfig != null) { throw new Exception("Cannot initialize locking ConfigEntry twice"); } lockedConfig = AddConfigEntry<T>(lockingConfig); lockingConfig.SettingChanged += delegate { this.lockedConfigChanged?.Invoke(); }; return (SyncedConfigEntry<T>)lockedConfig; } internal void AddCustomValue(CustomSyncedValueBase customValue) { if (allCustomValues.Select((CustomSyncedValueBase v) => v.Identifier).Concat(new string[1] { "serverversion" }).Contains(customValue.Identifier)) { throw new Exception("Cannot have multiple settings with the same name or with a reserved name (serverversion)"); } allCustomValues.Add(customValue); allCustomValues = new HashSet<CustomSyncedValueBase>(allCustomValues.OrderByDescending((CustomSyncedValueBase v) => v.Priority)); customValue.ValueChanged += delegate { if (!ProcessingServerUpdate) { Broadcast(0L, customValue); } }; } private void RPC_FromServerConfigSync(ZRpc rpc, ZPackage package) { lockedConfigChanged += serverLockedSettingChanged; IsSourceOfTruth = false; if (HandleConfigSyncRPC(0L, package, clientUpdate: false)) { InitialSyncDone = true; } } private void RPC_FromOtherClientConfigSync(long sender, ZPackage package) { HandleConfigSyncRPC(sender, package, clientUpdate: true); } private bool HandleConfigSyncRPC(long sender, ZPackage package, bool clientUpdate) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Expected O, but got Unknown //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Expected O, but got Unknown try { if (isServer && IsLocked) { ZRpc? currentRpc = SnatchCurrentlyHandlingRPC.currentRpc; object obj; if (currentRpc == null) { obj = null; } else { ISocket socket = currentRpc.GetSocket(); obj = ((socket != null) ? socket.GetHostName() : null); } string text = (string)obj; if (text != null) { MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList val = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); if (!(((object)methodInfo == null) ? val.Contains(text) : ((bool)methodInfo.Invoke(ZNet.instance, new object[2] { val, text })))) { return false; } } } cacheExpirations.RemoveAll(delegate(KeyValuePair<long, string> kv) { if (kv.Key < DateTimeOffset.Now.Ticks) { configValueCache.Remove(kv.Value); return true; } return false; }); byte b = package.ReadByte(); if ((b & 2) != 0) { long num = package.ReadLong(); string text2 = sender.ToString() + num; if (!configValueCache.TryGetValue(text2, out SortedDictionary<int, byte[]> value)) { value = new SortedDictionary<int, byte[]>(); configValueCache[text2] = value; cacheExpirations.Add(new KeyValuePair<long, string>(DateTimeOffset.Now.AddSeconds(60.0).Ticks, text2)); } int key = package.ReadInt(); int num2 = package.ReadInt(); value.Add(key, package.ReadByteArray()); if (value.Count < num2) { return false; } configValueCache.Remove(text2); package = new ZPackage(value.Values.SelectMany((byte[] a) => a).ToArray()); b = package.ReadByte(); } ProcessingServerUpdate = true; if ((b & 4) != 0) { byte[] buffer = package.ReadByteArray(); MemoryStream stream = new MemoryStream(buffer); MemoryStream memoryStream = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress)) { deflateStream.CopyTo(memoryStream); } package = new ZPackage(memoryStream.ToArray()); b = package.ReadByte(); } if ((b & 1) == 0) { resetConfigsFromServer(); } ParsedConfigs parsedConfigs = ReadConfigsFromPackage(package); ConfigFile val2 = null; bool saveOnConfigSet = false; foreach (KeyValuePair<OwnConfigEntryBase, object> configValue in parsedConfigs.configValues) { if (!isServer && configValue.Key.LocalBaseValue == null) { configValue.Key.LocalBaseValue = configValue.Key.BaseConfig.BoxedValue; } if (val2 == null) { val2 = configValue.Key.BaseConfig.ConfigFile; saveOnConfigSet = val2.SaveOnConfigSet; val2.SaveOnConfigSet = false; } configValue.Key.BaseConfig.BoxedValue = configValue.Value; } if (val2 != null) { val2.SaveOnConfigSet = saveOnConfigSet; val2.Save(); } foreach (KeyValuePair<CustomSyncedValueBase, object> customValue in parsedConfigs.customValues) { if (!isServer) { CustomSyncedValueBase key2 = customValue.Key; if (key2.LocalBaseValue == null) { key2.LocalBaseValue = customValue.Key.BoxedValue; } } customValue.Key.BoxedValue = customValue.Value; } Debug.Log((object)string.Format("Received {0} configs and {1} custom values from {2} for mod {3}", parsedConfigs.configValues.Count, parsedConfigs.customValues.Count, (isServer || clientUpdate) ? $"client {sender}" : "the server", DisplayName ?? Name)); if (!isServer) { serverLockedSettingChanged(); } return true; } finally { ProcessingServerUpdate = false; } } private ParsedConfigs ReadConfigsFromPackage(ZPackage package) { ParsedConfigs parsedConfigs = new ParsedConfigs(); Dictionary<string, OwnConfigEntryBase> dictionary = allConfigs.Where((OwnConfigEntryBase c) => c.SynchronizedConfig).ToDictionary((OwnConfigEntryBase c) => c.BaseConfig.Definition.Section + "_" + c.BaseConfig.Definition.Key, (OwnConfigEntryBase c) => c); Dictionary<string, CustomSyncedValueBase> dictionary2 = allCustomValues.ToDictionary((CustomSyncedValueBase c) => c.Identifier, (CustomSyncedValueBase c) => c); int num = package.ReadInt(); for (int num2 = 0; num2 < num; num2++) { string text = package.ReadString(); string text2 = package.ReadString(); string text3 = package.ReadString(); Type type = Type.GetType(text3); if (text3 == "" || type != null) { object obj; try { obj = ((text3 == "") ? null : ReadValueWithTypeFromZPackage(package, type)); } catch (InvalidDeserializationTypeException ex) { Debug.LogWarning((object)("Got unexpected struct internal type " + ex.received + " for field " + ex.field + " struct " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + ex.expected)); continue; } OwnConfigEntryBase value2; if (text == "Internal") { CustomSyncedValueBase value; if (text2 == "serverversion") { if (obj?.ToString() != CurrentVersion) { Debug.LogWarning((object)("Received server version is not equal: server version = " + (obj?.ToString() ?? "null") + "; local version = " + (CurrentVersion ?? "unknown"))); } } else if (text2 == "lockexempt") { if (obj is bool flag) { lockExempt = flag; } } else if (dictionary2.TryGetValue(text2, out value)) { if ((text3 == "" && (!value.Type.IsValueType || Nullable.GetUnderlyingType(value.Type) != null)) || GetZPackageTypeString(value.Type) == text3) { parsedConfigs.customValues[value] = obj; continue; } Debug.LogWarning((object)("Got unexpected type " + text3 + " for internal value " + text2 + " for mod " + (DisplayName ?? Name) + ", expecting " + value.Type.AssemblyQualifiedName)); } } else if (dictionary.TryGetValue(text + "_" + text2, out value2)) { Type type2 = configType(value2.BaseConfig); if ((text3 == "" && (!type2.IsValueType || Nullable.GetUnderlyingType(type2) != null)) || GetZPackageTypeString(type2) == text3) { parsedConfigs.configValues[value2] = obj; continue; } Debug.LogWarning((object)("Got unexpected type " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + type2.AssemblyQualifiedName)); } else { Debug.LogWarning((object)("Received unknown config entry " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ". This may happen if client and server versions of the mod do not match.")); } continue; } Debug.LogWarning((object)("Got invalid type " + text3 + ", abort reading of received configs")); return new ParsedConfigs(); } return parsedConfigs; } private static bool isWritableConfig(OwnConfigEntryBase config) { ConfigSync configSync = configSyncs.FirstOrDefault((ConfigSync cs) => cs.allConfigs.Contains(config)); if (configSync == null) { return true; } return configSync.IsSourceOfTruth || !config.SynchronizedConfig || config.LocalBaseValue == null || (!configSync.IsLocked && (config != configSync.lockedConfig || lockExempt)); } private void serverLockedSettingChanged() { foreach (OwnConfigEntryBase allConfig in allConfigs) { configAttribute<ConfigurationManagerAttributes>(allConfig.BaseConfig).ReadOnly = !isWritableConfig(allConfig); } } private void resetConfigsFromServer() { ConfigFile val = null; bool saveOnConfigSet = false; foreach (OwnConfigEntryBase item in allConfigs.Where((OwnConfigEntryBase config) => config.LocalBaseValue != null)) { if (val == null) { val = item.BaseConfig.ConfigFile; saveOnConfigSet = val.SaveOnConfigSet; val.SaveOnConfigSet = false; } item.BaseConfig.BoxedValue = item.LocalBaseValue; item.LocalBaseValue = null; } if (val != null) { val.SaveOnConfigSet = saveOnConfigSet; } foreach (CustomSyncedValueBase item2 in allCustomValues.Where((CustomSyncedValueBase config) => config.LocalBaseValue != null)) { item2.BoxedValue = item2.LocalBaseValue; item2.LocalBaseValue = null; }