Decompiled source of ItemReplacer v1.0.1
UserLibs/Scriban.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.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 agousing 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; } } } }