Decompiled source of ItemReplacer v1.0.1

UserLibs/Scriban.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Numerics;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Scriban.Functions;
using Scriban.Helpers;
using Scriban.Parsing;
using Scriban.Runtime;
using Scriban.Runtime.Accessors;
using Scriban.Syntax;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("Scriban.Tests")]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("Alexandre Mutel")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Alexandre Mutel")]
[assembly: AssemblyDescription("Scriban is a fast, powerful, safe and lightweight scripting language and engine for .NET, which was primarily developed for text templating with a compatibility mode for parsing liquid templates.")]
[assembly: AssemblyFileVersion("6.5.7.0")]
[assembly: AssemblyInformationalVersion("6.5.7+44876ae9de7b2855f1eb51d041c7b3ce5e1fe224")]
[assembly: AssemblyProduct("Scriban")]
[assembly: AssemblyTitle("Scriban")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/scriban/scriban")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: AssemblyVersion("6.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace Scriban
{
	[DebuggerDisplay("Count: {Count}")]
	public class LogMessageBag : IReadOnlyList<LogMessage>, IEnumerable<LogMessage>, IEnumerable, IReadOnlyCollection<LogMessage>
	{
		private readonly List<LogMessage> _messages;

		public int Count => _messages.Count;

		public LogMessage this[int index] => _messages[index];

		public bool HasErrors { get; private set; }

		public LogMessageBag()
		{
			_messages = new List<LogMessage>();
		}

		public void Add(LogMessage message)
		{
			if (message == null)
			{
				throw new ArgumentNullException("message");
			}
			if (message.Type == ParserMessageType.Error)
			{
				HasErrors = true;
			}
			_messages.Add(message);
		}

		public void AddRange(IEnumerable<LogMessage> messages)
		{
			if (messages == null)
			{
				throw new ArgumentNullException("messages");
			}
			foreach (LogMessage message in messages)
			{
				Add(message);
			}
		}

		public IEnumerator<LogMessage> GetEnumerator()
		{
			return _messages.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return ((IEnumerable)_messages).GetEnumerator();
		}

		public override string ToString()
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (LogMessage message in _messages)
			{
				stringBuilder.AppendLine(message.ToString());
			}
			return stringBuilder.ToString();
		}
	}
	public class Template
	{
		public string SourceFilePath { get; }

		public ScriptPage Page { get; private set; }

		public bool HasErrors { get; private set; }

		public LogMessageBag Messages { get; }

		public ParserOptions ParserOptions { get; }

		public LexerOptions LexerOptions { get; }

		private async ValueTask<object> EvaluateAndRenderAsync(TemplateContext context, bool render)
		{
			if (context == null)
			{
				throw new ArgumentNullException("context");
			}
			CheckErrors();
			if (SourceFilePath != null)
			{
				context.PushSourceFile(SourceFilePath);
			}
			try
			{
				context.UseScientific = LexerOptions.Lang == ScriptLang.Scientific;
				object result = await context.EvaluateAsync(Page).ConfigureAwait(continueOnCapturedContext: false);
				if (render && Page != null && context.EnableOutput && result != null)
				{
					await context.WriteAsync(Page.Span, result).ConfigureAwait(continueOnCapturedContext: false);
				}
				return result;
			}
			finally
			{
				if (SourceFilePath != null)
				{
					context.PopSourceFile();
				}
			}
		}

		public async ValueTask<object> EvaluateAsync(TemplateContext context)
		{
			bool previousOutput = context.EnableOutput;
			try
			{
				context.UseScientific = LexerOptions.Lang == ScriptLang.Scientific;
				context.EnableOutput = false;
				return await EvaluateAndRenderAsync(context, render: false).ConfigureAwait(continueOnCapturedContext: false);
			}
			finally
			{
				context.EnableOutput = previousOutput;
			}
		}

		public static async ValueTask<object> EvaluateAsync(string expression, TemplateContext context)
		{
			if (expression == null)
			{
				throw new ArgumentNullException("expression");
			}
			LexerOptions lexerOptions = default(LexerOptions);
			lexerOptions.Mode = ScriptMode.ScriptOnly;
			LexerOptions value = lexerOptions;
			LexerOptions? lexerOptions2 = value;
			return await Parse(expression, null, null, lexerOptions2).EvaluateAsync(context).ConfigureAwait(continueOnCapturedContext: false);
		}

		public async ValueTask<object> EvaluateAsync(object model = null, MemberRenamerDelegate memberRenamer = null, MemberFilterDelegate memberFilter = null)
		{
			ScriptObject scriptObject = new ScriptObject();
			if (model != null)
			{
				scriptObject.Import(model, memberFilter, memberRenamer);
			}
			TemplateContext context = ((LexerOptions.Lang == ScriptLang.Liquid) ? new LiquidTemplateContext() : new TemplateContext());
			context.EnableOutput = false;
			context.MemberRenamer = memberRenamer;
			context.MemberFilter = memberFilter;
			context.UseScientific = LexerOptions.Lang == ScriptLang.Scientific;
			context.PushGlobal(scriptObject);
			object result = await EvaluateAsync(context).ConfigureAwait(continueOnCapturedContext: false);
			context.PopGlobal();
			return result;
		}

		public static async ValueTask<object> EvaluateAsync(string expression, object model, MemberRenamerDelegate memberRenamer = null, MemberFilterDelegate memberFilter = null)
		{
			if (expression == null)
			{
				throw new ArgumentNullException("expression");
			}
			LexerOptions lexerOptions = default(LexerOptions);
			lexerOptions.Mode = ScriptMode.ScriptOnly;
			LexerOptions value = lexerOptions;
			LexerOptions? lexerOptions2 = value;
			return await Parse(expression, null, null, lexerOptions2).EvaluateAsync(model, memberRenamer, memberFilter).ConfigureAwait(continueOnCapturedContext: false);
		}

		public async ValueTask<string> RenderAsync(TemplateContext context)
		{
			await EvaluateAndRenderAsync(context, render: true).ConfigureAwait(continueOnCapturedContext: false);
			string? result = context.Output.ToString();
			if (context.Output is StringBuilderOutput stringBuilderOutput)
			{
				stringBuilderOutput.Builder.Length = 0;
			}
			return result;
		}

		public async ValueTask<string> RenderAsync(object model = null, MemberRenamerDelegate memberRenamer = null, MemberFilterDelegate memberFilter = null)
		{
			ScriptObject scriptObject = new ScriptObject();
			if (model != null)
			{
				scriptObject.Import(model, memberFilter, memberRenamer);
			}
			TemplateContext templateContext = ((LexerOptions.Lang == ScriptLang.Liquid) ? new LiquidTemplateContext() : new TemplateContext());
			templateContext.MemberRenamer = memberRenamer;
			templateContext.MemberFilter = memberFilter;
			templateContext.PushGlobal(scriptObject);
			return await RenderAsync(templateContext).ConfigureAwait(continueOnCapturedContext: false);
		}

		private Template(ParserOptions? parserOptions, LexerOptions? lexerOptions, string sourceFilePath)
		{
			ParserOptions = parserOptions.GetValueOrDefault();
			LexerOptions = lexerOptions.GetValueOrDefault();
			Messages = new LogMessageBag();
			SourceFilePath = sourceFilePath;
		}

		public static Template Parse(string text, string sourceFilePath = null, ParserOptions? parserOptions = null, LexerOptions? lexerOptions = null)
		{
			Template template = new Template(parserOptions, lexerOptions, sourceFilePath);
			template.ParseInternal(text, sourceFilePath);
			return template;
		}

		public static Template ParseLiquid(string text, string sourceFilePath = null, ParserOptions? parserOptions = null, LexerOptions? lexerOptions = null)
		{
			LexerOptions valueOrDefault = lexerOptions.GetValueOrDefault();
			valueOrDefault.Lang = ScriptLang.Liquid;
			return Parse(text, sourceFilePath, parserOptions, valueOrDefault);
		}

		public static object Evaluate(string expression, TemplateContext context)
		{
			if (expression == null)
			{
				throw new ArgumentNullException("expression");
			}
			LexerOptions lexerOptions = default(LexerOptions);
			lexerOptions.Mode = ScriptMode.ScriptOnly;
			LexerOptions value = lexerOptions;
			LexerOptions? lexerOptions2 = value;
			return Parse(expression, null, null, lexerOptions2).Evaluate(context);
		}

		public static object Evaluate(string expression, object model, MemberRenamerDelegate memberRenamer = null, MemberFilterDelegate memberFilter = null)
		{
			if (expression == null)
			{
				throw new ArgumentNullException("expression");
			}
			LexerOptions lexerOptions = default(LexerOptions);
			lexerOptions.Mode = ScriptMode.ScriptOnly;
			LexerOptions value = lexerOptions;
			LexerOptions? lexerOptions2 = value;
			return Parse(expression, null, null, lexerOptions2).Evaluate(model, memberRenamer, memberFilter);
		}

		public object Evaluate(TemplateContext context)
		{
			bool enableOutput = context.EnableOutput;
			try
			{
				context.UseScientific = LexerOptions.Lang == ScriptLang.Scientific;
				context.EnableOutput = false;
				return EvaluateAndRender(context, render: false);
			}
			finally
			{
				context.EnableOutput = enableOutput;
			}
		}

		public object Evaluate(object model = null, MemberRenamerDelegate memberRenamer = null, MemberFilterDelegate memberFilter = null)
		{
			ScriptObject scriptObject = new ScriptObject();
			if (model != null)
			{
				scriptObject.Import(model, memberFilter, memberRenamer);
			}
			TemplateContext templateContext = ((LexerOptions.Lang == ScriptLang.Liquid) ? new LiquidTemplateContext() : new TemplateContext());
			templateContext.EnableOutput = false;
			templateContext.MemberRenamer = memberRenamer;
			templateContext.MemberFilter = memberFilter;
			templateContext.UseScientific = LexerOptions.Lang == ScriptLang.Scientific;
			templateContext.PushGlobal(scriptObject);
			object result = Evaluate(templateContext);
			templateContext.PopGlobal();
			return result;
		}

		public string Render(TemplateContext context)
		{
			EvaluateAndRender(context, render: true);
			string? result = context.Output.ToString();
			if (context.Output is StringBuilderOutput stringBuilderOutput)
			{
				stringBuilderOutput.Builder.Length = 0;
			}
			return result;
		}

		public string Render(object model = null, MemberRenamerDelegate memberRenamer = null, MemberFilterDelegate memberFilter = null)
		{
			ScriptObject scriptObject = new ScriptObject();
			if (model != null)
			{
				scriptObject.Import(model, memberFilter, memberRenamer);
			}
			TemplateContext templateContext = ((LexerOptions.Lang == ScriptLang.Liquid) ? new LiquidTemplateContext() : new TemplateContext());
			templateContext.MemberRenamer = memberRenamer;
			templateContext.MemberFilter = memberFilter;
			templateContext.PushGlobal(scriptObject);
			return Render(templateContext);
		}

		public string ToText(ScriptPrinterOptions options = default(ScriptPrinterOptions))
		{
			CheckErrors();
			TextWriterOutput textWriterOutput = new TextWriterOutput();
			new ScriptPrinter(textWriterOutput, options).Write(Page);
			return textWriterOutput.ToString();
		}

		private object EvaluateAndRender(TemplateContext context, bool render)
		{
			if (context == null)
			{
				throw new ArgumentNullException("context");
			}
			CheckErrors();
			if (SourceFilePath != null)
			{
				context.PushSourceFile(SourceFilePath);
			}
			try
			{
				context.UseScientific = LexerOptions.Lang == ScriptLang.Scientific;
				object obj = context.Evaluate(Page);
				if (render && Page != null && context.EnableOutput && obj != null)
				{
					context.Write(Page.Span, obj);
				}
				return obj;
			}
			finally
			{
				if (SourceFilePath != null)
				{
					context.PopSourceFile();
				}
			}
		}

		private void CheckErrors()
		{
			if (HasErrors)
			{
				throw new InvalidOperationException("This template has errors. Check the <Template.HasError> and <Template.Messages> before evaluating a template. Messages:\n" + string.Join("\n", Messages));
			}
		}

		private void ParseInternal(string text, string sourceFilePath)
		{
			if (string.IsNullOrEmpty(text))
			{
				HasErrors = false;
				Page = new ScriptPage
				{
					Span = new SourceSpan(sourceFilePath, default(TextPosition), TextPosition.Eof)
				};
			}
			else
			{
				Parser parser = new Parser(new Lexer(text, sourceFilePath, LexerOptions), ParserOptions);
				Page = parser.Run();
				HasErrors = parser.HasErrors;
				Messages.AddRange(parser.Messages);
			}
		}
	}
	public class TemplateContext : IFormatProvider
	{
		public delegate bool TryGetMemberDelegate(TemplateContext context, SourceSpan span, object target, string member, out object value);

		public delegate bool TryGetVariableDelegate(TemplateContext context, SourceSpan span, ScriptVariable variable, out object value);

		public delegate string RenderRuntimeExceptionDelegate(ScriptRuntimeException exception);

		internal enum LoopType
		{
			Default,
			Queryable
		}

		private class VariableContext
		{
			public IScriptObject LocalObject;

			public FastStack<ScriptObject> Loops;

			public VariableContext(IScriptObject localObject)
			{
				LocalObject = localObject;
				Loops = new FastStack<ScriptObject>(4);
			}
		}

		private enum VariableScope
		{
			Local,
			Loop
		}

		[CompilerGenerated]
		private sealed class <GetStoreForRead>d__306 : IEnumerable<IScriptObject>, IEnumerable, IEnumerator<IScriptObject>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private IScriptObject <>2__current;

			private int <>l__initialThreadId;

			private ScriptVariable variable;

			public ScriptVariable <>3__variable;

			public TemplateContext <>4__this;

			private bool <isInLoop>5__2;

			private int <i>5__3;

			private VariableContext <context>5__4;

			private ScriptObject[] <loopItems>5__5;

			private int <j>5__6;

			IScriptObject IEnumerator<IScriptObject>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <GetStoreForRead>d__306(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<context>5__4 = null;
				<loopItems>5__5 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				TemplateContext templateContext = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
				{
					<>1__state = -1;
					ScriptVariableScope scope = variable.Scope;
					if (scope != 0)
					{
						if (scope == ScriptVariableScope.Local)
						{
							<loopItems>5__5 = templateContext._currentLocalContext.Loops.Items;
							<i>5__3 = templateContext._currentLocalContext.Loops.Count - 1;
							goto IL_01bf;
						}
						throw new NotImplementedException($"Variable scope `{scope}` is not implemented");
					}
					<isInLoop>5__2 = templateContext.IsInLoop;
					<i>5__3 = templateContext._globalContexts.Count - 1;
					goto IL_0149;
				}
				case 1:
					<>1__state = -1;
					<j>5__6--;
					goto IL_00ff;
				case 2:
					<>1__state = -1;
					<context>5__4 = null;
					<i>5__3--;
					goto IL_0149;
				case 3:
					<>1__state = -1;
					<i>5__3--;
					goto IL_01bf;
				case 4:
					<>1__state = -1;
					break;
				case 5:
					{
						<>1__state = -1;
						break;
					}
					IL_0149:
					if (<i>5__3 < 0)
					{
						break;
					}
					<context>5__4 = templateContext._globalContexts.Items[<i>5__3];
					if (<isInLoop>5__2)
					{
						int count = <context>5__4.Loops.Count;
						if (count > 0)
						{
							<loopItems>5__5 = <context>5__4.Loops.Items;
							<j>5__6 = count - 1;
							goto IL_00ff;
						}
					}
					goto IL_010f;
					IL_010f:
					<>2__current = <context>5__4.LocalObject;
					<>1__state = 2;
					return true;
					IL_01bf:
					if (<i>5__3 >= 0)
					{
						<>2__current = <loopItems>5__5[<i>5__3];
						<>1__state = 3;
						return true;
					}
					if (templateContext._currentLocalContext.LocalObject != null)
					{
						<>2__current = templateContext._currentLocalContext.LocalObject;
						<>1__state = 4;
						return true;
					}
					if (templateContext._globalContexts.Count > 0)
					{
						<>2__current = templateContext._globalContexts.Peek().LocalObject;
						<>1__state = 5;
						return true;
					}
					throw new ScriptRuntimeException(variable.Span, $"Invalid usage of the local variable `{variable}` in the current context");
					IL_00ff:
					if (<j>5__6 >= 0)
					{
						<>2__current = <loopItems>5__5[<j>5__6];
						<>1__state = 1;
						return true;
					}
					<loopItems>5__5 = null;
					goto IL_010f;
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<IScriptObject> IEnumerable<IScriptObject>.GetEnumerator()
			{
				<GetStoreForRead>d__306 <GetStoreForRead>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<GetStoreForRead>d__ = this;
				}
				else
				{
					<GetStoreForRead>d__ = new <GetStoreForRead>d__306(0)
					{
						<>4__this = <>4__this
					};
				}
				<GetStoreForRead>d__.variable = <>3__variable;
				return <GetStoreForRead>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<IScriptObject>)this).GetEnumerator();
			}
		}

		private FastStack<ScriptObject> _availableStores;

		internal FastStack<ScriptBlockStatement> BlockDelegates;

		private FastStack<VariableContext> _globalContexts;

		private FastStack<CultureInfo> _cultures;

		private readonly Dictionary<Type, IListAccessor> _listAccessors;

		private FastStack<ScriptLoopStatementBase> _loops;

		private readonly Dictionary<Type, IObjectAccessor> _memberAccessors;

		private FastStack<IScriptOutput> _outputs;

		private FastStack<VariableContext> _localContexts;

		private VariableContext _currentLocalContext;

		private IScriptOutput _output;

		private FastStack<string> _sourceFiles;

		private FastStack<object> _caseValues;

		private int _callDepth;

		private bool _isFunctionCallDisabled;

		private int _loopStep;

		private int _getOrSetValueLevel;

		private FastStack<VariableContext> _availableGlobalContexts;

		private FastStack<VariableContext> _availableLocalContexts;

		private FastStack<ScriptPipeArguments> _availablePipeArguments;

		private FastStack<ScriptPipeArguments> _pipeArguments;

		private FastStack<List<ScriptExpression>> _availableScriptExpressionLists;

		private object[][] _availableReflectionArguments;

		private ScriptPipeArguments _currentPipeArguments;

		private bool _previousTextWasNewLine;

		private readonly IEqualityComparer<string> _keyComparer;

		public static RenderRuntimeExceptionDelegate RenderRuntimeExceptionDefault = (ScriptRuntimeException ex) => $"[{ex.OriginalMessage}]";

		private static readonly object TrueObject = true;

		private static readonly object FalseObject = false;

		private int _objectToStringLevel;

		private int _currentToStringLength;

		internal bool AllowPipeArguments => _getOrSetValueLevel <= 1;

		public CultureInfo CurrentCulture
		{
			get
			{
				if (_cultures.Count != 0)
				{
					return _cultures.Peek();
				}
				return CultureInfo.InvariantCulture;
			}
		}

		public ITemplateLoader TemplateLoader { get; set; }

		public bool IsLiquid { get; protected set; }

		public bool AutoIndent { get; set; }

		public bool IndentOnEmptyLines { get; set; }

		[Obsolete("Use AutoIndent instead. Note that AutoIndent is true by default.")]
		public bool IndentWithInclude
		{
			get
			{
				return AutoIndent;
			}
			set
			{
				AutoIndent = value;
			}
		}

		public int LimitToString { get; set; }

		public int ObjectRecursionLimit { get; set; }

		public string NewLine { get; set; }

		public ScriptLang Language { get; set; }

		public CancellationToken CancellationToken { get; set; }

		public ParserOptions TemplateLoaderParserOptions { get; set; }

		public LexerOptions TemplateLoaderLexerOptions { get; set; }

		public MemberRenamerDelegate MemberRenamer { get; set; }

		public MemberFilterDelegate MemberFilter { get; set; }

		public int LoopLimit { get; set; }

		public int? LoopLimitQueryable { get; set; }

		public int RecursiveLimit { get; set; }

		public bool EnableOutput { get; set; }

		public IScriptOutput Output => _output;

		public bool UseScientific { get; set; }

		public bool ErrorForStatementFunctionAsExpression { get; set; }

		public ScriptObject BuiltinObject { get; }

		public IScriptObject CurrentGlobal => _globalContexts.Peek()?.LocalObject;

		public Dictionary<string, Template> CachedTemplates { get; }

		public string CurrentSourceFile => _sourceFiles.Peek();

		public TryGetVariableDelegate TryGetVariable { get; set; }

		public RenderRuntimeExceptionDelegate RenderRuntimeException { get; set; }

		public TryGetMemberDelegate TryGetMember { get; set; }

		public Dictionary<object, object> Tags { get; }

		internal ScriptPipeArguments CurrentPipeArguments => _currentPipeArguments;

		public int GlobalCount => _globalContexts.Count;

		public int OutputCount => _outputs.Count;

		public int CultureCount => _cultures.Count;

		public int SourceFileCount => _sourceFiles.Count;

		internal ScriptFlowState FlowState { get; set; }

		public TimeSpan RegexTimeOut { get; set; }

		public bool StrictVariables { get; set; }

		public bool EnableBreakAndContinueAsReturnOutsideLoop { get; set; }

		public bool EnableRelaxedTargetAccess { get; set; }

		public bool EnableRelaxedMemberAccess { get; set; }

		public bool EnableRelaxedFunctionAccess { get; set; }

		public bool EnableRelaxedIndexerAccess { get; set; }

		public bool EnableNullIndexer { get; set; }

		public ScriptNode CurrentNode { get; private set; }

		public SourceSpan CurrentSpan => CurrentNode?.Span ?? default(SourceSpan);

		public string CurrentIndent { get; set; }

		internal bool IsInLoop => _loops.Count > 0;

		internal bool IgnoreExceptionsWhileRewritingScientific { get; set; }

		protected virtual async ValueTask<Template> CreateTemplateAsync(string templatePath, ScriptNode callerContext)
		{
			string text;
			try
			{
				text = await TemplateLoader.LoadAsync(this, callerContext.Span, templatePath).ConfigureAwait(continueOnCapturedContext: false);
			}
			catch (Exception ex) when (!(ex is ScriptRuntimeException))
			{
				throw new ScriptRuntimeException(callerContext.Span, "Unexpected exception while creating template from path `" + templatePath + "`", ex);
			}
			if (text == null)
			{
				throw new ScriptRuntimeException(callerContext.Span, "The result of including `" + templatePath + "` cannot be null");
			}
			Template template = Template.Parse(text, templatePath, TemplateLoaderParserOptions, TemplateLoaderLexerOptions);
			if (template.HasErrors)
			{
				throw new ScriptParserRuntimeException(callerContext.Span, "Error while parsing template `" + templatePath + "`", template.Messages);
			}
			CachedTemplates.Add(templatePath, template);
			return template;
		}

		public virtual async ValueTask<object> EvaluateAsync(ScriptNode scriptNode, bool aliasReturnedFunction)
		{
			if (scriptNode == null)
			{
				return null;
			}
			bool previousFunctionCallState = _isFunctionCallDisabled;
			int previousLevel = _getOrSetValueLevel;
			ScriptNode previousNode = CurrentNode;
			try
			{
				CurrentNode = scriptNode;
				_getOrSetValueLevel = 0;
				_isFunctionCallDisabled = aliasReturnedFunction;
				return await scriptNode.EvaluateAsync(this).ConfigureAwait(continueOnCapturedContext: false);
			}
			catch (ScriptRuntimeException exception) when (RenderRuntimeException != null)
			{
				return RenderRuntimeException(exception);
			}
			catch (Exception ex) when (!(ex is ScriptRuntimeException))
			{
				ScriptRuntimeException ex2 = new ScriptRuntimeException(scriptNode.Span, ex.Message, ex);
				if (RenderRuntimeException != null)
				{
					return RenderRuntimeException(ex2);
				}
				throw ex2;
			}
			finally
			{
				CurrentNode = previousNode;
				_getOrSetValueLevel = previousLevel;
				_isFunctionCallDisabled = previousFunctionCallState;
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public async ValueTask<object> EvaluateAsync(ScriptNode scriptNode)
		{
			return await EvaluateAsync(scriptNode, aliasReturnedFunction: false).ConfigureAwait(continueOnCapturedContext: false);
		}

		public async ValueTask<Template> GetOrCreateTemplateAsync(string templatePath, ScriptNode callerContext)
		{
			if (!CachedTemplates.TryGetValue(templatePath, out var value))
			{
				value = await CreateTemplateAsync(templatePath, callerContext).ConfigureAwait(continueOnCapturedContext: false);
				CachedTemplates[templatePath] = value;
			}
			return value;
		}

		private async ValueTask<object> GetOrSetValueAsync(ScriptExpression targetExpression, object valueToSet, bool setter)
		{
			object value = null;
			try
			{
				if (targetExpression is IScriptVariablePath scriptVariablePath)
				{
					if (setter)
					{
						await scriptVariablePath.SetValueAsync(this, valueToSet).ConfigureAwait(continueOnCapturedContext: false);
					}
					else
					{
						value = await scriptVariablePath.GetValueAsync(this).ConfigureAwait(continueOnCapturedContext: false);
					}
				}
				else
				{
					if (setter)
					{
						throw new ScriptRuntimeException(targetExpression.Span, "Unsupported target expression for assignment.");
					}
					value = await EvaluateAsync(targetExpression).ConfigureAwait(continueOnCapturedContext: false);
				}
			}
			catch (Exception ex) when (_getOrSetValueLevel == 1 && !(ex is ScriptRuntimeException))
			{
				throw new ScriptRuntimeException(targetExpression.Span, "Unexpected exception while accessing target expression: " + ex.Message, ex);
			}
			if (((_isFunctionCallDisabled && _getOrSetValueLevel > 1) || !_isFunctionCallDisabled) && ScriptFunctionCall.IsFunction(value))
			{
				value = await ScriptFunctionCall.CallAsync(this, targetExpression, value, _getOrSetValueLevel == 1, null).ConfigureAwait(continueOnCapturedContext: false);
			}
			return value;
		}

		public async ValueTask<object> GetValueAsync(ScriptExpression target)
		{
			ScriptNode previousNode = CurrentNode;
			_getOrSetValueLevel++;
			try
			{
				CurrentNode = target;
				return await GetOrSetValueAsync(target, null, setter: false).ConfigureAwait(continueOnCapturedContext: false);
			}
			finally
			{
				CurrentNode = previousNode;
				_getOrSetValueLevel--;
			}
		}

		public async ValueTask<string> RenderTemplateAsync(Template template, ScriptArray arguments, ScriptNode callerContext)
		{
			string result = null;
			EnterRecursive(callerContext);
			string previousIndent = CurrentIndent;
			CurrentIndent = null;
			PushOutput();
			PushLocal();
			try
			{
				SetValue(ScriptVariable.Arguments, arguments, asReadOnly: true, force: true);
				IEnumerable<ScriptNamedArgument> enumerable = (callerContext as ScriptFunctionCall)?.Arguments.OfType<ScriptNamedArgument>();
				if (enumerable != null)
				{
					foreach (ScriptNamedArgument item in enumerable)
					{
						string name = item.Name.Name;
						object value = await item.Value.EvaluateAsync(this).ConfigureAwait(continueOnCapturedContext: false);
						ScriptVariable variable = ScriptVariable.Create(name, ScriptVariableScope.Local);
						SetValue(variable, value, asReadOnly: false, force: true);
					}
				}
				if (previousIndent != null)
				{
					ResetPreviousNewLine();
				}
				result = await template.RenderAsync(this).ConfigureAwait(continueOnCapturedContext: false);
				if (previousIndent != null)
				{
					ResetPreviousNewLine();
				}
			}
			finally
			{
				PopLocal();
				PopOutput();
				CurrentIndent = previousIndent;
				ExitRecursive(callerContext);
			}
			return result;
		}

		public async ValueTask SetValueAsync(ScriptExpression target, object value)
		{
			if (target == null)
			{
				throw new ArgumentNullException("target");
			}
			_getOrSetValueLevel++;
			try
			{
				await GetOrSetValueAsync(target, value, setter: true).ConfigureAwait(continueOnCapturedContext: false);
			}
			finally
			{
				_getOrSetValueLevel--;
			}
		}

		public async ValueTask<TemplateContext> WriteAsync(string text, int startIndex, int count)
		{
			if (text != null)
			{
				if (CurrentIndent != null)
				{
					int index = startIndex;
					int indexEnd = startIndex + count;
					while (index < indexEnd)
					{
						int newLineIndex = text.IndexOf('\n', index);
						if (newLineIndex < 0 || newLineIndex >= indexEnd)
						{
							if (_previousTextWasNewLine)
							{
								await Output.WriteAsync(CurrentIndent, 0, CurrentIndent.Length, CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
								_previousTextWasNewLine = false;
							}
							await Output.WriteAsync(text, index, indexEnd - index, CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
							break;
						}
						int length = newLineIndex - index;
						if (_previousTextWasNewLine && (IndentOnEmptyLines || (length != 0 && (length != 1 || text[index] != '\r'))))
						{
							await Output.WriteAsync(CurrentIndent, 0, CurrentIndent.Length, CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
							_previousTextWasNewLine = false;
						}
						await Output.WriteAsync(text, index, length + 1, CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
						index = newLineIndex + 1;
						_previousTextWasNewLine = true;
					}
				}
				else
				{
					if (count > 0)
					{
						_previousTextWasNewLine = text[startIndex + count - 1] == '\n';
					}
					await Output.WriteAsync(text, startIndex, count, CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
				}
			}
			return this;
		}

		public async ValueTask<TemplateContext> WriteAsync(string text)
		{
			if (text != null)
			{
				await WriteAsync(text, 0, text.Length).ConfigureAwait(continueOnCapturedContext: false);
			}
			return this;
		}

		public async ValueTask<TemplateContext> WriteAsync(ScriptStringSlice slice)
		{
			await WriteAsync(slice.FullText, slice.Index, slice.Length).ConfigureAwait(continueOnCapturedContext: false);
			return this;
		}

		public virtual async ValueTask<TemplateContext> WriteAsync(SourceSpan span, object textAsObject)
		{
			if (textAsObject != null)
			{
				string text = ObjectToString(textAsObject);
				await WriteAsync(text).ConfigureAwait(continueOnCapturedContext: false);
			}
			return this;
		}

		public async ValueTask<TemplateContext> WriteLineAsync()
		{
			await WriteAsync(NewLine).ConfigureAwait(continueOnCapturedContext: false);
			return this;
		}

		public TemplateContext()
			: this(null, null)
		{
		}

		public TemplateContext(ScriptObject builtin)
			: this(builtin, null)
		{
		}

		public TemplateContext(IEqualityComparer<string> keyComparer)
			: this(null, keyComparer)
		{
		}

		public TemplateContext(ScriptObject builtin, IEqualityComparer<string> keyComparer)
		{
			BuiltinObject = builtin ?? GetDefaultBuiltinObject();
			EnableOutput = true;
			EnableBreakAndContinueAsReturnOutsideLoop = false;
			EnableRelaxedTargetAccess = false;
			EnableRelaxedMemberAccess = true;
			EnableRelaxedFunctionAccess = false;
			EnableRelaxedIndexerAccess = true;
			AutoIndent = true;
			IndentOnEmptyLines = true;
			LoopLimit = 1000;
			RecursiveLimit = 100;
			LimitToString = 0;
			ObjectRecursionLimit = 0;
			MemberRenamer = StandardMemberRenamer.Default;
			RegexTimeOut = TimeSpan.FromSeconds(10.0);
			TemplateLoaderParserOptions = default(ParserOptions);
			TemplateLoaderLexerOptions = LexerOptions.Default;
			NewLine = Environment.NewLine;
			Language = ScriptLang.Default;
			_outputs = new FastStack<IScriptOutput>(4);
			_output = new StringBuilderOutput();
			_outputs.Push(_output);
			_globalContexts = new FastStack<VariableContext>(4);
			_availableGlobalContexts = new FastStack<VariableContext>(4);
			_availableLocalContexts = new FastStack<VariableContext>(4);
			_localContexts = new FastStack<VariableContext>(4);
			_availableStores = new FastStack<ScriptObject>(4);
			_cultures = new FastStack<CultureInfo>(4);
			_caseValues = new FastStack<object>(4);
			_sourceFiles = new FastStack<string>(4);
			_memberAccessors = new Dictionary<Type, IObjectAccessor>();
			_listAccessors = new Dictionary<Type, IListAccessor>();
			_loops = new FastStack<ScriptLoopStatementBase>(4);
			BlockDelegates = new FastStack<ScriptBlockStatement>(4);
			_availablePipeArguments = new FastStack<ScriptPipeArguments>(4);
			_pipeArguments = new FastStack<ScriptPipeArguments>(4);
			_availableScriptExpressionLists = new FastStack<List<ScriptExpression>>(4);
			_availableReflectionArguments = new object[65][];
			_keyComparer = keyComparer;
			for (int i = 0; i < _availableReflectionArguments.Length; i++)
			{
				_availableReflectionArguments[i] = new object[i];
			}
			_isFunctionCallDisabled = false;
			CachedTemplates = new Dictionary<string, Template>();
			Tags = new Dictionary<object, object>();
			PushGlobal(BuiltinObject);
		}

		public void CheckAbort()
		{
			RuntimeHelpers.EnsureSufficientExecutionStack();
			CancellationToken cancellationToken = CancellationToken;
			if (cancellationToken.IsCancellationRequested)
			{
				throw new ScriptAbortException(CurrentNode?.Span ?? default(SourceSpan), cancellationToken);
			}
		}

		public void PushCulture(CultureInfo culture)
		{
			if (culture == null)
			{
				throw new ArgumentNullException("culture");
			}
			_cultures.Push(culture);
		}

		public CultureInfo PopCulture()
		{
			if (_cultures.Count == 0)
			{
				throw new InvalidOperationException("Cannot PopCulture more than PushCulture");
			}
			return _cultures.Pop();
		}

		internal void PushPipeArguments()
		{
			ScriptPipeArguments scriptPipeArguments = ((_availablePipeArguments.Count > 0) ? _availablePipeArguments.Pop() : new ScriptPipeArguments(1));
			_pipeArguments.Push(scriptPipeArguments);
			_currentPipeArguments = scriptPipeArguments;
		}

		internal void ClearPipeArguments()
		{
			while (_pipeArguments.Count > 0)
			{
				PopPipeArguments();
			}
		}

		internal List<ScriptExpression> GetOrCreateListOfScriptExpressions(int capacity)
		{
			List<ScriptExpression> list = ((_availableScriptExpressionLists.Count > 0) ? _availableScriptExpressionLists.Pop() : new List<ScriptExpression>());
			if (capacity > list.Capacity)
			{
				list.Capacity = capacity;
			}
			return list;
		}

		internal void ReleaseListOfScriptExpressions(List<ScriptExpression> list)
		{
			_availableScriptExpressionLists.Push(list);
			list.Clear();
		}

		internal object[] GetOrCreateReflectionArguments(int length)
		{
			if (length < 0)
			{
				throw new ArgumentOutOfRangeException("length");
			}
			if (length >= _availableReflectionArguments.Length)
			{
				return new object[length];
			}
			object[] array = _availableReflectionArguments[length] ?? new object[length];
			if (length > 0)
			{
				_availableReflectionArguments[length] = (object[])array[0];
				array[0] = null;
			}
			return array;
		}

		internal void ReleaseReflectionArguments(object[] reflectionArguments)
		{
			if (reflectionArguments != null && reflectionArguments.Length < _availableReflectionArguments.Length)
			{
				Array.Clear(reflectionArguments, 0, reflectionArguments.Length);
				object[] array = _availableReflectionArguments[reflectionArguments.Length];
				_availableReflectionArguments[reflectionArguments.Length] = reflectionArguments;
				if (reflectionArguments.Length != 0)
				{
					reflectionArguments[0] = array;
				}
			}
		}

		internal void PopPipeArguments()
		{
			if (_pipeArguments.Count == 0)
			{
				throw new InvalidOperationException("Cannot PopPipeArguments more than PushPipeArguments");
			}
			ScriptPipeArguments scriptPipeArguments = _pipeArguments.Pop();
			scriptPipeArguments.Clear();
			_currentPipeArguments = ((_pipeArguments.Count > 0) ? _pipeArguments.Peek() : null);
			_availablePipeArguments.Push(scriptPipeArguments);
		}

		public void PushSourceFile(string sourceFile)
		{
			if (sourceFile == null)
			{
				throw new ArgumentNullException("sourceFile");
			}
			_sourceFiles.Push(sourceFile);
		}

		public string PopSourceFile()
		{
			if (_sourceFiles.Count == 0)
			{
				throw new InvalidOperationException("Cannot PopSourceFile more than PushSourceFile");
			}
			return _sourceFiles.Pop();
		}

		public object GetValue(ScriptExpression target)
		{
			ScriptNode currentNode = CurrentNode;
			_getOrSetValueLevel++;
			try
			{
				CurrentNode = target;
				return GetOrSetValue(target, null, setter: false);
			}
			finally
			{
				CurrentNode = currentNode;
				_getOrSetValueLevel--;
			}
		}

		public void SetValue(ScriptVariable variable, bool value)
		{
			SetValue(variable, value ? TrueObject : FalseObject);
		}

		public virtual void Import(SourceSpan span, object objectToImport)
		{
			if (!(objectToImport is ScriptObject))
			{
				throw new ScriptRuntimeException(span, "Unexpected value `" + GetTypeName(objectToImport) + "` for import. Expecting an plain script object.");
			}
			CurrentGlobal.Import(objectToImport);
		}

		public void SetValue(ScriptExpression target, object value)
		{
			if (target == null)
			{
				throw new ArgumentNullException("target");
			}
			_getOrSetValueLevel++;
			try
			{
				GetOrSetValue(target, value, setter: true);
			}
			finally
			{
				_getOrSetValueLevel--;
			}
		}

		public void PushOutput()
		{
			PushOutput(new StringBuilderOutput());
		}

		public void PushOutput(IScriptOutput output)
		{
			_output = output ?? throw new ArgumentNullException("output");
			_outputs.Push(_output);
		}

		public IScriptOutput PopOutput()
		{
			if (_outputs.Count == 1)
			{
				throw new InvalidOperationException("Unexpected PopOutput for top level writer");
			}
			IScriptOutput result = _outputs.Pop();
			_output = _outputs.Peek();
			return result;
		}

		public virtual TemplateContext Write(SourceSpan span, object textAsObject)
		{
			if (textAsObject != null)
			{
				string text = ObjectToString(textAsObject);
				Write(text);
			}
			return this;
		}

		public TemplateContext Write(string text)
		{
			if (text != null)
			{
				Write(text, 0, text.Length);
			}
			return this;
		}

		public TemplateContext WriteLine()
		{
			Write(NewLine);
			return this;
		}

		public TemplateContext Write(ScriptStringSlice slice)
		{
			Write(slice.FullText, slice.Index, slice.Length);
			return this;
		}

		public TemplateContext Write(string text, int startIndex, int count)
		{
			if (text != null)
			{
				if (CurrentIndent != null)
				{
					int num = startIndex;
					int num2 = startIndex + count;
					while (num < num2)
					{
						int num3 = text.IndexOf('\n', num);
						if (num3 < 0 || num3 >= num2)
						{
							if (_previousTextWasNewLine)
							{
								Output.Write(CurrentIndent, 0, CurrentIndent.Length);
								_previousTextWasNewLine = false;
							}
							Output.Write(text, num, num2 - num);
							break;
						}
						int num4 = num3 - num;
						if (_previousTextWasNewLine && (IndentOnEmptyLines || (num4 != 0 && (num4 != 1 || text[num] != '\r'))))
						{
							Output.Write(CurrentIndent, 0, CurrentIndent.Length);
							_previousTextWasNewLine = false;
						}
						Output.Write(text, num, num4 + 1);
						num = num3 + 1;
						_previousTextWasNewLine = true;
					}
				}
				else
				{
					if (count > 0)
					{
						_previousTextWasNewLine = text[startIndex + count - 1] == '\n';
					}
					Output.Write(text, startIndex, count);
				}
			}
			return this;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public object Evaluate(ScriptNode scriptNode)
		{
			return Evaluate(scriptNode, aliasReturnedFunction: false);
		}

		public virtual object Evaluate(ScriptNode scriptNode, bool aliasReturnedFunction)
		{
			if (scriptNode == null)
			{
				return null;
			}
			bool isFunctionCallDisabled = _isFunctionCallDisabled;
			int getOrSetValueLevel = _getOrSetValueLevel;
			ScriptNode currentNode = CurrentNode;
			try
			{
				CurrentNode = scriptNode;
				_getOrSetValueLevel = 0;
				_isFunctionCallDisabled = aliasReturnedFunction;
				return scriptNode.Evaluate(this);
			}
			catch (ScriptRuntimeException exception) when (RenderRuntimeException != null)
			{
				return RenderRuntimeException(exception);
			}
			catch (Exception ex) when (!(ex is ScriptRuntimeException))
			{
				ScriptRuntimeException ex2 = new ScriptRuntimeException(scriptNode.Span, ex.Message, ex);
				if (RenderRuntimeException != null)
				{
					return RenderRuntimeException(ex2);
				}
				throw ex2;
			}
			finally
			{
				CurrentNode = currentNode;
				_getOrSetValueLevel = getOrSetValueLevel;
				_isFunctionCallDisabled = isFunctionCallDisabled;
			}
		}

		public IObjectAccessor GetMemberAccessor(object target)
		{
			if (target == null)
			{
				return NullAccessor.Default;
			}
			Type type = target.GetType();
			if (!_memberAccessors.TryGetValue(type, out var value))
			{
				value = GetMemberAccessorImpl(target) ?? NullAccessor.Default;
				_memberAccessors.Add(type, value);
			}
			return value;
		}

		public virtual void Reset()
		{
			while (OutputCount > 1)
			{
				PopOutput();
			}
			((StringBuilderOutput)Output).Builder.Length = 0;
			while (GlobalCount > 1)
			{
				PopGlobal();
			}
			while (CultureCount > 0)
			{
				PopCulture();
			}
			while (SourceFileCount > 0)
			{
				PopSourceFile();
			}
		}

		protected virtual IObjectAccessor GetMemberAccessorImpl(object target)
		{
			Type type = target.GetType();
			if (target is IScriptObject)
			{
				return ScriptObjectAccessor.Default;
			}
			if (target is string)
			{
				return StringAccessor.Default;
			}
			if (type.IsPrimitiveOrDecimal())
			{
				return PrimitiveAccessor.Default;
			}
			if (!DictionaryAccessor.TryGet(target, out var accessor))
			{
				if (type.IsArray)
				{
					return ArrayAccessor.Default;
				}
				if (target is IList)
				{
					return ListAccessor.Default;
				}
				return new TypedObjectAccessor(type, _keyComparer, MemberFilter, MemberRenamer);
			}
			return accessor;
		}

		public static ScriptObject GetDefaultBuiltinObject()
		{
			return new BuiltinFunctions();
		}

		public void EnterRecursive(ScriptNode node)
		{
			try
			{
				RuntimeHelpers.EnsureSufficientExecutionStack();
			}
			catch (InsufficientExecutionStackException)
			{
				throw new ScriptRuntimeException(node.Span, "Exceeding recursive depth limit, near to stack overflow");
			}
			_callDepth++;
			if (RecursiveLimit != 0 && _callDepth > RecursiveLimit)
			{
				throw new ScriptRuntimeException(node.Span, $"Exceeding number of recursive depth limit `{RecursiveLimit}` for node: `{node}`");
			}
		}

		public void ExitRecursive(ScriptNode node)
		{
			_callDepth--;
			if (_callDepth < 0)
			{
				throw new ScriptRuntimeException(node.Span, $"unexpected ExitRecursive not matching EnterRecursive for `{node}`");
			}
		}

		internal void EnterFunction(ScriptNode caller)
		{
			EnterRecursive(caller);
		}

		internal void ExitFunction(ScriptNode caller)
		{
			ExitRecursive(caller);
		}

		internal void EnterLoop(ScriptLoopStatementBase loop)
		{
			if (loop == null)
			{
				throw new ArgumentNullException("loop");
			}
			_loops.Push(loop);
			PushVariableScope(VariableScope.Loop);
			OnEnterLoop(loop);
		}

		protected virtual void OnEnterLoop(ScriptLoopStatementBase loop)
		{
		}

		internal void ExitLoop(ScriptLoopStatementBase loop)
		{
			try
			{
				OnExitLoop(loop);
			}
			finally
			{
				PopVariableScope(VariableScope.Loop);
				_loops.Pop();
				if (!IsInLoop)
				{
					_loopStep = 0;
				}
			}
		}

		protected virtual void OnExitLoop(ScriptLoopStatementBase loop)
		{
		}

		internal bool StepLoop(ScriptLoopStatementBase loop, LoopType loopType = LoopType.Default)
		{
			_loopStep++;
			int num = ((loopType != LoopType.Queryable) ? LoopLimit : LoopLimitQueryable.GetValueOrDefault(LoopLimit));
			if (num != 0 && _loopStep > num)
			{
				throw new ScriptRuntimeException(_loops.Peek().Span, $"Exceeding number of iteration limit `{num}` for loop statement.");
			}
			return OnStepLoop(loop);
		}

		protected virtual bool OnStepLoop(ScriptLoopStatementBase loop)
		{
			return true;
		}

		internal void PushCase(object caseValue)
		{
			_caseValues.Push(caseValue);
		}

		internal object PeekCase()
		{
			return _caseValues.Peek();
		}

		internal object PopCase()
		{
			if (_caseValues.Count == 0)
			{
				throw new InvalidOperationException("Cannot PopCase more than PushCase");
			}
			return _caseValues.Pop();
		}

		private object GetOrSetValue(ScriptExpression targetExpression, object valueToSet, bool setter)
		{
			object obj = null;
			try
			{
				if (targetExpression is IScriptVariablePath scriptVariablePath)
				{
					if (setter)
					{
						scriptVariablePath.SetValue(this, valueToSet);
					}
					else
					{
						obj = scriptVariablePath.GetValue(this);
					}
				}
				else
				{
					if (setter)
					{
						throw new ScriptRuntimeException(targetExpression.Span, "Unsupported target expression for assignment.");
					}
					obj = Evaluate(targetExpression);
				}
			}
			catch (Exception ex) when (_getOrSetValueLevel == 1 && !(ex is ScriptRuntimeException))
			{
				throw new ScriptRuntimeException(targetExpression.Span, "Unexpected exception while accessing target expression: " + ex.Message, ex);
			}
			if (((_isFunctionCallDisabled && _getOrSetValueLevel > 1) || !_isFunctionCallDisabled) && ScriptFunctionCall.IsFunction(obj))
			{
				obj = ScriptFunctionCall.Call(this, targetExpression, obj, _getOrSetValueLevel == 1, null);
			}
			return obj;
		}

		public IListAccessor GetListAccessor(object target)
		{
			Type type = target.GetType();
			if (!_listAccessors.TryGetValue(type, out var value))
			{
				value = GetListAccessorImpl(target, type);
				_listAccessors.Add(type, value);
			}
			return value;
		}

		protected virtual IListAccessor GetListAccessorImpl(object target, Type type)
		{
			if (type.IsArray)
			{
				return ArrayAccessor.Default;
			}
			if (type == typeof(string))
			{
				return StringAccessor.Default;
			}
			if (type.IsPrimitiveOrDecimal())
			{
				return PrimitiveAccessor.Default;
			}
			if (target is IList)
			{
				return ListAccessor.Default;
			}
			return null;
		}

		public void ResetPreviousNewLine()
		{
			_previousTextWasNewLine = false;
		}

		public virtual string GetTemplatePathFromName(string templateName, ScriptNode callerContext)
		{
			if (string.IsNullOrEmpty(templateName))
			{
				throw new ScriptRuntimeException(callerContext.Span, "Include template name cannot be null or empty");
			}
			return ConvertTemplateNameToPath(templateName, callerContext);
		}

		protected string ConvertTemplateNameToPath(string templateName, ScriptNode callerContext)
		{
			if (TemplateLoader == null)
			{
				throw new ScriptRuntimeException(callerContext.Span, "Unable to include <" + templateName + ">. No TemplateLoader registered in TemplateContext.TemplateLoader");
			}
			string path;
			try
			{
				path = TemplateLoader.GetPath(this, callerContext.Span, templateName);
			}
			catch (Exception ex) when (!(ex is ScriptRuntimeException))
			{
				throw new ScriptRuntimeException(callerContext.Span, "Unexpected exception while getting the path for the include name `" + templateName + "`", ex);
			}
			if (path == null)
			{
				throw new ScriptRuntimeException(callerContext.Span, "Include template path is null for `" + templateName);
			}
			return path;
		}

		public Template GetOrCreateTemplate(string templatePath, ScriptNode callerContext)
		{
			if (!CachedTemplates.TryGetValue(templatePath, out var value))
			{
				value = CreateTemplate(templatePath, callerContext);
				CachedTemplates[templatePath] = value;
			}
			return value;
		}

		protected virtual Template CreateTemplate(string templatePath, ScriptNode callerContext)
		{
			string text;
			try
			{
				text = TemplateLoader.Load(this, callerContext.Span, templatePath);
			}
			catch (Exception ex) when (!(ex is ScriptRuntimeException))
			{
				throw new ScriptRuntimeException(callerContext.Span, "Unexpected exception while creating template from path `" + templatePath + "`", ex);
			}
			if (text == null)
			{
				throw new ScriptRuntimeException(callerContext.Span, "The result of including `" + templatePath + "` cannot be null");
			}
			Template template = Template.Parse(text, templatePath, TemplateLoaderParserOptions, TemplateLoaderLexerOptions);
			if (template.HasErrors)
			{
				throw new ScriptParserRuntimeException(callerContext.Span, "Error while parsing template `" + templatePath + "`", template.Messages);
			}
			CachedTemplates.Add(templatePath, template);
			return template;
		}

		public string RenderTemplate(Template template, ScriptArray arguments, ScriptNode callerContext)
		{
			string result = null;
			EnterRecursive(callerContext);
			string currentIndent = CurrentIndent;
			CurrentIndent = null;
			PushOutput();
			PushLocal();
			try
			{
				SetValue(ScriptVariable.Arguments, arguments, asReadOnly: true, force: true);
				IEnumerable<ScriptNamedArgument> enumerable = (callerContext as ScriptFunctionCall)?.Arguments.OfType<ScriptNamedArgument>();
				if (enumerable != null)
				{
					foreach (ScriptNamedArgument item in enumerable)
					{
						string name = item.Name.Name;
						object value = item.Value.Evaluate(this);
						ScriptVariable variable = ScriptVariable.Create(name, ScriptVariableScope.Local);
						SetValue(variable, value, asReadOnly: false, force: true);
					}
				}
				if (currentIndent != null)
				{
					ResetPreviousNewLine();
				}
				result = template.Render(this);
				if (currentIndent != null)
				{
					ResetPreviousNewLine();
				}
			}
			finally
			{
				PopLocal();
				PopOutput();
				CurrentIndent = currentIndent;
				ExitRecursive(callerContext);
			}
			return result;
		}

		public object GetFormat(Type formatType)
		{
			return CurrentCulture.GetFormat(formatType);
		}

		public virtual object IsEmpty(SourceSpan span, object against)
		{
			if (against == null)
			{
				return null;
			}
			if (against is IList)
			{
				return ((IList)against).Count == 0;
			}
			if (against is IEnumerable)
			{
				return !((IEnumerable)against).GetEnumerator().MoveNext();
			}
			if (against.GetType().IsPrimitiveOrDecimal())
			{
				return false;
			}
			return GetMemberAccessor(against).GetMemberCount(this, span, against) > 0;
		}

		public virtual IList ToList(SourceSpan span, object value)
		{
			if (value == null)
			{
				return null;
			}
			if (value is IList)
			{
				return (IList)value;
			}
			return new ScriptArray((value as IEnumerable) ?? throw new ScriptRuntimeException(span, "Unexpected list value. Expecting an array, list or iterator. Unable to convert to a list."));
		}

		public virtual string ObjectToString(object value, bool nested = false)
		{
			if (_objectToStringLevel == 0)
			{
				_currentToStringLength = 0;
			}
			try
			{
				_objectToStringLevel++;
				if (ObjectRecursionLimit != 0 && _objectToStringLevel > ObjectRecursionLimit)
				{
					throw new InvalidOperationException("Structure is too deeply nested or contains reference loops.");
				}
				string text = ObjectToStringImpl(value, nested);
				if (LimitToString > 0 && _objectToStringLevel == 1 && text != null && text.Length >= LimitToString)
				{
					return text + "...";
				}
				return text;
			}
			finally
			{
				_objectToStringLevel--;
			}
		}

		private string ObjectToStringImpl(object value, bool nested)
		{
			if (LimitToString > 0 && _currentToStringLength >= LimitToString)
			{
				return string.Empty;
			}
			if (value is string text)
			{
				if (LimitToString > 0 && _currentToStringLength + text.Length >= LimitToString)
				{
					int num = LimitToString - _currentToStringLength;
					if (num <= 0)
					{
						return string.Empty;
					}
					string text2 = text.Substring(0, num);
					if (!nested)
					{
						return text2;
					}
					return "\"" + StringFunctions.Escape(text2);
				}
				if (!nested)
				{
					return text;
				}
				return "\"" + StringFunctions.Escape(text) + "\"";
			}
			if (value == null || value == EmptyScriptObject.Default)
			{
				if (!nested)
				{
					return null;
				}
				return "null";
			}
			if (value is bool)
			{
				if (!(bool)value)
				{
					return "false";
				}
				return "true";
			}
			if (value is DateTime value2)
			{
				bool strictVariables = StrictVariables;
				try
				{
					StrictVariables = false;
					if (GetValue(DateTimeFunctions.DateVariable) is DateTimeFunctions dateTimeFunctions)
					{
						return dateTimeFunctions.ToString(value2, dateTimeFunctions.Format, CurrentCulture);
					}
				}
				finally
				{
					StrictVariables = strictVariables;
				}
			}
			if (value is IFormattable formattable)
			{
				return formattable.ToString(null, this);
			}
			if (value is IConvertible convertible)
			{
				return convertible.ToString(this);
			}
			if (value is IEnumerable enumerable)
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.Append("[");
				_currentToStringLength++;
				bool flag = true;
				foreach (object item in enumerable)
				{
					if (!flag)
					{
						stringBuilder.Append(", ");
						_currentToStringLength += 2;
					}
					string text3 = ObjectToString(item, nested: true);
					stringBuilder.Append(text3);
					if (text3 != null)
					{
						_currentToStringLength += text3.Length;
					}
					if (LimitToString > 0 && _currentToStringLength >= LimitToString)
					{
						return stringBuilder.ToString();
					}
					flag = false;
				}
				stringBuilder.Append("]");
				_currentToStringLength++;
				return stringBuilder.ToString();
			}
			string fullName = value.GetType().FullName;
			if (fullName != null && fullName.StartsWith("System.Collections.Generic.KeyValuePair"))
			{
				ScriptObject scriptObject = new ScriptObject(2);
				scriptObject.Import(value, null, MemberRenamer);
				return ObjectToString(scriptObject, nested: true);
			}
			if (value is IScriptCustomFunction && !(value is ScriptFunction))
			{
				return "<function>";
			}
			return value.ToString();
		}

		public virtual bool ToBool(SourceSpan span, object value)
		{
			if (value == null || value == EmptyScriptObject.Default)
			{
				return false;
			}
			if (value is bool)
			{
				return (bool)value;
			}
			if (UseScientific)
			{
				Type type = value.GetType();
				if (type.IsPrimitive || type == typeof(decimal))
				{
					return Convert.ToBoolean(value);
				}
				if (value is BigInteger bigInteger)
				{
					return bigInteger != BigInteger.Zero;
				}
			}
			return true;
		}

		public virtual int ToInt(SourceSpan span, object value)
		{
			if (value == null)
			{
				return 0;
			}
			if (value is int)
			{
				return (int)value;
			}
			checked
			{
				try
				{
					if (value is BigInteger bigInteger)
					{
						return (int)bigInteger;
					}
					if (value is IScriptConvertibleTo scriptConvertibleTo && scriptConvertibleTo.TryConvertTo(this, span, typeof(int), out object value2))
					{
						return (int)value2;
					}
					if (value is uint num)
					{
						return (int)num;
					}
					if (value is ulong num2)
					{
						return (int)num2;
					}
					return Convert.ToInt32(value, CurrentCulture);
				}
				catch (Exception innerException)
				{
					throw new ScriptRuntimeException(span, "Unable to convert type `" + GetTypeName(value) + "` to int", innerException);
				}
			}
		}

		public virtual string GetTypeName(object value)
		{
			if (value == null)
			{
				return "null";
			}
			if (value is Type type)
			{
				return type.ScriptPrettyName();
			}
			if (value is IScriptCustomTypeInfo scriptCustomTypeInfo)
			{
				return scriptCustomTypeInfo.TypeName;
			}
			return value.GetType().ScriptPrettyName();
		}

		public T ToObject<T>(SourceSpan span, object value)
		{
			return (T)ToObject(span, value, typeof(T));
		}

		public virtual object ToObject(SourceSpan span, object value, Type destinationType)
		{
			if (destinationType == null)
			{
				throw new ArgumentNullException("destinationType");
			}
			(bool IsNullable, Type DestinationType) tuple = GetNullableInfo(destinationType);
			bool item = tuple.IsNullable;
			destinationType = tuple.DestinationType;
			Type type = value?.GetType();
			if (destinationType == type)
			{
				return value;
			}
			if (item && value == null)
			{
				return null;
			}
			if (destinationType == typeof(string))
			{
				return ObjectToString(value);
			}
			if (destinationType == typeof(int))
			{
				return ToInt(span, value);
			}
			if (destinationType == typeof(bool))
			{
				return ToBool(span, value);
			}
			if (value == null)
			{
				if (destinationType == typeof(double))
				{
					return 0.0;
				}
				if (destinationType == typeof(float))
				{
					return 0f;
				}
				if (destinationType == typeof(long))
				{
					return 0L;
				}
				if (destinationType == typeof(decimal))
				{
					return 0m;
				}
				if (destinationType == typeof(BigInteger))
				{
					return new BigInteger(0);
				}
				return null;
			}
			if (destinationType.IsEnum)
			{
				try
				{
					if (value is string value2)
					{
						return Enum.Parse(destinationType, value2);
					}
					return Enum.ToObject(destinationType, value);
				}
				catch (Exception innerException)
				{
					throw new ScriptRuntimeException(span, "Unable to convert type `" + GetTypeName(value) + "` to `" + GetTypeName(destinationType) + "`", innerException);
				}
			}
			if (value is IScriptConvertibleTo scriptConvertibleTo && scriptConvertibleTo.TryConvertTo(this, span, destinationType, out object value3))
			{
				return value3;
			}
			if (typeof(IScriptConvertibleFrom).IsAssignableFrom(destinationType))
			{
				IScriptConvertibleFrom scriptConvertibleFrom = (IScriptConvertibleFrom)Activator.CreateInstance(destinationType);
				if (scriptConvertibleFrom.TryConvertFrom(this, span, value))
				{
					return scriptConvertibleFrom;
				}
			}
			Type c = type;
			if (type.IsPrimitiveOrDecimal() && destinationType.IsPrimitiveOrDecimal())
			{
				try
				{
					if (destinationType == typeof(BigInteger))
					{
						if (type == typeof(char))
						{
							return new BigInteger((char)value);
						}
						if (type == typeof(bool))
						{
							return new BigInteger(((bool)value) ? 1 : 0);
						}
						if (type == typeof(float))
						{
							return new BigInteger((float)value);
						}
						if (type == typeof(double))
						{
							return new BigInteger((double)value);
						}
						if (type == typeof(int))
						{
							return new BigInteger((int)value);
						}
						if (type == typeof(uint))
						{
							return new BigInteger((uint)value);
						}
						if (type == typeof(long))
						{
							return new BigInteger((long)value);
						}
						if (type == typeof(ulong))
						{
							return new BigInteger((ulong)value);
						}
					}
					else if (type == typeof(BigInteger))
					{
						if (destinationType == typeof(char))
						{
							return (char)(int)(BigInteger)value;
						}
						if (destinationType == typeof(float))
						{
							return (float)(BigInteger)value;
						}
						if (destinationType == typeof(double))
						{
							return (double)(BigInteger)value;
						}
						if (destinationType == typeof(uint))
						{
							return (uint)(BigInteger)value;
						}
						if (destinationType == typeof(long))
						{
							return (long)(BigInteger)value;
						}
						if (destinationType == typeof(ulong))
						{
							return (ulong)(BigInteger)value;
						}
					}
					return Convert.ChangeType(value, destinationType, CurrentCulture);
				}
				catch (Exception innerException2)
				{
					throw new ScriptRuntimeException(span, "Unable to convert type `" + GetTypeName(value) + "` to `" + GetTypeName(destinationType) + "`", innerException2);
				}
			}
			if (destinationType == typeof(IList))
			{
				return ToList(span, value);
			}
			if (destinationType.IsAssignableFrom(c))
			{
				return value;
			}
			throw new ScriptRuntimeException(span, "Unable to convert type `" + GetTypeName(value) + "` to `" + GetTypeName(destinationType) + "`");
			static (bool IsNullable, Type DestinationType) GetNullableInfo(Type destinationType)
			{
				destinationType = destinationType ?? throw new ArgumentNullException("destinationType");
				Type underlyingType = Nullable.GetUnderlyingType(destinationType);
				if ((object)underlyingType != null)
				{
					return (true, underlyingType);
				}
				return (false, destinationType);
			}
		}

		public void PushGlobal(IScriptObject scriptObject)
		{
			PushGlobalOnly(scriptObject);
			PushLocal();
		}

		internal void PushGlobalOnly(IScriptObject scriptObject)
		{
			if (scriptObject == null)
			{
				throw new ArgumentNullException("scriptObject");
			}
			_globalContexts.Push(GetOrCreateGlobalContext(scriptObject));
		}

		private VariableContext GetOrCreateGlobalContext(IScriptObject globalObject)
		{
			if (_availableGlobalContexts.Count == 0)
			{
				return new VariableContext(globalObject);
			}
			VariableContext variableContext = _availableGlobalContexts.Pop();
			variableContext.LocalObject = globalObject;
			return variableContext;
		}

		internal IScriptObject PopGlobalOnly()
		{
			if (_globalContexts.Count == 1)
			{
				throw new InvalidOperationException("Unexpected PopGlobal() not matching a PushGlobal");
			}
			VariableContext variableContext = _globalContexts.Pop();
			IScriptObject localObject = variableContext.LocalObject;
			variableContext.LocalObject = null;
			_availableGlobalContexts.Push(variableContext);
			return localObject;
		}

		public IScriptObject PopGlobal()
		{
			IScriptObject result = PopGlobalOnly();
			PopLocal();
			return result;
		}

		public void PushLocal()
		{
			PushVariableScope(VariableScope.Local);
		}

		public void PopLocal()
		{
			PopVariableScope(VariableScope.Local);
		}

		public void SetValue(ScriptVariable variable, object value, bool asReadOnly = false)
		{
			if (variable == null)
			{
				throw new ArgumentNullException("variable");
			}
			if (!GetStoreForWrite(variable).TrySetValue(this, variable.Span, variable.Name, value, asReadOnly))
			{
				throw new ScriptRuntimeException(variable.Span, $"Cannot set value on the readonly variable `{variable}`");
			}
		}

		public void SetValue(ScriptVariable variable, object value, bool asReadOnly, bool force)
		{
			if (variable == null)
			{
				throw new ArgumentNullException("variable");
			}
			IScriptObject storeForWrite = GetStoreForWrite(variable);
			if (force)
			{
				storeForWrite.Remove(variable.Name);
				storeForWrite.TrySetValue(this, variable.Span, variable.Name, value, asReadOnly);
			}
			else if (!storeForWrite.TrySetValue(this, variable.Span, variable.Name, value, asReadOnly))
			{
				throw new ScriptRuntimeException(variable.Span, $"Cannot set value on the readonly variable `{variable}`");
			}
		}

		public void DeleteValue(ScriptVariable variable)
		{
			if (variable == null)
			{
				throw new ArgumentNullException("variable");
			}
			GetStoreForWrite(variable).Remove(variable.Name);
		}

		public void SetReadOnly(ScriptVariable variable, bool isReadOnly = true)
		{
			if (variable == null)
			{
				throw new ArgumentNullException("variable");
			}
			GetStoreForWrite(variable).SetReadOnly(variable.Name, isReadOnly);
		}

		public virtual void SetLoopVariable(ScriptVariable variable, object value)
		{
			if (variable == null)
			{
				throw new ArgumentNullException("variable");
			}
			VariableContext obj = ((variable.Scope == ScriptVariableScope.Global) ? _globalContexts.Peek() : _currentLocalContext);
			if (obj.Loops.Count == 0)
			{
				throw new InvalidOperationException("Cannot set a loop global variable without a loop variable store.");
			}
			if (!obj.Loops.Peek().TrySetValue(this, variable.Span, variable.Name, value, readOnly: false))
			{
				throw new ScriptRuntimeException(variable.Span, $"Cannot set value on the variable `{variable}`");
			}
		}

		private void PushLocalContext(ScriptObject locals = null)
		{
			VariableContext variableContext = ((_availableLocalContexts.Count > 0) ? _availableLocalContexts.Pop() : new VariableContext(null));
			variableContext.LocalObject = locals;
			_localContexts.Push(variableContext);
			_currentLocalContext = variableContext;
		}

		private ScriptObject PopLocalContext()
		{
			VariableContext variableContext = _localContexts.Pop();
			ScriptObject result = (ScriptObject)variableContext.LocalObject;
			variableContext.LocalObject = null;
			_availableLocalContexts.Push(variableContext);
			_currentLocalContext = _localContexts.Peek();
			return result;
		}

		public object GetValue(ScriptVariable variable)
		{
			if (variable == null)
			{
				throw new ArgumentNullException("variable");
			}
			IEnumerable<IScriptObject> storeForRead = GetStoreForRead(variable);
			object value = null;
			foreach (IScriptObject item in storeForRead)
			{
				if (item.TryGetValue(this, variable.Span, variable.Name, out value))
				{
					return value;
				}
			}
			bool found = false;
			if (TryGetVariable != null && TryGetVariable(this, variable.Span, variable, out value))
			{
				found = true;
			}
			CheckVariableFound(variable, found);
			return value;
		}

		public object GetValue(ScriptVariableGlobal variable)
		{
			if (variable == null)
			{
				throw new ArgumentNullException("variable");
			}
			object value = null;
			int count = _globalContexts.Count;
			VariableContext[] items = _globalContexts.Items;
			bool isInLoop = IsInLoop;
			for (int num = count - 1; num >= 0; num--)
			{
				VariableContext variableContext = items[num];
				if (isInLoop)
				{
					int count2 = variableContext.Loops.Count;
					if (count2 > 0)
					{
						ScriptObject[] items2 = variableContext.Loops.Items;
						for (int num2 = count2 - 1; num2 >= 0; num2--)
						{
							if (items2[num2].TryGetValue(this, variable.Span, variable.Name, out value))
							{
								return value;
							}
						}
					}
				}
				if (items[num].LocalObject.TryGetValue(this, variable.Span, variable.Name, out value))
				{
					return value;
				}
			}
			bool found = false;
			if (TryGetVariable != null && TryGetVariable(this, variable.Span, variable, out value))
			{
				found = true;
			}
			CheckVariableFound(variable, found);
			return value;
		}

		public ValueTask<object> GetValueAsync(ScriptVariableGlobal variable)
		{
			return new ValueTask<object>(GetValue(variable));
		}

		public ValueTask<object> GetValueAsync(ScriptVariable variable)
		{
			return new ValueTask<object>(GetValue(variable));
		}

		public ValueTask SetValueAsync(ScriptVariable variable, object value, bool asReadOnly = false)
		{
			SetValue(variable, value, asReadOnly);
			return default(ValueTask);
		}

		private IScriptObject GetStoreForWrite(ScriptVariable variable)
		{
			ScriptVariableScope scope = variable.Scope;
			IScriptObject scriptObject = null;
			switch (scope)
			{
			case ScriptVariableScope.Global:
			{
				string name = variable.Name;
				int num = _globalContexts.Count - 1;
				scriptObject = _globalContexts.Items[num].LocalObject;
				if (!scriptObject.CanWrite(name))
				{
					string arg = ((scriptObject == BuiltinObject) ? "builtin " : string.Empty);
					throw new ScriptRuntimeException(variable.Span, $"Cannot set the {arg}readonly variable `{variable}`");
				}
				break;
			}
			case ScriptVariableScope.Local:
				if (_currentLocalContext.LocalObject != null)
				{
					scriptObject = _currentLocalContext.LocalObject;
					break;
				}
				if (_globalContexts.Count > 0)
				{
					scriptObject = _globalContexts.Peek().LocalObject;
					break;
				}
				throw new ScriptRuntimeException(variable.Span, $"Invalid usage of the local variable `{variable}` in the current context");
			}
			return scriptObject;
		}

		[IteratorStateMachine(typeof(<GetStoreForRead>d__306))]
		private IEnumerable<IScriptObject> GetStoreForRead(ScriptVariable variable)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <GetStoreForRead>d__306(-2)
			{
				<>4__this = this,
				<>3__variable = variable
			};
		}

		private void CheckVariableFound(ScriptVariable variable, bool found)
		{
			if (StrictVariables && !found && variable != ScriptVariable.Arguments)
			{
				throw new ScriptRuntimeException(variable.Span, $"The variable or function `{variable}` was not found");
			}
		}

		private void PushVariableScope(VariableScope scope)
		{
			if (scope == VariableScope.Local)
			{
				ScriptObject locals = ((_availableStores.Count > 0) ? _availableStores.Pop() : new ScriptObject());
				PushLocalContext(locals);
				return;
			}
			ScriptObject item = ((_availableStores.Count > 0) ? _availableStores.Pop() : new ScriptObject());
			ScriptObject item2 = ((_availableStores.Count > 0) ? _availableStores.Pop() : new ScriptObject());
			_globalContexts.Peek().Loops.Push(item2);
			_currentLocalContext.Loops.Push(item);
		}

		private void PopVariableScope(VariableScope scope)
		{
			if (scope == VariableScope.Local)
			{
				ScriptObject scriptObject = PopLocalContext();
				scriptObject.Clear();
				_availableStores.Push(scriptObject);
				return;
			}
			if (_currentLocalContext.Loops.Count == 0)
			{
				throw new InvalidOperationException("Invalid number of matching push/pop VariableScope.");
			}
			ScriptObject scriptObject2 = _globalContexts.Peek().Loops.Pop();
			scriptObject2.Clear();
			_availableStores.Push(scriptObject2);
			ScriptObject scriptObject3 = _currentLocalContext.Loops.Pop();
			scriptObject3.Clear();
			_availableStores.Push(scriptObject3);
		}
	}
	public class ScriptPrinter
	{
		private readonly IScriptOutput _output;

		private readonly bool _isScriptOnly;

		private bool _isInCode;

		private bool _expectSpace;

		private bool _expectEndOfStatement;

		private bool _previousHasSpace;

		private bool _hasEndOfStatement;

		private bool _hasComma;

		private FastStack<bool> _isWhileLoop;

		public readonly ScriptPrinterOptions Options;

		public bool PreviousHasSpace => _previousHasSpace;

		public bool IsInWhileLoop
		{
			get
			{
				if (_isWhileLoop.Count > 0)
				{
					return _isWhileLoop.Peek();
				}
				return false;
			}
		}

		public ScriptPrinter(IScriptOutput output, ScriptPrinterOptions options = default(ScriptPrinterOptions))
		{
			_isWhileLoop = new FastStack<bool>(4);
			Options = options;
			if (options.Mode != 0 && options.Mode != ScriptMode.ScriptOnly)
			{
				throw new ArgumentException($"The rendering mode `{options.Mode}` is not supported. Only `ScriptMode.Default` or `ScriptMode.ScriptOnly` are currently supported");
			}
			_isScriptOnly = options.Mode == ScriptMode.ScriptOnly;
			_isInCode = _isScriptOnly || options.Mode == ScriptMode.FrontMatterOnly || options.Mode == ScriptMode.FrontMatterAndContent;
			_output = output;
			_hasEndOfStatement = true;
		}

		public ScriptPrinter Write(ScriptNode node)
		{
			if (node != null)
			{
				bool flag = false;
				if (node is ScriptLoopStatementBase)
				{
					_isWhileLoop.Push(node is ScriptWhileStatement);
					flag = true;
				}
				try
				{
					WriteBegin(node);
					if (node is IScriptTerminal)
					{
						_hasComma = false;
					}
					node.PrintTo(this);
					WriteEnd(node);
				}
				finally
				{
					if (flag)
					{
						_isWhileLoop.Pop();
					}
				}
			}
			return this;
		}

		public ScriptPrinter Write(string text)
		{
			_previousHasSpace = text.Length > 0 && char.IsWhiteSpace(text[text.Length - 1]);
			_output.Write(text);
			return this;
		}

		public ScriptPrinter Write(ScriptStringSlice slice)
		{
			_previousHasSpace = slice.Length > 0 && char.IsWhiteSpace(slice[slice.Length - 1]);
			_output.Write(slice);
			return this;
		}

		public ScriptPrinter ExpectEos()
		{
			if (!_hasEndOfStatement)
			{
				_expectEndOfStatement = true;
			}
			return this;
		}

		public ScriptPrinter ExpectSpace()
		{
			_expectSpace = true;
			return this;
		}

		public ScriptPrinter WriteListWithCommas<T>(IList<T> list) where T : ScriptNode
		{
			if (list == null)
			{
				return this;
			}
			for (int i = 0; i < list.Count; i++)
			{
				T node = list[i];
				if (i > 0 && !_hasComma)
				{
					Write(",");
					_hasComma = true;
				}
				Write(node);
			}
			return this;
		}

		public ScriptPrinter WriteEnterCode(int escape = 0)
		{
			Write("{");
			for (int i = 0; i < escape; i++)
			{
				Write("%");
			}
			Write("{");
			_expectEndOfStatement = false;
			_expectSpace = false;
			_hasEndOfStatement = true;
			_isInCode = true;
			return this;
		}

		public ScriptPrinter WriteExitCode(int escape = 0)
		{
			Write("}");
			for (int i = 0; i < escape; i++)
			{
				Write("%");
			}
			Write("}");
			_expectEndOfStatement = false;
			_expectSpace = false;
			_hasEndOfStatement = false;
			_isInCode = false;
			return this;
		}

		private void WriteBegin(ScriptNode node)
		{
			WriteTrivias(node, before: true);
			HandleEos(node);
			if (_hasEndOfStatement)
			{
				_hasEndOfStatement = false;
				_expectEndOfStatement = false;
			}
			if (node.CanHaveLeadingTrivia())
			{
				if (_expectSpace && !_previousHasSpace)
				{
					Write(" ");
				}
				_expectSpace = false;
			}
		}

		private void WriteEnd(ScriptNode node)
		{
			WriteTrivias(node, before: false);
			if (node is ScriptPage && _isInCode && !_isScriptOnly)
			{
				WriteExitCode();
			}
		}

		private static bool IsFrontMarker(ScriptNode node)
		{
			if (node is ScriptToken scriptToken)
			{
				return scriptToken.TokenType == TokenType.FrontMatterMarker;
			}
			return false;
		}

		private void HandleEos(ScriptNode node)
		{
			bool flag = IsFrontMarker(node);
			if ((node is ScriptStatement || flag) && !IsBlockOrPage(node) && _isInCode && _expectEndOfStatement)
			{
				if (!_hasEndOfStatement && !(node is ScriptEscapeStatement))
				{
					Write(flag ? "\n" : "; ");
				}
				_expectEndOfStatement = false;
				_hasEndOfStatement = false;
				_hasComma = false;
			}
		}

		private static bool IsBlockOrPage(ScriptNode node)
		{
			if (!(node is ScriptBlockStatement))
			{
				return node is ScriptPage;
			}
			return true;
		}

		private void WriteTrivias(ScriptNode node, bool before)
		{
			if (!(node is IScriptTerminal scriptTerminal))
			{
				return;
			}
			ScriptTrivias trivias = scriptTerminal.Trivias;
			if (trivias == null)
			{
				return;
			}
			foreach (ScriptTrivia item in before ? trivias.Before : trivias.After)
			{
				item.Write(this);
				if (item.Type == ScriptTriviaType.NewLine || item.Type == ScriptTriviaType.SemiColon)
				{
					_hasEndOfStatement = true;
					if (item.Type == ScriptTriviaType.SemiColon)
					{
						_hasComma = false;
					}
					if (_expectSpace)
					{
						_expectSpace = false;
					}
				}
				if (item.Type == ScriptTriviaType.Comma)
				{
					_hasComma = true;
				}
			}
		}
	}
	public struct ScriptPrinterOptions
	{
		public ScriptMode Mode;
	}
	public class LiquidTemplateContext : TemplateContext
	{
		public LiquidTemplateContext()
			: base(new LiquidBuiltinsFunctions())
		{
			base.Language = ScriptLang.Liquid;
			base.EnableBreakAndContinueAsReturnOutsideLoop = true;
			base.EnableRelaxedTargetAccess = true;
			base.TemplateLoaderLexerOptions = new LexerOptions
			{
				Lang = ScriptLang.Liquid
			};
			base.TemplateLoaderParserOptions = new ParserOptions
			{
				LiquidFunctionsToScriban = true
			};
			base.IsLiquid = true;
		}

		public override string GetTemplatePathFromName(string templateName, ScriptNode callerContext)
		{
			if (string.IsNullOrEmpty(templateName))
			{
				return null;
			}
			return ConvertTemplateNameToPath(templateName, callerContext);
		}
	}
}
namespace Scriban.Syntax
{
	internal class ScientificFunctionCallRewriter
	{
		[DebuggerDisplay("Count = {Count}, Current = {Index} : {Current}")]
		private class BinaryExpressionIterator : List<BinaryExpressionOrOperator>
		{
			public int Index { get; set; }

			public BinaryExpressionOrOperator Current
			{
				get
				{
					if (Index >= base.Count)
					{
						return default(BinaryExpressionOrOperator);
					}
					return base[Index];
				}
			}

			public bool HasCurrent => Index < base.Count;

			public bool HasNext => Index + 1 < base.Count;

			public bool MoveNext()
			{
				Index++;
				return HasCurrent;
			}

			public BinaryExpressionOrOperator PeekNext()
			{
				return base[Index + 1];
			}
		}

		[DebuggerDisplay("{ToDebuggerDisplay(),nq}")]
		private readonly struct BinaryExpressionOrOperator
		{
			public readonly ScriptExpression Expression;

			public readonly ScriptBinaryOperator Operator;

			public readonly ScriptToken OperatorToken;

			public readonly FunctionCallKind CallKind;

			public BinaryExpressionOrOperator(ScriptExpression expression, FunctionCallKind kind)
			{
				Expression = expression;
				Operator = ScriptBinaryOperator.None;
				OperatorToken = null;
				CallKind = kind;
			}

			public BinaryExpressionOrOperator(ScriptBinaryOperator @operator, ScriptToken operatorToken)
			{
				Expression = null;
				Operator = @operator;
				OperatorToken = operatorToken;
				CallKind = FunctionCallKind.None;
			}

			private string ToDebuggerDisplay()
			{
				object obj;
				if (Expression == null)
				{
					obj = OperatorToken?.ToString();
					if (obj == null)
					{
						return $"`{Operator.ToText()}` - CallKind = {CallKind}";
					}
				}
				else
				{
					obj = Expression.ToString();
				}
				return (string)obj;
			}
		}

		private enum FunctionCallKind
		{
			None,
			Regular,
			Expression
		}

		private const int ImplicitFunctionCallPrecedence = 101;

		private static async ValueTask FlattenBinaryExpressionsAsync(TemplateContext context, ScriptExpression expression, List<BinaryExpressionOrOperator> expressions)
		{
			while (expression is ScriptBinaryExpression binaryExpression)
			{
				ScriptExpression expression2 = (ScriptExpression)binaryExpression.Left.Clone();
				ScriptExpression right = (ScriptExpression)binaryExpression.Right.Clone();
				ScriptToken token = (ScriptToken)(binaryExpression.OperatorToken?.Clone());
				await FlattenBinaryExpressionsAsync(context, expression2, expressions).ConfigureAwait(continueOnCapturedContext: false);
				expressions.Add(new BinaryExpressionOrOperator(binaryExpression.Operator, token));
				expression = right;
			}
			ScriptExpression expression3 = expression;
			expressions.Add(new BinaryExpressionOrOperator(expression3, await GetFunctionCallKindAsync(context, expression).ConfigureAwait(continueOnCapturedContext: false)));
		}

		private static async ValueTask<FunctionCallKind> GetFunctionCallKindAsync(TemplateContext context, ScriptExpression expression)
		{
			bool restoreStrictVariables = context.StrictVariables;
			context.StrictVariables = false;
			object result = null;
			try
			{
				result = await context.EvaluateAsync(expression, aliasReturnedFunction: true).ConfigureAwait(continueOnCapturedContext: false);
			}
			catch (ScriptRuntimeException) when (context.IgnoreExceptionsWhileRewritingScientific)
			{
			}
			finally
			{
				context.StrictVariables = restoreStrictVariables;
			}
			if (result is IScriptCustomFunction scriptCustomFunction && ((scriptCustomFunction.RequiredParameterCount != 0) ? scriptCustomFunction.RequiredParameterCount : ((scriptCustomFunction.ParameterCount > 0) ? 1 : 0)) > 0)
			{
				return (!scriptCustomFunction.IsParameterType<ScriptExpression>(0)) ? FunctionCallKind.Regular : FunctionCallKind.Expression;
			}
			return FunctionCallKind.None;
		}

		public static async ValueTask<ScriptExpression> RewriteAsync(TemplateContext context, ScriptBinaryExpression binaryExpression)
		{
			if (!HasImplicitBinaryExpression(binaryExpression))
			{
				return binaryExpression;
			}
			BinaryExpressionIterator iterator = new BinaryExpressionIterator();
			await FlattenBinaryExpressionsAsync(context, binaryExpression, iterator).ConfigureAwait(continueOnCapturedContext: false);
			return ParseBinaryExpressionTree(iterator, 0, isExpectingExpression: false);
		}

		public static ScriptExpression Rewrite(TemplateContext context, ScriptBinaryExpression binaryExpression)
		{
			if (!HasImplicitBinaryExpression(binaryExpression))
			{
				return binaryExpression;
			}
			BinaryExpressionIterator binaryExpressionIterator = new BinaryExpressionIterator();
			FlattenBinaryExpressions(context, binaryExpression, binaryExpressionIterator);
			return ParseBinaryExpressionTree(binaryExpressionIterator, 0, isExpectingExpression: false);
		}

		private static bool HasImplicitBinaryExpression(ScriptExpression expression)
		{
			if (expression is ScriptBinaryExpression scriptBinaryExpression)
			{
				if (scriptBinaryExpression.OperatorToken == null && scriptBinaryExpression.Operator == ScriptBinaryOperator.Multiply)
				{
					return true;
				}
				if (!HasImplicitBinaryExpression(scriptBinaryExpression.Left))
				{
					return HasImplicitBinaryExpression(scriptBinaryExpression.Right);
				}
				return true;
			}
			return false;
		}

		private static void FlattenBinaryExpressions(TemplateContext context, ScriptExpression expression, List<BinaryExpressionOrOperator> expressions)
		{
			while (expression is ScriptBinaryExpression scriptBinaryExpression)
			{
				ScriptExpression expression2 = (ScriptExpression)scriptBinaryExpression.Left.Clone();
				ScriptExpression obj = (ScriptExpression)scriptBinaryExpression.Right.Clone();
				ScriptToken operatorToken = (ScriptToken)(scriptBinaryExpression.OperatorToken?.Clone());
				FlattenBinaryExpressions(context, expression2, expressions);
				expressions.Add(new BinaryExpressionOrOperator(scriptBinaryExpression.Operator, operatorToken));
				expression = obj;
			}
			expressions.Add(new BinaryExpressionOrOperator(expression, GetFunctionCallKind(context, expression)));
		}

		private static ScriptExpression ParseBinaryExpressionTree(BinaryExpressionIterator it, int precedence, bool isExpectingExpression)
		{
			ScriptExpression scriptExpression = null;
			while (it.HasCurrent)
			{
				BinaryExpressionOrOperator current = it.Current;
				ScriptExpression expression = current.Expression;
				if (expression == null)
				{
					int num = Parser.GetDefaultBinaryOperatorPrecedence(current.Operator);
					if (!isExpectingExpression && it.HasNext && it.PeekNext().CallKind != 0)
					{
						num = ((num < 101) ? num : 101);
					}
					if (num <= precedence)
					{
						break;
					}
					it.MoveNext();
					ScriptBinaryExpression scriptBinaryExpression = new ScriptBinaryExpression
					{
						Left = scriptExpression,
						Operator = current.Operator,
						OperatorToken = (current.OperatorToken ?? ScriptToken.Star()),
						Span = 
						{
							Start = scriptExpression.Span.Start
						}
					};
					scriptBinaryExpression.Right = ParseBinaryExpressionTree(it, num, isExpectingExpression);
					scriptBinaryExpression.Span.End = scriptBinaryExpression.Right.Span.End;
					scriptExpression = scriptBinaryExpression;
				}
				else if (!isExpectingExpression && current.CallKind != 0)
				{
					ScriptFunctionCall obj = new ScriptFunctionCall
					{
						Target = expression,
						ExplicitCall = true,
						Span = 
						{
							Start = expression.Span.Start
						}
					};
					if (!it.MoveNext())
					{
						throw new ScriptRuntimeException(expression.Span, "The function is expecting at least one argument");
					}
					if (it.Current.Expression == null && (it.Current.Operator != ScriptBinaryOperator.Multiply || it.Current.OperatorToken != null))
					{
						throw new ScriptRuntimeException(expression.Span, "The function expecting one argument cannot be followed by the operator " + (it.Current.OperatorToken?.ToString() ?? it.Current.Operator.ToText()));
					}
					if (!it.MoveNext())
					{
						throw new ScriptRuntimeException(expression.Span, "The function is expecting at least one argument");
					}
					ScriptExpression scriptExpression2 = ParseBinaryExpressionTree(it, 101, current.CallKind == FunctionCallKind.Expression);
					obj.Arguments.Add(scriptExpression2);
					obj.Span.End = scriptExpression2.Span.End;
					scriptExpression = obj;
				}
				else
				{
					scriptExpression = expression;
					it.MoveNext();
				}
			}
			return scriptExpression;
		}

		private static FunctionCallKind GetFunctionCallKind(TemplateContext context, ScriptExpression expression)
		{
			bool strictVariables = context.StrictVariables;
			context.StrictVariables = false;
			object obj = null;
			try
			{
				obj = context.Evaluate(expression, aliasReturnedFunction: true);
			}
			catch (ScriptRuntimeException) when (context.IgnoreExceptionsWhileRewritingScientific)
			{
			}
			finally
			{
				context.StrictVariables = strictVariables;
			}
			if (obj is IScriptCustomFunction scriptCustomFunction && ((scriptCustomFunction.RequiredParameterCount != 0) ? scriptCustomFunction.RequiredParameterCount : ((scriptCustomFunction.ParameterCount > 0) ? 1 : 0)) > 0)
			{
				if (!scriptCustomFunction.IsParameterType<ScriptExpression>(0))
				{
					return FunctionCallKind.Regular;
				}
				return FunctionCallKind.Expression;
			}
			return FunctionCallKind.None;
		}
	}
	[ScriptSyntax("array initializer", "[item1, item2,...]")]
	public class ScriptArrayInitializerExpression : ScriptExpression
	{
		private ScriptList<ScriptExpression> _values;

		private ScriptToken _openBracketToken;

		private ScriptToken _closeBracketToken;

		public override int ChildrenCount => 3;

		public ScriptToken OpenBracketToken
		{
			get
			{
				return _openBracketToken;
			}
			set
			{
				ParentToThis(ref _openBracketToken, value);
			}
		}

		public ScriptList<ScriptExpression> Values
		{
			get
			{
				return _values;
			}
			set
			{
				ParentToThis(ref _values, value);
			}
		}

		public ScriptToken CloseBracketToken
		{
			get
			{
				return _closeBracketToken;
			}
			set
			{
				ParentToThis(ref _closeBracketToken, value);
			}
		}

		public override async ValueTask<object> EvaluateAsync(TemplateContext context)
		{
			ScriptArray scriptArray = new ScriptArray();
			foreach (ScriptExpression value in Values)
			{
				scriptArray.Add(await context.EvaluateAsync(value).ConfigureAwait(continueOnCapturedContext: false));
			}
			return scriptArray;
		}

		protected override ScriptNode GetChildrenImpl(int index)
		{
			return index switch
			{
				0 => OpenBracketToken, 
				1 => Values, 
				2 => CloseBracketToken, 
				_ => null, 
			};
		}

		public override void Accept(ScriptVisitor visitor)
		{
			visitor.Visit(this);
		}

		public override TResult Accept<TResult>(ScriptVisitor<TResult> visitor)
		{
			return visitor.Visit(this);
		}

		public ScriptArrayInitializerExpression()
		{
			OpenBracketToken = ScriptToken.OpenBracket();
			Values = new ScriptList<ScriptExpression>();
			CloseBracketToken = ScriptToken.CloseBracket();
		}

		public override object Evaluate(TemplateContext context)
		{
			ScriptArray scriptArray = new ScriptArray();
			foreach (ScriptExpression value in Values)
			{
				object item = context.Evaluate(value);
				scriptArray.Add(item);
			}
			return scriptArray;
		}

		public override void PrintTo(ScriptPrinter printer)
		{
			printer.Write(OpenBracketToken);
			printer.WriteListWithCommas(Values);
			printer.Write(CloseBracketToken);
		}
	}
	[ScriptSyntax("assign expression", "<target_expression> = <value_expression>")]
	public class ScriptAssignExpression : ScriptExpression
	{
		private ScriptExpression _target;

		private ScriptToken _equalToken;

		private ScriptExpression _value;

		public override int ChildrenCount => 3;

		public ScriptExpression Target
		{
			get
			{
				return _target;
			}
			set
			{
				ParentToThis(ref _target, value);
			}
		}

		public ScriptToken EqualToken
		{
			get
			{
				return _equalToken;
			}
			set
			{
				ParentToThis(ref _equalToken, value);
			}
		}

		public ScriptExpression Value
		{
			get
			{
				return _value;
			}
			set
			{
				ParentToThis(ref _value, value);
			}
		}

		public override async ValueTask<object> EvaluateAsync(TemplateContext context)
		{
			object obj = ((EqualToken.TokenType != TokenType.Equal) ? (await GetValueToSetAsync(context).ConfigureAwait(continueOnCapturedContext: false)) : (await context.EvaluateAsync(Value).ConfigureAwait(continueOnCapturedContext: false)));
			object value = obj;
			await context.SetValueAsync(Target, value).ConfigureAwait(continueOnCapturedContext: false);
			return null;
		}

		private async ValueTask<object> GetValueToSetAsync(TemplateContext context)
		{
			object right = await context.EvaluateAsync(Value).ConfigureAwait(continueOnCapturedContext: false);
			object leftValue = await context.EvaluateAsync(Target).ConfigureAwait(continueOnCapturedContext: false);
			ScriptBinaryOperator op = EqualToken.TokenType switch
			{
				TokenType.PlusEqual => ScriptBinaryOperator.Add, 
				TokenType.MinusEqual => ScriptBinaryOperator.Subtract, 
				TokenType.AsteriskEqual => ScriptBinaryOperator.Multiply, 
				TokenType.DivideEqual => ScriptBinaryOperator.Divide, 
				TokenType.DoubleDivideEqual => ScriptBinaryOperator.DivideRound, 
				TokenType.PercentEqual => ScriptBinaryOperator.Modulus, 
				_ => throw new ScriptRuntimeException(context.CurrentSpan, $"Operator {EqualToken} is not a valid compound assignment operator"), 
			};
			return ScriptBinaryExpression.Evaluate(context, Span, op, leftValue, right);
		}

		protected override ScriptNode GetChildrenImpl(int index)
		{
			return index switch
			{
				0 => Target, 
				1 => EqualToken, 
				2 => Value, 
				_ => null, 
			};
		}

		public override void Accept(ScriptVisitor visitor)
		{
			visitor.Visit(this);
		}

		public override TResult Accept<TResult>(ScriptVisitor<TResult> visitor)
		{
			return visitor.Visit(this);
		}

		public ScriptAssignExpression()
		{
			EqualToken = ScriptToken.Equal();
		}

		public override object Evaluate(TemplateContext context)
		{
			object value = ((EqualToken.TokenType == TokenType.Equal) ? context.Evaluate(Value) : GetValueToSet(context));
			context.SetValue(Target, value);
			return null;
		}

		private object GetValueToSet(TemplateContext context)
		{
			object rightValue = context.Evaluate(Value);
			object leftValue = context.Evaluate(Target);
			ScriptBinaryOperator op = EqualToken.TokenType switch
			{
				TokenType.PlusEqual => ScriptBinaryOperator.Add, 
				TokenType.MinusEqual => ScriptBinaryOperator.Subtract, 
				TokenType.AsteriskEqual => ScriptBinaryOperator.Multiply, 
				TokenType.DivideEqual => ScriptBinaryOperator.Divide, 
				TokenType.DoubleDivideEqual => ScriptBinaryOperator.DivideRound, 
				TokenType.PercentEqual => ScriptBinaryOperator.Modulus, 
				_ => throw new ScriptRuntimeException(context.CurrentSpan, $"Operator {EqualToken} is not a valid compound assignment operator"), 
			};
			return ScriptBinaryExpression.Evaluate(context, Span, op, leftValue, rightValue);
		}

		public override bool CanHaveLeadingTrivia()
		{
			return false;
		}

		public override void PrintTo(ScriptPrinter printer)
		{
			printer.Write(Target);
			printer.Write(EqualToken);
			printer.Write(Value);
		}
	}
	[ScriptSyntax("binary expression", "<expression> operator <expression>")]
	public class ScriptBinaryExpression : ScriptExpression
	{
		[CompilerGenerated]
		private sealed class <RangeExclude>d__32 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			private int <>l__initialThreadId;

			private long left;

			public long <>3__left;

			private long right;

			public long <>3__right;

			private long <i>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <RangeExclude>d__32(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					if (left < right)
					{
						<i>5__2 = left;
						goto IL_0070;
					}
					<i>5__2 = left;
					goto IL_00c0;
				case 1:
					<>1__state = -1;
					<i>5__2++;
					goto IL_0070;
				case 2:
					{
						<>1__state = -1;
						<i>5__2--;
						goto IL_00c0;
					}
					IL_00c0:
					if (<i>5__2 > right)
					{
						<>2__current = FitToBestInteger(<i>5__2);
						<>1__state = 2;
						return true;
					}
					break;
					IL_0070:
					if (<i>5__2 < right)
					{
						<>2__current = FitToBestInteger(<i>5__2);
						<>1__state = 1;
						return true;
					}
					break;
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<object> IEnumerable<object>.GetEnumerator()
			{
				<RangeExclude>d__32 <RangeExclude>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<RangeExclude>d__ = this;
				}
				else
				{
					<RangeExclude>d__ = new <RangeExclude>d__32(0);
				}
				<RangeExclude>d__.left = <>3__left;
				<RangeExclude>d__.right = <>3__right;
				return <RangeExclude>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<object>)this).GetEnumerator();
			}
		}

		[CompilerGenerated]
		private sealed class <RangeExclude>d__34 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			private int <>l__initialThreadId;

			private BigInteger left;

			public BigInteger <>3__left;

			private BigInteger right;

			public BigInteger <>3__right;

			private BigInteger <i>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <RangeExclude>d__34(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					if (left < right)
					{
						<i>5__2 = left;
						goto IL_0077;
					}
					<i>5__2 = left;
					goto IL_00ce;
				case 1:
					<>1__state = -1;
					<i>5__2++;
					goto IL_0077;
				case 2:
					{
						<>1__state = -1;
						<i>5__2--;
						goto IL_00ce;
					}
					IL_00ce:
					if (<i>5__2 > right)
					{
						<>2__current = FitToBestInteger(<i>5__2);
						<>1__state = 2;
						return true;
					}
					break;
					IL_0077:
					if (<i>5__2 < right)
					{
						<>2__current = FitToBestInteger(<i>5__2);
						<>1__state = 1;
						return true;
					}
					break;
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<object> IEnumerable<object>.GetEnumerator()
			{
				<RangeExclude>d__34 <RangeExclude>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<RangeExclude>d__ = this;
				}
				else
				{
					<RangeExclude>d__ = new <RangeExclude>d__34(0);
				}
				<RangeExclude>d__.left = <>3__left;
				<RangeExclude>d__.right = <>3__right;
				return <RangeExclude>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<object>)this).GetEnumerator();
			}
		}

		[CompilerGenerated]
		private sealed class <RangeInclude>d__31 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			private int <>l__initialThreadId;

			private long left;

			public long <>3__left;

			private long right;

			public long <>3__right;

			private long <i>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <RangeInclude>d__31(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					if (left < right)
					{
						<i>5__2 = left;
						goto IL_0070;
					}
					<i>5__2 = left;
					goto IL_00c0;
				case 1:
					<>1__state = -1;
					<i>5__2++;
					goto IL_0070;
				case 2:
					{
						<>1__state = -1;
						<i>5__2--;
						goto IL_00c0;
					}
					IL_00c0:
					if (<i>5__2 >= right)
					{
						<>2__current = FitToBestInteger(<i>5__2);
						<>1__state = 2;
						return true;
					}
					break;
					IL_0070:
					if (<i>5__2 <= right)
					{
						<>2__current = FitToBestInteger(<i>5__2);
						<>1__state = 1;
						return true;
					}
					break;
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<object> IEnumerable<object>.GetEnumerator()
			{
				<RangeInclude>d__31 <RangeInclude>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<RangeInclude>d__ = this;
				}
				else
				{
					<RangeInclude>d__ = new <RangeInclude>d__31(0);
				}
				<RangeInclude>d__.left = <>3__left;
				<RangeInclude>d__.right = <>3__right;
				return <RangeInclude>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<object>)this).GetEnumerator();
			}
		}

		[CompilerGenerated]
		private sealed class <RangeInclude>d__33 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			private int <>l__initialThreadId;

			private BigInteger left;

			public BigInteger <>3__left;

			private BigInteger right;

			public BigInteger <>3__right;

			private BigInteger <i>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <RangeInclude>d__33(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					if (left < right)
					{
						<i>5__2 = left;
						goto IL_0077;
					}
					<i>5__2 = left;
					goto IL_00ce;
				case 1:
					<>1__state = -1;
					<i>5__2++;
					goto IL_0077;
				case 2:
					{
						<>1__state = -1;
						<i>5__2--;
						goto IL_00ce;
					}
					IL_00ce:
					if (<i>5__2 >= right

Mods/ItemReplacer.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Security;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BoneLib;
using BoneLib.BoneMenu;
using BoneLib.BoneMenu.UI;
using BoneLib.Notifications;
using HarmonyLib;
using Il2CppCysharp.Threading.Tasks;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Marrow.Data;
using Il2CppSLZ.Marrow.Pool;
using Il2CppSLZ.Marrow.Warehouse;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppSystem.Reflection;
using Il2CppUltEvents;
using ItemReplacer;
using ItemReplacer.Helpers;
using ItemReplacer.Managers;
using ItemReplacer.Patches;
using ItemReplacer.Utilities;
using LabFusion.Downloading;
using LabFusion.Downloading.ModIO;
using LabFusion.Entities;
using LabFusion.Marrow;
using LabFusion.Marrow.Extenders;
using LabFusion.Marrow.Integration;
using LabFusion.Marrow.Messages;
using LabFusion.Marrow.Patching;
using LabFusion.Marrow.Pool;
using LabFusion.Network;
using LabFusion.Player;
using LabFusion.RPC;
using LabFusion.Scene;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Scriban;
using Scriban.Parsing;
using Scriban.Runtime;
using Semver;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "ItemReplacer", "1.0.0", "T&H Modding", "https://thunderstore.io/c/bonelab/p/TH_Modding/ItemReplacer/")]
[assembly: MelonColor(0, 153, 0, 204)]
[assembly: MelonPriority(1000)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: AssemblyTitle("ItemReplacer")]
[assembly: AssemblyDescription("Replaces Items in BONELAB with user specified replacements.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("T&H Modding")]
[assembly: AssemblyProduct("ItemReplacer")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ItemReplacer
{
	public static class ModInfo
	{
		public const string Name = "ItemReplacer";

		public const string Author = "T&H Modding";

		public const string ThunderstoreAuthor = "TH_Modding";

		public const string Version = "1.0.0";

		public const string Description = "Replaces Items in BONELAB with user specified replacements.";

		public const string DownloadLink = "https://thunderstore.io/c/bonelab/p/TH_Modding/ItemReplacer/";
	}
	public class Core : MelonMod
	{
		private bool thunderstoreNotif;

		public static Instance Logger { get; private set; }

		public static Thunderstore Thunderstore { get; private set; }

		public static Core Instance { get; private set; }

		public override void OnInitializeMelon()
		{
			Instance = this;
			Logger = ((MelonBase)this).LoggerInstance;
			((MelonBase)this).LoggerInstance.Msg("Loading dependencies");
			((MelonBase)this).LoggerInstance.Msg("Setting up preferences");
			PreferencesManager.Setup();
			Fusion.Setup();
			((MelonBase)this).LoggerInstance.Msg("Checking for updates");
			Thunderstore = new Thunderstore("ItemReplacer / 1.0.0 A BONELAB Mod");
			Thunderstore.BL_FetchPackage("ItemReplacer", "TH_Modding", "1.0.0", ((MelonBase)this).LoggerInstance);
			Hooking.OnLevelLoaded += OnLevelLoad;
			((MelonBase)this).LoggerInstance.Msg("Setting up replacers");
			ReplacerManager.Setup();
			ReplacerManager.CreateFileWatcher();
			AssetWarehouse._onReady += Action.op_Implicit((Action)delegate
			{
				((MelonBase)this).LoggerInstance.Msg("Setting up BoneMenu");
				MenuManager.Setup();
			});
			((MelonBase)this).LoggerInstance.Msg("Initialized.");
		}

		public void OnLevelLoad(LevelInfo info)
		{
			MelonPreferences_Entry<bool> debugMode = PreferencesManager.DebugMode;
			if (debugMode != null && debugMode.Value)
			{
				((MelonBase)this).LoggerInstance.Msg("Level Loaded!");
			}
			CrateSpawnerPatches.LevelReplacements = 0;
			MenuManager.UpdateDebugCounts();
			if (!thunderstoreNotif)
			{
				thunderstoreNotif = true;
				Thunderstore.BL_SendNotification();
			}
		}
	}
}
namespace ItemReplacer.Utilities
{
	internal static class Fusion
	{
		public enum ModResult
		{
			NONE,
			FAILED,
			SUCCEEDED,
			CANCELED
		}

		public static bool HasFusion => MelonBase.FindMelon("LabFusion", "Lakatrazz") != null;

		public static bool IsConnected
		{
			get
			{
				if (HasFusion)
				{
					return Internal_IsConnected();
				}
				return false;
			}
		}

		internal static void Setup()
		{
			if (HasFusion)
			{
				Core.Logger.Msg("Removing LabFusion Patch...");
				Internal_Setup();
			}
		}

		internal static void Internal_Setup()
		{
			MelonBase val = MelonBase.FindMelon("LabFusion", "Lakatrazz");
			val.HarmonyInstance.Unpatch((MethodBase)typeof(CrateSpawner).GetMethod("SpawnSpawnableAsync"), (HarmonyPatchType)1, val.MelonAssembly.Assembly.FullName + ":" + val.Info.Name);
		}

		internal static bool Internal_IsConnected()
		{
			return NetworkInfo.HasServer;
		}

		public static void NetworkSpawnSpawnable(string barcode, CrateSpawner spawner, UniTaskCompletionSource<Poolee> source)
		{
			if (IsConnected)
			{
				Internal_NetworkSpawnSpawnable(barcode, spawner, source);
			}
		}

		private static void Internal_NetworkSpawnSpawnable(string barcode, CrateSpawner spawner, UniTaskCompletionSource<Poolee> source)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0025: Expected O, but got Unknown
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_0052: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: 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)
			Spawnable val = new Spawnable
			{
				crateRef = new SpawnableCrateReference(barcode),
				policyData = null
			};
			if (val != null && ((ScannableReference)val.crateRef).IsValid())
			{
				Transform transform = ((Component)spawner).transform;
				SpawnRequestInfo val2 = default(SpawnRequestInfo);
				val2.Spawnable = val;
				val2.Position = transform.position;
				val2.Rotation = transform.rotation;
				val2.SpawnCallback = delegate(SpawnCallbackInfo info)
				{
					//IL_0006: Unknown result type (might be due to invalid IL or missing references)
					OnNetworkSpawn(spawner, info, source);
				};
				val2.SpawnSource = (EntitySource)1;
				NetworkAssetSpawner.Spawn(val2);
			}
		}

		private static void OnNetworkSpawn(CrateSpawner spawner, object _info, UniTaskCompletionSource<Poolee> source)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			if (!(_info is SpawnCallbackInfo))
			{
				return;
			}
			SpawnCallbackInfo val = (SpawnCallbackInfo)_info;
			if (!((Object)(object)spawner == (Object)null))
			{
				GameObject spawned = val.Spawned;
				Poolee val2 = Poolee.Cache.Get(spawned);
				source.TrySetResult(val2);
				if (val.Entity == null)
				{
					CrateSpawnerPatches.OnFinishNetworkSpawn(spawner, val.Spawned);
				}
				ushort iD = val.Entity.ID;
				CrateSpawnerMessage.SendCrateSpawnerMessage(spawner, iD, (PlayerID)null);
			}
		}

		internal static bool HandleFusionCrateSpawner(string barcode, CrateSpawner spawner, out UniTask<Poolee> res)
		{
			res = new UniTask<Poolee>((Poolee)null);
			if (!NetworkSceneManager.IsLevelNetworked)
			{
				return true;
			}
			if (IsSingleplayerOnly(spawner))
			{
				return true;
			}
			if (!HasOwnership(spawner))
			{
				res = new UniTask<Poolee>((Poolee)null);
				return false;
			}
			UniTaskCompletionSource<Poolee> val = new UniTaskCompletionSource<Poolee>();
			res = new UniTask<Poolee>(((Il2CppObjectBase)val).TryCast<IUniTaskSource<Poolee>>(), (short)0);
			try
			{
				NetworkSpawnSpawnable(barcode, spawner, val);
			}
			catch (Exception ex)
			{
				Core.Logger.Error("An unexpected error has occurred while spawning replacement in Fusion", ex);
			}
			return false;
		}

		private static bool HasOwnership(CrateSpawner spawner)
		{
			NetworkEntity val = default(NetworkEntity);
			if (CrateSpawnerExtender.Cache.TryGet(spawner, ref val))
			{
				return val.IsOwner;
			}
			return NetworkSceneManager.IsLevelHost;
		}

		private static bool IsSingleplayerOnly(CrateSpawner crateSpawner)
		{
			if (Desyncer.Cache.ContainsSource(((Component)crateSpawner).gameObject))
			{
				return true;
			}
			Spawnable spawnable = crateSpawner._spawnable;
			if (spawnable == null)
			{
				return false;
			}
			if (!((ScannableReference)spawnable.crateRef).IsValid() || (Object)(object)((CrateReferenceT<SpawnableCrate>)(object)spawnable.crateRef).Crate == (Object)null)
			{
				return false;
			}
			return CrateFilterer.HasTags((Crate)(object)((CrateReferenceT<SpawnableCrate>)(object)spawnable.crateRef).Crate, new string[1] { "Singleplayer Only" });
		}

		public static void RequestInstall(int modId, Action<ModResult> callback, FunctionElement element = null)
		{
			if (HasFusion)
			{
				Internal_RequestInstall(modId, callback, element);
			}
		}

		private static void Internal_RequestInstall(int modId, Action<ModResult> callback, FunctionElement element = null)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_0052: Expected O, but got Unknown
			//IL_0057: Expected O, but got Unknown
			ModIODownloader.EnqueueDownload(new ModTransaction
			{
				ModFile = new ModIOFile(modId, (int?)null),
				Temporary = false,
				Reporter = ((element != null) ? new MenuReporter(element) : null),
				Callback = (DownloadCallback)delegate(DownloadCallbackInfo c)
				{
					//IL_000b: Unknown result type (might be due to invalid IL or missing references)
					//IL_000c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0016: Expected I4, but got Unknown
					callback?.Invoke((ModResult)c.Result);
				}
			});
		}
	}
	internal class MenuReporter : IProgress<float>
	{
		[CompilerGenerated]
		private FunctionElement <elem>P;

		public string Name { get; set; }

		public MenuReporter(FunctionElement elem)
		{
			<elem>P = elem;
			Name = ((Element)<elem>P).ElementName;
			base..ctor();
		}

		public void Report(float value)
		{
			((Element)<elem>P).ElementName = $"{Name} [{Math.Round(value * 100f, 2)}%]";
		}
	}
	public class ScribanCrate
	{
		public enum CrateType
		{
			Spawnable,
			Avatar,
			Level,
			VFX
		}

		public CrateType Type { get; }

		public string Barcode { get; }

		public string Title { get; }

		public string Description { get; }

		public bool Redacted { get; }

		public bool Unlockable { get; }

		public ScriptArray<string> Tags { get; }

		public ScriptArray<ScribanBoneTag> BoneTags { get; }

		public ScribanPallet Pallet { get; }

		public ScribanCrate(Crate crate, ScribanPallet pallet = null)
		{
			ScribanCrate scribanCrate = this;
			Title = ((Scannable)crate).Title;
			Description = ((Scannable)crate).Description;
			Redacted = ((Scannable)crate).Redacted;
			Barcode = ((Scannable)crate).Barcode.ID;
			Unlockable = ((Scannable)crate).Unlockable;
			if (crate.Tags == null)
			{
				Tags = new ScriptArray<string>();
			}
			else
			{
				ScriptArray<string> val = new ScriptArray<string>();
				Enumerator<string> enumerator = crate.Tags.GetEnumerator();
				while (enumerator.MoveNext())
				{
					string current = enumerator.Current;
					val.Add(current);
				}
				Tags = val;
			}
			Pallet = pallet ?? new ScribanPallet(crate.Pallet);
			if (crate.BoneTags == null || crate.BoneTags.Tags == null)
			{
				BoneTags = new ScriptArray<ScribanBoneTag>();
			}
			else
			{
				List<ScribanBoneTag> scribanBoneTags = new List<ScribanBoneTag>();
				crate.BoneTags.Tags.ForEach(Action<BoneTagReference>.op_Implicit((Action<BoneTagReference>)delegate(BoneTagReference c)
				{
					scribanBoneTags.Add(new ScribanBoneTag(((DataCardReference<BoneTag>)(object)c).DataCard, scribanCrate.Pallet));
				}));
				ScriptArray<ScribanBoneTag> val2 = new ScriptArray<ScribanBoneTag>();
				foreach (ScribanBoneTag item in scribanBoneTags)
				{
					val2.Add(item);
				}
				BoneTags = val2;
			}
			if (((MemberInfo)((Object)crate).GetIl2CppType()).Name == "SpawnableCrate")
			{
				Type = CrateType.Spawnable;
				return;
			}
			if (((MemberInfo)((Object)crate).GetIl2CppType()).Name == "AvatarCrate")
			{
				Type = CrateType.Avatar;
				return;
			}
			if (((MemberInfo)((Object)crate).GetIl2CppType()).Name == "LevelCrate")
			{
				Type = CrateType.Level;
				return;
			}
			if (((MemberInfo)((Object)crate).GetIl2CppType()).Name == "VFXCrate")
			{
				Type = CrateType.VFX;
				return;
			}
			throw new ArgumentOutOfRangeException("Crate type " + ((MemberInfo)((Object)crate).GetIl2CppType()).Name + " is not supported.");
		}
	}
	public class ScribanPallet
	{
		public string Title { get; }

		public string Description { get; }

		public string Author { get; }

		public string Barcode { get; }

		public string[] Tags { get; }

		public bool Redacted { get; }

		public bool Unlockable { get; }

		public string Version { get; }

		public string SDKVersion { get; }

		public ScriptArray<ScribanCrate> Crates { get; }

		public ScriptArray<ScribanChangeLog> ChangeLogs { get; }

		public ScriptArray<ScribanDataCard> DataCards { get; }

		public string[] Dependencies { get; }

		public ScribanPallet(Pallet pallet)
		{
			Barcode = ((Scannable)pallet).Barcode.ID;
			Unlockable = ((Scannable)pallet).Unlockable;
			Redacted = ((Scannable)pallet).Redacted;
			Title = ((Scannable)pallet).Title;
			if (pallet.Tags == null)
			{
				Tags = Array.Empty<string>();
			}
			else
			{
				Tags = Il2CppArrayBase<string>.op_Implicit(pallet.Tags.ToArray());
			}
			Version = pallet.Version;
			if (pallet.Crates == null)
			{
				Crates = new ScriptArray<ScribanCrate>();
			}
			else
			{
				List<ScribanCrate> scribanCrates = new List<ScribanCrate>();
				pallet.Crates.ForEach(Action<Crate>.op_Implicit((Action<Crate>)delegate(Crate c)
				{
					scribanCrates.Add(new ScribanCrate(c, this));
				}));
				ScriptArray<ScribanCrate> val = new ScriptArray<ScribanCrate>();
				foreach (ScribanCrate item in scribanCrates)
				{
					val.Add(item);
				}
				Crates = val;
			}
			Author = pallet.Author;
			Description = ((Scannable)pallet).Description;
			SDKVersion = pallet.SDKVersion;
			if (pallet.ChangeLogs == null)
			{
				ChangeLogs = new ScriptArray<ScribanChangeLog>();
			}
			else
			{
				List<ScribanChangeLog> list = new List<ScribanChangeLog>();
				Enumerator<ChangeLog> enumerator2 = pallet.ChangeLogs.GetEnumerator();
				while (enumerator2.MoveNext())
				{
					ChangeLog current2 = enumerator2.Current;
					list.Add(new ScribanChangeLog(current2));
				}
				ScriptArray<ScribanChangeLog> val2 = new ScriptArray<ScribanChangeLog>();
				foreach (ScribanChangeLog item2 in list)
				{
					val2.Add(item2);
				}
				ChangeLogs = val2;
			}
			if (pallet.DataCards == null)
			{
				DataCards = new ScriptArray<ScribanDataCard>();
			}
			else
			{
				List<ScribanDataCard> scribanDataCards = new List<ScribanDataCard>();
				pallet.DataCards.ForEach(Action<DataCard>.op_Implicit((Action<DataCard>)delegate(DataCard c)
				{
					scribanDataCards.Add(new ScribanDataCard(c, this));
				}));
				ScriptArray<ScribanDataCard> val3 = new ScriptArray<ScribanDataCard>();
				foreach (ScribanDataCard item3 in scribanDataCards)
				{
					val3.Add(item3);
				}
				DataCards = val3;
			}
			if (pallet.PalletDependencies == null)
			{
				Dependencies = Array.Empty<string>();
				return;
			}
			List<string> dependencies = new List<string>();
			pallet.PalletDependencies.ForEach(Action<PalletReference>.op_Implicit((Action<PalletReference>)delegate(PalletReference p)
			{
				dependencies.Add(((ScannableReference)p).Barcode.ID);
			}));
			Dependencies = dependencies.ToArray();
		}
	}
	public class ScribanChangeLog
	{
		public string Title { get; } = changelog.title;


		public string Version { get; } = changelog.version;


		public string Text { get; } = changelog.text;


		public ScribanChangeLog(ChangeLog changelog)
		{
		}
	}
	public class ScribanDataCard
	{
		public string Title { get; } = ((Scannable)dataCard).Title;


		public string Description { get; } = ((Scannable)dataCard).Description;


		public string Barcode { get; } = ((Scannable)dataCard).Barcode.ID;


		public bool Redacted { get; } = ((Scannable)dataCard).Redacted;


		public bool Unlockable { get; } = ((Scannable)dataCard).Unlockable;


		public ScribanPallet Pallet { get; } = pallet ?? new ScribanPallet(dataCard.Pallet);


		public ScribanDataCard(DataCard dataCard, ScribanPallet pallet = null)
		{
		}
	}
	public class ScribanBoneTag
	{
		public string Title { get; } = ((Scannable)boneTag).Title;


		public string Description { get; } = ((Scannable)boneTag).Description;


		public string Barcode { get; } = ((Scannable)boneTag).Barcode.ID;


		public bool Redacted { get; } = ((Scannable)boneTag).Redacted;


		public bool Unlockable { get; } = ((Scannable)boneTag).Unlockable;


		public ScribanPallet Pallet { get; } = pallet ?? new ScribanPallet(((DataCard)boneTag).Pallet);


		public ScribanBoneTag(BoneTag boneTag, ScribanPallet pallet = null)
		{
		}
	}
	public static class ScribanHelper
	{
		public static ScribanPallet GetPallet(string barcode)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			Pallet pallet = default(Pallet);
			if (AssetWarehouse.Instance.TryGetPallet(new Barcode(barcode), ref pallet))
			{
				return new ScribanPallet(pallet);
			}
			return null;
		}
	}
	public static class ScribanMatcher
	{
		public static bool Match(string barcode, ReplacerEntry entry)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			Template template = entry.Template;
			if (template == null || template.HasErrors)
			{
				return false;
			}
			Crate crate = default(Crate);
			if (!AssetWarehouse.Instance.TryGetCrate(new Barcode(barcode), ref crate))
			{
				return false;
			}
			ScribanCrate scribanCrate = new ScribanCrate(crate);
			ScriptObject val = new ScriptObject((IEqualityComparer<string>)StringComparer.OrdinalIgnoreCase);
			ScriptObjectExtensions.Import((IScriptObject)(object)val, (object)scribanCrate, (MemberFilterDelegate)null, (MemberRenamerDelegate)null);
			ScriptObjectExtensions.Import((IScriptObject)(object)val, (object)typeof(ScribanHelper), (MemberFilterDelegate)null, (MemberRenamerDelegate)null);
			TemplateContext val2 = new TemplateContext();
			val2.PushGlobal((IScriptObject)(object)val);
			return entry.Template.Render(val2).Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase);
		}
	}
	public class Thunderstore
	{
		public readonly string UserAgent;

		private Package _fetchedPackage;

		private bool _isLatestVersion;

		private string _currentVersion;

		public bool IsV1Deprecated { get; set; }

		public Thunderstore(string userAgent)
		{
			UserAgent = userAgent;
		}

		public Thunderstore(string userAgent, bool isV1Deprecated)
			: this(userAgent)
		{
			IsV1Deprecated = isV1Deprecated;
		}

		public Thunderstore()
		{
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			if (executingAssembly != null)
			{
				AssemblyName name = executingAssembly.GetName();
				if (name != null)
				{
					UserAgent = $"{name.Name} / {name.Version} C# Application";
				}
			}
		}

		public Thunderstore(bool isV1Deprecated)
		{
			IsV1Deprecated = isV1Deprecated;
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			if (executingAssembly != null)
			{
				AssemblyName name = executingAssembly.GetName();
				if (name != null)
				{
					UserAgent = $"{name.Name} / {name.Version}";
				}
			}
		}

		public void BL_FetchPackage(string name, string author, string currentVersion, Instance logger = null)
		{
			if (_fetchedPackage != null)
			{
				return;
			}
			_currentVersion = currentVersion;
			try
			{
				_fetchedPackage = GetPackage(author, name);
				if (_fetchedPackage == null && logger != null)
				{
					logger.Warning("Could not find Thunderstore package for " + name);
				}
				if (string.IsNullOrWhiteSpace(_fetchedPackage.Latest?.Version) && logger != null)
				{
					logger.Warning("Latest version could not be found or the version is empty");
				}
				_isLatestVersion = _fetchedPackage.IsLatestVersion(currentVersion);
				if (!_isLatestVersion)
				{
					if (logger != null)
					{
						logger.Warning($"A new version of {name} is available: v{_fetchedPackage.Latest.Version} while the current is v{currentVersion}. It is recommended that you update");
					}
				}
				else if (SemVersion.Parse(currentVersion, false) == _fetchedPackage.Latest.SemanticVersion)
				{
					if (logger != null)
					{
						logger.Msg($"Latest version of {name} is installed! (v{currentVersion})");
					}
				}
				else if (logger != null)
				{
					logger.Msg($"Beta release of {name} is installed (v{_fetchedPackage.Latest.Version} is newest, v{currentVersion} is installed)");
				}
			}
			catch (ThunderstorePackageNotFoundException)
			{
				if (logger != null)
				{
					logger.Warning("Could not find Thunderstore package for " + name);
				}
			}
			catch (Exception ex2)
			{
				if (logger != null)
				{
					logger.Error("An unexpected error has occurred while trying to check if " + name + " is the latest version", ex2);
				}
			}
		}

		public void BL_CreateMenuLabel(Page page, bool createBlankSpace = true)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if (_fetchedPackage != null)
			{
				if (createBlankSpace)
				{
					((Element)page.CreateFunction("", Color.white, (Action)null)).SetProperty((ElementProperties)1);
				}
				((Element)page.CreateFunction("Current Version: v" + _currentVersion + ((_isLatestVersion || _fetchedPackage == null) ? string.Empty : "<br><color=#00FF00>(Update available!)</color>"), Color.white, (Action)null)).SetProperty((ElementProperties)1);
			}
		}

		public void BL_SendNotification()
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			if (_fetchedPackage != null && !_isLatestVersion)
			{
				NotificationText message = default(NotificationText);
				((NotificationText)(ref message))..ctor($"There is a new version of {_fetchedPackage.Name}. Go to Thunderstore and download the latest version which is <color=#00FF00>v{_fetchedPackage.Latest.Version}</color>", Color.white, true);
				Notifier.Send(new Notification
				{
					Title = NotificationText.op_Implicit("Update!"),
					Message = message,
					PopupLength = 5f,
					ShowTitleOnPopup = true,
					Type = (NotificationType)1
				});
			}
		}

		public Package GetPackage(string @namespace, string name, string version = null)
		{
			Package package = SendRequest<Package>($"https://thunderstore.io/api/experimental/package/{@namespace}/{name}/{version ?? string.Empty}");
			if (!IsV1Deprecated && package != null)
			{
				V1PackageMetrics packageMetrics = GetPackageMetrics(@namespace, name);
				if (packageMetrics != null)
				{
					package.TotalDownloads = packageMetrics.Downloads;
					package.RatingScore = packageMetrics.RatingScore;
				}
			}
			return package;
		}

		private static bool IsTaskGood<T>(Task<T> task) where T : class
		{
			if (task != null && task.IsCompletedSuccessfully)
			{
				return task.Result != null;
			}
			return false;
		}

		public T SendRequest<T>(string url)
		{
			Task<HttpResponseMessage> async = new HttpClient(new HttpClientHandler
			{
				ClientCertificateOptions = ClientCertificateOption.Manual,
				ServerCertificateCustomValidationCallback = (HttpRequestMessage _, X509Certificate2? _, X509Chain? _, SslPolicyErrors _) => true
			})
			{
				DefaultRequestHeaders = 
				{
					{ "User-Agent", UserAgent },
					{ "Accept", "application/json" }
				}
			}.GetAsync(url);
			async.Wait();
			HttpResponseMessage httpResponseMessage = async?.Result;
			if (IsTaskGood(async))
			{
				if (!httpResponseMessage.IsSuccessStatusCode)
				{
					HandleHttpError(httpResponseMessage);
					return default(T);
				}
				Task<string> task = httpResponseMessage.Content.ReadAsStringAsync();
				task.Wait();
				string text = task?.Result;
				if (IsTaskGood(task))
				{
					return JsonConvert.DeserializeObject<T>(text);
				}
			}
			return default(T);
		}

		private static void HandleHttpError(HttpResponseMessage result)
		{
			if (IsThunderstoreError(result, out var details))
			{
				if (IsPackageNotFound(result, details))
				{
					throw new ThunderstorePackageNotFoundException("Thunderstore could not find the package");
				}
				throw new ThunderstoreErrorException("Thunderstore API has thrown an unexpected error!", result);
			}
			result.EnsureSuccessStatusCode();
		}

		public V1PackageMetrics GetPackageMetrics(string @namespace, string name)
		{
			if (IsV1Deprecated)
			{
				return null;
			}
			return SendRequest<V1PackageMetrics>($"https://thunderstore.io/api/v1/package-metrics/{@namespace}/{name}/");
		}

		public bool IsLatestVersion(string @namespace, string name, string currentVersion)
		{
			SemVersion currentVersion2 = default(SemVersion);
			if (SemVersion.TryParse(currentVersion, ref currentVersion2, false))
			{
				return IsLatestVersion(@namespace, name, currentVersion2);
			}
			return false;
		}

		public bool IsLatestVersion(string @namespace, string name, Version currentVersion)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Expected O, but got Unknown
			return IsLatestVersion(@namespace, name, new SemVersion(currentVersion));
		}

		public bool IsLatestVersion(string @namespace, string name, SemVersion currentVersion)
		{
			if (!IsV1Deprecated)
			{
				return GetPackageMetrics(@namespace, name)?.IsLatestVersion(currentVersion) ?? false;
			}
			return GetPackage(@namespace, name)?.IsLatestVersion(currentVersion) ?? false;
		}

		private static bool IsPackageNotFound(HttpResponseMessage response, string details = "")
		{
			if (response.StatusCode != HttpStatusCode.NotFound)
			{
				return false;
			}
			if (details != null && details.Length == 0)
			{
				details = GetDetails(response);
			}
			return string.Equals(details, "Not found.", StringComparison.OrdinalIgnoreCase);
		}

		private static bool IsThunderstoreError(HttpResponseMessage response, out string details)
		{
			details = GetDetails(response);
			return !string.IsNullOrWhiteSpace(details);
		}

		private static string GetDetails(HttpResponseMessage response)
		{
			if (response.IsSuccessStatusCode)
			{
				return null;
			}
			Task<string> task = response.Content.ReadAsStringAsync();
			task.Wait();
			string result = task.Result;
			if (string.IsNullOrWhiteSpace(result))
			{
				return null;
			}
			MelonPreferences_Entry<bool> debugMode = PreferencesManager.DebugMode;
			if (debugMode != null && debugMode.Value)
			{
				Core.Logger.Msg("Thunderstore returned an error response: " + result);
			}
			ThunderstoreErrorResponse thunderstoreErrorResponse;
			try
			{
				thunderstoreErrorResponse = JsonConvert.DeserializeObject<ThunderstoreErrorResponse>(result);
			}
			catch (JsonException)
			{
				return null;
			}
			if (!string.IsNullOrWhiteSpace(thunderstoreErrorResponse?.Details))
			{
				return thunderstoreErrorResponse.Details;
			}
			return null;
		}
	}
	public class Package
	{
		[JsonProperty("namespace")]
		public string Namespace { get; internal set; }

		[JsonProperty("name")]
		public string Name { get; internal set; }

		[JsonProperty("full_name")]
		public string FullName { get; internal set; }

		[JsonProperty("owner")]
		public string Owner { get; internal set; }

		[JsonProperty("package_url")]
		public string PackageURL { get; internal set; }

		[JsonProperty("date_created")]
		public DateTime CreatedAt { get; internal set; }

		[JsonProperty("date_updated")]
		public DateTime UpdatedAt { get; internal set; }

		[JsonProperty("rating_score")]
		public int RatingScore { get; internal set; }

		[JsonProperty("is_pinned")]
		public bool IsPinned { get; internal set; }

		[JsonProperty("is_deprecated")]
		public bool IsDeprecated { get; internal set; }

		[JsonProperty("total_downloads")]
		public int TotalDownloads { get; internal set; }

		[JsonProperty("latest")]
		public PackageVersion Latest { get; internal set; }

		[JsonProperty("community_listings")]
		public PackageListing[] CommunityListings { get; internal set; }

		public bool IsLatestVersion(string current)
		{
			if (string.IsNullOrWhiteSpace(current))
			{
				return false;
			}
			if (Latest == null || Latest.SemanticVersion == (SemVersion)null)
			{
				return false;
			}
			SemVersion val = default(SemVersion);
			if (SemVersion.TryParse(current, ref val, false))
			{
				return val >= Latest.SemanticVersion;
			}
			return false;
		}

		public bool IsLatestVersion(SemVersion current)
		{
			if (current == (SemVersion)null)
			{
				return false;
			}
			if (Latest == null || Latest.SemanticVersion == (SemVersion)null)
			{
				return false;
			}
			return current >= Latest.SemanticVersion;
		}

		public bool IsLatestVersion(Version current)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			if (current == null)
			{
				return false;
			}
			if (Latest == null || Latest.SemanticVersion == (SemVersion)null)
			{
				return false;
			}
			return new SemVersion(current) >= Latest.SemanticVersion;
		}
	}
	public class PackageVersion
	{
		[JsonProperty("namespace")]
		public string Namespace { get; internal set; }

		[JsonProperty("name")]
		public string Name { get; internal set; }

		[JsonProperty("version_number")]
		public string Version
		{
			get
			{
				return ((object)SemanticVersion).ToString();
			}
			internal set
			{
				SemanticVersion = SemVersion.Parse(value, false);
			}
		}

		[JsonIgnore]
		public SemVersion SemanticVersion { get; internal set; }

		[JsonProperty("full_name")]
		public string FullName { get; internal set; }

		[JsonProperty("description")]
		public string Description { get; internal set; }

		[JsonProperty("icon")]
		public string Icon { get; internal set; }

		[JsonProperty("dependencies")]
		public List<string> Dependencies { get; internal set; }

		[JsonProperty("download_url")]
		public string DownloadURL { get; internal set; }

		[JsonProperty("date_created")]
		public DateTime CreatedAt { get; internal set; }

		[JsonProperty("downloads")]
		public int Downloads { get; internal set; }

		[JsonProperty("website_url")]
		public string WebsiteURL { get; internal set; }

		[JsonProperty("is_active")]
		public bool IsActive { get; internal set; }
	}
	public class PackageListing
	{
		public enum ReviewStatus
		{
			UNREVIEWED,
			APPROVED,
			REJECTED
		}

		[JsonProperty("has_nsfw_content")]
		public bool HasNSFWContent { get; internal set; }

		[JsonProperty("categories")]
		public List<string> Categories { get; internal set; }

		[JsonProperty("community")]
		public string Community { get; internal set; }

		[JsonProperty("review_status")]
		public string ReviewStatusString
		{
			get
			{
				return ReviewStatusValue.ToString();
			}
			internal set
			{
				if (value == null)
				{
					throw new ArgumentNullException("value");
				}
				if (string.Equals(value, "unreviewed", StringComparison.OrdinalIgnoreCase))
				{
					ReviewStatusValue = ReviewStatus.UNREVIEWED;
				}
				else if (string.Equals(value, "approved", StringComparison.OrdinalIgnoreCase))
				{
					ReviewStatusValue = ReviewStatus.APPROVED;
				}
				else if (string.Equals(value, "rejected", StringComparison.OrdinalIgnoreCase))
				{
					ReviewStatusValue = ReviewStatus.REJECTED;
				}
			}
		}

		[JsonIgnore]
		public ReviewStatus ReviewStatusValue { get; internal set; }
	}
	public class V1PackageMetrics
	{
		[JsonProperty("downloads")]
		public int Downloads { get; internal set; }

		[JsonProperty("rating_score")]
		public int RatingScore { get; internal set; }

		[JsonProperty("latest_version")]
		public string LatestVersion
		{
			get
			{
				return ((object)LatestSemanticVersion).ToString();
			}
			internal set
			{
				LatestSemanticVersion = SemVersion.Parse(value, false);
			}
		}

		[JsonIgnore]
		public SemVersion LatestSemanticVersion { get; internal set; }

		public bool IsLatestVersion(string current)
		{
			if (string.IsNullOrWhiteSpace(current))
			{
				return false;
			}
			if (LatestSemanticVersion == (SemVersion)null)
			{
				return false;
			}
			SemVersion val = default(SemVersion);
			if (SemVersion.TryParse(current, ref val, false))
			{
				return val >= LatestSemanticVersion;
			}
			return false;
		}

		public bool IsLatestVersion(SemVersion current)
		{
			if (current == (SemVersion)null)
			{
				return false;
			}
			if (LatestSemanticVersion == (SemVersion)null)
			{
				return false;
			}
			return current >= LatestSemanticVersion;
		}

		public bool IsLatestVersion(Version current)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			if (current == null)
			{
				return false;
			}
			if (LatestSemanticVersion == (SemVersion)null)
			{
				return false;
			}
			return new SemVersion(current) >= LatestSemanticVersion;
		}
	}
	public class ThunderstoreErrorResponse
	{
		[JsonProperty("detail")]
		public string Details { get; internal set; }
	}
	public class ThunderstoreErrorException : Exception
	{
		public string Details { get; }

		public HttpStatusCode HttpStatusCode { get; }

		public ThunderstoreErrorException()
		{
		}

		public ThunderstoreErrorException(string message)
			: base(message)
		{
		}

		public ThunderstoreErrorException(string message, Exception innerException)
			: base(message, innerException)
		{
		}

		public ThunderstoreErrorException(string message, string details, HttpStatusCode httpStatusCode, Exception innerException)
			: base(message, innerException)
		{
			Details = details;
			HttpStatusCode = httpStatusCode;
		}

		public ThunderstoreErrorException(string message, HttpResponseMessage response)
			: base(message)
		{
			if (response.IsSuccessStatusCode)
			{
				return;
			}
			HttpStatusCode = response.StatusCode;
			Task<string> task = response.Content.ReadAsStringAsync();
			task.Wait();
			string result = task.Result;
			if (string.IsNullOrWhiteSpace(result))
			{
				Details = string.Empty;
				return;
			}
			ThunderstoreErrorResponse thunderstoreErrorResponse;
			try
			{
				thunderstoreErrorResponse = JsonConvert.DeserializeObject<ThunderstoreErrorResponse>(result);
			}
			catch (JsonException)
			{
				Details = string.Empty;
				return;
			}
			if (thunderstoreErrorResponse != null)
			{
				Details = thunderstoreErrorResponse.Details;
			}
		}
	}
	public class ThunderstorePackageNotFoundException : ThunderstoreErrorException
	{
		public string Namespace { get; }

		public string Name { get; }

		public string Version { get; }

		public ThunderstorePackageNotFoundException(string message, string @namespace, string name, string details, HttpStatusCode httpStatusCode, Exception innerException)
			: base(message, details, httpStatusCode, innerException)
		{
			Namespace = @namespace;
			Name = name;
		}

		public ThunderstorePackageNotFoundException(string message, string @namespace, string name, string version, string details, HttpStatusCode httpStatusCode, Exception innerException)
			: base(message, details, httpStatusCode, innerException)
		{
			Namespace = @namespace;
			Name = name;
			Version = version;
		}

		public ThunderstorePackageNotFoundException(string message, string @namespace, string name, HttpResponseMessage response)
			: base(message, response)
		{
			Namespace = @namespace;
			Name = name;
		}

		public ThunderstorePackageNotFoundException(string message, string @namespace, string name, string version, HttpResponseMessage response)
			: base(message, response)
		{
			Namespace = @namespace;
			Name = name;
			Version = version;
		}

		public ThunderstorePackageNotFoundException()
		{
		}

		public ThunderstorePackageNotFoundException(string message)
			: base(message)
		{
		}

		public ThunderstorePackageNotFoundException(string message, Exception innerException)
			: base(message, innerException)
		{
		}

		public ThunderstorePackageNotFoundException(string message, string details, HttpStatusCode httpStatusCode, Exception innerException)
			: base(message, details, httpStatusCode, innerException)
		{
		}

		public ThunderstorePackageNotFoundException(string message, HttpResponseMessage response)
			: base(message, response)
		{
		}
	}
	public sealed class UnityFileSystemWatcher : FileSystemWatcher, IDisposable
	{
		private readonly List<EventArgs> _Queue = new List<EventArgs>();

		public IReadOnlyList<EventArgs> Queue => _Queue.AsReadOnly();

		public new event EventHandler<RenamedEventArgs> Renamed;

		public new event EventHandler<FileSystemEventArgs> Created;

		public new event EventHandler<FileSystemEventArgs> Deleted;

		public new event EventHandler<FileSystemEventArgs> Changed;

		public new event EventHandler Disposed;

		public new event EventHandler<ErrorEventArgs> Error;

		public UnityFileSystemWatcher()
		{
			Init();
		}

		public UnityFileSystemWatcher(string path)
			: base(path)
		{
			Init();
		}

		public UnityFileSystemWatcher(string path, string filter)
			: base(path, filter)
		{
			Init();
		}

		private void Init()
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			base.Renamed += delegate(object sender, RenamedEventArgs e)
			{
				_Queue.Add(e);
			};
			base.Created += delegate(object sender, FileSystemEventArgs e)
			{
				_Queue.Add(e);
			};
			base.Deleted += delegate(object sender, FileSystemEventArgs e)
			{
				_Queue.Add(e);
			};
			base.Changed += delegate(object sender, FileSystemEventArgs e)
			{
				_Queue.Add(e);
			};
			base.Disposed += delegate(object? sender, EventArgs e)
			{
				_Queue.Add(e);
			};
			base.Error += delegate(object sender, ErrorEventArgs e)
			{
				_Queue.Add(e);
			};
			((MelonEventBase<LemonAction>)(object)MelonEvents.OnUpdate).Subscribe(new LemonAction(Update), 0, false);
		}

		private void Update()
		{
			if (Queue.Count <= 0)
			{
				return;
			}
			for (int num = Queue.Count - 1; num >= 0; num--)
			{
				EventArgs eventArgs = _Queue[num];
				try
				{
					if (eventArgs is RenamedEventArgs e)
					{
						this.Renamed?.Invoke(this, e);
					}
					else if (eventArgs is FileSystemEventArgs args)
					{
						TriggerProperEvent(args);
					}
					else if (eventArgs is ErrorEventArgs e2)
					{
						this.Error?.Invoke(this, e2);
					}
					else if (eventArgs != null)
					{
						EventArgs args2 = eventArgs;
						WatcherDisposed(args2);
					}
				}
				catch (Exception ex)
				{
					ErrorMsg("An unexpected error has occurred while triggering file system watcher events", ex);
				}
				finally
				{
					_Queue.RemoveAt(num);
				}
			}
		}

		private void TriggerProperEvent(FileSystemEventArgs args)
		{
			if (args.ChangeType == WatcherChangeTypes.Created)
			{
				this.Created?.Invoke(this, args);
			}
			else if (args.ChangeType == WatcherChangeTypes.Deleted)
			{
				this.Deleted?.Invoke(this, args);
			}
			else if (args.ChangeType == WatcherChangeTypes.Changed)
			{
				this.Changed?.Invoke(this, args);
			}
		}

		private void WatcherDisposed(EventArgs args)
		{
			try
			{
				this.Disposed?.Invoke(this, args);
			}
			catch (Exception ex)
			{
				ErrorMsg("An unexpected error has occurred while running Disposed event", ex);
			}
			finally
			{
				Dispose();
			}
		}

		private static void ErrorMsg(string message, Exception ex)
		{
			MelonLogger.Error("[FileSystemWatcher] " + message, ex);
		}

		public new void Dispose()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			try
			{
				base.Dispose();
			}
			catch (Exception ex)
			{
				ErrorMsg("An exception occurred while disposing of FileSystemWatcher", ex);
			}
			((MelonEventBase<LemonAction>)(object)MelonEvents.OnUpdate).Unsubscribe(new LemonAction(Update));
			this.Disposed?.Invoke(this, EventArgs.Empty);
			GC.SuppressFinalize(this);
		}
	}
}
namespace ItemReplacer.Patches
{
	[HarmonyPatch(typeof(CrateSpawner))]
	internal static class CrateSpawnerPatches
	{
		public static int TotalReplacements { get; internal set; }

		public static int LevelReplacements { get; internal set; }

		[HarmonyPrefix]
		[HarmonyPriority(int.MaxValue)]
		[HarmonyPatch("SpawnSpawnableAsync")]
		public static bool SpawnSpawnableAsyncPrefix(CrateSpawner __instance, bool isHidden, ref UniTask<Poolee> __result)
		{
			Core.Logger.Msg("hi");
			try
			{
				if (ReplaceItem(__instance, ref __result))
				{
					return CrateSpawnerPatches.SpawnSpawnableAsyncPrefix(__instance, isHidden, ref __result);
				}
				return false;
			}
			catch (Exception ex)
			{
				Core.Logger.Error("An unexpected error has occurred in the CrateSpawner prefix", ex);
				return true;
			}
		}

		private static bool ReplaceItem(CrateSpawner __instance, ref UniTask<Poolee> __result)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			MelonPreferences_Entry<bool> enabled = PreferencesManager.Enabled;
			if (enabled == null || !enabled.Value)
			{
				return true;
			}
			CrateSpawner obj = __instance;
			object obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				SpawnableCrateReference spawnableCrateReference = obj.spawnableCrateReference;
				obj2 = ((spawnableCrateReference != null) ? ((ScannableReference)spawnableCrateReference).Barcode : null);
			}
			if ((Barcode)obj2 == (Barcode)null)
			{
				return true;
			}
			string iD = ((ScannableReference)__instance.spawnableCrateReference).Barcode.ID;
			SpawnableCrateReference spawnableCrateReference2 = __instance.spawnableCrateReference;
			object obj3;
			if (spawnableCrateReference2 == null)
			{
				obj3 = null;
			}
			else
			{
				SpawnableCrate crate = ((CrateReferenceT<SpawnableCrate>)(object)spawnableCrateReference2).Crate;
				obj3 = ((crate != null) ? ((Scannable)crate).Title : null);
			}
			if (obj3 == null)
			{
				obj3 = "N/A";
			}
			string text = (string)obj3;
			string replacement = GetReplacement(iD);
			if (replacement != null)
			{
				SpawnableCrateReference val = new SpawnableCrateReference(replacement);
				SpawnableCrate val2 = default(SpawnableCrate);
				if ((int)val == 0 || !((CrateReferenceT<SpawnableCrate>)val).TryGetCrate(ref val2))
				{
					Core.Logger.Error("Barcode does not exist in-game, the mod may not be installed. Not replacing item. (Barcode: " + replacement + ")");
					return true;
				}
				if (PreferencesManager.IsDebug())
				{
					Core.Logger.Msg($"Replacing with: {((Scannable)val2).Title.RemoveUnityRichText()} - {replacement} (Original: {text.RemoveUnityRichText()} - {iD}) (Spawner: {((Object)__instance).name})");
				}
				if (!Fusion.IsConnected)
				{
					SpawnItem(replacement, ((Component)__instance).transform.position, ((Component)__instance).transform.rotation, delegate(Poolee p)
					{
						HandleSpawner(__instance, p);
					}, out __result);
					ReplacedSuccess();
				}
				else
				{
					MelonPreferences_Entry<bool> fusionSupport = PreferencesManager.FusionSupport;
					if (fusionSupport != null && fusionSupport.Value)
					{
						FusionSpawn(__instance, replacement, out __result);
					}
				}
				return false;
			}
			return true;
		}

		private static void FusionSpawn(CrateSpawner __instance, string targetBarcode, out UniTask<Poolee> __result)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if (Fusion.HandleFusionCrateSpawner(targetBarcode, __instance, out __result))
			{
				SpawnItem(targetBarcode, ((Component)__instance).transform.position, ((Component)__instance).transform.rotation, delegate(Poolee p)
				{
					HandleSpawner(__instance, p);
				}, out __result);
			}
			ReplacedSuccess();
		}

		private static void SpawnItem(string barcode, Vector3 position, Quaternion rotation, Action<Poolee> callback, out UniTask<Poolee> source)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			UniTaskCompletionSource<Poolee> _source = new UniTaskCompletionSource<Poolee>();
			source = new UniTask<Poolee>(((Il2CppObjectBase)_source).TryCast<IUniTaskSource<Poolee>>(), (short)0);
			Spawnable obj = LocalAssetSpawner.CreateSpawnable(barcode);
			LocalAssetSpawner.Register(obj);
			LocalAssetSpawner.Spawn(obj, position, rotation, (Action<Poolee>)continuation);
			void continuation(Poolee poolee)
			{
				if (!((Object)(object)poolee == (Object)null))
				{
					_source.TrySetResult(poolee);
					if (callback != null)
					{
						callback?.Invoke(poolee);
					}
				}
			}
		}

		private static void HandleSpawner(CrateSpawner spawner, Poolee poolee)
		{
			GameObject val = ((poolee != null) ? ((Component)poolee).gameObject : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			try
			{
				spawner.OnPooleeSpawn(val);
			}
			catch (Exception ex)
			{
				Core.Logger.Error("An unexpected error has occurred while handling CrateSpawner OnPooleeSpawn", ex);
			}
			poolee.OnDespawnDelegate += Action<GameObject>.op_Implicit((Action<GameObject>)spawner.OnPooleeDespawn);
			try
			{
				((UltEvent<CrateSpawner, GameObject>)(object)spawner.onSpawnEvent)?.Invoke(spawner, val);
			}
			catch (Exception ex2)
			{
				Core.Logger.Error("An unexpected error has occurred while invoking CrateSpawner onSpawnEvent", ex2);
			}
		}

		private static void ReplacedSuccess()
		{
			TotalReplacements++;
			LevelReplacements++;
			MenuManager.UpdateDebugCounts();
		}

		public static string GetReplacement(string barcode)
		{
			foreach (ReplacerConfig config in ReplacerManager.Configs)
			{
				if (config == null || !config.Enabled)
				{
					continue;
				}
				foreach (ReplacerCategory category in config.Categories)
				{
					if (category == null || !category.Enabled)
					{
						continue;
					}
					ReplacerEntry replacerEntry = category?.Entries?.FirstOrDefault((ReplacerEntry e) => Match(barcode, e));
					if (replacerEntry != null)
					{
						if (string.IsNullOrWhiteSpace(replacerEntry.Replacement))
						{
							Core.Logger.Warning($"Replacement in category '{category.Name}' of config '{config.ID}' has no replacement specified. This might be caused by an outdated config.");
						}
						return replacerEntry.Replacement;
					}
				}
			}
			return null;
		}

		private static bool Match(string barcode, ReplacerEntry entry)
		{
			if (entry.MatchType == ItemReplacer.Managers.MatchType.RegEx)
			{
				return Regex.IsMatch(barcode, entry.Original);
			}
			if (entry.MatchType == ItemReplacer.Managers.MatchType.Scriban)
			{
				return ScribanMatcher.Match(barcode, entry);
			}
			return barcode == entry.Original;
		}

		public static string RemoveUnityRichText(this string text)
		{
			return Regex.Replace(text, "<.*?>", string.Empty);
		}
	}
}
namespace ItemReplacer.Managers
{
	public static class MenuManager
	{
		private const string dumpFormat = "{0} - {1} - {2}";

		public static Page AuthorPage { get; private set; }

		public static Page ModPage { get; private set; }

		public static Page ReplacersPage { get; private set; }

		public static Page DebugPage { get; private set; }

		internal static FunctionElement TotalReplacedElement { get; set; }

		internal static FunctionElement LevelReplacedElement { get; set; }

		private static Dictionary<string, Page> ReplacerPages { get; } = new Dictionary<string, Page>();


		public static void Setup()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			if (AuthorPage == null)
			{
				AuthorPage = Page.Root.CreatePage("T&H Modding", Color.white, 0, true);
			}
			if (ModPage == null)
			{
				ModPage = AuthorPage.CreatePage("ItemReplacer", new Color(0.6f, 0f, 0.8f), 0, true);
			}
			ModPage.CreateBoolPref("Enable Mod", new Color(0f, 1f, 0f), ref PreferencesManager.Enabled);
			ModPage.CreateBoolPref("LabFusion Support", Color.cyan, ref PreferencesManager.FusionSupport);
			if (ReplacersPage == null)
			{
				ReplacersPage = ModPage.CreatePage("Replacers", Color.yellow, 0, true);
			}
			SetupReplacers();
			if (DebugPage == null)
			{
				DebugPage = ModPage.CreatePage("Debug", Color.cyan, 0, true);
			}
			SetupDebug();
			Core.Thunderstore.BL_CreateMenuLabel(ModPage);
		}

		internal static void SetupReplacers()
		{
			if (ReplacersPage == null)
			{
				return;
			}
			ReplacersPage.RemoveAll();
			foreach (ReplacerConfig config in ReplacerManager.Configs)
			{
				if (config == null)
				{
					Core.Logger.Error("Replacer is null, cannot generate element");
					continue;
				}
				if (string.IsNullOrWhiteSpace(config.ID))
				{
					Core.Logger.Error("ID is null or empty, cannot generate element");
				}
				Page val = PageFromConfig(config);
				PageLinkElement val2 = ReplacersPage.CreatePageLink(val);
				if (!string.IsNullOrWhiteSpace(config.Description))
				{
					((Element)val2).SetTooltip(config.Description);
				}
				CreateReplacerPage(val, config);
			}
			if (Menu.CurrentPage.Parent == ReplacersPage && !ReplacerPages.Any((KeyValuePair<string, Page> x) => x.Value == Menu.CurrentPage))
			{
				Menu.OpenParentPage();
			}
		}

		internal static void CreateReplacerPage(Page page, ReplacerConfig config)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			page.RemoveAll();
			if (!string.IsNullOrWhiteSpace(config.FilePath) && File.Exists(config.FilePath))
			{
				((Element)page.CreateFunction("File: " + Path.GetFileName(config.FilePath), Color.white, (Action)null)).SetProperty((ElementProperties)1);
			}
			List<ReplacerDependency> missing = config.Dependencies?.Where((ReplacerDependency x) => !AssetWarehouse.Instance.HasPallet(new Barcode(x.Barcode)))?.ToList();
			List<ReplacerDependency> list = missing;
			if (list != null && list.Any())
			{
				page.Name = config.Name + " (!)";
				FunctionElement title = CreateDefaultReplacerElems(page, config, missing.Count);
				missing.ForEach(delegate(ReplacerDependency x)
				{
					//IL_005a: Unknown result type (might be due to invalid IL or missing references)
					FunctionElement elem = null;
					elem = page.CreateFunction(((!string.IsNullOrWhiteSpace(x.Title)) ? x.Title : x.Barcode) ?? "", Color.red, (Action)delegate
					{
						InstallMissing(x, elem, delegate
						{
							page.Remove((Element)(object)elem);
							missing.Remove(x);
							((Element)title).ElementName = $"Missing Dependencies ({missing.Count})";
							Notify("Success", "Successfully downloaded and installed missing dependency", 3.5f, (NotificationType)3);
							if (!missing.Any())
							{
								CreateReplacerPage(page, config);
							}
						});
					});
				});
			}
			else
			{
				page.Name = config.Name;
				page.CreateBool("Enabled", Color.green, config.Enabled, (Action<bool>)delegate(bool v)
				{
					config.Enabled = v;
					config.SaveToFile(printMessage: false);
				});
				((Element)page.CreateFunction(" ", Color.white, (Action)null)).SetProperty((ElementProperties)1);
				config.Categories.ForEach(delegate(ReplacerCategory x)
				{
					x.CreateCategory(config, page);
				});
			}
			if (Menu.CurrentPage == page)
			{
				CorrectPage(page);
			}
		}

		internal static FunctionElement CreateDefaultReplacerElems(Page page, ReplacerConfig config, int missing)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			FunctionElement obj = page.CreateFunction($"Missing Dependencies ({missing})", Color.red, (Action)null);
			((Element)obj).SetProperty((ElementProperties)1);
			if (Fusion.HasFusion)
			{
				((Element)page.CreateFunction("Press to install missing dependency", Color.white, (Action)null)).SetProperty((ElementProperties)1);
			}
			page.CreateFunction("Refresh", Color.white, (Action)delegate
			{
				CreateReplacerPage(page, config);
			});
			((Element)page.CreateFunction(" ", Color.white, (Action)null)).SetProperty((ElementProperties)1);
			return obj;
		}

		internal static void InstallMissing(ReplacerDependency dependency, FunctionElement element, Action success)
		{
			if (!Fusion.HasFusion)
			{
				return;
			}
			Core.Logger.Msg($"Requesting install of missing dependency '{dependency.Title}' (Mod ID: {dependency.ModID}) via LabFusion...");
			Notify("Info", "Beginning download and installation of missing dependency", 3.5f, (NotificationType)0);
			Fusion.RequestInstall(dependency.ModID, delegate(Fusion.ModResult r)
			{
				if (r == Fusion.ModResult.SUCCEEDED)
				{
					success?.Invoke();
				}
				else
				{
					Notify("Failure", "Failed to install missing dependency, check console for more information", 4.5f, (NotificationType)2);
				}
			}, element);
		}

		internal static void Notify(string title, string message, float length, NotificationType type = 0)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: 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_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			Notifier.Send(new Notification
			{
				Title = NotificationText.op_Implicit(title),
				Message = NotificationText.op_Implicit(message),
				PopupLength = length,
				Type = type,
				ShowTitleOnPopup = true
			});
		}

		internal static void CreateCategory(this ReplacerCategory category, ReplacerConfig config, Page page)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			FunctionElement elem = null;
			elem = page.CreateFunction($"{category.Name} ({category.Entries.Count})", StateColor(category.Enabled), (Action)delegate
			{
				//IL_0086: Unknown result type (might be due to invalid IL or missing references)
				category.Enabled = !category.Enabled;
				((Element)elem).ElementName = $"{category.Name} ({category.Entries.Count})";
				((Element)elem).ElementColor = StateColor(category.Enabled);
				config.SaveToFile(printMessage: false);
			});
			if (!string.IsNullOrWhiteSpace(category.Description))
			{
				((Element)elem).SetTooltip(category.Description);
			}
		}

		internal static void SetupDebug()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			if (DebugPage != null)
			{
				DebugPage.RemoveAll();
				TotalReplacedElement = DebugPage.CreateFunction($"Total Replaced: {CrateSpawnerPatches.TotalReplacements}", Color.white, (Action)null);
				LevelReplacedElement = DebugPage.CreateFunction($"Level Replaced: {CrateSpawnerPatches.LevelReplacements}", Color.white, (Action)null);
				((Element)TotalReplacedElement).SetProperty((ElementProperties)1);
				((Element)LevelReplacedElement).SetProperty((ElementProperties)1);
				DebugPage.CreateBoolPref("Debug Logging", Color.cyan, ref PreferencesManager.DebugMode);
				DebugPage.CreateFunction("Dump all barcodes to dump.txt", Color.red, (Action)DumpBarcodes);
			}
		}

		internal static void UpdateDebugCounts()
		{
			if (TotalReplacedElement != null && LevelReplacedElement != null)
			{
				((Element)TotalReplacedElement).ElementName = $"Total Replaced: {CrateSpawnerPatches.TotalReplacements}";
				((Element)LevelReplacedElement).ElementName = $"Level Replaced: {CrateSpawnerPatches.LevelReplacements}";
			}
		}

		private static void DumpBarcodes()
		{
			Core.Logger.Msg("Dumping all barcodes...");
			List<string> spawnables = new List<string>();
			List<string> avatars = new List<string>();
			List<string> levels = new List<string>();
			List<string> unidentified = new List<string>();
			AssetWarehouse.Instance.gamePallets.ForEach(delegate(Barcode x)
			{
				Pallet val = default(Pallet);
				if (AssetWarehouse.Instance.TryGetPallet(x, ref val))
				{
					val.Crates.ForEach(Action<Crate>.op_Implicit((Action<Crate>)delegate(Crate crate)
					{
						if (((Scannable)crate).Barcode != (Barcode)null)
						{
							if (((MemberInfo)((Object)crate).GetIl2CppType()).Name == "SpawnableCrate")
							{
								spawnables.Add(FormatBarcode(crate, "Spawnable"));
							}
							else if (((MemberInfo)((Object)crate).GetIl2CppType()).Name == "AvatarCrate")
							{
								avatars.Add(FormatBarcode(crate, "Avatar"));
							}
							else if (((MemberInfo)((Object)crate).GetIl2CppType()).Name == "LevelCrate")
							{
								levels.Add(FormatBarcode(crate, "Level"));
							}
							else
							{
								unidentified.Add(FormatBarcode(crate, "Unidentified"));
							}
						}
					}));
				}
			});
			using StreamWriter streamWriter = File.CreateText(Path.Combine(PreferencesManager.ConfigDir, "dump.txt"));
			streamWriter.WriteLine("Title - Barcode - Crate Type");
			streamWriter.WriteLine("==============================================" + streamWriter.NewLine);
			streamWriter.WriteList(avatars);
			streamWriter.WriteList(levels);
			streamWriter.WriteList(spawnables);
			unidentified.ForEach(streamWriter.WriteLine);
			streamWriter.Flush();
			streamWriter.Close();
			Core.Logger.Msg($"Dumped {spawnables.Count} spawnables, {avatars.Count} avatars, {levels.Count} levels and {unidentified.Count} unidentified crates to dump.txt");
		}

		private static void WriteList(this StreamWriter file, List<string> list)
		{
			list.ForEach(file.WriteLine);
			if (list.Count > 0)
			{
				file.WriteLine(file.NewLine + "==============================================" + file.NewLine);
			}
		}

		private static string FormatBarcode(Crate crate, string typeName)
		{
			string arg = ((Scannable)crate).Title?.RemoveUnityRichText() ?? "N/A";
			Barcode barcode = ((Scannable)crate).Barcode;
			return string.Format("{0} - {1} - {2}", arg, ((barcode != null) ? barcode.ID : null) ?? "N/A", typeName);
		}

		private static Page PageFromConfig(ReplacerConfig config)
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			List<ReplacerDependency> dependencies = config.Dependencies;
			string text = ((dependencies != null && dependencies.Any((ReplacerDependency x) => !AssetWarehouse.Instance.HasPallet(new Barcode(x.Barcode)))) ? (config.Name + " (!)") : config.Name);
			Page val;
			if (!ReplacerPages.ContainsKey(config.ID))
			{
				val = ReplacersPage.CreatePage(text, config.GetColor(), 0, false);
				ReplacerPages[config.ID] = val;
			}
			else
			{
				val = ReplacerPages[config.ID];
				val.Name = text;
				val.Color = config.GetColor();
			}
			return val;
		}

		private static void CorrectPage(Page page)
		{
			((object)GUIMenu.Instance).GetType().GetMethod("DrawHeader", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(GUIMenu.Instance, new object[1] { page });
		}

		private static Color GetColor(this ReplacerConfig config)
		{
			//IL_0030: 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 (config.Color.TryFromHEX(out var color))
			{
				return color;
			}
			Core.Logger.Error("Color for '" + config.ID + "' is invalid");
			return Color.white;
		}

		public static BoolElement CreateBoolPref(this Page page, string name, Color color, ref MelonPreferences_Entry<bool> pref, Action<bool> callback = null)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			MelonPreferences_Entry<bool> localPref = pref;
			BoolElement val = page.CreateBool(name, color, pref.Value, (Action<bool>)delegate(bool v)
			{
				localPref.Value = v;
				PreferencesManager.Category.SaveToFile(false);
				Action<bool> action = callback;
				if (action != null)
				{
					Extensions.InvokeActionSafe<bool>(action, v);
				}
			});
			if (!string.IsNullOrWhiteSpace(((MelonPreferences_Entry)pref).Description))
			{
				((Element)val).SetTooltip(((MelonPreferences_Entry)pref).Description);
			}
			return val;
		}

		private static Color StateColor(bool state)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			if (!state)
			{
				return Color.red;
			}
			return Color.green;
		}
	}
	internal static class PreferencesManager
	{
		internal static MelonPreferences_Entry<bool> Enabled;

		internal static MelonPreferences_Entry<bool> DebugMode;

		internal static MelonPreferences_Entry<bool> FusionSupport;

		internal static MelonPreferences_Category Category { get; private set; }

		internal static string ConfigDir => Path.Combine(MelonEnvironment.UserDataDirectory, "ItemReplacer");

		internal static string ConfigFile => Path.Combine(ConfigDir, "Config.cfg");

		internal static bool IsDebug()
		{
			MelonPreferences_Entry<bool> debugMode = DebugMode;
			if (debugMode != null && debugMode.Value)
			{
				return true;
			}
			if (MelonDebug.IsEnabled())
			{
				return true;
			}
			return false;
		}

		internal static void Setup()
		{
			EnsureFolder();
			Category = MelonPreferences.CreateCategory("ItemReplacer");
			Enabled = Category.CreateEntry<bool>("Enabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			DebugMode = Category.CreateEntry<bool>("Debug", false, (string)null, "When enabled, provides additional logging for debugging", false, false, (ValueValidator)null, (string)null);
			FusionSupport = Category.CreateEntry<bool>("FusionSupport", true, (string)null, "When enabled, items will be replaced even when you are in a LabFusion lobby", false, false, (ValueValidator)null, (string)null);
			Category.SetFilePath(ConfigFile, true, false);
			Category.SaveToFile(false);
		}

		internal static void EnsureFolder()
		{
			if (!Directory.Exists(ConfigDir))
			{
				Core.Logger.Msg("Creating missing folder in UserData");
				Directory.CreateDirectory(ConfigDir);
			}
		}
	}
	public static class ReplacerManager
	{
		private static readonly List<ReplacerConfig> _configs = new List<ReplacerConfig>();

		private static readonly Dictionary<string, DateTime> LastWrite = new Dictionary<string, DateTime>();

		public static IReadOnlyList<ReplacerConfig> Configs => _configs.AsReadOnly();

		internal static List<string> IgnoredFilePaths { get; } = new List<string>();


		public static string ConfigsDir => Path.Combine(PreferencesManager.ConfigDir, "Configs");

		private static UnityFileSystemWatcher FileSystemWatcher { get; set; }

		public static void Setup()
		{
			PreferencesManager.EnsureFolder();
			if (!Directory.Exists(ConfigsDir))
			{
				Core.Logger.Msg("Creating missing folder in Config directory");
				Directory.CreateDirectory(ConfigsDir);
			}
			Core.Logger.Msg("Loading configs from directory...");
			string[] files = Directory.GetFiles(ConfigsDir, "*.json");
			if (files == null || files.Length == 0)
			{
				return;
			}
			string[] array = files;
			foreach (string text in array)
			{
				try
				{
					if (Check(text))
					{
						Register(text);
					}
					else
					{
						Core.Logger.Error("Attempted to load " + Path.GetFileName(text) + ", but it failed the check");
					}
				}
				catch (Exception ex)
				{
					Core.Logger.Error("An unexpected error has occurred while loading '" + Path.GetFileName(text) + "'", ex);
				}
			}
		}

		public static void Register(ReplacerConfig config, bool saveToFile = true)
		{
			if (config == null)
			{
				throw new ArgumentNullException("config", "Config cannot be null.");
			}
			if (config.Categories.Count == 0)
			{
				throw new ArgumentNullException("config", "Config must have a category");
			}
			Core.Logger.Msg("Registering replacer, ID: '" + config.ID + "'");
			if (_configs.Exists((ReplacerConfig c) => c.ID == config.ID))
			{
				Core.Logger.Error("A replacer with the same ID is already registered.");
				throw new ArgumentException("A replacer with the ID '" + config.ID + "' is already registered.", "config");
			}
			_configs.Add(config);
			if (saveToFile && string.IsNullOrWhiteSpace(config.FilePath))
			{
				Core.Logger.Msg("Saving to file");
				string text = Path.Combine(ConfigsDir, config.ID + ".json");
				IgnoredFilePaths.TryAdd(text);
				FileStream fileStream = File.Open(text, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
				config.FilePath = text;
				string s = JsonConvert.SerializeObject((object)config, (Formatting)1);
				fileStream.Write(Encoding.UTF8.GetBytes(s));
				fileStream.Flush();
				fileStream.Position = 0L;
				fileStream.Dispose();
			}
			MenuManager.SetupReplacers();
		}

		public static void Register(string filePath)
		{
			Core.Logger.Msg("Registering replacer from file: '" + filePath + "'");
			if (File.Exists(filePath))
			{
				if (Path.GetExtension(filePath)?.ToLower() != ".json")
				{
					throw new ArgumentException("The file must be a JSON file");
				}
				string text = ReadAllTextUsedFile(filePath);
				if (string.IsNullOrWhiteSpace(text) || !IsJSON(text))
				{
					throw new ArgumentException("The contents of the file are not JSON");
				}
				ReplacerConfig replacerConfig = JsonConvert.DeserializeObject<ReplacerConfig>(text);
				if (replacerConfig != null)
				{
					replacerConfig.FilePath = filePath;
					if (!Configs.Any((ReplacerConfig x) => x.FilePath == filePath))
					{
						Register(replacerConfig, saveToFile: false);
					}
				}
				return;
			}
			throw new FileNotFoundException("Could not find file " + filePath);
		}

		private static bool Check(string path)
		{
			if (string.IsNullOrWhiteSpace(path))
			{
				throw new ArgumentNullException("path");
			}
			if (!File.Exists(path))
			{
				throw new FileNotFoundException("Config file at '" + path + "' could not be found");
			}
			string text;
			try
			{
				text = ReadAllTextUsedFile(path);
			}
			catch (Exception ex)
			{
				Core.Logger.Error("Unable to check the integrity of the file, because an unexpected error has occurred", ex);
				return false;
			}
			if (string.IsNullOrWhiteSpace(text) || !IsJSON(text) || text == "{}")
			{
				Core.Logger.Error("The content of the file is not correct");
				return false;
			}
			try
			{
				return DeserializeAndCheck(path, text);
			}
			catch (Exception ex2)
			{
				Core.Logger.Error("An unexpected error has occurred while deserializing config", ex2);
				return false;
			}
		}

		private static bool DeserializeAndCheck(string path, string text)
		{
			ReplacerConfig config = JsonConvert.DeserializeObject<ReplacerConfig>(text);
			if (config != null)
			{
				if (!string.IsNullOrWhiteSpace(config.ID))
				{
					if (Configs.Any((ReplacerConfig x) => x.ID == config.ID && x.FilePath != path))
					{
						Core.Logger.Error("The ID is already used in another replacer");
						return false;
					}
					return true;
				}
				Core.Logger.Error("The ID '" + config.ID + "' is null or empty");
				return false;
			}
			Core.Logger.Error("Deserialized config is null");
			return false;
		}

		internal static bool PreventDoubleTrigger(string file)
		{
			if (!File.Exists(file))
			{
				LastWrite.Remove(file);
				return false;
			}
			DateTime lastWriteTime = File.GetLastWriteTime(file);
			if (!LastWrite.ContainsKey(file))
			{
				LastWrite.Add(file, lastWriteTime);
				return IsIgnored(file);
			}
			bool num = LastWrite[file] == lastWriteTime;
			LastWrite[file] = lastWriteTime;
			if (!num)
			{
				return IsIgnored(file);
			}
			return true;
		}

		internal static bool IsIgnored(string path)
		{
			string fullPath = Path.GetFullPath(path);
			if (!IgnoredFilePaths.Any())
			{
				return false;
			}
			return IgnoredFilePaths.Any(delegate(string x)
			{
				bool num = Path.GetFullPath(x) == fullPath;
				if (num)
				{
					IgnoredFilePaths.Remove(x);
				}
				return num;
			});
		}

		internal static string ReadAllTextUsedFile(string path)
		{
			if (!File.Exists(path))
			{
				throw new FileNotFoundException("Could not read text from '" + path + "', because it doesn't exist");
			}
			using FileStream fileStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
			if (fileStream != null)
			{
				fileStream.Position = 0L;
				using StreamReader streamReader = new StreamReader(fileStream);
				return streamReader.ReadToEnd();
			}
			return null;
		}

		internal static void CreateFileWatcher()
		{
			Core.Logger.Msg("Creating file watcher");
			LastWrite.Clear();
			FileSystemWatcher?.Dispose();
			FileSystemWatcher = new UnityFileSystemWatcher(ConfigsDir)
			{
				EnableRaisingEvents = true,
				Filter = "*.json"
			};
			FileSystemWatcher.Error += delegate(object? x, ErrorEventArgs y)
			{
				Core.Logger.Error("An unexpected error was thrown by the file watcher for the configs", y.GetException());
			};
			FileSystemWatcher.Deleted += Event_DeletedFile;
			FileSystemWatcher.Created += Event_CreatedFile;
			FileSystemWatcher.Changed += Event_ModifiedFile;
			FileSystemWatcher.Renamed += Event_RenamedFile;
		}

		private static void Event_DeletedFile(object sender, FileSystemEventArgs args)
		{
			if (!IsIgnored(args.FullPath))
			{
				LastWrite.Remove(args.FullPath);
				Core.Logger.Msg(args.Name + " has been deleted, unregistering replacer");
				ReplacerConfig replacerConfig = Configs.FirstOrDefault((ReplacerConfig x) => x.FilePath == args.FullPath);
				if (replacerConfig != null)
				{
					Unregister(replacerConfig.ID, removeFile: false);
				}
				MenuManager.SetupReplacers();
			}
		}

		private static void Event_CreatedFile(object sender, FileSystemEventArgs args)
		{
			if (!IsIgnored(args.FullPath))
			{
				if (Check(args.FullPath))
				{
					Core.Logger.Msg(args.Name + " has been created, registering replacer");
					Register(args.FullPath);
				}
				MenuManager.SetupReplacers();
			}
		}

		private static void Event_ModifiedFile(object sender, FileSystemEventArgs args)
		{
			if (!File.Exists(args.FullPath) || PreventDoubleTrigger(args.FullPath))
			{
				return;
			}
			if (Check(args.FullPath))
			{
				if (!Update(args.FullPath, delegate(ReplacerConfig x)
				{
					x.Update(args.FullPath);
				}, requireFileWatcherOption: true))
				{
					Core.Logger.Msg(args.Name + " has been modified, but wasn't registered. Registering replacer");
					Register(args.FullPath);
				}
				else
				{
					Core.Logger.Msg(args.Name + " has been modified, updating");
				}
				Configs.Where((ReplacerConfig x) => x.AutoUpdate(args.FullPath)).ForEach(delegate(ReplacerConfig x)
				{
					x.Update(args.FullPath);
				});
			}
			else
			{
				Core.Logger.Error(args.Name + " was updated, but is not suitable to be a replacer");
				ReplacerConfig replacerConfig = Configs.FirstOrDefault((ReplacerConfig x) => x.FilePath == args.FullPath);
				if (replacerConfig != null)
				{
					UnregisterFile(replacerConfig.FilePath);
				}
			}
			MenuManager.SetupReplacers();
		}

		private static void Event_RenamedFile(object sender, RenamedEventArgs args)
		{
			if (!IsIgnored(args.FullPath))
			{
				if (LastWrite.ContainsKey(args.OldFullPath))
				{
					DateTime value = LastWrite[args.OldFullPath];
					LastWrite.Remove(args.OldFullPath);
					LastWrite.Add(args.FullPath, value);
				}
				Core.Logger.Msg(args.OldName + " has been renamed to " + args.Name + ", updating information");
				if (!Update(args.OldFullPath, delegate(ReplacerConfig x)
				{
					x.FilePath = args.FullPath;
				}) && Check(args.FullPath))
				{
					Core.Logger.Msg(args.Name + " has been renamed to " + args.Name + ", but wasn't registered. Registering replacer");
					Register(args.FullPath);
				}
				else
				{
					Core.Logger.Msg(args.OldName + " has been renamed to " + args.Name + ", updating information");
				}
				MenuManager.SetupReplacers();
			}
		}

		internal static bool Update(string filePath, Action<ReplacerConfig> action, bool requireFileWatcherOption = false)
		{
			IEnumerable<ReplacerConfig> enumerable = Configs.Where((ReplacerConfig x) => x.FilePath == filePath);
			if (enumerable.Any() && requireFileWatcherOption && enumerable.ToList().TrueForAll((ReplacerConfig x) => !x.IsFileWatcherEnabled))
			{
				return true;
			}
			if (enumerable.Any())
			{
				foreach (ReplacerConfig item in enumerable)
				{
					action(item);
				}
				return true;
			}
			return false;
		}

		internal static bool IsJSON(string text)
		{
			try
			{
				using (JsonDocument.Parse(text))
				{
					return true;
				}
			}
			catch (JsonException)
			{
				return false;
			}
		}

		public static void Unregister(string ID, bool removeFile = true)
		{
			Core.Logger.Msg("Unregistering config with ID '" + ID + "'");
			ReplacerConfig replacerConfig = _configs.FirstOrDefault((ReplacerConfig x) => x.ID == ID);
			if (replacerConfig != null)
			{
				_configs.Remove(replacerConfig);
				if (removeFile && !string.IsNullOrWhiteSpace(replacerConfig.FilePath))
				{
					Core.Logger.Msg("Removing file at '" + replacerConfig.FilePath + "'");
					if (File.Exists(replacerConfig.FilePath))
					{
						File.Delete(replacerConfig.FilePath);
					}
				}
				Core.Logger.Msg("Unregistered config with ID '" + ID + "'");
				return;
			}
			Core.Logger.Error("A config with ID '" + ID + "' does not exist!");
			throw new KeyNotFoundException("Config with ID '" + ID + "' could not be found!");
		}

		public static void UnregisterFile(string file, bool removeFile = true)
		{
			Core.Logger.Msg("Unregistering config at path '" + file + "'");
			ReplacerConfig replacerConfig = _configs.FirstOrDefault((ReplacerConfig x) => x.FilePath == file);
			if (replacerConfig != null)
			{
				_configs.Remove(replacerConfig);
				if (removeFile && !string.IsNullOrWhiteSpace(replacerConfig.FilePath))
				{
					Core.Logger.Msg("Removing file at '" + replacerConfig.FilePath + "'");
					if (File.Exists(replacerConfig.FilePath))
					{
						File.Delete(replacerConfig.FilePath);
					}
				}
				Core.Logger.Msg("Unregistered config at path '" + file + "'");
				return;
			}
			Core.Logger.Error("A config at path '" + file + "' does not exist!");
			throw new KeyNotFoundException("Config at path '" + file + "' could not be found!");
		}
	}
	public class ReplacerConfig
	{
		[JsonProperty("name")]
		public string Name { get; set; }

		[JsonProperty("description")]
		public string Description { get; set; }

		[JsonProperty("color")]
		public string Color { get; set; }

		[JsonProperty("id")]
		public string ID { get; set; }

		[JsonProperty("enabled")]
		public bool Enabled { get; set; }

		[JsonIgnore]
		public string FilePath { get; set; }

		[JsonIgnore]
		public bool IsFileWatcherEnabled { get; set; } = true;


		[JsonProperty("categories")]
		public List<ReplacerCategory> Categories { get; set; }

		[JsonProperty("dependencies")]
		public List<ReplacerDependency> Dependencies { get; set; }

		public ReplacerConfig(string name, string description, string color, string id, List<ReplacerCategory> categories, bool enabled = true)
		{
			ID = id;
			Description = description;
			Name = name;
			Color = color;
			Categories = categories;
			Enabled = enabled;
		}

		[JsonConstructor]
		public ReplacerConfig()
		{
		}

		internal bool AutoUpdate(string path)
		{
			if (FilePath == path)
			{
				return IsFileWatcherEnabled;
			}
			return false;
		}

		public void SaveToFile(bool printMessage = true)
		{
			if (!string.IsNullOrWhiteSpace(FilePath) && File.Exists(FilePath))
			{
				ReplacerManager.IgnoredFilePaths.TryAdd(FilePath);
				LoggerMsg("Saving '" + ID + "' to file...", printMessage);
				try
				{
					string value = JsonConvert.SerializeObject((object)this, (Formatting)1);
					FileStream fileStream = File.Create(FilePath);
					using StreamWriter streamWriter = new StreamWriter(fileStream)
					{
						AutoFlush = true
					};
					fileStream.Position = 0L;
					streamWriter.Write(value);
					streamWriter.DisposeAsync().AsTask().ContinueWith(delegate(Task task)
					{
						if (task.IsCompletedSuccessfully)
						{
							LoggerMsg("Saved '" + ID + "' to file successfully!", printMessage);
						}
						else
						{
							LoggerError("Failed to save '" + ID + "' to file", task.Exception, printMessage);
						}
					});
					return;
				}
				catch (Exception ex)
				{
					LoggerError("Failed to save '" + ID + "' to file", ex, printMessage);
					throw;
				}
			}
			LoggerError("Replacer '" + ID + "' does not have a file set or it doesn't exist!", null, printMessage);
			throw new FileNotFoundException("Replacer does not have a file!");
		}

		private static void LoggerMsg(string message, bool print = false)
		{
			if (print)
			{
				Core.Logger.Msg(message);
			}
		}

		private static void LoggerError(string message, Exception ex = null, bool print = false)
		{
			if (print)
			{
				if (ex != null)
				{
					Core.Logger.Error(message, ex);
				}
				else
				{
					Core.Logger.Error(message);
				}
			}
		}

		public bool TrySaveToFile(bool printMessage = true)
		{
			try
			{
				SaveToFile(printMessage);
				return true;
			}
			catch (Exception)
			{
				return false;
			}
		}

		internal void Update(ReplacerConfig config)
		{
			ArgumentNullException.ThrowIfNull(config, "config");
			if (!string.IsNullOrWhiteSpace(config.ID))
			{
				if (ReplacerManager.Configs.Any((ReplacerConfig x) => x.ID == config.ID && x != this))
				{
					Core.Logger.Error("The new ID is already used in another replacer, will not overwrite");
					throw new ArgumentException("The new ID is already used in another replacer, will not overwrite");
				}
				ID = config.ID;
				if (Name != config.Name)
				{
					Name = config.Name;
				}
				if (Color != config.Color)
				{
					Color = config.Color;
				}
				if (Enabled != config.Enabled)
				{
					Enabled = config.Enabled;
				}
				if (Categories != config.Categories)
				{
					Categories = config.Categories;
				}
				return;
			}
			Core.Logger.Error("The new ID is null or empty, will not overwrite");
			throw new ArgumentException("The new ID is null or empty, will not overwrite");
		}

		internal void Update(string path)
		{
			if (string.IsNullOrWhiteSpace(path))
			{
				throw new ArgumentNullException("path");
			}
			if (!File.Exists(path))
			{
				throw new FileNotFoundException("Config file at '" + path + "' could be found");
			}
			string text = ReplacerManager.ReadAllTextUsedFile(path);
			if (string.IsNullOrWhiteSpace(text) || !ReplacerManager.IsJSON(text))
			{
				Core.Logger.Error("A config file at '" + path + "' was changed and the content are no longer suitable for loading as a replacer. This means that the replacer at runtime will not be overwritten by new content");
				throw new InvalidDataException("A config file at '" + path + "' was changed and the content are no longer suitable for loading as a replacer. This means that the replacer at runtime will not be overwritten by new content");
			}
			ReplacerConfig replacerConfig = JsonConvert.DeserializeObject<ReplacerConfig>(text);
			if (replacerConfig != null)
			{
				Update(replacerConfig);
				return;
			}
			Core.Logger.Error("A config file at '" + path + "' was changed and the content are no longer suitable for loading as a replacer. This means that the replacer at runtime will not be overwritten by new content");
			throw new InvalidDataException("A cibfug file at '" + path + "' was changed and the content are no longer suitable for loading as a replacer. This means that the replacer at runtime will not be overwritten by new content");
		}
	}
	public class ReplacerCategory
	{
		[JsonProperty("name")]
		public string Name { get; set; }

		[JsonProperty("description")]
		public string Description { get; set; }

		[JsonProperty("enabled")]
		public bool Enabled { get; set; }

		[JsonProperty("entries")]
		public List<ReplacerEntry> Entries { get; set; }

		[JsonConstructor]
		public ReplacerCategory(string name, string description, List<ReplacerEntry> entries, bool enabled = true)
		{
			Name = name;
			Description = description;
			Entries = entries;
			Enabled = enabled;
		}

		public ReplacerCategory(string name)
		{
			Name = name;
			Entries = new List<ReplacerEntry>();
		}
	}
	public class ReplacerEntry
	{
		[JsonIgnore]
		private string _original;

		[JsonIgnore]
		private MatchType _matchType;

		[JsonProperty("original")]
		public string Original
		{
			get
			{
				return _original;
			}
			set
			{
				_original = value;
				ParseTemplate();
			}
		}

		[JsonProperty("replacement")]
		public string Replacement { get; set; }

		[DefaultValue("compare")]
		[JsonProperty(/*Could not decode attribute arguments.*/)]
		public string Type
		{
			get
			{
				if (MatchType == MatchType.Compare)
				{
					return "compare";
				}
				if (MatchType == MatchType.RegEx)
				{
					return "regex";
				}
				if (MatchType == MatchType.Scriban)
				{
					return "scriban";
				}
				return "compare";
			}
			set
			{
				if (string.Equals(value, "compare", StringComparison.OrdinalIgnoreCase))
				{
					MatchType = MatchType.Compare;
				}
				else if (string.Equals(value, "regex", StringComparison.OrdinalIgnoreCase))
				{
					MatchType = MatchType.RegEx;
				}
				else if (string.Equals(value, "scriban", StringComparison.OrdinalIgnoreCase))
				{
					MatchType = MatchType.Scriban;
				}
				else
				{
					MatchType = MatchType.Compare;
				}
			}
		}

		[JsonIgnore]
		public MatchType MatchType
		{
			get
			{
				return _matchType;
			}
			set
			{
				_matchType = value;
				ParseTemplate();
			}
		}

		[JsonIgnore]
		public Template Template { get; set; }

		public ReplacerEntry(string original, string replacement, MatchType matchType = MatchType.Compare)
		{
			Original = original;
			Replacement = replacement;
			MatchType = matchType;
		}

		[JsonConstructor]
		public ReplacerEntry(string original, string replacement, string type)
		{
			Original = original;
			Replacement = replacement;
			Type = type;
		}

		private void ParseTemplate()
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			if (_matchType != MatchType.Scriban)
			{
				return;
			}
			Template = Template.Parse(_original, (string)null, (ParserOptions?)null, (LexerOptions?)null);
			if (!Template.HasErrors)
			{
				return;
			}
			Core.Logger.Error("Error parsing Scriban template: " + _original);
			foreach (LogMessage message in Template.Messages)
			{
				Core.Logger.Error((((int)message.Type == 0) ? "[ERR]" : "[WARN]") + " " + message.Message);
			}
		}
	}
	public class ReplacerDependency
	{
		[JsonProperty("title")]
		public string Title { get; set; }

		[JsonProperty("barcode")]
		public string Barcode { get; set; }

		[JsonProperty("modId")]
		public int ModID { get; set; }

		public ReplacerDependency(string title, string barcode, int modId)
		{
			Title = title;
			Barcode = barcode;
			ModID = modId;
		}

		[JsonConstructor]
		public ReplacerDependency()
		{
		}
	}
	public enum MatchType
	{
		Compare,
		RegEx,
		Scriban
	}
}
namespace ItemReplacer.Helpers
{
	public static class CollectionHelper
	{
		public static void ForEach<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, Action<KeyValuePair<TKey, TValue>> action)
		{
			foreach (KeyValuePair<TKey, TValue> item in dictionary)
			{
				action?.Invoke(item);
			}
		}

		public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
		{
			foreach (T item in enumerable)
			{
				action?.Invoke(item);
			}
		}

		public static void ForEach<T>(this HashSet<T> hashSet, Action<T> action)
		{
			Enumerator<T> enumerator = hashSet.GetEnumerator();
			while (enumerator.MoveNext())
			{
				T current = enumerator.Current;
				action?.Invoke(current);
			}
		}

		public static void TryAdd<T>(this List<T> list, T item)
		{
			if (!list.Contains(item))
			{
				list.Add(item);
			}
		}
	}
	public static class ColorHelper
	{
		public static string ToHEX(this Color color)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			return $"{(int)(color.r * 255f):X2}{(int)(color.g * 255f):X2}{(int)(color.b * 255f):X2}";
		}

		public static Color FromHEX(this string hex)
		{
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			if (hex.StartsWith('#'))
			{
				string text = hex;
				hex = text.Substring(1, text.Length - 1);
			}
			if (hex.Length != 6 && hex.Length != 8)
			{
				throw new ArgumentException("HEX color must be 6 or 8 characters long.");
			}
			float num = (float)Convert.ToInt32(hex.Substring(0, 2), 16) / 255f;
			float num2 = (float)Convert.ToInt32(hex.Substring(2, 2), 16) / 255f;
			float num3 = (float)Convert.ToInt32(hex.Substring(4, 2), 16) / 255f;
			float num4 = 1f;
			if (hex.Length == 8)
			{
				num4 = (float)Convert.ToInt32(hex.Substring(6, 2), 16) / 255f;
			}
			return new Color(num, num2, num3, num4);
		}

		public static bool TryFromHEX(this string hex, out Color color)
		{
			//IL_0002: 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_0012: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				color = hex.FromHEX();
				return true;
			}
			catch
			{
				color = default(Color);
				return false;
			}
		}
	}
}