Decompiled source of Allspeak v0.2.1

BepInEx/plugins/Allspeak.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Allspeak")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+1b8ad2e3c4f43e7ef2f22003d122ed592a4189c3")]
[assembly: AssemblyProduct("Allspeak")]
[assembly: AssemblyTitle("Allspeak")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Allspeak
{
	[BepInPlugin("com.iyon.allspeak", "Allspeak", "0.2.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.iyon.allspeak";

		public const string Name = "Allspeak";

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<string> TargetLanguage;

		internal static ConfigEntry<string> IgnoredLanguages;

		internal static ConfigEntry<double> DetectionConfidence;

		internal static ConfigEntry<bool> IncludePlayerName;

		internal static ConfigEntry<bool> HideOriginal;

		internal static ConfigEntry<string> AlteredPrefix;

		internal static ConfigEntry<Color> NameColor;

		internal static ConfigEntry<Color> NormalColor;

		internal static ConfigEntry<Color> ShoutColor;

		internal static ConfigEntry<Color> WhisperColor;

		private const string ActiveText = "Allspeak is active";

		private const string InactiveText = "Allspeak inactive, check logs";

		private Harmony _harmony;

		private static readonly Dictionary<string, string> GameLanguageCodes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			["Arabic"] = "ar",
			["Bulgarian"] = "bg",
			["Chinese"] = "zh-CN",
			["Chinese_Trad"] = "zh-TW",
			["Croatian"] = "hr",
			["Czech"] = "cs",
			["Danish"] = "da",
			["Dutch"] = "nl",
			["English"] = "en",
			["Finnish"] = "fi",
			["French"] = "fr",
			["Georgian"] = "ka",
			["German"] = "de",
			["Greek"] = "el",
			["Hindi"] = "hi",
			["Hungarian"] = "hu",
			["Icelandic"] = "is",
			["Indonesian"] = "id",
			["Italian"] = "it",
			["Japanese"] = "ja",
			["Korean"] = "ko",
			["Latvian"] = "lv",
			["Lithuanian"] = "lt",
			["Macedonian"] = "mk",
			["Norwegian"] = "no",
			["Polish"] = "pl",
			["Portuguese_Brazilian"] = "pt",
			["Portuguese_European"] = "pt-PT",
			["Romanian"] = "ro",
			["Russian"] = "ru",
			["Serbian"] = "sr",
			["Slovak"] = "sk",
			["Spanish"] = "es",
			["Swedish"] = "sv",
			["Thai"] = "th",
			["Turkish"] = "tr",
			["Ukrainian"] = "uk",
			["Vietnamese"] = "vi"
		};

		private const int MaxWordsForFallback = 4;

		private void Awake()
		{
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: 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)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch. false = no translations.");
			TargetLanguage = ((BaseUnityPlugin)this).Config.Bind<string>("General", "TargetLanguage", "auto", "Language to translate into. 'auto' = game's UI language (fallback en). Otherwise a Google Translate code, e.g. en, de, no, pt-PT, zh-TW. Full list in README.");
			IgnoredLanguages = ((BaseUnityPlugin)this).Config.Bind<string>("General", "IgnoredLanguages", "", "Comma-separated Google Translate codes you understand; messages detected as these are not translated. E.g. de,fr");
			DetectionConfidence = ((BaseUnityPlugin)this).Config.Bind<double>("General", "DetectionConfidence", 0.9, "Minimum auto-detect confidence (0-1) to trust Google's language detection. Below this, a player's remembered language is used instead.");
			IncludePlayerName = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "IncludePlayerName", true, "Show sender's name on translated lines.");
			HideOriginal = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "HideOriginal", true, "Replace the original chat line with the translation. Original still shown if not translated (understood language or failure).");
			AlteredPrefix = ((BaseUnityPlugin)this).Config.Bind<string>("Display", "AlteredPrefix", "[\ud83d\udcac]", "Prefix on translated lines when HideOriginal = true. Empty = none. Emoji may render as a box if the game font lacks it.");
			NameColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Display", "NameColor", new Color(1f, 0.647f, 0f), "Player name color (default orange).");
			NormalColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Display", "NormalColor", Color.white, "Normal (proximity) chat text color.");
			ShoutColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Display", "ShoutColor", Color.yellow, "Shout text color.");
			WhisperColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Display", "WhisperColor", new Color(1f, 1f, 1f, 0.75f), "Whisper text color.");
			PlayerLanguages.Load(Path.Combine(Paths.ConfigPath, "com.iyon.allspeak.players.tsv"));
			_harmony = new Harmony("com.iyon.allspeak");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Allspeak 0.2.1 loaded");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		internal static HashSet<string> IgnoredSet()
		{
			return new HashSet<string>(from s in IgnoredLanguages.Value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries)
				select s.Trim().ToLowerInvariant(), StringComparer.OrdinalIgnoreCase);
		}

		internal static string ResolveTargetLanguage()
		{
			string text = TargetLanguage.Value.Trim();
			if (!text.Equals("auto", StringComparison.OrdinalIgnoreCase))
			{
				return text;
			}
			Localization instance = Localization.instance;
			string text2 = ((instance != null) ? instance.GetSelectedLanguage() : null);
			if (text2 == null || !GameLanguageCodes.TryGetValue(text2, out var value))
			{
				return "en";
			}
			return value;
		}

		internal void Translate(string text, string playerId, string playerName, Type type, Action showOriginal)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			string fallbackLang = ((text.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries).Length <= 4) ? PlayerLanguages.Get(playerId) : null);
			((MonoBehaviour)this).StartCoroutine(TranslateRoutine(text, fallbackLang, delegate(string source, string target, string translated, double? confidence)
			{
				//IL_008a: Unknown result type (might be due to invalid IL or missing references)
				if (confidence.HasValue)
				{
					PlayerLanguages.Observe(playerId, playerName, source, confidence.Value);
				}
				if (IsUnderstood(source) || string.IsNullOrWhiteSpace(translated))
				{
					showOriginal?.Invoke();
				}
				else
				{
					AddChatLine(FormatLine((showOriginal != null) ? AlteredPrefix.Value : ("[" + source + "→" + target + "]"), playerName, translated, type));
				}
			}, showOriginal));
		}

		private static string FormatLine(string tag, string name, string text, Type type)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			Color value = NormalColor.Value;
			if ((int)type == 2)
			{
				value = ShoutColor.Value;
				text = text.ToUpper();
			}
			else if ((int)type == 0)
			{
				value = WhisperColor.Value;
				text = text.ToLowerInvariant();
			}
			StringBuilder stringBuilder = new StringBuilder();
			if (!string.IsNullOrEmpty(tag))
			{
				stringBuilder.Append(tag).Append(' ');
			}
			if (IncludePlayerName.Value && !string.IsNullOrEmpty(name))
			{
				stringBuilder.Append("<color=#" + ColorUtility.ToHtmlStringRGBA(NameColor.Value) + ">" + name + "</color>: ");
			}
			stringBuilder.Append("<color=#" + ColorUtility.ToHtmlStringRGBA(value) + "><noparse>" + text + "</noparse></color>");
			return stringBuilder.ToString();
		}

		private static void AddChatLine(string line)
		{
			if (!((Object)(object)Chat.instance == (Object)null))
			{
				Chat.instance.m_hideTimer = 0f;
				((Terminal)Chat.instance).AddString(line);
			}
		}

		private static bool IsUnderstood(string lang)
		{
			if (!string.Equals(lang, ResolveTargetLanguage(), StringComparison.OrdinalIgnoreCase))
			{
				return IgnoredSet().Contains(lang);
			}
			return true;
		}

		internal void Announce()
		{
			((MonoBehaviour)this).StartCoroutine(TranslateRoutine("Allspeak is active", null, delegate(string source, string target, string translated, double? confidence)
			{
				Chat instance = Chat.instance;
				if (instance != null)
				{
					((Terminal)instance).AddString(string.IsNullOrWhiteSpace(translated) ? "Allspeak is active" : translated);
				}
			}, delegate
			{
				Chat instance = Chat.instance;
				if (instance != null)
				{
					((Terminal)instance).AddString("Allspeak inactive, check logs");
				}
			}));
		}

		private IEnumerator TranslateRoutine(string text, string fallbackLang, Action<string, string, string, double?> onSuccess, Action onFailure)
		{
			string target = ResolveTargetLanguage();
			string source = null;
			string translated = null;
			double? confidence = null;
			yield return Request(text, "auto", target, delegate(string src, string text2, double? conf, bool flag2)
			{
				if (flag2)
				{
					source = src;
					translated = text2;
					confidence = conf;
				}
			});
			if (source != null)
			{
				bool flag = confidence >= DetectionConfidence.Value;
				if (flag || fallbackLang == null || string.Equals(fallbackLang, source, StringComparison.OrdinalIgnoreCase))
				{
					onSuccess(source, target, translated, flag ? confidence : ((double?)null));
					yield break;
				}
			}
			if (fallbackLang != null)
			{
				string tr2 = null;
				bool ok2 = false;
				yield return Request(text, fallbackLang, target, delegate(string src, string text2, double? conf, bool flag2)
				{
					if (flag2)
					{
						tr2 = text2;
						ok2 = true;
					}
				});
				if (ok2)
				{
					onSuccess(fallbackLang, target, tr2, null);
					yield break;
				}
			}
			onFailure?.Invoke();
		}

		private IEnumerator Request(string text, string sourceLang, string target, Action<string, string, double?, bool> done)
		{
			string url = "https://clients5.google.com/translate_a/single?client=dict-chrome-ex&dt=t&sl=" + UnityWebRequest.EscapeURL(sourceLang) + "&tl=" + UnityWebRequest.EscapeURL(target) + "&q=" + UnityWebRequest.EscapeURL(text);
			for (int attempt = 0; attempt < 3; attempt++)
			{
				UnityWebRequest req = UnityWebRequest.Get(url);
				try
				{
					yield return req.SendWebRequest();
					if ((int)req.result == 1)
					{
						if (TryParse(req.downloadHandler.text, sourceLang, out var source, out var translated, out var confidence))
						{
							done(source, translated, confidence, arg4: true);
							yield break;
						}
						((BaseUnityPlugin)this).Logger.LogWarning((object)"Translate: unexpected response");
					}
					else
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)$"Translate attempt {attempt + 1}/3 failed: {req.error}");
						if (req.responseCode == 429 && attempt != 2)
						{
							yield return (object)new WaitForSeconds(2f * (float)(attempt + 1));
							continue;
						}
					}
				}
				finally
				{
					((IDisposable)req)?.Dispose();
				}
				break;
			}
			done(null, null, null, arg4: false);
		}

		private static bool TryParse(string json, string sourceLang, out string source, out string translated, out double? confidence)
		{
			source = null;
			translated = null;
			confidence = null;
			try
			{
				if (!(MiniJson.Parse(json) is List<object> { Count: not 0 } list))
				{
					return false;
				}
				StringBuilder stringBuilder = new StringBuilder();
				if (list[0] is List<object> list2)
				{
					foreach (object item in list2)
					{
						if (item is List<object> { Count: >0 } list3 && list3[0] is string value)
						{
							stringBuilder.Append(value);
						}
						else if (item is string value2)
						{
							stringBuilder.Append(value2);
						}
					}
				}
				translated = stringBuilder.ToString();
				if (list.Count > 2 && list[2] is string text && !string.IsNullOrEmpty(text))
				{
					source = text;
				}
				else if (!string.Equals(sourceLang, "auto", StringComparison.OrdinalIgnoreCase))
				{
					source = sourceLang;
				}
				if (list.Count > 6 && list[6] is double value3)
				{
					confidence = value3;
				}
				return source != null;
			}
			catch (Exception)
			{
				return false;
			}
		}
	}
	[HarmonyPatch(typeof(Terminal), "AddString", new Type[]
	{
		typeof(PlatformUserID),
		typeof(string),
		typeof(Type),
		typeof(bool)
	})]
	internal static class ChatLinePatch
	{
		private static bool _bypass;

		private unsafe static bool Prefix(Terminal __instance, PlatformUserID user, string text, Type type, bool timestamp)
		{
			//IL_000e: 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)
			//IL_001c: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Invalid comparison between Unknown and I4
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: 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_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			if (_bypass || !(__instance is Chat))
			{
				return true;
			}
			if (!Plugin.Enabled.Value)
			{
				return true;
			}
			if ((int)type == 3 || string.IsNullOrWhiteSpace(text))
			{
				return true;
			}
			if (user == UserInfo.GetLocalUser().UserId)
			{
				return true;
			}
			PlayerInfo val = default(PlayerInfo);
			if (!ZNet.TryGetPlayerByPlatformUserID(user, ref val))
			{
				return true;
			}
			string playerName = CensorShittyWords.FilterUGC(val.m_name, (UGCType)2, user, 0L, (GetUserProfileCompletedHandler)null);
			bool value = Plugin.HideOriginal.Value;
			Action showOriginal = null;
			if (value)
			{
				showOriginal = delegate
				{
					//IL_001c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0028: Unknown result type (might be due to invalid IL or missing references)
					if ((Object)(object)__instance == (Object)null)
					{
						return;
					}
					_bypass = true;
					try
					{
						__instance.AddString(user, text, type, timestamp);
					}
					finally
					{
						_bypass = false;
					}
				};
			}
			Plugin.Instance.Translate(text, ((PlatformUserID)(ref user)).IsValid ? ((object)(*(PlatformUserID*)(&user))/*cast due to .constrained prefix*/).ToString() : null, playerName, type, showOriginal);
			return !value;
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class SpawnPatch
	{
		private static ZNet _announcedFor;

		private static void Postfix(Player __instance)
		{
			if (Plugin.Enabled.Value && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)ZNet.instance == (Object)null) && !((Object)(object)_announcedFor == (Object)(object)ZNet.instance))
			{
				_announcedFor = ZNet.instance;
				Plugin.Instance.Announce();
			}
		}
	}
	internal static class PlayerLanguages
	{
		private class Entry
		{
			public string Lang;

			public double Confidence;

			public string Name;
		}

		private static readonly Dictionary<string, Entry> Entries = new Dictionary<string, Entry>();

		private static string _path;

		public static void Load(string path)
		{
			_path = path;
			if (!File.Exists(path))
			{
				return;
			}
			try
			{
				string[] array = File.ReadAllLines(path);
				for (int i = 0; i < array.Length; i++)
				{
					string[] array2 = array[i].Split(new char[1] { '\t' });
					if (array2.Length >= 3 && double.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
					{
						if (result > 1.0)
						{
							result = 1.0;
						}
						Entries[array2[0]] = new Entry
						{
							Lang = array2[1],
							Confidence = result,
							Name = ((array2.Length > 3) ? array2[3] : "")
						};
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to load " + path + ": " + ex.Message));
			}
		}

		public static string Get(string id)
		{
			if (id == null || !Entries.TryGetValue(id, out var value))
			{
				return null;
			}
			return value.Lang;
		}

		public static void Observe(string id, string name, string lang, double confidence)
		{
			if (id != null && !string.IsNullOrEmpty(lang) && (!Entries.TryGetValue(id, out var value) || (!(confidence < value.Confidence) && !string.Equals(value.Lang, lang, StringComparison.OrdinalIgnoreCase))))
			{
				Entries[id] = new Entry
				{
					Lang = lang,
					Confidence = confidence,
					Name = (name ?? "")
				};
				Save();
			}
		}

		private static void Save()
		{
			try
			{
				File.WriteAllLines(_path, Entries.Select((KeyValuePair<string, Entry> kv) => kv.Key + "\t" + kv.Value.Lang + "\t" + kv.Value.Confidence.ToString(CultureInfo.InvariantCulture) + "\t" + kv.Value.Name.Replace('\t', ' ')));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to save " + _path + ": " + ex.Message));
			}
		}
	}
	internal static class MiniJson
	{
		public static object Parse(string json)
		{
			int i = 0;
			return ParseValue(json, ref i);
		}

		private static void SkipWs(string s, ref int i)
		{
			while (i < s.Length && char.IsWhiteSpace(s[i]))
			{
				i++;
			}
		}

		private static object ParseValue(string s, ref int i)
		{
			SkipWs(s, ref i);
			if (i >= s.Length)
			{
				throw new FormatException("eof");
			}
			switch (s[i])
			{
			case '[':
				return ParseArray(s, ref i);
			case '{':
				return ParseObject(s, ref i);
			case '"':
				return ParseString(s, ref i);
			case 't':
				i += 4;
				return true;
			case 'f':
				i += 5;
				return false;
			case 'n':
				i += 4;
				return null;
			default:
				return ParseNumber(s, ref i);
			}
		}

		private static List<object> ParseArray(string s, ref int i)
		{
			List<object> list = new List<object>();
			i++;
			SkipWs(s, ref i);
			if (s[i] == ']')
			{
				i++;
				return list;
			}
			while (true)
			{
				SkipWs(s, ref i);
				if (s[i] == ',' || s[i] == ']')
				{
					list.Add(null);
				}
				else
				{
					list.Add(ParseValue(s, ref i));
				}
				SkipWs(s, ref i);
				if (s[i] != ',')
				{
					break;
				}
				i++;
			}
			if (s[i] == ']')
			{
				i++;
				return list;
			}
			throw new FormatException("array");
		}

		private static Dictionary<string, object> ParseObject(string s, ref int i)
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object>();
			i++;
			SkipWs(s, ref i);
			if (s[i] == '}')
			{
				i++;
				return dictionary;
			}
			while (true)
			{
				SkipWs(s, ref i);
				string key = ParseString(s, ref i);
				SkipWs(s, ref i);
				i++;
				dictionary[key] = ParseValue(s, ref i);
				SkipWs(s, ref i);
				if (s[i] != ',')
				{
					break;
				}
				i++;
			}
			if (s[i] == '}')
			{
				i++;
				return dictionary;
			}
			throw new FormatException("object");
		}

		private static string ParseString(string s, ref int i)
		{
			StringBuilder stringBuilder = new StringBuilder();
			i++;
			while (i < s.Length)
			{
				char c = s[i++];
				switch (c)
				{
				case '"':
					return stringBuilder.ToString();
				default:
					stringBuilder.Append(c);
					break;
				case '\\':
				{
					char c2 = s[i++];
					switch (c2)
					{
					case 'n':
						stringBuilder.Append('\n');
						break;
					case 't':
						stringBuilder.Append('\t');
						break;
					case 'r':
						stringBuilder.Append('\r');
						break;
					case 'b':
						stringBuilder.Append('\b');
						break;
					case 'f':
						stringBuilder.Append('\f');
						break;
					case 'u':
						stringBuilder.Append((char)Convert.ToInt32(s.Substring(i, 4), 16));
						i += 4;
						break;
					default:
						stringBuilder.Append(c2);
						break;
					}
					break;
				}
				}
			}
			throw new FormatException("string");
		}

		private static double ParseNumber(string s, ref int i)
		{
			int num = i;
			while (i < s.Length && "+-0123456789.eE".IndexOf(s[i]) >= 0)
			{
				i++;
			}
			return double.Parse(s.Substring(num, i - num), CultureInfo.InvariantCulture);
		}
	}
	internal static class BuildInfo
	{
		public const string Version = "0.2.1";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}