Decompiled source of DataForge v1.0.3

DataForge.dll

Decompiled 6 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
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.Text.RegularExpressions;
using System.Threading;
using System.Timers;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("DataForge")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("DataForge")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: AssemblyFileVersion("1.0.3")]
[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]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DataForge
{
	internal static class LocalizationOverrideManager
	{
		internal sealed class LocalizationPayload
		{
			public Dictionary<string, string> All { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

			public Dictionary<string, Dictionary<string, string>> Languages { get; set; } = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
		}

		private const string DomainName = "localization";

		private const string DefaultLanguageFileName = "English.yml";

		private const string SyncedPayloadKey = "localization";

		private const long ReloadDelayTicks = 10000000L;

		private static readonly object StateLock = new object();

		private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build();

		private static readonly ISerializer Serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).DisableAliases().Build();

		private static LocalizationPayload ActivePayload = new LocalizationPayload();

		private static CustomSyncedValue<string>? SyncedPayload;

		private static FileSystemWatcher? Watcher;

		private static DataForgeFileWatcher.DebouncedAction? ReloadDebouncer;

		private static string? LastParsedPayload;

		private static readonly Dictionary<string, Dictionary<string, string?>> OriginalTranslationsByLanguage = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, HashSet<string>> AppliedKeysByLanguage = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase);

		private static string ConfigDirectory => Path.Combine(Paths.ConfigPath, "DataForge");

		private static string LocalizationDirectory => Path.Combine(ConfigDirectory, "localization");

		internal static void Initialize(ConfigSync configSync)
		{
			SyncedPayload = new CustomSyncedValue<string>(configSync, "localization", "", 100);
			SyncedPayload.ValueChanged += OnSyncedPayloadChanged;
		}

		internal static void Dispose()
		{
			if (SyncedPayload != null)
			{
				SyncedPayload.ValueChanged -= OnSyncedPayloadChanged;
			}
			Watcher?.Dispose();
			Watcher = null;
			ReloadDebouncer?.Dispose();
			ReloadDebouncer = null;
		}

		internal static void SetupFileWatcher()
		{
			if (!DataForgePlugin.UsesLocalAuthorityFiles)
			{
				Watcher?.Dispose();
				Watcher = null;
				ReloadDebouncer?.Dispose();
				ReloadDebouncer = null;
			}
			else
			{
				EnsureConfigDirectoryAndDefaultOverride();
				Watcher?.Dispose();
				ReloadDebouncer?.Dispose();
				ReloadDebouncer = DataForgeFileWatcher.CreateDebouncedAction(10000000L, ReloadYamlValues);
				Watcher = DataForgeFileWatcher.Create(ConfigDirectory, "*.*", includeSubdirectories: true, ReadYamlValues);
			}
		}

		internal static void ReloadFromDiskAndSync()
		{
			if (!DataForgePlugin.UsesLocalAuthorityFiles)
			{
				ApplySyncedPayload(SyncedPayload?.Value ?? "");
				return;
			}
			EnsureConfigDirectoryAndDefaultOverride();
			LocalizationPayload localizationPayload = LoadPayloadFromDisk();
			string text = SerializePayload(localizationPayload);
			lock (StateLock)
			{
				ActivePayload = localizationPayload;
				LastParsedPayload = text;
			}
			PublishPayload(text);
			ApplyCurrentLocalization();
		}

		internal static void ApplyCurrentLocalization()
		{
			Localization instance = Localization.instance;
			if (instance != null)
			{
				ApplyCurrentLocalization(instance, instance.GetSelectedLanguage());
			}
		}

		internal static void ApplyCurrentLocalization(Localization localization, string? language)
		{
			if (localization == null)
			{
				return;
			}
			string text = NormalizeLanguage(language);
			Dictionary<string, string> dictionary;
			lock (StateLock)
			{
				dictionary = BuildTranslationsForLanguage(ActivePayload, text);
			}
			RestoreRemovedTranslations(localization, text, dictionary.Keys);
			foreach (KeyValuePair<string, string> item in dictionary)
			{
				ApplyTranslation(localization, text, item.Key, item.Value);
			}
			AppliedKeysByLanguage[text] = new HashSet<string>(dictionary.Keys, StringComparer.OrdinalIgnoreCase);
		}

		private static void ReadYamlValues(object sender, FileSystemEventArgs e)
		{
			if (ShouldReloadForFileEvent(e))
			{
				ReloadDebouncer?.Schedule();
			}
		}

		private static void ReloadYamlValues()
		{
			try
			{
				DataForgePlugin.Log.LogDebug((object)"Reloading localization YAML files...");
				ReloadFromDiskAndSync();
				DataForgePlugin.Log.LogInfo((object)"Localization YAML reload complete.");
			}
			catch (Exception arg)
			{
				DataForgePlugin.Log.LogError((object)$"Error reloading localization YAML files: {arg}");
			}
		}

		private static bool ShouldReloadForFileEvent(FileSystemEventArgs e)
		{
			if (!DataForgePlugin.UsesLocalAuthorityFiles)
			{
				return false;
			}
			if (IsLocalizationFile(e.FullPath))
			{
				return true;
			}
			if (e is RenamedEventArgs e2)
			{
				return IsLocalizationFile(e2.OldFullPath);
			}
			return false;
		}

		private static void OnSyncedPayloadChanged()
		{
			if (!DataForgePlugin.UsesLocalAuthorityFiles)
			{
				string payload = SyncedPayload?.Value ?? "";
				DataForgeProfiler.Profile(string.Format("{0}.ApplySyncedPayload chars={1}", "localization", payload.Length), delegate
				{
					ApplySyncedPayload(payload);
				});
			}
		}

		private static void ApplySyncedPayload(string payload)
		{
			if (!string.Equals(LastParsedPayload, payload, StringComparison.Ordinal))
			{
				LocalizationPayload activePayload = DeserializePayload(payload, "synced localization payload");
				lock (StateLock)
				{
					ActivePayload = activePayload;
					LastParsedPayload = payload;
				}
			}
			ApplyCurrentLocalization();
		}

		private static void PublishPayload(string payload)
		{
			DataForgeSync.PublishPayload(SyncedPayload, "localization", payload);
		}

		private static LocalizationPayload LoadPayloadFromDisk()
		{
			LocalizationPayload localizationPayload = new LocalizationPayload();
			if (!Directory.Exists(LocalizationDirectory))
			{
				return localizationPayload;
			}
			foreach (string item in Directory.GetFiles(LocalizationDirectory, "*.yml").Concat(Directory.GetFiles(LocalizationDirectory, "*.yaml")).OrderBy<string, string>((string path) => path, StringComparer.OrdinalIgnoreCase))
			{
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(item);
				Dictionary<string, string> dictionary = LoadTranslationMap(item, fileNameWithoutExtension + " localization");
				if (dictionary.Count != 0)
				{
					if (!localizationPayload.Languages.TryGetValue(fileNameWithoutExtension, out Dictionary<string, string> value))
					{
						value = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
						localizationPayload.Languages[fileNameWithoutExtension] = value;
					}
					MergeTranslations(value, dictionary);
				}
			}
			return localizationPayload;
		}

		private static Dictionary<string, string> LoadTranslationMap(string path, string source)
		{
			if (!File.Exists(path))
			{
				return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			}
			string text = File.ReadAllText(path);
			if (string.IsNullOrWhiteSpace(text))
			{
				return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			}
			try
			{
				return NormalizeTranslationMap(Deserializer.Deserialize<Dictionary<string, string>>(text), source);
			}
			catch (Exception ex)
			{
				DataForgePlugin.Log.LogError((object)("Failed to parse " + source + " from '" + path + "': " + ex.Message));
				return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			}
		}

		private static Dictionary<string, string> NormalizeTranslationMap(Dictionary<string, string?>? map, string source)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			if (map == null)
			{
				return dictionary;
			}
			foreach (KeyValuePair<string, string> item in map)
			{
				string text = NormalizeToken(item.Key);
				if (text.Length == 0)
				{
					DataForgePlugin.Log.LogWarning((object)("Skipping localization entry with an empty token in " + source + "."));
				}
				else
				{
					dictionary[text] = item.Value ?? "";
				}
			}
			return dictionary;
		}

		private static string SerializePayload(LocalizationPayload payload)
		{
			return Serializer.Serialize(payload);
		}

		private static LocalizationPayload DeserializePayload(string payload, string source)
		{
			if (string.IsNullOrWhiteSpace(payload))
			{
				return new LocalizationPayload();
			}
			try
			{
				return NormalizePayload(Deserializer.Deserialize<LocalizationPayload>(payload), source);
			}
			catch (Exception ex)
			{
				DataForgePlugin.Log.LogError((object)("Failed to parse " + source + ": " + ex.Message));
				return new LocalizationPayload();
			}
		}

		private static LocalizationPayload NormalizePayload(LocalizationPayload? payload, string source)
		{
			LocalizationPayload localizationPayload = new LocalizationPayload();
			if (payload == null)
			{
				return localizationPayload;
			}
			MergeTranslations(localizationPayload.All, NormalizeStringTranslationMap(payload.All, source + " common"));
			foreach (KeyValuePair<string, Dictionary<string, string>> language in payload.Languages)
			{
				string text = NormalizeLanguage(language.Key);
				if (text.Length != 0)
				{
					localizationPayload.Languages[text] = NormalizeStringTranslationMap(language.Value, source + " " + text);
				}
			}
			return localizationPayload;
		}

		private static Dictionary<string, string> NormalizeStringTranslationMap(Dictionary<string, string>? map, string source)
		{
			return NormalizeTranslationMap(map?.ToDictionary<KeyValuePair<string, string>, string, string>((KeyValuePair<string, string> pair) => pair.Key, (KeyValuePair<string, string> pair) => pair.Value, StringComparer.OrdinalIgnoreCase), source);
		}

		private static Dictionary<string, string> BuildTranslationsForLanguage(LocalizationPayload payload, string language)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			MergeTranslations(dictionary, payload.All);
			if (!language.Equals("English", StringComparison.OrdinalIgnoreCase) && payload.Languages.TryGetValue("English", out Dictionary<string, string> value))
			{
				MergeTranslations(dictionary, value);
			}
			if (payload.Languages.TryGetValue(language, out Dictionary<string, string> value2))
			{
				MergeTranslations(dictionary, value2);
			}
			return dictionary;
		}

		private static void MergeTranslations(Dictionary<string, string> target, Dictionary<string, string> source)
		{
			foreach (KeyValuePair<string, string> item in source)
			{
				target[NormalizeToken(item.Key)] = item.Value;
			}
		}

		private static void ApplyTranslation(Localization localization, string language, string token, string text)
		{
			token = NormalizeToken(token);
			if (token.Length != 0)
			{
				Dictionary<string, string> originalTranslations = GetOriginalTranslations(language);
				if (!originalTranslations.ContainsKey(token))
				{
					originalTranslations[token] = (localization.m_translations.TryGetValue(token, out var value) ? value : null);
				}
				localization.m_translations[token] = text;
			}
		}

		private static void RestoreRemovedTranslations(Localization localization, string language, IEnumerable<string> currentTokens)
		{
			HashSet<string> current = new HashSet<string>(currentTokens.Select(NormalizeToken), StringComparer.OrdinalIgnoreCase);
			if (!AppliedKeysByLanguage.TryGetValue(language, out HashSet<string> value))
			{
				return;
			}
			Dictionary<string, string> originalTranslations = GetOriginalTranslations(language);
			string[] array = value.Where((string token) => !current.Contains(token)).ToArray();
			foreach (string key in array)
			{
				if (originalTranslations.TryGetValue(key, out var value2))
				{
					if (value2 == null)
					{
						localization.m_translations.Remove(key);
					}
					else
					{
						localization.m_translations[key] = value2;
					}
					originalTranslations.Remove(key);
				}
			}
		}

		private static Dictionary<string, string?> GetOriginalTranslations(string language)
		{
			if (!OriginalTranslationsByLanguage.TryGetValue(language, out Dictionary<string, string> value))
			{
				value = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
				OriginalTranslationsByLanguage[language] = value;
			}
			return value;
		}

		private static void EnsureConfigDirectoryAndDefaultOverride()
		{
			Directory.CreateDirectory(ConfigDirectory);
			Directory.CreateDirectory(LocalizationDirectory);
			string path = Path.Combine(LocalizationDirectory, "English.yml");
			if (!File.Exists(path))
			{
				File.WriteAllText(path, DefaultEnglishLocalizationTemplate());
			}
		}

		private static string DefaultEnglishLocalizationTemplate()
		{
			return string.Join(Environment.NewLine, "# DataForge server-synced localization.", "#", "# Put language files in this folder using Valheim language names:", "# English.yml, Korean.yml, Turkish.yml, German.yml, etc.", "#", "# English.yml is the fallback file. If a client uses another language,", "# DataForge first applies English.yml and then applies that client's language file.", "#", "# To use a localization key, put a token like $df_item_meadhealthtest in an override field.", "# To override text directly, put plain text in the field instead of a $ token.", "#", "# Example localization entry:", "# $df_item_meadhealthtest: \"Test item\"", "# $df_item_meadhealthtest_description: \"A test item cloned from major healing mead.\"", "#", "# Example item override:", "# - item: MeadHealthtest", "#   cloneFrom: MeadHealthMajor", "#   name: $df_item_meadhealthtest", "#   description: Direct text override without localization", "");
		}

		private static bool IsLocalizationFile(string path)
		{
			string extension = Path.GetExtension(path);
			if (!extension.Equals(".yml", StringComparison.OrdinalIgnoreCase) && !extension.Equals(".yaml", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			string fullPath = Path.GetFullPath(path);
			string fullPath2 = Path.GetFullPath(LocalizationDirectory);
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			return fullPath.StartsWith(fullPath2 + directorySeparatorChar, StringComparison.OrdinalIgnoreCase);
		}

		private static string NormalizeToken(string token)
		{
			return (token ?? "").Trim().TrimStart(new char[1] { '$' }).Trim();
		}

		private static string NormalizeLanguage(string? language)
		{
			string text = language?.Trim() ?? "";
			if (text.Length != 0)
			{
				return text;
			}
			return "English";
		}
	}
	[HarmonyPatch(typeof(Localization), "SetupLanguage")]
	internal static class DataForgeLocalizationSetupLanguagePatch
	{
		private static void Postfix(Localization __instance, string language)
		{
			LocalizationOverrideManager.ApplyCurrentLocalization(__instance, language);
		}
	}
	[BepInPlugin("sighsorry.DataForge", "DataForge", "1.0.3")]
	public class DataForgePlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			On = 1,
			Off = 0
		}

		private sealed class ConfigurationManagerAttributes
		{
			[UsedImplicitly]
			public int? Order;

			[UsedImplicitly]
			public bool? Browsable;

			[UsedImplicitly]
			public string? Category;

			[UsedImplicitly]
			public Action<ConfigEntryBase>? CustomDrawer;
		}

		internal const string ModName = "DataForge";

		internal const string ModVersion = "1.0.3";

		internal const string Author = "sighsorry";

		internal const string ModGUID = "sighsorry.DataForge";

		private static readonly string ConfigFileName = "sighsorry.DataForge.cfg";

		private static readonly string ConfigFileFullPath = Path.Combine(Paths.ConfigPath, ConfigFileName);

		private static readonly ConfigSync ConfigSync = new ConfigSync("sighsorry.DataForge")
		{
			DisplayName = "DataForge",
			CurrentVersion = "1.0.3",
			MinimumRequiredVersion = "1.0.3"
		};

		private readonly Harmony _harmony = new Harmony("sighsorry.DataForge");

		private readonly object _reloadLock = new object();

		private FileSystemWatcher? _watcher;

		private DataForgeFileWatcher.DebouncedAction? _configReloadDebouncer;

		private string? _lastConfigFileText;

		private static bool _sourceOfTruthFileModeReady;

		internal static string ConnectionError = "";

		internal static readonly ManualLogSource Log = Logger.CreateLogSource("DataForge");

		private const long ReloadDelayTicks = 10000000L;

		private const int MaxStoredFireplaceFuelLimit = 9999;

		private static ConfigEntry<Toggle> _serverConfigLocked = null;

		private static ConfigEntry<Toggle> _enableItemOverrides = null;

		private static ConfigEntry<Toggle> _enableRecipeOverrides = null;

		private static ConfigEntry<Toggle> _enableStatusEffectOverrides = null;

		private static ConfigEntry<Toggle> _enablePieceOverrides = null;

		private static ConfigEntry<int> _stackableStackMultiplier = null;

		private static ConfigEntry<float> _itemWeightMultiplier = null;

		private static ConfigEntry<Toggle> _showPieceComfortInHammer = null;

		private static ConfigEntry<Toggle> _highlightStationExtensionsInHammer = null;

		private static ConfigEntry<Toggle> _ignoreStationExtensionSpacing = null;

		private static ConfigEntry<int> _maxStoredFireplaceFuel = null;

		private static ConfigEntry<Toggle> _logStartupTimings = null;

		internal static bool IsSourceOfTruth => ConfigSync.IsSourceOfTruth;

		internal static bool UsesLocalAuthorityFiles
		{
			get
			{
				if (IsSourceOfTruth)
				{
					return !IsRemoteServerClient;
				}
				return false;
			}
		}

		internal static bool IsRemoteServerClient
		{
			get
			{
				try
				{
					return ZNet.HasServerHost() && ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer());
				}
				catch
				{
					return false;
				}
			}
		}

		internal static bool ItemOverridesEnabled => _enableItemOverrides.Value.IsOn();

		internal static bool RecipeOverridesEnabled => _enableRecipeOverrides.Value.IsOn();

		internal static bool StatusEffectOverridesEnabled => _enableStatusEffectOverrides.Value.IsOn();

		internal static bool PieceOverridesEnabled => _enablePieceOverrides.Value.IsOn();

		internal static int StackableStackMultiplier => Math.Min(10, Math.Max(1, _stackableStackMultiplier.Value));

		internal static float ItemWeightMultiplier => Math.Min(2f, Math.Max(0f, _itemWeightMultiplier.Value));

		internal static bool ShowPieceComfortInHammer => _showPieceComfortInHammer.Value.IsOn();

		internal static bool HighlightStationExtensionsInHammer => _highlightStationExtensionsInHammer.Value.IsOn();

		internal static bool IgnoreStationExtensionSpacing => _ignoreStationExtensionSpacing.Value.IsOn();

		internal static int MaxStoredFireplaceFuel => Math.Min(9999, Math.Max(0, _maxStoredFireplaceFuel.Value));

		internal static bool LogStartupTimings => _logStartupTimings.Value.IsOn();

		public void Awake()
		{
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Expected O, but got Unknown
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Expected O, but got Unknown
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			_serverConfigLocked = ConfigEntry("1 - General", "Lock Configuration", Toggle.On, "If on, the configuration is locked and can be changed by server admins only.", synchronizedSetting: true, 1000);
			ConfigSync.AddLockingConfigEntry<Toggle>(_serverConfigLocked);
			_enableItemOverrides = ConfigEntry("1 - General", "Enable Item Overrides", Toggle.On, "If on, item YAML overrides are applied to matching ObjectDB item prefabs.", synchronizedSetting: true, 900);
			_enableItemOverrides.SettingChanged += delegate
			{
				ItemOverrideManager.ApplyCurrentConfiguration();
			};
			_enableRecipeOverrides = ConfigEntry("1 - General", "Enable Recipe Overrides", Toggle.On, "If on, recipe YAML overrides are applied to ObjectDB recipes.", synchronizedSetting: true, 800);
			_enableRecipeOverrides.SettingChanged += delegate
			{
				RecipeOverrideManager.ApplyCurrentConfiguration();
			};
			_enableStatusEffectOverrides = ConfigEntry("1 - General", "Enable Status Effect Overrides", Toggle.On, "If on, status effect YAML overrides are applied to ObjectDB status effects.", synchronizedSetting: true, 700);
			_enableStatusEffectOverrides.SettingChanged += delegate
			{
				StatusEffectOverrideManager.ApplyCurrentConfiguration();
			};
			_enablePieceOverrides = ConfigEntry("1 - General", "Enable Piece Overrides", Toggle.On, "If on, piece YAML overrides are applied to matching prefabs and loaded pieces.", synchronizedSetting: true, 600);
			_enablePieceOverrides.SettingChanged += delegate
			{
				PieceOverrideManager.ApplyCurrentConfiguration();
			};
			_stackableStackMultiplier = ConfigEntry("2 - Misc", "Stackable Stack Multiplier", 1, new ConfigDescription("Integer multiplier applied to baseline max stack size for stackable items unless maxStackSize is explicitly set in item YAML. 1 disables this feature.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>()), synchronizedSetting: true, 500);
			_stackableStackMultiplier.SettingChanged += delegate
			{
				ItemOverrideManager.ApplyCurrentConfiguration();
			};
			_itemWeightMultiplier = ConfigEntry("2 - Misc", "Item Weight Multiplier", 1f, new ConfigDescription("Multiplier applied to baseline item weight for all items unless weight is explicitly set in item YAML. 1 disables this feature; 0 makes affected items weightless.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()), synchronizedSetting: true, 400);
			_itemWeightMultiplier.SettingChanged += delegate
			{
				ItemOverrideManager.ApplyCurrentConfiguration();
			};
			_showPieceComfortInHammer = ConfigEntry("2 - Misc", "Show Comfort In Hammer", Toggle.On, "If on, hammer build icons show an orange comfort value badge for pieces with comfort 1 or higher.", synchronizedSetting: false, 300);
			_showPieceComfortInHammer.SettingChanged += delegate
			{
				PieceComfortHudBadges.RefreshVisibleHud();
			};
			_highlightStationExtensionsInHammer = ConfigEntry("2 - Misc", "Highlight Station Extensions In Hammer", Toggle.On, "If on, hovering a crafting station or station extension in the hammer tab highlights related station/extension pieces in pale cyan. This setting is client-side only.", synchronizedSetting: false, 250);
			_highlightStationExtensionsInHammer.SettingChanged += delegate
			{
				PieceComfortHudBadges.RefreshVisibleHud();
			};
			_ignoreStationExtensionSpacing = ConfigEntry("2 - Misc", "Ignore Station Extension Spacing", Toggle.On, "If on, station extensions ignore the vanilla spacing check against other station extensions, allowing close or overlapping extension placement. Other placement restrictions remain unchanged.", synchronizedSetting: true, 200);
			_maxStoredFireplaceFuel = ConfigEntry("2 - Misc", "maxStoredFuel", 100, $"Maximum stored fuel allowed in fireplaces without changing each fireplace's displayed max fuel. 0 disables this feature. Values are clamped to 0-{9999}. If this value is not greater than a fireplace's max fuel, that fireplace uses vanilla behavior.", synchronizedSetting: true, 100);
			_maxStoredFireplaceFuel.SettingChanged += delegate
			{
				ClampMaxStoredFireplaceFuel();
			};
			ClampMaxStoredFireplaceFuel();
			_logStartupTimings = ConfigEntry("2 - Misc", "Log Startup Timings", Toggle.Off, "If on, logs a lobby-to-world connection timeline plus DataForge synced payload parsing and world-data apply timings. Use only while diagnosing connection or loading delays.", synchronizedSetting: false, 50);
			LocalizationOverrideManager.Initialize(ConfigSync);
			StatusEffectOverrideManager.Initialize(ConfigSync);
			ItemOverrideManager.Initialize(ConfigSync);
			RecipeOverrideManager.Initialize(ConfigSync);
			PieceOverrideManager.Initialize(ConfigSync);
			ConfigSync.SourceOfTruthChanged += OnSourceOfTruthChanged;
			DataForgeConsoleCommands.Register();
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			_harmony.PatchAll(executingAssembly);
			SetupWatcher();
			((BaseUnityPlugin)this).Config.Save();
			_lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath);
			if (saveOnConfigSet)
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private void OnDestroy()
		{
			SaveWithRespectToConfigSet();
			_watcher?.Dispose();
			_watcher = null;
			_configReloadDebouncer?.Dispose();
			_configReloadDebouncer = null;
			LocalizationOverrideManager.Dispose();
			StatusEffectOverrideManager.Dispose();
			ItemOverrideManager.Dispose();
			RecipeOverrideManager.Dispose();
			PieceOverrideManager.Dispose();
			ConfigSync.SourceOfTruthChanged -= OnSourceOfTruthChanged;
			_harmony.UnpatchSelf();
		}

		private static void OnSourceOfTruthChanged(bool isSourceOfTruth)
		{
			if (isSourceOfTruth)
			{
				EnsureSourceOfTruthFileMode();
				return;
			}
			_sourceOfTruthFileModeReady = false;
			LocalizationOverrideManager.SetupFileWatcher();
			StatusEffectOverrideManager.SetupFileWatcher();
			ItemOverrideManager.SetupFileWatcher();
			RecipeOverrideManager.SetupFileWatcher();
			PieceOverrideManager.SetupFileWatcher();
		}

		internal static void EnsureSourceOfTruthFileMode()
		{
			if (UsesLocalAuthorityFiles && !_sourceOfTruthFileModeReady)
			{
				_sourceOfTruthFileModeReady = true;
				LocalizationOverrideManager.SetupFileWatcher();
				StatusEffectOverrideManager.SetupFileWatcher();
				ItemOverrideManager.SetupFileWatcher();
				RecipeOverrideManager.SetupFileWatcher();
				PieceOverrideManager.SetupFileWatcher();
				LocalizationOverrideManager.ReloadFromDiskAndSync();
				StatusEffectOverrideManager.ReloadFromDiskAndSync();
				ItemOverrideManager.ReloadFromDiskAndSync();
				RecipeOverrideManager.ReloadFromDiskAndSync();
				PieceOverrideManager.ReloadFromDiskAndSync();
			}
		}

		private void Update()
		{
			VneiPrefabCleanupGuard.TryPatchVneiIndexAll(_harmony);
		}

		private static void ClampMaxStoredFireplaceFuel()
		{
			int num = Math.Min(9999, Math.Max(0, _maxStoredFireplaceFuel.Value));
			if (_maxStoredFireplaceFuel.Value != num)
			{
				_maxStoredFireplaceFuel.Value = num;
			}
		}

		private void SetupWatcher()
		{
			_watcher?.Dispose();
			_configReloadDebouncer?.Dispose();
			_configReloadDebouncer = DataForgeFileWatcher.CreateDebouncedAction(10000000L, ReloadConfigValues);
			_watcher = DataForgeFileWatcher.Create(Paths.ConfigPath, ConfigFileName, includeSubdirectories: false, ReadConfigValues);
		}

		private void ReadConfigValues(object sender, FileSystemEventArgs e)
		{
			_configReloadDebouncer?.Schedule();
		}

		private void ReloadConfigValues()
		{
			lock (_reloadLock)
			{
				if (!File.Exists(ConfigFileFullPath))
				{
					Log.LogWarning((object)"Config file does not exist. Skipping reload.");
					return;
				}
				try
				{
					string b = ReadFileTextIfExists(ConfigFileFullPath);
					if (string.Equals(_lastConfigFileText, b, StringComparison.Ordinal))
					{
						Log.LogDebug((object)"Skipping configuration reload because the config file content did not change.");
						return;
					}
					Log.LogDebug((object)"Reloading configuration...");
					SaveWithRespectToConfigSet(reload: true);
					_lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath);
					StatusEffectOverrideManager.ApplyCurrentConfiguration();
					ItemOverrideManager.ApplyCurrentConfiguration();
					RecipeOverrideManager.ApplyCurrentConfiguration();
					PieceOverrideManager.ApplyCurrentConfiguration();
					Log.LogInfo((object)"Configuration reload complete.");
				}
				catch (Exception arg)
				{
					Log.LogError((object)$"Error reloading configuration: {arg}");
				}
			}
		}

		private void SaveWithRespectToConfigSet(bool reload = false)
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			if (reload)
			{
				((BaseUnityPlugin)this).Config.Reload();
			}
			else
			{
				((BaseUnityPlugin)this).Config.Save();
			}
			if (saveOnConfigSet)
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private static string? ReadFileTextIfExists(string path)
		{
			try
			{
				return File.Exists(path) ? File.ReadAllText(path) : null;
			}
			catch (IOException)
			{
				return null;
			}
			catch (UnauthorizedAccessException)
			{
				return null;
			}
		}

		private ConfigEntry<T> ConfigEntry<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true, int? order = null)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			object[] array = description.Tags ?? Array.Empty<object>();
			if (order.HasValue)
			{
				object[] array2 = new object[array.Length + 1];
				Array.Copy(array, array2, array.Length);
				array2[array.Length] = new ConfigurationManagerAttributes
				{
					Order = order.Value
				};
				array = array2;
			}
			ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, array);
			ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val);
			ConfigSync.AddConfigEntry<T>(val2).SynchronizedConfig = synchronizedSetting;
			return val2;
		}

		private ConfigEntry<T> ConfigEntry<T>(string group, string name, T value, string description, bool synchronizedSetting = true, int? order = null)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			return ConfigEntry(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting, order);
		}
	}
	public static class ToggleExtensions
	{
		public static bool IsOn(this DataForgePlugin.Toggle value)
		{
			return value == DataForgePlugin.Toggle.On;
		}

		public static bool IsOff(this DataForgePlugin.Toggle value)
		{
			return value == DataForgePlugin.Toggle.Off;
		}
	}
	internal static class DataForgeConsoleCommands
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__WriteFullScaffoldFiles;

			public static ConsoleOptionsFetcher <1>__GetFullTabOptions;
		}

		private const string WriteFullCommandName = "dataforge:full";

		private static readonly List<string> FullTabOptions = new List<string> { "item", "recipe", "effect", "piece", "all" };

		private static bool _registered;

		internal static void Register()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			if (!_registered)
			{
				_registered = true;
				object obj = <>O.<0>__WriteFullScaffoldFiles;
				if (obj == null)
				{
					ConsoleEvent val = WriteFullScaffoldFiles;
					<>O.<0>__WriteFullScaffoldFiles = val;
					obj = (object)val;
				}
				object obj2 = <>O.<1>__GetFullTabOptions;
				if (obj2 == null)
				{
					ConsoleOptionsFetcher val2 = GetFullTabOptions;
					<>O.<1>__GetFullTabOptions = val2;
					obj2 = (object)val2;
				}
				new ConsoleCommand("dataforge:full", "Write DataForge full scaffold YAML files with explicit defaults. Usage: dataforge:full [item|recipe|effect|piece|all]", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false);
			}
		}

		private static List<string> GetFullTabOptions()
		{
			return FullTabOptions;
		}

		private static void WriteFullScaffoldFiles(ConsoleEventArgs args)
		{
			if (!TryParseScope(args, out var includeItem, out var includeRecipe, out var includeEffect, out var includePiece))
			{
				return;
			}
			if (includeItem)
			{
				if (ItemOverrideManager.TryWriteFullScaffoldConfigurationFile(out string path, out string error))
				{
					Terminal context = args.Context;
					if (context != null)
					{
						context.AddString("Wrote item full scaffold to " + path);
					}
				}
				else
				{
					Terminal context2 = args.Context;
					if (context2 != null)
					{
						context2.AddString(error);
					}
				}
			}
			if (includeRecipe)
			{
				if (RecipeOverrideManager.TryWriteFullScaffoldConfigurationFile(out string path2, out string error2))
				{
					Terminal context3 = args.Context;
					if (context3 != null)
					{
						context3.AddString("Wrote recipe full scaffold to " + path2);
					}
				}
				else
				{
					Terminal context4 = args.Context;
					if (context4 != null)
					{
						context4.AddString(error2);
					}
				}
			}
			if (includeEffect)
			{
				if (StatusEffectOverrideManager.TryWriteFullScaffoldConfigurationFile(out string path3, out string error3))
				{
					Terminal context5 = args.Context;
					if (context5 != null)
					{
						context5.AddString("Wrote effect full scaffold to " + path3);
					}
				}
				else
				{
					Terminal context6 = args.Context;
					if (context6 != null)
					{
						context6.AddString(error3);
					}
				}
			}
			if (!includePiece)
			{
				return;
			}
			if (PieceOverrideManager.TryWriteFullScaffoldConfigurationFile(out string path4, out string error4))
			{
				Terminal context7 = args.Context;
				if (context7 != null)
				{
					context7.AddString("Wrote piece full scaffold to " + path4);
				}
			}
			else
			{
				Terminal context8 = args.Context;
				if (context8 != null)
				{
					context8.AddString(error4);
				}
			}
		}

		private static bool TryParseScope(ConsoleEventArgs args, out bool includeItem, out bool includeRecipe, out bool includeEffect, out bool includePiece)
		{
			string text = ((args.Length >= 2) ? (args[1] ?? "").Trim().ToLowerInvariant() : "all");
			if (text.Length == 0)
			{
				text = "all";
			}
			switch (text)
			{
			case "all":
				includeItem = true;
				includeRecipe = true;
				includeEffect = true;
				includePiece = true;
				return true;
			case "items":
			case "item":
				includeItem = true;
				includeRecipe = false;
				includeEffect = false;
				includePiece = false;
				return true;
			case "recipe":
			case "recipes":
				includeItem = false;
				includeRecipe = true;
				includeEffect = false;
				includePiece = false;
				return true;
			case "effect":
			case "effects":
				includeItem = false;
				includeRecipe = false;
				includeEffect = true;
				includePiece = false;
				return true;
			case "piece":
			case "pieces":
				includeItem = false;
				includeRecipe = false;
				includeEffect = false;
				includePiece = true;
				return true;
			default:
			{
				includeItem = false;
				includeRecipe = false;
				includeEffect = false;
				includePiece = false;
				Terminal context = args.Context;
				if (context != null)
				{
					context.AddString("Syntax: dataforge:full [item|recipe|effect|piece|all]");
				}
				return false;
			}
			}
		}
	}
	internal static class DataForgeLogContext
	{
		private sealed class PopWhenDisposed : IDisposable
		{
			private readonly string? previous;

			private bool disposed;

			internal PopWhenDisposed(string? previous)
			{
				this.previous = previous;
			}

			public void Dispose()
			{
				if (!disposed)
				{
					CurrentContext = previous;
					disposed = true;
				}
			}
		}

		[ThreadStatic]
		private static string? CurrentContext;

		internal static IDisposable Push(string? context)
		{
			string currentContext = CurrentContext;
			CurrentContext = (string.IsNullOrWhiteSpace(context) ? currentContext : context);
			return new PopWhenDisposed(currentContext);
		}

		internal static string FormatSource(string source, int entryIndex)
		{
			string text = source?.Trim() ?? "";
			string text2 = ((text.Length == 0) ? "unknown source" : Path.GetFileName(text));
			if (text2.Length == 0)
			{
				text2 = text;
			}
			return $"{text2}#{entryIndex}";
		}

		internal static void Warning(string message)
		{
			DataForgePlugin.Log.LogWarning((object)WithContext(message));
		}

		private static string WithContext(string message)
		{
			if (!string.IsNullOrWhiteSpace(CurrentContext))
			{
				return CurrentContext + ": " + message;
			}
			return message;
		}
	}
	internal static class DataForgeReferenceSections
	{
		private sealed class GroupedEntry<TSource>
		{
			public TSource Entry { get; set; }

			public string SortKey { get; set; } = "";

			public string OwnerName { get; set; } = "Unknown / Untracked";
		}

		internal const string VanillaOwnerName = "Valheim";

		internal const string UnknownOwnerName = "Unknown / Untracked";

		internal static string SerializeReferenceSections<TSource, TOutput>(IEnumerable<TSource> entries, Func<TSource, string> getSortKey, Func<TSource, string> getOwnerName, Func<TSource, TOutput> getOutput, ISerializer serializer)
		{
			List<IGrouping<string, GroupedEntry<TSource>>> list = (from entry in entries.Select(delegate(TSource entry)
				{
					string text = (getOwnerName(entry) ?? "").Trim();
					return new GroupedEntry<TSource>
					{
						Entry = entry,
						SortKey = (getSortKey(entry) ?? "").Trim(),
						OwnerName = ((text.Length > 0) ? text : "Unknown / Untracked")
					};
				})
				orderby GetOwnerSortBucket(entry.OwnerName)
				select entry).ThenBy<GroupedEntry<TSource>, string>((GroupedEntry<TSource> entry) => entry.OwnerName, StringComparer.OrdinalIgnoreCase).ThenBy<GroupedEntry<TSource>, string>((GroupedEntry<TSource> entry) => entry.SortKey, StringComparer.OrdinalIgnoreCase).GroupBy<GroupedEntry<TSource>, string>((GroupedEntry<TSource> entry) => entry.OwnerName, StringComparer.OrdinalIgnoreCase)
				.ToList();
			StringBuilder stringBuilder = new StringBuilder();
			bool flag = false;
			foreach (IGrouping<string, GroupedEntry<TSource>> item in list)
			{
				if (flag)
				{
					stringBuilder.AppendLine();
				}
				AppendSectionHeaderComment(stringBuilder, item.Key);
				foreach (GroupedEntry<TSource> item2 in item)
				{
					string value = CollapseScalarBlockListsToInlineLists(serializer.Serialize(new TOutput[1] { getOutput(item2.Entry) }).TrimEnd('\r', '\n'));
					stringBuilder.AppendLine(value);
				}
				flag = true;
			}
			if (!flag)
			{
				return "[]" + Environment.NewLine;
			}
			return stringBuilder.ToString();
		}

		private static void AppendSectionHeaderComment(StringBuilder builder, string ownerName)
		{
			builder.Append("# ===== ");
			builder.Append(string.IsNullOrWhiteSpace(ownerName) ? "Unknown / Untracked" : ownerName.Trim());
			builder.AppendLine(" =====");
		}

		private static int GetOwnerSortBucket(string ownerName)
		{
			if (string.Equals(ownerName, "Valheim", StringComparison.OrdinalIgnoreCase))
			{
				return 0;
			}
			if (!string.Equals(ownerName, "Unknown / Untracked", StringComparison.OrdinalIgnoreCase))
			{
				return 1;
			}
			return 2;
		}

		private static string CollapseScalarBlockListsToInlineLists(string yaml)
		{
			if (string.IsNullOrWhiteSpace(yaml) || yaml.IndexOf("- ", StringComparison.Ordinal) < 0)
			{
				return yaml;
			}
			string[] array = yaml.Replace("\r\n", "\n").Split(new char[1] { '\n' });
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < array.Length; i++)
			{
				if (TryCollapseScalarBlockList(array, ref i, out string collapsedLine))
				{
					stringBuilder.AppendLine(collapsedLine);
				}
				else
				{
					stringBuilder.AppendLine(array[i]);
				}
			}
			return stringBuilder.ToString().TrimEnd('\r', '\n');
		}

		private static bool TryCollapseScalarBlockList(string[] lines, ref int index, out string collapsedLine)
		{
			collapsedLine = "";
			string text = lines[index];
			int num = text.IndexOf(':');
			if (num < 0 || num != text.Length - 1)
			{
				return false;
			}
			int num2 = index + 1;
			if (num2 >= lines.Length)
			{
				return false;
			}
			int firstNonWhitespaceIndex = GetFirstNonWhitespaceIndex(text);
			int firstNonWhitespaceIndex2 = GetFirstNonWhitespaceIndex(lines[num2]);
			if (firstNonWhitespaceIndex < 0 || firstNonWhitespaceIndex2 <= firstNonWhitespaceIndex || !lines[num2].TrimStart(Array.Empty<char>()).StartsWith("- ", StringComparison.Ordinal))
			{
				return false;
			}
			List<string> list = new List<string>();
			int i;
			for (i = num2; i < lines.Length; i++)
			{
				string text2 = lines[i];
				if (GetFirstNonWhitespaceIndex(text2) != firstNonWhitespaceIndex2 || !text2.TrimStart(Array.Empty<char>()).StartsWith("- ", StringComparison.Ordinal))
				{
					break;
				}
				string text3 = text2.TrimStart(Array.Empty<char>()).Substring(2).Trim();
				if (text3.Length == 0 || Enumerable.Contains(text3, ':') || Enumerable.Contains(text3, ','))
				{
					return false;
				}
				list.Add(text3);
			}
			if (list.Count == 0)
			{
				return false;
			}
			collapsedLine = text + " [" + string.Join(", ", list) + "]";
			index = i - 1;
			return true;
		}

		private static int GetFirstNonWhitespaceIndex(string line)
		{
			for (int i = 0; i < line.Length; i++)
			{
				if (!char.IsWhiteSpace(line[i]))
				{
					return i;
				}
			}
			return -1;
		}
	}
	internal static class DataForgeOwnerResolver
	{
		internal static string GetPrefabOwnerName(string? prefabName)
		{
			string text = NormalizeName(prefabName);
			if (text.Length == 0)
			{
				return "Unknown / Untracked";
			}
			foreach (string item in EnumerateLookupCandidates(text))
			{
				if (DataForgeVanillaAssetCatalog.IsVanillaPrefab(item))
				{
					return "Valheim";
				}
			}
			return DataForgeAssetOwnerCatalog.GetOwnerName(text);
		}

		internal static string GetAssetOwnerName(string? assetName)
		{
			string text = NormalizeName(assetName);
			if (text.Length == 0)
			{
				return "Unknown / Untracked";
			}
			foreach (string item in EnumerateLookupCandidates(text))
			{
				if (DataForgeVanillaAssetCatalog.IsVanillaAsset(item))
				{
					return "Valheim";
				}
			}
			return DataForgeAssetOwnerCatalog.GetOwnerName(text);
		}

		private static IEnumerable<string> EnumerateLookupCandidates(string normalizedName)
		{
			HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			AddIfNew(normalizedName);
			AddIfNew(TrimCloneSuffix(normalizedName));
			int num = normalizedName.IndexOf(':');
			if (num > 0)
			{
				AddIfNew(normalizedName.Substring(0, num));
			}
			foreach (string item in seen)
			{
				yield return item;
			}
			void AddIfNew(string candidate)
			{
				string text = NormalizeName(candidate);
				if (text.Length > 0)
				{
					seen.Add(text);
				}
			}
		}

		private static string NormalizeName(string? name)
		{
			return (name ?? "").Replace("(Clone)", "").Trim();
		}

		private static string TrimCloneSuffix(string name)
		{
			if (!name.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				return name;
			}
			return name.Substring(0, name.Length - "(Clone)".Length).TrimEnd(Array.Empty<char>());
		}
	}
	internal static class DataForgeVanillaAssetCatalog
	{
		private enum CatalogState
		{
			Uninitialized,
			Loaded,
			Unavailable
		}

		private static readonly object Sync = new object();

		private static readonly HashSet<string> PrefabNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static readonly HashSet<string> AssetNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static CatalogState _state;

		internal static bool IsVanillaPrefab(string prefabName)
		{
			EnsureLoaded();
			if (_state == CatalogState.Loaded && !string.IsNullOrWhiteSpace(prefabName))
			{
				return PrefabNames.Contains(prefabName);
			}
			return false;
		}

		internal static bool IsVanillaAsset(string assetName)
		{
			EnsureLoaded();
			if (_state == CatalogState.Loaded && !string.IsNullOrWhiteSpace(assetName))
			{
				return AssetNames.Contains(assetName);
			}
			return false;
		}

		private static void EnsureLoaded()
		{
			if (_state != CatalogState.Uninitialized)
			{
				return;
			}
			lock (Sync)
			{
				if (_state != CatalogState.Uninitialized)
				{
					return;
				}
				string text = Path.Combine(Application.dataPath, "StreamingAssets", "SoftRef", "manifest_extended");
				if (!File.Exists(text))
				{
					_state = CatalogState.Unavailable;
					DataForgePlugin.Log.LogWarning((object)("Vanilla asset manifest was not found at '" + text + "'. Reference owner sections may place unmapped entries under 'Unknown / Untracked'."));
					return;
				}
				foreach (string item in File.ReadLines(text))
				{
					int num = item.IndexOf("path in bundle:", StringComparison.OrdinalIgnoreCase);
					if (num < 0)
					{
						continue;
					}
					string text2 = item.Substring(num + "path in bundle:".Length).Trim();
					string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text2);
					if (!string.IsNullOrWhiteSpace(fileNameWithoutExtension))
					{
						if (text2.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase))
						{
							PrefabNames.Add(fileNameWithoutExtension);
						}
						else if (text2.EndsWith(".asset", StringComparison.OrdinalIgnoreCase))
						{
							AssetNames.Add(fileNameWithoutExtension);
						}
					}
				}
				_state = CatalogState.Loaded;
				DataForgePlugin.Log.LogDebug((object)$"Loaded {PrefabNames.Count} vanilla prefab names and {AssetNames.Count} vanilla asset names from '{text}'.");
			}
		}
	}
	internal static class DataForgeAssetOwnerCatalog
	{
		private sealed class PluginResourceSnapshot
		{
			public string OwnerName { get; set; } = "";

			public string PluginName { get; set; } = "";

			public string PluginGuid { get; set; } = "";

			public string AssemblyName { get; set; } = "";

			public string[] ResourceNames { get; set; } = Array.Empty<string>();
		}

		private static readonly object Sync = new object();

		private static readonly Dictionary<string, string> AssetOwners = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private static string _loadedSignature = "";

		internal static string GetOwnerName(string assetName)
		{
			EnsureMappingsLoaded();
			foreach (string item in EnumerateLookupCandidates(assetName))
			{
				if (AssetOwners.TryGetValue(item, out string value) && !string.IsNullOrWhiteSpace(value))
				{
					return value;
				}
			}
			return "Unknown / Untracked";
		}

		private static void EnsureMappingsLoaded()
		{
			string text = BuildSignature();
			if (string.Equals(text, _loadedSignature, StringComparison.Ordinal))
			{
				return;
			}
			lock (Sync)
			{
				if (string.Equals(text, _loadedSignature, StringComparison.Ordinal))
				{
					return;
				}
				AssetOwners.Clear();
				List<PluginResourceSnapshot> pluginResources = GetPluginResources();
				foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles())
				{
					string text2 = ((Object)allLoadedAssetBundle).name ?? "";
					if (text2.Length == 0)
					{
						continue;
					}
					string value = ResolveOwnerName(text2, pluginResources);
					if (string.IsNullOrWhiteSpace(value))
					{
						continue;
					}
					string[] allAssetNames = allLoadedAssetBundle.GetAllAssetNames();
					foreach (string text3 in allAssetNames)
					{
						if (text3.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase) || text3.EndsWith(".asset", StringComparison.OrdinalIgnoreCase))
						{
							string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text3);
							if (!string.IsNullOrWhiteSpace(fileNameWithoutExtension))
							{
								AssetOwners[fileNameWithoutExtension] = value;
							}
						}
					}
				}
				_loadedSignature = text;
				DataForgePlugin.Log.LogDebug((object)$"Tracked {AssetOwners.Count} mod asset owner mapping(s) for reference sections.");
			}
		}

		private static IEnumerable<string> EnumerateLookupCandidates(string assetName)
		{
			string normalizedName = (assetName ?? "").Replace("(Clone)", "").Trim();
			if (normalizedName.Length != 0)
			{
				yield return normalizedName;
				int num = normalizedName.IndexOf(':');
				if (num > 0)
				{
					yield return normalizedName.Substring(0, num);
				}
			}
		}

		private static List<PluginResourceSnapshot> GetPluginResources()
		{
			return (from plugin in Chainloader.PluginInfos.Values.Select(delegate(PluginInfo pluginInfo)
				{
					string text = (pluginInfo.Metadata.Name ?? "").Trim();
					string text2 = (pluginInfo.Metadata.GUID ?? "").Trim();
					string assemblyName = "";
					string[] resourceNames = Array.Empty<string>();
					try
					{
						assemblyName = ((object)pluginInfo.Instance)?.GetType().Assembly.GetName().Name ?? "";
						resourceNames = ((object)pluginInfo.Instance)?.GetType().Assembly.GetManifestResourceNames() ?? Array.Empty<string>();
					}
					catch
					{
					}
					return new PluginResourceSnapshot
					{
						OwnerName = ((text.Length > 0) ? text : text2),
						PluginName = text,
						PluginGuid = text2,
						AssemblyName = assemblyName,
						ResourceNames = resourceNames
					};
				})
				where plugin.OwnerName.Length > 0
				select plugin).ToList();
		}

		private static string ResolveOwnerName(string bundleName, List<PluginResourceSnapshot> plugins)
		{
			PluginResourceSnapshot pluginResourceSnapshot = plugins.FirstOrDefault((PluginResourceSnapshot plugin) => plugin.ResourceNames.Any((string resourceName) => resourceName.EndsWith(bundleName, StringComparison.OrdinalIgnoreCase)));
			if (pluginResourceSnapshot != null)
			{
				return pluginResourceSnapshot.OwnerName;
			}
			string normalizedBundleName = NormalizeToken(Path.GetFileNameWithoutExtension(bundleName));
			if (normalizedBundleName.Length == 0)
			{
				return "";
			}
			return plugins.FirstOrDefault(delegate(PluginResourceSnapshot plugin)
			{
				string pluginToken = NormalizeToken(plugin.PluginName);
				string pluginToken2 = NormalizeToken(plugin.PluginGuid);
				string pluginToken3 = NormalizeToken(plugin.AssemblyName);
				return IsTokenMatch(normalizedBundleName, pluginToken) || IsTokenMatch(normalizedBundleName, pluginToken2) || IsTokenMatch(normalizedBundleName, pluginToken3);
			})?.OwnerName ?? "";
		}

		private static bool IsTokenMatch(string bundleName, string pluginToken)
		{
			if (pluginToken.Length > 0)
			{
				if (bundleName.IndexOf(pluginToken, StringComparison.OrdinalIgnoreCase) < 0)
				{
					return pluginToken.IndexOf(bundleName, StringComparison.OrdinalIgnoreCase) >= 0;
				}
				return true;
			}
			return false;
		}

		private static string NormalizeToken(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder();
			foreach (char c in value)
			{
				if (char.IsLetterOrDigit(c))
				{
					stringBuilder.Append(char.ToLowerInvariant(c));
				}
			}
			return stringBuilder.ToString();
		}

		private static string BuildSignature()
		{
			IEnumerable<string> values = (from bundle in AssetBundle.GetAllLoadedAssetBundles()
				select ((Object)bundle).name ?? "" into name
				where name.Length > 0
				select name).OrderBy<string, string>((string name) => name, StringComparer.OrdinalIgnoreCase);
			IEnumerable<string> values2 = Chainloader.PluginInfos.Values.Select(delegate(PluginInfo pluginInfo)
			{
				string text = pluginInfo.Metadata.Name ?? "";
				string text2 = pluginInfo.Metadata.GUID ?? "";
				string text3 = "";
				try
				{
					text3 = ((object)pluginInfo.Instance)?.GetType().Assembly.GetName().Name ?? "";
				}
				catch
				{
				}
				return text2 + ":" + text + ":" + text3;
			}).OrderBy<string, string>((string token) => token, StringComparer.OrdinalIgnoreCase);
			return string.Join("|", values) + "||" + string.Join("|", values2);
		}
	}
	internal readonly struct DataForgeItemSortGroup
	{
		internal int BigGroupRank { get; }

		internal int SubGroupRank { get; }

		internal int DetailRank { get; }

		internal DataForgeItemSortGroup(int bigGroupRank, int subGroupRank, int detailRank)
		{
			BigGroupRank = bigGroupRank;
			SubGroupRank = subGroupRank;
			DetailRank = detailRank;
		}
	}
	internal static class DataForgeItemSortClassifier
	{
		private const int Melee = 0;

		private const int Ranged = 1;

		private const int Magic = 2;

		private const int Equipment = 3;

		private const int Food = 4;

		private const int Consumable = 5;

		private const int Meadbase = 6;

		private const int Misc = 7;

		private const int Unknown = 8;

		internal static DataForgeItemSortGroup Classify(string? itemName, SharedData? shared)
		{
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Invalid comparison between Unknown and I4
			if (shared == null)
			{
				return ClassifyFallback(itemName);
			}
			if (MatchElementalMagic(shared))
			{
				return Group(2, 0);
			}
			if (MatchBloodMagic(shared))
			{
				return Group(2, 1);
			}
			if (MatchBow(itemName, shared))
			{
				return Group(1, 0);
			}
			if (MatchArrow(itemName, shared))
			{
				return Group(1, 1);
			}
			if (MatchCrossbow(itemName, shared))
			{
				return Group(1, 2);
			}
			if (MatchBolt(itemName, shared))
			{
				return Group(1, 3);
			}
			if (MatchAmmo(shared))
			{
				return Group(1, 4);
			}
			if (MatchBomb(shared))
			{
				return Group(1, 5);
			}
			if (MatchSword(itemName, shared))
			{
				return Group(0, 0, SwordDetail(itemName, shared));
			}
			if (MatchAxe(itemName, shared))
			{
				return Group(0, 1, AxeDetail(itemName, shared));
			}
			if (MatchClub(itemName, shared))
			{
				return Group(0, 2, ClubDetail(itemName, shared));
			}
			if (MatchKnife(itemName, shared))
			{
				return Group(0, 3);
			}
			if (MatchSpear(itemName, shared))
			{
				return Group(0, 4);
			}
			if (MatchPolearm(itemName, shared))
			{
				return Group(0, 5);
			}
			if (MatchFists(itemName, shared))
			{
				return Group(0, 6);
			}
			if (MatchShield(itemName, shared))
			{
				return Group(0, 7, ShieldDetail(itemName));
			}
			if (MatchPickaxe(itemName, shared))
			{
				return Group(0, 8);
			}
			if (MatchTool(itemName, shared))
			{
				return Group(0, 9);
			}
			if (MatchHelmet(shared))
			{
				return Group(3, 0);
			}
			if (MatchChest(shared))
			{
				return Group(3, 1);
			}
			if (MatchLegs(shared))
			{
				return Group(3, 2);
			}
			if (MatchCape(shared))
			{
				return Group(3, 3);
			}
			if (MatchUtility(shared))
			{
				return Group(3, 4);
			}
			if (MatchTrinket(shared))
			{
				return Group(3, 5);
			}
			if (MatchFeast(itemName))
			{
				return Group(4, 3);
			}
			if (MatchNativeFood(shared, out var subGroupRank))
			{
				return Group(4, subGroupRank);
			}
			if (MatchMeadbase(itemName, shared))
			{
				return Group(6, 0);
			}
			if (MatchMead(itemName, shared))
			{
				return Group(5, 0);
			}
			if (MatchPotion(itemName, shared))
			{
				return Group(5, 1);
			}
			if ((int)shared.m_itemType == 13)
			{
				return Group(7, 0);
			}
			if (shared.m_value > 0)
			{
				return Group(7, 1);
			}
			return ClassifyFallback(itemName);
		}

		private static DataForgeItemSortGroup ClassifyFallback(string? itemName)
		{
			if (HasToken(itemName, "staff", "magic", "elemental"))
			{
				return Group(2, 0);
			}
			if (HasToken(itemName, "bloodmagic"))
			{
				return Group(2, 1);
			}
			if (HasToken(itemName, "bow") && !HasToken(itemName, "crossbow"))
			{
				return Group(1, 0);
			}
			if (HasToken(itemName, "arrow"))
			{
				return Group(1, 1);
			}
			if (HasToken(itemName, "crossbow", "arbalest"))
			{
				return Group(1, 2);
			}
			if (HasToken(itemName, "bolt"))
			{
				return Group(1, 3);
			}
			if (HasToken(itemName, "bomb"))
			{
				return Group(1, 5);
			}
			if (HasToken(itemName, "sword"))
			{
				return Group(0, 0, SwordDetail(itemName, null));
			}
			if (HasToken(itemName, "axe", "battleaxe"))
			{
				return Group(0, 1, AxeDetail(itemName, null));
			}
			if (HasToken(itemName, "club", "mace", "sledge"))
			{
				return Group(0, 2, ClubDetail(itemName, null));
			}
			if (HasToken(itemName, "knife"))
			{
				return Group(0, 3);
			}
			if (HasToken(itemName, "spear"))
			{
				return Group(0, 4);
			}
			if (HasToken(itemName, "atgeir", "polearm"))
			{
				return Group(0, 5);
			}
			if (HasToken(itemName, "fist"))
			{
				return Group(0, 6);
			}
			if (HasToken(itemName, "shield", "buckler"))
			{
				return Group(0, 7, ShieldDetail(itemName));
			}
			if (HasToken(itemName, "pickaxe"))
			{
				return Group(0, 8);
			}
			if (HasToken(itemName, "hammer", "hoe", "cultivator", "torch", "fishingrod", "tankard"))
			{
				return Group(0, 9);
			}
			if (HasToken(itemName, "helmet", "hood", "helm"))
			{
				return Group(3, 0);
			}
			if (HasToken(itemName, "chest", "cuirass", "tunic", "robe"))
			{
				return Group(3, 1);
			}
			if (HasToken(itemName, "legs", "leg", "pants", "greaves"))
			{
				return Group(3, 2);
			}
			if (HasToken(itemName, "cape", "shoulder"))
			{
				return Group(3, 3);
			}
			if (HasToken(itemName, "trinket"))
			{
				return Group(3, 5);
			}
			if (HasToken(itemName, "feast"))
			{
				return Group(4, 3);
			}
			if (HasToken(itemName, "meadbase"))
			{
				return Group(6, 0);
			}
			if (HasToken(itemName, "mead"))
			{
				return Group(5, 0);
			}
			if (HasToken(itemName, "potion"))
			{
				return Group(5, 1);
			}
			if (HasToken(itemName, "trophy"))
			{
				return Group(7, 0);
			}
			return Group(8, 0);
		}

		private static bool MatchSword(string? itemName, SharedData shared)
		{
			if (!IsSkillAttack(shared, (SkillType)1))
			{
				return HasToken(itemName, shared, "sword");
			}
			return true;
		}

		private static bool MatchAxe(string? itemName, SharedData shared)
		{
			if (!IsSkillAttack(shared, (SkillType)7))
			{
				return HasToken(itemName, shared, "axe", "battleaxe");
			}
			return true;
		}

		private static bool MatchClub(string? itemName, SharedData shared)
		{
			if (!IsSkillAttack(shared, (SkillType)3))
			{
				return HasToken(itemName, shared, "club", "mace", "sledge");
			}
			return true;
		}

		private static bool MatchKnife(string? itemName, SharedData shared)
		{
			if (!IsSkillAttack(shared, (SkillType)2))
			{
				return HasToken(itemName, shared, "knife");
			}
			return true;
		}

		private static bool MatchSpear(string? itemName, SharedData shared)
		{
			if (!IsSkillAttack(shared, (SkillType)5))
			{
				return HasToken(itemName, shared, "spear");
			}
			return true;
		}

		private static bool MatchPolearm(string? itemName, SharedData shared)
		{
			if (!IsSkillAttack(shared, (SkillType)4) && !IsItemTypeOrAttach(shared, (ItemType)20))
			{
				return HasToken(itemName, shared, "atgeir", "polearm");
			}
			return true;
		}

		private static bool MatchFists(string? itemName, SharedData shared)
		{
			if (!IsSkillAttack(shared, (SkillType)11))
			{
				return HasToken(itemName, shared, "fist");
			}
			return true;
		}

		private static bool MatchShield(string? itemName, SharedData shared)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			if (!IsItemTypeOrAttach(shared, (ItemType)5) && (int)shared.m_skillType != 6)
			{
				return HasToken(itemName, shared, "shield", "buckler");
			}
			return true;
		}

		private static bool MatchPickaxe(string? itemName, SharedData shared)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			if ((int)shared.m_skillType != 12)
			{
				return HasToken(itemName, shared, "pickaxe");
			}
			return true;
		}

		private static bool MatchTool(string? itemName, SharedData shared)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			if ((int)shared.m_itemType != 19 && !IsItemTypeOrAttach(shared, (ItemType)15) && (int)shared.m_skillType != 104 && (int)shared.m_skillType != 106)
			{
				return HasToken(itemName, shared, "hammer", "hoe", "cultivator", "torch", "fishingrod", "tankard");
			}
			return true;
		}

		private static bool MatchBow(string? itemName, SharedData shared)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Invalid comparison between Unknown and I4
			if ((int)shared.m_skillType != 14 && (int)shared.m_skillType != 9 && (int)shared.m_skillType != 10)
			{
				if ((int)shared.m_itemType != 4 && !IsSkillAttack(shared, (SkillType)8))
				{
					if (HasToken(itemName, shared, "bow"))
					{
						return !HasToken(itemName, shared, "crossbow");
					}
					return false;
				}
				return true;
			}
			return false;
		}

		private static bool MatchCrossbow(string? itemName, SharedData shared)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Invalid comparison between Unknown and I4
			if (!MatchAmmo(shared) && ((int)shared.m_skillType == 14 || HasToken(itemName, shared, "crossbow", "arbalest")))
			{
				if ((int)shared.m_itemType != 4 && !HasAttackAnimation(shared))
				{
					return HasToken(itemName, shared, "crossbow", "arbalest");
				}
				return true;
			}
			return false;
		}

		private static bool MatchArrow(string? itemName, SharedData shared)
		{
			if (!HasAttackAnimation(shared) && (string.Equals(shared.m_ammoType, "$ammo_arrows", StringComparison.Ordinal) || HasToken(itemName, shared, "arrow")))
			{
				return GetTotalDamage(shared) > 0f;
			}
			return false;
		}

		private static bool MatchBolt(string? itemName, SharedData shared)
		{
			if (!HasAttackAnimation(shared) && (string.Equals(shared.m_ammoType, "$ammo_bolts", StringComparison.Ordinal) || HasToken(itemName, shared, "bolt")))
			{
				return GetTotalDamage(shared) > 0f;
			}
			return false;
		}

		private static bool MatchAmmo(SharedData shared)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			if ((int)shared.m_itemType != 9)
			{
				return (int)shared.m_itemType == 23;
			}
			return true;
		}

		private static bool MatchBomb(SharedData shared)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			if ((int)shared.m_itemType == 3 && (int)shared.m_animationState == 0)
			{
				Attack attack = shared.m_attack;
				if (attack != null && (int)attack.m_attackType == 2)
				{
					return string.Equals(shared.m_attack?.m_attackAnimation, "throw_bomb", StringComparison.Ordinal);
				}
			}
			return false;
		}

		private static bool MatchElementalMagic(SharedData shared)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			return (int)shared.m_skillType == 9;
		}

		private static bool MatchBloodMagic(SharedData shared)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			return (int)shared.m_skillType == 10;
		}

		private static bool MatchHelmet(SharedData shared)
		{
			return IsItemTypeOrAttach(shared, (ItemType)6);
		}

		private static bool MatchChest(SharedData shared)
		{
			return IsItemTypeOrAttach(shared, (ItemType)7);
		}

		private static bool MatchLegs(SharedData shared)
		{
			return IsItemTypeOrAttach(shared, (ItemType)11);
		}

		private static bool MatchCape(SharedData shared)
		{
			return IsItemTypeOrAttach(shared, (ItemType)17);
		}

		private static bool MatchUtility(SharedData shared)
		{
			return IsItemTypeOrAttach(shared, (ItemType)18);
		}

		private static bool MatchTrinket(SharedData shared)
		{
			return IsItemTypeOrAttach(shared, (ItemType)24);
		}

		private static bool MatchNativeFood(SharedData shared, out int subGroupRank)
		{
			SharedData foodSharedData = GetFoodSharedData(shared);
			subGroupRank = 0;
			if (!HasFoodCarrier(shared) || (foodSharedData.m_food <= 0f && foodSharedData.m_foodStamina <= 0f && foodSharedData.m_foodEitr <= 0f))
			{
				return false;
			}
			if (foodSharedData.m_food >= foodSharedData.m_foodStamina && foodSharedData.m_food >= foodSharedData.m_foodEitr)
			{
				subGroupRank = 0;
				return true;
			}
			if (foodSharedData.m_foodStamina >= foodSharedData.m_food && foodSharedData.m_foodStamina >= foodSharedData.m_foodEitr)
			{
				subGroupRank = 1;
				return true;
			}
			subGroupRank = 2;
			return true;
		}

		private static bool HasFoodCarrier(SharedData shared)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			if ((int)shared.m_itemType != 2)
			{
				if ((int)shared.m_itemType == 1)
				{
					return (Object)(object)shared.m_appendToolTip != (Object)null;
				}
				return false;
			}
			return true;
		}

		private static SharedData GetFoodSharedData(SharedData shared)
		{
			return shared.m_appendToolTip?.m_itemData?.m_shared ?? shared;
		}

		private static bool MatchFeast(string? itemName)
		{
			if (HasToken(itemName, "feast"))
			{
				return HasToken(itemName, "material");
			}
			return false;
		}

		private static bool MatchMeadbase(string? itemName, SharedData shared)
		{
			return HasToken(itemName, shared, "meadbase");
		}

		private static bool MatchMead(string? itemName, SharedData shared)
		{
			if (!MatchMeadbase(itemName, shared))
			{
				return HasToken(itemName, shared, "mead");
			}
			return false;
		}

		private static bool MatchPotion(string? itemName, SharedData shared)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Invalid comparison between Unknown and I4
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Invalid comparison between Unknown and I4
			if (!MatchMead(itemName, shared) && !MatchMeadbase(itemName, shared) && !MatchNativeFood(shared, out var _))
			{
				if (!HasToken(itemName, shared, "potion"))
				{
					if ((int)shared.m_itemType == 1 || (int)shared.m_itemType == 2)
					{
						return (Object)(object)shared.m_consumeStatusEffect != (Object)null;
					}
					return false;
				}
				return true;
			}
			return false;
		}

		private static int SwordDetail(string? itemName, SharedData? shared)
		{
			if (IsTwoHanded(shared) || HasToken(itemName, "greatsword", "twohandedsword", "2hsword"))
			{
				return 1;
			}
			return 0;
		}

		private static int AxeDetail(string? itemName, SharedData? shared)
		{
			if (IsBattleaxe(itemName, shared))
			{
				return 2;
			}
			if (!IsTwoHanded(shared) && !HasToken(itemName, "twohandedaxe", "2haxe", "dualaxe"))
			{
				return 0;
			}
			return 1;
		}

		private static bool IsBattleaxe(string? itemName, SharedData? shared)
		{
			if (HasToken(itemName, "dualaxe"))
			{
				return false;
			}
			if (HasToken(itemName, "battleaxe"))
			{
				return true;
			}
			string value = shared?.m_attack?.m_attackAnimation ?? "";
			string text = shared?.m_secondaryAttack?.m_attackAnimation ?? "";
			if (IsTwoHanded(shared) && string.Equals(text, "battleaxe_secondary", StringComparison.OrdinalIgnoreCase) && !ContainsIgnoreCase(value, "dualaxe"))
			{
				if (!ContainsIgnoreCase(value, "battleaxe"))
				{
					return ContainsIgnoreCase(text, "battleaxe");
				}
				return true;
			}
			return false;
		}

		private static int ClubDetail(string? itemName, SharedData? shared)
		{
			if (!IsTwoHanded(shared) && !HasToken(itemName, "sledge", "twohandedclub", "2hclub"))
			{
				return 0;
			}
			return 1;
		}

		private static bool IsTwoHanded(SharedData? shared)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Invalid comparison between Unknown and I4
			if (shared == null || (int)shared.m_itemType != 14)
			{
				if (shared == null)
				{
					return false;
				}
				return (int)shared.m_itemType == 22;
			}
			return true;
		}

		private static int ShieldDetail(string? itemName)
		{
			if (HasToken(itemName, "buckler"))
			{
				return 0;
			}
			if (!HasToken(itemName, "tower"))
			{
				return 1;
			}
			return 2;
		}

		private static bool IsSkillAttack(SharedData shared, SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (shared.m_skillType == skillType && HasAttackAnimation(shared))
			{
				return GetTotalDamage(shared) > 0f;
			}
			return false;
		}

		private static bool HasAttackAnimation(SharedData shared)
		{
			return !string.IsNullOrEmpty(shared.m_attack?.m_attackAnimation);
		}

		private static float GetTotalDamage(SharedData shared)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: 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)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			DamageTypes damages = shared.m_damages;
			return damages.m_blunt + damages.m_slash + damages.m_pierce + damages.m_chop + damages.m_pickaxe + damages.m_fire + damages.m_frost + damages.m_lightning + damages.m_poison + damages.m_spirit;
		}

		private static bool IsItemTypeOrAttach(SharedData shared, ItemType itemType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (shared.m_itemType != itemType)
			{
				return shared.m_attachOverride == itemType;
			}
			return true;
		}

		private static bool HasToken(string? itemName, SharedData? shared, params string[] tokens)
		{
			string[] array = new string[3]
			{
				itemName,
				shared?.m_name,
				StripLocalizationToken(shared?.m_name)
			};
			for (int i = 0; i < array.Length; i++)
			{
				if (HasToken(array[i], tokens))
				{
					return true;
				}
			}
			return false;
		}

		private static bool HasToken(string? value, params string[] tokens)
		{
			string text = DataForgeResourceMap.NormalizeResourceToken(value);
			if (text.Length == 0)
			{
				return false;
			}
			for (int i = 0; i < tokens.Length; i++)
			{
				string text2 = DataForgeResourceMap.NormalizeResourceToken(tokens[i]);
				if (text2.Length > 0 && text.IndexOf(text2, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private static bool ContainsIgnoreCase(string value, string token)
		{
			return value.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private static string StripLocalizationToken(string? value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return "";
			}
			string text = value.Trim();
			if (!text.StartsWith("$item_", StringComparison.OrdinalIgnoreCase))
			{
				return text.TrimStart(new char[1] { '$' });
			}
			return text.Substring("$item_".Length);
		}

		private static DataForgeItemSortGroup Group(int bigGroupRank, int subGroupRank, int detailRank = 0)
		{
			return new DataForgeItemSortGroup(bigGroupRank, subGroupRank, detailRank);
		}
	}
	internal static class DataForgeResourceMap
	{
		private readonly struct RecipeLookupEntry
		{
			internal Recipe Recipe { get; }

			internal int? Tier { get; }

			internal RecipeLookupEntry(Recipe recipe, int? tier)
			{
				Recipe = recipe;
				Tier = tier;
			}
		}

		private const string ResourceMapFileName = "z_resourcemap.txt";

		private const int UnknownTierSortValue = 999999;

		private static readonly object Sync = new object();

		private static Dictionary<string, int> ResourceTierByToken = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, ItemDrop?> ItemDropCache = new Dictionary<string, ItemDrop>(StringComparer.OrdinalIgnoreCase);

		private static Dictionary<string, RecipeLookupEntry>? RecipeOutputLookup;

		private static readonly Dictionary<string, DataForgeItemSortGroup> ItemSortGroupCache = new Dictionary<string, DataForgeItemSortGroup>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, int> ItemTierSortValueCache = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

		private static DateTime LoadedWriteTimeUtc = DateTime.MinValue;

		private static bool Loaded;

		private static int CachedObjectDbItemCount = -1;

		private static int CachedObjectDbRecipeCount = -1;

		private static string ConfigDirectory => Path.Combine(Paths.ConfigPath, "DataForge");

		private static string ResourceMapPath => Path.Combine(ConfigDirectory, "z_resourcemap.txt");

		internal static string BuildSortKey(int groupRank, int tierSortValue, string name)
		{
			return string.Join("|", Math.Max(0, groupRank).ToString("D3", CultureInfo.InvariantCulture), Math.Max(0, tierSortValue).ToString("D6", CultureInfo.InvariantCulture), name ?? "");
		}

		internal static string BuildTierSortKey(int tierSortValue, string name)
		{
			return BuildSortKey(0, tierSortValue, name);
		}

		internal static string BuildItemSortKey(string? itemName, int tierSortValue, string name)
		{
			DataForgeItemSortGroup itemSortGroup = GetItemSortGroup(itemName);
			return string.Join("|", Math.Max(0, itemSortGroup.BigGroupRank).ToString("D3", CultureInfo.InvariantCulture), Math.Max(0, itemSortGroup.SubGroupRank).ToString("D3", CultureInfo.InvariantCulture), Math.Max(0, itemSortGroup.DetailRank).ToString("D3", CultureInfo.InvariantCulture), Math.Max(0, tierSortValue).ToString("D6", CultureInfo.InvariantCulture), name ?? "");
		}

		private static DataForgeItemSortGroup GetItemSortGroup(string? itemName)
		{
			EnsureObjectDbCacheFresh();
			string text = CleanPrefabName(itemName);
			if (text.Length > 0 && ItemSortGroupCache.TryGetValue(text, out var value))
			{
				return value;
			}
			DataForgeItemSortGroup dataForgeItemSortGroup = DataForgeItemSortClassifier.Classify(itemName, ResolveItemDrop(itemName)?.m_itemData?.m_shared);
			if (text.Length > 0)
			{
				ItemSortGroupCache[text] = dataForgeItemSortGroup;
			}
			return dataForgeItemSortGroup;
		}

		internal static int GetItemTierSortValue(string? itemName)
		{
			EnsureObjectDbCacheFresh();
			string text = CleanPrefabName(itemName);
			if (text.Length > 0 && ItemTierSortValueCache.TryGetValue(text, out var value))
			{
				return value;
			}
			int? knownTierForItem = GetKnownTierForItem(itemName);
			int? knownTierForRecipe = GetKnownTierForRecipe(ResolveRecipeForItem(itemName));
			int num = SortValue(MaxTier(knownTierForItem, knownTierForRecipe));
			if (text.Length > 0)
			{
				ItemTierSortValueCache[text] = num;
			}
			return num;
		}

		internal static int GetResourceTierSortValue(IEnumerable<string?> resourceNames)
		{
			return SortValue(GetKnownTierForResourceNames(resourceNames));
		}

		private static int SortValue(int? tier)
		{
			return tier ?? 999999;
		}

		private static int? GetKnownTierForRecipe(Recipe? recipe)
		{
			if (recipe?.m_resources == null)
			{
				return null;
			}
			return GetKnownTierForResourceNames(from requirement in recipe.m_resources
				where (Object)(object)requirement?.m_resItem != (Object)null && requirement.m_amount > 0
				select ((Object)requirement.m_resItem).name);
		}

		private static int? GetKnownTierForItem(string? itemName)
		{
			ItemDrop val = ResolveItemDrop(itemName);
			List<string> list = new List<string>
			{
				itemName,
				((Object)(object)val != (Object)null) ? ((Object)val).name : null,
				val?.m_itemData?.m_shared?.m_name,
				StripLocalizationToken(val?.m_itemData?.m_shared?.m_name)
			};
			string text = val?.m_itemData?.m_shared?.m_name;
			if (text != null && text.Length > 0 && Localization.instance != null)
			{
				list.Add(Localization.instance.Localize(text));
			}
			return GetKnownTierForResourceNames(list);
		}

		private static int? GetKnownTierForResourceNames(IEnumerable<string?> resourceNames)
		{
			EnsureLoaded();
			int? num = null;
			foreach (string resourceName in resourceNames)
			{
				foreach (string resourceToken in GetResourceTokens(resourceName))
				{
					if (ResourceTierByToken.TryGetValue(resourceToken, out var value))
					{
						num = MaxTier(num, value);
					}
				}
			}
			return num;
		}

		private static int? MaxTier(int? left, int? right)
		{
			if (!left.HasValue)
			{
				return right;
			}
			if (!right.HasValue)
			{
				return left;
			}
			return Math.Max(left.Value, right.Value);
		}

		private static IEnumerable<string> GetResourceTokens(string? value)
		{
			string cleaned = CleanPrefabName(value);
			if (cleaned.Length != 0)
			{
				yield return NormalizeResourceToken(cleaned);
				string text = StripLocalizationToken(cleaned);
				if (text.Length > 0)
				{
					yield return NormalizeResourceToken(text);
				}
			}
		}

		private static void EnsureLoaded()
		{
			Directory.CreateDirectory(ConfigDirectory);
			EnsureDefaultResourceMapFile();
			DateTime dateTime = (File.Exists(ResourceMapPath) ? File.GetLastWriteTimeUtc(ResourceMapPath) : DateTime.MinValue);
			lock (Sync)
			{
				if (!Loaded || !(dateTime == LoadedWriteTimeUtc))
				{
					ResourceTierByToken = LoadResourceMap(ResourceMapPath);
					LoadedWriteTimeUtc = dateTime;
					Loaded = true;
					ClearSortCaches();
				}
			}
		}

		private static void EnsureObjectDbCacheFresh()
		{
			int num = ObjectDB.instance?.m_items?.Count ?? (-1);
			int num2 = ObjectDB.instance?.m_recipes?.Count ?? (-1);
			if (num != CachedObjectDbItemCount || num2 != CachedObjectDbRecipeCount)
			{
				CachedObjectDbItemCount = num;
				CachedObjectDbRecipeCount = num2;
				ClearSortCaches();
			}
		}

		private static void ClearSortCaches()
		{
			ItemDropCache.Clear();
			RecipeOutputLookup = null;
			ItemSortGroupCache.Clear();
			ItemTierSortValueCache.Clear();
		}

		private static Dictionary<string, int> LoadResourceMap(string path)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
			if (!File.Exists(path))
			{
				return dictionary;
			}
			int num = -1;
			string[] array = File.ReadAllLines(path);
			for (int i = 0; i < array.Length; i++)
			{
				string text = StripComment(array[i]).Trim();
				if (text.Length == 0)
				{
					continue;
				}
				if (text.StartsWith("[", StringComparison.Ordinal) && text.EndsWith("]", StringComparison.Ordinal))
				{
					num++;
					continue;
				}
				if (num < 0)
				{
					num = 0;
				}
				string text2 = NormalizeResourceToken(text);
				if (text2.Length > 0 && !dictionary.ContainsKey(text2))
				{
					dictionary[text2] = num;
				}
			}
			return dictionary;
		}

		private static void EnsureDefaultResourceMapFile()
		{
			if (!File.Exists(ResourceMapPath))
			{
				File.WriteAllText(ResourceMapPath, DefaultResourceMapText());
			}
		}

		private static string StripComment(string line)
		{
			int num = line.IndexOf('#');
			if (num < 0)
			{
				return line;
			}
			return line.Substring(0, num);
		}

		internal static string NormalizeResourceToken(string? token)
		{
			string text = CleanPrefabName(token);
			if (text.StartsWith("$item_", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring("$item_".Length);
			}
			else if (text.StartsWith("$", StringComparison.Ordinal))
			{
				text = text.Substring(1);
			}
			return new string(text.Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant).ToArray());
		}

		private static string CleanPrefabName(string? name)
		{
			if (!string.IsNullOrWhiteSpace(name))
			{
				return name.Replace("(Clone)", "").Trim();
			}
			return "";
		}

		private static string StripLocalizationToken(string? value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return "";
			}
			string text = value.Trim();
			if (!text.StartsWith("$item_", StringComparison.OrdinalIgnoreCase))
			{
				return text.TrimStart(new char[1] { '$' });
			}
			return text.Substring("$item_".Length);
		}

		private static ItemDrop? ResolveItemDrop(string? itemName)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null || string.IsNullOrWhiteSpace(itemName))
			{
				return null;
			}
			string normalized = CleanPrefabName(itemName);
			if (ItemDropCache.TryGetValue(normalized, out ItemDrop value))
			{
				return value;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(normalized);
			ItemDrop val = default(ItemDrop);
			if ((Object)(object)itemPrefab != (Object)null && itemPrefab.TryGetComponent<ItemDrop>(ref val))
			{
				ItemDropCache[normalized] = val;
				return val;
			}
			ItemDrop val2 = (from item in ObjectDB.instance.m_items
				where (Object)(object)item != (Object)null
				select item.GetComponent<ItemDrop>()).FirstOrDefault((Func<ItemDrop, bool>)((ItemDrop itemDrop) => (Object)(object)itemDrop != (Object)null && ItemNameMatches(itemDrop, normalized)));
			ItemDropCache[normalized] = val2;
			return val2;
		}

		private static Recipe? ResolveRecipeForItem(string? itemName)
		{
			if (ObjectDB.instance?.m_recipes == null || string.IsNullOrWhiteSpace(itemName))
			{
				return null;
			}
			string itemName2 = CleanPrefabName(itemName);
			if (TryGetRecipeOutputLookupEntry(GetRecipeOutputLookup(), itemName2, out var entry))
			{
				return entry.Recipe;
			}
			return null;
		}

		private static Dictionary<string, RecipeLookupEntry> GetRecipeOutputLookup()
		{
			if (RecipeOutputLookup != null)
			{
				return RecipeOutputLookup;
			}
			EnsureLoaded();
			Dictionary<string, RecipeLookupEntry> dictionary = new Dictionary<string, RecipeLookupEntry>(StringComparer.OrdinalIgnoreCase);
			IEnumerable<Recipe> enumerable = ObjectDB.instance?.m_recipes;
			foreach (Recipe item in enumerable ?? Enumerable.Empty<Recipe>())
			{
				if ((Object)(object)item?.m_item == (Object)null)
				{
					continue;
				}
				int? knownTierForRecipe = GetKnownTierForRecipe(item);
				foreach (string itemLookupKey in GetItemLookupKeys(item.m_item))
				{
					AddRecipeOutputLookupEntry(dictionary, itemLookupKey, item, knownTierForRecipe);
				}
			}
			RecipeOutputLookup = dictionary;
			return dictionary;
		}

		private static bool TryGetRecipeOutputLookupEntry(Dictionary<string, RecipeLookupEntry> lookup, string itemName, out RecipeLookupEntry entry)
		{
			foreach (string lookupKey in GetLookupKeys(itemName))
			{
				if (lookup.TryGetValue(lookupKey, out entry))
				{
					return true;
				}
			}
			entry = default(RecipeLookupEntry);
			return false;
		}

		private static void AddRecipeOutputLookupEntry(Dictionary<string, RecipeLookupEntry> lookup, string key, Recipe recipe, int? tier)
		{
			if (key.Length != 0 && (!lookup.TryGetValue(key, out var value) || (tier ?? (-1)) > (value.Tier ?? (-1))))
			{
				lookup[key] = new RecipeLookupEntry(recipe, tier);
			}
		}

		private static IEnumerable<string> GetItemLookupKeys(ItemDrop itemDrop)
		{
			if (itemDrop?.m_itemData?.m_shared == null)
			{
				yield break;
			}
			string[] array = new string[4]
			{
				((Object)itemDrop).name,
				((Object)((Component)itemDrop).gameObject).name,
				itemDrop.m_itemData.m_shared.m_name,
				StripLocalizationToken(itemDrop.m_itemData.m_shared.m_name)
			};
			foreach (string value in array)
			{
				foreach (string lookupKey in GetLookupKeys(value))
				{
					yield return lookupKey;
				}
			}
		}

		private static IEnumerable<string> GetLookupKeys(string? value)
		{
			string clean = CleanPrefabName(value);
			if (clean.Length > 0)
			{
				yield return clean;
			}
			string text = NormalizeResourceToken(clean);
			if (text.Length > 0 && !string.Equals(text, clean, StringComparison.OrdinalIgnoreCase))
			{
				yield return text;
			}
		}

		private static bool ItemNameMatches(ItemDrop itemDrop, string token)
		{
			if (itemDrop?.m_itemData?.m_shared == null)
			{
				return false;
			}
			string value = NormalizeResourceToken(token);
			string[] array = new string[4]
			{
				((Object)itemDrop).name,
				((Object)((Component)itemDrop).gameObject).name,
				itemDrop.m_itemData.m_shared.m_name,
				StripLocalizationToken(itemDrop.m_itemData.m_shared.m_name)
			};
			foreach (string text in array)
			{
				if (CleanPrefabName(text).Equals(token, StringComparison.OrdinalIgnoreCase) || NormalizeResourceToken(text).Equals(value, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		private static string DefaultResourceMapText()
		{
			return string.Join(Environment.NewLine, "# DataForge resource tier map.", "# User-editable sorting data for generated item, recipe, and piece reference/full scaffold files.", "# Sections near the top are lower tier; sections near the bottom are higher tier.", "# Generated files use the same direction: lower tiers first, higher tiers later.", "# Edit this file to change item/recipe/piece generated file sorting.", "", "[Meadows]", "Wood", "Stone", "Resin", "Dandelion", "Flint", "LeatherScraps", "BoneFragments", "Honey", "Raspberry", "Blueberries", "DeerHide", "DeerMeat", "Feathers", "GreydwarfEye", "RawMeat", "", "[BlackForest]", "HardAntler", "Bronze", "BronzeNails", "Copper", "Tin", "Ectoplasm", "SurtlingCore", "TrollHide", "BjornHide", "FineWood", "AncientSeed", "Carrot", "BjornMeat", "BjornPaw", "RoundLog", "Thistle", "", "[Swamp]", "Iron", "Ooze", "Entrails", "Guck", "Bloodbag", "Chain", "ElderBark", "IronNails", "Root", "Turnip", "WitheredBone", "CuredSquirrelHamstring", "", "[Ocean]", "Chitin", "SerpentScale", "SerpentMeat", "", "[Mountain]", "Silver", "Crystal", "DragonEgg", "JuteRed", "Obsidian", "WolfClaw", "WolfFang", "WolfHairBundle", "WolfMeat", "WolfPelt", "", "[Plains]", "UndeadBjornRibcage", "BlackMetal", "DragonTear", "Barley", "BarleyFlour", "ChickenEgg", "ChickenMeat", "Flax", "GoblinTotem", "LinenThread", "LoxMeat", "LoxPelt", "Needle", "Tar", "", "[Mistlands]", "Eitr", "Bilebag", "BlackCore", "BlackMarble", "BugMeat", "Carapace", "DvergrKeyFragment", "DvergrNeedle", "GiantBloodSack", "HareMeat", "JuteBlue", "Mandible", "Sap", "ScaleHide", "Softtissue", "Wisp", "YagluthDrop", "YggdrasilWood", "", "[Ashlands]", "FlametalNew", "AskBladder", "AskHide", "AsksvinEgg", "AsksvinMeat", "Blackwood", "BoneMawSerpentMeat", "BonemawSerpentTooth", "CelestialFeather", "CeramicPlate", "CharcoalResin", "CharredBone", "CharredCogwheel", "Charredskull", "Grausten", "MoltenCore", "MorgenHeart", "MorgenSinew", "ProustitePowder", "SulfurStone", "VoltureEgg", "VoltureMeat", "");
		}
	}
	internal static class DataForgeValue
	{
		internal static void Copy(string? value, Action<string> assign)
		{
			if (value != null)
			{
				assign(value);
			}
		}

		internal static void Copy(bool? value, Action<bool> assign)
		{
			if (value.HasValue)
			{
				assign(value.Value);
			}
		}

		internal static void Copy(int? value, Action<int> assign)
		{
			if (value.HasValue)
			{
				assign(value.Value);
			}
		}

		internal static void Copy(float? value, Action<float> assign)
		{
			if (value.HasValue)
			{
				assign(value.Value);
			}
		}

		internal static string[] SplitTuple(string? value)
		{
			return (from part in value?.Split(new char[1] { ',' }, StringSplitOptions.None)
				select part.Trim()).ToArray() ?? Array.Empty<string>();
		}

		internal static bool IsNone(string? value)
		{
			string text = value?.Trim() ?? "";
			if (text.Length != 0 && !text.Equals("none", StringComparison.OrdinalIgnoreCase))
			{
				return text.Equals("null", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}
	}
	internal static class DataForgeSync
	{
		internal static bool PublishPayload(CustomSyncedValue<string>? syncedPayload, string domainName, string payload)
		{
			if (syncedPayload == null || string.Equals(syncedPayload.Value ?? "", payload, StringComparison.Ordinal))
			{
				return false;
			}
			DataForgePlugin.Log.LogDebug((object)$"Publishing {domainName} payload ({Encoding.UTF8.GetByteCount(payload)} bytes).");
			syncedPayload.AssignLocalValue(payload);
			return true;
		}
	}
	internal static class DataForgeReferenceState
	{
		private static string ConfigDirectory => Path.Combine(Paths.ConfigPath, "DataForge");

		internal static bool ShouldSkip(string stateKey, string referencePath, string sourceSignature, string logicVersion)
		{
			if (!File.Exists(referencePath))
			{
				return false;
			}
			string statePath = GetStatePath(stateKey);
			if (!File.Exists(statePath))
			{
				return false;
			}
			try
			{
				string[] array = File.ReadAllLines(statePath);
				if (array.Length < 3)
				{
					return false;
				}
				return string.Equals(array[0].Trim(), sourceSignature, StringComparison.Ordinal) && string.Equals(array[1].Trim(), BuildFileStamp(referencePath), StringComparison.Ordinal) && string.Equals(array[2].Trim(), logicVersion, StringComparison.Ordinal);
			}
			catch
			{
				return false;
			}
		}

		internal static void Record(string stateKey, string referencePath, string sourceSignature, string logicVersion)
		{
			if (File.Exists(referencePath))
			{
				string statePath = GetStatePath(stateKey);
				string directoryName = Path.GetDirectoryName(statePath);
				if (!string.IsNullOrWhiteSpace(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				string content = sourceSignature.Trim() + Environment.NewLine + BuildFileStamp(referencePath) + Environment.NewLine + logicVersion + Environment.NewLine;
				GeneratedArtifactWriter.WriteTextIfChanged(statePath, content);
			}
		}

		internal static string BuildFileStamp(string path)
		{
			if (!File.Exists(path))
			{
				return "missing";
			}
			FileInfo fileInfo = new FileInfo(path);
			return fileInfo.Length.ToString(CultureInfo.InvariantCulture) + ":" + fileInfo.LastWriteTimeUtc.Ticks.ToString(CultureInfo.InvariantCulture);
		}

		internal static string ComputeStableHash(string value)
		{
			using SHA256 sHA = SHA256.Create();
			byte[] bytes = Encoding.UTF8.GetBytes(value ?? "");
			byte[] array = sHA.ComputeHash(bytes);
			StringBuilder stringBuilder = new StringBuilder(array.Length * 2);
			byte[] array2 = array;
			foreach (byte b in array2)
			{
				stringBuilder.Append(b.ToString("x2", CultureInfo.InvariantCulture));
			}
			return stringBuilder.ToString();
		}

		private static string GetStatePath(string stateKey)
		{
			return Path.Combine(ConfigDirectory, "cache", ".reference-state." + stateKey + ".txt");
		}
	}
	internal static class DataForgeProfiler
	{
		internal static void Profile(string label, Action action)
		{
			if (!DataForgePlugin.LogStartupTimings)
			{
				action();
				return;
			}
			Stopwatch stopwatch = Stopwatch.StartNew();
			try
			{
				action();
			}
			finally
			{
				stopwatch.Stop();
				DataForgeConnectionProfiler.MarkProfiledPhase(label, stopwatch.Elapsed.TotalMilliseconds);
				DataForgePlugin.Log.LogInfo((object)$"DataForge profile: {label} took {stopwatch.Elapsed.TotalMilliseconds:0.###} ms.");
			}
		}
	}
	internal static class DataForgeConnectionProfiler
	{
		private readonly struct Milestone
		{
			internal string Label { get; }

			internal double ElapsedMs { get; }

			internal Milestone(string label, double elapsedMs)
			{
				Label = label;
				ElapsedMs = elapsedMs;
			}
		}

		private static readonly object Lock = new object();

		private static readonly Stopwatch Stopwatch = new Stopwatch();

		private static readonly List<Milestone> Milestones = new List<Milestone>();

		private static readonly HashSet<string> SeenOneShotMilestones = new HashSet<string>(StringComparer.Ordinal);

		private static bool Active;

		private static bool Completed;

		internal static void Begin(string label)
		{
			if (!DataForgePlugin.LogStartupTimings)
			{
				return;
			}
			lock (Lock)
			{
				Active = true;
				Completed = false;
				Milestones.Clear();
				SeenOneShotMilestones.Clear();
				Stopwatch.Restart();
				AddMilestone(label);
			}
		}

		internal static void Mark(string label)
		{
			if (!DataForgePlugin.LogStartupTimings)
			{
				return;
			}
			lock (Lock)
			{
				if (Active && !Completed)
				{
					AddMilestone(label);
				}
			}
		}

		internal static void MarkOnce(string label)
		{
			if (!DataForgePlugin.LogStartupTimings)
			{
				return;
			}
			lock (Lock)
			{
				if (Active && !Completed && SeenOneShotMilestones.Add(label))
				{
					AddMilestone(label);
				}
			}
		}

		internal static void MarkProfiledPhase(string label, double elapsedMs)
		{
			if (DataForgePlugin.LogStartupTimings)
			{
				Mark($"DataForge {label} ({elapsedMs:0.###} ms)");
			}
		}

		internal static void Complete(string label)
		{
			if (!DataForgePlugin.LogStartupTimings)
			{
				return;
			}
			lock (Lock)
			{
				if (Active && !Completed)
				{
					AddMilestone(label);
					Completed = true;
					Stopwatch.Stop();
					LogSummary("completed");
					Active = false;
				}
			}
		}

		internal static void Abort(string label)
		{
			if (!DataForgePlugin.LogStartupTimings)
			{
				return;
			}
			lock (Lock)
			{
				if (Active && !Completed)
				{
					AddMilestone(label);
					Completed = true;
					Stopwatch.Stop();
					LogSummary("aborted");
					Active = false;
				}
			}
		}

		private static void AddMilestone(string label)
		{
			Milestones.Add(new Milestone(label, Stopwatch.Elapsed.TotalMilliseconds));
		}

		private static void LogSummary(string result)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine($"DataForge lobby-to-world profile {result}: total {Stopwatch.Elapsed.TotalMilliseconds:0.###} ms.");
			double num = 0.0;
			foreach (Milestone milestone in Milestones)
			{
				stringBuilder.Append("  +");
				stringBuilder.Append(milestone.ElapsedMs.ToString("0.###"));
				stringBuilder.Append(" ms");
				stringBuilder.Append(" (delta ");
				stringBuilder.Append((milestone.ElapsedMs - num).ToString("0.###"));
				stringBuilder.Append(" ms): ");
				stringBuilder.AppendLine(milestone.Label);
				num = milestone.ElapsedMs;
			}
			DataForgePlugin.Log.LogInfo((object)stringBuilder.ToString().TrimEnd(Array.Empty<char>()));
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "JoinServer")]
	internal static class DataForgeFejdStartupJoinServerProfilerPatch
	{
		private static void Prefix()
		{
			DataForgeConnectionProfiler.Begin("FejdStartup.JoinServer");
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "OnStartGame")]
	internal static class DataForgeFejdStartupOnStartGameProfilerPatch
	{
		private static void Prefix()
		{
			DataForgeConnectionProfiler.Begin("FejdStartup.OnStartGame");
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "TransitionToMainScene")]
	internal static class DataForgeFejdStartupTransitionToMainSceneProfilerPatch
	{
		private static void Prefix()
		{
			DataForgeConnectionProfiler.Mark("FejdStartup.TransitionToMainScene");
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "LoadMainScene")]
	internal static class DataForgeFejdStartupLoadMainSceneProfilerPatch
	{
		private static void Prefix()
		{
			DataForgeConnectionProfiler.Mark("FejdStartup.LoadMainScene");
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "ShowConnectError")]
	internal static class DataForgeFejdStartupShowConnectErrorProfilerPatch
	{
		private static void Postfix()
		{
			//IL_0