Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of WhatGotChanged v1.0.0
WhatGotChanged.dll
Decompiled 3 weeks 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.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Data; using System.Data.SqlTypes; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Globalization; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Net; using System.Numerics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Bson; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq.JsonPath; using Newtonsoft.Json.Schema; using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Utilities; using WhatGotChanged.Source; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("WhatGotChanged")] [assembly: AssemblyDescription("Auto-generates changelogs by comparing installed mods against previous Thunderstore versions")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("WhatGotChanged")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("d4e5f6a7-b8c9-0123-4567-89abcdef0123")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace WhatGotChanged { [BepInPlugin("IAmOnTheInternetAndItIsScary.WhatGotChanged", "WhatGotChanged", "1.0.0")] public class WhatGotChangedPlugin : BaseUnityPlugin { internal const string ModName = "WhatGotChanged"; internal const string ModGUID = "IAmOnTheInternetAndItIsScary.WhatGotChanged"; internal const string ModVersion = "1.0.0"; public void Awake() { try { ConfigEntry<string> val = ((BaseUnityPlugin)this).Config.Bind<string>("Filters", "TrackTeams", "", "Filter by Thunderstore team/owner names (comma-separated). Tracks ALL mods published by these teams."); ConfigEntry<string> val2 = ((BaseUnityPlugin)this).Config.Bind<string>("Filters", "TrackMods", "", "Filter by specific Thunderstore mod names (comma-separated). Use the exact name as it appears on Thunderstore."); ConfigEntry<ChangelogWriter.DetailLevel> val3 = ((BaseUnityPlugin)this).Config.Bind<ChangelogWriter.DetailLevel>("Output", "DetailLevel", ChangelogWriter.DetailLevel.Simple, "Detail level for changelogs.\nsimple — counts per class (e.g. \"3 methods changed, 1 added\")\ndetailed — lists every change by name\nfull — actual C# code diffs"); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; Scanner.Run(((BaseUnityPlugin)this).Logger, val.Value, val2.Value, val3.Value); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Fatal error: " + ex.Message)); ((BaseUnityPlugin)this).Logger.LogError((object)ex.StackTrace); } } } } namespace WhatGotChanged.Source { public static class Scanner { public static void Run(ManualLogSource log, string trackTeams, string trackMods, ChangelogWriter.DetailLevel detailLevel) { log.LogInfo((object)"=== WhatGotChanged? ==="); if (string.IsNullOrEmpty(trackTeams) && string.IsNullOrEmpty(trackMods)) { log.LogInfo((object)"No mods configured — edit config to add TrackTeams or TrackMods"); return; } List<ThunderstoreApi.PackageInfo> list = ThunderstoreApi.FetchPackageList(log); if (list == null || list.Count == 0) { log.LogWarning((object)"Could not fetch Thunderstore data — are you offline?"); return; } Dictionary<string, string> dictionary = ModDiscovery.GatherCandidates(log, trackTeams, trackMods, list); if (dictionary.Count == 0) { log.LogInfo((object)"No installed mods matched configured filters"); return; } Dictionary<string, ThunderstoreApi.PackageInfo> dictionary2 = new Dictionary<string, ThunderstoreApi.PackageInfo>(StringComparer.OrdinalIgnoreCase); foreach (ThunderstoreApi.PackageInfo item in list) { string key = item.Owner + "/" + item.Name; if (!dictionary2.ContainsKey(key)) { dictionary2[key] = item; } } log.LogInfo((object)$"Scanning {dictionary.Count} package(s)..."); string text = Path.Combine(Paths.ConfigPath, "WhatGotChanged"); Directory.CreateDirectory(text); int num = 0; foreach (KeyValuePair<string, string> item2 in dictionary) { string key2 = item2.Key; string value = item2.Value; ThunderstoreApi.PackageInfo packageInfo = dictionary2[key2]; if (packageInfo.Versions.Count < 2) { log.LogInfo((object)(" " + packageInfo.Name + " — only 1 version, nothing to compare")); continue; } ThunderstoreApi.VersionInfo versionInfo = packageInfo.Versions[0]; ThunderstoreApi.VersionInfo versionInfo2 = packageInfo.Versions[1]; string text2 = Path.Combine(text, packageInfo.Name + "-" + versionInfo.VersionNumber + ".md"); if (File.Exists(text2)) { log.LogInfo((object)(" " + packageInfo.Name + " v" + versionInfo.VersionNumber + " — changelog already exists, skipping")); continue; } bool flag = value != versionInfo.VersionNumber; if (flag) { log.LogInfo((object)(" " + packageInfo.Name + ": UPDATE AVAILABLE v" + value + " -> v" + versionInfo.VersionNumber)); } log.LogInfo((object)(" " + packageInfo.Name + ": comparing v" + versionInfo2.VersionNumber + " -> v" + versionInfo.VersionNumber)); string text3 = ThunderstoreApi.FetchDecompiledSource(packageInfo.Owner, packageInfo.Name, versionInfo2.VersionNumber, log); string text4 = ThunderstoreApi.FetchDecompiledSource(packageInfo.Owner, packageInfo.Name, versionInfo.VersionNumber, log); if (text3 == null && text4 == null) { log.LogInfo((object)" Decompiled source not available for either version, skipping"); continue; } if (text3 == null) { log.LogInfo((object)(" Decompiled source not available for v" + versionInfo2.VersionNumber + ", skipping")); continue; } if (text4 == null) { log.LogInfo((object)(" Decompiled source not available for v" + versionInfo.VersionNumber + ", skipping")); continue; } SourceDiffer.DiffResult diffResult = SourceDiffer.Diff(text3, text4, log); if (diffResult.Changes.Count > 0) { log.LogInfo((object)$" {diffResult.Changes.Count} change(s) detected"); } else { log.LogInfo((object)" No code changes — metadata/asset update only"); } ChangelogWriter.WriteChangelog(text2, packageInfo.Name, versionInfo2.VersionNumber, versionInfo.VersionNumber, value, flag, diffResult, detailLevel); num++; } if (num > 0) { log.LogInfo((object)"Changelogs: BepInEx/config/WhatGotChanged/"); } log.LogInfo((object)"=== Scan Complete ==="); } } public static class ModDiscovery { private class InstalledPlugin { public string GUID; public string Name; public string Version; } public static Dictionary<string, string> GatherCandidates(ManualLogSource log, string trackTeams, string trackMods, List<ThunderstoreApi.PackageInfo> allPackages) { HashSet<string> hashSet = ParseList(trackTeams); HashSet<string> hashSet2 = ParseList(trackMods); Dictionary<string, InstalledPlugin> plugins = BuildInstalledPluginMap(); Dictionary<string, ThunderstoreApi.PackageInfo> dictionary = new Dictionary<string, ThunderstoreApi.PackageInfo>(StringComparer.OrdinalIgnoreCase); foreach (ThunderstoreApi.PackageInfo allPackage in allPackages) { if (hashSet.Contains(allPackage.Owner) || hashSet2.Contains(allPackage.Name)) { dictionary[allPackage.Owner + "/" + allPackage.Name] = allPackage; } } log.LogInfo((object)$"Config matched {dictionary.Count} package(s) on Thunderstore"); Dictionary<string, string> dictionary2 = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); int num = 0; int num2 = 0; int num3 = 0; foreach (KeyValuePair<string, ThunderstoreApi.PackageInfo> item in dictionary) { string key = item.Key; ThunderstoreApi.PackageInfo value = item.Value; string text = FindDirect(value.Owner, value.Name, plugins); if (text != null) { dictionary2[key] = text; num++; log.LogInfo((object)(" " + key + " v" + text + " — found via direct match")); continue; } text = FindViaCfgFiles(value.Name); if (text != null) { dictionary2[key] = text; num2++; log.LogInfo((object)(" " + key + " v" + text + " — found via cfg scan")); continue; } text = FindViaChainloader(value.Owner, value.Name, plugins); if (text != null) { dictionary2[key] = text; num3++; log.LogInfo((object)(" " + key + " v" + text + " — found via chainloader scan")); } else { log.LogInfo((object)(" " + key + " — not installed, skipping")); } } log.LogInfo((object)$"Total installed: {num} direct, {num2} from cfg scan, {num3} from chainloader"); return dictionary2; } private static Dictionary<string, InstalledPlugin> BuildInstalledPluginMap() { Dictionary<string, InstalledPlugin> dictionary = new Dictionary<string, InstalledPlugin>(StringComparer.OrdinalIgnoreCase); if (Chainloader.PluginInfos == null) { return dictionary; } foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos) { BepInPlugin metadata = pluginInfo.Value.Metadata; dictionary[metadata.GUID] = new InstalledPlugin { GUID = metadata.GUID, Name = metadata.Name, Version = metadata.Version.ToString() }; } return dictionary; } private static string FindDirect(string owner, string mod, Dictionary<string, InstalledPlugin> plugins) { string[] array = new string[2] { owner + "." + mod, "com." + owner + "." + mod }; string[] array2 = array; foreach (string key in array2) { if (plugins.TryGetValue(key, out var value)) { return value.Version; } } foreach (InstalledPlugin value2 in plugins.Values) { if (!value2.Name.Equals(mod, StringComparison.OrdinalIgnoreCase) || value2.GUID.IndexOf(owner, StringComparison.OrdinalIgnoreCase) < 0) { continue; } return value2.Version; } return null; } private static string FindViaCfgFiles(string mod) { string configPath = Paths.ConfigPath; if (!Directory.Exists(configPath)) { return null; } string value = mod.ToLowerInvariant(); string[] files = Directory.GetFiles(configPath, "*.cfg"); foreach (string path in files) { string text = Path.GetFileNameWithoutExtension(path).ToLowerInvariant(); if (text.Contains(value)) { return "unknown"; } } return null; } private static string FindViaChainloader(string owner, string mod, Dictionary<string, InstalledPlugin> plugins) { if (plugins.Count == 0) { return null; } string text = mod.ToLowerInvariant(); string value = owner.ToLowerInvariant(); InstalledPlugin installedPlugin = null; foreach (InstalledPlugin value2 in plugins.Values) { string text2 = value2.GUID.ToLowerInvariant(); string text3 = value2.Name.ToLowerInvariant(); if (text2.Contains(text) || text3 == text) { if (text2.Contains(value)) { return value2.Version; } if (installedPlugin == null) { installedPlugin = value2; } } } return installedPlugin?.Version; } private static HashSet<string> ParseList(string csv) { return new HashSet<string>(from s in csv.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries) select s.Trim(), StringComparer.OrdinalIgnoreCase); } } public static class ThunderstoreApi { public class PackageInfo { [JsonProperty("name")] public string Name { get; set; } = ""; [JsonProperty("owner")] public string Owner { get; set; } = ""; [JsonProperty("versions")] public List<VersionInfo> Versions { get; set; } = new List<VersionInfo>(); } public class VersionInfo { [JsonProperty("version_number")] public string VersionNumber { get; set; } = ""; } public class DecompilationResponse { [JsonProperty("decompilations")] public List<DecompilationEntry> Decompilations { get; set; } = new List<DecompilationEntry>(); } public class DecompilationEntry { [JsonProperty("url")] public string Url { get; set; } = ""; } private const string PackageListUrl = "https://thunderstore.io/c/valheim/api/v1/package/"; private const string DecompilationUrl = "https://thunderstore.io/api/cyberstorm/package/{0}/{1}/v/{2}/source/"; private const string UserAgent = "WhatGotChanged/1.0"; private const int Timeout = 30000; public static List<PackageInfo> FetchPackageList(ManualLogSource log) { try { log.LogInfo((object)"Fetching Thunderstore package list..."); HttpWebRequest httpWebRequest = CreateRequest("https://thunderstore.io/c/valheim/api/v1/package/"); using HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse(); using StreamReader streamReader = new StreamReader(httpWebResponse.GetResponseStream()); return JsonConvert.DeserializeObject<List<PackageInfo>>(streamReader.ReadToEnd()); } catch (Exception ex) { log.LogWarning((object)("Thunderstore fetch failed: " + ex.Message)); return null; } } public static string FetchDecompiledSource(string owner, string packageName, string version, ManualLogSource log) { try { HttpWebRequest httpWebRequest = CreateRequest($"https://thunderstore.io/api/cyberstorm/package/{owner}/{packageName}/v/{version}/source/"); DecompilationResponse decompilationResponse; using (HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse()) { using StreamReader streamReader = new StreamReader(httpWebResponse.GetResponseStream()); decompilationResponse = JsonConvert.DeserializeObject<DecompilationResponse>(streamReader.ReadToEnd()); } if (decompilationResponse?.Decompilations == null || decompilationResponse.Decompilations.Count == 0) { return null; } string url = decompilationResponse.Decompilations[0].Url; if (string.IsNullOrEmpty(url)) { return null; } HttpWebRequest httpWebRequest2 = CreateRequest(url); using HttpWebResponse httpWebResponse2 = (HttpWebResponse)httpWebRequest2.GetResponse(); using StreamReader streamReader2 = new StreamReader(httpWebResponse2.GetResponseStream()); return streamReader2.ReadToEnd(); } catch (Exception ex) { log.LogWarning((object)(" Fetch failed for " + owner + "/" + packageName + " v" + version + ": " + ex.Message)); return null; } } private static HttpWebRequest CreateRequest(string url) { HttpWebRequest httpWebRequest = WebRequest.CreateHttp(url); httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; httpWebRequest.Timeout = 30000; httpWebRequest.UserAgent = "WhatGotChanged/1.0"; return httpWebRequest; } } public static class SourceDiffer { public enum ChangeKind { Added, Removed, Modified } public enum ChangeType { Type, Method, Field, Property } public class Change { public ChangeKind Kind; public ChangeType Type; public string ClassName; public string MemberName; public string NewBody; public string OldBody; public Change(ChangeKind kind, ChangeType type, string className, string memberName, string newBody, string oldBody = "") { Kind = kind; Type = type; ClassName = className; MemberName = memberName; NewBody = newBody; OldBody = oldBody; } } public class DiffResult { public List<Change> Changes = new List<Change>(); } private class ParsedClass { public Dictionary<string, string> Methods = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); public List<string> Fields = new List<string>(); public List<string> Properties = new List<string>(); } private static readonly string[] AccessModifiers = new string[13] { "public", "private", "protected", "internal", "static", "abstract", "sealed", "partial", "readonly", "new", "override", "virtual", "unsafe" }; private static readonly string[] TypeKeywords = new string[4] { "class", "struct", "interface", "enum" }; private static readonly HashSet<string> ReservedWords = new HashSet<string> { "class", "struct", "interface", "enum", "namespace", "return", "using", "get", "set" }; public static DiffResult Diff(string oldSource, string newSource, ManualLogSource log = null) { DiffResult diffResult = new DiffResult(); Dictionary<string, ParsedClass> dictionary = ParseSource(oldSource); Dictionary<string, ParsedClass> dictionary2 = ParseSource(newSource); if (log != null) { int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; foreach (ParsedClass value2 in dictionary.Values) { num += value2.Methods.Count; num3 += value2.Fields.Count + value2.Properties.Count; } foreach (ParsedClass value3 in dictionary2.Values) { num2 += value3.Methods.Count; num4 += value3.Fields.Count + value3.Properties.Count; } log.LogInfo((object)$" Parsed: {dictionary.Count}/{dictionary2.Count} classes, {num}/{num2} methods, {num3}/{num4} fields+props"); } foreach (KeyValuePair<string, ParsedClass> item in dictionary2) { if (!dictionary.ContainsKey(item.Key)) { diffResult.Changes.Add(new Change(ChangeKind.Added, ChangeType.Type, item.Key, "", "")); } } foreach (KeyValuePair<string, ParsedClass> item2 in dictionary) { if (!dictionary2.ContainsKey(item2.Key)) { diffResult.Changes.Add(new Change(ChangeKind.Removed, ChangeType.Type, item2.Key, "", "")); } } foreach (KeyValuePair<string, ParsedClass> item3 in dictionary2) { if (dictionary.TryGetValue(item3.Key, out var value)) { DiffMembers(item3.Key, value, item3.Value, diffResult); } } return diffResult; } private static void DiffMembers(string className, ParsedClass oldClass, ParsedClass newClass, DiffResult result) { string className2 = (className.Contains(".") ? className.Substring(className.LastIndexOf('.') + 1) : className); foreach (KeyValuePair<string, string> method in newClass.Methods) { if (!oldClass.Methods.ContainsKey(method.Key)) { result.Changes.Add(new Change(ChangeKind.Added, ChangeType.Method, className2, method.Key, method.Value)); } } foreach (KeyValuePair<string, string> method2 in oldClass.Methods) { if (!newClass.Methods.ContainsKey(method2.Key)) { result.Changes.Add(new Change(ChangeKind.Removed, ChangeType.Method, className2, method2.Key, method2.Value)); } } foreach (KeyValuePair<string, string> method3 in newClass.Methods) { if (oldClass.Methods.TryGetValue(method3.Key, out var value) && value != method3.Value) { result.Changes.Add(new Change(ChangeKind.Modified, ChangeType.Method, className2, method3.Key, method3.Value, value)); } } DiffStringSet(oldClass.Fields, newClass.Fields, ChangeType.Field, className2, result); DiffStringSet(oldClass.Properties, newClass.Properties, ChangeType.Property, className2, result); } private static void DiffStringSet(List<string> oldItems, List<string> newItems, ChangeType type, string className, DiffResult result) { HashSet<string> hashSet = new HashSet<string>(oldItems, StringComparer.OrdinalIgnoreCase); HashSet<string> hashSet2 = new HashSet<string>(newItems, StringComparer.OrdinalIgnoreCase); foreach (string item in hashSet2.Except(hashSet)) { result.Changes.Add(new Change(ChangeKind.Added, type, className, item, "")); } foreach (string item2 in hashSet.Except(hashSet2)) { result.Changes.Add(new Change(ChangeKind.Removed, type, className, item2, "")); } } private static Dictionary<string, ParsedClass> ParseSource(string source) { Dictionary<string, ParsedClass> dictionary = new Dictionary<string, ParsedClass>(StringComparer.OrdinalIgnoreCase); string[] array = source.Split(new char[2] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string text = ""; string text2 = ""; ParsedClass parsedClass = null; int num = 0; int num2 = -1; int num3 = -1; bool flag = false; List<string> list = new List<string>(); string key = ""; foreach (string text3 in array) { string text4 = text3.Trim(); if (text4.StartsWith("namespace ")) { text = text4.Substring("namespace ".Length).TrimEnd('{', ' ', '\t'); } int num4 = text4.Count((char c) => c == '{'); int num5 = text4.Count((char c) => c == '}'); int num6 = num + num4 - num5; if (flag && num4 > 0) { num2 = num + 1; flag = false; } if ((parsedClass == null || num < num2) && IsTypeDeclaration(text4, out var name)) { string text5 = (string.IsNullOrEmpty(text) ? name : (text + "." + name)); if (parsedClass != null && num >= num2) { text5 = text2 + "." + name; } text2 = text5; parsedClass = new ParsedClass(); if (num4 > 0) { num2 = num + 1; } else { flag = true; } if (!dictionary.ContainsKey(text5)) { dictionary[text5] = parsedClass; } } if (parsedClass != null && num >= num2 && num2 > 0 && !flag) { string signature; string signature2; string signature3; if (text4 == "{" || text4 == "}") { if (num3 >= 0) { list.Add(text3); if (num6 <= num3) { parsedClass.Methods[key] = string.Join("\n", list); num3 = -1; list.Clear(); } } } else if (num3 >= 0) { list.Add(text3); if (num6 <= num3) { parsedClass.Methods[key] = string.Join("\n", list); num3 = -1; list.Clear(); } } else if (IsMethodDeclaration(text4, out signature)) { key = signature; list.Clear(); list.Add(text3); if (text4.Contains("=>") || (num4 > 0 && num5 > 0 && num4 == num5)) { parsedClass.Methods[signature] = text3; list.Clear(); } else { num3 = num; } } else if (IsFieldDeclaration(text4, out signature2)) { parsedClass.Fields.Add(signature2); } else if (IsPropertyDeclaration(text4, out signature3)) { parsedClass.Properties.Add(signature3); } } num = num6; if (parsedClass != null && num < num2) { parsedClass = null; num2 = -1; } } return dictionary; } private static bool IsTypeDeclaration(string line, out string name) { name = ""; string input = line; string[] accessModifiers = AccessModifiers; foreach (string text in accessModifiers) { input = Regex.Replace(input, "\\b" + text + "\\b", "").Trim(); } string[] typeKeywords = TypeKeywords; foreach (string text2 in typeKeywords) { Match match = Regex.Match(input, "\\b" + text2 + "\\s+(\\w+)"); if (match.Success) { name = match.Groups[1].Value; return true; } } return false; } private static bool IsMethodDeclaration(string line, out string signature) { signature = ""; if (line.StartsWith("[") || line.StartsWith("//") || line.StartsWith("/*") || line.Contains(" event ")) { return false; } Match match = Regex.Match(line, "(?:(?:public|private|protected|internal|static|virtual|override|abstract|sealed|async|new|extern|unsafe)\\s+)*(\\w[\\w<>\\[\\],\\s\\?]*)\\s+(\\w+)\\s*\\(([^)]*)\\)"); if (match.Success) { string item = match.Groups[1].Value.Trim(); if (ReservedWords.Contains(item)) { return false; } signature = match.Groups[2].Value.Trim() + "(" + SimplifyParams(match.Groups[3].Value.Trim()) + ")"; return true; } match = Regex.Match(line, "(?:(?:public|private|protected|internal|static)\\s+)+(\\w+)\\s*\\(([^)]*)\\)\\s*(?::|{)"); if (match.Success) { signature = match.Groups[1].Value.Trim() + "(" + SimplifyParams(match.Groups[2].Value.Trim()) + ")"; return true; } return false; } private static string SimplifyParams(string parameters) { if (string.IsNullOrWhiteSpace(parameters)) { return ""; } return string.Join(", ", from p in parameters.Split(new char[1] { ',' }) select p.Trim().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries) into t where t.Length >= 1 select t[0]); } private static bool IsFieldDeclaration(string line, out string signature) { signature = ""; if (line.Contains("(") || line.Contains("=>") || line.StartsWith("[") || line.StartsWith("//") || line.Contains(" event ")) { return false; } Match match = Regex.Match(line, "(?:(?:public|private|protected|internal|static|readonly|const|volatile|new|unsafe)\\s+)*(\\w[\\w<>\\[\\],\\?\\s]*)\\s+(\\w+)\\s*[;=]"); if (!match.Success) { return false; } string text = match.Groups[1].Value.Trim(); if (ReservedWords.Contains(text)) { return false; } signature = text + " " + match.Groups[2].Value.Trim(); return true; } private static bool IsPropertyDeclaration(string line, out string signature) { signature = ""; Match match = Regex.Match(line, "(?:(?:public|private|protected|internal|static|virtual|override|abstract|new|unsafe)\\s+)*(\\w[\\w<>\\[\\],\\?\\s]*)\\s+(\\w+)\\s*\\{"); if (!match.Success || (!line.Contains("get") && !line.Contains("set"))) { return false; } string text = match.Groups[1].Value.Trim(); if (ReservedWords.Contains(text)) { return false; } signature = text + " " + match.Groups[2].Value.Trim(); return true; } } public static class ChangelogWriter { public enum DetailLevel { Simple, Detailed, Full } public static void WriteChangelog(string outputPath, string modName, string oldVersion, string newVersion, string installedVersion, bool hasUpdate, SourceDiffer.DiffResult diff, DetailLevel level) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# " + modName); stringBuilder.AppendLine("`v" + oldVersion + "` → `v" + newVersion + "`"); stringBuilder.AppendLine(); if (hasUpdate && !string.IsNullOrEmpty(installedVersion)) { stringBuilder.AppendLine("> ⚠ **Update available:** you have `v" + installedVersion + "`, latest is `v" + newVersion + "`"); stringBuilder.AppendLine(); } stringBuilder.AppendLine($"*Generated {DateTime.Now:yyyy-MM-dd HH:mm}*"); stringBuilder.AppendLine(); stringBuilder.AppendLine("---"); stringBuilder.AppendLine(); if (diff.Changes.Count == 0) { stringBuilder.AppendLine("No code changes — metadata/asset update only."); stringBuilder.AppendLine(); } else { switch (level) { case DetailLevel.Simple: WriteSimple(stringBuilder, diff); break; case DetailLevel.Detailed: WriteDetailed(stringBuilder, diff); break; case DetailLevel.Full: WriteFull(stringBuilder, diff); break; } } File.WriteAllText(outputPath, stringBuilder.ToString()); } private static IEnumerable<IGrouping<string, SourceDiffer.Change>> GroupByClass(SourceDiffer.DiffResult diff) { return from c in diff.Changes group c by c.ClassName into g orderby g.Key select g; } private static void WriteSimple(StringBuilder sb, SourceDiffer.DiffResult diff) { foreach (IGrouping<string, SourceDiffer.Change> item in GroupByClass(diff)) { IGrouping<string, SourceDiffer.Change> group = item; SourceDiffer.Change change = group.FirstOrDefault((SourceDiffer.Change c) => c.Type == SourceDiffer.ChangeType.Type); if (change != null) { sb.AppendLine("- **" + group.Key + "**: " + ((change.Kind == SourceDiffer.ChangeKind.Added) ? "new" : "removed") + " type"); continue; } List<string> parts = new List<string>(); Count(SourceDiffer.ChangeType.Method, SourceDiffer.ChangeKind.Added, "method(s) added"); Count(SourceDiffer.ChangeType.Method, SourceDiffer.ChangeKind.Modified, "method(s) changed"); Count(SourceDiffer.ChangeType.Method, SourceDiffer.ChangeKind.Removed, "method(s) removed"); Count(SourceDiffer.ChangeType.Field, SourceDiffer.ChangeKind.Added, "field(s) added"); Count(SourceDiffer.ChangeType.Field, SourceDiffer.ChangeKind.Removed, "field(s) removed"); Count(SourceDiffer.ChangeType.Property, SourceDiffer.ChangeKind.Added, "property(s) added"); Count(SourceDiffer.ChangeType.Property, SourceDiffer.ChangeKind.Removed, "property(s) removed"); if (parts.Count > 0) { sb.AppendLine("- **" + group.Key + "**: " + string.Join(", ", parts)); } void Count(SourceDiffer.ChangeType type, SourceDiffer.ChangeKind kind, string label) { int num = group.Count((SourceDiffer.Change c) => c.Type == type && c.Kind == kind); if (num > 0) { parts.Add($"{num} {label}"); } } } sb.AppendLine(); } private static void WriteDetailed(StringBuilder sb, SourceDiffer.DiffResult diff) { foreach (IGrouping<string, SourceDiffer.Change> item in GroupByClass(diff)) { sb.AppendLine("### " + item.Key); sb.AppendLine(); foreach (SourceDiffer.Change item2 in item.Where((SourceDiffer.Change c) => c.Kind == SourceDiffer.ChangeKind.Added)) { sb.AppendLine("- + `" + FormatMember(item2) + "`"); } foreach (SourceDiffer.Change item3 in item.Where((SourceDiffer.Change c) => c.Kind == SourceDiffer.ChangeKind.Removed)) { sb.AppendLine("- - `" + FormatMember(item3) + "`"); } foreach (SourceDiffer.Change item4 in item.Where((SourceDiffer.Change c) => c.Kind == SourceDiffer.ChangeKind.Modified)) { sb.AppendLine("- ~ `" + FormatMember(item4) + "`"); } sb.AppendLine(); } } private static void WriteFull(StringBuilder sb, SourceDiffer.DiffResult diff) { foreach (IGrouping<string, SourceDiffer.Change> item in GroupByClass(diff)) { sb.AppendLine("### " + item.Key); sb.AppendLine(); foreach (SourceDiffer.Change item2 in item) { sb.AppendLine("**" + KindSymbol(item2.Kind) + " " + FormatMember(item2) + "**"); if (item2.Type == SourceDiffer.ChangeType.Method) { if (item2.Kind == SourceDiffer.ChangeKind.Modified) { WriteLineDiff(sb, item2.OldBody, item2.NewBody); } else { string text = ((item2.Kind == SourceDiffer.ChangeKind.Added) ? item2.NewBody : item2.OldBody); if (!string.IsNullOrEmpty(text)) { string text2 = ((item2.Kind == SourceDiffer.ChangeKind.Added) ? "+" : "-"); sb.AppendLine("```csharp"); string[] array = text.Split(new char[1] { '\n' }); foreach (string text3 in array) { sb.AppendLine(text2 + " " + text3); } sb.AppendLine("```"); } } } sb.AppendLine(); } } } private static void WriteLineDiff(StringBuilder sb, string oldBody, string newBody) { string[] array = oldBody.Split(new char[1] { '\n' }); string[] array2 = newBody.Split(new char[1] { '\n' }); int num = array.Length; int num2 = array2.Length; int[,] array3 = new int[num + 1, num2 + 1]; for (int num3 = num - 1; num3 >= 0; num3--) { for (int num4 = num2 - 1; num4 >= 0; num4--) { array3[num3, num4] = ((array[num3].Trim() == array2[num4].Trim()) ? (array3[num3 + 1, num4 + 1] + 1) : Math.Max(array3[num3 + 1, num4], array3[num3, num4 + 1])); } } sb.AppendLine("```diff"); int num5 = 0; int num6 = 0; while (num5 < num || num6 < num2) { if (num5 < num && num6 < num2 && array[num5].Trim() == array2[num6].Trim()) { sb.AppendLine(" " + array2[num6]); num5++; num6++; } else if (num6 < num2 && (num5 >= num || array3[num5, num6 + 1] >= array3[num5 + 1, num6])) { sb.AppendLine("+ " + array2[num6]); num6++; } else if (num5 < num) { sb.AppendLine("- " + array[num5]); num5++; } } sb.AppendLine("```"); } private static string KindSymbol(SourceDiffer.ChangeKind kind) { return kind switch { SourceDiffer.ChangeKind.Added => "+", SourceDiffer.ChangeKind.Removed => "-", _ => "~", }; } private static string FormatMember(SourceDiffer.Change change) { return change.Type.ToString().ToLower() + ": " + change.MemberName; } } } [CompilerGenerated] internal sealed class <b0c506eb-6090-4c98-a5fc-7e64dce1d243><PrivateImplementationDetails> { [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 6)] internal struct __StaticArrayInitTypeSize=6 { } internal static readonly __StaticArrayInitTypeSize=6 515E2C3AE1FA25341D4979E6610F37196C07BB55D83828D69D8FCD2331395EE0/* Not supported: data(7B 00 20 00 09 00) */; } 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 System.Diagnostics.CodeAnalysis { [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = true)] internal sealed class NotNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] internal sealed class NotNullWhenAttribute : Attribute { public bool ReturnValue { get; } public NotNullWhenAttribute(bool returnValue) { ReturnValue = returnValue; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = false)] internal sealed class MaybeNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, Inherited = false)] internal sealed class AllowNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] internal class DoesNotReturnIfAttribute : Attribute { public bool ParameterValue { get; } public DoesNotReturnIfAttribute(bool parameterValue) { ParameterValue = parameterValue; } } } namespace Newtonsoft.Json { internal enum ConstructorHandling { Default, AllowNonPublicDefaultConstructor } internal enum DateFormatHandling { IsoDateFormat, MicrosoftDateFormat } internal enum DateParseHandling { None, DateTime, DateTimeOffset } internal enum DateTimeZoneHandling { Local, Utc, Unspecified, RoundtripKind } internal class DefaultJsonNameTable : JsonNameTable { private class Entry { internal readonly string Value; internal readonly int HashCode; internal Entry Next; internal Entry(string value, int hashCode, Entry next) { Value = value; HashCode = hashCode; Next = next; } } private static readonly int HashCodeRandomizer; private int _count; private Entry[] _entries; private int _mask = 31; static DefaultJsonNameTable() { HashCodeRandomizer = Environment.TickCount; } public DefaultJsonNameTable() { _entries = new Entry[_mask + 1]; } public override string? Get(char[] key, int start, int length) { if (length == 0) { return string.Empty; } int num = length + HashCodeRandomizer; num += (num << 7) ^ key[start]; int num2 = start + length; for (int i = start + 1; i < num2; i++) { num += (num << 7) ^ key[i]; } num -= num >> 17; num -= num >> 11; num -= num >> 5; int num3 = Volatile.Read(ref _mask); int num4 = num & num3; for (Entry entry = _entries[num4]; entry != null; entry = entry.Next) { if (entry.HashCode == num && TextEquals(entry.Value, key, start, length)) { return entry.Value; } } return null; } public string Add(string key) { if (key == null) { throw new ArgumentNullException("key"); } int length = key.Length; if (length == 0) { return string.Empty; } int num = length + HashCodeRandomizer; for (int i = 0; i < key.Length; i++) { num += (num << 7) ^ key[i]; } num -= num >> 17; num -= num >> 11; num -= num >> 5; for (Entry entry = _entries[num & _mask]; entry != null; entry = entry.Next) { if (entry.HashCode == num && entry.Value.Equals(key, StringComparison.Ordinal)) { return entry.Value; } } return AddEntry(key, num); } private string AddEntry(string str, int hashCode) { int num = hashCode & _mask; Entry entry = new Entry(str, hashCode, _entries[num]); _entries[num] = entry; if (_count++ == _mask) { Grow(); } return entry.Value; } private void Grow() { Entry[] entries = _entries; int num = _mask * 2 + 1; Entry[] array = new Entry[num + 1]; for (int i = 0; i < entries.Length; i++) { Entry entry = entries[i]; while (entry != null) { int num2 = entry.HashCode & num; Entry next = entry.Next; entry.Next = array[num2]; array[num2] = entry; entry = next; } } _entries = array; Volatile.Write(ref _mask, num); } private static bool TextEquals(string str1, char[] str2, int str2Start, int str2Length) { if (str1.Length != str2Length) { return false; } for (int i = 0; i < str1.Length; i++) { if (str1[i] != str2[str2Start + i]) { return false; } } return true; } } [Flags] internal enum DefaultValueHandling { Include = 0, Ignore = 1, Populate = 2, IgnoreAndPopulate = 3 } internal enum FloatFormatHandling { String, Symbol, DefaultValue } internal enum FloatParseHandling { Double, Decimal } internal enum Formatting { None, Indented } internal interface IArrayPool<T> { T[] Rent(int minimumLength); void Return(T[]? array); } internal interface IJsonLineInfo { int LineNumber { get; } int LinePosition { get; } bool HasLineInfo(); } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] internal sealed class JsonArrayAttribute : JsonContainerAttribute { private bool _allowNullItems; public bool AllowNullItems { get { return _allowNullItems; } set { _allowNullItems = value; } } public JsonArrayAttribute() { } public JsonArrayAttribute(bool allowNullItems) { _allowNullItems = allowNullItems; } public JsonArrayAttribute(string id) : base(id) { } } [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false)] internal sealed class JsonConstructorAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] internal abstract class JsonContainerAttribute : Attribute { internal bool? _isReference; internal bool? _itemIsReference; internal ReferenceLoopHandling? _itemReferenceLoopHandling; internal TypeNameHandling? _itemTypeNameHandling; private Type? _namingStrategyType; private object[]? _namingStrategyParameters; public string? Id { get; set; } public string? Title { get; set; } public string? Description { get; set; } public Type? ItemConverterType { get; set; } public object[]? ItemConverterParameters { get; set; } public Type? NamingStrategyType { get { return _namingStrategyType; } set { _namingStrategyType = value; NamingStrategyInstance = null; } } public object[]? NamingStrategyParameters { get { return _namingStrategyParameters; } set { _namingStrategyParameters = value; NamingStrategyInstance = null; } } internal NamingStrategy? NamingStrategyInstance { get; set; } public bool IsReference { get { return _isReference.GetValueOrDefault(); } set { _isReference = value; } } public bool ItemIsReference { get { return _itemIsReference.GetValueOrDefault(); } set { _itemIsReference = value; } } public ReferenceLoopHandling ItemReferenceLoopHandling { get { return _itemReferenceLoopHandling.GetValueOrDefault(); } set { _itemReferenceLoopHandling = value; } } public TypeNameHandling ItemTypeNameHandling { get { return _itemTypeNameHandling.GetValueOrDefault(); } set { _itemTypeNameHandling = value; } } protected JsonContainerAttribute() { } protected JsonContainerAttribute(string id) { Id = id; } } internal static class JsonConvert { public static readonly string True = "true"; public static readonly string False = "false"; public static readonly string Null = "null"; public static readonly string Undefined = "undefined"; public static readonly string PositiveInfinity = "Infinity"; public static readonly string NegativeInfinity = "-Infinity"; public static readonly string NaN = "NaN"; public static Func<JsonSerializerSettings>? DefaultSettings { get; set; } public static string ToString(DateTime value) { return ToString(value, DateFormatHandling.IsoDateFormat, DateTimeZoneHandling.RoundtripKind); } public static string ToString(DateTime value, DateFormatHandling format, DateTimeZoneHandling timeZoneHandling) { DateTime value2 = DateTimeUtils.EnsureDateTime(value, timeZoneHandling); using StringWriter stringWriter = StringUtils.CreateStringWriter(64); stringWriter.Write('"'); DateTimeUtils.WriteDateTimeString(stringWriter, value2, format, null, CultureInfo.InvariantCulture); stringWriter.Write('"'); return stringWriter.ToString(); } public static string ToString(DateTimeOffset value) { return ToString(value, DateFormatHandling.IsoDateFormat); } public static string ToString(DateTimeOffset value, DateFormatHandling format) { using StringWriter stringWriter = StringUtils.CreateStringWriter(64); stringWriter.Write('"'); DateTimeUtils.WriteDateTimeOffsetString(stringWriter, value, format, null, CultureInfo.InvariantCulture); stringWriter.Write('"'); return stringWriter.ToString(); } public static string ToString(bool value) { if (!value) { return False; } return True; } public static string ToString(char value) { return ToString(char.ToString(value)); } public static string ToString(Enum value) { return value.ToString("D"); } public static string ToString(int value) { return value.ToString(null, CultureInfo.InvariantCulture); } public static string ToString(short value) { return value.ToString(null, CultureInfo.InvariantCulture); } [CLSCompliant(false)] public static string ToString(ushort value) { return value.ToString(null, CultureInfo.InvariantCulture); } [CLSCompliant(false)] public static string ToString(uint value) { return value.ToString(null, CultureInfo.InvariantCulture); } public static string ToString(long value) { return value.ToString(null, CultureInfo.InvariantCulture); } private static string ToStringInternal(BigInteger value) { return value.ToString(null, CultureInfo.InvariantCulture); } [CLSCompliant(false)] public static string ToString(ulong value) { return value.ToString(null, CultureInfo.InvariantCulture); } public static string ToString(float value) { return EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)); } internal static string ToString(float value, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable) { return EnsureFloatFormat(value, EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)), floatFormatHandling, quoteChar, nullable); } private static string EnsureFloatFormat(double value, string text, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable) { if (floatFormatHandling == FloatFormatHandling.Symbol || (!double.IsInfinity(value) && !double.IsNaN(value))) { return text; } if (floatFormatHandling == FloatFormatHandling.DefaultValue) { if (nullable) { return Null; } return "0.0"; } return quoteChar + text + quoteChar; } public static string ToString(double value) { return EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)); } internal static string ToString(double value, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable) { return EnsureFloatFormat(value, EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)), floatFormatHandling, quoteChar, nullable); } private static string EnsureDecimalPlace(double value, string text) { if (double.IsNaN(value) || double.IsInfinity(value) || StringUtils.IndexOf(text, '.') != -1 || StringUtils.IndexOf(text, 'E') != -1 || StringUtils.IndexOf(text, 'e') != -1) { return text; } return text + ".0"; } private static string EnsureDecimalPlace(string text) { if (StringUtils.IndexOf(text, '.') != -1) { return text; } return text + ".0"; } public static string ToString(byte value) { return value.ToString(null, CultureInfo.InvariantCulture); } [CLSCompliant(false)] public static string ToString(sbyte value) { return value.ToString(null, CultureInfo.InvariantCulture); } public static string ToString(decimal value) { return EnsureDecimalPlace(value.ToString(null, CultureInfo.InvariantCulture)); } public static string ToString(Guid value) { return ToString(value, '"'); } internal static string ToString(Guid value, char quoteChar) { string text = value.ToString("D", CultureInfo.InvariantCulture); string text2 = quoteChar.ToString(CultureInfo.InvariantCulture); return text2 + text + text2; } public static string ToString(TimeSpan value) { return ToString(value, '"'); } internal static string ToString(TimeSpan value, char quoteChar) { return ToString(value.ToString(), quoteChar); } public static string ToString(Uri? value) { if (value == null) { return Null; } return ToString(value, '"'); } internal static string ToString(Uri value, char quoteChar) { return ToString(value.OriginalString, quoteChar); } public static string ToString(string? value) { return ToString(value, '"'); } public static string ToString(string? value, char delimiter) { return ToString(value, delimiter, StringEscapeHandling.Default); } public static string ToString(string? value, char delimiter, StringEscapeHandling stringEscapeHandling) { if (delimiter != '"' && delimiter != '\'') { throw new ArgumentException("Delimiter must be a single or double quote.", "delimiter"); } return JavaScriptUtils.ToEscapedJavaScriptString(value, delimiter, appendDelimiters: true, stringEscapeHandling); } public static string ToString(object? value) { if (value == null) { return Null; } return ConvertUtils.GetTypeCode(value.GetType()) switch { PrimitiveTypeCode.String => ToString((string)value), PrimitiveTypeCode.Char => ToString((char)value), PrimitiveTypeCode.Boolean => ToString((bool)value), PrimitiveTypeCode.SByte => ToString((sbyte)value), PrimitiveTypeCode.Int16 => ToString((short)value), PrimitiveTypeCode.UInt16 => ToString((ushort)value), PrimitiveTypeCode.Int32 => ToString((int)value), PrimitiveTypeCode.Byte => ToString((byte)value), PrimitiveTypeCode.UInt32 => ToString((uint)value), PrimitiveTypeCode.Int64 => ToString((long)value), PrimitiveTypeCode.UInt64 => ToString((ulong)value), PrimitiveTypeCode.Single => ToString((float)value), PrimitiveTypeCode.Double => ToString((double)value), PrimitiveTypeCode.DateTime => ToString((DateTime)value), PrimitiveTypeCode.Decimal => ToString((decimal)value), PrimitiveTypeCode.DBNull => Null, PrimitiveTypeCode.DateTimeOffset => ToString((DateTimeOffset)value), PrimitiveTypeCode.Guid => ToString((Guid)value), PrimitiveTypeCode.Uri => ToString((Uri)value), PrimitiveTypeCode.TimeSpan => ToString((TimeSpan)value), PrimitiveTypeCode.BigInteger => ToStringInternal((BigInteger)value), _ => throw new ArgumentException("Unsupported type: {0}. Use the JsonSerializer class to get the object's JSON representation.".FormatWith(CultureInfo.InvariantCulture, value.GetType())), }; } [DebuggerStepThrough] public static string SerializeObject(object? value) { return SerializeObject(value, (Type?)null, (JsonSerializerSettings?)null); } [DebuggerStepThrough] public static string SerializeObject(object? value, Formatting formatting) { return SerializeObject(value, formatting, (JsonSerializerSettings?)null); } [DebuggerStepThrough] public static string SerializeObject(object? value, params JsonConverter[] converters) { JsonSerializerSettings settings = ((converters != null && converters.Length != 0) ? new JsonSerializerSettings { Converters = converters } : null); return SerializeObject(value, null, settings); } [DebuggerStepThrough] public static string SerializeObject(object? value, Formatting formatting, params JsonConverter[] converters) { JsonSerializerSettings settings = ((converters != null && converters.Length != 0) ? new JsonSerializerSettings { Converters = converters } : null); return SerializeObject(value, null, formatting, settings); } [DebuggerStepThrough] public static string SerializeObject(object? value, JsonSerializerSettings? settings) { return SerializeObject(value, null, settings); } [DebuggerStepThrough] public static string SerializeObject(object? value, Type? type, JsonSerializerSettings? settings) { JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); return SerializeObjectInternal(value, type, jsonSerializer); } [DebuggerStepThrough] public static string SerializeObject(object? value, Formatting formatting, JsonSerializerSettings? settings) { return SerializeObject(value, null, formatting, settings); } [DebuggerStepThrough] public static string SerializeObject(object? value, Type? type, Formatting formatting, JsonSerializerSettings? settings) { JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); jsonSerializer.Formatting = formatting; return SerializeObjectInternal(value, type, jsonSerializer); } private static string SerializeObjectInternal(object? value, Type? type, JsonSerializer jsonSerializer) { StringWriter stringWriter = new StringWriter(new StringBuilder(256), CultureInfo.InvariantCulture); using (JsonTextWriter jsonTextWriter = new JsonTextWriter(stringWriter)) { jsonTextWriter.Formatting = jsonSerializer.Formatting; jsonSerializer.Serialize(jsonTextWriter, value, type); } return stringWriter.ToString(); } [DebuggerStepThrough] public static object? DeserializeObject(string value) { return DeserializeObject(value, (Type?)null, (JsonSerializerSettings?)null); } [DebuggerStepThrough] public static object? DeserializeObject(string value, JsonSerializerSettings settings) { return DeserializeObject(value, null, settings); } [DebuggerStepThrough] public static object? DeserializeObject(string value, Type type) { return DeserializeObject(value, type, (JsonSerializerSettings?)null); } [DebuggerStepThrough] public static T? DeserializeObject<T>(string value) { return JsonConvert.DeserializeObject<T>(value, (JsonSerializerSettings?)null); } [DebuggerStepThrough] public static T? DeserializeAnonymousType<T>(string value, T anonymousTypeObject) { return DeserializeObject<T>(value); } [DebuggerStepThrough] public static T? DeserializeAnonymousType<T>(string value, T anonymousTypeObject, JsonSerializerSettings settings) { return DeserializeObject<T>(value, settings); } [DebuggerStepThrough] public static T? DeserializeObject<T>(string value, params JsonConverter[] converters) { return (T)DeserializeObject(value, typeof(T), converters); } [DebuggerStepThrough] public static T? DeserializeObject<T>(string value, JsonSerializerSettings? settings) { return (T)DeserializeObject(value, typeof(T), settings); } [DebuggerStepThrough] public static object? DeserializeObject(string value, Type type, params JsonConverter[] converters) { JsonSerializerSettings settings = ((converters != null && converters.Length != 0) ? new JsonSerializerSettings { Converters = converters } : null); return DeserializeObject(value, type, settings); } public static object? DeserializeObject(string value, Type? type, JsonSerializerSettings? settings) { ValidationUtils.ArgumentNotNull(value, "value"); JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); if (!jsonSerializer.IsCheckAdditionalContentSet()) { jsonSerializer.CheckAdditionalContent = true; } using JsonTextReader reader = new JsonTextReader(new StringReader(value)); return jsonSerializer.Deserialize(reader, type); } [DebuggerStepThrough] public static void PopulateObject(string value, object target) { PopulateObject(value, target, null); } public static void PopulateObject(string value, object target, JsonSerializerSettings? settings) { JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); using JsonReader jsonReader = new JsonTextReader(new StringReader(value)); jsonSerializer.Populate(jsonReader, target); if (settings == null || !settings.CheckAdditionalContent) { return; } while (jsonReader.Read()) { if (jsonReader.TokenType != JsonToken.Comment) { throw JsonSerializationException.Create(jsonReader, "Additional text found in JSON string after finishing deserializing object."); } } } public static string SerializeXmlNode(XmlNode? node) { return SerializeXmlNode(node, Formatting.None); } public static string SerializeXmlNode(XmlNode? node, Formatting formatting) { XmlNodeConverter xmlNodeConverter = new XmlNodeConverter(); return SerializeObject(node, formatting, xmlNodeConverter); } public static string SerializeXmlNode(XmlNode? node, Formatting formatting, bool omitRootObject) { XmlNodeConverter xmlNodeConverter = new XmlNodeConverter { OmitRootObject = omitRootObject }; return SerializeObject(node, formatting, xmlNodeConverter); } public static XmlDocument? DeserializeXmlNode(string value) { return DeserializeXmlNode(value, null); } public static XmlDocument? DeserializeXmlNode(string value, string? deserializeRootElementName) { return DeserializeXmlNode(value, deserializeRootElementName, writeArrayAttribute: false); } public static XmlDocument? DeserializeXmlNode(string value, string? deserializeRootElementName, bool writeArrayAttribute) { return DeserializeXmlNode(value, deserializeRootElementName, writeArrayAttribute, encodeSpecialCharacters: false); } public static XmlDocument? DeserializeXmlNode(string value, string? deserializeRootElementName, bool writeArrayAttribute, bool encodeSpecialCharacters) { XmlNodeConverter xmlNodeConverter = new XmlNodeConverter(); xmlNodeConverter.DeserializeRootElementName = deserializeRootElementName; xmlNodeConverter.WriteArrayAttribute = writeArrayAttribute; xmlNodeConverter.EncodeSpecialCharacters = encodeSpecialCharacters; return (XmlDocument)DeserializeObject(value, typeof(XmlDocument), xmlNodeConverter); } public static string SerializeXNode(XObject? node) { return SerializeXNode(node, Formatting.None); } public static string SerializeXNode(XObject? node, Formatting formatting) { return SerializeXNode(node, formatting, omitRootObject: false); } public static string SerializeXNode(XObject? node, Formatting formatting, bool omitRootObject) { XmlNodeConverter xmlNodeConverter = new XmlNodeConverter { OmitRootObject = omitRootObject }; return SerializeObject(node, formatting, xmlNodeConverter); } public static XDocument? DeserializeXNode(string value) { return DeserializeXNode(value, null); } public static XDocument? DeserializeXNode(string value, string? deserializeRootElementName) { return DeserializeXNode(value, deserializeRootElementName, writeArrayAttribute: false); } public static XDocument? DeserializeXNode(string value, string? deserializeRootElementName, bool writeArrayAttribute) { return DeserializeXNode(value, deserializeRootElementName, writeArrayAttribute, encodeSpecialCharacters: false); } public static XDocument? DeserializeXNode(string value, string? deserializeRootElementName, bool writeArrayAttribute, bool encodeSpecialCharacters) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown XmlNodeConverter xmlNodeConverter = new XmlNodeConverter(); xmlNodeConverter.DeserializeRootElementName = deserializeRootElementName; xmlNodeConverter.WriteArrayAttribute = writeArrayAttribute; xmlNodeConverter.EncodeSpecialCharacters = encodeSpecialCharacters; return (XDocument)DeserializeObject(value, typeof(XDocument), xmlNodeConverter); } } internal abstract class JsonConverter { public virtual bool CanRead => true; public virtual bool CanWrite => true; public abstract void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer); public abstract object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer); public abstract bool CanConvert(Type objectType); } internal abstract class JsonConverter<T> : JsonConverter { public sealed override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { if (!((value != null) ? (value is T) : ReflectionUtils.IsNullable(typeof(T)))) { throw new JsonSerializationException("Converter cannot write specified value to JSON. {0} is required.".FormatWith(CultureInfo.InvariantCulture, typeof(T))); } WriteJson(writer, (T)value, serializer); } public abstract void WriteJson(JsonWriter writer, T? value, JsonSerializer serializer); public sealed override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { bool flag = existingValue == null; if (!flag && !(existingValue is T)) { throw new JsonSerializationException("Converter cannot read JSON with the specified existing value. {0} is required.".FormatWith(CultureInfo.InvariantCulture, typeof(T))); } return ReadJson(reader, objectType, flag ? default(T) : ((T)existingValue), !flag, serializer); } public abstract T? ReadJson(JsonReader reader, Type objectType, T? existingValue, bool hasExistingValue, JsonSerializer serializer); public sealed override bool CanConvert(Type objectType) { return typeof(T).IsAssignableFrom(objectType); } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Parameter, AllowMultiple = false)] internal sealed class JsonConverterAttribute : Attribute { private readonly Type _converterType; public Type ConverterType => _converterType; public object[]? ConverterParameters { get; } public JsonConverterAttribute(Type converterType) { if (converterType == null) { throw new ArgumentNullException("converterType"); } _converterType = converterType; } public JsonConverterAttribute(Type converterType, params object[] converterParameters) : this(converterType) { ConverterParameters = converterParameters; } } internal class JsonConverterCollection : Collection<JsonConverter> { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] internal sealed class JsonDictionaryAttribute : JsonContainerAttribute { public JsonDictionaryAttribute() { } public JsonDictionaryAttribute(string id) : base(id) { } } [Serializable] internal class JsonException : Exception { public JsonException() { } public JsonException(string message) : base(message) { } public JsonException(string message, Exception? innerException) : base(message, innerException) { } public JsonException(SerializationInfo info, StreamingContext context) : base(info, context) { } internal static JsonException Create(IJsonLineInfo lineInfo, string path, string message) { message = JsonPosition.FormatMessage(lineInfo, path, message); return new JsonException(message); } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] internal class JsonExtensionDataAttribute : Attribute { public bool WriteData { get; set; } public bool ReadData { get; set; } public JsonExtensionDataAttribute() { WriteData = true; ReadData = true; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] internal sealed class JsonIgnoreAttribute : Attribute { } internal abstract class JsonNameTable { public abstract string? Get(char[] key, int start, int length); } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = false)] internal sealed class JsonObjectAttribute : JsonContainerAttribute { private MemberSerialization _memberSerialization; internal MissingMemberHandling? _missingMemberHandling; internal Required? _itemRequired; internal NullValueHandling? _itemNullValueHandling; public MemberSerialization MemberSerialization { get { return _memberSerialization; } set { _memberSerialization = value; } } public MissingMemberHandling MissingMemberHandling { get { return _missingMemberHandling.GetValueOrDefault(); } set { _missingMemberHandling = value; } } public NullValueHandling ItemNullValueHandling { get { return _itemNullValueHandling.GetValueOrDefault(); } set { _itemNullValueHandling = value; } } public Required ItemRequired { get { return _itemRequired.GetValueOrDefault(); } set { _itemRequired = value; } } public JsonObjectAttribute() { } public JsonObjectAttribute(MemberSerialization memberSerialization) { MemberSerialization = memberSerialization; } public JsonObjectAttribute(string id) : base(id) { } } internal enum JsonContainerType { None, Object, Array, Constructor } internal struct JsonPosition { private static readonly char[] SpecialCharacters = new char[18] { '.', ' ', '\'', '/', '"', '[', ']', '(', ')', '\t', '\n', '\r', '\f', '\b', '\\', '\u0085', '\u2028', '\u2029' }; internal JsonContainerType Type; internal int Position; internal string? PropertyName; internal bool HasIndex; public JsonPosition(JsonContainerType type) { Type = type; HasIndex = TypeHasIndex(type); Position = -1; PropertyName = null; } internal int CalculateLength() { switch (Type) { case JsonContainerType.Object: return PropertyName.Length + 5; case JsonContainerType.Array: case JsonContainerType.Constructor: return MathUtils.IntLength((ulong)Position) + 2; default: throw new ArgumentOutOfRangeException("Type"); } } internal void WriteTo(StringBuilder sb, ref StringWriter? writer, ref char[]? buffer) { switch (Type) { case JsonContainerType.Object: { string propertyName = PropertyName; if (propertyName.IndexOfAny(SpecialCharacters) != -1) { sb.Append("['"); if (writer == null) { writer = new StringWriter(sb); } JavaScriptUtils.WriteEscapedJavaScriptString(writer, propertyName, '\'', appendDelimiters: false, JavaScriptUtils.SingleQuoteCharEscapeFlags, StringEscapeHandling.Default, null, ref buffer); sb.Append("']"); } else { if (sb.Length > 0) { sb.Append('.'); } sb.Append(propertyName); } break; } case JsonContainerType.Array: case JsonContainerType.Constructor: sb.Append('['); sb.Append(Position); sb.Append(']'); break; } } internal static bool TypeHasIndex(JsonContainerType type) { if (type != JsonContainerType.Array) { return type == JsonContainerType.Constructor; } return true; } internal static string BuildPath(List<JsonPosition> positions, JsonPosition? currentPosition) { int num = 0; if (positions != null) { for (int i = 0; i < positions.Count; i++) { num += positions[i].CalculateLength(); } } if (currentPosition.HasValue) { num += currentPosition.GetValueOrDefault().CalculateLength(); } StringBuilder stringBuilder = new StringBuilder(num); StringWriter writer = null; char[] buffer = null; if (positions != null) { foreach (JsonPosition position in positions) { position.WriteTo(stringBuilder, ref writer, ref buffer); } } currentPosition?.WriteTo(stringBuilder, ref writer, ref buffer); return stringBuilder.ToString(); } internal static string FormatMessage(IJsonLineInfo? lineInfo, string path, string message) { if (!message.EndsWith(Environment.NewLine, StringComparison.Ordinal)) { message = message.Trim(); if (!StringUtils.EndsWith(message, '.')) { message += "."; } message += " "; } message += "Path '{0}'".FormatWith(CultureInfo.InvariantCulture, path); if (lineInfo != null && lineInfo.HasLineInfo()) { message += ", line {0}, position {1}".FormatWith(CultureInfo.InvariantCulture, lineInfo.LineNumber, lineInfo.LinePosition); } message += "."; return message; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] internal sealed class JsonPropertyAttribute : Attribute { internal NullValueHandling? _nullValueHandling; internal DefaultValueHandling? _defaultValueHandling; internal ReferenceLoopHandling? _referenceLoopHandling; internal ObjectCreationHandling? _objectCreationHandling; internal TypeNameHandling? _typeNameHandling; internal bool? _isReference; internal int? _order; internal Required? _required; internal bool? _itemIsReference; internal ReferenceLoopHandling? _itemReferenceLoopHandling; internal TypeNameHandling? _itemTypeNameHandling; public Type? ItemConverterType { get; set; } public object[]? ItemConverterParameters { get; set; } public Type? NamingStrategyType { get; set; } public object[]? NamingStrategyParameters { get; set; } public NullValueHandling NullValueHandling { get { return _nullValueHandling.GetValueOrDefault(); } set { _nullValueHandling = value; } } public DefaultValueHandling DefaultValueHandling { get { return _defaultValueHandling.GetValueOrDefault(); } set { _defaultValueHandling = value; } } public ReferenceLoopHandling ReferenceLoopHandling { get { return _referenceLoopHandling.GetValueOrDefault(); } set { _referenceLoopHandling = value; } } public ObjectCreationHandling ObjectCreationHandling { get { return _objectCreationHandling.GetValueOrDefault(); } set { _objectCreationHandling = value; } } public TypeNameHandling TypeNameHandling { get { return _typeNameHandling.GetValueOrDefault(); } set { _typeNameHandling = value; } } public bool IsReference { get { return _isReference.GetValueOrDefault(); } set { _isReference = value; } } public int Order { get { return _order.GetValueOrDefault(); } set { _order = value; } } public Required Required { get { return _required.GetValueOrDefault(); } set { _required = value; } } public string? PropertyName { get; set; } public ReferenceLoopHandling ItemReferenceLoopHandling { get { return _itemReferenceLoopHandling.GetValueOrDefault(); } set { _itemReferenceLoopHandling = value; } } public TypeNameHandling ItemTypeNameHandling { get { return _itemTypeNameHandling.GetValueOrDefault(); } set { _itemTypeNameHandling = value; } } public bool ItemIsReference { get { return _itemIsReference.GetValueOrDefault(); } set { _itemIsReference = value; } } public JsonPropertyAttribute() { } public JsonPropertyAttribute(string propertyName) { PropertyName = propertyName; } } internal abstract class JsonReader : IDisposable { protected internal enum State { Start, Complete, Property, ObjectStart, Object, ArrayStart, Array, Closed, PostValue, ConstructorStart, Constructor, Error, Finished } private JsonToken _tokenType; private object? _value; internal char _quoteChar; internal State _currentState; private JsonPosition _currentPosition; private CultureInfo? _culture; private DateTimeZoneHandling _dateTimeZoneHandling; private int? _maxDepth; private bool _hasExceededMaxDepth; internal DateParseHandling _dateParseHandling; internal FloatParseHandling _floatParseHandling; private string? _dateFormatString; private List<JsonPosition>? _stack; protected State CurrentState => _currentState; public bool CloseInput { get; set; } public bool SupportMultipleContent { get; set; } public virtual char QuoteChar { get { return _quoteChar; } protected internal set { _quoteChar = value; } } public DateTimeZoneHandling DateTimeZoneHandling { get { return _dateTimeZoneHandling; } set { if (value < DateTimeZoneHandling.Local || value > DateTimeZoneHandling.RoundtripKind) { throw new ArgumentOutOfRangeException("value"); } _dateTimeZoneHandling = value; } } public DateParseHandling DateParseHandling { get { return _dateParseHandling; } set { if (value < DateParseHandling.None || value > DateParseHandling.DateTimeOffset) { throw new ArgumentOutOfRangeException("value"); } _dateParseHandling = value; } } public FloatParseHandling FloatParseHandling { get { return _floatParseHandling; } set { if (value < FloatParseHandling.Double || value > FloatParseHandling.Decimal) { throw new ArgumentOutOfRangeException("value"); } _floatParseHandling = value; } } public string? DateFormatString { get { return _dateFormatString; } set { _dateFormatString = value; } } public int? MaxDepth { get { return _maxDepth; } set { if (value <= 0) { throw new ArgumentException("Value must be positive.", "value"); } _maxDepth = value; } } public virtual JsonToken TokenType => _tokenType; public virtual object? Value => _value; public virtual Type? ValueType => _value?.GetType(); public virtual int Depth { get { int num = _stack?.Count ?? 0; if (JsonTokenUtils.IsStartToken(TokenType) || _currentPosition.Type == JsonContainerType.None) { return num; } return num + 1; } } public virtual string Path { get { if (_currentPosition.Type == JsonContainerType.None) { return string.Empty; } JsonPosition? currentPosition = ((_currentState != State.ArrayStart && _currentState != State.ConstructorStart && _currentState != State.ObjectStart) ? new JsonPosition?(_currentPosition) : null); return JsonPosition.BuildPath(_stack, currentPosition); } } public CultureInfo Culture { get { return _culture ?? CultureInfo.InvariantCulture; } set { _culture = value; } } public virtual Task<bool> ReadAsync(CancellationToken cancellationToken = default(CancellationToken)) { return cancellationToken.CancelIfRequestedAsync<bool>() ?? Read().ToAsync(); } public async Task SkipAsync(CancellationToken cancellationToken = default(CancellationToken)) { if (TokenType == JsonToken.PropertyName) { await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } if (JsonTokenUtils.IsStartToken(TokenType)) { int depth = Depth; while (await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false) && depth < Depth) { } } } internal async Task ReaderReadAndAssertAsync(CancellationToken cancellationToken) { if (!(await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false))) { throw CreateUnexpectedEndException(); } } public virtual Task<bool?> ReadAsBooleanAsync(CancellationToken cancellationToken = default(CancellationToken)) { return cancellationToken.CancelIfRequestedAsync<bool?>() ?? Task.FromResult(ReadAsBoolean()); } public virtual Task<byte[]?> ReadAsBytesAsync(CancellationToken cancellationToken = default(CancellationToken)) { return cancellationToken.CancelIfRequestedAsync<byte[]>() ?? Task.FromResult(ReadAsBytes()); } internal async Task<byte[]?> ReadArrayIntoByteArrayAsync(CancellationToken cancellationToken) { List<byte> buffer = new List<byte>(); do { if (!(await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false))) { SetToken(JsonToken.None); } } while (!ReadArrayElementIntoByteArrayReportDone(buffer)); byte[] array = buffer.ToArray(); SetToken(JsonToken.Bytes, array, updateIndex: false); return array; } public virtual Task<DateTime?> ReadAsDateTimeAsync(CancellationToken cancellationToken = default(CancellationToken)) { return cancellationToken.CancelIfRequestedAsync<DateTime?>() ?? Task.FromResult(ReadAsDateTime()); } public virtual Task<DateTimeOffset?> ReadAsDateTimeOffsetAsync(CancellationToken cancellationToken = default(CancellationToken)) { return cancellationToken.CancelIfRequestedAsync<DateTimeOffset?>() ?? Task.FromResult(ReadAsDateTimeOffset()); } public virtual Task<decimal?> ReadAsDecimalAsync(CancellationToken cancellationToken = default(CancellationToken)) { return cancellationToken.CancelIfRequestedAsync<decimal?>() ?? Task.FromResult(ReadAsDecimal()); } public virtual Task<double?> ReadAsDoubleAsync(CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(ReadAsDouble()); } public virtual Task<int?> ReadAsInt32Async(CancellationToken cancellationToken = default(CancellationToken)) { return cancellationToken.CancelIfRequestedAsync<int?>() ?? Task.FromResult(ReadAsInt32()); } public virtual Task<string?> ReadAsStringAsync(CancellationToken cancellationToken = default(CancellationToken)) { return cancellationToken.CancelIfRequestedAsync<string>() ?? Task.FromResult(ReadAsString()); } internal async Task<bool> ReadAndMoveToContentAsync(CancellationToken cancellationToken) { bool flag = await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); if (flag) { flag = await MoveToContentAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } return flag; } internal Task<bool> MoveToContentAsync(CancellationToken cancellationToken) { JsonToken tokenType = TokenType; if (tokenType == JsonToken.None || tokenType == JsonToken.Comment) { return MoveToContentFromNonContentAsync(cancellationToken); } return AsyncUtils.True; } private async Task<bool> MoveToContentFromNonContentAsync(CancellationToken cancellationToken) { JsonToken tokenType; do { if (!(await ReadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false))) { return false; } tokenType = TokenType; } while (tokenType == JsonToken.None || tokenType == JsonToken.Comment); return true; } internal JsonPosition GetPosition(int depth) { if (_stack != null && depth < _stack.Count) { return _stack[depth]; } return _currentPosition; } protected JsonReader() { _currentState = State.Start; _dateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind; _dateParseHandling = DateParseHandling.DateTime; _floatParseHandling = FloatParseHandling.Double; _maxDepth = 64; CloseInput = true; } private void Push(JsonContainerType value) { UpdateScopeWithFinishedValue(); if (_currentPosition.Type == JsonContainerType.None) { _currentPosition = new JsonPosition(value); return; } if (_stack == null) { _stack = new List<JsonPosition>(); } _stack.Add(_currentPosition); _currentPosition = new JsonPosition(value); if (!_maxDepth.HasValue || !(Depth + 1 > _maxDepth) || _hasExceededMaxDepth) { return; } _hasExceededMaxDepth = true; throw JsonReaderException.Create(this, "The reader's MaxDepth of {0} has been exceeded.".FormatWith(CultureInfo.InvariantCulture, _maxDepth)); } private JsonContainerType Pop() { JsonPosition currentPosition; if (_stack != null && _stack.Count > 0) { currentPosition = _currentPosition; _currentPosition = _stack[_stack.Count - 1]; _stack.RemoveAt(_stack.Count - 1); } else { currentPosition = _currentPosition; _currentPosition = default(JsonPosition); } if (_maxDepth.HasValue && Depth <= _maxDepth) { _hasExceededMaxDepth = false; } return currentPosition.Type; } private JsonContainerType Peek() { return _currentPosition.Type; } public abstract bool Read(); public virtual int? ReadAsInt32() { JsonToken contentToken = GetContentToken(); switch (contentToken) { case JsonToken.None: case JsonToken.Null: case JsonToken.EndArray: return null; case JsonToken.Integer: case JsonToken.Float: { object value = Value; if (value is int) { return (int)value; } int num; if (value is BigInteger bigInteger) { num = (int)bigInteger; } else { try { num = Convert.ToInt32(value, CultureInfo.InvariantCulture); } catch (Exception ex) { throw JsonReaderException.Create(this, "Could not convert to integer: {0}.".FormatWith(CultureInfo.InvariantCulture, value), ex); } } SetToken(JsonToken.Integer, num, updateIndex: false); return num; } case JsonToken.String: { string s = (string)Value; return ReadInt32String(s); } default: throw JsonReaderException.Create(this, "Error reading integer. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken)); } } internal int? ReadInt32String(string? s) { if (StringUtils.IsNullOrEmpty(s)) { SetToken(JsonToken.Null, null, updateIndex: false); return null; } if (int.TryParse(s, NumberStyles.Integer, Culture, out var result)) { SetToken(JsonToken.Integer, result, updateIndex: false); return result; } SetToken(JsonToken.String, s, updateIndex: false); throw JsonReaderException.Create(this, "Could not convert string to integer: {0}.".FormatWith(CultureInfo.InvariantCulture, s)); } public virtual string? ReadAsString() { JsonToken contentToken = GetContentToken(); switch (contentToken) { case JsonToken.None: case JsonToken.Null: case JsonToken.EndArray: return null; case JsonToken.String: return (string)Value; default: if (JsonTokenUtils.IsPrimitiveToken(contentToken)) { object value = Value; if (value != null) { string text = ((!(value is IFormattable formattable)) ? ((value is Uri uri) ? uri.OriginalString : value.ToString()) : formattable.ToString(null, Culture)); SetToken(JsonToken.String, text, updateIndex: false); return text; } } throw JsonReaderException.Create(this, "Error reading string. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken)); } } public virtual byte[]? ReadAsBytes() { JsonToken contentToken = GetContentToken(); switch (contentToken) { case JsonToken.StartObject: { ReadIntoWrappedTypeObject(); byte[] array2 = ReadAsBytes(); ReaderReadAndAssert(); if (TokenType != JsonToken.EndObject) { throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); } SetToken(JsonToken.Bytes, array2, updateIndex: false); return array2; } case JsonToken.String: { string text = (string)Value; Guid g; byte[] array3 = ((text.Length == 0) ? CollectionUtils.ArrayEmpty<byte>() : ((!ConvertUtils.TryConvertGuid(text, out g)) ? Convert.FromBase64String(text) : g.ToByteArray())); SetToken(JsonToken.Bytes, array3, updateIndex: false); return array3; } case JsonToken.None: case JsonToken.Null: case JsonToken.EndArray: return null; case JsonToken.Bytes: if (Value is Guid guid) { byte[] array = guid.ToByteArray(); SetToken(JsonToken.Bytes, array, updateIndex: false); return array; } return (byte[])Value; case JsonToken.StartArray: return ReadArrayIntoByteArray(); default: throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken)); } } internal byte[] ReadArrayIntoByteArray() { List<byte> list = new List<byte>(); do { if (!Read()) { SetToken(JsonToken.None); } } while (!ReadArrayElementIntoByteArrayReportDone(list)); byte[] array = list.ToArray(); SetToken(JsonToken.Bytes, array, updateIndex: false); return array; } private bool ReadArrayElementIntoByteArrayReportDone(List<byte> buffer) { switch (TokenType) { case JsonToken.None: throw JsonReaderException.Create(this, "Unexpected end when reading bytes."); case JsonToken.Integer: buffer.Add(Convert.ToByte(Value, CultureInfo.InvariantCulture)); return false; case JsonToken.EndArray: return true; case JsonToken.Comment: return false; default: throw JsonReaderException.Create(this, "Unexpected token when reading bytes: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); } } public virtual double? ReadAsDouble() { JsonToken contentToken = GetContentToken(); switch (contentToken) { case JsonToken.None: case JsonToken.Null: case JsonToken.EndArray: return null; case JsonToken.Integer: case JsonToken.Float: { object value = Value; if (value is double) { return (double)value; } double num = ((!(value is BigInteger bigInteger)) ? Convert.ToDouble(value, CultureInfo.InvariantCulture) : ((double)bigInteger)); SetToken(JsonToken.Float, num, updateIndex: false); return num; } case JsonToken.String: return ReadDoubleString((string)Value); default: throw JsonReaderException.Create(this, "Error reading double. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken)); } } internal double? ReadDoubleString(string? s) { if (StringUtils.IsNullOrEmpty(s)) { SetToken(JsonToken.Null, null, updateIndex: false); return null; } if (double.TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, Culture, out var result)) { SetToken(JsonToken.Float, result, updateIndex: false); return result; } SetToken(JsonToken.String, s, updateIndex: false); throw JsonReaderException.Create(this, "Could not convert string to double: {0}.".FormatWith(CultureInfo.InvariantCulture, s)); } public virtual bool? ReadAsBoolean() { JsonToken contentToken = GetContentToken(); switch (contentToken) { case JsonToken.None: case JsonToken.Null: case JsonToken.EndArray: return null; case JsonToken.Integer: case JsonToken.Float: { bool flag = ((!(Value is BigInteger bigInteger)) ? Convert.ToBoolean(Value, CultureInfo.InvariantCulture) : (bigInteger != 0L)); SetToken(JsonToken.Boolean, flag, updateIndex: false); return flag; } case JsonToken.String: return ReadBooleanString((string)Value); case JsonToken.Boolean: return (bool)Value; default: throw JsonReaderException.Create(this, "Error reading boolean. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken)); } } internal bool? ReadBooleanString(string? s) { if (StringUtils.IsNullOrEmpty(s)) { SetToken(JsonToken.Null, null, updateIndex: false); return null; } if (bool.TryParse(s, out var result)) { SetToken(JsonToken.Boolean, result, updateIndex: false); return result; } SetToken(JsonToken.String, s, updateIndex: false); throw JsonReaderException.Create(this, "Could not convert string to boolean: {0}.".FormatWith(CultureInfo.InvariantCulture, s)); } public virtual decimal? ReadAsDecimal() { JsonToken contentToken = GetContentToken(); switch (contentToken) { case JsonToken.None: case JsonToken.Null: case JsonToken.EndArray: return null; case JsonToken.Integer: case JsonToken.Float: { object value = Value; if (value is decimal) { return (decimal)value; } decimal num; if (value is BigInteger bigInteger) { num = (decimal)bigInteger; } else { try { num = Convert.ToDecimal(value, CultureInfo.InvariantCulture); } catch (Exception ex) { throw JsonReaderException.Create(this, "Could not convert to decimal: {0}.".FormatWith(CultureInfo.InvariantCulture, value), ex); } } SetToken(JsonToken.Float, num, updateIndex: false); return num; } case JsonToken.String: return ReadDecimalString((string)Value); default: throw JsonReaderException.Create(this, "Error reading decimal. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken)); } } internal decimal? ReadDecimalString(string? s) { if (StringUtils.IsNullOrEmpty(s)) { SetToken(JsonToken.Null, null, updateIndex: false); return null; } if (decimal.TryParse(s, NumberStyles.Number, Culture, out var result)) { SetToken(JsonToken.Float, result, updateIndex: false); return result; } if (ConvertUtils.DecimalTryParse(s.ToCharArray(), 0, s.Length, out result) == ParseResult.Success) { SetToken(JsonToken.Float, result, updateIndex: false); return result; } SetToken(JsonToken.String, s, updateIndex: false); throw JsonReaderException.Create(this, "Could not convert string to decimal: {0}.".FormatWith(CultureInfo.InvariantCulture, s)); } public virtual DateTime? ReadAsDateTime() { switch (GetContentToken()) { case JsonToken.None: case JsonToken.Null: case JsonToken.EndArray: return null; case JsonToken.Date: if (Value is DateTimeOffset dateTimeOffset) { SetToken(JsonToken.Date, dateTimeOffset.DateTime, updateIndex: false); } return (DateTime)Value; case JsonToken.String: return ReadDateTimeString((string)Value); default: throw JsonReaderException.Create(this, "Error reading date. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); } } internal DateTime? ReadDateTimeString(string? s) { if (StringUtils.IsNullOrEmpty(s)) { SetToken(JsonToken.Null, null, updateIndex: false); return null; } if (DateTimeUtils.TryParseDateTime(s, DateTimeZoneHandling, _dateFormatString, Culture, out var dt)) { dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling); SetToken(JsonToken.Date, dt, updateIndex: false); return dt; } if (DateTime.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt)) { dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling); SetToken(JsonToken.Date, dt, updateIndex: false); return dt; } throw JsonReaderException.Create(this, "Could not convert string to DateTime: {0}.".FormatWith(CultureInfo.InvariantCulture, s)); } public virtual DateTimeOffset? ReadAsDateTimeOffset() { JsonToken contentToken = GetContentToken(); switch (contentToken) { case JsonToken.None: case JsonToken.Null: case JsonToken.EndArray: return null; case JsonToken.Date: if (Value is DateTime dateTime) { SetToken(JsonToken.Date, new DateTimeOffset(dateTime), updateIndex: false); } return (DateTimeOffset)Value; case JsonToken.String: { string s = (string)Value; return ReadDateTimeOffsetString(s); } default: throw JsonReaderException.Create(this, "Error reading date. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, contentToken)); } } internal DateTimeOffset? ReadDateTimeOffsetString(string? s) { if (StringUtils.IsNullOrEmpty(s)) { SetToken(JsonToken.Null, null, updateIndex: false); return null; } if (DateTimeUtils.TryParseDateTimeOffset(s, _dateFormatString, Culture, out var dt)) { SetToken(JsonToken.Date, dt, updateIndex: false); return dt; } if (DateTimeOffset.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt)) { SetToken(JsonToken.Date, dt, updateIndex: false); return dt; } SetToken(JsonToken.String, s, updateIndex: false); throw JsonReaderException.Create(this, "Could not convert string to DateTimeOffset: {0}.".FormatWith(CultureInfo.InvariantCulture, s)); } internal void ReaderReadAndAssert() { if (!Read()) { throw CreateUnexpectedEndException(); } } internal JsonReaderException CreateUnexpectedEndException() { return JsonReaderException.Create(this, "Unexpected end when reading JSON."); } internal void ReadIntoWrappedTypeObject() { ReaderReadAndAssert(); if (Value != null && Value.ToString() == "$type") { ReaderReadAndAssert(); if (Value != null && Value.ToString().StartsWith("System.Byte[]", StringComparison.Ordinal)) { ReaderReadAndAssert(); if (Value.ToString() == "$value") { return; } } } throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, JsonToken.StartObject)); } public void Skip() { if (TokenType == JsonToken.PropertyName) { Read(); } if (JsonTokenUtils.IsStartToken(TokenType)) { int depth = Depth; while (Read() && depth < Depth) { } } } protected void SetToken(JsonToken newToken) { SetToken(newToken, null, updateIndex: true); } protected void SetToken(JsonToken newToken, object? value) { SetToken(newToken, value, updateIndex: true); } protected void SetToken(JsonToken newToken, object? value, bool updateIndex) { _tokenType = newToken; _value = value; switch (newToken) { case JsonToken.StartObject: _currentState = State.ObjectStart; Push(JsonContainerType.Object); break; case JsonToken.StartArray: _currentState = State.ArrayStart; Push(JsonContainerType.Array); break; case JsonToken.StartConstructor: _currentState = State.ConstructorStart; Push(JsonContainerType.Constructor); break; case JsonToken.EndObject: ValidateEnd(JsonToken.EndObject); break; case JsonToken.EndArray: ValidateEnd(JsonToken.EndArray); break; case JsonToken.EndConstructor: ValidateEnd(JsonToken.EndConstructor); break; case JsonToken.PropertyName: _currentState = State.Property; _currentPosition.PropertyName = (string)value; break; case JsonToken.Raw: case JsonToken.Integer: case JsonToken.Float: case JsonToken.String: case JsonToken.Boolean: case JsonToken.Null: case JsonToken.Undefined: case JsonToken.Date: case JsonToken.Bytes: SetPostValueState(updateIndex); break; case JsonToken.Comment: break; } } internal void SetPostValueState(bool updateIndex) { if (Peek() != 0 || SupportMultipleContent) { _currentState = State.PostValue; } else { SetFinished(); } if (updateIndex) { UpdateScopeWithFinishedValue(); } } private void UpdateScopeWithFinishedValue() { if (_currentPosition.HasIndex) { _currentPosition.Position++; } } private void ValidateEnd(JsonToken endToken) { JsonContainerType jsonContainerType = Pop(); if (GetTypeForCloseToken(endToken) != jsonContainerType) { throw JsonReaderException.Create(this, "JsonToken {0} is not valid for closing JsonType {1}.".FormatWith(CultureInfo.InvariantCulture, endToken, jsonContainerType)); } if (Peek() != 0 || SupportMultipleContent) { _currentState = State.PostValue; } else { SetFinished(); } } protected void SetStateBasedOnCurrent() { JsonContainerType jsonContainerType = Peek(); switch (jsonContainerType) { case JsonContainerType.Object: _currentState = State.Object; break; case JsonContainerType.Array: _currentState = State.Array; break; case JsonContainerType.Constructor: _currentState = State.Constructor; break; case JsonContainerType.None: SetFinished(); break; default: throw JsonReaderException.Create(this, "While setting the reader state back to current object an unexpected JsonType was encountered: {0}".FormatWith(CultureInfo.InvariantCulture, jsonContainerType)); } } private void SetFinished() { _currentState = ((!SupportMultipleContent) ? State.Finished : State.Start); } private JsonContainerType GetTypeForCloseToken(JsonToken token) { return token switch { JsonToken.EndObject => JsonContainerType.Object, JsonToken.EndArray => JsonContainerType.Array, JsonToken.EndConstructor => JsonContainerType.Constructor, _ => throw JsonReaderException.Create(this, "Not a valid close JsonToken: {0}".FormatWith(CultureInfo.InvariantCulture, token)), }; } void IDisposable.Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_currentState != State.Closed && disposing) { Close(); } } public virtual void Close() { _currentState = State.Closed; _tokenType = JsonToken.None; _value = null; } internal void ReadAndAssert() { if (!Read()) { throw JsonSerializationException.Create(this, "Unexpected end when reading JSON."); } } internal void ReadForTypeAndAssert(JsonContract? contract, bool hasConverter) { if (!ReadForType(contract, hasConverter)) { throw JsonSerializationException.Create(this, "Unexpected end when reading JSON."); } } internal bool ReadForType(JsonContract? contract, bool hasConverter) { if (hasConverter) { return Read(); } switch (contract?.InternalReadType ?? ReadType.Read) { case ReadType.Read: return ReadAndMoveToContent(); case ReadType.ReadAsInt32: ReadAsInt32(); break; case ReadType.ReadAsInt64: { bool result = ReadAndMoveToContent(); if (TokenType == JsonToken.Undefined) { throw JsonReaderException.Create(this, "An undefined token is not a valid {0}.".FormatWith(CultureInfo.InvariantCulture, contract?.UnderlyingType ?? typeof(long))); } return result; } case ReadType.ReadAsDecimal: ReadAsDecimal(); break; case ReadType.ReadAsDouble: ReadAsDouble(); break; case ReadType.ReadAsBytes: ReadAsBytes(); break; case ReadType.ReadAsBoolean: ReadAsBoolean(); break; case ReadType.ReadAsString: ReadAsString(); break; case ReadType.ReadAsDateTime: ReadAsDateTime(); break; case ReadType.ReadAsDateTimeOffset: ReadAsDateTimeOffset(); break; default: throw new ArgumentOutOfRangeException(); } return TokenType != JsonToken.None; } internal bool ReadAndMoveToContent() { if (Read()) { return MoveToContent(); } return false; } internal bool MoveToContent() { JsonToken tokenType = TokenType; while (tokenType == JsonToken.None || tokenType == JsonToken.Comment) { if (!Read()) { return false; } tokenType = TokenType; } return true; } private JsonToken GetContentToken() { JsonToken tokenType; do { if (!Read()) { SetToken(JsonToken.None); return JsonToken.None; } tokenType = TokenType; } while (tokenType == JsonToken.Comment); return tokenType; } } [Serializable] internal class JsonReaderException : JsonException { public int LineNumber { get; } public int LinePosition { get; } public string? Path { get; } public JsonReaderException() { } public JsonReaderException(string message) : base(message) { } public JsonReaderException(string message, Exception innerException) : base(message, innerException) { } public JsonReaderException(SerializationInfo info, StreamingContext context) : base(info, context) { } public JsonReaderException(string message, string path, int lineNumber, int linePosition, Exception? innerException) : base(message, innerException) { Path = path; LineNumber = lineNumber; LinePosition = linePosition; } internal static JsonReaderException Create(JsonReader reader, string message) { return Create(reader, message, null); } internal static JsonReaderException Create(JsonReader reader, string message, Exception? ex) { return Create(reader as IJsonLineInfo, reader.Path, message, ex); } internal static JsonReaderException Create(IJsonLineInfo? lineInfo, string path, string message, Exception? ex) { message = JsonPosition.FormatMessage(lineInfo, path, message); int lineNumber; int linePosition; if (lineInfo != null && lineInfo.HasLineInfo()) { lineNumber = lineInfo.LineNumber; linePosition = lineInfo.LinePosition; } else { lineNumber = 0; linePosition = 0; } return new JsonReaderException(message, path, lineNumber, linePosition, ex); } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] internal sealed class JsonRequiredAttribute : Attribute { } [Serializable] internal class JsonSerializationException : JsonException { public int LineNumber { get; } public int LinePosition { get; } public string? Path { get; } public JsonSerializationException() { } public JsonSerializationException(string message) : base(message) { } public JsonSerializationException(string message, Exception innerException) : base(message, innerException) { } public JsonSerializationException(SerializationInfo info, StreamingContext context) : base(info, context) { } public JsonSerializationException(string message, string path, int lineNumber, int linePosition, Exception? innerException) : base(message, innerException) { Path = path; LineNumber = lineNumber; LinePosition = linePosition; } internal static JsonSerializationException Create