Decompiled source of DSPCustomRecipes v1.0.0

DSPCustomRecipes.dll

Decompiled 15 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using xiaoye97;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DSPCustomRecipes")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("DSPCustomRecipes")]
[assembly: AssemblyTitle("DSPCustomRecipes")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DSPCustomRecipes
{
	public static class DefaultConfig
	{
		public const string Text = "{\n  // ===== 自定义配方配置 =====\n  // 修改后需要重启游戏才会生效。\n  // 物品可以填中文名(如 \"铁矿\")、英文名(如 \"Iron Ore\",会自动对照)或物品 ID(如 1001)。\n  // 一条配方里:\n  //   name        显示名称(可选)\n  //   description 说明文字(可选)\n  //   base        以某个现有配方为模板(配方 ID 或名称),下面写了的字段会覆盖模板内容\n  //   type        配方类型:Smelt(冶炼) / Chemical(化工) / Refine(精炼) / Assemble(制造)\n  //               / Particle(粒子) / Fractionate(分馏) / Research(研究)\n  //   time        单次生产耗时(秒)\n  //   inputs      输入物品\n  //   outputs     产出物品\n  //   handcraft   是否显示在复制台(手搓)里,默认 false\n  //   unlock      always(默认,直接解锁) / none(不自动解锁) / tech:<科技名称或ID>\n  //   icon        自定义图标所用物品(可选,默认使用产出物品的图标)\n  //   gridIndex   手动指定图标格子(可选,填 页*1000+行*100+列,例如 2101)\n\n  \"recipes\": [\n    {\n      // 示例 1:完全新增——电弧熔炉用 1 个铁矿直接熔出 2 个铁块\n      \"name\": \"高效冶炼铁块\",\n      \"description\": \"铁矿 x1 -> 铁块 x2\",\n      \"type\": \"Smelt\",\n      \"time\": 1,\n      \"inputs\":  [ { \"item\": \"铁矿\", \"count\": 1 } ],\n      \"outputs\": [ { \"item\": \"铁块\", \"count\": 2 } ],\n      \"handcraft\": false\n    },\n    {\n      // 示例 2:以现有配方 \"铁块\" 为模板,只改耗时并允许手搓\n      \"name\": \"铁块(快速手搓版)\",\n      \"description\": \"以原版铁块配方为模板,耗时缩短到 0.5 秒\",\n      \"base\": \"铁块\",\n      \"time\": 0.5,\n      \"inputs\":  [ { \"item\": \"铁矿\", \"count\": 2 } ],\n      \"outputs\": [ { \"item\": \"铁块\", \"count\": 1 } ],\n      \"handcraft\": true\n    }\n  ]\n}\n";
	}
	public enum JsonKind
	{
		Null,
		Bool,
		Number,
		String,
		Array,
		Object
	}
	public sealed class JsonValue
	{
		private sealed class Parser
		{
			private readonly string _text;

			private int _pos;

			public int Position => _pos;

			public bool AtEnd => _pos >= _text.Length;

			public Parser(string text)
			{
				_text = text ?? string.Empty;
			}

			public void SkipTrivia()
			{
				while (!AtEnd)
				{
					char c = _text[_pos];
					if (char.IsWhiteSpace(c))
					{
						_pos++;
						continue;
					}
					if (c != '/' || _pos + 1 >= _text.Length)
					{
						break;
					}
					switch (_text[_pos + 1])
					{
					case '/':
						_pos += 2;
						while (!AtEnd && _text[_pos] != '\n')
						{
							_pos++;
						}
						break;
					case '*':
						_pos += 2;
						while (_pos + 1 < _text.Length && (_text[_pos] != '*' || _text[_pos + 1] != '/'))
						{
							_pos++;
						}
						_pos = Math.Min(_pos + 2, _text.Length);
						break;
					default:
						return;
					}
				}
			}

			public JsonValue ParseValue()
			{
				SkipTrivia();
				if (AtEnd)
				{
					throw new FormatException("JSON 意外结束。");
				}
				switch (_text[_pos])
				{
				case '{':
					return ParseObject();
				case '[':
					return ParseArray();
				case '"':
					return new JsonValue
					{
						Kind = JsonKind.String,
						StringValue = ParseString()
					};
				case 't':
					Expect("true");
					return new JsonValue
					{
						Kind = JsonKind.Bool,
						BoolValue = true
					};
				case 'f':
					Expect("false");
					return new JsonValue
					{
						Kind = JsonKind.Bool,
						BoolValue = false
					};
				case 'n':
					Expect("null");
					return new JsonValue
					{
						Kind = JsonKind.Null
					};
				default:
					return ParseNumber();
				}
			}

			private JsonValue ParseObject()
			{
				Dictionary<string, JsonValue> dictionary = new Dictionary<string, JsonValue>(StringComparer.OrdinalIgnoreCase);
				_pos++;
				SkipTrivia();
				if (!AtEnd && _text[_pos] == '}')
				{
					_pos++;
					return new JsonValue
					{
						Kind = JsonKind.Object,
						ObjectValue = dictionary
					};
				}
				while (true)
				{
					SkipTrivia();
					if (AtEnd || _text[_pos] != '"')
					{
						throw new FormatException("JSON 对象的键必须是字符串(位置 " + _pos + ")。");
					}
					string key = ParseString();
					SkipTrivia();
					if (AtEnd || _text[_pos] != ':')
					{
						throw new FormatException("JSON 对象缺少冒号(位置 " + _pos + ")。");
					}
					_pos++;
					dictionary[key] = ParseValue();
					SkipTrivia();
					if (AtEnd)
					{
						throw new FormatException("JSON 对象未闭合。");
					}
					if (_text[_pos] != ',')
					{
						break;
					}
					_pos++;
				}
				if (_text[_pos] == '}')
				{
					_pos++;
					return new JsonValue
					{
						Kind = JsonKind.Object,
						ObjectValue = dictionary
					};
				}
				throw new FormatException("JSON 对象缺少逗号或右花括号(位置 " + _pos + ")。");
			}

			private JsonValue ParseArray()
			{
				List<JsonValue> list = new List<JsonValue>();
				_pos++;
				SkipTrivia();
				if (!AtEnd && _text[_pos] == ']')
				{
					_pos++;
					return new JsonValue
					{
						Kind = JsonKind.Array,
						ArrayValue = list
					};
				}
				while (true)
				{
					list.Add(ParseValue());
					SkipTrivia();
					if (AtEnd)
					{
						throw new FormatException("JSON 数组未闭合。");
					}
					if (_text[_pos] != ',')
					{
						break;
					}
					_pos++;
				}
				if (_text[_pos] == ']')
				{
					_pos++;
					return new JsonValue
					{
						Kind = JsonKind.Array,
						ArrayValue = list
					};
				}
				throw new FormatException("JSON 数组缺少逗号或右方括号(位置 " + _pos + ")。");
			}

			private string ParseString()
			{
				StringBuilder stringBuilder = new StringBuilder();
				_pos++;
				while (!AtEnd)
				{
					char c = _text[_pos++];
					switch (c)
					{
					case '"':
						return stringBuilder.ToString();
					default:
						stringBuilder.Append(c);
						break;
					case '\\':
					{
						if (AtEnd)
						{
							throw new FormatException("JSON 字符串转义不完整。");
						}
						char c2 = _text[_pos++];
						switch (c2)
						{
						case '"':
							stringBuilder.Append('"');
							break;
						case '\\':
							stringBuilder.Append('\\');
							break;
						case '/':
							stringBuilder.Append('/');
							break;
						case 'b':
							stringBuilder.Append('\b');
							break;
						case 'f':
							stringBuilder.Append('\f');
							break;
						case 'n':
							stringBuilder.Append('\n');
							break;
						case 'r':
							stringBuilder.Append('\r');
							break;
						case 't':
							stringBuilder.Append('\t');
							break;
						case 'u':
							if (_pos + 4 > _text.Length)
							{
								throw new FormatException("JSON \\u 转义不完整。");
							}
							stringBuilder.Append((char)Convert.ToInt32(_text.Substring(_pos, 4), 16));
							_pos += 4;
							break;
						default:
							throw new FormatException("无法识别的 JSON 转义:" + c2);
						}
						break;
					}
					}
				}
				throw new FormatException("JSON 字符串未闭合。");
			}

			private JsonValue ParseNumber()
			{
				int pos = _pos;
				while (!AtEnd)
				{
					char c = _text[_pos];
					if ((c < '0' || c > '9') && c != '-' && c != '+' && c != '.' && c != 'e' && c != 'E')
					{
						break;
					}
					_pos++;
				}
				string text = _text.Substring(pos, _pos - pos);
				if (!double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					throw new FormatException("无法识别的 JSON 数字:" + text);
				}
				return new JsonValue
				{
					Kind = JsonKind.Number,
					NumberValue = result
				};
			}

			private void Expect(string literal)
			{
				if (_pos + literal.Length > _text.Length || string.CompareOrdinal(_text, _pos, literal, 0, literal.Length) != 0)
				{
					throw new FormatException("JSON 关键字无效(位置 " + _pos + ")。");
				}
				_pos += literal.Length;
			}
		}

		public JsonKind Kind;

		public bool BoolValue;

		public double NumberValue;

		public string StringValue;

		public List<JsonValue> ArrayValue;

		public Dictionary<string, JsonValue> ObjectValue;

		public bool IsNull => Kind == JsonKind.Null;

		public JsonValue Get(string key)
		{
			if (Kind != JsonKind.Object || ObjectValue == null)
			{
				return null;
			}
			if (!ObjectValue.TryGetValue(key, out var value))
			{
				return null;
			}
			return value;
		}

		public string AsString(string fallback = null)
		{
			if (Kind == JsonKind.String)
			{
				return StringValue;
			}
			if (Kind == JsonKind.Number)
			{
				return NumberValue.ToString(CultureInfo.InvariantCulture);
			}
			if (Kind == JsonKind.Bool)
			{
				if (!BoolValue)
				{
					return "false";
				}
				return "true";
			}
			return fallback;
		}

		public double? AsNumber()
		{
			if (Kind == JsonKind.Number)
			{
				return NumberValue;
			}
			if (Kind == JsonKind.String && double.TryParse(StringValue, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			return null;
		}

		public int? AsInt()
		{
			double? num = AsNumber();
			if (!num.HasValue)
			{
				return null;
			}
			return (int)Math.Round(num.Value);
		}

		public bool? AsBool()
		{
			if (Kind == JsonKind.Bool)
			{
				return BoolValue;
			}
			if (Kind == JsonKind.Number)
			{
				return NumberValue != 0.0;
			}
			if (Kind == JsonKind.String)
			{
				switch (StringValue.Trim().ToLowerInvariant())
				{
				case "true":
				case "1":
				case "yes":
				case "是":
					return true;
				case "false":
				case "0":
				case "no":
				case "否":
					return false;
				}
			}
			return null;
		}

		public List<JsonValue> AsArray()
		{
			if (Kind != JsonKind.Array)
			{
				return null;
			}
			return ArrayValue;
		}

		public static JsonValue Parse(string text)
		{
			Parser parser = new Parser(text);
			JsonValue result = parser.ParseValue();
			parser.SkipTrivia();
			if (!parser.AtEnd)
			{
				throw new FormatException("JSON 末尾存在多余内容(位置 " + parser.Position + ")。");
			}
			return result;
		}
	}
	[BepInPlugin("dsp.customrecipes", "DSP Custom Recipes", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class CustomRecipesPlugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(GameHistoryData), "Import")]
		private static class HistoryImportPatch
		{
			private static void Postfix(GameHistoryData __instance)
			{
				UnlockAll(__instance, "读取存档");
			}
		}

		[HarmonyPatch(typeof(GameHistoryData), "SetForNewGame")]
		private static class HistoryNewGamePatch
		{
			private static void Postfix(GameHistoryData __instance)
			{
				UnlockAll(__instance, "新游戏");
			}
		}

		private static class Lookup
		{
			private static readonly Dictionary<string, ItemProto> Items = new Dictionary<string, ItemProto>(StringComparer.OrdinalIgnoreCase);

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

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

			private static bool _itemsBuilt;

			private static bool _recipesBuilt;

			private static bool _techsBuilt;

			private static Dictionary<string, string> _englishAliases;

			private static bool _englishAliasesLoaded;

			public static ItemProto FindItem(string spec)
			{
				return Find<ItemProto>(spec, (Action)EnsureItems, Items, ((ProtoSet<ItemProto>)(object)LDB.items).dataArray, (Func<int, ItemProto>)((int id) => ((ProtoSet<ItemProto>)(object)LDB.items).Select(id)));
			}

			public static RecipeProto FindRecipe(string spec)
			{
				return Find<RecipeProto>(spec, (Action)EnsureRecipes, Recipes, ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray, (Func<int, RecipeProto>)((int id) => ((ProtoSet<RecipeProto>)(object)LDB.recipes).Select(id)));
			}

			public static TechProto FindTech(string spec)
			{
				return Find<TechProto>(spec, (Action)EnsureTechs, Techs, ((ProtoSet<TechProto>)(object)LDB.techs).dataArray, (Func<int, TechProto>)((int id) => ((ProtoSet<TechProto>)(object)LDB.techs).Select(id)));
			}

			private static T Find<T>(string spec, Action ensure, Dictionary<string, T> map, T[] dataArray, Func<int, T> selectById) where T : Proto
			{
				if (string.IsNullOrEmpty(spec))
				{
					return default(T);
				}
				spec = spec.Trim();
				if (int.TryParse(spec, out var result))
				{
					T val = selectById(result);
					if (val != null)
					{
						return val;
					}
				}
				ensure();
				if (map.TryGetValue(spec, out var value))
				{
					return value;
				}
				string text = ResolveEnglishAlias(spec);
				if (!string.IsNullOrEmpty(text) && map.TryGetValue(text, out var value2))
				{
					return value2;
				}
				string text2 = (string.IsNullOrEmpty(text) ? spec : text);
				if (text2.Length < 2)
				{
					return default(T);
				}
				foreach (T val2 in dataArray)
				{
					if (val2 != null && !string.IsNullOrEmpty(((Proto)val2).Name))
					{
						if (((Proto)val2).Name.IndexOf(text2, StringComparison.OrdinalIgnoreCase) >= 0)
						{
							return val2;
						}
						string text3 = Localization.Translate(((Proto)val2).Name);
						if (!string.IsNullOrEmpty(text3) && text3.IndexOf(text2, StringComparison.OrdinalIgnoreCase) >= 0)
						{
							return val2;
						}
					}
				}
				return default(T);
			}

			private static void EnsureItems()
			{
				if (_itemsBuilt)
				{
					return;
				}
				_itemsBuilt = true;
				ItemProto[] dataArray = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray;
				foreach (ItemProto val in dataArray)
				{
					if (val != null && !string.IsNullOrEmpty(((Proto)val).Name))
					{
						TryAdd(Items, ((Proto)val).Name, val);
						string text = Localization.Translate(((Proto)val).Name);
						if (!string.IsNullOrEmpty(text))
						{
							TryAdd(Items, text, val);
						}
						if (!string.IsNullOrEmpty(((Proto)val).SID))
						{
							TryAdd(Items, ((Proto)val).SID, val);
						}
					}
				}
			}

			private static void EnsureRecipes()
			{
				if (_recipesBuilt)
				{
					return;
				}
				_recipesBuilt = true;
				RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
				foreach (RecipeProto val in dataArray)
				{
					if (val != null && !string.IsNullOrEmpty(((Proto)val).Name))
					{
						TryAdd(Recipes, ((Proto)val).Name, val);
						string text = Localization.Translate(((Proto)val).Name);
						if (!string.IsNullOrEmpty(text))
						{
							TryAdd(Recipes, text, val);
						}
					}
				}
			}

			private static void EnsureTechs()
			{
				if (_techsBuilt)
				{
					return;
				}
				_techsBuilt = true;
				TechProto[] dataArray = ((ProtoSet<TechProto>)(object)LDB.techs).dataArray;
				foreach (TechProto val in dataArray)
				{
					if (val != null && !string.IsNullOrEmpty(((Proto)val).Name))
					{
						TryAdd(Techs, ((Proto)val).Name, val);
						string text = Localization.Translate(((Proto)val).Name);
						if (!string.IsNullOrEmpty(text))
						{
							TryAdd(Techs, text, val);
						}
					}
				}
			}

			private static string ResolveEnglishAlias(string spec)
			{
				EnsureEnglishAliases();
				if (_englishAliases != null && _englishAliases.TryGetValue(spec, out var value))
				{
					return value;
				}
				return null;
			}

			private static void EnsureEnglishAliases()
			{
				if (_englishAliasesLoaded)
				{
					return;
				}
				_englishAliasesLoaded = true;
				try
				{
					string directoryName = Path.GetDirectoryName(Application.dataPath);
					if (string.IsNullOrEmpty(directoryName))
					{
						return;
					}
					string path = Path.Combine(directoryName, "Locale", "1033", "prototype.txt");
					if (!File.Exists(path))
					{
						return;
					}
					Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
					string[] array = File.ReadAllLines(path, Encoding.Unicode);
					foreach (string text in array)
					{
						if (string.IsNullOrEmpty(text) || text[0] == '[' || text[0] == '/')
						{
							continue;
						}
						string[] array2 = text.Split('\t');
						if (array2.Length >= 4)
						{
							string text2 = array2[0].Trim();
							string text3 = array2[3].Trim();
							if (text2.Length != 0 && text3.Length != 0 && !dictionary.ContainsKey(text3))
							{
								dictionary[text3] = text2;
							}
						}
					}
					_englishAliases = dictionary;
					Log.LogInfo((object)("已载入英文名称对照表,共 " + dictionary.Count + " 条。"));
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("载入英文名称对照表失败(不影响中文名与物品 ID):" + ex.Message));
				}
			}

			private static void TryAdd<T>(Dictionary<string, T> map, string key, T value)
			{
				if (!string.IsNullOrEmpty(key) && !map.ContainsKey(key))
				{
					map[key] = value;
				}
			}
		}

		public const string ModGuid = "dsp.customrecipes";

		public const string ModName = "DSP Custom Recipes";

		public const string ModVersion = "1.0.0";

		public const string LDBToolGuid = "me.xiaoye97.plugin.Dyson.LDBTool";

		internal static ManualLogSource Log;

		internal static CustomRecipesPlugin Instance;

		private static readonly List<RecipeProto> AddedRecipes = new List<RecipeProto>();

		private static readonly Dictionary<RecipeProto, RecipeDefinition> RecipeDefinitions = new Dictionary<RecipeProto, RecipeDefinition>();

		private static readonly List<RecipeDefinition> Definitions = new List<RecipeDefinition>();

		private Harmony _harmony;

		internal static bool SetupFinished;

		internal static int AppliedCount;

		private static bool _warnedGridOverflow;

		private static FieldInfo _ldbToolFinishedField;

		private static MethodInfo _findRecipesMethod;

		private static MethodInfo _unlockRecipeMethod;

		internal static string ConfigDirectory => Path.Combine(Paths.ConfigPath, "dsp.customrecipes");

		internal static string ConfigFile_ => Path.Combine(ConfigDirectory, "recipes.json");

		private static bool LDBToolFinished
		{
			get
			{
				try
				{
					if (_ldbToolFinishedField == null)
					{
						_ldbToolFinishedField = AccessTools.Field(typeof(LDBTool), "Finshed");
					}
					return _ldbToolFinishedField != null && (bool)_ldbToolFinishedField.GetValue(null);
				}
				catch
				{
					return false;
				}
			}
		}

		private void Awake()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			LoadDefinitions();
			if (Definitions.Count > 0)
			{
				LDBTool.PostAddDataAction = (Action)Delegate.Combine(LDBTool.PostAddDataAction, new Action(OnPostAddData));
			}
			_harmony = new Harmony("dsp.customrecipes");
			try
			{
				_harmony.PatchAll(typeof(CustomRecipesPlugin).Assembly);
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Harmony 补丁失败:" + ex));
			}
			PatchVFPreload();
		}

		private void PatchVFPreload()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			try
			{
				Type type = AccessTools.TypeByName("VFPreload");
				if (type == null)
				{
					Log.LogError((object)"找不到 VFPreload 类型,无法挂接加载完成回调,自定义配方可能不会生效。");
					return;
				}
				MethodInfo methodInfo = AccessTools.Method(type, "InvokeOnLoadWorkEnded", (Type[])null, (Type[])null);
				MethodInfo method = typeof(CustomRecipesPlugin).GetMethod("OnLoadWorkEndedPostfix", BindingFlags.Static | BindingFlags.NonPublic);
				_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch (Exception ex)
			{
				Log.LogError((object)("挂接 VFPreload 失败:" + ex));
			}
		}

		private static void OnLoadWorkEndedPostfix()
		{
			if ((Object)(object)Instance != (Object)null)
			{
				((MonoBehaviour)Instance).StartCoroutine(Instance.FinishSetupRoutine());
			}
		}

		private void LoadDefinitions()
		{
			try
			{
				if (!Directory.Exists(ConfigDirectory))
				{
					Directory.CreateDirectory(ConfigDirectory);
				}
				if (!File.Exists(ConfigFile_))
				{
					File.WriteAllText(ConfigFile_, "{\n  // ===== 自定义配方配置 =====\n  // 修改后需要重启游戏才会生效。\n  // 物品可以填中文名(如 \"铁矿\")、英文名(如 \"Iron Ore\",会自动对照)或物品 ID(如 1001)。\n  // 一条配方里:\n  //   name        显示名称(可选)\n  //   description 说明文字(可选)\n  //   base        以某个现有配方为模板(配方 ID 或名称),下面写了的字段会覆盖模板内容\n  //   type        配方类型:Smelt(冶炼) / Chemical(化工) / Refine(精炼) / Assemble(制造)\n  //               / Particle(粒子) / Fractionate(分馏) / Research(研究)\n  //   time        单次生产耗时(秒)\n  //   inputs      输入物品\n  //   outputs     产出物品\n  //   handcraft   是否显示在复制台(手搓)里,默认 false\n  //   unlock      always(默认,直接解锁) / none(不自动解锁) / tech:<科技名称或ID>\n  //   icon        自定义图标所用物品(可选,默认使用产出物品的图标)\n  //   gridIndex   手动指定图标格子(可选,填 页*1000+行*100+列,例如 2101)\n\n  \"recipes\": [\n    {\n      // 示例 1:完全新增——电弧熔炉用 1 个铁矿直接熔出 2 个铁块\n      \"name\": \"高效冶炼铁块\",\n      \"description\": \"铁矿 x1 -> 铁块 x2\",\n      \"type\": \"Smelt\",\n      \"time\": 1,\n      \"inputs\":  [ { \"item\": \"铁矿\", \"count\": 1 } ],\n      \"outputs\": [ { \"item\": \"铁块\", \"count\": 2 } ],\n      \"handcraft\": false\n    },\n    {\n      // 示例 2:以现有配方 \"铁块\" 为模板,只改耗时并允许手搓\n      \"name\": \"铁块(快速手搓版)\",\n      \"description\": \"以原版铁块配方为模板,耗时缩短到 0.5 秒\",\n      \"base\": \"铁块\",\n      \"time\": 0.5,\n      \"inputs\":  [ { \"item\": \"铁矿\", \"count\": 2 } ],\n      \"outputs\": [ { \"item\": \"铁块\", \"count\": 1 } ],\n      \"handcraft\": true\n    }\n  ]\n}\n", new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
					Log.LogInfo((object)("已生成默认配置文件:" + ConfigFile_));
				}
				JsonValue root = JsonValue.Parse(File.ReadAllText(ConfigFile_, Encoding.UTF8));
				Definitions.AddRange(RecipeDefinition.ParseAll(root));
				Log.LogInfo((object)("读取到 " + Definitions.Count + " 条自定义配方定义:" + ConfigFile_));
			}
			catch (Exception ex)
			{
				Log.LogError((object)("读取配置文件失败,本次不会添加任何配方:" + ex));
			}
		}

		private static void OnPostAddData()
		{
			RegisterAllRecipes(null);
		}

		private static HashSet<int> CollectUsedGridIndexes()
		{
			HashSet<int> hashSet = new HashSet<int>();
			RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
			foreach (RecipeProto val in dataArray)
			{
				if (val != null && val.GridIndex > 0)
				{
					hashSet.Add(val.GridIndex);
				}
			}
			foreach (RecipeProto item in EnumeratePendingProtos<RecipeProto>())
			{
				if (item != null && item.GridIndex > 0)
				{
					hashSet.Add(item.GridIndex);
				}
			}
			return hashSet;
		}

		private static IEnumerable<T> EnumeratePendingProtos<T>() where T : Proto
		{
			string[] array = new string[2] { "PostToAdd", "PreToAdd" };
			foreach (string text in array)
			{
				FieldInfo fieldInfo = null;
				try
				{
					fieldInfo = AccessTools.Field(typeof(LDBTool), text);
				}
				catch
				{
				}
				if (fieldInfo == null)
				{
					continue;
				}
				object obj2 = null;
				try
				{
					obj2 = fieldInfo.GetValue(null);
				}
				catch
				{
				}
				if (!(obj2 is IEnumerable enumerable))
				{
					continue;
				}
				foreach (object item in enumerable)
				{
					if (!(item is IEnumerable enumerable2))
					{
						continue;
					}
					foreach (object item2 in enumerable2)
					{
						T val = (T)((item2 is T) ? item2 : null);
						if (val != null)
						{
							yield return val;
						}
					}
				}
			}
		}

		private static HashSet<int> CollectUsedRecipeIds()
		{
			HashSet<int> hashSet = new HashSet<int>();
			RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
			foreach (RecipeProto val in dataArray)
			{
				if (val != null)
				{
					hashSet.Add(((Proto)val).ID);
				}
			}
			foreach (RecipeProto item in EnumeratePendingProtos<RecipeProto>())
			{
				hashSet.Add(((Proto)item).ID);
			}
			return hashSet;
		}

		private static void RegisterAllRecipes(HashSet<int> ids)
		{
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Definitions.Count == 0)
				{
					return;
				}
				if (ids == null)
				{
					ids = CollectUsedRecipeIds();
				}
				HashSet<int> usedGrids = CollectUsedGridIndexes();
				int nextId = ((ids.Count <= 0) ? 1 : (ids.Max() + 1));
				HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
				foreach (RecipeProto val in dataArray)
				{
					if (val != null && !string.IsNullOrEmpty(((Proto)val).Name))
					{
						hashSet.Add(((Proto)val).Name);
					}
				}
				foreach (RecipeDefinition definition in Definitions)
				{
					try
					{
						RecipeProto val2 = BuildRecipe(definition, ids, usedGrids, hashSet, ref nextId);
						if (val2 != null)
						{
							LDBTool.PostAddProto((Proto)(object)val2);
							AddedRecipes.Add(val2);
							RecipeDefinitions[val2] = definition;
							Log.LogInfo((object)$"已排队注入自定义配方 [{((Proto)val2).Name}] ID={((Proto)val2).ID} 类型={val2.Type} 耗时={(float)val2.TimeSpend / 60f:0.##}s 输入={DescribeItems(val2.Items, val2.ItemCounts)} 产出={DescribeItems(val2.Results, val2.ResultCounts)}");
						}
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"创建第 {definition.Index} 条自定义配方失败:{arg}");
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("注入自定义配方时发生异常:" + ex));
			}
		}

		private static RecipeProto BuildRecipe(RecipeDefinition def, HashSet<int> ids, HashSet<int> usedGrids, HashSet<string> usedNames, ref int nextId)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Invalid comparison between Unknown and I4
			//IL_0351: Unknown result type (might be due to invalid IL or missing references)
			//IL_0356: Unknown result type (might be due to invalid IL or missing references)
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			//IL_036e: Unknown result type (might be due to invalid IL or missing references)
			//IL_036f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0370: Unknown result type (might be due to invalid IL or missing references)
			//IL_0375: Unknown result type (might be due to invalid IL or missing references)
			//IL_037d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0389: Unknown result type (might be due to invalid IL or missing references)
			//IL_0395: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03af: Unknown result type (might be due to invalid IL or missing references)
			//IL_03db: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0407: Unknown result type (might be due to invalid IL or missing references)
			//IL_0410: Expected O, but got Unknown
			RecipeProto val = null;
			if (!string.IsNullOrEmpty(def.BaseSpec))
			{
				val = Lookup.FindRecipe(def.BaseSpec);
				if (val == null)
				{
					Log.LogWarning((object)$"配方 [{def.Name}] 的 base=\"{def.BaseSpec}\" 找不到,将只使用自定义内容。");
				}
			}
			ERecipeType type = (ERecipeType)4;
			if (!string.IsNullOrEmpty(def.TypeName))
			{
				if (!TryParseRecipeType(def.TypeName, out type))
				{
					type = (ERecipeType)((val == null) ? 4 : ((int)val.Type));
					Log.LogWarning((object)$"配方 [{def.Name}] 的类型 \"{def.TypeName}\" 无法识别,使用 {type}。");
				}
			}
			else if (val != null)
			{
				type = val.Type;
			}
			List<int> list = new List<int>();
			List<int> list2 = new List<int>();
			List<int> list3 = new List<int>();
			List<int> list4 = new List<int>();
			if (def.Inputs.Count == 0 && val != null)
			{
				list.AddRange(val.Items);
				list2.AddRange(val.ItemCounts);
			}
			else if (!ResolveIngredients(def, def.Inputs, list, list2, "输入"))
			{
				return null;
			}
			if (def.Outputs.Count == 0 && val != null)
			{
				list3.AddRange(val.Results);
				list4.AddRange(val.ResultCounts);
			}
			else if (!ResolveIngredients(def, def.Outputs, list3, list4, "产出"))
			{
				return null;
			}
			if (list.Count == 0 || list3.Count == 0)
			{
				Log.LogError((object)$"配方 [{def.Name}] 缺少输入或产出,已跳过。");
				return null;
			}
			if ((int)type == 8 && (list.Count != 1 || list3.Count != 1))
			{
				Log.LogError((object)$"配方 [{def.Name}] 是分馏配方,输入与产出都只能有 1 种物品,已跳过。");
				return null;
			}
			float num = def.TimeSeconds ?? ((val != null) ? ((float)val.TimeSpend / 60f) : 1f);
			int timeSpend = Mathf.Max(1, Mathf.RoundToInt(num * 60f));
			string text = def.Name;
			if (string.IsNullOrEmpty(text))
			{
				text = ((val != null) ? (((Proto)val).Name + " (自定义)") : ("自定义配方 " + def.Index));
			}
			string text2 = text;
			int num2 = 2;
			while (usedNames.Contains(text2))
			{
				text2 = text + " #" + num2;
				num2++;
			}
			usedNames.Add(text2);
			string text3 = def.Description;
			if (string.IsNullOrEmpty(text3))
			{
				text3 = $"自定义配方:{DescribeItems(list, list2)} → {DescribeItems(list3, list4)}";
			}
			TechProto val2 = null;
			if (def.Unlock == UnlockMode.Tech)
			{
				val2 = Lookup.FindTech(def.UnlockTech);
				if (val2 == null)
				{
					Log.LogWarning((object)$"配方 [{text2}] 指定的解锁科技 \"{def.UnlockTech}\" 找不到,改为直接解锁。");
				}
			}
			int num3;
			if (def.GridIndex.HasValue && def.GridIndex.Value > 0)
			{
				num3 = def.GridIndex.Value;
				if (num3 < 1101)
				{
					Log.LogWarning((object)$"配方 [{text2}] 的 gridIndex={num3} 小于 1101,配方可能不会显示在界面中。");
				}
				usedGrids.Add(num3);
			}
			else
			{
				num3 = AllocateGridIndex(usedGrids);
			}
			int i;
			for (i = nextId; ids.Contains(i); i++)
			{
			}
			nextId = i + 1;
			ids.Add(i);
			return new RecipeProto
			{
				ID = i,
				Name = text2,
				Description = text3,
				Type = type,
				TimeSpend = timeSpend,
				Items = list.ToArray(),
				ItemCounts = list2.ToArray(),
				Results = list3.ToArray(),
				ResultCounts = list4.ToArray(),
				Handcraft = (def.Handcraft ?? val?.Handcraft ?? false),
				Explicit = (def.Explicit ?? true),
				NonProductive = false,
				IconPath = string.Empty,
				GridIndex = num3,
				preTech = val2
			};
		}

		private static bool ResolveIngredients(RecipeDefinition def, List<IngredientSpec> specs, List<int> itemIds, List<int> counts, string label)
		{
			foreach (IngredientSpec spec in specs)
			{
				ItemProto val = Lookup.FindItem(spec.Item);
				if (val == null)
				{
					Log.LogError((object)$"配方 [{def.Name}] 的{label}物品 \"{spec.Item}\" 找不到,已跳过该配方。");
					return false;
				}
				if (spec.Count <= 0)
				{
					Log.LogError((object)$"配方 [{def.Name}] 的{label}物品 \"{spec.Item}\" 数量必须大于 0,已跳过该配方。");
					return false;
				}
				itemIds.Add(((Proto)val).ID);
				counts.Add(spec.Count);
			}
			return true;
		}

		private static int AllocateGridIndex(HashSet<int> used)
		{
			for (int i = 1; i <= 2; i++)
			{
				for (int j = 1; j <= 8; j++)
				{
					for (int k = 1; k <= 12; k++)
					{
						int num = i * 1000 + j * 100 + k;
						if (!used.Contains(num))
						{
							used.Add(num);
							return num;
						}
					}
				}
			}
			for (int l = 3; l <= 9; l++)
			{
				for (int m = 1; m <= 8; m++)
				{
					for (int n = 1; n <= 12; n++)
					{
						int num2 = l * 1000 + m * 100 + n;
						if (!used.Contains(num2))
						{
							used.Add(num2);
							if (!_warnedGridOverflow)
							{
								_warnedGridOverflow = true;
								Log.LogWarning((object)"配方图标格子前两页已占满,新配方被放到第 3 页以后,可能不会显示在配方选择器中,建议手动指定 gridIndex。");
							}
							return num2;
						}
					}
				}
			}
			int num3;
			for (num3 = 91001; used.Contains(num3); num3++)
			{
			}
			used.Add(num3);
			return num3;
		}

		private static string DescribeItems(IList<int> ids, IList<int> counts)
		{
			if (ids == null || counts == null || ids.Count == 0)
			{
				return "无";
			}
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < ids.Count; i++)
			{
				if (i > 0)
				{
					stringBuilder.Append(" + ");
				}
				ItemProto val = ((ProtoSet<ItemProto>)(object)LDB.items).Select(ids[i]);
				string value = ((val != null) ? Localization.Translate(((Proto)val).Name) : ("#" + ids[i]));
				stringBuilder.Append(value);
				if (i < counts.Count && counts[i] != 1)
				{
					stringBuilder.Append(" x").Append(counts[i]);
				}
			}
			return stringBuilder.ToString();
		}

		internal static bool TryParseRecipeType(string raw, out ERecipeType type)
		{
			//IL_04bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bf: Expected I4, but got Unknown
			type = (ERecipeType)4;
			if (string.IsNullOrEmpty(raw))
			{
				return false;
			}
			string text = raw.Trim();
			switch (text.ToLowerInvariant())
			{
			case "none":
				type = (ERecipeType)0;
				return true;
			case "smelting":
			case "熔炼":
			case "冶炼":
			case "smelt":
				type = (ERecipeType)1;
				return true;
			case "chemical":
			case "化工":
			case "化工厂":
				type = (ERecipeType)2;
				return true;
			case "refining":
			case "精炼":
			case "精炼厂":
			case "refine":
				type = (ERecipeType)3;
				return true;
			case "assemble":
			case "组装":
			case "制造":
			case "制造台":
			case "assembler":
				type = (ERecipeType)4;
				return true;
			case "particle":
			case "粒子":
			case "对撞":
				type = (ERecipeType)5;
				return true;
			case "exchange":
			case "交换":
				type = (ERecipeType)6;
				return true;
			case "光子":
			case "photon":
			case "photonstore":
				type = (ERecipeType)7;
				return true;
			case "fraction":
			case "分馏":
			case "分馏塔":
			case "fractionate":
				type = (ERecipeType)8;
				return true;
			case "research":
			case "研究":
			case "研究站":
				type = (ERecipeType)15;
				return true;
			default:
			{
				if (Enum.TryParse<ERecipeType>(text, ignoreCase: true, out ERecipeType result))
				{
					type = (ERecipeType)(int)result;
					return true;
				}
				return false;
			}
			}
		}

		private IEnumerator FinishSetupRoutine()
		{
			if (SetupFinished)
			{
				yield break;
			}
			float waited = 0f;
			while (!LDBToolFinished && waited < 20f)
			{
				waited += Time.unscaledDeltaTime;
				yield return null;
			}
			yield return null;
			if (!LDBToolFinished)
			{
				Log.LogWarning((object)"LDBTool 未在预期时间内完成注入,仍尝试继续。");
			}
			if (AddedRecipes.Count == 0 && Definitions.Count > 0)
			{
				Log.LogWarning((object)"自定义配方没有在 LDB 加载阶段注册,尝试补救注入。");
				TryFallbackRegister();
			}
			if (AddedRecipes.Count == 0)
			{
				Log.LogWarning((object)"没有需要添加的自定义配方。");
				SetupFinished = true;
				yield break;
			}
			try
			{
				AppliedCount = ApplyRecipes();
				RebuildItemRecipeCaches();
				RecreateIcons();
				SetupFinished = true;
				Log.LogInfo((object)$"自定义配方初始化完成:{AppliedCount}/{AddedRecipes.Count}。");
				if (GameMain.history != null)
				{
					UnlockAll(GameMain.history, "初始化");
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("初始化自定义配方失败:" + ex));
			}
		}

		private static void TryFallbackRegister()
		{
			try
			{
				RegisterAllRecipes(CollectUsedRecipeIds());
				if (AddedRecipes.Count == 0)
				{
					return;
				}
				MethodInfo method = typeof(LDBTool).GetMethod("AddProtosToSet", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (method == null)
				{
					Log.LogError((object)"补救注入失败:找不到 LDBTool.AddProtosToSet。");
					return;
				}
				List<Proto> list = new List<Proto>();
				foreach (RecipeProto addedRecipe in AddedRecipes)
				{
					list.Add((Proto)(object)addedRecipe);
				}
				method.MakeGenericMethod(typeof(RecipeProto)).Invoke(null, new object[2]
				{
					LDB.recipes,
					list
				});
				Log.LogWarning((object)("已使用补救方式注入 " + list.Count + " 条自定义配方。"));
			}
			catch (Exception ex)
			{
				Log.LogError((object)("补救注入失败:" + ex));
			}
		}

		private static int ApplyRecipes()
		{
			int num = 0;
			RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
			foreach (RecipeProto addedRecipe in AddedRecipes)
			{
				try
				{
					int num2 = Array.IndexOf(dataArray, addedRecipe);
					if (num2 < 0)
					{
						Log.LogError((object)$"配方 [{((Proto)addedRecipe).Name}] 没有出现在 LDB 中,可能与其它模组冲突,已跳过。");
						continue;
					}
					addedRecipe.Preload(num2);
					num++;
					if (RecipeDefinitions.TryGetValue(addedRecipe, out var value) && !string.IsNullOrEmpty(value.IconItem))
					{
						ItemProto val = Lookup.FindItem(value.IconItem);
						if (val != null && (Object)(object)val.iconSprite != (Object)null)
						{
							AccessTools.Field(typeof(RecipeProto), "_iconSprite")?.SetValue(addedRecipe, val.iconSprite);
						}
					}
				}
				catch (Exception arg)
				{
					Log.LogError((object)$"初始化配方 [{((Proto)addedRecipe).Name}] 失败:{arg}");
				}
			}
			return num;
		}

		private static void RebuildItemRecipeCaches()
		{
			if (_findRecipesMethod == null)
			{
				_findRecipesMethod = AccessTools.Method(typeof(ItemProto), "FindRecipes", (Type[])null, (Type[])null);
			}
			if (_findRecipesMethod == null)
			{
				Log.LogWarning((object)"找不到 ItemProto.FindRecipes,物品配方列表可能不会包含自定义配方。");
				return;
			}
			ItemProto[] dataArray = ((ProtoSet<ItemProto>)(object)LDB.items).dataArray;
			foreach (ItemProto val in dataArray)
			{
				if (val != null)
				{
					try
					{
						val.recipes = null;
						_findRecipesMethod.Invoke(val, null);
					}
					catch (Exception ex)
					{
						Log.LogWarning((object)("重建物品配方缓存失败(" + (((Proto)val).Name ?? "?") + "):" + ex.Message));
					}
				}
			}
		}

		private static void RecreateIcons()
		{
			try
			{
				if (GameMain.iconSet != null)
				{
					GameMain.iconSet.loaded = false;
					GameMain.iconSet.Create();
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("重建图标集失败(配方图标可能不显示):" + ex.Message));
			}
		}

		private static void UnlockAll(GameHistoryData history, string source)
		{
			if (history == null || AddedRecipes.Count == 0)
			{
				return;
			}
			int num = 0;
			foreach (RecipeProto addedRecipe in AddedRecipes)
			{
				try
				{
					RecipeDefinition recipeDefinition = null;
					if (RecipeDefinitions.TryGetValue(addedRecipe, out var value))
					{
						recipeDefinition = value;
					}
					if ((recipeDefinition == null || recipeDefinition.Unlock != UnlockMode.Never) && (recipeDefinition == null || recipeDefinition.Unlock != UnlockMode.Tech || addedRecipe.preTech == null || history.TechUnlocked(((Proto)addedRecipe.preTech).ID)) && !history.RecipeUnlocked(((Proto)addedRecipe).ID))
					{
						InvokeUnlockRecipe(history, ((Proto)addedRecipe).ID);
						num++;
					}
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)$"解锁配方 [{((Proto)addedRecipe).Name}] 失败:{ex.Message}");
				}
			}
			if (num > 0)
			{
				Log.LogInfo((object)$"({source})已解锁 {num} 条自定义配方。");
			}
		}

		private static void InvokeUnlockRecipe(GameHistoryData history, int recipeId)
		{
			if (_unlockRecipeMethod == null)
			{
				_unlockRecipeMethod = AccessTools.Method(typeof(GameHistoryData), "UnlockRecipe", new Type[1] { typeof(int) }, (Type[])null);
			}
			if (_unlockRecipeMethod == null)
			{
				Log.LogWarning((object)"找不到 GameHistoryData.UnlockRecipe,自定义配方可能不会自动解锁。");
				return;
			}
			_unlockRecipeMethod.Invoke(history, new object[1] { recipeId });
		}
	}
	public enum UnlockMode
	{
		Always,
		Tech,
		Never
	}
	public sealed class IngredientSpec
	{
		public string Item;

		public int Count = 1;

		public IngredientSpec()
		{
		}

		public IngredientSpec(string item, int count)
		{
			Item = item;
			Count = count;
		}

		public override string ToString()
		{
			return Item + " x" + Count;
		}
	}
	public sealed class RecipeDefinition
	{
		public int Index;

		public string Name;

		public string Description;

		public string BaseSpec;

		public string TypeName;

		public float? TimeSeconds;

		public List<IngredientSpec> Inputs = new List<IngredientSpec>();

		public List<IngredientSpec> Outputs = new List<IngredientSpec>();

		public bool? Handcraft;

		public UnlockMode Unlock;

		public string UnlockTech;

		public int? GridIndex;

		public string IconItem;

		public bool? Explicit;

		public static List<RecipeDefinition> ParseAll(JsonValue root)
		{
			List<RecipeDefinition> list = new List<RecipeDefinition>();
			if (root == null || root.IsNull)
			{
				return list;
			}
			JsonValue jsonValue = root;
			if (root.Kind == JsonKind.Object)
			{
				jsonValue = root.Get("recipes") ?? root.Get("items") ?? root.Get("list");
			}
			List<JsonValue> list2 = jsonValue?.AsArray();
			if (list2 == null)
			{
				throw new FormatException("配置文件根节点必须是数组,或者包含 \"recipes\" 数组的对象。");
			}
			for (int i = 0; i < list2.Count; i++)
			{
				JsonValue jsonValue2 = list2[i];
				if (jsonValue2 != null && jsonValue2.Kind == JsonKind.Object)
				{
					RecipeDefinition item = ParseOne(jsonValue2, i);
					list.Add(item);
				}
			}
			return list;
		}

		private static RecipeDefinition ParseOne(JsonValue entry, int index)
		{
			RecipeDefinition recipeDefinition = new RecipeDefinition
			{
				Index = index + 1
			};
			recipeDefinition.Name = ReadFirstString(entry, "name", "名称", "displayName");
			recipeDefinition.Description = ReadFirstString(entry, "description", "desc", "描述");
			recipeDefinition.BaseSpec = ReadFirstString(entry, "base", "from", "copyFrom", "基础配方");
			recipeDefinition.TypeName = ReadFirstString(entry, "type", "recipeType", "类型");
			recipeDefinition.IconItem = ReadFirstString(entry, "icon", "iconItem", "图标");
			recipeDefinition.UnlockTech = ReadFirstString(entry, "tech", "unlockTech", "解锁科技");
			JsonValue first = GetFirst(entry, "time", "timeSpend", "seconds", "时间", "耗时");
			if (first != null)
			{
				double? num = first.AsNumber();
				if (num.HasValue)
				{
					recipeDefinition.TimeSeconds = (float)num.Value;
				}
			}
			JsonValue first2 = GetFirst(entry, "gridIndex", "grid", "格子");
			if (first2 != null)
			{
				recipeDefinition.GridIndex = first2.AsInt();
			}
			JsonValue first3 = GetFirst(entry, "handcraft", "manual", "手搓");
			if (first3 != null)
			{
				recipeDefinition.Handcraft = first3.AsBool();
			}
			JsonValue first4 = GetFirst(entry, "explicit");
			if (first4 != null)
			{
				recipeDefinition.Explicit = first4.AsBool();
			}
			recipeDefinition.Inputs = ParseIngredients(GetFirst(entry, "inputs", "input", "in", "输入", "原料"));
			recipeDefinition.Outputs = ParseIngredients(GetFirst(entry, "outputs", "output", "out", "results", "产出", "输出", "产物"));
			JsonValue first5 = GetFirst(entry, "unlock", "解锁");
			if (first5 != null)
			{
				ApplyUnlock(recipeDefinition, first5.AsString());
			}
			else if (!string.IsNullOrEmpty(recipeDefinition.UnlockTech))
			{
				recipeDefinition.Unlock = UnlockMode.Tech;
			}
			return recipeDefinition;
		}

		private static void ApplyUnlock(RecipeDefinition def, string raw)
		{
			if (string.IsNullOrEmpty(raw))
			{
				return;
			}
			string text = raw.Trim();
			string text2 = text.ToLowerInvariant();
			if (!(text2 == "always"))
			{
				switch (text2)
				{
				default:
					if (text == "总是" || text == "默认")
					{
						break;
					}
					switch (text2)
					{
					default:
						if (!(text == "从不") && !(text == "无"))
						{
							if (text2.StartsWith("tech:"))
							{
								def.Unlock = UnlockMode.Tech;
								def.UnlockTech = text.Substring(5).Trim();
							}
							else
							{
								def.Unlock = UnlockMode.Tech;
								def.UnlockTech = text;
							}
							break;
						}
						goto case "never";
					case "never":
					case "none":
					case "false":
						def.Unlock = UnlockMode.Never;
						break;
					}
					return;
				case "true":
				case "auto":
				case "always":
					break;
				}
			}
			def.Unlock = UnlockMode.Always;
		}

		private static List<IngredientSpec> ParseIngredients(JsonValue value)
		{
			List<IngredientSpec> list = new List<IngredientSpec>();
			if (value == null || value.IsNull)
			{
				return list;
			}
			List<JsonValue> list2 = value.AsArray();
			if (list2 != null)
			{
				foreach (JsonValue item in list2)
				{
					if (item != null && !item.IsNull)
					{
						if (item.Kind == JsonKind.String || item.Kind == JsonKind.Number)
						{
							list.Add(new IngredientSpec(item.AsString(), 1));
						}
						else if (item.Kind == JsonKind.Object)
						{
							string text = ReadFirstString(item, "item", "id", "name", "物品", "物品ID");
							int num = 1;
							JsonValue first = GetFirst(item, "count", "amount", "num", "数量");
							if (first != null)
							{
								int? num2 = first.AsInt();
								if (num2.HasValue)
								{
									num = num2.Value;
								}
							}
							if (!string.IsNullOrEmpty(text) && num > 0)
							{
								list.Add(new IngredientSpec(text, num));
							}
						}
					}
				}
				return list;
			}
			if (value.Kind == JsonKind.Object)
			{
				foreach (KeyValuePair<string, JsonValue> item2 in value.ObjectValue)
				{
					int? num3 = item2.Value.AsInt();
					if (num3.HasValue && num3.Value > 0)
					{
						list.Add(new IngredientSpec(item2.Key, num3.Value));
					}
				}
			}
			return list;
		}

		private static JsonValue GetFirst(JsonValue obj, params string[] keys)
		{
			foreach (string key in keys)
			{
				JsonValue jsonValue = obj.Get(key);
				if (jsonValue != null)
				{
					return jsonValue;
				}
			}
			return null;
		}

		private static string ReadFirstString(JsonValue obj, params string[] keys)
		{
			return GetFirst(obj, keys)?.AsString();
		}
	}
}