using System; using System.Buffers; using System.Buffers.Binary; using System.Buffers.Text; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.Tracing; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Net; using System.Numerics; using System.Numerics.Hashing; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.AccessControl; using System.Security.Cryptography; using System.Security.Principal; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FxResources.System.Buffers; using FxResources.System.Memory; using Glitnir.Ranking.Patches; using HarmonyLib; using JetBrains.Annotations; using LiteDB; using LiteDB.Engine; using LiteDB.Utils; using LiteDB.Utils.Extensions; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: Guid("72b7e2b4-c4d2-4b2f-91db-99d0311d97bc")] [assembly: ComVisible(false)] [assembly: AssemblyTrademark("")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyProduct("GlitnirRanking")] [assembly: AssemblyCompany("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyDescription("")] [assembly: AssemblyTitle("GlitnirRanking")] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace Glitnir.Ranking { internal static class GlitnirRankingServerSync { internal static readonly ConfigSync ConfigSync = new ConfigSync("com.glitnir.ranking") { DisplayName = "Glitnir Ranking", CurrentVersion = "0.7.66", MinimumRequiredVersion = "0.7.66" }; internal static ConfigEntry BindConfig(this ConfigFile config, string group, string name, T value, string description, bool synced = true) { ConfigEntry val = config.Bind(group, name, value, description); if (synced) { ConfigSync.AddConfigEntry(val); } return val; } } [BepInPlugin("com.glitnir.ranking", "Glitnir Ranking", "0.7.66")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class GlitnirRankingPlugin : BaseUnityPlugin { private enum DebugCategory { General, Hit, Kill, Skill, PendingKill, Snapshot, Points } private sealed class SnapshotTopEntryData { public int Position; public string PlayerName = ""; public int Points; public bool IsLocalPlayer; public int TotalKillsPontuadas; public int TotalBossesPontuadas; public int TotalSkillLevelUpsPontuados; public int TotalMarketplaceQuestsPontuadas; public int MarketplaceQuestPointsTotal; public int TotalFishingPontuadas; public int TotalCraftPontuadas; public int TotalFarmJackpotsPontuados; public int TotalUniqueCraftJackpotsPontuados; public int TotalDeaths; public int KillPointsTotal; public int BossPointsTotal; public int SkillPointsTotal; public int FishingPointsTotal; public int CraftPointsTotal; public int FarmJackpotPointsTotal; public int UniqueCraftJackpotPointsTotal; public int DeathPenaltyPointsTotal; public int TotalPointsExchanges; public int PointsExchangePenaltyTotal; public int PointsExchangeCoinsTotal; public int ExplorationMapJackpotPointsTotal; public string LastReason = ""; public string LastUpdateUtc = ""; } private sealed class SnapshotPlayerData { public bool HasData; public int Position; public string PlayerName = ""; public int Points; public int TotalKillsPontuadas; public int TotalBossesPontuadas; public int TotalSkillLevelUpsPontuados; public int TotalMarketplaceQuestsPontuadas; public int KillPointsTotal; public int BossPointsTotal; public int SkillPointsTotal; public int MarketplaceQuestPointsTotal; public int TotalFishingPontuadas; public int TotalCraftPontuadas; public int TotalFarmJackpotsPontuados; public int TotalUniqueCraftJackpotsPontuados; public int TotalDeaths; public int FishingPointsTotal; public int CraftPointsTotal; public int FarmJackpotPointsTotal; public int UniqueCraftJackpotPointsTotal; public int DeathPenaltyPointsTotal; public int TotalPointsExchanges; public int PointsExchangePenaltyTotal; public int PointsExchangeCoinsTotal; public int ExplorationMapJackpotPointsTotal; public string LastReason = ""; public string LastUpdateUtc = ""; public bool RankingEnabled; public bool EnableKillPoints; public bool EnableBossPoints; public bool EnableSkillPoints; public bool EnableMarketplaceQuestPoints; public int TopCount; public bool RewardClaimsEnabled; public bool RewardCanClaim; public bool RewardAlreadyClaimed; public int RewardRank; public int RewardMinPoints; public string RewardLabel = ""; public string RewardPrefabName = ""; public int RewardAmount; public string RewardBlockReason = ""; public string RewardClaimCycleId = ""; public string HudKillRules = ""; public string HudBossRules = ""; public string HudSkillJackpotRules = ""; public string HudMarketplaceQuestRules = ""; public string HudFishingRules = ""; public string HudCraftRules = ""; public string HudFarmJackpotRules = ""; public string HudUniqueCraftJackpotRules = ""; public string HudDeathPenaltyRules = ""; public bool EnableDeathPenalty; public bool DeathPenaltyUseMultiplier; public int DeathPenaltyPerDeath; public string HudExplorationMapJackpotRules = ""; public Dictionary ProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary FloatProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); } private sealed class PendingKillCheck { public string PrefabName = ""; public float FirstSeenTime; public float LastHitTime; } private sealed class RankRewardInfo { public int Rank; public int MinPoints; public string Label = ""; public string PrefabName = ""; public int Amount; } private sealed class RuleGuideGroup { public readonly string Name; public readonly List Rows = new List(); public RuleGuideGroup(string name) { Name = (string.IsNullOrWhiteSpace(name) ? "Outros" : name); } } private class SkillGuideGroup { public string Name; public List Milestones; public SkillGuideGroup(string name) { Name = (string.IsNullOrWhiteSpace(name) ? "Habilidade" : name); Milestones = new List(); } } private class SkillGuideMilestone { public string Level; public string Points; public SkillGuideMilestone(string level, string points) { Level = (string.IsNullOrWhiteSpace(level) ? "?" : level); Points = (string.IsNullOrWhiteSpace(points) ? "0" : points); } } [CompilerGenerated] private sealed class d__45 : IEnumerable, IEnumerable, IEnumerator, IDisposable, IEnumerator { private int <>1__state; private string <>2__current; private int <>l__initialThreadId; private Dictionary categories; public Dictionary <>3__categories; private string[] preferredOrder; public string[] <>3__preferredOrder; public GlitnirRankingPlugin <>4__this; private HashSet 5__1; private string[] <>s__2; private int <>s__3; private string 5__4; private string 5__5; private IEnumerator <>s__6; private string 5__7; private string 5__8; string IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__45(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 2) { try { } finally { <>m__Finally1(); } } 5__1 = null; <>s__2 = null; 5__4 = null; 5__5 = null; <>s__6 = null; 5__7 = null; 5__8 = null; <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__1 = new HashSet(StringComparer.OrdinalIgnoreCase); if (preferredOrder != null) { <>s__2 = preferredOrder; <>s__3 = 0; goto IL_0119; } goto IL_0134; case 1: <>1__state = -1; goto IL_00fc; case 2: { <>1__state = -3; goto IL_0200; } IL_0119: if (<>s__3 < <>s__2.Length) { 5__4 = <>s__2[<>s__3]; 5__5 = NormalizeHudCategoryName(5__4); if (string.IsNullOrWhiteSpace(5__5)) { goto IL_010b; } if (categories != null && categories.ContainsKey(5__5) && 5__1.Add(5__5)) { <>2__current = 5__5; <>1__state = 1; return true; } goto IL_00fc; } <>s__2 = null; goto IL_0134; IL_00fc: 5__5 = null; 5__4 = null; goto IL_010b; IL_010b: <>s__3++; goto IL_0119; IL_0134: if (categories == null) { return false; } <>s__6 = categories.Keys.OrderBy((string k) => k, StringComparer.OrdinalIgnoreCase).GetEnumerator(); <>1__state = -3; goto IL_020f; IL_0200: 5__8 = null; 5__7 = null; goto IL_020f; IL_020f: do { if (<>s__6.MoveNext()) { 5__7 = <>s__6.Current; 5__8 = NormalizeHudCategoryName(5__7); continue; } <>m__Finally1(); <>s__6 = null; return false; } while (string.IsNullOrWhiteSpace(5__8)); if (5__1.Add(5__8)) { <>2__current = 5__8; <>1__state = 2; return true; } goto IL_0200; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>s__6 != null) { <>s__6.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__45 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__45(0) { <>4__this = <>4__this }; } d__.categories = <>3__categories; d__.preferredOrder = <>3__preferredOrder; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [CompilerGenerated] private sealed class d__638 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public Recipe recipe; public string prefabName; public string sharedName; public int beforeCount; public GlitnirRankingPlugin <>4__this; private float 5__1; private int 5__2; private int 5__3; private int 5__4; private int 5__5; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__638(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__1 = Time.time; 5__2 = beforeCount; break; case 1: <>1__state = -1; if ((Object)(object)Player.m_localPlayer == (Object)null) { return false; } 5__3 = <>4__this.CountLocalInventoryItemsByPrefabOrSharedName(prefabName, sharedName); 5__2 = 5__3; 5__4 = 5__3 - beforeCount; if (5__4 <= 0) { break; } 5__5 = Mathf.Clamp(5__4, 1, 100); <>4__this.NotifyLocalCraftedItem(prefabName, 5__5); <>4__this.DebugLog(DebugCategory.Points, "Craft confirmado por inventário: item=" + prefabName + " antes=" + beforeCount + " depois=" + 5__3 + " ganho=" + 5__5); return false; } if (Time.time - 5__1 <= 12f) { <>2__current = (object)new WaitForSeconds(0.25f); <>1__state = 1; return true; } <>4__this.DebugLog(DebugCategory.Points, "Craft não pontuado: item não entrou no inventário após início do craft. item=" + prefabName + " antes=" + beforeCount + " ultimo=" + 5__2); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private readonly Dictionary> _cfgProductionCategoryRuleEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _cfgUniqueCraftJackpotCategoryEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); public const string ModGuid = "com.glitnir.ranking"; public const string ModName = "Glitnir Ranking"; public const string ModVersion = "0.7.66"; internal static GlitnirRankingPlugin Instance; internal static ManualLogSource Log; private const string RpcRequestSnapshot = "glitnir.ranking.requestsnapshot"; private const string RpcReceiveSnapshot = "glitnir.ranking.receivesnapshot"; private const string RpcReportHit = "glitnir.ranking.reporthit"; private const string RpcReportKill = "glitnir.ranking.reportkill"; private const string RpcReportSkillGain = "glitnir.ranking.reportskillgain"; private const string RpcRequestRewardClaim = "glitnir.ranking.requestrewardclaim"; private const string RpcGrantRewardItem = "glitnir.ranking.grantrewarditem"; private const string RpcFinalizeRewardClaim = "glitnir.ranking.finalizerewardclaim"; private const string RpcRewardClaimFeedback = "glitnir.ranking.rewardclaimfeedback"; private const string RpcRequestPointsExchange = "glitnir.ranking.requestpointsexchange"; private const string RpcGrantExchangeCoins = "glitnir.ranking.grantexchangecoins"; private const string RpcFinalizePointsExchange = "glitnir.ranking.finalizepointsexchange"; private const string RpcPointsExchangeFeedback = "glitnir.ranking.pointsexchangefeedback"; private const string RpcReportMarketplaceQuestComplete = "glitnir.ranking.marketquestcomplete"; private const string RpcReportFishCaught = "glitnir.ranking.reportfishcaught"; private const string RpcReportCraftedItem = "glitnir.ranking.reportcrafteditem"; private const string RpcReportFarmHarvest = "glitnir.ranking.reportfarmharvest"; private const string RpcReportPlayerDeath = "glitnir.ranking.reportplayerdeath"; private const string RpcReportExplorationMap = "glitnir.ranking.reportexplorationmap"; private const float DamageCreditLifetimeSeconds = 180f; private const float ProcessedKillLifetimeSeconds = 180f; private const float LocalRecentHitLifetimeSeconds = 15f; private const float ServerPendingKillDelaySeconds = 0.25f; private const float ServerPendingKillTimeoutSeconds = 12f; private const float ServerPendingKillCheckInterval = 0.25f; private const int MaxHudChars = 3000; private const int MaxPlayerNameLength = 32; private const int MaxReasonLength = 64; private const float ClientRefreshInterval = 1f; private readonly Dictionary _hudPrefabDisplayNameCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _hudPrefabObjectCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private int _hudObjectDbItemCountCached = -1; private int _hudZNetScenePrefabCountCached = -1; private Type _hudLocalizationType; private FieldInfo _hudLocalizationInstanceField; private PropertyInfo _hudLocalizationInstanceProperty; private MethodInfo _hudLocalizationLocalizeMethod; private bool _hudLocalizationReflectionReady; private Harmony _harmony; private bool _rpcsRegistered = false; private ZRoutedRpc _registeredRoutedRpcInstance; private readonly object _databaseSaveLock = new object(); private string _rulesFilePath; private string _uiFolderPath; private string _uiFilePath; private string _databaseFilePath; private string _legacyDatabaseFilePath; private ConfigFile _rulesConfig; private FileSystemWatcher _rulesConfigWatcher; private bool _rulesConfigReloadQueued; private float _rulesConfigReloadAt; private float _rulesConfigApplyAt; private const float RulesConfigApplyInterval = 1f; private ConfigEntry _cfgRankingEnabled; private ConfigEntry _cfgEnableKillPoints; private ConfigEntry _cfgEnableBossPoints; private ConfigEntry _cfgDefaultKillPoints; private ConfigEntry _cfgTopCount; private ConfigEntry _cfgAllowRepeatedBossPoints; private ConfigEntry _cfgDebugLogging; private ConfigEntry _cfgLogHitReports; private ConfigEntry _cfgLogKillReports; private ConfigEntry _cfgLogSkillReports; private ConfigEntry _cfgLogPendingKillReports; private ConfigEntry _cfgLogSnapshotRequests; private ConfigEntry _cfgLogPointsChanges; private ConfigEntry _cfgEnableSkillPoints; private ConfigEntry _cfgIgnoreTamedKills; private ConfigEntry _cfgEnableMarketplaceQuestPoints; private ConfigEntry _cfgEnableFishingPoints; private ConfigEntry _cfgEnableDeathPenalty; private ConfigEntry _cfgEnableExplorationJackpots; private ConfigEntry _cfgExplorationMapJackpotRules; private ConfigEntry _cfgDeathPenaltyRules; private ConfigEntry _cfgDeathPenaltyUseMultiplier; private ConfigEntry _cfgDeathPenaltyPerDeath; private readonly Dictionary> _cfgCombatBiomeRuleEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private ConfigEntry _cfgProductionCategoryRules; private ConfigEntry _cfgMarketplaceQuestPointMap; private ConfigEntry _cfgFarmJackpotRules; private ConfigEntry _cfgRewardClaimsEnabled; private ConfigEntry _cfgRewardClaimCycleId; private ConfigEntry _cfgRewardTop1MinPoints; private ConfigEntry _cfgRewardTop1Label; private ConfigEntry _cfgRewardTop1Prefab; private ConfigEntry _cfgRewardTop1Amount; private ConfigEntry _cfgRewardTop2MinPoints; private ConfigEntry _cfgRewardTop2Label; private ConfigEntry _cfgRewardTop2Prefab; private ConfigEntry _cfgRewardTop2Amount; private ConfigEntry _cfgRewardTop3MinPoints; private ConfigEntry _cfgRewardTop3Label; private ConfigEntry _cfgRewardTop3Prefab; private ConfigEntry _cfgRewardTop3Amount; private ConfigEntry _cfgPointsExchangeEnabled; private ConfigEntry _cfgPointsExchangePrefab; private ConfigEntry _cfgPointsExchangeUseCoinsPerPoint; private ConfigEntry _cfgPointsExchangeCoinsPerPoint; private ConfigEntry _cfgPointsExchangeUsePointsPerCoin; private ConfigEntry _cfgPointsExchangePointsPerCoin; private ConfigEntry _cfgPointsExchangeMinPoints; private ConfigEntry _cfgPointsExchangeMaxPointsPerRequest; private ConfigEntry _cfgDiscordTop3WebhookEnabled; private ConfigEntry _cfgDiscordTop3WebhookUrl; private ConfigEntry _cfgIgnoreAdminsInRanking; private ConfigEntry _cfgIgnoredAdminNames; private readonly Dictionary> _cfgKillPointEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _cfgBossPointEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _cfgFishingPointEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _cfgCraftPointEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _cfgFarmJackpotEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _cfgUniqueCraftJackpotEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _cfgSkillMilestoneJackpotPointEntries = new Dictionary>(StringComparer.OrdinalIgnoreCase); private RankingDatabase _database = new RankingDatabase(); private RankingRules _rules = new RankingRules(); private readonly Dictionary _marketplaceQuestPointsByUid = new Dictionary(); private readonly Dictionary> _damageCredits = new Dictionary>(StringComparer.Ordinal); private readonly Dictionary _processedKills = new Dictionary(StringComparer.Ordinal); private readonly Dictionary _ignoredTamedKills = new Dictionary(StringComparer.Ordinal); private readonly Dictionary _localRecentHits = new Dictionary(StringComparer.Ordinal); private readonly Dictionary _pendingKillChecks = new Dictionary(StringComparer.Ordinal); private float _serverPendingKillCheckAt; private bool _hudVisible; private float _hudOpenTime; private Rect _windowRect = new Rect(660f, 56f, 548f, 804f); private Rect _iconRect = new Rect(24f, 180f, 56f, 56f); private GameObject _rankingInputBlockerObject; private Canvas _rankingInputBlockerCanvas; private RectTransform _rankingInputBlockerRect; private Vector2 _rankingScrollPosition = Vector2.zero; private Vector2 _playerInfoScrollPosition = Vector2.zero; private Vector2 _rulesInfoScrollPosition = Vector2.zero; private Vector2 _actionsScrollPosition = Vector2.zero; private readonly HashSet _expandedActionPlayers = new HashSet(StringComparer.OrdinalIgnoreCase); private int _hudTabIndex = 0; private float _hudTabSwitchTime = 0f; private readonly HashSet _expandedRuleCategories = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly HashSet _expandedGuideSkillRows = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly HashSet _expandedRuleSubCategories = new HashSet(StringComparer.OrdinalIgnoreCase); private bool _ruleCategoriesInitialized; private readonly HashSet _expandedPerformanceCategories = new HashSet(StringComparer.OrdinalIgnoreCase); private bool _performanceCategoriesInitialized; private float _clientRefreshTimer; private float _explorationReportTimer; private float _lastReportedMapExplorePercent = -1f; private long _lastKnownServerPeerUid; private bool _requestedInitialServerSnapshot; private string _lastSnapshotSyncPlayerName = ""; private bool _lastSnapshotHadLocalPlayer; private string _cachedTopText = "Carregando ranking..."; private string _cachedPlayerText = "Aguardando dados do servidor..."; private string _statusText = "Sincronizando..."; private float _statusUntil = 0f; private bool _rewardClaimRequestPending; private string _rewardClaimPendingCycleId = ""; private int _rewardClaimPendingRank = 0; private readonly HashSet _pendingRewardClaims = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly HashSet _pendingPointsExchanges = new HashSet(StringComparer.OrdinalIgnoreCase); private bool _pointsExchangeRequestPending; private string _pointsExchangeAmountInput = ""; private bool _iconDragging; private Vector2 _iconDragOffset = Vector2.zero; private Vector2 _iconMouseDownPosition = Vector2.zero; private bool _iconMovedDuringDrag; private readonly List _cachedTopEntries = new List(); private SnapshotPlayerData _cachedPlayerData = new SnapshotPlayerData(); private GUIStyle _windowStyle; private GUIStyle _headerStyle; private GUIStyle _sectionTitleStyle; private GUIStyle _rankingTextStyle; private GUIStyle _playerTextStyle; private GUIStyle _footerStyle; private GUIStyle _statusStyle; private GUIStyle _iconButtonStyle; private GUIStyle _panelHeadingStyle; private GUIStyle _cardLabelStyle; private GUIStyle _cardValueStyle; private GUIStyle _bodyRowStyle; private GUIStyle _bodyValueStyle; private GUIStyle _mutedBodyStyle; private GUIStyle _rankIndexStyle; private GUIStyle _rankNameStyle; private GUIStyle _rankPointsStyle; private GUIStyle _youTagStyle; private GUIStyle _emptyStateStyle; private GUIStyle _configStyle; private bool _stylesReady; private string _uiTitleFontNames = "Cinzel Decorative|Cinzel|Trajan Pro|Palatino Linotype|Georgia|Times New Roman"; private string _uiBodyFontNames = "Cormorant Garamond|Cormorant SC|Palatino Linotype|Georgia|Garamond|Arial"; private string _uiAccentFontNames = "Cinzel|Palatino Linotype|Georgia|Arial"; private bool _uiUseSystemFonts = false; private KeyCode _uiToggleKey = (KeyCode)121; private float _uiIconZoom = 0.96f; private Font _uiTitleFont; private Font _uiBodyFont; private Font _uiAccentFont; private Texture2D _uiTransparentTexture; private Texture2D _uiWhiteTexture; private Texture2D _uiPanelTexture; private Texture2D _uiBackgroundTexture; private Texture2D _uiRankingIconTexture; private Texture2D _uiTitleRankingTexture; private Texture2D _uiTitleTopTexture; private Texture2D _uiTitleGuideTexture; private Texture2D _uiTitlePlayerTexture; private Texture2D _uiTitleActionsTexture; private readonly Dictionary _uiRuleCategoryIcons = new Dictionary(StringComparer.OrdinalIgnoreCase); private const float RuleCategoryIconSize = 24f; private Texture2D _uiRank1IconTexture; private Texture2D _uiRank2IconTexture; private Texture2D _uiRank3IconTexture; private bool _uiTexturesLoaded; private readonly HashSet _missingUiTextureWarnings = new HashSet(StringComparer.OrdinalIgnoreCase); private const string DefaultKillPointRules = "Boar:1"; private const string DefaultCombatCategoryRules = "Prados:Boar:1,Deer:1,Neck:1;Floresta Negra:Greydwarf:1,Greydwarf_Elite:2,Greydwarf_Shaman:2,Troll:5;Pântano:Draugr:2,Draugr_Elite:3,Blob:2,Abomination:8;Montanha:Wolf:2,Hatchling:2,StoneGolem:8;Planícies:Goblin:3,GoblinBrute:5,Lox:6,Deathsquito:3;Oceano:Serpent:6;Mistlands:Seeker:4,SeekerBrute:8,Gjall:10;Ashlands:Charred_Melee:4,Charred_Archer:4,Volture:4;Especiais:Haldor:0"; private const string DefaultProductionCategoryRules = "Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"; private static readonly Dictionary ManualPortuguesePrefabNames = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "deerstew", "Ensopado de cervo" }, { "honeysglazedchicken", "Frango glaceado com mel" }, { "mushroomjotun", "Cogumelo Jotun" }, { "helmetbronze", "Elmo de bronze" }, { "helmetcarapace", "Elmo de carapaça" }, { "helmetdrake", "Elmo de draco" }, { "helmetdverger", "Tiara Dvergr" }, { "helmetfenris", "Capuz de Fenris" }, { "helmetiron", "Elmo de ferro" }, { "helmetleather", "Elmo de couro" }, { "helmetmage", "Capuz de mago" }, { "helmetpadded", "Elmo acolchoado" }, { "helmetroot", "Máscara de raiz" }, { "helmettrollleather", "Capuz de couro de troll" }, { "helmetflametal", "Elmo de flametal" }, { "armorbronzechest", "Peitoral de bronze" }, { "armorbronzelegs", "Perneiras de bronze" }, { "armorcarapacechest", "Peitoral de carapaça" }, { "armorcarapacelegs", "Perneiras de carapaça" }, { "armorfenringchest", "Peitoral de Fenris" }, { "armorfenringlegs", "Perneiras de Fenris" }, { "armorironchest", "Peitoral de ferro" }, { "armorironlegs", "Perneiras de ferro" }, { "armorleatherchest", "Túnica de couro" }, { "armorleatherlegs", "Calças de couro" }, { "armormagechest", "Manto de mago" }, { "armormagelegs", "Calças de mago" }, { "armorpaddedcuirass", "Couraça acolchoada" }, { "armorpaddedgreaves", "Grevas acolchoadas" }, { "armorragschest", "Túnica de trapos" }, { "armorragslegs", "Calças de trapos" }, { "armorrootchest", "Armadura de raiz" }, { "armorrootlegs", "Perneiras de raiz" }, { "armortrollleatherchest", "Túnica de couro de troll" }, { "armortrollleatherlegs", "Calças de couro de troll" }, { "knifeblackmetal", "Faca de metal negro" }, { "knifecopper", "Faca de cobre" }, { "knifeflint", "Faca de sílex" }, { "knifesilver", "Faca de prata" }, { "swordiron", "Espada de ferro" }, { "swordsilver", "Espada de prata" }, { "swordblackmetal", "Espada de metal negro" }, { "swordbronze", "Espada de bronze" }, { "swordmistwalker", "Mistwalker" }, { "swordflametal", "Espada de flametal" }, { "axeiron", "Machado de ferro" }, { "axebronze", "Machado de bronze" }, { "axeblackmetal", "Machado de metal negro" }, { "maceiron", "Maça de ferro" }, { "macebronze", "Maça de bronze" }, { "macesilver", "Frostner" }, { "sledgeiron", "Marreta de ferro" }, { "sledge_demolisher", "Demolidor" }, { "bow", "Arco bruto" }, { "bowfinewood", "Arco de madeira fina" }, { "bowhuntsman", "Arco do caçador" }, { "bowdraugrfang", "Presa de Draugr" }, { "bowspine", "Estilhaçador de espinha" }, { "crossbowarbalest", "Arbalest" }, { "crossbowripper", "Ripper" }, { "shieldwood", "Escudo de madeira" }, { "shieldbronze buckler", "Broquel de bronze" }, { "shieldironbuckler", "Broquel de ferro" }, { "shieldblackmetal", "Escudo de metal negro" }, { "shieldcarapace", "Escudo de carapaça" }, { "shieldflametal", "Escudo de flametal" }, { "atgierbronze", "Atgeir de bronze" }, { "atgieriron", "Atgeir de ferro" }, { "atgierblackmetal", "Atgeir de metal negro" }, { "spearflint", "Lança de sílex" }, { "spearbronze", "Lança de bronze" }, { "spearwolffang", "Lança de presas" }, { "carrot", "Cenoura" }, { "turnip", "Nabo" }, { "onion", "Cebola" }, { "barley", "Cevada" }, { "flax", "Linho" }, { "sap", "Seiva" }, { "jotunpuffs", "Cogumelo Jotun" }, { "magecap", "Chapéu de Mago" }, { "vineberry", "Baga de Videira" } }; private static readonly Dictionary BossDisplayNamesPtBr = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "Eikthyr", "Eikthyr" }, { "gd_king", "O Ancião" }, { "Bonemass", "Massa Óssea" }, { "Dragon", "Moder" }, { "GoblinKing", "Yagluth" }, { "SeekerQueen", "Rainha Seeker" }, { "Fader", "Fader" }, { "Charred_Melee_Dyrnwyn", "Guerreiro Carbonizado Dyrnwyn" }, { "Fenring_Cultist_Hildir", "Cultista Fenring da Hildir" }, { "GoblinBruteBros", "Irmãos Berserker Fuling" }, { "GoblinBrute_Hildir", "Berserker Fuling da Hildir" }, { "GoblinShaman_Hildir", "Xamã Fuling da Hildir" }, { "Skeleton_Hildir", "Esqueleto da Hildir" } }; private static readonly Dictionary BossPortugueseToPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "Eikthyr", "Eikthyr" }, { "O Ancião", "gd_king" }, { "Ancião", "gd_king" }, { "O Anciao", "gd_king" }, { "Anciao", "gd_king" }, { "Massa Óssea", "Bonemass" }, { "Massa Ossea", "Bonemass" }, { "Bonemass", "Bonemass" }, { "Moder", "Dragon" }, { "Yagluth", "GoblinKing" }, { "Rainha Seeker", "SeekerQueen" }, { "Rainha dos Seeker", "SeekerQueen" }, { "Seeker Queen", "SeekerQueen" }, { "Fader", "Fader" }, { "Guerreiro Carbonizado Dyrnwyn", "Charred_Melee_Dyrnwyn" }, { "Dyrnwyn", "Charred_Melee_Dyrnwyn" }, { "Cultista Fenring da Hildir", "Fenring_Cultist_Hildir" }, { "Irmãos Berserker Fuling", "GoblinBruteBros" }, { "Irmaos Berserker Fuling", "GoblinBruteBros" }, { "Berserker Fuling da Hildir", "GoblinBrute_Hildir" }, { "Xamã Fuling da Hildir", "GoblinShaman_Hildir" }, { "Xama Fuling da Hildir", "GoblinShaman_Hildir" }, { "Esqueleto da Hildir", "Skeleton_Hildir" } }; private static readonly Dictionary FishDisplayNamesPtBr = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "Fish1", "Perca" }, { "Fish2", "Lúcio" }, { "Fish3", "Atum" }, { "Fish4_cave", "Tetra" }, { "Fish5", "Peixe Troll" }, { "Fish6", "Arenque gigante" }, { "Fish7", "Garoupa" }, { "Fish8", "Garoupa estrelada" }, { "Fish9", "Peixe-pescador" }, { "Fish10", "Salmão do norte" }, { "Fish11", "Peixe magma" }, { "Fish12", "Baiacu" } }; private static readonly Dictionary FishPortugueseToPrefab = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "Perca", "Fish1" }, { "Lúcio", "Fish2" }, { "Lucio", "Fish2" }, { "Fish3", "Fish3" }, { "Atum", "Fish3" }, { "Tetra", "Fish4_cave" }, { "Peixe Troll", "Fish5" }, { "Arenque gigante", "Fish6" }, { "Garoupa", "Fish7" }, { "Garoupa estrelada", "Fish8" }, { "Peixe-pescador", "Fish9" }, { "Peixe pescador", "Fish9" }, { "Salmão do norte", "Fish10" }, { "Salmao do norte", "Fish10" }, { "Peixe magma", "Fish11" }, { "Baiacu", "Fish12" } }; private static readonly Dictionary HudFriendlyNames = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "Boar", "Javali" }, { "Neck", "Nixe" }, { "Greyling", "Greydwarf Jovem" }, { "Greydwarf", "Greydwarf" }, { "Greydwarf_Elite", "Greydwarf Elite" }, { "Greydwarf_Shaman", "Xamã Greydwarf" }, { "Troll", "Troll" }, { "Skeleton", "Esqueleto" }, { "Skeleton_Poison", "Esqueleto Venenoso" }, { "Draugr", "Draugr" }, { "Draugr_Elite", "Draugr Elite" }, { "Blob", "Gosma" }, { "BlobElite", "Gosma Elite" }, { "Leech", "Sanguessuga" }, { "Surtling", "Surtling" }, { "Abomination", "Abominação" }, { "Wolf", "Lobo" }, { "Hatchling", "Draco" }, { "Fenring", "Fenring" }, { "StoneGolem", "Golem de Pedra" }, { "Goblin", "Fuling" }, { "GoblinArcher", "Fuling Arqueiro" }, { "GoblinBrute", "Fuling Berserker" }, { "GoblinShaman", "Xamã Fuling" }, { "Lox", "Lox" }, { "Deathsquito", "Mosquito da Morte" }, { "Serpent", "Serpente Marinha" }, { "Bat", "Morcego" }, { "Ulv", "Ulv" }, { "Hare", "Lebre" }, { "Tick", "Carrapato" }, { "Seeker", "Seeker" }, { "SeekerBrute", "Seeker Brutamontes" }, { "Gjall", "Gjall" }, { "Dverger", "Dvergr" }, { "Charred_Melee", "Carbonizado Guerreiro" }, { "Charred_Archer", "Carbonizado Arqueiro" }, { "Charred_Mage", "Carbonizado Mago" }, { "Morgen", "Morgen" }, { "Asksvin", "Asksvin" }, { "Volture", "Abutre das Cinzas" }, { "Eikthyr", "Eikthyr" }, { "gd_king", "O Ancião" }, { "Bonemass", "Massa Óssea" }, { "Dragon", "Moder" }, { "GoblinKing", "Yagluth" }, { "SeekerQueen", "Rainha Seeker" }, { "Fader", "Fader" }, { "Fish1", "Perca" }, { "Fish2", "Lúcio" }, { "Fish3", "Atum" }, { "Fish4_cave", "Tetra" }, { "Fish5", "Peixe Troll" }, { "Fish6", "Arenque gigante" }, { "Fish7", "Garoupa" }, { "Fish8", "Garoupa estrelada" }, { "Fish9", "Peixe-pescador" }, { "Fish10", "Salmão do norte" }, { "Fish11", "Peixe magma" }, { "Fish12", "Baiacu" }, { "Carrot", "Cenoura" }, { "Turnip", "Nabo" }, { "Onion", "Cebola" }, { "Barley", "Cevada" }, { "Flax", "Linho" }, { "Sap", "Seiva" }, { "JotunPuffs", "Cogumelo Jotun" }, { "Magecap", "Chapéu de Mago" }, { "Vineberry", "Baga de Videira" }, { "Fiddleheadfern", "Samambaia" }, { "Run", "Corrida" }, { "Jump", "Salto" }, { "Swords", "Espadas" }, { "Knives", "Facas" }, { "Clubs", "Porretes" }, { "Polearms", "Armas de Haste" }, { "Spears", "Lanças" }, { "Blocking", "Bloqueio" }, { "Axes", "Machados" }, { "Bows", "Arcos" }, { "Crossbows", "Bestas" }, { "ElementalMagic", "Magia Elemental" }, { "BloodMagic", "Magia de Sangue" }, { "Fishing", "Pesca" }, { "Cooking", "Culinária" }, { "Deer", "Cervo" }, { "EvilHeart_Forest", "Coração Maligno da Floresta" }, { "Ghost", "Fantasma" }, { "Wraith", "Aparição" }, { "Fenring_Cultist", "Cultista Fenring" }, { "SeekerBrood", "Cria de Seeker" }, { "DvergerMage", "Mago Dvergr" }, { "DvergerMageFire", "Mago Dvergr de Fogo" }, { "DvergerMageIce", "Mago Dvergr de Gelo" }, { "DvergerMageSupport", "Mago Dvergr de Suporte" }, { "DvergerRogue", "Ladino Dvergr" }, { "Charred_Twitcher", "Carbonizado Retorcido" }, { "Charred_Melee_Dyrnwyn", "Guerreiro Carbonizado Dyrnwyn" }, { "GoblinBruteBros", "Irmãos Berserker Fuling" }, { "GoblinBrute_Hildir", "Berserker Fuling da Hildir" }, { "GoblinShaman_Hildir", "Xamã Fuling da Hildir" }, { "Fenring_Cultist_Hildir", "Cultista Fenring da Hildir" }, { "Skeleton_Hildir", "Esqueleto da Hildir" }, { "ExploreBlackForest", "Explorador da Floresta Negra" }, { "ExploreSwamp", "Explorador do Pântano" }, { "ExploreMountain", "Explorador das Montanhas" }, { "ExplorePlains", "Explorador das Planícies" }, { "ExploreMistlands", "Explorador das Terras Nebulosas" }, { "ExploreAshlands", "Explorador das Terras das Cinzas" }, { "Kill100Enemies", "Exterminador" }, { "CraftMaster", "Mestre Artesão" }, { "Mapa 5%", "Mapa 5%" } }; private const float ActionPlayerCollapsedHeight = 58f; private const float ActionPlayerExpandedHeight = 738f; private const string HudColorPoints = "#00FF7F"; private const string HudColorDanger = "#FF5555"; private const string HudColorProgress = "#4FC3F7"; private const string HudColorHighlight = "#FFD700"; private const string HudColorMuted = "#D7DCE5"; private const string HudColorName = "#FFFFFF"; private const string HudColorSeparator = "#8F6E38"; private const float CraftRankingVerifyIntervalSeconds = 0.25f; private const float CraftRankingVerifyTimeoutSeconds = 12f; private readonly Dictionary _entriesByPlayerName = new Dictionary(StringComparer.OrdinalIgnoreCase); private List _orderedRankingCache = new List(); private Dictionary _rankingSnapshotCache = new Dictionary(StringComparer.OrdinalIgnoreCase); private bool _rankingCacheDirty = true; private readonly HashSet _marketplaceQuestCreditKeys = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly HashSet _fishingCatchCreditKeys = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _marketplaceQuestCreditsByPlayer = new Dictionary>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary> _fishingCreditsByPlayer = new Dictionary>(StringComparer.OrdinalIgnoreCase); private void EnsureRulesFileExists() { try { if (!File.Exists(_rulesFilePath)) { File.WriteAllText(_rulesFilePath, BuildDefaultRulesFileContents(), Encoding.UTF8); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao criar arquivo de regras: " + ex)); } } private string BuildDefaultRulesFileContents() { StringBuilder stringBuilder = new StringBuilder(16384); stringBuilder.AppendLine("# ======================================================================="); stringBuilder.AppendLine("# GLITNIR RANKING - CONFIGURACAO DE PONTUACAO"); stringBuilder.AppendLine("# ======================================================================="); stringBuilder.AppendLine("# Este arquivo controla as regras do ranking do servidor Glitnir."); stringBuilder.AppendLine("# Prefab = nome interno do item/criatura/colheita no Valheim."); stringBuilder.AppendLine("# Pontos = valor entregue ao jogador quando a regra for validada."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Organizacao principal:"); stringBuilder.AppendLine("# - KillPoints: pontos por criatura normal. Peixes NAO ficam aqui."); stringBuilder.AppendLine("# - FishingPoints: somente pontos por peixe realmente pescado."); stringBuilder.AppendLine("# - BossPoints: pontos por bosses e minibosses."); stringBuilder.AppendLine("# - CraftPoints.Rules: pontos por craft, em uma linha limpa separada por ponto e virgula."); stringBuilder.AppendLine("# - FarmJackpots.Rules: jackpots de colheita, em uma linha limpa separada por ponto e virgula."); stringBuilder.AppendLine("# - UniqueCraftJackpots.Rules: jackpots unicos por craft, em uma linha limpa separada por ponto e virgula."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Nao use virgulas nas listas de producao/jackpot. Use ;"); stringBuilder.AppendLine("# Exemplos:"); stringBuilder.AppendLine("# CraftPoints.Rules=MeadHealthMinor:20;ArrowNeedle:2"); stringBuilder.AppendLine("# FarmJackpots.Rules=Carrot:200:1000"); stringBuilder.AppendLine("# UniqueCraftJackpots.Rules=IronSword:1:800"); stringBuilder.AppendLine("# ======================================================================="); stringBuilder.AppendLine(); stringBuilder.AppendLine("RankingEnabled=true"); stringBuilder.AppendLine("EnableKillPoints=true"); stringBuilder.AppendLine("EnableBossPoints=true"); stringBuilder.AppendLine("DefaultKillPoints=1"); stringBuilder.AppendLine("TopCount=10"); stringBuilder.AppendLine("AllowRepeatedBossPoints=false"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Logging"); stringBuilder.AppendLine("DebugLogging=false"); stringBuilder.AppendLine("LogHitReports=false"); stringBuilder.AppendLine("LogKillReports=false"); stringBuilder.AppendLine("LogSkillReports=false"); stringBuilder.AppendLine("LogPendingKillReports=false"); stringBuilder.AppendLine("LogSnapshotRequests=false"); stringBuilder.AppendLine("LogPointsChanges=false"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Skill milestone jackpot points"); stringBuilder.AppendLine("EnableSkillPoints=true"); stringBuilder.AppendLine("IgnoreTamedKills=true"); stringBuilder.AppendLine("EnableMarketplaceQuestPoints=true"); stringBuilder.AppendLine("EnableFishingPoints=true"); stringBuilder.AppendLine("EnableDeathPenalty=true"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Marketplace quest points"); stringBuilder.AppendLine("[MarketplaceQuestPoints]"); stringBuilder.AppendLine("QuestPointMap=ccq_b21:450,ccq_b22:150"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Penalidade por morte"); stringBuilder.AppendLine("[DeathPenalty]"); stringBuilder.AppendLine("# UseMultiplierMode=false usa a tabela Rules abaixo."); stringBuilder.AppendLine("# UseMultiplierMode=true ignora Rules e usa PenaltyPerDeath por morte."); stringBuilder.AppendLine("# Exemplo multiplicador: PenaltyPerDeath=100 e 15 mortes = -1500."); stringBuilder.AppendLine("UseMultiplierMode=false"); stringBuilder.AppendLine("PenaltyPerDeath=100"); stringBuilder.AppendLine("# Formato do modo tabela: quantidadeDeMortes:pontosPerdidos,quantidadeDeMortes:pontosPerdidos"); stringBuilder.AppendLine("# Exemplo: 1:0,2:100,5:300,10:800"); stringBuilder.AppendLine("Rules=1:0,2:100,5:300,10:800"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Reward claims"); stringBuilder.AppendLine("RewardClaimsEnabled=true"); stringBuilder.AppendLine("RewardClaimCycleId=temporada_001"); stringBuilder.AppendLine("RewardTop1MinPoints=300"); stringBuilder.AppendLine("RewardTop1Label=Recompensa do 1 lugar"); stringBuilder.AppendLine("RewardTop1Prefab=Coins"); stringBuilder.AppendLine("RewardTop1Amount=500"); stringBuilder.AppendLine("RewardTop2MinPoints=200"); stringBuilder.AppendLine("RewardTop2Label=Recompensa do 2 lugar"); stringBuilder.AppendLine("RewardTop2Prefab=AmberPearl"); stringBuilder.AppendLine("RewardTop2Amount=10"); stringBuilder.AppendLine("RewardTop3MinPoints=100"); stringBuilder.AppendLine("RewardTop3Label=Recompensa do 3 lugar"); stringBuilder.AppendLine("RewardTop3Prefab=Ruby"); stringBuilder.AppendLine("RewardTop3Amount=5"); stringBuilder.AppendLine(); AppendBossPoints(stringBuilder); AppendKillPoints(stringBuilder); AppendFishingPoints(stringBuilder); AppendSkillMilestoneJackpotPoints(stringBuilder); AppendProductionRankingRules(stringBuilder); return stringBuilder.ToString(); } private void AppendBossPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# BOSS POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Cada boss/miniboss fica em uma chave própria."); sb.AppendLine("BossPoints.Bonemass=2200"); sb.AppendLine("BossPoints.Charred_Melee_Dyrnwyn=40"); sb.AppendLine("BossPoints.Dragon=3000"); sb.AppendLine("BossPoints.Eikthyr=1200"); sb.AppendLine("BossPoints.Fader=10000"); sb.AppendLine("BossPoints.Fenring_Cultist_Hildir=30"); sb.AppendLine("BossPoints.gd_king=4200"); sb.AppendLine("BossPoints.GoblinBruteBros=35"); sb.AppendLine("BossPoints.GoblinBrute_Hildir=18"); sb.AppendLine("BossPoints.GoblinKing=6000"); sb.AppendLine("BossPoints.GoblinShaman_Hildir=18"); sb.AppendLine("BossPoints.SeekerQueen=8600"); sb.AppendLine("BossPoints.Skeleton_Hildir=25"); sb.AppendLine(); } private void AppendPoint(StringBuilder sb, string section, string key, int points) { sb.AppendLine(section + "." + key + "=" + points); } private void AppendKillPoint(StringBuilder sb, string prefabName, int points, string comment = null) { } private void AppendKillPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# KILL POINTS / COMBATE"); sb.AppendLine("# ========================="); sb.AppendLine("# Pontos por criaturas normais. Peixes nao devem ficar aqui; use [FishingPoints]."); sb.AppendLine("# Formato gerado pelo BepInEx: [KillPoints] Prefab = pontos."); sb.AppendLine("# Mantido no modo performance: o mod NAO envia RPC por hit; pontua apenas kill confirmada."); sb.AppendLine("KillPoints.Boar=1"); sb.AppendLine("KillPoints.Neck=1"); sb.AppendLine("KillPoints.Greyling=1"); sb.AppendLine("KillPoints.Greydwarf=1"); sb.AppendLine("KillPoints.Greydwarf_Elite=2"); sb.AppendLine("KillPoints.Greydwarf_Shaman=2"); sb.AppendLine("KillPoints.Skeleton=1"); sb.AppendLine("KillPoints.Troll=5"); sb.AppendLine("KillPoints.Draugr=2"); sb.AppendLine("KillPoints.Draugr_Elite=3"); sb.AppendLine("KillPoints.Blob=2"); sb.AppendLine("KillPoints.Wolf=2"); sb.AppendLine("KillPoints.Hatchling=2"); sb.AppendLine("KillPoints.StoneGolem=8"); sb.AppendLine("KillPoints.Deathsquito=3"); sb.AppendLine("KillPoints.Goblin=3"); sb.AppendLine("KillPoints.GoblinBrute=5"); sb.AppendLine("KillPoints.GoblinShaman=4"); sb.AppendLine("KillPoints.Lox=6"); sb.AppendLine("KillPoints.Serpent=6"); sb.AppendLine("KillPoints.Seeker=4"); sb.AppendLine("KillPoints.SeekerBrute=8"); sb.AppendLine("KillPoints.Gjall=10"); sb.AppendLine("KillPoints.Charred_Melee=4"); sb.AppendLine("KillPoints.Charred_Archer=4"); sb.AppendLine("KillPoints.Morgen=12"); sb.AppendLine(); } private void AppendFishingPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# FISHING POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# As chaves podem ficar em português; o código converte para o prefab real internamente."); sb.AppendLine("# Mapeamento: Perca=Fish1, Lúcio=Fish2, Fish3=Fish3, Tetra=Fish4_cave, Peixe Troll=Fish5, Arenque gigante=Fish6, Garoupa=Fish7, Garoupa estrelada=Fish8, Peixe-pescador=Fish9, Salmão do norte=Fish10, Peixe magma=Fish11, Baiacu=Fish12"); AppendFishingPoint(sb, "Perca", 1); AppendFishingPoint(sb, "Lúcio", 1); AppendFishingPoint(sb, "Fish3", 2); AppendFishingPoint(sb, "Tetra", 3); AppendFishingPoint(sb, "Peixe Troll", 2); AppendFishingPoint(sb, "Arenque gigante", 3); AppendFishingPoint(sb, "Garoupa", 3000); AppendFishingPoint(sb, "Garoupa estrelada", 4); AppendFishingPoint(sb, "Peixe-pescador", 4); AppendFishingPoint(sb, "Salmão do norte", 5); AppendFishingPoint(sb, "Peixe magma", 5); AppendFishingPoint(sb, "Baiacu", 6); sb.AppendLine(); } private void AppendFishingPoint(StringBuilder sb, string prefabName, int points) { sb.AppendLine("FishingPoints." + prefabName + "=" + points); } private void AppendSkillPoint(StringBuilder sb, string skillKey, int points, string comment = null) { if (!string.IsNullOrWhiteSpace(comment)) { sb.AppendLine(comment); } sb.AppendLine("SkillPoints." + skillKey + "=" + points); } private void AppendSkillMilestoneJackpotPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# SKILL MILESTONE JACKPOT POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Pontos únicos por marco de skill. Formato: SkillMilestoneJackpotPoints.Skill=20:pontos,40:pontos,60:pontos,80:pontos,100:pontos"); sb.AppendLine("# Exemplo: Bows 20/40/60/80/100. Cada marco pontua uma única vez por jogador/ciclo."); AppendSkillMilestoneJackpotPoint(sb, "BloodMagic", "20:1500,40:3000,60:5000,80:7500,100:12000"); AppendSkillMilestoneJackpotPoint(sb, "ElementalMagic", "20:1000,40:2000,60:3500,80:5500,100:8000"); AppendSkillMilestoneJackpotPoint(sb, "Bows", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Crossbows", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Swords", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Axes", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Clubs", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Spears", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Polearms", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Knives", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Blocking", "20:400,40:800,60:1500,80:2500,100:3500"); AppendSkillMilestoneJackpotPoint(sb, "Run", "20:300,40:700,60:1200,80:2000,100:3000"); AppendSkillMilestoneJackpotPoint(sb, "Jump", "20:300,40:700,60:1200,80:2000,100:3000"); AppendSkillMilestoneJackpotPoint(sb, "WoodCutting", "20:300,40:700,60:1200,80:1800,100:2500"); AppendSkillMilestoneJackpotPoint(sb, "Pickaxes", "20:300,40:700,60:1200,80:1800,100:2500"); AppendSkillMilestoneJackpotPoint(sb, "Farming", "20:0,40:0,60:0,80:0,100:0"); sb.AppendLine(); } private void AppendSkillMilestoneJackpotPoint(StringBuilder sb, string skillKey, string milestones) { sb.AppendLine("SkillMilestoneJackpotPoints." + skillKey + "=" + milestones); } private void AppendProductionRankingRules(StringBuilder sb) { sb.AppendLine("# ======================================================================="); sb.AppendLine("# PRODUCAO E JACKPOTS - GLITNIR RANKING"); sb.AppendLine("# ======================================================================="); sb.AppendLine("# Estes blocos usam UMA entrada por sistema para manter o arquivo limpo."); sb.AppendLine("# Nao use virgulas. Separe cada regra com ponto e virgula (;)."); sb.AppendLine("# Formatos:"); sb.AppendLine("# CraftPoints.Rules = Prefab:pontos;OutroPrefab:pontos"); sb.AppendLine("# FarmJackpots.Rules = Prefab:quantidade:pontos;OutroPrefab:quantidade:pontos"); sb.AppendLine("# UniqueCraftJackpots.Rules = Prefab:quantidade:pontos;OutroPrefab:quantidade:pontos"); sb.AppendLine("# Exemplos reais:"); sb.AppendLine("# MeadHealthMinor:20"); sb.AppendLine("# Carrot:200:1000"); sb.AppendLine("# IronSword:1:800"); sb.AppendLine("# ======================================================================="); sb.AppendLine(); sb.AppendLine("# ========================="); sb.AppendLine("# CRAFT POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Pontos por craft de qualquer prefab configurado."); sb.AppendLine("# Exemplo de item: MeadHealthMinor:20"); sb.AppendLine("CraftPoints.Rules=MeadHealthMinor:20"); sb.AppendLine(); sb.AppendLine("# ========================="); sb.AppendLine("# FARM JACKPOTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Jackpot por colheita. Premia uma vez por jogador/ciclo quando bater a quantidade."); sb.AppendLine("# Exemplo de item: Carrot:200:1000"); sb.AppendLine("FarmJackpots.Rules=Carrot:200:1000"); sb.AppendLine(); sb.AppendLine("# ========================="); sb.AppendLine("# UNIQUE CRAFT JACKPOTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Jackpot unico por craft. Ideal para armas, armaduras e marcos especiais."); sb.AppendLine("# Exemplo de item: IronSword:1:800"); sb.AppendLine("UniqueCraftJackpots.Rules=IronSword:1:800"); sb.AppendLine(); } private void AppendStringPoint(StringBuilder sb, string section, string key, string value) { sb.AppendLine(section + "." + key + "=" + value); } private void LoadRules() { _rules = new RankingRules(); try { if (!File.Exists(_rulesFilePath)) { return; } string[] array = File.ReadAllLines(_rulesFilePath, Encoding.UTF8); foreach (string text in array) { string text2 = text.Trim(); if (string.IsNullOrWhiteSpace(text2) || text2.StartsWith("#") || text2.StartsWith(";") || text2.StartsWith("//")) { continue; } int num = text2.IndexOf('='); if (num <= 0) { continue; } string text3 = text2.Substring(0, num).Trim(); string text4 = text2.Substring(num + 1).Trim(); if (text3.Equals("RankingEnabled", StringComparison.OrdinalIgnoreCase)) { _rules.RankingEnabled = ParseBool(text4, _rules.RankingEnabled); } else if (text3.Equals("EnableKillPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableKillPoints = ParseBool(text4, _rules.EnableKillPoints); } else if (text3.Equals("EnableBossPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableBossPoints = ParseBool(text4, _rules.EnableBossPoints); } else if (text3.Equals("DefaultKillPoints", StringComparison.OrdinalIgnoreCase)) { _rules.DefaultKillPoints = ParseInt(text4, _rules.DefaultKillPoints); } else if (text3.Equals("TopCount", StringComparison.OrdinalIgnoreCase)) { _rules.TopCount = Mathf.Clamp(ParseInt(text4, _rules.TopCount), 1, 50); } else if (text3.Equals("AllowRepeatedBossPoints", StringComparison.OrdinalIgnoreCase)) { _rules.AllowRepeatedBossPoints = ParseBool(text4, _rules.AllowRepeatedBossPoints); } else if (text3.Equals("DebugLogging", StringComparison.OrdinalIgnoreCase)) { _rules.DebugLogging = ParseBool(text4, _rules.DebugLogging); } else if (text3.Equals("LogHitReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogHitReports = ParseBool(text4, _rules.LogHitReports); } else if (text3.Equals("LogKillReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogKillReports = ParseBool(text4, _rules.LogKillReports); } else if (text3.Equals("LogPendingKillReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogPendingKillReports = ParseBool(text4, _rules.LogPendingKillReports); } else if (text3.Equals("LogSnapshotRequests", StringComparison.OrdinalIgnoreCase)) { _rules.LogSnapshotRequests = ParseBool(text4, _rules.LogSnapshotRequests); } else if (text3.Equals("LogPointsChanges", StringComparison.OrdinalIgnoreCase)) { _rules.LogPointsChanges = ParseBool(text4, _rules.LogPointsChanges); } else if (text3.Equals("EnableSkillPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableSkillPoints = ParseBool(text4, _rules.EnableSkillPoints); } else if (text3.Equals("LogSkillReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogSkillReports = ParseBool(text4, _rules.LogSkillReports); } else if (text3.Equals("EnableFishingPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableFishingPoints = ParseBool(text4, _rules.EnableFishingPoints); } else if (text3.Equals("EnableDeathPenalty", StringComparison.OrdinalIgnoreCase)) { _rules.EnableDeathPenalty = ParseBool(text4, _rules.EnableDeathPenalty); } else if (text3.Equals("DeathPenalty.UseMultiplierMode", StringComparison.OrdinalIgnoreCase) || text3.Equals("DeathPenaltyUseMultiplier", StringComparison.OrdinalIgnoreCase) || text3.Equals("UseMultiplierMode", StringComparison.OrdinalIgnoreCase)) { _rules.DeathPenaltyUseMultiplier = ParseBool(text4, _rules.DeathPenaltyUseMultiplier); } else if (text3.Equals("DeathPenalty.PenaltyPerDeath", StringComparison.OrdinalIgnoreCase) || text3.Equals("DeathPenaltyPerDeath", StringComparison.OrdinalIgnoreCase) || text3.Equals("PenaltyPerDeath", StringComparison.OrdinalIgnoreCase)) { _rules.DeathPenaltyPerDeath = Mathf.Max(0, ParseInt(text4, _rules.DeathPenaltyPerDeath)); } else if (text3.Equals("DeathPenalty.Rules", StringComparison.OrdinalIgnoreCase) || text3.Equals("DeathPenaltyRules", StringComparison.OrdinalIgnoreCase) || text3.Equals("Rules", StringComparison.OrdinalIgnoreCase)) { _rules.DeathPenaltyRules = ParseDeathPenaltyRules(text4); } else if (text3.Equals("RewardClaimsEnabled", StringComparison.OrdinalIgnoreCase)) { _rules.RewardClaimsEnabled = ParseBool(text4, _rules.RewardClaimsEnabled); } else if (text3.Equals("RewardClaimCycleId", StringComparison.OrdinalIgnoreCase)) { _rules.RewardClaimCycleId = SafeLimit(text4, 64); } else if (text3.Equals("RewardTop1MinPoints", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1MinPoints = Mathf.Max(0, ParseInt(text4, _rules.RewardTop1MinPoints)); } else if (text3.Equals("RewardTop1Label", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1Label = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop1Prefab", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1Prefab = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop1Amount", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1Amount = Mathf.Max(0, ParseInt(text4, _rules.RewardTop1Amount)); } else if (text3.Equals("RewardTop2MinPoints", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2MinPoints = Mathf.Max(0, ParseInt(text4, _rules.RewardTop2MinPoints)); } else if (text3.Equals("RewardTop2Label", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2Label = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop2Prefab", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2Prefab = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop2Amount", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2Amount = Mathf.Max(0, ParseInt(text4, _rules.RewardTop2Amount)); } else if (text3.Equals("RewardTop3MinPoints", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3MinPoints = Mathf.Max(0, ParseInt(text4, _rules.RewardTop3MinPoints)); } else if (text3.Equals("RewardTop3Label", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3Label = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop3Prefab", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3Prefab = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop3Amount", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3Amount = Mathf.Max(0, ParseInt(text4, _rules.RewardTop3Amount)); } else if (text3.StartsWith("KillPoints.", StringComparison.OrdinalIgnoreCase)) { string text5 = SafeKey(text3.Substring("KillPoints.".Length)); if (!IsFishPrefab(text5)) { _rules.KillPoints[text5] = ParseInt(text4, 0); } } else if (text3.StartsWith("BossPoints.", StringComparison.OrdinalIgnoreCase)) { _rules.BossPoints[NormalizeBossPrefabName(text3.Substring("BossPoints.".Length))] = ParseInt(text4, 0); } else if (text3.StartsWith("FishingPoints.", StringComparison.OrdinalIgnoreCase)) { string text6 = NormalizeFishPrefabName(text3.Substring("FishingPoints.".Length)); if (IsFishPrefab(text6)) { _rules.FishingPoints[text6] = ParseInt(text4, 0); } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao carregar regras do ranking: " + ex)); } } private bool ParseBool(string raw, bool fallback) { bool result; return bool.TryParse(raw, out result) ? result : fallback; } private int ParseInt(string raw, int fallback) { int result; return int.TryParse(raw, out result) ? result : fallback; } private void InitializeSyncedRulesConfig() { try { string filePath = Path.Combine(Paths.ConfigPath, "glitnir.ranking.rules.cfg"); Dictionary dictionary = LoadLegacyRulesOverrides(filePath); MergeSectionOverrides(dictionary, LoadSectionRulesOverrides(filePath)); _rulesConfig = ((BaseUnityPlugin)this).Config; BindSyncedRulesConfigEntries(dictionary); ApplyRulesFromSyncedConfig(); _rulesConfig.Save(); CleanupFishKillPointConfigEntries(); RewriteRulesConfigHeaderAndGroupedSections(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao inicializar config sincronizada do ranking: " + ex)); EnsureRulesFileExists(); LoadRules(); } } private void SetupRulesConfigWatcher() { try { if (!string.IsNullOrWhiteSpace(_rulesFilePath)) { string directoryName = Path.GetDirectoryName(_rulesFilePath); string fileName = Path.GetFileName(_rulesFilePath); if (!string.IsNullOrWhiteSpace(directoryName) && !string.IsNullOrWhiteSpace(fileName)) { _rulesConfigWatcher = new FileSystemWatcher(directoryName, fileName); _rulesConfigWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime; _rulesConfigWatcher.Changed += OnRulesConfigFileChanged; _rulesConfigWatcher.Created += OnRulesConfigFileChanged; _rulesConfigWatcher.Renamed += OnRulesConfigFileChanged; _rulesConfigWatcher.EnableRaisingEvents = true; } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Não foi possível iniciar watcher da config do ranking: " + ex.Message)); } } private void OnRulesConfigFileChanged(object sender, FileSystemEventArgs args) { _rulesConfigReloadQueued = true; _rulesConfigReloadAt = Time.realtimeSinceStartup + 0.5f; } private void ProcessRulesConfigReloadIfNeeded() { if (!_rulesConfigReloadQueued || Time.realtimeSinceStartup < _rulesConfigReloadAt) { return; } _rulesConfigReloadQueued = false; try { ConfigFile rulesConfig = _rulesConfig; if (rulesConfig != null) { rulesConfig.Reload(); } BindSyncedRulesConfigEntries(null); ApplyRulesFromSyncedConfig(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao recarregar config do ranking: " + ex)); } } private void RefreshRulesFromSyncedConfigIfNeeded() { if (!(Time.realtimeSinceStartup < _rulesConfigApplyAt)) { _rulesConfigApplyAt = Time.realtimeSinceStartup + 1f; ApplyRulesFromSyncedConfig(); } } private Dictionary LoadLegacyRulesOverrides(string filePath) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); try { if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath)) { return dictionary; } bool flag = false; string[] array = File.ReadAllLines(filePath, Encoding.UTF8); foreach (string text in array) { string text2 = text.Trim(); if (text2.StartsWith("[") && text2.EndsWith("]")) { flag = true; break; } } if (flag) { return dictionary; } string[] array2 = File.ReadAllLines(filePath, Encoding.UTF8); foreach (string text3 in array2) { string text4 = text3.Trim(); if (string.IsNullOrWhiteSpace(text4) || text4.StartsWith("#") || text4.StartsWith(";") || text4.StartsWith("//")) { continue; } int num = text4.IndexOf('='); if (num > 0) { string text5 = text4.Substring(0, num).Trim(); string value = text4.Substring(num + 1).Trim(); if (!string.IsNullOrWhiteSpace(text5)) { dictionary[text5] = value; } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Falha ao ler overrides legados da config do ranking: " + ex.Message)); } return dictionary; } private void MergeSectionOverrides(Dictionary target, Dictionary source) { if (target == null || source == null) { return; } foreach (KeyValuePair item in source) { if (!string.IsNullOrWhiteSpace(item.Key)) { target[item.Key] = item.Value; } } } private Dictionary LoadSectionRulesOverrides(string filePath) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); try { if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath)) { return dictionary; } string text = string.Empty; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase) { "BossPoints", "FishingPoints", "KillPoints", "CraftPoints", "FarmJackpots", "UniqueCraftJackpots", "SkillMilestoneJackpotPoints", "DeathPenalty", "ExplorationMapJackpots" }; string[] array = File.ReadAllLines(filePath, Encoding.UTF8); foreach (string text2 in array) { string text3 = ((text2 != null) ? text2.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text3) || text3.StartsWith("#") || text3.StartsWith(";") || text3.StartsWith("//")) { continue; } if (text3.StartsWith("[") && text3.EndsWith("]")) { text = SafeKey(text3.Substring(1, text3.Length - 2)); continue; } int num = text3.IndexOf('='); if (num <= 0) { continue; } string text4 = text3.Substring(0, num).Trim(); string value = text3.Substring(num + 1).Trim(); if (string.IsNullOrWhiteSpace(text4)) { continue; } string text5 = text4; if (!text4.Contains(".") && !string.IsNullOrWhiteSpace(text)) { text5 = text + "." + text4; } int num2 = text5.IndexOf('.'); if (num2 > 0) { string text6 = text5.Substring(0, num2).Trim(); string text7 = SafeKey(text5.Substring(num2 + 1)); if (hashSet.Contains(text6) && !string.IsNullOrWhiteSpace(text7) && (!text6.Equals("KillPoints", StringComparison.OrdinalIgnoreCase) || !IsFishPrefab(text7))) { dictionary[text6 + "." + text7] = value; } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Falha ao ler entradas dinâmicas da config do ranking: " + ex.Message)); } return dictionary; } private bool IsLegacyRulesFileFormat(string filePath) { try { if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath)) { return false; } string[] array = File.ReadAllLines(filePath, Encoding.UTF8); foreach (string text in array) { string text2 = text.Trim(); if (!string.IsNullOrWhiteSpace(text2) && !text2.StartsWith("#") && !text2.StartsWith(";") && !text2.StartsWith("//")) { return !text2.StartsWith("[") || !text2.EndsWith("]"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Falha ao detectar formato legado da config do ranking: " + ex.Message)); } return false; } private void BindSyncedRulesConfigEntries(Dictionary legacyOverrides) { if (_rulesConfig != null) { legacyOverrides = legacyOverrides ?? new Dictionary(StringComparer.OrdinalIgnoreCase); RankingRules rankingRules = new RankingRules(); _cfgRankingEnabled = _rulesConfig.BindConfig("General", "RankingEnabled", ReadLegacyBool(legacyOverrides, "RankingEnabled", rankingRules.RankingEnabled), "Ativa o sistema de ranking."); _cfgEnableKillPoints = _rulesConfig.BindConfig("General", "EnableKillPoints", ReadLegacyBool(legacyOverrides, "EnableKillPoints", rankingRules.EnableKillPoints), "Ativa a pontuação por criaturas normais."); _cfgEnableBossPoints = _rulesConfig.BindConfig("General", "EnableBossPoints", ReadLegacyBool(legacyOverrides, "EnableBossPoints", rankingRules.EnableBossPoints), "Ativa a pontuação por bosses e minibosses."); _cfgDefaultKillPoints = _rulesConfig.BindConfig("General", "DefaultKillPoints", ReadLegacyInt(legacyOverrides, "DefaultKillPoints", rankingRules.DefaultKillPoints), "Pontos padrão para kills sem regra específica."); _cfgTopCount = _rulesConfig.BindConfig("General", "TopCount", ReadLegacyInt(legacyOverrides, "TopCount", rankingRules.TopCount), "Quantidade de jogadores exibidos no topo do ranking."); _cfgAllowRepeatedBossPoints = _rulesConfig.BindConfig("General", "AllowRepeatedBossPoints", ReadLegacyBool(legacyOverrides, "AllowRepeatedBossPoints", rankingRules.AllowRepeatedBossPoints), "Permite pontuar boss repetido."); _cfgEnableSkillPoints = _rulesConfig.BindConfig("General", "EnableSkillPoints", ReadLegacyBool(legacyOverrides, "EnableSkillPoints", rankingRules.EnableSkillPoints), "Ativa jackpots por marcos de skill."); _cfgIgnoreTamedKills = _rulesConfig.BindConfig("General", "IgnoreTamedKills", ReadLegacyBool(legacyOverrides, "IgnoreTamedKills", rankingRules.IgnoreTamedKills), "Ignora pontos por matar criaturas domadas."); _cfgEnableMarketplaceQuestPoints = _rulesConfig.BindConfig("General", "EnableMarketplaceQuestPoints", ReadLegacyBool(legacyOverrides, "EnableMarketplaceQuestPoints", rankingRules.EnableMarketplaceQuestPoints), "Ativa pontuação por conclusão de quests do Marketplace."); _cfgEnableFishingPoints = _rulesConfig.BindConfig("General", "EnableFishingPoints", ReadLegacyBool(legacyOverrides, "EnableFishingPoints", rankingRules.EnableFishingPoints), "Ativa pontuação própria por pesca."); _cfgEnableDeathPenalty = _rulesConfig.BindConfig("General", "EnableDeathPenalty", ReadLegacyBool(legacyOverrides, "EnableDeathPenalty", rankingRules.EnableDeathPenalty), "Ativa penalidade por morte no ranking."); _cfgDebugLogging = _rulesConfig.BindConfig("Logging", "DebugLogging", ReadLegacyBool(legacyOverrides, "DebugLogging", rankingRules.DebugLogging), "Ativa logs de depuração."); _cfgLogHitReports = _rulesConfig.BindConfig("Logging", "LogHitReports", ReadLegacyBool(legacyOverrides, "LogHitReports", rankingRules.LogHitReports), "Ativa logs de hit reports."); _cfgLogKillReports = _rulesConfig.BindConfig("Logging", "LogKillReports", ReadLegacyBool(legacyOverrides, "LogKillReports", rankingRules.LogKillReports), "Ativa logs de kill reports."); _cfgLogSkillReports = _rulesConfig.BindConfig("Logging", "LogSkillReports", ReadLegacyBool(legacyOverrides, "LogSkillReports", rankingRules.LogSkillReports), "Ativa logs de skill reports."); _cfgLogPendingKillReports = _rulesConfig.BindConfig("Logging", "LogPendingKillReports", ReadLegacyBool(legacyOverrides, "LogPendingKillReports", rankingRules.LogPendingKillReports), "Ativa logs das checagens pendentes de kill."); _cfgLogSnapshotRequests = _rulesConfig.BindConfig("Logging", "LogSnapshotRequests", ReadLegacyBool(legacyOverrides, "LogSnapshotRequests", rankingRules.LogSnapshotRequests), "Ativa logs de snapshots do ranking."); _cfgLogPointsChanges = _rulesConfig.BindConfig("Logging", "LogPointsChanges", ReadLegacyBool(legacyOverrides, "LogPointsChanges", rankingRules.LogPointsChanges), "Ativa logs de alterações de pontos."); _cfgRewardClaimsEnabled = _rulesConfig.BindConfig("Rewards", "RewardClaimsEnabled", ReadLegacyBool(legacyOverrides, "RewardClaimsEnabled", rankingRules.RewardClaimsEnabled), "Ativa o resgate de recompensas no HUD."); _cfgRewardClaimCycleId = _rulesConfig.BindConfig("Rewards", "RewardClaimCycleId", ReadLegacyString(legacyOverrides, "RewardClaimCycleId", rankingRules.RewardClaimCycleId), "Identificador do ciclo atual de recompensas."); _cfgRewardTop1MinPoints = _rulesConfig.BindConfig("Rewards", "RewardTop1MinPoints", ReadLegacyInt(legacyOverrides, "RewardTop1MinPoints", rankingRules.RewardTop1MinPoints), "Pontos mínimos para o Top 1 resgatar."); _cfgRewardTop1Label = _rulesConfig.BindConfig("Rewards", "RewardTop1Label", ReadLegacyString(legacyOverrides, "RewardTop1Label", rankingRules.RewardTop1Label), "Texto exibido para a recompensa do Top 1."); _cfgRewardTop1Prefab = _rulesConfig.BindConfig("Rewards", "RewardTop1Prefab", ReadLegacyString(legacyOverrides, "RewardTop1Prefab", rankingRules.RewardTop1Prefab), "Prefab do item entregue ao Top 1."); _cfgRewardTop1Amount = _rulesConfig.BindConfig("Rewards", "RewardTop1Amount", ReadLegacyInt(legacyOverrides, "RewardTop1Amount", rankingRules.RewardTop1Amount), "Quantidade do item entregue ao Top 1."); _cfgRewardTop2MinPoints = _rulesConfig.BindConfig("Rewards", "RewardTop2MinPoints", ReadLegacyInt(legacyOverrides, "RewardTop2MinPoints", rankingRules.RewardTop2MinPoints), "Pontos mínimos para o Top 2 resgatar."); _cfgRewardTop2Label = _rulesConfig.BindConfig("Rewards", "RewardTop2Label", ReadLegacyString(legacyOverrides, "RewardTop2Label", rankingRules.RewardTop2Label), "Texto exibido para a recompensa do Top 2."); _cfgRewardTop2Prefab = _rulesConfig.BindConfig("Rewards", "RewardTop2Prefab", ReadLegacyString(legacyOverrides, "RewardTop2Prefab", rankingRules.RewardTop2Prefab), "Prefab do item entregue ao Top 2."); _cfgRewardTop2Amount = _rulesConfig.BindConfig("Rewards", "RewardTop2Amount", ReadLegacyInt(legacyOverrides, "RewardTop2Amount", rankingRules.RewardTop2Amount), "Quantidade do item entregue ao Top 2."); _cfgRewardTop3MinPoints = _rulesConfig.BindConfig("Rewards", "RewardTop3MinPoints", ReadLegacyInt(legacyOverrides, "RewardTop3MinPoints", rankingRules.RewardTop3MinPoints), "Pontos mínimos para o Top 3 resgatar."); _cfgRewardTop3Label = _rulesConfig.BindConfig("Rewards", "RewardTop3Label", ReadLegacyString(legacyOverrides, "RewardTop3Label", rankingRules.RewardTop3Label), "Texto exibido para a recompensa do Top 3."); _cfgRewardTop3Prefab = _rulesConfig.BindConfig("Rewards", "RewardTop3Prefab", ReadLegacyString(legacyOverrides, "RewardTop3Prefab", rankingRules.RewardTop3Prefab), "Prefab do item entregue ao Top 3."); _cfgRewardTop3Amount = _rulesConfig.BindConfig("Rewards", "RewardTop3Amount", ReadLegacyInt(legacyOverrides, "RewardTop3Amount", rankingRules.RewardTop3Amount), "Quantidade do item entregue ao Top 3."); _cfgPointsExchangeEnabled = _rulesConfig.BindConfig("PointsExchange", "Enabled", ReadLegacyBool(legacyOverrides, "PointsExchangeEnabled", rankingRules.PointsExchangeEnabled), "Ativa o botão de câmbio de pontos por moedas no HUD."); _cfgPointsExchangePrefab = _rulesConfig.BindConfig("PointsExchange", "Prefab", ReadLegacyString(legacyOverrides, "PointsExchangePrefab", rankingRules.PointsExchangePrefab), "Prefab entregue no câmbio de pontos. Exemplo: Coins."); _cfgPointsExchangeUseCoinsPerPoint = _rulesConfig.BindConfig("PointsExchange", "UseCoinsPerPoint", ReadLegacyBool(legacyOverrides, "PointsExchangeUseCoinsPerPoint", rankingRules.PointsExchangeUseCoinsPerPoint), "Ativa o modo antigo: moedas por ponto. Por padrão fica false."); _cfgPointsExchangeCoinsPerPoint = _rulesConfig.BindConfig("PointsExchange", "CoinsPerPoint", ReadLegacyInt(legacyOverrides, "PointsExchangeCoinsPerPoint", rankingRules.PointsExchangeCoinsPerPoint), "Quantidade de moedas entregues por cada ponto trocado. Só é usado se UseCoinsPerPoint=true."); _cfgPointsExchangeUsePointsPerCoin = _rulesConfig.BindConfig("PointsExchange", "UsePointsPerCoin", ReadLegacyBool(legacyOverrides, "PointsExchangeUsePointsPerCoin", rankingRules.PointsExchangeUsePointsPerCoin), "Ativa o modo recomendado: pontos necessários para receber 1 moeda."); _cfgPointsExchangePointsPerCoin = _rulesConfig.BindConfig("PointsExchange", "PointsPerCoin", ReadLegacyInt(legacyOverrides, "PointsExchangePointsPerCoin", rankingRules.PointsExchangePointsPerCoin), "Quantidade de pontos necessários para receber 1 moeda. Exemplo: 100 = a cada 100 pontos, 1 coin."); _cfgPointsExchangeMinPoints = _rulesConfig.BindConfig("PointsExchange", "MinPoints", ReadLegacyInt(legacyOverrides, "PointsExchangeMinPoints", rankingRules.PointsExchangeMinPoints), "Valor legado. O câmbio não exige requisito mínimo; basta o jogador ter pontos disponíveis."); _cfgPointsExchangeMaxPointsPerRequest = _rulesConfig.BindConfig("PointsExchange", "MaxPointsPerRequest", ReadLegacyInt(legacyOverrides, "PointsExchangeMaxPointsPerRequest", rankingRules.PointsExchangeMaxPointsPerRequest), "Máximo de pontos convertidos por clique. Use 0 para trocar todos."); _cfgMarketplaceQuestPointMap = _rulesConfig.BindConfig("MarketplaceQuestPoints", "QuestPointMap", BuildMarketplaceQuestPointMapDefault(legacyOverrides), "Formato: questId:pontos separados por vírgula. Ex: ccq_b21:450,ccq_b22:150"); _cfgDeathPenaltyRules = _rulesConfig.BindConfig("DeathPenalty", "Rules", ReadLegacyString(legacyOverrides, "DeathPenalty.Rules", ReadLegacyString(legacyOverrides, "DeathPenaltyRules", "1:0,2:100,5:300,10:800")), "Penalidade por morte em marcos. Formato: mortes:pontosPerdidos,mortes:pontosPerdidos. Exemplo: 1:0,2:100,5:300,10:800"); _cfgDeathPenaltyUseMultiplier = _rulesConfig.BindConfig("DeathPenalty", "UseMultiplierMode", ReadLegacyBool(legacyOverrides, "DeathPenalty.UseMultiplierMode", ReadLegacyBool(legacyOverrides, "DeathPenaltyUseMultiplier", fallback: false)), "Se true, ignora a tabela Rules e aplica PenaltyPerDeath a cada morte. Exemplo: 15 mortes x 100 = -1500."); _cfgDeathPenaltyPerDeath = _rulesConfig.BindConfig("DeathPenalty", "PenaltyPerDeath", Mathf.Max(0, ReadLegacyInt(legacyOverrides, "DeathPenalty.PenaltyPerDeath", ReadLegacyInt(legacyOverrides, "DeathPenaltyPerDeath", 100))), "Valor perdido por cada morte quando UseMultiplierMode=true."); _cfgEnableExplorationJackpots = _rulesConfig.BindConfig("ExplorationMapJackpots", "Enabled", ReadLegacyBool(legacyOverrides, "ExplorationMapJackpots.Enabled", fallback: true), "Ativa jackpots por porcentagem de mapa revelado."); _cfgExplorationMapJackpotRules = _rulesConfig.BindConfig("ExplorationMapJackpots", "Rules", ReadLegacyString(legacyOverrides, "ExplorationMapJackpots.Rules", ReadLegacyString(legacyOverrides, "ExplorationMapJackpotRules", "5:25;10:50;25:150;50:400;75:800;100:1500")), "Jackpots por porcentagem do mapa revelado. Formato: porcentagem:pontos;porcentagem:pontos. Exemplo: 5:25;10:50;25:150"); BindCombatBiomeRuleEntries(legacyOverrides); BindProductionCategoryEntries(legacyOverrides); BindPointSectionEntries("BossPoints", "Pontos por bosses e minibosses.", legacyOverrides, _cfgBossPointEntries); BindPointSectionEntries("FishingPoints", "Pontos por peixe pescado.", legacyOverrides, _cfgFishingPointEntries); BindStringPointSectionEntries("SkillMilestoneJackpotPoints", "Pontos únicos por marco de skill. Formato: 20:500,40:1000,60:2000,80:3000,100:4000.", legacyOverrides, _cfgSkillMilestoneJackpotPointEntries); _cfgFarmJackpotRules = _rulesConfig.BindConfig("FarmJackpots", "Rules", ReadGroupedRulesDefault(legacyOverrides, "FarmJackpots", "Carrot:200:1000;Turnip:200:1200;Onion:200:1500;Barley:500:2000;Flax:500:2000"), "Jackpots de colheita. Edite somente esta linha. Formato: Prefab:quantidade:pontos;Prefab:quantidade:pontos. Exemplo: Carrot:200:1000;Barley:500:2000."); BindUniqueCraftJackpotCategoryEntries(legacyOverrides); } } private void BindUniqueCraftJackpotCategoryEntries(Dictionary legacyOverrides) { if (_cfgUniqueCraftJackpotCategoryEntries == null) { return; } _cfgUniqueCraftJackpotCategoryEntries.Clear(); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "Armas", "SwordIron;1;800,SwordSilver;1;1500,SwordBlackmetal;1;2500" }, { "Armaduras", "ArmorWolfChest;1;2000,ArmorCarapaceChest;1;3500" }, { "Outros", "" } }; string text = ReadLegacyString(legacyOverrides, "UniqueCraftJackpots.Rules", null); if (!string.IsNullOrWhiteSpace(text)) { foreach (KeyValuePair item in SplitUniqueCraftJackpotsIntoCategories(text)) { dictionary[item.Key] = MergeCommaRules(dictionary.ContainsKey(item.Key) ? dictionary[item.Key] : string.Empty, item.Value); } } if (legacyOverrides != null) { foreach (KeyValuePair legacyOverride in legacyOverrides) { if (string.IsNullOrWhiteSpace(legacyOverride.Key) || !legacyOverride.Key.StartsWith("UniqueCraftJackpots.", StringComparison.OrdinalIgnoreCase)) { continue; } string text2 = NormalizeHudCategoryName(legacyOverride.Key.Substring("UniqueCraftJackpots.".Length)); if (!string.IsNullOrWhiteSpace(text2) && !text2.Equals("Rules", StringComparison.OrdinalIgnoreCase)) { string value = ConvertUniqueCraftJackpotItemsToConfigValue(legacyOverride.Value); if (!string.IsNullOrWhiteSpace(value) || !dictionary.ContainsKey(text2)) { dictionary[text2] = value; } } } } if (!dictionary.ContainsKey("Outros")) { dictionary["Outros"] = string.Empty; } foreach (string item2 in GetPreferredCategoryOrder(dictionary, new string[5] { "Armas", "Armaduras", "Ferramentas", "Comidas", "Outros" })) { BindSingleUniqueCraftJackpotCategory(item2, legacyOverrides, dictionary[item2]); } } private void BindSingleUniqueCraftJackpotCategory(string category, Dictionary legacyOverrides, string defaultValue) { category = NormalizeHudCategoryName(category); if (!string.IsNullOrWhiteSpace(category)) { string raw = ReadLegacyString(legacyOverrides, "UniqueCraftJackpots." + category, defaultValue); raw = ConvertUniqueCraftJackpotItemsToConfigValue(raw); ConfigEntry value = _rulesConfig.BindConfig("UniqueCraftJackpots", category, raw, "Jackpot único por craft para esta categoria. Formato: Prefab;Quantidade;Pontos,OutroPrefab;Quantidade;Pontos. Exemplo: SwordIron;1;800,ArmorWolfChest;1;2000. Aceita qualquer prefab, inclusive de mods. O nome desta chave vira a categoria da config."); _cfgUniqueCraftJackpotCategoryEntries[category] = value; } } private Dictionary SplitUniqueCraftJackpotsIntoCategories(string raw) { Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase) { { "Armas", new List() }, { "Armaduras", new List() }, { "Outros", new List() } }; foreach (KeyValuePair item in ParseFlexibleJackpotRules(raw)) { string key = GuessUniqueCraftJackpotCategory(item.Key); if (!dictionary.ContainsKey(key)) { dictionary[key] = new List(); } JackpotRule value = item.Value; dictionary[key].Add(item.Key + ";" + Mathf.Max(1, value.RequiredAmount) + ";" + Mathf.Max(0, value.Points)); } Dictionary dictionary2 = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair> item2 in dictionary) { dictionary2[item2.Key] = string.Join(",", item2.Value.ToArray()); } return dictionary2; } private string GuessUniqueCraftJackpotCategory(string prefab) { string text = SafeKey(prefab); if (string.IsNullOrWhiteSpace(text)) { return "Outros"; } if (text.StartsWith("Sword", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Axe", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Mace", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Knife", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Atgeir", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Spear", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Bow", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Crossbow", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Shield", StringComparison.OrdinalIgnoreCase)) { return "Armas"; } if (text.StartsWith("Armor", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Helmet", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Cape", StringComparison.OrdinalIgnoreCase)) { return "Armaduras"; } return "Outros"; } private string MergeCommaRules(string current, string extra) { current = ((current != null) ? current.Trim() : string.Empty); extra = ((extra != null) ? extra.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(current)) { return extra; } if (string.IsNullOrWhiteSpace(extra)) { return current; } return current + "," + extra; } private string BuildUniqueCraftJackpotRulesFromCategoryEntries() { if (_cfgUniqueCraftJackpotCategoryEntries == null || _cfgUniqueCraftJackpotCategoryEntries.Count == 0) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair> cfgUniqueCraftJackpotCategoryEntry in _cfgUniqueCraftJackpotCategoryEntries) { if (cfgUniqueCraftJackpotCategoryEntry.Value == null) { continue; } foreach (KeyValuePair item in ParseFlexibleJackpotRules(cfgUniqueCraftJackpotCategoryEntry.Value.Value)) { if (stringBuilder.Length > 0) { stringBuilder.Append(';'); } JackpotRule value = item.Value; stringBuilder.Append(SafeKey(item.Key)); stringBuilder.Append(':'); stringBuilder.Append(Mathf.Max(1, value.RequiredAmount)); stringBuilder.Append(':'); stringBuilder.Append(Mathf.Max(0, value.Points)); } } return stringBuilder.ToString(); } private string ConvertUniqueCraftJackpotItemsToConfigValue(string raw) { List list = new List(); foreach (KeyValuePair item in ParseFlexibleJackpotRules(raw)) { JackpotRule value = item.Value; list.Add(SafeKey(item.Key) + ";" + Mathf.Max(1, value.RequiredAmount) + ";" + Mathf.Max(0, value.Points)); } return string.Join(",", list.ToArray()); } private Dictionary ParseFlexibleJackpotRules(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string text = raw.Replace("\r", "\n").Replace("|", ","); List list = new List(); if (text.IndexOf(',') >= 0 || text.IndexOf('\n') >= 0) { string[] array = text.Split(new char[2] { ',', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { string text3 = ((text2 != null) ? text2.Trim() : string.Empty); if (!string.IsNullOrWhiteSpace(text3)) { list.Add(text3); } } } else if (text.IndexOf(':') >= 0) { string[] array2 = text.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text4 in array2) { string text5 = ((text4 != null) ? text4.Trim() : string.Empty); if (!string.IsNullOrWhiteSpace(text5)) { list.Add(text5); } } } else { string[] array3 = text.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int k = 0; k + 2 < array3.Length; k += 3) { list.Add(array3[k].Trim() + ";" + array3[k + 1].Trim() + ";" + array3[k + 2].Trim()); } } foreach (string item in list) { if (TryParseFlexibleJackpotItem(item, out var prefab, out var amount, out var points)) { dictionary[prefab] = new JackpotRule { RequiredAmount = Mathf.Max(1, amount), Points = Mathf.Max(0, points) }; } } return dictionary; } private bool TryParseFlexibleJackpotItem(string entry, out string prefab, out int amount, out int points) { prefab = string.Empty; amount = 0; points = 0; if (string.IsNullOrWhiteSpace(entry)) { return false; } string[] array = ((entry.IndexOf(';') >= 0) ? entry.Split(new char[1] { ';' }, 3, StringSplitOptions.None) : entry.Split(new char[1] { ':' }, 3, StringSplitOptions.None)); if (array.Length != 3) { return false; } prefab = SafeKey(array[0]); if (string.IsNullOrWhiteSpace(prefab)) { return false; } if (!int.TryParse(array[1].Trim(), out amount)) { return false; } if (!int.TryParse(array[2].Trim(), out points)) { return false; } amount = Mathf.Max(1, amount); points = Mathf.Max(0, points); return true; } private void BindProductionCategoryEntries(Dictionary legacyOverrides) { if (_cfgProductionCategoryRuleEntries == null) { return; } _cfgProductionCategoryRuleEntries.Clear(); Dictionary dictionary = ParseProductionCategoryDefaults("Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"); string text = ReadLegacyString(legacyOverrides, "HudCategories.Production", null); if (!string.IsNullOrWhiteSpace(text)) { foreach (KeyValuePair item in ParseProductionCategoryDefaults(text)) { dictionary[item.Key] = item.Value; } } if (legacyOverrides != null) { foreach (KeyValuePair legacyOverride in legacyOverrides) { if (string.IsNullOrWhiteSpace(legacyOverride.Key) || !legacyOverride.Key.StartsWith("HudCategories.", StringComparison.OrdinalIgnoreCase)) { continue; } string text2 = NormalizeHudCategoryName(legacyOverride.Key.Substring("HudCategories.".Length)); if (!string.IsNullOrWhiteSpace(text2) && !text2.Equals("Production", StringComparison.OrdinalIgnoreCase) && !text2.Equals("Combat", StringComparison.OrdinalIgnoreCase)) { string value = ConvertProductionCategoryItemsToConfigValue(legacyOverride.Value); if (!string.IsNullOrWhiteSpace(value)) { dictionary[text2] = value; } } } } if (dictionary.Count <= 0) { dictionary["Armas"] = "SwordIron;800"; dictionary["Armaduras"] = "HelmetBronze;800"; dictionary["Comidas"] = "DeerStew;25"; } if (!dictionary.ContainsKey("Outros")) { dictionary["Outros"] = string.Empty; } foreach (string item2 in GetPreferredCategoryOrder(dictionary, new string[6] { "Armas", "Armaduras", "Comidas", "Ferramentas", "Materiais", "Outros" })) { BindSingleProductionCategory(item2, legacyOverrides, dictionary[item2]); } } private void BindSingleProductionCategory(string category, Dictionary legacyOverrides, string defaultValue) { category = NormalizeHudCategoryName(category); if (!string.IsNullOrWhiteSpace(category)) { string raw = ReadLegacyString(legacyOverrides, "HudCategories." + category, defaultValue); raw = ConvertProductionCategoryItemsToConfigValue(raw); ConfigEntry val = _rulesConfig.BindConfig("HudCategories", category, raw, "Produção/Conquistas para esta aba do HUD. Formato: Prefab;Pontos,OutroPrefab;Pontos. Exemplo: SwordIron;800,HelmetBronze;800,DeerStew;25. Aceita prefabs de mods. O nome desta chave vira a categoria no HUD."); _cfgProductionCategoryRuleEntries[category] = val; if (_cfgProductionCategoryRules == null) { _cfgProductionCategoryRules = val; } } } private Dictionary ParseProductionCategoryDefaults(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[3] { ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text2)) { continue; } int num = text2.IndexOf(':'); if (num <= 0 || num >= text2.Length - 1) { continue; } string text3 = NormalizeHudCategoryName(text2.Substring(0, num).Trim()); string text4 = text2.Substring(num + 1).Trim(); if (!string.IsNullOrWhiteSpace(text3) && !string.IsNullOrWhiteSpace(text4)) { string value = ConvertProductionCategoryItemsToConfigValue(text4); if (!string.IsNullOrWhiteSpace(value)) { dictionary[text3] = value; } } } return dictionary; } private string BuildProductionCategoryRules() { if (_cfgProductionCategoryRuleEntries == null || _cfgProductionCategoryRuleEntries.Count == 0) { return "Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"; } try { List list = new List(); foreach (KeyValuePair> cfgProductionCategoryRuleEntry in _cfgProductionCategoryRuleEntries) { if (cfgProductionCategoryRuleEntry.Value == null) { continue; } string text = NormalizeHudCategoryName(cfgProductionCategoryRuleEntry.Key); if (!string.IsNullOrWhiteSpace(text)) { string text2 = ConvertProductionCategoryItemsToHudCategoryItems(cfgProductionCategoryRuleEntry.Value.Value); if (!string.IsNullOrWhiteSpace(text2)) { list.Add(text + ":" + text2); } } } if (list.Count <= 0) { return "Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"; } return string.Join(";", list.ToArray()); } catch { return "Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"; } } private string ConvertProductionCategoryItemsToConfigValue(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return string.Empty; } List list = new List(); string[] array = raw.Split(new char[4] { ',', '|', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string itemRaw in array2) { if (TryParseCombatBiomeItem(itemRaw, out var prefab, out var points)) { list.Add(prefab + ";" + Mathf.Max(0, points)); } } return string.Join(",", list.ToArray()); } private string ConvertProductionCategoryItemsToHudCategoryItems(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return string.Empty; } List list = new List(); string[] array = raw.Split(new char[4] { ',', '|', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string itemRaw in array2) { if (TryParseCombatBiomeItem(itemRaw, out var prefab, out var points)) { list.Add(prefab + "=" + Mathf.Max(0, points)); } } return string.Join(",", list.ToArray()); } private void BindCombatBiomeRuleEntries(Dictionary legacyOverrides) { if (_cfgCombatBiomeRuleEntries == null) { return; } _cfgCombatBiomeRuleEntries.Clear(); Dictionary dictionary = ParseCombatBiomeDefaults("Prados:Boar:1,Deer:1,Neck:1;Floresta Negra:Greydwarf:1,Greydwarf_Elite:2,Greydwarf_Shaman:2,Troll:5;Pântano:Draugr:2,Draugr_Elite:3,Blob:2,Abomination:8;Montanha:Wolf:2,Hatchling:2,StoneGolem:8;Planícies:Goblin:3,GoblinBrute:5,Lox:6,Deathsquito:3;Oceano:Serpent:6;Mistlands:Seeker:4,SeekerBrute:8,Gjall:10;Ashlands:Charred_Melee:4,Charred_Archer:4,Volture:4;Especiais:Haldor:0"); if (!dictionary.ContainsKey("Outros")) { dictionary["Outros"] = string.Empty; } if (legacyOverrides != null) { foreach (KeyValuePair legacyOverride in legacyOverrides) { if (!string.IsNullOrWhiteSpace(legacyOverride.Key) && legacyOverride.Key.StartsWith("Combat.", StringComparison.OrdinalIgnoreCase)) { string text = NormalizeHudCategoryName(legacyOverride.Key.Substring("Combat.".Length)); if (!string.IsNullOrWhiteSpace(text)) { string value = ConvertProductionCategoryItemsToConfigValue(legacyOverride.Value); dictionary[text] = value; } } } } foreach (string item in GetPreferredCategoryOrder(dictionary, new string[10] { "Prados", "Floresta Negra", "Pântano", "Montanha", "Planícies", "Oceano", "Mistlands", "Ashlands", "Especiais", "Outros" })) { BindSingleCombatBiome(item, dictionary, legacyOverrides); } } private void BindSingleCombatBiome(string category, Dictionary defaults, Dictionary legacyOverrides) { category = NormalizeHudCategoryName(category); if (!string.IsNullOrWhiteSpace(category)) { if (defaults == null || !defaults.TryGetValue(category, out var value)) { value = string.Empty; } string raw = ReadLegacyString(legacyOverrides, "Combat." + category, value); raw = ConvertProductionCategoryItemsToConfigValue(raw); _cfgCombatBiomeRuleEntries[category] = _rulesConfig.BindConfig("Combat", category, raw, "Mobs e pontos para esta aba do HUD. Formato: Prefab;Pontos,OutroPrefab;Pontos. Exemplo: Boar;2,Deer;3. Aceita qualquer quantidade de prefabs, inclusive de mods. O nome desta chave vira a categoria no HUD."); } } [IteratorStateMachine(typeof(d__45))] private IEnumerable GetPreferredCategoryOrder(Dictionary categories, string[] preferredOrder) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__45(-2) { <>4__this = this, <>3__categories = categories, <>3__preferredOrder = preferredOrder }; } private Dictionary ParseCombatBiomeDefaults(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[3] { ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text2)) { continue; } int num = text2.IndexOf(':'); if (num <= 0 || num >= text2.Length - 1) { continue; } string text3 = NormalizeHudCategoryName(text2.Substring(0, num).Trim()); string text4 = text2.Substring(num + 1).Trim(); if (string.IsNullOrWhiteSpace(text3) || string.IsNullOrWhiteSpace(text4)) { continue; } List list = new List(); string[] array3 = text4.Split(new char[2] { ',', '|' }, StringSplitOptions.RemoveEmptyEntries); string[] array4 = array3; foreach (string itemRaw in array4) { if (TryParseCombatBiomeItem(itemRaw, out var prefab, out var points)) { list.Add(prefab + ";" + Mathf.Max(0, points)); } } dictionary[text3] = string.Join(",", list.ToArray()); } return dictionary; } private string BuildCombatCategoryRulesFromBiomeEntries() { if (_cfgCombatBiomeRuleEntries == null || _cfgCombatBiomeRuleEntries.Count == 0) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair> cfgCombatBiomeRuleEntry in _cfgCombatBiomeRuleEntries) { if (cfgCombatBiomeRuleEntry.Value == null) { continue; } string value = NormalizeHudCategoryName(cfgCombatBiomeRuleEntry.Key); if (string.IsNullOrWhiteSpace(value)) { continue; } string value2 = ConvertCombatBiomeItemsToHudCategoryItems(cfgCombatBiomeRuleEntry.Value.Value); if (!string.IsNullOrWhiteSpace(value2)) { if (stringBuilder.Length > 0) { stringBuilder.Append(';'); } stringBuilder.Append(value); stringBuilder.Append(':'); stringBuilder.Append(value2); } } return stringBuilder.ToString(); } private string ConvertCombatBiomeItemsToHudCategoryItems(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return string.Empty; } List list = new List(); string[] array = raw.Split(new char[4] { ',', '|', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string itemRaw in array2) { if (TryParseCombatBiomeItem(itemRaw, out var prefab, out var points)) { list.Add(prefab + ":" + Mathf.Max(0, points)); } } return string.Join(",", list.ToArray()); } private bool TryParseCombatBiomeItem(string itemRaw, out string prefab, out int points) { prefab = string.Empty; points = 0; if (string.IsNullOrWhiteSpace(itemRaw)) { return false; } string text = itemRaw.Trim(); int num = text.LastIndexOf(';'); if (num <= 0) { num = text.LastIndexOf('='); } if (num <= 0) { num = text.LastIndexOf(':'); } if (num <= 0 || num >= text.Length - 1) { return false; } string value = text.Substring(0, num).Trim(); string s = text.Substring(num + 1).Trim(); if (!int.TryParse(s, out var result)) { return false; } prefab = SafeKey(value); points = Mathf.Max(0, result); return !string.IsNullOrWhiteSpace(prefab); } private void RewriteRulesConfigHeaderAndGroupedSections() { try { if (string.IsNullOrWhiteSpace(_rulesFilePath) || !File.Exists(_rulesFilePath)) { return; } string[] array = File.ReadAllLines(_rulesFilePath, Encoding.UTF8); List list = new List(array.Length + 40); string a = string.Empty; bool flag = false; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase) { "KillPoints", "CraftPoints", "FarmJackpots", "UniqueCraftJackpots", "DeathPenalty", "ExplorationMapJackpots" }; list.Add("## Glitnir Ranking - Regras de pontuação"); bool flag2 = false; bool flag3 = false; bool flag4 = false; bool flag5 = false; string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (text2.StartsWith("## Glitnir Ranking - Regras de pontuação", StringComparison.OrdinalIgnoreCase)) { continue; } if (string.Equals(a, "HudCategories", StringComparison.OrdinalIgnoreCase) && text2.IndexOf('=') > 0) { string text3 = text2.Substring(0, text2.IndexOf('=')).Trim(); if (text3.Equals("Combat", StringComparison.OrdinalIgnoreCase) || text3.Equals("Production", StringComparison.OrdinalIgnoreCase)) { continue; } } if (text2.StartsWith("[") && text2.EndsWith("]")) { string text4 = text2.Substring(1, text2.Length - 2).Trim(); flag = hashSet.Contains(text4); a = text4; if (flag) { if (text4.Equals("FarmJackpots", StringComparison.OrdinalIgnoreCase) && !flag2) { AppendGroupedFarmSection(list); flag2 = true; } else if (text4.Equals("UniqueCraftJackpots", StringComparison.OrdinalIgnoreCase) && !flag3) { AppendGroupedUniqueCraftSection(list); flag3 = true; } else if (text4.Equals("DeathPenalty", StringComparison.OrdinalIgnoreCase) && !flag4) { AppendGroupedDeathPenaltySection(list); flag4 = true; } else if (text4.Equals("ExplorationMapJackpots", StringComparison.OrdinalIgnoreCase) && !flag5) { AppendGroupedExplorationMapSection(list); flag5 = true; } } else { list.Add(text); } } else if (!flag) { list.Add(text); } } if (!flag2) { AppendGroupedFarmSection(list); } if (!flag3) { AppendGroupedUniqueCraftSection(list); } if (!flag4) { AppendGroupedDeathPenaltySection(list); } if (!flag5) { AppendGroupedExplorationMapSection(list); } File.WriteAllLines(_rulesFilePath, list.ToArray(), Encoding.UTF8); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao organizar config do ranking: " + ex.Message)); } } private void AppendGroupedFarmSection(List lines) { lines.Add(""); lines.Add("[FarmJackpots]"); lines.Add(""); lines.Add("## Jackpot de colheita por prefab configurado."); lines.Add("## Formato: Prefab:quantidade:pontos;Prefab:quantidade:pontos"); lines.Add("## Exemplo: Carrot:200:1000;Barley:500:2000"); lines.Add("Rules = " + ((_cfgFarmJackpotRules != null) ? SanitizeDelimitedJackpotRules(_cfgFarmJackpotRules.Value) : "Carrot:200:1000;Barley:500:2000")); } private void AppendGroupedUniqueCraftSection(List lines) { lines.Add(""); lines.Add("[UniqueCraftJackpots]"); lines.Add(""); lines.Add("## Jackpot único por craft separado por categoria, igual ao modelo do Combat."); lines.Add("## Formato: Prefab;Quantidade;Pontos,OutroPrefab;Quantidade;Pontos"); lines.Add("## Exemplo: SwordIron;1;800,SwordSilver;1;1500"); lines.Add("## Aceita qualquer prefab, inclusive de mods. Use Outros para o que não couber nas categorias."); if (_cfgUniqueCraftJackpotCategoryEntries != null && _cfgUniqueCraftJackpotCategoryEntries.Count > 0) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair> cfgUniqueCraftJackpotCategoryEntry in _cfgUniqueCraftJackpotCategoryEntries) { string text = NormalizeHudCategoryName(cfgUniqueCraftJackpotCategoryEntry.Key); if (!string.IsNullOrWhiteSpace(text)) { dictionary[text] = ((cfgUniqueCraftJackpotCategoryEntry.Value != null) ? ConvertUniqueCraftJackpotItemsToConfigValue(cfgUniqueCraftJackpotCategoryEntry.Value.Value) : string.Empty); } } if (!dictionary.ContainsKey("Outros")) { dictionary["Outros"] = string.Empty; } { foreach (string item in GetPreferredCategoryOrder(dictionary, new string[5] { "Armas", "Armaduras", "Ferramentas", "Comidas", "Outros" })) { lines.Add(item + " = " + dictionary[item]); } return; } } lines.Add("Armas = SwordIron;1;800,SwordSilver;1;1500,SwordBlackmetal;1;2500"); lines.Add("Armaduras = ArmorWolfChest;1;2000,ArmorCarapaceChest;1;3500"); lines.Add("Outros = "); } private void AppendGroupedDeathPenaltySection(List lines) { lines.Add(""); lines.Add("[DeathPenalty]"); lines.Add(""); lines.Add("## Penalidade por morte."); lines.Add("## UseMultiplierMode=false usa a tabela Rules abaixo."); lines.Add("## UseMultiplierMode=true ignora Rules e aplica PenaltyPerDeath para cada morte."); lines.Add("## Exemplo multiplicador: PenaltyPerDeath=100 e 15 mortes = -1500 pontos."); lines.Add("UseMultiplierMode = " + ((_cfgDeathPenaltyUseMultiplier != null) ? _cfgDeathPenaltyUseMultiplier.Value.ToString() : "false")); lines.Add("PenaltyPerDeath = " + ((_cfgDeathPenaltyPerDeath != null) ? Mathf.Max(0, _cfgDeathPenaltyPerDeath.Value).ToString() : "100")); lines.Add(""); lines.Add("## Formato do modo tabela: mortes:pontosPerdidos,mortes:pontosPerdidos"); lines.Add("## Exemplo: 1:0,2:100,5:300,10:800"); lines.Add("Rules = " + ((_cfgDeathPenaltyRules != null) ? SanitizeDeathPenaltyRules(_cfgDeathPenaltyRules.Value) : "1:0,2:100,5:300,10:800")); } private void AppendGroupedExplorationMapSection(List lines) { lines.Add(""); lines.Add("[ExplorationMapJackpots]"); lines.Add(""); lines.Add("## Jackpot por porcentagem de mapa revelado."); lines.Add("## Formato: porcentagem:pontos;porcentagem:pontos"); lines.Add("## Exemplo: 5:25;10:50;25:150;50:400;75:800;100:1500"); lines.Add("Enabled = " + ((_cfgEnableExplorationJackpots != null) ? _cfgEnableExplorationJackpots.Value.ToString() : "true")); lines.Add("Rules = " + ((_cfgExplorationMapJackpotRules != null) ? _cfgExplorationMapJackpotRules.Value : "5:25;10:50;25:150;50:400;75:800;100:1500")); } private void CleanupFishKillPointConfigEntries() { try { if (string.IsNullOrWhiteSpace(_rulesFilePath) || !File.Exists(_rulesFilePath)) { return; } string[] array = File.ReadAllLines(_rulesFilePath, Encoding.UTF8); List list = new List(array.Length); bool flag = false; bool flag2 = false; string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (text2.StartsWith("[") && text2.EndsWith("]")) { flag = text2.Equals("[KillPoints]", StringComparison.OrdinalIgnoreCase); } int num = text2.IndexOf('='); if (num > 0) { string text3 = text2.Substring(0, num).Trim(); string text4 = text3; if (text3.StartsWith("KillPoints.", StringComparison.OrdinalIgnoreCase)) { text4 = text3.Substring("KillPoints.".Length).Trim(); } else if (!flag) { text4 = null; } if (!string.IsNullOrWhiteSpace(text4) && IsFishPrefab(text4)) { flag2 = true; continue; } } list.Add(text); } if (flag2) { File.WriteAllLines(_rulesFilePath, list.ToArray(), Encoding.UTF8); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao limpar prefabs de peixe do config: " + ex.Message)); } } private void CleanupGroupedProductionConfigEntries() { try { if (string.IsNullOrWhiteSpace(_rulesFilePath) || !File.Exists(_rulesFilePath)) { return; } string[] array = File.ReadAllLines(_rulesFilePath, Encoding.UTF8); List list = new List(array.Length); string text = string.Empty; bool flag = false; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase) { "KillPoints", "CraftPoints", "FarmJackpots", "UniqueCraftJackpots", "DeathPenalty", "ExplorationMapJackpots" }; string[] array2 = array; foreach (string text2 in array2) { string text3 = ((text2 != null) ? text2.Trim() : string.Empty); if (text3.StartsWith("[") && text3.EndsWith("]")) { text = text3.Substring(1, text3.Length - 2).Trim(); list.Add(text2); continue; } int num = text3.IndexOf('='); if (num > 0 && hashSet.Contains(text)) { string text4 = text3.Substring(0, num).Trim(); bool flag2 = text4.Equals("Rules", StringComparison.OrdinalIgnoreCase); if (text.Equals("DeathPenalty", StringComparison.OrdinalIgnoreCase)) { flag2 = flag2 || text4.Equals("UseMultiplierMode", StringComparison.OrdinalIgnoreCase) || text4.Equals("PenaltyPerDeath", StringComparison.OrdinalIgnoreCase); } else if (text.Equals("ExplorationMapJackpots", StringComparison.OrdinalIgnoreCase)) { flag2 = flag2 || text4.Equals("Enabled", StringComparison.OrdinalIgnoreCase); } if (!flag2) { flag = true; continue; } } list.Add(text2); } if (flag) { File.WriteAllLines(_rulesFilePath, list.ToArray(), Encoding.UTF8); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao reorganizar config de produção: " + ex.Message)); } } private string BuildMarketplaceQuestPointMapDefault(Dictionary legacyOverrides) { string text = ReadLegacyString(legacyOverrides, "MarketplaceQuestPoints.QuestPointMap", null); if (!string.IsNullOrWhiteSpace(text)) { return SanitizeMarketplaceQuestPointMap(text); } text = ReadLegacyString(legacyOverrides, "QuestPointMap", null); if (!string.IsNullOrWhiteSpace(text)) { return SanitizeMarketplaceQuestPointMap(text); } Dictionary mergedDefaultPointEntries = GetMergedDefaultPointEntries("MarketplaceQuestPoints", legacyOverrides); if (mergedDefaultPointEntries.Count <= 0) { return "ccq_b21:450,ccq_b22:150"; } return string.Join(",", from p in mergedDefaultPointEntries.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase) select SafeMarketplaceQuestKey(p.Key) + ":" + Mathf.Max(0, p.Value)); } private Dictionary ParseMarketplaceQuestPointMap(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text2)) { continue; } string[] array3 = text2.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array3.Length == 2) { string text3 = SafeMarketplaceQuestKey(array3[0]); if (!string.IsNullOrWhiteSpace(text3) && int.TryParse(array3[1].Trim(), out var result)) { dictionary[text3] = result; } } } return dictionary; } private string SanitizeMarketplaceQuestPointMap(string raw) { Dictionary dictionary = ParseMarketplaceQuestPointMap(raw); if (dictionary.Count <= 0) { return string.Empty; } return string.Join(",", from p in dictionary.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase) select p.Key + ":" + Mathf.Max(0, p.Value)); } private Dictionary ParseSkillMilestoneMap(string raw) { Dictionary dictionary = new Dictionary(); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (!string.IsNullOrWhiteSpace(text2)) { string[] array3 = text2.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array3.Length == 2 && int.TryParse(array3[0].Trim(), out var result) && int.TryParse(array3[1].Trim(), out var result2)) { result = Mathf.Clamp(result, 1, 100); dictionary[result] = Mathf.Max(0, result2); } } } return dictionary; } private string SanitizeSkillMilestoneMap(string raw) { Dictionary dictionary = ParseSkillMilestoneMap(raw); if (dictionary.Count <= 0) { return string.Empty; } return string.Join(",", from p in dictionary orderby p.Key select p.Key + ":" + Mathf.Max(0, p.Value)); } private JackpotRule ParseJackpotRule(string raw) { JackpotRule jackpotRule = new JackpotRule(); if (string.IsNullOrWhiteSpace(raw)) { return jackpotRule; } string[] array = raw.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array.Length != 2) { return jackpotRule; } if (!int.TryParse(array[0].Trim(), out var result)) { return jackpotRule; } if (!int.TryParse(array[1].Trim(), out var result2)) { return jackpotRule; } jackpotRule.RequiredAmount = Mathf.Max(1, result); jackpotRule.Points = Mathf.Max(0, result2); return jackpotRule; } private string SanitizeJackpotRule(string raw) { JackpotRule jackpotRule = ParseJackpotRule(raw); if (jackpotRule == null || jackpotRule.RequiredAmount <= 0) { return "1:0"; } return jackpotRule.RequiredAmount + ":" + Mathf.Max(0, jackpotRule.Points); } private string StripHudCategoryPointPart(string itemRaw) { if (string.IsNullOrWhiteSpace(itemRaw)) { return ""; } string text = itemRaw.Trim(); int num = text.IndexOf('='); if (num > 0) { text = text.Substring(0, num).Trim(); } int num2 = text.LastIndexOf(':'); if (num2 > 0 && num2 < text.Length - 1) { string s = text.Substring(num2 + 1).Trim(); if (int.TryParse(s, out var _)) { text = text.Substring(0, num2).Trim(); } } return SafeKey(text); } private bool TryParseHudCategoryPointItem(string itemRaw, out string prefab, out int points) { prefab = ""; points = 0; if (string.IsNullOrWhiteSpace(itemRaw)) { return false; } string text = itemRaw.Trim(); int num = text.LastIndexOf('='); if (num <= 0) { num = text.LastIndexOf(':'); } if (num <= 0 || num >= text.Length - 1) { return false; } string value = text.Substring(0, num).Trim(); string s = text.Substring(num + 1).Trim(); if (!int.TryParse(s, out var result)) { return false; } prefab = SafeKey(value); points = Mathf.Max(0, result); return !string.IsNullOrWhiteSpace(prefab); } private Dictionary ParseHudCategoryRules(string raw) { ParseHudCategorizedPointRules(raw, out var categories); return categories; } private static string NormalizeHudCategoryName(string category) { if (string.IsNullOrWhiteSpace(category)) { return category; } string text = category.Trim(); if (text.Equals("Arcos e Munições", StringComparison.OrdinalIgnoreCase) || text.Equals("Arcos e Municoes", StringComparison.OrdinalIgnoreCase) || text.Equals("Armas, Armaduras e Munições", StringComparison.OrdinalIgnoreCase) || text.Equals("Armas, Armaduras e Municoes", StringComparison.OrdinalIgnoreCase)) { return "Armas"; } if (text.Equals("Comidas e Consumíveis", StringComparison.OrdinalIgnoreCase) || text.Equals("Comidas e Consumiveis", StringComparison.OrdinalIgnoreCase) || text.Equals("Comidas e bebidas", StringComparison.OrdinalIgnoreCase)) { return "Comidas"; } return text; } private Dictionary ParseHudCategorizedPointRules(string raw, out Dictionary categories) { categories = new Dictionary(StringComparer.OrdinalIgnoreCase); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[3] { ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : ""); if (string.IsNullOrWhiteSpace(text2)) { continue; } int num = text2.IndexOf(':'); if (num <= 0 || num >= text2.Length - 1) { continue; } string value = NormalizeHudCategoryName(text2.Substring(0, num).Trim()); if (string.IsNullOrWhiteSpace(value)) { continue; } string text3 = text2.Substring(num + 1); string[] array3 = text3.Split(new char[2] { ',', '|' }, StringSplitOptions.RemoveEmptyEntries); string[] array4 = array3; foreach (string itemRaw in array4) { string text4 = StripHudCategoryPointPart(itemRaw); if (!string.IsNullOrWhiteSpace(text4)) { categories[text4] = value; if (TryParseHudCategoryPointItem(itemRaw, out var prefab, out var points)) { dictionary[prefab] = points; } } } } return dictionary; } private Dictionary ParseDelimitedPointRules(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[4] { ';', ',', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text2)) { continue; } string[] array3 = text2.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array3.Length == 2) { string text3 = SafeKey(array3[0]); if (!string.IsNullOrWhiteSpace(text3) && int.TryParse(array3[1].Trim(), out var result)) { dictionary[text3] = Mathf.Max(0, result); } } } return dictionary; } private Dictionary ParseDelimitedJackpotRules(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[4] { ';', ',', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text2)) { continue; } string[] array3 = text2.Split(new char[1] { ':' }, 3, StringSplitOptions.None); if (array3.Length == 3) { string text3 = SafeKey(array3[0]); if (!string.IsNullOrWhiteSpace(text3) && int.TryParse(array3[1].Trim(), out var result) && int.TryParse(array3[2].Trim(), out var result2)) { dictionary[text3] = new JackpotRule { RequiredAmount = Mathf.Max(1, result), Points = Mathf.Max(0, result2) }; } } } return dictionary; } private Dictionary ParseDeathPenaltyRules(string raw) { Dictionary dictionary = new Dictionary(); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[4] { ',', ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (!string.IsNullOrWhiteSpace(text2)) { string[] array3 = text2.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array3.Length == 2 && int.TryParse(array3[0].Trim(), out var result) && int.TryParse(array3[1].Trim(), out var result2)) { result = Mathf.Max(1, result); result2 = (dictionary[result] = Mathf.Max(0, result2)); } } } return dictionary; } private string SanitizeDeathPenaltyRules(string raw) { Dictionary dictionary = ParseDeathPenaltyRules(raw); if (dictionary.Count <= 0) { return string.Empty; } return string.Join(",", from p in dictionary orderby p.Key select Mathf.Max(1, p.Key) + ":" + Mathf.Max(0, p.Value)); } private string SanitizeDelimitedPointRules(string raw) { return SanitizeDelimitedPointRules(raw, ";"); } private string SanitizeDelimitedPointRules(string raw, string delimiter) { Dictionary dictionary = ParseDelimitedPointRules(raw); if (dictionary.Count <= 0) { return string.Empty; } if (string.IsNullOrEmpty(delimiter)) { delimiter = ";"; } return string.Join(delimiter, from p in dictionary.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase) where !IsFishPrefab(p.Key) select p.Key + ":" + Mathf.Max(0, p.Value)); } private string SanitizeDelimitedJackpotRules(string raw) { Dictionary dictionary = ParseDelimitedJackpotRules(raw); if (dictionary.Count <= 0) { return string.Empty; } return string.Join(";", from p in dictionary.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase) select p.Key + ":" + Mathf.Max(1, p.Value.RequiredAmount) + ":" + Mathf.Max(0, p.Value.Points)); } private string ReadGroupedRulesDefault(Dictionary legacyOverrides, string section, string fallback) { string text = ReadLegacyString(legacyOverrides, section + ".Rules", null); if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } text = ReadLegacyString(legacyOverrides, "Rules", null); if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } return fallback; } private string BuildDelimitedPointRulesDefault(string section, Dictionary legacyOverrides, string fallback) { string text = ReadLegacyString(legacyOverrides, section + ".Rules", null); if (!string.IsNullOrWhiteSpace(text)) { return SanitizeDelimitedPointRules(text); } Dictionary mergedDefaultPointEntries = GetMergedDefaultPointEntries(section, legacyOverrides); if (mergedDefaultPointEntries.Count <= 0) { return fallback; } return string.Join(";", from p in mergedDefaultPointEntries.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase) select SafeKey(p.Key) + ":" + Mathf.Max(0, p.Value)); } private string BuildDelimitedJackpotRulesDefault(string section, Dictionary legacyOverrides, string fallback) { string text = ReadLegacyString(legacyOverrides, section + ".Rules", null); if (!string.IsNullOrWhiteSpace(text)) { return SanitizeDelimitedJackpotRules(text); } Dictionary mergedDefaultStringEntries = GetMergedDefaultStringEntries(section, legacyOverrides); if (mergedDefaultStringEntries.Count <= 0) { return fallback; } List list = new List(); foreach (KeyValuePair item in mergedDefaultStringEntries.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase)) { if (!item.Key.Equals("Rules", StringComparison.OrdinalIgnoreCase)) { JackpotRule jackpotRule = ParseJackpotRule(item.Value); if (jackpotRule != null && jackpotRule.RequiredAmount > 0) { list.Add(SafeKey(item.Key) + ":" + Mathf.Max(1, jackpotRule.RequiredAmount) + ":" + Mathf.Max(0, jackpotRule.Points)); } } } return (list.Count > 0) ? string.Join(";", list) : fallback; } private void BindPointSectionEntries(string prefix, string descriptionPrefix, Dictionary legacyOverrides, Dictionary> target) { Dictionary mergedDefaultPointEntries = GetMergedDefaultPointEntries(prefix, legacyOverrides); List list = new List(target.Keys); foreach (string item in list) { if (!mergedDefaultPointEntries.ContainsKey(item)) { mergedDefaultPointEntries[item] = target[item].Value; } } foreach (KeyValuePair item2 in mergedDefaultPointEntries.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase)) { string text = SafeKey(item2.Key); if (!string.IsNullOrWhiteSpace(text) && (!prefix.Equals("KillPoints", StringComparison.OrdinalIgnoreCase) || !IsFishPrefab(text)) && (!prefix.Equals("FishingPoints", StringComparison.OrdinalIgnoreCase) || IsFishPrefab(text)) && !target.TryGetValue(text, out var value)) { value = (target[text] = _rulesConfig.BindConfig(prefix, text, item2.Value, descriptionPrefix + " Chave: " + text)); } } } private void BindStringPointSectionEntries(string prefix, string descriptionPrefix, Dictionary legacyOverrides, Dictionary> target) { Dictionary mergedDefaultStringEntries = GetMergedDefaultStringEntries(prefix, legacyOverrides); List list = new List(target.Keys); foreach (string item in list) { if (!mergedDefaultStringEntries.ContainsKey(item)) { mergedDefaultStringEntries[item] = target[item].Value; } } foreach (KeyValuePair item2 in mergedDefaultStringEntries.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase)) { string text = SafeKey(item2.Key); if (!string.IsNullOrWhiteSpace(text) && !target.TryGetValue(text, out var value)) { value = (target[text] = _rulesConfig.BindConfig(prefix, text, SanitizeSkillMilestoneMap(item2.Value), descriptionPrefix + " Chave: " + text)); } } } private void BindGenericJackpotSectionEntries(string prefix, string descriptionPrefix, Dictionary legacyOverrides, Dictionary> target) { Dictionary mergedDefaultStringEntries = GetMergedDefaultStringEntries(prefix, legacyOverrides); List list = new List(target.Keys); foreach (string item in list) { if (!mergedDefaultStringEntries.ContainsKey(item)) { mergedDefaultStringEntries[item] = target[item].Value; } } foreach (KeyValuePair item2 in mergedDefaultStringEntries.OrderBy, string>((KeyValuePair p) => p.Key, StringComparer.OrdinalIgnoreCase)) { string text = SafeKey(item2.Key); if (!string.IsNullOrWhiteSpace(text) && !target.TryGetValue(text, out var value)) { value = (target[text] = _rulesConfig.BindConfig(prefix, text, SanitizeJackpotRule(item2.Value), descriptionPrefix + " Chave: " + text)); } } } private Dictionary GetMergedDefaultStringEntries(string prefix, Dictionary legacyOverrides) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair item in ParseRuleTemplate(BuildDefaultRulesFileContents())) { if (item.Key.StartsWith(prefix + ".", StringComparison.OrdinalIgnoreCase)) { string text = SafeKey(item.Key.Substring(prefix.Length + 1)); if (!string.IsNullOrWhiteSpace(text)) { dictionary[text] = item.Value; } } } foreach (KeyValuePair legacyOverride in legacyOverrides) { if (legacyOverride.Key.StartsWith(prefix + ".", StringComparison.OrdinalIgnoreCase)) { string text2 = SafeKey(legacyOverride.Key.Substring(prefix.Length + 1)); if (!string.IsNullOrWhiteSpace(text2)) { dictionary[text2] = legacyOverride.Value; } } } return dictionary; } private Dictionary GetMergedDefaultPointEntries(string prefix, Dictionary legacyOverrides) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair item in ParseRuleTemplate(BuildDefaultRulesFileContents())) { if (!item.Key.StartsWith(prefix + ".", StringComparison.OrdinalIgnoreCase)) { continue; } string text = SafeKey(item.Key.Substring(prefix.Length + 1)); if (!string.IsNullOrWhiteSpace(text) && (!prefix.Equals("KillPoints", StringComparison.OrdinalIgnoreCase) || !IsFishPrefab(text)) && (!prefix.Equals("FishingPoints", StringComparison.OrdinalIgnoreCase) || IsFishPrefab(text))) { if (!int.TryParse(item.Value, out var result)) { result = 0; } dictionary[text] = result; } } foreach (KeyValuePair legacyOverride in legacyOverrides) { if (legacyOverride.Key.StartsWith(prefix + ".", StringComparison.OrdinalIgnoreCase)) { string text2 = SafeKey(legacyOverride.Key.Substring(prefix.Length + 1)); if (!string.IsNullOrWhiteSpace(text2) && (!prefix.Equals("KillPoints", StringComparison.OrdinalIgnoreCase) || !IsFishPrefab(text2)) && (!prefix.Equals("FishingPoints", StringComparison.OrdinalIgnoreCase) || IsFishPrefab(text2)) && int.TryParse(legacyOverride.Value, out var result2)) { dictionary[text2] = result2; } } } return dictionary; } private Dictionary ParseRuleTemplate(string contents) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(contents)) { return dictionary; } using (StringReader stringReader = new StringReader(contents)) { string text; while ((text = stringReader.ReadLine()) != null) { string text2 = text.Trim(); if (string.IsNullOrWhiteSpace(text2) || text2.StartsWith("#") || text2.StartsWith(";") || text2.StartsWith("//")) { continue; } int num = text2.IndexOf('='); if (num > 0) { string text3 = text2.Substring(0, num).Trim(); string value = text2.Substring(num + 1).Trim(); if (!string.IsNullOrWhiteSpace(text3)) { dictionary[text3] = value; } } } } return dictionary; } private bool ReadLegacyBool(Dictionary values, string key, bool fallback) { string value; return (values != null && values.TryGetValue(key, out value)) ? ParseBool(value, fallback) : fallback; } private int ReadLegacyInt(Dictionary values, string key, int fallback) { string value; return (values != null && values.TryGetValue(key, out value)) ? ParseInt(value, fallback) : fallback; } private string ReadLegacyString(Dictionary values, string key, string fallback) { if (values != null && values.TryGetValue(key, out var value) && !string.IsNullOrWhiteSpace(value)) { return value; } return fallback; } private void ApplyRulesFromSyncedConfig() { if (_rulesConfig == null) { return; } RankingRules rankingRules = new RankingRules(); if (_cfgRankingEnabled != null) { rankingRules.RankingEnabled = _cfgRankingEnabled.Value; } if (_cfgEnableKillPoints != null) { rankingRules.EnableKillPoints = _cfgEnableKillPoints.Value; } if (_cfgEnableBossPoints != null) { rankingRules.EnableBossPoints = _cfgEnableBossPoints.Value; } if (_cfgDefaultKillPoints != null) { rankingRules.DefaultKillPoints = Mathf.Max(0, _cfgDefaultKillPoints.Value); } if (_cfgTopCount != null) { rankingRules.TopCount = Mathf.Clamp(_cfgTopCount.Value, 1, 50); } if (_cfgAllowRepeatedBossPoints != null) { rankingRules.AllowRepeatedBossPoints = _cfgAllowRepeatedBossPoints.Value; } if (_cfgDebugLogging != null) { rankingRules.DebugLogging = _cfgDebugLogging.Value; } if (_cfgLogHitReports != null) { rankingRules.LogHitReports = _cfgLogHitReports.Value; } if (_cfgLogKillReports != null) { rankingRules.LogKillReports = _cfgLogKillReports.Value; } if (_cfgLogSkillReports != null) { rankingRules.LogSkillReports = _cfgLogSkillReports.Value; } if (_cfgLogPendingKillReports != null) { rankingRules.LogPendingKillReports = _cfgLogPendingKillReports.Value; } if (_cfgLogSnapshotRequests != null) { rankingRules.LogSnapshotRequests = _cfgLogSnapshotRequests.Value; } if (_cfgLogPointsChanges != null) { rankingRules.LogPointsChanges = _cfgLogPointsChanges.Value; } if (_cfgEnableSkillPoints != null) { rankingRules.EnableSkillPoints = _cfgEnableSkillPoints.Value; } if (_cfgIgnoreTamedKills != null) { rankingRules.IgnoreTamedKills = _cfgIgnoreTamedKills.Value; } if (_cfgEnableMarketplaceQuestPoints != null) { rankingRules.EnableMarketplaceQuestPoints = _cfgEnableMarketplaceQuestPoints.Value; } if (_cfgEnableFishingPoints != null) { rankingRules.EnableFishingPoints = _cfgEnableFishingPoints.Value; } if (_cfgEnableDeathPenalty != null) { rankingRules.EnableDeathPenalty = _cfgEnableDeathPenalty.Value; } if (_cfgDeathPenaltyUseMultiplier != null) { rankingRules.DeathPenaltyUseMultiplier = _cfgDeathPenaltyUseMultiplier.Value; } if (_cfgDeathPenaltyPerDeath != null) { rankingRules.DeathPenaltyPerDeath = Mathf.Max(0, _cfgDeathPenaltyPerDeath.Value); } if (_cfgEnableExplorationJackpots != null) { rankingRules.EnableExplorationJackpots = _cfgEnableExplorationJackpots.Value; } if (_cfgRewardClaimsEnabled != null) { rankingRules.RewardClaimsEnabled = _cfgRewardClaimsEnabled.Value; } if (_cfgRewardClaimCycleId != null) { rankingRules.RewardClaimCycleId = SafeLimit(_cfgRewardClaimCycleId.Value, 64); } if (_cfgRewardTop1MinPoints != null) { rankingRules.RewardTop1MinPoints = Mathf.Max(0, _cfgRewardTop1MinPoints.Value); } if (_cfgRewardTop1Label != null) { rankingRules.RewardTop1Label = SafeLimit(_cfgRewardTop1Label.Value, 96); } if (_cfgRewardTop1Prefab != null) { rankingRules.RewardTop1Prefab = SafeLimit(_cfgRewardTop1Prefab.Value, 96); } if (_cfgRewardTop1Amount != null) { rankingRules.RewardTop1Amount = Mathf.Max(0, _cfgRewardTop1Amount.Value); } if (_cfgRewardTop2MinPoints != null) { rankingRules.RewardTop2MinPoints = Mathf.Max(0, _cfgRewardTop2MinPoints.Value); } if (_cfgRewardTop2Label != null) { rankingRules.RewardTop2Label = SafeLimit(_cfgRewardTop2Label.Value, 96); } if (_cfgRewardTop2Prefab != null) { rankingRules.RewardTop2Prefab = SafeLimit(_cfgRewardTop2Prefab.Value, 96); } if (_cfgRewardTop2Amount != null) { rankingRules.RewardTop2Amount = Mathf.Max(0, _cfgRewardTop2Amount.Value); } if (_cfgRewardTop3MinPoints != null) { rankingRules.RewardTop3MinPoints = Mathf.Max(0, _cfgRewardTop3MinPoints.Value); } if (_cfgRewardTop3Label != null) { rankingRules.RewardTop3Label = SafeLimit(_cfgRewardTop3Label.Value, 96); } if (_cfgRewardTop3Prefab != null) { rankingRules.RewardTop3Prefab = SafeLimit(_cfgRewardTop3Prefab.Value, 96); } if (_cfgRewardTop3Amount != null) { rankingRules.RewardTop3Amount = Mathf.Max(0, _cfgRewardTop3Amount.Value); } if (_cfgPointsExchangeEnabled != null) { rankingRules.PointsExchangeEnabled = _cfgPointsExchangeEnabled.Value; } if (_cfgPointsExchangePrefab != null) { rankingRules.PointsExchangePrefab = SafeLimit(_cfgPointsExchangePrefab.Value, 96); } if (_cfgPointsExchangeUseCoinsPerPoint != null) { rankingRules.PointsExchangeUseCoinsPerPoint = _cfgPointsExchangeUseCoinsPerPoint.Value; } if (_cfgPointsExchangeCoinsPerPoint != null) { rankingRules.PointsExchangeCoinsPerPoint = Mathf.Max(1, _cfgPointsExchangeCoinsPerPoint.Value); } if (_cfgPointsExchangeUsePointsPerCoin != null) { rankingRules.PointsExchangeUsePointsPerCoin = _cfgPointsExchangeUsePointsPerCoin.Value; } if (_cfgPointsExchangePointsPerCoin != null) { rankingRules.PointsExchangePointsPerCoin = Mathf.Max(1, _cfgPointsExchangePointsPerCoin.Value); } if (_cfgPointsExchangeMinPoints != null) { rankingRules.PointsExchangeMinPoints = Mathf.Max(0, _cfgPointsExchangeMinPoints.Value); } if (_cfgPointsExchangeMaxPointsPerRequest != null) { rankingRules.PointsExchangeMaxPointsPerRequest = Mathf.Max(0, _cfgPointsExchangeMaxPointsPerRequest.Value); } string raw = BuildCombatCategoryRulesFromBiomeEntries(); string raw2 = BuildProductionCategoryRules(); Dictionary categories; foreach (KeyValuePair item in ParseHudCategorizedPointRules(raw, out categories)) { if (!IsFishPrefab(item.Key)) { rankingRules.KillPoints[item.Key] = Mathf.Max(0, item.Value); } } Dictionary categories2; foreach (KeyValuePair item2 in ParseHudCategorizedPointRules(raw2, out categories2)) { rankingRules.CraftPoints[item2.Key] = Mathf.Max(0, item2.Value); } rankingRules.CombatHudCategories = categories; rankingRules.ProductionHudCategories = categories2; foreach (KeyValuePair> cfgKillPointEntry in _cfgKillPointEntries) { if (cfgKillPointEntry.Value != null) { string text = SafeKey(cfgKillPointEntry.Key); if (!string.IsNullOrWhiteSpace(text) && !IsFishPrefab(text)) { rankingRules.KillPoints[text] = Mathf.Max(0, cfgKillPointEntry.Value.Value); } } } foreach (KeyValuePair> cfgBossPointEntry in _cfgBossPointEntries) { rankingRules.BossPoints[cfgBossPointEntry.Key] = ((cfgBossPointEntry.Value != null) ? cfgBossPointEntry.Value.Value : 0); } foreach (KeyValuePair> cfgFishingPointEntry in _cfgFishingPointEntries) { string text2 = NormalizeFishPrefabName(cfgFishingPointEntry.Key); if (IsFishPrefab(text2)) { rankingRules.FishingPoints[text2] = ((cfgFishingPointEntry.Value != null) ? cfgFishingPointEntry.Value.Value : 0); } } foreach (KeyValuePair> cfgSkillMilestoneJackpotPointEntry in _cfgSkillMilestoneJackpotPointEntries) { rankingRules.SkillMilestoneJackpotPoints[cfgSkillMilestoneJackpotPointEntry.Key] = ((cfgSkillMilestoneJackpotPointEntry.Value != null) ? ParseSkillMilestoneMap(cfgSkillMilestoneJackpotPointEntry.Value.Value) : new Dictionary()); } if (_cfgFarmJackpotRules != null) { foreach (KeyValuePair item3 in ParseDelimitedJackpotRules(_cfgFarmJackpotRules.Value)) { rankingRules.FarmJackpots[item3.Key] = item3.Value; } } string raw3 = BuildUniqueCraftJackpotRulesFromCategoryEntries(); foreach (KeyValuePair item4 in ParseFlexibleJackpotRules(raw3)) { rankingRules.UniqueCraftJackpots[item4.Key] = item4.Value; } if (_cfgMarketplaceQuestPointMap != null) { foreach (KeyValuePair item5 in ParseMarketplaceQuestPointMap(_cfgMarketplaceQuestPointMap.Value)) { rankingRules.MarketplaceQuestPoints[item5.Key] = item5.Value; } } if (_cfgDeathPenaltyRules != null) { rankingRules.DeathPenaltyRules = ParseDeathPenaltyRules(_cfgDeathPenaltyRules.Value); } if (_cfgExplorationMapJackpotRules != null) { rankingRules.ExplorationMapJackpots = ParseDeathPenaltyRules(_cfgExplorationMapJackpotRules.Value); } _rules = rankingRules; RebuildMarketplaceQuestPointCache(); } private void RebuildMarketplaceQuestPointCache() { _marketplaceQuestPointsByUid.Clear(); if (_rules == null || _rules.MarketplaceQuestPoints == null) { return; } foreach (KeyValuePair marketplaceQuestPoint in _rules.MarketplaceQuestPoints) { string text = SafeMarketplaceQuestKey(marketplaceQuestPoint.Key); int value = marketplaceQuestPoint.Value; if (!string.IsNullOrWhiteSpace(text) && value != 0) { int num = MarketplaceQuestKeyToUid(text); if (num != 0) { _marketplaceQuestPointsByUid[num] = new MarketplaceQuestPointRule { QuestKey = text, Points = value }; } } } } private string SafeMarketplaceQuestKey(string questKey) { questKey = SafeLimit(questKey, 128).Trim().ToLowerInvariant(); questKey = questKey.Replace("|", "/"); return questKey; } private int MarketplaceQuestKeyToUid(string questKey) { questKey = SafeMarketplaceQuestKey(questKey); if (string.IsNullOrWhiteSpace(questKey)) { return 0; } try { return StringExtensionMethods.GetStableHashCode(questKey); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao converter quest key do Marketplace em UID: " + questKey + " erro=" + ex.Message)); return 0; } } private bool TryGetMarketplaceQuestRule(int questUid, out MarketplaceQuestPointRule rule) { rule = null; if (_rules == null || !_rules.EnableMarketplaceQuestPoints) { return false; } return _marketplaceQuestPointsByUid.TryGetValue(questUid, out rule) && rule != null && rule.Points != 0; } private void LoadDatabase() { _database = new RankingDatabase(); if (!IsDedicatedServerInstance()) { DebugLog(DebugCategory.General, "LoadDatabase ignorado fora do servidor dedicado. Banco LiteDB fica somente no servidor."); return; } try { if (string.IsNullOrWhiteSpace(_databaseFilePath)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"[Ranking] Caminho do banco LiteDB nao definido."); return; } EnsureDatabaseDirectoryExists(); using LiteDatabase liteDatabase = new LiteDatabase(new ConnectionString { Filename = _databaseFilePath, Connection = ConnectionType.Shared }); ILiteCollection collection = liteDatabase.GetCollection("ranking_entries"); ILiteCollection collection2 = liteDatabase.GetCollection("reward_claims"); ILiteCollection collection3 = liteDatabase.GetCollection("marketplace_quest_credits"); ILiteCollection collection4 = liteDatabase.GetCollection("fishing_catch_credits"); collection.EnsureIndex((RankingEntryDocument x) => x.PlayerName, unique: true); collection2.EnsureIndex((RewardClaimDocument x) => x.Key, unique: true); collection3.EnsureIndex((MarketplaceQuestCreditDocument x) => x.Key, unique: true); collection4.EnsureIndex((FishingCatchCreditDocument x) => x.ZdoKey, unique: true); RankingDatabase rankingDatabase = new RankingDatabase(); foreach (RankingEntryDocument item in collection.FindAll()) { RankingEntry rankingEntry = ConvertFromDocument(item); if (rankingEntry != null && !string.IsNullOrWhiteSpace(rankingEntry.PlayerName)) { rankingDatabase.Entries.Add(rankingEntry); } } foreach (RewardClaimDocument item2 in collection2.FindAll()) { RewardClaimRecord rewardClaimRecord = ConvertFromDocument(item2); if (rewardClaimRecord != null && !string.IsNullOrWhiteSpace(rewardClaimRecord.PlayerName)) { rankingDatabase.Claims.Add(rewardClaimRecord); } } foreach (MarketplaceQuestCreditDocument item3 in collection3.FindAll()) { MarketplaceQuestCreditRecord marketplaceQuestCreditRecord = ConvertFromDocument(item3); if (marketplaceQuestCreditRecord != null && !string.IsNullOrWhiteSpace(marketplaceQuestCreditRecord.PlayerName) && !string.IsNullOrWhiteSpace(marketplaceQuestCreditRecord.QuestKey)) { rankingDatabase.MarketplaceQuestCredits.Add(marketplaceQuestCreditRecord); } } foreach (FishingCatchCreditDocument item4 in collection4.FindAll()) { FishingCatchCreditRecord fishingCatchCreditRecord = ConvertFromDocument(item4); if (fishingCatchCreditRecord != null && !string.IsNullOrWhiteSpace(fishingCatchCreditRecord.ZdoKey)) { rankingDatabase.FishingCatchCredits.Add(fishingCatchCreditRecord); } } _database = NormalizeDatabase(rankingDatabase); if (_database == null || _database.Entries == null || _database.Entries.Count == 0) { RankingDatabase rankingDatabase2 = TryLoadDatabaseBackup(); if (rankingDatabase2 != null && rankingDatabase2.Entries != null && rankingDatabase2.Entries.Count > 0) { _database = NormalizeDatabase(rankingDatabase2); ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Banco principal estava vazio; backup carregado. Jogadores recuperados: " + _database.Entries.Count)); } } RebuildDatabaseIndexes(); } catch (Exception ex) { _database = new RankingDatabase(); RebuildDatabaseIndexes(); ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao carregar banco LiteDB do ranking: " + ex)); } } private void EnsureDatabaseDirectoryExists() { if (!IsDedicatedServerInstance()) { return; } try { if (!string.IsNullOrWhiteSpace(_databaseFilePath)) { string directoryName = Path.GetDirectoryName(_databaseFilePath); if (!string.IsNullOrWhiteSpace(directoryName) && !Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Nao foi possivel preparar pasta do banco LiteDB: " + ex.Message)); } } private RankingDatabase LoadLegacyDatabaseFile(string filePath) { RankingDatabase result = new RankingDatabase(); try { if (string.IsNullOrWhiteSpace(filePath)) { return result; } if (!File.Exists(filePath)) { return result; } RankingDatabase rankingDatabase = new RankingDatabase(); string[] array = File.ReadAllLines(filePath, Encoding.UTF8); string[] array2 = array; foreach (string text in array2) { string text2 = ((text == null) ? "" : text.Trim()); if (string.IsNullOrWhiteSpace(text2) || text2.StartsWith("#")) { continue; } if (text2.StartsWith("Claim|", StringComparison.Ordinal)) { string[] array3 = text2.Split(new char[1] { '|' }); if (array3.Length < 5) { continue; } RewardClaimRecord rewardClaimRecord = new RewardClaimRecord(); rewardClaimRecord.CycleId = SafeLimit(DecodeDatabaseField(array3[1]), 64); rewardClaimRecord.PlayerName = SanitizePlayerName(DecodeDatabaseField(array3[2])); rewardClaimRecord.Rank = Mathf.Clamp(ParseInt(array3[3], 0), 0, int.MaxValue); rewardClaimRecord.ClaimedAtUtc = SafeLimit(DecodeDatabaseField(array3[4]), 64); rewardClaimRecord.Status = ((array3.Length >= 6) ? SafeLimit(DecodeDatabaseField(array3[5]), 16) : "claimed"); if (!string.IsNullOrWhiteSpace(rewardClaimRecord.CycleId) && !string.IsNullOrWhiteSpace(rewardClaimRecord.PlayerName) && rewardClaimRecord.Rank > 0) { string claimKey = BuildRewardClaimKey(rewardClaimRecord.CycleId, rewardClaimRecord.PlayerName, rewardClaimRecord.Rank); if (!rankingDatabase.Claims.Any((RewardClaimRecord x) => string.Equals(BuildRewardClaimKey(x.CycleId, x.PlayerName, x.Rank), claimKey, StringComparison.OrdinalIgnoreCase))) { rankingDatabase.Claims.Add(rewardClaimRecord); } } } else if (text2.StartsWith("QuestCredit|", StringComparison.Ordinal)) { string[] array4 = text2.Split(new char[1] { '|' }); if (array4.Length < 4) { continue; } MarketplaceQuestCreditRecord marketplaceQuestCreditRecord = new MarketplaceQuestCreditRecord(); marketplaceQuestCreditRecord.PlayerName = SanitizePlayerName(DecodeDatabaseField(array4[1])); marketplaceQuestCreditRecord.QuestKey = SafeKey(DecodeDatabaseField(array4[2])); marketplaceQuestCreditRecord.GrantedAtUtc = ((array4.Length >= 4) ? SafeLimit(DecodeDatabaseField(array4[3]), 64) : ""); if (!string.IsNullOrWhiteSpace(marketplaceQuestCreditRecord.PlayerName) && !string.IsNullOrWhiteSpace(marketplaceQuestCreditRecord.QuestKey)) { string creditKey = BuildMarketplaceQuestCreditKey(marketplaceQuestCreditRecord.PlayerName, marketplaceQuestCreditRecord.QuestKey); if (!rankingDatabase.MarketplaceQuestCredits.Any((MarketplaceQuestCreditRecord x) => string.Equals(BuildMarketplaceQuestCreditKey(x.PlayerName, x.QuestKey), creditKey, StringComparison.OrdinalIgnoreCase))) { rankingDatabase.MarketplaceQuestCredits.Add(marketplaceQuestCreditRecord); } } } else { if (!text2.StartsWith("Entry|", StringComparison.Ordinal)) { continue; } string[] array5 = text2.Split(new char[1] { '|' }); if (array5.Length < 10) { continue; } RankingEntry entry = new RankingEntry(); entry.PlayerName = SanitizePlayerName(DecodeDatabaseField(array5[1])); entry.Points = Mathf.Clamp(ParseInt(array5[2], 0), -2147483647, int.MaxValue); entry.LastReason = SafeReason(DecodeDatabaseField(array5[3])); entry.LastUpdateUtc = SafeLimit(DecodeDatabaseField(array5[4]), 64); entry.TotalKillsPontuadas = Mathf.Clamp(ParseInt(array5[5], 0), 0, int.MaxValue); entry.TotalBossesPontuadas = Mathf.Clamp(ParseInt(array5[6], 0), 0, int.MaxValue); entry.KillPointsTotal = Mathf.Clamp(ParseInt(array5[7], 0), -2147483647, int.MaxValue); entry.BossPointsTotal = Mathf.Clamp(ParseInt(array5[8], 0), -2147483647, int.MaxValue); entry.BossCredits = new HashSet(StringComparer.OrdinalIgnoreCase); string text3 = DecodeDatabaseField(array5[9]); if (!string.IsNullOrWhiteSpace(text3)) { string[] array6 = text3.Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries); string[] array7 = array6; foreach (string value in array7) { string text4 = SafeKey(value); if (!string.IsNullOrWhiteSpace(text4)) { entry.BossCredits.Add(text4); } } } if (array5.Length >= 11) { entry.TotalSkillLevelUpsPontuados = Mathf.Clamp(ParseInt(array5[10], 0), 0, int.MaxValue); } if (array5.Length >= 12) { entry.SkillPointsTotal = Mathf.Clamp(ParseInt(array5[11], 0), -2147483647, int.MaxValue); } if (array5.Length >= 13) { entry.TotalCraftPontuadas = Mathf.Clamp(ParseInt(array5[12], 0), 0, int.MaxValue); } if (array5.Length >= 14) { entry.CraftPointsTotal = Mathf.Clamp(ParseInt(array5[13], 0), -2147483647, int.MaxValue); } if (array5.Length >= 15) { entry.TotalFarmJackpotsPontuados = Mathf.Clamp(ParseInt(array5[14], 0), 0, int.MaxValue); } if (array5.Length >= 16) { entry.FarmJackpotPointsTotal = Mathf.Clamp(ParseInt(array5[15], 0), -2147483647, int.MaxValue); } if (array5.Length >= 17) { entry.TotalUniqueCraftJackpotsPontuados = Mathf.Clamp(ParseInt(array5[16], 0), 0, int.MaxValue); } if (array5.Length >= 18) { entry.UniqueCraftJackpotPointsTotal = Mathf.Clamp(ParseInt(array5[17], 0), -2147483647, int.MaxValue); } if (array5.Length >= 19) { entry.ProgressCounters = DeserializeStringIntDictionary(DecodeDatabaseField(array5[18])); } if (array5.Length >= 20) { entry.GenericJackpotCredits = DeserializeStringSet(DecodeDatabaseField(array5[19])); } if (array5.Length >= 21) { entry.TotalDeaths = Mathf.Clamp(ParseInt(array5[20], 0), 0, int.MaxValue); } if (array5.Length >= 22) { entry.DeathPenaltyPointsTotal = Mathf.Clamp(ParseInt(array5[21], 0), 0, int.MaxValue); } if (array5.Length >= 23) { entry.TotalPointsExchanges = Mathf.Clamp(ParseInt(array5[22], 0), 0, int.MaxValue); } if (array5.Length >= 24) { entry.PointsExchangePenaltyTotal = Mathf.Clamp(ParseInt(array5[23], 0), 0, int.MaxValue); } if (array5.Length >= 25) { entry.PointsExchangeCoinsTotal = Mathf.Clamp(ParseInt(array5[24], 0), 0, int.MaxValue); } if (array5.Length >= 26) { entry.ExplorationMapJackpotPointsTotal = Mathf.Clamp(ParseInt(array5[25], 0), 0, int.MaxValue); } if (!string.IsNullOrWhiteSpace(entry.PlayerName)) { RankingEntry rankingEntry = rankingDatabase.Entries.FirstOrDefault((RankingEntry x) => string.Equals(x.PlayerName, entry.PlayerName, StringComparison.OrdinalIgnoreCase)); if (rankingEntry != null) { rankingEntry.Points = entry.Points; rankingEntry.LastReason = entry.LastReason; rankingEntry.LastUpdateUtc = entry.LastUpdateUtc; rankingEntry.TotalKillsPontuadas = entry.TotalKillsPontuadas; rankingEntry.TotalBossesPontuadas = entry.TotalBossesPontuadas; rankingEntry.KillPointsTotal = entry.KillPointsTotal; rankingEntry.BossPointsTotal = entry.BossPointsTotal; rankingEntry.BossCredits = entry.BossCredits; rankingEntry.TotalSkillLevelUpsPontuados = entry.TotalSkillLevelUpsPontuados; rankingEntry.SkillPointsTotal = entry.SkillPointsTotal; rankingEntry.TotalCraftPontuadas = entry.TotalCraftPontuadas; rankingEntry.CraftPointsTotal = entry.CraftPointsTotal; rankingEntry.TotalFarmJackpotsPontuados = entry.TotalFarmJackpotsPontuados; rankingEntry.FarmJackpotPointsTotal = entry.FarmJackpotPointsTotal; rankingEntry.TotalUniqueCraftJackpotsPontuados = entry.TotalUniqueCraftJackpotsPontuados; rankingEntry.UniqueCraftJackpotPointsTotal = entry.UniqueCraftJackpotPointsTotal; rankingEntry.ProgressCounters = entry.ProgressCounters ?? new Dictionary(StringComparer.OrdinalIgnoreCase); rankingEntry.GenericJackpotCredits = entry.GenericJackpotCredits ?? new HashSet(StringComparer.OrdinalIgnoreCase); rankingEntry.TotalDeaths = entry.TotalDeaths; rankingEntry.DeathPenaltyPointsTotal = entry.DeathPenaltyPointsTotal; rankingEntry.TotalPointsExchanges = entry.TotalPointsExchanges; rankingEntry.PointsExchangePenaltyTotal = entry.PointsExchangePenaltyTotal; rankingEntry.PointsExchangeCoinsTotal = entry.PointsExchangeCoinsTotal; rankingEntry.ExplorationMapJackpotPointsTotal = entry.ExplorationMapJackpotPointsTotal; } else { rankingDatabase.Entries.Add(entry); } } } } return NormalizeDatabase(rankingDatabase); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao importar banco legado do ranking: " + ex)); return result; } } private RankingDatabase NormalizeDatabase(RankingDatabase database) { if (database == null) { database = new RankingDatabase(); } if (database.Entries == null) { database.Entries = new List(); } if (database.Claims == null) { database.Claims = new List(); } if (database.MarketplaceQuestCredits == null) { database.MarketplaceQuestCredits = new List(); } if (database.FishingCatchCredits == null) { database.FishingCatchCredits = new List(); } foreach (RankingEntry entry in database.Entries) { if (entry != null) { entry.PlayerName = SanitizePlayerName(entry.PlayerName); entry.LastReason = SafeReason(entry.LastReason); entry.LastUpdateUtc = SafeLimit(entry.LastUpdateUtc, 64); entry.Points = Mathf.Clamp(entry.Points, -2147483647, int.MaxValue); entry.TotalKillsPontuadas = Mathf.Clamp(entry.TotalKillsPontuadas, 0, int.MaxValue); entry.TotalBossesPontuadas = Mathf.Clamp(entry.TotalBossesPontuadas, 0, int.MaxValue); entry.KillPointsTotal = Mathf.Clamp(entry.KillPointsTotal, -2147483647, int.MaxValue); entry.BossPointsTotal = Mathf.Clamp(entry.BossPointsTotal, -2147483647, int.MaxValue); entry.TotalSkillLevelUpsPontuados = Mathf.Clamp(entry.TotalSkillLevelUpsPontuados, 0, int.MaxValue); entry.SkillPointsTotal = Mathf.Clamp(entry.SkillPointsTotal, -2147483647, int.MaxValue); entry.TotalFishingPontuadas = Mathf.Clamp(entry.TotalFishingPontuadas, 0, int.MaxValue); entry.FishingPointsTotal = Mathf.Clamp(entry.FishingPointsTotal, -2147483647, int.MaxValue); entry.TotalCraftPontuadas = Mathf.Clamp(entry.TotalCraftPontuadas, 0, int.MaxValue); entry.CraftPointsTotal = Mathf.Clamp(entry.CraftPointsTotal, -2147483647, int.MaxValue); entry.TotalFarmJackpotsPontuados = Mathf.Clamp(entry.TotalFarmJackpotsPontuados, 0, int.MaxValue); entry.FarmJackpotPointsTotal = Mathf.Clamp(entry.FarmJackpotPointsTotal, -2147483647, int.MaxValue); entry.TotalUniqueCraftJackpotsPontuados = Mathf.Clamp(entry.TotalUniqueCraftJackpotsPontuados, 0, int.MaxValue); entry.UniqueCraftJackpotPointsTotal = Mathf.Clamp(entry.UniqueCraftJackpotPointsTotal, -2147483647, int.MaxValue); entry.TotalDeaths = Mathf.Clamp(entry.TotalDeaths, 0, int.MaxValue); entry.DeathPenaltyPointsTotal = Mathf.Clamp(entry.DeathPenaltyPointsTotal, 0, int.MaxValue); entry.TotalPointsExchanges = Mathf.Clamp(entry.TotalPointsExchanges, 0, int.MaxValue); entry.PointsExchangePenaltyTotal = Mathf.Clamp(entry.PointsExchangePenaltyTotal, 0, int.MaxValue); entry.PointsExchangeCoinsTotal = Mathf.Clamp(entry.PointsExchangeCoinsTotal, 0, int.MaxValue); entry.ExplorationMapJackpotPointsTotal = Mathf.Clamp(entry.ExplorationMapJackpotPointsTotal, 0, int.MaxValue); entry.BossCredits = ((entry.BossCredits == null) ? new HashSet(StringComparer.OrdinalIgnoreCase) : new HashSet(entry.BossCredits.Where((string x) => !string.IsNullOrWhiteSpace(x)).Select(SafeKey), StringComparer.OrdinalIgnoreCase)); entry.GenericJackpotCredits = ((entry.GenericJackpotCredits == null) ? new HashSet(StringComparer.OrdinalIgnoreCase) : new HashSet(entry.GenericJackpotCredits.Where((string x) => !string.IsNullOrWhiteSpace(x)).Select(SafeKey), StringComparer.OrdinalIgnoreCase)); entry.SkillLevel100JackpotCredits = ((entry.SkillLevel100JackpotCredits == null) ? new HashSet(StringComparer.OrdinalIgnoreCase) : new HashSet(entry.SkillLevel100JackpotCredits.Where((string x) => !string.IsNullOrWhiteSpace(x)).Select(SafeKey), StringComparer.OrdinalIgnoreCase)); entry.ProgressCounters = ((entry.ProgressCounters == null) ? new Dictionary(StringComparer.OrdinalIgnoreCase) : entry.ProgressCounters.Where((KeyValuePair x) => !string.IsNullOrWhiteSpace(x.Key)).GroupBy, string>((KeyValuePair x) => SafeKey(x.Key), StringComparer.OrdinalIgnoreCase).ToDictionary>, string, int>((IGrouping> x) => x.Key, (IGrouping> x) => Mathf.Clamp(x.Last().Value, 0, int.MaxValue), StringComparer.OrdinalIgnoreCase)); } } database.Entries = (from x in database.Entries.Where((RankingEntry x) => x != null && !string.IsNullOrWhiteSpace(x.PlayerName)).GroupBy((RankingEntry x) => x.PlayerName, StringComparer.OrdinalIgnoreCase) select x.First()).OrderBy((RankingEntry x) => x.PlayerName, StringComparer.OrdinalIgnoreCase).ToList(); database.Claims = (from x in database.Claims.Where((RewardClaimRecord x) => x != null && !string.IsNullOrWhiteSpace(x.CycleId) && !string.IsNullOrWhiteSpace(x.PlayerName) && x.Rank > 0).Select(delegate(RewardClaimRecord x) { x.CycleId = SafeLimit(x.CycleId, 64); x.PlayerName = SanitizePlayerName(x.PlayerName); x.Rank = Mathf.Clamp(x.Rank, 1, int.MaxValue); x.ClaimedAtUtc = SafeLimit(x.ClaimedAtUtc, 64); x.Status = SafeLimit(string.IsNullOrWhiteSpace(x.Status) ? "claimed" : x.Status, 16); return x; }).GroupBy((RewardClaimRecord x) => BuildRewardClaimKey(x.CycleId, x.PlayerName, x.Rank), StringComparer.OrdinalIgnoreCase) select x.First()).ToList(); database.MarketplaceQuestCredits = (from x in database.MarketplaceQuestCredits.Where((MarketplaceQuestCreditRecord x) => x != null && !string.IsNullOrWhiteSpace(x.PlayerName) && !string.IsNullOrWhiteSpace(x.QuestKey)).Select(delegate(MarketplaceQuestCreditRecord x) { x.PlayerName = SanitizePlayerName(x.PlayerName); x.QuestKey = SafeKey(x.QuestKey); x.GrantedAtUtc = SafeLimit(x.GrantedAtUtc, 64); return x; }).GroupBy((MarketplaceQuestCreditRecord x) => BuildMarketplaceQuestCreditKey(x.PlayerName, x.QuestKey), StringComparer.OrdinalIgnoreCase) select x.First()).ToList(); database.FishingCatchCredits = (from x in database.FishingCatchCredits.Where((FishingCatchCreditRecord x) => x != null && !string.IsNullOrWhiteSpace(x.ZdoKey)).Select(delegate(FishingCatchCreditRecord x) { x.PlayerName = SanitizePlayerName(x.PlayerName); x.FishPrefab = SafeKey(x.FishPrefab); x.ZdoKey = SafeKey(x.ZdoKey); x.GrantedAtUtc = SafeLimit(x.GrantedAtUtc, 64); return x; }).GroupBy((FishingCatchCreditRecord x) => x.ZdoKey, StringComparer.OrdinalIgnoreCase) select x.First()).ToList(); return database; } private void SaveDatabase() { lock (_databaseSaveLock) { try { if (!IsDedicatedServerInstance() || string.IsNullOrWhiteSpace(_databaseFilePath) || _database == null) { return; } EnsureDatabaseDirectoryExists(); CreateDatabaseBackupCopy(); RankingDatabase rankingDatabase = NormalizeDatabase(_database); MarkRankingCacheDirty(); using (LiteDatabase liteDatabase = new LiteDatabase(new ConnectionString { Filename = _databaseFilePath, Connection = ConnectionType.Shared })) { ILiteCollection collection = liteDatabase.GetCollection("ranking_entries"); ILiteCollection collection2 = liteDatabase.GetCollection("reward_claims"); ILiteCollection collection3 = liteDatabase.GetCollection("marketplace_quest_credits"); ILiteCollection collection4 = liteDatabase.GetCollection("fishing_catch_credits"); collection.EnsureIndex((RankingEntryDocument x) => x.PlayerName, unique: true); collection2.EnsureIndex((RewardClaimDocument x) => x.Key, unique: true); collection3.EnsureIndex((MarketplaceQuestCreditDocument x) => x.Key, unique: true); collection4.EnsureIndex((FishingCatchCreditDocument x) => x.ZdoKey, unique: true); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); HashSet hashSet2 = new HashSet(StringComparer.OrdinalIgnoreCase); HashSet hashSet3 = new HashSet(StringComparer.OrdinalIgnoreCase); HashSet hashSet4 = new HashSet(StringComparer.OrdinalIgnoreCase); if (rankingDatabase.Entries != null) { foreach (RankingEntry entry in rankingDatabase.Entries) { RankingEntryDocument rankingEntryDocument = ConvertToDocument(entry); if (rankingEntryDocument != null && !string.IsNullOrWhiteSpace(rankingEntryDocument.PlayerName)) { hashSet.Add(rankingEntryDocument.PlayerName); collection.Upsert(rankingEntryDocument); } } } if (rankingDatabase.Claims != null) { foreach (RewardClaimRecord claim in rankingDatabase.Claims) { RewardClaimDocument rewardClaimDocument = ConvertToDocument(claim); if (rewardClaimDocument != null && !string.IsNullOrWhiteSpace(rewardClaimDocument.Key)) { hashSet2.Add(rewardClaimDocument.Key); collection2.Upsert(rewardClaimDocument); } } } if (rankingDatabase.MarketplaceQuestCredits != null) { foreach (MarketplaceQuestCreditRecord marketplaceQuestCredit in rankingDatabase.MarketplaceQuestCredits) { MarketplaceQuestCreditDocument marketplaceQuestCreditDocument = ConvertToDocument(marketplaceQuestCredit); if (marketplaceQuestCreditDocument != null && !string.IsNullOrWhiteSpace(marketplaceQuestCreditDocument.Key)) { hashSet3.Add(marketplaceQuestCreditDocument.Key); collection3.Upsert(marketplaceQuestCreditDocument); } } } if (rankingDatabase.FishingCatchCredits != null) { foreach (FishingCatchCreditRecord fishingCatchCredit in rankingDatabase.FishingCatchCredits) { FishingCatchCreditDocument fishingCatchCreditDocument = ConvertToDocument(fishingCatchCredit); if (fishingCatchCreditDocument != null && !string.IsNullOrWhiteSpace(fishingCatchCreditDocument.ZdoKey)) { hashSet4.Add(fishingCatchCreditDocument.ZdoKey); collection4.Upsert(fishingCatchCreditDocument); } } } DeleteStaleRankingDocuments(collection, hashSet); DeleteStaleRewardClaimDocuments(collection2, hashSet2); DeleteStaleMarketplaceCreditDocuments(collection3, hashSet3); DeleteStaleFishingCreditDocuments(collection4, hashSet4); } _database = rankingDatabase; RebuildDatabaseIndexes(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao salvar banco LiteDB do ranking: " + ex)); } } } private void DeleteStaleRankingDocuments(ILiteCollection collection, HashSet validKeys) { foreach (RankingEntryDocument item in collection.FindAll().ToList()) { if (item != null && (string.IsNullOrWhiteSpace(item.PlayerName) || !validKeys.Contains(item.PlayerName))) { collection.Delete(item.PlayerName); } } } private void DeleteStaleRewardClaimDocuments(ILiteCollection collection, HashSet validKeys) { foreach (RewardClaimDocument item in collection.FindAll().ToList()) { if (item != null && (string.IsNullOrWhiteSpace(item.Key) || !validKeys.Contains(item.Key))) { collection.Delete(item.Key); } } } private void DeleteStaleMarketplaceCreditDocuments(ILiteCollection collection, HashSet validKeys) { foreach (MarketplaceQuestCreditDocument item in collection.FindAll().ToList()) { if (item != null && (string.IsNullOrWhiteSpace(item.Key) || !validKeys.Contains(item.Key))) { collection.Delete(item.Key); } } } private void DeleteStaleFishingCreditDocuments(ILiteCollection collection, HashSet validKeys) { foreach (FishingCatchCreditDocument item in collection.FindAll().ToList()) { if (item != null && (string.IsNullOrWhiteSpace(item.ZdoKey) || !validKeys.Contains(item.ZdoKey))) { collection.Delete(item.ZdoKey); } } } private string GetDatabaseBackupFilePath() { if (string.IsNullOrWhiteSpace(_databaseFilePath)) { return ""; } return _databaseFilePath + ".bak"; } private void CreateDatabaseBackupCopy() { try { if (IsDedicatedServerInstance() && !string.IsNullOrWhiteSpace(_databaseFilePath) && File.Exists(_databaseFilePath)) { string databaseBackupFilePath = GetDatabaseBackupFilePath(); if (!string.IsNullOrWhiteSpace(databaseBackupFilePath)) { File.Copy(_databaseFilePath, databaseBackupFilePath, overwrite: true); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Nao foi possivel criar backup do banco LiteDB: " + ex.Message)); } } private RankingDatabase TryLoadDatabaseBackup() { try { string databaseBackupFilePath = GetDatabaseBackupFilePath(); if (string.IsNullOrWhiteSpace(databaseBackupFilePath) || !File.Exists(databaseBackupFilePath)) { return null; } using LiteDatabase liteDatabase = new LiteDatabase(databaseBackupFilePath); RankingDatabase rankingDatabase = new RankingDatabase(); ILiteCollection collection = liteDatabase.GetCollection("ranking_entries"); ILiteCollection collection2 = liteDatabase.GetCollection("reward_claims"); ILiteCollection collection3 = liteDatabase.GetCollection("marketplace_quest_credits"); ILiteCollection collection4 = liteDatabase.GetCollection("fishing_catch_credits"); foreach (RankingEntryDocument item in collection.FindAll()) { RankingEntry rankingEntry = ConvertFromDocument(item); if (rankingEntry != null && !string.IsNullOrWhiteSpace(rankingEntry.PlayerName)) { rankingDatabase.Entries.Add(rankingEntry); } } foreach (RewardClaimDocument item2 in collection2.FindAll()) { RewardClaimRecord rewardClaimRecord = ConvertFromDocument(item2); if (rewardClaimRecord != null && !string.IsNullOrWhiteSpace(rewardClaimRecord.PlayerName)) { rankingDatabase.Claims.Add(rewardClaimRecord); } } foreach (MarketplaceQuestCreditDocument item3 in collection3.FindAll()) { MarketplaceQuestCreditRecord marketplaceQuestCreditRecord = ConvertFromDocument(item3); if (marketplaceQuestCreditRecord != null && !string.IsNullOrWhiteSpace(marketplaceQuestCreditRecord.PlayerName) && !string.IsNullOrWhiteSpace(marketplaceQuestCreditRecord.QuestKey)) { rankingDatabase.MarketplaceQuestCredits.Add(marketplaceQuestCreditRecord); } } foreach (FishingCatchCreditDocument item4 in collection4.FindAll()) { FishingCatchCreditRecord fishingCatchCreditRecord = ConvertFromDocument(item4); if (fishingCatchCreditRecord != null && !string.IsNullOrWhiteSpace(fishingCatchCreditRecord.ZdoKey)) { rankingDatabase.FishingCatchCredits.Add(fishingCatchCreditRecord); } } return NormalizeDatabase(rankingDatabase); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Backup do banco tambem falhou: " + ex.Message)); return null; } } private RankingEntryDocument ConvertToDocument(RankingEntry entry) { if (entry == null) { return null; } RankingEntryDocument rankingEntryDocument = new RankingEntryDocument(); rankingEntryDocument.PlayerName = SanitizePlayerName(entry.PlayerName); rankingEntryDocument.Points = entry.Points; rankingEntryDocument.LastReason = SafeReason(entry.LastReason); rankingEntryDocument.LastUpdateUtc = SafeLimit(entry.LastUpdateUtc, 64); rankingEntryDocument.TotalKillsPontuadas = entry.TotalKillsPontuadas; rankingEntryDocument.TotalBossesPontuadas = entry.TotalBossesPontuadas; rankingEntryDocument.KillPointsTotal = entry.KillPointsTotal; rankingEntryDocument.BossPointsTotal = entry.BossPointsTotal; rankingEntryDocument.TotalSkillLevelUpsPontuados = entry.TotalSkillLevelUpsPontuados; rankingEntryDocument.SkillPointsTotal = entry.SkillPointsTotal; rankingEntryDocument.TotalFishingPontuadas = entry.TotalFishingPontuadas; rankingEntryDocument.FishingPointsTotal = entry.FishingPointsTotal; rankingEntryDocument.TotalCraftPontuadas = entry.TotalCraftPontuadas; rankingEntryDocument.CraftPointsTotal = entry.CraftPointsTotal; rankingEntryDocument.TotalFarmJackpotsPontuados = entry.TotalFarmJackpotsPontuados; rankingEntryDocument.FarmJackpotPointsTotal = entry.FarmJackpotPointsTotal; rankingEntryDocument.TotalUniqueCraftJackpotsPontuados = entry.TotalUniqueCraftJackpotsPontuados; rankingEntryDocument.UniqueCraftJackpotPointsTotal = entry.UniqueCraftJackpotPointsTotal; rankingEntryDocument.TotalDeaths = entry.TotalDeaths; rankingEntryDocument.DeathPenaltyPointsTotal = entry.DeathPenaltyPointsTotal; rankingEntryDocument.TotalPointsExchanges = entry.TotalPointsExchanges; rankingEntryDocument.PointsExchangePenaltyTotal = entry.PointsExchangePenaltyTotal; rankingEntryDocument.PointsExchangeCoinsTotal = entry.PointsExchangeCoinsTotal; rankingEntryDocument.ExplorationMapJackpotPointsTotal = entry.ExplorationMapJackpotPointsTotal; rankingEntryDocument.ProgressCounters = SerializeStringIntDictionary(entry.ProgressCounters); rankingEntryDocument.FloatProgressCounters = SerializeStringFloatDictionary(entry.FloatProgressCounters); rankingEntryDocument.GenericJackpotCredits = SerializeStringSet(entry.GenericJackpotCredits); rankingEntryDocument.BossCredits = SerializeStringSet(entry.BossCredits); rankingEntryDocument.SkillLevel100JackpotCredits = SerializeStringSet(entry.SkillLevel100JackpotCredits); return rankingEntryDocument; } private RankingEntry ConvertFromDocument(RankingEntryDocument doc) { if (doc == null) { return null; } RankingEntry rankingEntry = new RankingEntry(); rankingEntry.PlayerName = SanitizePlayerName(doc.PlayerName); rankingEntry.Points = doc.Points; rankingEntry.LastReason = SafeReason(doc.LastReason); rankingEntry.LastUpdateUtc = SafeLimit(doc.LastUpdateUtc, 64); rankingEntry.TotalKillsPontuadas = doc.TotalKillsPontuadas; rankingEntry.TotalBossesPontuadas = doc.TotalBossesPontuadas; rankingEntry.KillPointsTotal = doc.KillPointsTotal; rankingEntry.BossPointsTotal = doc.BossPointsTotal; rankingEntry.TotalSkillLevelUpsPontuados = doc.TotalSkillLevelUpsPontuados; rankingEntry.SkillPointsTotal = doc.SkillPointsTotal; rankingEntry.TotalFishingPontuadas = doc.TotalFishingPontuadas; rankingEntry.FishingPointsTotal = doc.FishingPointsTotal; rankingEntry.TotalCraftPontuadas = doc.TotalCraftPontuadas; rankingEntry.CraftPointsTotal = doc.CraftPointsTotal; rankingEntry.TotalFarmJackpotsPontuados = doc.TotalFarmJackpotsPontuados; rankingEntry.FarmJackpotPointsTotal = doc.FarmJackpotPointsTotal; rankingEntry.TotalUniqueCraftJackpotsPontuados = doc.TotalUniqueCraftJackpotsPontuados; rankingEntry.UniqueCraftJackpotPointsTotal = doc.UniqueCraftJackpotPointsTotal; rankingEntry.TotalDeaths = doc.TotalDeaths; rankingEntry.DeathPenaltyPointsTotal = doc.DeathPenaltyPointsTotal; rankingEntry.TotalPointsExchanges = doc.TotalPointsExchanges; rankingEntry.PointsExchangePenaltyTotal = doc.PointsExchangePenaltyTotal; rankingEntry.PointsExchangeCoinsTotal = doc.PointsExchangeCoinsTotal; rankingEntry.ExplorationMapJackpotPointsTotal = doc.ExplorationMapJackpotPointsTotal; rankingEntry.ProgressCounters = DeserializeStringIntDictionary(doc.ProgressCounters); rankingEntry.FloatProgressCounters = DeserializeStringFloatDictionary(doc.FloatProgressCounters); rankingEntry.GenericJackpotCredits = DeserializeStringSet(doc.GenericJackpotCredits); rankingEntry.BossCredits = DeserializeStringSet(doc.BossCredits); rankingEntry.SkillLevel100JackpotCredits = DeserializeStringSet(doc.SkillLevel100JackpotCredits); return rankingEntry; } private RewardClaimDocument ConvertToDocument(RewardClaimRecord claim) { if (claim == null) { return null; } RewardClaimDocument rewardClaimDocument = new RewardClaimDocument(); rewardClaimDocument.CycleId = SafeKey(claim.CycleId); rewardClaimDocument.PlayerName = SanitizePlayerName(claim.PlayerName); rewardClaimDocument.Rank = claim.Rank; rewardClaimDocument.ClaimedAtUtc = SafeLimit(claim.ClaimedAtUtc, 64); rewardClaimDocument.Status = SafeLimit(claim.Status, 32); rewardClaimDocument.Key = BuildRewardClaimKey(rewardClaimDocument.CycleId, rewardClaimDocument.PlayerName, rewardClaimDocument.Rank); return rewardClaimDocument; } private RewardClaimRecord ConvertFromDocument(RewardClaimDocument doc) { if (doc == null) { return null; } RewardClaimRecord rewardClaimRecord = new RewardClaimRecord(); rewardClaimRecord.CycleId = SafeKey(doc.CycleId); rewardClaimRecord.PlayerName = SanitizePlayerName(doc.PlayerName); rewardClaimRecord.Rank = doc.Rank; rewardClaimRecord.ClaimedAtUtc = SafeLimit(doc.ClaimedAtUtc, 64); rewardClaimRecord.Status = SafeLimit(doc.Status, 32); return rewardClaimRecord; } private MarketplaceQuestCreditDocument ConvertToDocument(MarketplaceQuestCreditRecord credit) { if (credit == null) { return null; } MarketplaceQuestCreditDocument marketplaceQuestCreditDocument = new MarketplaceQuestCreditDocument(); marketplaceQuestCreditDocument.PlayerName = SanitizePlayerName(credit.PlayerName); marketplaceQuestCreditDocument.QuestKey = SafeKey(credit.QuestKey); marketplaceQuestCreditDocument.GrantedAtUtc = SafeLimit(credit.GrantedAtUtc, 64); marketplaceQuestCreditDocument.Key = BuildMarketplaceQuestCreditKey(marketplaceQuestCreditDocument.PlayerName, marketplaceQuestCreditDocument.QuestKey); return marketplaceQuestCreditDocument; } private MarketplaceQuestCreditRecord ConvertFromDocument(MarketplaceQuestCreditDocument doc) { if (doc == null) { return null; } MarketplaceQuestCreditRecord marketplaceQuestCreditRecord = new MarketplaceQuestCreditRecord(); marketplaceQuestCreditRecord.PlayerName = SanitizePlayerName(doc.PlayerName); marketplaceQuestCreditRecord.QuestKey = SafeKey(doc.QuestKey); marketplaceQuestCreditRecord.GrantedAtUtc = SafeLimit(doc.GrantedAtUtc, 64); return marketplaceQuestCreditRecord; } private FishingCatchCreditDocument ConvertToDocument(FishingCatchCreditRecord credit) { if (credit == null) { return null; } FishingCatchCreditDocument fishingCatchCreditDocument = new FishingCatchCreditDocument(); fishingCatchCreditDocument.PlayerName = SanitizePlayerName(credit.PlayerName); fishingCatchCreditDocument.FishPrefab = SafeKey(credit.FishPrefab); fishingCatchCreditDocument.ZdoKey = SafeKey(credit.ZdoKey); fishingCatchCreditDocument.GrantedAtUtc = SafeLimit(credit.GrantedAtUtc, 64); return fishingCatchCreditDocument; } private FishingCatchCreditRecord ConvertFromDocument(FishingCatchCreditDocument doc) { if (doc == null) { return null; } FishingCatchCreditRecord fishingCatchCreditRecord = new FishingCatchCreditRecord(); fishingCatchCreditRecord.PlayerName = SanitizePlayerName(doc.PlayerName); fishingCatchCreditRecord.FishPrefab = SafeKey(doc.FishPrefab); fishingCatchCreditRecord.ZdoKey = SafeKey(doc.ZdoKey); fishingCatchCreditRecord.GrantedAtUtc = SafeLimit(doc.GrantedAtUtc, 64); return fishingCatchCreditRecord; } private string SerializeStringSet(HashSet values) { if (values == null || values.Count == 0) { return ""; } return string.Join("\n", values.Where((string x) => !string.IsNullOrWhiteSpace(x)).Select(SafeKey).Distinct(StringComparer.OrdinalIgnoreCase) .OrderBy((string x) => x, StringComparer.OrdinalIgnoreCase) .ToArray()); } private HashSet DeserializeStringSet(string raw) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return hashSet; } string[] array = raw.Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string value in array) { string text = SafeKey(value); if (!string.IsNullOrWhiteSpace(text)) { hashSet.Add(text); } } return hashSet; } private string SerializeStringIntDictionary(Dictionary values) { if (values == null || values.Count == 0) { return ""; } return string.Join("\n", (from p in values where !string.IsNullOrWhiteSpace(p.Key) select EncodeDatabaseField(SafeKey(p.Key)) + ":" + Mathf.Clamp(p.Value, 0, int.MaxValue)).OrderBy((string x) => x, StringComparer.OrdinalIgnoreCase).ToArray()); } private Dictionary DeserializeStringIntDictionary(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { string text2 = ((text == null) ? "" : text.Trim()); if (string.IsNullOrWhiteSpace(text2)) { continue; } int num = text2.LastIndexOf(':'); if (num <= 0 || num >= text2.Length - 1) { continue; } string text3 = text2.Substring(0, num); string s = text2.Substring(num + 1); if (int.TryParse(s, out var result)) { string value = DecodeDatabaseField(text3); if (string.IsNullOrWhiteSpace(value)) { value = text3; } value = SafeKey(value); if (!string.IsNullOrWhiteSpace(value)) { dictionary[value] = Mathf.Clamp(result, 0, int.MaxValue); } } } return dictionary; } private string SerializeStringFloatDictionary(Dictionary values) { if (values == null || values.Count == 0) { return ""; } return string.Join("\n", (from p in values where !string.IsNullOrWhiteSpace(p.Key) select EncodeDatabaseField(SafeKey(p.Key)) + ":" + Mathf.Clamp(p.Value, 0f, 1000000f).ToString("R", CultureInfo.InvariantCulture)).OrderBy((string x) => x, StringComparer.OrdinalIgnoreCase).ToArray()); } private Dictionary DeserializeStringFloatDictionary(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { string text2 = ((text == null) ? "" : text.Trim()); if (string.IsNullOrWhiteSpace(text2)) { continue; } int num = text2.LastIndexOf(':'); if (num <= 0 || num >= text2.Length - 1) { continue; } string text3 = text2.Substring(0, num); string s = text2.Substring(num + 1); if (float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { string value = DecodeDatabaseField(text3); if (string.IsNullOrWhiteSpace(value)) { value = text3; } value = SafeKey(value); if (!string.IsNullOrWhiteSpace(value)) { dictionary[value] = Mathf.Clamp(result, 0f, 1000000f); } } } return dictionary; } private string EncodeDatabaseField(string value) { try { byte[] bytes = Encoding.UTF8.GetBytes(value ?? ""); return Convert.ToBase64String(bytes); } catch { return ""; } } private string DecodeDatabaseField(string value) { try { if (string.IsNullOrWhiteSpace(value)) { return ""; } byte[] bytes = Convert.FromBase64String(value); return Encoding.UTF8.GetString(bytes); } catch { return ""; } } private void DebugLog(DebugCategory category, string message) { if (_rules == null || !_rules.DebugLogging) { return; } bool flag = false; if (category switch { DebugCategory.Hit => _rules.LogHitReports, DebugCategory.Kill => _rules.LogKillReports, DebugCategory.Skill => _rules.LogSkillReports, DebugCategory.PendingKill => _rules.LogPendingKillReports, DebugCategory.Snapshot => _rules.LogSnapshotRequests, DebugCategory.Points => _rules.LogPointsChanges, _ => true, }) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)$"[{category}] {message}"); } } } public void AddPoints(string playerName, int amount, string reason) { if (IsServerInstance() && _rules.RankingEnabled && !string.IsNullOrWhiteSpace(playerName)) { playerName = SanitizePlayerName(playerName); if (ShouldIgnorePlayerForRanking(playerName)) { DebugLog(DebugCategory.Points, "Pontuação ignorada para admin: " + playerName + " motivo=" + reason); return; } RankingEntry orCreateEntry = GetOrCreateEntry(playerName); ApplyPointsToEntry(orCreateEntry, amount, reason); } } private void ApplyPointsToEntry(RankingEntry entry, int amount, string reason) { if (entry == null) { return; } if (ShouldIgnorePlayerForRanking(entry.PlayerName)) { DebugLog(DebugCategory.Points, "Pontuação ignorada para admin: " + entry.PlayerName + " motivo=" + reason); return; } Dictionary oldRanks = (IsServerInstance() ? GetRankingSnapshot() : new Dictionary(StringComparer.OrdinalIgnoreCase)); entry.Points = Mathf.Clamp(entry.Points + amount, -2147483647, int.MaxValue); entry.LastReason = SafeReason(reason); entry.LastUpdateUtc = DateTime.UtcNow.ToString("O"); RegisterRankingEntry(entry); Dictionary newRanks = (IsServerInstance() ? GetRankingSnapshot() : new Dictionary(StringComparer.OrdinalIgnoreCase)); DebugLog(DebugCategory.Points, "Pontuação alterada: player=" + entry.PlayerName + " amount=" + amount + " total=" + entry.Points + " motivo=" + entry.LastReason); if (IsServerInstance()) { TrySendTop3DiscordWebhooks(oldRanks, newRanks, entry.LastReason); SaveDatabase(); } } private void TrySendTop3DiscordWebhooks(Dictionary oldRanks, Dictionary newRanks, string reason) { try { if (!IsServerInstance() || _cfgDiscordTop3WebhookEnabled == null || !_cfgDiscordTop3WebhookEnabled.Value || _cfgDiscordTop3WebhookUrl == null || string.IsNullOrWhiteSpace(_cfgDiscordTop3WebhookUrl.Value)) { return; } if (oldRanks == null) { oldRanks = new Dictionary(StringComparer.OrdinalIgnoreCase); } if (newRanks == null) { newRanks = new Dictionary(StringComparer.OrdinalIgnoreCase); } HashSet hashSet = new HashSet(oldRanks.Keys, StringComparer.OrdinalIgnoreCase); foreach (string key in newRanks.Keys) { hashSet.Add(key); } foreach (string item in hashSet) { if (string.IsNullOrWhiteSpace(item)) { continue; } int value; int num = (oldRanks.TryGetValue(item, out value) ? value : int.MaxValue); int value2; int num2 = (newRanks.TryGetValue(item, out value2) ? value2 : int.MaxValue); bool flag = num > 3 && num2 >= 1 && num2 <= 3; bool flag2 = num >= 1 && num <= 3 && num2 > 3; bool flag3 = num >= 1 && num <= 3 && num2 >= 1 && num2 <= 3 && num2 > num; if (flag || flag2 || flag3) { int points = FindRankingEntryByName(item)?.Points ?? 0; if (flag) { SendDiscordTop3EnteredWebhook(item, num2, points, reason); } else if (flag2) { SendDiscordTop3LostWebhook(item, num, num2, points, reason); } else if (flag3) { SendDiscordTop3DroppedWebhook(item, num, num2, points, reason); } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Erro ao preparar webhook Top 3 do Discord: " + ex.Message)); } } private RankRewardInfo GetRankRewardInfo(int rank) { return rank switch { 1 => new RankRewardInfo { Rank = 1, MinPoints = Mathf.Max(0, _rules.RewardTop1MinPoints), Label = SafeLimit(_rules.RewardTop1Label, 96), PrefabName = SafeLimit(_rules.RewardTop1Prefab, 96), Amount = Mathf.Max(0, _rules.RewardTop1Amount) }, 2 => new RankRewardInfo { Rank = 2, MinPoints = Mathf.Max(0, _rules.RewardTop2MinPoints), Label = SafeLimit(_rules.RewardTop2Label, 96), PrefabName = SafeLimit(_rules.RewardTop2Prefab, 96), Amount = Mathf.Max(0, _rules.RewardTop2Amount) }, 3 => new RankRewardInfo { Rank = 3, MinPoints = Mathf.Max(0, _rules.RewardTop3MinPoints), Label = SafeLimit(_rules.RewardTop3Label, 96), PrefabName = SafeLimit(_rules.RewardTop3Prefab, 96), Amount = Mathf.Max(0, _rules.RewardTop3Amount) }, _ => null, }; } private string BuildRewardClaimKey(string cycleId, string playerName, int rank) { return SafeLimit(cycleId, 64) + "|" + SanitizePlayerName(playerName) + "|" + Mathf.Clamp(rank, 0, int.MaxValue); } private string BuildPendingRewardKey(long sender, string cycleId, int rank) { return sender + "|" + SafeLimit(cycleId, 64) + "|" + Mathf.Clamp(rank, 0, int.MaxValue); } private bool HasClaimedReward(string cycleId, string playerName, int rank) { if (_database == null || _database.Claims == null) { return false; } string key = BuildRewardClaimKey(cycleId, playerName, rank); return _database.Claims.Any((RewardClaimRecord x) => x != null && string.Equals(BuildRewardClaimKey(x.CycleId, x.PlayerName, x.Rank), key, StringComparison.OrdinalIgnoreCase) && string.Equals(SafeLimit(x.Status, 16), "claimed", StringComparison.OrdinalIgnoreCase)); } private bool HasPendingRewardClaim(string cycleId, string playerName, int rank) { if (_database == null || _database.Claims == null) { return false; } string key = BuildRewardClaimKey(cycleId, playerName, rank); return _database.Claims.Any((RewardClaimRecord x) => x != null && string.Equals(BuildRewardClaimKey(x.CycleId, x.PlayerName, x.Rank), key, StringComparison.OrdinalIgnoreCase) && string.Equals(SafeLimit(x.Status, 16), "pending", StringComparison.OrdinalIgnoreCase)); } private void ReserveRewardClaim(string cycleId, string playerName, int rank) { if (_database == null) { _database = new RankingDatabase(); } if (_database.Claims == null) { _database.Claims = new List(); } string cycleId2 = SafeLimit(cycleId, 64); string playerName2 = SanitizePlayerName(playerName); int rank2 = Mathf.Clamp(rank, 1, int.MaxValue); string key = BuildRewardClaimKey(cycleId2, playerName2, rank2); RewardClaimRecord rewardClaimRecord = _database.Claims.FirstOrDefault((RewardClaimRecord x) => x != null && string.Equals(BuildRewardClaimKey(x.CycleId, x.PlayerName, x.Rank), key, StringComparison.OrdinalIgnoreCase)); if (rewardClaimRecord != null) { rewardClaimRecord.Status = "pending"; rewardClaimRecord.ClaimedAtUtc = ""; return; } _database.Claims.Add(new RewardClaimRecord { CycleId = cycleId2, PlayerName = playerName2, Rank = rank2, ClaimedAtUtc = "", Status = "pending" }); } private void ClearPendingRewardClaim(string cycleId, string playerName, int rank) { if (_database != null && _database.Claims != null) { string key = BuildRewardClaimKey(cycleId, playerName, rank); _database.Claims.RemoveAll((RewardClaimRecord x) => x != null && string.Equals(BuildRewardClaimKey(x.CycleId, x.PlayerName, x.Rank), key, StringComparison.OrdinalIgnoreCase) && string.Equals(SafeLimit(x.Status, 16), "pending", StringComparison.OrdinalIgnoreCase)); } } private void MarkRewardClaimed(string cycleId, string playerName, int rank) { if (_database == null) { _database = new RankingDatabase(); } if (_database.Claims == null) { _database.Claims = new List(); } string cycleId2 = SafeLimit(cycleId, 64); string playerName2 = SanitizePlayerName(playerName); int rank2 = Mathf.Clamp(rank, 1, int.MaxValue); string key = BuildRewardClaimKey(cycleId2, playerName2, rank2); RewardClaimRecord rewardClaimRecord = _database.Claims.FirstOrDefault((RewardClaimRecord x) => x != null && string.Equals(BuildRewardClaimKey(x.CycleId, x.PlayerName, x.Rank), key, StringComparison.OrdinalIgnoreCase)); if (rewardClaimRecord != null) { rewardClaimRecord.Status = "claimed"; rewardClaimRecord.ClaimedAtUtc = DateTime.UtcNow.ToString("O"); return; } _database.Claims.Add(new RewardClaimRecord { CycleId = cycleId2, PlayerName = playerName2, Rank = rank2, ClaimedAtUtc = DateTime.UtcNow.ToString("O"), Status = "claimed" }); } private void FillRewardSnapshotData(SnapshotPlayerData playerData) { if (playerData == null) { return; } playerData.RewardClaimsEnabled = _rules != null && _rules.RewardClaimsEnabled; playerData.RewardCanClaim = false; playerData.RewardAlreadyClaimed = false; playerData.RewardRank = Mathf.Max(0, playerData.Position); playerData.RewardMinPoints = 0; playerData.RewardLabel = ""; playerData.RewardPrefabName = ""; playerData.RewardAmount = 0; playerData.RewardBlockReason = ""; playerData.RewardClaimCycleId = ((_rules != null) ? SafeLimit(_rules.RewardClaimCycleId, 64) : ""); if (_rules == null || !_rules.RewardClaimsEnabled) { playerData.RewardBlockReason = "Resgate de recompensa desativado."; return; } if (!playerData.HasData) { playerData.RewardBlockReason = "Você ainda não possui pontuação no ranking."; return; } RankRewardInfo rankRewardInfo = GetRankRewardInfo(playerData.Position); if (rankRewardInfo == null) { playerData.RewardBlockReason = "Somente Top 1, 2 e 3 podem resgatar."; return; } playerData.RewardRank = rankRewardInfo.Rank; playerData.RewardMinPoints = rankRewardInfo.MinPoints; playerData.RewardLabel = rankRewardInfo.Label ?? ""; playerData.RewardPrefabName = rankRewardInfo.PrefabName ?? ""; playerData.RewardAmount = Mathf.Max(0, rankRewardInfo.Amount); if (string.IsNullOrWhiteSpace(playerData.RewardClaimCycleId)) { playerData.RewardBlockReason = "Ciclo de recompensa não configurado."; } else if (string.IsNullOrWhiteSpace(playerData.RewardPrefabName) || playerData.RewardAmount <= 0) { playerData.RewardBlockReason = "Recompensa não configurada para esta posição."; } else if (playerData.Points < rankRewardInfo.MinPoints) { playerData.RewardBlockReason = "Requer " + rankRewardInfo.MinPoints + " pontos para resgatar."; } else if (HasClaimedReward(playerData.RewardClaimCycleId, playerData.PlayerName, rankRewardInfo.Rank)) { playerData.RewardAlreadyClaimed = true; playerData.RewardBlockReason = "Recompensa já resgatada."; } else { playerData.RewardCanClaim = true; playerData.RewardBlockReason = "Recompensa disponível para resgate."; } } private bool TryAddRewardItemToLocalInventory(string prefabName, int amount, out string message) { message = ""; try { if ((Object)(object)Player.m_localPlayer == (Object)null) { message = "Jogador local não encontrado."; return false; } if (string.IsNullOrWhiteSpace(prefabName)) { message = "Prefab da recompensa não configurado."; return false; } if (amount <= 0) { message = "Quantidade de recompensa inválida."; return false; } GameObject val = null; if ((Object)(object)ObjectDB.instance != (Object)null) { val = ObjectDB.instance.GetItemPrefab(prefabName); } if ((Object)(object)val == (Object)null && (Object)(object)ZNetScene.instance != (Object)null) { val = ZNetScene.instance.GetPrefab(prefabName); } if ((Object)(object)val == (Object)null) { message = "Prefab '" + prefabName + "' não encontrado."; return false; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { message = "Prefab '" + prefabName + "' não é um item válido."; return false; } Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory(); if (inventory == null) { message = "Inventário não encontrado."; return false; } int num = Mathf.Max(0, amount); int num2 = 0; int num3 = 0; int num4 = 1; int num5 = 0; int num6 = 1; string text = prefabName; if (component.m_itemData != null) { num4 = Mathf.Max(1, component.m_itemData.m_quality); num5 = Mathf.Max(0, component.m_itemData.m_variant); if (component.m_itemData.m_shared != null) { num6 = Mathf.Max(1, component.m_itemData.m_shared.m_maxStackSize); if (!string.IsNullOrWhiteSpace(component.m_itemData.m_shared.m_name)) { text = component.m_itemData.m_shared.m_name; } } } while (num > 0) { int num7 = Mathf.Min(num6, num); ItemData val2 = inventory.AddItem(prefabName, num7, num4, num5, 0L, "", false); if (val2 == null) { break; } num2 += num7; num -= num7; } if (num > 0) { num3 = DropExchangeItemOverflowNearPlayer(val, component, num, num6, num4, num5); num -= num3; } if (num > 0) { message = "Não foi possível entregar toda a recompensa."; return false; } try { if (num3 > 0 && num2 > 0) { ((Character)Player.m_localPlayer).Message((MessageType)2, "Recompensa recebida: " + text + " x" + num2 + " no inventário, x" + num3 + " no chão.", 0, (Sprite)null); } else if (num3 > 0) { ((Character)Player.m_localPlayer).Message((MessageType)2, "Inventário cheio. Recompensa caiu no chão: " + text + " x" + num3 + ".", 0, (Sprite)null); } else { ((Character)Player.m_localPlayer).Message((MessageType)2, "Recompensa recebida: " + text + " x" + num2 + ".", 0, (Sprite)null); } } catch { } if (num3 > 0 && num2 > 0) { message = "Parte da recompensa foi para o inventário e o restante caiu no chão."; } else if (num3 > 0) { message = "Inventário cheio: a recompensa caiu no chão."; } else { message = "Recompensa adicionada ao inventário."; } return true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao entregar recompensa: " + ex)); message = "Erro ao entregar recompensa."; return false; } } private bool TryAddExchangeItemToLocalInventoryOrDropOverflow(string prefabName, int amount, out string message) { message = ""; try { if ((Object)(object)Player.m_localPlayer == (Object)null) { message = "Jogador local não encontrado."; return false; } if (string.IsNullOrWhiteSpace(prefabName)) { message = "Prefab do câmbio não configurado."; return false; } if (amount <= 0) { message = "Quantidade de câmbio inválida."; return false; } GameObject val = null; if ((Object)(object)ObjectDB.instance != (Object)null) { val = ObjectDB.instance.GetItemPrefab(prefabName); } if ((Object)(object)val == (Object)null && (Object)(object)ZNetScene.instance != (Object)null) { val = ZNetScene.instance.GetPrefab(prefabName); } if ((Object)(object)val == (Object)null) { message = "Prefab '" + prefabName + "' não encontrado."; return false; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { message = "Prefab '" + prefabName + "' não é um item válido."; return false; } Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory(); if (inventory == null) { message = "Inventário não encontrado."; return false; } int num = Mathf.Max(0, amount); int num2 = 0; int num3 = 0; int num4 = 1; int num5 = 0; int num6 = 1; string text = prefabName; if (component.m_itemData != null) { num4 = Mathf.Max(1, component.m_itemData.m_quality); num5 = Mathf.Max(0, component.m_itemData.m_variant); if (component.m_itemData.m_shared != null) { num6 = Mathf.Max(1, component.m_itemData.m_shared.m_maxStackSize); if (!string.IsNullOrWhiteSpace(component.m_itemData.m_shared.m_name)) { text = component.m_itemData.m_shared.m_name; } } } while (num > 0) { int num7 = Mathf.Min(num6, num); ItemData val2 = inventory.AddItem(prefabName, num7, num4, num5, 0L, "", false); if (val2 == null) { break; } num2 += num7; num -= num7; } if (num > 0) { num3 = DropExchangeItemOverflowNearPlayer(val, component, num, num6, num4, num5); num -= num3; } if (num > 0) { message = "Não foi possível entregar todo o câmbio."; return false; } try { if (num3 > 0 && num2 > 0) { ((Character)Player.m_localPlayer).Message((MessageType)2, "Câmbio recebido: " + text + " x" + num2 + " no inventário, x" + num3 + " no chão.", 0, (Sprite)null); } else if (num3 > 0) { ((Character)Player.m_localPlayer).Message((MessageType)2, "Inventário cheio. Câmbio caiu no chão: " + text + " x" + num3 + ".", 0, (Sprite)null); } else { ((Character)Player.m_localPlayer).Message((MessageType)2, "Câmbio recebido: " + text + " x" + num2 + ".", 0, (Sprite)null); } } catch { } if (num3 > 0 && num2 > 0) { message = "Parte foi para o inventário e o restante caiu no chão."; } else if (num3 > 0) { message = "Inventário cheio: as moedas caíram no chão."; } else { message = "Moedas adicionadas ao inventário."; } return true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao entregar moedas do câmbio: " + ex)); message = "Erro ao entregar moedas do câmbio."; return false; } } private int DropExchangeItemOverflowNearPlayer(GameObject itemPrefab, ItemDrop sourceDrop, int amount, int maxStackSize, int quality, int variant) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)itemPrefab == (Object)null || amount <= 0) { return 0; } int num = amount; int num2 = 0; Transform transform = ((Component)Player.m_localPlayer).transform; Vector3 val = transform.position + transform.forward * 1.25f + Vector3.up * 0.65f; Vector3 val2 = default(Vector3); while (num > 0) { int num3 = Mathf.Min(Mathf.Max(1, maxStackSize), num); ((Vector3)(ref val2))..ctor(Random.Range(-0.35f, 0.35f), 0f, Random.Range(-0.35f, 0.35f)); GameObject val3 = Object.Instantiate(itemPrefab, val + val2, Quaternion.identity); ItemDrop val4 = (((Object)(object)val3 != (Object)null) ? val3.GetComponent() : null); if ((Object)(object)val4 != (Object)null && val4.m_itemData != null) { val4.m_itemData.m_stack = num3; val4.m_itemData.m_quality = Mathf.Max(1, quality); val4.m_itemData.m_variant = Mathf.Max(0, variant); } Rigidbody val5 = (((Object)(object)val3 != (Object)null) ? val3.GetComponent() : null); if ((Object)(object)val5 != (Object)null) { val5.linearVelocity = transform.forward * 1.5f + Vector3.up * 1.25f; } num2 += num3; num -= num3; } return num2; } private void RegisterRpcsIfNeeded() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null && (!_rpcsRegistered || _registeredRoutedRpcInstance != instance)) { instance.Register("glitnir.ranking.requestsnapshot", (Action)RPC_RequestSnapshot); instance.Register("glitnir.ranking.receivesnapshot", (Action)RPC_ReceiveSnapshot); instance.Register("glitnir.ranking.reporthit", (Action)RPC_ReportHit); instance.Register("glitnir.ranking.reportkill", (Action)RPC_ReportKill); instance.Register("glitnir.ranking.reportskillgain", (Action)RPC_ReportSkillGain); instance.Register("glitnir.ranking.requestrewardclaim", (Action)RPC_RequestRewardClaim); instance.Register("glitnir.ranking.grantrewarditem", (Action)RPC_GrantRewardItem); instance.Register("glitnir.ranking.finalizerewardclaim", (Action)RPC_FinalizeRewardClaim); instance.Register("glitnir.ranking.rewardclaimfeedback", (Action)RPC_RewardClaimFeedback); instance.Register("glitnir.ranking.requestpointsexchange", (Action)RPC_RequestPointsExchange); instance.Register("glitnir.ranking.grantexchangecoins", (Action)RPC_GrantExchangeCoins); instance.Register("glitnir.ranking.finalizepointsexchange", (Action)RPC_FinalizePointsExchange); instance.Register("glitnir.ranking.pointsexchangefeedback", (Action)RPC_PointsExchangeFeedback); instance.Register("glitnir.ranking.marketquestcomplete", (Action)RPC_ReportMarketplaceQuestComplete); instance.Register("glitnir.ranking.reportfishcaught", (Action)RPC_ReportFishCaught); instance.Register("glitnir.ranking.reportcrafteditem", (Action)RPC_ReportCraftedItem); instance.Register("glitnir.ranking.reportfarmharvest", (Action)RPC_ReportFarmHarvest); instance.Register("glitnir.ranking.reportplayerdeath", (Action)RPC_ReportPlayerDeath); instance.Register("glitnir.ranking.reportexplorationmap", (Action)RPC_ReportExplorationMap); _registeredRoutedRpcInstance = instance; _rpcsRegistered = true; } } private void Awake() { //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; _rulesFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; _uiFolderPath = string.Empty; _uiFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; _databaseFilePath = Path.Combine(Paths.ConfigPath, "GlitnirRanking", "SavedData", "DB.db"); _legacyDatabaseFilePath = Path.Combine(Paths.ConfigPath, "glitnir.ranking.database.legacy.db"); if (IsDedicatedServerInstance()) { EnsureDatabaseDirectoryExists(); } LoadUiSettings(); _cfgDiscordTop3WebhookEnabled = ((BaseUnityPlugin)this).Config.Bind("Discord", "EnableTop3Webhook", true, "Ativa o webhook do Discord quando um jogador entra no Top 3 do ranking."); _cfgDiscordTop3WebhookUrl = ((BaseUnityPlugin)this).Config.Bind("Discord", "Top3WebhookUrl", "", "URL do webhook do Discord para anunciar ascensão ao Top 3."); _cfgIgnoreAdminsInRanking = ((BaseUnityPlugin)this).Config.Bind("Ranking", "IgnoreAdminsInRanking", true, "Se ativo, jogadores presentes na adminlist do servidor nao pontuam nem aparecem no ranking."); _cfgIgnoredAdminNames = ((BaseUnityPlugin)this).Config.Bind("Ranking", "IgnoredAdminNames", "", "Fallback opcional: nomes ou IDs de admins para ignorar. Separe por virgula, ponto e virgula ou quebra de linha."); InitializeSyncedRulesConfig(); SetupRulesConfigWatcher(); if (IsDedicatedServerInstance()) { LoadDatabase(); } else { _database = new RankingDatabase(); } _harmony = new Harmony("com.glitnir.ranking"); _harmony.PatchAll(); try { GlitnirAAACraftMaxClamp.Apply(_harmony); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Glitnir Ranking] Falha ao aplicar AAA Craft Max Clamp: " + ex.Message)); } } private void OnDestroy() { try { if (IsDedicatedServerInstance()) { SaveDatabase(); } try { if (_rulesConfigWatcher != null) { _rulesConfigWatcher.EnableRaisingEvents = false; _rulesConfigWatcher.Dispose(); _rulesConfigWatcher = null; } } catch { } DestroyRankingInputBlocker(); SaveUiSettings(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch { } } private void Update() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) RegisterRpcsIfNeeded(); ProcessRulesConfigReloadIfNeeded(); RefreshRulesFromSyncedConfigIfNeeded(); if (!Application.isBatchMode) { CleanupOldLocalRecentHits(); CheckInitialServerSnapshotRequest(); } if (Application.isBatchMode) { CleanupOldDamageCredits(); CleanupOldProcessedKills(); ProcessPendingKillChecks(); return; } if ((int)_uiToggleKey != 0 && Input.GetKeyDown(_uiToggleKey)) { ToggleRankingHud(); } if (_hudVisible) { if (Input.GetKeyDown((KeyCode)27)) { CloseRankingHudAndCollapseAll(); return; } _clientRefreshTimer += Time.unscaledDeltaTime; if (_clientRefreshTimer >= 1f) { _clientRefreshTimer = 0f; RequestSnapshotFromServer(); } ProcessLocalExplorationMapReport(); } else { SetRankingInputBlockerVisible(visible: false); } if (IsServerInstance()) { CleanupOldDamageCredits(); CleanupOldProcessedKills(); ProcessPendingKillChecks(); } } private bool IsServerInstance() { if (Application.isBatchMode) { return true; } try { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } } catch { } return false; } private bool IsDedicatedServerInstance() { return Application.isBatchMode; } private bool IsConnectedToDedicatedServer() { return !Application.isBatchMode && GetServerPeerUid() != 0; } public void ReportLocalMarketplaceQuestCompletion(int questUid) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown try { if (_rules != null && _rules.RankingEnabled && _rules.EnableMarketplaceQuestPoints && TryGetMarketplaceQuestRule(questUid, out var rule) && !((Object)(object)Player.m_localPlayer == (Object)null) && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(questUid); val.Write(SafeMarketplaceQuestKey(rule.QuestKey)); val.Write(rule.Points); val.Write(SafeLimit(Player.m_localPlayer.GetPlayerName(), 32)); ZRoutedRpc.instance.InvokeRoutedRPC(GetServerPeerUid(), "glitnir.ranking.marketquestcomplete", new object[1] { val }); DebugLog(DebugCategory.Points, "Quest Marketplace reportada: uid=" + questUid + " key=" + rule.QuestKey + " points=" + rule.Points); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao reportar conclusão de quest do Marketplace: " + ex)); } } private void RPC_ReportMarketplaceQuestComplete(long sender, ZPackage pkg) { try { if (!IsServerInstance() || pkg == null) { return; } int questUid = pkg.ReadInt(); string questKey = SafeMarketplaceQuestKey(pkg.ReadString()); int requestedPoints = Mathf.Clamp(pkg.ReadInt(), -2147483647, int.MaxValue); string text = SanitizePlayerName(pkg.ReadString()); string text2 = ResolvePlayerNameFromSender(sender); if (string.IsNullOrWhiteSpace(text2)) { text2 = text; } if (!string.IsNullOrWhiteSpace(text2)) { if (ShouldIgnoreSenderForRanking(sender, text2)) { DebugLog(DebugCategory.Points, "Quest Marketplace ignorada para admin: " + text2); } else { ProcessMarketplaceQuestReport(text2, questUid, questKey, requestedPoints, "rpc-marketplace-quest"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_ReportMarketplaceQuestComplete: " + ex)); } } private void ProcessMarketplaceQuestReport(string playerName, int questUid, string questKey, int requestedPoints, string source) { try { if (!IsServerInstance() || _rules == null || !_rules.RankingEnabled || !_rules.EnableMarketplaceQuestPoints) { return; } if (!TryGetMarketplaceQuestRule(questUid, out var rule)) { DebugLog(DebugCategory.Points, "Quest do Marketplace ignorada sem regra: uid=" + questUid + " key=" + questKey + " source=" + source); return; } if (!string.IsNullOrWhiteSpace(questKey) && !string.Equals(rule.QuestKey, questKey, StringComparison.OrdinalIgnoreCase)) { DebugLog(DebugCategory.Points, "Quest do Marketplace rejeitada por chave divergente: uid=" + questUid + " key=" + questKey + " esperado=" + rule.QuestKey); return; } playerName = SanitizePlayerName(playerName); if (ShouldIgnorePlayerForRanking(playerName)) { DebugLog(DebugCategory.Points, "Quest Marketplace ignorada para admin: " + playerName); return; } if (HasMarketplaceQuestCredit(playerName, rule.QuestKey)) { DebugLog(DebugCategory.Points, "Quest Marketplace já creditada: player=" + playerName + " key=" + rule.QuestKey); return; } RankingEntry orCreateEntry = GetOrCreateEntry(playerName); if (orCreateEntry != null) { int points = rule.Points; if (requestedPoints != points) { DebugLog(DebugCategory.Points, "Quest Marketplace com pontos normalizados: player=" + playerName + " key=" + rule.QuestKey + " solicitado=" + requestedPoints + " configurado=" + points); } ApplyPointsToEntry(orCreateEntry, points, "Quest Marketplace: " + rule.QuestKey); MarkMarketplaceQuestCredit(playerName, rule.QuestKey); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao processar conclusão de quest do Marketplace: " + ex)); } } private string BuildMarketplaceQuestCreditKey(string playerName, string questKey) { return SanitizePlayerName(playerName).ToLowerInvariant() + "|" + SafeMarketplaceQuestKey(questKey); } private bool HasMarketplaceQuestCredit(string playerName, string questKey) { return HasMarketplaceQuestCreditCached(playerName, questKey); } private void MarkMarketplaceQuestCredit(string playerName, string questKey) { if (_database == null) { _database = new RankingDatabase(); } if (_database.MarketplaceQuestCredits == null) { _database.MarketplaceQuestCredits = new List(); } if (!HasMarketplaceQuestCredit(playerName, questKey)) { _database.MarketplaceQuestCredits.Add(new MarketplaceQuestCreditRecord { PlayerName = SanitizePlayerName(playerName), QuestKey = SafeMarketplaceQuestKey(questKey), GrantedAtUtc = DateTime.UtcNow.ToString("O") }); if (IsServerInstance()) { SaveDatabase(); } } } private void ProcessLocalExplorationMapReport() { //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Expected O, but got Unknown try { if (_rules == null || !_rules.RankingEnabled || !_rules.EnableExplorationJackpots || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Minimap.instance == (Object)null) { return; } _explorationReportTimer += Time.unscaledDeltaTime; if (_explorationReportTimer < 15f) { return; } _explorationReportTimer = 0f; float localMapExplorationPercent = GetLocalMapExplorationPercent(); if (!(localMapExplorationPercent < 0f) && !(localMapExplorationPercent <= _lastReportedMapExplorePercent + 0.05f)) { _lastReportedMapExplorePercent = localMapExplorationPercent; long serverPeerUid = GetServerPeerUid(); if (serverPeerUid != 0L && _rpcsRegistered && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(localMapExplorationPercent); val.Write(SafeLimit(Player.m_localPlayer.GetPlayerName(), 32)); ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerUid, "glitnir.ranking.reportexplorationmap", new object[1] { val }); DebugLog(DebugCategory.Points, "Exploração de mapa reportada: " + localMapExplorationPercent + "%"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao reportar exploração de mapa: " + ex.Message)); } } private float GetLocalMapExplorationPercent() { try { Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null) { return -1f; } FieldInfo fieldInfo = AccessTools.Field(typeof(Minimap), "m_explored"); if (fieldInfo == null) { return -1f; } if (!(fieldInfo.GetValue(instance) is bool[] array) || array.Length == 0) { return -1f; } int num = 0; for (int i = 0; i < array.Length; i++) { if (array[i]) { num++; } } float num2 = (float)num * 100f / (float)Mathf.Max(1, array.Length); return Mathf.Clamp(num2, 0f, 100f); } catch { return -1f; } } private void RPC_ReportExplorationMap(long sender, ZPackage pkg) { try { if (IsServerInstance() && pkg != null) { float percent = Mathf.Clamp(pkg.ReadSingle(), 0f, 100f); string text = SanitizePlayerName(pkg.ReadString()); string text2 = ResolvePlayerNameFromSender(sender); if (string.IsNullOrWhiteSpace(text2)) { text2 = text; } ProcessExplorationMapReport(text2, percent); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_ReportExplorationMap: " + ex)); } } private void ProcessExplorationMapReport(string playerName, float percent) { try { if (!IsServerInstance() || _rules == null || !_rules.RankingEnabled || !_rules.EnableExplorationJackpots) { return; } playerName = SanitizePlayerName(playerName); if (string.IsNullOrWhiteSpace(playerName) || ShouldIgnorePlayerForRanking(playerName) || _rules.ExplorationMapJackpots == null || _rules.ExplorationMapJackpots.Count == 0) { return; } RankingEntry orCreateEntry = GetOrCreateEntry(playerName); if (orCreateEntry == null) { return; } if (orCreateEntry.ProgressCounters == null) { orCreateEntry.ProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); } if (orCreateEntry.GenericJackpotCredits == null) { orCreateEntry.GenericJackpotCredits = new HashSet(StringComparer.OrdinalIgnoreCase); } if (orCreateEntry.FloatProgressCounters == null) { orCreateEntry.FloatProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); } float num = 0f; int value = 0; if (orCreateEntry.FloatProgressCounters.TryGetValue("ExplorationMap:Percent", out var value2)) { num = value2; } else if (orCreateEntry.ProgressCounters.TryGetValue("ExplorationMap:Percent", out value)) { num = value; } float num2 = Mathf.Clamp(Mathf.Max(num, percent), 0f, 100f); int value3 = Mathf.Clamp(Mathf.FloorToInt(num2), 0, 100); orCreateEntry.FloatProgressCounters["ExplorationMap:Percent"] = num2; orCreateEntry.FloatProgressCounters["Exploration:Map"] = num2; orCreateEntry.FloatProgressCounters["Exploracao:Mapa"] = num2; orCreateEntry.ProgressCounters["ExplorationMap:Percent"] = value3; orCreateEntry.ProgressCounters["Exploration:Map"] = value3; orCreateEntry.ProgressCounters["Exploracao:Mapa"] = value3; string text = SafeLimit(_rules.RewardClaimCycleId, 64); foreach (KeyValuePair item2 in _rules.ExplorationMapJackpots.OrderBy((KeyValuePair p) => p.Key)) { int num3 = Mathf.Clamp(item2.Key, 1, 100); int num4 = Mathf.Max(0, item2.Value); if (!(num2 < (float)num3) && num4 > 0) { string item = text + ":ExplorationMap:" + num3; if (!orCreateEntry.GenericJackpotCredits.Contains(item)) { orCreateEntry.GenericJackpotCredits.Add(item); ApplyPointsToEntry(orCreateEntry, num4, "Exploração do mapa: " + num3 + "%"); orCreateEntry.ExplorationMapJackpotPointsTotal = Mathf.Clamp(orCreateEntry.ExplorationMapJackpotPointsTotal + num4, 0, int.MaxValue); } } } SaveDatabase(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao processar exploração de mapa: " + ex)); } } private void EnsureUiDirectoryExists() { } private void MigrateLegacyUiSettingsIfNeeded() { try { string text = Path.Combine(Paths.ConfigPath, "glitnir.ranking.ui.cfg"); if (!string.IsNullOrWhiteSpace(_uiFilePath) && !string.Equals(text, _uiFilePath, StringComparison.OrdinalIgnoreCase) && !File.Exists(_uiFilePath) && File.Exists(text)) { File.Copy(text, _uiFilePath, overwrite: true); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao migrar configuração antiga da UI do ranking: " + ex)); } } private void LoadUiSettings() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) try { _uiToggleKey = ((BaseUnityPlugin)this).Config.Bind("UI", "HudShortcutKey", _uiToggleKey, "Tecla para abrir/fechar o HUD. Use nomes do Unity KeyCode, ex.: F, G, Alpha1, None.").Value; _uiUseSystemFonts = ((BaseUnityPlugin)this).Config.Bind("UI", "UseSystemFonts", _uiUseSystemFonts, "Ative apenas se o cliente renderizar bem fontes instaladas no sistema.").Value; _uiTitleFontNames = ((BaseUnityPlugin)this).Config.Bind("UI", "TitleFontNames", _uiTitleFontNames, "Fontes opcionais para titulos, separadas por |.").Value; _uiBodyFontNames = ((BaseUnityPlugin)this).Config.Bind("UI", "BodyFontNames", _uiBodyFontNames, "Fontes opcionais para texto, separadas por |.").Value; _uiAccentFontNames = ((BaseUnityPlugin)this).Config.Bind("UI", "AccentFontNames", _uiAccentFontNames, "Fontes opcionais para detalhes/acento, separadas por |.").Value; ((Rect)(ref _iconRect)).x = ((BaseUnityPlugin)this).Config.Bind("UI", "IconX", ((Rect)(ref _iconRect)).x, "Posição X do botão do ranking.").Value; ((Rect)(ref _iconRect)).y = ((BaseUnityPlugin)this).Config.Bind("UI", "IconY", ((Rect)(ref _iconRect)).y, "Posição Y do botão do ranking.").Value; float value = ((BaseUnityPlugin)this).Config.Bind("UI", "IconSize", ((Rect)(ref _iconRect)).width, "Tamanho do botão do ranking.").Value; ((Rect)(ref _iconRect)).width = Mathf.Clamp(value, 36f, 120f); ((Rect)(ref _iconRect)).height = ((Rect)(ref _iconRect)).width; _uiIconZoom = Mathf.Clamp(((BaseUnityPlugin)this).Config.Bind("UI", "IconZoom", _uiIconZoom, "Zoom interno do ícone do botão.").Value, 0.75f, 1.3f); ((Rect)(ref _windowRect)).x = ((BaseUnityPlugin)this).Config.Bind("UI", "WindowX", ((Rect)(ref _windowRect)).x, "Posição X da janela do ranking.").Value; ((Rect)(ref _windowRect)).y = ((BaseUnityPlugin)this).Config.Bind("UI", "WindowY", ((Rect)(ref _windowRect)).y, "Posição Y da janela do ranking.").Value; ((Rect)(ref _windowRect)).width = Mathf.Max(420f, ((BaseUnityPlugin)this).Config.Bind("UI", "WindowW", ((Rect)(ref _windowRect)).width, "Largura da janela do ranking.").Value); ((Rect)(ref _windowRect)).height = Mathf.Max(760f, ((BaseUnityPlugin)this).Config.Bind("UI", "WindowH", ((Rect)(ref _windowRect)).height, "Altura da janela do ranking.").Value); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao carregar UI do ranking: " + ex)); } } private void SaveUiSettings() { try { ((BaseUnityPlugin)this).Config.Save(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao salvar configuração do ranking: " + ex)); } } private Texture2D LoadTextureFromUiFolder(params string[] fileNames) { if (fileNames == null || fileNames.Length == 0) { return null; } foreach (string text in fileNames) { if (!string.IsNullOrWhiteSpace(text)) { Texture2D val = LoadTextureFromUiFolder(text); if ((Object)(object)val != (Object)null) { return val; } } } return null; } private Texture2D LoadTextureFromUiFolder(string fileName) { Texture2D val = LoadEmbeddedTexture(fileName); if ((Object)(object)val != (Object)null) { return val; } try { if (string.IsNullOrWhiteSpace(_uiFolderPath)) { return null; } string text = Path.Combine(_uiFolderPath, fileName); if (!File.Exists(text)) { return null; } byte[] array = File.ReadAllBytes(text); if (array == null || array.Length == 0) { if (_missingUiTextureWarnings.Add(fileName + ":empty")) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking UI] Arquivo vazio ou inválido: " + text)); } return null; } Texture2D val2 = CreateTextureFromBytes(array, fileName); if ((Object)(object)val2 != (Object)null) { return val2; } if (_missingUiTextureWarnings.Add(fileName + ":decoder")) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking UI] Não foi possível decodificar a textura: " + text + ". Verifique se o PNG/JPG é válido.")); } } catch (Exception ex) { if (_missingUiTextureWarnings.Add(fileName + ":exception")) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking UI] Falha ao carregar textura " + fileName + ": " + ex.Message)); } } return null; } private Texture2D LoadEmbeddedTexture(string fileName) { if (string.IsNullOrWhiteSpace(fileName)) { return null; } try { Assembly executingAssembly = Assembly.GetExecutingAssembly(); if (executingAssembly == null) { return null; } string[] manifestResourceNames = executingAssembly.GetManifestResourceNames(); if (manifestResourceNames == null || manifestResourceNames.Length == 0) { return null; } string normalizedFileName = fileName.Replace('/', '.').Replace('\\', '.'); StringComparison comparison = StringComparison.OrdinalIgnoreCase; string text = manifestResourceNames.FirstOrDefault((string name) => !string.IsNullOrWhiteSpace(name) && (name.Equals(normalizedFileName, comparison) || name.EndsWith("." + normalizedFileName, comparison) || name.EndsWith(fileName, comparison) || name.IndexOf(normalizedFileName, comparison) >= 0)); if (string.IsNullOrWhiteSpace(text)) { return null; } using Stream stream = executingAssembly.GetManifestResourceStream(text); if (stream == null) { return null; } byte[] array = ReadAllBytes(stream); if (array == null || array.Length == 0) { return null; } Texture2D val = CreateTextureFromBytes(array, text); if ((Object)(object)val != (Object)null) { return val; } } catch (Exception ex) { if (_missingUiTextureWarnings.Add(fileName + ":embeddedexception")) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking UI] Falha ao carregar textura embutida " + fileName + ": " + ex.Message)); } } return null; } private Texture2D CreateTextureFromBytes(byte[] bytes, string textureName) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown if (bytes == null || bytes.Length == 0) { return null; } Texture2D val = null; try { val = new Texture2D(2, 2, (TextureFormat)5, false); ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; ((Object)val).name = textureName; if (TryLoadTextureBytes(val, bytes)) { return val; } } catch (Exception ex) { if (_missingUiTextureWarnings.Add((textureName ?? "texture") + ":createtextureexception")) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking UI] Falha ao criar textura " + textureName + ": " + ex.Message)); } } if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } return null; } private byte[] ReadAllBytes(Stream stream) { if (stream == null) { return null; } if (stream is MemoryStream memoryStream) { return memoryStream.ToArray(); } using MemoryStream memoryStream2 = new MemoryStream(); stream.CopyTo(memoryStream2); return memoryStream2.ToArray(); } private bool TryLoadTextureBytes(Texture2D texture, byte[] bytes) { if ((Object)(object)texture == (Object)null || bytes == null || bytes.Length == 0) { return false; } try { Type type = FindTypeInLoadedAssemblies("UnityEngine.ImageConversion"); if (type != null) { MethodInfo method = type.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(Texture2D), typeof(byte[]) }, null); if (method != null) { if (!(method.Invoke(null, new object[2] { texture, bytes }) is bool result)) { return true; } return result; } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking UI] Falha no carregamento refletido da textura: " + ex.Message)); } return false; } private Type FindTypeInLoadedAssemblies(string fullTypeName) { if (string.IsNullOrWhiteSpace(fullTypeName)) { return null; } try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (!(assembly == null)) { Type type = assembly.GetType(fullTypeName, throwOnError: false); if (type != null) { return type; } } } } catch { } return null; } private Texture2D CreateSolidTexture(Color color) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_000e: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(1, 1, (TextureFormat)5, false); val.SetPixel(0, 0, color); val.Apply(false, true); ((Texture)val).wrapMode = (TextureWrapMode)1; return val; } private void EnsureUiTexturesLoaded() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) if (!_uiTexturesLoaded) { _uiTransparentTexture = CreateSolidTexture(new Color(0f, 0f, 0f, 0f)); _uiWhiteTexture = CreateSolidTexture(new Color(1f, 1f, 1f, 1f)); _uiPanelTexture = CreateSolidTexture(new Color(0.05f, 0.04f, 0.03f, 0.72f)); _uiBackgroundTexture = LoadTextureFromUiFolder("background_epic_forest_transparent.png"); _uiRankingIconTexture = LoadTextureFromUiFolder("ranking_icon.png"); _uiTitleRankingTexture = LoadTextureFromUiFolder("title_ranking_glitnir_transparent.png"); _uiTitleTopTexture = LoadTextureFromUiFolder("topRanking.png", "title_top_glitnir_transparent.png", "title_top_glitnir.png", "title_top_ranking.png"); _uiTitleGuideTexture = LoadTextureFromUiFolder("guiaHonra.png", "guideHonor.png", "title_guia_honra_transparent.png", "title_guia_de_honra_transparent.png", "title_guide_honor.png"); _uiTitlePlayerTexture = LoadTextureFromUiFolder("title_seu_desempenho_transparent.png"); _uiTitleActionsTexture = LoadTextureFromUiFolder("title_oraculo_ranking_transparent.png", "title_oraculo_do_ranking_transparent.png", "oraculo_ranking.png", "oraculo_do_ranking.png"); _uiRank1IconTexture = LoadTextureFromUiFolder("icon/rank_1.png", "icons/rank_1.png", "rank_1.png"); _uiRank2IconTexture = LoadTextureFromUiFolder("icon/rank_2.png", "icons/rank_2.png", "rank_2.png"); _uiRank3IconTexture = LoadTextureFromUiFolder("icon/rank_3.png", "icons/rank_3.png", "rank_3.png"); LoadRuleCategoryIcons(); _uiTexturesLoaded = true; } } private void LoadRuleCategoryIcons() { _uiRuleCategoryIcons.Clear(); RegisterRuleCategoryIcon("Combate", "0 (5)"); RegisterRuleCategoryIcon("Bosses", "0 (14)"); RegisterRuleCategoryIcon("Skills", "0 (1)"); RegisterRuleCategoryIcon("Quests", "0 (31)"); RegisterRuleCategoryIcon("Pesca", "0 (32)"); RegisterRuleCategoryIcon("Exploração", "0 (27)"); RegisterRuleCategoryIcon("Exploracao", "0 (27)"); RegisterRuleCategoryIcon("Exploration", "0 (27)"); RegisterRuleCategoryIcon("Exploração", "icon_exploration.png"); RegisterRuleCategoryIcon("Exploracao", "icon_exploration.png"); RegisterRuleCategoryIcon("Exploration", "icon_exploration.png"); RegisterRuleCategoryIcon("Produção", "0 (26)"); RegisterRuleCategoryIcon("Cultivo", "0 (11)"); RegisterRuleCategoryIcon("Conquistas", "0 (29)"); RegisterRuleCategoryIcon("Penalidades", "0 (23)"); RegisterRuleCategoryIcon("Resumo", "0 (29)"); RegisterRuleCategoryIcon("Origem", "0 (12)"); RegisterRuleCategoryIcon("Último registro", "0 (7)"); RegisterRuleCategoryIcon("Recompensa", "0 (29)"); RegisterRuleCategoryIcon("Configuração", "0 (27)"); } private void RegisterRuleCategoryIcon(string categoryKey, string iconFileNameWithoutExtension) { if (!string.IsNullOrWhiteSpace(categoryKey) && !string.IsNullOrWhiteSpace(iconFileNameWithoutExtension)) { string text = (iconFileNameWithoutExtension.EndsWith(".png", StringComparison.OrdinalIgnoreCase) ? iconFileNameWithoutExtension : (iconFileNameWithoutExtension + ".png")); Texture2D val = LoadTextureFromUiFolder("icon/" + text, "icons/" + text, text); if ((Object)(object)val != (Object)null) { _uiRuleCategoryIcons[categoryKey] = val; } } } private bool IsLocalPlayerAttackAuthor(HitData hit) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (hit == null || (Object)(object)Player.m_localPlayer == (Object)null) { return false; } try { ZDOID attacker = hit.m_attacker; ZDOID zDOID = ((Character)Player.m_localPlayer).GetZDOID(); if (!((ZDOID)(ref attacker)).IsNone() && !((ZDOID)(ref zDOID)).IsNone() && ((ZDOID)(ref attacker)).UserID == ((ZDOID)(ref zDOID)).UserID && ((ZDOID)(ref attacker)).ID == ((ZDOID)(ref zDOID)).ID) { return true; } } catch { } try { Character attacker2 = hit.GetAttacker(); if ((Object)(object)attacker2 == (Object)(object)Player.m_localPlayer) { return true; } } catch { } return false; } private string GetLocalPlayerKey() { try { if ((Object)(object)Player.m_localPlayer != (Object)null) { return GetZdoKey((Character)(object)Player.m_localPlayer); } } catch { } return ""; } internal void ReportLocalPlayerDeathToServer() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown try { if (!((Object)(object)Player.m_localPlayer == (Object)null) && ZRoutedRpc.instance != null) { long serverPeerUid = GetServerPeerUid(); ZPackage val = new ZPackage(); val.Write(SafeLimit(Player.m_localPlayer.GetPlayerName(), 32)); ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerUid, "glitnir.ranking.reportplayerdeath", new object[1] { val }); DebugLog(DebugCategory.Points, "Cliente reportando morte: player=" + GetLocalPlayerName()); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao enviar morte local: " + ex)); } } private void RPC_ReportPlayerDeath(long sender, ZPackage pkg) { try { if (IsServerInstance() && pkg != null) { string text = SanitizePlayerName(pkg.ReadString()); string text2 = ResolvePlayerNameFromSender(sender); if (string.IsNullOrWhiteSpace(text2)) { text2 = text; } if (ShouldIgnoreSenderForRanking(sender, text2)) { DebugLog(DebugCategory.Points, "Morte ignorada para admin: " + text2); } else { ProcessPlayerDeathReport(text2, "rpc-death"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_ReportPlayerDeath: " + ex)); } } private void ProcessPlayerDeathReport(string playerName, string source) { try { if (!IsServerInstance() || _rules == null || !_rules.RankingEnabled || !_rules.EnableDeathPenalty) { return; } playerName = SanitizePlayerName(playerName); if (string.IsNullOrWhiteSpace(playerName)) { return; } if (ShouldIgnorePlayerForRanking(playerName)) { DebugLog(DebugCategory.Points, "Morte ignorada para admin: " + playerName); return; } RankingEntry orCreateEntry = GetOrCreateEntry(playerName); if (orCreateEntry == null) { return; } if (orCreateEntry.ProgressCounters == null) { orCreateEntry.ProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); } if (orCreateEntry.GenericJackpotCredits == null) { orCreateEntry.GenericJackpotCredits = new HashSet(StringComparer.OrdinalIgnoreCase); } string key = "DeathPenalty:Deaths"; int value = 0; orCreateEntry.ProgressCounters.TryGetValue(key, out value); value = Mathf.Clamp(value + 1, 0, int.MaxValue); orCreateEntry.ProgressCounters[key] = value; orCreateEntry.TotalDeaths = value; DebugLog(DebugCategory.Points, "Morte registrada: player=" + playerName + " deaths=" + value + " source=" + source); if (_rules.DeathPenaltyUseMultiplier) { int num = Mathf.Max(0, _rules.DeathPenaltyPerDeath); if (num > 0) { ApplyPointsToEntry(orCreateEntry, -num, "Penalidade por morte: " + value + "x -" + num); orCreateEntry.DeathPenaltyPointsTotal = Mathf.Clamp(orCreateEntry.DeathPenaltyPointsTotal + num, 0, int.MaxValue); } SaveDatabase(); return; } if (_rules.DeathPenaltyRules != null && _rules.DeathPenaltyRules.Count > 0) { string text = SafeLimit(_rules.RewardClaimCycleId, 64); foreach (KeyValuePair item2 in _rules.DeathPenaltyRules.OrderBy((KeyValuePair p) => p.Key)) { int num2 = Mathf.Max(1, item2.Key); int num3 = Mathf.Max(0, item2.Value); if (value < num2) { continue; } string item = text + ":DeathPenalty:" + num2; if (!orCreateEntry.GenericJackpotCredits.Contains(item)) { orCreateEntry.GenericJackpotCredits.Add(item); if (num3 > 0) { ApplyPointsToEntry(orCreateEntry, -num3, "Penalidade por morte: " + value + " mortes"); orCreateEntry.DeathPenaltyPointsTotal = Mathf.Clamp(orCreateEntry.DeathPenaltyPointsTotal + num3, 0, int.MaxValue); } } } } SaveDatabase(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao processar penalidade por morte: " + ex)); } } private void IncrementProgressCounter(RankingEntry entry, string category, string key, int amount) { if (entry != null && !string.IsNullOrWhiteSpace(category) && !string.IsNullOrWhiteSpace(key) && amount > 0) { if (entry.ProgressCounters == null) { entry.ProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); } string key2 = SafeKey(category) + ":" + SafeKey(key); int value = 0; entry.ProgressCounters.TryGetValue(key2, out value); entry.ProgressCounters[key2] = Mathf.Clamp(value + amount, 0, int.MaxValue); } } private void IncrementProgressCounterAlias(RankingEntry entry, string category, string key, int amount) { IncrementProgressCounter(entry, category, key, amount); } private void AddProgressAndCheckJackpot(RankingEntry entry, string category, string key, int amount, JackpotRule rule, string reason, bool unique) { if (entry == null || rule == null || rule.RequiredAmount <= 0 || rule.Points <= 0) { return; } if (entry.ProgressCounters == null) { entry.ProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); } if (entry.GenericJackpotCredits == null) { entry.GenericJackpotCredits = new HashSet(StringComparer.OrdinalIgnoreCase); } string text = SafeKey(category) + ":" + SafeKey(key); string item = SafeLimit(_rules.RewardClaimCycleId, 64) + ":" + text + ":" + rule.RequiredAmount; if (entry.GenericJackpotCredits.Contains(item)) { return; } int value = 0; entry.ProgressCounters.TryGetValue(text, out value); value = (unique ? Mathf.Max(value, amount) : Mathf.Clamp(value + amount, 0, int.MaxValue)); entry.ProgressCounters[text] = value; if (value < rule.RequiredAmount) { if (IsDedicatedServerInstance()) { SaveDatabase(); } return; } entry.GenericJackpotCredits.Add(item); ApplyPointsToEntry(entry, rule.Points, reason + " (" + value + "/" + rule.RequiredAmount + ")"); if (category.Equals("Farm", StringComparison.OrdinalIgnoreCase)) { entry.TotalFarmJackpotsPontuados++; entry.FarmJackpotPointsTotal = Mathf.Clamp(entry.FarmJackpotPointsTotal + rule.Points, -2147483647, int.MaxValue); } else if (category.Equals("UniqueCraft", StringComparison.OrdinalIgnoreCase)) { entry.TotalUniqueCraftJackpotsPontuados++; entry.UniqueCraftJackpotPointsTotal = Mathf.Clamp(entry.UniqueCraftJackpotPointsTotal + rule.Points, -2147483647, int.MaxValue); } if (IsServerInstance()) { SaveDatabase(); } } private void SendDiscordTop3EnteredWebhook(string playerName, int rank, int points, string reason) { if (_cfgDiscordTop3WebhookUrl != null && !string.IsNullOrWhiteSpace(_cfgDiscordTop3WebhookUrl.Value)) { string text = rank switch { 2 => "\ud83e\udd48", 1 => "\ud83e\udd47", _ => "\ud83e\udd49", }; string field1Value = (string.IsNullOrWhiteSpace(playerName) ? "Jogador" : playerName.Trim()); string field4Value = (string.IsNullOrWhiteSpace(reason) ? "Ascensão no ranking" : reason.Trim()); string json = BuildDiscordTop3EmbedJson("\ud83c\udfdb\ufe0f ORÁCULO DO RANKING", "✨ Um novo nome ascendeu ao pódio de Glitnir.", 15844367, "\ud83d\udc64 Jogador", field1Value, "\ud83c\udfc6 Posição", text + " #" + rank, "⭐ Pontos", points.ToString(), "\ud83d\udcdc Registro", field4Value, "\ud83c\udf1f Seu nome agora ecoa nos salões de Glitnir."); PostDiscordWebhook(json); } } private void SendDiscordTop3LostWebhook(string playerName, int oldRank, int newRank, int points, string reason) { if (_cfgDiscordTop3WebhookUrl != null && !string.IsNullOrWhiteSpace(_cfgDiscordTop3WebhookUrl.Value)) { string field1Value = (string.IsNullOrWhiteSpace(playerName) ? "Jogador" : playerName.Trim()); string field4Value = (string.IsNullOrWhiteSpace(reason) ? "Movimento no ranking" : reason.Trim()); string text = ((newRank == int.MaxValue) ? "Fora do ranking" : ("#" + newRank)); string json = BuildDiscordTop3EmbedJson("\ud83c\udfdb\ufe0f ORÁCULO DO RANKING", "⚠\ufe0f Um campeão perdeu seu lugar entre os três maiores.", 15158332, "\ud83d\udc64 Jogador", field1Value, "\ud83d\udcc9 Mudança", "#" + oldRank + " → " + text, "⭐ Pontos", points.ToString(), "\ud83d\udcdc Registro", field4Value, "⏳ Os salões de Glitnir aguardam sua retomada."); PostDiscordWebhook(json); } } private void SendDiscordTop3DroppedWebhook(string playerName, int oldRank, int newRank, int points, string reason) { if (_cfgDiscordTop3WebhookUrl != null && !string.IsNullOrWhiteSpace(_cfgDiscordTop3WebhookUrl.Value)) { string field1Value = (string.IsNullOrWhiteSpace(playerName) ? "Jogador" : playerName.Trim()); string field4Value = (string.IsNullOrWhiteSpace(reason) ? "Movimento no ranking" : reason.Trim()); string json = BuildDiscordTop3EmbedJson("\ud83c\udfdb\ufe0f ORÁCULO DO RANKING", "⚖\ufe0f A balança da honra se moveu dentro do Top 3.", 3447003, "\ud83d\udc64 Jogador", field1Value, "\ud83d\udcc9 Posição", "#" + oldRank + " → #" + newRank, "⭐ Pontos", points.ToString(), "\ud83d\udcdc Registro", field4Value, "\ud83d\udd25 A disputa pelo topo continua."); PostDiscordWebhook(json); } } private string BuildDiscordTop3EmbedJson(string title, string description, int color, string field1Name, string field1Value, string field2Name, string field2Value, string field3Name, string field3Value, string field4Name, string field4Value, string footerText) { string value = DateTime.UtcNow.ToString("o"); string value2 = BuildDiscordTop3PodiumValue(); return "{\"username\":\"Glitnir Ranking\",\"embeds\":[{\"title\":\"" + EscapeJson(title) + "\",\"description\":\"" + EscapeJson(description) + "\",\"color\":" + color + ",\"fields\":[{\"name\":\"\ud83c\udfc6 Pódio de Glitnir\",\"value\":\"" + EscapeJson(value2) + "\",\"inline\":false},{\"name\":\"" + EscapeJson(field1Name) + "\",\"value\":\"" + EscapeJson(field1Value) + "\",\"inline\":true},{\"name\":\"" + EscapeJson(field2Name) + "\",\"value\":\"" + EscapeJson(field2Value) + "\",\"inline\":true},{\"name\":\"" + EscapeJson(field3Name) + "\",\"value\":\"" + EscapeJson(field3Value) + "\",\"inline\":true},{\"name\":\"" + EscapeJson(field4Name) + "\",\"value\":\"" + EscapeJson(field4Value) + "\",\"inline\":false}],\"footer\":{\"text\":\"" + EscapeJson(footerText) + "\"},\"timestamp\":\"" + EscapeJson(value) + "\"}]}"; } private string BuildDiscordTop3PodiumValue() { try { if (_database == null || _database.Entries == null) { return "Nenhum nome registrado no pódio ainda."; } List list = (from x in _database.Entries where x != null && !string.IsNullOrWhiteSpace(x.PlayerName) && !ShouldIgnorePlayerForRanking(x.PlayerName) orderby x.Points descending, x.BossPointsTotal descending, x.KillPointsTotal descending, x.SkillPointsTotal descending, x.PlayerName select x).Take(3).ToList(); if (list.Count == 0) { return "Nenhum nome registrado no pódio ainda."; } StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < list.Count; i++) { RankingEntry rankingEntry = list[i]; string value = i switch { 1 => "\ud83e\udd48", 0 => "\ud83e\udd47", _ => "\ud83e\udd49", }; string value2 = (string.IsNullOrWhiteSpace(rankingEntry.PlayerName) ? "Jogador" : rankingEntry.PlayerName.Trim()); stringBuilder.Append(value).Append(" **#").Append(i + 1) .Append("** — ") .Append(value2) .Append("\n") .Append("⭐ **") .Append(rankingEntry.Points) .Append(" pts**"); if (i < list.Count - 1) { stringBuilder.Append("\n\n"); } } return stringBuilder.ToString(); } catch (Exception ex) { Log.LogWarning((object)("Erro ao montar pódio do webhook: " + ex.Message)); return "Pódio indisponível no momento."; } } private void PostDiscordWebhook(string json) { string url = ((_cfgDiscordTop3WebhookUrl != null) ? _cfgDiscordTop3WebhookUrl.Value : ""); if (string.IsNullOrWhiteSpace(url)) { return; } ThreadPool.QueueUserWorkItem(delegate { try { byte[] bytes = Encoding.UTF8.GetBytes(json); HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url); httpWebRequest.Method = "POST"; httpWebRequest.ContentType = "application/json"; httpWebRequest.ContentLength = bytes.Length; httpWebRequest.Timeout = 10000; using (Stream stream = httpWebRequest.GetRequestStream()) { stream.Write(bytes, 0, bytes.Length); } using HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse(); int statusCode = (int)httpWebResponse.StatusCode; if (statusCode < 200 || statusCode >= 300) { Log.LogWarning((object)("Webhook Top 3 Discord retornou status: " + statusCode)); } } catch (Exception ex) { Log.LogWarning((object)("Webhook Top 3 Discord falhou: " + ex.Message)); } }); } private string EscapeJson(string value) { if (string.IsNullOrEmpty(value)) { return ""; } return value.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\r", "\\r") .Replace("\n", "\\n"); } private string SafeLimit(string value, int maxLength) { if (string.IsNullOrEmpty(value)) { return ""; } value = value.Replace("\r", " ").Replace("\n", " ").Replace("\t", " ") .Trim(); if (value.Length > maxLength) { value = value.Substring(0, maxLength); } return value; } private string SafeKey(string value) { value = SafeLimit(value, 128); value = value.Replace("|", "/"); return value; } private string SafeReason(string reason) { reason = SafeLimit(reason, 64); reason = reason.Replace("|", "/"); return reason; } private string SanitizePlayerName(string playerName) { playerName = SafeLimit(playerName, 32); playerName = playerName.Replace("|", "/"); if (string.IsNullOrWhiteSpace(playerName)) { return "Jogador"; } return playerName; } private RankingEntry GetOrCreateEntry(string playerName) { string playerName2 = SanitizePlayerName(playerName); RankingEntry rankingEntry = FindRankingEntryByName(playerName2); if (rankingEntry != null) { return rankingEntry; } rankingEntry = new RankingEntry { PlayerName = playerName2, Points = 0, LastReason = "", LastUpdateUtc = DateTime.UtcNow.ToString("O"), TotalSkillLevelUpsPontuados = 0, SkillPointsTotal = 0, BossCredits = new HashSet(StringComparer.OrdinalIgnoreCase), TotalDeaths = 0, DeathPenaltyPointsTotal = 0 }; _database.Entries.Add(rankingEntry); RegisterRankingEntry(rankingEntry); return rankingEntry; } private string GetLocalPlayerName() { try { if ((Object)(object)Player.m_localPlayer != (Object)null) { return SanitizePlayerName(Player.m_localPlayer.GetPlayerName()); } } catch { } return "Jogador"; } private long GetServerPeerUid() { try { if ((Object)(object)ZNet.instance == (Object)null) { return 0L; } if (ZNet.instance.IsServer()) { return ZDOMan.GetSessionID(); } ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer != null) { return serverPeer.m_uid; } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao obter peer do servidor: " + ex.Message)); } return 0L; } public string GetPrefabName(GameObject go) { if ((Object)(object)go == (Object)null) { return ""; } try { string text = ((Object)go).name; if (string.IsNullOrWhiteSpace(text)) { return ""; } int num = text.IndexOf('('); if (num > 0) { text = text.Substring(0, num); } return SafeKey(text.Trim()); } catch { } return ""; } private string GetZdoKey(GameObject go) { if ((Object)(object)go == (Object)null) { return ""; } try { ZNetView component = go.GetComponent(); if ((Object)(object)component != (Object)null && component.GetZDO() != null) { ZDO zDO = component.GetZDO(); return ((ZDOID)(ref zDO.m_uid)).UserID + ":" + ((ZDOID)(ref zDO.m_uid)).ID; } } catch { } return ""; } private string GetZdoKey(Character character) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character == (Object)null) { return ""; } try { ZDOID zDOID = character.GetZDOID(); if (((ZDOID)(ref zDOID)).IsNone()) { return ""; } return ((ZDOID)(ref zDOID)).UserID + ":" + ((ZDOID)(ref zDOID)).ID; } catch { } try { ZNetView component = ((Component)character).GetComponent(); if ((Object)(object)component != (Object)null && component.GetZDO() != null) { ZDO zDO = component.GetZDO(); return ((ZDOID)(ref zDO.m_uid)).UserID + ":" + ((ZDOID)(ref zDO.m_uid)).ID; } } catch { } return ""; } private bool IsLocalPlayerSkillsInstance(Skills skills) { if ((Object)(object)skills == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { return false; } try { Player value = Traverse.Create((object)skills).Field("m_player").GetValue(); if ((Object)(object)value != (Object)null && (Object)(object)value == (Object)(object)Player.m_localPlayer) { return true; } } catch { } return false; } private float GetSafeSkillLevel(Skills skills, SkillType skillType) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)skills != (Object)null) { return Mathf.Clamp(skills.GetSkillLevel(skillType), 0f, 100f); } } catch { } return 0f; } private int GetEffectiveSkillLevel(float rawLevel) { return Mathf.Clamp(Mathf.FloorToInt(rawLevel + 0.0001f), 0, 100); } private bool IsTrackableSkill(SkillType skillType) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) string skillKey = GetSkillKey(skillType); if (string.IsNullOrWhiteSpace(skillKey)) { return false; } if (string.Equals(skillKey, "None", StringComparison.OrdinalIgnoreCase)) { return false; } if (string.Equals(skillKey, "All", StringComparison.OrdinalIgnoreCase)) { return false; } return true; } private string GetSkillKey(SkillType skillType) { return SafeKey(((object)(SkillType)(ref skillType)).ToString()); } private string GetSkillDisplayName(string skillKey) { skillKey = SafeKey(skillKey); switch (skillKey) { case "WoodCutting": return "Wood Cutting"; case "BloodMagic": return "Blood Magic"; case "ElementalMagic": return "Elemental Magic"; default: { StringBuilder stringBuilder = new StringBuilder(skillKey.Length + 8); for (int i = 0; i < skillKey.Length; i++) { char c = skillKey[i]; if (i > 0 && char.IsUpper(c) && char.IsLetter(skillKey[i - 1])) { stringBuilder.Append(' '); } stringBuilder.Append(c); } return stringBuilder.ToString().Trim(); } } } public float CaptureLocalSkillLevelBeforeRaise(Skills skills, SkillType skillType) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)skills == (Object)null || !IsTrackableSkill(skillType)) { return 0f; } if (!IsLocalPlayerSkillsInstance(skills)) { return 0f; } return GetSafeSkillLevel(skills, skillType); } public void NotifyLocalSkillGain(Skills skills, SkillType skillType, float oldRawLevel) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Expected O, but got Unknown try { if ((Object)(object)skills == (Object)null || !IsTrackableSkill(skillType) || (Object)(object)Player.m_localPlayer == (Object)null || !IsLocalPlayerSkillsInstance(skills)) { return; } float safeSkillLevel = GetSafeSkillLevel(skills, skillType); int effectiveSkillLevel = GetEffectiveSkillLevel(oldRawLevel); int effectiveSkillLevel2 = GetEffectiveSkillLevel(safeSkillLevel); int num = Mathf.Clamp(effectiveSkillLevel2 - effectiveSkillLevel, 0, 100); if (num <= 0) { return; } string skillKey = GetSkillKey(skillType); if (string.IsNullOrWhiteSpace(skillKey)) { return; } if (IsServerInstance()) { ProcessSkillGainReport(GetLocalPlayerName(), skillKey, num, effectiveSkillLevel, effectiveSkillLevel2, "local-host"); return; } long serverPeerUid = GetServerPeerUid(); if (serverPeerUid != 0L && _rpcsRegistered && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(skillKey); val.Write(num); val.Write(effectiveSkillLevel); val.Write(effectiveSkillLevel2); ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerUid, "glitnir.ranking.reportskillgain", new object[1] { val }); DebugLog(DebugCategory.Skill, "Cliente reportando SKILL: player=" + GetLocalPlayerName() + " skill=" + skillKey + " gainedLevels=" + num + " oldLevel=" + effectiveSkillLevel + " newLevel=" + effectiveSkillLevel2); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao reportar ganho de skill local do ranking: " + ex)); } } private void RPC_ReportSkillGain(long sender, ZPackage pkg) { try { if (!IsServerInstance() || pkg == null) { return; } string text = SafeKey(pkg.ReadString()); int num = Mathf.Clamp(pkg.ReadInt(), 0, 100); int oldLevel = Mathf.Clamp(pkg.ReadInt(), 0, 100); int newLevel = Mathf.Clamp(pkg.ReadInt(), 0, 100); string text2 = ResolvePlayerNameFromSender(sender); DebugLog(DebugCategory.Skill, "Servidor recebeu SKILL: sender=" + sender + " player=" + text2 + " skill=" + text + " gainedLevels=" + num + " oldLevel=" + oldLevel + " newLevel=" + newLevel); if (!string.IsNullOrWhiteSpace(text2) && !string.IsNullOrWhiteSpace(text) && num > 0) { if (ShouldIgnoreSenderForRanking(sender, text2)) { DebugLog(DebugCategory.Skill, "Skill ignorada para admin: " + text2); } else { ProcessSkillGainReport(text2, text, num, oldLevel, newLevel, "rpc-skill"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_ReportSkillGain: " + ex)); } } private void ProcessSkillGainReport(string playerName, string skillKey, int gainedLevels, int oldLevel, int newLevel, string source) { try { if (!IsServerInstance() || !_rules.RankingEnabled || !_rules.EnableSkillPoints) { return; } playerName = SanitizePlayerName(playerName); skillKey = SafeKey(skillKey); gainedLevels = Mathf.Clamp(gainedLevels, 0, 100); if (string.IsNullOrWhiteSpace(playerName) || string.IsNullOrWhiteSpace(skillKey) || gainedLevels <= 0) { return; } if (ShouldIgnorePlayerForRanking(playerName)) { DebugLog(DebugCategory.Skill, "Skill ignorada para admin: " + playerName); return; } RankingEntry orCreateEntry = GetOrCreateEntry(playerName); int num = 0; int num2 = 0; List list = new List(); Dictionary value = null; if (_rules.SkillMilestoneJackpotPoints != null) { _rules.SkillMilestoneJackpotPoints.TryGetValue(skillKey, out value); } if (value != null && value.Count > 0) { foreach (KeyValuePair item2 in value.OrderBy((KeyValuePair p) => p.Key)) { int num3 = Mathf.Clamp(item2.Key, 1, 100); int num4 = Mathf.Max(0, item2.Value); if (oldLevel < num3 && newLevel >= num3) { string item = skillKey + ":" + num3; if (!orCreateEntry.SkillLevel100JackpotCredits.Contains(item)) { orCreateEntry.SkillLevel100JackpotCredits.Add(item); num2++; list.Add(num3); num = Mathf.Clamp(num + num4, -2147483647, int.MaxValue); } } } if (num > 0) { orCreateEntry.TotalSkillLevelUpsPontuados = Mathf.Clamp(orCreateEntry.TotalSkillLevelUpsPontuados + num2, 0, int.MaxValue); orCreateEntry.SkillPointsTotal = Mathf.Clamp(orCreateEntry.SkillPointsTotal + num, -2147483647, int.MaxValue); string reason = "Jackpot Skill: " + GetSkillDisplayName(skillKey) + " " + string.Join("/", list.Select((int l) => l.ToString()).ToArray()); ApplyPointsToEntry(orCreateEntry, num, reason); return; } } DebugLog(DebugCategory.Skill, "Skill ignorada fora dos jackpots de marco: player=" + playerName + " skill=" + skillKey + " oldLevel=" + oldLevel + " newLevel=" + newLevel + " source=" + source); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao processar skill reportado: " + ex)); } } private string FormatHudLastUpdate(string rawUtc) { if (string.IsNullOrWhiteSpace(rawUtc)) { return "sem registro"; } try { return DateTime.Parse(rawUtc, null, DateTimeStyles.RoundtripKind).ToLocalTime().ToString("dd/MM/yyyy HH:mm"); } catch { return rawUtc; } } private string FormatIntRulesForHud(Dictionary rules) { if (rules == null || rules.Count == 0) { return ""; } return string.Join(",", (from x in rules orderby x.Key select SafeKey(x.Key) + ":" + x.Value).ToArray()); } private string FormatCategorizedIntRulesForHud(Dictionary rules, Dictionary manualCategories = null, bool combatMode = false) { if (rules == null || rules.Count == 0) { return ""; } List list = new List(); foreach (IGrouping> item in from x in rules orderby GetHudRuleCategoryOrder(x.Key, manualCategories, combatMode), combatMode ? SafeKey(x.Key) : FriendlyRuleNameForHud(x.Key) group x by GetHudRuleCategoryName(x.Key, manualCategories, combatMode)) { list.Add(BuildHudCategoryHeader(item.Key)); foreach (KeyValuePair item2 in item) { string text = (combatMode ? SafeKey(item2.Key) : item2.Key); list.Add(text + ":" + item2.Value); } } return string.Join(",", list.ToArray()); } private string FormatCategorizedJackpotRulesForHud(Dictionary rules, Dictionary manualCategories = null, bool combatMode = false) { if (rules == null || rules.Count == 0) { return ""; } List list = new List(); foreach (IGrouping> item in from x in rules orderby GetHudRuleCategoryOrder(x.Key, manualCategories, combatMode), FriendlyRuleNameForHud(x.Key) group x by GetHudRuleCategoryName(x.Key, manualCategories, combatMode)) { list.Add(BuildHudCategoryHeader(item.Key)); foreach (KeyValuePair item2 in item) { JackpotRule jackpotRule = item2.Value ?? new JackpotRule(); list.Add(item2.Key + ":" + Mathf.Max(1, jackpotRule.RequiredAmount) + ":" + jackpotRule.Points); } } return string.Join(",", list.ToArray()); } private string BuildHudCategoryHeader(string categoryName) { return "__GLITNIR_CAT__|" + (string.IsNullOrWhiteSpace(categoryName) ? "Outros" : categoryName.Trim()); } private bool IsHudCategoryHeader(string row) { return !string.IsNullOrWhiteSpace(row) && row.StartsWith("__GLITNIR_CAT__|", StringComparison.Ordinal); } private string GetHudCategoryHeaderTitle(string row) { if (!IsHudCategoryHeader(row)) { return ""; } return row.Substring("__GLITNIR_CAT__|".Length).Trim(); } private string GetHudRuleCategoryName(string prefabName, Dictionary manualCategories, bool combatMode) { if (manualCategories != null && manualCategories.TryGetValue(SafeKey(prefabName), out var value) && !string.IsNullOrWhiteSpace(value)) { return NormalizeHudCategoryName(value.Trim(), prefabName, combatMode); } return combatMode ? GetHudCombatCategoryName(prefabName) : GetHudPrefabCategoryName(prefabName); } private string NormalizeHudCategoryName(string category, string prefabName, bool combatMode) { if (combatMode || string.IsNullOrWhiteSpace(category)) { return category; } string text = category.Trim(); if (text.Equals("Arcos e Munições", StringComparison.OrdinalIgnoreCase) || text.Equals("Arcos e munições", StringComparison.OrdinalIgnoreCase) || text.Equals("Armas e escudos", StringComparison.OrdinalIgnoreCase)) { return GetHudPrefabCategoryName(prefabName); } return text; } private int GetHudRuleCategoryOrder(string prefabName, Dictionary manualCategories, bool combatMode) { string hudRuleCategoryName = GetHudRuleCategoryName(prefabName, manualCategories, combatMode); return combatMode ? GetHudCombatCategoryOrder(hudRuleCategoryName) : GetHudProductionCategoryOrder(hudRuleCategoryName); } private int GetHudProductionCategoryOrder(string category) { return category switch { "Armas, armaduras e munições" => 10, "Armaduras" => 10, "Armas" => 10, "Escudos" => 10, "Munições" => 10, "Comidas" => 40, "Comidas e bebidas" => 40, "Ferramentas" => 50, "Materiais" => 60, "Troféus" => 70, "Cultivo" => 80, _ => 999, }; } private int GetHudCombatCategoryOrder(string category) { switch (category) { case "Prados": return 10; case "Floresta Negra": return 20; case "Pântano": return 30; case "Montanha": return 40; case "Planícies": return 50; case "Oceano": return 60; case "Mistlands": return 70; case "Ashlands": return 80; default: if (!(category == "Criaturas Especiais")) { return 999; } goto case "Especiais"; case "Especiais": return 90; } } private string GetHudCombatCategoryName(string prefabName) { string text = SafeKey(prefabName).ToLowerInvariant().Replace("_", "").Replace("-", "") .Replace(" ", ""); if (text.Contains("boar") || text.Contains("deer") || text.Contains("greyling") || text.Contains("neck") || text.Contains("crow") || text.Contains("hen") || text.Contains("chicken")) { return "Prados"; } if (text.Contains("greydwarf") || text.Contains("skeleton") || text.Contains("ghost") || text.Contains("troll")) { return "Floresta Negra"; } if (text.Contains("blob") || text.Contains("abomination") || text.Contains("draugr") || text.Contains("leech") || text.Contains("surtling") || text.Contains("wraith")) { return "Pântano"; } if (text.Contains("hatchling") || text.Contains("fenring") || text.Contains("stonegolem") || text.Contains("ulv") || text.Contains("wolf") || text.Contains("bat")) { return "Montanha"; } if (text.Contains("deathsquito") || text.Contains("goblin") || text.Contains("fuling") || text.Contains("lox")) { return "Planícies"; } if (text.Contains("serpent") || text.Contains("bonemaw") || text.Contains("leviathan") || text.Contains("seagal")) { return "Oceano"; } if (text.Contains("seeker") || text.Contains("gjall") || text.Contains("tick") || text.Contains("hare") || text.Contains("dverger")) { return "Mistlands"; } if (text.Contains("asksvin") || text.Contains("charred") || text.Contains("morgen") || text.Contains("valkyrie") || text.Contains("volture") || text.Contains("tentaroot") || text.Contains("bloblava")) { return "Ashlands"; } if (text.Contains("haldor") || text.Contains("hildir") || text.Contains("odin") || text.Contains("wisp") || text.Contains("trainingdummy")) { return "Especiais"; } return "Outros"; } private int GetHudPrefabCategoryOrder(string prefabName) { return GetHudPrefabCategoryName(prefabName) switch { "Armaduras" => 10, "Armas" => 20, "Escudos" => 25, "Munições" => 30, "Comidas" => 40, "Comidas e bebidas" => 40, "Ferramentas" => 50, "Materiais" => 60, "Troféus" => 70, "Cultivo" => 80, _ => 999, }; } private string GetHudPrefabCategoryName(string prefabName) { string text = SafeKey(prefabName).ToLowerInvariant().Replace("_", "").Replace("-", "") .Replace(" ", ""); if (text.Contains("helmet") || text.Contains("armor") || text.Contains("cape") || text.Contains("chest") || text.Contains("legs") || text.Contains("greaves")) { return "Armaduras"; } if (text.Contains("shield")) { return "Escudos"; } if (text.Contains("arrow") || text.Contains("bolt") || text.Contains("bomb") || text.Contains("oozebomb") || text.Contains("missile")) { return "Munições"; } if (text.Contains("sword") || text.Contains("knife") || text.Contains("axe") || text.Contains("mace") || text.Contains("spear") || text.Contains("atgeir") || text.Contains("bow") || text.Contains("crossbow") || text.Contains("staff") || text.Contains("club") || text.Contains("pickaxe")) { return "Armas"; } if (text.Contains("stew") || text.Contains("soup") || text.Contains("mead") || text.Contains("wine") || text.Contains("cooked") || text.Contains("jerky") || text.Contains("honey") || text.Contains("chicken") || text.Contains("fish") || text.Contains("meat") || text.Contains("mushroom") || text.Contains("bread") || text.Contains("pie") || text.Contains("pudding") || text.Contains("sausages") || text.Contains("eyescream") || text.Contains("salad") || text.Contains("omelette") || text.Contains("seekeraspic") || text.Contains("wolfskewer") || text.Contains("mincemeatsauce") || text.Contains("bloodpudding")) { return "Comidas e bebidas"; } if (text.Contains("hoe") || text.Contains("hammer") || text.Contains("cultivator") || text.Contains("fishingrod") || text.Contains("torch") || text.Contains("wishbone")) { return "Ferramentas"; } if (text.Contains("trophy")) { return "Troféus"; } if (text.Contains("seed") || text.Contains("sapling") || text.Contains("barley") || text.Contains("flax") || text.Contains("carrot") || text.Contains("turnip") || text.Contains("onion") || text.Contains("jotunpuffs") || text.Contains("magecap") || text.Contains("vineberry")) { return "Cultivo"; } if (text.Contains("wood") || text.Contains("stone") || text.Contains("ore") || text.Contains("metal") || text.Contains("bar") || text.Contains("ingot") || text.Contains("hide") || text.Contains("leather") || text.Contains("scale") || text.Contains("chitin") || text.Contains("thread") || text.Contains("linen") || text.Contains("resin") || text.Contains("coal") || text.Contains("core") || text.Contains("crystal") || text.Contains("bone") || text.Contains("fang") || text.Contains("feather") || text.Contains("needle") || text.Contains("claw") || text.Contains("carapace") || text.Contains("mandible") || text.Contains("bloodbag")) { return "Materiais"; } return "Outros"; } private string FormatJackpotRulesForHud(Dictionary rules) { if (rules == null || rules.Count == 0) { return ""; } return string.Join(",", rules.OrderBy((KeyValuePair x) => x.Key).Select(delegate(KeyValuePair x) { JackpotRule jackpotRule = x.Value ?? new JackpotRule(); return SafeKey(x.Key) + ":" + Mathf.Max(1, jackpotRule.RequiredAmount) + ":" + jackpotRule.Points; }).ToArray()); } private string FriendlyRuleNameForHud(string rawKey) { string text = SafeKey(rawKey); if (string.IsNullOrWhiteSpace(text)) { return "Regra"; } string prefabDisplayNameForHud = GetPrefabDisplayNameForHud(text); if (!string.IsNullOrWhiteSpace(prefabDisplayNameForHud) && !string.Equals(prefabDisplayNameForHud, text, StringComparison.OrdinalIgnoreCase)) { return prefabDisplayNameForHud; } if (BossDisplayNamesPtBr.TryGetValue(text, out var value) && !string.IsNullOrWhiteSpace(value)) { return value; } if (BossPortugueseToPrefab.TryGetValue(text, out var value2) && BossDisplayNamesPtBr.TryGetValue(value2, out value) && !string.IsNullOrWhiteSpace(value)) { return value; } if (FishDisplayNamesPtBr.TryGetValue(text, out var value3) && !string.IsNullOrWhiteSpace(value3)) { return value3; } if (FishPortugueseToPrefab.TryGetValue(text, out var value4) && FishDisplayNamesPtBr.TryGetValue(value4, out value3) && !string.IsNullOrWhiteSpace(value3)) { return value3; } if (HudFriendlyNames.TryGetValue(text, out var value5) && !string.IsNullOrWhiteSpace(value5)) { return value5; } string text2 = TryManualPortugueseNameForHud(text); if (!string.IsNullOrWhiteSpace(text2)) { return text2; } if (!string.IsNullOrWhiteSpace(prefabDisplayNameForHud)) { return prefabDisplayNameForHud; } return HumanizePrefabNamePt(text); } private string GetPrefabDisplayNameForHud(string prefabName) { if (string.IsNullOrWhiteSpace(prefabName)) { return ""; } string text = CleanPrefabNameForHud(prefabName); if (string.IsNullOrWhiteSpace(text)) { return ""; } if (_hudPrefabDisplayNameCache.TryGetValue(text, out var value) && !string.IsNullOrWhiteSpace(value)) { return value; } string text2 = ""; bool flag = false; try { GameObject val = ResolvePrefabForHud(text); if ((Object)(object)val != (Object)null) { text2 = TryGetLocalizedPrefabNameForHud(val, text); flag = !string.IsNullOrWhiteSpace(text2); } } catch (Exception ex) { if (_rules != null && _rules.DebugLogging) { Log.LogWarning((object)("[Glitnir Ranking] Falha ao localizar nome do prefab '" + prefabName + "': " + ex.Message)); } } if (string.IsNullOrWhiteSpace(text2) && BossDisplayNamesPtBr.TryGetValue(text, out var value2) && !string.IsNullOrWhiteSpace(value2)) { text2 = value2; flag = true; } if (string.IsNullOrWhiteSpace(text2) && BossPortugueseToPrefab.TryGetValue(text, out var value3) && BossDisplayNamesPtBr.TryGetValue(value3, out var value4) && !string.IsNullOrWhiteSpace(value4)) { text2 = value4; flag = true; } if (string.IsNullOrWhiteSpace(text2) && FishDisplayNamesPtBr.TryGetValue(text, out var value5) && !string.IsNullOrWhiteSpace(value5)) { text2 = value5; flag = true; } if (string.IsNullOrWhiteSpace(text2) && FishPortugueseToPrefab.TryGetValue(text, out var value6) && FishDisplayNamesPtBr.TryGetValue(value6, out var value7) && !string.IsNullOrWhiteSpace(value7)) { text2 = value7; flag = true; } if (string.IsNullOrWhiteSpace(text2) && HudFriendlyNames.TryGetValue(text, out var value8) && !string.IsNullOrWhiteSpace(value8)) { text2 = value8; flag = true; } if (string.IsNullOrWhiteSpace(text2)) { string text3 = TryManualPortugueseNameForHud(text); if (!string.IsNullOrWhiteSpace(text3)) { text2 = text3; flag = true; } } if (string.IsNullOrWhiteSpace(text2)) { return HumanizePrefabNamePt(text); } if (flag) { _hudPrefabDisplayNameCache[text] = text2; } return text2; } private string TryGetLocalizedPrefabNameForHud(GameObject prefab, string fallbackPrefabName) { if ((Object)(object)prefab == (Object)null) { return ""; } string text = ""; try { ItemDrop component = prefab.GetComponent(); if ((Object)(object)component != (Object)null && component.m_itemData != null && component.m_itemData.m_shared != null && !string.IsNullOrWhiteSpace(component.m_itemData.m_shared.m_name)) { text = component.m_itemData.m_shared.m_name; } if (string.IsNullOrWhiteSpace(text)) { Character component2 = prefab.GetComponent(); if ((Object)(object)component2 != (Object)null && !string.IsNullOrWhiteSpace(component2.m_name)) { text = component2.m_name; } } if (string.IsNullOrWhiteSpace(text)) { Piece component3 = prefab.GetComponent(); if ((Object)(object)component3 != (Object)null && !string.IsNullOrWhiteSpace(component3.m_name)) { text = component3.m_name; } } if (string.IsNullOrWhiteSpace(text)) { Pickable component4 = prefab.GetComponent(); if ((Object)(object)component4 != (Object)null && (Object)(object)component4.m_itemPrefab != (Object)null) { ItemDrop component5 = component4.m_itemPrefab.GetComponent(); if ((Object)(object)component5 != (Object)null && component5.m_itemData != null && component5.m_itemData.m_shared != null && !string.IsNullOrWhiteSpace(component5.m_itemData.m_shared.m_name)) { text = component5.m_itemData.m_shared.m_name; } } } if (string.IsNullOrWhiteSpace(text)) { return ""; } string text2 = TryLocalizeTokenForHud(text); if (string.IsNullOrWhiteSpace(text2)) { return ""; } text2 = text2.Trim(); if (text2.StartsWith("$", StringComparison.Ordinal)) { text2 = HumanizeTokenNameForHud(text2); } if (string.IsNullOrWhiteSpace(text2) || text2 == fallbackPrefabName) { return text2; } return text2; } catch { return ""; } } private GameObject ResolvePrefabForHud(string cleanName) { if (string.IsNullOrWhiteSpace(cleanName)) { return null; } if (_hudPrefabObjectCache.TryGetValue(cleanName, out var value)) { return value; } RebuildHudPrefabObjectCacheIfNeeded(); if (_hudPrefabObjectCache.TryGetValue(cleanName, out value)) { return value; } GameObject val = null; if ((Object)(object)ObjectDB.instance != (Object)null) { val = ObjectDB.instance.GetItemPrefab(cleanName); } if ((Object)(object)val == (Object)null && (Object)(object)ZNetScene.instance != (Object)null) { val = ZNetScene.instance.GetPrefab(cleanName); } if ((Object)(object)val != (Object)null) { _hudPrefabObjectCache[cleanName] = val; } return val; } private void RebuildHudPrefabObjectCacheIfNeeded() { int num = (((Object)(object)ObjectDB.instance != (Object)null && ObjectDB.instance.m_items != null) ? ObjectDB.instance.m_items.Count : (-1)); int num2 = (((Object)(object)ZNetScene.instance != (Object)null && ZNetScene.instance.m_prefabs != null) ? ZNetScene.instance.m_prefabs.Count : (-1)); if (_hudObjectDbItemCountCached == num && _hudZNetScenePrefabCountCached == num2 && _hudPrefabObjectCache.Count > 0) { return; } _hudPrefabObjectCache.Clear(); if ((Object)(object)ObjectDB.instance != (Object)null && ObjectDB.instance.m_items != null) { for (int i = 0; i < ObjectDB.instance.m_items.Count; i++) { GameObject val = ObjectDB.instance.m_items[i]; if (!((Object)(object)val == (Object)null) && !string.IsNullOrWhiteSpace(((Object)val).name) && !_hudPrefabObjectCache.ContainsKey(((Object)val).name)) { _hudPrefabObjectCache[((Object)val).name] = val; } } } if ((Object)(object)ZNetScene.instance != (Object)null && ZNetScene.instance.m_prefabs != null) { for (int j = 0; j < ZNetScene.instance.m_prefabs.Count; j++) { GameObject val2 = ZNetScene.instance.m_prefabs[j]; if (!((Object)(object)val2 == (Object)null) && !string.IsNullOrWhiteSpace(((Object)val2).name) && !_hudPrefabObjectCache.ContainsKey(((Object)val2).name)) { _hudPrefabObjectCache[((Object)val2).name] = val2; } } } _hudObjectDbItemCountCached = num; _hudZNetScenePrefabCountCached = num2; } private string HumanizeTokenNameForHud(string tokenOrName) { if (string.IsNullOrWhiteSpace(tokenOrName)) { return ""; } string text = tokenOrName.Trim(); if (text.StartsWith("$item_", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(6); } else if (text.StartsWith("$", StringComparison.Ordinal)) { text = text.Substring(1); } return HumanizePrefabName(text); } private string CleanPrefabNameForHud(string prefabName) { if (string.IsNullOrWhiteSpace(prefabName)) { return ""; } string text = prefabName.Trim(); int num = text.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase); if (num >= 0) { text = text.Substring(0, num).Trim(); } return text; } private string TryLocalizeTokenForHud(string tokenOrName) { if (string.IsNullOrWhiteSpace(tokenOrName)) { return ""; } try { if (!_hudLocalizationReflectionReady) { _hudLocalizationReflectionReady = true; _hudLocalizationType = AccessTools.TypeByName("Localization"); if (_hudLocalizationType == null) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type[] types; try { types = assemblies[i].GetTypes(); } catch { continue; } foreach (Type type in types) { if (type != null && type.Name == "Localization") { _hudLocalizationType = type; break; } } if (_hudLocalizationType != null) { break; } } } if (_hudLocalizationType != null) { _hudLocalizationInstanceField = AccessTools.Field(_hudLocalizationType, "instance"); _hudLocalizationInstanceProperty = AccessTools.Property(_hudLocalizationType, "instance"); _hudLocalizationLocalizeMethod = AccessTools.Method(_hudLocalizationType, "Localize", new Type[1] { typeof(string) }, (Type[])null); if (_hudLocalizationLocalizeMethod == null) { MethodInfo[] methods = _hudLocalizationType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo == null) && !(methodInfo.Name != "Localize")) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length >= 1 && parameters[0].ParameterType == typeof(string)) { _hudLocalizationLocalizeMethod = methodInfo; break; } } } } } } if (_hudLocalizationType == null || (_hudLocalizationInstanceField == null && _hudLocalizationInstanceProperty == null) || _hudLocalizationLocalizeMethod == null) { return tokenOrName; } object obj2 = null; if (_hudLocalizationInstanceField != null) { obj2 = _hudLocalizationInstanceField.GetValue(null); } if (obj2 == null && _hudLocalizationInstanceProperty != null) { obj2 = _hudLocalizationInstanceProperty.GetValue(null, null); } if (obj2 == null) { return tokenOrName; } ParameterInfo[] parameters2 = _hudLocalizationLocalizeMethod.GetParameters(); object[] array = new object[parameters2.Length]; array[0] = tokenOrName; for (int l = 1; l < array.Length; l++) { if (parameters2[l].ParameterType == typeof(bool)) { array[l] = true; } else { array[l] = parameters2[l].DefaultValue; } } object obj3 = _hudLocalizationLocalizeMethod.Invoke(obj2, array); return (obj3 as string) ?? tokenOrName; } catch { return tokenOrName; } } private string TryManualPortugueseNameForHud(string key) { if (string.IsNullOrWhiteSpace(key)) { return ""; } string text = key.Trim(); if (text.StartsWith("$item_", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(6); } else if (text.StartsWith("$", StringComparison.Ordinal)) { text = text.Substring(1); } text = text.Replace("_", "").Replace("-", "").Replace(" ", ""); if (ManualPortuguesePrefabNames.TryGetValue(text, out var value)) { return value; } return ""; } private string HumanizePrefabNamePt(string key) { if (string.IsNullOrWhiteSpace(key)) { return "Regra"; } string text = TryManualPortugueseNameForHud(key); if (!string.IsNullOrWhiteSpace(text)) { return text; } string text2 = key.Trim(); int num = text2.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase); if (num >= 0) { text2 = text2.Substring(0, num); } text2 = Regex.Replace(text2, "([a-z])([A-Z])", "$1 $2"); text2 = text2.Replace("_", " ").Replace("-", " ").Trim(); string text3 = text2.ToLowerInvariant(); text3 = text3.Replace("helmet", "elmo"); text3 = text3.Replace("armor", "armadura"); text3 = text3.Replace("chest", "peitoral"); text3 = text3.Replace("legs", "perneiras"); text3 = text3.Replace("greaves", "grevas"); text3 = text3.Replace("knife", "faca"); text3 = text3.Replace("sword", "espada"); text3 = text3.Replace("axe", "machado"); text3 = text3.Replace("mace", "maça"); text3 = text3.Replace("bow", "arco"); text3 = text3.Replace("shield", "escudo"); text3 = text3.Replace("spear", "lança"); text3 = text3.Replace("atgier", "atgeir"); text3 = text3.Replace("crossbow", "besta"); text3 = text3.Replace("bronze", "bronze"); text3 = text3.Replace("iron", "ferro"); text3 = text3.Replace("leather", "couro"); text3 = text3.Replace("rag", "trapos"); text3 = text3.Replace("padded", "acolchoado"); text3 = text3.Replace("root", "raiz"); text3 = text3.Replace("trollleather", "couro de troll"); text3 = text3.Replace("troll leather", "couro de troll"); text3 = text3.Replace("blackmetal", "metal negro"); text3 = text3.Replace("black metal", "metal negro"); text3 = text3.Replace("silver", "prata"); text3 = text3.Replace("copper", "cobre"); text3 = text3.Replace("flint", "sílex"); text3 = text3.Replace("carapace", "carapaça"); text3 = text3.Replace("flametal", "flametal"); text3 = text3.Replace("mage", "mago"); text3 = text3.Replace("fenris", "fenris"); text3 = text3.Replace("dverger", "dvergr"); text3 = text3.Replace("drake", "draco"); while (text3.IndexOf(" ", StringComparison.Ordinal) >= 0) { text3 = text3.Replace(" ", " "); } text3 = text3.Trim(); if (text3.Length == 0) { return key; } return char.ToUpperInvariant(text3[0]) + text3.Substring(1); } private string StripRichText(string value) { if (string.IsNullOrWhiteSpace(value)) { return ""; } try { return Regex.Replace(value, "<.*?>", ""); } catch { return value; } } private string HumanizePrefabName(string key) { if (string.IsNullOrWhiteSpace(key)) { return "Regra"; } string text = key.Trim(); if (text.StartsWith("$", StringComparison.Ordinal)) { text = text.Substring(1); } int num = text.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase); if (num >= 0) { text = text.Substring(0, num); } text = text.Replace("_", " ").Replace("-", " "); while (text.IndexOf(" ", StringComparison.Ordinal) >= 0) { text = text.Replace(" ", " "); } text = text.Trim(); if (text.Length == 0) { return key; } string[] array = text.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text2 = array[i]; if (text2.Length <= 1) { array[i] = text2.ToUpperInvariant(); } else { array[i] = char.ToUpperInvariant(text2[0]) + text2.Substring(1).ToLowerInvariant(); } } return string.Join(" ", array); } private string FormatExplorationMapJackpotRulesForHud(Dictionary rules) { if (rules == null || rules.Count == 0) { return ""; } return string.Join(";", (from p in rules where p.Key > 0 && p.Value > 0 orderby p.Key select "Mapa " + Mathf.Clamp(p.Key, 1, 100) + "%:" + p.Value).ToArray()); } private string FormatDeathPenaltyRulesForHud(Dictionary rules) { if (rules == null || rules.Count == 0) { return ""; } return string.Join(",", (from x in rules orderby x.Key select Mathf.Max(1, x.Key) + ":" + Mathf.Max(0, x.Value)).ToArray()); } private string FormatSkillJackpotRulesForHud(Dictionary> rules) { if (rules == null || rules.Count == 0) { return ""; } List list = new List(); foreach (KeyValuePair> item in rules.OrderBy((KeyValuePair> x) => x.Key)) { if (item.Value == null) { continue; } foreach (KeyValuePair item2 in item.Value.OrderBy((KeyValuePair x) => x.Key)) { list.Add(SafeKey(item.Key) + ":" + Mathf.Max(1, item2.Key) + ":" + item2.Value); } } return string.Join(",", list.ToArray()); } private Texture2D GetRuleCategoryIcon(string categoryKey) { if (string.IsNullOrWhiteSpace(categoryKey)) { return null; } Texture2D value; return _uiRuleCategoryIcons.TryGetValue(categoryKey, out value) ? value : null; } private void DrawTextureOrLabel(Rect rect, Texture2D texture, string fallbackText, GUIStyle fallbackStyle) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)texture != (Object)null) { DrawTextureAspectFit(rect, texture); } else { GUI.Label(rect, fallbackText, fallbackStyle); } } private void DrawTextureAspectFit(Rect rect, Texture2D texture) { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)texture == (Object)null) && !(((Rect)(ref rect)).width <= 0f) && !(((Rect)(ref rect)).height <= 0f) && ((Texture)texture).width > 0 && ((Texture)texture).height > 0) { float num = (float)((Texture)texture).width / (float)((Texture)texture).height; float num2 = ((Rect)(ref rect)).width / ((Rect)(ref rect)).height; float num3; float num4; if (num2 > num) { num3 = ((Rect)(ref rect)).height; num4 = num3 * num; } else { num4 = ((Rect)(ref rect)).width; num3 = num4 / num; } Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref rect)).x + (((Rect)(ref rect)).width - num4) * 0.5f, ((Rect)(ref rect)).y + (((Rect)(ref rect)).height - num3) * 0.5f, num4, num3); GUI.DrawTexture(val, (Texture)(object)texture, (ScaleMode)2, true); } } private void DrawTextureOrLabelCropped(Rect rect, Texture2D texture, string fallbackText, GUIStyle fallbackStyle, float cropTop, float cropBottom) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)texture != (Object)null) { DrawTextureCroppedAspectFit(rect, texture, cropTop, cropBottom); } else { GUI.Label(rect, fallbackText, fallbackStyle); } } private void DrawTextureCroppedAspectFit(Rect rect, Texture2D texture, float cropTop, float cropBottom) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)texture == (Object)null || ((Rect)(ref rect)).width <= 0f || ((Rect)(ref rect)).height <= 0f || ((Texture)texture).width <= 0 || ((Texture)texture).height <= 0) { return; } cropTop = Mathf.Clamp01(cropTop); cropBottom = Mathf.Clamp01(cropBottom); if (cropBottom <= cropTop + 0.01f) { DrawTextureAspectFit(rect, texture); return; } float num = (float)((Texture)texture).height * (cropBottom - cropTop); float num2 = (float)((Texture)texture).width / Mathf.Max(1f, num); float num3 = ((Rect)(ref rect)).width / ((Rect)(ref rect)).height; float num4; float num5; if (num3 > num2) { num4 = ((Rect)(ref rect)).height; num5 = num4 * num2; } else { num5 = ((Rect)(ref rect)).width; num4 = num5 / num2; } Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref rect)).x + (((Rect)(ref rect)).width - num5) * 0.5f, ((Rect)(ref rect)).y + (((Rect)(ref rect)).height - num4) * 0.5f, num5, num4); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(0f, 1f - cropBottom, 1f, cropBottom - cropTop); GUI.DrawTextureWithTexCoords(val, (Texture)(object)texture, val2, true); } private float GetTextureHeightForWidth(Texture2D texture, float width, float fallbackHeight, float minHeight, float maxHeight) { if ((Object)(object)texture != (Object)null && ((Texture)texture).width > 0 && ((Texture)texture).height > 0) { float num = width * ((float)((Texture)texture).height / (float)((Texture)texture).width); return Mathf.Clamp(num, minHeight, maxHeight); } return Mathf.Clamp(fallbackHeight, minHeight, maxHeight); } private void DrawCroppedIcon(Rect rect, Texture2D texture) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (!(((Rect)(ref rect)).width <= 0f) && !(((Rect)(ref rect)).height <= 0f)) { if ((Object)(object)texture == (Object)null) { GUI.Label(rect, "RANK", _headerStyle); return; } float num = Mathf.Clamp(_uiIconZoom, 0.75f, 1.2f); float num2 = ((Rect)(ref rect)).width * num; float num3 = ((Rect)(ref rect)).height * num; float num4 = (((Rect)(ref rect)).width - num2) * 0.5f; float num5 = (((Rect)(ref rect)).height - num3) * 0.5f; GUI.BeginGroup(rect); GUI.DrawTexture(new Rect(num4, num5, num2, num3), (Texture)(object)texture, (ScaleMode)2, true); GUI.EndGroup(); } } private void DrawPanel(Rect rect) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_uiWhiteTexture == (Object)null)) { GUI.DrawTexture(rect, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.02f, 0.015f, 0.01f, 0.08f), 0f, 0f); if ((Object)(object)_uiPanelTexture != (Object)null) { GUI.DrawTexture(rect, (Texture)(object)_uiPanelTexture, (ScaleMode)0, true); } Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref rect)).x + 8f, ((Rect)(ref rect)).y + 8f, ((Rect)(ref rect)).width - 16f, ((Rect)(ref rect)).height - 16f); if (((Rect)(ref val)).width > 0f && ((Rect)(ref val)).height > 0f) { GUI.DrawTexture(val, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.08f, 0.05f, 0.025f, 0.08f), 0f, 0f); } Color val2 = default(Color); ((Color)(ref val2))..ctor(0.92f, 0.76f, 0.42f, 0.34f); Color val3 = default(Color); ((Color)(ref val3))..ctor(0.97f, 0.83f, 0.48f, 0.05f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, 2f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val2, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - 2f, ((Rect)(ref rect)).width, 2f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val2, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, 2f, ((Rect)(ref rect)).height), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val2, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMax - 2f, ((Rect)(ref rect)).y, 2f, ((Rect)(ref rect)).height), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val2, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 2f, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).width - 4f, 1f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val3, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 2f, ((Rect)(ref rect)).yMax - 3f, ((Rect)(ref rect)).width - 4f, 1f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val3, 0f, 0f); } } private void OnGUI() { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) if (!Application.isBatchMode && !((Object)(object)Player.m_localPlayer == (Object)null)) { EnsureUiTexturesLoaded(); EnsureGuiStyles(); DrawRankingIcon(); if (_hudVisible) { ClampRankingWindowRect(); float num = Mathf.Clamp01((Time.realtimeSinceStartup - _hudOpenTime) * 4f); float num2 = (1f - num) * 26f; Rect windowRect = _windowRect; ((Rect)(ref windowRect)).y = ((Rect)(ref windowRect)).y + num2; Color color = GUI.color; GUI.color = new Color(color.r, color.g, color.b, color.a * num); Rect windowRect2 = GUI.Window(918273, windowRect, new WindowFunction(DrawRankingWindow), "", _windowStyle); GUI.color = color; _windowRect = windowRect2; ref Rect windowRect3 = ref _windowRect; ((Rect)(ref windowRect3)).y = ((Rect)(ref windowRect3)).y - num2; ClampRankingWindowRect(); UpdateRankingInputBlockerRect(); ConsumeRankingMouseEventIfNeeded(); } } } private void EnsureRankingInputBlocker() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) if (!Application.isBatchMode && !((Object)(object)_rankingInputBlockerObject != (Object)null)) { _rankingInputBlockerObject = new GameObject("GlitnirRanking_InputBlocker"); Object.DontDestroyOnLoad((Object)(object)_rankingInputBlockerObject); _rankingInputBlockerCanvas = _rankingInputBlockerObject.AddComponent(); _rankingInputBlockerCanvas.renderMode = (RenderMode)0; _rankingInputBlockerCanvas.overrideSorting = true; _rankingInputBlockerCanvas.sortingOrder = 32760; _rankingInputBlockerObject.AddComponent(); GameObject val = new GameObject("Blocker"); val.transform.SetParent(_rankingInputBlockerObject.transform, false); _rankingInputBlockerRect = val.AddComponent(); _rankingInputBlockerRect.anchorMin = new Vector2(0f, 0f); _rankingInputBlockerRect.anchorMax = new Vector2(0f, 0f); _rankingInputBlockerRect.pivot = new Vector2(0f, 0f); Image val2 = val.AddComponent(); ((Graphic)val2).color = new Color(0f, 0f, 0f, 0f); ((Graphic)val2).raycastTarget = true; _rankingInputBlockerObject.SetActive(false); } } private void UpdateRankingInputBlockerRect() { //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) if (Application.isBatchMode || !_hudVisible) { SetRankingInputBlockerVisible(visible: false); return; } EnsureRankingInputBlocker(); if (!((Object)(object)_rankingInputBlockerObject == (Object)null) && !((Object)(object)_rankingInputBlockerRect == (Object)null)) { float num = Mathf.Clamp(((Rect)(ref _windowRect)).x, 0f, (float)Screen.width); float num2 = Mathf.Clamp((float)Screen.height - ((Rect)(ref _windowRect)).yMax, 0f, (float)Screen.height); float num3 = Mathf.Clamp(((Rect)(ref _windowRect)).width, 0f, (float)Screen.width - num); float num4 = Mathf.Clamp(((Rect)(ref _windowRect)).height, 0f, (float)Screen.height - num2); _rankingInputBlockerRect.anchoredPosition = new Vector2(num, num2); _rankingInputBlockerRect.sizeDelta = new Vector2(num3, num4); SetRankingInputBlockerVisible(visible: true); } } private void SetRankingInputBlockerVisible(bool visible) { if ((Object)(object)_rankingInputBlockerObject != (Object)null && _rankingInputBlockerObject.activeSelf != visible) { _rankingInputBlockerObject.SetActive(visible); } } private void DestroyRankingInputBlocker() { if (!((Object)(object)_rankingInputBlockerObject == (Object)null)) { try { Object.Destroy((Object)(object)_rankingInputBlockerObject); } catch { } _rankingInputBlockerObject = null; _rankingInputBlockerCanvas = null; _rankingInputBlockerRect = null; } } private void ConsumeRankingMouseEventIfNeeded() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Invalid comparison between Unknown and I4 //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Invalid comparison between Unknown and I4 Event current = Event.current; if (current != null && ShouldRankingBlockGameInput()) { EventType type = current.type; if ((int)type == 0 || (int)type == 1 || (int)type == 3 || (int)type == 6) { current.Use(); } } } public bool IsRankingHudUnderMouse() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) if (Application.isBatchMode) { return false; } Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(Input.mousePosition.x, (float)Screen.height - Input.mousePosition.y); if (_hudVisible && ((Rect)(ref _windowRect)).Contains(val)) { return true; } if (((Rect)(ref _iconRect)).Contains(val)) { return true; } return false; } public bool ShouldRankingBlockGameInput() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (Application.isBatchMode || !_hudVisible) { return false; } Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(Input.mousePosition.x, (float)Screen.height - Input.mousePosition.y); return ((Rect)(ref _windowRect)).Contains(val); } private void ClampRankingWindowRect() { float num = 500f; float num2 = 760f; float num3 = Mathf.Max(num, (float)Screen.width - 24f); float num4 = Mathf.Max(num2, (float)Screen.height - 24f); ((Rect)(ref _windowRect)).width = Mathf.Clamp(((Rect)(ref _windowRect)).width, num, num3); ((Rect)(ref _windowRect)).height = Mathf.Clamp(((Rect)(ref _windowRect)).height, num2, num4); ((Rect)(ref _windowRect)).x = Mathf.Clamp(((Rect)(ref _windowRect)).x, 4f, Mathf.Max(4f, (float)Screen.width - ((Rect)(ref _windowRect)).width - 4f)); ((Rect)(ref _windowRect)).y = Mathf.Clamp(((Rect)(ref _windowRect)).y, 4f, Mathf.Max(4f, (float)Screen.height - ((Rect)(ref _windowRect)).height - 4f)); } private void ToggleRankingHud() { if (_hudVisible) { CloseRankingHudAndCollapseAll(); return; } _hudVisible = true; _clientRefreshTimer = 1f; _hudOpenTime = Time.realtimeSinceStartup; _hudTabSwitchTime = Time.realtimeSinceStartup; RequestSnapshotFromServer(); } private void CloseRankingHudAndCollapseAll() { _hudVisible = false; GUI.FocusControl((string)null); SetRankingInputBlockerVisible(visible: false); CollapseAllHudSections(); } private void CollapseAllHudSections() { _expandedActionPlayers.Clear(); _expandedRuleCategories.Clear(); _expandedRuleSubCategories.Clear(); _expandedGuideSkillRows.Clear(); _expandedPerformanceCategories.Clear(); _ruleCategoriesInitialized = false; _performanceCategoriesInitialized = false; } private bool TryParseKeyCode(string value, out KeyCode keyCode) { keyCode = (KeyCode)0; if (string.IsNullOrWhiteSpace(value)) { return false; } value = value.Trim(); try { if (Enum.TryParse(value, ignoreCase: true, out keyCode)) { return true; } } catch { } return false; } private void DrawRankingIcon() { //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Unknown result type (might be due to invalid IL or missing references) //IL_0333: Unknown result type (might be due to invalid IL or missing references) //IL_0345: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) //IL_0383: Unknown result type (might be due to invalid IL or missing references) //IL_03b5: Unknown result type (might be due to invalid IL or missing references) //IL_03c7: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Invalid comparison between Unknown and I4 //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Invalid comparison between Unknown and I4 //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) Event current = Event.current; if (current != null) { if ((int)current.type == 0 && current.button == 0 && ((Rect)(ref _iconRect)).Contains(current.mousePosition)) { _iconDragging = true; _iconDragOffset = new Vector2(current.mousePosition.x - ((Rect)(ref _iconRect)).x, current.mousePosition.y - ((Rect)(ref _iconRect)).y); _iconMouseDownPosition = current.mousePosition; _iconMovedDuringDrag = false; current.Use(); } else if ((int)current.type == 3 && _iconDragging) { Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(current.mousePosition.x - _iconDragOffset.x, current.mousePosition.y - _iconDragOffset.y); float num = Mathf.Max(0f, (float)Screen.width - ((Rect)(ref _iconRect)).width); float num2 = Mathf.Max(0f, (float)Screen.height - ((Rect)(ref _iconRect)).height); ((Rect)(ref _iconRect)).x = Mathf.Clamp(val.x, 0f, num); ((Rect)(ref _iconRect)).y = Mathf.Clamp(val.y, 0f, num2); if (!_iconMovedDuringDrag && Vector2.Distance(current.mousePosition, _iconMouseDownPosition) > 6f) { _iconMovedDuringDrag = true; } current.Use(); } else if ((int)current.type == 1 && current.button == 0 && _iconDragging) { bool flag = !_iconMovedDuringDrag && Vector2.Distance(current.mousePosition, _iconMouseDownPosition) <= 6f; _iconDragging = false; current.Use(); if (flag) { ToggleRankingHud(); } SaveUiSettings(); } } Rect rect = default(Rect); ((Rect)(ref rect))..ctor(((Rect)(ref _iconRect)).x + ((Rect)(ref _iconRect)).width * 0.1f, ((Rect)(ref _iconRect)).y + ((Rect)(ref _iconRect)).height * 0.1f, ((Rect)(ref _iconRect)).width * 0.8f, ((Rect)(ref _iconRect)).height * 0.8f); DrawCroppedIcon(rect, _uiRankingIconTexture); if ((Object)(object)_uiWhiteTexture != (Object)null) { Color val2 = (_hudVisible ? new Color(0.98f, 0.82f, 0.34f, 0.42f) : new Color(0.95f, 0.78f, 0.38f, 0.12f)); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, 1f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val2, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - 1f, ((Rect)(ref rect)).width, 1f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val2, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, 1f, ((Rect)(ref rect)).height), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val2, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMax - 1f, ((Rect)(ref rect)).y, 1f, ((Rect)(ref rect)).height), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val2, 0f, 0f); } } private void DrawRankingWindow(int windowId) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_042a: Unknown result type (might be due to invalid IL or missing references) //IL_0489: Unknown result type (might be due to invalid IL or missing references) //IL_0328: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Unknown result type (might be due to invalid IL or missing references) //IL_0330: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_033a: Unknown result type (might be due to invalid IL or missing references) //IL_0358: Unknown result type (might be due to invalid IL or missing references) //IL_0380: Unknown result type (might be due to invalid IL or missing references) //IL_0397: Unknown result type (might be due to invalid IL or missing references) //IL_03b0: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Unknown result type (might be due to invalid IL or missing references) //IL_03be: Unknown result type (might be due to invalid IL or missing references) //IL_03d6: Unknown result type (might be due to invalid IL or missing references) //IL_0642: Unknown result type (might be due to invalid IL or missing references) //IL_06bb: Unknown result type (might be due to invalid IL or missing references) //IL_0534: Unknown result type (might be due to invalid IL or missing references) //IL_0537: Unknown result type (might be due to invalid IL or missing references) //IL_053c: Unknown result type (might be due to invalid IL or missing references) //IL_0541: Unknown result type (might be due to invalid IL or missing references) //IL_0546: Unknown result type (might be due to invalid IL or missing references) //IL_0564: Unknown result type (might be due to invalid IL or missing references) //IL_058c: Unknown result type (might be due to invalid IL or missing references) //IL_05a3: Unknown result type (might be due to invalid IL or missing references) //IL_05bc: Unknown result type (might be due to invalid IL or missing references) //IL_05c8: Unknown result type (might be due to invalid IL or missing references) //IL_05ca: Unknown result type (might be due to invalid IL or missing references) //IL_05e2: Unknown result type (might be due to invalid IL or missing references) //IL_0766: Unknown result type (might be due to invalid IL or missing references) //IL_0769: Unknown result type (might be due to invalid IL or missing references) //IL_076e: Unknown result type (might be due to invalid IL or missing references) //IL_0773: Unknown result type (might be due to invalid IL or missing references) //IL_0778: Unknown result type (might be due to invalid IL or missing references) //IL_0796: Unknown result type (might be due to invalid IL or missing references) //IL_07ec: Unknown result type (might be due to invalid IL or missing references) //IL_0853: Unknown result type (might be due to invalid IL or missing references) //IL_08fe: Unknown result type (might be due to invalid IL or missing references) //IL_0901: Unknown result type (might be due to invalid IL or missing references) //IL_0906: Unknown result type (might be due to invalid IL or missing references) //IL_090b: Unknown result type (might be due to invalid IL or missing references) //IL_0910: Unknown result type (might be due to invalid IL or missing references) //IL_092e: Unknown result type (might be due to invalid IL or missing references) //IL_0956: Unknown result type (might be due to invalid IL or missing references) //IL_096d: Unknown result type (might be due to invalid IL or missing references) //IL_0986: Unknown result type (might be due to invalid IL or missing references) //IL_0992: Unknown result type (might be due to invalid IL or missing references) //IL_0994: Unknown result type (might be due to invalid IL or missing references) //IL_09ac: Unknown result type (might be due to invalid IL or missing references) Rect val = default(Rect); ((Rect)(ref val))..ctor(0f, 0f, ((Rect)(ref _windowRect)).width, ((Rect)(ref _windowRect)).height); if ((Object)(object)_uiBackgroundTexture != (Object)null) { GUI.DrawTexture(val, (Texture)(object)_uiBackgroundTexture, (ScaleMode)0, true); } else if ((Object)(object)_uiPanelTexture != (Object)null) { GUI.DrawTexture(val, (Texture)(object)_uiPanelTexture, (ScaleMode)0, true); } if ((Object)(object)_uiWhiteTexture != (Object)null) { GUI.DrawTexture(val, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0f, 0f, 0f, 0.03f), 0f, 0f); } float width = ((Rect)(ref _windowRect)).width; float height = ((Rect)(ref _windowRect)).height; float num = Mathf.Clamp(width * 0.075f, 24f, 38f); float num2 = width - num * 2f; float num3 = Mathf.Clamp(num2 * 0.125f, 58f, 82f); float num4 = Mathf.Clamp(num2 * 0.105f, 44f, 64f); float num5 = (((Object)(object)_uiTitleGuideTexture != (Object)null) ? Mathf.Clamp(num2 * 0.26f, 108f, 156f) : num4); float num6 = (((Object)(object)_uiTitleActionsTexture != (Object)null) ? Mathf.Clamp(num2 * 0.26f, 108f, 156f) : num4); Rect rect = default(Rect); ((Rect)(ref rect))..ctor(num, 16f, num2, num3); DrawTextureOrLabel(rect, _uiTitleRankingTexture, "RANKING DE GLITNIR", _headerStyle); Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(num + 6f, ((Rect)(ref rect)).yMax + 8f, num2 - 12f, 34f); DrawHudTabs(rect2); float num7 = 28f; if (_hudTabIndex == 1) { BeginHudTabTransition(out var previousColor, out var previousMatrix); Rect rect3 = default(Rect); ((Rect)(ref rect3))..ctor(num + 4f, ((Rect)(ref rect2)).yMax + 8f, num2 - 8f, num5); DrawTextureOrLabelCropped(rect3, _uiTitleGuideTexture, "GUIA DE HONRA", _sectionTitleStyle, 0.22f, 0.82f); Rect rect4 = default(Rect); ((Rect)(ref rect4))..ctor(num + 6f, ((Rect)(ref rect3)).yMax + 8f, num2 - 12f, height - (((Rect)(ref rect3)).yMax + 8f) - num7 - 18f); DrawPanel(rect4); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref rect4)).x + 14f, ((Rect)(ref rect4)).y + 12f, ((Rect)(ref rect4)).width - 28f, ((Rect)(ref rect4)).height - 24f); float rulesContentHeight = GetRulesContentHeight(((Rect)(ref val2)).width); bool flag = rulesContentHeight > ((Rect)(ref val2)).height + 1f; float num8 = (flag ? 18f : 0f); Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(0f, 0f, Mathf.Max(40f, ((Rect)(ref val2)).width - num8), Mathf.Max(((Rect)(ref val2)).height, rulesContentHeight)); _rulesInfoScrollPosition = GUI.BeginScrollView(val2, _rulesInfoScrollPosition, val3, false, flag); DrawRulesContent(new Rect(0f, 0f, ((Rect)(ref val3)).width, ((Rect)(ref val3)).height)); GUI.EndScrollView(); Rect rect5 = default(Rect); ((Rect)(ref rect5))..ctor(num, height - 34f, num2, 20f); DrawShadowLabel(rect5, "ESC fecha • Clique nas categorias para expandir as regras", _footerStyle, new Vector2(1f, 1f), new Color(0f, 0f, 0f, 0.92f)); EndHudTabTransition(previousColor, previousMatrix); GUI.DragWindow(new Rect(0f, 0f, width, 110f)); return; } if (_hudTabIndex == 2) { BeginHudTabTransition(out var previousColor2, out var previousMatrix2); Rect rect6 = default(Rect); ((Rect)(ref rect6))..ctor(num + 4f, ((Rect)(ref rect2)).yMax + 8f, num2 - 8f, num6); DrawTextureOrLabelCropped(rect6, _uiTitleActionsTexture, "ORÁCULO DO RANKING", _sectionTitleStyle, 0.22f, 0.82f); Rect rect7 = default(Rect); ((Rect)(ref rect7))..ctor(num + 6f, ((Rect)(ref rect6)).yMax + 8f, num2 - 12f, height - (((Rect)(ref rect6)).yMax + 8f) - num7 - 18f); DrawPanel(rect7); Rect val4 = default(Rect); ((Rect)(ref val4))..ctor(((Rect)(ref rect7)).x + 14f, ((Rect)(ref rect7)).y + 12f, ((Rect)(ref rect7)).width - 28f, ((Rect)(ref rect7)).height - 24f); float actionsContentHeight = GetActionsContentHeight(((Rect)(ref val4)).width); bool flag2 = actionsContentHeight > ((Rect)(ref val4)).height + 1f; float num9 = (flag2 ? 18f : 0f); Rect val5 = default(Rect); ((Rect)(ref val5))..ctor(0f, 0f, Mathf.Max(40f, ((Rect)(ref val4)).width - num9), Mathf.Max(((Rect)(ref val4)).height, actionsContentHeight)); _actionsScrollPosition = GUI.BeginScrollView(val4, _actionsScrollPosition, val5, false, flag2); DrawActionsContent(new Rect(0f, 0f, ((Rect)(ref val5)).width, ((Rect)(ref val5)).height)); GUI.EndScrollView(); Rect rect8 = default(Rect); ((Rect)(ref rect8))..ctor(num, height - 34f, num2, 20f); DrawShadowLabel(rect8, "ESC fecha • Clique em cada jogador para abrir/fechar as ações", _footerStyle, new Vector2(1f, 1f), new Color(0f, 0f, 0f, 0.92f)); EndHudTabTransition(previousColor2, previousMatrix2); GUI.DragWindow(new Rect(0f, 0f, width, 110f)); return; } BeginHudTabTransition(out var previousColor3, out var previousMatrix3); float num10 = (((Object)(object)_uiTitleTopTexture != (Object)null) ? Mathf.Clamp(num2 * 0.18f, 76f, 118f) : num4); Rect rect9 = default(Rect); ((Rect)(ref rect9))..ctor(num, ((Rect)(ref rect2)).yMax + 2f, num2, num10); DrawTextureOrLabel(rect9, _uiTitleTopTexture, "TOP GLITNIR", _sectionTitleStyle); float num11 = num4 + num7 + 54f; float num12 = height - (((Rect)(ref rect9)).yMax + 6f) - num11; float num13 = Mathf.Clamp(num12 * 0.48f, 150f, 230f); Rect rect10 = default(Rect); ((Rect)(ref rect10))..ctor(num + 6f, ((Rect)(ref rect9)).yMax + 4f, num2 - 12f, num13); DrawPanel(rect10); Rect val6 = default(Rect); ((Rect)(ref val6))..ctor(((Rect)(ref rect10)).x + 14f, ((Rect)(ref rect10)).y + 12f, ((Rect)(ref rect10)).width - 28f, ((Rect)(ref rect10)).height - 24f); float topEntriesContentHeight = GetTopEntriesContentHeight(((Rect)(ref val6)).width); bool flag3 = topEntriesContentHeight > ((Rect)(ref val6)).height + 1f; float num14 = (flag3 ? 18f : 0f); Rect val7 = default(Rect); ((Rect)(ref val7))..ctor(0f, 0f, Mathf.Max(40f, ((Rect)(ref val6)).width - num14), Mathf.Max(((Rect)(ref val6)).height, topEntriesContentHeight)); _rankingScrollPosition = GUI.BeginScrollView(val6, _rankingScrollPosition, val7, false, flag3); DrawTopEntriesContent(new Rect(0f, 0f, ((Rect)(ref val7)).width, ((Rect)(ref val7)).height)); GUI.EndScrollView(); float num15 = (((Object)(object)_uiTitlePlayerTexture != (Object)null) ? Mathf.Clamp(num2 * 0.12f, 54f, 78f) : num4); Rect rect11 = default(Rect); ((Rect)(ref rect11))..ctor(num, ((Rect)(ref rect10)).yMax + 8f, num2, num15); DrawTextureOrLabel(rect11, _uiTitlePlayerTexture, "SEU DESEMPENHO", _sectionTitleStyle); float num16 = height - (((Rect)(ref rect11)).yMax + 4f) - num7 - 14f; num16 = Mathf.Max(250f, num16); Rect rect12 = default(Rect); ((Rect)(ref rect12))..ctor(num + 6f, ((Rect)(ref rect11)).yMax + 4f, num2 - 12f, num16); DrawPanel(rect12); Rect val8 = default(Rect); ((Rect)(ref val8))..ctor(((Rect)(ref rect12)).x + 14f, ((Rect)(ref rect12)).y + 12f, ((Rect)(ref rect12)).width - 28f, ((Rect)(ref rect12)).height - 24f); float playerContentHeight = GetPlayerContentHeight(((Rect)(ref val8)).width); bool flag4 = playerContentHeight > ((Rect)(ref val8)).height + 1f; float num17 = (flag4 ? 18f : 0f); Rect val9 = default(Rect); ((Rect)(ref val9))..ctor(0f, 0f, Mathf.Max(40f, ((Rect)(ref val8)).width - num17), Mathf.Max(((Rect)(ref val8)).height, playerContentHeight)); _playerInfoScrollPosition = GUI.BeginScrollView(val8, _playerInfoScrollPosition, val9, false, flag4); DrawPlayerContent(new Rect(0f, 0f, ((Rect)(ref val9)).width, ((Rect)(ref val9)).height)); GUI.EndScrollView(); Rect rect13 = default(Rect); ((Rect)(ref rect13))..ctor(num, height - 34f, num2, 20f); DrawShadowLabel(rect13, "ESC fecha • Clique nos cards do desempenho para expandir", _footerStyle, new Vector2(1f, 1f), new Color(0f, 0f, 0f, 0.92f)); EndHudTabTransition(previousColor3, previousMatrix3); GUI.DragWindow(new Rect(0f, 0f, width, 110f)); } private void BeginHudTabTransition(out Color previousColor, out Matrix4x4 previousMatrix) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) previousColor = GUI.color; previousMatrix = GUI.matrix; float num = Mathf.Clamp01((Time.realtimeSinceStartup - _hudTabSwitchTime) * 5.5f); float num2 = Mathf.SmoothStep(0f, 1f, num); float num3 = (1f - num2) * 18f; GUI.color = new Color(previousColor.r, previousColor.g, previousColor.b, previousColor.a * num2); GUI.matrix = Matrix4x4.TRS(new Vector3(num3, 0f, 0f), Quaternion.identity, Vector3.one) * previousMatrix; } private void EndHudTabTransition(Color previousColor, Matrix4x4 previousMatrix) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) GUI.matrix = previousMatrix; GUI.color = previousColor; } private void DrawHudTabs(Rect rect) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) float num = 8f; float num2 = (((Rect)(ref rect)).width - num * 2f) / 3f; DrawHudTabButton(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, num2, ((Rect)(ref rect)).height), "Ranking", 0); DrawHudTabButton(new Rect(((Rect)(ref rect)).x + num2 + num, ((Rect)(ref rect)).y, num2, ((Rect)(ref rect)).height), "Guia", 1); DrawHudTabButton(new Rect(((Rect)(ref rect)).x + (num2 + num) * 2f, ((Rect)(ref rect)).y, num2, ((Rect)(ref rect)).height), "Ações", 2); } private void DrawHudTabButton(Rect rect, string label, int index) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) bool flag = _hudTabIndex == index; Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = (flag ? new Color(0.91f, 0.75f, 0.34f, 0.95f) : new Color(0.34f, 0.26f, 0.13f, 0.9f)); if (GUI.Button(rect, label)) { if (_hudTabIndex != index) { _hudTabIndex = index; _hudTabSwitchTime = Time.realtimeSinceStartup; } GUI.FocusControl((string)null); } GUI.backgroundColor = backgroundColor; if ((Object)(object)_uiWhiteTexture != (Object)null && flag) { GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - 2f, ((Rect)(ref rect)).width, 2f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(1f, 0.86f, 0.34f, 0.95f), 0f, 0f); } } private float GetTopEntriesContentHeight(float width) { int num = ((_cachedTopEntries != null) ? _cachedTopEntries.Count : 0); if (num <= 0) { return 120f; } return 12f + (float)num * 50f + (float)(num - 1) * 6f + 14f; } private void DrawTopEntriesContent(Rect rect) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) if (_cachedTopEntries == null || _cachedTopEntries.Count == 0) { DrawShadowLabel(new Rect(12f, 10f, ((Rect)(ref rect)).width - 24f, 72f), "Nenhum herói pontuado ainda. Saia para caçar, evoluir skills e dominar Glitnir.", _emptyStateStyle); return; } float num = 6f; Rect rect2 = default(Rect); for (int i = 0; i < _cachedTopEntries.Count; i++) { SnapshotTopEntryData entry = _cachedTopEntries[i]; ((Rect)(ref rect2))..ctor(4f, num, ((Rect)(ref rect)).width - 8f, 50f); DrawRankRow(rect2, entry); num += ((Rect)(ref rect2)).height + 6f; } } private void DrawRankRow(Rect rect, SnapshotTopEntryData entry) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_0384: Unknown result type (might be due to invalid IL or missing references) //IL_040c: Unknown result type (might be due to invalid IL or missing references) //IL_033b: Unknown result type (might be due to invalid IL or missing references) //IL_045a: Unknown result type (might be due to invalid IL or missing references) if (entry != null && !((Object)(object)_uiWhiteTexture == (Object)null)) { Color rankAccentColor = GetRankAccentColor(entry.Position); bool flag = entry.Position <= 3; float num = (flag ? (0.55f + (Mathf.Sin(Time.time * 3.2f + (float)entry.Position) + 1f) * 0.225f) : 0f); float num2 = (flag ? (0.13f + num * 0.06f) : 0.06f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height - 4f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.03f, 0.02f, 0.01f, num2), 0f, 0f); if (flag) { GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 4f, ((Rect)(ref rect)).y + 3f, ((Rect)(ref rect)).width - 8f, ((Rect)(ref rect)).height - 6f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(rankAccentColor.r, rankAccentColor.g, rankAccentColor.b, 0.05f + num * 0.1f), 0f, 0f); } GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, 4f, ((Rect)(ref rect)).height), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(rankAccentColor.r, rankAccentColor.g, rankAccentColor.b, flag ? (0.75f + num * 0.2f) : rankAccentColor.a), 0f, 0f); if (flag) { GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 4f, ((Rect)(ref rect)).y + 1f, ((Rect)(ref rect)).width - 8f, 1.5f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(rankAccentColor.r, rankAccentColor.g, rankAccentColor.b, 0.35f + num * 0.35f), 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 4f, ((Rect)(ref rect)).yMax - 2.5f, ((Rect)(ref rect)).width - 8f, 1.5f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(rankAccentColor.r, rankAccentColor.g, rankAccentColor.b, 0.18f + num * 0.22f), 0f, 0f); } Texture2D rankPodiumIcon = GetRankPodiumIcon(entry.Position); float num3 = ((Rect)(ref rect)).x + 58f; if ((Object)(object)rankPodiumIcon != (Object)null) { float num4 = ((entry.Position == 1) ? 42f : ((entry.Position == 2) ? 40f : 38f)); Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(((Rect)(ref rect)).x + 8f, ((Rect)(ref rect)).y + 5f, num4, num4); DrawTextureAspectFit(rect2, rankPodiumIcon); num3 = ((Rect)(ref rect)).x + 58f; } else { Rect rect3 = default(Rect); ((Rect)(ref rect3))..ctor(((Rect)(ref rect)).x + 10f, ((Rect)(ref rect)).y + 8f, 48f, 20f); DrawShadowLabel(rect3, "#" + Mathf.Max(1, entry.Position), _rankIndexStyle); } float num5 = 10f; if (entry.Position <= 3) { num5 += 18f; } Rect rect4 = default(Rect); ((Rect)(ref rect4))..ctor(num3, ((Rect)(ref rect)).y + 6f, ((Rect)(ref rect)).width - (num3 - ((Rect)(ref rect)).x) - 74f - num5, 22f); DrawShadowLabel(rect4, string.IsNullOrWhiteSpace(entry.PlayerName) ? "Jogador" : entry.PlayerName.ToUpperInvariant(), _rankNameStyle); Rect rect5 = default(Rect); ((Rect)(ref rect5))..ctor(num3, ((Rect)(ref rect)).y + 26f, 140f, 16f); DrawShadowLabel(rect5, ColorizePoints(FormatPoints(entry.Points)), _rankPointsStyle); } } private Texture2D GetRankPodiumIcon(int position) { return (Texture2D)(position switch { 1 => _uiRank1IconTexture, 2 => _uiRank2IconTexture, 3 => _uiRank3IconTexture, _ => null, }); } private string GetRankBadgeLabel(int position) { return position switch { 1 => "OURO", 2 => "PRATA", 3 => "BRONZE", _ => "", }; } private float GetActionsContentHeight(float width) { int num = ((_cachedTopEntries != null) ? _cachedTopEntries.Count : 0); if (num <= 0) { return 130f; } float num2 = 12f; for (int i = 0; i < num; i++) { SnapshotTopEntryData entry = _cachedTopEntries[i]; num2 += (IsActionPlayerExpanded(entry) ? 738f : 58f); num2 += 8f; } return num2 + 14f; } private void DrawActionsContent(Rect rect) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) if (_cachedTopEntries == null || _cachedTopEntries.Count == 0) { DrawShadowLabel(new Rect(12f, 10f, ((Rect)(ref rect)).width - 24f, 72f), "Nenhum jogador pontuado ainda. Quando o ranking receber pontos, as ações aparecerão aqui.", _emptyStateStyle); return; } float num = 8f; Rect rect2 = default(Rect); for (int i = 0; i < _cachedTopEntries.Count; i++) { SnapshotTopEntryData entry = _cachedTopEntries[i]; float num2 = (IsActionPlayerExpanded(entry) ? 738f : 58f); ((Rect)(ref rect2))..ctor(4f, num, ((Rect)(ref rect)).width - 8f, num2); DrawActionPlayerRow(rect2, entry); num += ((Rect)(ref rect2)).height + 8f; } } private string BuildActionPlayerKey(SnapshotTopEntryData entry) { if (entry == null) { return ""; } return Mathf.Max(1, entry.Position) + ":" + SafeKey(entry.PlayerName); } private bool IsActionPlayerExpanded(SnapshotTopEntryData entry) { string text = BuildActionPlayerKey(entry); return !string.IsNullOrWhiteSpace(text) && _expandedActionPlayers.Contains(text); } private void ToggleActionPlayerExpanded(SnapshotTopEntryData entry) { string text = BuildActionPlayerKey(entry); if (!string.IsNullOrWhiteSpace(text)) { if (_expandedActionPlayers.Contains(text)) { _expandedActionPlayers.Remove(text); } else { _expandedActionPlayers.Add(text); } } } private string GetNextExplorationGoalText(float currentPercent) { SnapshotPlayerData cachedPlayerData = _cachedPlayerData; if (cachedPlayerData == null || string.IsNullOrWhiteSpace(cachedPlayerData.HudExplorationMapJackpotRules)) { return "Sem marco configurado"; } try { string[] array = cachedPlayerData.HudExplorationMapJackpotRules.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { string[] array2 = text.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array2.Length == 2) { string s = array2[0].Replace("Mapa", "").Replace("%", "").Trim(); if (int.TryParse(s, out var result) && int.TryParse(array2[1].Trim(), out var result2) && currentPercent < (float)result) { return result + "% (+" + result2 + " pts)"; } } } } catch { } return "Completo"; } private void DrawExplorationActionCard(Rect rect, Color accent, SnapshotTopEntryData entry) { //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) float currentPercent = 0f; string text = "Disponível no seu perfil"; if (entry != null && entry.IsLocalPlayer) { currentPercent = GetExplorationMapProgressPercent(); text = GetNextExplorationGoalText(currentPercent); } if ((Object)(object)_uiWhiteTexture != (Object)null) { GUI.DrawTexture(rect, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0f, 0f, 0f, 0.26f), 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, 1f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(accent.r, accent.g, accent.b, 0.36f), 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 26f, ((Rect)(ref rect)).width, 1f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(accent.r, accent.g, accent.b, 0.18f), 0f, 0f); } DrawShadowLabel(new Rect(((Rect)(ref rect)).x + 10f, ((Rect)(ref rect)).y + 5f, ((Rect)(ref rect)).width - 20f, 20f), "EXPLORAÇÃO", _cardLabelStyle); string amount = ((entry != null && entry.IsLocalPlayer) ? (currentPercent.ToString("0.0", CultureInfo.InvariantCulture) + "%") : "—"); DrawActionDetailLine(new Rect(((Rect)(ref rect)).x + 12f, ((Rect)(ref rect)).y + 32f, ((Rect)(ref rect)).width - 24f, 20f), "Mapa revelado", amount, 0); DrawShadowLabel(new Rect(((Rect)(ref rect)).x + 12f, ((Rect)(ref rect)).y + 55f, ((Rect)(ref rect)).width - 24f, 20f), ColorizeMuted("Próximo marco: ") + ColorizeHighlight(text), _bodyRowStyle); } private void DrawActionPlayerRow(Rect rect, SnapshotTopEntryData entry) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_0493: Unknown result type (might be due to invalid IL or missing references) //IL_049d: Unknown result type (might be due to invalid IL or missing references) //IL_0437: Unknown result type (might be due to invalid IL or missing references) //IL_04f5: Unknown result type (might be due to invalid IL or missing references) //IL_04ff: Unknown result type (might be due to invalid IL or missing references) //IL_0559: Unknown result type (might be due to invalid IL or missing references) //IL_055e: Unknown result type (might be due to invalid IL or missing references) //IL_0579: Unknown result type (might be due to invalid IL or missing references) //IL_0583: Unknown result type (might be due to invalid IL or missing references) //IL_05c8: Unknown result type (might be due to invalid IL or missing references) //IL_05d2: Unknown result type (might be due to invalid IL or missing references) //IL_062b: Unknown result type (might be due to invalid IL or missing references) //IL_0635: Unknown result type (might be due to invalid IL or missing references) //IL_068e: Unknown result type (might be due to invalid IL or missing references) //IL_0698: Unknown result type (might be due to invalid IL or missing references) //IL_0778: Unknown result type (might be due to invalid IL or missing references) //IL_078a: Unknown result type (might be due to invalid IL or missing references) //IL_0790: Unknown result type (might be due to invalid IL or missing references) //IL_0796: Unknown result type (might be due to invalid IL or missing references) //IL_07a1: Unknown result type (might be due to invalid IL or missing references) //IL_07e3: Unknown result type (might be due to invalid IL or missing references) //IL_0726: Unknown result type (might be due to invalid IL or missing references) //IL_0749: Unknown result type (might be due to invalid IL or missing references) if (entry == null || (Object)(object)_uiWhiteTexture == (Object)null) { return; } bool flag = IsActionPlayerExpanded(entry); Color rankAccentColor = GetRankAccentColor(entry.Position); Color val = (flag ? new Color(0.035f, 0.022f, 0.01f, 0.5f) : new Color(0.025f, 0.018f, 0.01f, 0.34f)); GUI.DrawTexture(rect, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, 5f, ((Rect)(ref rect)).height), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, rankAccentColor, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 5f, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width - 10f, 1.5f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(rankAccentColor.r, rankAccentColor.g, rankAccentColor.b, flag ? 0.72f : 0.36f), 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 5f, ((Rect)(ref rect)).yMax - 1.5f, ((Rect)(ref rect)).width - 10f, 1.5f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(rankAccentColor.r, rankAccentColor.g, rankAccentColor.b, flag ? 0.46f : 0.2f), 0f, 0f); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref rect)).x + 12f, ((Rect)(ref rect)).y + 8f, ((Rect)(ref rect)).width - 24f, 42f); if (GUI.Button(val2, GUIContent.none, GUIStyle.none)) { ToggleActionPlayerExpanded(entry); } string text = (flag ? "▼" : "▶"); string text2 = (string.IsNullOrWhiteSpace(entry.PlayerName) ? "JOGADOR" : entry.PlayerName.ToUpperInvariant()); DrawShadowLabel(new Rect(((Rect)(ref val2)).x + 2f, ((Rect)(ref val2)).y + 8f, 26f, 22f), text, _bodyValueStyle); DrawShadowLabel(new Rect(((Rect)(ref val2)).x + 32f, ((Rect)(ref val2)).y + 3f, ((Rect)(ref val2)).width - 184f, 26f), "#" + Mathf.Max(1, entry.Position) + " " + text2, _panelHeadingStyle); DrawShadowLabel(new Rect(((Rect)(ref val2)).xMax - 142f, ((Rect)(ref val2)).y + 5f, 132f, 22f), ColorizePoints(FormatPoints(entry.Points)), _bodyValueStyle); if (!flag) { string text3 = ColorizeDanger(entry.TotalKillsPontuadas + " kills") + " • " + ColorizeDanger(entry.TotalBossesPontuadas + " bosses") + " • " + ColorizeProgress(entry.TotalSkillLevelUpsPontuados + " skills") + " • " + ColorizeHighlight(entry.TotalMarketplaceQuestsPontuadas + " quests") + " • " + ColorizeProgress(entry.TotalCraftPontuadas + " crafts") + " • " + ColorizeDanger(entry.TotalPointsExchanges + " câmbios"); DrawShadowLabel(new Rect(((Rect)(ref val2)).x + 32f, ((Rect)(ref val2)).y + 27f, ((Rect)(ref val2)).width - 48f, 16f), text3, _mutedBodyStyle); return; } float num = ((Rect)(ref rect)).x + 20f; float num2 = ((Rect)(ref rect)).y + 58f; float num3 = ((Rect)(ref rect)).width - 40f; float num4 = 78f; float num5 = 10f; DrawActionSectionCard(new Rect(num, num2, num3, num4), "COMBATE", rankAccentColor, "Kills", entry.TotalKillsPontuadas.ToString() ?? "", entry.KillPointsTotal, "Bosses", entry.TotalBossesPontuadas.ToString() ?? "", entry.BossPointsTotal); num2 += num4 + num5; DrawActionSectionCard(new Rect(num, num2, num3, num4), "PROGRESSÃO", rankAccentColor, "Skills", entry.TotalSkillLevelUpsPontuados + " marcos", entry.SkillPointsTotal, "Pesca", entry.TotalFishingPontuadas + " peixes", entry.FishingPointsTotal); num2 += num4 + num5; DrawExplorationActionCard(new Rect(num, num2, num3, num4), rankAccentColor, entry); num2 += num4 + num5; DrawActionSectionCard(new Rect(num, num2, num3, num4), "QUESTS MARKETPLACE", rankAccentColor, "Quests", entry.TotalMarketplaceQuestsPontuadas + " concluídas", entry.MarketplaceQuestPointsTotal, "Tipo", "Marketplace", 0); num2 += num4 + num5; DrawActionSectionCard(new Rect(num, num2, num3, num4), "ECONOMIA", rankAccentColor, "Crafts", entry.TotalCraftPontuadas.ToString() ?? "", entry.CraftPointsTotal, "Cultivo", entry.TotalFarmJackpotsPontuados + " jackpots", entry.FarmJackpotPointsTotal); num2 += num4 + num5; DrawActionSectionCard(new Rect(num, num2, num3, num4), "FEITOS", rankAccentColor, "Únicos", entry.TotalUniqueCraftJackpotsPontuados.ToString() ?? "", entry.UniqueCraftJackpotPointsTotal, "Mortes", entry.TotalDeaths.ToString() ?? "", -entry.DeathPenaltyPointsTotal); num2 += num4 + num5; DrawActionSectionCard(new Rect(num, num2, num3, num4), "CÂMBIO", rankAccentColor, "Trocas", entry.TotalPointsExchanges + " realizadas", -entry.PointsExchangePenaltyTotal, "Moedas", entry.PointsExchangeCoinsTotal + " Coins", 0); string text4 = (string.IsNullOrWhiteSpace(entry.LastReason) ? "Sem último registro" : entry.LastReason); Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(num, ((Rect)(ref rect)).yMax - 32f, num3, 22f); if ((Object)(object)_uiWhiteTexture != (Object)null) { GUI.DrawTexture(val3, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0f, 0f, 0f, 0.24f), 0f, 0f); } GUI.DrawTexture(new Rect(((Rect)(ref val3)).x, ((Rect)(ref val3)).y, 3f, ((Rect)(ref val3)).height), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(rankAccentColor.r, rankAccentColor.g, rankAccentColor.b, 0.7f), 0f, 0f); DrawShadowLabel(new Rect(((Rect)(ref val3)).x + 10f, ((Rect)(ref val3)).y + 3f, ((Rect)(ref val3)).width - 20f, 18f), ColorizeMuted("Último registro: ") + ColorizeHighlight(text4), _configStyle); } private void DrawActionSectionCard(Rect rect, string title, Color accent, string labelA, string amountA, int pointsA, string labelB, string amountB, int pointsB) { //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_uiWhiteTexture != (Object)null) { GUI.DrawTexture(rect, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0f, 0f, 0f, 0.26f), 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, 1f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(accent.r, accent.g, accent.b, 0.36f), 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 26f, ((Rect)(ref rect)).width, 1f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(accent.r, accent.g, accent.b, 0.18f), 0f, 0f); } DrawShadowLabel(new Rect(((Rect)(ref rect)).x + 10f, ((Rect)(ref rect)).y + 5f, ((Rect)(ref rect)).width - 20f, 20f), title, _cardLabelStyle); DrawActionDetailLine(new Rect(((Rect)(ref rect)).x + 12f, ((Rect)(ref rect)).y + 32f, ((Rect)(ref rect)).width - 24f, 20f), labelA, amountA, pointsA); DrawActionDetailLine(new Rect(((Rect)(ref rect)).x + 12f, ((Rect)(ref rect)).y + 55f, ((Rect)(ref rect)).width - 24f, 20f), labelB, amountB, pointsB); } private void DrawActionDetailLine(Rect rect, string label, string amount, int points) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) GUIStyle style = new GUIStyle(_bodyRowStyle) { clipping = (TextClipping)1, wordWrap = false, alignment = (TextAnchor)3 }; GUIStyle style2 = new GUIStyle(_bodyValueStyle) { clipping = (TextClipping)1, wordWrap = false, alignment = (TextAnchor)5 }; float num = Mathf.Clamp(((Rect)(ref rect)).width * 0.42f, 145f, 190f); float num2 = Mathf.Max(90f, ((Rect)(ref rect)).width - num - 12f); Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, num2, ((Rect)(ref rect)).height); Rect rect3 = default(Rect); ((Rect)(ref rect3))..ctor(((Rect)(ref rect)).xMax - num, ((Rect)(ref rect)).y, num, ((Rect)(ref rect)).height); string amount2 = (string.IsNullOrWhiteSpace(amount) ? "0" : amount.Trim()); string text = "" + label + " " + ColorizeActionAmount(label, amount2); DrawShadowLabel(rect2, text, style); DrawShadowLabel(rect3, FormatSignedPointsColored(points), style2); } private void DrawActionMetricBox(Rect rect, string label, string amount, int points) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_uiWhiteTexture != (Object)null) { GUI.DrawTexture(rect, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0f, 0f, 0f, 0.2f), 0f, 0f); } GUIStyle style = new GUIStyle(_mutedBodyStyle) { clipping = (TextClipping)1, wordWrap = false, alignment = (TextAnchor)3 }; GUIStyle style2 = new GUIStyle(_bodyRowStyle) { clipping = (TextClipping)1, wordWrap = false, alignment = (TextAnchor)3 }; GUIStyle style3 = new GUIStyle(_bodyValueStyle) { clipping = (TextClipping)1, wordWrap = false, alignment = (TextAnchor)5 }; float num = ((Rect)(ref rect)).x + 7f; float num2 = ((Rect)(ref rect)).y + 2f; float num3 = ((Rect)(ref rect)).width - 14f; float num4 = ((Rect)(ref rect)).height - 4f; float num5 = num3 * 0.34f; float num6 = num3 * 0.25f; float num7 = num3 - num5 - num6 - 10f; Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(num, num2, num5, num4); Rect rect3 = default(Rect); ((Rect)(ref rect3))..ctor(((Rect)(ref rect2)).xMax + 4f, num2, num6, num4); Rect rect4 = default(Rect); ((Rect)(ref rect4))..ctor(((Rect)(ref rect3)).xMax + 6f, num2, num7, num4); DrawShadowLabel(rect2, label, style); DrawShadowLabel(rect3, ColorizeActionAmount(label, amount), style2); DrawShadowLabel(rect4, FormatSignedPointsColored(points), style3); } private string FormatPoints(int points) { return points.ToString("N0", CultureInfo.InvariantCulture).Replace(",", ".") + " pts"; } private string FormatSignedPoints(int points) { if (points > 0) { return "+" + FormatPoints(points); } if (points < 0) { return "-" + FormatPoints(Mathf.Abs(points)); } return "0 pts"; } private string ColorizeHud(string text, string color) { return "" + text + ""; } private string ColorizePoints(string text) { return ColorizeHud(text, "#00FF7F"); } private string ColorizeDanger(string text) { return ColorizeHud(text, "#FF5555"); } private string ColorizeProgress(string text) { return ColorizeHud(text, "#4FC3F7"); } private string ColorizeHighlight(string text) { return ColorizeHud(text, "#FFD700"); } private string ColorizeMuted(string text) { return ColorizeHud(text, "#D7DCE5"); } private string FormatSignedPointsColored(int points) { if (points > 0) { return ColorizePoints("+" + FormatPoints(points)); } if (points < 0) { return ColorizeDanger("-" + FormatPoints(Mathf.Abs(points))); } return ColorizeMuted("0 pts"); } private string ColorizeActionAmount(string label, string amount) { string text = (label ?? "").ToLowerInvariant(); if (text.Contains("morte") || text.Contains("câmbio") || text.Contains("cambio") || text.Contains("kill") || text.Contains("boss") || text.Contains("combate")) { return ColorizeDanger(amount); } if (text.Contains("skill") || text.Contains("pesca") || text.Contains("craft") || text.Contains("cultivo") || text.Contains("únic") || text.Contains("unico")) { return ColorizeProgress(amount); } return ColorizeHighlight(amount); } private string ColorizePerformanceValue(string label, string value) { string text = (label ?? "").ToLowerInvariant(); if (text.Contains("pontos")) { if (int.TryParse(value, out var result)) { return FormatSignedPointsColored(result); } return ColorizePoints(value); } if (text.Contains("kill") || text.Contains("boss") || text.Contains("morte") || text.Contains("câmbio") || text.Contains("cambio") || text.Contains("penalidade")) { return ColorizeDanger(value); } if (text.Contains("skill") || text.Contains("craft") || text.Contains("pesca") || text.Contains("cultivo") || text.Contains("níve") || text.Contains("nive")) { return ColorizeProgress(value); } if (text.Contains("posição") || text.Contains("origem") || text.Contains("item") || text.Contains("recompensa")) { return ColorizeHighlight(value); } return ColorizeMuted(value); } private float GetPlayerContentHeight(float width) { if (_cachedPlayerData == null || !_cachedPlayerData.HasData) { return 230f; } float num = 12f; num += 80f; num += EstimatePerformanceBlockHeight("Resumo", 108f); num += EstimatePerformanceBlockHeight("Origem", 304f); num += EstimatePerformanceBlockHeight("Último registro", 58f); num += EstimatePerformanceBlockHeight("Recompensa", 250f); num += EstimatePerformanceBlockHeight("Configuração", 44f); return Mathf.Max(230f, num + 18f); } private float EstimatePerformanceBlockHeight(string categoryKey, float expandedContentHeight) { if (!IsPerformanceCategoryExpanded(categoryKey)) { return 42f; } return 72f + expandedContentHeight + 14f; } private void DrawPlayerContent(Rect rect) { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) float num = 8f; float num2 = ((Rect)(ref rect)).width - 16f; float num3 = 12f; if (_cachedPlayerData == null || !_cachedPlayerData.HasData) { DrawPerformanceEmptyState(new Rect(num, num3, num2, 150f)); num3 += 166f; DrawPerformanceBlock(new Rect(num, num3, num2, 10f), "Configuração", "Configuração", "Resumo rápido das regras ativas no servidor.", 44f, delegate(Rect contentRect) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) DrawConfigSection(contentRect, _cachedPlayerData); }); return; } DrawPlayerSummaryBlock(new Rect(num, num3, num2, 58f)); num3 += 80f; num3 = DrawPerformanceBlock(new Rect(num, num3, num2, 10f), "Resumo da jornada", "Resumo", "Sua atividade principal registrada no ranking.", 134f, delegate(Rect contentRect) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) DrawSimpleInfoRows(contentRect, new string[5] { "Kills pontuadas|" + _cachedPlayerData.TotalKillsPontuadas, "Bosses pontuados|" + _cachedPlayerData.TotalBossesPontuadas, "Níveis de skill|" + _cachedPlayerData.TotalSkillLevelUpsPontuados, "Quests Marketplace|" + _cachedPlayerData.TotalMarketplaceQuestsPontuadas, "Câmbios realizados|" + _cachedPlayerData.TotalPointsExchanges }); }); num3 = DrawPerformanceBlock(new Rect(num, num3, num2, 10f), "Origem dos pontos", "Origem", "Veja quais caminhos estão sustentando sua posição.", 330f, delegate(Rect contentRect) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) DrawSimpleInfoRows(contentRect, new string[12] { "Pontos por kills|" + _cachedPlayerData.KillPointsTotal, "Pontos por bosses|" + _cachedPlayerData.BossPointsTotal, "Pontos por skills|" + _cachedPlayerData.SkillPointsTotal, "Pontos por quests|" + _cachedPlayerData.MarketplaceQuestPointsTotal, "Pontos por pesca|" + _cachedPlayerData.FishingPointsTotal, "Pontos por craft|" + _cachedPlayerData.CraftPointsTotal, "Pontos por cultivo|" + _cachedPlayerData.FarmJackpotPointsTotal, "Pontos por itens únicos|" + _cachedPlayerData.UniqueCraftJackpotPointsTotal, "Pontos por exploração|" + _cachedPlayerData.ExplorationMapJackpotPointsTotal, "Penalidade por mortes|-" + _cachedPlayerData.DeathPenaltyPointsTotal, "Penalidade por câmbio|-" + _cachedPlayerData.PointsExchangePenaltyTotal, "Coins recebidas no câmbio|" + _cachedPlayerData.PointsExchangeCoinsTotal }); }); num3 = DrawPerformanceBlock(new Rect(num, num3, num2, 10f), "Último registro", "Último registro", "Última ação registrada pelo servidor para sua jornada.", 58f, delegate(Rect contentRect) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) DrawSimpleInfoRows(contentRect, new string[2] { "Origem|" + (string.IsNullOrWhiteSpace(_cachedPlayerData.LastReason) ? "sem registro" : _cachedPlayerData.LastReason), "Atualização|" + FormatHudLastUpdate(_cachedPlayerData.LastUpdateUtc) }); }); num3 = DrawPerformanceBlock(new Rect(num, num3, num2, 10f), "Recompensa do ranking", "Recompensa", "Prêmios disponíveis para os melhores colocados da temporada e câmbio de pontos por moedas.", 300f, delegate(Rect contentRect) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) DrawRewardClaimSection(contentRect); }); num3 = DrawPerformanceBlock(new Rect(num, num3, num2, 10f), "Configuração atual", "Configuração", "Estado das regras principais que afetam sua pontuação.", 44f, delegate(Rect contentRect) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) DrawConfigSection(contentRect, _cachedPlayerData); }); } private void DrawPerformanceEmptyState(Rect rect) { //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_uiWhiteTexture != (Object)null) { GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height - 4f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.03f, 0.02f, 0.01f, 0.06f), 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, 4f, ((Rect)(ref rect)).height), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.91f, 0.75f, 0.34f, 0.82f), 0f, 0f); } DrawShadowLabel(new Rect(((Rect)(ref rect)).x + 14f, ((Rect)(ref rect)).y + 12f, ((Rect)(ref rect)).width - 28f, 24f), "Sua jornada ainda não começou", _panelHeadingStyle); DrawBodyParagraph(new Rect(((Rect)(ref rect)).x + 14f, ((Rect)(ref rect)).y + 42f, ((Rect)(ref rect)).width - 28f, 72f), "Ganhe pontos derrotando criaturas, produzindo itens, pescando, completando jackpots e evoluindo seu personagem. Quando o servidor registrar seus primeiros pontos, seu desempenho será exibido em cards expansíveis."); } private float DrawPerformanceBlock(Rect startRect, string title, string categoryKey, string description, float expandedContentHeight, Action drawContent) { //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) float x = ((Rect)(ref startRect)).x; float y = ((Rect)(ref startRect)).y; float width = ((Rect)(ref startRect)).width; bool flag = IsPerformanceCategoryExpanded(categoryKey); Rect val = default(Rect); ((Rect)(ref val))..ctor(x, y, width, 34f); if ((Object)(object)_uiWhiteTexture != (Object)null) { Color val2 = (flag ? new Color(0.17f, 0.11f, 0.035f, 0.55f) : new Color(0.045f, 0.032f, 0.018f, 0.42f)); GUI.DrawTexture(val, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val2, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).yMax - 1f, ((Rect)(ref val)).width, 1f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.95f, 0.76f, 0.33f, flag ? 0.55f : 0.24f), 0f, 0f); } Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = new Color(0.27f, 0.2f, 0.09f, flag ? 0.86f : 0.62f); if (GUI.Button(val, GUIContent.none)) { TogglePerformanceCategory(categoryKey); GUI.FocusControl((string)null); } GUI.backgroundColor = backgroundColor; string text = (flag ? "▼" : "▶"); DrawShadowLabel(new Rect(x + 10f, y + 7f, 20f, 22f), text, _panelHeadingStyle); Texture2D ruleCategoryIcon = GetRuleCategoryIcon(categoryKey); float num = x + 36f; if ((Object)(object)ruleCategoryIcon != (Object)null) { Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(x + 31f, y + 5f, 24f, 24f); GUI.DrawTexture(val3, (Texture)(object)ruleCategoryIcon, (ScaleMode)2, true); num = ((Rect)(ref val3)).xMax + 8f; } DrawShadowLabel(new Rect(num, y + 7f, width - (num - x) - 12f, 22f), title, _panelHeadingStyle); y += 40f; if (!flag) { return y + 2f; } DrawBodyParagraph(new Rect(x + 8f, y, width - 16f, 24f), description); y += 30f; Rect obj = default(Rect); ((Rect)(ref obj))..ctor(x + 8f, y, width - 16f, expandedContentHeight); drawContent?.Invoke(obj); return y + (expandedContentHeight + 14f); } private void EnsurePerformanceCategoriesInitialized() { if (!_performanceCategoriesInitialized) { _expandedPerformanceCategories.Clear(); _performanceCategoriesInitialized = true; } } private bool IsPerformanceCategoryExpanded(string categoryKey) { if (string.IsNullOrWhiteSpace(categoryKey)) { return false; } EnsurePerformanceCategoriesInitialized(); return _expandedPerformanceCategories.Contains(categoryKey); } private void TogglePerformanceCategory(string categoryKey) { if (!string.IsNullOrWhiteSpace(categoryKey)) { EnsurePerformanceCategoriesInitialized(); if (_expandedPerformanceCategories.Contains(categoryKey)) { _expandedPerformanceCategories.Remove(categoryKey); } else { _expandedPerformanceCategories.Add(categoryKey); } } } private float GetRulesContentHeight(float width) { SnapshotPlayerData snapshotPlayerData = _cachedPlayerData ?? new SnapshotPlayerData(); float num = 14f; num += 54f; num += 34f; num += EstimateRulesBlockHeight(width, "Combate", snapshotPlayerData.HudKillRules); num += EstimateRulesBlockHeight(width, "Bosses", snapshotPlayerData.HudBossRules); num += EstimateRulesBlockHeight(width, "Skills", snapshotPlayerData.HudSkillJackpotRules); num += EstimateRulesBlockHeight(width, "Quests", snapshotPlayerData.HudMarketplaceQuestRules); num += EstimateRulesBlockHeight(width, "Pesca", snapshotPlayerData.HudFishingRules); num += EstimateRulesBlockHeight(width, "Exploração", snapshotPlayerData.HudExplorationMapJackpotRules); num += EstimateRulesBlockHeight(width, "Produção", snapshotPlayerData.HudCraftRules); num += EstimateRulesBlockHeight(width, "Cultivo", snapshotPlayerData.HudFarmJackpotRules); num += EstimateRulesBlockHeight(width, "Conquistas", snapshotPlayerData.HudUniqueCraftJackpotRules); num += EstimateRulesBlockHeight(width, "Penalidades", GetDeathPenaltyHudRules(snapshotPlayerData)); return Mathf.Max(260f, num + 18f); } private float EstimateRulesBlockHeight(float width, string title, string rules) { if (!IsRuleCategoryExpanded(title)) { return 42f; } if (string.Equals(title, "Skills", StringComparison.OrdinalIgnoreCase)) { return EstimateSkillGuideBlockHeight(rules); } if (ShouldGroupRulesByItemCategory(title)) { return EstimateCategorizedGuideBlockHeight(title, rules); } int num = SplitRulesForHud(rules).Count(); num = Mathf.Max(1, num); float num2 = ((string.Equals(title, "Exploração", StringComparison.OrdinalIgnoreCase) || string.Equals(title, "Exploracao", StringComparison.OrdinalIgnoreCase) || string.Equals(title, "Exploration", StringComparison.OrdinalIgnoreCase)) ? 58f : 0f); return 72f + num2 + (float)num * 26f + 14f; } private void DrawRulesContent(Rect rect) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) SnapshotPlayerData snapshotPlayerData = _cachedPlayerData ?? new SnapshotPlayerData(); float num = 8f; float num2 = ((Rect)(ref rect)).width - 16f; float num3 = 12f; DrawBodyParagraph(new Rect(num, num3, num2, 42f), "Clique em uma categoria para abrir ou fechar. Use este guia como mapa rápido de pontos: ações comuns aparecem com +pts, jackpots mostram meta e recompensa, e penalidades aparecem como perda."); num3 += 54f; num3 = DrawRulesBlock(new Rect(num, num3, num2, 10f), "Combate", "Combate", snapshotPlayerData.EnableKillPoints, snapshotPlayerData.HudKillRules, "Mate criaturas configuradas pelo servidor.", "Nome:pontos", jackpotFormat: false); num3 = DrawRulesBlock(new Rect(num, num3, num2, 10f), "Bosses", "Bosses", snapshotPlayerData.EnableBossPoints, snapshotPlayerData.HudBossRules, "Derrote bosses válidos e receba pontos pelo crédito registrado.", "Nome:pontos", jackpotFormat: false); num3 = DrawRulesBlock(new Rect(num, num3, num2, 10f), "Skills", "Skills", snapshotPlayerData.EnableSkillPoints, snapshotPlayerData.HudSkillJackpotRules, "Evolua uma habilidade até um marco configurado para receber pontos. Cada habilidade pode ser aberta separadamente para ver seus marcos.", "Clique na habilidade para ver níveis e recompensas", jackpotFormat: false); num3 = DrawRulesBlock(new Rect(num, num3, num2, 10f), "Quests", "Quests", snapshotPlayerData.EnableMarketplaceQuestPoints, snapshotPlayerData.HudMarketplaceQuestRules, "Complete quests configuradas do Marketplace para receber pontos únicos.", "Quest:pontos", jackpotFormat: false); num3 = DrawRulesBlock(new Rect(num, num3, num2, 10f), "Pesca", "Pesca", enabled: true, snapshotPlayerData.HudFishingRules, "Pesque os peixes configurados para receber pontos.", "Nome:pontos", jackpotFormat: false); num3 = DrawRulesBlock(new Rect(num, num3, num2, 10f), "Exploração", "Exploração", snapshotPlayerData.RankingEnabled, snapshotPlayerData.HudExplorationMapJackpotRules, "Revele o mapa para ativar jackpots de exploração.", "Mapa %:pontos", jackpotFormat: false); num3 = DrawRulesBlock(new Rect(num, num3, num2, 10f), "Produção", "Produção", enabled: true, snapshotPlayerData.HudCraftRules, "Crie armas, armaduras, comidas ou outros itens configurados.", "Nome:pontos", jackpotFormat: false); num3 = DrawRulesBlock(new Rect(num, num3, num2, 10f), "Cultivo", "Cultivo", enabled: true, snapshotPlayerData.HudFarmJackpotRules, "Complete grandes metas de colheita para ativar jackpots.", "Nome:quantidade:pontos", jackpotFormat: true); num3 = DrawRulesBlock(new Rect(num, num3, num2, 10f), "Conquistas", "Conquistas", enabled: true, snapshotPlayerData.HudUniqueCraftJackpotRules, "Crie itens especiais uma única vez para ganhar bônus.", "Nome:quantidade:pontos", jackpotFormat: true); string deathPenaltyHudRules = GetDeathPenaltyHudRules(snapshotPlayerData); string description = (snapshotPlayerData.DeathPenaltyUseMultiplier ? "Modo multiplicador ativo: cada morte reduz pontos pelo valor fixo configurado." : "Mortes podem reduzir pontos conforme a configuração por marcos."); string formatHint = (snapshotPlayerData.DeathPenaltyUseMultiplier ? "mortes x pontos por morte" : "mortes:pontos perdidos"); num3 = DrawRulesBlock(new Rect(num, num3, num2, 10f), "Penalidades", "Penalidades", snapshotPlayerData.EnableDeathPenalty, deathPenaltyHudRules, description, formatHint, !snapshotPlayerData.DeathPenaltyUseMultiplier); } private string GetDeathPenaltyHudRules(SnapshotPlayerData data) { if (data == null) { return string.Empty; } if (!data.DeathPenaltyUseMultiplier) { return data.HudDeathPenaltyRules ?? string.Empty; } int num = Mathf.Max(0, data.DeathPenaltyPerDeath); return "1 morte:-" + num + ";15 mortes:-" + num * 15; } private float GetExplorationMapProgressPercent() { SnapshotPlayerData cachedPlayerData = _cachedPlayerData; if (cachedPlayerData == null) { return 0f; } if (cachedPlayerData.FloatProgressCounters != null) { if (cachedPlayerData.FloatProgressCounters.TryGetValue("ExplorationMap:Percent", out var value)) { return Mathf.Clamp(value, 0f, 100f); } if (cachedPlayerData.FloatProgressCounters.TryGetValue("Exploration:Map", out value)) { return Mathf.Clamp(value, 0f, 100f); } if (cachedPlayerData.FloatProgressCounters.TryGetValue("Exploracao:Mapa", out value)) { return Mathf.Clamp(value, 0f, 100f); } } if (cachedPlayerData.ProgressCounters == null) { return 0f; } if (cachedPlayerData.ProgressCounters.TryGetValue("ExplorationMap:Percent", out var value2)) { return Mathf.Clamp(value2, 0, 100); } if (cachedPlayerData.ProgressCounters.TryGetValue("Exploration:Map", out value2)) { return Mathf.Clamp(value2, 0, 100); } if (cachedPlayerData.ProgressCounters.TryGetValue("Exploracao:Mapa", out value2)) { return Mathf.Clamp(value2, 0, 100); } return 0f; } private void DrawExplorationMapProgressBar(Rect rect) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) float explorationMapProgressPercent = GetExplorationMapProgressPercent(); float num = Mathf.Clamp01(explorationMapProgressPercent / 100f); Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, 18f); DrawShadowLabel(rect2, "Mapa revelado: " + explorationMapProgressPercent.ToString("0.0", CultureInfo.InvariantCulture) + "%", GetHudTintedStyle(_bodyValueStyle, new Color(0.58f, 0.9f, 1f, 1f), (TextAnchor)3)); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 24f, ((Rect)(ref rect)).width, 14f); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).x + 2f, ((Rect)(ref val)).y + 2f, Mathf.Max(0f, (((Rect)(ref val)).width - 4f) * num), ((Rect)(ref val)).height - 4f); if ((Object)(object)_uiWhiteTexture != (Object)null) { GUI.DrawTexture(val, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0f, 0f, 0f, 0.48f), 0f, 6f); GUI.DrawTexture(val2, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.22f, 0.75f, 1f, 0.86f), 0f, 5f); GUI.DrawTexture(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width, 1f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.95f, 0.76f, 0.33f, 0.42f), 0f, 0f); } DrawShadowLabel(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y - 1f, ((Rect)(ref val)).width, ((Rect)(ref val)).height + 2f), explorationMapProgressPercent.ToString("0.0", CultureInfo.InvariantCulture) + "%", GetHudTintedStyle(_mutedBodyStyle, new Color(0.95f, 0.95f, 0.88f, 1f), (TextAnchor)4)); } private float DrawRulesBlock(Rect startRect, string title, string categoryKey, bool enabled, string rules, string description, string formatHint, bool jackpotFormat) { //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_03d9: Unknown result type (might be due to invalid IL or missing references) //IL_03a0: Unknown result type (might be due to invalid IL or missing references) float x = ((Rect)(ref startRect)).x; float y = ((Rect)(ref startRect)).y; float width = ((Rect)(ref startRect)).width; bool flag = IsRuleCategoryExpanded(categoryKey); Rect val = default(Rect); ((Rect)(ref val))..ctor(x, y, width, 34f); if ((Object)(object)_uiWhiteTexture != (Object)null) { Color val2 = (flag ? new Color(0.17f, 0.11f, 0.035f, 0.55f) : new Color(0.045f, 0.032f, 0.018f, 0.42f)); GUI.DrawTexture(val, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val2, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).yMax - 1f, ((Rect)(ref val)).width, 1f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.95f, 0.76f, 0.33f, flag ? 0.55f : 0.24f), 0f, 0f); } Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = new Color(0.27f, 0.2f, 0.09f, flag ? 0.86f : 0.62f); if (GUI.Button(val, GUIContent.none)) { ToggleRuleCategory(categoryKey); GUI.FocusControl((string)null); } GUI.backgroundColor = backgroundColor; string text = (flag ? "▼" : "▶"); DrawShadowLabel(new Rect(x + 10f, y + 7f, 20f, 22f), text, _panelHeadingStyle); Texture2D ruleCategoryIcon = GetRuleCategoryIcon(categoryKey); float num = x + 36f; if ((Object)(object)ruleCategoryIcon != (Object)null) { Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(x + 31f, y + 5f, 24f, 24f); GUI.DrawTexture(val3, (Texture)(object)ruleCategoryIcon, (ScaleMode)2, true); num = ((Rect)(ref val3)).xMax + 8f; } DrawShadowLabel(new Rect(num, y + 7f, width - (num - x) - 12f, 22f), title, _panelHeadingStyle); y += 40f; if (!flag) { return y + 2f; } float num2 = (string.Equals(categoryKey, "Skills", StringComparison.OrdinalIgnoreCase) ? 52f : 28f); DrawBodyParagraph(new Rect(x + 8f, y, width - 16f, num2), description + " • " + formatHint); y += num2 + 8f; if (string.Equals(categoryKey, "Exploração", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Exploracao", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Exploration", StringComparison.OrdinalIgnoreCase)) { DrawExplorationMapProgressBar(new Rect(x + 8f, y, width - 16f, 46f)); y += 56f; } if (string.Equals(categoryKey, "Skills", StringComparison.OrdinalIgnoreCase)) { return DrawSkillGuideRows(x, y, width, rules); } if (ShouldGroupRulesByItemCategory(categoryKey)) { return DrawCategorizedGuideRows(x, y, width, categoryKey, rules, jackpotFormat); } List list = SplitRulesForHud(rules).ToList(); if (list.Count == 0) { list.Add("Nenhuma regra encontrada."); } for (int i = 0; i < list.Count; i++) { if (IsHudCategoryHeader(list[i])) { DrawGuideCategoryHeader(new Rect(x + 8f, y + 4f, width - 16f, 24f), GetHudCategoryHeaderTitle(list[i])); y += 30f; } else { DrawGuideRuleRow(new Rect(x + 8f, y, width - 16f, 24f), list[i], categoryKey, jackpotFormat, title.IndexOf("Penalidades", StringComparison.OrdinalIgnoreCase) >= 0); y += 26f; } } return y + 14f; } private int GetRuleProgressCount(string categoryKey, string prefab) { SnapshotPlayerData cachedPlayerData = _cachedPlayerData; if (cachedPlayerData == null || cachedPlayerData.ProgressCounters == null || string.IsNullOrWhiteSpace(prefab)) { return 0; } string text = SafeKey(prefab); string[] array; if (string.Equals(categoryKey, "Combate", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Combat", StringComparison.OrdinalIgnoreCase)) { array = new string[2] { "Kill:" + text, "Combat:" + text }; } else if (string.Equals(categoryKey, "Produção", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Producao", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Production", StringComparison.OrdinalIgnoreCase)) { array = new string[2] { "Craft:" + text, "Production:" + text }; } else if (string.Equals(categoryKey, "Conquistas", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Achievements", StringComparison.OrdinalIgnoreCase)) { array = new string[1] { "UniqueCraft:" + text }; } else if (string.Equals(categoryKey, "Bosses", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Chefes", StringComparison.OrdinalIgnoreCase)) { string text2 = NormalizeBossPrefabName(text); array = ((!string.Equals(text2, text, StringComparison.OrdinalIgnoreCase)) ? new string[2] { "Bosses:" + text, "Bosses:" + text2 } : new string[1] { "Bosses:" + text }); } else { array = ((!string.Equals(categoryKey, "Pesca", StringComparison.OrdinalIgnoreCase) && !string.Equals(categoryKey, "Fishing", StringComparison.OrdinalIgnoreCase)) ? ((!string.Equals(categoryKey, "Quests", StringComparison.OrdinalIgnoreCase) && !string.Equals(categoryKey, "Quest", StringComparison.OrdinalIgnoreCase) && !string.Equals(categoryKey, "Marketplace", StringComparison.OrdinalIgnoreCase)) ? (string.Equals(categoryKey, "Cultivo", StringComparison.OrdinalIgnoreCase) ? new string[1] { "Farm:" + text } : ((!string.Equals(categoryKey, "Exploração", StringComparison.OrdinalIgnoreCase) && !string.Equals(categoryKey, "Exploracao", StringComparison.OrdinalIgnoreCase) && !string.Equals(categoryKey, "Exploration", StringComparison.OrdinalIgnoreCase)) ? new string[1] { SafeKey(categoryKey) + ":" + text } : new string[3] { "ExplorationMap:Percent", "Exploration:Map", "Exploracao:Mapa" })) : new string[4] { "MarketplaceQuest:" + text, "Marketplace:" + text, "Quest:" + text, "Quests:" + text }) : new string[2] { "Fishing:" + text, "Pesca:" + text }); } for (int i = 0; i < array.Length; i++) { if (cachedPlayerData.ProgressCounters.TryGetValue(array[i], out var value)) { return Mathf.Clamp(value, 0, int.MaxValue); } } string[] array2; if (string.Equals(categoryKey, "Bosses", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Chefes", StringComparison.OrdinalIgnoreCase)) { string text3 = NormalizeBossPrefabName(text); array2 = ((!string.Equals(text3, text, StringComparison.OrdinalIgnoreCase)) ? new string[2] { ":" + text, ":" + text3 } : new string[1] { ":" + text }); } else { array2 = new string[1] { ":" + text }; } int num = 0; foreach (KeyValuePair progressCounter in cachedPlayerData.ProgressCounters) { if (string.IsNullOrWhiteSpace(progressCounter.Key)) { continue; } for (int j = 0; j < array2.Length; j++) { if (progressCounter.Key.EndsWith(array2[j], StringComparison.OrdinalIgnoreCase)) { num = Mathf.Clamp(num + Mathf.Max(0, progressCounter.Value), 0, int.MaxValue); break; } } } return num; } private float EstimateCategorizedGuideBlockHeight(string categoryKey, string rules) { List list = BuildRuleGuideGroups(categoryKey, rules, applySearchFilter: false); if (list.Count == 0) { return 118f; } float num = 76f; for (int i = 0; i < list.Count; i++) { RuleGuideGroup ruleGuideGroup = list[i]; num += 32f; if (IsRuleSubCategoryExpanded(categoryKey, ruleGuideGroup.Name)) { num += (float)Mathf.Max(1, ruleGuideGroup.Rows.Count) * 26f + 6f; } } return num + 14f; } private float DrawCategorizedGuideRows(float x, float y, float width, string categoryKey, string rules, bool jackpotFormat) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_0321: Unknown result type (might be due to invalid IL or missing references) List list = BuildRuleGuideGroups(categoryKey, rules, applySearchFilter: false); if (list.Count == 0) { DrawGuideRuleRow(new Rect(x + 8f, y, width - 16f, 24f), "Nenhuma regra configurada.", categoryKey, jackpotFormat, penaltyFormat: false); return y + 40f; } Rect val = default(Rect); for (int i = 0; i < list.Count; i++) { RuleGuideGroup ruleGuideGroup = list[i]; bool flag = IsRuleSubCategoryExpanded(categoryKey, ruleGuideGroup.Name); ((Rect)(ref val))..ctor(x + 8f, y, width - 16f, 28f); if ((Object)(object)_uiWhiteTexture != (Object)null) { Color val2 = (flag ? new Color(0.16f, 0.1f, 0.035f, 0.62f) : new Color(0.05f, 0.035f, 0.015f, 0.48f)); GUI.DrawTexture(val, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val2, 0f, 8f); GUI.DrawTexture(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).yMax - 1f, ((Rect)(ref val)).width, 1f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.95f, 0.76f, 0.33f, flag ? 0.42f : 0.18f), 0f, 0f); } if (GUI.Button(val, GUIContent.none, GUIStyle.none)) { ToggleRuleSubCategory(categoryKey, ruleGuideGroup.Name); GUI.FocusControl((string)null); } string text = (flag ? "▼" : "▶"); int ruleGroupProgressCount = GetRuleGroupProgressCount(categoryKey, ruleGuideGroup); string text2 = ((ruleGuideGroup.Rows.Count == 1) ? "1 item" : (ruleGuideGroup.Rows.Count + " itens")) + ((ruleGroupProgressCount > 0) ? (" • " + ruleGroupProgressCount + "x") : ""); DrawShadowLabel(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 4f, 22f, 20f), text, _panelHeadingStyle); DrawShadowLabel(new Rect(((Rect)(ref val)).x + 34f, ((Rect)(ref val)).y + 5f, ((Rect)(ref val)).width - 160f, 18f), ruleGuideGroup.Name.ToUpperInvariant(), GetHudTintedStyle(_configStyle, new Color(1f, 0.78f, 0.28f, 1f), (TextAnchor)3)); DrawShadowLabel(new Rect(((Rect)(ref val)).xMax - 118f, ((Rect)(ref val)).y + 5f, 110f, 18f), text2, GetRuleProgressStyle(categoryKey, penaltyFormat: false)); y += 32f; if (flag) { for (int j = 0; j < ruleGuideGroup.Rows.Count; j++) { DrawGuideRuleRow(new Rect(x + 22f, y, width - 30f, 24f), ruleGuideGroup.Rows[j], categoryKey, jackpotFormat, penaltyFormat: false); y += 26f; } y += 6f; } } return y + 14f; } private bool ShouldGroupRulesByItemCategory(string categoryKey) { return string.Equals(categoryKey, "Produção", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Producao", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Production", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Conquistas", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Achievements", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Combate", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Combat", StringComparison.OrdinalIgnoreCase); } private bool IsRuleSubCategoryExpanded(string categoryKey, string subCategory) { if (string.IsNullOrWhiteSpace(categoryKey) || string.IsNullOrWhiteSpace(subCategory)) { return false; } return _expandedRuleSubCategories.Contains(BuildRuleSubCategoryKey(categoryKey, subCategory)); } private void ToggleRuleSubCategory(string categoryKey, string subCategory) { if (!string.IsNullOrWhiteSpace(categoryKey) && !string.IsNullOrWhiteSpace(subCategory)) { string item = BuildRuleSubCategoryKey(categoryKey, subCategory); if (_expandedRuleSubCategories.Contains(item)) { _expandedRuleSubCategories.Remove(item); } else { _expandedRuleSubCategories.Add(item); } } } private string BuildRuleSubCategoryKey(string categoryKey, string subCategory) { return (categoryKey ?? "").Trim() + "::" + (subCategory ?? "").Trim(); } private int GetRuleGroupProgressCount(string categoryKey, RuleGuideGroup group) { if (group == null || group.Rows == null) { return 0; } int num = 0; for (int i = 0; i < group.Rows.Count; i++) { string prefab = ExtractRulePrefabKey(group.Rows[i]); num = Mathf.Clamp(num + GetRuleProgressCount(categoryKey, prefab), 0, int.MaxValue); } return num; } private List BuildRuleGuideGroups(string categoryKey, string rules, bool applySearchFilter) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); List list = new List(); List list2 = SplitRulesForHud(rules); string text = string.Empty; bool flag = false; for (int i = 0; i < list2.Count; i++) { string text2 = list2[i] ?? ""; if (IsHudCategoryHeader(text2)) { text = GetHudCategoryHeaderTitle(text2); if (string.IsNullOrWhiteSpace(text)) { text = "Outros"; } flag = true; if (!dictionary.TryGetValue(text, out var value)) { value = (dictionary[text] = new RuleGuideGroup(text)); list.Add(value); } continue; } string prefab = ExtractRulePrefabKey(text2); string text3 = ((flag && !string.IsNullOrWhiteSpace(text)) ? text : GetRuleGuideGroupName(categoryKey, prefab)); if (string.IsNullOrWhiteSpace(text3)) { text3 = "Outros"; } if (!dictionary.TryGetValue(text3, out var value2)) { value2 = (dictionary[text3] = new RuleGuideGroup(text3)); list.Add(value2); } value2.Rows.Add(text2); } if (flag) { return list.Where((RuleGuideGroup g) => g.Rows != null && g.Rows.Count > 0).ToList(); } string[] order = GetRuleGuideGroupOrder(categoryKey); List list3 = (from g in dictionary.Values orderby (Array.IndexOf(order, g.Name) < 0) ? 999 : Array.IndexOf(order, g.Name), g.Name select g).ToList(); for (int j = 0; j < list3.Count; j++) { list3[j].Rows.Sort((string a, string b) => string.Compare(FriendlyRuleNameForHud(ExtractRulePrefabKey(a)), FriendlyRuleNameForHud(ExtractRulePrefabKey(b)), StringComparison.OrdinalIgnoreCase)); } return list3; } private string ExtractRulePrefabKey(string rawRule) { if (string.IsNullOrWhiteSpace(rawRule)) { return ""; } string[] array = rawRule.Split(new char[1] { ':' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return rawRule.Trim(); } return (array[0] ?? "").Trim(); } private string GetRuleGuideGroupName(string categoryKey, string prefab) { if (string.Equals(categoryKey, "Combate", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Combat", StringComparison.OrdinalIgnoreCase)) { return GetCreatureBiomeCategoryName(prefab); } return GetRuleItemCategoryName(prefab); } private string[] GetRuleGuideGroupOrder(string categoryKey) { if (string.Equals(categoryKey, "Combate", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Combat", StringComparison.OrdinalIgnoreCase)) { return new string[10] { "Prados", "Floresta Negra", "Pântano", "Montanha", "Planícies", "Mistlands", "Ashlands", "Oceano", "Criaturas Especiais", "Outros" }; } return new string[9] { "Armaduras", "Armas", "Escudos", "Munições", "Comidas", "Comidas e Consumíveis", "Ferramentas", "Materiais", "Outros" }; } private string GetCreatureBiomeCategoryName(string prefab) { string text = (prefab ?? "").Trim().ToLowerInvariant(); if (string.IsNullOrWhiteSpace(text)) { return "Outros"; } text = text.Replace(" ", "").Replace("-", "").Replace("_", ""); if (text.Contains("boar") || text.Contains("deer") || text.Contains("neck") || text.Contains("greyling")) { return "Prados"; } if (text.Contains("greydwarf") || text.Contains("troll") || text.Contains("skeleton") || text.Contains("ghost") || text.Contains("rancidremains")) { return "Floresta Negra"; } if (text.Contains("draugr") || text.Contains("blob") || text.Contains("oozer") || text.Contains("leech") || text.Contains("wraith") || text.Contains("surtling") || text.Contains("abomination")) { return "Pântano"; } if (text.Contains("wolf") || text.Contains("fenring") || text.Contains("hatchling") || text.Contains("stonegolem") || text.Contains("cultist") || text.Contains("ulv") || text.Contains("bat")) { return "Montanha"; } if (text.Contains("goblin") || text.Contains("fuling") || text.Contains("lox") || text.Contains("deathsquito") || text.Contains("tarblob") || text.Contains("growth")) { return "Planícies"; } if (text.Contains("seeker") || text.Contains("gjall") || text.Contains("tick") || text.Contains("dverger") || text.Contains("hare")) { return "Mistlands"; } if (text.Contains("charred") || text.Contains("morgen") || text.Contains("valkyrie") || text.Contains("volture") || text.Contains("asksvin") || text.Contains("bonemaw") || text.Contains("fader")) { return "Ashlands"; } if (text.Contains("serpent") || text.Contains("leviathan")) { return "Oceano"; } if (text.Contains("eikthyr") || text.Contains("gdking") || text.Contains("elder") || text.Contains("bonemass") || text.Contains("dragon") || text.Contains("moder") || text.Contains("goblinking") || text.Contains("yagluth") || text.Contains("queen")) { return "Criaturas Especiais"; } return "Outros"; } private string GetRuleItemCategoryName(string prefab) { string text = (prefab ?? "").Trim().ToLowerInvariant(); if (string.IsNullOrWhiteSpace(text)) { return "Outros"; } if (text.Contains("helmet") || text.Contains("chest") || text.Contains("legs") || text.Contains("armor") || text.Contains("cape") || text.Contains("root") || text.Contains("fenris")) { return "Armaduras"; } if (text.Contains("shield") || text.Contains("buckler")) { return "Escudos"; } if (text.Contains("arrow") || text.Contains("bolt")) { return "Munições"; } if (text.Contains("bow") || text.Contains("crossbow") || text.Contains("sword") || text.Contains("knife") || text.Contains("axe") || text.Contains("mace") || text.Contains("club") || text.Contains("spear") || text.Contains("atgeir") || text.Contains("staff") || text.Contains("bomb")) { return "Armas"; } if (text.Contains("pickaxe") || text.Contains("hammer") || text.Contains("hoe") || text.Contains("cultivator") || text.Contains("fishingrod") || text.Contains("torch") || text.Contains("tankard")) { return "Ferramentas"; } if (text.Contains("mead") || text.Contains("stew") || text.Contains("soup") || text.Contains("jerky") || text.Contains("honey") || text.Contains("chicken") || text.Contains("meat") || text.Contains("fish") || text.Contains("pie") || text.Contains("pudding") || text.Contains("porridge") || text.Contains("mushroom") || text.Contains("carrot") || text.Contains("turnip") || text.Contains("onion") || text.Contains("salad") || text.Contains("omelet") || text.Contains("seekeraspic") || text.Contains("bloodpudding") || text.Contains("sausage") || text.Contains("deerstew") || text.Contains("queensjam") || text.Contains("bukeperries")) { return "Comidas e Consumíveis"; } if (text.Contains("wood") || text.Contains("stone") || text.Contains("ore") || text.Contains("scrap") || text.Contains("bar") || text.Contains("iron") || text.Contains("copper") || text.Contains("tin") || text.Contains("bronze") || text.Contains("silver") || text.Contains("blackmetal") || text.Contains("flametal") || text.Contains("chitin") || text.Contains("thread") || text.Contains("linen") || text.Contains("leather") || text.Contains("hide") || text.Contains("pelt") || text.Contains("trophy") || text.Contains("resin") || text.Contains("coal") || text.Contains("crystal") || text.Contains("yagluth") || text.Contains("eitr") || text.Contains("sap")) { return "Materiais"; } return "Outros"; } private float EstimateSkillGuideBlockHeight(string rules) { if (!IsRuleCategoryExpanded("Skills")) { return 42f; } List list = BuildSkillGuideGroups(rules); if (list.Count == 0) { return 122f; } float num = 102f; for (int i = 0; i < list.Count; i++) { SkillGuideGroup skillGuideGroup = list[i]; num += 58f; if (IsGuideSkillRowExpanded(skillGuideGroup.Name)) { num += (float)Mathf.Max(1, skillGuideGroup.Milestones.Count) * 28f + 10f; } } return num + 14f; } private float DrawSkillGuideRows(float x, float y, float width, string rules) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02dc: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_0440: Unknown result type (might be due to invalid IL or missing references) //IL_0487: Unknown result type (might be due to invalid IL or missing references) //IL_03bf: Unknown result type (might be due to invalid IL or missing references) //IL_03a4: Unknown result type (might be due to invalid IL or missing references) //IL_03c4: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Unknown result type (might be due to invalid IL or missing references) List list = BuildSkillGuideGroups(rules); if (list.Count == 0) { DrawGuideRuleRow(new Rect(x + 8f, y, width - 16f, 24f), "Nenhuma regra configurada.", "Skills", jackpotFormat: false, penaltyFormat: false); return y + 40f; } Rect val = default(Rect); Rect val3 = default(Rect); for (int i = 0; i < list.Count; i++) { SkillGuideGroup skillGuideGroup = list[i]; bool flag = IsGuideSkillRowExpanded(skillGuideGroup.Name); ((Rect)(ref val))..ctor(x + 8f, y, width - 16f, 54f); if ((Object)(object)_uiWhiteTexture != (Object)null) { Color val2 = (flag ? new Color(0.13f, 0.09f, 0.035f, 0.58f) : new Color(0f, 0f, 0f, 0.24f)); GUI.DrawTexture(val, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val2, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, 4f, ((Rect)(ref val)).height), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, GetSkillGuideAccentColor(skillGuideGroup.Name), 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref val)).xMax - 116f, ((Rect)(ref val)).y + 15f, 84f, 24f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.05f, 0.035f, 0.015f, 0.62f), 0f, 12f); } if (GUI.Button(val, GUIContent.none, GUIStyle.none)) { ToggleGuideSkillRow(skillGuideGroup.Name); GUI.FocusControl((string)null); } string skillGuideDisplayName = GetSkillGuideDisplayName(skillGuideGroup.Name); string skillGuideDescription = GetSkillGuideDescription(skillGuideGroup.Name); string text = (flag ? "▼" : "▶"); string text2 = ((skillGuideGroup.Milestones.Count == 1) ? "1 marco" : (skillGuideGroup.Milestones.Count + " marcos")); float num = ((Rect)(ref val)).x + 14f; float num2 = 126f; float num3 = ((Rect)(ref val)).width - num2 - 24f; DrawShadowLabel(new Rect(num, ((Rect)(ref val)).y + 7f, num3, 20f), skillGuideDisplayName, _bodyRowStyle); DrawShadowLabel(new Rect(num, ((Rect)(ref val)).y + 30f, num3, 18f), skillGuideDescription, _configStyle); DrawShadowLabel(new Rect(((Rect)(ref val)).xMax - 112f, ((Rect)(ref val)).y + 18f, 78f, 18f), text2, _bodyValueStyle); DrawShadowLabel(new Rect(((Rect)(ref val)).xMax - 28f, ((Rect)(ref val)).y + 17f, 20f, 20f), text, _panelHeadingStyle); y += 58f; if (!flag) { continue; } for (int j = 0; j < skillGuideGroup.Milestones.Count; j++) { SkillGuideMilestone skillGuideMilestone = skillGuideGroup.Milestones[j]; ((Rect)(ref val3))..ctor(x + 24f, y, width - 40f, 26f); if ((Object)(object)_uiWhiteTexture != (Object)null) { Color val4 = ((j % 2 == 0) ? new Color(0f, 0f, 0f, 0.16f) : new Color(0.08f, 0.055f, 0.025f, 0.18f)); GUI.DrawTexture(new Rect(((Rect)(ref val3)).x, ((Rect)(ref val3)).y + 1f, ((Rect)(ref val3)).width, ((Rect)(ref val3)).height - 2f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, val4, 0f, 0f); } DrawShadowLabel(new Rect(((Rect)(ref val3)).x + 10f, ((Rect)(ref val3)).y + 3f, ((Rect)(ref val3)).width - 128f, 18f), "Alcance o nível " + skillGuideMilestone.Level, _bodyRowStyle); DrawShadowLabel(new Rect(((Rect)(ref val3)).xMax - 112f, ((Rect)(ref val3)).y + 3f, 104f, 18f), "+" + skillGuideMilestone.Points + " pontos", _bodyValueStyle); y += 28f; } y += 10f; } return y + 14f; } private string GetSkillGuideDisplayName(string skillName) { string text = (skillName ?? "").Trim(); return text.ToLowerInvariant() switch { "axes" => "Machados", "blocking" => "Bloqueio", "bloodmagic" => "Magia de Sangue", "bows" => "Arcos", "clubs" => "Porretes", "crossbows" => "Bestas", "elementalmagic" => "Magia Elemental", "farming" => "Cultivo", "jump" => "Salto", "knives" => "Facas", "pickaxes" => "Picaretas", "polearms" => "Armas de Haste", "run" => "Corrida", "spears" => "Lanças", "swords" => "Espadas", "swim" => "Natação", "unarmed" => "Combate Desarmado", "woodcutting" => "Lenhador", "fishing" => "Pesca", "sneak" => "Furtividade", "ride" => "Montaria", _ => FriendlyRuleNameForHud(text), }; } private string GetSkillGuideDescription(string skillName) { string text = (skillName ?? "").Trim(); return text.ToLowerInvariant() switch { "axes" => "Evolua usando machados.", "blocking" => "Ganhe resistência defendendo ataques.", "bloodmagic" => "Aprimore poderes de sangue.", "bows" => "Treine ataques à distância.", "clubs" => "Esmague inimigos com armas contundentes.", "crossbows" => "Use bestas com precisão.", "elementalmagic" => "Domine fogo, gelo e raios.", "farming" => "Plante, colha e prospere.", "jump" => "Explore o mundo superando obstáculos.", "knives" => "Ataque rápido e de perto.", "pickaxes" => "Extraia minérios e recursos.", "polearms" => "Controle distância com armas longas.", "run" => "Corra, viaje e sobreviva.", "spears" => "Lance ou ataque mantendo distância.", "swords" => "Equilibre ataque e defesa.", "swim" => "Atravesse águas com segurança.", "unarmed" => "Lute sem armas.", "woodcutting" => "Derrube árvores e colete madeira.", "fishing" => "Pesque para receber honra.", "sneak" => "Mova-se sem ser percebido.", "ride" => "Evolua cavalgando.", _ => "Abra para ver níveis e recompensas.", }; } private Color GetSkillGuideAccentColor(string skillName) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) int num = Math.Abs((skillName ?? "skill").ToLowerInvariant().GetHashCode()); float num2 = 0.32f + (float)(num & 0xFF) / 255f * 0.34f; float num3 = 0.24f + (float)((num >> 8) & 0xFF) / 255f * 0.28f; float num4 = 0.12f + (float)((num >> 16) & 0xFF) / 255f * 0.24f; return new Color(num2, num3, num4, 0.78f); } private bool IsGuideSkillRowExpanded(string skillName) { if (string.IsNullOrWhiteSpace(skillName)) { return false; } return _expandedGuideSkillRows.Contains(skillName); } private void ToggleGuideSkillRow(string skillName) { if (!string.IsNullOrWhiteSpace(skillName)) { if (_expandedGuideSkillRows.Contains(skillName)) { _expandedGuideSkillRows.Remove(skillName); } else { _expandedGuideSkillRows.Add(skillName); } } } private List BuildSkillGuideGroups(string rules) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); List list = SplitRulesForHud(rules); for (int i = 0; i < list.Count; i++) { string text = list[i] ?? ""; string[] array = text.Split(new char[1] { ':' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length >= 3) { string text2 = (array[0] ?? "").Trim(); string level = (array[1] ?? "").Trim(); string points = (array[2] ?? "").Trim(); if (string.IsNullOrWhiteSpace(text2)) { text2 = "Habilidade"; } if (!dictionary.TryGetValue(text2, out var value)) { value = (dictionary[text2] = new SkillGuideGroup(text2)); } value.Milestones.Add(new SkillGuideMilestone(level, points)); } } List list2 = dictionary.Values.OrderBy((SkillGuideGroup x) => GetSkillGuideDisplayName(x.Name)).ToList(); for (int j = 0; j < list2.Count; j++) { list2[j].Milestones = (from x in list2[j].Milestones orderby ParseIntSafe(x.Level), x.Level select x).ToList(); } return list2; } private int ParseIntSafe(string value) { if (int.TryParse(value, out var result)) { return result; } return int.MaxValue; } private void EnsureRuleCategoriesInitialized() { if (!_ruleCategoriesInitialized) { _expandedRuleCategories.Clear(); _ruleCategoriesInitialized = true; } } private bool IsRuleCategoryExpanded(string categoryKey) { if (string.IsNullOrWhiteSpace(categoryKey)) { return false; } EnsureRuleCategoriesInitialized(); return _expandedRuleCategories.Contains(categoryKey); } private void ToggleRuleCategory(string categoryKey) { if (!string.IsNullOrWhiteSpace(categoryKey)) { EnsureRuleCategoriesInitialized(); if (_expandedRuleCategories.Contains(categoryKey)) { _expandedRuleCategories.Remove(categoryKey); } else { _expandedRuleCategories.Add(categoryKey); } } } private void DrawGuideCategoryHeader(Rect rect, string title) { //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(title)) { title = "Outros"; } if ((Object)(object)_uiWhiteTexture != (Object)null) { GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 10f, ((Rect)(ref rect)).width, 1f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.95f, 0.76f, 0.33f, 0.36f), 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, Mathf.Min(210f, ((Rect)(ref rect)).width), ((Rect)(ref rect)).height), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.12f, 0.075f, 0.025f, 0.72f), 0f, 8f); } DrawShadowLabel(new Rect(((Rect)(ref rect)).x + 10f, ((Rect)(ref rect)).y + 3f, ((Rect)(ref rect)).width - 20f, 18f), title.ToUpperInvariant(), _configStyle); } private void DrawGuideRuleRow(Rect rect, string rawRule, string categoryKey, bool jackpotFormat, bool penaltyFormat) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) FormatGuideRule(rawRule, categoryKey, jackpotFormat, penaltyFormat, out var left, out var right); if ((Object)(object)_uiWhiteTexture != (Object)null) { GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 1f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height - 2f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0f, 0f, 0f, 0.12f), 0f, 0f); } int ruleProgressCount = GetRuleProgressCount(categoryKey, ExtractRulePrefabKey(rawRule)); string text = ((string.Equals(categoryKey, "Exploração", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Exploracao", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Exploration", StringComparison.OrdinalIgnoreCase)) ? (GetExplorationMapProgressPercent().ToString("0.0", CultureInfo.InvariantCulture) + "%") : ((ruleProgressCount > 0) ? (ruleProgressCount + "x") : "0x")); GUIStyle ruleNameStyle = GetRuleNameStyle(categoryKey, penaltyFormat); GUIStyle ruleProgressStyle = GetRuleProgressStyle(categoryKey, penaltyFormat); GUIStyle ruleValueStyle = GetRuleValueStyle(categoryKey, penaltyFormat); DrawShadowLabel(new Rect(((Rect)(ref rect)).x + 8f, ((Rect)(ref rect)).y + 3f, ((Rect)(ref rect)).width - 190f, 18f), left, ruleNameStyle); DrawShadowLabel(new Rect(((Rect)(ref rect)).xMax - 176f, ((Rect)(ref rect)).y + 3f, 48f, 18f), text, ruleProgressStyle); DrawShadowLabel(new Rect(((Rect)(ref rect)).xMax - 126f, ((Rect)(ref rect)).y + 3f, 118f, 18f), right, ruleValueStyle); } private GUIStyle GetRuleNameStyle(string categoryKey, bool penaltyFormat) { //IL_0214: Unknown result type (might be due to invalid IL or missing references) Color color = default(Color); ((Color)(ref color))..ctor(0.92f, 0.9f, 0.82f, 1f); if (penaltyFormat || string.Equals(categoryKey, "Mortes", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Deaths", StringComparison.OrdinalIgnoreCase)) { ((Color)(ref color))..ctor(1f, 0.42f, 0.36f, 1f); } else if (string.Equals(categoryKey, "Combate", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Combat", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Bosses", StringComparison.OrdinalIgnoreCase)) { ((Color)(ref color))..ctor(1f, 0.7f, 0.58f, 1f); } else if (string.Equals(categoryKey, "Produção", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Producao", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Production", StringComparison.OrdinalIgnoreCase)) { ((Color)(ref color))..ctor(0.78f, 0.9f, 1f, 1f); } else if (string.Equals(categoryKey, "Conquistas", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Achievements", StringComparison.OrdinalIgnoreCase)) { ((Color)(ref color))..ctor(1f, 0.86f, 0.38f, 1f); } else if (string.Equals(categoryKey, "Skills", StringComparison.OrdinalIgnoreCase)) { ((Color)(ref color))..ctor(0.82f, 0.68f, 1f, 1f); } else if (string.Equals(categoryKey, "Cultivo", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Farming", StringComparison.OrdinalIgnoreCase)) { ((Color)(ref color))..ctor(0.64f, 1f, 0.58f, 1f); } else if (string.Equals(categoryKey, "Exploração", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Exploracao", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Exploration", StringComparison.OrdinalIgnoreCase)) { ((Color)(ref color))..ctor(0.58f, 0.9f, 1f, 1f); } return GetHudTintedStyle(_bodyRowStyle, color, (TextAnchor)3); } private GUIStyle GetRuleValueStyle(string categoryKey, bool penaltyFormat) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) Color color = (penaltyFormat ? new Color(1f, 0.28f, 0.22f, 1f) : new Color(0.25f, 1f, 0.45f, 1f)); return GetHudTintedStyle(penaltyFormat ? _configStyle : _bodyValueStyle, color, (TextAnchor)5); } private GUIStyle GetRuleProgressStyle(string categoryKey, bool penaltyFormat) { //IL_01be: Unknown result type (might be due to invalid IL or missing references) Color color = default(Color); ((Color)(ref color))..ctor(0.45f, 0.78f, 1f, 1f); if (penaltyFormat || string.Equals(categoryKey, "Mortes", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Deaths", StringComparison.OrdinalIgnoreCase)) { ((Color)(ref color))..ctor(1f, 0.28f, 0.22f, 1f); } else if (string.Equals(categoryKey, "Combate", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Combat", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Bosses", StringComparison.OrdinalIgnoreCase)) { ((Color)(ref color))..ctor(1f, 0.28f, 0.22f, 1f); } else if (string.Equals(categoryKey, "Produção", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Producao", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Production", StringComparison.OrdinalIgnoreCase)) { ((Color)(ref color))..ctor(0.45f, 0.78f, 1f, 1f); } else if (string.Equals(categoryKey, "Conquistas", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Achievements", StringComparison.OrdinalIgnoreCase)) { ((Color)(ref color))..ctor(1f, 0.82f, 0.28f, 1f); } else if (string.Equals(categoryKey, "Skills", StringComparison.OrdinalIgnoreCase)) { ((Color)(ref color))..ctor(0.82f, 0.68f, 1f, 1f); } else if (string.Equals(categoryKey, "Cultivo", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Farming", StringComparison.OrdinalIgnoreCase)) { ((Color)(ref color))..ctor(0.42f, 1f, 0.48f, 1f); } return GetHudTintedStyle(_configStyle, color, (TextAnchor)5); } private GUIStyle GetHudTintedStyle(GUIStyle baseStyle, Color color, TextAnchor alignment) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) GUIStyle val = new GUIStyle(baseStyle ?? GUI.skin.label); val.alignment = alignment; val.normal.textColor = color; return val; } private void FormatGuideRule(string rawRule, string categoryKey, bool jackpotFormat, bool penaltyFormat, out string left, out string right) { left = (string.IsNullOrWhiteSpace(rawRule) ? "Regra" : rawRule.Trim()); right = ""; if (IsHudCategoryHeader(left)) { left = GetHudCategoryHeaderTitle(left); return; } string[] array = left.Split(new char[1] { ':' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length <= 1) { return; } if (penaltyFormat && array.Length >= 2) { left = array[0].Trim() + " morte(s)"; right = "-" + array[1].Trim() + " pts"; return; } if (string.Equals(categoryKey, "Skills", StringComparison.OrdinalIgnoreCase) && array.Length >= 3) { left = array[0].Trim() + " • ao alcançar nível " + array[1].Trim(); right = "+" + array[2].Trim() + " pts"; return; } bool flag = string.Equals(categoryKey, "Combate", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Combat", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Bosses", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Produção", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Conquistas", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Producao", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Production", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Achievements", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Cultivo", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Pesca", StringComparison.OrdinalIgnoreCase) || string.Equals(categoryKey, "Fishing", StringComparison.OrdinalIgnoreCase); string text = array[0].Trim(); if (flag) { text = FriendlyRuleNameForHud(text); } if (jackpotFormat && array.Length >= 3) { left = text + " • " + array[1].Trim() + "x"; right = "+" + array[2].Trim() + " pts"; } else { left = text; right = "+" + array[1].Trim() + " pts"; } } private List SplitRulesForHud(string rules) { List list = new List(); if (string.IsNullOrWhiteSpace(rules)) { return list; } string[] array = rules.Split(new char[2] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = (array[i] ?? "").Trim(); if (!string.IsNullOrWhiteSpace(text)) { list.Add(text); } } return list; } private void DrawPlayerSummaryBlock(Rect rect) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_uiWhiteTexture != (Object)null) { GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height - 4f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.03f, 0.02f, 0.01f, 0.05f), 0f, 0f); } string text = (string.IsNullOrWhiteSpace(_cachedPlayerData.PlayerName) ? GetLocalPlayerName() : _cachedPlayerData.PlayerName); DrawShadowLabel(new Rect(((Rect)(ref rect)).x + 2f, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).width - 120f, 26f), text.ToUpperInvariant(), _panelHeadingStyle); DrawShadowLabel(new Rect(((Rect)(ref rect)).x + 2f, ((Rect)(ref rect)).y + 30f, ((Rect)(ref rect)).width - 140f, 22f), ColorizeHighlight("Posição #" + Mathf.Max(1, _cachedPlayerData.Position)) + " • " + ColorizePoints(FormatPoints(_cachedPlayerData.Points)), _bodyValueStyle); Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(((Rect)(ref rect)).xMax - 102f, ((Rect)(ref rect)).y + 15f, 96f, 20f); DrawPill(rect2, GetRankTitle(_cachedPlayerData.Position), new Color(0.91f, 0.75f, 0.34f, 0.88f), new Color(0.18f, 0.11f, 0.03f, 0.88f)); } private void DrawSectionHeader(Rect rect, string title) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) DrawShadowLabel(rect, title, _panelHeadingStyle); } private void DrawBodyParagraph(Rect rect, string text) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) DrawShadowLabel(rect, text, _mutedBodyStyle); } private void DrawSimpleInfoRows(Rect rect, string[] rows) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) if (rows != null && rows.Length != 0) { float num = Mathf.Max(24f, ((Rect)(ref rect)).height / (float)Mathf.Max(1, rows.Length)); Rect rect2 = default(Rect); for (int i = 0; i < rows.Length; i++) { string text = rows[i] ?? ""; int num2 = text.IndexOf('|'); string label = ((num2 >= 0) ? text.Substring(0, num2) : text); string value = ((num2 >= 0) ? text.Substring(num2 + 1) : ""); ((Rect)(ref rect2))..ctor(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + num * (float)i, ((Rect)(ref rect)).width, num); DrawInfoRow(rect2, label, value); } } } private void DrawRewardClaimSection(Rect rect) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_0334: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_0321: Unknown result type (might be due to invalid IL or missing references) //IL_0379: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_uiWhiteTexture != (Object)null) { GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height - 4f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.03f, 0.02f, 0.01f, 0.06f), 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, 4f, ((Rect)(ref rect)).height), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.91f, 0.75f, 0.34f, 0.88f), 0f, 0f); } SnapshotPlayerData snapshotPlayerData = _cachedPlayerData ?? new SnapshotPlayerData(); string text = ((snapshotPlayerData.RewardRank >= 1 && snapshotPlayerData.RewardRank <= 3) ? ("Top " + snapshotPlayerData.RewardRank + " • " + (string.IsNullOrWhiteSpace(snapshotPlayerData.RewardLabel) ? "Recompensa" : snapshotPlayerData.RewardLabel)) : "Recompensa bloqueada"); float num = ((Rect)(ref rect)).x + 12f; float num2 = 144f; float num3 = ((Rect)(ref rect)).xMax - num2 - 12f; float num4 = Mathf.Max(120f, num3 - num - 12f); DrawShadowLabel(new Rect(num, ((Rect)(ref rect)).y + 8f, ((Rect)(ref rect)).width - 24f, 20f), text, _bodyValueStyle); string text2 = ((string.IsNullOrWhiteSpace(snapshotPlayerData.RewardPrefabName) || snapshotPlayerData.RewardAmount <= 0) ? "Item ainda não configurado." : ("Item: " + snapshotPlayerData.RewardPrefabName + " x" + snapshotPlayerData.RewardAmount)); string text3 = ((snapshotPlayerData.RewardRank >= 1 && snapshotPlayerData.RewardRank <= 3) ? ("Exigência: " + snapshotPlayerData.RewardMinPoints + " pontos") : "Somente Top 1, 2 e 3 têm resgate."); DrawShadowLabel(new Rect(num, ((Rect)(ref rect)).y + 34f, num4, 18f), text2, _mutedBodyStyle); DrawShadowLabel(new Rect(num, ((Rect)(ref rect)).y + 54f, num4, 18f), text3, _mutedBodyStyle); string text4 = (string.IsNullOrWhiteSpace(snapshotPlayerData.RewardBlockReason) ? "Recompensa indisponível." : snapshotPlayerData.RewardBlockReason); DrawShadowLabel(new Rect(num, ((Rect)(ref rect)).y + 76f, num4, 34f), text4, _configStyle); Rect val = default(Rect); ((Rect)(ref val))..ctor(num3, ((Rect)(ref rect)).y + 56f, num2, 30f); if (snapshotPlayerData.RewardCanClaim && !_rewardClaimRequestPending) { Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = new Color(0.22f, 0.72f, 0.28f, 1f); if (GUI.Button(val, "Resgatar")) { RequestRewardClaimFromServer(); } GUI.backgroundColor = backgroundColor; } else { GUI.enabled = false; GUI.Button(val, _rewardClaimRequestPending ? "Processando..." : "Resgatar"); GUI.enabled = true; } DrawPointsExchangeSection(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 122f, ((Rect)(ref rect)).width, 158f), snapshotPlayerData); } private string SanitizeNumericInput(string value, int maxDigits = 8) { if (string.IsNullOrEmpty(value)) { return ""; } StringBuilder stringBuilder = new StringBuilder(Mathf.Max(0, value.Length)); for (int i = 0; i < value.Length; i++) { if (stringBuilder.Length >= maxDigits) { break; } char c = value[i]; if (c >= '0' && c <= '9') { stringBuilder.Append(c); } } string text = stringBuilder.ToString().TrimStart(new char[1] { '0' }); return string.IsNullOrEmpty(text) ? "" : text; } private void DrawPointsExchangeSection(Rect rect, SnapshotPlayerData data) { //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0425: Unknown result type (might be due to invalid IL or missing references) //IL_045b: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_0317: Unknown result type (might be due to invalid IL or missing references) //IL_034c: Unknown result type (might be due to invalid IL or missing references) //IL_047d: Unknown result type (might be due to invalid IL or missing references) //IL_0519: Unknown result type (might be due to invalid IL or missing references) //IL_04c5: Unknown result type (might be due to invalid IL or missing references) //IL_04ca: Unknown result type (might be due to invalid IL or missing references) //IL_04e0: Unknown result type (might be due to invalid IL or missing references) //IL_04eb: Unknown result type (might be due to invalid IL or missing references) //IL_0506: Unknown result type (might be due to invalid IL or missing references) bool flag = _rules != null && _rules.PointsExchangeEnabled; int num = ((data != null) ? Mathf.Max(0, data.Points) : 0); bool flag2 = _rules != null && _rules.PointsExchangeUsePointsPerCoin; bool flag3 = _rules != null && _rules.PointsExchangeUseCoinsPerPoint; int num2 = ((_rules != null) ? Mathf.Max(1, _rules.PointsExchangePointsPerCoin) : 100); int num3 = ((_rules == null) ? 1 : Mathf.Max(1, _rules.PointsExchangeCoinsPerPoint)); int num4 = ((_rules != null) ? Mathf.Max(0, _rules.PointsExchangeMaxPointsPerRequest) : 0); string text = ((_rules != null && !string.IsNullOrWhiteSpace(_rules.PointsExchangePrefab)) ? _rules.PointsExchangePrefab : "Coins"); int num5 = ((num4 > 0) ? Mathf.Min(num, num4) : num); if (flag2) { num5 = num5 / num2 * num2; } if (string.IsNullOrWhiteSpace(_pointsExchangeAmountInput) && num5 > 0) { _pointsExchangeAmountInput = num5.ToString(); } int result = 0; int.TryParse((_pointsExchangeAmountInput ?? "").Trim(), out result); result = Mathf.Clamp(result, 0, Mathf.Max(0, num5)); int num6 = 0; int requestedPoints = result; if (flag2) { num6 = Mathf.Max(0, result / num2); requestedPoints = num6 * num2; } else if (flag3) { num6 = Mathf.Max(0, result * num3); } float num7 = ((Rect)(ref rect)).x + 12f; float num8 = 144f; float num9 = ((Rect)(ref rect)).xMax - num8 - 12f; float num10 = 8f; float num11 = 86f; float num12 = 86f; DrawShadowLabel(new Rect(num7, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width - 24f, 20f), "Câmbio de pontos", _bodyValueStyle); string text2 = (flag ? ("Disponível: " + num + " pontos") : "Câmbio de pontos desativado."); DrawShadowLabel(new Rect(num7, ((Rect)(ref rect)).y + 24f, ((Rect)(ref rect)).width - 24f, 18f), text2, _mutedBodyStyle); if (flag) { string text3 = (flag2 ? ("Taxa: " + num2 + " pontos = 1 " + text) : (flag3 ? ("Taxa: 1 ponto = " + num3 + " " + text) : "Nenhum modo de câmbio ativo.")); DrawShadowLabel(new Rect(num7, ((Rect)(ref rect)).y + 44f, ((Rect)(ref rect)).width - 24f, 18f), "Máximo por troca: " + num5 + " pontos", _mutedBodyStyle); DrawShadowLabel(new Rect(num7, ((Rect)(ref rect)).y + 64f, ((Rect)(ref rect)).width - 24f, 18f), text3, _mutedBodyStyle); DrawShadowLabel(new Rect(num7, ((Rect)(ref rect)).y + 82f, ((Rect)(ref rect)).width - 24f, 18f), "Gasta: " + requestedPoints + " pontos • Recebe: " + num6 + " " + text, _mutedBodyStyle); } Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(num7, ((Rect)(ref rect)).y + 104f, 52f, 18f); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref rect2)).xMax + 6f, ((Rect)(ref rect)).y + 98f, num11, 30f); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).xMax + num10, ((Rect)(ref rect)).y + 98f, num12, 30f); Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(num9, ((Rect)(ref rect)).y + 126f, num8, 30f); DrawShadowLabel(rect2, "Pontos:", _mutedBodyStyle); bool flag5 = (GUI.enabled = flag && !_pointsExchangeRequestPending && num > 0 && num5 > 0); _pointsExchangeAmountInput = SanitizeNumericInput(GUI.TextField(val, _pointsExchangeAmountInput ?? "", 8)); if (GUI.Button(val2, "Máximo")) { _pointsExchangeAmountInput = num5.ToString(); } GUI.enabled = true; if (flag5 && result > 0 && result <= num5 && num6 > 0) { Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = new Color(0.78f, 0.55f, 0.18f, 1f); if (GUI.Button(val3, "Trocar")) { RequestPointsExchangeFromServer(requestedPoints); } GUI.backgroundColor = backgroundColor; } else { GUI.enabled = false; GUI.Button(val3, _pointsExchangeRequestPending ? "Processando..." : "Trocar"); GUI.enabled = true; } } private void DrawConfigSection(Rect rect, SnapshotPlayerData data) { //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) string text = "Ranking " + ((data != null && data.RankingEnabled) ? "ON" : "OFF") + " • Kills " + ((data != null && data.EnableKillPoints) ? "ON" : "OFF") + " • Bosses " + ((data != null && data.EnableBossPoints) ? "ON" : "OFF"); string text2 = "Skills " + ((data != null && data.EnableSkillPoints) ? "ON" : "OFF") + " • Top exibido " + ((data != null) ? data.TopCount.ToString() : _rules.TopCount.ToString()); DrawShadowLabel(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, 20f), text, _configStyle); DrawShadowLabel(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 20f, ((Rect)(ref rect)).width, 20f), text2, _configStyle); } private void DrawInfoRow(Rect rect, string label, string value) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Clamp(((Rect)(ref rect)).width * 0.28f, 74f, 130f); Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).width - num - 12f, Mathf.Max(24f, ((Rect)(ref rect)).height - 4f)); Rect rect3 = default(Rect); ((Rect)(ref rect3))..ctor(((Rect)(ref rect)).xMax - num - 4f, ((Rect)(ref rect)).y + 2f, num, Mathf.Max(24f, ((Rect)(ref rect)).height - 4f)); DrawShadowLabel(rect2, label, _bodyRowStyle); DrawShadowLabel(rect3, ColorizePerformanceValue(label, value), _bodyValueStyle); } private string GetRankTitle(int position) { if (position <= 1) { return "LENDÁRIO"; } if (position <= 3) { return "GLORIOSO"; } if (position <= 10) { return "ASCENDENTE"; } return "GUERREIRO"; } private void DrawShadowLabel(Rect rect, string text, GUIStyle style) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) DrawShadowLabel(rect, text, style, new Vector2(1f, 1f), new Color(0f, 0f, 0f, 0.92f)); } private void DrawShadowLabel(Rect rect, string text, GUIStyle style, Vector2 shadowOffset, Color shadowColor) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (style != null) { GUIStyle val = new GUIStyle(style); val.normal.textColor = shadowColor; Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref rect)).x + shadowOffset.x, ((Rect)(ref rect)).y + shadowOffset.y, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height); GUI.Label(val2, text ?? "", val); GUI.Label(rect, text ?? "", style); } } private void DrawPill(Rect rect, string text, Color borderColor, Color fillColor) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_uiWhiteTexture == (Object)null)) { GUI.DrawTexture(rect, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, fillColor, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, 1.5f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, borderColor, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - 1.5f, ((Rect)(ref rect)).width, 1.5f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, borderColor, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, 1.5f, ((Rect)(ref rect)).height), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, borderColor, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMax - 1.5f, ((Rect)(ref rect)).y, 1.5f, ((Rect)(ref rect)).height), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, borderColor, 0f, 0f); DrawShadowLabel(rect, text, _youTagStyle); } } private void DrawInnerCard(Rect rect, Color borderColor, float fillAlpha) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_uiWhiteTexture == (Object)null)) { GUI.DrawTexture(rect, (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.02f, 0.015f, 0.01f, 0.68f + fillAlpha), 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 6f, ((Rect)(ref rect)).y + 6f, ((Rect)(ref rect)).width - 12f, ((Rect)(ref rect)).height - 12f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, new Color(0.07f, 0.045f, 0.02f, 0.52f), 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, 1.5f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, borderColor, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - 1.5f, ((Rect)(ref rect)).width, 1.5f), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, borderColor, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, 1.5f, ((Rect)(ref rect)).height), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, borderColor, 0f, 0f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMax - 1.5f, ((Rect)(ref rect)).y, 1.5f, ((Rect)(ref rect)).height), (Texture)(object)_uiWhiteTexture, (ScaleMode)0, true, 0f, borderColor, 0f, 0f); } } private Color GetRankAccentColor(int position) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) return (Color)(position switch { 1 => new Color(0.95f, 0.77f, 0.26f, 0.95f), 2 => new Color(0.82f, 0.82f, 0.78f, 0.9f), 3 => new Color(0.8f, 0.58f, 0.31f, 0.9f), _ => new Color(0.79f, 0.63f, 0.32f, 0.72f), }); } private Font TryCreateFont(string preferredNames, int size) { if (string.IsNullOrWhiteSpace(preferredNames)) { return null; } try { string[] array = (from x in preferredNames.Split(new char[3] { '|', ',', ';' }, StringSplitOptions.RemoveEmptyEntries) select x.Trim() into x where !string.IsNullOrWhiteSpace(x) select x).Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); if (array.Length == 0) { return null; } return Font.CreateDynamicFontFromOSFont(array, Mathf.Max(10, size)); } catch { return null; } } private void ApplyFont(GUIStyle style, Font font) { if (style != null && (Object)(object)font != (Object)null) { style.font = font; } } private void EnsureGuiStyles() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Expected O, but got Unknown //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Expected O, but got Unknown //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Expected O, but got Unknown //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Expected O, but got Unknown //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Expected O, but got Unknown //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Expected O, but got Unknown //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Expected O, but got Unknown //IL_02dc: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Expected O, but got Unknown //IL_032d: Unknown result type (might be due to invalid IL or missing references) //IL_0379: Unknown result type (might be due to invalid IL or missing references) //IL_0383: Expected O, but got Unknown //IL_03ca: Unknown result type (might be due to invalid IL or missing references) //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_0413: Expected O, but got Unknown //IL_045a: Unknown result type (might be due to invalid IL or missing references) //IL_0483: Unknown result type (might be due to invalid IL or missing references) //IL_048d: Expected O, but got Unknown //IL_04c7: Unknown result type (might be due to invalid IL or missing references) //IL_0513: Unknown result type (might be due to invalid IL or missing references) //IL_051d: Expected O, but got Unknown //IL_0564: Unknown result type (might be due to invalid IL or missing references) //IL_05c6: Unknown result type (might be due to invalid IL or missing references) //IL_05d0: Expected O, but got Unknown //IL_060a: Unknown result type (might be due to invalid IL or missing references) //IL_064d: Unknown result type (might be due to invalid IL or missing references) //IL_0657: Expected O, but got Unknown //IL_069e: Unknown result type (might be due to invalid IL or missing references) //IL_06c7: Unknown result type (might be due to invalid IL or missing references) //IL_06d1: Expected O, but got Unknown //IL_0725: Unknown result type (might be due to invalid IL or missing references) //IL_0771: Unknown result type (might be due to invalid IL or missing references) //IL_077b: Expected O, but got Unknown //IL_07b5: Unknown result type (might be due to invalid IL or missing references) //IL_07eb: Unknown result type (might be due to invalid IL or missing references) //IL_07f5: Expected O, but got Unknown //IL_083c: Unknown result type (might be due to invalid IL or missing references) //IL_087b: Unknown result type (might be due to invalid IL or missing references) //IL_0885: Expected O, but got Unknown //IL_08cc: Unknown result type (might be due to invalid IL or missing references) //IL_08f5: Unknown result type (might be due to invalid IL or missing references) //IL_08ff: Expected O, but got Unknown //IL_0939: Unknown result type (might be due to invalid IL or missing references) //IL_097c: Unknown result type (might be due to invalid IL or missing references) //IL_0986: Expected O, but got Unknown //IL_09cd: Unknown result type (might be due to invalid IL or missing references) //IL_09f6: Unknown result type (might be due to invalid IL or missing references) //IL_0a00: Expected O, but got Unknown //IL_0a47: Unknown result type (might be due to invalid IL or missing references) //IL_0a70: Unknown result type (might be due to invalid IL or missing references) //IL_0a7a: Expected O, but got Unknown //IL_0ae0: Unknown result type (might be due to invalid IL or missing references) //IL_0aea: Expected O, but got Unknown //IL_0af5: Unknown result type (might be due to invalid IL or missing references) //IL_0aff: Expected O, but got Unknown //IL_0b0a: Unknown result type (might be due to invalid IL or missing references) //IL_0b14: Expected O, but got Unknown if (!_stylesReady) { if ((Object)(object)_uiTransparentTexture == (Object)null) { _uiTransparentTexture = CreateSolidTexture(new Color(0f, 0f, 0f, 0f)); } _uiTitleFont = (_uiUseSystemFonts ? TryCreateFont(_uiTitleFontNames, 26) : null); _uiBodyFont = (_uiUseSystemFonts ? TryCreateFont(_uiBodyFontNames, 15) : null); _uiAccentFont = (_uiUseSystemFonts ? TryCreateFont(_uiAccentFontNames, 16) : null); _windowStyle = new GUIStyle(GUI.skin.window); _windowStyle.normal.background = _uiTransparentTexture; _windowStyle.active.background = _uiTransparentTexture; _windowStyle.hover.background = _uiTransparentTexture; _windowStyle.focused.background = _uiTransparentTexture; _windowStyle.border = new RectOffset(0, 0, 0, 0); _windowStyle.padding = new RectOffset(0, 0, 0, 0); _headerStyle = new GUIStyle(GUI.skin.label); _headerStyle.fontSize = 26; _headerStyle.fontStyle = (FontStyle)1; _headerStyle.alignment = (TextAnchor)4; _headerStyle.wordWrap = true; _headerStyle.richText = true; _headerStyle.normal.textColor = new Color(0.98f, 0.92f, 0.78f, 1f); ApplyFont(_headerStyle, _uiTitleFont); _sectionTitleStyle = new GUIStyle(_headerStyle); _sectionTitleStyle.fontSize = 21; ApplyFont(_sectionTitleStyle, _uiTitleFont); _rankingTextStyle = new GUIStyle(GUI.skin.label); _rankingTextStyle.fontSize = 15; _rankingTextStyle.wordWrap = true; _rankingTextStyle.richText = true; _rankingTextStyle.alignment = (TextAnchor)0; _rankingTextStyle.normal.textColor = new Color(0.97f, 0.92f, 0.82f, 1f); _rankingTextStyle.padding = new RectOffset(2, 2, 0, 0); ApplyFont(_rankingTextStyle, _uiBodyFont); _playerTextStyle = new GUIStyle(_rankingTextStyle); _playerTextStyle.fontSize = 14; ApplyFont(_playerTextStyle, _uiBodyFont); _panelHeadingStyle = new GUIStyle(GUI.skin.label); _panelHeadingStyle.fontSize = 15; _panelHeadingStyle.fontStyle = (FontStyle)1; _panelHeadingStyle.alignment = (TextAnchor)3; _panelHeadingStyle.normal.textColor = new Color(0.96f, 0.82f, 0.47f, 1f); _panelHeadingStyle.richText = true; ApplyFont(_panelHeadingStyle, ((Object)(object)_uiAccentFont != (Object)null) ? _uiAccentFont : _uiTitleFont); _cardLabelStyle = new GUIStyle(GUI.skin.label); _cardLabelStyle.fontSize = 11; _cardLabelStyle.alignment = (TextAnchor)0; _cardLabelStyle.fontStyle = (FontStyle)1; _cardLabelStyle.normal.textColor = new Color(0.86f, 0.73f, 0.48f, 0.95f); ApplyFont(_cardLabelStyle, ((Object)(object)_uiAccentFont != (Object)null) ? _uiAccentFont : _uiTitleFont); _cardValueStyle = new GUIStyle(GUI.skin.label); _cardValueStyle.fontSize = 24; _cardValueStyle.alignment = (TextAnchor)0; _cardValueStyle.fontStyle = (FontStyle)1; _cardValueStyle.normal.textColor = new Color(0.98f, 0.93f, 0.84f, 1f); ApplyFont(_cardValueStyle, _uiTitleFont); _bodyRowStyle = new GUIStyle(GUI.skin.label); _bodyRowStyle.fontSize = 13; _bodyRowStyle.alignment = (TextAnchor)3; _bodyRowStyle.normal.textColor = new Color(0.96f, 0.9f, 0.78f, 1f); _bodyRowStyle.clipping = (TextClipping)0; _bodyRowStyle.wordWrap = false; _bodyRowStyle.richText = true; ApplyFont(_bodyRowStyle, _uiBodyFont); _bodyValueStyle = new GUIStyle(_bodyRowStyle); _bodyValueStyle.fontSize = 13; _bodyValueStyle.fontStyle = (FontStyle)1; _bodyValueStyle.alignment = (TextAnchor)5; _bodyValueStyle.normal.textColor = new Color(0.98f, 0.92f, 0.8f, 1f); _bodyValueStyle.clipping = (TextClipping)0; _bodyValueStyle.wordWrap = false; _bodyValueStyle.richText = true; ApplyFont(_bodyValueStyle, ((Object)(object)_uiAccentFont != (Object)null) ? _uiAccentFont : _uiBodyFont); _mutedBodyStyle = new GUIStyle(_bodyRowStyle); _mutedBodyStyle.fontSize = 13; _mutedBodyStyle.wordWrap = true; _mutedBodyStyle.normal.textColor = new Color(0.86f, 0.79f, 0.67f, 0.95f); _mutedBodyStyle.clipping = (TextClipping)0; _mutedBodyStyle.richText = true; ApplyFont(_mutedBodyStyle, _uiBodyFont); _rankIndexStyle = new GUIStyle(GUI.skin.label); _rankIndexStyle.fontSize = 18; _rankIndexStyle.fontStyle = (FontStyle)1; _rankIndexStyle.alignment = (TextAnchor)3; _rankIndexStyle.normal.textColor = new Color(0.97f, 0.82f, 0.33f, 1f); ApplyFont(_rankIndexStyle, _uiTitleFont); _rankNameStyle = new GUIStyle(GUI.skin.label); _rankNameStyle.fontSize = 16; _rankNameStyle.fontStyle = (FontStyle)1; _rankNameStyle.alignment = (TextAnchor)3; _rankNameStyle.clipping = (TextClipping)1; _rankNameStyle.normal.textColor = new Color(0.95f, 0.89f, 0.77f, 1f); _rankNameStyle.richText = true; ApplyFont(_rankNameStyle, ((Object)(object)_uiAccentFont != (Object)null) ? _uiAccentFont : _uiTitleFont); _rankPointsStyle = new GUIStyle(GUI.skin.label); _rankPointsStyle.fontSize = 12; _rankPointsStyle.alignment = (TextAnchor)3; _rankPointsStyle.normal.textColor = new Color(0.88f, 0.78f, 0.6f, 0.95f); _rankPointsStyle.richText = true; ApplyFont(_rankPointsStyle, _uiBodyFont); _youTagStyle = new GUIStyle(GUI.skin.label); _youTagStyle.fontSize = 10; _youTagStyle.alignment = (TextAnchor)4; _youTagStyle.fontStyle = (FontStyle)1; _youTagStyle.normal.textColor = new Color(0.86f, 0.97f, 0.82f, 1f); ApplyFont(_youTagStyle, ((Object)(object)_uiAccentFont != (Object)null) ? _uiAccentFont : _uiBodyFont); _emptyStateStyle = new GUIStyle(GUI.skin.label); _emptyStateStyle.fontSize = 16; _emptyStateStyle.wordWrap = true; _emptyStateStyle.alignment = (TextAnchor)0; _emptyStateStyle.normal.textColor = new Color(0.95f, 0.89f, 0.78f, 0.95f); ApplyFont(_emptyStateStyle, _uiBodyFont); _configStyle = new GUIStyle(GUI.skin.label); _configStyle.fontSize = 12; _configStyle.alignment = (TextAnchor)0; _configStyle.normal.textColor = new Color(0.88f, 0.82f, 0.73f, 0.94f); _configStyle.clipping = (TextClipping)0; _configStyle.richText = true; ApplyFont(_configStyle, _uiBodyFont); _footerStyle = new GUIStyle(GUI.skin.label); _footerStyle.fontSize = 13; _footerStyle.alignment = (TextAnchor)4; _footerStyle.richText = true; _footerStyle.normal.textColor = new Color(0.95f, 0.89f, 0.76f, 0.95f); ApplyFont(_footerStyle, _uiBodyFont); _statusStyle = new GUIStyle(GUI.skin.label); _statusStyle.fontSize = 13; _statusStyle.fontStyle = (FontStyle)1; _statusStyle.alignment = (TextAnchor)4; _statusStyle.normal.textColor = new Color(0.95f, 0.89f, 0.76f, 1f); ApplyFont(_statusStyle, _uiBodyFont); _iconButtonStyle = new GUIStyle(GUI.skin.button); _iconButtonStyle.normal.background = _uiTransparentTexture; _iconButtonStyle.active.background = _uiTransparentTexture; _iconButtonStyle.hover.background = _uiTransparentTexture; _iconButtonStyle.focused.background = _uiTransparentTexture; _iconButtonStyle.border = new RectOffset(0, 0, 0, 0); _iconButtonStyle.padding = new RectOffset(0, 0, 0, 0); _iconButtonStyle.margin = new RectOffset(0, 0, 0, 0); _stylesReady = true; } } private int GetRankingPosition(RankingEntry target) { if (target == null || _database == null || _database.Entries == null) { return int.MaxValue; } IReadOnlyList orderedRankingEntries = GetOrderedRankingEntries(); for (int i = 0; i < orderedRankingEntries.Count; i++) { RankingEntry rankingEntry = orderedRankingEntries[i]; if (rankingEntry == target) { return i + 1; } if (rankingEntry != null && string.Equals(rankingEntry.PlayerName, target.PlayerName, StringComparison.OrdinalIgnoreCase)) { return i + 1; } } return int.MaxValue; } private RankingEntry FindRankingEntryByName(string playerName) { if (string.IsNullOrWhiteSpace(playerName) || _database == null || _database.Entries == null) { return null; } string key = SanitizePlayerName(playerName); if (_entriesByPlayerName.TryGetValue(key, out var value)) { return value; } RebuildDatabaseIndexes(); return _entriesByPlayerName.TryGetValue(key, out value) ? value : null; } private string GetHudTopAccent(int position) { return position switch { 1 => "#FFD700", 2 => "#D7DCE5", 3 => "#D79A63", _ => "#E8C988", }; } private string BuildHudTopLine(int position, RankingEntry entry, string localPlayerName) { string hudTopAccent = GetHudTopAccent(position); string text = ((entry != null) ? entry.PlayerName : "Jogador"); int num = entry?.Points ?? 0; return "#" + position + " " + text + "\n" + num + " pts"; } private void SetStatus(string text, float seconds = 2.5f) { _statusText = (string.IsNullOrWhiteSpace(text) ? "" : text); _statusUntil = Time.unscaledTime + Mathf.Max(0.1f, seconds); } private void RequestRewardClaimFromServer() { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Expected O, but got Unknown try { if (_cachedPlayerData == null || _rewardClaimRequestPending) { return; } if (!_cachedPlayerData.RewardCanClaim) { SetStatus(string.IsNullOrWhiteSpace(_cachedPlayerData.RewardBlockReason) ? "Recompensa indisponível." : _cachedPlayerData.RewardBlockReason, 3f); } else if (_rpcsRegistered && ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null)) { long serverPeerUid = GetServerPeerUid(); if (serverPeerUid != 0) { ZPackage val = new ZPackage(); val.Write(_cachedPlayerData.RewardClaimCycleId ?? ""); val.Write(Mathf.Clamp(_cachedPlayerData.RewardRank, 0, int.MaxValue)); ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerUid, "glitnir.ranking.requestrewardclaim", new object[1] { val }); _rewardClaimRequestPending = true; _rewardClaimPendingCycleId = _cachedPlayerData.RewardClaimCycleId ?? ""; _rewardClaimPendingRank = Mathf.Clamp(_cachedPlayerData.RewardRank, 0, int.MaxValue); SetStatus("Solicitando resgate da recompensa...", 2f); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao solicitar resgate da recompensa: " + ex)); } } private void RequestPointsExchangeFromServer(int requestedPoints) { //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Expected O, but got Unknown try { if (_pointsExchangeRequestPending) { return; } if (_cachedPlayerData == null || _cachedPlayerData.Points <= 0) { SetStatus("Você não possui pontos para trocar.", 3f); return; } if (_rules == null || !_rules.PointsExchangeEnabled) { SetStatus("Câmbio de pontos desativado.", 3f); return; } int num = Mathf.Max(0, _cachedPlayerData.Points); int num2 = Mathf.Max(0, _rules.PointsExchangeMaxPointsPerRequest); int num3 = ((num2 > 0) ? Mathf.Min(num, num2) : num); if (requestedPoints <= 0) { SetStatus("Informe uma quantidade válida para trocar.", 3f); } else if (requestedPoints > num3) { SetStatus("Quantidade acima do limite disponível para câmbio.", 3f); } else if (_rpcsRegistered && ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null)) { long serverPeerUid = GetServerPeerUid(); if (serverPeerUid != 0) { ZPackage val = new ZPackage(); val.Write(SafeLimit(_rules.RewardClaimCycleId, 64)); val.Write(Mathf.Clamp(requestedPoints, 1, int.MaxValue)); ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerUid, "glitnir.ranking.requestpointsexchange", new object[1] { val }); _pointsExchangeRequestPending = true; SetStatus("Solicitando câmbio de " + requestedPoints + " pontos por moedas...", 2f); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao solicitar câmbio de pontos: " + ex)); } } private void RPC_RequestRewardClaim(long sender, ZPackage pkg) { //IL_0392: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Expected O, but got Unknown try { if (!IsServerInstance() || pkg == null) { return; } string text = SafeLimit(pkg.ReadString(), 64); int num = Mathf.Clamp(pkg.ReadInt(), 0, int.MaxValue); string text2 = ResolvePlayerNameFromSender(sender); if (string.IsNullOrWhiteSpace(text2)) { SendRewardClaimFeedback(sender, success: false, "Não foi possível identificar o jogador."); return; } if (_rules == null || !_rules.RewardClaimsEnabled) { SendRewardClaimFeedback(sender, success: false, "Sistema de recompensas desativado."); return; } if (!string.Equals(SafeLimit(_rules.RewardClaimCycleId, 64), text, StringComparison.OrdinalIgnoreCase)) { SendRewardClaimFeedback(sender, success: false, "Ciclo de recompensa inválido. Abra o ranking novamente."); return; } List list = (from x in _database.Entries where x != null && !string.IsNullOrWhiteSpace(x.PlayerName) && !ShouldIgnorePlayerForRanking(x.PlayerName) orderby x.Points descending, x.BossPointsTotal descending, x.KillPointsTotal descending, x.SkillPointsTotal descending, x.PlayerName select x).ToList(); RankingEntry rankingEntry = null; int num2 = -1; for (int i = 0; i < list.Count; i++) { if (list[i] != null && string.Equals(list[i].PlayerName, text2, StringComparison.OrdinalIgnoreCase)) { rankingEntry = list[i]; num2 = i + 1; break; } } if (rankingEntry == null || num2 <= 0) { SendRewardClaimFeedback(sender, success: false, "Você não está no ranking."); return; } if (num2 != num) { SendRewardClaimFeedback(sender, success: false, "Sua posição mudou. Abra o ranking novamente."); return; } RankRewardInfo rankRewardInfo = GetRankRewardInfo(num2); if (rankRewardInfo == null) { SendRewardClaimFeedback(sender, success: false, "Somente Top 1, 2 e 3 podem resgatar."); return; } if (rankingEntry.Points < rankRewardInfo.MinPoints) { SendRewardClaimFeedback(sender, success: false, "Você precisa de " + rankRewardInfo.MinPoints + " pontos para resgatar."); return; } if (string.IsNullOrWhiteSpace(rankRewardInfo.PrefabName) || rankRewardInfo.Amount <= 0) { SendRewardClaimFeedback(sender, success: false, "Recompensa não configurada."); return; } if (HasClaimedReward(text, text2, num2)) { SendRewardClaimFeedback(sender, success: false, "Essa recompensa já foi resgatada."); return; } if (HasPendingRewardClaim(text, text2, num2)) { SendRewardClaimFeedback(sender, success: false, "Essa recompensa já está em processamento."); return; } string item = BuildPendingRewardKey(sender, text, num2); if (_pendingRewardClaims.Contains(item)) { SendRewardClaimFeedback(sender, success: false, "Já existe um resgate em andamento."); return; } ReserveRewardClaim(text, text2, num2); SaveDatabase(); _pendingRewardClaims.Add(item); ZPackage val = new ZPackage(); val.Write(text); val.Write(num2); val.Write(SafeLimit(text2, 32)); val.Write(rankRewardInfo.PrefabName ?? ""); val.Write(Mathf.Max(1, rankRewardInfo.Amount)); val.Write(rankRewardInfo.Label ?? ""); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "glitnir.ranking.grantrewarditem", new object[1] { val }); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_RequestRewardClaim: " + ex)); SendRewardClaimFeedback(sender, success: false, "Erro interno ao validar recompensa."); } } private void RPC_GrantRewardItem(long sender, ZPackage pkg) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown try { if (pkg != null) { string text = SafeLimit(pkg.ReadString(), 64); int num = Mathf.Clamp(pkg.ReadInt(), 0, int.MaxValue); string text2 = SanitizePlayerName(pkg.ReadString()); string prefabName = SafeLimit(pkg.ReadString(), 96); int amount = Mathf.Clamp(pkg.ReadInt(), 0, int.MaxValue); string text3 = SafeLimit(pkg.ReadString(), 96); string message; bool flag = TryAddRewardItemToLocalInventory(prefabName, amount, out message); ZPackage val = new ZPackage(); val.Write(text); val.Write(num); val.Write(text2); val.Write(flag); val.Write(string.IsNullOrWhiteSpace(message) ? text3 : message); ZRoutedRpc.instance.InvokeRoutedRPC(GetServerPeerUid(), "glitnir.ranking.finalizerewardclaim", new object[1] { val }); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_GrantRewardItem: " + ex)); } } private void RPC_FinalizeRewardClaim(long sender, ZPackage pkg) { try { if (IsServerInstance() && pkg != null) { string cycleId = SafeLimit(pkg.ReadString(), 64); int rank = Mathf.Clamp(pkg.ReadInt(), 0, int.MaxValue); string text = SanitizePlayerName(pkg.ReadString()); bool flag = pkg.ReadBool(); string text2 = SafeLimit(pkg.ReadString(), 128); string item = BuildPendingRewardKey(sender, cycleId, rank); if (_pendingRewardClaims.Contains(item)) { _pendingRewardClaims.Remove(item); } string text3 = ResolvePlayerNameFromSender(sender); if (!string.IsNullOrWhiteSpace(text3)) { text = text3; } if (string.IsNullOrWhiteSpace(text)) { ClearPendingRewardClaim(cycleId, text, rank); SaveDatabase(); SendRewardClaimFeedback(sender, success: false, "Não foi possível confirmar o jogador."); } else if (!flag) { ClearPendingRewardClaim(cycleId, text, rank); SaveDatabase(); SendRewardClaimFeedback(sender, success: false, string.IsNullOrWhiteSpace(text2) ? "Falha ao adicionar item no inventário." : text2); } else if (HasClaimedReward(cycleId, text, rank)) { SendRewardClaimFeedback(sender, success: false, "Essa recompensa já foi resgatada."); } else { MarkRewardClaimed(cycleId, text, rank); SaveDatabase(); SendRewardClaimFeedback(sender, success: true, string.IsNullOrWhiteSpace(text2) ? "Recompensa adicionada ao inventário." : text2); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_FinalizeRewardClaim: " + ex)); SendRewardClaimFeedback(sender, success: false, "Erro ao finalizar o resgate."); } } private void RPC_RequestPointsExchange(long sender, ZPackage pkg) { //IL_0320: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Expected O, but got Unknown try { if (!IsServerInstance() || pkg == null) { return; } string text = SafeLimit(pkg.ReadString(), 64); int num = Mathf.Clamp(pkg.ReadInt(), 0, int.MaxValue); string playerName = ResolvePlayerNameFromSender(sender); if (string.IsNullOrWhiteSpace(playerName)) { SendPointsExchangeFeedback(sender, success: false, "Não foi possível identificar o jogador."); return; } if (_rules == null || !_rules.PointsExchangeEnabled) { SendPointsExchangeFeedback(sender, success: false, "Câmbio de pontos desativado."); return; } if (!string.Equals(SafeLimit(_rules.RewardClaimCycleId, 64), text, StringComparison.OrdinalIgnoreCase)) { SendPointsExchangeFeedback(sender, success: false, "Ciclo inválido. Abra o ranking novamente."); return; } if (string.IsNullOrWhiteSpace(_rules.PointsExchangePrefab)) { SendPointsExchangeFeedback(sender, success: false, "Prefab do câmbio não configurado."); return; } RankingEntry rankingEntry = _database.Entries.FirstOrDefault((RankingEntry x) => x != null && string.Equals(x.PlayerName, playerName, StringComparison.OrdinalIgnoreCase)); if (rankingEntry == null) { SendPointsExchangeFeedback(sender, success: false, "Você não está no ranking."); return; } int num2 = Mathf.Max(0, rankingEntry.Points); if (num2 <= 0) { SendPointsExchangeFeedback(sender, success: false, "Você não possui pontos para trocar."); return; } string item = BuildPendingPointsExchangeKey(sender, text); if (_pendingPointsExchanges.Contains(item)) { SendPointsExchangeFeedback(sender, success: false, "Já existe um câmbio em andamento."); return; } int num3 = Mathf.Max(0, _rules.PointsExchangeMaxPointsPerRequest); int num4 = ((num3 > 0) ? Mathf.Min(num2, num3) : num2); if (num <= 0) { SendPointsExchangeFeedback(sender, success: false, "Informe uma quantidade válida para trocar."); return; } if (num > num2) { SendPointsExchangeFeedback(sender, success: false, "Você possui apenas " + num2 + " pontos."); return; } if (num > num4) { SendPointsExchangeFeedback(sender, success: false, "O limite por câmbio é " + num4 + " pontos."); return; } int num5 = num; int num6 = 0; if (_rules.PointsExchangeUsePointsPerCoin) { int num7 = Mathf.Max(1, _rules.PointsExchangePointsPerCoin); num6 = Mathf.Max(0, num / num7); num5 = num6 * num7; if (num6 <= 0 || num5 <= 0) { SendPointsExchangeFeedback(sender, success: false, "Você precisa de pelo menos " + num7 + " pontos para receber 1 moeda."); return; } } else { if (!_rules.PointsExchangeUseCoinsPerPoint) { SendPointsExchangeFeedback(sender, success: false, "Nenhum modo de câmbio está ativo. Ative UsePointsPerCoin ou UseCoinsPerPoint."); return; } int num8 = Mathf.Max(1, _rules.PointsExchangeCoinsPerPoint); num6 = Mathf.Max(1, num5 * num8); } _pendingPointsExchanges.Add(item); ZPackage val = new ZPackage(); val.Write(text); val.Write(SafeLimit(playerName, 32)); val.Write(num5); val.Write(_rules.PointsExchangePrefab ?? "Coins"); val.Write(num6); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "glitnir.ranking.grantexchangecoins", new object[1] { val }); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_RequestPointsExchange: " + ex)); SendPointsExchangeFeedback(sender, success: false, "Erro ao processar o câmbio."); } } private void RPC_GrantExchangeCoins(long sender, ZPackage pkg) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown try { if (pkg != null) { string text = SafeLimit(pkg.ReadString(), 64); string text2 = SanitizePlayerName(pkg.ReadString()); int num = Mathf.Clamp(pkg.ReadInt(), 0, int.MaxValue); string text3 = SafeLimit(pkg.ReadString(), 96); int amount = Mathf.Clamp(pkg.ReadInt(), 0, int.MaxValue); string message; bool flag = TryAddExchangeItemToLocalInventoryOrDropOverflow(text3, amount, out message); ZPackage val = new ZPackage(); val.Write(text); val.Write(text2); val.Write(num); val.Write(flag); val.Write(flag ? ("Câmbio concluído: -" + num + " pontos, +" + amount + " " + text3 + ". " + message) : message); ZRoutedRpc.instance.InvokeRoutedRPC(GetServerPeerUid(), "glitnir.ranking.finalizepointsexchange", new object[1] { val }); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_GrantExchangeCoins: " + ex)); } } private void RPC_FinalizePointsExchange(long sender, ZPackage pkg) { try { if (IsServerInstance() && pkg != null) { string cycleId = SafeLimit(pkg.ReadString(), 64); string playerName = SanitizePlayerName(pkg.ReadString()); int num = Mathf.Clamp(pkg.ReadInt(), 0, int.MaxValue); bool flag = pkg.ReadBool(); string text = SafeLimit(pkg.ReadString(), 128); string item = BuildPendingPointsExchangeKey(sender, cycleId); if (_pendingPointsExchanges.Contains(item)) { _pendingPointsExchanges.Remove(item); } string text2 = ResolvePlayerNameFromSender(sender); if (!string.IsNullOrWhiteSpace(text2)) { playerName = text2; } RankingEntry rankingEntry = _database.Entries.FirstOrDefault((RankingEntry x) => x != null && string.Equals(x.PlayerName, playerName, StringComparison.OrdinalIgnoreCase)); if (rankingEntry == null) { SendPointsExchangeFeedback(sender, success: false, "Jogador não encontrado no ranking."); return; } if (!flag) { SendPointsExchangeFeedback(sender, success: false, string.IsNullOrWhiteSpace(text) ? "Falha ao adicionar moedas no inventário." : text); return; } if (num <= 0 || rankingEntry.Points < num) { SendPointsExchangeFeedback(sender, success: false, "Pontos insuficientes para finalizar o câmbio."); return; } int num2 = Mathf.Max(1, num * Mathf.Max(1, (_rules == null) ? 1 : _rules.PointsExchangeCoinsPerPoint)); rankingEntry.Points = Mathf.Max(0, rankingEntry.Points - num); rankingEntry.TotalPointsExchanges = Mathf.Clamp(rankingEntry.TotalPointsExchanges + 1, 0, int.MaxValue); rankingEntry.PointsExchangePenaltyTotal = Mathf.Clamp(rankingEntry.PointsExchangePenaltyTotal + num, 0, int.MaxValue); rankingEntry.PointsExchangeCoinsTotal = Mathf.Clamp(rankingEntry.PointsExchangeCoinsTotal + num2, 0, int.MaxValue); rankingEntry.LastReason = "Câmbio de pontos: -" + num + " pts, +" + num2 + " moedas"; rankingEntry.LastUpdateUtc = DateTime.UtcNow.ToString("o"); SaveDatabase(); SendPointsExchangeFeedback(sender, success: true, string.IsNullOrWhiteSpace(text) ? "Câmbio concluído." : text); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_FinalizePointsExchange: " + ex)); SendPointsExchangeFeedback(sender, success: false, "Erro ao finalizar o câmbio."); } } private void RPC_PointsExchangeFeedback(long sender, ZPackage pkg) { try { if (pkg != null) { bool flag = pkg.ReadBool(); string text = SafeLimit(pkg.ReadString(), 128); _pointsExchangeRequestPending = false; SetStatus((!string.IsNullOrWhiteSpace(text)) ? text : (flag ? "Câmbio concluído." : "Câmbio não concluído."), flag ? 4f : 3.5f); if (flag) { SpawnGlitnirRewardClaimEffect(); } RequestSnapshotFromServer(); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_PointsExchangeFeedback: " + ex)); } } private void SendPointsExchangeFeedback(long sender, bool success, string message) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(success); val.Write(SafeLimit(message, 128)); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "glitnir.ranking.pointsexchangefeedback", new object[1] { val }); } } private string BuildPendingPointsExchangeKey(long sender, string cycleId) { return sender + "|" + SafeLimit(cycleId, 64); } private void RPC_RewardClaimFeedback(long sender, ZPackage pkg) { try { if (pkg != null) { bool flag = pkg.ReadBool(); string text = SafeLimit(pkg.ReadString(), 128); _rewardClaimRequestPending = false; _rewardClaimPendingCycleId = ""; _rewardClaimPendingRank = 0; SetStatus((!string.IsNullOrWhiteSpace(text)) ? text : (flag ? "Recompensa resgatada." : "Resgate não concluído."), flag ? 4f : 3.5f); if (flag) { SpawnGlitnirRewardClaimEffect(); } RequestSnapshotFromServer(); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_RewardClaimFeedback: " + ex)); } } private void SendRewardClaimFeedback(long sender, bool success, string message) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(success); val.Write(SafeLimit(message, 128)); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "glitnir.ranking.rewardclaimfeedback", new object[1] { val }); } } private void SpawnGlitnirRewardClaimEffect() { //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return; } string[] array = new string[4] { "vfx_Potion_stamina_medium", "vfx_Potion_health_medium", "vfx_Potion_eitr_minor", "vfx_coin_pile_destroyed" }; GameObject val = null; string[] array2 = array; foreach (string text in array2) { val = ZNetScene.instance.GetPrefab(text); if ((Object)(object)val != (Object)null) { break; } } if ((Object)(object)val == (Object)null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"[GlitnirRanking] Nenhum prefab de efeito encontrado para o claim."); return; } Vector3 position = ((Component)localPlayer).transform.position; for (int j = 0; j < 10; j++) { float num = (float)j * (float)Math.PI * 2f / 10f; Vector3 val2 = position + new Vector3(Mathf.Cos(num) * 2.1f, 0.9f, Mathf.Sin(num) * 2.1f); Object.Instantiate(val, val2, Quaternion.identity); } Object.Instantiate(val, position + Vector3.up * 1.6f, Quaternion.identity); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[GlitnirRanking] Falha ao criar efeito visual do claim: " + ex.Message)); } } private void CheckInitialServerSnapshotRequest() { try { if (!_rpcsRegistered || ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null) { return; } long serverPeerUid = GetServerPeerUid(); if (serverPeerUid == 0) { _lastKnownServerPeerUid = 0L; _requestedInitialServerSnapshot = false; _lastSnapshotSyncPlayerName = ""; _lastSnapshotHadLocalPlayer = false; return; } bool flag = (Object)(object)Player.m_localPlayer != (Object)null; string text = (flag ? GetLocalPlayerName() : ""); if (!flag) { _requestedInitialServerSnapshot = false; _lastSnapshotSyncPlayerName = ""; _lastSnapshotHadLocalPlayer = false; return; } bool flag2 = serverPeerUid != _lastKnownServerPeerUid; bool flag3 = !string.Equals(text, _lastSnapshotSyncPlayerName, StringComparison.OrdinalIgnoreCase); bool flag4 = !_lastSnapshotHadLocalPlayer && flag; if (flag2 || flag3 || flag4) { _lastKnownServerPeerUid = serverPeerUid; _lastSnapshotSyncPlayerName = text; _lastSnapshotHadLocalPlayer = true; _requestedInitialServerSnapshot = false; _cachedTopText = "Sincronizando ranking..."; _cachedPlayerText = "Aguardando dados do servidor..."; } if (!_requestedInitialServerSnapshot) { _requestedInitialServerSnapshot = true; _clientRefreshTimer = 0f; RequestSnapshotFromServer(); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao solicitar snapshot inicial: " + ex.Message)); } } private Dictionary GetRankingSnapshot() { return GetRankingSnapshotFromCache(); } private void RequestSnapshotFromServer() { //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown try { if (IsServerInstance()) { long serverPeerUid = GetServerPeerUid(); if (serverPeerUid == 0) { string localPlayerName = GetLocalPlayerName(); BuildSnapshotTexts(localPlayerName, out var topText, out var playerText); BuildSnapshotData(localPlayerName, out var topEntries, out var playerData); _cachedTopText = topText; _cachedPlayerText = playerText; CacheSnapshotData(topEntries, playerData); SetStatus("Snapshot local atualizado.", 2f); return; } } if (_rpcsRegistered && ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null)) { long serverPeerUid2 = GetServerPeerUid(); if (serverPeerUid2 != 0) { ZPackage val = new ZPackage(); val.Write(GetLocalPlayerName()); ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerUid2, "glitnir.ranking.requestsnapshot", new object[1] { val }); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao solicitar snapshot do ranking: " + ex)); } } private void RPC_RequestSnapshot(long sender, ZPackage pkg) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Expected O, but got Unknown try { if (IsServerInstance()) { string text = ""; if (pkg != null) { text = SanitizePlayerName(pkg.ReadString()); } DebugLog(DebugCategory.Snapshot, "Snapshot solicitado por sender=" + sender + " player=" + text + " entries=" + _database.Entries.Count); BuildSnapshotTexts(text, out var topText, out var playerText); BuildSnapshotData(text, out var topEntries, out var playerData); ZPackage val = new ZPackage(); val.Write(topText); val.Write(playerText); WriteSnapshotPayload(val, topEntries, playerData); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "glitnir.ranking.receivesnapshot", new object[1] { val }); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_RequestSnapshot: " + ex)); } } private void RPC_ReceiveSnapshot(long sender, ZPackage pkg) { try { if (pkg != null) { _cachedTopText = pkg.ReadString(); _cachedPlayerText = pkg.ReadString(); ReadSnapshotPayload(pkg); SetStatus("Ranking sincronizado.", 1f); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_ReceiveSnapshot: " + ex)); } } private void CacheSnapshotData(List topEntries, SnapshotPlayerData playerData) { _cachedTopEntries.Clear(); if (topEntries != null) { _cachedTopEntries.AddRange(topEntries); } _cachedPlayerData = playerData ?? new SnapshotPlayerData(); } private void BuildSnapshotData(string playerName, out List topEntries, out SnapshotPlayerData playerData) { topEntries = new List(); playerData = new SnapshotPlayerData { RankingEnabled = _rules.RankingEnabled, EnableKillPoints = _rules.EnableKillPoints, EnableBossPoints = _rules.EnableBossPoints, EnableSkillPoints = _rules.EnableSkillPoints, EnableMarketplaceQuestPoints = _rules.EnableMarketplaceQuestPoints, EnableDeathPenalty = _rules.EnableDeathPenalty, DeathPenaltyUseMultiplier = _rules.DeathPenaltyUseMultiplier, DeathPenaltyPerDeath = Mathf.Max(0, _rules.DeathPenaltyPerDeath), TopCount = _rules.TopCount, HudKillRules = FormatCategorizedIntRulesForHud(_rules.KillPoints, _rules.CombatHudCategories, combatMode: true), HudBossRules = FormatIntRulesForHud(_rules.BossPoints), HudSkillJackpotRules = FormatSkillJackpotRulesForHud(_rules.SkillMilestoneJackpotPoints), HudMarketplaceQuestRules = FormatIntRulesForHud(_rules.MarketplaceQuestPoints), HudFishingRules = FormatIntRulesForHud(_rules.FishingPoints), HudCraftRules = FormatCategorizedIntRulesForHud(_rules.CraftPoints, _rules.ProductionHudCategories), HudFarmJackpotRules = FormatCategorizedJackpotRulesForHud(_rules.FarmJackpots, _rules.ProductionHudCategories), HudUniqueCraftJackpotRules = FormatCategorizedJackpotRulesForHud(_rules.UniqueCraftJackpots, _rules.ProductionHudCategories), HudDeathPenaltyRules = FormatDeathPenaltyRulesForHud(_rules.DeathPenaltyRules), HudExplorationMapJackpotRules = FormatExplorationMapJackpotRulesForHud(_rules.ExplorationMapJackpots) }; if (!_rules.RankingEnabled) { FillRewardSnapshotData(playerData); return; } IReadOnlyList orderedRankingEntries = GetOrderedRankingEntries(); playerName = SanitizePlayerName(playerName); int num = Mathf.Min(Mathf.Clamp(_rules.TopCount, 1, 50), orderedRankingEntries.Count); for (int i = 0; i < num; i++) { RankingEntry rankingEntry = orderedRankingEntries[i]; topEntries.Add(new SnapshotTopEntryData { Position = i + 1, PlayerName = ((rankingEntry != null) ? rankingEntry.PlayerName : "Jogador"), Points = (rankingEntry?.Points ?? 0), IsLocalPlayer = (rankingEntry != null && string.Equals(rankingEntry.PlayerName, playerName, StringComparison.OrdinalIgnoreCase)), TotalKillsPontuadas = (rankingEntry?.TotalKillsPontuadas ?? 0), TotalBossesPontuadas = (rankingEntry?.TotalBossesPontuadas ?? 0), TotalSkillLevelUpsPontuados = (rankingEntry?.TotalSkillLevelUpsPontuados ?? 0), TotalMarketplaceQuestsPontuadas = ((rankingEntry != null) ? CountMarketplaceQuestCreditsForPlayer(rankingEntry.PlayerName) : 0), MarketplaceQuestPointsTotal = ((rankingEntry != null) ? CalculateMarketplaceQuestPointsForPlayer(rankingEntry.PlayerName) : 0), TotalFishingPontuadas = (rankingEntry?.TotalFishingPontuadas ?? 0), TotalCraftPontuadas = (rankingEntry?.TotalCraftPontuadas ?? 0), TotalFarmJackpotsPontuados = (rankingEntry?.TotalFarmJackpotsPontuados ?? 0), TotalUniqueCraftJackpotsPontuados = (rankingEntry?.TotalUniqueCraftJackpotsPontuados ?? 0), TotalDeaths = (rankingEntry?.TotalDeaths ?? 0), KillPointsTotal = (rankingEntry?.KillPointsTotal ?? 0), BossPointsTotal = (rankingEntry?.BossPointsTotal ?? 0), SkillPointsTotal = (rankingEntry?.SkillPointsTotal ?? 0), FishingPointsTotal = (rankingEntry?.FishingPointsTotal ?? 0), CraftPointsTotal = (rankingEntry?.CraftPointsTotal ?? 0), FarmJackpotPointsTotal = (rankingEntry?.FarmJackpotPointsTotal ?? 0), UniqueCraftJackpotPointsTotal = (rankingEntry?.UniqueCraftJackpotPointsTotal ?? 0), DeathPenaltyPointsTotal = (rankingEntry?.DeathPenaltyPointsTotal ?? 0), TotalPointsExchanges = (rankingEntry?.TotalPointsExchanges ?? 0), PointsExchangePenaltyTotal = (rankingEntry?.PointsExchangePenaltyTotal ?? 0), PointsExchangeCoinsTotal = (rankingEntry?.PointsExchangeCoinsTotal ?? 0), ExplorationMapJackpotPointsTotal = (rankingEntry?.ExplorationMapJackpotPointsTotal ?? 0), LastReason = ((rankingEntry != null) ? (rankingEntry.LastReason ?? "") : ""), LastUpdateUtc = ((rankingEntry != null) ? (rankingEntry.LastUpdateUtc ?? "") : "") }); } for (int j = 0; j < orderedRankingEntries.Count; j++) { RankingEntry rankingEntry2 = orderedRankingEntries[j]; if (rankingEntry2 != null && string.Equals(rankingEntry2.PlayerName, playerName, StringComparison.OrdinalIgnoreCase)) { playerData.HasData = true; playerData.Position = j + 1; playerData.PlayerName = rankingEntry2.PlayerName; playerData.Points = rankingEntry2.Points; playerData.TotalKillsPontuadas = rankingEntry2.TotalKillsPontuadas; playerData.TotalBossesPontuadas = rankingEntry2.TotalBossesPontuadas; playerData.TotalSkillLevelUpsPontuados = rankingEntry2.TotalSkillLevelUpsPontuados; playerData.TotalMarketplaceQuestsPontuadas = CountMarketplaceQuestCreditsForPlayer(rankingEntry2.PlayerName); playerData.MarketplaceQuestPointsTotal = CalculateMarketplaceQuestPointsForPlayer(rankingEntry2.PlayerName); playerData.KillPointsTotal = rankingEntry2.KillPointsTotal; playerData.BossPointsTotal = rankingEntry2.BossPointsTotal; playerData.SkillPointsTotal = rankingEntry2.SkillPointsTotal; playerData.TotalFishingPontuadas = rankingEntry2.TotalFishingPontuadas; playerData.TotalCraftPontuadas = rankingEntry2.TotalCraftPontuadas; playerData.TotalFarmJackpotsPontuados = rankingEntry2.TotalFarmJackpotsPontuados; playerData.TotalUniqueCraftJackpotsPontuados = rankingEntry2.TotalUniqueCraftJackpotsPontuados; playerData.TotalDeaths = rankingEntry2.TotalDeaths; playerData.FishingPointsTotal = rankingEntry2.FishingPointsTotal; playerData.CraftPointsTotal = rankingEntry2.CraftPointsTotal; playerData.FarmJackpotPointsTotal = rankingEntry2.FarmJackpotPointsTotal; playerData.UniqueCraftJackpotPointsTotal = rankingEntry2.UniqueCraftJackpotPointsTotal; playerData.DeathPenaltyPointsTotal = rankingEntry2.DeathPenaltyPointsTotal; playerData.TotalPointsExchanges = rankingEntry2.TotalPointsExchanges; playerData.PointsExchangePenaltyTotal = rankingEntry2.PointsExchangePenaltyTotal; playerData.PointsExchangeCoinsTotal = rankingEntry2.PointsExchangeCoinsTotal; playerData.ExplorationMapJackpotPointsTotal = rankingEntry2.ExplorationMapJackpotPointsTotal; playerData.LastReason = rankingEntry2.LastReason ?? ""; playerData.LastUpdateUtc = rankingEntry2.LastUpdateUtc ?? ""; playerData.ProgressCounters = ((rankingEntry2.ProgressCounters != null) ? new Dictionary(rankingEntry2.ProgressCounters, StringComparer.OrdinalIgnoreCase) : new Dictionary(StringComparer.OrdinalIgnoreCase)); EnsureFishingProgressCountersFromCredits(playerData.PlayerName, playerData.ProgressCounters); EnsureMarketplaceQuestProgressCountersFromCredits(playerData.PlayerName, playerData.ProgressCounters); playerData.FloatProgressCounters = ((rankingEntry2.FloatProgressCounters != null) ? new Dictionary(rankingEntry2.FloatProgressCounters, StringComparer.OrdinalIgnoreCase) : new Dictionary(StringComparer.OrdinalIgnoreCase)); FillRewardSnapshotData(playerData); return; } } FillRewardSnapshotData(playerData); } private int CountMarketplaceQuestCreditsForPlayer(string playerName) { try { if (string.IsNullOrWhiteSpace(playerName) || _database == null || _database.MarketplaceQuestCredits == null) { return 0; } return GetMarketplaceQuestCreditsForPlayer(playerName).Count; } catch { return 0; } } private int CalculateMarketplaceQuestPointsForPlayer(string playerName) { try { if (string.IsNullOrWhiteSpace(playerName) || _database == null || _database.MarketplaceQuestCredits == null || _rules == null || _rules.MarketplaceQuestPoints == null) { return 0; } int num = 0; List marketplaceQuestCreditsForPlayer = GetMarketplaceQuestCreditsForPlayer(playerName); for (int i = 0; i < marketplaceQuestCreditsForPlayer.Count; i++) { MarketplaceQuestCreditRecord marketplaceQuestCreditRecord = marketplaceQuestCreditsForPlayer[i]; if (marketplaceQuestCreditRecord != null && _rules.MarketplaceQuestPoints.TryGetValue(SafeMarketplaceQuestKey(marketplaceQuestCreditRecord.QuestKey), out var value)) { num = Mathf.Clamp(num + Mathf.Max(0, value), 0, int.MaxValue); } } return num; } catch { return 0; } } private void EnsureMarketplaceQuestProgressCountersFromCredits(string playerName, Dictionary counters) { try { if (string.IsNullOrWhiteSpace(playerName) || counters == null || _database == null || _database.MarketplaceQuestCredits == null) { return; } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); List marketplaceQuestCreditsForPlayer = GetMarketplaceQuestCreditsForPlayer(playerName); for (int i = 0; i < marketplaceQuestCreditsForPlayer.Count; i++) { MarketplaceQuestCreditRecord marketplaceQuestCreditRecord = marketplaceQuestCreditsForPlayer[i]; if (marketplaceQuestCreditRecord != null) { string text = SafeMarketplaceQuestKey(marketplaceQuestCreditRecord.QuestKey); if (!string.IsNullOrWhiteSpace(text)) { int value = 0; dictionary.TryGetValue(text, out value); dictionary[text] = Mathf.Clamp(value + 1, 0, int.MaxValue); } } } foreach (KeyValuePair item in dictionary) { SetProgressCounterMinimum(counters, "MarketplaceQuest", item.Key, item.Value); SetProgressCounterMinimum(counters, "Marketplace", item.Key, item.Value); SetProgressCounterMinimum(counters, "Quest", item.Key, item.Value); SetProgressCounterMinimum(counters, "Quests", item.Key, item.Value); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao reconstruir contagem de quests Marketplace no snapshot: " + ex)); } } private void EnsureFishingProgressCountersFromCredits(string playerName, Dictionary counters) { try { if (string.IsNullOrWhiteSpace(playerName) || counters == null || _database == null || _database.FishingCatchCredits == null) { return; } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < _database.FishingCatchCredits.Count; i++) { FishingCatchCreditRecord fishingCatchCreditRecord = _database.FishingCatchCredits[i]; if (fishingCatchCreditRecord != null && string.Equals(SanitizePlayerName(fishingCatchCreditRecord.PlayerName), SanitizePlayerName(playerName), StringComparison.OrdinalIgnoreCase)) { string text = NormalizeFishPrefabName(fishingCatchCreditRecord.FishPrefab); if (!string.IsNullOrWhiteSpace(text) && IsFishPrefab(text)) { int value = 0; dictionary.TryGetValue(text, out value); dictionary[text] = Mathf.Clamp(value + 1, 0, int.MaxValue); } } } foreach (KeyValuePair item in dictionary) { SetProgressCounterMinimum(counters, "Fishing", item.Key, item.Value); SetProgressCounterMinimum(counters, "Pesca", item.Key, item.Value); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao reconstruir contagem de pesca no snapshot: " + ex)); } } private void SetProgressCounterMinimum(Dictionary counters, string category, string key, int value) { if (counters != null && !string.IsNullOrWhiteSpace(category) && !string.IsNullOrWhiteSpace(key) && value > 0) { string key2 = SafeKey(category) + ":" + SafeKey(key); int value2 = 0; counters.TryGetValue(key2, out value2); counters[key2] = Mathf.Clamp(Mathf.Max(value2, value), 0, int.MaxValue); } } private void WriteSnapshotPayload(ZPackage response, List topEntries, SnapshotPlayerData playerData) { if (response != null) { int num = topEntries?.Count ?? 0; response.Write(num); for (int i = 0; i < num; i++) { SnapshotTopEntryData snapshotTopEntryData = topEntries[i] ?? new SnapshotTopEntryData(); response.Write(snapshotTopEntryData.Position); response.Write(snapshotTopEntryData.PlayerName ?? ""); response.Write(snapshotTopEntryData.Points); response.Write(snapshotTopEntryData.IsLocalPlayer); response.Write(snapshotTopEntryData.TotalKillsPontuadas); response.Write(snapshotTopEntryData.TotalBossesPontuadas); response.Write(snapshotTopEntryData.TotalSkillLevelUpsPontuados); response.Write(snapshotTopEntryData.TotalFishingPontuadas); response.Write(snapshotTopEntryData.TotalCraftPontuadas); response.Write(snapshotTopEntryData.TotalFarmJackpotsPontuados); response.Write(snapshotTopEntryData.TotalUniqueCraftJackpotsPontuados); response.Write(snapshotTopEntryData.TotalDeaths); response.Write(snapshotTopEntryData.KillPointsTotal); response.Write(snapshotTopEntryData.BossPointsTotal); response.Write(snapshotTopEntryData.SkillPointsTotal); response.Write(snapshotTopEntryData.FishingPointsTotal); response.Write(snapshotTopEntryData.CraftPointsTotal); response.Write(snapshotTopEntryData.FarmJackpotPointsTotal); response.Write(snapshotTopEntryData.UniqueCraftJackpotPointsTotal); response.Write(snapshotTopEntryData.DeathPenaltyPointsTotal); response.Write(snapshotTopEntryData.TotalPointsExchanges); response.Write(snapshotTopEntryData.PointsExchangePenaltyTotal); response.Write(snapshotTopEntryData.PointsExchangeCoinsTotal); response.Write(snapshotTopEntryData.ExplorationMapJackpotPointsTotal); response.Write(snapshotTopEntryData.LastReason ?? ""); response.Write(snapshotTopEntryData.LastUpdateUtc ?? ""); response.Write(snapshotTopEntryData.TotalMarketplaceQuestsPontuadas); response.Write(snapshotTopEntryData.MarketplaceQuestPointsTotal); } SnapshotPlayerData snapshotPlayerData = playerData ?? new SnapshotPlayerData(); response.Write(snapshotPlayerData.HasData); response.Write(snapshotPlayerData.Position); response.Write(snapshotPlayerData.PlayerName ?? ""); response.Write(snapshotPlayerData.Points); response.Write(snapshotPlayerData.TotalKillsPontuadas); response.Write(snapshotPlayerData.TotalBossesPontuadas); response.Write(snapshotPlayerData.TotalSkillLevelUpsPontuados); response.Write(snapshotPlayerData.KillPointsTotal); response.Write(snapshotPlayerData.BossPointsTotal); response.Write(snapshotPlayerData.SkillPointsTotal); response.Write(snapshotPlayerData.TotalFishingPontuadas); response.Write(snapshotPlayerData.TotalCraftPontuadas); response.Write(snapshotPlayerData.TotalFarmJackpotsPontuados); response.Write(snapshotPlayerData.TotalUniqueCraftJackpotsPontuados); response.Write(snapshotPlayerData.TotalDeaths); response.Write(snapshotPlayerData.FishingPointsTotal); response.Write(snapshotPlayerData.CraftPointsTotal); response.Write(snapshotPlayerData.FarmJackpotPointsTotal); response.Write(snapshotPlayerData.UniqueCraftJackpotPointsTotal); response.Write(snapshotPlayerData.DeathPenaltyPointsTotal); response.Write(snapshotPlayerData.TotalPointsExchanges); response.Write(snapshotPlayerData.PointsExchangePenaltyTotal); response.Write(snapshotPlayerData.PointsExchangeCoinsTotal); response.Write(snapshotPlayerData.ExplorationMapJackpotPointsTotal); response.Write(snapshotPlayerData.LastReason ?? ""); response.Write(snapshotPlayerData.LastUpdateUtc ?? ""); response.Write(snapshotPlayerData.RankingEnabled); response.Write(snapshotPlayerData.EnableKillPoints); response.Write(snapshotPlayerData.EnableBossPoints); response.Write(snapshotPlayerData.EnableSkillPoints); response.Write(snapshotPlayerData.EnableMarketplaceQuestPoints); response.Write(snapshotPlayerData.TopCount); response.Write(snapshotPlayerData.RewardClaimsEnabled); response.Write(snapshotPlayerData.RewardCanClaim); response.Write(snapshotPlayerData.RewardAlreadyClaimed); response.Write(snapshotPlayerData.RewardRank); response.Write(snapshotPlayerData.RewardMinPoints); response.Write(snapshotPlayerData.RewardLabel ?? ""); response.Write(snapshotPlayerData.RewardPrefabName ?? ""); response.Write(snapshotPlayerData.RewardAmount); response.Write(snapshotPlayerData.RewardBlockReason ?? ""); response.Write(snapshotPlayerData.RewardClaimCycleId ?? ""); response.Write(snapshotPlayerData.HudKillRules ?? ""); response.Write(snapshotPlayerData.HudBossRules ?? ""); response.Write(snapshotPlayerData.HudSkillJackpotRules ?? ""); response.Write(snapshotPlayerData.HudMarketplaceQuestRules ?? ""); response.Write(snapshotPlayerData.HudFishingRules ?? ""); response.Write(snapshotPlayerData.HudCraftRules ?? ""); response.Write(snapshotPlayerData.HudFarmJackpotRules ?? ""); response.Write(snapshotPlayerData.HudUniqueCraftJackpotRules ?? ""); response.Write(snapshotPlayerData.HudDeathPenaltyRules ?? ""); response.Write(snapshotPlayerData.HudExplorationMapJackpotRules ?? ""); response.Write(snapshotPlayerData.EnableDeathPenalty); response.Write(snapshotPlayerData.DeathPenaltyUseMultiplier); response.Write(Mathf.Max(0, snapshotPlayerData.DeathPenaltyPerDeath)); response.Write(SerializeStringIntDictionary(snapshotPlayerData.ProgressCounters)); response.Write(SerializeStringFloatDictionary(snapshotPlayerData.FloatProgressCounters)); response.Write(snapshotPlayerData.TotalMarketplaceQuestsPontuadas); response.Write(snapshotPlayerData.MarketplaceQuestPointsTotal); } } private void ReadSnapshotPayload(ZPackage pkg) { _cachedTopEntries.Clear(); _cachedPlayerData = new SnapshotPlayerData(); if (pkg == null) { return; } int num = 0; try { num = Mathf.Clamp(pkg.ReadInt(), 0, 100); } catch { num = 0; } for (int i = 0; i < num; i++) { SnapshotTopEntryData snapshotTopEntryData = new SnapshotTopEntryData(); try { snapshotTopEntryData.Position = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.PlayerName = pkg.ReadString(); } catch { snapshotTopEntryData.PlayerName = ""; } try { snapshotTopEntryData.Points = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.IsLocalPlayer = pkg.ReadBool(); } catch { } try { snapshotTopEntryData.TotalKillsPontuadas = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.TotalBossesPontuadas = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.TotalSkillLevelUpsPontuados = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.TotalFishingPontuadas = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.TotalCraftPontuadas = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.TotalFarmJackpotsPontuados = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.TotalUniqueCraftJackpotsPontuados = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.TotalDeaths = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.KillPointsTotal = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.BossPointsTotal = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.SkillPointsTotal = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.FishingPointsTotal = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.CraftPointsTotal = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.FarmJackpotPointsTotal = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.UniqueCraftJackpotPointsTotal = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.DeathPenaltyPointsTotal = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.TotalPointsExchanges = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.PointsExchangePenaltyTotal = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.PointsExchangeCoinsTotal = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.ExplorationMapJackpotPointsTotal = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.LastReason = pkg.ReadString(); } catch { } try { snapshotTopEntryData.LastUpdateUtc = pkg.ReadString(); } catch { } try { snapshotTopEntryData.TotalMarketplaceQuestsPontuadas = pkg.ReadInt(); } catch { } try { snapshotTopEntryData.MarketplaceQuestPointsTotal = pkg.ReadInt(); } catch { } _cachedTopEntries.Add(snapshotTopEntryData); } SnapshotPlayerData snapshotPlayerData = new SnapshotPlayerData(); try { snapshotPlayerData.HasData = pkg.ReadBool(); } catch { } try { snapshotPlayerData.Position = pkg.ReadInt(); } catch { } try { snapshotPlayerData.PlayerName = pkg.ReadString(); } catch { } try { snapshotPlayerData.Points = pkg.ReadInt(); } catch { } try { snapshotPlayerData.TotalKillsPontuadas = pkg.ReadInt(); } catch { } try { snapshotPlayerData.TotalBossesPontuadas = pkg.ReadInt(); } catch { } try { snapshotPlayerData.TotalSkillLevelUpsPontuados = pkg.ReadInt(); } catch { } try { snapshotPlayerData.KillPointsTotal = pkg.ReadInt(); } catch { } try { snapshotPlayerData.BossPointsTotal = pkg.ReadInt(); } catch { } try { snapshotPlayerData.SkillPointsTotal = pkg.ReadInt(); } catch { } try { snapshotPlayerData.TotalFishingPontuadas = pkg.ReadInt(); } catch { } try { snapshotPlayerData.TotalCraftPontuadas = pkg.ReadInt(); } catch { } try { snapshotPlayerData.TotalFarmJackpotsPontuados = pkg.ReadInt(); } catch { } try { snapshotPlayerData.TotalUniqueCraftJackpotsPontuados = pkg.ReadInt(); } catch { } try { snapshotPlayerData.TotalDeaths = pkg.ReadInt(); } catch { } try { snapshotPlayerData.FishingPointsTotal = pkg.ReadInt(); } catch { } try { snapshotPlayerData.CraftPointsTotal = pkg.ReadInt(); } catch { } try { snapshotPlayerData.FarmJackpotPointsTotal = pkg.ReadInt(); } catch { } try { snapshotPlayerData.UniqueCraftJackpotPointsTotal = pkg.ReadInt(); } catch { } try { snapshotPlayerData.DeathPenaltyPointsTotal = pkg.ReadInt(); } catch { } try { snapshotPlayerData.TotalPointsExchanges = pkg.ReadInt(); } catch { } try { snapshotPlayerData.PointsExchangePenaltyTotal = pkg.ReadInt(); } catch { } try { snapshotPlayerData.PointsExchangeCoinsTotal = pkg.ReadInt(); } catch { } try { snapshotPlayerData.ExplorationMapJackpotPointsTotal = pkg.ReadInt(); } catch { } try { snapshotPlayerData.LastReason = pkg.ReadString(); } catch { } try { snapshotPlayerData.LastUpdateUtc = pkg.ReadString(); } catch { } try { snapshotPlayerData.RankingEnabled = pkg.ReadBool(); } catch { snapshotPlayerData.RankingEnabled = _rules.RankingEnabled; } try { snapshotPlayerData.EnableKillPoints = pkg.ReadBool(); } catch { snapshotPlayerData.EnableKillPoints = _rules.EnableKillPoints; } try { snapshotPlayerData.EnableBossPoints = pkg.ReadBool(); } catch { snapshotPlayerData.EnableBossPoints = _rules.EnableBossPoints; } try { snapshotPlayerData.EnableSkillPoints = pkg.ReadBool(); } catch { snapshotPlayerData.EnableSkillPoints = _rules.EnableSkillPoints; } try { snapshotPlayerData.EnableMarketplaceQuestPoints = pkg.ReadBool(); } catch { snapshotPlayerData.EnableMarketplaceQuestPoints = _rules.EnableMarketplaceQuestPoints; } try { snapshotPlayerData.TopCount = pkg.ReadInt(); } catch { snapshotPlayerData.TopCount = _rules.TopCount; } try { snapshotPlayerData.RewardClaimsEnabled = pkg.ReadBool(); } catch { snapshotPlayerData.RewardClaimsEnabled = _rules.RewardClaimsEnabled; } try { snapshotPlayerData.RewardCanClaim = pkg.ReadBool(); } catch { } try { snapshotPlayerData.RewardAlreadyClaimed = pkg.ReadBool(); } catch { } try { snapshotPlayerData.RewardRank = pkg.ReadInt(); } catch { } try { snapshotPlayerData.RewardMinPoints = pkg.ReadInt(); } catch { } try { snapshotPlayerData.RewardLabel = pkg.ReadString(); } catch { } try { snapshotPlayerData.RewardPrefabName = pkg.ReadString(); } catch { } try { snapshotPlayerData.RewardAmount = pkg.ReadInt(); } catch { } try { snapshotPlayerData.RewardBlockReason = pkg.ReadString(); } catch { } try { snapshotPlayerData.RewardClaimCycleId = pkg.ReadString(); } catch { } try { snapshotPlayerData.HudKillRules = pkg.ReadString(); } catch { snapshotPlayerData.HudKillRules = FormatCategorizedIntRulesForHud(_rules.KillPoints, _rules.CombatHudCategories, combatMode: true); } try { snapshotPlayerData.HudBossRules = pkg.ReadString(); } catch { snapshotPlayerData.HudBossRules = FormatIntRulesForHud(_rules.BossPoints); } try { snapshotPlayerData.HudSkillJackpotRules = pkg.ReadString(); } catch { snapshotPlayerData.HudSkillJackpotRules = FormatSkillJackpotRulesForHud(_rules.SkillMilestoneJackpotPoints); } try { snapshotPlayerData.HudMarketplaceQuestRules = pkg.ReadString(); } catch { snapshotPlayerData.HudMarketplaceQuestRules = FormatIntRulesForHud(_rules.MarketplaceQuestPoints); } try { snapshotPlayerData.HudFishingRules = pkg.ReadString(); } catch { snapshotPlayerData.HudFishingRules = FormatIntRulesForHud(_rules.FishingPoints); } try { snapshotPlayerData.HudCraftRules = pkg.ReadString(); } catch { snapshotPlayerData.HudCraftRules = FormatCategorizedIntRulesForHud(_rules.CraftPoints, _rules.ProductionHudCategories); } try { snapshotPlayerData.HudFarmJackpotRules = pkg.ReadString(); } catch { snapshotPlayerData.HudFarmJackpotRules = FormatCategorizedJackpotRulesForHud(_rules.FarmJackpots, _rules.ProductionHudCategories); } try { snapshotPlayerData.HudUniqueCraftJackpotRules = pkg.ReadString(); } catch { snapshotPlayerData.HudUniqueCraftJackpotRules = FormatCategorizedJackpotRulesForHud(_rules.UniqueCraftJackpots, _rules.ProductionHudCategories); } try { snapshotPlayerData.HudDeathPenaltyRules = pkg.ReadString(); } catch { snapshotPlayerData.HudDeathPenaltyRules = FormatDeathPenaltyRulesForHud(_rules.DeathPenaltyRules); } try { snapshotPlayerData.HudExplorationMapJackpotRules = pkg.ReadString(); } catch { snapshotPlayerData.HudExplorationMapJackpotRules = FormatExplorationMapJackpotRulesForHud(_rules.ExplorationMapJackpots); } try { snapshotPlayerData.EnableDeathPenalty = pkg.ReadBool(); } catch { snapshotPlayerData.EnableDeathPenalty = _rules.EnableDeathPenalty; } try { snapshotPlayerData.DeathPenaltyUseMultiplier = pkg.ReadBool(); } catch { snapshotPlayerData.DeathPenaltyUseMultiplier = _rules.DeathPenaltyUseMultiplier; } try { snapshotPlayerData.DeathPenaltyPerDeath = Mathf.Max(0, pkg.ReadInt()); } catch { snapshotPlayerData.DeathPenaltyPerDeath = Mathf.Max(0, _rules.DeathPenaltyPerDeath); } try { snapshotPlayerData.ProgressCounters = DeserializeStringIntDictionary(pkg.ReadString()); } catch { snapshotPlayerData.ProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); } try { snapshotPlayerData.FloatProgressCounters = DeserializeStringFloatDictionary(pkg.ReadString()); } catch { snapshotPlayerData.FloatProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); } try { snapshotPlayerData.TotalMarketplaceQuestsPontuadas = pkg.ReadInt(); } catch { } try { snapshotPlayerData.MarketplaceQuestPointsTotal = pkg.ReadInt(); } catch { } _cachedPlayerData = snapshotPlayerData; } private void BuildSnapshotTexts(string playerName, out string topText, out string playerText) { if (!_rules.RankingEnabled) { topText = "Ranking desativado."; playerText = "Status: desativado"; return; } IReadOnlyList orderedRankingEntries = GetOrderedRankingEntries(); int num = Mathf.Clamp(_rules.TopCount, 1, 50); playerName = SanitizePlayerName(playerName); int num2 = -1; RankingEntry rankingEntry = null; for (int i = 0; i < orderedRankingEntries.Count; i++) { if (string.Equals(orderedRankingEntries[i].PlayerName, playerName, StringComparison.OrdinalIgnoreCase)) { num2 = i + 1; rankingEntry = orderedRankingEntries[i]; break; } } StringBuilder stringBuilder = new StringBuilder(1400); if (orderedRankingEntries.Count == 0) { stringBuilder.Append("Nenhum jogador pontuado ainda.\n"); stringBuilder.Append("Saia para caçar, evoluir skills e dominar Glitnir."); } else { int num3 = Mathf.Min(num, orderedRankingEntries.Count); for (int j = 0; j < num3; j++) { RankingEntry entry = orderedRankingEntries[j]; string text = BuildHudTopLine(j + 1, entry, playerName); if (stringBuilder.Length + text.Length + 8 > 3000) { break; } if (j > 0) { stringBuilder.Append("\n\n────────────────────────\n\n"); } stringBuilder.Append(text); } } topText = stringBuilder.ToString().TrimEnd(Array.Empty()); StringBuilder stringBuilder2 = new StringBuilder(1800); if (rankingEntry != null) { stringBuilder2.Append("POSIÇÃO #" + num2 + "\n"); stringBuilder2.Append("Pontuação total\n"); stringBuilder2.Append("" + rankingEntry.Points + " pts\n\n"); stringBuilder2.Append("Resumo da jornada\n"); stringBuilder2.Append("• Kills pontuadas: " + rankingEntry.TotalKillsPontuadas + "\n"); stringBuilder2.Append("• Bosses pontuados: " + rankingEntry.TotalBossesPontuadas + "\n"); stringBuilder2.Append("• Níveis de skills pontuados: " + rankingEntry.TotalSkillLevelUpsPontuados + "\n\n"); stringBuilder2.Append("Origem dos pontos\n"); stringBuilder2.Append("• Kills: " + rankingEntry.KillPointsTotal + "\n"); stringBuilder2.Append("• Bosses: " + rankingEntry.BossPointsTotal + "\n"); stringBuilder2.Append("• Skills: " + rankingEntry.SkillPointsTotal + "\n\n"); stringBuilder2.Append("Último registro\n"); stringBuilder2.Append("• Origem: " + (string.IsNullOrWhiteSpace(rankingEntry.LastReason) ? "sem registro" : rankingEntry.LastReason) + "\n"); stringBuilder2.Append("• Atualização: " + FormatHudLastUpdate(rankingEntry.LastUpdateUtc) + ""); } else { stringBuilder2.Append("Você ainda não tem pontos.\n"); stringBuilder2.Append("Participe de combates, derrote bosses e evolua skills para subir no ranking."); } stringBuilder2.Append("\n\n────────────────────────\n\n"); stringBuilder2.Append("Configuração atual\n"); stringBuilder2.Append("• Ranking: " + (_rules.RankingEnabled ? "ON" : "OFF") + "\n"); stringBuilder2.Append("• Kills: " + (_rules.EnableKillPoints ? "ON" : "OFF") + "\n"); stringBuilder2.Append("• Bosses: " + (_rules.EnableBossPoints ? "ON" : "OFF") + "\n"); stringBuilder2.Append("• Skills: " + (_rules.EnableSkillPoints ? "ON" : "OFF") + "\n"); stringBuilder2.Append("• Top exibido: " + _rules.TopCount + ""); playerText = stringBuilder2.ToString().TrimEnd(Array.Empty()); } public void NotifyLocalCraftingStarted(Recipe recipe) { try { if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)recipe == (Object)null) && !((Object)(object)recipe.m_item == (Object)null)) { string prefabName = GetPrefabName(((Component)recipe.m_item).gameObject); prefabName = SafeKey(prefabName); if (!string.IsNullOrWhiteSpace(prefabName)) { string recipeItemSharedName = GetRecipeItemSharedName(recipe); int beforeCount = CountLocalInventoryItemsByPrefabOrSharedName(prefabName, recipeItemSharedName); ((MonoBehaviour)this).StartCoroutine(VerifyCraftCompletedAndReport(recipe, prefabName, recipeItemSharedName, beforeCount)); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao iniciar verificação de craft local: " + ex)); } } public void NotifyLocalCraftedRecipe(Recipe recipe) { NotifyLocalCraftingStarted(recipe); } [IteratorStateMachine(typeof(d__638))] private IEnumerator VerifyCraftCompletedAndReport(Recipe recipe, string prefabName, string sharedName, int beforeCount) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__638(0) { <>4__this = this, recipe = recipe, prefabName = prefabName, sharedName = sharedName, beforeCount = beforeCount }; } private string GetRecipeItemSharedName(Recipe recipe) { try { if ((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null && recipe.m_item.m_itemData != null && recipe.m_item.m_itemData.m_shared != null) { return SafeKey(recipe.m_item.m_itemData.m_shared.m_name); } } catch { } return ""; } private int CountLocalInventoryItemsByPrefabOrSharedName(string prefabName, string sharedName) { try { if ((Object)(object)Player.m_localPlayer == (Object)null) { return 0; } Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory(); if (inventory == null) { return 0; } prefabName = SafeKey(prefabName); sharedName = SafeKey(sharedName); if (string.IsNullOrWhiteSpace(prefabName) && string.IsNullOrWhiteSpace(sharedName)) { return 0; } List allItems = inventory.GetAllItems(); if (allItems == null || allItems.Count == 0) { return 0; } int num = 0; for (int i = 0; i < allItems.Count; i++) { ItemData val = allItems[i]; if (val != null) { string a = SafeKey(GetPrefabNameFromItemData(val)); string a2 = SafeKey(GetSharedNameFromItemData(val)); bool flag = !string.IsNullOrWhiteSpace(prefabName) && string.Equals(a, prefabName, StringComparison.OrdinalIgnoreCase); bool flag2 = !string.IsNullOrWhiteSpace(sharedName) && string.Equals(a2, sharedName, StringComparison.OrdinalIgnoreCase); if (flag || flag2) { num += Mathf.Max(1, val.m_stack); } } } return Mathf.Clamp(num, 0, int.MaxValue); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Falha ao contar item craftado no inventário: " + ex.Message)); return 0; } } private string GetSharedNameFromItemData(ItemData item) { try { if (item != null && item.m_shared != null && !string.IsNullOrWhiteSpace(item.m_shared.m_name)) { return item.m_shared.m_name; } } catch { } return ""; } private string GetPrefabNameFromItemData(ItemData item) { if (item == null) { return ""; } try { FieldInfo field = ((object)item).GetType().GetField("m_dropPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { object? value = field.GetValue(item); GameObject val = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val != (Object)null) { return GetPrefabName(val); } } } catch { } try { if (item.m_shared != null && !string.IsNullOrWhiteSpace(item.m_shared.m_name)) { return SafeKey(item.m_shared.m_name); } } catch { } return ""; } public void NotifyLocalCraftedItem(string prefabName, int amount) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown try { if ((Object)(object)Player.m_localPlayer == (Object)null) { return; } prefabName = SafeKey(prefabName); amount = Mathf.Clamp(amount, 1, 100); if (!string.IsNullOrWhiteSpace(prefabName)) { long serverPeerUid = GetServerPeerUid(); if (serverPeerUid != 0L && _rpcsRegistered && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(prefabName); val.Write(amount); val.Write(SafeLimit(Player.m_localPlayer.GetPlayerName(), 32)); ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerUid, "glitnir.ranking.reportcrafteditem", new object[1] { val }); DebugLog(DebugCategory.Points, "Cliente reportando craft confirmado: player=" + GetLocalPlayerName() + " item=" + prefabName + " amount=" + amount); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao enviar craft local: " + ex)); } } private void RPC_ReportCraftedItem(long sender, ZPackage pkg) { try { if (IsServerInstance() && pkg != null) { string prefabName = SafeKey(pkg.ReadString()); int amount = Mathf.Clamp(pkg.ReadInt(), 1, 100); string text = SanitizePlayerName(pkg.ReadString()); string text2 = ResolvePlayerNameFromSender(sender); if (string.IsNullOrWhiteSpace(text2)) { text2 = text; } if (ShouldIgnoreSenderForRanking(sender, text2)) { DebugLog(DebugCategory.Points, "Craft ignorado para admin: " + text2); } else { ProcessCraftReport(text2, prefabName, amount, "rpc-craft-confirmed"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_ReportCraftedItem: " + ex)); } } private void ProcessCraftReport(string playerName, string prefabName, int amount, string source) { try { if (!IsServerInstance() || _rules == null || !_rules.RankingEnabled) { return; } playerName = SanitizePlayerName(playerName); prefabName = SafeKey(prefabName); amount = Mathf.Clamp(amount, 1, 100); if (string.IsNullOrWhiteSpace(playerName) || string.IsNullOrWhiteSpace(prefabName)) { return; } if (ShouldIgnorePlayerForRanking(playerName)) { DebugLog(DebugCategory.Points, "Ação de ranking ignorada para admin: " + playerName); return; } RankingEntry orCreateEntry = GetOrCreateEntry(playerName); if (orCreateEntry != null) { int value = 0; if (_rules.CraftPoints != null && _rules.CraftPoints.TryGetValue(prefabName, out value) && value > 0) { int num = Mathf.Clamp(value * amount, 0, int.MaxValue); ApplyPointsToEntry(orCreateEntry, num, "Craft: " + prefabName + " x" + amount); orCreateEntry.TotalCraftPontuadas = Mathf.Clamp(orCreateEntry.TotalCraftPontuadas + amount, 0, int.MaxValue); orCreateEntry.CraftPointsTotal = Mathf.Clamp(orCreateEntry.CraftPointsTotal + num, -2147483647, int.MaxValue); IncrementProgressCounter(orCreateEntry, "Craft", prefabName, amount); SaveDatabase(); } JackpotRule value2 = null; bool flag = _rules.UniqueCraftJackpots != null && _rules.UniqueCraftJackpots.TryGetValue(prefabName, out value2) && value2 != null && value2.Points > 0; if (flag) { AddProgressAndCheckJackpot(orCreateEntry, "UniqueCraft", prefabName, amount, value2, "Jackpot craft único: " + prefabName, unique: true); } if (value <= 0 && !flag) { DebugLog(DebugCategory.Points, "Craft sem regra configurada: prefab=" + prefabName + " amount=" + amount + " player=" + playerName); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao processar craft: " + ex)); } } public void NotifyLocalFarmHarvest(string prefabName, int amount) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown try { if ((Object)(object)Player.m_localPlayer == (Object)null) { return; } prefabName = SafeKey(prefabName); amount = Mathf.Clamp(amount, 1, 100); if (!string.IsNullOrWhiteSpace(prefabName)) { long serverPeerUid = GetServerPeerUid(); if (serverPeerUid != 0L && _rpcsRegistered && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(prefabName); val.Write(amount); val.Write(SafeLimit(Player.m_localPlayer.GetPlayerName(), 32)); ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerUid, "glitnir.ranking.reportfarmharvest", new object[1] { val }); DebugLog(DebugCategory.Points, "Cliente reportando colheita: player=" + GetLocalPlayerName() + " item=" + prefabName + " amount=" + amount); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao enviar colheita local: " + ex)); } } private void RPC_ReportFarmHarvest(long sender, ZPackage pkg) { try { if (IsServerInstance() && pkg != null) { string prefabName = SafeKey(pkg.ReadString()); int amount = Mathf.Clamp(pkg.ReadInt(), 1, 100); string text = SanitizePlayerName(pkg.ReadString()); string text2 = ResolvePlayerNameFromSender(sender); if (string.IsNullOrWhiteSpace(text2)) { text2 = text; } if (ShouldIgnoreSenderForRanking(sender, text2)) { DebugLog(DebugCategory.Points, "Cultivo ignorado para admin: " + text2); } else { ProcessFarmHarvestReport(text2, prefabName, amount, "rpc-farm"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_ReportFarmHarvest: " + ex)); } } private void ProcessFarmHarvestReport(string playerName, string prefabName, int amount, string source) { try { if (!IsServerInstance() || _rules == null || !_rules.RankingEnabled) { return; } playerName = SanitizePlayerName(playerName); prefabName = SafeKey(prefabName); amount = Mathf.Clamp(amount, 1, 100); if (string.IsNullOrWhiteSpace(playerName) || string.IsNullOrWhiteSpace(prefabName)) { return; } JackpotRule value; if (ShouldIgnorePlayerForRanking(playerName)) { DebugLog(DebugCategory.Points, "Cultivo ignorado para admin: " + playerName); } else if (_rules.FarmJackpots == null || !_rules.FarmJackpots.TryGetValue(prefabName, out value) || value == null || value.Points <= 0) { DebugLog(DebugCategory.Points, "Colheita sem jackpot configurado: prefab=" + prefabName + " amount=" + amount + " player=" + playerName); } else { RankingEntry orCreateEntry = GetOrCreateEntry(playerName); if (orCreateEntry != null) { AddProgressAndCheckJackpot(orCreateEntry, "Farm", prefabName, amount, value, "Jackpot colheita: " + prefabName, unique: false); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao processar colheita: " + ex)); } } public void NotifyLocalFishCaught(object fishInstance, Player player) { //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown try { if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return; } Component val = (Component)((fishInstance is Component) ? fishInstance : null); if ((Object)(object)val == (Object)null) { return; } string prefabName = GetPrefabName(val.gameObject); if (!IsFishPrefab(prefabName)) { return; } string zdoKey = GetZdoKey(val.gameObject); if (string.IsNullOrWhiteSpace(zdoKey)) { DebugLog(DebugCategory.Points, "Pesca ignorada: peixe sem ZDO real. prefab=" + prefabName); return; } long serverPeerUid = GetServerPeerUid(); if (serverPeerUid != 0L && _rpcsRegistered && ZRoutedRpc.instance != null) { ZPackage val2 = new ZPackage(); val2.Write(SafeKey(zdoKey)); val2.Write(SafeKey(prefabName)); val2.Write(SafeLimit(Player.m_localPlayer.GetPlayerName(), 32)); ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerUid, "glitnir.ranking.reportfishcaught", new object[1] { val2 }); DebugLog(DebugCategory.Points, "Cliente reportando pesca: player=" + GetLocalPlayerName() + " fish=" + prefabName + " zdo=" + zdoKey); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao reportar pesca local: " + ex)); } } public void NotifyLocalFishItemAdded(string fishPrefab, int amount) { try { fishPrefab = SafeKey(fishPrefab); if (IsFishPrefab(fishPrefab)) { DebugLog(DebugCategory.Points, "Peixe em inventário ignorado para evitar exploit: fish=" + fishPrefab + " amount=" + amount); } } catch { } } private bool IsValidFishCatchCreditKey(string zdoKey) { zdoKey = SafeKey(zdoKey); if (string.IsNullOrWhiteSpace(zdoKey)) { return false; } if (zdoKey.StartsWith("inventory-fish:", StringComparison.OrdinalIgnoreCase)) { return false; } if (zdoKey.StartsWith("Fish", StringComparison.OrdinalIgnoreCase) && zdoKey.Split(new char[1] { ':' }).Length >= 3) { return false; } return true; } private void RPC_ReportFishCaught(long sender, ZPackage pkg) { try { if (IsServerInstance() && pkg != null) { string text = SafeKey(pkg.ReadString()); string text2 = SafeKey(pkg.ReadString()); string text3 = SanitizePlayerName(pkg.ReadString()); string text4 = ResolvePlayerNameFromSender(sender); if (string.IsNullOrWhiteSpace(text4)) { text4 = text3; } if (ShouldIgnoreSenderForRanking(sender, text4)) { DebugLog(DebugCategory.Points, "Pesca ignorada para admin: " + text4); } else if (!IsValidFishCatchCreditKey(text)) { DebugLog(DebugCategory.Points, "Pesca rejeitada por chave inválida/forjada: player=" + text4 + " fish=" + text2 + " zdo=" + text); } else { ProcessFishingCatchReport(text4, text2, text, "rpc-fishing"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_ReportFishCaught: " + ex)); } } private void ProcessFishingCatchReport(string playerName, string fishPrefab, string zdoKey, string source) { try { if (!IsServerInstance() || _rules == null || !_rules.RankingEnabled || !_rules.EnableFishingPoints) { return; } playerName = SanitizePlayerName(playerName); fishPrefab = NormalizeFishPrefabName(fishPrefab); zdoKey = SafeKey(zdoKey); if (string.IsNullOrWhiteSpace(playerName) || !IsFishPrefab(fishPrefab)) { return; } if (ShouldIgnorePlayerForRanking(playerName)) { DebugLog(DebugCategory.Points, "Pesca ignorada para admin: " + playerName); } else { if (_rules.FishingPoints == null || !_rules.FishingPoints.TryGetValue(fishPrefab, out var value)) { return; } value = Mathf.Max(0, value); if (value <= 0) { return; } if (HasFishingCatchCredit(zdoKey)) { DebugLog(DebugCategory.Points, "Pesca já creditada: zdo=" + zdoKey + " fish=" + fishPrefab + " source=" + source); return; } RankingEntry orCreateEntry = GetOrCreateEntry(playerName); if (orCreateEntry != null) { ApplyPointsToEntry(orCreateEntry, value, "Pesca: " + fishPrefab); orCreateEntry.TotalFishingPontuadas++; orCreateEntry.FishingPointsTotal = Mathf.Clamp(orCreateEntry.FishingPointsTotal + value, 0, int.MaxValue); IncrementProgressCounter(orCreateEntry, "Fishing", fishPrefab, 1); IncrementProgressCounter(orCreateEntry, "Pesca", fishPrefab, 1); MarkFishingCatchCredit(playerName, fishPrefab, zdoKey); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao processar pontuação de pesca: " + ex)); } } private bool HasFishingCatchCredit(string zdoKey) { return HasFishingCatchCreditCached(zdoKey); } private void MarkFishingCatchCredit(string playerName, string fishPrefab, string zdoKey) { if (_database == null) { _database = new RankingDatabase(); } if (_database.FishingCatchCredits == null) { _database.FishingCatchCredits = new List(); } if (!HasFishingCatchCredit(zdoKey)) { _database.FishingCatchCredits.Add(new FishingCatchCreditRecord { PlayerName = SanitizePlayerName(playerName), FishPrefab = SafeKey(fishPrefab), ZdoKey = SafeKey(zdoKey), GrantedAtUtc = DateTime.UtcNow.ToString("O") }); if (IsServerInstance()) { SaveDatabase(); } } } private string GetAttackerKeyFromHit(HitData hit) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (hit == null) { return ""; } try { ZDOID attacker = hit.m_attacker; if (!((ZDOID)(ref attacker)).IsNone()) { return ((ZDOID)(ref attacker)).UserID + ":" + ((ZDOID)(ref attacker)).ID; } } catch { } try { Character attacker2 = hit.GetAttacker(); if ((Object)(object)attacker2 != (Object)null) { return GetZdoKey(attacker2); } } catch { } return ""; } private void CleanupOldLocalRecentHits() { List list = new List(); foreach (KeyValuePair localRecentHit in _localRecentHits) { if (Time.time - localRecentHit.Value > 15f) { list.Add(localRecentHit.Key); } } foreach (string item in list) { _localRecentHits.Remove(item); } } private bool HasRecentLocalHit(string zdoKey) { if (string.IsNullOrWhiteSpace(zdoKey)) { return false; } if (!_localRecentHits.TryGetValue(zdoKey, out var value)) { return false; } if (Time.time - value > 15f) { _localRecentHits.Remove(zdoKey); return false; } return true; } private void SendKillReportToServer(string zdoKey, string prefabName, string attackerKey, string sourceTag, bool victimTamed) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown try { if (string.IsNullOrWhiteSpace(zdoKey) || string.IsNullOrWhiteSpace(prefabName)) { return; } if (string.IsNullOrWhiteSpace(attackerKey)) { attackerKey = GetLocalPlayerKey(); } if (!string.IsNullOrWhiteSpace(attackerKey)) { long serverPeerUid = GetServerPeerUid(); if (serverPeerUid != 0L && _rpcsRegistered && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(zdoKey); val.Write(prefabName); val.Write(attackerKey); val.Write(victimTamed); ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerUid, "glitnir.ranking.reportkill", new object[1] { val }); DebugLog(DebugCategory.Kill, "Cliente reportando KILL(" + sourceTag + "): player=" + GetLocalPlayerName() + " attackerKey=" + attackerKey + " zdo=" + zdoKey + " prefab=" + prefabName + " tamed=" + victimTamed); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao enviar KILL local do ranking: " + ex)); } } public void NotifyLocalDamage(Character victim, HitData hit, bool postDamage) { //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Expected O, but got Unknown try { if ((Object)(object)victim == (Object)null || hit == null || victim is Player || (_rules.IgnoreTamedKills && IsTamedCharacter(victim)) || (Object)(object)Player.m_localPlayer == (Object)null || !IsLocalPlayerAttackAuthor(hit)) { return; } string zdoKey = GetZdoKey(victim); if (string.IsNullOrWhiteSpace(zdoKey)) { return; } string prefabName = GetPrefabName(((Component)victim).gameObject); if (string.IsNullOrWhiteSpace(prefabName)) { return; } string text = GetAttackerKeyFromHit(hit); if (string.IsNullOrWhiteSpace(text)) { text = GetLocalPlayerKey(); } if (string.IsNullOrWhiteSpace(text)) { return; } long serverPeerUid = GetServerPeerUid(); if (serverPeerUid == 0L || !_rpcsRegistered || ZRoutedRpc.instance == null) { return; } _localRecentHits[zdoKey] = Time.time; ZPackage val = new ZPackage(); val.Write(zdoKey); val.Write(prefabName); val.Write(text); val.Write(IsTamedCharacter(victim)); ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerUid, "glitnir.ranking.reporthit", new object[1] { val }); DebugLog(DebugCategory.Hit, "Cliente reportando HIT: player=" + GetLocalPlayerName() + " attackerKey=" + text + " zdo=" + zdoKey + " prefab=" + prefabName); if (postDamage) { bool flag = false; float num = 1f; try { num = victim.GetHealth(); } catch { } try { flag = victim.IsDead(); } catch { } if ((flag || !(num > 0f)) && HasRecentLocalHit(zdoKey)) { SendKillReportToServer(zdoKey, prefabName, text, "Damage", IsTamedCharacter(victim)); _localRecentHits.Remove(zdoKey); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao reportar dano local do ranking: " + ex)); } } public void NotifyLocalCharacterDeath(Character victim) { try { if ((Object)(object)victim == (Object)null || victim is Player || (_rules.IgnoreTamedKills && IsTamedCharacter(victim)) || (Object)(object)Player.m_localPlayer == (Object)null) { return; } string zdoKey = GetZdoKey(victim); if (string.IsNullOrWhiteSpace(zdoKey) || !HasRecentLocalHit(zdoKey)) { return; } string prefabName = GetPrefabName(((Component)victim).gameObject); if (!string.IsNullOrWhiteSpace(prefabName)) { string localPlayerKey = GetLocalPlayerKey(); if (!string.IsNullOrWhiteSpace(localPlayerKey)) { SendKillReportToServer(zdoKey, prefabName, localPlayerKey, "OnDeath", IsTamedCharacter(victim)); _localRecentHits.Remove(zdoKey); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao reportar morte local do ranking: " + ex)); } } private bool DoesSenderMatchAttackerKey(long sender, string attackerKey) { if (sender == 0L || string.IsNullOrWhiteSpace(attackerKey)) { return false; } int num = attackerKey.IndexOf(':'); string s = ((num >= 0) ? attackerKey.Substring(0, num) : attackerKey); if (!long.TryParse(s, out var result)) { return false; } return result == sender; } private void SchedulePendingKillCheck(string zdoKey, string prefabName) { if (!IsServerInstance()) { return; } zdoKey = SafeKey(zdoKey); prefabName = SafeKey(prefabName); if (string.IsNullOrWhiteSpace(zdoKey)) { return; } if (!_pendingKillChecks.TryGetValue(zdoKey, out var value) || value == null) { value = new PendingKillCheck(); value.PrefabName = prefabName; value.FirstSeenTime = Time.time; value.LastHitTime = Time.time; _pendingKillChecks[zdoKey] = value; } else { value.LastHitTime = Time.time; if (!string.IsNullOrWhiteSpace(prefabName)) { value.PrefabName = prefabName; } } } private bool TryFindCharacterByZdoKey(string zdoKey, out Character found) { found = null; if (string.IsNullOrWhiteSpace(zdoKey)) { return false; } try { Character[] array = Object.FindObjectsByType((FindObjectsSortMode)0); if (array == null || array.Length == 0) { return false; } foreach (Character val in array) { if (!((Object)(object)val == (Object)null) && !(val is Player)) { string zdoKey2 = GetZdoKey(val); if (string.Equals(zdoKey2, zdoKey, StringComparison.Ordinal)) { found = val; return true; } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao localizar Character por zdoKey=" + zdoKey + ": " + ex.Message)); } return false; } private void ProcessPendingKillChecks() { if (!IsServerInstance() || _pendingKillChecks.Count == 0 || Time.time < _serverPendingKillCheckAt) { return; } _serverPendingKillCheckAt = Time.time + 0.25f; List list = new List(); KeyValuePair[] array = _pendingKillChecks.ToArray(); KeyValuePair[] array2 = array; for (int i = 0; i < array2.Length; i++) { KeyValuePair keyValuePair = array2[i]; string key = keyValuePair.Key; PendingKillCheck value = keyValuePair.Value; if (!_pendingKillChecks.ContainsKey(key)) { continue; } if (value == null) { list.Add(key); } else if (_processedKills.ContainsKey(key)) { list.Add(key); } else if (Time.time - value.FirstSeenTime > 12f) { DebugLog(DebugCategory.PendingKill, "Pending kill expirado: zdo=" + key + " prefab=" + value.PrefabName); RemoveDamageCredit(key); list.Add(key); } else { if (Time.time - value.LastHitTime < 0.25f) { continue; } List creditedPlayerNames = GetCreditedPlayerNames(key); Character found; if (creditedPlayerNames.Count == 0) { list.Add(key); } else if (TryFindCharacterByZdoKey(key, out found)) { if (_rules.IgnoreTamedKills && IsTamedCharacter(found)) { MarkTamedKillIgnored(key, "pending-check"); list.Add(key); continue; } bool flag = false; float num = 1f; try { num = found.GetHealth(); } catch { } try { flag = found.IsDead(); } catch { } if (flag || !(num > 0f)) { string text = ((!string.IsNullOrWhiteSpace(value.PrefabName)) ? value.PrefabName : GetPrefabName(((Component)found).gameObject)); DebugLog(DebugCategory.PendingKill, "Pending kill confirmado por estado morto: zdo=" + key + " prefab=" + text + " credited=" + string.Join(",", creditedPlayerNames.ToArray())); ProcessKillReportMulti(creditedPlayerNames, key, text, "pending-dead"); list.Add(key); } } else { DebugLog(DebugCategory.PendingKill, "Pending kill confirmado por alvo ausente: zdo=" + key + " prefab=" + value.PrefabName + " credited=" + string.Join(",", creditedPlayerNames.ToArray())); ProcessKillReportMulti(creditedPlayerNames, key, value.PrefabName, "pending-missing"); list.Add(key); } } } foreach (string item in list.Distinct(StringComparer.Ordinal)) { _pendingKillChecks.Remove(item); } } private void RPC_ReportHit(long sender, ZPackage pkg) { try { if (!IsServerInstance() || pkg == null) { return; } string text = SafeKey(pkg.ReadString()); string text2 = SafeKey(pkg.ReadString()); string text3 = SafeKey(pkg.ReadString()); bool flag = false; try { flag = pkg.ReadBool(); } catch { } string text4 = ResolvePlayerNameFromSender(sender); DebugLog(DebugCategory.Hit, "Servidor recebeu HIT: sender=" + sender + " reporter=" + text4 + " attackerKey=" + text3 + " zdo=" + text + " prefab=" + text2); if (!string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(text2) && !string.IsNullOrWhiteSpace(text4)) { if (ShouldIgnoreSenderForRanking(sender, text4)) { DebugLog(DebugCategory.Hit, "HIT ignorado para admin: sender=" + sender + " reporter=" + text4); } else if (!DoesSenderMatchAttackerKey(sender, text3)) { DebugLog(DebugCategory.Hit, "HIT rejeitado por mismatch: sender=" + sender + " reporter=" + text4 + " attackerKey=" + text3); } else if (_rules.IgnoreTamedKills && flag) { MarkTamedKillIgnored(text, "rpc-hit-client-flag"); } else if (!ShouldIgnoreTamedKill(text, "rpc-hit")) { RecordDamageCredit(text, text4); SchedulePendingKillCheck(text, text2); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_ReportHit: " + ex)); } } private void RPC_ReportKill(long sender, ZPackage pkg) { try { if (!IsServerInstance() || pkg == null) { return; } string text = SafeKey(pkg.ReadString()); string text2 = SafeKey(pkg.ReadString()); string text3 = SafeKey(pkg.ReadString()); bool flag = false; try { flag = pkg.ReadBool(); } catch { } string text4 = ResolvePlayerNameFromSender(sender); DebugLog(DebugCategory.Kill, "Servidor recebeu KILL: sender=" + sender + " reporter=" + text4 + " attackerKey=" + text3 + " zdo=" + text + " prefab=" + text2); if (string.IsNullOrWhiteSpace(text) || string.IsNullOrWhiteSpace(text2) || string.IsNullOrWhiteSpace(text4)) { return; } if (ShouldIgnoreSenderForRanking(sender, text4)) { DebugLog(DebugCategory.Kill, "KILL ignorado para admin: sender=" + sender + " reporter=" + text4); } else if (!DoesSenderMatchAttackerKey(sender, text3)) { DebugLog(DebugCategory.Kill, "KILL rejeitado por mismatch: sender=" + sender + " reporter=" + text4 + " attackerKey=" + text3); } else if (_rules.IgnoreTamedKills && flag) { MarkTamedKillIgnored(text, "rpc-kill-client-flag"); } else if (!ShouldIgnoreTamedKill(text, "rpc-kill")) { List creditedPlayerNames = GetCreditedPlayerNames(text); if (creditedPlayerNames.Count == 0) { creditedPlayerNames.Add(text4); } ProcessKillReportMulti(creditedPlayerNames, text, text2, "rpc-kill"); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro no RPC_ReportKill: " + ex)); } } private string ResolvePlayerNameFromSender(long sender) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)ZNet.instance != (Object)null) { ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer != null && !string.IsNullOrWhiteSpace(peer.m_playerName)) { return SanitizePlayerName(peer.m_playerName); } List playerList = ZNet.instance.GetPlayerList(); if (playerList != null) { foreach (PlayerInfo item in playerList) { try { ZDOID characterID = item.m_characterID; long num = Convert.ToInt64(((ZDOID)(ref characterID)).UserID); if (num == sender && !string.IsNullOrWhiteSpace(item.m_name)) { return SanitizePlayerName(item.m_name); } } catch { } } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao resolver player por sender=" + sender + ": " + ex.Message)); } return ""; } private bool ShouldIgnoreSenderForRanking(long sender, string playerName) { try { if (_cfgIgnoreAdminsInRanking == null || !_cfgIgnoreAdminsInRanking.Value) { return false; } if (ShouldIgnorePlayerForRanking(playerName)) { return true; } if (sender != 0L && IsSenderDetectedInAdminList(sender)) { return true; } } catch { } return false; } private bool ShouldIgnorePlayerForRanking(string playerName) { if (_cfgIgnoreAdminsInRanking == null || !_cfgIgnoreAdminsInRanking.Value) { return false; } string text = SanitizePlayerName(playerName); if (string.IsNullOrWhiteSpace(text)) { return false; } if (IsNameOrIdInIgnoredAdminConfig(text)) { return true; } HashSet allKnownAdminIdentifiers = GetAllKnownAdminIdentifiers(); if (allKnownAdminIdentifiers.Contains(text)) { return true; } return false; } private bool IsNameOrIdInIgnoredAdminConfig(string value) { try { if (_cfgIgnoredAdminNames == null || string.IsNullOrWhiteSpace(_cfgIgnoredAdminNames.Value)) { return false; } string b = SanitizePlayerName(value); string[] array = _cfgIgnoredAdminNames.Value.Split(new char[5] { ',', ';', '\n', '\r', '|' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = SanitizePlayerName(array[i]); if (!string.IsNullOrWhiteSpace(text) && string.Equals(text, b, StringComparison.OrdinalIgnoreCase)) { return true; } } } catch { } return false; } private bool IsSenderDetectedInAdminList(long sender) { try { HashSet allKnownAdminIdentifiers = GetAllKnownAdminIdentifiers(); if (allKnownAdminIdentifiers == null || allKnownAdminIdentifiers.Count == 0) { return false; } string item = sender.ToString(); if (allKnownAdminIdentifiers.Contains(item)) { return true; } if ((Object)(object)ZNet.instance != (Object)null) { ZNetPeer peer = ZNet.instance.GetPeer(sender); HashSet possiblePeerIdentifiers = GetPossiblePeerIdentifiers(peer); foreach (string item2 in possiblePeerIdentifiers) { if (!string.IsNullOrWhiteSpace(item2) && allKnownAdminIdentifiers.Contains(item2.Trim())) { return true; } } } } catch { } return false; } private HashSet GetAllKnownAdminIdentifiers() { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); try { if ((Object)(object)ZNet.instance != (Object)null) { HashSet adminIdentifiersFromZNet = GetAdminIdentifiersFromZNet(ZNet.instance); foreach (string item in adminIdentifiersFromZNet) { if (!string.IsNullOrWhiteSpace(item)) { hashSet.Add(item.Trim()); } } } } catch { } try { foreach (string item2 in ReadAdminIdentifiersFromKnownFiles()) { if (!string.IsNullOrWhiteSpace(item2)) { hashSet.Add(item2.Trim()); } } } catch { } return hashSet; } private HashSet ReadAdminIdentifiersFromKnownFiles() { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); string[] array = new string[6] { Path.Combine(Paths.ConfigPath, "adminlist.txt"), Path.Combine(Paths.ConfigPath, "adminlist"), Path.Combine(Application.persistentDataPath, "adminlist.txt"), Path.Combine(Application.persistentDataPath, "adminlist"), Path.Combine(Directory.GetCurrentDirectory(), "adminlist.txt"), Path.Combine(Directory.GetCurrentDirectory(), "adminlist") }; foreach (string text in array) { try { if (string.IsNullOrWhiteSpace(text) || !File.Exists(text)) { continue; } string[] array2 = File.ReadAllLines(text, Encoding.UTF8); foreach (string text2 in array2) { string text3 = ((text2 != null) ? text2.Trim() : ""); if (!string.IsNullOrWhiteSpace(text3) && !text3.StartsWith("#") && !text3.StartsWith("//")) { int num = text3.IndexOf('#'); if (num >= 0) { text3 = text3.Substring(0, num).Trim(); } if (!string.IsNullOrWhiteSpace(text3)) { hashSet.Add(text3); } } } } catch { } } return hashSet; } private HashSet GetAdminIdentifiersFromZNet(object znet) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); try { if (znet == null) { return hashSet; } Type type = znet.GetType(); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; FieldInfo[] fields = type.GetFields(bindingAttr); foreach (FieldInfo fieldInfo in fields) { if (!(fieldInfo == null) && !string.IsNullOrWhiteSpace(fieldInfo.Name) && fieldInfo.Name.IndexOf("admin", StringComparison.OrdinalIgnoreCase) >= 0) { object value = fieldInfo.GetValue(znet); AddStringsFromObject(value, hashSet); } } PropertyInfo[] properties = type.GetProperties(bindingAttr); foreach (PropertyInfo propertyInfo in properties) { if (!(propertyInfo == null) && !string.IsNullOrWhiteSpace(propertyInfo.Name) && propertyInfo.GetIndexParameters().Length == 0 && propertyInfo.Name.IndexOf("admin", StringComparison.OrdinalIgnoreCase) >= 0) { try { object value2 = propertyInfo.GetValue(znet, null); AddStringsFromObject(value2, hashSet); } catch { } } } } catch { } return hashSet; } private void AddStringsFromObject(object value, HashSet output) { if (value == null || output == null) { return; } try { string text = value as string; if (!string.IsNullOrWhiteSpace(text)) { output.Add(text.Trim()); } else { if (!(value is IEnumerable enumerable)) { return; } { foreach (object item in enumerable) { if (item == null) { continue; } string text2 = item as string; if (!string.IsNullOrWhiteSpace(text2)) { output.Add(text2.Trim()); continue; } string text3 = item.ToString(); if (!string.IsNullOrWhiteSpace(text3)) { output.Add(text3.Trim()); } } return; } } } catch { } } private HashSet GetPossiblePeerIdentifiers(object peer) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); try { if (peer == null) { return hashSet; } Type type = peer.GetType(); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; string[] array = new string[16] { "m_uid", "uid", "UID", "m_userId", "userId", "UserId", "m_userID", "UserID", "m_steamID", "steamID", "SteamID", "m_hostName", "hostName", "HostName", "m_socketHost", "socketHost" }; for (int i = 0; i < array.Length; i++) { string stringMemberByNames = GetStringMemberByNames(peer, array[i]); if (!string.IsNullOrWhiteSpace(stringMemberByNames)) { hashSet.Add(stringMemberByNames.Trim()); } } FieldInfo[] fields = type.GetFields(bindingAttr); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo == null || string.IsNullOrWhiteSpace(fieldInfo.Name)) { continue; } string name = fieldInfo.Name; if (name.IndexOf("uid", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("id", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("host", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("steam", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("playfab", StringComparison.OrdinalIgnoreCase) >= 0) { object value = fieldInfo.GetValue(peer); if (value != null && !string.IsNullOrWhiteSpace(value.ToString())) { hashSet.Add(value.ToString().Trim()); } } } } catch { } return hashSet; } private string GetStringMemberByNames(object obj, params string[] names) { if (obj == null || names == null) { return null; } try { Type type = obj.GetType(); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; for (int i = 0; i < names.Length; i++) { FieldInfo field = type.GetField(names[i], bindingAttr); if (field != null) { object value = field.GetValue(obj); if (value != null) { return value.ToString(); } } PropertyInfo property = type.GetProperty(names[i], bindingAttr); if (property != null && property.GetIndexParameters().Length == 0) { object value2 = property.GetValue(obj, null); if (value2 != null) { return value2.ToString(); } } } } catch { } return null; } private bool IsTamedCharacter(Character character) { try { if ((Object)(object)character == (Object)null) { return false; } try { if (character.IsTamed()) { return true; } } catch { } try { ZNetView component = ((Component)character).GetComponent(); if ((Object)(object)component != (Object)null && component.GetZDO() != null && component.GetZDO().GetBool("tamed", false)) { return true; } } catch { } try { Tameable component2 = ((Component)character).GetComponent(); if ((Object)(object)component2 != (Object)null) { ZNetView component3 = ((Component)component2).GetComponent(); if ((Object)(object)component3 != (Object)null && component3.GetZDO() != null && component3.GetZDO().GetBool("tamed", false)) { return true; } } } catch { } return false; } catch { return false; } } private void MarkTamedKillIgnored(string zdoKey, string source) { zdoKey = SafeKey(zdoKey); if (!string.IsNullOrWhiteSpace(zdoKey)) { _ignoredTamedKills[zdoKey] = Time.time; _processedKills[zdoKey] = Time.time; RemoveDamageCredit(zdoKey); DebugLog(DebugCategory.Kill, "Kill ignorado por criatura domada: source=" + source + " zdo=" + zdoKey); } } private bool ShouldIgnoreTamedKill(string zdoKey, string source) { if (_rules == null || !_rules.IgnoreTamedKills) { return false; } zdoKey = SafeKey(zdoKey); if (string.IsNullOrWhiteSpace(zdoKey)) { return false; } if (_ignoredTamedKills.ContainsKey(zdoKey)) { return true; } if (TryFindCharacterByZdoKey(zdoKey, out var found) && IsTamedCharacter(found)) { MarkTamedKillIgnored(zdoKey, source); return true; } return false; } private bool TryNormalizeBossPrefabName(string rawName, out string prefabName) { prefabName = ""; string text = SafeKey(rawName); if (string.IsNullOrWhiteSpace(text)) { return false; } if (BossPortugueseToPrefab.TryGetValue(text, out prefabName)) { return true; } if (BossDisplayNamesPtBr.ContainsKey(text)) { prefabName = text; return true; } return false; } private string NormalizeBossPrefabName(string rawName) { string prefabName; return TryNormalizeBossPrefabName(rawName, out prefabName) ? prefabName : SafeKey(rawName); } private bool TryNormalizeFishPrefabName(string rawName, out string prefabName) { prefabName = ""; string text = SafeKey(rawName); if (string.IsNullOrWhiteSpace(text)) { return false; } if (FishPortugueseToPrefab.TryGetValue(text, out prefabName)) { return true; } if (FishDisplayNamesPtBr.ContainsKey(text)) { prefabName = text; return true; } if (text.StartsWith("Fish", StringComparison.OrdinalIgnoreCase)) { prefabName = text; return true; } return false; } private string NormalizeFishPrefabName(string rawName) { string prefabName; return TryNormalizeFishPrefabName(rawName, out prefabName) ? prefabName : SafeKey(rawName); } private bool IsFishPrefab(string prefabName) { string prefabName2; return TryNormalizeFishPrefabName(prefabName, out prefabName2); } public void NotifyServerCharacterDeath(Character victim) { try { if (!IsServerInstance() || (Object)(object)victim == (Object)null || victim is Player) { return; } string zdoKey = GetZdoKey(victim); string prefabName = GetPrefabName(((Component)victim).gameObject); List creditedPlayerNames = GetCreditedPlayerNames(zdoKey); DebugLog(DebugCategory.Kill, "OnDeath servidor: zdo=" + zdoKey + " prefab=" + prefabName + " credited=" + string.Join(",", creditedPlayerNames.ToArray())); if (!string.IsNullOrWhiteSpace(zdoKey) && !string.IsNullOrWhiteSpace(prefabName) && creditedPlayerNames.Count != 0) { if (IsFishPrefab(prefabName)) { DebugLog(DebugCategory.Kill, "Kill de peixe ignorado: source=ondeath zdo=" + zdoKey + " prefab=" + prefabName); } else if (_rules.IgnoreTamedKills && IsTamedCharacter(victim)) { MarkTamedKillIgnored(zdoKey, "ondeath"); } else { ProcessKillReportMulti(creditedPlayerNames, zdoKey, prefabName, "ondeath"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao processar OnDeath do ranking: " + ex)); } } private bool IsConfiguredCombatPointPrefab(string prefabName) { prefabName = SafeKey(prefabName); if (string.IsNullOrWhiteSpace(prefabName) || _rules == null) { return false; } string key = NormalizeBossPrefabName(prefabName); if (_rules.EnableBossPoints && _rules.BossPoints != null && _rules.BossPoints.ContainsKey(key)) { return true; } if (_rules.EnableKillPoints && _rules.KillPoints != null && _rules.KillPoints.ContainsKey(prefabName)) { return true; } return false; } private void ProcessKillReportMulti(List playerNames, string zdoKey, string prefabName, string source) { try { if (!IsServerInstance() || !_rules.RankingEnabled) { return; } zdoKey = SafeKey(zdoKey); prefabName = SafeKey(prefabName); if (string.IsNullOrWhiteSpace(zdoKey) || string.IsNullOrWhiteSpace(prefabName) || playerNames == null || playerNames.Count == 0) { return; } if (IsFishPrefab(prefabName)) { DebugLog(DebugCategory.Kill, "Kill de peixe ignorado: source=" + source + " zdo=" + zdoKey + " prefab=" + prefabName); RemoveDamageCredit(zdoKey); return; } if (!IsConfiguredCombatPointPrefab(prefabName)) { DebugLog(DebugCategory.Kill, "Kill ignorado porque o prefab nao esta configurado em KillPoints/BossPoints: source=" + source + " zdo=" + zdoKey + " prefab=" + prefabName); RemoveDamageCredit(zdoKey); return; } CleanupOldProcessedKills(); if (ShouldIgnoreTamedKill(zdoKey, source)) { return; } if (_processedKills.ContainsKey(zdoKey)) { DebugLog(DebugCategory.Kill, "Kill duplicado ignorado: source=" + source + " zdo=" + zdoKey + " prefab=" + prefabName); return; } _processedKills[zdoKey] = Time.time; foreach (string item in playerNames.Distinct(StringComparer.OrdinalIgnoreCase)) { string text = SanitizePlayerName(item); if (string.IsNullOrWhiteSpace(text)) { continue; } RankingEntry orCreateEntry = GetOrCreateEntry(text); string text2 = NormalizeBossPrefabName(prefabName); if (_rules.EnableBossPoints && _rules.BossPoints.TryGetValue(text2, out var value)) { bool flag = orCreateEntry.BossCredits != null && orCreateEntry.BossCredits.Contains(text2); orCreateEntry.TotalBossesPontuadas++; IncrementProgressCounter(orCreateEntry, "Bosses", text2, 1); if (!_rules.AllowRepeatedBossPoints && flag) { DebugLog(DebugCategory.Kill, "Boss repetido sem pontos, progresso contado: player=" + text + " boss=" + prefabName); SaveDatabase(); continue; } orCreateEntry.BossPointsTotal = Mathf.Clamp(orCreateEntry.BossPointsTotal + value, -2147483647, int.MaxValue); if (orCreateEntry.BossCredits == null) { orCreateEntry.BossCredits = new HashSet(StringComparer.OrdinalIgnoreCase); } orCreateEntry.BossCredits.Add(text2); string reason = "Boss: " + prefabName; ApplyPointsToEntry(orCreateEntry, value, reason); } else if (_rules.EnableKillPoints && _rules.KillPoints.TryGetValue(prefabName, out value)) { orCreateEntry.TotalKillsPontuadas++; orCreateEntry.KillPointsTotal = Mathf.Clamp(orCreateEntry.KillPointsTotal + value, -2147483647, int.MaxValue); IncrementProgressCounter(orCreateEntry, "Kill", prefabName, 1); IncrementProgressCounterAlias(orCreateEntry, "Combat", prefabName, 1); IncrementProgressCounterAlias(orCreateEntry, "Combate", prefabName, 1); string reason = "Kill: " + prefabName; ApplyPointsToEntry(orCreateEntry, value, reason); } else { DebugLog(DebugCategory.Kill, "Kill sem regra de pontos: player=" + text + " prefab=" + prefabName); } } RemoveDamageCredit(zdoKey); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao processar kill múltiplo reportado: " + ex)); } } private void RecordDamageCredit(string zdoKey, string playerName) { if (!IsServerInstance()) { return; } zdoKey = SafeKey(zdoKey); playerName = SanitizePlayerName(playerName); if (string.IsNullOrWhiteSpace(zdoKey) || string.IsNullOrWhiteSpace(playerName)) { return; } if (ShouldIgnorePlayerForRanking(playerName)) { DebugLog(DebugCategory.Hit, "Crédito de dano ignorado para admin: " + playerName); return; } if (!_damageCredits.TryGetValue(zdoKey, out var value) || value == null) { value = new Dictionary(StringComparer.OrdinalIgnoreCase); _damageCredits[zdoKey] = value; } value[playerName] = Time.time; } private List GetCreditedPlayerNames(string zdoKey) { List list = new List(); if (string.IsNullOrWhiteSpace(zdoKey)) { return list; } if (!_damageCredits.TryGetValue(zdoKey, out var value) || value == null) { return list; } foreach (KeyValuePair item in value) { if (Time.time - item.Value <= 180f) { string text = SanitizePlayerName(item.Key); if (!ShouldIgnorePlayerForRanking(text)) { list.Add(text); } } } return list.Where((string x) => !string.IsNullOrWhiteSpace(x)).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); } private void CleanupOldDamageCredits() { List list = new List(); foreach (KeyValuePair> damageCredit in _damageCredits) { Dictionary value = damageCredit.Value; if (value == null) { list.Add(damageCredit.Key); continue; } List list2 = new List(); foreach (KeyValuePair item in value) { if (Time.time - item.Value > 180f) { list2.Add(item.Key); } } foreach (string item2 in list2) { value.Remove(item2); } if (value.Count == 0) { list.Add(damageCredit.Key); } } foreach (string item3 in list) { _damageCredits.Remove(item3); } } private void CleanupOldProcessedKills() { List list = new List(); foreach (KeyValuePair processedKill in _processedKills) { if (Time.time - processedKill.Value > 180f) { list.Add(processedKill.Key); } } foreach (string item in list) { _processedKills.Remove(item); } list.Clear(); foreach (KeyValuePair ignoredTamedKill in _ignoredTamedKills) { if (Time.time - ignoredTamedKill.Value > 180f) { list.Add(ignoredTamedKill.Key); } } foreach (string item2 in list) { _ignoredTamedKills.Remove(item2); } } private void RemoveDamageCredit(string zdoKey) { if (!string.IsNullOrWhiteSpace(zdoKey)) { _damageCredits.Remove(zdoKey); _pendingKillChecks.Remove(zdoKey); } } private void RebuildDatabaseIndexes() { _entriesByPlayerName.Clear(); _marketplaceQuestCreditKeys.Clear(); _fishingCatchCreditKeys.Clear(); _marketplaceQuestCreditsByPlayer.Clear(); _fishingCreditsByPlayer.Clear(); if (_database == null || _database.Entries == null) { _orderedRankingCache = new List(); _rankingSnapshotCache = new Dictionary(StringComparer.OrdinalIgnoreCase); _rankingCacheDirty = false; return; } foreach (RankingEntry entry in _database.Entries) { if (entry != null && !string.IsNullOrWhiteSpace(entry.PlayerName)) { string key = (entry.PlayerName = SanitizePlayerName(entry.PlayerName)); if (!_entriesByPlayerName.ContainsKey(key)) { _entriesByPlayerName.Add(key, entry); } } } if (_database.MarketplaceQuestCredits != null) { foreach (MarketplaceQuestCreditRecord marketplaceQuestCredit in _database.MarketplaceQuestCredits) { if (marketplaceQuestCredit != null) { string text = BuildMarketplaceQuestCreditKey(marketplaceQuestCredit.PlayerName, marketplaceQuestCredit.QuestKey); if (!string.IsNullOrWhiteSpace(text)) { _marketplaceQuestCreditKeys.Add(text); } string key2 = SanitizePlayerName(marketplaceQuestCredit.PlayerName); if (!_marketplaceQuestCreditsByPlayer.TryGetValue(key2, out var value)) { value = new List(); _marketplaceQuestCreditsByPlayer[key2] = value; } value.Add(marketplaceQuestCredit); } } } if (_database.FishingCatchCredits != null) { foreach (FishingCatchCreditRecord fishingCatchCredit in _database.FishingCatchCredits) { if (fishingCatchCredit != null) { string text2 = SafeKey(fishingCatchCredit.ZdoKey); if (!string.IsNullOrWhiteSpace(text2)) { _fishingCatchCreditKeys.Add(text2); } string key3 = SanitizePlayerName(fishingCatchCredit.PlayerName); if (!_fishingCreditsByPlayer.TryGetValue(key3, out var value2)) { value2 = new List(); _fishingCreditsByPlayer[key3] = value2; } value2.Add(fishingCatchCredit); } } } _rankingCacheDirty = true; } private void RegisterRankingEntry(RankingEntry entry) { if (entry != null && !string.IsNullOrWhiteSpace(entry.PlayerName)) { string key = (entry.PlayerName = SanitizePlayerName(entry.PlayerName)); _entriesByPlayerName[key] = entry; MarkRankingCacheDirty(); } } private void MarkRankingCacheDirty() { _rankingCacheDirty = true; } private IReadOnlyList GetOrderedRankingEntries() { if (!_rankingCacheDirty) { return _orderedRankingCache; } if (_database == null || _database.Entries == null) { _orderedRankingCache = new List(); _rankingSnapshotCache = new Dictionary(StringComparer.OrdinalIgnoreCase); _rankingCacheDirty = false; return _orderedRankingCache; } _orderedRankingCache = (from x in _database.Entries where x != null && !string.IsNullOrWhiteSpace(x.PlayerName) && !ShouldIgnorePlayerForRanking(x.PlayerName) orderby x.Points descending, x.BossPointsTotal descending, x.KillPointsTotal descending, x.SkillPointsTotal descending, x.PlayerName select x).ToList(); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < _orderedRankingCache.Count; i++) { RankingEntry rankingEntry = _orderedRankingCache[i]; if (rankingEntry != null && !string.IsNullOrWhiteSpace(rankingEntry.PlayerName)) { dictionary[rankingEntry.PlayerName] = i + 1; } } _rankingSnapshotCache = dictionary; _rankingCacheDirty = false; return _orderedRankingCache; } private bool HasMarketplaceQuestCreditCached(string playerName, string questKey) { if (_database == null || _database.MarketplaceQuestCredits == null) { return false; } string item = BuildMarketplaceQuestCreditKey(playerName, questKey); if (_marketplaceQuestCreditKeys.Contains(item)) { return true; } RebuildDatabaseIndexes(); return _marketplaceQuestCreditKeys.Contains(item); } private bool HasFishingCatchCreditCached(string zdoKey) { if (string.IsNullOrWhiteSpace(zdoKey) || _database == null || _database.FishingCatchCredits == null) { return false; } string item = SafeKey(zdoKey); if (_fishingCatchCreditKeys.Contains(item)) { return true; } RebuildDatabaseIndexes(); return _fishingCatchCreditKeys.Contains(item); } private List GetMarketplaceQuestCreditsForPlayer(string playerName) { string key = SanitizePlayerName(playerName); if (_marketplaceQuestCreditsByPlayer.TryGetValue(key, out var value)) { return value; } RebuildDatabaseIndexes(); return _marketplaceQuestCreditsByPlayer.TryGetValue(key, out value) ? value : new List(); } private List GetFishingCreditsForPlayer(string playerName) { string key = SanitizePlayerName(playerName); if (_fishingCreditsByPlayer.TryGetValue(key, out var value)) { return value; } RebuildDatabaseIndexes(); return _fishingCreditsByPlayer.TryGetValue(key, out value) ? value : new List(); } private Dictionary GetRankingSnapshotFromCache() { GetOrderedRankingEntries(); return new Dictionary(_rankingSnapshotCache, StringComparer.OrdinalIgnoreCase); } } [HarmonyPatch] internal static class RankingPatches { [HarmonyPostfix] [HarmonyPatch(typeof(Character), "ApplyDamage")] private static void Character_ApplyDamage_Postfix(Character __instance, HitData hit) { if (!((Object)(object)GlitnirRankingPlugin.Instance == (Object)null)) { GlitnirRankingPlugin.Instance.NotifyLocalDamage(__instance, hit, postDamage: false); } } [HarmonyPatch(typeof(Character), "Damage")] [HarmonyPostfix] private static void Character_Damage_Postfix(Character __instance, HitData hit) { if (!((Object)(object)GlitnirRankingPlugin.Instance == (Object)null)) { GlitnirRankingPlugin.Instance.NotifyLocalDamage(__instance, hit, postDamage: true); } } [HarmonyPostfix] [HarmonyPatch(typeof(Character), "OnDeath")] private static void Character_OnDeath_Postfix(Character __instance) { if (!((Object)(object)GlitnirRankingPlugin.Instance == (Object)null)) { GlitnirRankingPlugin.Instance.NotifyLocalCharacterDeath(__instance); GlitnirRankingPlugin.Instance.NotifyServerCharacterDeath(__instance); } } [HarmonyPrefix] [HarmonyPatch(typeof(Skills), "RaiseSkill")] private static void Skills_RaiseSkill_Prefix(Skills __instance, SkillType skillType, ref float __state) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)GlitnirRankingPlugin.Instance == (Object)null)) { __state = GlitnirRankingPlugin.Instance.CaptureLocalSkillLevelBeforeRaise(__instance, skillType); } } [HarmonyPostfix] [HarmonyPatch(typeof(Skills), "RaiseSkill")] private static void Skills_RaiseSkill_Postfix(Skills __instance, SkillType skillType, float __state) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)GlitnirRankingPlugin.Instance == (Object)null)) { GlitnirRankingPlugin.Instance.NotifyLocalSkillGain(__instance, skillType, __state); } } } [HarmonyPatch] internal static class CraftRankingPatches { [CompilerGenerated] private sealed class d__0 : IEnumerable, IEnumerable, IEnumerator, IDisposable, IEnumerator { private int <>1__state; private MethodBase <>2__current; private int <>l__initialThreadId; private Type 5__1; private MethodInfo[] <>s__2; private int <>s__3; private MethodInfo 5__4; MethodBase IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__0(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { 5__1 = null; <>s__2 = null; 5__4 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_00ac; } <>1__state = -1; 5__1 = AccessTools.TypeByName("InventoryGui"); if (5__1 == null) { return false; } <>s__2 = 5__1.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); <>s__3 = 0; goto IL_00c2; IL_00ac: 5__4 = null; <>s__3++; goto IL_00c2; IL_00c2: if (<>s__3 < <>s__2.Length) { 5__4 = <>s__2[<>s__3]; if (string.Equals(5__4.Name, "DoCrafting", StringComparison.OrdinalIgnoreCase)) { <>2__current = 5__4; <>1__state = 1; return true; } goto IL_00ac; } <>s__2 = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new d__0(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [HarmonyTargetMethods] [IteratorStateMachine(typeof(d__0))] private static IEnumerable TargetMethods() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__0(-2); } [HarmonyPostfix] private static void Postfix(object __instance, object[] __args) { try { if (!((Object)(object)GlitnirRankingPlugin.Instance == (Object)null)) { Recipe val = ExtractRecipe(__args); if ((Object)(object)val == (Object)null && __instance != null) { val = ExtractRecipeFromInventoryGui(__instance); } if (!((Object)(object)val == (Object)null)) { GlitnirRankingPlugin.Instance.NotifyLocalCraftingStarted(val); } } } catch (Exception ex) { GlitnirRankingPlugin.Log.LogError((object)("Erro no patch de craft: " + ex)); } } private static Recipe ExtractRecipe(object[] args) { if (args == null) { return null; } foreach (object obj in args) { Recipe val = (Recipe)((obj is Recipe) ? obj : null); if ((Object)(object)val != (Object)null) { return val; } } return null; } private static Recipe ExtractRecipeFromInventoryGui(object inventoryGui) { try { FieldInfo field = inventoryGui.GetType().GetField("m_craftRecipe", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { object? value = field.GetValue(inventoryGui); return (Recipe)((value is Recipe) ? value : null); } } catch { } return null; } } [HarmonyPatch] internal static class MarketplaceQuestRankingPatches { [HarmonyTargetMethod] private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("Marketplace.Modules.Quests.Quests_DataTypes+Quest"); if (type == null) { return null; } return AccessTools.Method(type, "RemoveQuestComplete", new Type[1] { typeof(int) }, (Type[])null); } [HarmonyPostfix] private static void Postfix(int UID) { if (!((Object)(object)GlitnirRankingPlugin.Instance == (Object)null)) { GlitnirRankingPlugin.Instance.ReportLocalMarketplaceQuestCompletion(UID); } } } [Serializable] [HarmonyPatch(typeof(Player), "OnDeath")] public static class GlitnirRankingPlayerDeathPatch { private static float _lastLocalDeathReportAt; private static void Postfix(Player __instance) { try { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !(Time.realtimeSinceStartup - _lastLocalDeathReportAt < 2f)) { _lastLocalDeathReportAt = Time.realtimeSinceStartup; GlitnirRankingPlugin.Instance?.ReportLocalPlayerDeathToServer(); } } catch (Exception ex) { ManualLogSource log = GlitnirRankingPlugin.Log; if (log != null) { log.LogWarning((object)("[Ranking] Falha ao reportar morte do jogador: " + ex.Message)); } } } } public class MarketplaceQuestPointRule { public string QuestKey = ""; public int Points = 0; } [Serializable] public class MarketplaceQuestCreditRecord { public string PlayerName = ""; public string QuestKey = ""; public string GrantedAtUtc = ""; } [Serializable] public class RewardClaimRecord { public string CycleId = ""; public string PlayerName = ""; public int Rank = 0; public string ClaimedAtUtc = ""; public string Status = "claimed"; } public class RankingEntryDocument { [BsonId] public string PlayerName { get; set; } public int Points { get; set; } public string LastReason { get; set; } public string LastUpdateUtc { get; set; } public int TotalKillsPontuadas { get; set; } public int TotalBossesPontuadas { get; set; } public int KillPointsTotal { get; set; } public int BossPointsTotal { get; set; } public int TotalSkillLevelUpsPontuados { get; set; } public int SkillPointsTotal { get; set; } public int TotalFishingPontuadas { get; set; } public int FishingPointsTotal { get; set; } public int TotalCraftPontuadas { get; set; } public int CraftPointsTotal { get; set; } public int TotalFarmJackpotsPontuados { get; set; } public int FarmJackpotPointsTotal { get; set; } public int TotalUniqueCraftJackpotsPontuados { get; set; } public int UniqueCraftJackpotPointsTotal { get; set; } public int TotalDeaths { get; set; } public int DeathPenaltyPointsTotal { get; set; } public int TotalPointsExchanges { get; set; } public int PointsExchangePenaltyTotal { get; set; } public int PointsExchangeCoinsTotal { get; set; } public int ExplorationMapJackpotPointsTotal { get; set; } public string ProgressCounters { get; set; } public string FloatProgressCounters { get; set; } public string GenericJackpotCredits { get; set; } public string BossCredits { get; set; } public string SkillLevel100JackpotCredits { get; set; } } public class RewardClaimDocument { [BsonId] public string Key { get; set; } public string CycleId { get; set; } public string PlayerName { get; set; } public int Rank { get; set; } public string ClaimedAtUtc { get; set; } public string Status { get; set; } } public class MarketplaceQuestCreditDocument { [BsonId] public string Key { get; set; } public string PlayerName { get; set; } public string QuestKey { get; set; } public string GrantedAtUtc { get; set; } } public class FishingCatchCreditDocument { public string PlayerName { get; set; } public string FishPrefab { get; set; } [BsonId] public string ZdoKey { get; set; } public string GrantedAtUtc { get; set; } } [Serializable] public class RankingDatabase { public List Entries = new List(); public List Claims = new List(); public List MarketplaceQuestCredits = new List(); public List FishingCatchCredits = new List(); } [Serializable] public class RankingEntry { public string PlayerName = ""; public int Points = 0; public string LastReason = ""; public string LastUpdateUtc = ""; public int TotalKillsPontuadas = 0; public int TotalBossesPontuadas = 0; public int KillPointsTotal = 0; public int BossPointsTotal = 0; public int TotalSkillLevelUpsPontuados = 0; public int SkillPointsTotal = 0; public int TotalFishingPontuadas = 0; public int FishingPointsTotal = 0; public int TotalCraftPontuadas = 0; public int CraftPointsTotal = 0; public int TotalFarmJackpotsPontuados = 0; public int FarmJackpotPointsTotal = 0; public int TotalUniqueCraftJackpotsPontuados = 0; public int UniqueCraftJackpotPointsTotal = 0; public int TotalDeaths = 0; public int DeathPenaltyPointsTotal = 0; public int TotalPointsExchanges = 0; public int PointsExchangePenaltyTotal = 0; public int PointsExchangeCoinsTotal = 0; public int ExplorationMapJackpotPointsTotal = 0; public Dictionary ProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary FloatProgressCounters = new Dictionary(StringComparer.OrdinalIgnoreCase); public HashSet GenericJackpotCredits = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet BossCredits = new HashSet(StringComparer.OrdinalIgnoreCase); public HashSet SkillLevel100JackpotCredits = new HashSet(StringComparer.OrdinalIgnoreCase); } [Serializable] public class FishingCatchCreditRecord { public string PlayerName = ""; public string FishPrefab = ""; public string ZdoKey = ""; public string GrantedAtUtc = ""; } [Serializable] public class JackpotRule { public int RequiredAmount = 1; public int Points = 0; } [Serializable] public class RankingRules { public bool RankingEnabled = true; public bool EnableKillPoints = true; public bool EnableBossPoints = true; public int DefaultKillPoints = 1; public int TopCount = 10; public bool AllowRepeatedBossPoints = false; public bool DebugLogging = false; public bool LogHitReports = false; public bool LogKillReports = false; public bool LogSkillReports = false; public bool LogPendingKillReports = false; public bool LogSnapshotRequests = false; public bool LogPointsChanges = false; public bool EnableSkillPoints = true; public bool IgnoreTamedKills = true; public bool EnableMarketplaceQuestPoints = true; public bool EnableFishingPoints = true; public bool EnableDeathPenalty = true; public bool DeathPenaltyUseMultiplier = false; public int DeathPenaltyPerDeath = 100; public bool EnableExplorationJackpots = true; public bool RewardClaimsEnabled = true; public string RewardClaimCycleId = "temporada_001"; public int RewardTop1MinPoints = 300; public string RewardTop1Label = "Recompensa do 1 lugar"; public string RewardTop1Prefab = "Coins"; public int RewardTop1Amount = 500; public int RewardTop2MinPoints = 200; public string RewardTop2Label = "Recompensa do 2 lugar"; public string RewardTop2Prefab = "AmberPearl"; public int RewardTop2Amount = 10; public int RewardTop3MinPoints = 100; public string RewardTop3Label = "Recompensa do 3 lugar"; public string RewardTop3Prefab = "Ruby"; public int RewardTop3Amount = 5; public bool PointsExchangeEnabled = true; public string PointsExchangePrefab = "Coins"; public bool PointsExchangeUseCoinsPerPoint = false; public int PointsExchangeCoinsPerPoint = 1; public bool PointsExchangeUsePointsPerCoin = true; public int PointsExchangePointsPerCoin = 100; public int PointsExchangeMinPoints = 1; public int PointsExchangeMaxPointsPerRequest = 0; public Dictionary KillPoints = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary CombatHudCategories = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary ProductionHudCategories = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary BossPoints = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary> SkillMilestoneJackpotPoints = new Dictionary>(StringComparer.OrdinalIgnoreCase); public Dictionary MarketplaceQuestPoints = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary FishingPoints = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary CraftPoints = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary FarmJackpots = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary UniqueCraftJackpots = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary DeathPenaltyRules = new Dictionary(); public Dictionary ExplorationMapJackpots = new Dictionary(); } [HarmonyPatch] internal static class FarmHarvestRankingPatches { [CompilerGenerated] private sealed class d__0 : IEnumerable, IEnumerable, IEnumerator, IDisposable, IEnumerator { private int <>1__state; private MethodBase <>2__current; private int <>l__initialThreadId; private Type 5__1; private MethodInfo[] <>s__2; private int <>s__3; private MethodInfo 5__4; MethodBase IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__0(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { 5__1 = null; <>s__2 = null; 5__4 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_00ac; } <>1__state = -1; 5__1 = AccessTools.TypeByName("Pickable"); if (5__1 == null) { return false; } <>s__2 = 5__1.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); <>s__3 = 0; goto IL_00c2; IL_00ac: 5__4 = null; <>s__3++; goto IL_00c2; IL_00c2: if (<>s__3 < <>s__2.Length) { 5__4 = <>s__2[<>s__3]; if (string.Equals(5__4.Name, "Interact", StringComparison.OrdinalIgnoreCase)) { <>2__current = 5__4; <>1__state = 1; return true; } goto IL_00ac; } <>s__2 = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new d__0(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [HarmonyTargetMethods] [IteratorStateMachine(typeof(d__0))] private static IEnumerable TargetMethods() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__0(-2); } [HarmonyPostfix] private static void Postfix(object __instance, object[] __args, object __result) { try { if (!((Object)(object)GlitnirRankingPlugin.Instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null) && __instance != null && (!(__result is bool) || (bool)__result)) { Player val = ExtractPlayer(__args); if ((!((Object)(object)val != (Object)null) || !((Object)(object)val != (Object)(object)Player.m_localPlayer)) && TryExtractPickableDrop(__instance, out var prefabName, out var amount)) { GlitnirRankingPlugin.Instance.NotifyLocalFarmHarvest(prefabName, amount); } } } catch (Exception ex) { GlitnirRankingPlugin.Log.LogError((object)("Erro no patch de colheita: " + ex)); } } private static Player ExtractPlayer(object[] args) { if (args == null) { return null; } for (int i = 0; i < args.Length; i++) { object obj = args[i]; Player val = (Player)((obj is Player) ? obj : null); if ((Object)(object)val != (Object)null) { return val; } object obj2 = args[i]; Humanoid val2 = (Humanoid)((obj2 is Humanoid) ? obj2 : null); if (val2 is Player) { return (Player)(object)((val2 is Player) ? val2 : null); } } return null; } private static bool TryExtractPickableDrop(object pickable, out string prefabName, out int amount) { prefabName = ""; amount = 1; try { Type type = pickable.GetType(); FieldInfo field = type.GetField("m_itemPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); GameObject val = (GameObject)((field != null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val == (Object)null) { return false; } prefabName = ((Object)val).name.Replace("(Clone)", "").Trim(); FieldInfo field2 = type.GetField("m_amount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field2 != null && field2.GetValue(pickable) is int) { amount = Mathf.Max(1, (int)field2.GetValue(pickable)); } return !string.IsNullOrWhiteSpace(prefabName); } catch { return false; } } } [HarmonyPatch] internal static class FishingRankingPatches { [CompilerGenerated] private sealed class d__1 : IEnumerable, IEnumerable, IEnumerator, IDisposable, IEnumerator { private int <>1__state; private MethodBase <>2__current; private int <>l__initialThreadId; private Type 5__1; private MethodInfo 5__2; MethodBase IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__1(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { 5__1 = null; 5__2 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__1 = AccessTools.TypeByName("FishingFloat"); if (5__1 == null) { return false; } 5__2 = AccessTools.Method(5__1, "SetCatch", (Type[])null, (Type[])null); if (5__2 != null) { <>2__current = 5__2; <>1__state = 1; return true; } break; case 1: <>1__state = -1; break; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new d__1(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [HarmonyPrepare] private static bool Prepare() { try { Type type = AccessTools.TypeByName("FishingFloat"); if (type == null) { GlitnirRankingPlugin.Log.LogWarning((object)"[Ranking] Patch de pesca não aplicado: classe FishingFloat não encontrada."); return false; } MethodInfo methodInfo = AccessTools.Method(type, "SetCatch", (Type[])null, (Type[])null); if (methodInfo == null) { GlitnirRankingPlugin.Log.LogWarning((object)"[Ranking] Patch de pesca não aplicado: FishingFloat.SetCatch não encontrado."); return false; } return true; } catch { return false; } } [IteratorStateMachine(typeof(d__1))] [HarmonyTargetMethods] private static IEnumerable TargetMethods() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__1(-2); } [HarmonyPostfix] private static void Postfix(MethodBase __originalMethod, object __instance, object[] __args) { try { if (!((Object)(object)GlitnirRankingPlugin.Instance == (Object)null)) { string text = ((__originalMethod != null) ? (__originalMethod.Name ?? "") : ""); GameObject val = TryExtractFishGameObject(__instance, __args); if (!((Object)(object)val == (Object)null)) { GlitnirRankingPlugin.Instance.NotifyLocalFishCaught(val.transform, Player.m_localPlayer); } } } catch (Exception ex) { GlitnirRankingPlugin.Log.LogError((object)("Erro no patch FishingFloat de pesca: " + ex)); } } private static GameObject TryExtractFishGameObject(object instance, object[] args) { try { GameObject val = FindGameObjectRecursive(args, 0); if ((Object)(object)val != (Object)null) { return val; } Component val2 = (Component)((instance is Component) ? instance : null); if ((Object)(object)val2 == (Object)null) { return null; } GameObject val3 = FindGameObjectInFields(val2, 0); if ((Object)(object)val3 != (Object)null) { return val3; } Transform transform = val2.transform; if ((Object)(object)transform != (Object)null) { for (int i = 0; i < transform.childCount; i++) { Transform child = transform.GetChild(i); if (!((Object)(object)child == (Object)null) && IsFishObject(((Component)child).gameObject)) { return ((Component)child).gameObject; } } } } catch { } return null; } private static GameObject FindGameObjectRecursive(object value, int depth) { if (value == null || depth > 2) { return null; } if (value is object[] array) { for (int i = 0; i < array.Length; i++) { GameObject val = FindGameObjectRecursive(array[i], depth + 1); if ((Object)(object)val != (Object)null) { return val; } } return null; } GameObject val2 = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val2 != (Object)null && IsFishObject(val2)) { return val2; } Component val3 = (Component)((value is Component) ? value : null); if ((Object)(object)val3 != (Object)null && (Object)(object)val3.gameObject != (Object)null && IsFishObject(val3.gameObject)) { return val3.gameObject; } return null; } private static GameObject FindGameObjectInFields(Component component, int depth) { if ((Object)(object)component == (Object)null || depth > 2) { return null; } Type type = ((object)component).GetType(); FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo == null) { continue; } string text = fieldInfo.Name ?? ""; if (text.IndexOf("fish", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("catch", StringComparison.OrdinalIgnoreCase) >= 0) { object value = null; try { value = fieldInfo.GetValue(component); } catch { } GameObject val = FindGameObjectRecursive(value, depth + 1); if ((Object)(object)val != (Object)null) { return val; } } } return null; } private static bool IsFishObject(GameObject go) { if ((Object)(object)go == (Object)null) { return false; } string text = ((Object)go).name ?? ""; if (text.StartsWith("Fish", StringComparison.OrdinalIgnoreCase)) { return true; } try { ZNetView component = go.GetComponent(); if ((Object)(object)component != (Object)null && component.GetZDO() != null) { string text2 = (((Object)(object)GlitnirRankingPlugin.Instance != (Object)null) ? GlitnirRankingPlugin.Instance.GetPrefabName(go) : ""); return !string.IsNullOrWhiteSpace(text2) && text2.StartsWith("Fish", StringComparison.OrdinalIgnoreCase); } } catch { } return false; } } [HarmonyPatch] internal static class FishingDropBlockPatches { [CompilerGenerated] private sealed class d__1 : IEnumerable, IEnumerable, IEnumerator, IDisposable, IEnumerator { private int <>1__state; private MethodBase <>2__current; private int <>l__initialThreadId; private MethodInfo 5__1; MethodBase IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__1(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { 5__1 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__1 = AccessTools.Method(typeof(Player), "DropItem", (Type[])null, (Type[])null); if (5__1 != null) { <>2__current = 5__1; <>1__state = 1; return true; } break; case 1: <>1__state = -1; break; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new d__1(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [HarmonyPrepare] private static bool Prepare() { try { MethodInfo methodInfo = AccessTools.Method(typeof(Player), "DropItem", (Type[])null, (Type[])null); if (methodInfo == null) { GlitnirRankingPlugin.Log.LogWarning((object)"[Ranking] Bloqueio de drop de peixe não aplicado: Player.DropItem não encontrado."); return false; } return true; } catch { return false; } } [IteratorStateMachine(typeof(d__1))] [HarmonyTargetMethods] private static IEnumerable TargetMethods() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__1(-2); } [HarmonyPrefix] private static bool Prefix(object[] __args) { try { ItemData item = TryExtractItemData(__args); if (!IsFishItem(item)) { return true; } ShowBlockedDropMessage(); GlitnirRankingPlugin.Log.LogInfo((object)"[Ranking] Drop de peixe bloqueado para evitar farm infinito de pontos."); return false; } catch (Exception ex) { GlitnirRankingPlugin.Log.LogWarning((object)("[Ranking] Erro ao validar bloqueio de drop de peixe: " + ex.Message)); return true; } } private static ItemData TryExtractItemData(object[] args) { if (args == null) { return null; } foreach (object obj in args) { ItemData val = (ItemData)((obj is ItemData) ? obj : null); if (val != null) { return val; } } return null; } private static bool IsFishItem(ItemData item) { if (item == null) { return false; } try { GameObject dropPrefab = item.m_dropPrefab; if ((Object)(object)dropPrefab != (Object)null) { string cleanName = GetCleanName(((Object)dropPrefab).name); if (cleanName.StartsWith("Fish", StringComparison.OrdinalIgnoreCase)) { return true; } } } catch { } try { if (item.m_shared != null) { string text = item.m_shared.m_name ?? ""; string text2 = text.ToLowerInvariant(); if (text2.Contains("fish")) { return true; } if (text2.Contains("$item_fish")) { return true; } } } catch { } return false; } private static string GetCleanName(string rawName) { if (string.IsNullOrWhiteSpace(rawName)) { return ""; } string text = rawName; int num = text.IndexOf('('); if (num > 0) { text = text.Substring(0, num); } return text.Trim(); } private static void ShowBlockedDropMessage() { try { if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)2, "Necromancia aquática detectada. Sjar está decepcionado.", 0, (Sprite)null, false); } } catch { } } } } namespace Glitnir.Ranking.Patches { internal static class GlitnirAAACraftMaxClamp { public static void Apply(Harmony harmony) { } } } [CompilerGenerated] internal sealed class <23adf39d-285a-4bba-9372-15fca660823f> { [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 6)] internal struct __StaticArrayInitTypeSize=6 { } [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 10)] internal struct __StaticArrayInitTypeSize=10 { } internal static readonly __StaticArrayInitTypeSize=6 02BCD48CBC6E5751BB41C821F52F8EEA4D2BEF8503ACFB9C51085B675F312A37/* Not supported: data(7C 00 2C 00 3B 00) */; internal static readonly long 3CD85BE4972E1FDD2AC3F0E63CBAB7DB10C42889E5EF517854CEE2D2B36376D3/* Not supported: data(3B 00 2C 00 0A 00 0D 00) */; internal static readonly long 58153EAB21A3DE42582A4CD7C5786CF851B3611E3F783DD277127F09F67CC09D/* Not supported: data(2C 00 7C 00 0A 00 0D 00) */; internal static readonly long 635977557E0426652C011DCAB402133ABD15F32A14E78AE863212406F86B0F42/* Not supported: data(2C 00 3B 00 0A 00 0D 00) */; internal static readonly __StaticArrayInitTypeSize=6 B8C389D2F313D5741446DE97ADDC07691F7F6340B0808CB171BFFD4470C9C43D/* Not supported: data(3B 00 0A 00 0D 00) */; internal static readonly __StaticArrayInitTypeSize=10 BBAF862F424755B23DC6DF23EB3C26ABF6F6C1F9E51CBABF532B86F06393FE47/* Not supported: data(2C 00 3B 00 0A 00 0D 00 7C 00) */; internal static uint ComputeStringHash(string s) { uint num = default(uint); if (s != null) { num = 2166136261u; for (int i = 0; i < s.Length; i++) { num = (s[i] ^ num) * 16777619; } } return num; } } namespace Microsoft.CodeAnalysis { [<86ffe179-ffa4-461d-a2ab-b95520f827ff>Embedded] [CompilerGenerated] internal sealed class <86ffe179-ffa4-461d-a2ab-b95520f827ff>EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [<86ffe179-ffa4-461d-a2ab-b95520f827ff>Embedded] internal sealed class <72e81469-6a2a-4525-bb04-ef1f9b4d38cd>IsReadOnlyAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] [<86ffe179-ffa4-461d-a2ab-b95520f827ff>Embedded] [CompilerGenerated] internal sealed class <1a6b8089-db68-4773-8208-3d1ff2b8d41c>NullableAttribute : Attribute { public readonly byte[] NullableFlags; public <1a6b8089-db68-4773-8208-3d1ff2b8d41c>NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public <1a6b8089-db68-4773-8208-3d1ff2b8d41c>NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [<86ffe179-ffa4-461d-a2ab-b95520f827ff>Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class <54a6aabc-2dc5-4c99-85e4-c5255fbf9a29>NullableContextAttribute : Attribute { public readonly byte Flag; public <54a6aabc-2dc5-4c99-85e4-c5255fbf9a29>NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [<86ffe179-ffa4-461d-a2ab-b95520f827ff>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 LiteDB { internal sealed class LiteCollection : ILiteCollection { private readonly string _collection; private readonly ILiteEngine _engine; private readonly List _includes; private readonly BsonMapper _mapper; private readonly EntityMapper _entity; private readonly MemberMapper _id; private readonly BsonAutoId _autoId; public string Name => _collection; public BsonAutoId AutoId => _autoId; public EntityMapper EntityMapper => _entity; public int Count() { return Query().Count(); } public int Count(BsonExpression predicate) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return Query().Where(predicate).Count(); } public int Count(string predicate, BsonDocument parameters) { return Count(BsonExpression.Create(predicate, parameters)); } public int Count(string predicate, params BsonValue[] args) { return Count(BsonExpression.Create(predicate, args)); } public int Count(Expression> predicate) { return Count(_mapper.GetExpression(predicate)); } public int Count(Query query) { return new LiteQueryable(_engine, _mapper, _collection, query).Count(); } public long LongCount() { return Query().LongCount(); } public long LongCount(BsonExpression predicate) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return Query().Where(predicate).LongCount(); } public long LongCount(string predicate, BsonDocument parameters) { return LongCount(BsonExpression.Create(predicate, parameters)); } public long LongCount(string predicate, params BsonValue[] args) { return LongCount(BsonExpression.Create(predicate, args)); } public long LongCount(Expression> predicate) { return LongCount(_mapper.GetExpression(predicate)); } public long LongCount(Query query) { return new LiteQueryable(_engine, _mapper, _collection, query).Count(); } public bool Exists(BsonExpression predicate) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return Query().Where(predicate).Exists(); } public bool Exists(string predicate, BsonDocument parameters) { return Exists(BsonExpression.Create(predicate, parameters)); } public bool Exists(string predicate, params BsonValue[] args) { return Exists(BsonExpression.Create(predicate, args)); } public bool Exists(Expression> predicate) { return Exists(_mapper.GetExpression(predicate)); } public bool Exists(Query query) { return new LiteQueryable(_engine, _mapper, _collection, query).Exists(); } public BsonValue Min(BsonExpression keySelector) { if (string.IsNullOrEmpty(keySelector)) { throw new ArgumentNullException("keySelector"); } BsonDocument bsonDocument = Query().OrderBy(keySelector).Select(keySelector).ToDocuments() .First(); return bsonDocument[bsonDocument.Keys.First()]; } public BsonValue Min() { return Min("_id"); } public K Min(Expression> keySelector) { if (keySelector == null) { throw new ArgumentNullException("keySelector"); } BsonExpression expression = _mapper.GetExpression(keySelector); BsonValue value = Min(expression); return (K)_mapper.Deserialize(typeof(K), value); } public BsonValue Max(BsonExpression keySelector) { if (string.IsNullOrEmpty(keySelector)) { throw new ArgumentNullException("keySelector"); } BsonDocument bsonDocument = Query().OrderByDescending(keySelector).Select(keySelector).ToDocuments() .First(); return bsonDocument[bsonDocument.Keys.First()]; } public BsonValue Max() { return Max("_id"); } public K Max(Expression> keySelector) { if (keySelector == null) { throw new ArgumentNullException("keySelector"); } BsonExpression expression = _mapper.GetExpression(keySelector); BsonValue value = Max(expression); return (K)_mapper.Deserialize(typeof(K), value); } public bool Delete(BsonValue id) { if (id == null || id.IsNull) { throw new ArgumentNullException("id"); } return _engine.Delete(_collection, new BsonValue[1] { id }) == 1; } public int DeleteAll() { return _engine.DeleteMany(_collection, null); } public int DeleteMany(BsonExpression predicate) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return _engine.DeleteMany(_collection, predicate); } public int DeleteMany(string predicate, BsonDocument parameters) { return DeleteMany(BsonExpression.Create(predicate, parameters)); } public int DeleteMany(string predicate, params BsonValue[] args) { return DeleteMany(BsonExpression.Create(predicate, args)); } public int DeleteMany(Expression> predicate) { return DeleteMany(_mapper.GetExpression(predicate)); } public ILiteQueryable Query() { return new LiteQueryable(_engine, _mapper, _collection, new Query()).Include(_includes); } public IEnumerable Find(BsonExpression predicate, int skip = 0, int limit = int.MaxValue) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return Query().Include(_includes).Where(predicate).Skip(skip) .Limit(limit) .ToEnumerable(); } public IEnumerable Find(Query query, int skip = 0, int limit = int.MaxValue) { if (query == null) { throw new ArgumentNullException("query"); } if (skip != 0) { query.Offset = skip; } if (limit != int.MaxValue) { query.Limit = limit; } return new LiteQueryable(_engine, _mapper, _collection, query).ToEnumerable(); } public IEnumerable Find(Expression> predicate, int skip = 0, int limit = int.MaxValue) { return Find(_mapper.GetExpression(predicate), skip, limit); } public T FindById(BsonValue id) { if (id == null || id.IsNull) { throw new ArgumentNullException("id"); } return Find(BsonExpression.Create("_id = @0", id)).FirstOrDefault(); } public T FindOne(BsonExpression predicate) { return Find(predicate).FirstOrDefault(); } public T FindOne(string predicate, BsonDocument parameters) { return FindOne(BsonExpression.Create(predicate, parameters)); } public T FindOne(BsonExpression predicate, params BsonValue[] args) { return FindOne(BsonExpression.Create(predicate, args)); } public T FindOne(Expression> predicate) { return FindOne(_mapper.GetExpression(predicate)); } public T FindOne(Query query) { return Find(query).FirstOrDefault(); } public IEnumerable FindAll() { return Query().Include(_includes).ToEnumerable(); } public ILiteCollection Include(Expression> keySelector) { if (keySelector == null) { throw new ArgumentNullException("keySelector"); } BsonExpression expression = _mapper.GetExpression(keySelector); return Include(expression); } public ILiteCollection Include(BsonExpression keySelector) { if (string.IsNullOrEmpty(keySelector)) { throw new ArgumentNullException("keySelector"); } LiteCollection liteCollection = new LiteCollection(_collection, _autoId, _engine, _mapper); liteCollection._includes.AddRange(_includes); liteCollection._includes.Add(keySelector); return liteCollection; } public bool EnsureIndex(string name, BsonExpression expression, bool unique = false) { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException("name"); } if (expression == null) { throw new ArgumentNullException("expression"); } return _engine.EnsureIndex(_collection, name, expression, unique); } public bool EnsureIndex(BsonExpression expression, bool unique = false) { if (expression == null) { throw new ArgumentNullException("expression"); } string name = Regex.Replace(expression.Source, "[^a-z0-9]", "", RegexOptions.IgnoreCase | RegexOptions.Compiled); return EnsureIndex(name, expression, unique); } public bool EnsureIndex(Expression> keySelector, bool unique = false) { BsonExpression indexExpression = GetIndexExpression(keySelector); return EnsureIndex(indexExpression, unique); } public bool EnsureIndex(string name, Expression> keySelector, bool unique = false) { BsonExpression indexExpression = GetIndexExpression(keySelector); return EnsureIndex(name, indexExpression, unique); } private BsonExpression GetIndexExpression(Expression> keySelector) { BsonExpression bsonExpression = _mapper.GetIndexExpression(keySelector); if (typeof(K).IsEnumerable() && bsonExpression.IsScalar) { if (bsonExpression.Type != BsonExpressionType.Path) { throw new LiteException(0, "Expression `" + bsonExpression.Source + "` must return a enumerable expression"); } bsonExpression = bsonExpression.Source + "[*]"; } return bsonExpression; } public bool DropIndex(string name) { return _engine.DropIndex(_collection, name); } public BsonValue Insert(T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } BsonDocument bsonDocument = _mapper.ToDocument(entity); bool flag = RemoveDocId(bsonDocument); _engine.Insert(_collection, new BsonDocument[1] { bsonDocument }, _autoId); BsonValue bsonValue = bsonDocument["_id"]; if (flag) { _id.Setter(entity, bsonValue.RawValue); } return bsonValue; } public void Insert(BsonValue id, T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } if (id == null || id.IsNull) { throw new ArgumentNullException("id"); } BsonDocument bsonDocument = _mapper.ToDocument(entity); bsonDocument["_id"] = id; _engine.Insert(_collection, new BsonDocument[1] { bsonDocument }, _autoId); } public int Insert(IEnumerable entities) { if (entities == null) { throw new ArgumentNullException("entities"); } return _engine.Insert(_collection, GetBsonDocs(entities), _autoId); } [Obsolete("Use normal Insert()")] public int InsertBulk(IEnumerable entities, int batchSize = 5000) { if (entities == null) { throw new ArgumentNullException("entities"); } return _engine.Insert(_collection, GetBsonDocs(entities), _autoId); } private IEnumerable GetBsonDocs(IEnumerable documents) { foreach (T document in documents) { BsonDocument doc = _mapper.ToDocument(document); bool removed = RemoveDocId(doc); yield return doc; if (removed && _id != null) { _id.Setter(document, doc["_id"].RawValue); } } } private bool RemoveDocId(BsonDocument doc) { if (_id != null && doc.TryGetValue("_id", out var value) && ((_autoId == BsonAutoId.Int32 && value.IsInt32 && value.AsInt32 == 0) || (_autoId == BsonAutoId.ObjectId && (value.IsNull || (value.IsObjectId && value.AsObjectId == ObjectId.Empty))) || (_autoId == BsonAutoId.Guid && value.IsGuid && value.AsGuid == Guid.Empty) || (_autoId == BsonAutoId.Int64 && value.IsInt64 && value.AsInt64 == 0L))) { doc.Remove("_id"); return true; } return false; } public bool Update(T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } BsonDocument bsonDocument = _mapper.ToDocument(entity); return _engine.Update(_collection, new BsonDocument[1] { bsonDocument }) > 0; } public bool Update(BsonValue id, T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } if (id == null || id.IsNull) { throw new ArgumentNullException("id"); } BsonDocument bsonDocument = _mapper.ToDocument(entity); bsonDocument["_id"] = id; return _engine.Update(_collection, new BsonDocument[1] { bsonDocument }) > 0; } public int Update(IEnumerable entities) { if (entities == null) { throw new ArgumentNullException("entities"); } return _engine.Update(_collection, entities.Select((T x) => _mapper.ToDocument(x))); } public int UpdateMany(BsonExpression transform, BsonExpression predicate) { if (transform == null) { throw new ArgumentNullException("transform"); } if (predicate == null) { throw new ArgumentNullException("predicate"); } if (transform.Type != BsonExpressionType.Document) { throw new ArgumentException("Extend expression must return a document. Eg: `col.UpdateMany('{ Name: UPPER(Name) }', 'Age > 10')`"); } return _engine.UpdateMany(_collection, transform, predicate); } public int UpdateMany(Expression> extend, Expression> predicate) { if (extend == null) { throw new ArgumentNullException("extend"); } if (predicate == null) { throw new ArgumentNullException("predicate"); } BsonExpression expression = _mapper.GetExpression(extend); BsonExpression expression2 = _mapper.GetExpression(predicate); if (expression.Type != BsonExpressionType.Document) { throw new ArgumentException("Extend expression must return an anonymous class to be merge with entities. Eg: `col.UpdateMany(x => new { Name = x.Name.ToUpper() }, x => x.Age > 10)`"); } return _engine.UpdateMany(_collection, expression, expression2); } public bool Upsert(T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } return Upsert(new T[1] { entity }) == 1; } public int Upsert(IEnumerable entities) { if (entities == null) { throw new ArgumentNullException("entities"); } return _engine.Upsert(_collection, GetBsonDocs(entities), _autoId); } public bool Upsert(BsonValue id, T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } if (id == null || id.IsNull) { throw new ArgumentNullException("id"); } BsonDocument bsonDocument = _mapper.ToDocument(entity); bsonDocument["_id"] = id; return _engine.Upsert(_collection, new BsonDocument[1] { bsonDocument }, _autoId) > 0; } internal LiteCollection(string name, BsonAutoId autoId, ILiteEngine engine, BsonMapper mapper) { _collection = name ?? mapper.ResolveCollectionName(typeof(T)); _engine = engine; _mapper = mapper; _includes = new List(); if (typeof(T) == typeof(BsonDocument)) { _entity = null; _id = null; _autoId = autoId; return; } _entity = mapper.GetEntityMapper(typeof(T)); _id = _entity.Id; if (_id != null && _id.AutoId) { _autoId = ((_id.DataType == typeof(int) || _id.DataType == typeof(int?)) ? BsonAutoId.Int32 : ((_id.DataType == typeof(long) || _id.DataType == typeof(long?)) ? BsonAutoId.Int64 : ((_id.DataType == typeof(Guid) || _id.DataType == typeof(Guid?)) ? BsonAutoId.Guid : BsonAutoId.ObjectId))); } else { _autoId = autoId; } } } internal interface ILiteCollection { string Name { get; } BsonAutoId AutoId { get; } EntityMapper EntityMapper { get; } ILiteCollection Include(Expression> keySelector); ILiteCollection Include(BsonExpression keySelector); bool Upsert(T entity); int Upsert(IEnumerable entities); bool Upsert(BsonValue id, T entity); bool Update(T entity); bool Update(BsonValue id, T entity); int Update(IEnumerable entities); int UpdateMany(BsonExpression transform, BsonExpression predicate); int UpdateMany(Expression> extend, Expression> predicate); BsonValue Insert(T entity); void Insert(BsonValue id, T entity); int Insert(IEnumerable entities); int InsertBulk(IEnumerable entities, int batchSize = 5000); bool EnsureIndex(string name, BsonExpression expression, bool unique = false); bool EnsureIndex(BsonExpression expression, bool unique = false); bool EnsureIndex(Expression> keySelector, bool unique = false); bool EnsureIndex(string name, Expression> keySelector, bool unique = false); bool DropIndex(string name); ILiteQueryable Query(); IEnumerable Find(BsonExpression predicate, int skip = 0, int limit = int.MaxValue); IEnumerable Find(Query query, int skip = 0, int limit = int.MaxValue); IEnumerable Find(Expression> predicate, int skip = 0, int limit = int.MaxValue); T FindById(BsonValue id); T FindOne(BsonExpression predicate); T FindOne(string predicate, BsonDocument parameters); T FindOne(BsonExpression predicate, params BsonValue[] args); T FindOne(Expression> predicate); T FindOne(Query query); IEnumerable FindAll(); bool Delete(BsonValue id); int DeleteAll(); int DeleteMany(BsonExpression predicate); int DeleteMany(string predicate, BsonDocument parameters); int DeleteMany(string predicate, params BsonValue[] args); int DeleteMany(Expression> predicate); int Count(); int Count(BsonExpression predicate); int Count(string predicate, BsonDocument parameters); int Count(string predicate, params BsonValue[] args); int Count(Expression> predicate); int Count(Query query); long LongCount(); long LongCount(BsonExpression predicate); long LongCount(string predicate, BsonDocument parameters); long LongCount(string predicate, params BsonValue[] args); long LongCount(Expression> predicate); long LongCount(Query query); bool Exists(BsonExpression predicate); bool Exists(string predicate, BsonDocument parameters); bool Exists(string predicate, params BsonValue[] args); bool Exists(Expression> predicate); bool Exists(Query query); BsonValue Min(BsonExpression keySelector); BsonValue Min(); K Min(Expression> keySelector); BsonValue Max(BsonExpression keySelector); BsonValue Max(); K Max(Expression> keySelector); } internal interface ILiteDatabase : IDisposable { BsonMapper Mapper { get; } ILiteStorage FileStorage { get; } int UserVersion { get; set; } TimeSpan Timeout { get; set; } bool UtcDate { get; set; } long LimitSize { get; set; } int CheckpointSize { get; set; } Collation Collation { get; } ILiteCollection GetCollection(string name, BsonAutoId autoId = BsonAutoId.ObjectId); ILiteCollection GetCollection(); ILiteCollection GetCollection(BsonAutoId autoId); ILiteCollection GetCollection(string name, BsonAutoId autoId = BsonAutoId.ObjectId); bool BeginTrans(); bool Commit(); bool Rollback(); ILiteStorage GetStorage(string filesCollection = "_files", string chunksCollection = "_chunks"); IEnumerable GetCollectionNames(); bool CollectionExists(string name); bool DropCollection(string name); bool RenameCollection(string oldName, string newName); IBsonDataReader Execute(TextReader commandReader, BsonDocument parameters = null); IBsonDataReader Execute(string command, BsonDocument parameters = null); IBsonDataReader Execute(string command, params BsonValue[] args); void Checkpoint(); long Rebuild(RebuildOptions options = null); BsonValue Pragma(string name); BsonValue Pragma(string name, BsonValue value); } internal interface ILiteQueryable : ILiteQueryableResult { ILiteQueryable Include(BsonExpression path); ILiteQueryable Include(List paths); ILiteQueryable Include(Expression> path); ILiteQueryable Where(BsonExpression predicate); ILiteQueryable Where(string predicate, BsonDocument parameters); ILiteQueryable Where(string predicate, params BsonValue[] args); ILiteQueryable Where(Expression> predicate); ILiteQueryable OrderBy(BsonExpression keySelector, int order = 1); ILiteQueryable OrderBy(Expression> keySelector, int order = 1); ILiteQueryable OrderByDescending(BsonExpression keySelector); ILiteQueryable OrderByDescending(Expression> keySelector); ILiteQueryable GroupBy(BsonExpression keySelector); ILiteQueryable Having(BsonExpression predicate); ILiteQueryableResult Select(BsonExpression selector); ILiteQueryableResult Select(Expression> selector); } internal interface ILiteQueryableResult { ILiteQueryableResult Limit(int limit); ILiteQueryableResult Skip(int offset); ILiteQueryableResult Offset(int offset); ILiteQueryableResult ForUpdate(); BsonDocument GetPlan(); IBsonDataReader ExecuteReader(); IEnumerable ToDocuments(); IEnumerable ToEnumerable(); List ToList(); T[] ToArray(); int Into(string newCollection, BsonAutoId autoId = BsonAutoId.ObjectId); T First(); T FirstOrDefault(); T Single(); T SingleOrDefault(); int Count(); long LongCount(); bool Exists(); } internal interface ILiteRepository : IDisposable { ILiteDatabase Database { get; } BsonValue Insert(T entity, string collectionName = null); int Insert(IEnumerable entities, string collectionName = null); bool Update(T entity, string collectionName = null); int Update(IEnumerable entities, string collectionName = null); bool Upsert(T entity, string collectionName = null); int Upsert(IEnumerable entities, string collectionName = null); bool Delete(BsonValue id, string collectionName = null); int DeleteMany(BsonExpression predicate, string collectionName = null); int DeleteMany(Expression> predicate, string collectionName = null); ILiteQueryable Query(string collectionName = null); bool EnsureIndex(string name, BsonExpression expression, bool unique = false, string collectionName = null); bool EnsureIndex(BsonExpression expression, bool unique = false, string collectionName = null); bool EnsureIndex(Expression> keySelector, bool unique = false, string collectionName = null); bool EnsureIndex(string name, Expression> keySelector, bool unique = false, string collectionName = null); T SingleById(BsonValue id, string collectionName = null); List Fetch(BsonExpression predicate, string collectionName = null); List Fetch(Expression> predicate, string collectionName = null); T First(BsonExpression predicate, string collectionName = null); T First(Expression> predicate, string collectionName = null); T FirstOrDefault(BsonExpression predicate, string collectionName = null); T FirstOrDefault(Expression> predicate, string collectionName = null); T Single(BsonExpression predicate, string collectionName = null); T Single(Expression> predicate, string collectionName = null); T SingleOrDefault(BsonExpression predicate, string collectionName = null); T SingleOrDefault(Expression> predicate, string collectionName = null); } internal class LiteDatabase : ILiteDatabase, IDisposable { private readonly ILiteEngine _engine; private readonly BsonMapper _mapper; private readonly bool _disposeOnClose; private ILiteStorage _fs; public BsonMapper Mapper => _mapper; public ILiteStorage FileStorage => _fs ?? (_fs = GetStorage()); public int UserVersion { get { return _engine.Pragma("USER_VERSION"); } set { _engine.Pragma("USER_VERSION", value); } } public TimeSpan Timeout { get { return TimeSpan.FromSeconds(_engine.Pragma("TIMEOUT").AsInt32); } set { _engine.Pragma("TIMEOUT", (int)value.TotalSeconds); } } public bool UtcDate { get { return _engine.Pragma("UTC_DATE"); } set { _engine.Pragma("UTC_DATE", value); } } public long LimitSize { get { return _engine.Pragma("LIMIT_SIZE"); } set { _engine.Pragma("LIMIT_SIZE", value); } } public int CheckpointSize { get { return _engine.Pragma("CHECKPOINT"); } set { _engine.Pragma("CHECKPOINT", value); } } public Collation Collation => new Collation(_engine.Pragma("COLLATION").AsString); public LiteDatabase(string connectionString, BsonMapper mapper = null) : this(new ConnectionString(connectionString), mapper) { } public LiteDatabase(ConnectionString connectionString, BsonMapper mapper = null) { if (connectionString == null) { throw new ArgumentNullException("connectionString"); } _engine = connectionString.CreateEngine(); _mapper = mapper ?? BsonMapper.Global; _disposeOnClose = true; } public LiteDatabase(Stream stream, BsonMapper mapper = null, Stream logStream = null) { EngineSettings settings = new EngineSettings { DataStream = (stream ?? throw new ArgumentNullException("stream")), LogStream = logStream }; _engine = new LiteEngine(settings); _mapper = mapper ?? BsonMapper.Global; _disposeOnClose = true; } public LiteDatabase(ILiteEngine engine, BsonMapper mapper = null, bool disposeOnClose = true) { _engine = engine ?? throw new ArgumentNullException("engine"); _mapper = mapper ?? BsonMapper.Global; _disposeOnClose = disposeOnClose; } public ILiteCollection GetCollection(string name, BsonAutoId autoId = BsonAutoId.ObjectId) { return new LiteCollection(name, autoId, _engine, _mapper); } public ILiteCollection GetCollection() { return GetCollection(null); } public ILiteCollection GetCollection(BsonAutoId autoId) { return GetCollection(null, autoId); } public ILiteCollection GetCollection(string name, BsonAutoId autoId = BsonAutoId.ObjectId) { if (name.IsNullOrWhiteSpace()) { throw new ArgumentNullException("name"); } return new LiteCollection(name, autoId, _engine, _mapper); } public bool BeginTrans() { return _engine.BeginTrans(); } public bool Commit() { return _engine.Commit(); } public bool Rollback() { return _engine.Rollback(); } public ILiteStorage GetStorage(string filesCollection = "_files", string chunksCollection = "_chunks") { return new LiteStorage(this, filesCollection, chunksCollection); } public IEnumerable GetCollectionNames() { return (from x in GetCollection("$cols").Query().Where("type = 'user'").ToDocuments() select x["name"].AsString).ToArray(); } public bool CollectionExists(string name) { if (name.IsNullOrWhiteSpace()) { throw new ArgumentNullException("name"); } return GetCollectionNames().Contains(name, StringComparer.OrdinalIgnoreCase); } public bool DropCollection(string name) { if (name.IsNullOrWhiteSpace()) { throw new ArgumentNullException("name"); } return _engine.DropCollection(name); } public bool RenameCollection(string oldName, string newName) { if (oldName.IsNullOrWhiteSpace()) { throw new ArgumentNullException("oldName"); } if (newName.IsNullOrWhiteSpace()) { throw new ArgumentNullException("newName"); } return _engine.RenameCollection(oldName, newName); } public IBsonDataReader Execute(TextReader commandReader, BsonDocument parameters = null) { if (commandReader == null) { throw new ArgumentNullException("commandReader"); } Tokenizer tokenizer = new Tokenizer(commandReader); return new SqlParser(_engine, tokenizer, parameters).Execute(); } public IBsonDataReader Execute(string command, BsonDocument parameters = null) { if (command == null) { throw new ArgumentNullException("command"); } Tokenizer tokenizer = new Tokenizer(command); return new SqlParser(_engine, tokenizer, parameters).Execute(); } public IBsonDataReader Execute(string command, params BsonValue[] args) { BsonDocument bsonDocument = new BsonDocument(); int num = 0; foreach (BsonValue value in args) { bsonDocument[num.ToString()] = value; num++; } return Execute(command, bsonDocument); } public void Checkpoint() { _engine.Checkpoint(); } public long Rebuild(RebuildOptions options = null) { return _engine.Rebuild(options ?? new RebuildOptions()); } public BsonValue Pragma(string name) { return _engine.Pragma(name); } public BsonValue Pragma(string name, BsonValue value) { return _engine.Pragma(name, value); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } ~LiteDatabase() { Dispose(disposing: false); } protected virtual void Dispose(bool disposing) { if (disposing && _disposeOnClose) { _engine.Dispose(); } } } internal class LiteQueryable : ILiteQueryable, ILiteQueryableResult { protected readonly ILiteEngine _engine; protected readonly BsonMapper _mapper; protected readonly string _collection; protected readonly Query _query; private readonly bool _isSimpleType = Reflection.IsSimpleType(typeof(T)); internal LiteQueryable(ILiteEngine engine, BsonMapper mapper, string collection, Query query) { _engine = engine; _mapper = mapper; _collection = collection; _query = query; } public ILiteQueryable Include(Expression> path) { _query.Includes.Add(_mapper.GetExpression(path)); return this; } public ILiteQueryable Include(BsonExpression path) { _query.Includes.Add(path); return this; } public ILiteQueryable Include(List paths) { _query.Includes.AddRange(paths); return this; } public ILiteQueryable Where(BsonExpression predicate) { _query.Where.Add(predicate); return this; } public ILiteQueryable Where(string predicate, BsonDocument parameters) { _query.Where.Add(BsonExpression.Create(predicate, parameters)); return this; } public ILiteQueryable Where(string predicate, params BsonValue[] args) { _query.Where.Add(BsonExpression.Create(predicate, args)); return this; } public ILiteQueryable Where(Expression> predicate) { return Where(_mapper.GetExpression(predicate)); } public ILiteQueryable OrderBy(BsonExpression keySelector, int order = 1) { if (_query.OrderBy != null) { throw new ArgumentException("ORDER BY already defined in this query builder"); } _query.OrderBy = keySelector; _query.Order = order; return this; } public ILiteQueryable OrderBy(Expression> keySelector, int order = 1) { return OrderBy(_mapper.GetExpression(keySelector), order); } public ILiteQueryable OrderByDescending(BsonExpression keySelector) { return OrderBy(keySelector, -1); } public ILiteQueryable OrderByDescending(Expression> keySelector) { return OrderBy(keySelector, -1); } public ILiteQueryable GroupBy(BsonExpression keySelector) { if (_query.GroupBy != null) { throw new ArgumentException("GROUP BY already defined in this query"); } _query.GroupBy = keySelector; return this; } public ILiteQueryable Having(BsonExpression predicate) { if (_query.Having != null) { throw new ArgumentException("HAVING already defined in this query"); } _query.Having = predicate; return this; } public ILiteQueryableResult Select(BsonExpression selector) { _query.Select = selector; return new LiteQueryable(_engine, _mapper, _collection, _query); } public ILiteQueryableResult Select(Expression> selector) { if (_query.GroupBy != null) { throw new ArgumentException("Use Select(BsonExpression selector) when using GroupBy query"); } _query.Select = _mapper.GetExpression(selector); return new LiteQueryable(_engine, _mapper, _collection, _query); } public ILiteQueryableResult ForUpdate() { _query.ForUpdate = true; return this; } public ILiteQueryableResult Offset(int offset) { _query.Offset = offset; return this; } public ILiteQueryableResult Skip(int offset) { return Offset(offset); } public ILiteQueryableResult Limit(int limit) { _query.Limit = limit; return this; } public IBsonDataReader ExecuteReader() { _query.ExplainPlan = false; return _engine.Query(_collection, _query); } public IEnumerable ToDocuments() { using IBsonDataReader reader = ExecuteReader(); while (reader.Read()) { yield return reader.Current as BsonDocument; } } public IEnumerable ToEnumerable() { if (_isSimpleType) { return from x in ToDocuments() select x[x.Keys.First()] into x select (T)_mapper.Deserialize(typeof(T), x); } return from x in ToDocuments() select (T)_mapper.Deserialize(typeof(T), x); } public List ToList() { return ToEnumerable().ToList(); } public T[] ToArray() { return ToEnumerable().ToArray(); } public BsonDocument GetPlan() { _query.ExplainPlan = true; return _engine.Query(_collection, _query).ToEnumerable().FirstOrDefault()?.AsDocument; } public T Single() { return ToEnumerable().Single(); } public T SingleOrDefault() { return ToEnumerable().SingleOrDefault(); } public T First() { return ToEnumerable().First(); } public T FirstOrDefault() { return ToEnumerable().FirstOrDefault(); } public int Count() { BsonExpression select = _query.Select; try { Select("{ count: COUNT(*._id) }"); return ToDocuments().Single()["count"].AsInt32; } finally { _query.Select = select; } } public long LongCount() { BsonExpression select = _query.Select; try { Select("{ count: COUNT(*._id) }"); return ToDocuments().Single()["count"].AsInt64; } finally { _query.Select = select; } } public bool Exists() { BsonExpression select = _query.Select; try { Select("{ exists: ANY(*._id) }"); return ToDocuments().Single()["exists"].AsBoolean; } finally { _query.Select = select; } } public int Into(string newCollection, BsonAutoId autoId = BsonAutoId.ObjectId) { _query.Into = newCollection; _query.IntoAutoId = autoId; using IBsonDataReader bsonDataReader = ExecuteReader(); return bsonDataReader.Current.AsInt32; } } internal class LiteRepository : ILiteRepository, IDisposable { private readonly ILiteDatabase _db; public ILiteDatabase Database => _db; public LiteRepository(ILiteDatabase database) { _db = database; } public LiteRepository(string connectionString, BsonMapper mapper = null) { _db = new LiteDatabase(connectionString, mapper); } public LiteRepository(ConnectionString connectionString, BsonMapper mapper = null) { _db = new LiteDatabase(connectionString, mapper); } public LiteRepository(Stream stream, BsonMapper mapper = null, Stream logStream = null) { _db = new LiteDatabase(stream, mapper, logStream); } public BsonValue Insert(T entity, string collectionName = null) { return _db.GetCollection(collectionName).Insert(entity); } public int Insert(IEnumerable entities, string collectionName = null) { return _db.GetCollection(collectionName).Insert(entities); } public bool Update(T entity, string collectionName = null) { return _db.GetCollection(collectionName).Update(entity); } public int Update(IEnumerable entities, string collectionName = null) { return _db.GetCollection(collectionName).Update(entities); } public bool Upsert(T entity, string collectionName = null) { return _db.GetCollection(collectionName).Upsert(entity); } public int Upsert(IEnumerable entities, string collectionName = null) { return _db.GetCollection(collectionName).Upsert(entities); } public bool Delete(BsonValue id, string collectionName = null) { return _db.GetCollection(collectionName).Delete(id); } public int DeleteMany(BsonExpression predicate, string collectionName = null) { return _db.GetCollection(collectionName).DeleteMany(predicate); } public int DeleteMany(Expression> predicate, string collectionName = null) { return _db.GetCollection(collectionName).DeleteMany(predicate); } public ILiteQueryable Query(string collectionName = null) { return _db.GetCollection(collectionName).Query(); } public bool EnsureIndex(string name, BsonExpression expression, bool unique = false, string collectionName = null) { return _db.GetCollection(collectionName).EnsureIndex(name, expression, unique); } public bool EnsureIndex(BsonExpression expression, bool unique = false, string collectionName = null) { return _db.GetCollection(collectionName).EnsureIndex(expression, unique); } public bool EnsureIndex(Expression> keySelector, bool unique = false, string collectionName = null) { return _db.GetCollection(collectionName).EnsureIndex(keySelector, unique); } public bool EnsureIndex(string name, Expression> keySelector, bool unique = false, string collectionName = null) { return _db.GetCollection(collectionName).EnsureIndex(name, keySelector, unique); } public T SingleById(BsonValue id, string collectionName = null) { return _db.GetCollection(collectionName).Query().Where("_id = @0", id) .Single(); } public List Fetch(BsonExpression predicate, string collectionName = null) { return Query(collectionName).Where(predicate).ToList(); } public List Fetch(Expression> predicate, string collectionName = null) { return Query(collectionName).Where(predicate).ToList(); } public T First(BsonExpression predicate, string collectionName = null) { return Query(collectionName).Where(predicate).First(); } public T First(Expression> predicate, string collectionName = null) { return Query(collectionName).Where(predicate).First(); } public T FirstOrDefault(BsonExpression predicate, string collectionName = null) { return Query(collectionName).Where(predicate).FirstOrDefault(); } public T FirstOrDefault(Expression> predicate, string collectionName = null) { return Query(collectionName).Where(predicate).FirstOrDefault(); } public T Single(BsonExpression predicate, string collectionName = null) { return Query(collectionName).Where(predicate).Single(); } public T Single(Expression> predicate, string collectionName = null) { return Query(collectionName).Where(predicate).Single(); } public T SingleOrDefault(BsonExpression predicate, string collectionName = null) { return Query(collectionName).Where(predicate).SingleOrDefault(); } public T SingleOrDefault(Expression> predicate, string collectionName = null) { return Query(collectionName).Where(predicate).SingleOrDefault(); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } ~LiteRepository() { Dispose(disposing: false); } protected virtual void Dispose(bool disposing) { if (disposing) { _db.Dispose(); } } } internal class BsonCtorAttribute : Attribute { } internal class BsonFieldAttribute : Attribute { public string Name { get; set; } public BsonFieldAttribute(string name) { Name = name; } public BsonFieldAttribute() { } } internal class BsonIdAttribute : Attribute { public bool AutoId { get; private set; } public BsonIdAttribute() { AutoId = true; } public BsonIdAttribute(bool autoId) { AutoId = autoId; } } internal class BsonIgnoreAttribute : Attribute { } internal class BsonRefAttribute : Attribute { public string Collection { get; set; } public BsonRefAttribute(string collection) { Collection = collection; } public BsonRefAttribute() { Collection = null; } } internal class BsonMapper { public delegate BsonValue DeserializationCallback(BsonMapper sender, Type target, BsonValue value); private readonly Dictionary _entities = new Dictionary(); private readonly ConcurrentDictionary> _customSerializer = new ConcurrentDictionary>(); private readonly ConcurrentDictionary> _customDeserializer = new ConcurrentDictionary>(); private readonly Func _typeInstantiator; private readonly ITypeNameBinder _typeNameBinder; public static BsonMapper Global = new BsonMapper(); public Func ResolveFieldName; public Action ResolveMember; public Func ResolveCollectionName; private readonly Regex _lowerCaseDelimiter = new Regex("(?!(^[A-Z]))([A-Z])", RegexOptions.Compiled); private readonly HashSet _bsonTypes = new HashSet { typeof(string), typeof(int), typeof(long), typeof(bool), typeof(Guid), typeof(DateTime), typeof(byte[]), typeof(ObjectId), typeof(double), typeof(decimal) }; private readonly HashSet _basicTypes = new HashSet { typeof(short), typeof(ushort), typeof(uint), typeof(float), typeof(char), typeof(byte), typeof(sbyte) }; public bool SerializeNullValues { get; set; } public bool TrimWhitespace { get; set; } public bool EmptyStringToNull { get; set; } public bool EnumAsInteger { get; set; } public bool IncludeFields { get; set; } public bool IncludeNonPublic { get; set; } public int MaxDepth { get; set; } [<1a6b8089-db68-4773-8208-3d1ff2b8d41c>Nullable(2)] [field: <1a6b8089-db68-4773-8208-3d1ff2b8d41c>Nullable(2)] public DeserializationCallback OnDeserialization { [<54a6aabc-2dc5-4c99-85e4-c5255fbf9a29>NullableContext(2)] get; [<54a6aabc-2dc5-4c99-85e4-c5255fbf9a29>NullableContext(2)] set; } public BsonMapper(Func customTypeInstantiator = null, ITypeNameBinder typeNameBinder = null) { SerializeNullValues = false; TrimWhitespace = true; EmptyStringToNull = true; EnumAsInteger = false; ResolveFieldName = (string s) => s; ResolveMember = delegate { }; ResolveCollectionName = (Type t) => (!Reflection.IsEnumerable(t)) ? t.Name : Reflection.GetListItemType(t).Name; IncludeFields = false; MaxDepth = 20; _typeInstantiator = customTypeInstantiator ?? ((Func)((Type t) => null)); _typeNameBinder = typeNameBinder ?? DefaultTypeNameBinder.Instance; RegisterType((Uri uri) => uri.IsAbsoluteUri ? uri.AbsoluteUri : uri.ToString(), (BsonValue bson) => new Uri(bson.AsString)); RegisterType((DateTimeOffset value) => new BsonValue(value.UtcDateTime), (BsonValue bson) => bson.AsDateTime.ToUniversalTime()); RegisterType((TimeSpan value) => new BsonValue(value.Ticks), (BsonValue bson) => new TimeSpan(bson.AsInt64)); RegisterType((Regex r) => (r.Options != 0) ? new BsonDocument { { "p", r.ToString() }, { "o", (int)r.Options } } : new BsonValue(r.ToString()), (BsonValue value) => (!value.IsString) ? new Regex(value.AsDocument["p"].AsString, (RegexOptions)value.AsDocument["o"].AsInt32) : new Regex(value)); } public void RegisterType(Func serialize, Func deserialize) { _customSerializer[typeof(T)] = (object o) => serialize((T)o); _customDeserializer[typeof(T)] = (BsonValue b) => deserialize(b); } public void RegisterType(Type type, Func serialize, Func deserialize) { _customSerializer[type] = (object o) => serialize(o); _customDeserializer[type] = (BsonValue b) => deserialize(b); } public EntityBuilder Entity() { return new EntityBuilder(this, _typeNameBinder); } public BsonExpression GetExpression(Expression> predicate) { return new LinqExpressionVisitor(this, predicate).Resolve(typeof(K) == typeof(bool)); } public BsonExpression GetIndexExpression(Expression> predicate) { return new LinqExpressionVisitor(this, predicate).Resolve(predicate: false); } public BsonMapper UseCamelCase() { ResolveFieldName = (string s) => char.ToLower(s[0]) + s.Substring(1); return this; } public BsonMapper UseLowerCaseDelimiter(char delimiter = '_') { ResolveFieldName = (string s) => _lowerCaseDelimiter.Replace(s, delimiter + "$2").ToLower(); return this; } internal EntityMapper GetEntityMapper(Type type) { if (!_entities.TryGetValue(type, out var value)) { lock (_entities) { if (!_entities.TryGetValue(type, out value)) { return BuildAddEntityMapper(type); } } } return value; } protected virtual EntityMapper BuildAddEntityMapper(Type type) { EntityMapper entityMapper = new EntityMapper(type); _entities[type] = entityMapper; Type typeFromHandle = typeof(BsonIdAttribute); Type typeFromHandle2 = typeof(BsonIgnoreAttribute); Type typeFromHandle3 = typeof(BsonFieldAttribute); Type typeFromHandle4 = typeof(BsonRefAttribute); IEnumerable typeMembers = GetTypeMembers(type); MemberInfo idMember = GetIdMember(typeMembers); foreach (MemberInfo item in typeMembers) { if (!CustomAttributeExtensions.IsDefined(item, typeFromHandle2, inherit: true)) { string name = ResolveFieldName(item.Name); BsonFieldAttribute bsonFieldAttribute = (BsonFieldAttribute)CustomAttributeExtensions.GetCustomAttributes(item, typeFromHandle3, inherit: true).FirstOrDefault(); if (bsonFieldAttribute != null && bsonFieldAttribute.Name != null) { name = bsonFieldAttribute.Name; } if (item == idMember) { name = "_id"; } GenericGetter getter = Reflection.CreateGenericGetter(type, item); GenericSetter setter = Reflection.CreateGenericSetter(type, item); BsonIdAttribute bsonIdAttribute = (BsonIdAttribute)CustomAttributeExtensions.GetCustomAttributes(item, typeFromHandle, inherit: true).FirstOrDefault(); Type type2 = ((item is PropertyInfo) ? (item as PropertyInfo).PropertyType : (item as FieldInfo).FieldType); bool flag = Reflection.IsEnumerable(type2); MemberMapper memberMapper = new MemberMapper { AutoId = (bsonIdAttribute?.AutoId ?? true), FieldName = name, MemberName = item.Name, DataType = type2, IsEnumerable = flag, UnderlyingType = (flag ? Reflection.GetListItemType(type2) : type2), Getter = getter, Setter = setter }; BsonRefAttribute bsonRefAttribute = (BsonRefAttribute)CustomAttributeExtensions.GetCustomAttributes(item, typeFromHandle4, inherit: false).FirstOrDefault(); if (bsonRefAttribute != null && item is PropertyInfo) { RegisterDbRef(this, memberMapper, _typeNameBinder, bsonRefAttribute.Collection ?? ResolveCollectionName((item as PropertyInfo).PropertyType)); } ResolveMember?.Invoke(type, item, memberMapper); if (memberMapper.FieldName != null && !entityMapper.Members.Any((MemberMapper x) => x.FieldName.Equals(name, StringComparison.OrdinalIgnoreCase)) && !memberMapper.IsIgnore) { entityMapper.Members.Add(memberMapper); } } } return entityMapper; } protected virtual MemberInfo GetIdMember(IEnumerable members) { return Reflection.SelectMember(members, (MemberInfo x) => CustomAttributeExtensions.IsDefined(x, typeof(BsonIdAttribute), inherit: true), (MemberInfo x) => x.Name.Equals("Id", StringComparison.OrdinalIgnoreCase), (MemberInfo x) => x.Name.Equals(x.DeclaringType.Name + "Id", StringComparison.OrdinalIgnoreCase)); } protected virtual IEnumerable GetTypeMembers(Type type) { List list = new List(); BindingFlags bindingAttr = (IncludeNonPublic ? (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) : (BindingFlags.Instance | BindingFlags.Public)); list.AddRange((from x in type.GetProperties(bindingAttr) where x.CanRead && x.GetIndexParameters().Length == 0 select x).Select((Func)((PropertyInfo x) => x))); if (IncludeFields) { list.AddRange((from x in type.GetFields(bindingAttr) where !x.Name.EndsWith("k__BackingField") && !x.IsStatic select x).Select((Func)((FieldInfo x) => x))); } return list; } protected virtual CreateObject GetTypeCtor(EntityMapper mapper) { Type type = mapper.ForType; List list = new List(); bool flag = false; ConstructorInfo[] constructors = type.GetConstructors(); foreach (ConstructorInfo constructorInfo in constructors) { ParameterInfo[] parameters = constructorInfo.GetParameters(); if (parameters.Length == 0) { flag = true; continue; } KeyValuePair[] paramMap = new KeyValuePair[parameters.Length]; int j; for (j = 0; j < parameters.Length; j++) { ParameterInfo parameterInfo = parameters[j]; MemberMapper memberMapper = null; foreach (MemberMapper member in mapper.Members) { if (member.MemberName.ToLower() == parameterInfo.Name.ToLower() && member.DataType == parameterInfo.ParameterType) { memberMapper = member; break; } } if (memberMapper == null) { break; } paramMap[j] = new KeyValuePair(memberMapper.FieldName, memberMapper.DataType); } if (j < parameters.Length) { continue; } CreateObject createObject = (BsonDocument value) => Activator.CreateInstance(type, paramMap.Select((KeyValuePair x) => Deserialize(x.Value, value[x.Key])).ToArray()); if (constructorInfo.GetCustomAttribute() != null) { return createObject; } list.Add(createObject); } if (flag) { return null; } return list.FirstOrDefault(); } internal static void RegisterDbRef(BsonMapper mapper, MemberMapper member, ITypeNameBinder typeNameBinder, string collection) { member.IsDbRef = true; if (member.IsEnumerable) { RegisterDbRefList(mapper, member, typeNameBinder, collection); } else { RegisterDbRefItem(mapper, member, typeNameBinder, collection); } } private static void RegisterDbRefItem(BsonMapper mapper, MemberMapper member, ITypeNameBinder typeNameBinder, string collection) { EntityMapper entity = mapper.GetEntityMapper(member.DataType); member.Serialize = delegate(object obj, BsonMapper m) { if (obj == null) { return BsonValue.Null; } object obj2 = (entity.Id ?? throw new LiteException(0, "There is no _id field mapped in your type: " + member.DataType.FullName)).Getter(obj); BsonDocument bsonDocument = new BsonDocument { ["$id"] = m.Serialize(obj2.GetType(), obj2, 0), ["$ref"] = collection }; if (member.DataType != obj.GetType()) { bsonDocument["$type"] = typeNameBinder.GetName(obj.GetType()); } return bsonDocument; }; member.Deserialize = delegate(BsonValue bson, BsonMapper m) { if (bson == null || !bson.IsDocument) { return null; } BsonDocument asDocument = bson.AsDocument; BsonValue value = asDocument["$id"]; bool num = asDocument["$missing"] == true; bool flag = !asDocument.ContainsKey("$ref"); if (num) { return null; } if (flag) { asDocument["_id"] = value; if (asDocument.ContainsKey("$type")) { asDocument["_type"] = bson["$type"]; } return m.Deserialize(entity.ForType, asDocument); } return m.Deserialize(entity.ForType, asDocument.ContainsKey("$type") ? new BsonDocument { ["_id"] = value, ["_type"] = bson["$type"] } : new BsonDocument { ["_id"] = value }); }; } private static void RegisterDbRefList(BsonMapper mapper, MemberMapper member, ITypeNameBinder typeNameBinder, string collection) { EntityMapper entity = mapper.GetEntityMapper(member.UnderlyingType); member.Serialize = delegate(object list, BsonMapper m) { if (list == null) { return BsonValue.Null; } BsonArray bsonArray2 = new BsonArray(); MemberMapper id = entity.Id; foreach (object item in (IEnumerable)list) { if (item != null) { object obj = id.Getter(item); BsonDocument bsonDocument2 = new BsonDocument { ["$id"] = m.Serialize(obj.GetType(), obj, 0), ["$ref"] = collection }; if (member.UnderlyingType != item.GetType()) { bsonDocument2["$type"] = typeNameBinder.GetName(item.GetType()); } bsonArray2.Add(bsonDocument2); } } return bsonArray2; }; member.Deserialize = delegate(BsonValue bson, BsonMapper m) { if (!bson.IsArray) { return null; } BsonArray asArray = bson.AsArray; if (asArray.Count == 0) { return m.Deserialize(member.DataType, asArray); } BsonArray bsonArray = new BsonArray(); foreach (BsonValue item2 in asArray) { if (item2.IsDocument) { BsonDocument asDocument = item2.AsDocument; BsonValue value = asDocument["$id"]; bool flag = asDocument["$missing"] == true; bool flag2 = !asDocument.ContainsKey("$ref"); if (!flag) { if (flag2) { item2["_id"] = value; if (item2.AsDocument.ContainsKey("$type")) { item2["_type"] = item2["$type"]; } bsonArray.Add(item2); } else { BsonDocument bsonDocument = new BsonDocument { ["_id"] = value }; if (item2.AsDocument.ContainsKey("$type")) { bsonDocument["_type"] = item2["$type"]; } bsonArray.Add(bsonDocument); } } } } return m.Deserialize(member.DataType, bsonArray); }; } public virtual object ToObject(Type type, BsonDocument doc) { if (doc == null) { throw new ArgumentNullException("doc"); } if (type == typeof(BsonDocument)) { return doc; } return Deserialize(type, doc); } public virtual T ToObject(BsonDocument doc) { return (T)ToObject(typeof(T), doc); } public T Deserialize(BsonValue value) { if (value == null) { return default(T); } return (T)Deserialize(typeof(T), value); } public object Deserialize(Type type, BsonValue value) { if (OnDeserialization != null) { BsonValue bsonValue = OnDeserialization(this, type, value); if ((object)bsonValue != null) { value = bsonValue; } } if (value.IsNull) { return null; } if (Reflection.IsNullable(type)) { type = Reflection.UnderlyingTypeOf(type); } if (_customDeserializer.TryGetValue(type, out var value2)) { return value2(value); } TypeInfo typeInfo = type.GetTypeInfo(); if (type == typeof(BsonValue)) { return value; } if (type == typeof(BsonDocument)) { return value.AsDocument; } if (type == typeof(BsonArray)) { return value.AsArray; } if (_bsonTypes.Contains(type)) { return value.RawValue; } if (_basicTypes.Contains(type)) { return Convert.ChangeType(value.RawValue, type); } if (type == typeof(ulong)) { return (ulong)value.AsInt64; } if (typeInfo.IsEnum) { if (value.IsString) { return Enum.Parse(type, value.AsString); } if (value.IsNumber) { return Enum.ToObject(type, value.AsInt32); } } else { if (value.IsArray) { if (type == typeof(object)) { return DeserializeArray(typeof(object), value.AsArray); } if (type.IsArray) { return DeserializeArray(type.GetElementType(), value.AsArray); } return DeserializeList(type, value.AsArray); } if (value.IsDocument) { if (type.IsAnonymousType()) { return DeserializeAnonymousType(type, value.AsDocument); } BsonDocument asDocument = value.AsDocument; if (asDocument.TryGetValue("_type", out var value3) && value3.IsString) { Type type2 = _typeNameBinder.GetType(value3.AsString); if (type2 == null) { throw LiteException.InvalidTypedName(value3.AsString); } if (!type.IsAssignableFrom(type2)) { throw LiteException.DataTypeNotAssignable(type.FullName, type2.FullName); } type = type2; } else if (type == typeof(object)) { type = typeof(Dictionary); } EntityMapper entity = GetEntityMapper(type); if (entity.CreateInstance == null) { entity.CreateInstance = GetTypeCtor(entity) ?? ((CreateObject)((BsonDocument v) => Reflection.CreateInstance(entity.ForType))); } object obj = _typeInstantiator(type) ?? entity.CreateInstance(asDocument); if (obj is IDictionary dict) { if (obj.GetType().GetTypeInfo().IsGenericType) { Type k = type.GetGenericArguments()[0]; Type t = type.GetGenericArguments()[1]; DeserializeDictionary(k, t, dict, value.AsDocument); } else { DeserializeDictionary(typeof(object), typeof(object), dict, value.AsDocument); } } else { DeserializeObject(entity, obj, asDocument); } return obj; } } return value.RawValue; } private object DeserializeArray(Type type, BsonArray array) { Array array2 = Array.CreateInstance(type, array.Count); int num = 0; foreach (BsonValue item in array) { array2.SetValue(Deserialize(type, item), num++); } return array2; } private object DeserializeList(Type type, BsonArray value) { Type listItemType = Reflection.GetListItemType(type); IEnumerable enumerable = (IEnumerable)Reflection.CreateInstance(type); if (enumerable is IList list) { foreach (BsonValue item in value) { list.Add(Deserialize(listItemType, item)); } } else { MethodInfo method = type.GetMethod("Add", new Type[1] { listItemType }); foreach (BsonValue item2 in value) { method.Invoke(enumerable, new object[1] { Deserialize(listItemType, item2) }); } } return enumerable; } private void DeserializeDictionary(Type K, Type T, IDictionary dict, BsonDocument value) { bool isEnum = K.GetTypeInfo().IsEnum; foreach (KeyValuePair element in value.GetElements()) { object key = (isEnum ? Enum.Parse(K, element.Key) : ((K == typeof(Uri)) ? new Uri(element.Key) : Convert.ChangeType(element.Key, K))); object value2 = Deserialize(T, element.Value); dict.Add(key, value2); } } private void DeserializeObject(EntityMapper entity, object obj, BsonDocument value) { foreach (MemberMapper item in entity.Members.Where((MemberMapper x) => x.Setter != null)) { if (value.TryGetValue(item.FieldName, out var value2)) { if (item.Deserialize != null) { item.Setter(obj, item.Deserialize(value2, this)); } else { item.Setter(obj, Deserialize(item.DataType, value2)); } } } } private object DeserializeAnonymousType(Type type, BsonDocument value) { List list = new List(); ParameterInfo[] parameters = type.GetConstructors()[0].GetParameters(); foreach (ParameterInfo parameterInfo in parameters) { object obj = Deserialize(parameterInfo.ParameterType, value[parameterInfo.Name]); if (obj == null && StringComparer.OrdinalIgnoreCase.Equals(parameterInfo.Name, "Id") && value.TryGetValue("_id", out var value2)) { obj = Deserialize(parameterInfo.ParameterType, value2); } list.Add(obj); } return Activator.CreateInstance(type, list.ToArray()); } public virtual BsonDocument ToDocument(Type type, object entity) { if (entity == null) { throw new ArgumentNullException("entity"); } if (entity is BsonDocument) { return (BsonDocument)entity; } return Serialize(type, entity, 0).AsDocument; } public virtual BsonDocument ToDocument(T entity) { return ToDocument(typeof(T), entity)?.AsDocument; } public BsonValue Serialize(T obj) { return Serialize(typeof(T), obj, 0); } public BsonValue Serialize(Type type, object obj) { return Serialize(type, obj, 0); } internal BsonValue Serialize(Type type, object obj, int depth) { if (++depth > MaxDepth) { throw LiteException.DocumentMaxDepth(MaxDepth, type); } if (obj == null) { return BsonValue.Null; } if (obj is BsonValue result) { return result; } if (_customSerializer.TryGetValue(type, out var value) || _customSerializer.TryGetValue(obj.GetType(), out value)) { return value(obj); } if (obj is string) { string text = (TrimWhitespace ? (obj as string).Trim() : ((string)obj)); if (EmptyStringToNull && text.Length == 0) { return BsonValue.Null; } return new BsonValue(text); } if (obj is int) { return new BsonValue((int)obj); } if (obj is long) { return new BsonValue((long)obj); } if (obj is double) { return new BsonValue((double)obj); } if (obj is decimal) { return new BsonValue((decimal)obj); } if (obj is byte[]) { return new BsonValue((byte[])obj); } if (obj is ObjectId) { return new BsonValue((ObjectId)obj); } if (obj is Guid) { return new BsonValue((Guid)obj); } if (obj is bool) { return new BsonValue((bool)obj); } if (obj is DateTime) { return new BsonValue((DateTime)obj); } if (obj is short || obj is ushort || obj is byte || obj is sbyte) { return new BsonValue(Convert.ToInt32(obj)); } if (obj is uint) { return new BsonValue(Convert.ToInt64(obj)); } if (obj is ulong) { return new BsonValue((long)(ulong)obj); } if (obj is float) { return new BsonValue(Convert.ToDouble(obj)); } if (obj is char) { return new BsonValue(obj.ToString()); } if (obj is Enum) { if (EnumAsInteger) { return new BsonValue((int)obj); } return new BsonValue(obj.ToString()); } if (obj is IDictionary dict) { if (type == typeof(object)) { type = obj.GetType(); } Type type2 = (type.GetTypeInfo().IsGenericType ? type.GetGenericArguments()[1] : typeof(object)); return SerializeDictionary(type2, dict, depth); } if (obj is IEnumerable) { return SerializeArray(Reflection.GetListItemType(type), obj as IEnumerable, depth); } return SerializeObject(type, obj, depth); } private BsonArray SerializeArray(Type type, IEnumerable array, int depth) { BsonArray bsonArray = new BsonArray(); foreach (object item in array) { bsonArray.Add(Serialize(type, item, depth)); } return bsonArray; } private BsonDocument SerializeDictionary(Type type, IDictionary dict, int depth) { BsonDocument bsonDocument = new BsonDocument(); foreach (object key in dict.Keys) { object obj = dict[key]; string name = key.ToString(); if (key is DateTime dateTime) { name = dateTime.ToString("o"); } bsonDocument[name] = Serialize(type, obj, depth); } return bsonDocument; } private BsonDocument SerializeObject(Type type, object obj, int depth) { Type type2 = obj.GetType(); BsonDocument bsonDocument = new BsonDocument(); EntityMapper entityMapper = GetEntityMapper(type2); if (type != type2) { bsonDocument["_type"] = new BsonValue(_typeNameBinder.GetName(type2)); } foreach (MemberMapper item in entityMapper.Members.Where((MemberMapper x) => x.Getter != null)) { object obj2 = item.Getter(obj); if (obj2 != null || SerializeNullValues || !(item.FieldName != "_id")) { if (item.Serialize != null) { bsonDocument[item.FieldName] = item.Serialize(obj2, this); } else { bsonDocument[item.FieldName] = Serialize(item.DataType, obj2, depth); } } } return bsonDocument; } } internal class EntityBuilder { private readonly BsonMapper _mapper; private readonly EntityMapper _entity; private readonly ITypeNameBinder _typeNameBinder; internal EntityBuilder(BsonMapper mapper, ITypeNameBinder typeNameBinder) { _mapper = mapper; _typeNameBinder = typeNameBinder; _entity = mapper.GetEntityMapper(typeof(T)); } public EntityBuilder Ignore(Expression> member) { return GetMember(member, delegate(MemberMapper p) { _entity.Members.Remove(p); }); } public EntityBuilder Field(Expression> member, string field) { if (field.IsNullOrWhiteSpace()) { throw new ArgumentNullException("field"); } return GetMember(member, delegate(MemberMapper p) { p.FieldName = field; }); } public EntityBuilder Id(Expression> member, bool autoId = true) { return GetMember(member, delegate(MemberMapper p) { MemberMapper memberMapper = _entity.Members.FirstOrDefault((MemberMapper x) => x.FieldName == "_id"); if (memberMapper != null) { memberMapper.FieldName = _mapper.ResolveFieldName(memberMapper.MemberName); memberMapper.AutoId = false; } p.FieldName = "_id"; p.AutoId = autoId; }); } public EntityBuilder Ctor(Func createInstance) { _entity.CreateInstance = (BsonDocument v) => createInstance(v); return this; } public EntityBuilder DbRef(Expression> member, string collection = null) { return GetMember(member, delegate(MemberMapper p) { BsonMapper.RegisterDbRef(_mapper, p, _typeNameBinder, collection ?? _mapper.ResolveCollectionName(typeof(K))); }); } private EntityBuilder GetMember(Expression> member, Action action) { if (member == null) { throw new ArgumentNullException("member"); } MemberMapper member2 = _entity.GetMember(member); if (member2 == null) { throw new ArgumentNullException("Member '" + member.GetPath() + "' not found in type '" + _entity.ForType.Name + "' (use IncludeFields in BsonMapper)"); } action(member2); return this; } } internal class EntityMapper { public Type ForType { get; } public List Members { get; } = new List(); public MemberMapper Id => Members.SingleOrDefault((MemberMapper x) => x.FieldName == "_id"); public CreateObject CreateInstance { get; set; } public EntityMapper(Type forType) { ForType = forType; } public MemberMapper GetMember(Expression expr) { return Members.FirstOrDefault((MemberMapper x) => x.MemberName == expr.GetPath()); } } internal class LinqExpressionVisitor : ExpressionVisitor { private static readonly Dictionary _resolver = new Dictionary { [typeof(BsonValue)] = new BsonValueResolver(), [typeof(BsonArray)] = new BsonValueResolver(), [typeof(BsonDocument)] = new BsonValueResolver(), [typeof(Convert)] = new ConvertResolver(), [typeof(DateTime)] = new DateTimeResolver(), [typeof(int)] = new NumberResolver("INT32"), [typeof(long)] = new NumberResolver("INT64"), [typeof(decimal)] = new NumberResolver("DECIMAL"), [typeof(double)] = new NumberResolver("DOUBLE"), [typeof(ICollection)] = new ICollectionResolver(), [typeof(Enumerable)] = new EnumerableResolver(), [typeof(Guid)] = new GuidResolver(), [typeof(Math)] = new MathResolver(), [typeof(Regex)] = new RegexResolver(), [typeof(ObjectId)] = new ObjectIdResolver(), [typeof(string)] = new StringResolver(), [typeof(Nullable)] = new NullableResolver() }; private readonly BsonMapper _mapper; private readonly Expression _expr; private readonly ParameterExpression _rootParameter; private readonly BsonDocument _parameters = new BsonDocument(); private int _paramIndex; private Type _dbRefType; private readonly StringBuilder _builder = new StringBuilder(); private readonly Stack _nodes = new Stack(); public LinqExpressionVisitor(BsonMapper mapper, Expression expr) { _mapper = mapper; _expr = expr; if (expr is LambdaExpression lambdaExpression) { _rootParameter = lambdaExpression.Parameters.First(); return; } throw new NotSupportedException("Expression " + expr.ToString() + " must be a lambda expression"); } public BsonExpression Resolve(bool predicate) { Visit(_expr); Constants.ENSURE(_nodes.Count == 0, "node stack must be empty when finish expression resolve"); string text = _builder.ToString(); try { BsonExpression bsonExpression = BsonExpression.Create(text, _parameters); if (predicate && (bsonExpression.Type == BsonExpressionType.Path || bsonExpression.Type == BsonExpressionType.Call || bsonExpression.Type == BsonExpressionType.Parameter)) { text = "(" + text + " = true)"; bsonExpression = BsonExpression.Create(text, _parameters); } return bsonExpression; } catch (Exception innerException) { throw new NotSupportedException("Invalid BsonExpression when converted from Linq expression: " + _expr.ToString() + " - `" + text + "`", innerException); } } protected override Expression VisitLambda(Expression node) { Expression result = base.VisitLambda(node); _builder.Length--; return result; } protected override Expression VisitInvocation(InvocationExpression node) { Expression result = base.VisitInvocation(node); _builder.Length--; return result; } protected override Expression VisitParameter(ParameterExpression node) { _builder.Append(_rootParameter.Equals(node) ? "$" : "@"); return base.VisitParameter(node); } protected override Expression VisitMember(MemberExpression node) { bool flag = ParameterExpressionVisitor.Test(node); MemberInfo member = node.Member; if (TryGetResolver(member.DeclaringType, out var typeResolver)) { string text = typeResolver.ResolveMember(member); if (text == null) { throw new NotSupportedException("Member " + member.Name + " are not support in " + member.DeclaringType.Name + " when convert to BsonExpression (" + node.ToString() + ")."); } ResolvePattern(text, node.Expression, new Expression[0]); } else if (node.Expression != null) { _nodes.Push(node); base.Visit(node.Expression); if (flag) { string value = ResolveMember(member); _builder.Append(value); } } else { object value2 = Evaluate(node); base.Visit(Expression.Constant(value2)); } if (_nodes.Count > 0) { _nodes.Pop(); } return node; } protected override Expression VisitMethodCall(MethodCallExpression node) { if (IsMethodIndexEval(node, out var obj, out var idx)) { Visit(obj); object obj2 = Evaluate(idx, typeof(string), typeof(int)); if (obj2 is string) { _builder.Append("."); _builder.Append($"['{obj2}']"); } else { _builder.Append($"[{obj2}]"); } return node; } if (!TryGetResolver(node.Method.DeclaringType, out var typeResolver)) { if (ParameterExpressionVisitor.Test(node)) { throw new NotSupportedException("Method " + node.Method.Name + " not available to convert to BsonExpression (" + node.ToString() + ")."); } object value = Evaluate(node); base.Visit(Expression.Constant(value)); return node; } string text = typeResolver.ResolveMethod(node.Method); if (text == null) { throw new NotSupportedException("Method " + Reflection.MethodName(node.Method) + " in " + node.Method.DeclaringType.Name + " are not supported when convert to BsonExpression (" + node.ToString() + ")."); } ResolvePattern(text, node.Object, node.Arguments); return node; } protected override Expression VisitConstant(ConstantExpression node) { object value = node.Value; while (_nodes.Count > 0 && _nodes.Peek() is MemberExpression memberExpression) { if (memberExpression.Member is FieldInfo fieldInfo) { value = fieldInfo.GetValue(value); } else if (memberExpression.Member is PropertyInfo propertyInfo) { value = propertyInfo.GetValue(value); } _nodes.Pop(); } Constants.ENSURE(_nodes.Count == 0, "counter stack must be zero to eval all properties/field over object"); string text = "p" + _paramIndex++; _builder.AppendFormat("@" + text); Type type = value?.GetType(); BsonValue value2 = ((type == null) ? BsonValue.Null : ((type == typeof(string)) ? new BsonValue((string)value) : _mapper.Serialize(value.GetType(), value))); _parameters[text] = value2; return node; } protected override Expression VisitUnary(UnaryExpression node) { if (node.NodeType == ExpressionType.Not) { if (node.Operand.NodeType == ExpressionType.MemberAccess) { _builder.Append("("); Visit(node.Operand); _builder.Append(" = false)"); } else { _builder.Append("("); Visit(node.Operand); _builder.Append(")"); _builder.Append(" = false"); } } else if (node.NodeType == ExpressionType.Convert) { Type fromType = node.Operand.Type; Type type = node.Type; if ((fromType == typeof(double) || fromType == typeof(decimal)) && (type == typeof(int) || type == typeof(long))) { string methodName = "To" + type.Name.ToString(); MethodInfo methodInfo = (from x in typeof(Convert).GetMethods() where x.Name == methodName where x.GetParameters().Length == 1 && x.GetParameters().Any((ParameterInfo z) => z.ParameterType == fromType) select x).FirstOrDefault(); if (methodInfo == null) { throw new NotSupportedException("Cast from " + fromType.Name + " are not supported when convert to BsonExpression"); } MethodCallExpression node2 = Expression.Call(null, methodInfo, node.Operand); VisitMethodCall(node2); } else { base.VisitUnary(node); } } else if (node.NodeType == ExpressionType.ArrayLength) { _builder.Append("LENGTH("); Visit(node.Operand); _builder.Append(")"); } else { base.VisitUnary(node); } return node; } protected override Expression VisitNew(NewExpression node) { if (node.Members == null) { if (!TryGetResolver(node.Type, out var typeResolver)) { throw new NotSupportedException($"New instance are not supported for {node.Type} when convert to BsonExpression ({node.ToString()})."); } string text = typeResolver.ResolveCtor(node.Constructor); if (text == null) { throw new NotSupportedException("Constructor for " + node.Type.Name + " are not supported when convert to BsonExpression (" + node.ToString() + ")."); } ResolvePattern(text, null, node.Arguments); } else { _builder.Append("{ "); for (int i = 0; i < node.Members.Count; i++) { MemberInfo memberInfo = node.Members[i]; _builder.Append((i > 0) ? ", " : ""); _builder.AppendFormat("'{0}': ", memberInfo.Name); Visit(node.Arguments[i]); } _builder.Append(" }"); } return node; } protected override Expression VisitMemberInit(MemberInitExpression node) { if (node.NewExpression.Constructor.GetParameters().Length != 0) { throw new NotSupportedException($"New instance of {node.Type} are not supported because contains ctor with parameter. Try use only property initializers: `new {node.Type.Name} {{ PropA = 1, PropB == \"John\" }}`."); } _builder.Append("{"); for (int i = 0; i < node.Bindings.Count; i++) { MemberAssignment memberAssignment = node.Bindings[i] as MemberAssignment; string text = ResolveMember(memberAssignment.Member); _builder.Append((i > 0) ? ", " : ""); _builder.Append(text.Substring(1)); _builder.Append(":"); Visit(memberAssignment.Expression); } _builder.Append("}"); return node; } protected override Expression VisitNewArray(NewArrayExpression node) { _builder.Append("[ "); for (int i = 0; i < node.Expressions.Count; i++) { _builder.Append((i > 0) ? ", " : ""); Visit(node.Expressions[i]); } _builder.Append(" ]"); return node; } protected override Expression VisitBinary(BinaryExpression node) { bool ensurePredicate = node.NodeType == ExpressionType.AndAlso || node.NodeType == ExpressionType.OrElse; if (node.NodeType == ExpressionType.Coalesce) { return VisitCoalesce(node); } if (node.NodeType == ExpressionType.ArrayIndex) { return VisitArrayIndex(node); } string @operator = GetOperator(node.NodeType); _builder.Append("("); VisitAsPredicate(node.Left, ensurePredicate); _builder.Append(@operator); if (!_mapper.EnumAsInteger && node.Left.NodeType == ExpressionType.Convert && node.Left is UnaryExpression unaryExpression && unaryExpression.Operand.Type.GetTypeInfo().IsEnum && unaryExpression.Type == typeof(int)) { VisitAsPredicate(Expression.Constant(Enum.GetName(unaryExpression.Operand.Type, Evaluate(node.Right))), ensurePredicate); } else { VisitAsPredicate(node.Right, ensurePredicate); } _builder.Append(")"); return node; } protected override Expression VisitConditional(ConditionalExpression node) { _builder.Append("IIF("); Visit(node.Test); _builder.Append(", "); Visit(node.IfTrue); _builder.Append(", "); Visit(node.IfFalse); _builder.Append(")"); return node; } private Expression VisitCoalesce(BinaryExpression node) { _builder.Append("COALESCE("); Visit(node.Left); _builder.Append(", "); Visit(node.Right); _builder.Append(")"); return node; } private Expression VisitArrayIndex(BinaryExpression node) { Visit(node.Left); _builder.Append("["); object value = Evaluate(node.Right, typeof(int)); _builder.Append(value); _builder.Append("]"); return node; } private void ResolvePattern(string pattern, Expression obj, IList args) { Tokenizer tokenizer = new Tokenizer(pattern); while (!tokenizer.EOF) { Token token = tokenizer.ReadToken(eatWhitespace: false); if (token.Type == TokenType.Hashtag) { Visit(obj); } else if (token.Type == TokenType.At && tokenizer.LookAhead(eatWhitespace: false).Type == TokenType.Int) { int index = Convert.ToInt32(tokenizer.ReadToken(eatWhitespace: false).Expect(TokenType.Int).Value); Visit(args[index]); } else if (token.Type == TokenType.Percent) { VisitEnumerablePredicate(args[1] as LambdaExpression); } else { _builder.Append((token.Type == TokenType.String) ? ("'" + token.Value + "'") : token.Value); } } } private void VisitEnumerablePredicate(LambdaExpression lambda) { Expression body = lambda.Body; if (body is BinaryExpression binaryExpression) { if (binaryExpression.Left.NodeType != ExpressionType.Parameter) { throw new LiteException(0, "Any/All requires simple parameter on left side. Eg: `x => x.Phones.Select(p => p.Number).Any(n => n > 5)`"); } string @operator = GetOperator(binaryExpression.NodeType); _builder.Append(@operator); VisitAsPredicate(binaryExpression.Right, ensurePredicate: false); return; } if (body is MethodCallExpression methodCallExpression) { if (methodCallExpression.Object.NodeType != ExpressionType.Parameter) { throw new NotSupportedException("Any/All requires simple parameter on left side. Eg: `x.Customers.Select(c => c.Name).Any(n => n.StartsWith('J'))`"); } if (!TryGetResolver(methodCallExpression.Method.DeclaringType, out var typeResolver)) { throw new NotSupportedException("Method " + methodCallExpression.Method.Name + " not available to convert to BsonExpression inside Any/All call."); } string text = typeResolver.ResolveMethod(methodCallExpression.Method); if (text == null || !text.StartsWith("#")) { throw new NotSupportedException("Method " + methodCallExpression.Method.Name + " not available to convert to BsonExpression inside Any/All call."); } ResolvePattern(text.Substring(1), methodCallExpression.Object, methodCallExpression.Arguments); return; } throw new LiteException(0, "When using Any/All method test do only simple predicate variable. Eg: `x => x.Phones.Select(p => p.Number).Any(n => n > 5)`"); } private string GetOperator(ExpressionType nodeType) { return nodeType switch { ExpressionType.Add => " + ", ExpressionType.Multiply => " * ", ExpressionType.Subtract => " - ", ExpressionType.Divide => " / ", ExpressionType.Equal => " = ", ExpressionType.NotEqual => " != ", ExpressionType.GreaterThan => " > ", ExpressionType.GreaterThanOrEqual => " >= ", ExpressionType.LessThan => " < ", ExpressionType.LessThanOrEqual => " <= ", ExpressionType.And => " AND ", ExpressionType.AndAlso => " AND ", ExpressionType.Or => " OR ", ExpressionType.OrElse => " OR ", _ => throw new NotSupportedException($"Operator not supported {nodeType}"), }; } private string ResolveMember(MemberInfo member) { string name = member.Name; bool flag = _dbRefType != null && member.DeclaringType.IsAssignableFrom(_dbRefType); MemberMapper memberMapper = _mapper.GetEntityMapper(member.DeclaringType).Members.FirstOrDefault((MemberMapper x) => x.MemberName == name); if (memberMapper == null) { throw new NotSupportedException($"Member {name} not found on BsonMapper for type {member.DeclaringType}."); } _dbRefType = (memberMapper.IsDbRef ? memberMapper.UnderlyingType : null); return "." + ((flag && memberMapper.FieldName == "_id") ? "$id" : memberMapper.FieldName); } private bool IsMethodIndexEval(MethodCallExpression node, out Expression obj, out Expression idx) { MethodInfo method = node.Method; _ = method.DeclaringType; ParameterInfo[] parameters = method.GetParameters(); if (method.Name == "get_Item" && parameters.Length == 1 && (parameters[0].ParameterType == typeof(int) || parameters[0].ParameterType == typeof(string))) { obj = node.Object; idx = node.Arguments[0]; return true; } obj = null; idx = null; return false; } private void VisitAsPredicate(Expression expr, bool ensurePredicate) { ensurePredicate = ensurePredicate && (expr.NodeType == ExpressionType.MemberAccess || expr.NodeType == ExpressionType.Call || expr.NodeType == ExpressionType.Invoke || expr.NodeType == ExpressionType.Constant); if (ensurePredicate) { _builder.Append("("); _builder.Append("("); base.Visit(expr); _builder.Append(")"); _builder.Append(" = true)"); } else { base.Visit(expr); } } private object Evaluate(Expression expr, params Type[] validTypes) { object value = null; if (expr.NodeType == ExpressionType.Constant) { ConstantExpression constantExpression = (ConstantExpression)expr; value = constantExpression.Value; } else { Delegate @delegate = Expression.Lambda(expr).Compile(); value = @delegate.DynamicInvoke(); } if (validTypes.Length != 0 && value == null) { throw new NotSupportedException($"Expression {expr} can't return null value"); } if (validTypes.Length != 0 && !validTypes.Any((Type x) => x == value.GetType())) { throw new NotSupportedException(string.Format("Expression {0} must return on of this types: {1}", expr, string.Join(", ", validTypes.Select((Type x) => "`" + x.Name + "`")))); } return value; } private bool TryGetResolver(Type declaringType, out ITypeResolver typeResolver) { bool num = Reflection.IsCollection(declaringType); bool flag = Reflection.IsEnumerable(declaringType); bool flag2 = Reflection.IsNullable(declaringType); Type key = (num ? typeof(ICollection) : (flag ? typeof(Enumerable) : (flag2 ? typeof(Nullable) : declaringType))); return _resolver.TryGetValue(key, out typeResolver); } } internal class ParameterExpressionVisitor : ExpressionVisitor { public bool IsParameter { get; private set; } protected override Expression VisitParameter(ParameterExpression node) { IsParameter = true; return base.VisitParameter(node); } public static bool Test(Expression node) { ParameterExpressionVisitor parameterExpressionVisitor = new ParameterExpressionVisitor(); parameterExpressionVisitor.Visit(node); return parameterExpressionVisitor.IsParameter; } } internal class BsonValueResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { return null; } public string ResolveMember(MemberInfo member) { switch (member.Name) { case "AsInt32": case "AsInt64": case "AsArray": case "AsDateTime": case "AsDocument": case "AsObjectId": case "AsString": case "AsBinary": case "AsDouble": case "AsBoolean": case "AsDecimal": case "AsGuid": return "#"; case "IsNull": return "IS_NULL(#)"; case "IsArray": return "IS_ARRAY(#)"; case "IsDocument": return "IS_DOCUMENT(#)"; case "IsInt32": return "IS_INT32(#)"; case "IsInt64": return "IS_INT64(#)"; case "IsDouble": return "IS_DOUBLE(#)"; case "IsDecimal": return "IS_DECIMAL(#)"; case "IsNumber": return "IS_NUMBER(#)"; case "IsBinary": return "IS_BINARY(#)"; case "IsBoolean": return "IS_BOOLEAN(#)"; case "IsString": return "IS_STRING(#)"; case "IsObjectId": return "IS_OBJECTID(#)"; case "IsGuid": return "IS_GUID(#)"; case "IsDateTime": return "IS_DATETIME(#)"; case "IsMinValue": return "IS_MINVALUE(#)"; case "IsMaxValue": return "IS_MAXVALUE(#)"; default: return null; } } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class ConvertResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { return method.Name switch { "ToInt32" => "INT32(@0)", "ToInt64" => "INT64(@0)", "ToDouble" => "DOUBLE(@0)", "ToDecimal" => "DECIMAL(@0)", "ToDateTime" => "DATE(@0)", "FromBase64String" => "BINARY(@0)", "ToBoolean" => "BOOL(@0)", "ToString" => "STRING(@0)", _ => null, }; } public string ResolveMember(MemberInfo member) { return null; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class DateTimeResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { switch (method.Name) { case "AddYears": return "DATEADD('y', @0, #)"; case "AddMonths": return "DATEADD('M', @0, #)"; case "AddDays": return "DATEADD('d', @0, #)"; case "AddHours": return "DATEADD('h', @0, #)"; case "AddMinutes": return "DATEADD('m', @0, #)"; case "AddSeconds": return "DATEADD('s', @0, #)"; case "ToString": { ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length == 0) { return "STRING(#)"; } if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string)) { return "FORMAT(#, @0)"; } break; } case "ToUniversalTime": return "TO_UTC(#)"; case "Parse": return "DATETIME(@0)"; case "Equals": return "# = @0"; } return null; } public string ResolveMember(MemberInfo member) { return member.Name switch { "Now" => "NOW()", "UtcNow" => "NOW_UTC()", "Today" => "TODAY()", "Year" => "YEAR(#)", "Month" => "MONTH(#)", "Day" => "DAY(#)", "Hour" => "HOUR(#)", "Minute" => "MINUTE(#)", "Second" => "SECOND(#)", "Date" => "DATETIME(YEAR(#), MONTH(#), DAY(#))", "ToLocalTime" => "TO_LOCAL(#)", "ToUniversalTime" => "TO_UTC(#)", _ => null, }; } public string ResolveCtor(ConstructorInfo ctor) { ParameterInfo[] parameters = ctor.GetParameters(); if (parameters.Length == 3 && parameters[0].ParameterType == typeof(int) && parameters[1].ParameterType == typeof(int) && parameters[2].ParameterType == typeof(int)) { return "DATETIME(@0, @1, @2)"; } return null; } } internal class EnumerableResolver : ITypeResolver { public virtual string ResolveMethod(MethodInfo method) { switch (Reflection.MethodName(method, 1)) { case "AsEnumerable()": return "@0[*]"; case "get_Item(int)": return "#[@0]"; case "ElementAt(int)": return "@0[@1]"; case "Single()": case "First()": case "SingleOrDefault()": case "FirstOrDefault()": return "@0[0]"; case "Last()": case "LastOrDefault()": return "@0[-1]"; case "Single(Func)": case "First(Func)": case "SingleOrDefault(Func)": case "FirstOrDefault(Func)": return "FIRST(FILTER(@0 => @1))"; case "Last(Func)": case "LastOrDefault(Func)": return "LAST(FILTER(@0 => @1))"; case "Where(Func)": return "FILTER(@0 => @1)"; case "Select(Func)": return "MAP(@0 => @1)"; case "Count()": return "COUNT(@0)"; case "Sum()": return "SUM(@0)"; case "Average()": return "AVG(@0)"; case "Max()": return "MAX(@0)"; case "Min()": return "MIN(@0)"; case "Count(Func)": return "COUNT(FILTER(@0 => @1))"; case "Sum(Func)": return "SUM(MAP(@0 => @1))"; case "Average(Func)": return "AVG(MAP(@0 => @1))"; case "Max(Func)": return "MAX(MAP(@0 => @1))"; case "Min(Func)": return "MIN(MAP(@0 => @1))"; case "ToList()": case "ToArray()": return "ARRAY(@0)"; case "Any(Func)": return "@0 ANY %"; case "All(Func)": return "@0 ALL %"; case "Any()": return "COUNT(@0) > 0"; default: if (method.Name == "Contains") { return "@0 ANY = @1"; } return null; } } public virtual string ResolveMember(MemberInfo member) { string name = member.Name; if (!(name == "Length")) { if (name == "Count") { return "COUNT(#)"; } return null; } return "LENGTH(#)"; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class GuidResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { return method.Name switch { "ToString" => "STRING(#)", "NewGuid" => "GUID()", "Parse" => "GUID(@0)", "TryParse" => throw new NotSupportedException("There is no TryParse translate. Use Guid.Parse()"), "Equals" => "# = @0", _ => null, }; } public string ResolveMember(MemberInfo member) { if (member.Name == "Empty") { return "GUID('00000000-0000-0000-0000-000000000000')"; } return null; } public string ResolveCtor(ConstructorInfo ctor) { ParameterInfo[] parameters = ctor.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string)) { return "GUID(@0)"; } return null; } } internal class ICollectionResolver : EnumerableResolver { public override string ResolveMethod(MethodInfo method) { if (method.Name == "Contains") { return "# ANY = @0"; } return base.ResolveMethod(method); } } internal interface ITypeResolver { string ResolveMethod(MethodInfo method); string ResolveMember(MemberInfo member); string ResolveCtor(ConstructorInfo ctor); } internal class MathResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { int num = method.GetParameters().Length; switch (method.Name) { case "Abs": return "ABS(@0)"; case "Pow": return "POW(@0, @1)"; case "Round": if (num != 2) { throw new ArgumentOutOfRangeException("Method Round need 2 arguments when convert to BsonExpression"); } return "ROUND(@0, @1)"; default: return null; } } public string ResolveMember(MemberInfo member) { return null; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class NullableResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { return null; } public string ResolveMember(MemberInfo member) { string name = member.Name; if (!(name == "HasValue")) { if (name == "Value") { return "#"; } return null; } return "(IS_NULL(#) = false)"; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class NumberResolver : ITypeResolver { private readonly string _parseMethod; public NumberResolver(string parseMethod) { _parseMethod = parseMethod; } public string ResolveMethod(MethodInfo method) { switch (method.Name) { case "ToString": { ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length == 0) { return "STRING(#)"; } if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string)) { return "FORMAT(#, @0)"; } break; } case "Parse": return _parseMethod + "(@0)"; case "Equals": return "# = @0"; } return null; } public string ResolveMember(MemberInfo member) { return null; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class ObjectIdResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { string name = method.Name; if (!(name == "ToString")) { if (name == "Equals") { return "# = @0"; } return null; } return "STRING(#)"; } public string ResolveMember(MemberInfo member) { string name = member.Name; if (!(name == "Empty")) { if (name == "CreationTime") { return "OID_CREATIONTIME(#)"; } return null; } return "OBJECTID('000000000000000000000000')"; } public string ResolveCtor(ConstructorInfo ctor) { ParameterInfo[] parameters = ctor.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string)) { return "OBJECTID(@0)"; } return null; } } internal class RegexResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { string name = method.Name; if (!(name == "Split")) { if (name == "IsMatch") { return "IS_MATCH(@0, @1)"; } return null; } return "SPLIT(@0, @1, true)"; } public string ResolveMember(MemberInfo member) { return null; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class StringResolver : ITypeResolver { public string ResolveMethod(MethodInfo method) { int num = method.GetParameters().Length; switch (method.Name) { case "Count": return "LENGTH(#)"; case "Trim": return "TRIM(#)"; case "TrimStart": return "LTRIM(#)"; case "TrimEnd": return "RTRIM(#)"; case "ToUpper": return "UPPER(#)"; case "ToUpperInvariant": return "UPPER(#)"; case "ToLower": return "LOWER(#)"; case "ToLowerInvariant": return "LOWER(#)"; case "Replace": return "REPLACE(#, @0, @1)"; case "PadLeft": return "LPAD(#, @0, @1)"; case "RightLeft": return "RPAD(#, @0, @1)"; case "IndexOf": if (num != 1) { return "INDEXOF(#, @0, @1)"; } return "INDEXOF(#, @0)"; case "Substring": if (num != 1) { return "SUBSTRING(#, @0, @1)"; } return "SUBSTRING(#, @0)"; case "StartsWith": return "# LIKE (@0 + '%')"; case "Contains": return "# LIKE ('%' + @0 + '%')"; case "EndsWith": return "# LIKE ('%' + @0)"; case "ToString": return "#"; case "Equals": return "# = @0"; case "IsNullOrEmpty": return "(LENGTH(@0) = 0)"; case "IsNullOrWhiteSpace": return "(LENGTH(TRIM(@0)) = 0)"; case "Format": throw new NotImplementedException(); case "Join": throw new NotImplementedException(); default: return null; } } public string ResolveMember(MemberInfo member) { string name = member.Name; if (!(name == "Length")) { if (name == "Empty") { return "''"; } return null; } return "LENGTH(#)"; } public string ResolveCtor(ConstructorInfo ctor) { return null; } } internal class MemberMapper { public bool AutoId { get; set; } public string MemberName { get; set; } public Type DataType { get; set; } public string FieldName { get; set; } public GenericGetter Getter { get; set; } public GenericSetter Setter { get; set; } public Func Serialize { get; set; } public Func Deserialize { get; set; } public bool IsDbRef { get; set; } public bool IsEnumerable { get; set; } public Type UnderlyingType { get; set; } public bool IsIgnore { get; set; } } internal delegate object CreateObject(BsonDocument value); internal delegate void GenericSetter(object target, object value); internal delegate object GenericGetter(object obj); internal class Reflection { private static readonly Dictionary _cacheCtor = new Dictionary(); public static readonly Dictionary ConvertType = new Dictionary { [typeof(DateTime)] = typeof(BsonValue).GetProperty("AsDateTime"), [typeof(decimal)] = typeof(BsonValue).GetProperty("AsDecimal"), [typeof(double)] = typeof(BsonValue).GetProperty("AsDouble"), [typeof(long)] = typeof(BsonValue).GetProperty("AsInt64"), [typeof(int)] = typeof(BsonValue).GetProperty("AsInt32"), [typeof(bool)] = typeof(BsonValue).GetProperty("AsBoolean"), [typeof(byte[])] = typeof(BsonValue).GetProperty("AsBinary"), [typeof(BsonDocument)] = typeof(BsonValue).GetProperty("AsDocument"), [typeof(BsonArray)] = typeof(BsonValue).GetProperty("AsArray"), [typeof(ObjectId)] = typeof(BsonValue).GetProperty("AsObjectId"), [typeof(string)] = typeof(BsonValue).GetProperty("AsString"), [typeof(Guid)] = typeof(BsonValue).GetProperty("AsGuid") }; public static readonly PropertyInfo DocumentItemProperty = (from x in typeof(BsonDocument).GetProperties() where x.Name == "Item" && x.GetGetMethod().GetParameters().First() .ParameterType == typeof(string) select x).First(); private static readonly Dictionary _cacheName = new Dictionary(); public static object CreateInstance(Type type) { try { if (_cacheCtor.TryGetValue(type, out var value)) { return value(null); } } catch (Exception inner) { throw LiteException.InvalidCtor(type, inner); } lock (_cacheCtor) { try { if (_cacheCtor.TryGetValue(type, out var value2)) { return value2(null); } TypeInfo typeInfo = type.GetTypeInfo(); if (typeInfo.IsClass) { _cacheCtor.Add(type, value2 = CreateClass(type)); } else { if (typeInfo.IsInterface) { if (typeInfo.IsGenericType) { Type genericTypeDefinition = type.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(ISet<>)) { return CreateInstance(GetGenericSetOfType(UnderlyingTypeOf(type))); } if (genericTypeDefinition == typeof(IDictionary<, >)) { Type k = type.GetGenericArguments()[0]; Type v = type.GetGenericArguments()[1]; return CreateInstance(GetGenericDictionaryOfType(k, v)); } if (genericTypeDefinition == typeof(IList<>) || genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>) || typeof(IEnumerable).IsAssignableFrom(genericTypeDefinition)) { return CreateInstance(GetGenericListOfType(UnderlyingTypeOf(type))); } } throw LiteException.InvalidCtor(type, null); } _cacheCtor.Add(type, value2 = CreateStruct(type)); } return value2(null); } catch (Exception inner2) { throw LiteException.InvalidCtor(type, inner2); } } } public static bool IsNullable(Type type) { if (!type.GetTypeInfo().IsGenericType) { return false; } return type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)); } public static Type UnderlyingTypeOf(Type type) { if (!type.GetTypeInfo().IsGenericType) { return type; } return type.GetGenericArguments()[0]; } public static Type GetGenericListOfType(Type type) { return typeof(List<>).MakeGenericType(type); } public static Type GetGenericSetOfType(Type type) { return typeof(HashSet<>).MakeGenericType(type); } public static Type GetGenericDictionaryOfType(Type k, Type v) { return typeof(Dictionary<, >).MakeGenericType(k, v); } public static Type GetListItemType(Type listType) { if (listType.IsArray) { return listType.GetElementType(); } Type[] interfaces = listType.GetInterfaces(); foreach (Type type in interfaces) { if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) { return type.GetGenericArguments()[0]; } if (listType.GetTypeInfo().IsGenericType && type == typeof(IEnumerable)) { return listType.GetGenericArguments()[0]; } } return typeof(object); } public static bool IsEnumerable(Type type) { if (type == typeof(IEnumerable) || type.IsArray) { return true; } if (type == typeof(string)) { return false; } Type[] interfaces = type.GetInterfaces(); foreach (Type type2 in interfaces) { if (type2.GetTypeInfo().IsGenericType && type2.GetGenericTypeDefinition() == typeof(IEnumerable<>)) { return true; } } return false; } public static bool IsSimpleType(Type type) { if (!(type == typeof(string)) && !(type == typeof(bool)) && !(type == typeof(byte)) && !(type == typeof(sbyte)) && !(type == typeof(short)) && !(type == typeof(int)) && !(type == typeof(long)) && !(type == typeof(ushort)) && !(type == typeof(uint)) && !(type == typeof(ulong)) && !(type == typeof(double)) && !(type == typeof(float)) && !(type == typeof(decimal)) && !(type == typeof(ObjectId)) && !(type == typeof(DateTime))) { return type == typeof(Guid); } return true; } public static bool IsCollection(Type type) { if (!type.GetTypeInfo().IsGenericType || !type.GetGenericTypeDefinition().Equals(typeof(ICollection<>))) { return type.GetInterfaces().Any((Type x) => x == typeof(ICollection) || (x.GetTypeInfo().IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>))); } return true; } public static bool IsDictionary(Type type) { if (!type.GetTypeInfo().IsGenericType || !type.GetGenericTypeDefinition().Equals(typeof(IDictionary<, >))) { return type.GetInterfaces().Any((Type x) => x == typeof(IDictionary) || (x.GetTypeInfo().IsGenericType && x.GetGenericTypeDefinition().Equals(typeof(IDictionary<, >)))); } return true; } public static MemberInfo SelectMember(IEnumerable members, params Func[] predicates) { foreach (Func predicate in predicates) { MemberInfo memberInfo = members.FirstOrDefault(predicate); if (memberInfo != null) { return memberInfo; } } return null; } public static string MethodName(MethodInfo method, int skipParameters = 0) { lock (_cacheName) { if (_cacheName.TryGetValue(method, out var value)) { return value; } value = MethodNameInternal(method, skipParameters); _cacheName.Add(method, value); return value; } } private static string MethodNameInternal(MethodInfo method, int skipParameters = 0) { StringBuilder stringBuilder = new StringBuilder(method.Name + "("); int num = 0; foreach (ParameterInfo item in method.GetParameters().Skip(skipParameters)) { if (num++ > 0) { stringBuilder.Append(","); } stringBuilder.Append(FriendlyTypeName(item.ParameterType)); if (item.ParameterType.GetTypeInfo().IsGenericType) { Type[] genericArguments = item.ParameterType.GetGenericTypeDefinition().GetGenericArguments(); stringBuilder.Append("<"); stringBuilder.Append(string.Join(",", genericArguments.Select((Type x) => FriendlyTypeName(x)))); stringBuilder.Append(">"); } } stringBuilder.Append(")"); return stringBuilder.ToString(); } private static string FriendlyTypeName(Type type) { int num = type.Name.IndexOf("`"); switch (type.FullName) { case "System.Object": return "object"; case "System.String": return "string"; case "System.Boolean": return "bool"; case "System.Byte": return "byte"; case "System.Char": return "char"; case "System.Decimal": return "decimal"; case "System.Double": return "double"; case "System.Int16": return "short"; case "System.Int32": return "int"; case "System.Int64": return "long"; case "System.SByte": return "sbyte"; case "System.Single": return "float"; case "System.UInt16": return "ushort"; case "System.UInt32": return "uint"; case "System.UInt64": return "ulong"; default: if (num <= 0) { return type.Name; } return type.Name.Substring(0, num); } } public static CreateObject CreateClass(Type type) { ParameterExpression parameterExpression = Expression.Parameter(typeof(BsonDocument), "_doc"); return Expression.Lambda(Expression.New(type), new ParameterExpression[1] { parameterExpression }).Compile(); } public static CreateObject CreateStruct(Type type) { ParameterExpression parameterExpression = Expression.Parameter(typeof(BsonDocument), "_doc"); return Expression.Lambda(Expression.Convert(Expression.New(type), typeof(object)), new ParameterExpression[1] { parameterExpression }).Compile(); } public static GenericGetter CreateGenericGetter(Type type, MemberInfo memberInfo) { if (memberInfo == null) { throw new ArgumentNullException("memberInfo"); } if (memberInfo is PropertyInfo && !(memberInfo as PropertyInfo).CanRead) { return null; } ParameterExpression parameterExpression = Expression.Parameter(typeof(object), "o"); return Expression.Lambda(Expression.Convert(Expression.MakeMemberAccess(Expression.Convert(parameterExpression, memberInfo.DeclaringType), memberInfo), typeof(object)), new ParameterExpression[1] { parameterExpression }).Compile(); } public static GenericSetter CreateGenericSetter(Type type, MemberInfo memberInfo) { if (memberInfo == null) { throw new ArgumentNullException("memberInfo"); } FieldInfo fieldInfo = memberInfo as FieldInfo; PropertyInfo propertyInfo = memberInfo as PropertyInfo; if (memberInfo is PropertyInfo && !propertyInfo.CanWrite) { return null; } if (type.GetTypeInfo().IsValueType) { if (!(memberInfo is FieldInfo)) { return delegate(object t, object v) { propertyInfo.SetValue(t, v, null); }; } return fieldInfo.SetValue; } Type type2 = ((memberInfo is PropertyInfo) ? propertyInfo.PropertyType : fieldInfo.FieldType); ParameterExpression parameterExpression = Expression.Parameter(typeof(object), "obj"); ParameterExpression parameterExpression2 = Expression.Parameter(typeof(object), "val"); UnaryExpression expression = Expression.Convert(parameterExpression, type); UnaryExpression right = Expression.ConvertChecked(parameterExpression2, type2); return Expression.Lambda(Expression.Convert(Expression.Assign((memberInfo is PropertyInfo) ? Expression.Property(expression, propertyInfo) : Expression.Field(expression, fieldInfo), right), typeof(object)), new ParameterExpression[2] { parameterExpression, parameterExpression2 }).Compile(); } } internal class DefaultTypeNameBinder : ITypeNameBinder { private static readonly HashSet _disallowedTypeNames = new HashSet(StringComparer.OrdinalIgnoreCase) { "System.Workflow.ComponentModel.AppSettings", "System.Core", "WinRT.BaseActivationFactory", "System.Data", "System.Windows.Data.ObjectDataProvider", "System.CodeDom.Compiler.CompilerResults", "System.Collections.ArrayList", "System.Diagnostics.Process", "System.Diagnostics.ProcessStartInfo", "System.Management.Automation", "System.Windows.Markup.XamlReader", "System.Web.Security.RolePrincipal", "System.Security.Principal.WindowsIdentity", "System.Security.Principal.WindowsPrincipal", "Microsoft.VisualStudio.Text.Formatting.TextFormattingRunProperties", "System.Drawing.Design.ToolboxItemContainer", "Microsoft.IdentityModel.Claims.WindowsClaimsIdentity", "System.Resources.ResXResourceReader", "System.Resources.ResXResourceWriter", "System.Windows.Forms", "Microsoft.ApplicationId.Framework.InfiniteProgressPage", "Microsoft.VisualBasic.Logging.FileLogTraceListener", "Grpc.Core.Internal.UnmanagedLibrary", "MongoDB.Libmongocrypt.LibraryLoader+WindowsLibrary", "Xunit.Xunit1Executor", "Apache.NMS.ActiveMQ.Commands.ActiveMQObjectMessage", "Apache.NMS.ActiveMQ.Transport.Failover.FailoverTransport", "Apache.NMS.ActiveMQ.Util.IdGenerator", "Xunit.Sdk.TestFrameworkDiscoverer+PreserveWorkingFolder", "Xunit.Xunit1AssemblyInfo", "Amazon.Runtime.Internal.Util.OptimisticLockedTextFile", "Microsoft.Azure.Cosmos.Query.Core.QueryPlan.QueryPartitionProvider", "NLog.Internal.FileAppenders.SingleProcessFileAppender", "NLog.Targets.FileTarget", "Google.Apis.Util.Store.FileDataStore" }; public static DefaultTypeNameBinder Instance { get; } = new DefaultTypeNameBinder(); private DefaultTypeNameBinder() { } public string GetName(Type type) { return type.FullName + ", " + type.GetTypeInfo().Assembly.GetName().Name; } public Type GetType(string name) { Type type = Type.GetType(name); if (type == null) { return null; } if (_disallowedTypeNames.Contains(type.FullName)) { throw LiteException.IllegalDeserializationType(type.FullName); } return type; } } internal interface ITypeNameBinder { string GetName(Type type); Type GetType(string name); } internal class SharedDataReader : IBsonDataReader, IDisposable { private readonly IBsonDataReader _reader; private readonly Action _dispose; private bool _disposed; public BsonValue this[string field] => _reader[field]; public string Collection => _reader.Collection; public BsonValue Current => _reader.Current; public bool HasValues => _reader.HasValues; public SharedDataReader(IBsonDataReader reader, Action dispose) { _reader = reader; _dispose = dispose; } public bool Read() { return _reader.Read(); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } ~SharedDataReader() { Dispose(disposing: false); } protected virtual void Dispose(bool disposing) { if (!_disposed) { _disposed = true; if (disposing) { _reader.Dispose(); _dispose(); } } } } internal class SharedEngine : ILiteEngine, IDisposable { private readonly EngineSettings _settings; private readonly Mutex _mutex; private LiteEngine _engine; private bool _transactionRunning; public SharedEngine(EngineSettings settings) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown _settings = settings; string text = Path.GetFullPath(settings.Filename).ToLower().Sha1(); try { MutexAccessRule val = new MutexAccessRule((IdentityReference)new SecurityIdentifier(WellKnownSidType.WorldSid, null), (MutexRights)2031617, AccessControlType.Allow); MutexSecurity val2 = new MutexSecurity(); val2.AddAccessRule(val); bool flag = default(bool); _mutex = new Mutex(false, "Global\\" + text + ".Mutex", ref flag, val2); } catch (NotSupportedException inner) { throw new PlatformNotSupportedException("Shared mode is not supported in platforms that do not implement named mutex.", inner); } } private void OpenDatabase() { try { _mutex.WaitOne(); } catch (AbandonedMutexException) { } if (!_transactionRunning && _engine == null) { try { _engine = new LiteEngine(_settings); } catch { _mutex.ReleaseMutex(); throw; } } } private void CloseDatabase() { if (!_transactionRunning && _engine != null) { _engine.Dispose(); _engine = null; } _mutex.ReleaseMutex(); } public bool BeginTrans() { OpenDatabase(); try { _transactionRunning = _engine.BeginTrans(); return _transactionRunning; } catch { CloseDatabase(); throw; } } public bool Commit() { if (_engine == null) { return false; } try { return _engine.Commit(); } finally { _transactionRunning = false; CloseDatabase(); } } public bool Rollback() { if (_engine == null) { return false; } try { return _engine.Rollback(); } finally { _transactionRunning = false; CloseDatabase(); } } public IBsonDataReader Query(string collection, Query query) { OpenDatabase(); return new SharedDataReader(_engine.Query(collection, query), delegate { CloseDatabase(); }); } public BsonValue Pragma(string name) { OpenDatabase(); try { return _engine.Pragma(name); } finally { CloseDatabase(); } } public bool Pragma(string name, BsonValue value) { OpenDatabase(); try { return _engine.Pragma(name, value); } finally { CloseDatabase(); } } public int Checkpoint() { OpenDatabase(); try { return _engine.Checkpoint(); } finally { CloseDatabase(); } } public long Rebuild(RebuildOptions options) { OpenDatabase(); try { return _engine.Rebuild(options); } finally { CloseDatabase(); } } public int Insert(string collection, IEnumerable docs, BsonAutoId autoId) { OpenDatabase(); try { return _engine.Insert(collection, docs, autoId); } finally { CloseDatabase(); } } public int Update(string collection, IEnumerable docs) { OpenDatabase(); try { return _engine.Update(collection, docs); } finally { CloseDatabase(); } } public int UpdateMany(string collection, BsonExpression extend, BsonExpression predicate) { OpenDatabase(); try { return _engine.UpdateMany(collection, extend, predicate); } finally { CloseDatabase(); } } public int Upsert(string collection, IEnumerable docs, BsonAutoId autoId) { OpenDatabase(); try { return _engine.Upsert(collection, docs, autoId); } finally { CloseDatabase(); } } public int Delete(string collection, IEnumerable ids) { OpenDatabase(); try { return _engine.Delete(collection, ids); } finally { CloseDatabase(); } } public int DeleteMany(string collection, BsonExpression predicate) { OpenDatabase(); try { return _engine.DeleteMany(collection, predicate); } finally { CloseDatabase(); } } public bool DropCollection(string name) { OpenDatabase(); try { return _engine.DropCollection(name); } finally { CloseDatabase(); } } public bool RenameCollection(string name, string newName) { OpenDatabase(); try { return _engine.RenameCollection(name, newName); } finally { CloseDatabase(); } } public bool DropIndex(string collection, string name) { OpenDatabase(); try { return _engine.DropIndex(collection, name); } finally { CloseDatabase(); } } public bool EnsureIndex(string collection, string name, BsonExpression expression, bool unique) { OpenDatabase(); try { return _engine.EnsureIndex(collection, name, expression, unique); } finally { CloseDatabase(); } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } ~SharedEngine() { Dispose(disposing: false); } protected virtual void Dispose(bool disposing) { if (disposing && _engine != null) { _engine.Dispose(); _engine = null; _mutex.ReleaseMutex(); } } } internal class SqlParser { private readonly ILiteEngine _engine; private readonly Tokenizer _tokenizer; private readonly BsonDocument _parameters; private readonly Lazy _collation; private BsonDataReader ParseBegin() { _tokenizer.ReadToken().Expect("BEGIN"); Token token = _tokenizer.ReadToken().Expect(TokenType.Word, TokenType.EOF, TokenType.SemiColon); if (token.Is("TRANS") || token.Is("TRANSACTION")) { _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); } return new BsonDataReader(_engine.BeginTrans()); } private BsonDataReader ParseCheckpoint() { _tokenizer.ReadToken().Expect("CHECKPOINT"); _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); return new BsonDataReader(_engine.Checkpoint()); } private BsonDataReader ParseCommit() { _tokenizer.ReadToken().Expect("COMMIT"); Token token = _tokenizer.ReadToken().Expect(TokenType.Word, TokenType.EOF, TokenType.SemiColon); if (token.Is("TRANS") || token.Is("TRANSACTION")) { _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); } return new BsonDataReader(_engine.Commit()); } private BsonDataReader ParseCreate() { _tokenizer.ReadToken().Expect("CREATE"); Token token = _tokenizer.ReadToken().Expect(TokenType.Word); bool flag = token.Is("UNIQUE"); if (flag) { token = _tokenizer.ReadToken(); } token.Expect("INDEX"); string value = _tokenizer.ReadToken().Expect(TokenType.Word).Value; _tokenizer.ReadToken().Expect("ON"); string value2 = _tokenizer.ReadToken().Expect(TokenType.Word).Value; _tokenizer.ReadToken().Expect(TokenType.OpenParenthesis); BsonExpression expression = BsonExpression.Create(_tokenizer, BsonExpressionParserMode.Full, new BsonDocument()); _tokenizer.ReadToken().Expect(TokenType.CloseParenthesis); _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); return new BsonDataReader(_engine.EnsureIndex(value2, value, expression, flag)); } private BsonDataReader ParseDelete() { _tokenizer.ReadToken().Expect("DELETE"); string value = _tokenizer.ReadToken().Expect(TokenType.Word).Value; BsonExpression predicate = null; if (_tokenizer.LookAhead().Is("WHERE")) { _tokenizer.ReadToken(); predicate = BsonExpression.Create(_tokenizer, BsonExpressionParserMode.Full, _parameters); } _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); _tokenizer.ReadToken(); return new BsonDataReader(_engine.DeleteMany(value, predicate)); } private BsonDataReader ParseDrop() { _tokenizer.ReadToken().Expect("DROP"); Token token = _tokenizer.ReadToken().Expect(TokenType.Word); if (token.Is("INDEX")) { string value = _tokenizer.ReadToken().Expect(TokenType.Word).Value; _tokenizer.ReadToken().Expect(TokenType.Period); string value2 = _tokenizer.ReadToken().Expect(TokenType.Word).Value; _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); return new BsonDataReader(_engine.DropIndex(value, value2)); } if (token.Is("COLLECTION")) { string value3 = _tokenizer.ReadToken().Expect(TokenType.Word).Value; _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); return new BsonDataReader(_engine.DropCollection(value3)); } throw LiteException.UnexpectedToken(token, "INDEX|COLLECTION"); } private BsonDataReader ParseInsert() { _tokenizer.ReadToken().Expect("INSERT"); _tokenizer.ReadToken().Expect("INTO"); string value = _tokenizer.ReadToken().Expect(TokenType.Word).Value; BsonAutoId autoId = ParseWithAutoId(); _tokenizer.ReadToken().Expect("VALUES"); IEnumerable docs = ParseListOfDocuments(); return new BsonDataReader(_engine.Insert(value, docs, autoId)); } private BsonAutoId ParseWithAutoId() { if (_tokenizer.LookAhead().Type == TokenType.Colon) { _tokenizer.ReadToken(); Token token = _tokenizer.ReadToken().Expect(TokenType.Word); return token.Value.ToUpper() switch { "GUID" => BsonAutoId.Guid, "INT" => BsonAutoId.Int32, "LONG" => BsonAutoId.Int64, "OBJECTID" => BsonAutoId.ObjectId, _ => throw LiteException.UnexpectedToken(token, "DATE, GUID, INT, LONG, OBJECTID"), }; } return BsonAutoId.ObjectId; } private IEnumerable ParseListOfExpressions() { while (true) { yield return BsonExpression.Create(_tokenizer, BsonExpressionParserMode.Full, _parameters); if (_tokenizer.LookAhead().Type == TokenType.Comma) { _tokenizer.ReadToken(); continue; } break; } } private IEnumerable ParseListOfDocuments() { JsonReader reader = new JsonReader(_tokenizer); Token token; while (true) { BsonValue bsonValue = reader.Deserialize(); if (bsonValue.IsDocument) { yield return bsonValue as BsonDocument; token = _tokenizer.LookAhead(); if (token.Type != TokenType.Comma) { break; } _tokenizer.ReadToken(); continue; } throw LiteException.UnexpectedToken("Value must be a valid document", _tokenizer.Current); } token.Expect(TokenType.EOF, TokenType.SemiColon); _tokenizer.ReadToken(); } private IBsonDataReader ParsePragma() { _tokenizer.ReadToken().Expect("PRAGMA"); string value = _tokenizer.ReadToken().Expect(TokenType.Word).Value; Token token = _tokenizer.LookAhead(); if (token.Type == TokenType.EOF || token.Type == TokenType.SemiColon) { _tokenizer.ReadToken(); return new BsonDataReader(_engine.Pragma(value)); } if (token.Type == TokenType.Equals) { _tokenizer.ReadToken().Expect(TokenType.Equals); BsonValue value2 = new JsonReader(_tokenizer).Deserialize(); _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); return new BsonDataReader(_engine.Pragma(value, value2)); } throw LiteException.UnexpectedToken(token); } private BsonDataReader ParseRebuild() { _tokenizer.ReadToken().Expect("REBUILD"); RebuildOptions rebuildOptions = new RebuildOptions(); Token token = _tokenizer.LookAhead(); if (token.Type == TokenType.EOF || token.Type == TokenType.SemiColon) { rebuildOptions = null; _tokenizer.ReadToken(); } else { BsonValue bsonValue = new JsonReader(_tokenizer).Deserialize(); if (!bsonValue.IsDocument) { throw LiteException.UnexpectedToken(token); } if (bsonValue["password"].IsString) { rebuildOptions.Password = bsonValue["password"]; } if (bsonValue["collation"].IsString) { rebuildOptions.Collation = new Collation(bsonValue["collation"].AsString); } } return new BsonDataReader((int)_engine.Rebuild(rebuildOptions)); } private BsonDataReader ParseRename() { _tokenizer.ReadToken().Expect("RENAME"); _tokenizer.ReadToken().Expect("COLLECTION"); string value = _tokenizer.ReadToken().Expect(TokenType.Word).Value; _tokenizer.ReadToken().Expect("TO"); string value2 = _tokenizer.ReadToken().Expect(TokenType.Word).Value; _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); return new BsonDataReader(_engine.RenameCollection(value, value2)); } private BsonDataReader ParseRollback() { _tokenizer.ReadToken().Expect("ROLLBACK"); Token token = _tokenizer.ReadToken().Expect(TokenType.Word, TokenType.EOF, TokenType.SemiColon); if (token.Is("TRANS") || token.Is("TRANSACTION")) { _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); } return new BsonDataReader(_engine.Rollback()); } private IBsonDataReader ParseSelect() { Query query = new Query(); Token token = _tokenizer.ReadToken(); query.ExplainPlan = token.Is("EXPLAIN"); if (query.ExplainPlan) { token = _tokenizer.ReadToken(); } token.Expect("SELECT"); query.Select = BsonExpression.Create(_tokenizer, BsonExpressionParserMode.SelectDocument, _parameters); Token token2 = _tokenizer.ReadToken(); if (token2.Type == TokenType.EOF || token2.Type == TokenType.SemiColon) { IEnumerable source = query.Select.Execute(_collation.Value); string defaultName = "expr"; return new BsonDataReader(source.Select((BsonValue x) => (!x.IsDocument) ? new BsonDocument { [defaultName] = x } : x.AsDocument).FirstOrDefault()); } if (token2.Is("INTO")) { query.Into = ParseCollection(_tokenizer); query.IntoAutoId = ParseWithAutoId(); _tokenizer.ReadToken().Expect("FROM"); } else { token2.Expect("FROM"); } string collection = ParseCollection(_tokenizer); if (_tokenizer.LookAhead().Expect(TokenType.Word, TokenType.EOF, TokenType.SemiColon).Is("INCLUDE")) { _tokenizer.ReadToken(); foreach (BsonExpression item2 in ParseListOfExpressions()) { query.Includes.Add(item2); } } if (_tokenizer.LookAhead().Expect(TokenType.Word, TokenType.EOF, TokenType.SemiColon).Is("WHERE")) { _tokenizer.ReadToken(); BsonExpression item = BsonExpression.Create(_tokenizer, BsonExpressionParserMode.Full, _parameters); query.Where.Add(item); } if (_tokenizer.LookAhead().Expect(TokenType.Word, TokenType.EOF, TokenType.SemiColon).Is("GROUP")) { _tokenizer.ReadToken(); _tokenizer.ReadToken().Expect("BY"); BsonExpression groupBy = BsonExpression.Create(_tokenizer, BsonExpressionParserMode.Full, _parameters); query.GroupBy = groupBy; if (_tokenizer.LookAhead().Expect(TokenType.Word, TokenType.EOF, TokenType.SemiColon).Is("HAVING")) { _tokenizer.ReadToken(); BsonExpression having = BsonExpression.Create(_tokenizer, BsonExpressionParserMode.Full, _parameters); query.Having = having; } } if (_tokenizer.LookAhead().Expect(TokenType.Word, TokenType.EOF, TokenType.SemiColon).Is("ORDER")) { _tokenizer.ReadToken(); _tokenizer.ReadToken().Expect("BY"); BsonExpression orderBy = BsonExpression.Create(_tokenizer, BsonExpressionParserMode.Full, _parameters); int order = 1; Token token3 = _tokenizer.LookAhead(); if (token3.Is("ASC") || token3.Is("DESC")) { order = (_tokenizer.ReadToken().Is("ASC") ? 1 : (-1)); } query.OrderBy = orderBy; query.Order = order; } if (_tokenizer.LookAhead().Expect(TokenType.Word, TokenType.EOF, TokenType.SemiColon).Is("LIMIT")) { _tokenizer.ReadToken(); string value = _tokenizer.ReadToken().Expect(TokenType.Int).Value; query.Limit = Convert.ToInt32(value); } if (_tokenizer.LookAhead().Expect(TokenType.Word, TokenType.EOF, TokenType.SemiColon).Is("OFFSET")) { _tokenizer.ReadToken(); string value2 = _tokenizer.ReadToken().Expect(TokenType.Int).Value; query.Offset = Convert.ToInt32(value2); } if (_tokenizer.LookAhead().Expect(TokenType.Word, TokenType.EOF, TokenType.SemiColon).Is("FOR")) { _tokenizer.ReadToken(); _tokenizer.ReadToken().Expect("UPDATE"); query.ForUpdate = true; } _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); return _engine.Query(collection, query); } public static string ParseCollection(Tokenizer tokenizer) { string name; BsonValue options; return ParseCollection(tokenizer, out name, out options); } public static string ParseCollection(Tokenizer tokenizer, out string name, out BsonValue options) { name = tokenizer.ReadToken().Expect(TokenType.Word).Value; if (name.StartsWith("$")) { if (tokenizer.LookAhead().Type == TokenType.OpenParenthesis) { tokenizer.ReadToken(); if (tokenizer.LookAhead().Type == TokenType.CloseParenthesis) { options = null; } else { options = new JsonReader(tokenizer).Deserialize(); } tokenizer.ReadToken().Expect(TokenType.CloseParenthesis); } else { options = null; } } else { options = null; } return name + ((options == null) ? "" : ("(" + JsonSerializer.Serialize(options) + ")")); } private BsonDataReader ParseUpdate() { _tokenizer.ReadToken().Expect("UPDATE"); string value = _tokenizer.ReadToken().Expect(TokenType.Word).Value; _tokenizer.ReadToken().Expect("SET"); BsonExpression transform = BsonExpression.Create(_tokenizer, BsonExpressionParserMode.UpdateDocument, _parameters); BsonExpression predicate = null; if (_tokenizer.LookAhead().Is("WHERE")) { _tokenizer.ReadToken(); predicate = BsonExpression.Create(_tokenizer, BsonExpressionParserMode.Full, _parameters); } _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); return new BsonDataReader(_engine.UpdateMany(value, transform, predicate)); } public SqlParser(ILiteEngine engine, Tokenizer tokenizer, BsonDocument parameters) { _engine = engine; _tokenizer = tokenizer; _parameters = parameters ?? new BsonDocument(); _collation = new Lazy(() => new Collation(_engine.Pragma("COLLATION"))); } public IBsonDataReader Execute() { Token token = _tokenizer.LookAhead().Expect(TokenType.Word); switch (token.Value.ToUpper()) { case "SELECT": case "EXPLAIN": return ParseSelect(); case "INSERT": return ParseInsert(); case "DELETE": return ParseDelete(); case "UPDATE": return ParseUpdate(); case "DROP": return ParseDrop(); case "RENAME": return ParseRename(); case "CREATE": return ParseCreate(); case "CHECKPOINT": return ParseCheckpoint(); case "REBUILD": return ParseRebuild(); case "BEGIN": return ParseBegin(); case "ROLLBACK": return ParseRollback(); case "COMMIT": return ParseCommit(); case "PRAGMA": return ParsePragma(); default: throw LiteException.UnexpectedToken(token); } } } internal interface ILiteStorage { LiteFileInfo FindById(TFileId id); IEnumerable> Find(BsonExpression predicate); IEnumerable> Find(string predicate, BsonDocument parameters); IEnumerable> Find(string predicate, params BsonValue[] args); IEnumerable> Find(Expression, bool>> predicate); IEnumerable> FindAll(); bool Exists(TFileId id); LiteFileStream OpenWrite(TFileId id, string filename, BsonDocument metadata = null); LiteFileInfo Upload(TFileId id, string filename, Stream stream, BsonDocument metadata = null); LiteFileInfo Upload(TFileId id, string filename); bool SetMetadata(TFileId id, BsonDocument metadata); LiteFileStream OpenRead(TFileId id); LiteFileInfo Download(TFileId id, Stream stream); LiteFileInfo Download(TFileId id, string filename, bool overwritten); bool Delete(TFileId id); } internal class LiteFileInfo { private BsonValue _fileId; private ILiteCollection> _files; private ILiteCollection _chunks; public TFileId Id { get; internal set; } [BsonField("filename")] public string Filename { get; internal set; } [BsonField("mimeType")] public string MimeType { get; internal set; } [BsonField("length")] public long Length { get; internal set; } [BsonField("chunks")] public int Chunks { get; internal set; } [BsonField("uploadDate")] public DateTime UploadDate { get; internal set; } = DateTime.Now; [BsonField("metadata")] public BsonDocument Metadata { get; set; } = new BsonDocument(); internal void SetReference(BsonValue fileId, ILiteCollection> files, ILiteCollection chunks) { _fileId = fileId; _files = files; _chunks = chunks; } public LiteFileStream OpenRead() { return new LiteFileStream(_files, _chunks, this, _fileId, FileAccess.Read); } public LiteFileStream OpenWrite() { return new LiteFileStream(_files, _chunks, this, _fileId, FileAccess.Write); } public void CopyTo(Stream stream) { if (stream == null) { throw new ArgumentNullException("stream"); } using LiteFileStream liteFileStream = OpenRead(); liteFileStream.CopyTo(stream); } public void SaveAs(string filename, bool overwritten = true) { if (filename.IsNullOrWhiteSpace()) { throw new ArgumentNullException("filename"); } using FileStream destination = File.Open(filename, (!overwritten) ? FileMode.CreateNew : FileMode.Create); using LiteFileStream liteFileStream = OpenRead(); liteFileStream.CopyTo(destination); } } internal class LiteFileStream : Stream { public const int MAX_CHUNK_SIZE = 261120; private readonly ILiteCollection> _files; private readonly ILiteCollection _chunks; private readonly LiteFileInfo _file; private readonly BsonValue _fileId; private readonly FileAccess _mode; private long _streamPosition; private int _currentChunkIndex; private byte[] _currentChunkData; private int _positionInChunk; private MemoryStream _buffer; private bool _disposed; private Dictionary _chunkLengths = new Dictionary(); public LiteFileInfo FileInfo => _file; public override long Length => _file.Length; public override bool CanRead => _mode == FileAccess.Read; public override bool CanWrite => _mode == FileAccess.Write; public override bool CanSeek => _mode == FileAccess.Read; public override long Position { get { return _streamPosition; } set { if (_mode == FileAccess.Read) { SetReadStreamPosition(value); return; } throw new NotSupportedException(); } } internal LiteFileStream(ILiteCollection> files, ILiteCollection chunks, LiteFileInfo file, BsonValue fileId, FileAccess mode) { _files = files; _chunks = chunks; _file = file; _fileId = fileId; _mode = mode; switch (mode) { case FileAccess.Read: _currentChunkData = GetChunkData(_currentChunkIndex); break; case FileAccess.Write: _buffer = new MemoryStream(261120); if (_file.Length > 0) { Constants.ENSURE(_chunks.DeleteMany("_id BETWEEN { f: @0, n: 0 } AND { f: @0, n: 99999999 }", _fileId) == _file.Chunks); _file.Length = 0L; _file.Chunks = 0; } break; } } public override long Seek(long offset, SeekOrigin origin) { if (_mode == FileAccess.Write) { throw new NotSupportedException(); } switch (origin) { case SeekOrigin.Begin: SetReadStreamPosition(offset); break; case SeekOrigin.Current: SetReadStreamPosition(_streamPosition + offset); break; case SeekOrigin.End: SetReadStreamPosition(Length + offset); break; } return _streamPosition; } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (!_disposed) { if (disposing && CanWrite) { Flush(); _buffer?.Dispose(); } _disposed = true; } } public override void SetLength(long value) { throw new NotSupportedException(); } public override int Read(byte[] buffer, int offset, int count) { if (_mode != FileAccess.Read) { throw new NotSupportedException(); } if (_streamPosition == Length) { return 0; } int num = count; while (_currentChunkData != null && num > 0) { int num2 = Math.Min(num, _currentChunkData.Length - _positionInChunk); Buffer.BlockCopy(_currentChunkData, _positionInChunk, buffer, offset, num2); _positionInChunk += num2; num -= num2; offset += num2; _streamPosition += num2; if (_positionInChunk >= _currentChunkData.Length) { _positionInChunk = 0; _currentChunkData = GetChunkData(++_currentChunkIndex); } } return count - num; } private byte[] GetChunkData(int index) { byte[] array = _chunks.FindOne("_id = { f: @0, n: @1 }", _fileId, index)?["data"].AsBinary; if (array != null) { _chunkLengths[index] = array.Length; } return array; } private void SetReadStreamPosition(long newPosition) { if (newPosition < 0) { throw new ArgumentOutOfRangeException(); } if (newPosition >= Length) { _streamPosition = Length; return; } _streamPosition = newPosition; long num = 0L; int num2 = _currentChunkIndex; int num3 = 0; while (num <= _streamPosition) { if (_chunkLengths.TryGetValue(num3, out var value)) { num += value; } else { num2 = num3; _currentChunkData = GetChunkData(num3); num += _currentChunkData.Length; } num3++; } num3--; num -= _chunkLengths[num3]; _positionInChunk = (int)(_streamPosition - num); _currentChunkIndex = num3; if (num2 != _currentChunkIndex) { _currentChunkData = GetChunkData(_currentChunkIndex); } } public override void Write(byte[] buffer, int offset, int count) { _streamPosition += count; _buffer.Write(buffer, offset, count); if (_buffer.Length >= 261120) { WriteChunks(flush: false); } } public override void Flush() { WriteChunks(flush: true); } private void WriteChunks(bool flush) { byte[] array = new byte[261120]; int num = 0; _buffer.Seek(0L, SeekOrigin.Begin); while ((num = _buffer.Read(array, 0, 261120)) > 0) { BsonDocument bsonDocument = new BsonDocument { ["_id"] = new BsonDocument { ["f"] = _fileId, ["n"] = _file.Chunks++ } }; if (num != 261120) { byte[] array2 = new byte[num]; Buffer.BlockCopy(array, 0, array2, 0, num); bsonDocument["data"] = array2; } else { bsonDocument["data"] = array; } _chunks.Insert(bsonDocument); } if (flush) { _file.UploadDate = DateTime.Now; _file.Length = _streamPosition; _files.Upsert(_file); } _buffer = new MemoryStream(); } } internal class LiteStorage : ILiteStorage { private readonly ILiteDatabase _db; private readonly ILiteCollection> _files; private readonly ILiteCollection _chunks; public LiteStorage(ILiteDatabase db, string filesCollection, string chunksCollection) { _db = db; _files = db.GetCollection>(filesCollection); _chunks = db.GetCollection(chunksCollection); } public LiteFileInfo FindById(TFileId id) { if (id == null) { throw new ArgumentNullException("id"); } BsonValue bsonValue = _db.Mapper.Serialize(typeof(TFileId), id); LiteFileInfo liteFileInfo = _files.FindById(bsonValue); if (liteFileInfo == null) { return null; } liteFileInfo.SetReference(bsonValue, _files, _chunks); return liteFileInfo; } public IEnumerable> Find(BsonExpression predicate) { ILiteQueryable> liteQueryable = _files.Query(); if (predicate != null) { liteQueryable = liteQueryable.Where(predicate); } foreach (LiteFileInfo item in liteQueryable.ToEnumerable()) { BsonValue fileId = _db.Mapper.Serialize(typeof(TFileId), item.Id); item.SetReference(fileId, _files, _chunks); yield return item; } } public IEnumerable> Find(string predicate, BsonDocument parameters) { return Find(BsonExpression.Create(predicate, parameters)); } public IEnumerable> Find(string predicate, params BsonValue[] args) { return Find(BsonExpression.Create(predicate, args)); } public IEnumerable> Find(Expression, bool>> predicate) { return Find(_db.Mapper.GetExpression(predicate)); } public IEnumerable> FindAll() { return Find((BsonExpression)null); } public bool Exists(TFileId id) { if (id == null) { throw new ArgumentNullException("id"); } BsonValue bsonValue = _db.Mapper.Serialize(typeof(TFileId), id); return _files.Exists("_id = @0", bsonValue); } public LiteFileStream OpenWrite(TFileId id, string filename, BsonDocument metadata = null) { BsonValue fileId = _db.Mapper.Serialize(typeof(TFileId), id); LiteFileInfo liteFileInfo = FindById(id); if (liteFileInfo == null) { liteFileInfo = new LiteFileInfo { Id = id, Filename = Path.GetFileName(filename), MimeType = MimeTypeConverter.GetMimeType(filename), Metadata = (metadata ?? new BsonDocument()) }; liteFileInfo.SetReference(fileId, _files, _chunks); } else { liteFileInfo.Filename = Path.GetFileName(filename); liteFileInfo.MimeType = MimeTypeConverter.GetMimeType(filename); liteFileInfo.Metadata = metadata ?? liteFileInfo.Metadata; } return liteFileInfo.OpenWrite(); } public LiteFileInfo Upload(TFileId id, string filename, Stream stream, BsonDocument metadata = null) { using LiteFileStream liteFileStream = OpenWrite(id, filename, metadata); stream.CopyTo(liteFileStream); return liteFileStream.FileInfo; } public LiteFileInfo Upload(TFileId id, string filename) { if (filename.IsNullOrWhiteSpace()) { throw new ArgumentNullException("filename"); } using FileStream stream = File.OpenRead(filename); return Upload(id, Path.GetFileName(filename), stream); } public bool SetMetadata(TFileId id, BsonDocument metadata) { LiteFileInfo liteFileInfo = FindById(id); if (liteFileInfo == null) { return false; } liteFileInfo.Metadata = metadata ?? new BsonDocument(); _files.Update(liteFileInfo); return true; } public LiteFileStream OpenRead(TFileId id) { return (FindById(id) ?? throw LiteException.FileNotFound(id.ToString())).OpenRead(); } public LiteFileInfo Download(TFileId id, Stream stream) { LiteFileInfo obj = FindById(id) ?? throw LiteException.FileNotFound(id.ToString()); obj.CopyTo(stream); return obj; } public LiteFileInfo Download(TFileId id, string filename, bool overwritten) { LiteFileInfo obj = FindById(id) ?? throw LiteException.FileNotFound(id.ToString()); obj.SaveAs(filename, overwritten); return obj; } public bool Delete(TFileId id) { if (id == null) { throw new ArgumentNullException("id"); } BsonValue bsonValue = _db.Mapper.Serialize(typeof(TFileId), id); bool flag = _files.Delete(bsonValue); if (flag) { _chunks.DeleteMany("_id BETWEEN { f: @0, n: 0} AND {f: @0, n: @1 }", bsonValue, int.MaxValue); } return flag; } } internal class ConnectionString { private readonly Dictionary _values; public ConnectionType Connection { get; set; } public string Filename { get; set; } = ""; public string Password { get; set; } public long InitialSize { get; set; } public bool ReadOnly { get; set; } public bool Upgrade { get; set; } public bool AutoRebuild { get; set; } public Collation Collation { get; set; } public string this[string key] => _values.GetOrDefault(key); public ConnectionString() { _values = new Dictionary(StringComparer.OrdinalIgnoreCase); } public ConnectionString(string connectionString) : this() { if (string.IsNullOrEmpty(connectionString)) { throw new ArgumentNullException("connectionString"); } if (connectionString.Contains("=")) { _values.ParseKeyValue(connectionString); } else { _values["filename"] = connectionString; } Connection = _values.GetValue("connection", Connection); Filename = _values.GetValue("filename", Filename).Trim(); Password = _values.GetValue("password", Password); if (Password == string.Empty) { Password = null; } InitialSize = _values.GetFileSize("initial size", InitialSize); ReadOnly = _values.GetValue("readonly", ReadOnly); Collation = (_values.ContainsKey("collation") ? new Collation(_values.GetValue("collation")) : Collation); Upgrade = _values.GetValue("upgrade", Upgrade); AutoRebuild = _values.GetValue("auto-rebuild", AutoRebuild); } internal ILiteEngine CreateEngine(Action engineSettingsAction = null) { EngineSettings engineSettings = new EngineSettings { Filename = Filename, Password = Password, InitialSize = InitialSize, ReadOnly = ReadOnly, Collation = Collation, Upgrade = Upgrade, AutoRebuild = AutoRebuild }; engineSettingsAction?.Invoke(engineSettings); if (Connection == ConnectionType.Direct) { return new LiteEngine(engineSettings); } if (Connection == ConnectionType.Shared) { return new SharedEngine(engineSettings); } throw new NotImplementedException(); } } internal enum ConnectionType { Direct, Shared } internal class Query { public const int Ascending = 1; public const int Descending = -1; public BsonExpression Select { get; set; } = BsonExpression.Root; public List Includes { get; } = new List(); public List Where { get; } = new List(); public BsonExpression OrderBy { get; set; } public int Order { get; set; } = 1; public BsonExpression GroupBy { get; set; } public BsonExpression Having { get; set; } public int Offset { get; set; } public int Limit { get; set; } = int.MaxValue; public bool ForUpdate { get; set; } public string Into { get; set; } public BsonAutoId IntoAutoId { get; set; } = BsonAutoId.ObjectId; public bool ExplainPlan { get; set; } public static Query All() { return new Query(); } public static Query All(int order = 1) { return new Query { OrderBy = "_id", Order = order }; } public static Query All(string field, int order = 1) { return new Query { OrderBy = field, Order = order }; } public static BsonExpression EQ(string field, BsonValue value) { if (field.IsNullOrWhiteSpace()) { throw new ArgumentNullException("field"); } return BsonExpression.Create($"{field} = {value ?? BsonValue.Null}"); } public static BsonExpression LT(string field, BsonValue value) { if (field.IsNullOrWhiteSpace()) { throw new ArgumentNullException("field"); } return BsonExpression.Create($"{field} < {value ?? BsonValue.Null}"); } public static BsonExpression LTE(string field, BsonValue value) { if (field.IsNullOrWhiteSpace()) { throw new ArgumentNullException("field"); } return BsonExpression.Create($"{field} <= {value ?? BsonValue.Null}"); } public static BsonExpression GT(string field, BsonValue value) { if (field.IsNullOrWhiteSpace()) { throw new ArgumentNullException("field"); } return BsonExpression.Create($"{field} > {value ?? BsonValue.Null}"); } public static BsonExpression GTE(string field, BsonValue value) { if (field.IsNullOrWhiteSpace()) { throw new ArgumentNullException("field"); } return BsonExpression.Create($"{field} >= {value ?? BsonValue.Null}"); } public static BsonExpression Between(string field, BsonValue start, BsonValue end) { if (field.IsNullOrWhiteSpace()) { throw new ArgumentNullException("field"); } return BsonExpression.Create($"{field} BETWEEN {start ?? BsonValue.Null} AND {end ?? BsonValue.Null}"); } public static BsonExpression StartsWith(string field, string value) { if (field.IsNullOrWhiteSpace()) { throw new ArgumentNullException("field"); } if (value.IsNullOrWhiteSpace()) { throw new ArgumentNullException("value"); } return BsonExpression.Create(string.Format("{0} LIKE {1}", field, new BsonValue(value + "%"))); } public static BsonExpression Contains(string field, string value) { if (field.IsNullOrWhiteSpace()) { throw new ArgumentNullException("field"); } if (value.IsNullOrEmpty()) { throw new ArgumentNullException("value"); } return BsonExpression.Create(string.Format("{0} LIKE {1}", field, new BsonValue("%" + value + "%"))); } public static BsonExpression Not(string field, BsonValue value) { if (field.IsNullOrWhiteSpace()) { throw new ArgumentNullException("field"); } return BsonExpression.Create($"{field} != {value ?? BsonValue.Null}"); } public static BsonExpression In(string field, BsonArray value) { if (field.IsNullOrWhiteSpace()) { throw new ArgumentNullException("field"); } if (value == null) { throw new ArgumentNullException("value"); } return BsonExpression.Create($"{field} IN {value}"); } public static BsonExpression In(string field, params BsonValue[] values) { return In(field, new BsonArray(values)); } public static BsonExpression In(string field, IEnumerable values) { return In(field, new BsonArray(values)); } public static QueryAny Any() { return new QueryAny(); } public static BsonExpression And(BsonExpression left, BsonExpression right) { if (left == null) { throw new ArgumentNullException("left"); } if (right == null) { throw new ArgumentNullException("right"); } return "(" + left.Source + " AND " + right.Source + ")"; } public static BsonExpression And(params BsonExpression[] queries) { if (queries == null || queries.Length < 2) { throw new ArgumentException("At least two Query should be passed"); } BsonExpression bsonExpression = queries[0]; for (int i = 1; i < queries.Length; i++) { bsonExpression = And(bsonExpression, queries[i]); } return bsonExpression; } public static BsonExpression Or(BsonExpression left, BsonExpression right) { if (left == null) { throw new ArgumentNullException("left"); } if (right == null) { throw new ArgumentNullException("right"); } return "(" + left.Source + " OR " + right.Source + ")"; } public static BsonExpression Or(params BsonExpression[] queries) { if (queries == null || queries.Length < 2) { throw new ArgumentException("At least two Query should be passed"); } BsonExpression bsonExpression = queries[0]; for (int i = 1; i < queries.Length; i++) { bsonExpression = Or(bsonExpression, queries[i]); } return bsonExpression; } public string ToSQL(string collection) { StringBuilder stringBuilder = new StringBuilder(); if (ExplainPlan) { stringBuilder.AppendLine("EXPLAIN"); } stringBuilder.AppendLine("SELECT " + Select.Source); if (Into != null) { stringBuilder.AppendLine("INTO " + Into + ":" + IntoAutoId.ToString().ToLower()); } stringBuilder.AppendLine("FROM " + collection); if (Includes.Count > 0) { stringBuilder.AppendLine("INCLUDE " + string.Join(", ", Includes.Select((BsonExpression x) => x.Source))); } if (Where.Count > 0) { stringBuilder.AppendLine("WHERE " + string.Join(" AND ", Where.Select((BsonExpression x) => x.Source))); } if (GroupBy != null) { stringBuilder.AppendLine("GROUP BY " + GroupBy.Source); } if (Having != null) { stringBuilder.AppendLine("HAVING " + Having.Source); } if (OrderBy != null) { stringBuilder.AppendLine("ORDER BY " + OrderBy.Source + " " + ((Order == 1) ? "ASC" : "DESC")); } if (Limit != int.MaxValue) { stringBuilder.AppendLine($"LIMIT {Limit}"); } if (Offset != 0) { stringBuilder.AppendLine($"OFFSET {Offset}"); } if (ForUpdate) { stringBuilder.AppendLine("FOR UPDATE"); } return stringBuilder.ToString().Trim(); } } internal class QueryAny { public BsonExpression EQ(string arrayField, BsonValue value) { if (arrayField.IsNullOrWhiteSpace()) { throw new ArgumentNullException("arrayField"); } return BsonExpression.Create($"{arrayField} ANY = {value ?? BsonValue.Null}"); } public BsonExpression LT(string arrayField, BsonValue value) { if (arrayField.IsNullOrWhiteSpace()) { throw new ArgumentNullException("arrayField"); } return BsonExpression.Create($"{arrayField} ANY < {value ?? BsonValue.Null}"); } public BsonExpression LTE(string arrayField, BsonValue value) { if (arrayField.IsNullOrWhiteSpace()) { throw new ArgumentNullException("arrayField"); } return BsonExpression.Create($"{arrayField} ANY <= {value ?? BsonValue.Null}"); } public BsonExpression GT(string arrayField, BsonValue value) { if (arrayField.IsNullOrWhiteSpace()) { throw new ArgumentNullException("arrayField"); } return BsonExpression.Create($"{arrayField} ANY > {value ?? BsonValue.Null}"); } public BsonExpression GTE(string arrayField, BsonValue value) { if (arrayField.IsNullOrWhiteSpace()) { throw new ArgumentNullException("arrayField"); } return BsonExpression.Create($"{arrayField} ANY >= {value ?? BsonValue.Null}"); } public BsonExpression Between(string arrayField, BsonValue start, BsonValue end) { if (arrayField.IsNullOrWhiteSpace()) { throw new ArgumentNullException("arrayField"); } return BsonExpression.Create($"{arrayField} ANY BETWEEN {start ?? BsonValue.Null} AND {end ?? BsonValue.Null}"); } public BsonExpression StartsWith(string arrayField, string value) { if (arrayField.IsNullOrWhiteSpace()) { throw new ArgumentNullException("arrayField"); } if (value.IsNullOrWhiteSpace()) { throw new ArgumentNullException("value"); } return BsonExpression.Create(string.Format("{0} ANY LIKE {1}", arrayField, new BsonValue(value + "%"))); } public BsonExpression Not(string arrayField, BsonValue value) { if (arrayField.IsNullOrWhiteSpace()) { throw new ArgumentNullException("arrayField"); } return BsonExpression.Create($"{arrayField} ANY != {value ?? BsonValue.Null}"); } } internal class BsonArray : BsonValue, IList, ICollection, IEnumerable, IEnumerable { private int _length; public new IList RawValue => (IList)base.RawValue; public override BsonValue this[int index] { get { return RawValue[index]; } set { RawValue[index] = value ?? BsonValue.Null; } } public int Count => RawValue.Count; public bool IsReadOnly => false; public BsonArray() : base(BsonType.Array, new List()) { } public BsonArray(List array) : this() { if (array == null) { throw new ArgumentNullException("array"); } AddRange(array); } public BsonArray(params BsonValue[] array) : this() { if (array == null) { throw new ArgumentNullException("array"); } AddRange(array); } public BsonArray(IEnumerable items) : this() { if (items == null) { throw new ArgumentNullException("items"); } AddRange(items); } public void Add(BsonValue item) { RawValue.Add(item ?? BsonValue.Null); } public void AddRange(TCollection collection) where TCollection : ICollection { if (collection == null) { throw new ArgumentNullException("collection"); } List list = (List)base.RawValue; if (list.Capacity - list.Count < collection.Count) { list.Capacity += collection.Count; } foreach (BsonValue item in collection) { list.Add(item ?? BsonValue.Null); } } public void AddRange(IEnumerable items) { if (items == null) { throw new ArgumentNullException("items"); } foreach (BsonValue item in items) { Add(item ?? BsonValue.Null); } } public void Clear() { RawValue.Clear(); } public bool Contains(BsonValue item) { return RawValue.Contains(item ?? BsonValue.Null); } public void CopyTo(BsonValue[] array, int arrayIndex) { RawValue.CopyTo(array, arrayIndex); } public IEnumerator GetEnumerator() { return RawValue.GetEnumerator(); } public int IndexOf(BsonValue item) { return RawValue.IndexOf(item ?? BsonValue.Null); } public void Insert(int index, BsonValue item) { RawValue.Insert(index, item ?? BsonValue.Null); } public bool Remove(BsonValue item) { return RawValue.Remove(item); } public void RemoveAt(int index) { RawValue.RemoveAt(index); } IEnumerator IEnumerable.GetEnumerator() { foreach (BsonValue item in RawValue) { yield return item; } } public override int CompareTo(BsonValue other) { if (other.Type != BsonType.Array) { return base.Type.CompareTo(other.Type); } BsonArray asArray = other.AsArray; int num = 0; int num2 = 0; int num3 = Math.Min(Count, asArray.Count); while (num == 0 && num2 < num3) { num = this[num2].CompareTo(asArray[num2]); num2++; } if (num != 0) { return num; } if (num2 == Count) { if (num2 != asArray.Count) { return -1; } return 0; } return 1; } internal override int GetBytesCount(bool recalc) { if (!recalc && _length > 0) { return _length; } int num = 5; IList rawValue = RawValue; for (int i = 0; i < rawValue.Count; i++) { num += GetBytesCountElement(i.ToString(), rawValue[i]); } return _length = num; } } internal enum BsonAutoId { Int32 = 2, Int64 = 3, ObjectId = 10, Guid = 11 } internal class BsonDocument : BsonValue, IDictionary, ICollection>, IEnumerable>, IEnumerable { private int _length; public new IDictionary RawValue => base.RawValue as IDictionary; internal PageAddress RawId { get; set; } = PageAddress.Empty; public override BsonValue this[string key] { get { return RawValue.GetOrDefault(key, BsonValue.Null); } set { RawValue[key] = value ?? BsonValue.Null; } } public ICollection Keys => RawValue.Keys; public ICollection Values => RawValue.Values; public int Count => RawValue.Count; public bool IsReadOnly => false; public BsonDocument() : base(BsonType.Document, new Dictionary(StringComparer.OrdinalIgnoreCase)) { } public BsonDocument(ConcurrentDictionary dict) : this() { if (dict == null) { throw new ArgumentNullException("dict"); } foreach (KeyValuePair item in dict) { Add(item); } } public BsonDocument(IDictionary dict) : this() { if (dict == null) { throw new ArgumentNullException("dict"); } foreach (KeyValuePair item in dict) { Add(item); } } public override int CompareTo(BsonValue other) { if (other.Type != BsonType.Document) { return base.Type.CompareTo(other.Type); } string[] array = Keys.ToArray(); int num = array.Length; BsonDocument asDocument = other.AsDocument; int num2 = asDocument.Keys.ToArray().Length; int num3 = 0; int num4 = 0; int num5 = Math.Min(num, num2); while (num3 == 0 && num4 < num5) { num3 = this[array[num4]].CompareTo(asDocument[array[num4]]); num4++; } if (num3 != 0) { return num3; } if (num4 == num) { if (num4 != num2) { return -1; } return 0; } return 1; } public bool ContainsKey(string key) { return RawValue.ContainsKey(key); } public IEnumerable> GetElements() { if (RawValue.TryGetValue("_id", out var value)) { yield return new KeyValuePair("_id", value); } foreach (KeyValuePair item in RawValue.Where((KeyValuePair x) => x.Key != "_id")) { yield return item; } } public void Add(string key, BsonValue value) { RawValue.Add(key, value ?? BsonValue.Null); } public bool Remove(string key) { return RawValue.Remove(key); } public void Clear() { RawValue.Clear(); } public bool TryGetValue(string key, out BsonValue value) { return RawValue.TryGetValue(key, out value); } public void Add(KeyValuePair item) { Add(item.Key, item.Value); } public bool Contains(KeyValuePair item) { return RawValue.Contains(item); } public bool Remove(KeyValuePair item) { return Remove(item.Key); } public IEnumerator> GetEnumerator() { return RawValue.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return RawValue.GetEnumerator(); } public void CopyTo(KeyValuePair[] array, int arrayIndex) { RawValue.CopyTo(array, arrayIndex); } public void CopyTo(BsonDocument other) { using IEnumerator> enumerator = GetEnumerator(); while (enumerator.MoveNext()) { KeyValuePair current = enumerator.Current; other[current.Key] = current.Value; } } internal override int GetBytesCount(bool recalc) { if (!recalc && _length > 0) { return _length; } int num = 5; foreach (KeyValuePair item in RawValue) { num += GetBytesCountElement(item.Key, item.Value); } return _length = num; } } internal enum BsonType : byte { MinValue, Null, Int32, Int64, Double, Decimal, String, Document, Array, Binary, ObjectId, Guid, Boolean, DateTime, MaxValue } internal class BsonValue : IComparable, IEquatable { public static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); public static BsonValue Null = new BsonValue(BsonType.Null, null); public static BsonValue MinValue = new BsonValue(BsonType.MinValue, "-oo"); public static BsonValue MaxValue = new BsonValue(BsonType.MaxValue, "+oo"); public BsonType Type { get; } public virtual object RawValue { get; } public virtual BsonValue this[string name] { get { throw new InvalidOperationException("Cannot access non-document type value on " + RawValue); } set { throw new InvalidOperationException("Cannot access non-document type value on " + RawValue); } } public virtual BsonValue this[int index] { get { throw new InvalidOperationException("Cannot access non-array type value on " + RawValue); } set { throw new InvalidOperationException("Cannot access non-array type value on " + RawValue); } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] public BsonArray AsArray => this as BsonArray; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public BsonDocument AsDocument => this as BsonDocument; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public byte[] AsBinary => RawValue as byte[]; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool AsBoolean => (bool)RawValue; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public string AsString => (string)RawValue; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public int AsInt32 => Convert.ToInt32(RawValue); [DebuggerBrowsable(DebuggerBrowsableState.Never)] public long AsInt64 => Convert.ToInt64(RawValue); [DebuggerBrowsable(DebuggerBrowsableState.Never)] public double AsDouble => Convert.ToDouble(RawValue); [DebuggerBrowsable(DebuggerBrowsableState.Never)] public decimal AsDecimal => Convert.ToDecimal(RawValue); [DebuggerBrowsable(DebuggerBrowsableState.Never)] public DateTime AsDateTime => (DateTime)RawValue; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public ObjectId AsObjectId => (ObjectId)RawValue; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public Guid AsGuid => (Guid)RawValue; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsNull => Type == BsonType.Null; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsArray => Type == BsonType.Array; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsDocument => Type == BsonType.Document; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsInt32 => Type == BsonType.Int32; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsInt64 => Type == BsonType.Int64; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsDouble => Type == BsonType.Double; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsDecimal => Type == BsonType.Decimal; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsNumber { get { if (!IsInt32 && !IsInt64 && !IsDouble) { return IsDecimal; } return true; } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsBinary => Type == BsonType.Binary; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsBoolean => Type == BsonType.Boolean; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsString => Type == BsonType.String; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsObjectId => Type == BsonType.ObjectId; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsGuid => Type == BsonType.Guid; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsDateTime => Type == BsonType.DateTime; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsMinValue => Type == BsonType.MinValue; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool IsMaxValue => Type == BsonType.MaxValue; public static BsonDocument DbRef(BsonValue id, string collection) { return new BsonDocument { ["$id"] = id, ["$ref"] = collection }; } public BsonValue() { Type = BsonType.Null; RawValue = null; } public BsonValue(int value) { Type = BsonType.Int32; RawValue = value; } public BsonValue(long value) { Type = BsonType.Int64; RawValue = value; } public BsonValue(double value) { Type = BsonType.Double; RawValue = value; } public BsonValue(decimal value) { Type = BsonType.Decimal; RawValue = value; } public BsonValue(string value) { Type = ((value == null) ? BsonType.Null : BsonType.String); RawValue = value; } public BsonValue(byte[] value) { Type = ((value == null) ? BsonType.Null : BsonType.Binary); RawValue = value; } public BsonValue(ObjectId value) { Type = ((value == null) ? BsonType.Null : BsonType.ObjectId); RawValue = value; } public BsonValue(Guid value) { Type = BsonType.Guid; RawValue = value; } public BsonValue(bool value) { Type = BsonType.Boolean; RawValue = value; } public BsonValue(DateTime value) { Type = BsonType.DateTime; RawValue = value.Truncate(); } protected BsonValue(BsonType type, object rawValue) { Type = type; RawValue = rawValue; } public BsonValue(object value) { RawValue = value; if (value == null) { Type = BsonType.Null; return; } if (value is int) { Type = BsonType.Int32; return; } if (value is long) { Type = BsonType.Int64; return; } if (value is double) { Type = BsonType.Double; return; } if (value is decimal) { Type = BsonType.Decimal; return; } if (value is string) { Type = BsonType.String; return; } if (value is IDictionary) { Type = BsonType.Document; return; } if (value is IList) { Type = BsonType.Array; return; } if (value is byte[]) { Type = BsonType.Binary; return; } if (value is ObjectId) { Type = BsonType.ObjectId; return; } if (value is Guid) { Type = BsonType.Guid; return; } if (value is bool) { Type = BsonType.Boolean; return; } if (value is DateTime) { Type = BsonType.DateTime; RawValue = ((DateTime)value).Truncate(); return; } if (value is BsonValue) { BsonValue bsonValue = (BsonValue)value; Type = bsonValue.Type; RawValue = bsonValue.RawValue; return; } IEnumerable enumerable = value as IEnumerable; if (value is IDictionary dictionary) { Dictionary dictionary2 = new Dictionary(); foreach (object key in dictionary.Keys) { dictionary2.Add(key.ToString(), new BsonValue(dictionary[key])); } Type = BsonType.Document; RawValue = dictionary2; return; } if (enumerable != null) { List list = new List(); foreach (object item in enumerable) { list.Add(new BsonValue(item)); } Type = BsonType.Array; RawValue = list; return; } throw new InvalidCastException("Value is not a valid BSON data type - Use Mapper.ToDocument for more complex types converts"); } public static implicit operator int(BsonValue value) { return (int)value.RawValue; } public static implicit operator BsonValue(int value) { return new BsonValue(value); } public static implicit operator long(BsonValue value) { return (long)value.RawValue; } public static implicit operator BsonValue(long value) { return new BsonValue(value); } public static implicit operator double(BsonValue value) { return (double)value.RawValue; } public static implicit operator BsonValue(double value) { return new BsonValue(value); } public static implicit operator decimal(BsonValue value) { return (decimal)value.RawValue; } public static implicit operator BsonValue(decimal value) { return new BsonValue(value); } public static implicit operator ulong(BsonValue value) { return (ulong)value.RawValue; } public static implicit operator BsonValue(ulong value) { return new BsonValue((double)value); } public static implicit operator string(BsonValue value) { return (string)value.RawValue; } public static implicit operator BsonValue(string value) { return new BsonValue(value); } public static implicit operator byte[](BsonValue value) { return (byte[])value.RawValue; } public static implicit operator BsonValue(byte[] value) { return new BsonValue(value); } public static implicit operator ObjectId(BsonValue value) { return (ObjectId)value.RawValue; } public static implicit operator BsonValue(ObjectId value) { return new BsonValue(value); } public static implicit operator Guid(BsonValue value) { return (Guid)value.RawValue; } public static implicit operator BsonValue(Guid value) { return new BsonValue(value); } public static implicit operator bool(BsonValue value) { return (bool)value.RawValue; } public static implicit operator BsonValue(bool value) { return new BsonValue(value); } public static implicit operator DateTime(BsonValue value) { return (DateTime)value.RawValue; } public static implicit operator BsonValue(DateTime value) { return new BsonValue(value); } public static BsonValue operator +(BsonValue left, BsonValue right) { if (!left.IsNumber || !right.IsNumber) { return Null; } if (left.IsInt32 && right.IsInt32) { return left.AsInt32 + right.AsInt32; } if (left.IsInt64 && right.IsInt64) { return left.AsInt64 + right.AsInt64; } if (left.IsDouble && right.IsDouble) { return left.AsDouble + right.AsDouble; } if (left.IsDecimal && right.IsDecimal) { return left.AsDecimal + right.AsDecimal; } decimal num = left.AsDecimal + right.AsDecimal; return (BsonType)(byte)Math.Max((int)left.Type, (int)right.Type) switch { BsonType.Double => new BsonValue((double)num), BsonType.Int64 => new BsonValue((long)num), _ => new BsonValue(num), }; } public static BsonValue operator -(BsonValue left, BsonValue right) { if (!left.IsNumber || !right.IsNumber) { return Null; } if (left.IsInt32 && right.IsInt32) { return left.AsInt32 - right.AsInt32; } if (left.IsInt64 && right.IsInt64) { return left.AsInt64 - right.AsInt64; } if (left.IsDouble && right.IsDouble) { return left.AsDouble - right.AsDouble; } if (left.IsDecimal && right.IsDecimal) { return left.AsDecimal - right.AsDecimal; } decimal num = left.AsDecimal - right.AsDecimal; return (BsonType)(byte)Math.Max((int)left.Type, (int)right.Type) switch { BsonType.Double => new BsonValue((double)num), BsonType.Int64 => new BsonValue((long)num), _ => new BsonValue(num), }; } public static BsonValue operator *(BsonValue left, BsonValue right) { if (!left.IsNumber || !right.IsNumber) { return Null; } if (left.IsInt32 && right.IsInt32) { return left.AsInt32 * right.AsInt32; } if (left.IsInt64 && right.IsInt64) { return left.AsInt64 * right.AsInt64; } if (left.IsDouble && right.IsDouble) { return left.AsDouble * right.AsDouble; } if (left.IsDecimal && right.IsDecimal) { return left.AsDecimal * right.AsDecimal; } decimal num = left.AsDecimal * right.AsDecimal; return (BsonType)(byte)Math.Max((int)left.Type, (int)right.Type) switch { BsonType.Double => new BsonValue((double)num), BsonType.Int64 => new BsonValue((long)num), _ => new BsonValue(num), }; } public static BsonValue operator /(BsonValue left, BsonValue right) { if (!left.IsNumber || !right.IsNumber) { return Null; } if (left.IsDecimal || right.IsDecimal) { return left.AsDecimal / right.AsDecimal; } return left.AsDouble / right.AsDouble; } public override string ToString() { return JsonSerializer.Serialize(this); } public virtual int CompareTo(BsonValue other) { return CompareTo(other, Collation.Binary); } public virtual int CompareTo(BsonValue other, Collation collation) { if (Type != other.Type) { if (IsNumber && other.IsNumber) { return Convert.ToDecimal(RawValue).CompareTo(Convert.ToDecimal(other.RawValue)); } int num = Type.CompareTo(other.Type); if (num >= 0) { return (num > 0) ? 1 : 0; } return -1; } switch (Type) { case BsonType.MinValue: case BsonType.Null: case BsonType.MaxValue: return 0; case BsonType.Int32: return AsInt32.CompareTo(other.AsInt32); case BsonType.Int64: return AsInt64.CompareTo(other.AsInt64); case BsonType.Double: return AsDouble.CompareTo(other.AsDouble); case BsonType.Decimal: return AsDecimal.CompareTo(other.AsDecimal); case BsonType.String: return collation.Compare(AsString, other.AsString); case BsonType.Document: return AsDocument.CompareTo(other); case BsonType.Array: return AsArray.CompareTo(other); case BsonType.Binary: return AsBinary.BinaryCompareTo(other.AsBinary); case BsonType.ObjectId: return AsObjectId.CompareTo(other.AsObjectId); case BsonType.Guid: return AsGuid.CompareTo(other.AsGuid); case BsonType.Boolean: return AsBoolean.CompareTo(other.AsBoolean); case BsonType.DateTime: { DateTime dateTime = AsDateTime; DateTime value = other.AsDateTime; if (dateTime.Kind != DateTimeKind.Utc) { dateTime = dateTime.ToUniversalTime(); } if (value.Kind != DateTimeKind.Utc) { value = value.ToUniversalTime(); } return dateTime.CompareTo(value); } default: throw new NotImplementedException(); } } public bool Equals(BsonValue other) { return CompareTo(other) == 0; } public static bool operator ==(BsonValue lhs, BsonValue rhs) { if ((object)lhs == null) { return (object)rhs == null; } if ((object)rhs == null) { return false; } return lhs.Equals(rhs); } public static bool operator !=(BsonValue lhs, BsonValue rhs) { return !(lhs == rhs); } public static bool operator >=(BsonValue lhs, BsonValue rhs) { return lhs.CompareTo(rhs) >= 0; } public static bool operator >(BsonValue lhs, BsonValue rhs) { return lhs.CompareTo(rhs) > 0; } public static bool operator <(BsonValue lhs, BsonValue rhs) { return lhs.CompareTo(rhs) < 0; } public static bool operator <=(BsonValue lhs, BsonValue rhs) { return lhs.CompareTo(rhs) <= 0; } public override bool Equals(object obj) { if (obj is BsonValue other) { return Equals(other); } return false; } public override int GetHashCode() { int num = 17; num = 37 * num + Type.GetHashCode(); return 37 * num + (RawValue?.GetHashCode() ?? 0); } internal virtual int GetBytesCount(bool recalc) { switch (Type) { case BsonType.MinValue: case BsonType.Null: case BsonType.MaxValue: return 0; case BsonType.Int32: return 4; case BsonType.Int64: return 8; case BsonType.Double: return 8; case BsonType.Decimal: return 16; case BsonType.String: return StringEncoding.UTF8.GetByteCount(AsString); case BsonType.Binary: return AsBinary.Length; case BsonType.ObjectId: return 12; case BsonType.Guid: return 16; case BsonType.Boolean: return 1; case BsonType.DateTime: return 8; case BsonType.Document: return AsDocument.GetBytesCount(recalc); case BsonType.Array: return AsArray.GetBytesCount(recalc); default: throw new ArgumentException(); } } protected int GetBytesCountElement(string key, BsonValue value) { bool flag = value.Type == BsonType.String || value.Type == BsonType.Binary || value.Type == BsonType.Guid; return 1 + StringEncoding.UTF8.GetByteCount(key) + 1 + value.GetBytesCount(recalc: true) + (flag ? 5 : 0); } } internal class BsonSerializer { public static byte[] Serialize(BsonDocument doc) { if (doc == null) { throw new ArgumentNullException("doc"); } byte[] array = new byte[doc.GetBytesCount(recalc: true)]; using BufferWriter bufferWriter = new BufferWriter(array); bufferWriter.WriteDocument(doc, recalc: false); return array; } public static BsonDocument Deserialize(byte[] buffer, bool utcDate = false, HashSet fields = null) { if (buffer == null || buffer.Length == 0) { throw new ArgumentNullException("buffer"); } using BufferReader bufferReader = new BufferReader(buffer, utcDate); return bufferReader.ReadDocument(fields).GetValue(); } } internal class BsonDataReader : IBsonDataReader, IDisposable { private readonly IEnumerator _source; private readonly EngineState _state; private readonly string _collection; private readonly bool _hasValues; private BsonValue _current; private bool _isFirst; private bool _disposed; public bool HasValues => _hasValues; public BsonValue Current => _current; public string Collection => _collection; public BsonValue this[string field] => _current.AsDocument[field] ?? BsonValue.Null; internal BsonDataReader() { _hasValues = false; } internal BsonDataReader(BsonValue value, string collection = null) { _current = value; _isFirst = (_hasValues = true); _collection = collection; } internal BsonDataReader(IEnumerable values, string collection, EngineState state) { _collection = collection; _source = values.GetEnumerator(); _state = state; try { _state.Validate(); if (_source.MoveNext()) { _hasValues = (_isFirst = true); _current = _state.ReadTransform(_collection, _source.Current); } } catch (Exception ex) { _state.Handle(ex); throw; } } public bool Read() { if (!_hasValues) { return false; } if (_isFirst) { _isFirst = false; return true; } if (_source != null) { _state.Validate(); try { bool result = _source.MoveNext(); _current = _state.ReadTransform(_collection, _source.Current); return result; } catch (Exception ex) { _state.Handle(ex); throw ex; } } return false; } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } ~BsonDataReader() { Dispose(disposing: false); } protected virtual void Dispose(bool disposing) { if (!_disposed) { _disposed = true; if (disposing) { _source?.Dispose(); } } } } internal static class BsonDataReaderExtensions { public static IEnumerable ToEnumerable(this IBsonDataReader reader) { try { while (reader.Read()) { yield return reader.Current; } } finally { reader.Dispose(); } } public static BsonValue[] ToArray(this IBsonDataReader reader) { return reader.ToEnumerable().ToArray(); } public static IList ToList(this IBsonDataReader reader) { return reader.ToEnumerable().ToList(); } public static BsonValue First(this IBsonDataReader reader) { return reader.ToEnumerable().First(); } public static BsonValue FirstOrDefault(this IBsonDataReader reader) { return reader.ToEnumerable().FirstOrDefault(); } public static BsonValue Single(this IBsonDataReader reader) { return reader.ToEnumerable().Single(); } public static BsonValue SingleOrDefault(this IBsonDataReader reader) { return reader.ToEnumerable().SingleOrDefault(); } } internal interface IBsonDataReader : IDisposable { BsonValue this[string field] { get; } string Collection { get; } BsonValue Current { get; } bool HasValues { get; } bool Read(); } internal delegate IEnumerable BsonExpressionEnumerableDelegate(IEnumerable source, BsonDocument root, BsonValue current, Collation collation, BsonDocument parameters); internal delegate BsonValue BsonExpressionScalarDelegate(IEnumerable source, BsonDocument root, BsonValue current, Collation collation, BsonDocument parameters); internal sealed class BsonExpression { private BsonExpressionEnumerableDelegate _funcEnumerable; private BsonExpressionScalarDelegate _funcScalar; private static readonly ConcurrentDictionary _cacheEnumerable = new ConcurrentDictionary(); private static readonly ConcurrentDictionary _cacheScalar = new ConcurrentDictionary(); public static BsonExpression Root = Create("$"); private static readonly Dictionary _methods = typeof(BsonExpressionMethods).GetMethods(BindingFlags.Static | BindingFlags.Public).ToDictionary((MethodInfo m) => m.Name.ToUpperInvariant() + "~" + (from p in m.GetParameters() where p.ParameterType != typeof(Collation) select p).Count()); private static readonly Dictionary _functions = typeof(BsonExpressionFunctions).GetMethods(BindingFlags.Static | BindingFlags.Public).ToDictionary((MethodInfo m) => m.Name.ToUpperInvariant() + "~" + m.GetParameters().Skip(5).Count()); public string Source { get; internal set; } public BsonExpressionType Type { get; internal set; } public bool IsImmutable { get; internal set; } public BsonDocument Parameters { get; internal set; } internal BsonExpression Left { get; set; } internal BsonExpression Right { get; set; } internal bool UseSource { get; set; } internal Expression Expression { get; set; } public HashSet Fields { get; internal set; } public bool IsScalar { get; internal set; } internal bool IsPredicate { get { if (Type != BsonExpressionType.Equal && Type != BsonExpressionType.Like && Type != BsonExpressionType.Between && Type != BsonExpressionType.GreaterThan && Type != BsonExpressionType.GreaterThanOrEqual && Type != BsonExpressionType.LessThan && Type != BsonExpressionType.LessThanOrEqual && Type != BsonExpressionType.NotEqual) { return Type == BsonExpressionType.In; } return true; } } internal bool IsIndexable { get { if (Fields.Count > 0 && IsImmutable) { return Parameters.Count == 0; } return false; } } internal bool IsValue => Fields.Count == 0; internal bool IsANY { get { if (IsPredicate) { return Expression.ToString().Contains("_ANY"); } return false; } } public static IEnumerable Methods => _methods.Values; public static IEnumerable Functions => _functions.Values; internal string DefaultFieldName() { string text = string.Join("_", Fields.Where((string x) => x != "$")); if (!string.IsNullOrEmpty(text)) { return text; } return "expr"; } internal BsonExpression() { } public static implicit operator string(BsonExpression expr) { return expr.Source; } public static implicit operator BsonExpression(string expr) { return Create(expr); } public IEnumerable Execute(Collation collation = null) { BsonDocument bsonDocument = new BsonDocument(); BsonDocument[] source = new BsonDocument[1] { bsonDocument }; return Execute(source, bsonDocument, bsonDocument, collation); } public IEnumerable Execute(BsonDocument root, Collation collation = null) { if (root == null) { throw new ArgumentNullException("root"); } BsonDocument[] source = new BsonDocument[1] { root }; return Execute(source, root, root, collation); } public IEnumerable Execute(IEnumerable source, Collation collation = null) { if (source == null) { throw new ArgumentNullException("source"); } return Execute(source, null, null, collation); } internal IEnumerable Execute(IEnumerable source, BsonDocument root, BsonValue current, Collation collation) { if (IsScalar) { yield return _funcScalar(source, root, current, collation ?? Collation.Binary, Parameters); yield break; } IEnumerable enumerable = _funcEnumerable(source, root, current, collation ?? Collation.Binary, Parameters); foreach (BsonValue item in enumerable) { yield return item; } } internal IEnumerable GetIndexKeys(BsonDocument doc, Collation collation) { return Execute(doc, collation).Distinct(); } public BsonValue ExecuteScalar(Collation collation = null) { BsonDocument bsonDocument = new BsonDocument(); BsonDocument[] source = new BsonDocument[0]; return ExecuteScalar(source, bsonDocument, bsonDocument, collation); } public BsonValue ExecuteScalar(BsonDocument root, Collation collation = null) { if (root == null) { throw new ArgumentNullException("root"); } BsonDocument[] source = new BsonDocument[1] { root }; return ExecuteScalar(source, root, root, collation); } public BsonValue ExecuteScalar(IEnumerable source, Collation collation = null) { if (source == null) { throw new ArgumentNullException("source"); } return ExecuteScalar(source, null, null, collation); } internal BsonValue ExecuteScalar(IEnumerable source, BsonDocument root, BsonValue current, Collation collation) { if (IsScalar) { return _funcScalar(source, root, current, collation ?? Collation.Binary, Parameters); } throw new LiteException(0, "Expression `" + Source + "` is not a scalar expression and can return more than one result"); } public static BsonExpression Create(string expression) { return Create(expression, new BsonDocument()); } public static BsonExpression Create(string expression, params BsonValue[] args) { BsonDocument bsonDocument = new BsonDocument(); for (int i = 0; i < args.Length; i++) { bsonDocument[i.ToString()] = args[i]; } return Create(expression, bsonDocument); } public static BsonExpression Create(string expression, BsonDocument parameters) { if (string.IsNullOrWhiteSpace(expression)) { throw new ArgumentNullException("expression"); } Tokenizer tokenizer = new Tokenizer(expression); BsonExpression result = Create(tokenizer, BsonExpressionParserMode.Full, parameters); tokenizer.LookAhead().Expect(TokenType.EOF); return result; } internal static BsonExpression Create(Tokenizer tokenizer, BsonExpressionParserMode mode, BsonDocument parameters) { if (tokenizer == null) { throw new ArgumentNullException("tokenizer"); } return ParseAndCompile(tokenizer, mode, parameters, DocumentScope.Root); } internal static BsonExpression ParseAndCompile(Tokenizer tokenizer, BsonExpressionParserMode mode, BsonDocument parameters, DocumentScope scope) { if (tokenizer == null) { throw new ArgumentNullException("tokenizer"); } ExpressionContext context = new ExpressionContext(); object obj = mode switch { BsonExpressionParserMode.SelectDocument => BsonExpressionParser.ParseSelectDocumentBuilder(tokenizer, context, parameters), BsonExpressionParserMode.Single => BsonExpressionParser.ParseSingleExpression(tokenizer, context, parameters, scope), BsonExpressionParserMode.Full => BsonExpressionParser.ParseFullExpression(tokenizer, context, parameters, scope), _ => BsonExpressionParser.ParseUpdateDocumentBuilder(tokenizer, context, parameters), }; Compile((BsonExpression)obj, context); return (BsonExpression)obj; } internal static void Compile(BsonExpression expr, ExpressionContext context) { if (expr.IsScalar) { BsonExpressionScalarDelegate orAdd = _cacheScalar.GetOrAdd(expr.Source, (string s) => Expression.Lambda(expr.Expression, new ParameterExpression[5] { context.Source, context.Root, context.Current, context.Collation, context.Parameters }).Compile()); expr._funcScalar = orAdd; } else { BsonExpressionEnumerableDelegate orAdd2 = _cacheEnumerable.GetOrAdd(expr.Source, (string s) => Expression.Lambda(expr.Expression, new ParameterExpression[5] { context.Source, context.Root, context.Current, context.Collation, context.Parameters }).Compile()); expr._funcEnumerable = orAdd2; } if (expr.Left != null) { Compile(expr.Left, context); } if (expr.Right != null) { Compile(expr.Right, context); } } internal static void SetParameters(BsonExpression expr, BsonDocument parameters) { expr.Parameters = parameters; if (expr.Left != null) { SetParameters(expr.Left, parameters); } if (expr.Right != null) { SetParameters(expr.Right, parameters); } } internal static MethodInfo GetMethod(string name, int parameterCount) { string key = name.ToUpperInvariant() + "~" + parameterCount; return _methods.GetOrDefault(key); } internal static MethodInfo GetFunction(string name, int parameterCount = 0) { string key = name.ToUpperInvariant() + "~" + parameterCount; return _functions.GetOrDefault(key); } public override string ToString() { return $"`{Source}` [{Type}]"; } } internal class BsonExpressionMethods { private static readonly Random _random = new Random(); public static BsonValue COUNT(IEnumerable values) { return values.Count(); } public static BsonValue MIN(IEnumerable values) { BsonValue bsonValue = BsonValue.MaxValue; foreach (BsonValue value in values) { if (value.CompareTo(bsonValue) <= 0) { bsonValue = value; } } if (!(bsonValue == BsonValue.MaxValue)) { return bsonValue; } return BsonValue.MinValue; } public static BsonValue MAX(IEnumerable values) { BsonValue bsonValue = BsonValue.MinValue; foreach (BsonValue value in values) { if (value.CompareTo(bsonValue) >= 0) { bsonValue = value; } } if (!(bsonValue == BsonValue.MinValue)) { return bsonValue; } return BsonValue.MaxValue; } public static BsonValue FIRST(IEnumerable values) { return values.FirstOrDefault(); } public static BsonValue LAST(IEnumerable values) { return values.LastOrDefault(); } public static BsonValue AVG(IEnumerable values) { BsonValue bsonValue = new BsonValue(0); int num = 0; foreach (BsonValue item in values.Where((BsonValue x) => x.IsNumber)) { bsonValue += item; num++; } if (num > 0) { return bsonValue / num; } return 0; } public static BsonValue SUM(IEnumerable values) { BsonValue result = new BsonValue(0); foreach (BsonValue item in values.Where((BsonValue x) => x.IsNumber)) { result += item; } return result; } public static BsonValue ANY(IEnumerable values) { return values.Any(); } public static BsonValue MINVALUE() { return BsonValue.MinValue; } [Volatile] public static BsonValue OBJECTID() { return ObjectId.NewObjectId(); } [Volatile] public static BsonValue GUID() { return Guid.NewGuid(); } [Volatile] public static BsonValue NOW() { return DateTime.Now; } [Volatile] public static BsonValue NOW_UTC() { return DateTime.UtcNow; } [Volatile] public static BsonValue TODAY() { return DateTime.Today; } public static BsonValue MAXVALUE() { return BsonValue.MaxValue; } public static BsonValue INT32(BsonValue value) { if (value.IsNumber) { return value.AsInt32; } if (value.IsString && int.TryParse(value.AsString, out var result)) { return result; } return BsonValue.Null; } public static BsonValue INT64(BsonValue value) { if (value.IsNumber) { return value.AsInt64; } if (value.IsString && long.TryParse(value.AsString, out var result)) { return result; } return BsonValue.Null; } public static BsonValue DOUBLE(Collation collation, BsonValue value) { if (value.IsNumber) { return value.AsDouble; } if (value.IsString && double.TryParse(value.AsString, NumberStyles.Any, collation.Culture.NumberFormat, out var result)) { return result; } return BsonValue.Null; } public static BsonValue DOUBLE(BsonValue value, BsonValue culture) { if (value.IsNumber) { return value.AsDouble; } if (value.IsString && culture.IsString) { CultureInfo cultureInfo = new CultureInfo(culture.AsString); if (double.TryParse(value.AsString, NumberStyles.Any, cultureInfo.NumberFormat, out var result)) { return result; } } return BsonValue.Null; } public static BsonValue DECIMAL(Collation collation, BsonValue value) { if (value.IsNumber) { return value.AsDecimal; } if (value.IsString && decimal.TryParse(value.AsString, NumberStyles.Any, collation.Culture.NumberFormat, out var result)) { return result; } return BsonValue.Null; } public static BsonValue DECIMAL(BsonValue value, BsonValue culture) { if (value.IsNumber) { return value.AsDecimal; } if (value.IsString && culture.IsString) { CultureInfo cultureInfo = new CultureInfo(culture.AsString); if (decimal.TryParse(value.AsString, NumberStyles.Any, cultureInfo.NumberFormat, out var result)) { return result; } } return BsonValue.Null; } public static BsonValue STRING(BsonValue value) { return value.IsNull ? "" : (value.IsString ? value.AsString : value.ToString()); } public static BsonValue ARRAY(IEnumerable values) { return new BsonArray(values); } public static BsonValue BINARY(BsonValue value) { if (value.IsBinary) { return value; } if (value.IsString) { byte[] array = null; bool flag = false; try { array = Convert.FromBase64String(value.AsString); flag = true; } catch (FormatException) { } if (flag) { return array; } } return BsonValue.Null; } public static BsonValue OBJECTID(BsonValue value) { if (value.IsObjectId) { return value.AsObjectId; } if (value.IsString) { ObjectId objectId = null; bool flag = false; try { objectId = new ObjectId(value.AsString); flag = true; } catch { } if (flag) { return objectId; } } return BsonValue.Null; } public static BsonValue GUID(BsonValue value) { if (value.IsGuid) { return value.AsGuid; } if (value.IsString) { Guid guid = Guid.Empty; bool flag = false; try { guid = new Guid(value.AsString); flag = true; } catch { } if (flag) { return guid; } } return BsonValue.Null; } public static BsonValue BOOLEAN(BsonValue value) { if (value.IsBoolean) { return value.AsBoolean; } bool flag = false; bool flag2 = false; try { flag = Convert.ToBoolean(value.AsString); flag2 = true; } catch { } if (flag2) { return flag; } return BsonValue.Null; } public static BsonValue DATETIME(Collation collation, BsonValue value) { if (value.IsDateTime) { return value.AsDateTime; } if (value.IsString && DateTime.TryParse(value.AsString, collation.Culture.DateTimeFormat, DateTimeStyles.None, out var result)) { return result; } return BsonValue.Null; } public static BsonValue DATETIME(BsonValue value, BsonValue culture) { if (value.IsDateTime) { return value.AsDateTime; } if (value.IsString && culture.IsString) { CultureInfo cultureInfo = new CultureInfo(culture.AsString); if (DateTime.TryParse(value.AsString, cultureInfo.DateTimeFormat, DateTimeStyles.None, out var result)) { return result; } } return BsonValue.Null; } public static BsonValue DATETIME_UTC(Collation collation, BsonValue value) { if (value.IsDateTime) { return value.AsDateTime; } if (value.IsString && DateTime.TryParse(value.AsString, collation.Culture.DateTimeFormat, DateTimeStyles.AssumeUniversal, out var result)) { return result; } return BsonValue.Null; } public static BsonValue DATETIME_UTC(BsonValue value, BsonValue culture) { if (value.IsDateTime) { return value.AsDateTime; } if (value.IsString && culture.IsString) { CultureInfo cultureInfo = new CultureInfo(culture.AsString); if (DateTime.TryParse(value.AsString, cultureInfo.DateTimeFormat, DateTimeStyles.AssumeUniversal, out var result)) { return result; } } return BsonValue.Null; } public static BsonValue DATETIME(BsonValue year, BsonValue month, BsonValue day) { if (year.IsNumber && month.IsNumber && day.IsNumber) { return new DateTime(year.AsInt32, month.AsInt32, day.AsInt32); } return BsonValue.Null; } public static BsonValue DATETIME_UTC(BsonValue year, BsonValue month, BsonValue day) { if (year.IsNumber && month.IsNumber && day.IsNumber) { return new DateTime(year.AsInt32, month.AsInt32, day.AsInt32, 0, 0, 0, DateTimeKind.Utc); } return BsonValue.Null; } public static BsonValue IS_MINVALUE(BsonValue value) { return value.IsMinValue; } public static BsonValue IS_NULL(BsonValue value) { return value.IsNull; } public static BsonValue IS_INT32(BsonValue value) { return value.IsInt32; } public static BsonValue IS_INT64(BsonValue value) { return value.IsInt64; } public static BsonValue IS_DOUBLE(BsonValue value) { return value.IsDouble; } public static BsonValue IS_DECIMAL(BsonValue value) { return value.IsDecimal; } public static BsonValue IS_NUMBER(BsonValue value) { return value.IsNumber; } public static BsonValue IS_STRING(BsonValue value) { return value.IsString; } public static BsonValue IS_DOCUMENT(BsonValue value) { return value.IsDocument; } public static BsonValue IS_ARRAY(BsonValue value) { return value.IsArray; } public static BsonValue IS_BINARY(BsonValue value) { return value.IsBinary; } public static BsonValue IS_OBJECTID(BsonValue value) { return value.IsObjectId; } public static BsonValue IS_GUID(BsonValue value) { return value.IsGuid; } public static BsonValue IS_BOOLEAN(BsonValue value) { return value.IsBoolean; } public static BsonValue IS_DATETIME(BsonValue value) { return value.IsDateTime; } public static BsonValue IS_MAXVALUE(BsonValue value) { return value.IsMaxValue; } public static BsonValue INT(BsonValue value) { return INT32(value); } public static BsonValue LONG(BsonValue value) { return INT64(value); } public static BsonValue BOOL(BsonValue value) { return BOOLEAN(value); } public static BsonValue DATE(Collation collation, BsonValue value) { return DATETIME(collation, value); } public static BsonValue DATE(BsonValue values, BsonValue culture) { return DATETIME(values, culture); } public static BsonValue DATE_UTC(Collation collation, BsonValue value) { return DATETIME_UTC(collation, value); } public static BsonValue DATE_UTC(BsonValue values, BsonValue culture) { return DATETIME_UTC(values, culture); } public static BsonValue DATE(BsonValue year, BsonValue month, BsonValue day) { return DATETIME(year, month, day); } public static BsonValue DATE_UTC(BsonValue year, BsonValue month, BsonValue day) { return DATETIME_UTC(year, month, day); } public static BsonValue IS_INT(BsonValue value) { return IS_INT32(value); } public static BsonValue IS_LONG(BsonValue value) { return IS_INT64(value); } public static BsonValue IS_BOOL(BsonValue value) { return IS_BOOLEAN(value); } public static BsonValue IS_DATE(BsonValue value) { return IS_DATETIME(value); } public static BsonValue YEAR(BsonValue value) { if (value.IsDateTime) { return value.AsDateTime.Year; } return BsonValue.Null; } public static BsonValue MONTH(BsonValue value) { if (value.IsDateTime) { return value.AsDateTime.Month; } return BsonValue.Null; } public static BsonValue DAY(BsonValue value) { if (value.IsDateTime) { return value.AsDateTime.Day; } return BsonValue.Null; } public static BsonValue HOUR(BsonValue value) { if (value.IsDateTime) { return value.AsDateTime.Hour; } return BsonValue.Null; } public static BsonValue MINUTE(BsonValue value) { if (value.IsDateTime) { return value.AsDateTime.Minute; } return BsonValue.Null; } public static BsonValue SECOND(BsonValue value) { if (value.IsDateTime) { return value.AsDateTime.Second; } return BsonValue.Null; } public static BsonValue DATEADD(BsonValue dateInterval, BsonValue number, BsonValue value) { if (dateInterval.IsString && number.IsNumber && value.IsDateTime) { string asString = dateInterval.AsString; int asInt = number.AsInt32; DateTime asDateTime = value.AsDateTime; switch ((asString == "M") ? "month" : asString.ToLower()) { case "y": case "year": return asDateTime.AddYears(asInt); case "month": return asDateTime.AddMonths(asInt); case "d": case "day": return asDateTime.AddDays(asInt); case "h": case "hour": return asDateTime.AddHours(asInt); case "m": case "minute": return asDateTime.AddMinutes(asInt); case "s": case "second": return asDateTime.AddSeconds(asInt); } } return BsonValue.Null; } public static BsonValue DATEDIFF(BsonValue dateInterval, BsonValue starts, BsonValue ends) { if (dateInterval.IsString && starts.IsDateTime && ends.IsDateTime) { string asString = dateInterval.AsString; DateTime asDateTime = starts.AsDateTime; DateTime asDateTime2 = ends.AsDateTime; switch ((asString == "M") ? "month" : asString.ToLower()) { case "y": case "year": return asDateTime.YearDifference(asDateTime2); case "month": return asDateTime.MonthDifference(asDateTime2); case "d": case "day": return Convert.ToInt32(Math.Truncate(asDateTime2.Subtract(asDateTime).TotalDays)); case "h": case "hour": return Convert.ToInt32(Math.Truncate(asDateTime2.Subtract(asDateTime).TotalHours)); case "m": case "minute": return Convert.ToInt32(Math.Truncate(asDateTime2.Subtract(asDateTime).TotalMinutes)); case "s": case "second": return Convert.ToInt32(Math.Truncate(asDateTime2.Subtract(asDateTime).TotalSeconds)); } } return BsonValue.Null; } public static BsonValue TO_LOCAL(BsonValue date) { if (date.IsDateTime) { return date.AsDateTime.ToLocalTime(); } return BsonValue.Null; } public static BsonValue TO_UTC(BsonValue date) { if (date.IsDateTime) { return date.AsDateTime.ToUniversalTime(); } return BsonValue.Null; } public static BsonValue ABS(BsonValue value) { return value.Type switch { BsonType.Int32 => Math.Abs(value.AsInt32), BsonType.Int64 => Math.Abs(value.AsInt64), BsonType.Double => Math.Abs(value.AsDouble), BsonType.Decimal => Math.Abs(value.AsDecimal), _ => BsonValue.Null, }; } public static BsonValue ROUND(BsonValue value, BsonValue digits) { if (digits.IsNumber) { switch (value.Type) { case BsonType.Int32: return value.AsInt32; case BsonType.Int64: return value.AsInt64; case BsonType.Double: return Math.Round(value.AsDouble, digits.AsInt32); case BsonType.Decimal: return Math.Round(value.AsDecimal, digits.AsInt32); } } return BsonValue.Null; } public static BsonValue POW(BsonValue x, BsonValue y) { if (x.IsNumber && y.IsNumber) { return Math.Pow(x.AsDouble, y.AsDouble); } return BsonValue.Null; } public static BsonValue JSON(BsonValue json) { if (json.IsString) { BsonValue result = null; bool flag = false; try { result = JsonSerializer.Deserialize(json.AsString); flag = true; } catch (LiteException ex) when (ex.ErrorCode == 203) { } if (flag) { return result; } } return BsonValue.Null; } public static BsonValue EXTEND(BsonValue source, BsonValue extend) { if (source.IsDocument && extend.IsDocument) { BsonDocument bsonDocument = new BsonDocument(); source.AsDocument.CopyTo(bsonDocument); extend.AsDocument.CopyTo(bsonDocument); bsonDocument.RawId = source.AsDocument.RawId; return bsonDocument; } if (source.IsDocument) { return source; } if (extend.IsDocument) { return extend; } return new BsonDocument(); } public static IEnumerable ITEMS(BsonValue array) { if (array.IsArray) { foreach (BsonValue item in array.AsArray) { yield return item; } } else if (array.IsBinary) { byte[] asBinary = array.AsBinary; foreach (byte b in asBinary) { yield return (int)b; } } else { yield return array; } } public static IEnumerable CONCAT(IEnumerable first, IEnumerable second) { return first.Concat(second); } public static IEnumerable KEYS(BsonValue document) { if (!document.IsDocument) { yield break; } foreach (string key in document.AsDocument.Keys) { yield return key; } } public static IEnumerable VALUES(BsonValue document) { if (!document.IsDocument) { yield break; } foreach (BsonValue value in document.AsDocument.Values) { yield return value; } } public static BsonValue OID_CREATIONTIME(BsonValue objectId) { if (objectId.IsObjectId) { return objectId.AsObjectId.CreationTime; } return BsonValue.Null; } public static BsonValue IIF(BsonValue test, BsonValue ifTrue, BsonValue ifFalse) { throw new NotImplementedException(); } public static BsonValue COALESCE(BsonValue left, BsonValue right) { if (!left.IsNull) { return left; } return right; } public static BsonValue LENGTH(BsonValue value) { if (value.IsString) { return value.AsString.Length; } if (value.IsBinary) { return value.AsBinary.Length; } if (value.IsArray) { return value.AsArray.Count; } if (value.IsDocument) { return value.AsDocument.Keys.Count; } if (value.IsNull) { return 0; } return BsonValue.Null; } public static IEnumerable TOP(IEnumerable values, BsonValue num) { if (num.IsInt32 || num.IsInt64) { int asInt = num.AsInt32; if (asInt > 0) { return values.Take(asInt); } } return Enumerable.Empty(); } public static IEnumerable UNION(IEnumerable left, IEnumerable right) { return left.Union(right); } public static IEnumerable EXCEPT(IEnumerable left, IEnumerable right) { return left.Except(right); } public static IEnumerable DISTINCT(IEnumerable items) { return items.Distinct(); } [Volatile] public static BsonValue RANDOM() { return _random.Next(); } [Volatile] public static BsonValue RANDOM(BsonValue min, BsonValue max) { if (min.IsNumber && max.IsNumber) { return _random.Next(min.AsInt32, max.AsInt32); } return BsonValue.Null; } public static BsonValue LOWER(BsonValue value) { if (value.IsString) { return value.AsString.ToLowerInvariant(); } return BsonValue.Null; } public static BsonValue UPPER(BsonValue value) { if (value.IsString) { return value.AsString.ToUpperInvariant(); } return BsonValue.Null; } public static BsonValue LTRIM(BsonValue value) { if (value.IsString) { return value.AsString.TrimStart(new char[0]); } return BsonValue.Null; } public static BsonValue RTRIM(BsonValue value) { if (value.IsString) { return value.AsString.TrimEnd(new char[0]); } return BsonValue.Null; } public static BsonValue TRIM(BsonValue value) { if (value.IsString) { return value.AsString.Trim(); } return BsonValue.Null; } public static BsonValue INDEXOF(BsonValue value, BsonValue search) { if (value.IsString && search.IsString) { return value.AsString.IndexOf(search.AsString); } return BsonValue.Null; } public static BsonValue INDEXOF(BsonValue value, BsonValue search, BsonValue startIndex) { if (value.IsString && search.IsString && startIndex.IsNumber) { return value.AsString.IndexOf(search.AsString, startIndex.AsInt32); } return BsonValue.Null; } public static BsonValue SUBSTRING(BsonValue value, BsonValue startIndex) { if (value.IsString && startIndex.IsNumber) { return value.AsString.Substring(startIndex.AsInt32); } return BsonValue.Null; } public static BsonValue SUBSTRING(BsonValue value, BsonValue startIndex, BsonValue length) { if (value.IsString && startIndex.IsNumber && length.IsNumber) { return value.AsString.Substring(startIndex.AsInt32, length.AsInt32); } return BsonValue.Null; } public static BsonValue REPLACE(BsonValue value, BsonValue oldValue, BsonValue newValue) { if (value.IsString && oldValue.IsString && newValue.IsString) { return value.AsString.Replace(oldValue.AsString, newValue.AsString); } return BsonValue.Null; } public static BsonValue LPAD(BsonValue value, BsonValue totalWidth, BsonValue paddingChar) { if (value.IsString && totalWidth.IsNumber && paddingChar.IsString) { string asString = paddingChar.AsString; if (string.IsNullOrEmpty(asString)) { throw new ArgumentOutOfRangeException("paddingChar"); } return value.AsString.PadLeft(totalWidth.AsInt32, asString[0]); } return BsonValue.Null; } public static BsonValue RPAD(BsonValue value, BsonValue totalWidth, BsonValue paddingChar) { if (value.IsString && totalWidth.IsNumber && paddingChar.IsString) { string asString = paddingChar.AsString; if (string.IsNullOrEmpty(asString)) { throw new ArgumentOutOfRangeException("paddingChar"); } return value.AsString.PadRight(totalWidth.AsInt32, asString[0]); } return BsonValue.Null; } public static IEnumerable SPLIT(BsonValue value, BsonValue separator) { if (value.IsString && separator.IsString) { string[] array = value.AsString.Split(new string[1] { separator.AsString }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { yield return text; } } } public static IEnumerable SPLIT(BsonValue value, BsonValue pattern, BsonValue useRegex) { if (useRegex.IsBoolean && useRegex.AsBoolean) { if (value.IsString && pattern.IsString) { string[] array = Regex.Split(value.AsString, pattern.AsString, RegexOptions.Compiled); string[] array2 = array; foreach (string text in array2) { yield return text; } } yield break; } foreach (BsonValue item in SPLIT(value, pattern)) { yield return item; } } public static BsonValue FORMAT(BsonValue value, BsonValue format) { if (format.IsString) { return string.Format("{0:" + format.AsString + "}", value.RawValue); } return BsonValue.Null; } public static BsonValue JOIN(IEnumerable values) { return JOIN(values, ""); } public static BsonValue JOIN(IEnumerable values, BsonValue separator) { if (separator.IsString) { return string.Join(separator.AsString, values.Select((BsonValue x) => STRING(x).AsString).ToArray()); } return BsonValue.Null; } public static BsonValue IS_MATCH(BsonValue value, BsonValue pattern) { if (!value.IsString || !pattern.IsString) { return false; } return Regex.IsMatch(value.AsString, pattern.AsString); } public static BsonValue MATCH(BsonValue value, BsonValue pattern, BsonValue group) { if (!value.IsString || !pattern.IsString) { return null; } Match match = Regex.Match(value.AsString, pattern.AsString); if (!match.Success) { return BsonValue.Null; } if (group.IsNumber) { return match.Groups[group.AsInt32].Value; } if (group.IsString) { return match.Groups[group.AsString].Value; } return BsonValue.Null; } } internal class VolatileAttribute : Attribute { } internal class BsonExpressionFunctions { public static IEnumerable MAP(BsonDocument root, Collation collation, BsonDocument parameters, IEnumerable input, BsonExpression mapExpr) { foreach (BsonValue item in input) { IEnumerable enumerable = mapExpr.Execute(new BsonDocument[1] { root }, root, item, collation); foreach (BsonValue item2 in enumerable) { yield return item2; } } } public static IEnumerable FILTER(BsonDocument root, Collation collation, BsonDocument parameters, IEnumerable input, BsonExpression filterExpr) { foreach (BsonValue item in input) { BsonValue bsonValue = filterExpr.ExecuteScalar(new BsonDocument[1] { root }, root, item, collation); if (bsonValue.IsBoolean && bsonValue.AsBoolean) { yield return item; } } } public static IEnumerable SORT(BsonDocument root, Collation collation, BsonDocument parameters, IEnumerable input, BsonExpression sortExpr, BsonValue order) { if ((!order.IsInt32 || order.AsInt32 <= 0) && (!order.IsString || !order.AsString.Equals("asc", StringComparison.OrdinalIgnoreCase))) { return from x in source().OrderByDescending((Tuple x) => x.Item2, collation) select x.Item1; } return from x in source().OrderBy((Tuple x) => x.Item2, collation) select x.Item1; IEnumerable> source() { foreach (BsonValue item2 in input) { BsonValue item = sortExpr.ExecuteScalar(new BsonDocument[1] { root }, root, item2, collation); yield return new Tuple(item2, item); } } } public static IEnumerable SORT(BsonDocument root, Collation collation, BsonDocument parameters, IEnumerable input, BsonExpression sortExpr) { return SORT(root, collation, parameters, input, sortExpr, 1); } } internal class BsonExpressionOperators { public static BsonValue ADD(BsonValue left, BsonValue right) { if (left.IsString && right.IsString) { return left.AsString + right.AsString; } if (left.IsString || right.IsString) { return BsonExpressionMethods.STRING(left).AsString + BsonExpressionMethods.STRING(right).AsString; } if (left.IsDateTime && right.IsNumber) { return left.AsDateTime.AddTicks(right.AsInt64); } if (left.IsNumber && right.IsDateTime) { return right.AsDateTime.AddTicks(left.AsInt64); } if (left.IsNumber && right.IsNumber) { return left + right; } return BsonValue.Null; } public static BsonValue MINUS(BsonValue left, BsonValue right) { if (left.IsDateTime && right.IsNumber) { return left.AsDateTime.AddTicks(-right.AsInt64); } if (left.IsNumber && right.IsDateTime) { return right.AsDateTime.AddTicks(-left.AsInt64); } if (left.IsNumber && right.IsNumber) { return left - right; } return BsonValue.Null; } public static BsonValue MULTIPLY(BsonValue left, BsonValue right) { if (left.IsNumber && right.IsNumber) { return left * right; } return BsonValue.Null; } public static BsonValue DIVIDE(BsonValue left, BsonValue right) { if (left.IsNumber && right.IsNumber) { return left / right; } return BsonValue.Null; } public static BsonValue MOD(BsonValue left, BsonValue right) { if (left.IsNumber && right.IsNumber) { return (int)left % (int)right; } return BsonValue.Null; } public static BsonValue EQ(Collation collation, BsonValue left, BsonValue right) { return collation.Equals(left, right); } public static BsonValue EQ_ALL(Collation collation, IEnumerable left, BsonValue right) { return left.All((BsonValue x) => collation.Equals(x, right)); } public static BsonValue EQ_ANY(Collation collation, IEnumerable left, BsonValue right) { return left.Any((BsonValue x) => collation.Equals(x, right)); } public static BsonValue GT(BsonValue left, BsonValue right) { return left > right; } public static BsonValue GT_ANY(Collation collation, IEnumerable left, BsonValue right) { return left.Any((BsonValue x) => collation.Compare(x, right) > 0); } public static BsonValue GT_ALL(Collation collation, IEnumerable left, BsonValue right) { return left.All((BsonValue x) => collation.Compare(x, right) > 0); } public static BsonValue GTE(BsonValue left, BsonValue right) { return left >= right; } public static BsonValue GTE_ANY(Collation collation, IEnumerable left, BsonValue right) { return left.Any((BsonValue x) => collation.Compare(x, right) >= 0); } public static BsonValue GTE_ALL(Collation collation, IEnumerable left, BsonValue right) { return left.All((BsonValue x) => collation.Compare(x, right) >= 0); } public static BsonValue LT(BsonValue left, BsonValue right) { return left < right; } public static BsonValue LT_ANY(Collation collation, IEnumerable left, BsonValue right) { return left.Any((BsonValue x) => collation.Compare(x, right) < 0); } public static BsonValue LT_ALL(Collation collation, IEnumerable left, BsonValue right) { return left.All((BsonValue x) => collation.Compare(x, right) < 0); } public static BsonValue LTE(Collation collation, BsonValue left, BsonValue right) { return left <= right; } public static BsonValue LTE_ANY(Collation collation, IEnumerable left, BsonValue right) { return left.Any((BsonValue x) => collation.Compare(x, right) <= 0); } public static BsonValue LTE_ALL(Collation collation, IEnumerable left, BsonValue right) { return left.All((BsonValue x) => collation.Compare(x, right) <= 0); } public static BsonValue NEQ(Collation collation, BsonValue left, BsonValue right) { return !collation.Equals(left, right); } public static BsonValue NEQ_ANY(Collation collation, IEnumerable left, BsonValue right) { return left.Any((BsonValue x) => !collation.Equals(x, right)); } public static BsonValue NEQ_ALL(Collation collation, IEnumerable left, BsonValue right) { return left.All((BsonValue x) => !collation.Equals(x, right)); } public static BsonValue LIKE(Collation collation, BsonValue left, BsonValue right) { if (left.IsString && right.IsString) { return left.AsString.SqlLike(right.AsString, collation); } return false; } public static BsonValue LIKE_ANY(Collation collation, IEnumerable left, BsonValue right) { return left.Any((BsonValue x) => LIKE(collation, x, right)); } public static BsonValue LIKE_ALL(Collation collation, IEnumerable left, BsonValue right) { return left.All((BsonValue x) => LIKE(collation, x, right)); } public static BsonValue BETWEEN(Collation collation, BsonValue left, BsonValue right) { if (!right.IsArray) { throw new InvalidOperationException("BETWEEN expression need an array with 2 values"); } BsonArray asArray = right.AsArray; if (asArray.Count != 2) { throw new InvalidOperationException("BETWEEN expression need an array with 2 values"); } BsonValue rigth = asArray[0]; BsonValue rigth2 = asArray[1]; return collation.Compare(left, rigth) >= 0 && collation.Compare(left, rigth2) <= 0; } public static BsonValue BETWEEN_ANY(Collation collation, IEnumerable left, BsonValue right) { return left.Any((BsonValue x) => BETWEEN(collation, x, right)); } public static BsonValue BETWEEN_ALL(Collation collation, IEnumerable left, BsonValue right) { return left.All((BsonValue x) => BETWEEN(collation, x, right)); } public static BsonValue IN(Collation collation, BsonValue left, BsonValue right) { if (right.IsArray) { return right.AsArray.Contains(left, collation); } return left == right; } public static BsonValue IN_ANY(Collation collation, IEnumerable left, BsonValue right) { return left.Any((BsonValue x) => IN(collation, x, right)); } public static BsonValue IN_ALL(Collation collation, IEnumerable left, BsonValue right) { return left.All((BsonValue x) => IN(collation, x, right)); } public static BsonValue PARAMETER_PATH(BsonDocument doc, string name) { if (string.IsNullOrEmpty(name)) { return doc; } if (doc.TryGetValue(name, out var value)) { return value; } return BsonValue.Null; } public static BsonValue MEMBER_PATH(BsonValue value, string name) { if (value == null) { throw new LiteException(0, "Field '" + name + "' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."); } if (string.IsNullOrEmpty(name)) { return value; } if (value.IsDocument && value.AsDocument.TryGetValue(name, out var value2)) { return value2; } return BsonValue.Null; } public static BsonValue ARRAY_INDEX(BsonValue value, int index, BsonExpression expr, BsonDocument root, Collation collation, BsonDocument parameters) { if (!value.IsArray) { return BsonValue.Null; } BsonArray asArray = value.AsArray; if (expr.Type == BsonExpressionType.Parameter) { BsonValue bsonValue = expr.ExecuteScalar(root, collation); if (!bsonValue.IsNumber) { throw new LiteException(0, "Parameter expression must return number when called inside an array"); } index = bsonValue.AsInt32; } int num = ((index < 0) ? (asArray.Count + index) : index); if (asArray.Count > num) { return asArray[num]; } return BsonValue.Null; } public static IEnumerable ARRAY_FILTER(BsonValue value, int index, BsonExpression filterExpr, BsonDocument root, Collation collation, BsonDocument parameters) { if (!value.IsArray) { yield break; } BsonArray asArray = value.AsArray; if (index == int.MaxValue) { foreach (BsonValue item in asArray) { yield return item; } yield break; } foreach (BsonValue item2 in asArray) { BsonValue bsonValue = filterExpr.ExecuteScalar(new BsonDocument[1] { root }, root, item2, collation); if (bsonValue.IsBoolean && bsonValue.AsBoolean) { yield return item2; } } } public static BsonValue DOCUMENT_INIT(string[] keys, BsonValue[] values) { Constants.ENSURE(keys.Length == values.Length, "both keys/value must contains same length"); if (keys.Length == 1 && keys[0][0] == '$' && values[0].IsString) { switch (keys[0]) { case "$binary": return BsonExpressionMethods.BINARY(values[0]); case "$oid": return BsonExpressionMethods.OBJECTID(values[0]); case "$guid": return BsonExpressionMethods.GUID(values[0]); case "$date": return BsonExpressionMethods.DATE(Collation.Binary, values[0]); case "$numberLong": return BsonExpressionMethods.LONG(values[0]); case "$numberDecimal": return BsonExpressionMethods.DECIMAL(Collation.Binary, values[0]); case "$minValue": return BsonExpressionMethods.MINVALUE(); case "$maxValue": return BsonExpressionMethods.MAXVALUE(); } } BsonDocument bsonDocument = new BsonDocument(); for (int i = 0; i < keys.Length; i++) { bsonDocument[keys[i]] = values[i]; } return bsonDocument; } public static BsonValue ARRAY_INIT(BsonValue[] values) { return new BsonArray(values); } } internal enum BsonExpressionParserMode { Full, Single, SelectDocument, UpdateDocument } internal class BsonExpressionParser { private static readonly Dictionary> _operators = new Dictionary> { ["%"] = Tuple.Create("%", M("MOD"), BsonExpressionType.Modulo), ["/"] = Tuple.Create("/", M("DIVIDE"), BsonExpressionType.Divide), ["*"] = Tuple.Create("*", M("MULTIPLY"), BsonExpressionType.Multiply), ["+"] = Tuple.Create("+", M("ADD"), BsonExpressionType.Add), ["-"] = Tuple.Create("-", M("MINUS"), BsonExpressionType.Subtract), ["LIKE"] = Tuple.Create(" LIKE ", M("LIKE"), BsonExpressionType.Like), ["BETWEEN"] = Tuple.Create(" BETWEEN ", M("BETWEEN"), BsonExpressionType.Between), ["IN"] = Tuple.Create(" IN ", M("IN"), BsonExpressionType.In), [">"] = Tuple.Create(">", M("GT"), BsonExpressionType.GreaterThan), [">="] = Tuple.Create(">=", M("GTE"), BsonExpressionType.GreaterThanOrEqual), ["<"] = Tuple.Create("<", M("LT"), BsonExpressionType.LessThan), ["<="] = Tuple.Create("<=", M("LTE"), BsonExpressionType.LessThanOrEqual), ["!="] = Tuple.Create("!=", M("NEQ"), BsonExpressionType.NotEqual), ["="] = Tuple.Create("=", M("EQ"), BsonExpressionType.Equal), ["ANY LIKE"] = Tuple.Create(" ANY LIKE ", M("LIKE_ANY"), BsonExpressionType.Like), ["ANY BETWEEN"] = Tuple.Create(" ANY BETWEEN ", M("BETWEEN_ANY"), BsonExpressionType.Between), ["ANY IN"] = Tuple.Create(" ANY IN ", M("IN_ANY"), BsonExpressionType.In), ["ANY >"] = Tuple.Create(" ANY>", M("GT_ANY"), BsonExpressionType.GreaterThan), ["ANY >="] = Tuple.Create(" ANY>=", M("GTE_ANY"), BsonExpressionType.GreaterThanOrEqual), ["ANY <"] = Tuple.Create(" ANY<", M("LT_ANY"), BsonExpressionType.LessThan), ["ANY <="] = Tuple.Create(" ANY<=", M("LTE_ANY"), BsonExpressionType.LessThanOrEqual), ["ANY !="] = Tuple.Create(" ANY!=", M("NEQ_ANY"), BsonExpressionType.NotEqual), ["ANY ="] = Tuple.Create(" ANY=", M("EQ_ANY"), BsonExpressionType.Equal), ["ALL LIKE"] = Tuple.Create(" ALL LIKE ", M("LIKE_ALL"), BsonExpressionType.Like), ["ALL BETWEEN"] = Tuple.Create(" ALL BETWEEN ", M("BETWEEN_ALL"), BsonExpressionType.Between), ["ALL IN"] = Tuple.Create(" ALL IN ", M("IN_ALL"), BsonExpressionType.In), ["ALL >"] = Tuple.Create(" ALL>", M("GT_ALL"), BsonExpressionType.GreaterThan), ["ALL >="] = Tuple.Create(" ALL>=", M("GTE_ALL"), BsonExpressionType.GreaterThanOrEqual), ["ALL <"] = Tuple.Create(" ALL<", M("LT_ALL"), BsonExpressionType.LessThan), ["ALL <="] = Tuple.Create(" ALL<=", M("LTE_ALL"), BsonExpressionType.LessThanOrEqual), ["ALL !="] = Tuple.Create(" ALL!=", M("NEQ_ALL"), BsonExpressionType.NotEqual), ["ALL ="] = Tuple.Create(" ALL=", M("EQ_ALL"), BsonExpressionType.Equal), ["AND"] = Tuple.Create(" AND ", null, BsonExpressionType.And), ["OR"] = Tuple.Create(" OR ", null, BsonExpressionType.Or) }; private static readonly MethodInfo _parameterPathMethod = M("PARAMETER_PATH"); private static readonly MethodInfo _memberPathMethod = M("MEMBER_PATH"); private static readonly MethodInfo _arrayIndexMethod = M("ARRAY_INDEX"); private static readonly MethodInfo _arrayFilterMethod = M("ARRAY_FILTER"); private static readonly MethodInfo _documentInitMethod = M("DOCUMENT_INIT"); private static readonly MethodInfo _arrayInitMethod = M("ARRAY_INIT"); private static readonly MethodInfo _itemsMethod = typeof(BsonExpressionMethods).GetMethod("ITEMS"); private static readonly MethodInfo _arrayMethod = typeof(BsonExpressionMethods).GetMethod("ARRAY"); private static MethodInfo M(string s) { return typeof(BsonExpressionOperators).GetMethod(s); } public static BsonExpression ParseFullExpression(Tokenizer tokenizer, ExpressionContext context, BsonDocument parameters, DocumentScope scope) { BsonExpression item = ParseSingleExpression(tokenizer, context, parameters, scope); List list = new List { item }; List list2 = new List(); while (!tokenizer.EOF) { string text = ReadOperant(tokenizer); if (text == null) { break; } BsonExpression bsonExpression = ParseSingleExpression(tokenizer, context, parameters, scope); if (text.EndsWith("BETWEEN", StringComparison.OrdinalIgnoreCase)) { tokenizer.ReadToken().Expect("AND"); BsonExpression item2 = ParseSingleExpression(tokenizer, context, parameters, scope); bsonExpression = NewArray(bsonExpression, item2); } list.Add(bsonExpression); list2.Add(text.ToUpperInvariant()); } int num = 0; while (list.Count >= 2) { KeyValuePair> keyValuePair = _operators.ElementAt(num); int num2 = list2.IndexOf(keyValuePair.Key); if (num2 == -1) { num++; continue; } BsonExpression bsonExpression2 = list.ElementAt(num2); BsonExpression bsonExpression3 = list.ElementAt(num2 + 1); string item3 = keyValuePair.Value.Item1; MethodInfo item4 = keyValuePair.Value.Item2; BsonExpressionType item5 = keyValuePair.Value.Item3; int num3; if (!keyValuePair.Key.StartsWith("ALL")) { num3 = (keyValuePair.Key.StartsWith("ANY") ? 1 : 0); if (num3 == 0) { goto IL_0138; } } else { num3 = 1; } if (bsonExpression2.IsScalar) { bsonExpression2 = ConvertToEnumerable(bsonExpression2); } goto IL_0138; IL_0138: if (num3 == 0 && !bsonExpression2.IsScalar) { throw new LiteException(0, "Left expression `" + bsonExpression2.Source + "` returns more than one result. Try use ANY or ALL before operant."); } if (num3 == 0 && !bsonExpression3.IsScalar) { throw new LiteException(0, "Left expression `" + bsonExpression3.Source + "` must return a single value"); } if (!bsonExpression3.IsScalar) { throw new LiteException(0, "Right expression `" + bsonExpression3.Source + "` must return a single value"); } BsonExpression item6; if (item5 == BsonExpressionType.And || item5 == BsonExpressionType.Or) { item6 = CreateLogicExpression(item5, bsonExpression2, bsonExpression3); } else { List list3 = new List(); if (item4?.GetParameters().FirstOrDefault()?.ParameterType == typeof(Collation)) { list3.Add(context.Collation); } list3.Add(bsonExpression2.Expression); list3.Add(bsonExpression3.Expression); item6 = new BsonExpression { Type = item5, Parameters = parameters, IsImmutable = (bsonExpression2.IsImmutable && bsonExpression3.IsImmutable), UseSource = (bsonExpression2.UseSource || bsonExpression3.UseSource), IsScalar = true, Fields = new HashSet(StringComparer.OrdinalIgnoreCase).AddRange(bsonExpression2.Fields).AddRange(bsonExpression3.Fields), Expression = Expression.Call(item4, list3.ToArray()), Left = bsonExpression2, Right = bsonExpression3, Source = bsonExpression2.Source + item3 + bsonExpression3.Source }; } list.Insert(num2, item6); list.RemoveRange(num2 + 1, 2); list2.RemoveAt(num2); } return list.Single(); } public static BsonExpression ParseSingleExpression(Tokenizer tokenizer, ExpressionContext context, BsonDocument parameters, DocumentScope scope) { Token token = tokenizer.ReadToken(); return TryParseDouble(tokenizer, parameters) ?? TryParseInt(tokenizer, parameters) ?? TryParseBool(tokenizer, parameters) ?? TryParseNull(tokenizer, parameters) ?? TryParseString(tokenizer, parameters) ?? TryParseSource(tokenizer, context, parameters, scope) ?? TryParseDocument(tokenizer, context, parameters, scope) ?? TryParseArray(tokenizer, context, parameters, scope) ?? TryParseParameter(tokenizer, context, parameters, scope) ?? TryParseInnerExpression(tokenizer, context, parameters, scope) ?? TryParseFunction(tokenizer, context, parameters, scope) ?? TryParseMethodCall(tokenizer, context, parameters, scope) ?? TryParsePath(tokenizer, context, parameters, scope) ?? throw LiteException.UnexpectedToken(token); } public static BsonExpression ParseSelectDocumentBuilder(Tokenizer tokenizer, ExpressionContext context, BsonDocument parameters) { List> fields = new List>(); HashSet names = new HashSet(); int counter = 1; while (true) { BsonExpression bsonExpression = ParseFullExpression(tokenizer, context, parameters, DocumentScope.Root); Token token = tokenizer.LookAhead(); if (stop(token)) { Add(bsonExpression.DefaultFieldName(), bsonExpression); break; } if (token.Type == TokenType.Comma) { tokenizer.ReadToken(); Add(bsonExpression.DefaultFieldName(), bsonExpression); continue; } if (token.Is("AS")) { tokenizer.ReadToken(); } Add(tokenizer.ReadToken().Expect(TokenType.Word).Value, bsonExpression); token = tokenizer.LookAhead(); if (stop(token)) { break; } tokenizer.ReadToken().Expect(TokenType.Comma); } BsonExpression value = fields[0].Value; if (fields.Count == 1) { if (value.Type == BsonExpressionType.Path && value.Source == "$") { return BsonExpression.Root; } if (fields.Count == 1 && value.Type == BsonExpressionType.Document) { return value; } if (fields.Count == 1 && value.Type == BsonExpressionType.Call && value.Source.StartsWith("EXTEND")) { return value; } } Type typeFromHandle = typeof(string); Expression[] initializers = fields.Select((KeyValuePair x) => Expression.Constant(x.Key)).ToArray(); NewArrayExpression newArrayExpression = Expression.NewArrayInit(typeFromHandle, initializers); NewArrayExpression newArrayExpression2 = Expression.NewArrayInit(typeof(BsonValue), fields.Select((KeyValuePair x) => x.Value.Expression).ToArray()); BsonExpression bsonExpression2 = new BsonExpression(); bsonExpression2.Type = BsonExpressionType.Document; bsonExpression2.Parameters = parameters; bsonExpression2.IsImmutable = fields.All((KeyValuePair x) => x.Value.IsImmutable); bsonExpression2.UseSource = fields.Any((KeyValuePair x) => x.Value.UseSource); bsonExpression2.IsScalar = true; bsonExpression2.Fields = new HashSet(StringComparer.OrdinalIgnoreCase).AddRange(fields.SelectMany((KeyValuePair x) => x.Value.Fields)); bsonExpression2.Expression = Expression.Call(_documentInitMethod, new Expression[2] { newArrayExpression, newArrayExpression2 }); bsonExpression2.Source = "{" + string.Join(",", fields.Select((KeyValuePair x) => x.Key + ":" + x.Value.Source)) + "}"; return bsonExpression2; void Add(string alias, BsonExpression expr) { if (names.Contains(alias)) { alias += counter++; } names.Add(alias); if (!expr.IsScalar) { expr = ConvertToArray(expr); } fields.Add(new KeyValuePair(alias, expr)); } static bool stop(Token t) { if (!t.Is("FROM") && !t.Is("INTO") && t.Type != TokenType.EOF) { return t.Type == TokenType.SemiColon; } return true; } } public static BsonExpression ParseUpdateDocumentBuilder(Tokenizer tokenizer, ExpressionContext context, BsonDocument parameters) { if (tokenizer.LookAhead().Type == TokenType.OpenBrace) { tokenizer.ReadToken(); return TryParseDocument(tokenizer, context, parameters, DocumentScope.Root); } List list = new List(); List list2 = new List(); StringBuilder stringBuilder = new StringBuilder(); bool isImmutable = true; bool useSource = false; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); stringBuilder.Append("{"); while (!tokenizer.CheckEOF()) { string value = ReadKey(tokenizer, stringBuilder); tokenizer.ReadToken().Expect(TokenType.Equals); stringBuilder.Append(":"); BsonExpression bsonExpression = ParseFullExpression(tokenizer, context, parameters, DocumentScope.Root); if (!bsonExpression.IsScalar) { bsonExpression = ConvertToArray(bsonExpression); } if (!bsonExpression.IsImmutable) { isImmutable = false; } if (bsonExpression.UseSource) { useSource = true; } hashSet.AddRange(bsonExpression.Fields); list.Add(Expression.Constant(value)); list2.Add(bsonExpression.Expression); stringBuilder.Append(bsonExpression.Source); if (tokenizer.LookAhead().Type != TokenType.Comma) { break; } stringBuilder.Append(tokenizer.ReadToken().Value); } stringBuilder.Append("}"); NewArrayExpression newArrayExpression = Expression.NewArrayInit(typeof(string), list.ToArray()); NewArrayExpression newArrayExpression2 = Expression.NewArrayInit(typeof(BsonValue), list2.ToArray()); MethodCallExpression expression = Expression.Call(_documentInitMethod, new Expression[2] { newArrayExpression, newArrayExpression2 }); return new BsonExpression { Type = BsonExpressionType.Document, Parameters = parameters, IsImmutable = isImmutable, UseSource = useSource, IsScalar = true, Fields = hashSet, Expression = expression, Source = stringBuilder.ToString() }; } private static BsonExpression TryParseDouble(Tokenizer tokenizer, BsonDocument parameters) { string text = null; if (tokenizer.Current.Type == TokenType.Double) { text = tokenizer.Current.Value; } else if (tokenizer.Current.Type == TokenType.Minus && tokenizer.LookAhead(eatWhitespace: false).Type == TokenType.Double) { text = "-" + tokenizer.ReadToken().Value; } if (text != null) { double value = Convert.ToDouble(text, CultureInfo.InvariantCulture.NumberFormat); ConstantExpression expression = Expression.Constant(new BsonValue(value)); return new BsonExpression { Type = BsonExpressionType.Double, Parameters = parameters, IsImmutable = true, UseSource = false, IsScalar = true, Fields = new HashSet(StringComparer.OrdinalIgnoreCase), Expression = expression, Source = value.ToString("0.0########", CultureInfo.InvariantCulture.NumberFormat) }; } return null; } private static BsonExpression TryParseInt(Tokenizer tokenizer, BsonDocument parameters) { string text = null; if (tokenizer.Current.Type == TokenType.Int) { text = tokenizer.Current.Value; } else if (tokenizer.Current.Type == TokenType.Minus && tokenizer.LookAhead(eatWhitespace: false).Type == TokenType.Int) { text = "-" + tokenizer.ReadToken().Value; } if (text != null) { if (int.TryParse(text, NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat, out var result)) { ConstantExpression expression = Expression.Constant(new BsonValue(result)); return new BsonExpression { Type = BsonExpressionType.Int, Parameters = parameters, IsImmutable = true, UseSource = false, IsScalar = true, Fields = new HashSet(StringComparer.OrdinalIgnoreCase), Expression = expression, Source = result.ToString(CultureInfo.InvariantCulture.NumberFormat) }; } long value = long.Parse(text, NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat); ConstantExpression expression2 = Expression.Constant(new BsonValue(value)); return new BsonExpression { Type = BsonExpressionType.Int, Parameters = parameters, IsImmutable = true, UseSource = false, IsScalar = true, Fields = new HashSet(StringComparer.OrdinalIgnoreCase), Expression = expression2, Source = value.ToString(CultureInfo.InvariantCulture.NumberFormat) }; } return null; } private static BsonExpression TryParseBool(Tokenizer tokenizer, BsonDocument parameters) { if (tokenizer.Current.Type == TokenType.Word && (tokenizer.Current.Is("true") || tokenizer.Current.Is("false"))) { bool value = Convert.ToBoolean(tokenizer.Current.Value); ConstantExpression expression = Expression.Constant(new BsonValue(value)); return new BsonExpression { Type = BsonExpressionType.Boolean, Parameters = parameters, IsImmutable = true, UseSource = false, IsScalar = true, Fields = new HashSet(StringComparer.OrdinalIgnoreCase), Expression = expression, Source = value.ToString().ToLower() }; } return null; } private static BsonExpression TryParseNull(Tokenizer tokenizer, BsonDocument parameters) { if (tokenizer.Current.Type == TokenType.Word && tokenizer.Current.Is("null")) { ConstantExpression expression = Expression.Constant(BsonValue.Null); return new BsonExpression { Type = BsonExpressionType.Null, Parameters = parameters, IsImmutable = true, UseSource = false, IsScalar = true, Fields = new HashSet(StringComparer.OrdinalIgnoreCase), Expression = expression, Source = "null" }; } return null; } private static BsonExpression TryParseString(Tokenizer tokenizer, BsonDocument parameters) { if (tokenizer.Current.Type == TokenType.String) { BsonValue value = new BsonValue(tokenizer.Current.Value); ConstantExpression expression = Expression.Constant(value); return new BsonExpression { Type = BsonExpressionType.String, Parameters = parameters, IsImmutable = true, UseSource = false, IsScalar = true, Fields = new HashSet(StringComparer.OrdinalIgnoreCase), Expression = expression, Source = JsonSerializer.Serialize(value) }; } return null; } private static BsonExpression TryParseDocument(Tokenizer tokenizer, ExpressionContext context, BsonDocument parameters, DocumentScope scope) { if (tokenizer.Current.Type != 0) { return null; } List list = new List(); List list2 = new List(); StringBuilder stringBuilder = new StringBuilder(); bool isImmutable = true; bool useSource = false; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); stringBuilder.Append("{"); BsonExpression bsonExpression2; if (tokenizer.LookAhead().Type == TokenType.CloseBrace) { stringBuilder.Append(tokenizer.ReadToken().Value); } else { while (!tokenizer.CheckEOF()) { StringBuilder stringBuilder2 = new StringBuilder(); string text = ReadKey(tokenizer, stringBuilder2); stringBuilder.Append((object?)stringBuilder2); tokenizer.ReadToken(); stringBuilder.Append(":"); BsonExpression bsonExpression; if (tokenizer.Current.Type == TokenType.Colon) { bsonExpression = ParseFullExpression(tokenizer, context, parameters, scope); tokenizer.ReadToken(); } else { string text2 = stringBuilder2.ToString(); bsonExpression2 = new BsonExpression(); bsonExpression2.Type = BsonExpressionType.Path; bsonExpression2.Parameters = parameters; bsonExpression2.IsImmutable = isImmutable; bsonExpression2.UseSource = useSource; bsonExpression2.IsScalar = true; bsonExpression2.Fields = new HashSet(StringComparer.OrdinalIgnoreCase).AddRange(new string[1] { text }); bsonExpression2.Expression = Expression.Call(_memberPathMethod, context.Root, Expression.Constant(text)); bsonExpression2.Source = "$." + (text2.IsWord() ? text2 : ("[" + text2 + "]")); bsonExpression = bsonExpression2; } if (!bsonExpression.IsScalar) { bsonExpression = ConvertToArray(bsonExpression); } if (!bsonExpression.IsImmutable) { isImmutable = false; } if (bsonExpression.UseSource) { useSource = true; } hashSet.AddRange(bsonExpression.Fields); list.Add(Expression.Constant(text)); list2.Add(bsonExpression.Expression); stringBuilder.Append(bsonExpression.Source); tokenizer.Current.Expect(TokenType.Comma, TokenType.CloseBrace); stringBuilder.Append(tokenizer.Current.Value); if (tokenizer.Current.Type != TokenType.Comma) { break; } } } NewArrayExpression newArrayExpression = Expression.NewArrayInit(typeof(string), list.ToArray()); NewArrayExpression newArrayExpression2 = Expression.NewArrayInit(typeof(BsonValue), list2.ToArray()); bsonExpression2 = new BsonExpression(); bsonExpression2.Type = BsonExpressionType.Document; bsonExpression2.Parameters = parameters; bsonExpression2.IsImmutable = isImmutable; bsonExpression2.UseSource = useSource; bsonExpression2.IsScalar = true; bsonExpression2.Fields = hashSet; bsonExpression2.Expression = Expression.Call(_documentInitMethod, new Expression[2] { newArrayExpression, newArrayExpression2 }); bsonExpression2.Source = stringBuilder.ToString(); return bsonExpression2; } private static BsonExpression TryParseSource(Tokenizer tokenizer, ExpressionContext context, BsonDocument parameters, DocumentScope scope) { if (tokenizer.Current.Type != TokenType.Asterisk) { return null; } BsonExpression bsonExpression = new BsonExpression { Type = BsonExpressionType.Source, Parameters = parameters, IsImmutable = true, UseSource = true, IsScalar = false, Fields = new HashSet(StringComparer.OrdinalIgnoreCase) { "$" }, Expression = context.Source, Source = "*" }; if (tokenizer.LookAhead(eatWhitespace: false).Type == TokenType.Period) { tokenizer.ReadToken(); BsonExpression bsonExpression2 = BsonExpression.ParseAndCompile(tokenizer, BsonExpressionParserMode.Single, parameters, DocumentScope.Source); if (bsonExpression2 == null) { throw LiteException.UnexpectedToken(tokenizer.Current); } return new BsonExpression { Type = BsonExpressionType.Map, Parameters = parameters, IsImmutable = bsonExpression2.IsImmutable, UseSource = true, IsScalar = false, Fields = new HashSet(StringComparer.OrdinalIgnoreCase).AddRange(bsonExpression2.Fields), Expression = Expression.Call(BsonExpression.GetFunction("MAP"), context.Root, context.Collation, context.Parameters, bsonExpression.Expression, Expression.Constant(bsonExpression2)), Source = "MAP(*=>" + bsonExpression2.Source + ")" }; } return bsonExpression; } private static BsonExpression TryParseArray(Tokenizer tokenizer, ExpressionContext context, BsonDocument parameters, DocumentScope scope) { if (tokenizer.Current.Type != TokenType.OpenBracket) { return null; } List list = new List(); StringBuilder stringBuilder = new StringBuilder(); bool isImmutable = true; bool useSource = false; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); stringBuilder.Append("["); if (tokenizer.LookAhead().Type == TokenType.CloseBracket) { stringBuilder.Append(tokenizer.ReadToken().Value); } else { while (!tokenizer.CheckEOF()) { BsonExpression bsonExpression = ParseFullExpression(tokenizer, context, parameters, scope); if (!bsonExpression.IsScalar) { bsonExpression = ConvertToArray(bsonExpression); } stringBuilder.Append(bsonExpression.Source); if (!bsonExpression.IsImmutable) { isImmutable = false; } if (bsonExpression.UseSource) { useSource = true; } hashSet.AddRange(bsonExpression.Fields); list.Add(bsonExpression.Expression); Token token = tokenizer.ReadToken().Expect(TokenType.Comma, TokenType.CloseBracket); stringBuilder.Append(token.Value); if (token.Type != TokenType.Comma) { break; } } } NewArrayExpression arg = Expression.NewArrayInit(typeof(BsonValue), list.ToArray()); return new BsonExpression { Type = BsonExpressionType.Array, Parameters = parameters, IsImmutable = isImmutable, UseSource = useSource, IsScalar = true, Fields = hashSet, Expression = Expression.Call(_arrayInitMethod, arg), Source = stringBuilder.ToString() }; } private static BsonExpression TryParseParameter(Tokenizer tokenizer, ExpressionContext context, BsonDocument parameters, DocumentScope scope) { if (tokenizer.Current.Type != TokenType.At) { return null; } Token token = tokenizer.LookAhead(eatWhitespace: false); if (token.Type == TokenType.Word || token.Type == TokenType.Int) { string value = tokenizer.ReadToken(eatWhitespace: false).Value; ConstantExpression arg = Expression.Constant(value); return new BsonExpression { Type = BsonExpressionType.Parameter, Parameters = parameters, IsImmutable = false, UseSource = false, IsScalar = true, Fields = new HashSet(StringComparer.OrdinalIgnoreCase), Expression = Expression.Call(_parameterPathMethod, context.Parameters, arg), Source = "@" + value }; } return null; } private static BsonExpression TryParseInnerExpression(Tokenizer tokenizer, ExpressionContext context, BsonDocument parameters, DocumentScope scope) { if (tokenizer.Current.Type != TokenType.OpenParenthesis) { return null; } BsonExpression bsonExpression = ParseFullExpression(tokenizer, context, parameters, scope); tokenizer.ReadToken().Expect(TokenType.CloseParenthesis); return new BsonExpression { Type = bsonExpression.Type, Parameters = bsonExpression.Parameters, IsImmutable = bsonExpression.IsImmutable, UseSource = bsonExpression.UseSource, IsScalar = bsonExpression.IsScalar, Fields = bsonExpression.Fields, Expression = bsonExpression.Expression, Left = bsonExpression.Left, Right = bsonExpression.Right, Source = "(" + bsonExpression.Source + ")" }; } private static BsonExpression TryParseMethodCall(Tokenizer tokenizer, ExpressionContext context, BsonDocument parameters, DocumentScope scope) { Token current = tokenizer.Current; if (tokenizer.Current.Type != TokenType.Word) { return null; } if (tokenizer.LookAhead().Type != TokenType.OpenParenthesis) { return null; } tokenizer.ReadToken(); List list = new List(); StringBuilder stringBuilder = new StringBuilder(); bool isImmutable = true; bool useSource = false; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); stringBuilder.Append(current.Value.ToUpperInvariant() + "("); if (tokenizer.LookAhead().Type == TokenType.CloseParenthesis) { stringBuilder.Append(tokenizer.ReadToken().Value); } else { while (!tokenizer.CheckEOF()) { BsonExpression bsonExpression = ParseFullExpression(tokenizer, context, parameters, scope); if (!bsonExpression.IsImmutable) { isImmutable = false; } if (bsonExpression.UseSource) { useSource = true; } hashSet.AddRange(bsonExpression.Fields); list.Add(bsonExpression); stringBuilder.Append(bsonExpression.Source); Token token = tokenizer.ReadToken().Expect(TokenType.Comma, TokenType.CloseParenthesis); stringBuilder.Append(token.Value); if (token.Type != TokenType.Comma) { break; } } } MethodInfo method = BsonExpression.GetMethod(current.Value, list.Count); if (method == null) { throw LiteException.UnexpectedToken("Method '" + current.Value.ToUpperInvariant() + "' does not exist or contains invalid parameters", current); } if (method.GetCustomAttribute() != null) { isImmutable = false; } List list2 = new List(); if (method.GetParameters().FirstOrDefault()?.ParameterType == typeof(Collation)) { list2.Add(context.Collation); } foreach (var item in (from x in method.GetParameters() where x.ParameterType != typeof(Collation) select x).Zip(list, (ParameterInfo parameter, BsonExpression expr) => new { parameter, expr })) { if (!item.parameter.ParameterType.IsEnumerable() && !item.expr.IsScalar) { list2.Add(ConvertToArray(item.expr).Expression); } else if (item.parameter.ParameterType.IsEnumerable() && item.expr.IsScalar) { list2.Add(ConvertToEnumerable(item.expr).Expression); } else { list2.Add(item.expr.Expression); } } if (method.Name == "IIF" && list.Count == 3) { return CreateConditionalExpression(list[0], list[1], list[2]); } return new BsonExpression { Type = BsonExpressionType.Call, Parameters = parameters, IsImmutable = isImmutable, UseSource = useSource, IsScalar = !method.ReturnType.IsEnumerable(), Fields = hashSet, Expression = Expression.Call(method, list2.ToArray()), Source = stringBuilder.ToString() }; } private static BsonExpression TryParsePath(Tokenizer tokenizer, ExpressionContext context, BsonDocument parameters, DocumentScope scope) { if (tokenizer.Current.Type != TokenType.At && tokenizer.Current.Type != TokenType.Dollar && tokenizer.Current.Type != TokenType.Word) { return null; } TokenType tokenType = ((scope == DocumentScope.Root) ? TokenType.Dollar : TokenType.At); if (tokenizer.Current.Type == TokenType.At || tokenizer.Current.Type == TokenType.Dollar) { tokenType = tokenizer.Current.Type; if (tokenizer.LookAhead(eatWhitespace: false).Type == TokenType.Period) { tokenizer.ReadToken(); tokenizer.ReadToken(); } } StringBuilder stringBuilder = new StringBuilder(); bool isImmutable = true; bool useSource = false; bool isScalar = true; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); stringBuilder.Append((tokenType == TokenType.Dollar) ? "$" : "@"); string text = ReadField(tokenizer, stringBuilder); ConstantExpression arg = Expression.Constant(text); Expression expression = Expression.Call(_memberPathMethod, (tokenType == TokenType.Dollar) ? context.Root : context.Current, arg); if (tokenType == TokenType.Dollar || scope == DocumentScope.Source) { hashSet.Add((text.Length == 0) ? "$" : text); } while (!tokenizer.EOF) { Expression expression2 = ParsePath(tokenizer, expression, context, parameters, hashSet, ref isImmutable, ref useSource, ref isScalar, stringBuilder); if (!isScalar) { expression = expression2; break; } if (expression2 == null) { break; } expression = expression2; } BsonExpression bsonExpression = new BsonExpression { Type = BsonExpressionType.Path, Parameters = parameters, IsImmutable = isImmutable, UseSource = useSource, IsScalar = isScalar, Fields = hashSet, Expression = expression, Source = stringBuilder.ToString() }; if (!isScalar && tokenizer.LookAhead(eatWhitespace: false).Type == TokenType.Period) { tokenizer.ReadToken(); BsonExpression bsonExpression2 = BsonExpression.ParseAndCompile(tokenizer, BsonExpressionParserMode.Single, parameters, DocumentScope.Current); if (bsonExpression2 == null) { throw LiteException.UnexpectedToken(tokenizer.Current); } BsonExpression bsonExpression3 = new BsonExpression(); bsonExpression3.Type = BsonExpressionType.Map; bsonExpression3.Parameters = parameters; bsonExpression3.IsImmutable = bsonExpression.IsImmutable && bsonExpression2.IsImmutable; bsonExpression3.UseSource = bsonExpression.UseSource || bsonExpression2.UseSource; bsonExpression3.IsScalar = false; bsonExpression3.Fields = new HashSet(StringComparer.OrdinalIgnoreCase).AddRange(bsonExpression.Fields).AddRange(bsonExpression2.Fields); bsonExpression3.Expression = Expression.Call(BsonExpression.GetFunction("MAP"), context.Root, context.Collation, context.Parameters, bsonExpression.Expression, Expression.Constant(bsonExpression2)); bsonExpression3.Source = "MAP(" + bsonExpression.Source + "=>" + bsonExpression2.Source + ")"; return bsonExpression3; } return bsonExpression; } private static Expression ParsePath(Tokenizer tokenizer, Expression expr, ExpressionContext context, BsonDocument parameters, HashSet fields, ref bool isImmutable, ref bool useSource, ref bool isScalar, StringBuilder src) { Token token = tokenizer.LookAhead(eatWhitespace: false); if (token.Type == TokenType.Period) { tokenizer.ReadToken(); tokenizer.ReadToken(eatWhitespace: false); ConstantExpression arg = Expression.Constant(ReadField(tokenizer, src)); return Expression.Call(_memberPathMethod, expr, arg); } if (token.Type == TokenType.OpenBracket) { src.Append("["); tokenizer.ReadToken(); token = tokenizer.LookAhead(); int num = 0; BsonExpression bsonExpression = new BsonExpression(); MethodInfo method = _arrayIndexMethod; if (token.Type == TokenType.Int) { src.Append(tokenizer.ReadToken().Value); num = Convert.ToInt32(tokenizer.Current.Value); } else if (token.Type == TokenType.Minus) { src.Append(tokenizer.ReadToken().Value + tokenizer.ReadToken().Expect(TokenType.Int).Value); num = -Convert.ToInt32(tokenizer.Current.Value); } else if (token.Type == TokenType.Asterisk) { method = _arrayFilterMethod; isScalar = false; num = int.MaxValue; src.Append(tokenizer.ReadToken().Value); } else { bsonExpression = BsonExpression.ParseAndCompile(tokenizer, BsonExpressionParserMode.Full, parameters, DocumentScope.Current); if (bsonExpression == null) { throw LiteException.UnexpectedToken(tokenizer.Current); } if (!bsonExpression.IsImmutable) { isImmutable = false; } if (bsonExpression.UseSource) { useSource = true; } if (bsonExpression.Type != BsonExpressionType.Parameter) { method = _arrayFilterMethod; isScalar = false; } fields.AddRange(bsonExpression.Fields); src.Append(bsonExpression.Source); } tokenizer.ReadToken().Expect(TokenType.CloseBracket); src.Append("]"); return Expression.Call(method, expr, Expression.Constant(num), Expression.Constant(bsonExpression), context.Root, context.Collation, context.Parameters); } return null; } private static BsonExpression TryParseFunction(Tokenizer tokenizer, ExpressionContext context, BsonDocument parameters, DocumentScope scope) { if (tokenizer.Current.Type != TokenType.Word) { return null; } if (tokenizer.LookAhead().Type != TokenType.OpenParenthesis) { return null; } string text = tokenizer.Current.Value.ToUpperInvariant(); return text switch { "MAP" => ParseFunction(text, BsonExpressionType.Map, tokenizer, context, parameters, scope), "FILTER" => ParseFunction(text, BsonExpressionType.Filter, tokenizer, context, parameters, scope), "SORT" => ParseFunction(text, BsonExpressionType.Sort, tokenizer, context, parameters, scope), _ => null, }; } private static BsonExpression ParseFunction(string functionName, BsonExpressionType type, Tokenizer tokenizer, ExpressionContext context, BsonDocument parameters, DocumentScope scope) { if (tokenizer.LookAhead().Type != TokenType.OpenParenthesis) { return null; } tokenizer.ReadToken().Expect(TokenType.OpenParenthesis); BsonExpression bsonExpression = ParseSingleExpression(tokenizer, context, parameters, scope); if (bsonExpression.IsScalar) { bsonExpression = ConvertToEnumerable(bsonExpression); } List list = new List(); list.Add(context.Root); list.Add(context.Collation); list.Add(context.Parameters); StringBuilder stringBuilder = new StringBuilder(functionName + "(" + bsonExpression.Source); bool isImmutable = bsonExpression.IsImmutable; bool useSource = bsonExpression.UseSource; HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); list.Add(bsonExpression.Expression); hashSet.AddRange(bsonExpression.Fields); if (tokenizer.LookAhead().Type == TokenType.Equals) { tokenizer.ReadToken().Expect(TokenType.Equals); tokenizer.ReadToken().Expect(TokenType.Greater); BsonExpression bsonExpression2 = BsonExpression.ParseAndCompile(tokenizer, BsonExpressionParserMode.Full, parameters, (bsonExpression.Type != BsonExpressionType.Source) ? DocumentScope.Current : DocumentScope.Source); stringBuilder.Append("=>" + bsonExpression2.Source); list.Add(Expression.Constant(bsonExpression2)); hashSet.AddRange(bsonExpression2.Fields); } if (tokenizer.LookAhead().Type != TokenType.CloseParenthesis) { tokenizer.ReadToken().Expect(TokenType.Comma); stringBuilder.Append(","); while (!tokenizer.CheckEOF()) { BsonExpression bsonExpression3 = ParseFullExpression(tokenizer, context, parameters, scope); if (!bsonExpression3.IsImmutable) { isImmutable = false; } if (bsonExpression3.UseSource) { useSource = true; } list.Add(bsonExpression3.Expression); stringBuilder.Append(bsonExpression3.Source); hashSet.AddRange(bsonExpression3.Fields); if (tokenizer.LookAhead().Type != TokenType.Comma) { break; } stringBuilder.Append(tokenizer.ReadToken().Value); } } tokenizer.ReadToken().Expect(TokenType.CloseParenthesis); stringBuilder.Append(")"); MethodInfo function = BsonExpression.GetFunction(functionName, list.Count - 5); return new BsonExpression { Type = type, Parameters = parameters, IsImmutable = isImmutable, UseSource = useSource, IsScalar = false, Fields = hashSet, Expression = Expression.Call(function, list.ToArray()), Source = stringBuilder.ToString() }; } private static BsonExpression NewArray(BsonExpression item0, BsonExpression item1) { Expression[] source = new Expression[2] { item0.Expression, item1.Expression }; if (!item0.IsScalar) { throw new LiteException(0, "Expression `" + item0.Source + "` must be a scalar expression"); } if (!item1.IsScalar) { throw new LiteException(0, "Expression `" + item0.Source + "` must be a scalar expression"); } NewArrayExpression newArrayExpression = Expression.NewArrayInit(typeof(BsonValue), source.ToArray()); BsonExpression bsonExpression = new BsonExpression(); bsonExpression.Type = BsonExpressionType.Array; bsonExpression.Parameters = item0.Parameters; bsonExpression.IsImmutable = item0.IsImmutable && item1.IsImmutable; bsonExpression.UseSource = item0.UseSource || item1.UseSource; bsonExpression.IsScalar = true; bsonExpression.Fields = new HashSet(StringComparer.OrdinalIgnoreCase).AddRange(item0.Fields).AddRange(item1.Fields); bsonExpression.Expression = Expression.Call(_arrayInitMethod, new Expression[1] { newArrayExpression }); bsonExpression.Source = item0.Source + " AND " + item1.Source; return bsonExpression; } private static string ReadField(Tokenizer tokenizer, StringBuilder source) { string text = ""; if (tokenizer.Current.Type == TokenType.OpenBracket) { text = tokenizer.ReadToken().Expect(TokenType.String).Value; tokenizer.ReadToken().Expect(TokenType.CloseBracket); } else if (tokenizer.Current.Type == TokenType.Word) { text = tokenizer.Current.Value; } if (text.Length > 0) { source.Append("."); if (text.IsWord()) { source.Append(text); } else { source.Append("["); JsonSerializer.Serialize(text, source); source.Append("]"); } } return text; } public static string ReadKey(Tokenizer tokenizer, StringBuilder source) { Token token = tokenizer.ReadToken(); string text = ""; text = ((token.Type != TokenType.String) ? token.Expect(TokenType.Word, TokenType.Int).Value : token.Value); if (text.IsWord()) { source.Append(text); } else { JsonSerializer.Serialize(text, source); } return text; } private static string ReadOperant(Tokenizer tokenizer) { Token token = tokenizer.LookAhead(); if (token.IsOperand) { tokenizer.ReadToken(); return token.Value; } if (token.Is("ALL") || token.Is("ANY")) { string text = token.Value.ToUpperInvariant(); tokenizer.ReadToken(); token = tokenizer.ReadToken(); if (!token.IsOperand) { throw LiteException.UnexpectedToken("Expected valid operand", token); } return text + " " + token.Value; } return null; } private static BsonExpression ConvertToEnumerable(BsonExpression expr) { string source = ((expr.Type == BsonExpressionType.Path) ? (expr.Source + "[*]") : ("ITEMS(" + expr.Source + ")")); BsonExpressionType type = ((expr.Type == BsonExpressionType.Path) ? BsonExpressionType.Path : BsonExpressionType.Call); return new BsonExpression { Type = type, Parameters = expr.Parameters, IsImmutable = expr.IsImmutable, UseSource = expr.UseSource, IsScalar = false, Fields = expr.Fields, Expression = Expression.Call(_itemsMethod, expr.Expression), Source = source }; } private static BsonExpression ConvertToArray(BsonExpression expr) { return new BsonExpression { Type = BsonExpressionType.Call, Parameters = expr.Parameters, IsImmutable = expr.IsImmutable, UseSource = expr.UseSource, IsScalar = true, Fields = expr.Fields, Expression = Expression.Call(_arrayMethod, expr.Expression), Source = "ARRAY(" + expr.Source + ")" }; } internal static BsonExpression CreateLogicExpression(BsonExpressionType type, BsonExpression left, BsonExpression right) { MemberExpression left2 = Expression.Property(left.Expression, typeof(BsonValue), "AsBoolean"); MemberExpression right2 = Expression.Property(right.Expression, typeof(BsonValue), "AsBoolean"); BinaryExpression binaryExpression = ((type == BsonExpressionType.And) ? Expression.AndAlso(left2, right2) : Expression.OrElse(left2, right2)); ConstructorInfo constructor = typeof(BsonValue).GetConstructors().First((ConstructorInfo x) => x.GetParameters().FirstOrDefault()?.ParameterType == typeof(bool)); BsonExpression bsonExpression = new BsonExpression(); bsonExpression.Type = type; bsonExpression.Parameters = left.Parameters; bsonExpression.IsImmutable = left.IsImmutable && right.IsImmutable; bsonExpression.UseSource = left.UseSource || right.UseSource; bsonExpression.IsScalar = left.IsScalar && right.IsScalar; bsonExpression.Fields = new HashSet(StringComparer.OrdinalIgnoreCase).AddRange(left.Fields).AddRange(right.Fields); bsonExpression.Expression = Expression.New(constructor, binaryExpression); bsonExpression.Left = left; bsonExpression.Right = right; bsonExpression.Source = left.Source + " " + type.ToString().ToUpperInvariant() + " " + right.Source; return bsonExpression; } internal static BsonExpression CreateConditionalExpression(BsonExpression test, BsonExpression ifTrue, BsonExpression ifFalse) { ConditionalExpression expression = Expression.Condition(Expression.Property(test.Expression, typeof(BsonValue), "AsBoolean"), ifTrue.Expression, ifFalse.Expression); BsonExpression bsonExpression = new BsonExpression(); bsonExpression.Type = BsonExpressionType.Call; bsonExpression.Parameters = test.Parameters; bsonExpression.IsImmutable = (test.IsImmutable && ifTrue.IsImmutable) || ifFalse.IsImmutable; bsonExpression.UseSource = test.UseSource || ifTrue.UseSource || ifFalse.UseSource; bsonExpression.IsScalar = test.IsScalar && ifTrue.IsScalar && ifFalse.IsScalar; bsonExpression.Fields = new HashSet(StringComparer.OrdinalIgnoreCase).AddRange(test.Fields).AddRange(ifTrue.Fields).AddRange(ifFalse.Fields); bsonExpression.Expression = expression; bsonExpression.Source = "IIF(" + test.Source + "," + ifTrue.Source + "," + ifFalse.Source + ")"; return bsonExpression; } } internal enum BsonExpressionType : byte { Double = 1, Int, String, Boolean, Null, Array, Document, Parameter, Call, Path, Modulo, Add, Subtract, Multiply, Divide, Equal, Like, Between, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual, NotEqual, In, Or, And, Map, Filter, Sort, Source } internal enum DocumentScope { Source, Root, Current } internal class ExpressionContext { public ParameterExpression Source { get; } public ParameterExpression Root { get; } public ParameterExpression Current { get; } public ParameterExpression Collation { get; } public ParameterExpression Parameters { get; } public ExpressionContext() { Source = Expression.Parameter(typeof(IEnumerable), "source"); Root = Expression.Parameter(typeof(BsonDocument), "root"); Current = Expression.Parameter(typeof(BsonValue), "current"); Collation = Expression.Parameter(typeof(Collation), "collation"); Parameters = Expression.Parameter(typeof(BsonDocument), "parameters"); } } internal class JsonReader { private static readonly IFormatProvider _numberFormat = CultureInfo.InvariantCulture.NumberFormat; private readonly Tokenizer _tokenizer; public long Position => _tokenizer.Position; public JsonReader(TextReader reader) { if (reader == null) { throw new ArgumentNullException("reader"); } _tokenizer = new Tokenizer(reader); } internal JsonReader(Tokenizer tokenizer) { _tokenizer = tokenizer ?? throw new ArgumentNullException("tokenizer"); } public BsonValue Deserialize() { Token token = _tokenizer.ReadToken(); if (token.Type == TokenType.EOF) { return BsonValue.Null; } return ReadValue(token); } public IEnumerable DeserializeArray() { Token token = _tokenizer.ReadToken(); if (token.Type == TokenType.EOF) { yield break; } token.Expect(TokenType.OpenBracket); token = _tokenizer.ReadToken(); while (token.Type != TokenType.CloseBracket) { yield return ReadValue(token); token = _tokenizer.ReadToken(); if (token.Type == TokenType.Comma) { token = _tokenizer.ReadToken(); } } token.Expect(TokenType.CloseBracket); } internal BsonValue ReadValue(Token token) { string text = token.Value; switch (token.Type) { case TokenType.String: return text; case TokenType.OpenBrace: return ReadObject(); case TokenType.OpenBracket: return ReadArray(); case TokenType.Minus: { Token token2 = _tokenizer.ReadToken(eatWhitespace: false).Expect(TokenType.Int, TokenType.Double); text = "-" + token2.Value; if (token2.Type == TokenType.Int) { goto case TokenType.Int; } if (token2.Type != TokenType.Double) { break; } goto case TokenType.Double; } case TokenType.Int: { if (int.TryParse(text, NumberStyles.Any, _numberFormat, out var result)) { return new BsonValue(result); } return new BsonValue(long.Parse(text, NumberStyles.Any, _numberFormat)); } case TokenType.Double: return new BsonValue(Convert.ToDouble(text, _numberFormat)); case TokenType.Word: return text.ToLower() switch { "null" => BsonValue.Null, "true" => true, "false" => false, _ => throw LiteException.UnexpectedToken(token), }; } throw LiteException.UnexpectedToken(token); } private BsonValue ReadObject() { BsonDocument bsonDocument = new BsonDocument(); Token token = _tokenizer.ReadToken(); while (token.Type != TokenType.CloseBrace) { token.Expect(TokenType.String, TokenType.Word); string value = token.Value; token = _tokenizer.ReadToken(); token.Expect(TokenType.Colon); token = _tokenizer.ReadToken(); if (value[0] == '$' && bsonDocument.Count == 0) { BsonValue bsonValue = ReadExtendedDataType(value, token.Value); if (!bsonValue.IsNull) { return bsonValue; } } bsonDocument[value] = ReadValue(token); token = _tokenizer.ReadToken(); if (token.Type == TokenType.Comma) { token = _tokenizer.ReadToken(); } } return bsonDocument; } private BsonArray ReadArray() { BsonArray bsonArray = new BsonArray(); Token token = _tokenizer.ReadToken(); while (token.Type != TokenType.CloseBracket) { BsonValue item = ReadValue(token); bsonArray.Add(item); token = _tokenizer.ReadToken(); if (token.Type == TokenType.Comma) { token = _tokenizer.ReadToken(); } } return bsonArray; } private BsonValue ReadExtendedDataType(string key, string value) { BsonValue result; switch (key) { case "$binary": result = new BsonValue(Convert.FromBase64String(value)); break; case "$oid": result = new BsonValue(new ObjectId(value)); break; case "$guid": result = new BsonValue(new Guid(value)); break; case "$date": result = new BsonValue(DateTime.Parse(value).ToLocalTime()); break; case "$numberLong": result = new BsonValue(Convert.ToInt64(value, _numberFormat)); break; case "$numberDecimal": result = new BsonValue(Convert.ToDecimal(value, _numberFormat)); break; case "$minValue": result = BsonValue.MinValue; break; case "$maxValue": result = BsonValue.MaxValue; break; default: return BsonValue.Null; } _tokenizer.ReadToken().Expect(TokenType.CloseBrace); return result; } } internal class JsonSerializer { public static string Serialize(BsonValue value, bool indent = false) { StringBuilder stringBuilder = new StringBuilder(); Serialize(value, stringBuilder, indent); return stringBuilder.ToString(); } public static void Serialize(BsonValue value, TextWriter writer, bool indent = false) { JsonWriter jsonWriter = new JsonWriter(writer); jsonWriter.Pretty = indent; jsonWriter.Serialize(value ?? BsonValue.Null); } public static void Serialize(BsonValue value, StringBuilder sb, bool indent = false) { using StringWriter writer = new StringWriter(sb); JsonWriter jsonWriter = new JsonWriter(writer); jsonWriter.Pretty = indent; jsonWriter.Serialize(value ?? BsonValue.Null); } public static BsonValue Deserialize(string json) { if (json == null) { throw new ArgumentNullException("json"); } using StringReader reader = new StringReader(json); return new JsonReader(reader).Deserialize(); } public static BsonValue Deserialize(TextReader reader) { if (reader == null) { throw new ArgumentNullException("reader"); } return new JsonReader(reader).Deserialize(); } public static IEnumerable DeserializeArray(string json) { if (json == null) { throw new ArgumentNullException("json"); } return new JsonReader(new StringReader(json)).DeserializeArray(); } public static IEnumerable DeserializeArray(TextReader reader) { if (reader == null) { throw new ArgumentNullException("reader"); } return new JsonReader(reader).DeserializeArray(); } } internal class JsonWriter { private static readonly IFormatProvider _numberFormat = CultureInfo.InvariantCulture.NumberFormat; private readonly TextWriter _writer; private int _indent; private string _spacer = ""; public int Indent { get; set; } = 4; public bool Pretty { get; set; } public JsonWriter(TextWriter writer) { _writer = writer; } public void Serialize(BsonValue value) { _indent = 0; _spacer = (Pretty ? " " : ""); WriteValue(value ?? BsonValue.Null); } private void WriteValue(BsonValue value) { switch (value.Type) { case BsonType.Null: _writer.Write("null"); break; case BsonType.Array: WriteArray(value.AsArray); break; case BsonType.Document: WriteObject(value.AsDocument); break; case BsonType.Boolean: _writer.Write(value.AsBoolean.ToString().ToLower()); break; case BsonType.String: WriteString(value.AsString); break; case BsonType.Int32: _writer.Write(value.AsInt32.ToString(_numberFormat)); break; case BsonType.Double: { double asDouble = value.AsDouble; if (double.IsNaN(asDouble) || double.IsNegativeInfinity(asDouble) || double.IsPositiveInfinity(asDouble)) { _writer.Write("null"); } else { _writer.Write(value.AsDouble.ToString("0.0########", _numberFormat)); } break; } case BsonType.Binary: { byte[] asBinary = value.AsBinary; WriteExtendDataType("$binary", Convert.ToBase64String(asBinary, 0, asBinary.Length)); break; } case BsonType.ObjectId: WriteExtendDataType("$oid", value.AsObjectId.ToString()); break; case BsonType.Guid: WriteExtendDataType("$guid", value.AsGuid.ToString()); break; case BsonType.DateTime: WriteExtendDataType("$date", value.AsDateTime.ToUniversalTime().ToString("o")); break; case BsonType.Int64: WriteExtendDataType("$numberLong", value.AsInt64.ToString(_numberFormat)); break; case BsonType.Decimal: WriteExtendDataType("$numberDecimal", value.AsDecimal.ToString(_numberFormat)); break; case BsonType.MinValue: WriteExtendDataType("$minValue", "1"); break; case BsonType.MaxValue: WriteExtendDataType("$maxValue", "1"); break; } } private void WriteObject(BsonDocument obj) { int num = obj.Keys.Count(); bool hasData = num > 0; WriteStartBlock("{", hasData); int num2 = 0; foreach (KeyValuePair element in obj.GetElements()) { WriteKeyValue(element.Key, element.Value, num2++ < num - 1); } WriteEndBlock("}", hasData); } private void WriteArray(BsonArray arr) { bool hasData = arr.Count > 0; WriteStartBlock("[", hasData); for (int i = 0; i < arr.Count; i++) { BsonValue bsonValue = arr[i]; if (Pretty && bsonValue != null && (!bsonValue.IsDocument || !bsonValue.AsDocument.Keys.Any()) && (!bsonValue.IsArray || bsonValue.AsArray.Count <= 0)) { WriteIndent(); } WriteValue(bsonValue ?? BsonValue.Null); if (i < arr.Count - 1) { _writer.Write(','); } WriteNewLine(); } WriteEndBlock("]", hasData); } private void WriteString(string s) { _writer.Write('"'); int length = s.Length; for (int i = 0; i < length; i++) { char c = s[i]; switch (c) { case '"': _writer.Write("\\\""); continue; case '\\': _writer.Write("\\\\"); continue; case '\b': _writer.Write("\\b"); continue; case '\f': _writer.Write("\\f"); continue; case '\n': _writer.Write("\\n"); continue; case '\r': _writer.Write("\\r"); continue; case '\t': _writer.Write("\\t"); continue; } switch (CharUnicodeInfo.GetUnicodeCategory(c)) { case UnicodeCategory.UppercaseLetter: case UnicodeCategory.LowercaseLetter: case UnicodeCategory.TitlecaseLetter: case UnicodeCategory.OtherLetter: case UnicodeCategory.DecimalDigitNumber: case UnicodeCategory.LetterNumber: case UnicodeCategory.OtherNumber: case UnicodeCategory.SpaceSeparator: case UnicodeCategory.ConnectorPunctuation: case UnicodeCategory.DashPunctuation: case UnicodeCategory.OpenPunctuation: case UnicodeCategory.ClosePunctuation: case UnicodeCategory.InitialQuotePunctuation: case UnicodeCategory.FinalQuotePunctuation: case UnicodeCategory.OtherPunctuation: case UnicodeCategory.MathSymbol: case UnicodeCategory.CurrencySymbol: case UnicodeCategory.ModifierSymbol: case UnicodeCategory.OtherSymbol: _writer.Write(c); continue; } _writer.Write("\\u"); TextWriter writer = _writer; int num = c; writer.Write(num.ToString("x04")); } _writer.Write('"'); } private void WriteExtendDataType(string type, string value) { _writer.Write("{\""); _writer.Write(type); _writer.Write("\":"); _writer.Write(_spacer); _writer.Write("\""); _writer.Write(value); _writer.Write("\"}"); } private void WriteKeyValue(string key, BsonValue value, bool comma) { WriteIndent(); _writer.Write('"'); _writer.Write(key); _writer.Write("\":"); if (Pretty) { _writer.Write(' '); if (value != null && ((value.IsDocument && value.AsDocument.Keys.Any()) || (value.IsArray && value.AsArray.Count > 0))) { WriteNewLine(); } } WriteValue(value ?? BsonValue.Null); if (comma) { _writer.Write(','); } WriteNewLine(); } private void WriteStartBlock(string str, bool hasData) { if (hasData) { WriteIndent(); _writer.Write(str); WriteNewLine(); _indent++; } else { _writer.Write(str); } } private void WriteEndBlock(string str, bool hasData) { if (hasData) { _indent--; WriteIndent(); _writer.Write(str); } else { _writer.Write(str); } } private void WriteNewLine() { if (Pretty) { _writer.WriteLine(); } } private void WriteIndent() { if (Pretty) { _writer.Write("".PadRight(_indent * Indent, ' ')); } } } internal class ObjectId : IComparable, IEquatable { private static readonly int _machine; private static readonly short _pid; private static int _increment; public static ObjectId Empty => new ObjectId(); public int Timestamp { get; } public int Machine { get; } public short Pid { get; } public int Increment { get; } public DateTime CreationTime { get { DateTime unixEpoch = BsonValue.UnixEpoch; return unixEpoch.AddSeconds(Timestamp); } } public ObjectId() { Timestamp = 0; Machine = 0; Pid = 0; Increment = 0; } public ObjectId(int timestamp, int machine, short pid, int increment) { Timestamp = timestamp; Machine = machine; Pid = pid; Increment = increment; } public ObjectId(ObjectId from) { Timestamp = from.Timestamp; Machine = from.Machine; Pid = from.Pid; Increment = from.Increment; } public ObjectId(string value) : this(FromHex(value)) { } public ObjectId(byte[] bytes, int startIndex = 0) { if (bytes == null) { throw new ArgumentNullException("bytes"); } Timestamp = (bytes[startIndex] << 24) + (bytes[startIndex + 1] << 16) + (bytes[startIndex + 2] << 8) + bytes[startIndex + 3]; Machine = (bytes[startIndex + 4] << 16) + (bytes[startIndex + 5] << 8) + bytes[startIndex + 6]; Pid = (short)((bytes[startIndex + 7] << 8) + bytes[startIndex + 8]); Increment = (bytes[startIndex + 9] << 16) + (bytes[startIndex + 10] << 8) + bytes[startIndex + 11]; } private static byte[] FromHex(string value) { if (string.IsNullOrEmpty(value)) { throw new ArgumentNullException("value"); } if (value.Length != 24) { throw new ArgumentException($"ObjectId strings should be 24 hex characters, got {value.Length} : \"{value}\""); } byte[] array = new byte[12]; for (int i = 0; i < 24; i += 2) { array[i / 2] = Convert.ToByte(value.Substring(i, 2), 16); } return array; } public bool Equals(ObjectId other) { if (other != null && Timestamp == other.Timestamp && Machine == other.Machine && Pid == other.Pid) { return Increment == other.Increment; } return false; } public override bool Equals(object other) { return Equals(other as ObjectId); } public override int GetHashCode() { int num = 17; num = 37 * num + Timestamp.GetHashCode(); num = 37 * num + Machine.GetHashCode(); num = 37 * num + Pid.GetHashCode(); return 37 * num + Increment.GetHashCode(); } public int CompareTo(ObjectId other) { int num = Timestamp.CompareTo(other.Timestamp); if (num != 0) { return num; } num = Machine.CompareTo(other.Machine); if (num != 0) { return num; } num = Pid.CompareTo(other.Pid); if (num != 0) { if (num >= 0) { return 1; } return -1; } return Increment.CompareTo(other.Increment); } public void ToByteArray(byte[] bytes, int startIndex) { bytes[startIndex] = (byte)(Timestamp >> 24); bytes[startIndex + 1] = (byte)(Timestamp >> 16); bytes[startIndex + 2] = (byte)(Timestamp >> 8); bytes[startIndex + 3] = (byte)Timestamp; bytes[startIndex + 4] = (byte)(Machine >> 16); bytes[startIndex + 5] = (byte)(Machine >> 8); bytes[startIndex + 6] = (byte)Machine; bytes[startIndex + 7] = (byte)(Pid >> 8); bytes[startIndex + 8] = (byte)Pid; bytes[startIndex + 9] = (byte)(Increment >> 16); bytes[startIndex + 10] = (byte)(Increment >> 8); bytes[startIndex + 11] = (byte)Increment; } public byte[] ToByteArray() { byte[] array = new byte[12]; ToByteArray(array, 0); return array; } public override string ToString() { return BitConverter.ToString(ToByteArray()).Replace("-", "").ToLower(); } public static bool operator ==(ObjectId lhs, ObjectId rhs) { if ((object)lhs == null) { return (object)rhs == null; } if ((object)rhs == null) { return false; } return lhs.Equals(rhs); } public static bool operator !=(ObjectId lhs, ObjectId rhs) { return !(lhs == rhs); } public static bool operator >=(ObjectId lhs, ObjectId rhs) { return lhs.CompareTo(rhs) >= 0; } public static bool operator >(ObjectId lhs, ObjectId rhs) { return lhs.CompareTo(rhs) > 0; } public static bool operator <(ObjectId lhs, ObjectId rhs) { return lhs.CompareTo(rhs) < 0; } public static bool operator <=(ObjectId lhs, ObjectId rhs) { return lhs.CompareTo(rhs) <= 0; } static ObjectId() { _machine = (GetMachineHash() + 10000) & 0xFFFFFF; _increment = new Random().Next(); try { _pid = (short)GetCurrentProcessId(); } catch (SecurityException) { _pid = 0; } } [MethodImpl(MethodImplOptions.NoInlining)] private static int GetCurrentProcessId() { return new Random().Next(0, 5000); } private static int GetMachineHash() { string text = "SOMENAME"; return 0xFFFFFF & text.GetHashCode(); } public static ObjectId NewObjectId() { long num = (long)Math.Floor((DateTime.UtcNow - BsonValue.UnixEpoch).TotalSeconds); return new ObjectId(increment: Interlocked.Increment(ref _increment) & 0xFFFFFF, timestamp: (int)num, machine: _machine, pid: _pid); } } internal class AesEncryption : IDisposable { private Aes _aes; public AesEncryption(string password, byte[] salt) { _aes = Aes.Create(); _aes.Padding = PaddingMode.Zeros; Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt); using (rfc2898DeriveBytes) { _aes.Key = rfc2898DeriveBytes.GetBytes(32); _aes.IV = rfc2898DeriveBytes.GetBytes(16); } } public byte[] Encrypt(byte[] bytes) { using ICryptoTransform transform = _aes.CreateEncryptor(); using MemoryStream memoryStream = new MemoryStream(); using CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write); cryptoStream.Write(bytes, 0, bytes.Length); cryptoStream.FlushFinalBlock(); memoryStream.Position = 0L; byte[] array = new byte[memoryStream.Length]; memoryStream.Read(array, 0, array.Length); return array; } public byte[] Decrypt(byte[] encryptedValue, int offset = 0, int count = -1) { using ICryptoTransform transform = _aes.CreateDecryptor(); using MemoryStream memoryStream = new MemoryStream(); using CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write); cryptoStream.Write(encryptedValue, offset, (count == -1) ? encryptedValue.Length : count); cryptoStream.FlushFinalBlock(); memoryStream.Position = 0L; byte[] array = new byte[memoryStream.Length]; memoryStream.Read(array, 0, array.Length); return array; } public static byte[] HashSHA1(string password) { return SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(password)); } public static byte[] Salt(int maxLength = 16) { byte[] array = new byte[maxLength]; RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create(); using (randomNumberGenerator) { randomNumberGenerator.GetBytes(array); return array; } } public void Dispose() { if (_aes != null) { _aes.Dispose(); _aes = null; } } } internal class BsonReader { private readonly bool _utcDate; public BsonReader(bool utcDate) { _utcDate = utcDate; } public BsonDocument Deserialize(byte[] bson) { return ReadDocument(new ByteReader(bson)); } public BsonDocument ReadDocument(ByteReader reader) { int num = reader.ReadInt32(); int num2 = reader.Position + num - 5; BsonDocument bsonDocument = new BsonDocument(); while (reader.Position < num2) { string name; BsonValue value = ReadElement(reader, out name); bsonDocument.RawValue[name] = value; } reader.ReadByte(); return bsonDocument; } public BsonArray ReadArray(ByteReader reader) { int num = reader.ReadInt32(); int num2 = reader.Position + num - 5; BsonArray bsonArray = new BsonArray(); while (reader.Position < num2) { string name; BsonValue item = ReadElement(reader, out name); bsonArray.Add(item); } reader.ReadByte(); return bsonArray; } private BsonValue ReadElement(ByteReader reader, out string name) { byte b = reader.ReadByte(); name = reader.ReadCString(); switch (b) { case 1: return reader.ReadDouble(); case 2: return reader.ReadBsonString(); case 3: return ReadDocument(reader); case 4: return ReadArray(reader); case 5: { int count = reader.ReadInt32(); byte b2 = reader.ReadByte(); byte[] array = reader.ReadBytes(count); switch (b2) { case 0: return array; case 4: return new Guid(array); } break; } case 7: return new ObjectId(reader.ReadBytes(12)); case 8: return reader.ReadBoolean(); case 9: { long num = reader.ReadInt64(); switch (num) { case 253402300800000L: return DateTime.MaxValue; case -62135596800000L: return DateTime.MinValue; default: { DateTime unixEpoch = BsonValue.UnixEpoch; DateTime dateTime = unixEpoch.AddMilliseconds(num); return _utcDate ? dateTime : dateTime.ToLocalTime(); } } } case 10: return BsonValue.Null; case 16: return reader.ReadInt32(); case 18: return reader.ReadInt64(); case 19: return reader.ReadDecimal(); case byte.MaxValue: return BsonValue.MinValue; case 127: return BsonValue.MaxValue; } throw new NotSupportedException("BSON type not supported"); } } internal class ByteReader { private readonly byte[] _buffer; private readonly int _length; private int _pos; public int Position { get { return _pos; } set { _pos = value; } } public ByteReader(byte[] buffer) { _buffer = buffer; _length = buffer.Length; _pos = 0; } public void Skip(int length) { _pos += length; } public byte ReadByte() { byte result = _buffer[_pos]; _pos++; return result; } public bool ReadBoolean() { byte num = _buffer[_pos]; _pos++; return num != 0; } public ushort ReadUInt16() { _pos += 2; return BitConverter.ToUInt16(_buffer, _pos - 2); } public uint ReadUInt32() { _pos += 4; return BitConverter.ToUInt32(_buffer, _pos - 4); } public ulong ReadUInt64() { _pos += 8; return BitConverter.ToUInt64(_buffer, _pos - 8); } public short ReadInt16() { _pos += 2; return BitConverter.ToInt16(_buffer, _pos - 2); } public int ReadInt32() { _pos += 4; return BitConverter.ToInt32(_buffer, _pos - 4); } public long ReadInt64() { _pos += 8; return BitConverter.ToInt64(_buffer, _pos - 8); } public float ReadSingle() { _pos += 4; return BitConverter.ToSingle(_buffer, _pos - 4); } public double ReadDouble() { _pos += 8; return BitConverter.ToDouble(_buffer, _pos - 8); } public decimal ReadDecimal() { _pos += 16; int num = BitConverter.ToInt32(_buffer, _pos - 16); int num2 = BitConverter.ToInt32(_buffer, _pos - 12); int num3 = BitConverter.ToInt32(_buffer, _pos - 8); int num4 = BitConverter.ToInt32(_buffer, _pos - 4); return new decimal(new int[4] { num, num2, num3, num4 }); } public byte[] ReadBytes(int count) { byte[] array = new byte[count]; Buffer.BlockCopy(_buffer, _pos, array, 0, count); _pos += count; return array; } public string ReadString() { int num = ReadInt32(); string @string = Encoding.UTF8.GetString(_buffer, _pos, num); _pos += num; return @string; } public string ReadString(int length) { string @string = Encoding.UTF8.GetString(_buffer, _pos, length); _pos += length; return @string; } public string ReadBsonString() { int num = ReadInt32(); string @string = Encoding.UTF8.GetString(_buffer, _pos, num - 1); _pos += num; return @string; } public string ReadCString() { int num = _pos; int num2 = 0; while (true) { if (_buffer[num] == 0) { string @string = Encoding.UTF8.GetString(_buffer, _pos, num2); _pos += num2 + 1; return @string; } if (num > _length) { break; } num++; num2++; } return "_"; } public DateTime ReadDateTime() { return new DateTime(ReadInt64(), DateTimeKind.Utc).ToLocalTime(); } public Guid ReadGuid() { return new Guid(ReadBytes(16)); } public ObjectId ReadObjectId() { return new ObjectId(ReadBytes(12)); } public BsonValue ReadBsonValue(ushort length) { return (BsonType)ReadByte() switch { BsonType.Null => BsonValue.Null, BsonType.Int32 => ReadInt32(), BsonType.Int64 => ReadInt64(), BsonType.Double => ReadDouble(), BsonType.Decimal => ReadDecimal(), BsonType.String => ReadString(length), BsonType.Document => new BsonReader(utcDate: false).ReadDocument(this), BsonType.Array => new BsonReader(utcDate: false).ReadArray(this), BsonType.Binary => ReadBytes(length), BsonType.ObjectId => ReadObjectId(), BsonType.Guid => ReadGuid(), BsonType.Boolean => ReadBoolean(), BsonType.DateTime => ReadDateTime(), BsonType.MinValue => BsonValue.MinValue, BsonType.MaxValue => BsonValue.MaxValue, _ => throw new NotImplementedException(), }; } } internal class AsyncManualResetEvent { private volatile TaskCompletionSource _tcs = new TaskCompletionSource(); public Task WaitAsync() { return _tcs.Task; } public void Set() { _tcs.TrySetResult(result: true); } public void Reset() { TaskCompletionSource tcs; do { tcs = _tcs; } while (tcs.Task.IsCompleted && Interlocked.CompareExchange(ref _tcs, new TaskCompletionSource(), tcs) != tcs); } } internal class BufferSlice { public int Offset { get; } public int Count { get; } public byte[] Array { get; } public byte this[int index] { get { return Array[Offset + index]; } set { Array[Offset + index] = value; } } public BufferSlice(byte[] array, int offset, int count) { Array = array; Offset = offset; Count = count; } public void Clear() { System.Array.Clear(Array, Offset, Count); } public void Clear(int offset, int count) { Constants.ENSURE(offset + count <= Count, "must fit in this page"); System.Array.Clear(Array, Offset + offset, count); } public void Fill(byte value) { for (int i = 0; i < Count; i++) { Array[Offset + i] = value; } } public bool All(byte value) { for (int i = 0; i < Count; i++) { if (Array[Offset + i] != value) { return false; } } return true; } public string ToHex() { StringBuilder stringBuilder = new StringBuilder(); long num = 0L; while (num < Count) { for (int i = 0; i < 32; i++) { if (num >= Count) { break; } stringBuilder.Append(Array[Offset + num].ToString("X2") + " "); num++; } stringBuilder.AppendLine(); } return stringBuilder.ToString(); } public BufferSlice Slice(int offset, int count) { return new BufferSlice(Array, Offset + offset, count); } public byte[] ToArray() { byte[] array = new byte[Count]; Buffer.BlockCopy(Array, Offset, array, 0, Count); return array; } public override string ToString() { return $"Offset: {Offset} - Count: {Count}"; } } internal class Collation : IComparer, IComparer, IEqualityComparer { private readonly CompareInfo _compareInfo; public static Collation Default = new Collation(LiteDB.LCID.Current, CompareOptions.IgnoreCase); public static Collation Binary = new Collation(127, CompareOptions.Ordinal); public int LCID { get; } public CultureInfo Culture { get; } public CompareOptions SortOptions { get; } public Collation(string collation) { string[] array = collation.Split(new char[1] { '/' }); string text = array[0]; CompareOptions compareOptions = ((array.Length > 1) ? ((CompareOptions)Enum.Parse(typeof(CompareOptions), array[1])) : CompareOptions.None); LCID = LiteDB.LCID.GetLCID(text); SortOptions = compareOptions; Culture = new CultureInfo(text); _compareInfo = Culture.CompareInfo; } public Collation(int lcid, CompareOptions sortOptions) { LCID = lcid; SortOptions = sortOptions; Culture = LiteDB.LCID.GetCulture(lcid); _compareInfo = Culture.CompareInfo; } public int Compare(string left, string right) { int num = _compareInfo.Compare(left, right, SortOptions); if (num >= 0) { return (num > 0) ? 1 : 0; } return -1; } public int Compare(BsonValue left, BsonValue rigth) { return left.CompareTo(rigth, this); } public bool Equals(BsonValue x, BsonValue y) { return Compare(x, y) == 0; } public int GetHashCode(BsonValue obj) { return obj.GetHashCode(); } public override string ToString() { return Culture.Name + "/" + SortOptions; } } internal class Constants { public const int PAGE_SIZE = 8192; public const int PAGE_HEADER_SIZE = 32; public const int ENCRYPTION_SALT_SIZE = 16; public static int BUFFER_WRITABLE = -1; public static int INDEX_NAME_MAX_LENGTH = 32; public const int MAX_LEVEL_LENGTH = 32; public const int MAX_INDEX_KEY_LENGTH = 1023; public const int MAX_INDEX_LENGTH = 1400; public const int PAGE_FREE_LIST_SLOTS = 5; public const int MAX_DOCUMENT_SIZE = 16683050; public const int MAX_OPEN_TRANSACTIONS = 100; public const int MAX_TRANSACTION_SIZE = 100000; public static int[] MEMORY_SEGMENT_SIZES = new int[5] { 12, 50, 100, 500, 1000 }; public const int VIRTUAL_INDEX_MAX_CACHE = 2000; public const int CONTAINER_SORT_SIZE = 819200; public const int RANDOMIZER_SEED = 0; [Conditional("DEBUG")] [DebuggerHidden] public static void LOG(string message, string category) { } [Conditional("DEBUG")] [DebuggerHidden] public static void LOG(bool conditional, string message, string category) { } [DebuggerHidden] public static void ENSURE(bool conditional, string message = null) { if (!conditional) { if (Debugger.IsAttached) { Debugger.Break(); } throw LiteException.InvalidDatafileState(message); } } [DebuggerHidden] public static void ENSURE(bool conditional, string format, params object[] args) { if (!conditional) { if (Debugger.IsAttached) { Debugger.Break(); } string.Format(CultureInfo.InvariantCulture, format, args); throw LiteException.InvalidDatafileState(format); } } [DebuggerHidden] public static void ENSURE(bool ifTest, bool conditional, string message = null) { if (ifTest && !conditional) { if (Debugger.IsAttached) { Debugger.Break(); } throw LiteException.InvalidDatafileState(message); } } [Conditional("DEBUG")] [DebuggerHidden] public static void DEBUG(bool conditional, string message = null) { if (!conditional) { if (Debugger.IsAttached) { Debugger.Break(); } throw LiteException.InvalidDatafileState(message); } } } internal class StringEncoding { public static Encoding UTF8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); } internal static class ExtendedLengthHelper { public static void ReadLength(byte typeByte, byte lengthByte, out BsonType type, out ushort length) { byte b = (byte)(typeByte & 0x3Fu); byte b2 = lengthByte; byte b3 = (byte)(typeByte & 0xC0u); type = (BsonType)b; length = (ushort)((b3 << 2) | b2); } public static void WriteLength(BsonType type, ushort length, out byte typeByte, out byte lengthByte) { if (length > 1023) { throw new ArgumentOutOfRangeException("length"); } byte b = (byte)type; byte b2 = (byte)length; byte b3 = (byte)((length & 0x300) >> 2); typeByte = (byte)(b3 | b); lengthByte = b2; } } internal static class BufferExtensions { public static int BinaryCompareTo(this byte[] lh, byte[] rh) { if (lh == null) { if (rh != null) { return -1; } return 0; } if (rh == null) { return 1; } int num = 0; int num2 = 0; int num3 = Math.Min(lh.Length, rh.Length); while (num == 0 && num2 < num3) { num = lh[num2].CompareTo(rh[num2]); num2++; } if (num != 0) { if (num >= 0) { return 1; } return -1; } if (num2 == lh.Length) { if (num2 != rh.Length) { return -1; } return 0; } return 1; } public unsafe static bool IsFullZero(this byte[] data) { fixed (byte* ptr = data) { int num = data.Length; int num2 = num % 128; long* ptr2 = (long*)ptr; for (long* ptr3 = (long*)(ptr + num - num2); ptr2 < ptr3; ptr2 += 16) { if ((*ptr2 | ptr2[1] | ptr2[2] | ptr2[3] | ptr2[4] | ptr2[5] | ptr2[6] | ptr2[7] | ptr2[8] | ptr2[9] | ptr2[10] | ptr2[11] | ptr2[12] | ptr2[13] | ptr2[14] | ptr2[15]) != 0L) { return false; } } for (int i = 0; i < num2; i++) { if (data[num - 1 - i] != 0) { return false; } } return true; } } public static byte[] Fill(this byte[] array, byte value, int offset, int count) { for (int i = 0; i < count; i++) { array[i + offset] = value; } return array; } public static string ReadCString(this byte[] bytes, int startIndex, out int bytesCount) { int num = Array.IndexOf(bytes, (byte)0, startIndex); if (num > 0) { bytesCount = num - startIndex; return Encoding.UTF8.GetString(bytes, startIndex, bytesCount); } bytesCount = 0; return null; } public unsafe static void ToBytes(this short value, byte[] array, int startIndex) { fixed (byte* ptr = &array[startIndex]) { *(short*)ptr = value; } } public unsafe static void ToBytes(this int value, byte[] array, int startIndex) { fixed (byte* ptr = &array[startIndex]) { *(int*)ptr = value; } } public unsafe static void ToBytes(this long value, byte[] array, int startIndex) { fixed (byte* ptr = &array[startIndex]) { *(long*)ptr = value; } } public unsafe static void ToBytes(this ushort value, byte[] array, int startIndex) { fixed (byte* ptr = &array[startIndex]) { *(ushort*)ptr = value; } } public unsafe static void ToBytes(this uint value, byte[] array, int startIndex) { fixed (byte* ptr = &array[startIndex]) { *(uint*)ptr = value; } } public unsafe static void ToBytes(this ulong value, byte[] array, int startIndex) { fixed (byte* ptr = &array[startIndex]) { *(ulong*)ptr = value; } } public unsafe static void ToBytes(this float value, byte[] array, int startIndex) { (*(uint*)(&value)).ToBytes(array, startIndex); } public unsafe static void ToBytes(this double value, byte[] array, int startIndex) { (*(ulong*)(&value)).ToBytes(array, startIndex); } } internal static class BufferSliceExtensions { public static bool ReadBool(this BufferSlice buffer, int offset) { return buffer.Array[buffer.Offset + offset] != 0; } public static byte ReadByte(this BufferSlice buffer, int offset) { return buffer.Array[buffer.Offset + offset]; } public static short ReadInt16(this BufferSlice buffer, int offset) { return BitConverter.ToInt16(buffer.Array, buffer.Offset + offset); } public static ushort ReadUInt16(this BufferSlice buffer, int offset) { return BitConverter.ToUInt16(buffer.Array, buffer.Offset + offset); } public static int ReadInt32(this BufferSlice buffer, int offset) { return BitConverter.ToInt32(buffer.Array, buffer.Offset + offset); } public static uint ReadUInt32(this BufferSlice buffer, int offset) { return BitConverter.ToUInt32(buffer.Array, buffer.Offset + offset); } public static long ReadInt64(this BufferSlice buffer, int offset) { return BitConverter.ToInt64(buffer.Array, buffer.Offset + offset); } public static double ReadDouble(this BufferSlice buffer, int offset) { return BitConverter.ToDouble(buffer.Array, buffer.Offset + offset); } public static decimal ReadDecimal(this BufferSlice buffer, int offset) { int num = buffer.ReadInt32(offset); int num2 = buffer.ReadInt32(offset + 4); int num3 = buffer.ReadInt32(offset + 8); int num4 = buffer.ReadInt32(offset + 12); return new decimal(new int[4] { num, num2, num3, num4 }); } public static ObjectId ReadObjectId(this BufferSlice buffer, int offset) { return new ObjectId(buffer.Array, buffer.Offset + offset); } public static Guid ReadGuid(this BufferSlice buffer, int offset) { return new Guid(buffer.ReadBytes(offset, 16)); } public static byte[] ReadBytes(this BufferSlice buffer, int offset, int count) { byte[] array = new byte[count]; Buffer.BlockCopy(buffer.Array, buffer.Offset + offset, array, 0, count); return array; } public static DateTime ReadDateTime(this BufferSlice buffer, int offset) { long num = buffer.ReadInt64(offset); return num switch { 0L => DateTime.MinValue, 3155378975999999999L => DateTime.MaxValue, _ => new DateTime(num, DateTimeKind.Utc), }; } public static PageAddress ReadPageAddress(this BufferSlice buffer, int offset) { return new PageAddress(buffer.ReadUInt32(offset), buffer[offset + 4]); } public static string ReadString(this BufferSlice buffer, int offset, int count) { return Encoding.UTF8.GetString(buffer.Array, buffer.Offset + offset, count); } public static string ReadCString(this BufferSlice buffer, int offset, out int length) { length = buffer.Count - buffer.Offset - offset; for (int i = offset + buffer.Offset; i < buffer.Count; i++) { if (buffer[i] == 0) { length = i - buffer.Offset - offset + 1; break; } } return Encoding.UTF8.GetString(buffer.Array, buffer.Offset + offset, length - 1); } public static BsonValue ReadIndexKey(this BufferSlice buffer, int offset) { ExtendedLengthHelper.ReadLength(buffer[offset++], buffer[offset], out var type, out var length); switch (type) { case BsonType.Null: return BsonValue.Null; case BsonType.Int32: return buffer.ReadInt32(offset); case BsonType.Int64: return buffer.ReadInt64(offset); case BsonType.Double: return buffer.ReadDouble(offset); case BsonType.Decimal: return buffer.ReadDecimal(offset); case BsonType.String: offset++; return buffer.ReadString(offset, length); case BsonType.Document: { using BufferReader bufferReader2 = new BufferReader(buffer); bufferReader2.Skip(offset); return bufferReader2.ReadDocument().GetValue(); } case BsonType.Array: { using BufferReader bufferReader = new BufferReader(buffer); bufferReader.Skip(offset); return bufferReader.ReadArray().GetValue(); } case BsonType.Binary: offset++; return buffer.ReadBytes(offset, length); case BsonType.ObjectId: return buffer.ReadObjectId(offset); case BsonType.Guid: return buffer.ReadGuid(offset); case BsonType.Boolean: return buffer[offset] != 0; case BsonType.DateTime: return buffer.ReadDateTime(offset); case BsonType.MinValue: return BsonValue.MinValue; case BsonType.MaxValue: return BsonValue.MaxValue; default: throw new NotImplementedException(); } } public static void Write(this BufferSlice buffer, bool value, int offset) { buffer.Array[buffer.Offset + offset] = (value ? ((byte)1) : ((byte)0)); } public static void Write(this BufferSlice buffer, byte value, int offset) { buffer.Array[buffer.Offset + offset] = value; } public static void Write(this BufferSlice buffer, short value, int offset) { value.ToBytes(buffer.Array, buffer.Offset + offset); } public static void Write(this BufferSlice buffer, ushort value, int offset) { value.ToBytes(buffer.Array, buffer.Offset + offset); } public static void Write(this BufferSlice buffer, int value, int offset) { value.ToBytes(buffer.Array, buffer.Offset + offset); } public static void Write(this BufferSlice buffer, uint value, int offset) { value.ToBytes(buffer.Array, buffer.Offset + offset); } public static void Write(this BufferSlice buffer, long value, int offset) { value.ToBytes(buffer.Array, buffer.Offset + offset); } public static void Write(this BufferSlice buffer, double value, int offset) { value.ToBytes(buffer.Array, buffer.Offset + offset); } public static void Write(this BufferSlice buffer, decimal value, int offset) { int[] bits = decimal.GetBits(value); buffer.Write(bits[0], offset); buffer.Write(bits[1], offset + 4); buffer.Write(bits[2], offset + 8); buffer.Write(bits[3], offset + 12); } public static void Write(this BufferSlice buffer, DateTime value, int offset) { value.ToUniversalTime().Ticks.ToBytes(buffer.Array, buffer.Offset + offset); } public static void Write(this BufferSlice buffer, PageAddress value, int offset) { value.PageID.ToBytes(buffer.Array, buffer.Offset + offset); buffer[offset + 4] = value.Index; } public static void Write(this BufferSlice buffer, Guid value, int offset) { buffer.Write(value.ToByteArray(), offset); } public static void Write(this BufferSlice buffer, ObjectId value, int offset) { value.ToByteArray(buffer.Array, buffer.Offset + offset); } public static void Write(this BufferSlice buffer, byte[] value, int offset) { Buffer.BlockCopy(value, 0, buffer.Array, buffer.Offset + offset, value.Length); } public static void Write(this BufferSlice buffer, string value, int offset) { Encoding.UTF8.GetBytes(value, 0, value.Length, buffer.Array, buffer.Offset + offset); } public static void WriteIndexKey(this BufferSlice buffer, BsonValue value, int offset) { if (value.IsString) { string asString = value.AsString; ushort length = (ushort)Encoding.UTF8.GetByteCount(asString); ExtendedLengthHelper.WriteLength(BsonType.String, length, out var typeByte, out var lengthByte); buffer[offset++] = typeByte; buffer[offset++] = lengthByte; buffer.Write(asString, offset); return; } if (value.IsBinary) { byte[] asBinary = value.AsBinary; ExtendedLengthHelper.WriteLength(BsonType.Binary, (ushort)asBinary.Length, out var typeByte2, out var lengthByte2); buffer[offset++] = typeByte2; buffer[offset++] = lengthByte2; buffer.Write(asBinary, offset); return; } buffer[offset++] = (byte)value.Type; switch (value.Type) { case BsonType.Int32: buffer.Write(value.AsInt32, offset); break; case BsonType.Int64: buffer.Write(value.AsInt64, offset); break; case BsonType.Double: buffer.Write(value.AsDouble, offset); break; case BsonType.Decimal: buffer.Write(value.AsDecimal, offset); break; case BsonType.Document: { using BufferWriter bufferWriter2 = new BufferWriter(buffer); bufferWriter2.Skip(offset); bufferWriter2.WriteDocument(value.AsDocument, recalc: true); break; } case BsonType.Array: { using BufferWriter bufferWriter = new BufferWriter(buffer); bufferWriter.Skip(offset); bufferWriter.WriteArray(value.AsArray, recalc: true); break; } case BsonType.ObjectId: buffer.Write(value.AsObjectId, offset); break; case BsonType.Guid: buffer.Write(value.AsGuid, offset); break; case BsonType.Boolean: buffer[offset] = (value.AsBoolean ? ((byte)1) : ((byte)0)); break; case BsonType.DateTime: buffer.Write(value.AsDateTime, offset); break; default: throw new NotImplementedException(); case BsonType.MinValue: case BsonType.Null: case BsonType.MaxValue: break; } } } internal static class DateExtensions { public static DateTime Truncate(this DateTime dt) { if (dt == DateTime.MaxValue || dt == DateTime.MinValue) { return dt; } return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, dt.Millisecond, dt.Kind); } public static int MonthDifference(this DateTime startDate, DateTime endDate) { int num = endDate.Month + endDate.Year * 12 - (startDate.Month + startDate.Year * 12); double num2 = (endDate - endDate.AddMonths(1)).Days; return Convert.ToInt32(Math.Truncate((double)num + (double)(startDate.Day - endDate.Day) / num2)); } public static int YearDifference(this DateTime startDate, DateTime endDate) { int num = endDate.Year - startDate.Year; if (startDate.Month == endDate.Month && endDate.Day < startDate.Day) { num--; } else if (endDate.Month < startDate.Month) { num--; } return num; } } internal static class DictionaryExtensions { public static T GetOrDefault(this IDictionary dict, K key, T defaultValue = default(T)) { if (dict.TryGetValue(key, out var value)) { return value; } return defaultValue; } public static T GetOrAdd(this IDictionary dict, K key, Func valueFactoy) { if (!dict.TryGetValue(key, out var value)) { value = valueFactoy(key); dict.Add(key, value); } return value; } public static void ParseKeyValue(this IDictionary dict, string connectionString) { int position = 0; while (position < connectionString.Length) { EatWhitespace(); string key = ReadKey(); EatWhitespace(); string value = ReadValue(); dict[key] = value; } void EatWhitespace() { for (; position < connectionString.Length && (connectionString[position] == ' ' || connectionString[position] == '\t' || connectionString[position] == '\f'); position++) { } } string ReadKey() { StringBuilder stringBuilder2 = new StringBuilder(); for (; position < connectionString.Length; position++) { char c3 = connectionString[position]; if (c3 == '=') { position++; return stringBuilder2.ToString().Trim(); } stringBuilder2.Append(c3); } return stringBuilder2.ToString().Trim(); } string ReadValue() { StringBuilder stringBuilder = new StringBuilder(); char c = ((connectionString[position] == '"') ? '"' : ((connectionString[position] == '\'') ? '\'' : ' ')); if (c != ' ') { position++; } for (; position < connectionString.Length; position++) { char c2 = connectionString[position]; if (c == ' ') { if (c2 == ';') { position++; return stringBuilder.ToString().Trim(); } } else if (c != ' ' && c2 == c) { if (connectionString[position - 1] != '\\') { position++; EatWhitespace(); if (position < connectionString.Length && connectionString[position] == ';') { position++; } return stringBuilder.ToString(); } stringBuilder.Length--; } stringBuilder.Append(c2); } return stringBuilder.ToString().Trim(); } } public static T GetValue(this Dictionary dict, string key, T defaultValue = default(T)) { try { if (!dict.TryGetValue(key, out var value)) { return defaultValue; } if (typeof(T) == typeof(TimeSpan)) { if (Regex.IsMatch(value, "^\\d+$", RegexOptions.Compiled)) { return (T)(object)TimeSpan.FromSeconds(Convert.ToInt32(value)); } return (T)(object)TimeSpan.Parse(value); } if (typeof(T).GetTypeInfo().IsEnum) { return (T)Enum.Parse(typeof(T), value, ignoreCase: true); } return (T)Convert.ChangeType(value, typeof(T)); } catch (Exception) { throw new LiteException(0, "Invalid connection string value type for `" + key + "`"); } } public static long GetFileSize(this Dictionary dict, string key, long defaultValue) { string value = dict.GetValue(key); if (value == null) { return defaultValue; } Match match = Regex.Match(value, "^(\\d+)\\s*([tgmk])?(b|byte|bytes)?$", RegexOptions.IgnoreCase); if (!match.Success) { return 0L; } long num = Convert.ToInt64(match.Groups[1].Value); string text = match.Groups[2].Value.ToLower(); switch (text) { default: if (text.Length != 0) { break; } return num; case "t": return num * 1024 * 1024 * 1024 * 1024; case "g": return num * 1024 * 1024 * 1024; case "m": return num * 1024 * 1024; case "k": return num * 1024; case null: break; } return 0L; } } internal static class ExpressionExtensions { private static readonly Regex _removeSelect = new Regex("\\.Select\\s*\\(\\s*\\w+\\s*=>\\s*\\w+\\.", RegexOptions.Compiled); private static readonly Regex _removeList = new Regex("\\.get_Item\\(\\d+\\)", RegexOptions.Compiled); private static readonly Regex _removeArray = new Regex("\\[\\d+\\]", RegexOptions.Compiled); public static string GetPath(this Expression expr) { while (expr.NodeType == ExpressionType.Convert || expr.NodeType == ExpressionType.ConvertChecked) { expr = ((UnaryExpression)expr).Operand; } while (expr.NodeType == ExpressionType.Lambda && ((LambdaExpression)expr).Body is UnaryExpression unaryExpression) { expr = unaryExpression.Operand; } string text = expr.ToString(); int num = text.IndexOf('.'); string input = ((num < 0) ? text : text.Substring(num + 1).TrimEnd(new char[1] { ')' })); input = _removeList.Replace(input, ""); input = _removeArray.Replace(input, ""); return _removeSelect.Replace(input, ".").Replace(")", ""); } } internal static class HashSetExtensions { public static HashSet AddRange(this HashSet hash, IEnumerable items) { if (items == null) { return hash; } foreach (T item in items) { hash.Add(item); } return hash; } } internal static class IOExceptionExtensions { private const int ERROR_SHARING_VIOLATION = 32; private const int ERROR_LOCK_VIOLATION = 33; public static bool IsLocked(this IOException ex) { int num = Marshal.GetHRForException(ex) & 0xFFFF; if (num != 32) { return num == 33; } return true; } public static void WaitIfLocked(this IOException ex, int timerInMilliseconds) { if (ex.IsLocked()) { if (timerInMilliseconds > 0) { Task.Delay(timerInMilliseconds).Wait(); } return; } throw ex; } } internal static class LinqExtensions { public static IEnumerable> Batch(this IEnumerable source, int batchSize) { using IEnumerator enumerator = source.GetEnumerator(); while (enumerator.MoveNext()) { yield return YieldBatchElements(enumerator, batchSize - 1); } } private static IEnumerable YieldBatchElements(IEnumerator source, int batchSize) { yield return source.Current; for (int i = 0; i < batchSize; i++) { if (!source.MoveNext()) { break; } yield return source.Current; } } public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector, IEqualityComparer comparer) { if (source == null) { throw new ArgumentNullException("source"); } if (keySelector == null) { throw new ArgumentNullException("keySelector"); } return _(); IEnumerable _() { HashSet knownKeys = new HashSet(comparer); foreach (TSource item in source) { if (knownKeys.Add(keySelector(item))) { yield return item; } } } } public static IEnumerable> IsLast(this IEnumerable source) where T : class { T val = null; foreach (T item in source) { if (val != null) { yield return new LastItem { Item = val, IsLast = false }; } val = item; } if (val != null) { yield return new LastItem { Item = val, IsLast = true }; } } } internal struct LastItem { public T Item { [<72e81469-6a2a-4525-bb04-ef1f9b4d38cd>IsReadOnly] get; set; } public bool IsLast { [<72e81469-6a2a-4525-bb04-ef1f9b4d38cd>IsReadOnly] get; set; } } internal static class StreamExtensions { public static void FlushToDisk(this Stream stream) { if (stream is FileStream fileStream) { fileStream.Flush(flushToDisk: true); } else { stream.Flush(); } } } internal static class StringExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNullOrWhiteSpace(this string str) { return string.IsNullOrWhiteSpace(str); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNullOrEmpty(this string str) { return string.IsNullOrEmpty(str); } public static bool IsWord(this string str) { if (string.IsNullOrWhiteSpace(str)) { return false; } for (int i = 0; i < str.Length; i++) { if (!Tokenizer.IsWordChar(str[i], i == 0)) { return false; } } return true; } public static string Sha1(this string value) { byte[] bytes = Encoding.UTF8.GetBytes(value); using SHA1 sHA = SHA1.Create(); byte[] array = sHA.ComputeHash(bytes); StringBuilder stringBuilder = new StringBuilder(); byte[] array2 = array; foreach (byte b in array2) { stringBuilder.Append(b.ToString("X2")); } return stringBuilder.ToString(); } public static bool SqlLike(this string str, string pattern, Collation collation) { bool flag = true; bool flag2 = false; bool flag3 = false; bool flag4 = false; bool flag5 = false; bool flag6 = false; int num = -1; int i = 0; char c = '\0'; for (int j = 0; j < str.Length; j++) { char c2 = str[j]; if (i < pattern.Length) { c = pattern[i]; if (!flag2 && c == '%') { num = i; flag2 = true; for (; i < pattern.Length && pattern[i] == '%'; i++) { } c = ((i < pattern.Length) ? pattern[i] : '\0'); } else if (c == '_') { flag3 = true; i++; } } if (flag2) { if (collation.Compare(c2.ToString(), c.ToString()) == 0) { flag2 = false; i++; } } else if (flag3) { flag3 = false; } else if (flag4 || flag5) { if (flag4) { if (num < 0) { flag = false; break; } i = num; } flag5 = (flag4 = false); } else if (collation.Compare(c2.ToString(), c.ToString()) == 0) { i++; } else { if (num < 0) { flag = false; break; } int num2 = i - num - 1; j -= num2; i = num; } } flag6 = i >= pattern.Length; if (flag && !flag6) { bool flag7 = true; for (int k = i; k < pattern.Length; k++) { if (pattern[k] != '%') { flag7 = false; break; } } if (flag7) { flag6 = true; } } return flag && flag6; } public static string SqlLikeStartsWith(this string str, out bool hasMore) { int i = 0; int length = str.Length; char c = '\0'; for (; i < length; i++) { c = str[i]; if (c == '%' || c == '_') { break; } } hasMore = i != length && i != length - 1; return str.Substring(0, i); } } internal static class TypeInfoExtensions { public static bool IsAnonymousType(this Type type) { if (type.FullName.Contains("AnonymousType")) { return type.GetTypeInfo().GetCustomAttributes(typeof(CompilerGeneratedAttribute), inherit: false).Any(); } return false; } public static bool IsEnumerable(this Type type) { if (type != typeof(string)) { return typeof(IEnumerable).IsAssignableFrom(type); } return false; } } internal static class FileHelper { public static string GetSuffixFile(string filename, string suffix = "-temp", bool checkIfExists = true) { int num = 0; string text = Path.Combine(Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename) + suffix + Path.GetExtension(filename)); while (checkIfExists && File.Exists(text)) { string? directoryName = Path.GetDirectoryName(filename); string[] obj = new string[5] { Path.GetFileNameWithoutExtension(filename), suffix, "-", null, null }; int num2 = ++num; obj[3] = num2.ToString(); obj[4] = Path.GetExtension(filename); text = Path.Combine(directoryName, string.Concat(obj)); } return text; } public static string GetLogFile(string filename) { return GetSuffixFile(filename, "-log", checkIfExists: false); } public static string GetTempFile(string filename) { return GetSuffixFile(filename, "-tmp", checkIfExists: false); } public static bool IsFileLocked(string filename) { FileStream fileStream = null; FileInfo fileInfo = new FileInfo(filename); try { fileStream = fileInfo.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None); } catch (IOException ex) { return ex.IsLocked(); } finally { fileStream?.Dispose(); } return false; } public static bool TryExec(int timeout, Action action) { DateTime dateTime = DateTime.UtcNow.AddSeconds(timeout); do { try { action(); return true; } catch (IOException ex) { ex.WaitIfLocked(25); } } while (DateTime.UtcNow < dateTime); return false; } public static void Exec(int timeout, Action action) { DateTime dateTime = DateTime.UtcNow.AddSeconds(timeout); IOException ex2; do { try { action(); return; } catch (IOException ex) { ex2 = ex; ex.WaitIfLocked(25); } } while (DateTime.UtcNow < dateTime); throw ex2; } public static long ParseFileSize(string size) { Match match = Regex.Match(size, "^(\\d+)\\s*([tgmk])?(b|byte|bytes)?$", RegexOptions.IgnoreCase); if (!match.Success) { return 0L; } long num = Convert.ToInt64(match.Groups[1].Value); string text = match.Groups[2].Value.ToLower(); switch (text) { default: if (text.Length != 0) { break; } return num; case "t": return num * 1024 * 1024 * 1024 * 1024; case "g": return num * 1024 * 1024 * 1024; case "m": return num * 1024 * 1024; case "k": return num * 1024; case null: break; } return 0L; } public static string FormatFileSize(long byteCount) { string[] array = new string[5] { "B", "KB", "MB", "GB", "TB" }; if (byteCount == 0L) { return "0" + array[0]; } long num = Math.Abs(byteCount); long num2 = Convert.ToInt64(Math.Floor(Math.Log(num, 1024.0))); double num3 = Math.Round((double)num / Math.Pow(1024.0, num2), 1); return (double)Math.Sign(byteCount) * num3 + array[num2]; } } internal class LCID { private static readonly IDictionary _mappings = new Dictionary { [1] = "ar", [2] = "bg", [3] = "ca", [4] = "zh-Hans", [5] = "cs", [6] = "da", [7] = "de", [8] = "el", [9] = "en", [10] = "es", [11] = "fi", [12] = "fr", [13] = "he", [14] = "hu", [15] = "is", [16] = "it", [17] = "ja", [18] = "ko", [19] = "nl", [20] = "no", [21] = "pl", [22] = "pt", [23] = "rm", [24] = "ro", [25] = "ru", [26] = "hr", [27] = "sk", [28] = "sq", [29] = "sv", [30] = "th", [31] = "tr", [32] = "ur", [33] = "id", [34] = "uk", [35] = "be", [36] = "sl", [37] = "et", [38] = "lv", [39] = "lt", [40] = "tg", [41] = "fa", [42] = "vi", [43] = "hy", [44] = "az", [45] = "eu", [46] = "hsb", [47] = "mk", [48] = "st", [49] = "ts", [50] = "tn", [51] = "ve", [52] = "xh", [53] = "zu", [54] = "af", [55] = "ka", [56] = "fo", [57] = "hi", [58] = "mt", [59] = "se", [60] = "ga", [61] = "yi", [62] = "ms", [63] = "kk", [64] = "ky", [65] = "sw", [66] = "tk", [67] = "uz", [68] = "tt", [69] = "bn", [70] = "pa", [71] = "gu", [72] = "or", [73] = "ta", [74] = "te", [75] = "kn", [76] = "ml", [77] = "as", [78] = "mr", [79] = "sa", [80] = "mn", [81] = "bo", [82] = "cy", [83] = "km", [84] = "lo", [85] = "my", [86] = "gl", [87] = "kok", [88] = "mni", [89] = "sd", [90] = "syr", [91] = "si", [92] = "chr", [93] = "iu", [94] = "am", [95] = "tzm", [96] = "ks", [97] = "ne", [98] = "fy", [99] = "ps", [100] = "fil", [101] = "dv", [102] = "bin", [103] = "ff", [104] = "ha", [105] = "ibb", [106] = "yo", [107] = "quz", [108] = "nso", [109] = "ba", [110] = "lb", [111] = "kl", [112] = "ig", [113] = "kr", [114] = "om", [115] = "ti", [116] = "gn", [117] = "haw", [118] = "la", [119] = "so", [120] = "ii", [121] = "pap", [122] = "arn", [124] = "moh", [126] = "br", [127] = "", [128] = "ug", [129] = "mi", [130] = "oc", [131] = "co", [132] = "gsw", [133] = "sah", [134] = "quc", [135] = "rw", [136] = "wo", [140] = "prs", [145] = "gd", [146] = "ku", [1025] = "ar-SA", [1026] = "bg-BG", [1027] = "ca-ES", [1028] = "zh-TW", [1029] = "cs-CZ", [1030] = "da-DK", [1031] = "de-DE", [1032] = "el-GR", [1033] = "en-US", [1035] = "fi-FI", [1036] = "fr-FR", [1037] = "he-IL", [1038] = "hu-HU", [1039] = "is-IS", [1040] = "it-IT", [1041] = "ja-JP", [1042] = "ko-KR", [1043] = "nl-NL", [1044] = "nb-NO", [1045] = "pl-PL", [1046] = "pt-BR", [1047] = "rm-CH", [1048] = "ro-RO", [1049] = "ru-RU", [1050] = "hr-HR", [1051] = "sk-SK", [1052] = "sq-AL", [1053] = "sv-SE", [1054] = "th-TH", [1055] = "tr-TR", [1056] = "ur-PK", [1057] = "id-ID", [1058] = "uk-UA", [1059] = "be-BY", [1060] = "sl-SI", [1061] = "et-EE", [1062] = "lv-LV", [1063] = "lt-LT", [1064] = "tg-Cyrl-TJ", [1065] = "fa-IR", [1066] = "vi-VN", [1067] = "hy-AM", [1068] = "az-Latn-AZ", [1069] = "eu-ES", [1070] = "hsb-DE", [1071] = "mk-MK", [1072] = "st-ZA", [1073] = "ts-ZA", [1074] = "tn-ZA", [1075] = "ve-ZA", [1076] = "xh-ZA", [1077] = "zu-ZA", [1078] = "af-ZA", [1079] = "ka-GE", [1080] = "fo-FO", [1081] = "hi-IN", [1082] = "mt-MT", [1083] = "se-NO", [1085] = "yi-001", [1086] = "ms-MY", [1087] = "kk-KZ", [1088] = "ky-KG", [1089] = "sw-KE", [1090] = "tk-TM", [1091] = "uz-Latn-UZ", [1092] = "tt-RU", [1093] = "bn-IN", [1094] = "pa-IN", [1095] = "gu-IN", [1096] = "or-IN", [1097] = "ta-IN", [1098] = "te-IN", [1099] = "kn-IN", [1100] = "ml-IN", [1101] = "as-IN", [1102] = "mr-IN", [1103] = "sa-IN", [1104] = "mn-MN", [1105] = "bo-CN", [1106] = "cy-GB", [1107] = "km-KH", [1108] = "lo-LA", [1109] = "my-MM", [1110] = "gl-ES", [1111] = "kok-IN", [1112] = "mni-IN", [1113] = "sd-Deva-IN", [1114] = "syr-SY", [1115] = "si-LK", [1116] = "chr-Cher-US", [1117] = "iu-Cans-CA", [1118] = "am-ET", [1119] = "tzm-Arab-MA", [1120] = "ks-Arab", [1121] = "ne-NP", [1122] = "fy-NL", [1123] = "ps-AF", [1124] = "fil-PH", [1125] = "dv-MV", [1126] = "bin-NG", [1127] = "ff-NG", [1128] = "ha-Latn-NG", [1129] = "ibb-NG", [1130] = "yo-NG", [1131] = "quz-BO", [1132] = "nso-ZA", [1133] = "ba-RU", [1134] = "lb-LU", [1135] = "kl-GL", [1136] = "ig-NG", [1137] = "kr-NG", [1138] = "om-ET", [1139] = "ti-ET", [1140] = "gn-PY", [1141] = "haw-US", [1142] = "la-001", [1143] = "so-SO", [1144] = "ii-CN", [1145] = "pap-029", [1146] = "arn-CL", [1148] = "moh-CA", [1150] = "br-FR", [1152] = "ug-CN", [1153] = "mi-NZ", [1154] = "oc-FR", [1155] = "co-FR", [1156] = "gsw-FR", [1157] = "sah-RU", [1158] = "quc-Latn-GT", [1159] = "rw-RW", [1160] = "wo-SN", [1164] = "prs-AF", [1169] = "gd-GB", [1170] = "ku-Arab-IQ", [2049] = "ar-IQ", [2051] = "ca-ES-valencia", [2052] = "zh-CN", [2055] = "de-CH", [2057] = "en-GB", [2058] = "es-MX", [2060] = "fr-BE", [2064] = "it-CH", [2067] = "nl-BE", [2068] = "nn-NO", [2070] = "pt-PT", [2072] = "ro-MD", [2073] = "ru-MD", [2077] = "sv-FI", [2080] = "ur-IN", [2092] = "az-Cyrl-AZ", [2094] = "dsb-DE", [2098] = "tn-BW", [2107] = "se-SE", [2108] = "ga-IE", [2110] = "ms-BN", [2115] = "uz-Cyrl-UZ", [2117] = "bn-BD", [2118] = "pa-Arab-PK", [2121] = "ta-LK", [2128] = "mn-Mong-CN", [2137] = "sd-Arab-PK", [2141] = "iu-Latn-CA", [2143] = "tzm-Latn-DZ", [2144] = "ks-Deva-IN", [2145] = "ne-IN", [2151] = "ff-Latn-SN", [2155] = "quz-EC", [2163] = "ti-ER", [3073] = "ar-EG", [3076] = "zh-HK", [3079] = "de-AT", [3081] = "en-AU", [3082] = "es-ES", [3084] = "fr-CA", [3131] = "se-FI", [3152] = "mn-Mong-MN", [3153] = "dz-BT", [3179] = "quz-PE", [4097] = "ar-LY", [4100] = "zh-SG", [4103] = "de-LU", [4105] = "en-CA", [4106] = "es-GT", [4108] = "fr-CH", [4122] = "hr-BA", [4155] = "smj-NO", [4191] = "tzm-Tfng-MA", [5121] = "ar-DZ", [5124] = "zh-MO", [5127] = "de-LI", [5129] = "en-NZ", [5130] = "es-CR", [5132] = "fr-LU", [5146] = "bs-Latn-BA", [5179] = "smj-SE", [6145] = "ar-MA", [6153] = "en-IE", [6154] = "es-PA", [6156] = "fr-MC", [6170] = "sr-Latn-BA", [6203] = "sma-NO", [7169] = "ar-TN", [7177] = "en-ZA", [7178] = "es-DO", [7180] = "fr-029", [7194] = "sr-Cyrl-BA", [7227] = "sma-SE", [8193] = "ar-OM", [8201] = "en-JM", [8202] = "es-VE", [8204] = "fr-RE", [8218] = "bs-Cyrl-BA", [8251] = "sms-FI", [9217] = "ar-YE", [9225] = "en-029", [9226] = "es-CO", [9228] = "fr-CD", [9242] = "sr-Latn-RS", [9275] = "smn-FI", [10241] = "ar-SY", [10249] = "en-BZ", [10250] = "es-PE", [10252] = "fr-SN", [10266] = "sr-Cyrl-RS", [11265] = "ar-JO", [11273] = "en-TT", [11274] = "es-AR", [11276] = "fr-CM", [11290] = "sr-Latn-ME", [12289] = "ar-LB", [12297] = "en-ZW", [12298] = "es-EC", [12300] = "fr-CI", [12314] = "sr-Cyrl-ME", [13313] = "ar-KW", [13321] = "en-PH", [13322] = "es-CL", [13324] = "fr-ML", [14337] = "ar-AE", [14345] = "en-ID", [14346] = "es-UY", [14348] = "fr-MA", [15361] = "ar-BH", [15369] = "en-HK", [15370] = "es-PY", [15372] = "fr-HT", [16385] = "ar-QA", [16393] = "en-IN", [16394] = "es-BO", [17417] = "en-MY", [17418] = "es-SV", [18441] = "en-SG", [18442] = "es-HN", [19466] = "es-NI", [20490] = "es-PR", [21514] = "es-US", [22538] = "es-419", [23562] = "es-CU", [25626] = "bs-Cyrl", [26650] = "bs-Latn", [27674] = "sr-Cyrl", [28698] = "sr-Latn", [28731] = "smn", [29740] = "az-Cyrl", [29755] = "sms", [30724] = "zh", [30740] = "nn", [30746] = "bs", [30764] = "az-Latn", [30779] = "sma", [30787] = "uz-Cyrl", [30800] = "mn-Cyrl", [30813] = "iu-Cans", [30815] = "tzm-Tfng", [31748] = "zh-Hant", [31764] = "nb", [31770] = "sr", [31784] = "tg-Cyrl", [31790] = "dsb", [31803] = "smj", [31811] = "uz-Latn", [31814] = "pa-Arab", [31824] = "mn-Mong", [31833] = "sd-Arab", [31836] = "chr-Cher", [31837] = "iu-Latn", [31839] = "tzm-Latn", [31847] = "ff-Latn", [31848] = "ha-Latn", [31878] = "quc-Latn", [31890] = "ku-Arab" }; public static int Current { get { string current = CultureInfo.CurrentCulture.Name; KeyValuePair keyValuePair = _mappings.FirstOrDefault((KeyValuePair x) => x.Value == current); if (keyValuePair.Key == 0) { return 127; } return keyValuePair.Key; } } public static CultureInfo GetCulture(int lcid) { if (_mappings.TryGetValue(lcid, out var value)) { return new CultureInfo(value); } throw new ArgumentException("Invalid LCID code"); } public static int GetLCID(string culture) { foreach (KeyValuePair mapping in _mappings) { if (mapping.Value == culture) { return mapping.Key; } } throw new LiteException(0, "Invalid culture name"); } } internal class LiteException : Exception { public const int FILE_NOT_FOUND = 101; public const int DATABASE_SHUTDOWN = 102; public const int INVALID_DATABASE = 103; public const int FILE_SIZE_EXCEEDED = 105; public const int COLLECTION_LIMIT_EXCEEDED = 106; public const int INDEX_DROP_ID = 108; public const int INDEX_DUPLICATE_KEY = 110; public const int INVALID_INDEX_KEY = 111; public const int INDEX_NOT_FOUND = 112; public const int INVALID_DBREF = 113; public const int LOCK_TIMEOUT = 120; public const int INVALID_COMMAND = 121; public const int ALREADY_EXISTS_COLLECTION_NAME = 122; public const int ALREADY_OPEN_DATAFILE = 124; public const int INVALID_TRANSACTION_STATE = 126; public const int INDEX_NAME_LIMIT_EXCEEDED = 128; public const int INVALID_INDEX_NAME = 129; public const int INVALID_COLLECTION_NAME = 130; public const int TEMP_ENGINE_ALREADY_DEFINED = 131; public const int INVALID_EXPRESSION_TYPE = 132; public const int COLLECTION_NOT_FOUND = 133; public const int COLLECTION_ALREADY_EXIST = 134; public const int INDEX_ALREADY_EXIST = 135; public const int INVALID_UPDATE_FIELD = 136; public const int ENGINE_DISPOSED = 137; public const int INVALID_FORMAT = 200; public const int DOCUMENT_MAX_DEPTH = 201; public const int INVALID_CTOR = 202; public const int UNEXPECTED_TOKEN = 203; public const int INVALID_DATA_TYPE = 204; public const int PROPERTY_NOT_MAPPED = 206; public const int INVALID_TYPED_NAME = 207; public const int PROPERTY_READ_WRITE = 209; public const int INITIALSIZE_CRYPTO_NOT_SUPPORTED = 210; public const int INVALID_INITIALSIZE = 211; public const int INVALID_NULL_CHAR_STRING = 212; public const int INVALID_FREE_SPACE_PAGE = 213; public const int DATA_TYPE_NOT_ASSIGNABLE = 214; public const int AVOID_USE_OF_PROCESS = 215; public const int NOT_ENCRYPTED = 216; public const int INVALID_PASSWORD = 217; public const int ILLEGAL_DESERIALIZATION_TYPE = 218; public const int INVALID_DATAFILE_STATE = 999; public int ErrorCode { get; private set; } public long Position { get; private set; } public bool IsCritical => ErrorCode >= 900; public LiteException(int code, string message) : base(message) { ErrorCode = code; } internal LiteException(int code, string message, params object[] args) : base(string.Format(message, args)) { ErrorCode = code; } internal LiteException(int code, Exception inner, string message, params object[] args) : base(string.Format(message, args), inner) { ErrorCode = code; } internal static LiteException FileNotFound(object fileId) { return new LiteException(101, "File '{0}' not found.", fileId); } internal static LiteException DatabaseShutdown() { return new LiteException(102, "Database is in shutdown process."); } internal static LiteException InvalidDatabase() { return new LiteException(103, "File is not a valid LiteDB database format or contains a invalid password."); } internal static LiteException FileSizeExceeded(long limit) { return new LiteException(105, "Database size exceeds limit of {0}.", FileHelper.FormatFileSize(limit)); } internal static LiteException CollectionLimitExceeded(int limit) { return new LiteException(106, "This database exceeded the maximum limit of collection names size: {0} bytes", limit); } internal static LiteException IndexNameLimitExceeded(int limit) { return new LiteException(128, "This collection exceeded the maximum limit of indexes names/expression size: {0} bytes", limit); } internal static LiteException InvalidIndexName(string name, string collection, string reason) { return new LiteException(129, "Invalid index name '{0}' on collection '{1}': {2}", name, collection, reason); } internal static LiteException InvalidCollectionName(string name, string reason) { return new LiteException(130, "Invalid collection name '{0}': {1}", name, reason); } internal static LiteException IndexDropId() { return new LiteException(108, "Primary key index '_id' can't be dropped."); } internal static LiteException TempEngineAlreadyDefined() { return new LiteException(131, "Temporary engine already defined or auto created."); } internal static LiteException CollectionNotFound(string key) { return new LiteException(133, "Collection not found: '{0}'", key); } internal static LiteException InvalidExpressionType(BsonExpression expr, BsonExpressionType type) { return new LiteException(132, "Expression '{0}' must be a {1} type.", expr.Source, type); } internal static LiteException InvalidExpressionTypePredicate(BsonExpression expr) { return new LiteException(132, "Expression '{0}' are not supported as predicate expression.", expr.Source); } internal static LiteException CollectionAlreadyExist(string key) { return new LiteException(134, "Collection already exist: '{0}'", key); } internal static LiteException IndexAlreadyExist(string name) { return new LiteException(135, "Index name '{0}' already exist with a differnt expression. Try drop index first.", name); } internal static LiteException InvalidUpdateField(string field) { return new LiteException(136, "'{0}' can't be modified in UPDATE command.", field); } internal static LiteException IndexDuplicateKey(string field, BsonValue key) { return new LiteException(110, "Cannot insert duplicate key in unique index '{0}'. The duplicate value is '{1}'.", field, key); } internal static LiteException InvalidIndexKey(string text) { return new LiteException(111, text); } internal static LiteException IndexNotFound(string name) { return new LiteException(112, "Index not found '{0}'.", name); } internal static LiteException LockTimeout(string mode, TimeSpan ts) { return new LiteException(120, "Database lock timeout when entering in {0} mode after {1}", mode, ts.ToString()); } internal static LiteException LockTimeout(string mode, string collection, TimeSpan ts) { return new LiteException(120, "Collection '{0}' lock timeout when entering in {1} mode after {2}", collection, mode, ts.ToString()); } internal static LiteException InvalidCommand(string command) { return new LiteException(121, "Command '{0}' is not a valid shell command.", command); } internal static LiteException AlreadyExistsCollectionName(string newName) { return new LiteException(122, "New collection name '{0}' already exists.", newName); } internal static LiteException AlreadyOpenDatafile(string filename) { return new LiteException(124, "Your datafile '{0}' is open in another process.", filename); } internal static LiteException InvalidDbRef(string path) { return new LiteException(113, "Invalid value for DbRef in path '{0}'. Value must be document like {{ $ref: \"?\", $id: ? }}", path); } internal static LiteException AlreadyExistsTransaction() { return new LiteException(126, "The current thread already contains an open transaction. Use the Commit/Rollback method to release the previous transaction."); } internal static LiteException CollectionLockerNotFound(string collection) { return new LiteException(126, "Collection locker '{0}' was not found inside dictionary.", collection); } internal static LiteException InvalidFormat(string field) { return new LiteException(200, "Invalid format: {0}", field); } internal static LiteException DocumentMaxDepth(int depth, Type type) { return new LiteException(201, "Document has more than {0} nested documents in '{1}'. Check for circular references (use DbRef).", depth, (type == null) ? "-" : type.Name); } internal static LiteException InvalidCtor(Type type, Exception inner) { return new LiteException(202, inner, "Failed to create instance for type '{0}' from assembly '{1}'. Checks if the class has a public constructor with no parameters.", type.FullName, type.AssemblyQualifiedName); } internal static LiteException UnexpectedToken(Token token, string expected = null) { long valueOrDefault = (token?.Position - (token?.Value?.Length).GetValueOrDefault()).GetValueOrDefault(); string arg = ((token != null && token.Type == TokenType.EOF) ? "[EOF]" : (token?.Value ?? "")); string arg2 = ((expected == null) ? "" : (" Expected `" + expected + "`.")); return new LiteException(203, $"Unexpected token `{arg}` in position {valueOrDefault}.{arg2}") { Position = valueOrDefault }; } internal static LiteException UnexpectedToken(string message, Token token) { long valueOrDefault = (token?.Position - (token?.Value?.Length).GetValueOrDefault()).GetValueOrDefault(); return new LiteException(203, message) { Position = valueOrDefault }; } internal static LiteException InvalidDataType(string field, BsonValue value) { return new LiteException(204, "Invalid BSON data type '{0}' on field '{1}'.", value.Type, field); } internal static LiteException PropertyReadWrite(PropertyInfo prop) { return new LiteException(209, "'{0}' property must have public getter and setter.", prop.Name); } internal static LiteException PropertyNotMapped(string name) { return new LiteException(206, "Property '{0}' was not mapped into BsonDocument.", name); } internal static LiteException InvalidTypedName(string type) { return new LiteException(207, "Type '{0}' not found in current domain (_type format is 'Type.FullName, AssemblyName').", type); } internal static LiteException InitialSizeCryptoNotSupported() { return new LiteException(210, "Initial Size option is not supported for encrypted datafiles."); } internal static LiteException InvalidInitialSize() { return new LiteException(211, "Initial Size must be a multiple of page size ({0} bytes).", 8192); } internal static LiteException EngineDisposed() { return new LiteException(137, "This engine instance already disposed."); } internal static LiteException InvalidNullCharInString() { return new LiteException(212, "Invalid null character (\\0) was found in the string"); } internal static LiteException InvalidPageType(PageType pageType, BasePage page) { StringBuilder stringBuilder = new StringBuilder($"Invalid {pageType} on {page.PageID}. "); stringBuilder.Append($"Full zero: {page.Buffer.All(0)}. "); stringBuilder.Append($"Page Type: {page.PageType}. "); stringBuilder.Append($"Prev/Next: {page.PrevPageID}/{page.NextPageID}. "); stringBuilder.Append($"UniqueID: {page.Buffer.UniqueID}. "); stringBuilder.Append($"ShareCounter: {page.Buffer.ShareCounter}. "); return new LiteException(0, stringBuilder.ToString()); } internal static LiteException InvalidFreeSpacePage(uint pageID, int freeBytes, int length) { return new LiteException(213, $"An operation that would corrupt page {pageID} was prevented. The operation required {length} free bytes, but the page had only {freeBytes} available."); } internal static LiteException DataTypeNotAssignable(string type1, string type2) { return new LiteException(214, "Data type " + type1 + " is not assignable from data type " + type2); } internal static LiteException FileNotEncrypted() { return new LiteException(216, "File is not encrypted."); } internal static LiteException InvalidPassword() { return new LiteException(217, "Invalid password."); } internal static LiteException IllegalDeserializationType(string typeName) { return new LiteException(218, "Illegal deserialization type: " + typeName); } internal static LiteException InvalidDatafileState(string message) { return new LiteException(999, message); } } internal class MimeTypeConverter { private static readonly IDictionary _mappings = new Dictionary(StringComparer.OrdinalIgnoreCase) { { ".323", "text/h323" }, { ".3g2", "video/3gpp2" }, { ".3gp", "video/3gpp" }, { ".3gp2", "video/3gpp2" }, { ".3gpp", "video/3gpp" }, { ".7z", "application/x-7z-compressed" }, { ".aa", "audio/audible" }, { ".AAC", "audio/aac" }, { ".aaf", "application/octet-stream" }, { ".aax", "audio/vnd.audible.aax" }, { ".ac3", "audio/ac3" }, { ".aca", "application/octet-stream" }, { ".accda", "application/msaccess.addin" }, { ".accdb", "application/msaccess" }, { ".accdc", "application/msaccess.cab" }, { ".accde", "application/msaccess" }, { ".accdr", "application/msaccess.runtime" }, { ".accdt", "application/msaccess" }, { ".accdw", "application/msaccess.webapplication" }, { ".accft", "application/msaccess.ftemplate" }, { ".acx", "application/internet-property-stream" }, { ".AddIn", "text/xml" }, { ".ade", "application/msaccess" }, { ".adobebridge", "application/x-bridge-url" }, { ".adp", "application/msaccess" }, { ".ADT", "audio/vnd.dlna.adts" }, { ".ADTS", "audio/aac" }, { ".afm", "application/octet-stream" }, { ".ai", "application/postscript" }, { ".aif", "audio/x-aiff" }, { ".aifc", "audio/aiff" }, { ".aiff", "audio/aiff" }, { ".air", "application/vnd.adobe.air-application-installer-package+zip" }, { ".amc", "application/x-mpeg" }, { ".application", "application/x-ms-application" }, { ".art", "image/x-jg" }, { ".asa", "application/xml" }, { ".asax", "application/xml" }, { ".ascx", "application/xml" }, { ".asd", "application/octet-stream" }, { ".asf", "video/x-ms-asf" }, { ".ashx", "application/xml" }, { ".asi", "application/octet-stream" }, { ".asm", "text/plain" }, { ".asmx", "application/xml" }, { ".aspx", "application/xml" }, { ".asr", "video/x-ms-asf" }, { ".asx", "video/x-ms-asf" }, { ".atom", "application/atom+xml" }, { ".au", "audio/basic" }, { ".avi", "video/x-msvideo" }, { ".axs", "application/olescript" }, { ".bas", "text/plain" }, { ".bcpio", "application/x-bcpio" }, { ".bin", "application/octet-stream" }, { ".bmp", "image/bmp" }, { ".c", "text/plain" }, { ".cab", "application/octet-stream" }, { ".caf", "audio/x-caf" }, { ".calx", "application/vnd.ms-office.calx" }, { ".cat", "application/vnd.ms-pki.seccat" }, { ".cc", "text/plain" }, { ".cd", "text/plain" }, { ".cdda", "audio/aiff" }, { ".cdf", "application/x-cdf" }, { ".cer", "application/x-x509-ca-cert" }, { ".chm", "application/octet-stream" }, { ".class", "application/x-java-applet" }, { ".clp", "application/x-msclip" }, { ".cmx", "image/x-cmx" }, { ".cnf", "text/plain" }, { ".cod", "image/cis-cod" }, { ".config", "application/xml" }, { ".contact", "text/x-ms-contact" }, { ".coverage", "application/xml" }, { ".cpio", "application/x-cpio" }, { ".cpp", "text/plain" }, { ".crd", "application/x-mscardfile" }, { ".crl", "application/pkix-crl" }, { ".crt", "application/x-x509-ca-cert" }, { ".cs", "text/plain" }, { ".csdproj", "text/plain" }, { ".csh", "application/x-csh" }, { ".csproj", "text/plain" }, { ".css", "text/css" }, { ".csv", "text/csv" }, { ".cur", "application/octet-stream" }, { ".cxx", "text/plain" }, { ".dat", "application/octet-stream" }, { ".datasource", "application/xml" }, { ".dbproj", "text/plain" }, { ".dcr", "application/x-director" }, { ".def", "text/plain" }, { ".deploy", "application/octet-stream" }, { ".der", "application/x-x509-ca-cert" }, { ".dgml", "application/xml" }, { ".dib", "image/bmp" }, { ".dif", "video/x-dv" }, { ".dir", "application/x-director" }, { ".disco", "text/xml" }, { ".dll", "application/x-msdownload" }, { ".dll.config", "text/xml" }, { ".dlm", "text/dlm" }, { ".doc", "application/msword" }, { ".docm", "application/vnd.ms-word.document.macroEnabled.12" }, { ".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }, { ".dot", "application/msword" }, { ".dotm", "application/vnd.ms-word.template.macroEnabled.12" }, { ".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template" }, { ".dsp", "application/octet-stream" }, { ".dsw", "text/plain" }, { ".dtd", "text/xml" }, { ".dtsConfig", "text/xml" }, { ".dv", "video/x-dv" }, { ".dvi", "application/x-dvi" }, { ".dwf", "drawing/x-dwf" }, { ".dwp", "application/octet-stream" }, { ".dxr", "application/x-director" }, { ".eml", "message/rfc822" }, { ".emz", "application/octet-stream" }, { ".eot", "application/octet-stream" }, { ".eps", "application/postscript" }, { ".etl", "application/etl" }, { ".etx", "text/x-setext" }, { ".evy", "application/envoy" }, { ".exe", "application/octet-stream" }, { ".exe.config", "text/xml" }, { ".fdf", "application/vnd.fdf" }, { ".fif", "application/fractals" }, { ".filters", "Application/xml" }, { ".fla", "application/octet-stream" }, { ".flr", "x-world/x-vrml" }, { ".flv", "video/x-flv" }, { ".fsscript", "application/fsharp-script" }, { ".fsx", "application/fsharp-script" }, { ".generictest", "application/xml" }, { ".gif", "image/gif" }, { ".group", "text/x-ms-group" }, { ".gsm", "audio/x-gsm" }, { ".gtar", "application/x-gtar" }, { ".gz", "application/x-gzip" }, { ".h", "text/plain" }, { ".hdf", "application/x-hdf" }, { ".hdml", "text/x-hdml" }, { ".hhc", "application/x-oleobject" }, { ".hhk", "application/octet-stream" }, { ".hhp", "application/octet-stream" }, { ".hlp", "application/winhlp" }, { ".hpp", "text/plain" }, { ".hqx", "application/mac-binhex40" }, { ".hta", "application/hta" }, { ".htc", "text/x-component" }, { ".htm", "text/html" }, { ".html", "text/html" }, { ".htt", "text/webviewhtml" }, { ".hxa", "application/xml" }, { ".hxc", "application/xml" }, { ".hxd", "application/octet-stream" }, { ".hxe", "application/xml" }, { ".hxf", "application/xml" }, { ".hxh", "application/octet-stream" }, { ".hxi", "application/octet-stream" }, { ".hxk", "application/xml" }, { ".hxq", "application/octet-stream" }, { ".hxr", "application/octet-stream" }, { ".hxs", "application/octet-stream" }, { ".hxt", "text/html" }, { ".hxv", "application/xml" }, { ".hxw", "application/octet-stream" }, { ".hxx", "text/plain" }, { ".i", "text/plain" }, { ".ico", "image/x-icon" }, { ".ics", "application/octet-stream" }, { ".idl", "text/plain" }, { ".ief", "image/ief" }, { ".iii", "application/x-iphone" }, { ".inc", "text/plain" }, { ".inf", "application/octet-stream" }, { ".inl", "text/plain" }, { ".ins", "application/x-internet-signup" }, { ".ipa", "application/x-itunes-ipa" }, { ".ipg", "application/x-itunes-ipg" }, { ".ipproj", "text/plain" }, { ".ipsw", "application/x-itunes-ipsw" }, { ".iqy", "text/x-ms-iqy" }, { ".isp", "application/x-internet-signup" }, { ".ite", "application/x-itunes-ite" }, { ".itlp", "application/x-itunes-itlp" }, { ".itms", "application/x-itunes-itms" }, { ".itpc", "application/x-itunes-itpc" }, { ".IVF", "video/x-ivf" }, { ".jar", "application/java-archive" }, { ".java", "application/octet-stream" }, { ".jck", "application/liquidmotion" }, { ".jcz", "application/liquidmotion" }, { ".jfif", "image/pjpeg" }, { ".jnlp", "application/x-java-jnlp-file" }, { ".jpb", "application/octet-stream" }, { ".jpe", "image/jpeg" }, { ".jpeg", "image/jpeg" }, { ".jpg", "image/jpeg" }, { ".js", "application/x-javascript" }, { ".jsx", "text/jscript" }, { ".jsxbin", "text/plain" }, { ".latex", "application/x-latex" }, { ".library-ms", "application/windows-library+xml" }, { ".lit", "application/x-ms-reader" }, { ".loadtest", "application/xml" }, { ".lpk", "application/octet-stream" }, { ".lsf", "video/x-la-asf" }, { ".lst", "text/plain" }, { ".lsx", "video/x-la-asf" }, { ".lzh", "application/octet-stream" }, { ".m13", "application/x-msmediaview" }, { ".m14", "application/x-msmediaview" }, { ".m1v", "video/mpeg" }, { ".m2t", "video/vnd.dlna.mpeg-tts" }, { ".m2ts", "video/vnd.dlna.mpeg-tts" }, { ".m2v", "video/mpeg" }, { ".m3u", "audio/x-mpegurl" }, { ".m3u8", "audio/x-mpegurl" }, { ".m4a", "audio/m4a" }, { ".m4b", "audio/m4b" }, { ".m4p", "audio/m4p" }, { ".m4r", "audio/x-m4r" }, { ".m4v", "video/x-m4v" }, { ".mac", "image/x-macpaint" }, { ".mak", "text/plain" }, { ".man", "application/x-troff-man" }, { ".manifest", "application/x-ms-manifest" }, { ".map", "text/plain" }, { ".master", "application/xml" }, { ".mda", "application/msaccess" }, { ".mdb", "application/x-msaccess" }, { ".mde", "application/msaccess" }, { ".mdp", "application/octet-stream" }, { ".me", "application/x-troff-me" }, { ".mfp", "application/x-shockwave-flash" }, { ".mht", "message/rfc822" }, { ".mhtml", "message/rfc822" }, { ".mid", "audio/mid" }, { ".midi", "audio/mid" }, { ".mix", "application/octet-stream" }, { ".mk", "text/plain" }, { ".mmf", "application/x-smaf" }, { ".mno", "text/xml" }, { ".mny", "application/x-msmoney" }, { ".mod", "video/mpeg" }, { ".mov", "video/quicktime" }, { ".movie", "video/x-sgi-movie" }, { ".mp2", "video/mpeg" }, { ".mp2v", "video/mpeg" }, { ".mp3", "audio/mpeg" }, { ".mp4", "video/mp4" }, { ".mp4v", "video/mp4" }, { ".mpa", "video/mpeg" }, { ".mpe", "video/mpeg" }, { ".mpeg", "video/mpeg" }, { ".mpf", "application/vnd.ms-mediapackage" }, { ".mpg", "video/mpeg" }, { ".mpp", "application/vnd.ms-project" }, { ".mpv2", "video/mpeg" }, { ".mqv", "video/quicktime" }, { ".ms", "application/x-troff-ms" }, { ".msi", "application/octet-stream" }, { ".mso", "application/octet-stream" }, { ".mts", "video/vnd.dlna.mpeg-tts" }, { ".mtx", "application/xml" }, { ".mvb", "application/x-msmediaview" }, { ".mvc", "application/x-miva-compiled" }, { ".mxp", "application/x-mmxp" }, { ".nc", "application/x-netcdf" }, { ".nsc", "video/x-ms-asf" }, { ".nws", "message/rfc822" }, { ".ocx", "application/octet-stream" }, { ".oda", "application/oda" }, { ".odc", "text/x-ms-odc" }, { ".odh", "text/plain" }, { ".odl", "text/plain" }, { ".odp", "application/vnd.oasis.opendocument.presentation" }, { ".ods", "application/oleobject" }, { ".odt", "application/vnd.oasis.opendocument.text" }, { ".one", "application/onenote" }, { ".onea", "application/onenote" }, { ".onepkg", "application/onenote" }, { ".onetmp", "application/onenote" }, { ".onetoc", "application/onenote" }, { ".onetoc2", "application/onenote" }, { ".orderedtest", "application/xml" }, { ".osdx", "application/opensearchdescription+xml" }, { ".p10", "application/pkcs10" }, { ".p12", "application/x-pkcs12" }, { ".p7b", "application/x-pkcs7-certificates" }, { ".p7c", "application/pkcs7-mime" }, { ".p7m", "application/pkcs7-mime" }, { ".p7r", "application/x-pkcs7-certreqresp" }, { ".p7s", "application/pkcs7-signature" }, { ".pbm", "image/x-portable-bitmap" }, { ".pcast", "application/x-podcast" }, { ".pct", "image/pict" }, { ".pcx", "application/octet-stream" }, { ".pcz", "application/octet-stream" }, { ".pdf", "application/pdf" }, { ".pfb", "application/octet-stream" }, { ".pfm", "application/octet-stream" }, { ".pfx", "application/x-pkcs12" }, { ".pgm", "image/x-portable-graymap" }, { ".pic", "image/pict" }, { ".pict", "image/pict" }, { ".pkgdef", "text/plain" }, { ".pkgundef", "text/plain" }, { ".pko", "application/vnd.ms-pki.pko" }, { ".pls", "audio/scpls" }, { ".pma", "application/x-perfmon" }, { ".pmc", "application/x-perfmon" }, { ".pml", "application/x-perfmon" }, { ".pmr", "application/x-perfmon" }, { ".pmw", "application/x-perfmon" }, { ".png", "image/png" }, { ".pnm", "image/x-portable-anymap" }, { ".pnt", "image/x-macpaint" }, { ".pntg", "image/x-macpaint" }, { ".pnz", "image/png" }, { ".pot", "application/vnd.ms-powerpoint" }, { ".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12" }, { ".potx", "application/vnd.openxmlformats-officedocument.presentationml.template" }, { ".ppa", "application/vnd.ms-powerpoint" }, { ".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12" }, { ".ppm", "image/x-portable-pixmap" }, { ".pps", "application/vnd.ms-powerpoint" }, { ".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12" }, { ".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow" }, { ".ppt", "application/vnd.ms-powerpoint" }, { ".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12" }, { ".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation" }, { ".prf", "application/pics-rules" }, { ".prm", "application/octet-stream" }, { ".prx", "application/octet-stream" }, { ".ps", "application/postscript" }, { ".psc1", "application/PowerShell" }, { ".psd", "application/octet-stream" }, { ".psess", "application/xml" }, { ".psm", "application/octet-stream" }, { ".psp", "application/octet-stream" }, { ".pub", "application/x-mspublisher" }, { ".pwz", "application/vnd.ms-powerpoint" }, { ".qht", "text/x-html-insertion" }, { ".qhtm", "text/x-html-insertion" }, { ".qt", "video/quicktime" }, { ".qti", "image/x-quicktime" }, { ".qtif", "image/x-quicktime" }, { ".qtl", "application/x-quicktimeplayer" }, { ".qxd", "application/octet-stream" }, { ".ra", "audio/x-pn-realaudio" }, { ".ram", "audio/x-pn-realaudio" }, { ".rar", "application/octet-stream" }, { ".ras", "image/x-cmu-raster" }, { ".rat", "application/rat-file" }, { ".rc", "text/plain" }, { ".rc2", "text/plain" }, { ".rct", "text/plain" }, { ".rdlc", "application/xml" }, { ".resx", "application/xml" }, { ".rf", "image/vnd.rn-realflash" }, { ".rgb", "image/x-rgb" }, { ".rgs", "text/plain" }, { ".rm", "application/vnd.rn-realmedia" }, { ".rmi", "audio/mid" }, { ".rmp", "application/vnd.rn-rn_music_package" }, { ".roff", "application/x-troff" }, { ".rpm", "audio/x-pn-realaudio-plugin" }, { ".rqy", "text/x-ms-rqy" }, { ".rtf", "application/rtf" }, { ".rtx", "text/richtext" }, { ".ruleset", "application/xml" }, { ".s", "text/plain" }, { ".safariextz", "application/x-safari-safariextz" }, { ".scd", "application/x-msschedule" }, { ".sct", "text/scriptlet" }, { ".sd2", "audio/x-sd2" }, { ".sdp", "application/sdp" }, { ".sea", "application/octet-stream" }, { ".searchConnector-ms", "application/windows-search-connector+xml" }, { ".setpay", "application/set-payment-initiation" }, { ".setreg", "application/set-registration-initiation" }, { ".settings", "application/xml" }, { ".sgimb", "application/x-sgimb" }, { ".sgml", "text/sgml" }, { ".sh", "application/x-sh" }, { ".shar", "application/x-shar" }, { ".shtml", "text/html" }, { ".sit", "application/x-stuffit" }, { ".sitemap", "application/xml" }, { ".skin", "application/xml" }, { ".sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12" }, { ".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide" }, { ".slk", "application/vnd.ms-excel" }, { ".sln", "text/plain" }, { ".slupkg-ms", "application/x-ms-license" }, { ".smd", "audio/x-smd" }, { ".smi", "application/octet-stream" }, { ".smx", "audio/x-smd" }, { ".smz", "audio/x-smd" }, { ".snd", "audio/basic" }, { ".snippet", "application/xml" }, { ".snp", "application/octet-stream" }, { ".sol", "text/plain" }, { ".sor", "text/plain" }, { ".spc", "application/x-pkcs7-certificates" }, { ".spl", "application/futuresplash" }, { ".src", "application/x-wais-source" }, { ".srf", "text/plain" }, { ".SSISDeploymentManifest", "text/xml" }, { ".ssm", "application/streamingmedia" }, { ".sst", "application/vnd.ms-pki.certstore" }, { ".stl", "application/vnd.ms-pki.stl" }, { ".sv4cpio", "application/x-sv4cpio" }, { ".sv4crc", "application/x-sv4crc" }, { ".svc", "application/xml" }, { ".swf", "application/x-shockwave-flash" }, { ".t", "application/x-troff" }, { ".tar", "application/x-tar" }, { ".tcl", "application/x-tcl" }, { ".testrunconfig", "application/xml" }, { ".testsettings", "application/xml" }, { ".tex", "application/x-tex" }, { ".texi", "application/x-texinfo" }, { ".texinfo", "application/x-texinfo" }, { ".tgz", "application/x-compressed" }, { ".thmx", "application/vnd.ms-officetheme" }, { ".thn", "application/octet-stream" }, { ".tif", "image/tiff" }, { ".tiff", "image/tiff" }, { ".tlh", "text/plain" }, { ".tli", "text/plain" }, { ".toc", "application/octet-stream" }, { ".tr", "application/x-troff" }, { ".trm", "application/x-msterminal" }, { ".trx", "application/xml" }, { ".ts", "video/vnd.dlna.mpeg-tts" }, { ".tsv", "text/tab-separated-values" }, { ".ttf", "application/octet-stream" }, { ".tts", "video/vnd.dlna.mpeg-tts" }, { ".txt", "text/plain" }, { ".u32", "application/octet-stream" }, { ".uls", "text/iuls" }, { ".user", "text/plain" }, { ".ustar", "application/x-ustar" }, { ".vb", "text/plain" }, { ".vbdproj", "text/plain" }, { ".vbk", "video/mpeg" }, { ".vbproj", "text/plain" }, { ".vbs", "text/vbscript" }, { ".vcf", "text/x-vcard" }, { ".vcproj", "Application/xml" }, { ".vcs", "text/plain" }, { ".vcxproj", "Application/xml" }, { ".vddproj", "text/plain" }, { ".vdp", "text/plain" }, { ".vdproj", "text/plain" }, { ".vdx", "application/vnd.ms-visio.viewer" }, { ".vml", "text/xml" }, { ".vscontent", "application/xml" }, { ".vsct", "text/xml" }, { ".vsd", "application/vnd.visio" }, { ".vsi", "application/ms-vsi" }, { ".vsix", "application/vsix" }, { ".vsixlangpack", "text/xml" }, { ".vsixmanifest", "text/xml" }, { ".vsmdi", "application/xml" }, { ".vspscc", "text/plain" }, { ".vss", "application/vnd.visio" }, { ".vsscc", "text/plain" }, { ".vssettings", "text/xml" }, { ".vssscc", "text/plain" }, { ".vst", "application/vnd.visio" }, { ".vstemplate", "text/xml" }, { ".vsto", "application/x-ms-vsto" }, { ".vsw", "application/vnd.visio" }, { ".vsx", "application/vnd.visio" }, { ".vtx", "application/vnd.visio" }, { ".wav", "audio/wav" }, { ".wave", "audio/wav" }, { ".wax", "audio/x-ms-wax" }, { ".wbk", "application/msword" }, { ".wbmp", "image/vnd.wap.wbmp" }, { ".wcm", "application/vnd.ms-works" }, { ".wdb", "application/vnd.ms-works" }, { ".wdp", "image/vnd.ms-photo" }, { ".webarchive", "application/x-safari-webarchive" }, { ".webp", "image/webp" }, { ".webtest", "application/xml" }, { ".wiq", "application/xml" }, { ".wiz", "application/msword" }, { ".wks", "application/vnd.ms-works" }, { ".WLMP", "application/wlmoviemaker" }, { ".wlpginstall", "application/x-wlpg-detect" }, { ".wlpginstall3", "application/x-wlpg3-detect" }, { ".wm", "video/x-ms-wm" }, { ".wma", "audio/x-ms-wma" }, { ".wmd", "application/x-ms-wmd" }, { ".wmf", "application/x-msmetafile" }, { ".wml", "text/vnd.wap.wml" }, { ".wmlc", "application/vnd.wap.wmlc" }, { ".wmls", "text/vnd.wap.wmlscript" }, { ".wmlsc", "application/vnd.wap.wmlscriptc" }, { ".wmp", "video/x-ms-wmp" }, { ".wmv", "video/x-ms-wmv" }, { ".wmx", "video/x-ms-wmx" }, { ".wmz", "application/x-ms-wmz" }, { ".wpl", "application/vnd.ms-wpl" }, { ".wps", "application/vnd.ms-works" }, { ".wri", "application/x-mswrite" }, { ".wrl", "x-world/x-vrml" }, { ".wrz", "x-world/x-vrml" }, { ".wsc", "text/scriptlet" }, { ".wsdl", "text/xml" }, { ".wvx", "video/x-ms-wvx" }, { ".x", "application/directx" }, { ".xaf", "x-world/x-vrml" }, { ".xaml", "application/xaml+xml" }, { ".xap", "application/x-silverlight-app" }, { ".xbap", "application/x-ms-xbap" }, { ".xbm", "image/x-xbitmap" }, { ".xdr", "text/plain" }, { ".xht", "application/xhtml+xml" }, { ".xhtml", "application/xhtml+xml" }, { ".xla", "application/vnd.ms-excel" }, { ".xlam", "application/vnd.ms-excel.addin.macroEnabled.12" }, { ".xlc", "application/vnd.ms-excel" }, { ".xld", "application/vnd.ms-excel" }, { ".xlk", "application/vnd.ms-excel" }, { ".xll", "application/vnd.ms-excel" }, { ".xlm", "application/vnd.ms-excel" }, { ".xls", "application/vnd.ms-excel" }, { ".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12" }, { ".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12" }, { ".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }, { ".xlt", "application/vnd.ms-excel" }, { ".xltm", "application/vnd.ms-excel.template.macroEnabled.12" }, { ".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template" }, { ".xlw", "application/vnd.ms-excel" }, { ".xml", "text/xml" }, { ".xmta", "application/xml" }, { ".xof", "x-world/x-vrml" }, { ".XOML", "text/plain" }, { ".xpm", "image/x-xpixmap" }, { ".xps", "application/vnd.ms-xpsdocument" }, { ".xrm-ms", "text/xml" }, { ".xsc", "application/xml" }, { ".xsd", "text/xml" }, { ".xsf", "text/xml" }, { ".xsl", "text/xml" }, { ".xslt", "text/xml" }, { ".xsn", "application/octet-stream" }, { ".xss", "application/xml" }, { ".xtp", "application/octet-stream" }, { ".xwd", "image/x-xwindowdump" }, { ".z", "application/x-compress" }, { ".zip", "application/x-zip-compressed" } }; public static string GetMimeType(string filename) { if (string.IsNullOrEmpty(filename)) { throw new ArgumentNullException("filename"); } string text = Path.GetExtension(filename); if (!text.StartsWith(".")) { text = "." + text; } if (!_mappings.TryGetValue(text, out var value)) { return "application/octet-stream"; } return value; } } internal static class BucketHelper { public const int BucketCount = 17; private static readonly int[] _bucketSize; static BucketHelper() { _bucketSize = new int[17]; for (int i = 0; i < 17; i++) { _bucketSize[i] = GetMaxSizeForBucket(i); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int GetBucketIndex(int bufferSize) { for (int i = 0; i < _bucketSize.Length; i++) { if (_bucketSize[i] >= bufferSize) { return i; } } return -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int GetMaxSizeForBucket(int binIndex) { return 16 << binIndex; } } internal static class Randomizer { private static readonly Random _random = new Random(0); public static int Next() { lock (_random) { return _random.Next(); } } public static int Next(int minValue, int maxValue) { lock (_random) { return _random.Next(minValue, maxValue); } } } internal struct Result where T : class { public T Value; public Exception Exception; public bool Ok => Exception == null; public bool Fail => Exception != null; public T GetValue() { if (!Ok) { throw Exception; } return Value; } public Result(T value, Exception ex = null) { Value = value; Exception = ex; } public static implicit operator T(Result value) { return value.Value; } public static implicit operator Result(T value) { return new Result(value); } } internal enum TokenType { OpenBrace, CloseBrace, OpenBracket, CloseBracket, OpenParenthesis, CloseParenthesis, Comma, Colon, SemiColon, At, Hashtag, Til, Period, Ampersand, Dollar, Exclamation, NotEquals, Equals, Greater, GreaterOrEquals, Less, LessOrEquals, Minus, Plus, Asterisk, Slash, Backslash, Percent, String, Int, Double, Whitespace, Word, EOF, Unknown } internal class Token { private static readonly HashSet _keywords = new HashSet(StringComparer.OrdinalIgnoreCase) { "BETWEEN", "LIKE", "IN", "AND", "OR" }; public TokenType Type { get; private set; } public string Value { get; private set; } public long Position { get; private set; } public bool IsOperand { get { switch (Type) { case TokenType.NotEquals: case TokenType.Equals: case TokenType.Greater: case TokenType.GreaterOrEquals: case TokenType.Less: case TokenType.LessOrEquals: case TokenType.Minus: case TokenType.Plus: case TokenType.Asterisk: case TokenType.Slash: case TokenType.Percent: return true; case TokenType.Word: return _keywords.Contains(Value); default: return false; } } } public Token(TokenType tokenType, string value, long position) { Position = position; Value = value; Type = tokenType; } public Token Expect(TokenType type) { if (Type != type) { throw LiteException.UnexpectedToken(this); } return this; } public Token Expect(TokenType type1, TokenType type2) { if (Type != type1 && Type != type2) { throw LiteException.UnexpectedToken(this); } return this; } public Token Expect(TokenType type1, TokenType type2, TokenType type3) { if (Type != type1 && Type != type2 && Type != type3) { throw LiteException.UnexpectedToken(this); } return this; } public Token Expect(string value, bool ignoreCase = true) { if (!Is(value, ignoreCase)) { throw LiteException.UnexpectedToken(this, value); } return this; } public bool Is(string value, bool ignoreCase = true) { if (Type == TokenType.Word) { return value.Equals(Value, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); } return false; } public override string ToString() { return Value + " (" + Type.ToString() + ")"; } } internal class Tokenizer { private readonly TextReader _reader; private char _char; private Token _ahead; private bool _eof; public bool EOF { get { if (_eof) { return _ahead == null; } return false; } } public long Position { get; private set; } public Token Current { get; private set; } public bool CheckEOF() { if (_eof) { throw LiteException.UnexpectedToken(Current); } return false; } public Tokenizer(string source) : this(new StringReader(source)) { } public Tokenizer(TextReader reader) { _reader = reader; Position = 0L; ReadChar(); } public static bool IsWordChar(char c, bool first) { if (first) { if (!char.IsLetter(c) && c != '_') { return c == '$'; } return true; } if (!char.IsLetterOrDigit(c) && c != '_') { return c == '$'; } return true; } private char ReadChar() { if (_eof) { return '\0'; } int num = _reader.Read(); Position++; if (num == -1) { _char = '\0'; _eof = true; } else { _char = (char)num; } return _char; } public Token LookAhead(bool eatWhitespace = true) { if (_ahead != null) { if (eatWhitespace && _ahead.Type == TokenType.Whitespace) { _ahead = ReadNext(eatWhitespace); } return _ahead; } return _ahead = ReadNext(eatWhitespace); } public Token ReadToken(bool eatWhitespace = true) { if (_ahead == null) { return Current = ReadNext(eatWhitespace); } if (eatWhitespace && _ahead.Type == TokenType.Whitespace) { _ahead = ReadNext(eatWhitespace); } Current = _ahead; _ahead = null; return Current; } private Token ReadNext(bool eatWhitespace) { if (eatWhitespace) { EatWhitespace(); } if (_eof) { return new Token(TokenType.EOF, null, Position); } Token token = null; switch (_char) { case '{': token = new Token(TokenType.OpenBrace, "{", Position); ReadChar(); break; case '}': token = new Token(TokenType.CloseBrace, "}", Position); ReadChar(); break; case '[': token = new Token(TokenType.OpenBracket, "[", Position); ReadChar(); break; case ']': token = new Token(TokenType.CloseBracket, "]", Position); ReadChar(); break; case '(': token = new Token(TokenType.OpenParenthesis, "(", Position); ReadChar(); break; case ')': token = new Token(TokenType.CloseParenthesis, ")", Position); ReadChar(); break; case ',': token = new Token(TokenType.Comma, ",", Position); ReadChar(); break; case ':': token = new Token(TokenType.Colon, ":", Position); ReadChar(); break; case ';': token = new Token(TokenType.SemiColon, ";", Position); ReadChar(); break; case '@': token = new Token(TokenType.At, "@", Position); ReadChar(); break; case '#': token = new Token(TokenType.Hashtag, "#", Position); ReadChar(); break; case '~': token = new Token(TokenType.Til, "~", Position); ReadChar(); break; case '.': token = new Token(TokenType.Period, ".", Position); ReadChar(); break; case '&': token = new Token(TokenType.Ampersand, "&", Position); ReadChar(); break; case '$': ReadChar(); token = ((!IsWordChar(_char, first: true)) ? new Token(TokenType.Dollar, "$", Position) : new Token(TokenType.Word, "$" + ReadWord(), Position)); break; case '!': ReadChar(); if (_char == '=') { token = new Token(TokenType.NotEquals, "!=", Position); ReadChar(); } else { token = new Token(TokenType.Exclamation, "!", Position); } break; case '=': token = new Token(TokenType.Equals, "=", Position); ReadChar(); break; case '>': ReadChar(); if (_char == '=') { token = new Token(TokenType.GreaterOrEquals, ">=", Position); ReadChar(); } else { token = new Token(TokenType.Greater, ">", Position); } break; case '<': ReadChar(); if (_char == '=') { token = new Token(TokenType.LessOrEquals, "<=", Position); ReadChar(); } else { token = new Token(TokenType.Less, "<", Position); } break; case '-': ReadChar(); if (_char == '-') { ReadLine(); token = ReadNext(eatWhitespace); } else { token = new Token(TokenType.Minus, "-", Position); } break; case '+': token = new Token(TokenType.Plus, "+", Position); ReadChar(); break; case '*': token = new Token(TokenType.Asterisk, "*", Position); ReadChar(); break; case '/': token = new Token(TokenType.Slash, "/", Position); ReadChar(); break; case '\\': token = new Token(TokenType.Backslash, "\\", Position); ReadChar(); break; case '%': token = new Token(TokenType.Percent, "%", Position); ReadChar(); break; case '"': case '\'': token = new Token(TokenType.String, ReadString(_char), Position); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { bool dbl = false; string value = ReadNumber(ref dbl); token = new Token(dbl ? TokenType.Double : TokenType.Int, value, Position); break; } case '\t': case '\n': case '\r': case ' ': { StringBuilder stringBuilder = new StringBuilder(); while (char.IsWhiteSpace(_char) && !_eof) { stringBuilder.Append(_char); ReadChar(); } token = new Token(TokenType.Whitespace, stringBuilder.ToString(), Position); break; } default: if (IsWordChar(_char, first: true)) { token = new Token(TokenType.Word, ReadWord(), Position); } else { ReadChar(); } break; } return token ?? new Token(TokenType.Unknown, _char.ToString(), Position); } private void EatWhitespace() { while (char.IsWhiteSpace(_char) && !_eof) { ReadChar(); } } private string ReadWord() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(_char); ReadChar(); while (!_eof && IsWordChar(_char, first: false)) { stringBuilder.Append(_char); ReadChar(); } return stringBuilder.ToString(); } private string ReadNumber(ref bool dbl) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(_char); bool flag = true; bool flag2 = true; bool flag3 = false; ReadChar(); while (!_eof && (char.IsDigit(_char) || _char == '+' || _char == '-' || _char == '.' || _char == 'e' || _char == 'E')) { if (_char == '.') { if (!flag) { break; } dbl = true; flag = false; } else if (_char == 'e' || _char == 'E') { if (!flag2) { break; } flag2 = false; flag3 = true; dbl = true; } else if (_char == '-' || _char == '+') { if (!flag3) { break; } flag3 = false; } stringBuilder.Append(_char); ReadChar(); } return stringBuilder.ToString(); } private string ReadString(char quote) { StringBuilder stringBuilder = new StringBuilder(); ReadChar(); while (_char != quote && !_eof) { if (_char == '\\') { ReadChar(); if (_char == quote) { stringBuilder.Append(quote); } switch (_char) { case '\\': stringBuilder.Append('\\'); break; case '/': stringBuilder.Append('/'); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; case 't': stringBuilder.Append('\t'); break; case 'u': { uint num = ParseUnicode(ReadChar(), ReadChar(), ReadChar(), ReadChar()); stringBuilder.Append((char)num); break; } } } else { stringBuilder.Append(_char); } ReadChar(); } ReadChar(); return stringBuilder.ToString(); } private void ReadLine() { while (_char != '\n' && !_eof) { ReadChar(); } if (_char == '\n') { ReadChar(); } } public static uint ParseUnicode(char c1, char c2, char c3, char c4) { uint num = ParseSingleChar(c1, 4096u); uint num2 = ParseSingleChar(c2, 256u); uint num3 = ParseSingleChar(c3, 16u); uint num4 = ParseSingleChar(c4, 1u); return num + num2 + num3 + num4; } public static uint ParseSingleChar(char c1, uint multiplier) { uint result = 0u; if (c1 >= '0' && c1 <= '9') { result = (uint)(c1 - 48) * multiplier; } else if (c1 >= 'A' && c1 <= 'F') { result = (uint)(c1 - 65 + 10) * multiplier; } else if (c1 >= 'a' && c1 <= 'f') { result = (uint)(c1 - 97 + 10) * multiplier; } return result; } public override string ToString() { return Current?.ToString() + " [ahead: " + _ahead?.ToString() + "] - position: " + Position; } } } namespace LiteDB.Utils { internal class TryCatch { public readonly List Exceptions = new List(); public bool InvalidDatafileState => Exceptions.Any((Exception ex) => ex is LiteException ex2 && ex2.ErrorCode == 999); public TryCatch() { } public TryCatch(Exception initial) { Exceptions.Add(initial); } [DebuggerHidden] public void Catch(Action action) { try { action(); } catch (Exception item) { Exceptions.Add(item); } } } } namespace LiteDB.Utils.Extensions { internal static class EnumerableExtensions { public static IEnumerable OnDispose(this IEnumerable source, Action onDispose) { try { foreach (T item in source) { yield return item; } } finally { onDispose(); } } } internal static class StopWatchExtensions { private class DisposableAction : IDisposable { private readonly Action _action; public DisposableAction(Action action) { _action = action; } public void Dispose() { _action(); } } public static IDisposable StartDisposable(this Stopwatch stopwatch) { stopwatch.Start(); return new DisposableAction(stopwatch.Stop); } } } namespace LiteDB.Engine { internal class DiskReader : IDisposable { private readonly EngineState _state; private readonly MemoryCache _cache; private readonly StreamPool _dataPool; private readonly StreamPool _logPool; private readonly Lazy _dataStream; private readonly Lazy _logStream; public DiskReader(EngineState state, MemoryCache cache, StreamPool dataPool, StreamPool logPool) { _state = state; _cache = cache; _dataPool = dataPool; _logPool = logPool; _dataStream = new Lazy(() => _dataPool.Rent()); _logStream = new Lazy(() => _logPool.Rent()); } public PageBuffer ReadPage(long position, bool writable, FileOrigin origin) { Constants.ENSURE(position % 8192 == 0, "invalid page position"); Stream stream = ((origin == FileOrigin.Data) ? _dataStream.Value : _logStream.Value); if (!writable) { return _cache.GetReadablePage(position, origin, delegate(long pos, BufferSlice buf) { ReadStream(stream, pos, buf); }); } return _cache.GetWritablePage(position, origin, delegate(long pos, BufferSlice buf) { ReadStream(stream, pos, buf); }); } private void ReadStream(Stream stream, long position, BufferSlice buffer) { stream.Position = position; stream.Read(buffer.Array, buffer.Offset, buffer.Count); } public PageBuffer NewPage() { return _cache.NewPage(); } public void Dispose() { if (_dataStream.IsValueCreated) { _dataPool.Return(_dataStream.Value); } if (_logStream.IsValueCreated) { _logPool.Return(_logStream.Value); } } } internal class DiskService : IDisposable { private readonly MemoryCache _cache; private readonly EngineState _state; private IStreamFactory _dataFactory; private readonly IStreamFactory _logFactory; private StreamPool _dataPool; private readonly StreamPool _logPool; private readonly Lazy _writer; private long _dataLength; private long _logLength; private static readonly ArrayPool _bufferPool = ArrayPool.Shared; public MemoryCache Cache => _cache; public uint MAX_ITEMS_COUNT => (uint)((int)((_dataLength + _logLength) / 8192 + 10) * 255); public DiskService(EngineSettings settings, EngineState state, int[] memorySegmentSizes) { _cache = new MemoryCache(memorySegmentSizes); _state = state; _dataFactory = settings.CreateDataFactory(); _logFactory = settings.CreateLogFactory(); _dataPool = new StreamPool(_dataFactory, appendOnly: false); _logPool = new StreamPool(_logFactory, appendOnly: true); _writer = _logPool.Writer; if (_dataFactory.GetLength() == 0) { Initialize(_dataPool.Writer.Value, settings.Collation, settings.InitialSize); } if (!settings.ReadOnly) { _ = _dataPool.Writer.Value.CanRead; } _dataLength = _dataFactory.GetLength() - 8192; if (_logFactory.Exists()) { _logLength = _logFactory.GetLength() - 8192; } else { _logLength = -8192L; } } private void Initialize(Stream stream, Collation collation, long initialSize) { PageBuffer pageBuffer = new PageBuffer(new byte[8192], 0, 0); HeaderPage headerPage = new HeaderPage(pageBuffer, 0u); headerPage.Pragmas.Set("COLLATION", (collation ?? Collation.Default).ToString(), validate: false); headerPage.UpdateBuffer(); stream.Write(pageBuffer.Array, pageBuffer.Offset, 8192); if (initialSize > 0) { if (stream is AesStream) { throw LiteException.InitialSizeCryptoNotSupported(); } if (initialSize % 8192 != 0L) { throw LiteException.InvalidInitialSize(); } stream.SetLength(initialSize); } stream.FlushToDisk(); } public DiskReader GetReader() { return new DiskReader(_state, _cache, _dataPool, _logPool); } public void DiscardDirtyPages(IEnumerable pages) { foreach (PageBuffer page in pages) { _cache.DiscardPage(page); } } public void DiscardCleanPages(IEnumerable pages) { foreach (PageBuffer page in pages) { if (!_cache.TryMoveToReadable(page)) { _cache.DiscardPage(page); } } } public PageBuffer NewPage() { return _cache.NewPage(); } public int WriteLogDisk(IEnumerable pages) { int num = 0; Stream value = _writer.Value; lock (value) { foreach (PageBuffer page in pages) { Constants.ENSURE(page.ShareCounter == Constants.BUFFER_WRITABLE, "to enqueue page, page must be writable"); page.Position = Interlocked.Add(ref _logLength, 8192L); page.Origin = FileOrigin.Log; _cache.MoveToReadable(page); value.Position = page.Position; value.Write(page.Array, page.Offset, 8192); page.Release(); num++; } return num; } } public long GetFileLength(FileOrigin origin) { if (origin == FileOrigin.Log) { return _logLength + 8192; } return _dataLength + 8192; } internal void MarkAsInvalidState() { FileHelper.TryExec(60, delegate { using Stream stream = _dataFactory.GetStream(canWrite: true, sequencial: true); byte[] array = _bufferPool.Rent(8192); stream.Read(array, 0, 8192); array[191] = 1; stream.Position = 0L; stream.Write(array, 0, 8192); _bufferPool.Return(array, clearArray: true); }); } public IEnumerable ReadFull(FileOrigin origin) { byte[] buffer = new byte[8192]; StreamPool pool = ((origin == FileOrigin.Log) ? _logPool : _dataPool); Stream stream = pool.Rent(); try { long length = GetFileLength(origin); stream.Position = 0L; while (stream.Position < length) { long position = stream.Position; int num = stream.Read(buffer, 0, 8192); Constants.ENSURE(num == 8192, "ReadFull must read PAGE_SIZE bytes [{0}]", num); yield return new PageBuffer(buffer, 0, 0) { Position = position, Origin = origin, ShareCounter = 0 }; } } finally { pool.Return(stream); } } public void WriteDataDisk(IEnumerable pages) { Stream value = _dataPool.Writer.Value; foreach (PageBuffer page in pages) { Constants.ENSURE(page.ShareCounter == 0, "this page can't be shared to use sync operation - do not use cached pages"); _dataLength = Math.Max(_dataLength, page.Position); value.Position = page.Position; value.Write(page.Array, page.Offset, 8192); } value.FlushToDisk(); } public void SetLength(long length, FileOrigin origin) { Lazy obj = ((origin == FileOrigin.Log) ? _logPool.Writer : _dataPool.Writer); if (origin == FileOrigin.Log) { Interlocked.Exchange(ref _logLength, length - 8192); } else { Interlocked.Exchange(ref _dataLength, length - 8192); } obj.Value.SetLength(length); } public string GetName(FileOrigin origin) { if (origin != FileOrigin.Data) { return _logFactory.Name; } return _dataFactory.Name; } public void Dispose() { bool num = _logFactory.Exists() && _logPool.Writer.Value.Length == 0; _dataPool.Dispose(); _logPool.Dispose(); if (num) { _logFactory.Delete(); } _cache.Dispose(); } } internal class MemoryCache : IDisposable { private readonly ConcurrentQueue _free = new ConcurrentQueue(); private readonly ConcurrentDictionary _readable = new ConcurrentDictionary(); private int _extends; private readonly int[] _segmentSizes; public int PagesInUse => _readable.Values.Where((PageBuffer x) => x.ShareCounter != 0).Count(); public int FreePages => _free.Count; public int ExtendSegments => _extends; public int ExtendPages => (from x in Enumerable.Range(0, _extends) select _segmentSizes[Math.Min(_segmentSizes.Length - 1, x)]).Sum(); public int WritablePages => ExtendPages - _free.Count - _readable.Count; public MemoryCache(int[] memorySegmentSizes) { _segmentSizes = memorySegmentSizes; Extend(); } public PageBuffer GetReadablePage(long position, FileOrigin origin, Action factory) { long readableKey = GetReadableKey(position, origin); PageBuffer orAdd = _readable.GetOrAdd(readableKey, delegate { PageBuffer freePage = GetFreePage(); freePage.Position = position; freePage.Origin = origin; factory(position, freePage); return freePage; }); Interlocked.Exchange(ref orAdd.Timestamp, DateTime.UtcNow.Ticks); Interlocked.Increment(ref orAdd.ShareCounter); return orAdd; } private long GetReadableKey(long position, FileOrigin origin) { Constants.ENSURE(origin != FileOrigin.None, "file origin must be defined"); if (origin == FileOrigin.Data) { return position; } if (position == 0L) { return long.MinValue; } return -position; } public PageBuffer GetWritablePage(long position, FileOrigin origin, Action factory) { long readableKey = GetReadableKey(position, origin); PageBuffer pageBuffer = NewPage(position, origin); if (_readable.TryGetValue(readableKey, out var value)) { Buffer.BlockCopy(value.Array, value.Offset, pageBuffer.Array, pageBuffer.Offset, 8192); } else { factory(position, pageBuffer); } return pageBuffer; } public PageBuffer NewPage() { return NewPage(long.MaxValue, FileOrigin.None); } private PageBuffer NewPage(long position, FileOrigin origin) { PageBuffer freePage = GetFreePage(); freePage.Position = position; freePage.ShareCounter = Constants.BUFFER_WRITABLE; if (freePage.Timestamp > 0) { freePage.Clear(); } freePage.Origin = origin; freePage.Timestamp = DateTime.UtcNow.Ticks; return freePage; } public bool TryMoveToReadable(PageBuffer page) { Constants.ENSURE(page.Position != long.MaxValue, "page must have a position"); Constants.ENSURE(page.ShareCounter == Constants.BUFFER_WRITABLE, "page must be writable"); Constants.ENSURE(page.Origin != FileOrigin.None, "page must have origin defined"); long readableKey = GetReadableKey(page.Position, page.Origin); page.ShareCounter = 0; bool num = _readable.TryAdd(readableKey, page); if (!num) { page.ShareCounter = Constants.BUFFER_WRITABLE; } return num; } public PageBuffer MoveToReadable(PageBuffer page) { Constants.ENSURE(page.Position != long.MaxValue, "page must have position to be readable"); Constants.ENSURE(page.Origin != FileOrigin.None, "page should be a source before move to readable"); Constants.ENSURE(page.ShareCounter == Constants.BUFFER_WRITABLE, "page must be writable before move to readable dict"); long readableKey = GetReadableKey(page.Position, page.Origin); bool added = true; page.ShareCounter = 1; PageBuffer result = _readable.AddOrUpdate(readableKey, page, delegate(long newKey, PageBuffer current) { Constants.ENSURE(current.ShareCounter == 0, "user must ensure this page is not in use when marked as read only"); Constants.ENSURE(current.Origin == page.Origin, "origin must be same"); current.ShareCounter = 1; Buffer.BlockCopy(page.Array, page.Offset, current.Array, current.Offset, 8192); added = false; page.ShareCounter = Constants.BUFFER_WRITABLE; return current; }); if (!added) { DiscardPage(page); } return result; } public void DiscardPage(PageBuffer page) { Constants.ENSURE(page.ShareCounter == Constants.BUFFER_WRITABLE, "discarded page must be writable"); page.ShareCounter = 0; page.Position = long.MaxValue; page.Origin = FileOrigin.None; _free.Enqueue(page); } private PageBuffer GetFreePage() { if (_free.TryDequeue(out var result)) { Constants.ENSURE(result.Position == long.MaxValue, "pages in memory store must have no position defined"); Constants.ENSURE(result.ShareCounter == 0, "pages in memory store must be non-shared"); Constants.ENSURE(result.Origin == FileOrigin.None, "page in memory must have no page origin"); return result; } lock (_free) { if (_free.Count > 0) { return GetFreePage(); } Extend(); } return GetFreePage(); } private void Extend() { int num = _readable.Values.Count((PageBuffer x) => x.ShareCounter == 0); int num2 = _segmentSizes[Math.Min(_segmentSizes.Length - 1, _extends)]; if (num > num2) { long[] array = (from x in _readable where x.Value.ShareCounter == 0 orderby x.Value.Timestamp select x.Key).Take(num2).ToArray(); foreach (long key in array) { Constants.ENSURE(_readable.TryRemove(key, out var value), "page should be in readable list before moving to free list"); if (value.ShareCounter > 0) { if (!_readable.TryAdd(key, value)) { throw new LiteException(0, "MemoryCache: removed in-use memory page. This situation has no way to fix (yet). Throwing exception to avoid database corruption. No other thread can read/write from database now."); } } else { Constants.ENSURE(value.ShareCounter == 0, "page should not be in use by anyone"); value.Position = long.MaxValue; value.Origin = FileOrigin.None; _free.Enqueue(value); } } } else { byte[] buffer = new byte[8192 * num2]; int num3 = ExtendPages + 1; for (int j = 0; j < num2; j++) { _free.Enqueue(new PageBuffer(buffer, j * 8192, num3++)); } _extends++; } } public ICollection GetPages() { return _readable.Values; } public int Clear() { int num = 0; Constants.ENSURE(PagesInUse == 0, "must have no pages in use when call Clear() cache"); foreach (PageBuffer value in _readable.Values) { value.Position = long.MaxValue; value.Origin = FileOrigin.None; _free.Enqueue(value); num++; } _readable.Clear(); return num; } public void Dispose() { } } internal class BufferReader : IDisposable { private readonly IEnumerator _source; private readonly bool _utcDate; private BufferSlice _current; private int _currentPosition; private int _position; private bool _isEOF; private static readonly ArrayPool _bufferPool = ArrayPool.Shared; public int Position => _position; public bool IsEOF => _isEOF; public BufferReader(byte[] buffer, bool utcDate = false) : this(new BufferSlice(buffer, 0, buffer.Length), utcDate) { } public BufferReader(BufferSlice buffer, bool utcDate = false) { _source = null; _utcDate = utcDate; _current = buffer; } public BufferReader(IEnumerable source, bool utcDate = false) { _source = source.GetEnumerator(); _utcDate = utcDate; _source.MoveNext(); _current = _source.Current; } private bool MoveForward(int count) { if (_isEOF) { return false; } Constants.ENSURE(_currentPosition + count <= _current.Count, "forward is only for current segment"); _currentPosition += count; _position += count; if (_currentPosition == _current.Count) { if (_source == null || !_source.MoveNext()) { _isEOF = true; } else { _current = _source.Current; _currentPosition = 0; } return true; } return false; } public int Read(byte[] buffer, int offset, int count) { int num = 0; while (num < count) { int val = _current.Count - _currentPosition; int num2 = Math.Min(count - num, val); if (buffer != null) { Buffer.BlockCopy(_current.Array, _current.Offset + _currentPosition, buffer, offset + num, num2); } num += num2; MoveForward(num2); if (_isEOF) { break; } } Constants.ENSURE(count == num, "current value must fit inside defined buffer"); return num; } public int Skip(int count) { return Read(null, 0, count); } public void Consume() { if (_source != null) { while (_source.MoveNext()) { } } } public string ReadString(int count) { string @string; if (_currentPosition + count <= _current.Count) { @string = StringEncoding.UTF8.GetString(_current.Array, _current.Offset + _currentPosition, count); MoveForward(count); } else { byte[] array = _bufferPool.Rent(count); Read(array, 0, count); @string = StringEncoding.UTF8.GetString(array, 0, count); _bufferPool.Return(array, clearArray: true); } return @string; } public string ReadCString() { if (TryReadCStringCurrentSegment(out var value)) { return value; } using MemoryStream memoryStream = new MemoryStream(); int count = _current.Count - _currentPosition; memoryStream.Write(_current.Array, _current.Offset + _currentPosition, count); MoveForward(count); while (_current[_currentPosition] != 0 && !_isEOF) { memoryStream.WriteByte(_current[_currentPosition]); MoveForward(1); } MoveForward(1); return StringEncoding.UTF8.GetString(memoryStream.ToArray()); } private bool TryReadCStringCurrentSegment(out string value) { int i = _currentPosition; int num = 0; for (; i < _current.Count; i++) { if (_current[i] == 0) { value = StringEncoding.UTF8.GetString(_current.Array, _current.Offset + _currentPosition, num); MoveForward(num + 1); return true; } num++; } value = null; return false; } private T ReadNumber(Func convert, int size) { T result; if (_currentPosition + size <= _current.Count) { result = convert(_current.Array, _current.Offset + _currentPosition); MoveForward(size); } else { byte[] array = _bufferPool.Rent(size); Read(array, 0, size); result = convert(array, 0); _bufferPool.Return(array, clearArray: true); } return result; } public int ReadInt32() { return ReadNumber(BitConverter.ToInt32, 4); } public long ReadInt64() { return ReadNumber(BitConverter.ToInt64, 8); } public uint ReadUInt32() { return ReadNumber(BitConverter.ToUInt32, 4); } public double ReadDouble() { return ReadNumber(BitConverter.ToDouble, 8); } public decimal ReadDecimal() { int num = ReadInt32(); int num2 = ReadInt32(); int num3 = ReadInt32(); int num4 = ReadInt32(); return new decimal(new int[4] { num, num2, num3, num4 }); } public DateTime ReadDateTime() { DateTime result = new DateTime(ReadInt64(), DateTimeKind.Utc); if (!_utcDate) { return result; } return result.ToLocalTime(); } public Guid ReadGuid() { Guid result; if (_currentPosition + 16 <= _current.Count) { result = _current.ReadGuid(_currentPosition); MoveForward(16); } else { result = new Guid(ReadBytes(16)); } return result; } public ObjectId ReadObjectId() { ObjectId result; if (_currentPosition + 12 <= _current.Count) { result = new ObjectId(_current.Array, _current.Offset + _currentPosition); MoveForward(12); } else { byte[] array = _bufferPool.Rent(12); Read(array, 0, 12); result = new ObjectId(array); _bufferPool.Return(array, clearArray: true); } return result; } public bool ReadBoolean() { bool result = _current[_currentPosition] != 0; MoveForward(1); return result; } public byte ReadByte() { byte result = _current[_currentPosition]; MoveForward(1); return result; } internal PageAddress ReadPageAddress() { return new PageAddress(ReadUInt32(), ReadByte()); } public byte[] ReadBytes(int count) { byte[] array = new byte[count]; Read(array, 0, count); return array; } public BsonValue ReadIndexKey() { return (BsonType)ReadByte() switch { BsonType.Null => BsonValue.Null, BsonType.Int32 => ReadInt32(), BsonType.Int64 => ReadInt64(), BsonType.Double => ReadDouble(), BsonType.Decimal => ReadDecimal(), BsonType.String => ReadString(ReadByte()), BsonType.Document => ReadDocument().GetValue(), BsonType.Array => ReadArray().GetValue(), BsonType.Binary => ReadBytes(ReadByte()), BsonType.ObjectId => ReadObjectId(), BsonType.Guid => ReadGuid(), BsonType.Boolean => ReadBoolean(), BsonType.DateTime => ReadDateTime(), BsonType.MinValue => BsonValue.MinValue, BsonType.MaxValue => BsonValue.MaxValue, _ => throw new NotImplementedException(), }; } public Result ReadDocument(HashSet fields = null) { BsonDocument bsonDocument = new BsonDocument(); try { int num = ReadInt32(); int num2 = _position + num - 5; HashSet hashSet = ((fields == null || fields.Count == 0) ? null : new HashSet(fields, StringComparer.OrdinalIgnoreCase)); while (_position < num2 && (hashSet == null || (hashSet != null && hashSet.Count > 0))) { string name; BsonValue bsonValue = ReadElement(hashSet, out name); if (bsonValue != null) { bsonDocument[name] = bsonValue; hashSet?.Remove(name); } } MoveForward(1); return bsonDocument; } catch (Exception ex) { return new Result(bsonDocument, ex); } } public Result ReadArray() { BsonArray bsonArray = new BsonArray(); try { int num = ReadInt32(); int num2 = _position + num - 5; while (_position < num2) { string name; BsonValue item = ReadElement(null, out name); bsonArray.Add(item); } MoveForward(1); return bsonArray; } catch (Exception ex) { return new Result(bsonArray, ex); } } private BsonValue ReadElement(HashSet remaining, out string name) { byte b = ReadByte(); name = ReadCString(); if (remaining != null && !remaining.Contains(name)) { int num; switch (b) { default: num = 0; break; case 3: case 4: num = ReadInt32() - 4; break; case 5: num = ReadInt32() + 1; break; case 2: num = ReadInt32(); break; case 19: num = 16; break; case 7: num = 12; break; case 1: case 9: case 18: num = 8; break; case 16: num = 4; break; case 8: num = 1; break; case 10: case 127: case byte.MaxValue: num = 0; break; } int num2 = num; if (num2 > 0) { Skip(num2); } return null; } switch (b) { case 1: return ReadDouble(); case 2: { int num4 = ReadInt32(); string text = ReadString(num4 - 1); MoveForward(1); return text; } case 3: return ReadDocument().GetValue(); case 4: return ReadArray().GetValue(); case 5: { int count = ReadInt32(); byte b2 = ReadByte(); byte[] array = ReadBytes(count); switch (b2) { case 0: return array; case 4: return new Guid(array); } break; } case 7: return ReadObjectId(); case 8: return ReadBoolean(); case 9: { long num3 = ReadInt64(); switch (num3) { case 253402300800000L: return DateTime.MaxValue; case -62135596800000L: return DateTime.MinValue; default: { DateTime unixEpoch = BsonValue.UnixEpoch; DateTime dateTime = unixEpoch.AddMilliseconds(num3); return _utcDate ? dateTime : dateTime.ToLocalTime(); } } } case 10: return BsonValue.Null; case 16: return ReadInt32(); case 18: return ReadInt64(); case 19: return ReadDecimal(); case byte.MaxValue: return BsonValue.MinValue; case 127: return BsonValue.MaxValue; } throw new NotSupportedException("BSON type not supported"); } public void Dispose() { _source?.Dispose(); } } internal class BufferWriter : IDisposable { private readonly IEnumerator _source; private BufferSlice _current; private int _currentPosition; private int _position; private bool _isEOF; private static readonly ArrayPool _bufferPool = ArrayPool.Shared; public int Position => _position; public bool IsEOF => _isEOF; public BufferWriter(byte[] buffer) : this(new BufferSlice(buffer, 0, buffer.Length)) { } public BufferWriter(BufferSlice buffer) { _source = null; _current = buffer; } public BufferWriter(IEnumerable source) { _source = source.GetEnumerator(); _source.MoveNext(); _current = _source.Current; } private bool MoveForward(int count) { if (_isEOF) { return false; } Constants.ENSURE(_currentPosition + count <= _current.Count, "forward is only for current segment"); _currentPosition += count; _position += count; if (_currentPosition == _current.Count) { if (_source == null || !_source.MoveNext()) { _isEOF = true; } else { _current = _source.Current; _currentPosition = 0; } return true; } return false; } public int Write(byte[] buffer, int offset, int count) { int num = 0; while (num < count) { int val = _current.Count - _currentPosition; int num2 = Math.Min(count - num, val); if (buffer != null) { Buffer.BlockCopy(buffer, offset + num, _current.Array, _current.Offset + _currentPosition, num2); } num += num2; MoveForward(num2); if (_isEOF) { break; } } Constants.ENSURE(count == num, "current value must fit inside defined buffer"); return num; } public int Write(byte[] buffer) { return Write(buffer, 0, buffer.Length); } public int Skip(int count) { return Write(null, 0, count); } public void Consume() { if (_source != null) { while (_source.MoveNext()) { } } } public void WriteCString(string value) { if (value.IndexOf('\0') > -1) { throw LiteException.InvalidNullCharInString(); } int byteCount = StringEncoding.UTF8.GetByteCount(value); int num = _current.Count - _currentPosition; if (byteCount < num) { StringEncoding.UTF8.GetBytes(value, 0, value.Length, _current.Array, _current.Offset + _currentPosition); _current[_currentPosition + byteCount] = 0; MoveForward(byteCount + 1); return; } byte[] array = _bufferPool.Rent(byteCount); StringEncoding.UTF8.GetBytes(value, 0, value.Length, array, 0); Write(array, 0, byteCount); _current[_currentPosition] = 0; MoveForward(1); _bufferPool.Return(array, clearArray: true); } public void WriteString(string value, bool specs) { int byteCount = StringEncoding.UTF8.GetByteCount(value); if (specs) { Write(byteCount + 1); } if (byteCount <= _current.Count - _currentPosition) { StringEncoding.UTF8.GetBytes(value, 0, value.Length, _current.Array, _current.Offset + _currentPosition); MoveForward(byteCount); } else { byte[] array = _bufferPool.Rent(byteCount); StringEncoding.UTF8.GetBytes(value, 0, value.Length, array, 0); Write(array, 0, byteCount); _bufferPool.Return(array, clearArray: true); } if (specs) { Write((byte)0); } } private void WriteNumber(T value, Action toBytes, int size) { if (_currentPosition + size <= _current.Count) { toBytes(value, _current.Array, _current.Offset + _currentPosition); MoveForward(size); return; } byte[] array = _bufferPool.Rent(size); toBytes(value, array, 0); Write(array, 0, size); _bufferPool.Return(array, clearArray: true); } public void Write(int value) { WriteNumber(value, BufferExtensions.ToBytes, 4); } public void Write(long value) { WriteNumber(value, BufferExtensions.ToBytes, 8); } public void Write(uint value) { WriteNumber(value, BufferExtensions.ToBytes, 4); } public void Write(double value) { WriteNumber(value, BufferExtensions.ToBytes, 8); } public void Write(decimal value) { int[] bits = decimal.GetBits(value); Write(bits[0]); Write(bits[1]); Write(bits[2]); Write(bits[3]); } public void Write(DateTime value) { Write(((value == DateTime.MinValue || value == DateTime.MaxValue) ? value : value.ToUniversalTime()).Ticks); } public void Write(Guid value) { byte[] buffer = value.ToByteArray(); Write(buffer, 0, 16); } public void Write(ObjectId value) { if (_currentPosition + 12 <= _current.Count) { value.ToByteArray(_current.Array, _current.Offset + _currentPosition); MoveForward(12); return; } byte[] array = _bufferPool.Rent(12); value.ToByteArray(array, 0); Write(array, 0, 12); _bufferPool.Return(array, clearArray: true); } public void Write(bool value) { _current[_currentPosition] = (value ? ((byte)1) : ((byte)0)); MoveForward(1); } public void Write(byte value) { _current[_currentPosition] = value; MoveForward(1); } internal void Write(PageAddress address) { Write(address.PageID); Write(address.Index); } public int WriteArray(BsonArray value, bool recalc) { int bytesCount = value.GetBytesCount(recalc); Write(bytesCount); for (int i = 0; i < value.Count; i++) { WriteElement(i.ToString(), value[i]); } Write((byte)0); return bytesCount; } public int WriteDocument(BsonDocument value, bool recalc) { int bytesCount = value.GetBytesCount(recalc); Write(bytesCount); foreach (KeyValuePair element in value.GetElements()) { WriteElement(element.Key, element.Value); } Write((byte)0); return bytesCount; } private void WriteElement(string key, BsonValue value) { switch (value.Type) { case BsonType.Double: Write((byte)1); WriteCString(key); Write(value.AsDouble); break; case BsonType.String: Write((byte)2); WriteCString(key); WriteString(value.AsString, specs: true); break; case BsonType.Document: Write((byte)3); WriteCString(key); WriteDocument(value.AsDocument, recalc: false); break; case BsonType.Array: Write((byte)4); WriteCString(key); WriteArray(value.AsArray, recalc: false); break; case BsonType.Binary: { Write((byte)5); WriteCString(key); byte[] asBinary = value.AsBinary; Write(asBinary.Length); Write((byte)0); Write(asBinary, 0, asBinary.Length); break; } case BsonType.Guid: { Write((byte)5); WriteCString(key); Guid asGuid = value.AsGuid; Write(16); Write((byte)4); Write(asGuid); break; } case BsonType.ObjectId: Write((byte)7); WriteCString(key); Write(value.AsObjectId); break; case BsonType.Boolean: Write((byte)8); WriteCString(key); Write(value.AsBoolean ? ((byte)1) : ((byte)0)); break; case BsonType.DateTime: { Write((byte)9); WriteCString(key); DateTime asDateTime = value.AsDateTime; Write(Convert.ToInt64((((asDateTime == DateTime.MinValue || asDateTime == DateTime.MaxValue) ? asDateTime : asDateTime.ToUniversalTime()) - BsonValue.UnixEpoch).TotalMilliseconds)); break; } case BsonType.Null: Write((byte)10); WriteCString(key); break; case BsonType.Int32: Write((byte)16); WriteCString(key); Write(value.AsInt32); break; case BsonType.Int64: Write((byte)18); WriteCString(key); Write(value.AsInt64); break; case BsonType.Decimal: Write((byte)19); WriteCString(key); Write(value.AsDecimal); break; case BsonType.MinValue: Write(byte.MaxValue); WriteCString(key); break; case BsonType.MaxValue: Write((byte)127); WriteCString(key); break; } } public void Dispose() { _source?.Dispose(); } } internal class FileStreamFactory : IStreamFactory { private readonly string _filename; private readonly string _password; private readonly bool _readonly; private readonly bool _hidden; private readonly bool _useAesStream; public string Name => Path.GetFileName(_filename); public bool CloseOnDispose => true; public FileStreamFactory(string filename, string password, bool readOnly, bool hidden, bool useAesStream = true) { _filename = filename; _password = password; _readonly = readOnly; _hidden = hidden; _useAesStream = useAesStream; } public Stream GetStream(bool canWrite, bool sequencial) { bool num = canWrite && !_readonly; FileMode mode = (_readonly ? FileMode.Open : FileMode.OpenOrCreate); FileAccess access = ((!num) ? FileAccess.Read : FileAccess.ReadWrite); FileShare share = (num ? FileShare.Read : FileShare.ReadWrite); FileOptions options = (sequencial ? FileOptions.SequentialScan : FileOptions.RandomAccess); bool num2 = num && !Exists(); FileStream fileStream = new FileStream(_filename, mode, access, share, 8192, options); if (num2 && _hidden) { File.SetAttributes(_filename, FileAttributes.Hidden); } if (_password != null && _useAesStream) { return new AesStream(_password, fileStream); } return fileStream; } public long GetLength() { if (!Exists()) { return 0L; } long num = new FileInfo(_filename).Length; if (num % 8192 != 0L) { num -= num % 8192; using FileStream fileStream = new FileStream(_filename, FileMode.Open, FileAccess.Write, FileShare.None, 8192, FileOptions.SequentialScan); fileStream.SetLength(num); fileStream.FlushToDisk(); } if (num <= 0) { return 0L; } return num - ((_password != null) ? 8192 : 0); } public bool Exists() { return File.Exists(_filename); } public void Delete() { File.Delete(_filename); } public bool IsLocked() { if (Exists()) { return FileHelper.IsFileLocked(_filename); } return false; } } internal interface IStreamFactory { string Name { get; } bool CloseOnDispose { get; } Stream GetStream(bool canWrite, bool sequencial); long GetLength(); bool Exists(); void Delete(); bool IsLocked(); } internal class StreamFactory : IStreamFactory { private readonly Stream _stream; private readonly string _password; public string Name { get { if (!(_stream is MemoryStream)) { if (!(_stream is TempStream)) { return ":stream:"; } return ":temp:"; } return ":memory:"; } } public bool CloseOnDispose => false; public StreamFactory(Stream stream, string password) { _stream = stream; _password = password; } public Stream GetStream(bool canWrite, bool sequencial) { if (_password == null) { return new ConcurrentStream(_stream, canWrite); } return new AesStream(_password, new ConcurrentStream(_stream, canWrite)); } public long GetLength() { long num = _stream.Length; if (num % 8192 != 0L) { num -= num % 8192; _stream.SetLength(num); _stream.FlushToDisk(); } if (num <= 0) { return 0L; } return num - ((_password != null) ? 8192 : 0); } public bool Exists() { return _stream.Length > 0; } public void Delete() { } public bool IsLocked() { return false; } } internal class StreamPool : IDisposable { private readonly ConcurrentBag _pool = new ConcurrentBag(); private readonly Lazy _writer; private readonly IStreamFactory _factory; public Lazy Writer => _writer; public StreamPool(IStreamFactory factory, bool appendOnly) { StreamPool streamPool = this; _factory = factory; _writer = new Lazy(() => streamPool._factory.GetStream(canWrite: true, appendOnly), isThreadSafe: true); } public Stream Rent() { if (!_pool.TryTake(out var result)) { return _factory.GetStream(canWrite: false, sequencial: false); } return result; } public void Return(Stream stream) { _pool.Add(stream); } public void Dispose() { if (!_factory.CloseOnDispose) { return; } foreach (Stream item in _pool) { item.Dispose(); } if (_writer.IsValueCreated) { _writer.Value.Dispose(); } } } internal class AesStream : Stream { private readonly Aes _aes; private readonly ICryptoTransform _encryptor; private readonly ICryptoTransform _decryptor; private readonly string _name; private readonly Stream _stream; private readonly CryptoStream _reader; private readonly CryptoStream _writer; private readonly byte[] _decryptedZeroes = new byte[16]; private static readonly byte[] _emptyContent = new byte[8175]; private static readonly ArrayPool _bufferPool = ArrayPool.Shared; public byte[] Salt { get; } public override bool CanRead => _stream.CanRead; public override bool CanSeek => _stream.CanSeek; public override bool CanWrite => _stream.CanWrite; public override long Length => _stream.Length - 8192; public override long Position { get { return _stream.Position - 8192; } set { Seek(value, SeekOrigin.Begin); } } public long StreamPosition => _stream.Position; public AesStream(string password, Stream stream) { _stream = stream; _name = ((_stream is FileStream fileStream) ? Path.GetFileName(fileStream.Name) : null); bool flag = _stream.Length < 8192; _stream.Position = 0L; byte[] array = _bufferPool.Rent(32); byte[] array2 = _bufferPool.Rent(16); try { if (flag) { Salt = NewSalt(); _stream.WriteByte(1); _stream.Write(Salt, 0, 16); } else { Salt = new byte[16]; if (_stream.ReadByte() != 1) { throw LiteException.FileNotEncrypted(); } _stream.Read(Salt, 0, 16); } _aes = Aes.Create(); _aes.Padding = PaddingMode.None; _aes.Mode = CipherMode.ECB; Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, Salt); using (rfc2898DeriveBytes) { _aes.Key = rfc2898DeriveBytes.GetBytes(32); _aes.IV = rfc2898DeriveBytes.GetBytes(16); } _encryptor = _aes.CreateEncryptor(); _decryptor = _aes.CreateDecryptor(); _reader = (_stream.CanRead ? new CryptoStream(_stream, _decryptor, CryptoStreamMode.Read) : null); _writer = (_stream.CanWrite ? new CryptoStream(_stream, _encryptor, CryptoStreamMode.Write) : null); _stream.Position = 32L; if (!flag) { _stream.Read(array, 0, 32); flag = array.All((byte x) => x == 0); Array.Clear(array, 0, 32); _stream.Position = 32L; } if (flag) { array.Fill(1, 0, 32); _writer.Write(array, 0, 32); _stream.Position = 8191L; _stream.WriteByte(0); } else { _reader.Read(array, 0, 32); if (!array.All((byte x) => x == 1)) { throw LiteException.InvalidPassword(); } } _stream.Position = 8192L; _stream.FlushToDisk(); using MemoryStream stream2 = new MemoryStream(array2); using CryptoStream cryptoStream = new CryptoStream(stream2, _decryptor, CryptoStreamMode.Read); cryptoStream.Read(_decryptedZeroes, 0, _decryptedZeroes.Length); } catch { _stream.Dispose(); throw; } finally { _bufferPool.Return(array2, clearArray: true); _bufferPool.Return(array, clearArray: true); } } public override int Read(byte[] array, int offset, int count) { Constants.ENSURE(Position % 8192 == 0, "AesRead: position must be in PAGE_SIZE module. Position={0}, File={1}", Position, _name); int result = _reader.Read(array, offset, count); if (IsBlank(array, offset)) { array.Fill(0, offset, count); } return result; } public override void Write(byte[] array, int offset, int count) { Constants.ENSURE(count == 8192 || count == 1, "buffer size must be PAGE_SIZE"); Constants.ENSURE(Position == 191 || Position % 8192 == 0, "AesWrite: position must be in PAGE_SIZE module. Position={0}, File={1}", Position, _name); _writer.Write(array, offset, count); } protected override void Dispose(bool disposing) { base.Dispose(disposing); _stream?.Dispose(); _encryptor.Dispose(); _decryptor.Dispose(); _aes.Dispose(); } public static byte[] NewSalt() { byte[] array = new byte[16]; using RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create(); randomNumberGenerator.GetBytes(array); return array; } public override void Flush() { _stream.Flush(); } public override long Seek(long offset, SeekOrigin origin) { return _stream.Seek(offset + 8192, origin); } public override void SetLength(long value) { _stream.SetLength(value + 8192); } private unsafe bool IsBlank(byte[] array, int offset) { fixed (byte* ptr3 = array) { fixed (byte* ptr = _decryptedZeroes) { void* ptr2 = ptr; ulong* ptr4 = (ulong*)(ptr3 + offset); ulong* ptr5 = (ulong*)ptr2; if (*ptr4 == *ptr5) { return ptr4[1] == ptr5[1]; } return false; } } } } internal class ConcurrentStream : Stream { private readonly Stream _stream; private readonly bool _canWrite; private long _position; public override bool CanRead => _stream.CanRead; public override bool CanSeek => _stream.CanSeek; public override bool CanWrite => _canWrite; public override long Length => _stream.Length; public override long Position { get { return _position; } set { _position = value; } } public ConcurrentStream(Stream stream, bool canWrite) { _stream = stream; _canWrite = canWrite; } public override void Flush() { _stream.Flush(); } public override void SetLength(long value) { _stream.SetLength(value); } protected override void Dispose(bool disposing) { _stream.Dispose(); } public override long Seek(long offset, SeekOrigin origin) { lock (_stream) { _position = origin switch { SeekOrigin.Current => _position + offset, SeekOrigin.Begin => offset, _ => _position - offset, }; return _position; } } public override int Read(byte[] buffer, int offset, int count) { lock (_stream) { _stream.Position = _position; int result = _stream.Read(buffer, offset, count); _position = _stream.Position; return result; } } public override void Write(byte[] buffer, int offset, int count) { if (!_canWrite) { throw new NotSupportedException("Current stream are readonly"); } lock (_stream) { _stream.Position = _position; _stream.Write(buffer, offset, count); _position = _stream.Position; } } } internal class TempStream : Stream { private Stream _stream = new MemoryStream(); private string _filename; private readonly long _maxMemoryUsage; public bool InMemory => _stream is MemoryStream; public bool InDisk => _stream is FileStream; public string Filename => _filename; public override bool CanRead => _stream.CanRead; public override bool CanSeek => _stream.CanWrite; public override bool CanWrite => _stream.CanWrite; public override long Length => _stream.Length; public override long Position { get { return _stream.Position; } set { Seek(value, SeekOrigin.Begin); } } public TempStream(string filename = null, long maxMemoryUsage = 10485760L) { _maxMemoryUsage = maxMemoryUsage; _filename = filename; } public override void Flush() { _stream.Flush(); } public override int Read(byte[] buffer, int offset, int count) { return _stream.Read(buffer, offset, count); } public override void SetLength(long value) { _stream.SetLength(value); } public override void Write(byte[] buffer, int offset, int count) { _stream.Write(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { if (origin switch { SeekOrigin.Current => _stream.Position + offset, SeekOrigin.Begin => offset, _ => _stream.Position - offset, } > _maxMemoryUsage && InMemory) { _filename = _filename ?? Path.Combine(Path.GetTempPath(), "litedb_" + Guid.NewGuid().ToString() + ".db"); FileStream fileStream = new FileStream(_filename, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 8192, FileOptions.RandomAccess); _stream.Position = 0L; _stream.CopyTo(fileStream); _stream.Dispose(); _stream = fileStream; } return _stream.Seek(offset, origin); } protected override void Dispose(bool disposing) { base.Dispose(disposing); _stream.Dispose(); if (InDisk) { File.Delete(_filename); } } } internal class EnginePragmas { public const int P_USER_VERSION = 76; public const int P_COLLATION_LCID = 80; public const int P_COLLATION_SORT = 84; public const int P_TIMEOUT = 88; public const int P_UTC_DATE = 96; public const int P_CHECKPOINT = 97; public const int P_LIMIT_SIZE = 101; private readonly Dictionary _pragmas; private bool _isDirty; private readonly HeaderPage _headerPage; public int UserVersion { get; private set; } public Collation Collation { get; private set; } = Collation.Default; public TimeSpan Timeout { get; private set; } = TimeSpan.FromMinutes(1.0); public long LimitSize { get; private set; } = long.MaxValue; public bool UtcDate { get; private set; } public int Checkpoint { get; private set; } = 1000; public IEnumerable Pragmas => _pragmas.Values; public EnginePragmas(HeaderPage headerPage) { _headerPage = headerPage; _pragmas = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["USER_VERSION"] = new Pragma { Name = "USER_VERSION", Get = () => UserVersion, Set = delegate(BsonValue v) { UserVersion = v.AsInt32; }, Read = delegate(BufferSlice b) { UserVersion = b.ReadInt32(76); }, Validate = delegate { }, Write = delegate(BufferSlice b) { b.Write(UserVersion, 76); } }, ["COLLATION"] = new Pragma { Name = "COLLATION", Get = () => Collation.ToString(), Set = delegate(BsonValue v) { Collation = new Collation(v.AsString); }, Read = delegate(BufferSlice b) { Collation = new Collation(b.ReadInt32(80), (CompareOptions)b.ReadInt32(84)); }, Validate = delegate { throw new LiteException(0, "Pragma COLLATION is read only. Use Rebuild options."); }, Write = delegate(BufferSlice b) { b.Write(Collation.LCID, 80); b.Write((int)Collation.SortOptions, 84); } }, ["TIMEOUT"] = new Pragma { Name = "TIMEOUT", Get = () => (int)Timeout.TotalSeconds, Set = delegate(BsonValue v) { Timeout = TimeSpan.FromSeconds(v.AsInt32); }, Read = delegate(BufferSlice b) { Timeout = TimeSpan.FromSeconds(b.ReadInt32(88)); }, Validate = delegate(BsonValue v, HeaderPage h) { if (v <= 0) { throw new LiteException(0, "Pragma TIMEOUT must be greater than zero"); } }, Write = delegate(BufferSlice b) { b.Write((int)Timeout.TotalSeconds, 88); } }, ["LIMIT_SIZE"] = new Pragma { Name = "LIMIT_SIZE", Get = () => LimitSize, Set = delegate(BsonValue v) { LimitSize = v.AsInt64; }, Read = delegate(BufferSlice b) { long num = b.ReadInt64(101); LimitSize = ((num == 0L) ? long.MaxValue : num); }, Validate = delegate(BsonValue v, HeaderPage h) { if (v < 32768) { throw new LiteException(0, "Pragma LIMIT_SIZE must be at least 4 pages (32768 bytes)"); } if (h != null && v.AsInt64 < (h.LastPageID + 1) * 8192) { throw new LiteException(0, "Pragma LIMIT_SIZE must be greater or equal to the current file size"); } }, Write = delegate(BufferSlice b) { b.Write(LimitSize, 101); } }, ["UTC_DATE"] = new Pragma { Name = "UTC_DATE", Get = () => UtcDate, Set = delegate(BsonValue v) { UtcDate = v.AsBoolean; }, Read = delegate(BufferSlice b) { UtcDate = b.ReadBool(96); }, Validate = delegate { }, Write = delegate(BufferSlice b) { b.Write(UtcDate, 96); } }, ["CHECKPOINT"] = new Pragma { Name = "CHECKPOINT", Get = () => Checkpoint, Set = delegate(BsonValue v) { Checkpoint = v.AsInt32; }, Read = delegate(BufferSlice b) { Checkpoint = b.ReadInt32(97); }, Validate = delegate(BsonValue v, HeaderPage h) { if (v < 0) { throw new LiteException(0, "Pragma CHECKPOINT must be greater or equal to zero"); } }, Write = delegate(BufferSlice b) { b.Write(Checkpoint, 97); } } }; _isDirty = true; } public EnginePragmas(BufferSlice buffer, HeaderPage headerPage) : this(headerPage) { foreach (Pragma value in _pragmas.Values) { value.Read(buffer); } _isDirty = false; } public void UpdateBuffer(BufferSlice buffer) { if (!_isDirty) { return; } foreach (KeyValuePair pragma in _pragmas) { pragma.Value.Write(buffer); } _isDirty = false; } public BsonValue Get(string name) { if (_pragmas.TryGetValue(name, out var value)) { return value.Get(); } throw new LiteException(0, "Pragma `" + name + "` not exist"); } public void Set(string name, BsonValue value, bool validate) { if (_pragmas.TryGetValue(name, out var value2)) { if (validate) { value2.Validate(value, _headerPage); } value2.Set(value); _isDirty = true; return; } throw new LiteException(0, "Pragma `" + name + "` not exist"); } } internal class EngineSettings { public Stream DataStream { get; set; } public Stream LogStream { get; set; } public Stream TempStream { get; set; } public string Filename { get; set; } public string Password { get; set; } public long InitialSize { get; set; } public Collation Collation { get; set; } public bool ReadOnly { get; set; } public bool AutoRebuild { get; set; } public bool Upgrade { get; set; } public Func ReadTransform { get; set; } internal IStreamFactory CreateDataFactory(bool useAesStream = true) { if (DataStream != null) { return new StreamFactory(DataStream, Password); } if (Filename == ":memory:") { return new StreamFactory(new MemoryStream(), Password); } if (Filename == ":temp:") { return new StreamFactory(new TempStream(null, 10485760L), Password); } if (!string.IsNullOrEmpty(Filename)) { return new FileStreamFactory(Filename, Password, ReadOnly, hidden: false, useAesStream); } throw new ArgumentException("EngineSettings must have Filename or DataStream as data source"); } internal IStreamFactory CreateLogFactory() { if (LogStream != null) { return new StreamFactory(LogStream, Password); } if (Filename == ":memory:") { return new StreamFactory(new MemoryStream(), Password); } if (Filename == ":temp:") { return new StreamFactory(new TempStream(null, 10485760L), Password); } if (!string.IsNullOrEmpty(Filename)) { return new FileStreamFactory(FileHelper.GetLogFile(Filename), Password, ReadOnly, hidden: false); } return new StreamFactory(new MemoryStream(), Password); } internal IStreamFactory CreateTempFactory() { if (TempStream != null) { return new StreamFactory(TempStream, Password); } if (Filename == ":memory:") { return new StreamFactory(new MemoryStream(), Password); } if (Filename == ":temp:") { return new StreamFactory(new TempStream(null, 10485760L), Password); } if (!string.IsNullOrEmpty(Filename)) { return new FileStreamFactory(FileHelper.GetTempFile(Filename), Password, readOnly: false, hidden: true); } return new StreamFactory(new TempStream(null, 10485760L), Password); } } internal class EngineState { public bool Disposed; private Exception _exception; private readonly LiteEngine _engine; private readonly EngineSettings _settings; public EngineState(LiteEngine engine, EngineSettings settings) { _engine = engine; _settings = settings; } public void Validate() { if (Disposed) { throw _exception ?? LiteException.EngineDisposed(); } } public bool Handle(Exception ex) { if (ex is IOException || (ex is LiteException ex2 && ex2.ErrorCode == 999)) { _exception = ex; _engine?.Close(ex); return false; } return true; } public BsonValue ReadTransform(string collection, BsonValue value) { if (_settings?.ReadTransform == null) { return value; } return _settings.ReadTransform(collection, value); } } internal class LiteEngine : ILiteEngine, IDisposable { private static readonly ArrayPool _bufferPool = ArrayPool.Shared; private LockService _locker; private DiskService _disk; private WalIndexService _walIndex; private HeaderPage _header; private TransactionMonitor _monitor; private SortDisk _sortDisk; private EngineState _state; private readonly EngineSettings _settings; private Dictionary _systemCollections; private ConcurrentDictionary _sequences; public IEnumerable GetCollectionNames() { return from x in _header.GetCollections() select x.Key; } public bool DropCollection(string name) { if (name.IsNullOrWhiteSpace()) { throw new ArgumentNullException("name"); } if (_locker.IsInTransaction) { throw LiteException.AlreadyExistsTransaction(); } return AutoTransaction(delegate(TransactionService transaction) { Snapshot snapshot = transaction.CreateSnapshot(LockMode.Write, name, addIfNotExists: false); if (snapshot.CollectionPage == null) { return false; } snapshot.DropCollection(transaction.Safepoint); _sequences.TryRemove(name, out var _); return true; }); } public bool RenameCollection(string collection, string newName) { if (collection.IsNullOrWhiteSpace()) { throw new ArgumentNullException("collection"); } if (newName.IsNullOrWhiteSpace()) { throw new ArgumentNullException("newName"); } if (_locker.IsInTransaction) { throw LiteException.AlreadyExistsTransaction(); } if (collection.Equals(newName, StringComparison.OrdinalIgnoreCase)) { throw LiteException.InvalidCollectionName(newName, "New name must be different from current name"); } CollectionService.CheckName(newName, _header); if (_locker.IsInTransaction) { throw LiteException.AlreadyExistsTransaction(); } return AutoTransaction(delegate(TransactionService transaction) { Snapshot snapshot = transaction.CreateSnapshot(LockMode.Write, collection, addIfNotExists: false); transaction.CreateSnapshot(LockMode.Write, newName, addIfNotExists: false); if (snapshot.CollectionPage == null) { return false; } if (_header.GetCollectionPageID(newName) != uint.MaxValue) { throw LiteException.AlreadyExistsCollectionName(newName); } transaction.Pages.Commit += delegate(HeaderPage h) { h.RenameCollection(collection, newName); }; return true; }); } public int Delete(string collection, IEnumerable ids) { if (collection.IsNullOrWhiteSpace()) { throw new ArgumentNullException("collection"); } if (ids == null) { throw new ArgumentNullException("ids"); } return AutoTransaction(delegate(TransactionService transaction) { Snapshot snapshot = transaction.CreateSnapshot(LockMode.Write, collection, addIfNotExists: false); CollectionPage collectionPage = snapshot.CollectionPage; DataService dataService = new DataService(snapshot, _disk.MAX_ITEMS_COUNT); IndexService indexService = new IndexService(snapshot, _header.Pragmas.Collation, _disk.MAX_ITEMS_COUNT); if (collectionPage == null) { return 0; } int num = 0; CollectionIndex pK = collectionPage.PK; foreach (BsonValue id in ids) { IndexNode indexNode = indexService.Find(pK, id, sibling: false, 1); if (indexNode != null) { _state.Validate(); dataService.Delete(indexNode.DataBlock); indexService.DeleteAll(indexNode.Position); transaction.Safepoint(); num++; } } return num; }); } public int DeleteMany(string collection, BsonExpression predicate) { if (collection.IsNullOrWhiteSpace()) { throw new ArgumentNullException("collection"); } if (predicate != null && predicate.Type == BsonExpressionType.Equal && predicate.Left.Type == BsonExpressionType.Path && predicate.Left.Source == "$._id" && predicate.Right.IsValue) { BsonValue bsonValue = predicate.Right.Execute(_header.Pragmas.Collation).First(); return Delete(collection, new BsonValue[1] { bsonValue }); } return Delete(collection, getIds()); IEnumerable getIds() { Query query = new Query { Select = "{ i: _id }", ForUpdate = true }; if (predicate != null) { query.Where.Add(predicate); } using IBsonDataReader reader = Query(collection, query); while (reader.Read()) { BsonValue bsonValue2 = reader.Current["i"]; if (bsonValue2 != BsonValue.Null) { yield return bsonValue2; } } } } public bool EnsureIndex(string collection, string name, BsonExpression expression, bool unique) { if (collection.IsNullOrWhiteSpace()) { throw new ArgumentNullException("collection"); } if (name.IsNullOrWhiteSpace()) { throw new ArgumentNullException("name"); } if (expression == null) { throw new ArgumentNullException("expression"); } if (!expression.IsIndexable) { throw new ArgumentException("Index expressions must contains at least one document field. Used methods must be immutable. Parameters are not supported.", "expression"); } if (name.Length > Constants.INDEX_NAME_MAX_LENGTH) { throw LiteException.InvalidIndexName(name, collection, "MaxLength = " + Constants.INDEX_NAME_MAX_LENGTH); } if (!name.IsWord()) { throw LiteException.InvalidIndexName(name, collection, "Use only [a-Z$_]"); } if (name.StartsWith("$")) { throw LiteException.InvalidIndexName(name, collection, "Index name can't start with `$`"); } if (!expression.IsScalar && unique) { throw new LiteException(0, "Multikey index expression do not support unique option"); } if (expression.Source == "$._id") { return false; } return AutoTransaction(delegate(TransactionService transaction) { Snapshot snapshot = transaction.CreateSnapshot(LockMode.Write, collection, addIfNotExists: true); CollectionPage collectionPage = snapshot.CollectionPage; IndexService indexService = new IndexService(snapshot, _header.Pragmas.Collation, _disk.MAX_ITEMS_COUNT); DataService dataService = new DataService(snapshot, _disk.MAX_ITEMS_COUNT); CollectionIndex collectionIndex = collectionPage.GetCollectionIndex(name); if (collectionIndex != null) { if (collectionIndex.Expression != expression.Source) { throw LiteException.IndexAlreadyExist(name); } return false; } CollectionIndex index = indexService.CreateIndex(name, expression.Source, unique); uint num = 0u; foreach (IndexNode item in new IndexAll("_id", 1).Run(collectionPage, indexService)) { using (BufferReader bufferReader = new BufferReader(dataService.Read(item.DataBlock))) { BsonDocument value = bufferReader.ReadDocument(expression.Fields).GetValue(); IndexNode indexNode = null; IndexNode indexNode2 = null; foreach (BsonValue indexKey in expression.GetIndexKeys(value, _header.Pragmas.Collation)) { _state.Validate(); IndexNode indexNode3 = indexService.AddNode(index, indexKey, item.DataBlock, indexNode); if (indexNode2 == null) { indexNode2 = indexNode3; } indexNode = indexNode3; num++; } if (indexNode2 != null) { indexNode.SetNextNode(item.NextNode); item.SetNextNode(indexNode2.Position); } } transaction.Safepoint(); } return true; }); } public bool DropIndex(string collection, string name) { if (collection.IsNullOrWhiteSpace()) { throw new ArgumentNullException("collection"); } if (name.IsNullOrWhiteSpace()) { throw new ArgumentNullException("name"); } if (name == "_id") { throw LiteException.IndexDropId(); } return AutoTransaction(delegate(TransactionService transaction) { Snapshot snapshot = transaction.CreateSnapshot(LockMode.Write, collection, addIfNotExists: false); CollectionPage collectionPage = snapshot.CollectionPage; IndexService indexService = new IndexService(snapshot, _header.Pragmas.Collation, _disk.MAX_ITEMS_COUNT); if (collectionPage == null) { return false; } CollectionIndex collectionIndex = collectionPage.GetCollectionIndex(name); if (collectionIndex == null) { return false; } indexService.DropIndex(collectionIndex); snapshot.CollectionPage.DeleteCollectionIndex(name); return true; }); } public int Insert(string collection, IEnumerable docs, BsonAutoId autoId) { if (collection.IsNullOrWhiteSpace()) { throw new ArgumentNullException("collection"); } if (docs == null) { throw new ArgumentNullException("docs"); } return AutoTransaction(delegate(TransactionService transaction) { Snapshot snapshot = transaction.CreateSnapshot(LockMode.Write, collection, addIfNotExists: true); int num = 0; IndexService indexer = new IndexService(snapshot, _header.Pragmas.Collation, _disk.MAX_ITEMS_COUNT); DataService data = new DataService(snapshot, _disk.MAX_ITEMS_COUNT); foreach (BsonDocument doc in docs) { _state.Validate(); transaction.Safepoint(); InsertDocument(snapshot, doc, autoId, indexer, data); num++; } return num; }); } private void InsertDocument(Snapshot snapshot, BsonDocument doc, BsonAutoId autoId, IndexService indexer, DataService data) { if (!doc.TryGetValue("_id", out var value)) { value = (doc["_id"] = autoId switch { BsonAutoId.Guid => new BsonValue(Guid.NewGuid()), BsonAutoId.ObjectId => new BsonValue(ObjectId.NewObjectId()), _ => GetSequence(snapshot, autoId), }); } else if (value.IsNumber) { SetSequence(snapshot, value); } if (value.IsNull || value.IsMinValue || value.IsMaxValue) { throw LiteException.InvalidDataType("_id", value); } PageAddress dataBlock = data.Insert(doc); IndexNode last = null; foreach (CollectionIndex collectionIndex in snapshot.CollectionPage.GetCollectionIndexes()) { foreach (BsonValue indexKey in collectionIndex.BsonExpr.GetIndexKeys(doc, _header.Pragmas.Collation)) { last = indexer.AddNode(collectionIndex, indexKey, dataBlock, last); } } } public BsonValue Pragma(string name) { return _header.Pragmas.Get(name); } public bool Pragma(string name, BsonValue value) { if (Pragma(name) == value) { return false; } if (_locker.IsInTransaction) { throw LiteException.AlreadyExistsTransaction(); } return AutoTransaction(delegate(TransactionService transaction) { transaction.Pages.Commit += delegate(HeaderPage h) { h.Pragmas.Set(name, value, validate: true); }; return true; }); } public IBsonDataReader Query(string collection, Query query) { if (string.IsNullOrWhiteSpace(collection)) { throw new ArgumentNullException("collection"); } if (query == null) { throw new ArgumentNullException("query"); } IEnumerable source = null; if (collection.StartsWith("$")) { SqlParser.ParseCollection(new Tokenizer(collection), out var name, out var options); SystemCollection systemCollection = GetSystemCollection(name); source = systemCollection.Input(options); collection = systemCollection.Name; } return new QueryExecutor(this, _state, _monitor, _sortDisk, _disk, _header.Pragmas, collection, query, source).ExecuteQuery(); } public long Rebuild(RebuildOptions options) { if (string.IsNullOrEmpty(_settings.Filename)) { return 0L; } Close(); long result = new RebuildService(_settings).Rebuild(options); Open(); _state.Disposed = false; return result; } public long Rebuild() { Collation collation = new Collation(Pragma("COLLATION")); string password = _settings.Password; return Rebuild(new RebuildOptions { Password = password, Collation = collation }); } internal void RebuildContent(IFileReader reader) { bool isNew; TransactionService transaction = _monitor.GetTransaction(create: true, queryOnly: false, out isNew); try { foreach (string collection in reader.GetCollections()) { Snapshot snapshot = transaction.CreateSnapshot(LockMode.Write, collection, addIfNotExists: true); IndexService indexer = new IndexService(snapshot, _header.Pragmas.Collation, _disk.MAX_ITEMS_COUNT); DataService data = new DataService(snapshot, _disk.MAX_ITEMS_COUNT); foreach (BsonDocument document in reader.GetDocuments(collection)) { transaction.Safepoint(); InsertDocument(snapshot, document, BsonAutoId.ObjectId, indexer, data); } foreach (IndexInfo index in reader.GetIndexes(collection)) { EnsureIndex(collection, index.Name, BsonExpression.Create(index.Expression), index.Unique); } } transaction.Commit(); _monitor.ReleaseTransaction(transaction); } catch (Exception ex) { Close(ex); throw; } } private void Recovery(Collation collation) { RebuildService rebuildService = new RebuildService(_settings); RebuildOptions options = new RebuildOptions { Collation = collation, Password = _settings.Password, IncludeErrorReport = true }; rebuildService.Rebuild(options); } private BsonValue GetSequence(Snapshot snapshot, BsonAutoId autoId) { long num = _sequences.AddOrUpdate(snapshot.CollectionName, delegate { BsonValue lastId = GetLastId(snapshot); if (lastId.IsMinValue) { return 1L; } if (!lastId.IsNumber) { throw new LiteException(0, $"It's not possible use AutoId={autoId} because '{snapshot.CollectionName}' collection contains not only numbers in _id index ({lastId})."); } return lastId.AsInt64 + 1; }, (string s, long value) => value + 1); if (autoId != BsonAutoId.Int32) { return new BsonValue(num); } return new BsonValue((int)num); } private void SetSequence(Snapshot snapshot, BsonValue newId) { _sequences.AddOrUpdate(snapshot.CollectionName, delegate { BsonValue lastId = GetLastId(snapshot); return lastId.IsNumber ? Math.Max(lastId.AsInt64, newId.AsInt64) : newId.AsInt64; }, (string s, long value) => Math.Max(value, newId.AsInt64)); } private BsonValue GetLastId(Snapshot snapshot) { CollectionIndex pK = snapshot.CollectionPage.PK; IndexPage page = snapshot.GetPage(pK.Tail.PageID); PageAddress pageAddress = page.GetIndexNode(pK.Tail.Index).Prev[0]; if (pageAddress == pK.Head) { return BsonValue.MinValue; } return ((pageAddress.PageID == page.PageID) ? page : snapshot.GetPage(pageAddress.PageID)).GetIndexNode(pageAddress.Index).Key; } internal SystemCollection GetSystemCollection(string name) { if (_systemCollections.TryGetValue(name, out var value)) { return value; } throw new LiteException(0, "System collection '" + name + "' are not registered as system collection"); } internal void RegisterSystemCollection(SystemCollection systemCollection) { if (systemCollection == null) { throw new ArgumentNullException("systemCollection"); } _systemCollections[systemCollection.Name] = systemCollection; } internal void RegisterSystemCollection(string collectionName, Func> factory) { if (collectionName.IsNullOrWhiteSpace()) { throw new ArgumentNullException("collectionName"); } if (factory == null) { throw new ArgumentNullException("factory"); } _systemCollections[collectionName] = new SystemCollection(collectionName, factory); } public bool BeginTrans() { _state.Validate(); bool isNew; TransactionService transaction = _monitor.GetTransaction(create: true, queryOnly: false, out isNew); transaction.ExplicitTransaction = true; if (transaction.OpenCursors.Count > 0) { throw new LiteException(0, "This thread contains an open cursors/query. Close cursors before Begin()"); } return isNew; } public bool Commit() { _state.Validate(); bool isNew; TransactionService transaction = _monitor.GetTransaction(create: false, queryOnly: false, out isNew); if (transaction != null) { if (transaction.OpenCursors.Count > 0) { throw new LiteException(0, "Current transaction contains open cursors. Close cursors before run Commit()"); } if (transaction.State == TransactionState.Active) { CommitAndReleaseTransaction(transaction); return true; } } return false; } public bool Rollback() { _state.Validate(); bool isNew; TransactionService transaction = _monitor.GetTransaction(create: false, queryOnly: false, out isNew); if (transaction != null && transaction.State == TransactionState.Active) { transaction.Rollback(); _monitor.ReleaseTransaction(transaction); return true; } return false; } private T AutoTransaction(Func fn) { _state.Validate(); bool isNew; TransactionService transaction = _monitor.GetTransaction(create: true, queryOnly: false, out isNew); try { T result = fn(transaction); if (isNew) { CommitAndReleaseTransaction(transaction); } return result; } catch (Exception ex) { if (_state.Handle(ex)) { transaction.Rollback(); _monitor.ReleaseTransaction(transaction); } throw; } } private void CommitAndReleaseTransaction(TransactionService transaction) { transaction.Commit(); _monitor.ReleaseTransaction(transaction); if (_header.Pragmas.Checkpoint > 0 && _disk.GetFileLength(FileOrigin.Log) > _header.Pragmas.Checkpoint * 8192) { _walIndex.TryCheckpoint(); } } public int Update(string collection, IEnumerable docs) { if (collection.IsNullOrWhiteSpace()) { throw new ArgumentNullException("collection"); } if (docs == null) { throw new ArgumentNullException("docs"); } return AutoTransaction(delegate(TransactionService transaction) { Snapshot snapshot = transaction.CreateSnapshot(LockMode.Write, collection, addIfNotExists: false); CollectionPage collectionPage = snapshot.CollectionPage; IndexService indexer = new IndexService(snapshot, _header.Pragmas.Collation, _disk.MAX_ITEMS_COUNT); DataService data = new DataService(snapshot, _disk.MAX_ITEMS_COUNT); int num = 0; if (collectionPage == null) { return 0; } foreach (BsonDocument doc in docs) { _state.Validate(); transaction.Safepoint(); if (UpdateDocument(snapshot, collectionPage, doc, indexer, data)) { num++; } } return num; }); } public int UpdateMany(string collection, BsonExpression transform, BsonExpression predicate) { if (collection.IsNullOrWhiteSpace()) { throw new ArgumentNullException("collection"); } if (transform == null) { throw new ArgumentNullException("transform"); } return Update(collection, transformDocs()); IEnumerable transformDocs() { Query query = new Query { Select = "$", ForUpdate = true }; if (predicate != null) { query.Where.Add(predicate); } using IBsonDataReader reader = Query(collection, query); while (reader.Read()) { BsonDocument asDocument = reader.Current.AsDocument; BsonValue bsonValue = asDocument["_id"]; BsonValue bsonValue2 = transform.ExecuteScalar(asDocument, _header.Pragmas.Collation); if (!bsonValue2.IsDocument) { throw new ArgumentException("Extend expression must return a document", "transform"); } BsonDocument asDocument2 = BsonExpressionMethods.EXTEND(asDocument, bsonValue2.AsDocument).AsDocument; if (asDocument2.TryGetValue("_id", out var value)) { if (value != bsonValue) { throw LiteException.InvalidUpdateField("_id"); } } else { asDocument2["_id"] = bsonValue; } yield return asDocument2; } } } private bool UpdateDocument(Snapshot snapshot, CollectionPage col, BsonDocument doc, IndexService indexer, DataService data) { BsonValue bsonValue = doc["_id"]; if (bsonValue.IsNull || bsonValue.IsMinValue || bsonValue.IsMaxValue) { throw LiteException.InvalidDataType("_id", bsonValue); } IndexNode indexNode = indexer.Find(col.PK, bsonValue, sibling: false, 1); if (indexNode == null) { return false; } data.Update(col, indexNode.DataBlock, doc); Tuple[] oldKeys = (from x in indexer.GetNodeList(indexNode.NextNode) select new Tuple(x.Slot, x.Key, x.Position)).ToArray(); List> newKeys = new List>(); foreach (CollectionIndex item in from x in col.GetCollectionIndexes() where x.Name != "_id" select x) { foreach (BsonValue indexKey in item.BsonExpr.GetIndexKeys(doc, _header.Pragmas.Collation)) { newKeys.Add(new Tuple(item.Slot, indexKey, item.Name)); } } if (oldKeys.Length == 0 && newKeys.Count == 0) { return true; } HashSet hashSet = new HashSet(from x in oldKeys where !newKeys.Any((Tuple n) => n.Item1 == x.Item1 && n.Item2 == x.Item2) select x.Item3); Tuple[] array = newKeys.Where((Tuple x) => !oldKeys.Any((Tuple o) => o.Item1 == x.Item1 && o.Item2 == x.Item2)).ToArray(); if (hashSet.Count == 0 && array.Length == 0) { return true; } IndexNode last = indexer.DeleteList(indexNode.Position, hashSet); Tuple[] array2 = array; foreach (Tuple tuple in array2) { CollectionIndex collectionIndex = col.GetCollectionIndex(tuple.Item3); last = indexer.AddNode(collectionIndex, tuple.Item2, indexNode.DataBlock, last); } return true; } private void TryUpgrade() { if (!File.Exists(_settings.Filename)) { return; } byte[] array = _bufferPool.Rent(1024); using (FileStream fileStream = new FileStream(_settings.Filename, FileMode.Open, FileAccess.Read, FileShare.Read, 1024)) { fileStream.Position = 0L; fileStream.Read(array, 0, 1024); if (!FileReaderV7.IsVersion(array)) { return; } } _bufferPool.Return(array, clearArray: true); Recovery(_settings.Collation); } [Obsolete("Upgrade your LiteDB v4 datafiles using Upgrade=true in EngineSettings. You can use upgrade=true in connection string.")] public static bool Upgrade(string filename, string password = null, Collation collation = null) { if (filename.IsNullOrWhiteSpace()) { throw new ArgumentNullException("filename"); } if (!File.Exists(filename)) { return false; } using (new LiteEngine(new EngineSettings { Filename = filename, Password = password, Collation = collation, Upgrade = true })) { } return true; } public int Upsert(string collection, IEnumerable docs, BsonAutoId autoId) { if (collection.IsNullOrWhiteSpace()) { throw new ArgumentNullException("collection"); } if (docs == null) { throw new ArgumentNullException("docs"); } return AutoTransaction(delegate(TransactionService transaction) { Snapshot snapshot = transaction.CreateSnapshot(LockMode.Write, collection, addIfNotExists: true); CollectionPage collectionPage = snapshot.CollectionPage; IndexService indexer = new IndexService(snapshot, _header.Pragmas.Collation, _disk.MAX_ITEMS_COUNT); DataService data = new DataService(snapshot, _disk.MAX_ITEMS_COUNT); int num = 0; foreach (BsonDocument doc in docs) { _state.Validate(); transaction.Safepoint(); if (doc["_id"] == BsonValue.Null || !UpdateDocument(snapshot, collectionPage, doc, indexer, data)) { InsertDocument(snapshot, doc, autoId, indexer, data); num++; } } return num; }); } public LiteEngine() : this(new EngineSettings { DataStream = new MemoryStream() }) { } public LiteEngine(string filename) : this(new EngineSettings { Filename = filename }) { } public LiteEngine(EngineSettings settings) { _settings = settings ?? throw new ArgumentNullException("settings"); Open(); } internal bool Open() { _systemCollections = new Dictionary(StringComparer.OrdinalIgnoreCase); _sequences = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); try { _state = new EngineState(this, _settings); if (_settings.Upgrade) { TryUpgrade(); } _disk = new DiskService(_settings, _state, Constants.MEMORY_SEGMENT_SIZES); PageBuffer pageBuffer = _disk.ReadFull(FileOrigin.Data).First(); if (pageBuffer[0] == 1) { throw new LiteException(0, "This data file is encrypted and needs a password to open"); } _header = new HeaderPage(pageBuffer); if (pageBuffer[191] != 0 && _settings.AutoRebuild) { _disk.Dispose(); _disk = null; Recovery(_header.Pragmas.Collation); _disk = new DiskService(_settings, _state, Constants.MEMORY_SEGMENT_SIZES); pageBuffer = _disk.ReadFull(FileOrigin.Data).First(); _header = new HeaderPage(pageBuffer); } if (_settings.Collation != null && _settings.Collation.ToString() != _header.Pragmas.Collation.ToString()) { throw new LiteException(0, $"Datafile collation '{_header.Pragmas.Collation}' is different from engine settings. Use Rebuild database to change collation."); } _locker = new LockService(_header.Pragmas); _walIndex = new WalIndexService(_disk, _locker); if (_disk.GetFileLength(FileOrigin.Log) > 0) { _walIndex.RestoreIndex(ref _header); } _sortDisk = new SortDisk(_settings.CreateTempFactory(), 819200, _header.Pragmas); _monitor = new TransactionMonitor(_header, _locker, _disk, _walIndex); InitializeSystemCollections(); return true; } catch (Exception ex) { Close(ex); throw; } } internal List Close() { if (_state.Disposed) { return new List(); } _state.Disposed = true; TryCatch tryCatch = new TryCatch(); tryCatch.Catch(delegate { _monitor?.Dispose(); }); HeaderPage header = _header; if (header != null && header.Pragmas.Checkpoint > 0) { tryCatch.Catch(delegate { _walIndex?.TryCheckpoint(); }); } tryCatch.Catch(delegate { _disk?.Dispose(); }); tryCatch.Catch(delegate { _sortDisk?.Dispose(); }); tryCatch.Catch(delegate { _locker?.Dispose(); }); return tryCatch.Exceptions; } internal List Close(Exception ex) { if (_state.Disposed) { return new List(); } _state.Disposed = true; TryCatch tryCatch = new TryCatch(ex); tryCatch.Catch(delegate { _monitor?.Dispose(); }); tryCatch.Catch(delegate { _disk?.Dispose(); }); tryCatch.Catch(delegate { _sortDisk?.Dispose(); }); tryCatch.Catch(delegate { _locker?.Dispose(); }); if (tryCatch.InvalidDatafileState) { tryCatch.Catch(delegate { _disk.MarkAsInvalidState(); }); } return tryCatch.Exceptions; } public int Checkpoint() { return _walIndex.Checkpoint(); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { Close(); } private void InitializeSystemCollections() { RegisterSystemCollection("$database", () => SysDatabase()); RegisterSystemCollection("$cols", () => SysCols()); RegisterSystemCollection("$indexes", () => SysIndexes()); RegisterSystemCollection("$sequences", () => SysSequences()); RegisterSystemCollection("$transactions", () => SysTransactions()); RegisterSystemCollection("$snapshots", () => SysSnapshots()); RegisterSystemCollection("$open_cursors", () => SysOpenCursors()); RegisterSystemCollection(new SysFile()); RegisterSystemCollection(new SysDump(_header, _monitor)); RegisterSystemCollection(new SysPageList(_header, _monitor)); RegisterSystemCollection(new SysQuery(this)); } private IEnumerable SysCols() { foreach (KeyValuePair collection in _header.GetCollections()) { yield return new BsonDocument { ["name"] = collection.Key, ["type"] = "user" }; } foreach (KeyValuePair systemCollection in _systemCollections) { yield return new BsonDocument { ["name"] = systemCollection.Key, ["type"] = "system" }; } } private IEnumerable SysDatabase() { Version version = typeof(LiteEngine).GetTypeInfo().Assembly.GetName().Version; yield return new BsonDocument { ["name"] = _disk.GetName(FileOrigin.Data), ["encrypted"] = _settings.Password != null, ["readOnly"] = _settings.ReadOnly, ["lastPageID"] = (int)_header.LastPageID, ["freeEmptyPageID"] = (int)_header.FreeEmptyPageList, ["creationTime"] = _header.CreationTime, ["dataFileSize"] = (int)_disk.GetFileLength(FileOrigin.Data), ["logFileSize"] = (int)_disk.GetFileLength(FileOrigin.Log), ["currentReadVersion"] = _walIndex.CurrentReadVersion, ["lastTransactionID"] = _walIndex.LastTransactionID, ["engine"] = $"litedb-ce-v{version.Major}.{version.Minor}.{version.Build}", ["pragmas"] = new BsonDocument(_header.Pragmas.Pragmas.ToDictionary((Pragma x) => x.Name, (Pragma x) => x.Get())), ["cache"] = new BsonDocument { ["extendSegments"] = _disk.Cache.ExtendSegments, ["extendPages"] = _disk.Cache.ExtendPages, ["freePages"] = _disk.Cache.FreePages, ["readablePages"] = _disk.Cache.GetPages().Count, ["writablePages"] = _disk.Cache.WritablePages, ["pagesInUse"] = _disk.Cache.PagesInUse }, ["transactions"] = new BsonDocument { ["open"] = _monitor.Transactions.Count, ["maxOpenTransactions"] = 100, ["initialTransactionSize"] = _monitor.InitialSize, ["availableSize"] = _monitor.FreePages } }; } private IEnumerable SysIndexes() { TransactionService transaction = _monitor.GetThreadTransaction(); foreach (KeyValuePair collection in _header.GetCollections()) { Snapshot snapshot = transaction.CreateSnapshot(LockMode.Read, collection.Key, addIfNotExists: false); foreach (CollectionIndex collectionIndex in snapshot.CollectionPage.GetCollectionIndexes()) { yield return new BsonDocument { ["collection"] = collection.Key, ["name"] = collectionIndex.Name, ["expression"] = collectionIndex.Expression, ["unique"] = collectionIndex.Unique }; } } } private IEnumerable SysOpenCursors() { foreach (TransactionService transaction in _monitor.Transactions) { foreach (CursorInfo openCursor in transaction.OpenCursors) { yield return new BsonDocument { ["threadID"] = transaction.ThreadID, ["transactionID"] = (int)transaction.TransactionID, ["elapsedMS"] = (int)openCursor.Elapsed.ElapsedMilliseconds, ["collection"] = openCursor.Collection, ["mode"] = (openCursor.Query.ForUpdate ? "write" : "read"), ["sql"] = openCursor.Query.ToSQL(openCursor.Collection).Replace(Environment.NewLine, " "), ["running"] = openCursor.Elapsed.IsRunning, ["fetched"] = openCursor.Fetched }; } } } private IEnumerable SysSequences() { KeyValuePair[] array = _sequences.ToArray(); KeyValuePair[] array2 = array; for (int i = 0; i < array2.Length; i++) { KeyValuePair keyValuePair = array2[i]; yield return new BsonDocument { ["collection"] = keyValuePair.Key, ["value"] = keyValuePair.Value }; } } private IEnumerable SysSnapshots() { foreach (TransactionService transaction in _monitor.Transactions) { foreach (Snapshot snapshot in transaction.Snapshots) { yield return new BsonDocument { ["transactionID"] = (int)transaction.TransactionID, ["collection"] = snapshot.CollectionName, ["mode"] = snapshot.Mode.ToString(), ["readVersion"] = snapshot.ReadVersion, ["pagesInMemory"] = snapshot.LocalPages.Count, ["collectionDirty"] = snapshot.CollectionPage?.IsDirty ?? false }; } } } private IEnumerable SysTransactions() { foreach (TransactionService transaction in _monitor.Transactions) { yield return new BsonDocument { ["threadID"] = transaction.ThreadID, ["transactionID"] = (int)transaction.TransactionID, ["startTime"] = transaction.StartTime, ["mode"] = transaction.Mode.ToString(), ["transactionSize"] = transaction.Pages.TransactionSize, ["maxTransactionSize"] = transaction.MaxTransactionSize, ["pagesInLogFile"] = transaction.Pages.DirtyPages.Count, ["newPages"] = transaction.Pages.NewPages.Count, ["deletedPages"] = transaction.Pages.DeletedPages, ["modifiedPages"] = transaction.Snapshots.Select((Snapshot x) => x.GetWritablePages(dirty: true, includeCollectionPage: true).Count()).Sum() }; } } } internal class FileReaderError { public DateTime Created { get; } = DateTime.Now; public FileOrigin Origin { get; set; } public long Position { get; set; } public uint? PageID { get; set; } public PageType PageType { get; set; } public string Collection { get; set; } public string Message { get; set; } public Exception Exception { get; set; } } internal class FileReaderV7 : IFileReader, IDisposable { private const int V7_PAGE_SIZE = 4096; private readonly EngineSettings _settings; private Stream _stream; private AesEncryption _aes; private BsonDocument _header; private byte[] _buffer = new byte[4096]; private bool _disposedValue; private static readonly byte[] arrayByteEmpty = new byte[0]; public IDictionary GetPragmas() { return new Dictionary { { "USER_VERSION", _header["userVersion"].AsInt32 }, { "CHECKPOINT", 1000 }, { "TIMEOUT", 60 }, { "LIMIT_SIZE", long.MaxValue }, { "UTC_DATE", true } }; } public FileReaderV7(EngineSettings settings) { _settings = settings; } public void Open() { IStreamFactory streamFactory = _settings.CreateDataFactory(useAesStream: false); _stream = streamFactory.GetStream(canWrite: true, sequencial: true); _header = ReadPage(0u); if (_settings.Password == null && !_header["salt"].AsBinary.IsFullZero()) { throw LiteException.InvalidPassword(); } if (_settings.Password != null) { if (_header["salt"].AsBinary.IsFullZero()) { throw LiteException.FileNotEncrypted(); } if (!AesEncryption.HashSHA1(_settings.Password).SequenceEqual(_header["password"].AsBinary)) { throw LiteException.InvalidPassword(); } } _aes = ((_settings.Password == null) ? null : new AesEncryption(_settings.Password, _header["salt"].AsBinary)); } public static bool IsVersion(byte[] buffer) { string @string = Encoding.UTF8.GetString(buffer, 25, "** This is a LiteDB file **".Length); byte b = buffer[52]; if (@string == "** This is a LiteDB file **") { return b == 7; } return false; } public IEnumerable GetCollections() { return _header["collections"].AsDocument.Keys; } public IEnumerable GetIndexes(string collection) { uint asInt = (uint)_header["collections"].AsDocument[collection].AsInt32; BsonDocument bsonDocument = ReadPage(asInt); foreach (BsonValue item in bsonDocument["indexes"].AsArray) { string text = Regex.Replace(item["name"].AsString, "[^a-z0-9]", "", RegexOptions.IgnoreCase | RegexOptions.Compiled); if (text.Length > Constants.INDEX_NAME_MAX_LENGTH) { text = text.Substring(0, Constants.INDEX_NAME_MAX_LENGTH); } yield return new IndexInfo { Collection = collection, Name = text, Expression = item["expression"].AsString, Unique = item["unique"].AsBoolean }; } } public IEnumerable GetDocuments(string collection) { uint asInt = (uint)_header["collections"].AsDocument[collection].AsInt32; uint asInt2 = (uint)ReadPage(asInt)["indexes"][0]["headPageID"].AsInt32; HashSet hashSet = VisitIndexPages(asInt2); foreach (uint item in hashSet) { BsonDocument bsonDocument = ReadPage(item); foreach (BsonValue item2 in bsonDocument["nodes"].AsArray) { BsonValue dataBlock = item2["dataBlock"]; if (dataBlock["pageID"].AsInt32 == -1) { continue; } BsonDocument bsonDocument2 = ReadPage((uint)dataBlock["pageID"].AsInt32); if (bsonDocument2["pageType"].AsInt32 != 4) { continue; } BsonDocument bsonDocument3 = bsonDocument2["blocks"].AsArray.FirstOrDefault((BsonValue x) => x["index"] == dataBlock["index"])?.AsDocument; if (bsonDocument3 == null) { continue; } byte[] array = ((bsonDocument3["extendPageID"] == -1) ? bsonDocument3["data"].AsBinary : ReadExtendData((uint)bsonDocument3["extendPageID"].AsInt32)); if (array.Length == 0) { continue; } BsonDocument bsonDocument4 = BsonSerializer.Deserialize(array); if (collection == "_chunks") { string[] array2 = bsonDocument4["_id"].AsString.Split(new char[1] { '\\' }); if (!int.TryParse(array2[1], out var result)) { throw LiteException.InvalidFormat("_id"); } bsonDocument4["_id"] = new BsonDocument { ["f"] = array2[0], ["n"] = result }; } yield return bsonDocument4; } } } private BsonDocument ReadPage(uint pageID) { if (pageID * 4096 > _stream.Length) { return null; } _stream.Position = pageID * 4096; _stream.Read(_buffer, 0, 4096); if (_aes != null && pageID != 0) { _buffer = _aes.Decrypt(_buffer); } ByteReader byteReader = new ByteReader(_buffer); BsonDocument bsonDocument = new BsonDocument { ["pageID"] = (int)byteReader.ReadUInt32(), ["pageType"] = (int)byteReader.ReadByte(), ["prevPageID"] = (int)byteReader.ReadUInt32(), ["nextPageID"] = (int)byteReader.ReadUInt32(), ["itemCount"] = (int)byteReader.ReadUInt16() }; byteReader.ReadBytes(10); if (bsonDocument["pageType"] == 1) { string strA = byteReader.ReadString(27); byte b = byteReader.ReadByte(); if (string.CompareOrdinal(strA, "** This is a LiteDB file **") != 0 || b != 7) { throw LiteException.InvalidDatabase(); } byteReader.ReadBytes(10); bsonDocument["userVersion"] = (int)byteReader.ReadUInt16(); bsonDocument["password"] = byteReader.ReadBytes(20); bsonDocument["salt"] = byteReader.ReadBytes(16); bsonDocument["collections"] = new BsonDocument(); byte b2 = byteReader.ReadByte(); for (int i = 0; i < b2; i++) { string name = byteReader.ReadString(); uint num = byteReader.ReadUInt32(); bsonDocument["collections"][name] = (int)num; } } else if (bsonDocument["pageType"] == 2) { bsonDocument["collectionName"] = byteReader.ReadString(); bsonDocument["indexes"] = new BsonArray(); byteReader.ReadBytes(12); for (int j = 0; j < 16; j++) { BsonDocument bsonDocument2 = new BsonDocument(); string text = byteReader.ReadString(); int num2 = text.IndexOf('='); if (num2 > 0) { bsonDocument2["name"] = text.Substring(0, num2); bsonDocument2["expression"] = text.Substring(num2 + 1); } else { bsonDocument2["name"] = text; bsonDocument2["expression"] = "$." + text; } bsonDocument2["unique"] = byteReader.ReadBoolean(); bsonDocument2["headPageID"] = (int)byteReader.ReadUInt32(); byteReader.ReadBytes(12); if (text.Length > 0) { bsonDocument["indexes"].AsArray.Add(bsonDocument2); } } } else if (bsonDocument["pageType"] == 3) { bsonDocument["nodes"] = new BsonArray(); for (int k = 0; k < bsonDocument["itemCount"].AsInt32; k++) { BsonDocument bsonDocument3 = new BsonDocument { ["index"] = (int)byteReader.ReadUInt16() }; byte b3 = byteReader.ReadByte(); byteReader.ReadBytes(13); ushort num3 = byteReader.ReadUInt16(); byteReader.ReadBytes(1 + num3); bsonDocument3["dataBlock"] = new BsonDocument { ["pageID"] = (int)byteReader.ReadUInt32(), ["index"] = (int)byteReader.ReadUInt16() }; bsonDocument3["prev"] = new BsonDocument { ["pageID"] = (int)byteReader.ReadUInt32(), ["index"] = (int)byteReader.ReadUInt16() }; bsonDocument3["next"] = new BsonDocument { ["pageID"] = (int)byteReader.ReadUInt32(), ["index"] = (int)byteReader.ReadUInt16() }; byteReader.ReadBytes((b3 - 1) * 12); bsonDocument["nodes"].AsArray.Add(bsonDocument3); } } else if (bsonDocument["pageType"] == 4) { bsonDocument["blocks"] = new BsonArray(); for (int l = 0; l < bsonDocument["itemCount"].AsInt32; l++) { BsonDocument bsonDocument4 = new BsonDocument { ["index"] = (int)byteReader.ReadUInt16(), ["extendPageID"] = (int)byteReader.ReadUInt32() }; ushort count = byteReader.ReadUInt16(); bsonDocument4["data"] = byteReader.ReadBytes(count); bsonDocument["blocks"].AsArray.Add(bsonDocument4); } } else if (bsonDocument["pageType"] == 5) { bsonDocument["data"] = byteReader.ReadBytes(bsonDocument["itemCount"].AsInt32); } return bsonDocument; } private byte[] ReadExtendData(uint extendPageID) { using MemoryStream memoryStream = new MemoryStream(); while (extendPageID != uint.MaxValue) { BsonDocument bsonDocument = ReadPage(extendPageID); if (bsonDocument["pageType"].AsInt32 != 5) { return arrayByteEmpty; } memoryStream.Write(bsonDocument["data"].AsBinary, 0, bsonDocument["itemCount"].AsInt32); extendPageID = (uint)bsonDocument["nextPageID"].AsInt32; } return memoryStream.ToArray(); } private HashSet VisitIndexPages(uint startPageID) { HashSet hashSet = new HashSet { startPageID }; HashSet hashSet2 = new HashSet(); while (hashSet.Count > 0) { uint num = hashSet.First(); hashSet.Remove(num); BsonDocument bsonDocument = ReadPage(num); if (bsonDocument == null || bsonDocument["pageType"] != 3) { continue; } hashSet2.Add(num); foreach (BsonValue item in bsonDocument["nodes"].AsArray) { uint asInt = (uint)item["prev"]["pageID"].AsInt32; uint asInt2 = (uint)item["next"]["pageID"].AsInt32; if (!hashSet2.Contains(asInt)) { hashSet.Add(asInt); } if (!hashSet2.Contains(asInt2)) { hashSet.Add(asInt2); } } } return hashSet2; } protected virtual void Dispose(bool disposing) { if (!_disposedValue) { if (disposing) { _stream?.Dispose(); _aes?.Dispose(); } _disposedValue = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } internal class FileReaderV8 : IFileReader, IDisposable { private struct PageInfo { public uint PageID; public FileOrigin Origin; public PageType PageType; public long Position; public uint ColID; } private readonly Dictionary _collections = new Dictionary(); private readonly Dictionary> _indexes = new Dictionary>(); private readonly Dictionary> _collectionsDataPages = new Dictionary>(); private readonly Dictionary _pragmas = new Dictionary { ["USER_VERSION"] = 0, ["CHECKPOINT"] = 1000, ["TIMEOUT"] = 60, ["UTC_DATE"] = true, ["LIMIT_SIZE"] = long.MaxValue }; private Stream _dataStream; private Stream _logStream; private readonly IDictionary _logIndexMap = new Dictionary(); private uint _maxPageID; private bool _disposed; private readonly EngineSettings _settings; private readonly IList _errors; public FileReaderV8(EngineSettings settings, IList errors) { _settings = settings; _errors = errors; } public void Open() { try { IStreamFactory streamFactory = _settings.CreateDataFactory(); IStreamFactory streamFactory2 = _settings.CreateLogFactory(); _maxPageID = (uint)((streamFactory.GetLength() + streamFactory2.GetLength()) / 8192); _dataStream = streamFactory.GetStream(canWrite: true, sequencial: false); _dataStream.Position = 0L; if (streamFactory2.Exists()) { _logStream = streamFactory2.GetStream(canWrite: false, sequencial: true); LoadIndexMap(); } LoadPragmas(); LoadDataPages(); LoadCollections(); LoadIndexes(); } catch (Exception ex) { HandleError(ex, default(PageInfo)); } } public IDictionary GetPragmas() { return _pragmas; } public IEnumerable GetCollections() { return _collections.Keys; } public IEnumerable GetIndexes(string collection) { if (!_indexes.ContainsKey(collection)) { return new List(); } return _indexes[collection]; } public IEnumerable GetDocuments(string collection) { if (!_collections.ContainsKey(collection)) { yield break; } uint colID = _collections[collection]; if (!_collectionsDataPages.ContainsKey(colID)) { yield break; } List list = _collectionsDataPages[colID]; HashSet uniqueIDs = new HashSet(); foreach (uint item in list) { PageInfo pageInfo; Result result = ReadPage(item, out pageInfo); if (result.Fail) { HandleError(result.Exception, pageInfo); continue; } PageBuffer buffer = result.Value.Buffer; byte itemsCount = result.Value.ItemsCount; byte highestIndex = result.Value.HighestIndex; if (itemsCount == 0 || highestIndex == byte.MaxValue) { continue; } for (int i = 0; i <= highestIndex; i++) { BsonDocument bsonDocument; try { int offset = BasePage.CalcPositionAddr((byte)i); int offset2 = BasePage.CalcLengthAddr((byte)i); ushort num = buffer.ReadUInt16(offset); ushort num2 = buffer.ReadUInt16(offset2); if (num == 0) { continue; } Constants.ENSURE(num > 0 && num2 > 0, "Invalid footer ref position {0} with length {1}", num, num2); Constants.ENSURE(num + num2 < 8192, "Invalid footer ref position {0} with length {1}", num, num2); BufferSlice bufferSlice = buffer.Slice(num, num2); bool num3 = bufferSlice.ReadBool(0); PageAddress pageAddress = bufferSlice.ReadPageAddress(1); BufferSlice bufferSlice2 = bufferSlice.Slice(6, bufferSlice.Count - 6); if (num3) { continue; } using (MemoryStream memoryStream = new MemoryStream()) { memoryStream.Write(bufferSlice2.Array, bufferSlice2.Offset, bufferSlice2.Count); while (!pageAddress.IsEmpty) { Result result2 = ReadPage(pageAddress.PageID, out pageInfo); if (result2.Fail) { throw result2.Exception; } PageBuffer buffer2 = result2.Value.Buffer; Constants.ENSURE(result2.Value.PageType == PageType.Data, "Invalid PageType (excepted Data, get {0})", result2.Value.PageType); Constants.ENSURE(result2.Value.ColID == colID, "Invalid ColID in this page (expected {0}, get {1})", colID, result2.Value.ColID); Constants.ENSURE(result2.Value.ItemsCount > 0, "Page with no items count"); offset = BasePage.CalcPositionAddr(pageAddress.Index); offset2 = BasePage.CalcLengthAddr(pageAddress.Index); num = buffer2.ReadUInt16(offset); num2 = buffer2.ReadUInt16(offset2); Constants.ENSURE(num2 > 0, "Last DataBlock request a next extend to {0}, but this block are empty footer", pageAddress); bufferSlice = buffer2.Slice(num, num2); bool conditional = bufferSlice.ReadBool(0); pageAddress = bufferSlice.ReadPageAddress(1); bufferSlice2 = bufferSlice.Slice(6, bufferSlice.Count - 6); Constants.ENSURE(conditional, "Next datablock always be an extend. Invalid data block {0}", pageAddress); memoryStream.Write(bufferSlice2.Array, bufferSlice2.Offset, bufferSlice2.Count); } using BufferReader bufferReader = new BufferReader(memoryStream.ToArray()); Result result3 = bufferReader.ReadDocument(); BsonValue bsonValue = result3.Value["_id"]; Constants.ENSURE(!(bsonValue == BsonValue.Null) && !(bsonValue == BsonValue.MinValue) && !(bsonValue == BsonValue.MaxValue), "Invalid _id value: {0}", bsonValue); Constants.ENSURE(!uniqueIDs.Contains(bsonValue), "Duplicated _id value: {0}", bsonValue); uniqueIDs.Add(bsonValue); if (result3.Fail) { HandleError(result3.Exception, pageInfo); } bsonDocument = result3.Value; } goto IL_04c5; } catch (Exception ex) { HandleError(ex, pageInfo); bsonDocument = null; goto IL_04c5; } IL_04c5: if (bsonDocument != null) { yield return bsonDocument; } } } } private void LoadPragmas() { PageInfo pageInfo; Result result = ReadPage(0u, out pageInfo); if (result.Ok) { PageBuffer buffer = result.Value.Buffer; _pragmas["USER_VERSION"] = buffer.ReadInt32(76); _pragmas["CHECKPOINT"] = buffer.ReadInt32(97); _pragmas["TIMEOUT"] = buffer.ReadInt32(88); _pragmas["UTC_DATE"] = buffer.ReadBool(96); _pragmas["LIMIT_SIZE"] = buffer.ReadInt64(101); } else { HandleError(result.Exception, pageInfo); } } private void LoadDataPages() { PageInfo pageInfo; uint num = ReadPage(0u, out pageInfo).GetValue().Buffer.ReadUInt32(64); Constants.ENSURE(num <= _maxPageID, "LastPageID {0} should be less or equals to maxPageID {1}", num, _maxPageID); for (uint num2 = 0u; num2 <= num; num2++) { Result result = ReadPage(num2, out pageInfo); if (result.Ok) { BasePage value = result.Value; if (value.PageType == PageType.Data) { if (_collectionsDataPages.TryGetValue(value.ColID, out var value2)) { value2.Add(value.PageID); continue; } _collectionsDataPages[value.ColID] = new List { value.PageID }; } } else { HandleError(result.Exception, pageInfo); } } } private void LoadCollections() { PageInfo pageInfo; BufferSlice bufferSlice = ReadPage(0u, out pageInfo).GetValue().Buffer.Slice(192, 8000); using (BufferReader bufferReader = new BufferReader(new BufferSlice[1] { bufferSlice })) { Result result = bufferReader.ReadDocument(); BsonDocument value = result.Value; foreach (string key in value.Keys) { BsonValue bsonValue = value[key]; if (!bsonValue.IsNumber) { HandleError($"ColID expect a number but get {bsonValue}", pageInfo); } else { _collections[key] = (uint)value[key].AsInt32; } } if (result.Fail) { HandleError(result.Exception, pageInfo); } } foreach (KeyValuePair> collectionsDataPage in _collectionsDataPages) { if (!_collections.ContainsValue(collectionsDataPage.Key)) { _collections["col_" + collectionsDataPage.Key] = collectionsDataPage.Key; } } } private void LoadIndexes() { foreach (KeyValuePair collection in _collections) { PageInfo pageInfo; Result result = ReadPage(collection.Value, out pageInfo); if (result.Fail) { HandleError(result.Exception, pageInfo); continue; } PageBuffer buffer = result.Value.Buffer; byte b = buffer.ReadByte(96); int num = 97; try { for (int i = 0; i < b; i++) { num += 2; int length; string text = buffer.ReadCString(num, out length); num += length; int length2; string text2 = buffer.ReadCString(num, out length2); num += length2; bool unique = buffer.ReadBool(num); num++; num += 15; Constants.ENSURE(!string.IsNullOrEmpty(text), "Index name can't be empty (collection {0} - index: {1})", collection.Key, i); Constants.ENSURE(!string.IsNullOrEmpty(text2), "Index expression can't be empty (collection {0} - index: {1})", collection.Key, i); IndexInfo item = new IndexInfo { Collection = collection.Key, Name = text, Expression = text2, Unique = unique }; if (!(text == "_id")) { if (_indexes.TryGetValue(collection.Key, out var value)) { value.Add(item); continue; } _indexes[collection.Key] = new List { item }; } } } catch (Exception ex) { HandleError(ex, pageInfo); } } } public static bool IsVersion(byte[] buffer) { string @string = Encoding.UTF8.GetString(buffer, 32, "** This is a LiteDB file **".Length); byte b = buffer[59]; if (!(@string == "** This is a LiteDB file **") || b != 8) { return buffer[0] == 1; } return true; } private void LoadIndexMap() { PageBuffer pageBuffer = new PageBuffer(new byte[8192], 0, 0); Dictionary> dictionary = new Dictionary>(); List list = new List(); long num = 0L; PageInfo pageInfo = default(PageInfo); pageInfo.Origin = FileOrigin.Log; PageInfo pageInfo2 = pageInfo; _logStream.Position = 0L; while (_logStream.Position < _logStream.Length) { try { _logStream.Position = (pageInfo2.Position = num); int num2 = _logStream.Read(pageBuffer.Array, pageBuffer.Offset, 8192); if (pageBuffer.IsBlank()) { num += 8192; continue; } uint pageID = pageBuffer.ReadUInt32(0); bool flag = pageBuffer.ReadBool(18); uint num3 = pageBuffer.ReadUInt32(14); pageInfo2.PageID = pageID; pageInfo2.ColID = pageBuffer.ReadUInt32(19); Constants.ENSURE(num2 == 8192, "Page position {0} read only than {1} bytes (instead {2})", _logStream, num2, 8192); PagePosition item = new PagePosition(pageID, num); if (dictionary.TryGetValue(num3, out var value)) { value.Add(item); } else { dictionary[num3] = new List { item }; } if (flag) { list.Add(num3); } } catch (Exception ex) { HandleError(ex, pageInfo2); } finally { num += 8192; } } foreach (uint item2 in list) { foreach (PagePosition item3 in dictionary[item2]) { _logIndexMap[item3.PageID] = item3.Position; } } } private Result ReadPage(uint pageID, out PageInfo pageInfo) { pageInfo = new PageInfo { PageID = pageID }; try { Constants.ENSURE(pageID <= _maxPageID, "PageID: {0} should be less then or equals to maxPageID: {1}", pageID, _maxPageID); PageBuffer pageBuffer = new PageBuffer(new byte[8192], 0, 8192); Stream stream; if (_logIndexMap.TryGetValue(pageID, out pageInfo.Position)) { pageInfo.Origin = FileOrigin.Log; stream = _logStream; } else { pageInfo.Origin = FileOrigin.Data; pageInfo.Position = BasePage.GetPagePosition(pageID); stream = _dataStream; } stream.Position = pageInfo.Position; int num = stream.Read(pageBuffer.Array, pageBuffer.Offset, pageBuffer.Count); Constants.ENSURE(num == 8192, "Page position {0} read only than {1} bytes (instead {2})", stream.Position, num, 8192); BasePage basePage = new BasePage(pageBuffer); pageInfo.ColID = basePage.ColID; Constants.ENSURE(basePage.PageID == pageID, "Expect read pageID: {0} but header contains pageID: {1}", pageID, basePage.PageID); return basePage; } catch (Exception ex) { return new Result(null, ex); } } private void HandleError(Exception ex, PageInfo pageInfo) { string key = _collections.FirstOrDefault((KeyValuePair x) => x.Value == pageInfo.ColID).Key; _errors.Add(new FileReaderError { Position = pageInfo.Position, Origin = pageInfo.Origin, PageID = pageInfo.PageID, PageType = pageInfo.PageType, Collection = key, Message = ex.Message, Exception = ex }); if (ex is IOException) { throw ex; } } private void HandleError(string message, PageInfo pageInfo) { HandleError(new Exception(message), pageInfo); } protected virtual void Dispose(bool disposing) { if (!_disposed) { _dataStream?.Dispose(); _logStream?.Dispose(); _disposed = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } internal interface IFileReader : IDisposable { void Open(); IDictionary GetPragmas(); IEnumerable GetCollections(); IEnumerable GetIndexes(string name); IEnumerable GetDocuments(string collection); } internal class IndexInfo { public string Collection { get; set; } public string Name { get; set; } public string Expression { get; set; } public bool Unique { get; set; } } internal interface ILiteEngine : IDisposable { int Checkpoint(); long Rebuild(RebuildOptions options); bool BeginTrans(); bool Commit(); bool Rollback(); IBsonDataReader Query(string collection, Query query); int Insert(string collection, IEnumerable docs, BsonAutoId autoId); int Update(string collection, IEnumerable docs); int UpdateMany(string collection, BsonExpression transform, BsonExpression predicate); int Upsert(string collection, IEnumerable docs, BsonAutoId autoId); int Delete(string collection, IEnumerable ids); int DeleteMany(string collection, BsonExpression predicate); bool DropCollection(string name); bool RenameCollection(string name, string newName); bool EnsureIndex(string collection, string name, BsonExpression expression, bool unique); bool DropIndex(string collection, string name); BsonValue Pragma(string name); bool Pragma(string name, BsonValue value); } internal enum PageType { Empty, Header, Collection, Index, Data } internal class BasePage { protected readonly PageBuffer _buffer; public const int SLOT_SIZE = 4; public const int P_PAGE_ID = 0; public const int P_PAGE_TYPE = 4; public const int P_PREV_PAGE_ID = 5; public const int P_NEXT_PAGE_ID = 9; public const int P_INITIAL_SLOT = 13; public const int P_TRANSACTION_ID = 14; public const int P_IS_CONFIRMED = 18; public const int P_COL_ID = 19; public const int P_ITEMS_COUNT = 23; public const int P_USED_BYTES = 24; public const int P_FRAGMENTED_BYTES = 26; public const int P_NEXT_FREE_POSITION = 28; public const int P_HIGHEST_INDEX = 30; private byte _startIndex; public uint PageID { get; } public PageType PageType { get; private set; } public uint PrevPageID { get; set; } public uint NextPageID { get; set; } public byte PageListSlot { get; set; } public byte ItemsCount { get; private set; } public ushort UsedBytes { get; private set; } public ushort FragmentedBytes { get; private set; } public ushort NextFreePosition { get; private set; } public byte HighestIndex { get; private set; } public int FreeBytes { get { if (ItemsCount != byte.MaxValue) { return 8160 - UsedBytes - FooterSize; } return 0; } } public int FooterSize { get { if (HighestIndex != byte.MaxValue) { return (HighestIndex + 1) * 4; } return 0; } } public uint ColID { get; set; } public uint TransactionID { get; set; } public bool IsConfirmed { get; set; } public bool IsDirty { get; set; } public PageBuffer Buffer => _buffer; public BasePage(PageBuffer buffer, uint pageID, PageType pageType) { _buffer = buffer; PageID = pageID; PageType = pageType; PrevPageID = uint.MaxValue; NextPageID = uint.MaxValue; PageListSlot = byte.MaxValue; ColID = uint.MaxValue; TransactionID = uint.MaxValue; IsConfirmed = false; ItemsCount = 0; UsedBytes = 0; FragmentedBytes = 0; NextFreePosition = 32; HighestIndex = byte.MaxValue; IsDirty = false; _buffer.Write(PageID, 0); _buffer.Write((byte)PageType, 4); } public BasePage(PageBuffer buffer) { _buffer = buffer; PageID = _buffer.ReadUInt32(0); PageType = (PageType)_buffer.ReadByte(4); PrevPageID = _buffer.ReadUInt32(5); NextPageID = _buffer.ReadUInt32(9); PageListSlot = _buffer.ReadByte(13); TransactionID = _buffer.ReadUInt32(14); IsConfirmed = _buffer.ReadBool(18); ColID = _buffer.ReadUInt32(19); ItemsCount = _buffer.ReadByte(23); UsedBytes = _buffer.ReadUInt16(24); FragmentedBytes = _buffer.ReadUInt16(26); NextFreePosition = _buffer.ReadUInt16(28); HighestIndex = _buffer.ReadByte(30); } public virtual PageBuffer UpdateBuffer() { Constants.ENSURE(PageID == _buffer.ReadUInt32(0), "pageID can't be changed"); _buffer.Write(PrevPageID, 5); _buffer.Write(NextPageID, 9); _buffer.Write(PageListSlot, 13); _buffer.Write(TransactionID, 14); _buffer.Write(IsConfirmed, 18); _buffer.Write(ColID, 19); _buffer.Write(ItemsCount, 23); _buffer.Write(UsedBytes, 24); _buffer.Write(FragmentedBytes, 26); _buffer.Write(NextFreePosition, 28); _buffer.Write(HighestIndex, 30); return _buffer; } public void MarkAsEmtpy() { IsDirty = true; PageType = PageType.Empty; PrevPageID = uint.MaxValue; NextPageID = uint.MaxValue; PageListSlot = byte.MaxValue; ColID = uint.MaxValue; TransactionID = uint.MaxValue; IsConfirmed = false; ItemsCount = 0; UsedBytes = 0; FragmentedBytes = 0; NextFreePosition = 32; HighestIndex = byte.MaxValue; _buffer.Clear(32, 8160); _buffer.Write((byte)PageType, 4); } public BufferSlice Get(byte index) { Constants.ENSURE(ItemsCount > 0, "should have items in this page"); Constants.ENSURE(HighestIndex != byte.MaxValue, "should have at least 1 index in this page"); Constants.ENSURE(index <= HighestIndex, "get only index below highest index"); int offset = CalcPositionAddr(index); int offset2 = CalcLengthAddr(index); ushort num = _buffer.ReadUInt16(offset); ushort num2 = _buffer.ReadUInt16(offset2); Constants.ENSURE(IsValidPos(num), "invalid segment position in index footer: {0}/{1}", this, index); Constants.ENSURE(IsValidLen(num2), "invalid segment length in index footer: {0}/{1}", this, index); return _buffer.Slice(num, num2); } public BufferSlice Insert(ushort bytesLength, out byte index) { index = byte.MaxValue; return InternalInsert(bytesLength, ref index); } private BufferSlice InternalInsert(ushort bytesLength, ref byte index) { bool flag = index == byte.MaxValue; Constants.ENSURE(_buffer.ShareCounter == Constants.BUFFER_WRITABLE, "page must be writable to support changes"); Constants.ENSURE(bytesLength > 0, "must insert more than 0 bytes"); Constants.ENSURE(FreeBytes >= bytesLength + (flag ? 4 : 0), "length must be always lower than current free space"); Constants.ENSURE(ItemsCount < byte.MaxValue, "page full"); Constants.ENSURE(FreeBytes >= FragmentedBytes, "fragmented bytes must be at most free bytes"); if (FreeBytes < bytesLength + (flag ? 4 : 0)) { throw LiteException.InvalidFreeSpacePage(PageID, FreeBytes, bytesLength + (flag ? 4 : 0)); } int num = FreeBytes - FragmentedBytes - (flag ? 4 : 0); Constants.ENSURE(num == 8192 - NextFreePosition - FooterSize - (flag ? 4 : 0), "continuousBlock must be same as from NextFreePosition"); if (bytesLength > num) { Defrag(); } if (index == byte.MaxValue) { index = GetFreeIndex(); } if (index > HighestIndex || HighestIndex == byte.MaxValue) { Constants.ENSURE(index == (byte)(HighestIndex + 1), "new index must be next highest index"); HighestIndex = index; } int offset = CalcPositionAddr(index); int offset2 = CalcLengthAddr(index); Constants.ENSURE(_buffer.ReadUInt16(offset) == 0, "slot position must be empty before use"); Constants.ENSURE(_buffer.ReadUInt16(offset2) == 0, "slot length must be empty before use"); ushort nextFreePosition = NextFreePosition; _buffer.Write(nextFreePosition, offset); _buffer.Write(bytesLength, offset2); ItemsCount++; UsedBytes += bytesLength; NextFreePosition += bytesLength; IsDirty = true; Constants.ENSURE(nextFreePosition + bytesLength <= 8192 - (HighestIndex + 1) * 4, "new buffer slice could not override footer area"); return _buffer.Slice(nextFreePosition, bytesLength); } public void Delete(byte index) { Constants.ENSURE(_buffer.ShareCounter == Constants.BUFFER_WRITABLE, "page must be writable to support changes"); int offset = CalcPositionAddr(index); int offset2 = CalcLengthAddr(index); ushort num = _buffer.ReadUInt16(offset); ushort num2 = _buffer.ReadUInt16(offset2); Constants.ENSURE(IsValidPos(num), "invalid segment position"); Constants.ENSURE(IsValidLen(num2), "invalid segment length"); ((BufferSlice)_buffer).Write((ushort)0, offset); ((BufferSlice)_buffer).Write((ushort)0, offset2); ItemsCount--; UsedBytes -= num2; _buffer.Array.Fill(0, _buffer.Offset + num, num2); if (num + num2 == NextFreePosition) { NextFreePosition = num; } else { FragmentedBytes += num2; } if (HighestIndex == index) { UpdateHighestIndex(); } _startIndex = 0; if (ItemsCount == 0) { Constants.ENSURE(HighestIndex == byte.MaxValue, "if there is no items, HighestIndex must be clear"); Constants.ENSURE(UsedBytes == 0, "should be no bytes used in clean page"); NextFreePosition = 32; FragmentedBytes = 0; } IsDirty = true; } public BufferSlice Update(byte index, ushort bytesLength) { Constants.ENSURE(_buffer.ShareCounter == Constants.BUFFER_WRITABLE, "page must be writable to support changes"); Constants.ENSURE(bytesLength > 0, "must update more than 0 bytes"); int offset = CalcPositionAddr(index); int offset2 = CalcLengthAddr(index); ushort num = _buffer.ReadUInt16(offset); ushort num2 = _buffer.ReadUInt16(offset2); Constants.ENSURE(IsValidPos(num), "invalid segment position"); Constants.ENSURE(IsValidLen(num2), "invalid segment length"); bool flag = num + num2 == NextFreePosition; IsDirty = true; if (bytesLength == num2) { return _buffer.Slice(num, num2); } if (bytesLength < num2) { ushort num3 = (ushort)(num2 - bytesLength); if (flag) { NextFreePosition -= num3; } else { FragmentedBytes += num3; } UsedBytes -= num3; _buffer.Write(bytesLength, offset2); _buffer.Clear(num + bytesLength, num3); return _buffer.Slice(num, bytesLength); } _buffer.Clear(num, num2); ItemsCount--; UsedBytes -= num2; if (flag) { NextFreePosition = num; } else { FragmentedBytes += num2; } ((BufferSlice)_buffer).Write((ushort)0, offset); ((BufferSlice)_buffer).Write((ushort)0, offset2); return InternalInsert(bytesLength, ref index); } public void Defrag() { Constants.ENSURE(FragmentedBytes > 0, "do not call this when page has no fragmentation"); Constants.ENSURE(_buffer.ShareCounter == Constants.BUFFER_WRITABLE, "page must be writable to support changes"); Constants.ENSURE(HighestIndex < byte.MaxValue, "there is no items in this page to run defrag"); SortedList sortedList = new SortedList(); for (int i = 0; i <= HighestIndex; i++) { int offset = CalcPositionAddr((byte)i); ushort num = _buffer.ReadUInt16(offset); if (num != 0) { Constants.ENSURE(IsValidPos(num), "invalid segment position"); sortedList.Add(num, (byte)i); } } ushort num2 = 32; foreach (KeyValuePair item in sortedList) { byte value = item.Value; ushort key = item.Key; int offset2 = CalcLengthAddr(value); ushort num3 = _buffer.ReadUInt16(offset2); Constants.ENSURE(IsValidLen(num3), "invalid segment length"); if (key != num2) { Constants.ENSURE(key > num2, "current segment position must be greater than current empty space"); System.Buffer.BlockCopy(_buffer.Array, _buffer.Offset + key, _buffer.Array, _buffer.Offset + num2, num3); int offset3 = CalcPositionAddr(value); _buffer.Write(num2, offset3); } num2 += num3; } int count = 8192 - num2 - FooterSize; _buffer.Array.Fill(0, _buffer.Offset + num2, count); FragmentedBytes = 0; NextFreePosition = num2; } private byte GetFreeIndex() { for (byte b = _startIndex; b <= HighestIndex; b++) { int offset = CalcPositionAddr(b); if (_buffer.ReadUInt16(offset) == 0) { _startIndex = (byte)(b + 1); return b; } } return (byte)(HighestIndex + 1); } public IEnumerable GetUsedIndexs() { if (ItemsCount == 0) { yield break; } Constants.ENSURE(HighestIndex != byte.MaxValue, "if has items count, Highest index should be not empty"); for (byte index = 0; index <= HighestIndex; index++) { int offset = CalcPositionAddr(index); if (_buffer.ReadUInt16(offset) != 0) { yield return index; } } } private void UpdateHighestIndex() { Constants.ENSURE(HighestIndex < byte.MaxValue, "can run only if contains a valid HighestIndex"); if (HighestIndex == 0) { HighestIndex = byte.MaxValue; return; } for (int num = HighestIndex - 1; num >= 0; num--) { int offset = CalcPositionAddr((byte)num); ushort num2 = _buffer.ReadUInt16(offset); if (num2 != 0) { Constants.ENSURE(IsValidPos(num2), "invalid segment position"); HighestIndex = (byte)num; return; } } HighestIndex = byte.MaxValue; } private bool IsValidPos(ushort position) { if (position >= 32) { return position < 8192 - FooterSize; } return false; } private bool IsValidLen(ushort length) { if (length > 0) { return length <= 8160 - FooterSize; } return false; } public static int CalcPositionAddr(byte index) { return 8192 - (index + 1) * 4 + 2; } public static int CalcLengthAddr(byte index) { return 8192 - (index + 1) * 4; } public static long GetPagePosition(uint pageID) { checked { return unchecked((long)pageID) * 8192L; } } public static long GetPagePosition(int pageID) { Constants.ENSURE(pageID >= 0, "page could not be less than 0."); return GetPagePosition((uint)pageID); } public static T ReadPage(PageBuffer buffer) where T : BasePage { if (typeof(T) == typeof(BasePage)) { return (T)new BasePage(buffer); } if (typeof(T) == typeof(HeaderPage)) { return (T)(BasePage)new HeaderPage(buffer); } if (typeof(T) == typeof(CollectionPage)) { return (T)(BasePage)new CollectionPage(buffer); } if (typeof(T) == typeof(IndexPage)) { return (T)(BasePage)new IndexPage(buffer); } if (typeof(T) == typeof(DataPage)) { return (T)(BasePage)new DataPage(buffer); } throw new InvalidCastException(); } public static T CreatePage(PageBuffer buffer, uint pageID) where T : BasePage { if (typeof(T) == typeof(CollectionPage)) { return (T)(BasePage)new CollectionPage(buffer, pageID); } if (typeof(T) == typeof(IndexPage)) { return (T)(BasePage)new IndexPage(buffer, pageID); } if (typeof(T) == typeof(DataPage)) { return (T)(BasePage)new DataPage(buffer, pageID); } throw new InvalidCastException(); } public override string ToString() { return $"PageID: {PageID.ToString().PadLeft(4, '0')} : {PageType} ({ItemsCount} Items)"; } } internal class CollectionPage : BasePage { public const int P_INDEXES = 96; public const int P_INDEXES_COUNT = 8096; private readonly Dictionary _indexes = new Dictionary(); public uint[] FreeDataPageList { get; } = new uint[5]; public CollectionIndex PK => _indexes["_id"]; public CollectionPage(PageBuffer buffer, uint pageID) : base(buffer, pageID, PageType.Collection) { for (int i = 0; i < 5; i++) { FreeDataPageList[i] = uint.MaxValue; } } public CollectionPage(PageBuffer buffer) : base(buffer) { Constants.ENSURE(base.PageType == PageType.Collection, "page type must be collection page"); if (base.PageType != PageType.Collection) { throw LiteException.InvalidPageType(PageType.Collection, this); } BufferSlice bufferSlice = _buffer.Slice(32, 8160); using BufferReader bufferReader = new BufferReader(new BufferSlice[1] { bufferSlice }); for (int i = 0; i < 5; i++) { FreeDataPageList[i] = bufferReader.ReadUInt32(); } bufferReader.Skip(64 - bufferReader.Position); byte b = bufferReader.ReadByte(); for (int j = 0; j < b; j++) { CollectionIndex collectionIndex = new CollectionIndex(bufferReader); _indexes[collectionIndex.Name] = collectionIndex; } } public override PageBuffer UpdateBuffer() { if (base.PageType == PageType.Empty) { return base.UpdateBuffer(); } using (BufferWriter bufferWriter = new BufferWriter(_buffer.Slice(32, 8160))) { for (int i = 0; i < 5; i++) { bufferWriter.Write(FreeDataPageList[i]); } bufferWriter.Skip(64 - bufferWriter.Position); bufferWriter.Write((byte)_indexes.Count); foreach (CollectionIndex value in _indexes.Values) { value.UpdateBuffer(bufferWriter); } } return base.UpdateBuffer(); } public CollectionIndex GetCollectionIndex(string name) { if (_indexes.TryGetValue(name, out var value)) { return value; } return null; } public ICollection GetCollectionIndexes() { return _indexes.Values; } public CollectionIndex[] GetCollectionIndexesSlots() { CollectionIndex[] array = new CollectionIndex[_indexes.Max((KeyValuePair x) => x.Value.Slot) + 1]; foreach (CollectionIndex value in _indexes.Values) { array[value.Slot] = value; } return array; } public CollectionIndex InsertCollectionIndex(string name, string expr, bool unique) { int num = 1 + _indexes.Sum((KeyValuePair x) => CollectionIndex.GetLength(x.Value)) + CollectionIndex.GetLength(name, expr); if (_indexes.Count == 255 || num >= 8096) { throw new LiteException(0, "This collection has no more space for new indexes"); } CollectionIndex collectionIndex = new CollectionIndex((byte)((_indexes.Count != 0) ? ((uint)(_indexes.Max((KeyValuePair x) => x.Value.Slot) + 1)) : 0u), 0, name, expr, unique); _indexes[name] = collectionIndex; base.IsDirty = true; return collectionIndex; } public CollectionIndex UpdateCollectionIndex(string name) { base.IsDirty = true; return _indexes[name]; } public void DeleteCollectionIndex(string name) { _indexes.Remove(name); base.IsDirty = true; } } internal class DataPage : BasePage { private static readonly int[] _freePageSlots = new int[4] { 7344, 6120, 4896, 2448 }; public DataPage(PageBuffer buffer) : base(buffer) { Constants.ENSURE(base.PageType == PageType.Data, "Page type must be data page: {0}", base.PageType); if (base.PageType != PageType.Data) { throw LiteException.InvalidPageType(PageType.Data, this); } } public DataPage(PageBuffer buffer, uint pageID) : base(buffer, pageID, PageType.Data) { } public DataBlock GetBlock(byte index) { BufferSlice segment = Get(index); return new DataBlock(this, index, segment); } public DataBlock InsertBlock(int bytesLength, bool extend) { byte index; BufferSlice segment = Insert((ushort)(bytesLength + 6), out index); return new DataBlock(this, index, segment, extend, PageAddress.Empty); } public DataBlock UpdateBlock(DataBlock currentBlock, int bytesLength) { BufferSlice segment = Update(currentBlock.Position.Index, (ushort)(bytesLength + 6)); return new DataBlock(this, currentBlock.Position.Index, segment, currentBlock.Extend, currentBlock.NextBlock); } public void DeleteBlock(byte index) { Delete(index); } public IEnumerable GetBlocks() { foreach (byte usedIndex in GetUsedIndexs()) { int offset = BasePage.CalcPositionAddr(usedIndex); ushort offset2 = _buffer.ReadUInt16(offset); if (!_buffer.ReadBool(offset2)) { yield return new PageAddress(base.PageID, usedIndex); } } } public static byte FreeIndexSlot(int freeBytes) { Constants.ENSURE(freeBytes >= 0, "FreeBytes must be positive: {0}", freeBytes); for (int i = 0; i < _freePageSlots.Length; i++) { if (freeBytes >= _freePageSlots[i]) { return (byte)i; } } return 4; } public static int GetMinimumIndexSlot(int length) { return FreeIndexSlot(length) - 1; } } internal class HeaderPage : BasePage { public const string HEADER_INFO = "** This is a LiteDB file **"; public const byte FILE_VERSION = 8; public const int P_HEADER_INFO = 32; public const int P_FILE_VERSION = 59; public const int P_FREE_EMPTY_PAGE_ID = 60; public const int P_LAST_PAGE_ID = 64; public const int P_CREATION_TIME = 68; public const int P_INVALID_DATAFILE_STATE = 191; public const int P_COLLECTIONS = 192; public const int COLLECTIONS_SIZE = 8000; private BsonDocument _collections; private bool _isCollectionsChanged; public uint FreeEmptyPageList { get; set; } public uint LastPageID { get; set; } public DateTime CreationTime { get; } public EnginePragmas Pragmas { get; set; } public HeaderPage(PageBuffer buffer, uint pageID) : base(buffer, 0u, PageType.Header) { CreationTime = DateTime.UtcNow; FreeEmptyPageList = uint.MaxValue; LastPageID = 0u; Pragmas = new EnginePragmas(this); _buffer.Write("** This is a LiteDB file **", 32); ((BufferSlice)_buffer).Write((byte)8, 59); _buffer.Write(CreationTime, 68); _collections = new BsonDocument(); } public HeaderPage(PageBuffer buffer) : base(buffer) { CreationTime = _buffer.ReadDateTime(68); LoadPage(); } private void LoadPage() { string strA = _buffer.ReadString(32, "** This is a LiteDB file **".Length); byte b = _buffer[59]; if (string.CompareOrdinal(strA, "** This is a LiteDB file **") != 0 || b != 8) { throw LiteException.InvalidDatabase(); } FreeEmptyPageList = _buffer.ReadUInt32(60); LastPageID = _buffer.ReadUInt32(64); Pragmas = new EnginePragmas(_buffer, this); BufferSlice bufferSlice = _buffer.Slice(192, 8000); using (BufferReader bufferReader = new BufferReader(new BufferSlice[1] { bufferSlice })) { _collections = bufferReader.ReadDocument().GetValue(); } _isCollectionsChanged = false; } public override PageBuffer UpdateBuffer() { _buffer.Write(FreeEmptyPageList, 60); _buffer.Write(LastPageID, 64); Pragmas.UpdateBuffer(_buffer); if (_isCollectionsChanged) { using (BufferWriter bufferWriter = new BufferWriter(_buffer.Slice(192, 8000))) { bufferWriter.WriteDocument(_collections, recalc: true); } _isCollectionsChanged = false; } return base.UpdateBuffer(); } public PageBuffer Savepoint() { UpdateBuffer(); PageBuffer pageBuffer = new PageBuffer(new byte[8192], 0, 0); System.Buffer.BlockCopy(_buffer.Array, _buffer.Offset, pageBuffer.Array, pageBuffer.Offset, 8192); return pageBuffer; } public void Restore(PageBuffer savepoint) { System.Buffer.BlockCopy(savepoint.Array, savepoint.Offset, _buffer.Array, _buffer.Offset, 8192); LoadPage(); } public uint GetCollectionPageID(string collection) { if (_collections.TryGetValue(collection, out var value)) { return (uint)value.AsInt32; } return uint.MaxValue; } public IEnumerable> GetCollections() { foreach (KeyValuePair element in _collections.GetElements()) { yield return new KeyValuePair(element.Key, (uint)element.Value.AsInt32); } } public void InsertCollection(string name, uint pageID) { _collections[name] = (int)pageID; _isCollectionsChanged = true; } public void DeleteCollection(string name) { _collections.Remove(name); _isCollectionsChanged = true; } public void RenameCollection(string oldName, string newName) { BsonValue value = _collections[oldName]; _collections.Remove(oldName); _collections.Add(newName, value); _isCollectionsChanged = true; } public int GetAvailableCollectionSpace() { return 8000 - _collections.GetBytesCount(recalc: true) - 1 - 1 - 4 - 8; } } internal class IndexPage : BasePage { public IndexPage(PageBuffer buffer) : base(buffer) { Constants.ENSURE(base.PageType == PageType.Index, "page type must be index page"); if (base.PageType != PageType.Index) { throw LiteException.InvalidPageType(PageType.Index, this); } } public IndexPage(PageBuffer buffer, uint pageID) : base(buffer, pageID, PageType.Index) { } public IndexNode GetIndexNode(byte index) { BufferSlice segment = Get(index); return new IndexNode(this, index, segment); } public IndexNode InsertIndexNode(byte slot, byte level, BsonValue key, PageAddress dataBlock, int bytesLength) { byte index; BufferSlice segment = Insert((ushort)bytesLength, out index); return new IndexNode(this, index, segment, slot, level, key, dataBlock); } public void DeleteIndexNode(byte index) { Delete(index); } public IEnumerable GetIndexNodes() { foreach (byte usedIndex in GetUsedIndexs()) { yield return GetIndexNode(usedIndex); } } public static byte FreeIndexSlot(int freeBytes) { Constants.ENSURE(freeBytes >= 0, "freeBytes must be positive"); return (freeBytes < 1400) ? ((byte)1) : ((byte)0); } } internal static class Pragmas { public const string USER_VERSION = "USER_VERSION"; public const string COLLATION = "COLLATION"; public const string TIMEOUT = "TIMEOUT"; public const string LIMIT_SIZE = "LIMIT_SIZE"; public const string UTC_DATE = "UTC_DATE"; public const string CHECKPOINT = "CHECKPOINT"; } internal abstract class Index { public string Name { get; private set; } public int Order { get; set; } internal Index(string name, int order) { Name = name; Order = order; } public abstract uint GetCost(CollectionIndex index); public abstract IEnumerable Execute(IndexService indexer, CollectionIndex index); public virtual IEnumerable Run(CollectionPage col, IndexService indexer) { CollectionIndex collectionIndex = col.GetCollectionIndex(Name); if (collectionIndex == null) { throw LiteException.IndexNotFound(Name); } return Execute(indexer, collectionIndex).DistinctBy((IndexNode x) => x.DataBlock, null); } } internal class IndexAll : Index { public IndexAll(string name, int order) : base(name, order) { } public override uint GetCost(CollectionIndex index) { return 100u; } public override IEnumerable Execute(IndexService indexer, CollectionIndex index) { return indexer.FindAll(index, base.Order); } public override string ToString() { return $"FULL INDEX SCAN({base.Name})"; } } internal class IndexEquals : Index { private readonly BsonValue _value; public IndexEquals(string name, BsonValue value) : base(name, 1) { _value = value; } public override uint GetCost(CollectionIndex index) { if (index.Unique) { return 1u; } return 10u; } public override IEnumerable Execute(IndexService indexer, CollectionIndex index) { IndexNode node2 = indexer.Find(index, _value, sibling: false, 1); if (node2 == null) { yield break; } yield return node2; if (index.Unique) { yield break; } IndexNode first = node2; while (!node2.Next[0].IsEmpty) { IndexNode node3; node2 = (node3 = indexer.GetNode(node2.Next[0])); if (node3.Key.CompareTo(_value, indexer.Collation) != 0 || node2.Key.IsMinValue || node2.Key.IsMaxValue) { break; } yield return node2; } node2 = first; while (!node2.Prev[0].IsEmpty) { IndexNode node3; node2 = (node3 = indexer.GetNode(node2.Prev[0])); if (node3.Key.CompareTo(_value, indexer.Collation) == 0 && !node2.Key.IsMinValue && !node2.Key.IsMaxValue) { yield return node2; continue; } break; } } public override string ToString() { return $"INDEX SEEK({base.Name} = {_value})"; } } internal class IndexIn : Index { private readonly BsonArray _values; public IndexIn(string name, BsonArray values, int order) : base(name, order) { _values = values; } public override uint GetCost(CollectionIndex index) { if (!index.Unique) { return (uint)(_values.Count * 10); } return (uint)_values.Count; } public override IEnumerable Execute(IndexService indexer, CollectionIndex index) { foreach (BsonValue item in _values.Distinct()) { IndexEquals indexEquals = new IndexEquals(base.Name, item); foreach (IndexNode item2 in indexEquals.Execute(indexer, index)) { yield return item2; } } } public override string ToString() { return $"INDEX SEEK({base.Name} IN {JsonSerializer.Serialize(_values)})"; } } internal class IndexLike : Index { private readonly string _startsWith; private readonly bool _equals; private readonly bool _testSqlLike; private readonly string _pattern; public IndexLike(string name, BsonValue value, int order) : base(name, order) { _pattern = value.AsString; _startsWith = _pattern.SqlLikeStartsWith(out _testSqlLike); _equals = _pattern == _startsWith; } public override uint GetCost(CollectionIndex index) { if (_startsWith.Length > 0) { return 10u; } return 100u; } public override IEnumerable Execute(IndexService indexer, CollectionIndex index) { if (_startsWith.Length <= 0) { return ExecuteLike(indexer, index); } return ExecuteStartsWith(indexer, index); } private IEnumerable ExecuteStartsWith(IndexService indexer, CollectionIndex index) { IndexNode first = indexer.Find(index, _startsWith, sibling: true, base.Order); IndexNode node = first; if (first == null) { yield break; } while (node != null && !node.Key.IsMinValue && !node.Key.IsMaxValue) { string text = (node.Key.IsString ? node.Key.AsString : (node.Key.IsNull ? "" : node.Key.ToString())); if (!(_equals ? text.Equals(_startsWith, StringComparison.OrdinalIgnoreCase) : text.StartsWith(_startsWith, StringComparison.OrdinalIgnoreCase))) { break; } if (!_testSqlLike || (_testSqlLike && text.SqlLike(_pattern, indexer.Collation))) { yield return node; } node = indexer.GetNode(node.GetNextPrev(0, -base.Order)); } node = indexer.GetNode(first.GetNextPrev(0, base.Order)); while (node != null && !node.Key.IsMinValue && !node.Key.IsMaxValue) { string text2 = (node.Key.IsString ? node.Key.AsString : (node.Key.IsNull ? "" : node.Key.ToString())); if (_equals ? text2.Equals(_pattern, StringComparison.OrdinalIgnoreCase) : text2.StartsWith(_startsWith, StringComparison.OrdinalIgnoreCase)) { if (!node.DataBlock.IsEmpty && (!_testSqlLike || (_testSqlLike && text2.SqlLike(_pattern, indexer.Collation)))) { yield return node; } node = indexer.GetNode(node.GetNextPrev(0, base.Order)); continue; } break; } } private IEnumerable ExecuteLike(IndexService indexer, CollectionIndex index) { return from x in indexer.FindAll(index, base.Order) where x.Key.IsString && x.Key.AsString.SqlLike(_pattern, indexer.Collation) select x; } public override string ToString() { return string.Format("{0}({1} LIKE \"{2}\")", (_startsWith.Length > 0) ? "INDEX SEEK (+RANGE SCAN)" : "FULL INDEX SCAN", base.Name, _pattern); } } internal class IndexRange : Index { private readonly BsonValue _start; private readonly BsonValue _end; private readonly bool _startEquals; private readonly bool _endEquals; public IndexRange(string name, BsonValue start, BsonValue end, bool startEquals, bool endEquals, int order) : base(name, order) { _start = start; _end = end; _startEquals = startEquals; _endEquals = endEquals; } public override uint GetCost(CollectionIndex index) { return 20u; } public override IEnumerable Execute(IndexService indexer, CollectionIndex index) { BsonValue start = ((base.Order == 1) ? _start : _end); BsonValue end = ((base.Order == 1) ? _end : _start); bool startEquals = ((base.Order == 1) ? _startEquals : _endEquals); bool endEquals = ((base.Order == 1) ? _endEquals : _startEquals); IndexNode first = ((start.Type == BsonType.MinValue) ? indexer.GetNode(index.Head) : ((start.Type == BsonType.MaxValue) ? indexer.GetNode(index.Tail) : indexer.Find(index, start, sibling: true, base.Order))); IndexNode node = first; if (startEquals && node != null) { while (!node.GetNextPrev(0, -base.Order).IsEmpty) { IndexNode node2; node = (node2 = indexer.GetNode(node.GetNextPrev(0, -base.Order))); if (node2.Key.CompareTo(start) != 0 || node.Key.IsMinValue || node.Key.IsMaxValue) { break; } yield return node; } node = first; } while (node != null && node.Key.CompareTo(start, indexer.Collation) == 0) { if (startEquals && !node.Key.IsMinValue && !node.Key.IsMaxValue) { yield return node; } node = indexer.GetNode(node.GetNextPrev(0, base.Order)); } while (node != null) { int num = node.Key.CompareTo(end, indexer.Collation); if (endEquals && num == 0 && !node.Key.IsMinValue && !node.Key.IsMaxValue) { yield return node; } else { if (num != -base.Order || node.Key.IsMinValue || node.Key.IsMaxValue) { break; } yield return node; } node = indexer.GetNode(node.GetNextPrev(0, base.Order)); } } public override string ToString() { if (_start.IsMinValue && !_endEquals) { return $"INDEX SCAN({base.Name} < {_end})"; } if (_start.IsMinValue && _endEquals) { return $"INDEX SCAN({base.Name} <= {_end})"; } if (_end.IsMaxValue && !_startEquals) { return $"INDEX SCAN({base.Name} > {_start})"; } if (_end.IsMaxValue && _startEquals) { return $"INDEX SCAN({base.Name} >= {_start})"; } return $"INDEX RANGE SCAN({base.Name} BETWEEN {_start} AND {_end})"; } } internal class IndexScan : Index { private readonly Func _func; public IndexScan(string name, Func func, int order) : base(name, order) { _func = func; } public override uint GetCost(CollectionIndex index) { return 80u; } public override IEnumerable Execute(IndexService indexer, CollectionIndex index) { return from i in indexer.FindAll(index, base.Order) where _func(i.Key) select i; } public override string ToString() { return $"FULL INDEX SCAN({base.Name})"; } } internal class IndexVirtual : Index, IDocumentLookup { private readonly IEnumerable _source; private Dictionary _cache = new Dictionary(); public IndexVirtual(IEnumerable source) : base(null, 0) { _source = source; } public override uint GetCost(CollectionIndex index) { return 100u; } public override IEnumerable Execute(IndexService indexer, CollectionIndex index) { throw new NotImplementedException(); } public override IEnumerable Run(CollectionPage col, IndexService indexer) { uint rawId = 0u; foreach (BsonDocument item in _source) { rawId++; item.RawId = new PageAddress(rawId, 0); if (_cache != null) { _cache[rawId] = item; if (_cache.Count > 2000) { _cache = null; } } yield return new IndexNode(item); } } public BsonDocument Load(IndexNode node) { return node.Key as BsonDocument; } public BsonDocument Load(PageAddress rawId) { if (_cache == null) { throw new LiteException(0, $"OrderBy/GroupBy operation are supported only in virtual collection with less than {2000} documents"); } return _cache[rawId.PageID]; } public override string ToString() { return $"FULL COLLECTION SCAN"; } } internal class DatafileLookup : IDocumentLookup { protected readonly DataService _data; protected readonly bool _utcDate; protected readonly HashSet _fields; public DatafileLookup(DataService data, bool utcDate, HashSet fields) { _data = data; _utcDate = utcDate; _fields = fields; } public virtual BsonDocument Load(IndexNode node) { Constants.ENSURE(node.DataBlock != PageAddress.Empty, "data block must be a valid block address"); return Load(node.DataBlock); } public virtual BsonDocument Load(PageAddress rawId) { using BufferReader bufferReader = new BufferReader(_data.Read(rawId), _utcDate); BsonDocument value = bufferReader.ReadDocument(_fields).GetValue(); value.RawId = rawId; return value; } } internal interface IDocumentLookup { BsonDocument Load(IndexNode node); BsonDocument Load(PageAddress rawId); } internal class IndexLookup : IDocumentLookup { private readonly IndexService _indexer; private readonly string _name; public IndexLookup(IndexService indexer, string name) { _indexer = indexer; _name = name; } public BsonDocument Load(IndexNode node) { Constants.ENSURE(!node.DataBlock.IsEmpty, "Never should be empty rawid"); return new BsonDocument { [_name] = node.Key, RawId = node.DataBlock }; } public BsonDocument Load(PageAddress rawId) { IndexNode node = _indexer.GetNode(rawId); return Load(node); } } internal abstract class BasePipe { protected readonly TransactionService _transaction; protected readonly IDocumentLookup _lookup; protected readonly SortDisk _tempDisk; protected readonly EnginePragmas _pragmas; protected readonly uint _maxItemsCount; public BasePipe(TransactionService transaction, IDocumentLookup lookup, SortDisk tempDisk, EnginePragmas pragmas, uint maxItemsCount) { _transaction = transaction; _lookup = lookup; _tempDisk = tempDisk; _pragmas = pragmas; _maxItemsCount = maxItemsCount; } public abstract IEnumerable Pipe(IEnumerable nodes, QueryPlan query); protected IEnumerable LoadDocument(IEnumerable nodes) { foreach (IndexNode node in nodes) { yield return _lookup.Load(node); _transaction.Safepoint(); } } protected IEnumerable Include(IEnumerable source, BsonExpression path) { string last = null; Snapshot snapshot = null; IndexService indexer = null; DataService data = null; CollectionIndex index = null; IDocumentLookup lookup = null; foreach (BsonDocument item in source) { foreach (BsonValue item2 in (from x in path.Execute(item, _pragmas.Collation) where x.IsDocument || x.IsArray select x).ToList()) { if (item2.IsDocument) { DoInclude(item2.AsDocument); continue; } foreach (BsonDocument item3 in from x in item2.AsArray where x.IsDocument select x.AsDocument) { DoInclude(item3); } } yield return item; } void DoInclude(BsonDocument value) { BsonValue bsonValue = value["$id"]; BsonValue bsonValue2 = value["$ref"]; if (!bsonValue.IsNull && bsonValue2.IsString) { if (last != bsonValue2.AsString) { last = bsonValue2.AsString; snapshot = _transaction.CreateSnapshot(LockMode.Read, last, addIfNotExists: false); indexer = new IndexService(snapshot, _pragmas.Collation, _maxItemsCount); data = new DataService(snapshot, _maxItemsCount); lookup = new DatafileLookup(data, _pragmas.UtcDate, null); index = snapshot.CollectionPage?.PK; } if (index != null) { IndexNode indexNode = indexer.Find(index, bsonValue, sibling: false, 1); if (indexNode != null) { BsonDocument source2 = lookup.Load(indexNode); value.Remove("$ref"); { foreach (KeyValuePair item4 in source2.Where((KeyValuePair x) => x.Key != "_id")) { value[item4.Key] = item4.Value; } return; } } value["$missing"] = true; } } } } protected IEnumerable Filter(IEnumerable source, BsonExpression expr) { foreach (BsonDocument item in source) { BsonValue bsonValue = expr.ExecuteScalar(item, _pragmas.Collation); if (bsonValue.IsBoolean && bsonValue.AsBoolean) { yield return item; } } } protected IEnumerable OrderBy(IEnumerable source, BsonExpression expr, int order, int offset, int limit) { IEnumerable> items = source.Select((BsonDocument x) => new KeyValuePair(expr.ExecuteScalar(x, _pragmas.Collation), x.RawId)); using SortService sorter = new SortService(_tempDisk, order, _pragmas); sorter.Insert(items); IEnumerable> enumerable = sorter.Sort().Skip(offset).Take(limit); foreach (KeyValuePair item in enumerable) { yield return _lookup.Load(item.Value); } } } internal class DocumentCacheEnumerable : IEnumerable, IEnumerable, IDisposable { private IEnumerator _enumerator; private readonly List _cache = new List(); private readonly IDocumentLookup _lookup; public DocumentCacheEnumerable(IEnumerable source, IDocumentLookup lookup) { _enumerator = source.GetEnumerator(); _lookup = lookup; } public void Dispose() { if (_enumerator != null) { while (_enumerator.MoveNext()) { } _enumerator.Dispose(); _enumerator = null; } } public IEnumerator GetEnumerator() { int index; for (index = 0; index < _cache.Count; index++) { PageAddress rawId = _cache[index]; yield return _lookup.Load(rawId); } while (_enumerator != null && _enumerator.MoveNext()) { BsonDocument current = _enumerator.Current; Constants.ENSURE(!current.RawId.IsEmpty, "rawId must have a valid value"); _cache.Add(current.RawId); yield return current; index++; } if (_enumerator != null) { _enumerator.Dispose(); _enumerator = null; } for (; index < _cache.Count; index++) { PageAddress rawId2 = _cache[index]; yield return _lookup.Load(rawId2); } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } internal class GroupByPipe : BasePipe { public GroupByPipe(TransactionService transaction, IDocumentLookup loader, SortDisk tempDisk, EnginePragmas pragmas, uint maxItemsCount) : base(transaction, loader, tempDisk, pragmas, maxItemsCount) { } public override IEnumerable Pipe(IEnumerable nodes, QueryPlan query) { IEnumerable source = LoadDocument(nodes); foreach (BsonExpression filter in query.Filters) { source = Filter(source, filter); } if (query.OrderBy != null) { source = OrderBy(source, query.OrderBy.Expression, query.OrderBy.Order, 0, int.MaxValue); } IEnumerable groups = GroupBy(source, query.GroupBy); IEnumerable enumerable = SelectGroupBy(groups, query.GroupBy); if (query.Offset > 0) { enumerable = enumerable.Skip(query.Offset); } if (query.Limit < int.MaxValue) { enumerable = enumerable.Take(query.Limit); } return enumerable; } private IEnumerable GroupBy(IEnumerable source, GroupBy groupBy) { using IEnumerator enumerator = source.GetEnumerator(); Done done = new Done { Running = enumerator.MoveNext() }; while (done.Running) { BsonValue bsonValue = groupBy.Expression.ExecuteScalar(enumerator.Current, _pragmas.Collation); groupBy.Select.Parameters["key"] = bsonValue; IEnumerable source2 = YieldDocuments(bsonValue, enumerator, groupBy, done); yield return new DocumentCacheEnumerable(source2, _lookup); } } private IEnumerable YieldDocuments(BsonValue key, IEnumerator enumerator, GroupBy groupBy, Done done) { yield return enumerator.Current; while (done.Running = enumerator.MoveNext()) { BsonValue bsonValue = groupBy.Expression.ExecuteScalar(enumerator.Current, _pragmas.Collation); if (key == bsonValue) { yield return enumerator.Current; continue; } groupBy.Select.Parameters["key"] = bsonValue; break; } } private IEnumerable SelectGroupBy(IEnumerable groups, GroupBy groupBy) { string defaultName = groupBy.Select.DefaultFieldName(); foreach (DocumentCacheEnumerable group in groups) { BsonValue bsonValue2; try { if (groupBy.Having == null) { goto IL_00b2; } BsonValue bsonValue = groupBy.Having.ExecuteScalar(group, null, null, _pragmas.Collation); if (bsonValue.IsBoolean && bsonValue.AsBoolean) { goto IL_00b2; } goto end_IL_006e; IL_00b2: bsonValue2 = groupBy.Select.ExecuteScalar(group, null, null, _pragmas.Collation); goto IL_00db; end_IL_006e:; } finally { group.Dispose(); } continue; IL_00db: if (bsonValue2.IsDocument) { yield return bsonValue2.AsDocument; continue; } yield return new BsonDocument { [defaultName] = bsonValue2 }; } } } internal class QueryPipe : BasePipe { public QueryPipe(TransactionService transaction, IDocumentLookup loader, SortDisk tempDisk, EnginePragmas pragmas, uint maxItemsCount) : base(transaction, loader, tempDisk, pragmas, maxItemsCount) { } public override IEnumerable Pipe(IEnumerable nodes, QueryPlan query) { IEnumerable source = LoadDocument(nodes); foreach (BsonExpression item in query.IncludeBefore) { source = Include(source, item); } foreach (BsonExpression filter in query.Filters) { source = Filter(source, filter); } if (query.OrderBy != null) { source = OrderBy(source, query.OrderBy.Expression, query.OrderBy.Order, query.Offset, query.Limit); } else { if (query.Offset > 0) { source = source.Skip(query.Offset); } if (query.Limit < int.MaxValue) { source = source.Take(query.Limit); } } foreach (BsonExpression item2 in query.IncludeAfter) { source = Include(source, item2); } if (query.Select.All) { return SelectAll(source, query.Select.Expression); } return Select(source, query.Select.Expression); } private IEnumerable Select(IEnumerable source, BsonExpression select) { string defaultName = select.DefaultFieldName(); foreach (BsonDocument item in source) { BsonValue bsonValue = select.ExecuteScalar(item, _pragmas.Collation); if (bsonValue.IsDocument) { yield return bsonValue.AsDocument; continue; } yield return new BsonDocument { [defaultName] = bsonValue }; } } private IEnumerable SelectAll(IEnumerable source, BsonExpression select) { DocumentCacheEnumerable source2 = new DocumentCacheEnumerable(source, _lookup); string defaultName = select.DefaultFieldName(); IEnumerable enumerable = select.Execute(source2, _pragmas.Collation); foreach (BsonValue item in enumerable) { if (item.IsDocument) { yield return item.AsDocument; continue; } yield return new BsonDocument { [defaultName] = item }; } } } internal class QueryExecutor { private readonly LiteEngine _engine; private readonly EngineState _state; private readonly TransactionMonitor _monitor; private readonly SortDisk _sortDisk; private readonly DiskService _disk; private readonly EnginePragmas _pragmas; private readonly CursorInfo _cursor; private readonly string _collection; private readonly Query _query; private readonly IEnumerable _source; public QueryExecutor(LiteEngine engine, EngineState state, TransactionMonitor monitor, SortDisk sortDisk, DiskService disk, EnginePragmas pragmas, string collection, Query query, IEnumerable source) { _engine = engine; _state = state; _monitor = monitor; _sortDisk = sortDisk; _disk = disk; _pragmas = pragmas; _collection = collection; _query = query; _cursor = new CursorInfo(collection, query); _source = source; } public BsonDataReader ExecuteQuery() { if (_query.Into == null) { return ExecuteQuery(_query.ExplainPlan); } return ExecuteQueryInto(_query.Into, _query.IntoAutoId); } internal BsonDataReader ExecuteQuery(bool executionPlan) { bool isNew; TransactionService transaction = _monitor.GetTransaction(create: true, queryOnly: true, out isNew); transaction.OpenCursors.Add(_cursor); IEnumerable source = RunQuery(); source = source.OnDispose(delegate { transaction.OpenCursors.Remove(_cursor); }); if (isNew) { source = source.OnDispose(delegate { _monitor.ReleaseTransaction(transaction); }); } return new BsonDataReader(source, _collection, _state); IEnumerable RunQuery() { Snapshot snapshot = transaction.CreateSnapshot(_query.ForUpdate ? LockMode.Write : LockMode.Read, _collection, addIfNotExists: false); if (snapshot.CollectionPage == null && _source == null) { if (_query.Select.UseSource) { yield return _query.Select.ExecuteScalar(_pragmas.Collation).AsDocument; } } else { QueryPlan queryPlan = new QueryOptimization(snapshot, _query, _source, _pragmas.Collation).ProcessQuery(); queryPlan.GetExecutionPlan(); if (!executionPlan) { IEnumerable nodes = queryPlan.Index.Run(snapshot.CollectionPage, new IndexService(snapshot, _pragmas.Collation, _disk.MAX_ITEMS_COUNT)); BasePipe pipe = queryPlan.GetPipe(transaction, snapshot, _sortDisk, _pragmas, _disk.MAX_ITEMS_COUNT); using (_cursor.Elapsed.StartDisposable()) { using (IEnumerator enumerator = pipe.Pipe(nodes, queryPlan).GetEnumerator()) { bool flag; try { flag = enumerator.MoveNext(); } catch (Exception ex) { _state.Handle(ex); throw ex; } while (flag) { _cursor.Fetched++; _cursor.Elapsed.Stop(); yield return enumerator.Current; if (transaction.State != 0) { throw new LiteException(0, "There is no more active transaction for this cursor: " + _cursor.Query.ToSQL(_cursor.Collection)); } _cursor.Elapsed.Start(); try { flag = enumerator.MoveNext(); } catch (Exception ex2) { _state.Handle(ex2); throw ex2; } } } yield break; } } yield return queryPlan.GetExecutionPlan(); } } } internal BsonDataReader ExecuteQueryInto(string into, BsonAutoId autoId) { int num; if (into.StartsWith("$")) { SqlParser.ParseCollection(new Tokenizer(into), out var name, out var options); num = _engine.GetSystemCollection(name).Output(GetResultset(), options); } else { num = _engine.Insert(into, GetResultset(), autoId); } return new BsonDataReader(num); IEnumerable GetResultset() { using BsonDataReader reader = ExecuteQuery(executionPlan: false); while (reader.Read()) { yield return reader.Current.AsDocument; } } } } internal class QueryOptimization { private readonly Snapshot _snapshot; private readonly Query _query; private readonly Collation _collation; private readonly QueryPlan _queryPlan; private readonly List _terms = new List(); public QueryOptimization(Snapshot snapshot, Query query, IEnumerable source, Collation collation) { if (query.Select == null) { throw new ArgumentNullException("Select"); } _snapshot = snapshot; _query = query; _collation = collation; _queryPlan = new QueryPlan(snapshot.CollectionName) { Index = ((source != null) ? new IndexVirtual(source) : null), Select = new Select(_query.Select, _query.Select.UseSource), ForUpdate = query.ForUpdate, Limit = query.Limit, Offset = query.Offset }; } public QueryPlan ProcessQuery() { SplitWherePredicateInTerms(); OptimizeTerms(); DefineQueryFields(); DefineIndex(); DefineOrderBy(); DefineGroupBy(); DefineIncludes(); return _queryPlan; } private void SplitWherePredicateInTerms() { foreach (BsonExpression item in _query.Where) { add(item); } void add(BsonExpression predicate) { if (predicate.UseSource) { throw new LiteException(0, "WHERE filter can not use `*` expression in `" + predicate.Source); } if (predicate.IsPredicate || predicate.Type == BsonExpressionType.Or) { _terms.Add(predicate); } else { if (predicate.Type != BsonExpressionType.And) { throw LiteException.InvalidExpressionTypePredicate(predicate); } BsonExpression left = predicate.Left; BsonExpression right = predicate.Right; add(left); add(right); } } } private void OptimizeTerms() { for (int i = 0; i < _terms.Count; i++) { BsonExpression bsonExpression = _terms[i]; BsonExpression left = bsonExpression.Left; if (left != null && !left.IsScalar && bsonExpression.IsANY && bsonExpression.Type == BsonExpressionType.Equal) { BsonExpression right = bsonExpression.Right; if (right != null && right.Type == BsonExpressionType.Path) { _terms[i] = BsonExpression.Create(bsonExpression.Right.Source + " IN ARRAY(" + bsonExpression.Left.Source + ")", bsonExpression.Parameters); } } } } private void DefineQueryFields() { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); hashSet.AddRange(_query.Select.Fields); hashSet.AddRange(_terms.SelectMany((BsonExpression x) => x.Fields)); hashSet.AddRange(_query.Includes.SelectMany((BsonExpression x) => x.Fields)); hashSet.AddRange(_query.GroupBy?.Fields); hashSet.AddRange(_query.Having?.Fields); hashSet.AddRange(_query.OrderBy?.Fields); if (hashSet.Contains("$")) { hashSet.Clear(); } _queryPlan.Fields = hashSet; } private void DefineIndex() { BsonExpression selected = null; if (_queryPlan.Index == null) { IndexCost indexCost = ChooseIndex(_queryPlan.Fields); if (indexCost != null) { _queryPlan.Index = indexCost.Index; _queryPlan.IndexCost = indexCost.Cost; _queryPlan.IndexExpression = indexCost.IndexExpression; } else { CollectionIndex pK = _snapshot.CollectionPage.PK; _queryPlan.Index = new IndexAll("_id", 1); _queryPlan.IndexCost = _queryPlan.Index.GetCost(pK); _queryPlan.IndexExpression = "$._id"; } selected = indexCost?.Expression; } else { Constants.ENSURE(_queryPlan.Index is IndexVirtual, "pre-defined index must be only for virtual collections"); _queryPlan.IndexCost = 0u; } if (_queryPlan.Fields.Count == 1 && _queryPlan.IndexExpression == "$." + _queryPlan.Fields.First()) { _queryPlan.IsIndexKeyOnly = true; } _queryPlan.Filters.AddRange(_terms.Where((BsonExpression x) => x != selected)); } private IndexCost ChooseIndex(HashSet fields) { CollectionIndex[] source = _snapshot.CollectionPage.GetCollectionIndexes().ToArray(); string preferred = ((fields.Count == 1) ? ("$." + fields.First()) : null); IndexCost indexCost = null; foreach (BsonExpression expr in _terms.Where((BsonExpression x) => x.IsPredicate)) { Constants.ENSURE(expr.Left != null && expr.Right != null, "predicate expression must has left/right expressions"); Tuple tuple = null; if (!expr.Left.IsScalar && expr.Right.IsScalar) { if (expr.IsANY) { tuple = (from x in source where x.Expression == expr.Left.Source && expr.Right.IsValue select Tuple.Create(x, expr.Right)).FirstOrDefault(); } } else { tuple = (from x in source where x.Expression == expr.Left.Source && expr.Right.IsValue select Tuple.Create(x, expr.Right)).Union(from x in source where x.Expression == expr.Right.Source && expr.Left.IsValue select Tuple.Create(x, expr.Left)).FirstOrDefault(); } if (tuple != null) { IndexCost indexCost2 = new IndexCost(tuple.Item1, expr, tuple.Item2, _collation); if (indexCost == null || indexCost2.Cost < indexCost.Cost) { indexCost = indexCost2; } } } if (indexCost == null && (_query.OrderBy != null || _query.GroupBy != null || preferred != null)) { CollectionIndex collectionIndex = source.FirstOrDefault((CollectionIndex x) => x.Expression == _query.GroupBy?.Source) ?? source.FirstOrDefault((CollectionIndex x) => x.Expression == _query.OrderBy?.Source) ?? source.FirstOrDefault((CollectionIndex x) => x.Expression == preferred); if (collectionIndex != null) { indexCost = new IndexCost(collectionIndex); } } return indexCost; } private void DefineOrderBy() { if (_query.OrderBy != null) { OrderBy orderBy = new OrderBy(_query.OrderBy, _query.Order); if (orderBy.Expression.Source == _queryPlan.IndexExpression) { _queryPlan.Index.Order = orderBy.Order; orderBy = null; } _queryPlan.OrderBy = orderBy; } } private void DefineGroupBy() { if (_query.GroupBy != null) { if (_query.OrderBy != null) { throw new NotSupportedException("GROUP BY expression do not support ORDER BY"); } if (_query.Includes.Count > 0) { throw new NotSupportedException("GROUP BY expression do not support INCLUDE"); } GroupBy groupBy = new GroupBy(_query.GroupBy, _queryPlan.Select.Expression, _query.Having); OrderBy orderBy = null; if (!(groupBy.Expression.Source == _queryPlan.IndexExpression)) { orderBy = new OrderBy(groupBy.Expression, 1); } _queryPlan.GroupBy = groupBy; _queryPlan.OrderBy = orderBy; } } private void DefineIncludes() { foreach (BsonExpression include in _query.Includes) { string field = include.Fields.Single(); int num; if (!_queryPlan.Filters.Any((BsonExpression x) => x.Fields.Contains(field))) { OrderBy orderBy = _queryPlan.OrderBy; if (orderBy == null) { num = 0; } else { num = (orderBy.Expression.Fields.Contains(field) ? 1 : 0); if (num != 0) { goto IL_0080; } } goto IL_0091; } num = 1; goto IL_0080; IL_0080: _queryPlan.IncludeBefore.Add(include); goto IL_0091; IL_0091: if (num == 0 || _queryPlan.OrderBy != null) { _queryPlan.IncludeAfter.Add(include); } } } } internal class GroupBy { public BsonExpression Expression { get; } public BsonExpression Select { get; } public BsonExpression Having { get; } public GroupBy(BsonExpression expression, BsonExpression select, BsonExpression having) { Expression = expression; Select = select; Having = having; } } internal class IndexCost { public uint Cost { get; } public BsonExpression Expression { get; } public string IndexExpression { get; } public Index Index { get; } public IndexCost(CollectionIndex index, BsonExpression expr, BsonExpression value, Collation collation) { IndexCost indexCost = this; IndexExpression = index.Expression; Expression = expr; BsonExpressionType exprType = expr.Type; if (expr.Left.IsValue) { switch (expr.Type) { case BsonExpressionType.GreaterThan: exprType = BsonExpressionType.LessThan; break; case BsonExpressionType.GreaterThanOrEqual: exprType = BsonExpressionType.LessThanOrEqual; break; case BsonExpressionType.LessThan: exprType = BsonExpressionType.GreaterThan; break; case BsonExpressionType.LessThanOrEqual: exprType = BsonExpressionType.GreaterThanOrEqual; break; } } Index = (from x in value.Execute(collation) select indexCost.CreateIndex(exprType, index.Name, x)).FirstOrDefault(); Constants.ENSURE(Index != null, "index must be not null"); Cost = Index.GetCost(index); } public IndexCost(CollectionIndex index) { Expression = BsonExpression.Create(index.Expression); Index = new IndexAll(index.Name, 1); Cost = Index.GetCost(index); IndexExpression = index.Expression; } private Index CreateIndex(BsonExpressionType type, string name, BsonValue value) { switch (type) { case BsonExpressionType.Equal: return new IndexEquals(name, value); case BsonExpressionType.Between: return new IndexRange(name, value.AsArray[0], value.AsArray[1], startEquals: true, endEquals: true, 1); case BsonExpressionType.Like: return new IndexLike(name, value.AsString, 1); case BsonExpressionType.GreaterThan: return new IndexRange(name, value, BsonValue.MaxValue, startEquals: false, endEquals: true, 1); case BsonExpressionType.GreaterThanOrEqual: return new IndexRange(name, value, BsonValue.MaxValue, startEquals: true, endEquals: true, 1); case BsonExpressionType.LessThan: return new IndexRange(name, BsonValue.MinValue, value, startEquals: true, endEquals: false, 1); case BsonExpressionType.LessThanOrEqual: return new IndexRange(name, BsonValue.MinValue, value, startEquals: true, endEquals: true, 1); case BsonExpressionType.NotEqual: return new IndexScan(name, (BsonValue x) => x.CompareTo(value) != 0, 1); case BsonExpressionType.In: if (!value.IsArray) { return new IndexEquals(name, value); } return new IndexIn(name, value.AsArray, 1); default: return null; } } } internal class OrderBy { public BsonExpression Expression { get; } public int Order { get; set; } public OrderBy(BsonExpression expression, int order) { Expression = expression; Order = order; } } internal class QueryPlan { public string Collection { get; set; } public Index Index { get; set; } public string IndexExpression { get; set; } public uint IndexCost { get; internal set; } public bool IsIndexKeyOnly { get; set; } public List Filters { get; set; } = new List(); public List IncludeBefore { get; set; } = new List(); public List IncludeAfter { get; set; } = new List(); public OrderBy OrderBy { get; set; } public GroupBy GroupBy { get; set; } public Select Select { get; set; } public HashSet Fields { get; set; } public int Limit { get; set; } = int.MaxValue; public int Offset { get; set; } public bool ForUpdate { get; set; } public QueryPlan(string collection) { Collection = collection; } public BasePipe GetPipe(TransactionService transaction, Snapshot snapshot, SortDisk tempDisk, EnginePragmas pragmas, uint maxItemsCount) { if (GroupBy == null) { return new QueryPipe(transaction, GetLookup(snapshot, pragmas, maxItemsCount), tempDisk, pragmas, maxItemsCount); } return new GroupByPipe(transaction, GetLookup(snapshot, pragmas, maxItemsCount), tempDisk, pragmas, maxItemsCount); } public IDocumentLookup GetLookup(Snapshot snapshot, EnginePragmas pragmas, uint maxItemsCount) { DataService data = new DataService(snapshot, maxItemsCount); IndexService indexer = new IndexService(snapshot, pragmas.Collation, maxItemsCount); IDocumentLookup documentLookup = Index as IDocumentLookup; if (documentLookup == null) { documentLookup = ((!IsIndexKeyOnly) ? ((IDocumentLookup)new DatafileLookup(data, pragmas.UtcDate, Fields)) : ((IDocumentLookup)new IndexLookup(indexer, Fields.Single()))); } return documentLookup; } public BsonDocument GetExecutionPlan() { BsonDocument bsonDocument = new BsonDocument { ["collection"] = Collection, ["snaphost"] = (ForUpdate ? "write" : "read"), ["pipe"] = ((GroupBy == null) ? "queryPipe" : "groupByPipe") }; bsonDocument["index"] = new BsonDocument { ["name"] = Index.Name, ["expr"] = IndexExpression, ["order"] = Index.Order, ["mode"] = Index.ToString(), ["cost"] = (int)IndexCost }; bsonDocument["lookup"] = new BsonDocument { ["loader"] = ((Index is IndexVirtual) ? "virtual" : (IsIndexKeyOnly ? "index" : "document")), ["fields"] = ((Fields.Count == 0) ? new BsonValue("$") : new BsonArray(Fields.Select((string x) => new BsonValue(x)))) }; if (IncludeBefore.Count > 0) { bsonDocument["includeBefore"] = new BsonArray(IncludeBefore.Select((BsonExpression x) => new BsonValue(x.Source))); } if (Filters.Count > 0) { bsonDocument["filters"] = new BsonArray(Filters.Select((BsonExpression x) => new BsonValue(x.Source))); } if (OrderBy != null) { bsonDocument["orderBy"] = new BsonDocument { ["expr"] = OrderBy.Expression.Source, ["order"] = OrderBy.Order }; } if (Limit != int.MaxValue) { bsonDocument["limit"] = Limit; } if (Offset != 0) { bsonDocument["offset"] = Offset; } if (IncludeAfter.Count > 0) { bsonDocument["includeAfter"] = new BsonArray(IncludeAfter.Select((BsonExpression x) => new BsonValue(x.Source))); } if (GroupBy != null) { bsonDocument["groupBy"] = new BsonDocument { ["expr"] = GroupBy.Expression.Source, ["having"] = GroupBy.Having?.Source, ["select"] = GroupBy.Select?.Source }; } else { bsonDocument["select"] = new BsonDocument { ["expr"] = Select.Expression.Source, ["all"] = Select.All }; } return bsonDocument; } } internal class Select { public BsonExpression Expression { get; } public bool All { get; } public Select(BsonExpression expression, bool all) { Expression = expression; All = all; } } internal class CollectionService { private readonly HeaderPage _header; private readonly DiskService _disk; private readonly Snapshot _snapshot; private readonly TransactionPages _transPages; public CollectionService(HeaderPage header, DiskService disk, Snapshot snapshot, TransactionPages transPages) { _header = header; _disk = disk; _snapshot = snapshot; _transPages = transPages; } public static void CheckName(string name, HeaderPage header) { if (Encoding.UTF8.GetByteCount(name) > header.GetAvailableCollectionSpace()) { throw LiteException.InvalidCollectionName(name, "There is no space in header this collection name"); } if (!name.IsWord()) { throw LiteException.InvalidCollectionName(name, "Use only [a-Z$_]"); } if (name.StartsWith("$")) { throw LiteException.InvalidCollectionName(name, "Collection can't starts with `$` (reserved for system collections)"); } } public bool Get(string name, bool addIfNotExists, ref CollectionPage collectionPage) { uint collectionPageID = _header.GetCollectionPageID(name); if (collectionPageID != uint.MaxValue) { collectionPage = _snapshot.GetPage(collectionPageID); return false; } if (addIfNotExists) { Add(name, ref collectionPage); return true; } return false; } private void Add(string name, ref CollectionPage collectionPage) { CheckName(name, _header); collectionPage = _snapshot.NewPage(); uint pageID = collectionPage.PageID; _transPages.Commit += delegate(HeaderPage h) { h.InsertCollection(name, pageID); }; new IndexService(_snapshot, _header.Pragmas.Collation, _disk.MAX_ITEMS_COUNT).CreateIndex("_id", "$._id", unique: true); } } internal class DataService { public const int MAX_DATA_BYTES_PER_PAGE = 8150; private readonly Snapshot _snapshot; private readonly uint _maxItemsCount; public DataService(Snapshot snapshot, uint maxItemsCount) { _snapshot = snapshot; _maxItemsCount = maxItemsCount; } public PageAddress Insert(BsonDocument doc) { int bytesLeft = doc.GetBytesCount(recalc: true); if (bytesLeft > 16683050) { throw new LiteException(0, "Document size exceed {0} limit", 16683050); } PageAddress firstBlock = PageAddress.Empty; using (BufferWriter bufferWriter = new BufferWriter(source())) { bufferWriter.WriteDocument(doc, recalc: false); bufferWriter.Consume(); } return firstBlock; IEnumerable source() { int blockIndex = 0; DataBlock dataBlock2 = null; int bytesToCopy; for (; bytesLeft > 0; bytesLeft -= bytesToCopy) { bytesToCopy = Math.Min(bytesLeft, 8150); DataPage freeDataPage = _snapshot.GetFreeDataPage(bytesToCopy + 6); DataBlock dataBlock = freeDataPage.InsertBlock(bytesToCopy, blockIndex++ > 0); dataBlock2?.SetNextBlock(dataBlock.Position); if (firstBlock.IsEmpty) { firstBlock = dataBlock.Position; } _snapshot.AddOrRemoveFreeDataList(freeDataPage); yield return dataBlock.Buffer; dataBlock2 = dataBlock; } } } public void Update(CollectionPage col, PageAddress blockAddress, BsonDocument doc) { int bytesLeft = doc.GetBytesCount(recalc: true); if (bytesLeft > 16683050) { throw new LiteException(0, "Document size exceed {0} limit", 16683050); } DataBlock lastBlock = null; PageAddress updateAddress = blockAddress; using (BufferWriter bufferWriter = new BufferWriter(source())) { bufferWriter.WriteDocument(doc, recalc: false); bufferWriter.Consume(); } IEnumerable source() { int bytesToCopy; for (; bytesLeft > 0; bytesLeft -= bytesToCopy) { if (!updateAddress.IsEmpty) { DataPage page = _snapshot.GetPage(updateAddress.PageID); DataBlock block = page.GetBlock(updateAddress.Index); bytesToCopy = Math.Min(bytesLeft, page.FreeBytes + block.Buffer.Count); DataBlock updateBlock2 = page.UpdateBlock(block, bytesToCopy); _snapshot.AddOrRemoveFreeDataList(page); yield return updateBlock2.Buffer; lastBlock = updateBlock2; updateAddress = updateBlock2.NextBlock; } else { bytesToCopy = Math.Min(bytesLeft, 8150); DataPage freeDataPage = _snapshot.GetFreeDataPage(bytesToCopy + 6); DataBlock updateBlock2 = freeDataPage.InsertBlock(bytesToCopy, extend: true); if (lastBlock != null) { lastBlock.SetNextBlock(updateBlock2.Position); } _snapshot.AddOrRemoveFreeDataList(freeDataPage); yield return updateBlock2.Buffer; lastBlock = updateBlock2; } } if (!lastBlock.NextBlock.IsEmpty) { PageAddress nextBlock = lastBlock.NextBlock; lastBlock.SetNextBlock(PageAddress.Empty); Delete(nextBlock); } } } public IEnumerable Read(PageAddress address) { uint counter = 0u; while (address != PageAddress.Empty) { Constants.ENSURE(counter++ < _maxItemsCount, "Detected loop in data Read({0})", address); DataPage page = _snapshot.GetPage(address.PageID); DataBlock block = page.GetBlock(address.Index); yield return block.Buffer; address = block.NextBlock; } } public void Delete(PageAddress blockAddress) { while (blockAddress != PageAddress.Empty) { DataPage page = _snapshot.GetPage(blockAddress.PageID); DataBlock block = page.GetBlock(blockAddress.Index); page.DeleteBlock(blockAddress.Index); _snapshot.AddOrRemoveFreeDataList(page); blockAddress = block.NextBlock; } } } internal class IndexService { private readonly Snapshot _snapshot; private readonly Collation _collation; private readonly uint _maxItemsCount; public Collation Collation => _collation; public IndexService(Snapshot snapshot, Collation collation, uint maxItemsCount) { _snapshot = snapshot; _collation = collation; _maxItemsCount = maxItemsCount; } public CollectionIndex CreateIndex(string name, string expr, bool unique) { int keyLength; int nodeLength = IndexNode.GetNodeLength(32, BsonValue.MinValue, out keyLength); IndexPage indexPage = _snapshot.NewPage(); CollectionIndex collectionIndex = _snapshot.CollectionPage.InsertCollectionIndex(name, expr, unique); IndexNode indexNode = indexPage.InsertIndexNode(collectionIndex.Slot, 32, BsonValue.MinValue, PageAddress.Empty, nodeLength); IndexNode indexNode2 = indexPage.InsertIndexNode(collectionIndex.Slot, 32, BsonValue.MaxValue, PageAddress.Empty, nodeLength); indexNode.SetNext(0, indexNode2.Position); indexNode2.SetPrev(0, indexNode.Position); collectionIndex.FreeIndexPageList = indexPage.PageID; indexPage.PageListSlot = 0; collectionIndex.Head = indexNode.Position; collectionIndex.Tail = indexNode2.Position; return collectionIndex; } public IndexNode AddNode(CollectionIndex index, BsonValue key, PageAddress dataBlock, IndexNode last) { if (key.IsMaxValue || key.IsMinValue) { throw LiteException.InvalidIndexKey("BsonValue MaxValue/MinValue are not supported as index key"); } byte insertLevels = Flip(); return AddNode(index, key, dataBlock, insertLevels, last); } private IndexNode AddNode(CollectionIndex index, BsonValue key, PageAddress dataBlock, byte insertLevels, IndexNode last) { int keyLength; int nodeLength = IndexNode.GetNodeLength(insertLevels, key, out keyLength); if (keyLength > 1023) { throw LiteException.InvalidIndexKey($"Index key must be less than {1023} bytes."); } IndexNode indexNode = _snapshot.GetFreeIndexPage(nodeLength, ref index.FreeIndexPageList).InsertIndexNode(index.Slot, insertLevels, key, dataBlock, nodeLength); IndexNode indexNode2 = GetNode(index.Head); uint num = 0u; for (int num2 = 31; num2 >= 0; num2--) { PageAddress pageAddress = indexNode2.Next[num2]; while (!pageAddress.IsEmpty && pageAddress != index.Tail) { Constants.ENSURE(num++ < _maxItemsCount, "Detected loop in AddNode({0})", indexNode.Position); IndexNode node = GetNode(pageAddress); int num3 = node.Key.CompareTo(key, _collation); if (num3 == 0 && index.Unique) { throw LiteException.IndexDuplicateKey(index.Name, key); } if (num3 == 1) { break; } indexNode2 = node; pageAddress = node.Next[num2]; } if (num2 <= insertLevels - 1) { PageAddress position = indexNode2.Position; PageAddress value = indexNode2.Next[num2]; if (value.IsEmpty) { value = index.Tail; } indexNode.SetNext((byte)num2, value); indexNode.SetPrev((byte)num2, position); indexNode2.SetNext((byte)num2, indexNode.Position); pageAddress = indexNode.Next[num2]; GetNode(pageAddress).SetPrev((byte)num2, indexNode.Position); } } if (last != null) { Constants.ENSURE(last.NextNode == PageAddress.Empty, "last index node must point to null"); last = GetNode(last.Position); last.SetNextNode(indexNode.Position); } _snapshot.AddOrRemoveFreeIndexList(indexNode.Page, ref index.FreeIndexPageList); return indexNode; } public byte Flip() { byte b = 1; int num = Randomizer.Next(); while ((num & 1) == 1) { b++; if (b == 32) { break; } num >>= 1; } return b; } public IndexNode GetNode(PageAddress address) { if (address.PageID == uint.MaxValue) { return null; } return _snapshot.GetPage(address.PageID).GetIndexNode(address.Index); } public IEnumerable GetNodeList(PageAddress nodeAddress) { IndexNode node = GetNode(nodeAddress); uint counter = 0u; while (node != null) { Constants.ENSURE(counter++ < _maxItemsCount, "Detected loop in GetNodeList({0})", nodeAddress); yield return node; node = GetNode(node.NextNode); } } public void DeleteAll(PageAddress pkAddress) { IndexNode node = GetNode(pkAddress); CollectionIndex[] collectionIndexesSlots = _snapshot.CollectionPage.GetCollectionIndexesSlots(); uint num = 0u; while (node != null) { Constants.ENSURE(num++ < _maxItemsCount, "Detected loop in DeleteAll({0})", pkAddress); DeleteSingleNode(node, collectionIndexesSlots[node.Slot]); node = GetNode(node.NextNode); } } public IndexNode DeleteList(PageAddress pkAddress, HashSet toDelete) { IndexNode indexNode = GetNode(pkAddress); IndexNode node = GetNode(indexNode.NextNode); CollectionIndex[] collectionIndexesSlots = _snapshot.CollectionPage.GetCollectionIndexesSlots(); uint num = 0u; while (node != null) { Constants.ENSURE(num++ < _maxItemsCount, "Detected loop in DeleteList({0})", pkAddress); if (toDelete.Contains(node.Position)) { DeleteSingleNode(node, collectionIndexesSlots[node.Slot]); indexNode.SetNextNode(node.NextNode); } else { indexNode = node; } node = GetNode(node.NextNode); } return indexNode; } private void DeleteSingleNode(IndexNode node, CollectionIndex index) { for (int num = node.Levels - 1; num >= 0; num--) { IndexNode node2 = GetNode(node.Prev[num]); IndexNode node3 = GetNode(node.Next[num]); node2?.SetNext((byte)num, node.Next[num]); node3?.SetPrev((byte)num, node.Prev[num]); } node.Page.DeleteIndexNode(node.Position.Index); _snapshot.AddOrRemoveFreeIndexList(node.Page, ref index.FreeIndexPageList); } public void DropIndex(CollectionIndex index) { byte slot = index.Slot; CollectionIndex pK = _snapshot.CollectionPage.PK; foreach (IndexNode item in FindAll(pK, 1)) { PageAddress nextNode = item.NextNode; IndexNode indexNode = item; while (nextNode != PageAddress.Empty) { IndexNode node = GetNode(nextNode); if (node.Slot == slot) { node.Page.DeleteIndexNode(node.Position.Index); indexNode.SetNextNode(node.NextNode); } else { indexNode = node; } nextNode = node.NextNode; } } GetNode(index.Head).Page.DeleteIndexNode(index.Head.Index); GetNode(index.Tail).Page.DeleteIndexNode(index.Tail.Index); } public IEnumerable FindAll(CollectionIndex index, int order) { IndexNode cur = ((order == 1) ? GetNode(index.Head) : GetNode(index.Tail)); uint counter = 0u; while (!cur.GetNextPrev(0, order).IsEmpty) { Constants.ENSURE(counter++ < _maxItemsCount, "Detected loop in FindAll({0})", index.Name); cur = GetNode(cur.GetNextPrev(0, order)); if (cur.Key.IsMinValue || cur.Key.IsMaxValue) { break; } yield return cur; } } public IndexNode Find(CollectionIndex index, BsonValue value, bool sibling, int order) { IndexNode indexNode = ((order == 1) ? GetNode(index.Head) : GetNode(index.Tail)); uint num = 0u; for (int num2 = 31; num2 >= 0; num2--) { PageAddress nextPrev = indexNode.GetNextPrev((byte)num2, order); while (!nextPrev.IsEmpty) { Constants.ENSURE(num++ < _maxItemsCount, "Detected loop in Find({0}, {1})", index.Name, value); IndexNode node = GetNode(nextPrev); int num3 = node.Key.CompareTo(value, _collation); if (num3 == order && (num2 > 0 || !sibling)) { break; } if (num3 == order && num2 == 0 && sibling) { if (!node.Key.IsMinValue && !node.Key.IsMaxValue) { return node; } return null; } if (num3 == 0) { return node; } indexNode = node; nextPrev = node.GetNextPrev((byte)num2, order); } } return null; } } internal class LockService : IDisposable { private readonly EnginePragmas _pragmas; private readonly ReaderWriterLockSlim _transaction = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); private readonly ConcurrentDictionary _collections = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); public bool IsInTransaction { get { if (!_transaction.IsReadLockHeld) { return _transaction.IsWriteLockHeld; } return true; } } public int TransactionsCount => _transaction.CurrentReadCount; internal LockService(EnginePragmas pragmas) { _pragmas = pragmas; } public void EnterTransaction() { if (_transaction.IsWriteLockHeld || _transaction.TryEnterReadLock(_pragmas.Timeout)) { return; } throw LiteException.LockTimeout("transaction", _pragmas.Timeout); } public void ExitTransaction() { if (_transaction.IsWriteLockHeld || !_transaction.IsReadLockHeld) { return; } try { _transaction.ExitReadLock(); } catch { } } public void EnterLock(string collectionName) { Constants.ENSURE(_transaction.IsReadLockHeld || _transaction.IsWriteLockHeld, "Use EnterTransaction() before EnterLock(name)"); if (!Monitor.TryEnter(_collections.GetOrAdd(collectionName, (string s) => new object()), _pragmas.Timeout)) { throw LiteException.LockTimeout("write", collectionName, _pragmas.Timeout); } } public void ExitLock(string collectionName) { if (!_collections.TryGetValue(collectionName, out var value)) { throw LiteException.CollectionLockerNotFound(collectionName); } Monitor.Exit(value); } public bool EnterExclusive() { if (_transaction.IsWriteLockHeld) { return false; } if (!_transaction.TryEnterWriteLock(_pragmas.Timeout)) { throw LiteException.LockTimeout("exclusive", _pragmas.Timeout); } return true; } public bool TryEnterExclusive(out bool mustExit) { if (_transaction.IsWriteLockHeld) { mustExit = false; return true; } if (_transaction.IsReadLockHeld || _transaction.CurrentReadCount > 0) { mustExit = false; return false; } if (!_transaction.TryEnterWriteLock(10)) { mustExit = false; return false; } Constants.ENSURE(_transaction.RecursiveReadCount == 0, "must have no other transaction here"); mustExit = true; return true; } public void ExitExclusive() { _transaction.ExitWriteLock(); } public void Dispose() { try { _transaction.Dispose(); } catch (SynchronizationLockException) { } } } internal class RebuildService { private readonly EngineSettings _settings; private readonly int _fileVersion; public RebuildService(EngineSettings settings) { _settings = settings; if (FileReaderV7.IsVersion(ReadFirstBytes(useAesStream: false))) { _fileVersion = 7; return; } byte[] buffer = ReadFirstBytes(); if (!FileReaderV8.IsVersion(buffer)) { throw LiteException.InvalidDatabase(); } _fileVersion = 8; } public long Rebuild(RebuildOptions options) { string backupFilename = FileHelper.GetSuffixFile(_settings.Filename, "-backup"); string suffixFile = FileHelper.GetSuffixFile(FileHelper.GetLogFile(_settings.Filename), "-backup"); string suffixFile2 = FileHelper.GetSuffixFile(_settings.Filename); IFileReader fileReader2; if (_fileVersion != 7) { IFileReader fileReader = new FileReaderV8(_settings, options.Errors); fileReader2 = fileReader; } else { IFileReader fileReader = new FileReaderV7(_settings); fileReader2 = fileReader; } using (IFileReader fileReader3 = fileReader2) { fileReader3.Open(); using LiteEngine liteEngine = new LiteEngine(new EngineSettings { Filename = suffixFile2, Collation = options.Collation, Password = options.Password }); liteEngine.Pragma("CHECKPOINT", 0); liteEngine.RebuildContent(fileReader3); if (options.IncludeErrorReport && options.Errors.Count > 0) { IEnumerable errorReport = options.GetErrorReport(); liteEngine.Insert("_rebuild_errors", errorReport, BsonAutoId.Int32); } IDictionary pragmas = fileReader3.GetPragmas(); liteEngine.Pragma("CHECKPOINT", pragmas["CHECKPOINT"]); liteEngine.Pragma("TIMEOUT", pragmas["TIMEOUT"]); liteEngine.Pragma("LIMIT_SIZE", pragmas["LIMIT_SIZE"]); liteEngine.Pragma("UTC_DATE", pragmas["UTC_DATE"]); liteEngine.Pragma("USER_VERSION", pragmas["USER_VERSION"]); liteEngine.Checkpoint(); } string logFile = FileHelper.GetLogFile(_settings.Filename); if (File.Exists(logFile)) { File.Move(logFile, suffixFile); } FileHelper.Exec(5, delegate { File.Move(_settings.Filename, backupFilename); }); File.Move(suffixFile2, _settings.Filename); return new FileInfo(backupFilename).Length - new FileInfo(_settings.Filename).Length; } private byte[] ReadFirstBytes(bool useAesStream = true) { byte[] array = new byte[16384]; using Stream stream = _settings.CreateDataFactory(useAesStream).GetStream(canWrite: false, sequencial: true); stream.Position = 0L; stream.Read(array, 0, array.Length); return array; } } internal class Snapshot : IDisposable { private readonly HeaderPage _header; private readonly LockService _locker; private readonly DiskReader _reader; private readonly DiskService _disk; private readonly WalIndexService _walIndex; private readonly uint _transactionID; private readonly TransactionPages _transPages; private readonly int _readVersion; private readonly LockMode _mode; private readonly string _collectionName; private readonly CollectionPage _collectionPage; private readonly Dictionary _localPages = new Dictionary(); private bool _disposed; public LockMode Mode => _mode; public string CollectionName => _collectionName; public CollectionPage CollectionPage => _collectionPage; public ICollection LocalPages => _localPages.Values; public int ReadVersion => _readVersion; public Snapshot(LockMode mode, string collectionName, HeaderPage header, uint transactionID, TransactionPages transPages, LockService locker, WalIndexService walIndex, DiskReader reader, DiskService disk, bool addIfNotExists) { _mode = mode; _collectionName = collectionName; _header = header; _transactionID = transactionID; _transPages = transPages; _locker = locker; _walIndex = walIndex; _reader = reader; _disk = disk; if (mode == LockMode.Write) { _locker.EnterLock(_collectionName); } _readVersion = _walIndex.CurrentReadVersion; new CollectionService(_header, _disk, this, _transPages).Get(_collectionName, addIfNotExists, ref _collectionPage); if (_collectionPage != null) { _localPages.Remove(_collectionPage.PageID); } } public IEnumerable GetWritablePages(bool dirty, bool includeCollectionPage) { Constants.ENSURE(!_disposed, "the snapshot is disposed"); if (_mode == LockMode.Read) { yield break; } foreach (BasePage item in _localPages.Values.Where((BasePage x) => x.IsDirty == dirty)) { Constants.ENSURE(item.PageType != PageType.Header && item.PageType != PageType.Collection, "local cache cann't contains this page type"); yield return item; } if (includeCollectionPage && _collectionPage != null && _collectionPage.IsDirty == dirty) { yield return _collectionPage; } } public void Clear() { Constants.ENSURE(!_disposed, "the snapshot is disposed"); if (_mode == LockMode.Read) { foreach (BasePage value in _localPages.Values) { value.Buffer.Release(); } } _localPages.Clear(); } public void Dispose() { if (!_disposed) { Clear(); _disposed = true; if (_mode == LockMode.Read && _collectionPage != null) { _collectionPage.Buffer.Release(); } if (_mode == LockMode.Write) { _locker.ExitLock(_collectionName); } } } public T GetPage(uint pageID, bool useLatestVersion = false) where T : BasePage { FileOrigin origin; long position; int walVersion; return GetPage(pageID, out origin, out position, out walVersion, useLatestVersion); } public T GetPage(uint pageID, out FileOrigin origin, out long position, out int walVersion, bool useLatestVersion = false) where T : BasePage { Constants.ENSURE(!_disposed, "the snapshot is disposed"); Constants.ENSURE(pageID <= _header.LastPageID, "request page must be less or equals lastest page in data file"); if (pageID == 0) { origin = FileOrigin.None; position = 0L; walVersion = 0; return (T)(BasePage)_header; } if (_localPages.TryGetValue(pageID, out var value)) { origin = FileOrigin.None; position = 0L; walVersion = 0; return (T)value; } value = ReadPage(pageID, out origin, out position, out walVersion, useLatestVersion); _localPages[pageID] = value; _transPages.TransactionSize++; return (T)value; } private T ReadPage(uint pageID, out FileOrigin origin, out long position, out int walVersion, bool useLatestVersion = false) where T : BasePage { if (_transPages.DirtyPages.TryGetValue(pageID, out var value)) { T val = BasePage.ReadPage(_reader.ReadPage(value.Position, _mode == LockMode.Write, FileOrigin.Log)); origin = FileOrigin.Log; position = value.Position; walVersion = _readVersion; Constants.ENSURE(val.TransactionID == _transactionID, "this page must came from same transaction"); return val; } long pageIndex = _walIndex.GetPageIndex(pageID, useLatestVersion ? int.MaxValue : _readVersion, out walVersion); if (pageIndex != long.MaxValue) { T val2 = BasePage.ReadPage(_reader.ReadPage(pageIndex, _mode == LockMode.Write, FileOrigin.Log)); val2.TransactionID = 0u; val2.IsConfirmed = false; origin = FileOrigin.Log; position = pageIndex; return val2; } long pagePosition = BasePage.GetPagePosition(pageID); T val3 = BasePage.ReadPage(_reader.ReadPage(pagePosition, _mode == LockMode.Write, FileOrigin.Data)); origin = FileOrigin.Data; position = pagePosition; Constants.ENSURE(!val3.IsConfirmed || val3.TransactionID != 0, "page are not header-clear in data file"); return val3; } public DataPage GetFreeDataPage(int bytesLength) { Constants.ENSURE(!_disposed, "the snapshot is disposed"); int num = bytesLength + 4; for (int num2 = DataPage.GetMinimumIndexSlot(num); num2 >= 0; num2--) { uint num3 = _collectionPage.FreeDataPageList[num2]; if (num3 != uint.MaxValue) { DataPage page = GetPage(num3); Constants.ENSURE(page.PageListSlot == num2, "stored slot must be same as called"); Constants.ENSURE(page.FreeBytes >= num, "ensure selected page has space enough for this content"); page.IsDirty = true; return page; } } return NewPage(); } public IndexPage GetFreeIndexPage(int bytesLength, ref uint freeIndexPageList) { Constants.ENSURE(!_disposed, "the snapshot is disposed"); IndexPage indexPage; if (freeIndexPageList == uint.MaxValue) { indexPage = NewPage(); } else { indexPage = GetPage(freeIndexPageList); Constants.ENSURE(indexPage.FreeBytes > bytesLength, "this page shout be space enouth for this new node"); Constants.ENSURE(indexPage.PageListSlot == 0, "this page should be in slot #0"); } return indexPage; } public T NewPage() where T : BasePage { Constants.ENSURE(!_disposed, "the snapshot is disposed"); Constants.ENSURE(_collectionPage == null, typeof(T) == typeof(CollectionPage), "if no collection page defined yet, must be first request"); Constants.ENSURE(typeof(T) == typeof(CollectionPage), _collectionPage == null, "there is no new collection page if page already exists"); uint num = 0u; PageBuffer buffer; lock (_header) { if (_header.FreeEmptyPageList != uint.MaxValue) { BasePage page = GetPage(_header.FreeEmptyPageList, useLatestVersion: true); Constants.ENSURE(page.PageType == PageType.Empty, "empty page must be defined as empty type"); _header.FreeEmptyPageList = page.NextPageID; page.NextPageID = uint.MaxValue; num = page.PageID; buffer = page.Buffer; } else { if ((_header.LastPageID + 1) * 8192 > _header.Pragmas.LimitSize) { throw new LiteException(0, "Maximum data file size has been reached: " + FileHelper.FormatFileSize(_header.Pragmas.LimitSize)); } PageBuffer savepoint = _header.Savepoint(); try { num = ++_header.LastPageID; buffer = _reader.NewPage(); } catch { _header.Restore(savepoint); throw; } } _transPages.NewPages.Add(num); } T val = BasePage.CreatePage(buffer, num); if (val.PageType != PageType.Collection) { _localPages[num] = val; } val.ColID = _collectionPage?.PageID ?? val.PageID; val.IsDirty = true; _transPages.TransactionSize++; return val; } public void AddOrRemoveFreeDataList(DataPage page) { Constants.ENSURE(!_disposed, "the snapshot is disposed"); byte b = DataPage.FreeIndexSlot(page.FreeBytes); byte pageListSlot = page.PageListSlot; if (b != pageListSlot || page.ItemsCount <= 0) { if (pageListSlot != byte.MaxValue) { RemoveFreeList(page, ref _collectionPage.FreeDataPageList[pageListSlot]); } if (page.ItemsCount == 0) { DeletePage(page); return; } AddFreeList(page, ref _collectionPage.FreeDataPageList[b]); page.PageListSlot = b; } } public void AddOrRemoveFreeIndexList(IndexPage page, ref uint startPageID) { Constants.ENSURE(!_disposed, "the snapshot is disposed"); byte b = IndexPage.FreeIndexSlot(page.FreeBytes); bool flag = page.PageListSlot == 0; bool flag2 = b == 0; if (page.ItemsCount == 0) { if (flag) { RemoveFreeList(page, ref startPageID); } DeletePage(page); return; } if (flag && !flag2) { RemoveFreeList(page, ref startPageID); } else if (!flag && flag2) { AddFreeList(page, ref startPageID); } page.PageListSlot = b; page.IsDirty = true; } private void AddFreeList(T page, ref uint startPageID) where T : BasePage { Constants.ENSURE(page.PrevPageID == uint.MaxValue && page.NextPageID == uint.MaxValue, "only non-linked page can be added in linked list"); if (startPageID != uint.MaxValue) { T page2 = GetPage(startPageID); page2.PrevPageID = page.PageID; page2.IsDirty = true; } page.PrevPageID = uint.MaxValue; page.NextPageID = startPageID; page.IsDirty = true; Constants.ENSURE(page.PageType == PageType.Data || page.PageType == PageType.Index, "only data/index pages must be first on free stack"); startPageID = page.PageID; _collectionPage.IsDirty = true; } private void RemoveFreeList(T page, ref uint startPageID) where T : BasePage { if (page.PrevPageID != uint.MaxValue) { T page2 = GetPage(page.PrevPageID); page2.NextPageID = page.NextPageID; page2.IsDirty = true; } if (page.NextPageID != uint.MaxValue) { T page3 = GetPage(page.NextPageID); page3.PrevPageID = page.PrevPageID; page3.IsDirty = true; } if (startPageID == page.PageID) { startPageID = page.NextPageID; Constants.ENSURE(page.NextPageID == uint.MaxValue || GetPage(page.NextPageID).PageType != PageType.Empty, "first page on free stack must be non empty page"); _collectionPage.IsDirty = true; } uint prevPageID = (page.NextPageID = uint.MaxValue); page.PrevPageID = prevPageID; page.IsDirty = true; } private void DeletePage(T page) where T : BasePage { Constants.ENSURE(page.PrevPageID == uint.MaxValue && page.NextPageID == uint.MaxValue, "before delete a page, no linked list with any another page"); Constants.ENSURE(page.ItemsCount == 0 && page.UsedBytes == 0 && page.HighestIndex == byte.MaxValue && page.FragmentedBytes == 0, "no items on page when delete this page"); Constants.ENSURE(page.PageType == PageType.Data || page.PageType == PageType.Index, "only data/index page can be deleted"); page.MarkAsEmtpy(); if (_transPages.FirstDeletedPageID == uint.MaxValue) { Constants.ENSURE(_transPages.DeletedPages == 0, "if has no firstDeletedPageID must has deleted pages"); _transPages.FirstDeletedPageID = page.PageID; _transPages.LastDeletedPageID = page.PageID; } else { Constants.ENSURE(_transPages.DeletedPages > 0, "must have at least 1 deleted page"); page.NextPageID = _transPages.FirstDeletedPageID; _transPages.FirstDeletedPageID = page.PageID; } _transPages.DeletedPages++; } public void DropCollection(Action safePoint) { Constants.ENSURE(!_disposed, "the snapshot is disposed"); IndexService indexService = new IndexService(this, _header.Pragmas.Collation, _disk.MAX_ITEMS_COUNT); _transPages.FirstDeletedPageID = _collectionPage.PageID; _transPages.LastDeletedPageID = _collectionPage.PageID; _collectionPage.MarkAsEmtpy(); _transPages.DeletedPages = 1; HashSet hashSet = new HashSet(); foreach (CollectionIndex collectionIndex in _collectionPage.GetCollectionIndexes()) { hashSet.Add(collectionIndex.Head.PageID); foreach (IndexNode item in indexService.FindAll(collectionIndex, 1)) { hashSet.Add(item.Page.PageID); safePoint(); } } foreach (uint item2 in hashSet) { IndexPage page = GetPage(item2); page.MarkAsEmtpy(); page.NextPageID = _transPages.FirstDeletedPageID; _transPages.FirstDeletedPageID = page.PageID; _transPages.DeletedPages++; safePoint(); } uint[] freeDataPageList = _collectionPage.FreeDataPageList; for (int i = 0; i < freeDataPageList.Length; i++) { uint num = freeDataPageList[i]; while (num != uint.MaxValue) { DataPage page2 = GetPage(num); num = page2.NextPageID; page2.MarkAsEmtpy(); page2.NextPageID = _transPages.FirstDeletedPageID; _transPages.FirstDeletedPageID = page2.PageID; _transPages.DeletedPages++; safePoint(); } } _transPages.Commit += delegate(HeaderPage h) { h.DeleteCollection(_collectionName); }; } public override string ToString() { return $"{_collectionName} (pages: {_localPages.Count})"; } } internal class TransactionMonitor : IDisposable { private readonly Dictionary _transactions = new Dictionary(); private readonly ThreadLocal _slot = new ThreadLocal(); private readonly HeaderPage _header; private readonly LockService _locker; private readonly DiskService _disk; private readonly WalIndexService _walIndex; private int _freePages; private readonly int _initialSize; public ICollection Transactions => _transactions.Values; public int FreePages => _freePages; public int InitialSize => _initialSize; public TransactionMonitor(HeaderPage header, LockService locker, DiskService disk, WalIndexService walIndex) { _header = header; _locker = locker; _disk = disk; _walIndex = walIndex; _freePages = 100000; _initialSize = 1000; } public TransactionService GetTransaction(bool create, bool queryOnly, out bool isNew) { TransactionService transactionService = _slot.Value; if (create && transactionService == null) { isNew = true; bool flag; lock (_transactions) { if (_transactions.Count >= 100) { throw new LiteException(0, "Maximum number of transactions reached"); } int initialSize = GetInitialSize(); flag = _transactions.Values.Any((TransactionService x) => x.ThreadID == Environment.CurrentManagedThreadId); transactionService = new TransactionService(_header, _locker, _disk, _walIndex, initialSize, this, queryOnly); _transactions[transactionService.TransactionID] = transactionService; } if (!flag) { try { _locker.EnterTransaction(); } catch { transactionService.Dispose(); lock (_transactions) { _freePages += transactionService.MaxTransactionSize; _transactions.Remove(transactionService.TransactionID); } throw; } } if (!queryOnly) { _slot.Value = transactionService; } } else { isNew = false; } return transactionService; } public void ReleaseTransaction(TransactionService transaction) { transaction.Dispose(); bool flag; lock (_transactions) { _transactions.Remove(transaction.TransactionID); _freePages += transaction.MaxTransactionSize; flag = _transactions.Values.Any((TransactionService x) => x.ThreadID == Environment.CurrentManagedThreadId); } if (!flag) { _locker.ExitTransaction(); } if (!transaction.QueryOnly) { Constants.ENSURE(_slot.Value == transaction, "current thread must contains transaction parameter"); _slot.Value = null; } } public TransactionService GetThreadTransaction() { lock (_transactions) { return _slot.Value ?? _transactions.Values.FirstOrDefault((TransactionService x) => x.ThreadID == Environment.CurrentManagedThreadId); } } private int GetInitialSize() { if (_freePages >= _initialSize) { _freePages -= _initialSize; return _initialSize; } int num = 0; foreach (TransactionService value in _transactions.Values) { int num2 = value.MaxTransactionSize / _initialSize; value.MaxTransactionSize -= num2; num += num2; } return num; } private bool TryExtend(TransactionService trans) { lock (_transactions) { if (_freePages >= _initialSize) { trans.MaxTransactionSize += _initialSize; _freePages -= _initialSize; return true; } return false; } } public bool CheckSafepoint(TransactionService trans) { if (trans.Pages.TransactionSize >= trans.MaxTransactionSize) { return !TryExtend(trans); } return false; } public void Dispose() { if (_transactions.Count <= 0) { return; } foreach (TransactionService value in _transactions.Values) { value.Dispose(); } _transactions.Clear(); } } internal class TransactionService : IDisposable { private readonly HeaderPage _header; private readonly LockService _locker; private readonly DiskService _disk; private readonly DiskReader _reader; private readonly WalIndexService _walIndex; private readonly TransactionMonitor _monitor; private readonly Dictionary _snapshots = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly TransactionPages _transPages = new TransactionPages(); private readonly int _threadID = Environment.CurrentManagedThreadId; private readonly uint _transactionID; private readonly DateTime _startTime; private LockMode _mode; private TransactionState _state; public int ThreadID => _threadID; public uint TransactionID => _transactionID; public TransactionState State => _state; public LockMode Mode => _mode; public TransactionPages Pages => _transPages; public DateTime StartTime => _startTime; public IEnumerable Snapshots => _snapshots.Values; public bool QueryOnly { get; } public int MaxTransactionSize { get; set; } public List OpenCursors { get; } = new List(); public bool ExplicitTransaction { get; set; } public TransactionService(HeaderPage header, LockService locker, DiskService disk, WalIndexService walIndex, int maxTransactionSize, TransactionMonitor monitor, bool queryOnly) { _header = header; _locker = locker; _disk = disk; _walIndex = walIndex; _monitor = monitor; QueryOnly = queryOnly; MaxTransactionSize = maxTransactionSize; _transactionID = walIndex.NextTransactionID(); _startTime = DateTime.UtcNow; _reader = _disk.GetReader(); } ~TransactionService() { Dispose(dispose: false); } public Snapshot CreateSnapshot(LockMode mode, string collection, bool addIfNotExists) { Constants.ENSURE(_state == TransactionState.Active, "transaction must be active to create new snapshot"); if (!_snapshots.TryGetValue(collection, out var value)) { value = (_snapshots[collection] = create()); } else if ((mode == LockMode.Write && value.Mode == LockMode.Read) || (addIfNotExists && value.CollectionPage == null)) { value.Dispose(); _snapshots.Remove(collection); value = (_snapshots[collection] = create()); } if (mode == LockMode.Write) { _mode = LockMode.Write; } return value; Snapshot create() { return new Snapshot(mode, collection, _header, _transactionID, _transPages, _locker, _walIndex, _reader, _disk, addIfNotExists); } } public void Safepoint() { if (_state != 0) { throw new LiteException(0, "This transaction are invalid state"); } if (!_monitor.CheckSafepoint(this)) { return; } if (_mode == LockMode.Write) { PersistDirtyPages(commit: false); } foreach (Snapshot value in _snapshots.Values) { value.Clear(); } _transPages.TransactionSize = 0; } private int PersistDirtyPages(bool commit) { int dirty = 0; int result = _disk.WriteLogDisk(source()); _disk.DiscardCleanPages(from x in _snapshots.Values.Where((Snapshot x) => x.Mode == LockMode.Write).SelectMany((Snapshot x) => x.GetWritablePages(dirty: false, commit)) select x.Buffer); return result; IEnumerable source() { IEnumerable source = _snapshots.Values.Where((Snapshot x) => x.Mode == LockMode.Write).SelectMany((Snapshot x) => x.GetWritablePages(dirty: true, commit)); bool markLastAsConfirmed = commit && !_transPages.HeaderChanged; foreach (LastItem page in source.IsLast()) { page.Item.TransactionID = _transactionID; if (page.IsLast) { page.Item.IsConfirmed = markLastAsConfirmed; } if (_transPages.LastDeletedPageID == page.Item.PageID && commit) { Constants.ENSURE(_transPages.HeaderChanged, "must header be in lock"); Constants.ENSURE(page.Item.PageType == PageType.Empty, "must be marked as deleted page"); page.Item.NextPageID = _header.FreeEmptyPageList; _header.FreeEmptyPageList = _transPages.FirstDeletedPageID; } PageBuffer buffer = page.Item.UpdateBuffer(); yield return buffer; dirty++; _transPages.DirtyPages[page.Item.PageID] = new PagePosition(page.Item.PageID, buffer.Position); } if (commit && _transPages.HeaderChanged) { _header.TransactionID = _transactionID; _header.IsConfirmed = true; _transPages.OnCommit(_header); PageBuffer pageBuffer = _header.UpdateBuffer(); PageBuffer pageBuffer2 = _disk.NewPage(); Buffer.BlockCopy(pageBuffer.Array, pageBuffer.Offset, pageBuffer2.Array, pageBuffer2.Offset, pageBuffer2.Count); yield return pageBuffer2; } } } public void Commit() { Constants.ENSURE(_state == TransactionState.Active, "transaction must be active to commit (current state: {0})", _state); if (_mode == LockMode.Write || _transPages.HeaderChanged) { lock (_header) { if (PersistDirtyPages(commit: true) > 0) { _walIndex.ConfirmTransaction(_transactionID, _transPages.DirtyPages.Values); } } } foreach (Snapshot value in _snapshots.Values) { value.Dispose(); } _state = TransactionState.Committed; } public void Rollback() { Constants.ENSURE(_state == TransactionState.Active, "transaction must be active to rollback (current state: {0})", _state); if (_transPages.NewPages.Count > 0) { ReturnNewPages(); } foreach (Snapshot value in _snapshots.Values) { if (value.Mode == LockMode.Write) { _disk.DiscardDirtyPages(from x in value.GetWritablePages(dirty: true, includeCollectionPage: true) select x.Buffer); _disk.DiscardCleanPages(from x in value.GetWritablePages(dirty: false, includeCollectionPage: true) select x.Buffer); } value.Dispose(); } _state = TransactionState.Aborted; } private void ReturnNewPages() { uint transactionID = _walIndex.NextTransactionID(); Dictionary pagePositions; lock (_header) { pagePositions = new Dictionary(); PageBuffer savepoint = _header.Savepoint(); try { _disk.WriteLogDisk(source()); } catch { _header.Restore(savepoint); throw; } _walIndex.ConfirmTransaction(transactionID, pagePositions.Values); } IEnumerable source() { for (int i = 0; i < _transPages.NewPages.Count; i++) { uint pageID = _transPages.NewPages[i]; uint nextPageID = ((i < _transPages.NewPages.Count - 1) ? _transPages.NewPages[i + 1] : _header.FreeEmptyPageList); PageBuffer buffer = _disk.NewPage(); BasePage basePage = new BasePage(buffer, pageID, PageType.Empty) { NextPageID = nextPageID, TransactionID = transactionID }; yield return basePage.UpdateBuffer(); pagePositions[pageID] = new PagePosition(pageID, buffer.Position); } _header.TransactionID = transactionID; _header.FreeEmptyPageList = _transPages.NewPages[0]; _header.IsConfirmed = true; PageBuffer pageBuffer = _header.UpdateBuffer(); PageBuffer pageBuffer2 = _disk.NewPage(); Buffer.BlockCopy(pageBuffer.Array, pageBuffer.Offset, pageBuffer2.Array, pageBuffer2.Offset, pageBuffer2.Count); yield return pageBuffer2; } } public void Dispose() { Dispose(dispose: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool dispose) { if (_state == TransactionState.Disposed) { return; } Constants.ENSURE(_state != TransactionState.Disposed, "transaction must be active before call Done"); if (_state == TransactionState.Active && _snapshots.Count > 0) { foreach (Snapshot item in _snapshots.Values.Where((Snapshot x) => x.Mode == LockMode.Write)) { _disk.DiscardDirtyPages(from x in item.GetWritablePages(dirty: true, includeCollectionPage: true) select x.Buffer); _disk.DiscardCleanPages(from x in item.GetWritablePages(dirty: false, includeCollectionPage: true) select x.Buffer); } foreach (Snapshot item2 in _snapshots.Values.Where((Snapshot x) => x.Mode == LockMode.Read)) { foreach (BasePage localPage in item2.LocalPages) { localPage.Buffer.Release(); } item2.CollectionPage?.Buffer.Release(); } } _reader.Dispose(); _state = TransactionState.Disposed; if (!dispose) { _monitor.ReleaseTransaction(this); } } } internal class WalIndexService { private readonly DiskService _disk; private readonly LockService _locker; private readonly Dictionary>> _index = new Dictionary>>(); private readonly ReaderWriterLockSlim _indexLock = new ReaderWriterLockSlim(); private readonly HashSet _confirmTransactions = new HashSet(); private int _currentReadVersion; private int _lastTransactionID; public int CurrentReadVersion { get { _indexLock.TryEnterReadLock(-1); try { return _currentReadVersion; } finally { _indexLock.ExitReadLock(); } } } public int LastTransactionID => _lastTransactionID; public WalIndexService(DiskService disk, LockService locker) { _disk = disk; _locker = locker; } public void Clear() { _indexLock.TryEnterWriteLock(-1); try { _confirmTransactions.Clear(); _index.Clear(); _lastTransactionID = 0; _currentReadVersion = 0; _disk.Cache.Clear(); _disk.SetLength(0L, FileOrigin.Log); } finally { _indexLock.ExitWriteLock(); } } public uint NextTransactionID() { return (uint)Interlocked.Increment(ref _lastTransactionID); } public long GetPageIndex(uint pageID, int version, out int walVersion) { if (version == 0) { walVersion = 0; return long.MaxValue; } _indexLock.TryEnterReadLock(-1); try { if (_index.TryGetValue(pageID, out var value)) { int num = value.Count; long result = long.MaxValue; walVersion = version; while (num > 0) { num--; KeyValuePair keyValuePair = value[num]; if (keyValuePair.Key <= version) { walVersion = keyValuePair.Key; result = keyValuePair.Value; break; } } return result; } walVersion = int.MaxValue; return long.MaxValue; } finally { _indexLock.ExitReadLock(); } } public void ConfirmTransaction(uint transactionID, ICollection pagePositions) { _indexLock.TryEnterWriteLock(-1); try { _currentReadVersion++; foreach (PagePosition pagePosition in pagePositions) { if (!_index.TryGetValue(pagePosition.PageID, out var value)) { value = new List>(); _index.Add(pagePosition.PageID, value); } value.Add(new KeyValuePair(_currentReadVersion, pagePosition.Position)); } _confirmTransactions.Add(transactionID); } finally { _indexLock.ExitWriteLock(); } } public void RestoreIndex(ref HeaderPage header) { Dictionary> dictionary = new Dictionary>(); long num = 0L; foreach (PageBuffer item2 in _disk.ReadFull(FileOrigin.Log)) { if (item2.IsBlank()) { num += 8192; continue; } uint pageID = item2.ReadUInt32(0); bool num2 = item2.ReadBool(18); uint num3 = item2.ReadUInt32(14); PagePosition item = new PagePosition(pageID, num); if (dictionary.TryGetValue(num3, out var value)) { value.Add(item); } else { dictionary[num3] = new List { item }; } if (num2) { ConfirmTransaction(num3, dictionary[num3]); if (item2.ReadByte(4) == 1) { PageBuffer buffer = header.Buffer; Buffer.BlockCopy(item2.Array, item2.Offset, buffer.Array, buffer.Offset, 8192); header = new HeaderPage(buffer); header.TransactionID = uint.MaxValue; header.IsConfirmed = false; } } _lastTransactionID = (int)num3; num += 8192; } } public int Checkpoint() { if (_disk.GetFileLength(FileOrigin.Log) == 0L || _confirmTransactions.Count == 0) { return 0; } bool flag = _locker.EnterExclusive(); try { return CheckpointInternal(); } finally { if (flag) { _locker.ExitExclusive(); } } } public int TryCheckpoint() { if (_disk.GetFileLength(FileOrigin.Log) == 0L || _confirmTransactions.Count == 0) { return 0; } if (!_locker.TryEnterExclusive(out var mustExit)) { return 0; } try { return CheckpointInternal(); } finally { if (mustExit) { _locker.ExitExclusive(); } } } private int CheckpointInternal() { int counter = 0; _disk.WriteDataDisk(source()); Clear(); return counter; IEnumerable source() { foreach (PageBuffer item2 in _disk.ReadFull(FileOrigin.Log)) { if (!item2.IsBlank()) { uint item = item2.ReadUInt32(14); if (_confirmTransactions.Contains(item)) { uint pageID = item2.ReadUInt32(0); item2.Write(uint.MaxValue, 14); item2.Write(value: false, 18); item2.Position = BasePage.GetPagePosition(pageID); counter++; yield return item2; } } } } } } internal class SortContainer : IDisposable { private readonly Collation _collation; private readonly int _size; private int _remaining; private int _count; private bool _isEOF; private int _readPosition; private BufferReader _reader; private static readonly ArrayPool _bufferPool = ArrayPool.Shared; public KeyValuePair Current; public bool IsEOF => _isEOF; public long Position { get; set; } = -1L; public int Count => _count; public SortContainer(Collation collation, int size) { _collation = collation; _size = size; } public void Insert(IEnumerable> items, int order, BufferSlice buffer) { IOrderedEnumerable> obj = ((order == 1) ? items.OrderBy((KeyValuePair x) => x.Key, _collation) : items.OrderByDescending((KeyValuePair x) => x.Key, _collation)); int num = 0; foreach (KeyValuePair item in obj) { buffer.WriteIndexKey(item.Key, num); int keyLength = IndexNode.GetKeyLength(item.Key, recalc: false); if (keyLength > 1023) { throw LiteException.InvalidIndexKey($"Sort key must be less than {1023} bytes."); } num += keyLength; buffer.Write(item.Value, num); num += 5; _remaining++; } _count = _remaining; } public void InitializeReader(Stream stream, BufferSlice buffer, bool utcDate) { if (stream != null) { _reader = new BufferReader(GetSourceFromStream(stream), utcDate); } else { _reader = new BufferReader(buffer, utcDate); } MoveNext(); } public bool MoveNext() { if (_remaining == 0) { _isEOF = true; return false; } BsonValue key = _reader.ReadIndexKey(); PageAddress value = _reader.ReadPageAddress(); Current = new KeyValuePair(key, value); _remaining--; return true; } private IEnumerable GetSourceFromStream(Stream stream) { byte[] bytes = _bufferPool.Rent(8192); BufferSlice buffer = new BufferSlice(bytes, 0, 8192); while (_readPosition < _size) { stream.Position = Position + _readPosition; stream.Read(bytes, 0, 8192); _readPosition += 8192; yield return buffer; } _bufferPool.Return(bytes, clearArray: true); } public void Dispose() { _reader?.Dispose(); } } internal class SortDisk : IDisposable { private readonly IStreamFactory _factory; private readonly StreamPool _pool; private readonly ConcurrentBag _freePositions = new ConcurrentBag(); private long _lastContainerPosition; private readonly int _containerSize; private readonly EnginePragmas _pragmas; public int ContainerSize => _containerSize; public SortDisk(IStreamFactory factory, int containerSize, EnginePragmas pragmas) { Constants.ENSURE(containerSize % 8192 == 0, "size must be PAGE_SIZE multiple"); _factory = factory; _containerSize = containerSize; _pragmas = pragmas; _lastContainerPosition = -containerSize; _pool = new StreamPool(_factory, appendOnly: false); } public Stream GetReader() { return _pool.Rent(); } public void Return(Stream stream) { _pool.Return(stream); } public void Return(long position) { _freePositions.Add(position); } public long GetContainerPosition() { if (_freePositions.TryTake(out var result)) { return result; } return Interlocked.Add(ref _lastContainerPosition, _containerSize); } public void Write(long position, BufferSlice buffer) { Stream value = _pool.Writer.Value; lock (value) { for (int i = 0; i < _containerSize / 8192; i++) { value.Position = position + i * 8192; value.Write(buffer.Array, buffer.Offset + i * 8192, 8192); } } } public void Dispose() { _pool.Dispose(); _factory.Delete(); } } internal class SortService : IDisposable { private readonly SortDisk _disk; private readonly List _containers = new List(); private readonly int _containerSize; private readonly Done _done = new Done { Running = true }; private readonly int _order; private readonly EnginePragmas _pragmas; private readonly BufferSlice _buffer; private readonly Lazy _reader; private static readonly ArrayPool _bufferPool = ArrayPool.Shared; public int Count => _done.Count; public IReadOnlyCollection Containers => _containers; public SortService(SortDisk disk, int order, EnginePragmas pragmas) { _disk = disk; _order = order; _pragmas = pragmas; _containerSize = disk.ContainerSize; _reader = new Lazy(() => _disk.GetReader()); byte[] array = new byte[disk.ContainerSize]; _buffer = new BufferSlice(array, 0, _containerSize); } public void Dispose() { foreach (SortContainer container in _containers) { container.Dispose(); if (container.Position >= 0) { _disk.Return(container.Position); } } if (_reader.IsValueCreated) { _disk.Return(_reader.Value); } } public void Insert(IEnumerable> items) { foreach (IEnumerable> item in SliptValues(items, _done)) { SortContainer sortContainer = new SortContainer(_pragmas.Collation, _containerSize); sortContainer.Insert(item, _order, _buffer); _containers.Add(sortContainer); if (!_done.Running && _containers.Count == 1) { sortContainer.InitializeReader(null, _buffer, _pragmas.UtcDate); continue; } sortContainer.Position = _disk.GetContainerPosition(); _disk.Write(sortContainer.Position, _buffer); sortContainer.InitializeReader(_reader.Value, null, _pragmas.UtcDate); } } public IEnumerable> Sort() { if (_containers.Count == 0) { yield break; } SortContainer current = _containers[0]; if (_containers.Count == 1) { do { yield return current.Current; } while (current.MoveNext()); current.Dispose(); yield break; } int diffOrder = _order * -1; while (_containers.Any((SortContainer x) => !x.IsEOF)) { foreach (SortContainer item in _containers.Where((SortContainer x) => !x.IsEOF)) { if (item.Current.Key.CompareTo(current.Current.Key, _pragmas.Collation) == diffOrder) { current = item; } } yield return current.Current; BsonValue lastKey = current.Current.Key; if (!current.MoveNext()) { current = _containers.FirstOrDefault((SortContainer x) => !x.IsEOF); } while (current?.Current.Key == lastKey) { yield return current.Current; if (!current.MoveNext()) { current = _containers.FirstOrDefault((SortContainer x) => !x.IsEOF); } } } } private IEnumerable>> SliptValues(IEnumerable> source, Done done) { using IEnumerator> enumerator = source.GetEnumerator(); if (enumerator.MoveNext()) { done.Count = 1; while (done.Running) { yield return YieldValues(enumerator, done); } } } private IEnumerable> YieldValues(IEnumerator> source, Done done) { int size = IndexNode.GetKeyLength(source.Current.Key, recalc: false) + 5; yield return source.Current; while (source.MoveNext()) { int num = IndexNode.GetKeyLength(source.Current.Key, recalc: false) + 5; done.Count++; if (size + num > _containerSize) { yield break; } size += num; yield return source.Current; } done.Running = false; } } internal class CollectionIndex { public uint FreeIndexPageList; public byte Slot { get; } public byte IndexType { get; } public string Name { get; } public string Expression { get; } public BsonExpression BsonExpr { get; } public bool Unique { get; } public PageAddress Head { get; set; } public PageAddress Tail { get; set; } public byte Reserved { get; set; } = 1; public bool IsEmpty => string.IsNullOrEmpty(Name); public CollectionIndex(byte slot, byte indexType, string name, string expr, bool unique) { Slot = slot; IndexType = indexType; Name = name; Expression = expr; Unique = unique; FreeIndexPageList = uint.MaxValue; BsonExpr = BsonExpression.Create(expr); } public CollectionIndex(BufferReader reader) { Slot = reader.ReadByte(); IndexType = reader.ReadByte(); Name = reader.ReadCString(); Expression = reader.ReadCString(); Unique = reader.ReadBoolean(); Head = reader.ReadPageAddress(); Tail = reader.ReadPageAddress(); Reserved = reader.ReadByte(); FreeIndexPageList = reader.ReadUInt32(); BsonExpr = BsonExpression.Create(Expression); } public void UpdateBuffer(BufferWriter writer) { writer.Write(Slot); writer.Write(IndexType); writer.WriteCString(Name); writer.WriteCString(Expression); writer.Write(Unique); writer.Write(Head); writer.Write(Tail); writer.Write(Reserved); writer.Write(FreeIndexPageList); } public static int GetLength(CollectionIndex index) { return GetLength(index.Name, index.Expression); } public static int GetLength(string name, string expr) { return 2 + StringEncoding.UTF8.GetByteCount(name) + 1 + StringEncoding.UTF8.GetByteCount(expr) + 1 + 1 + 5 + 5 + 1 + 4; } } internal class CursorInfo { public string Collection { get; } public Query Query { get; set; } public int Fetched { get; set; } public Stopwatch Elapsed { get; } = new Stopwatch(); public CursorInfo(string collection, Query query) { Collection = collection; Query = query; } } internal class DataBlock { public const int DATA_BLOCK_FIXED_SIZE = 6; public const int P_EXTEND = 0; public const int P_NEXT_BLOCK = 1; public const int P_BUFFER = 6; private readonly DataPage _page; private readonly BufferSlice _segment; public PageAddress Position { get; } public bool Extend { get; } public PageAddress NextBlock { get; private set; } public BufferSlice Buffer { get; } public DataBlock(DataPage page, byte index, BufferSlice segment) { _page = page; _segment = segment; Position = new PageAddress(page.PageID, index); Extend = segment.ReadBool(0); NextBlock = segment.ReadPageAddress(1); Buffer = segment.Slice(6, segment.Count - 6); } public DataBlock(DataPage page, byte index, BufferSlice segment, bool extend, PageAddress nextBlock) { _page = page; _segment = segment; Position = new PageAddress(page.PageID, index); NextBlock = nextBlock; Extend = extend; segment.Write(extend, 0); segment.Write(nextBlock, 1); Buffer = segment.Slice(6, segment.Count - 6); page.IsDirty = true; } public void SetNextBlock(PageAddress nextBlock) { NextBlock = nextBlock; _segment.Write(nextBlock, 1); _page.IsDirty = true; } public override string ToString() { return $"Pos: [{Position}] - Ext: [{Extend}] - Next: [{NextBlock}] - Buffer: [{Buffer}]"; } } internal class Done { public bool Running; public int Count; } internal enum FileOrigin : byte { None, Data, Log } internal class IndexNode { private const int INDEX_NODE_FIXED_SIZE = 12; private const int P_SLOT = 0; private const int P_LEVELS = 1; private const int P_DATA_BLOCK = 2; private const int P_NEXT_NODE = 7; private const int P_PREV_NEXT = 12; private readonly IndexPage _page; private readonly BufferSlice _segment; private static readonly byte[] arrayByteEmpty = new byte[0]; private int P_KEY => 12 + Levels * 5 * 2; public PageAddress Position { get; } public byte Slot { get; } public byte Levels { get; } public BsonValue Key { get; } public PageAddress DataBlock { get; } public PageAddress NextNode { get; private set; } public PageAddress[] Prev { get; private set; } public PageAddress[] Next { get; private set; } public IndexPage Page => _page; public static int GetNodeLength(byte level, BsonValue key, out int keyLength) { keyLength = GetKeyLength(key, recalc: true); return 12 + level * 2 * 5 + keyLength; } public static int GetKeyLength(BsonValue key, bool recalc) { return 1 + ((key.IsString || key.IsBinary) ? 1 : 0) + key.GetBytesCount(recalc); } public IndexNode(IndexPage page, byte index, BufferSlice segment) { _page = page; _segment = segment; Position = new PageAddress(page.PageID, index); Slot = segment.ReadByte(0); Levels = segment.ReadByte(1); DataBlock = segment.ReadPageAddress(2); NextNode = segment.ReadPageAddress(7); Next = new PageAddress[Levels]; Prev = new PageAddress[Levels]; for (int i = 0; i < Levels; i++) { Prev[i] = segment.ReadPageAddress(12 + i * 5 * 2); Next[i] = segment.ReadPageAddress(12 + i * 5 * 2 + 5); } Key = segment.ReadIndexKey(P_KEY); } public IndexNode(IndexPage page, byte index, BufferSlice segment, byte slot, byte levels, BsonValue key, PageAddress dataBlock) { _page = page; _segment = segment; Position = new PageAddress(page.PageID, index); Slot = slot; Levels = levels; DataBlock = dataBlock; NextNode = PageAddress.Empty; Next = new PageAddress[levels]; Prev = new PageAddress[levels]; Key = key; segment.Write(slot, 0); segment.Write(levels, 1); segment.Write(dataBlock, 2); segment.Write(NextNode, 7); for (int i = 0; i < levels; i++) { SetPrev((byte)i, PageAddress.Empty); SetNext((byte)i, PageAddress.Empty); } segment.WriteIndexKey(key, P_KEY); page.IsDirty = true; } public IndexNode(BsonDocument doc) { _page = null; _segment = new BufferSlice(arrayByteEmpty, 0, 0); Position = new PageAddress(0u, 0); Slot = 0; Levels = 0; DataBlock = PageAddress.Empty; NextNode = PageAddress.Empty; Next = new PageAddress[0]; Prev = new PageAddress[0]; Key = doc; } public void SetNextNode(PageAddress value) { NextNode = value; _segment.Write(value, 7); _page.IsDirty = true; } public void SetPrev(byte level, PageAddress value) { Constants.ENSURE(level <= Levels, "out of index in level"); Prev[level] = value; _segment.Write(value, 12 + level * 5 * 2); _page.IsDirty = true; } public void SetNext(byte level, PageAddress value) { Constants.ENSURE(level <= Levels, "out of index in level"); Next[level] = value; _segment.Write(value, 12 + level * 5 * 2 + 5); _page.IsDirty = true; } public PageAddress GetNextPrev(byte level, int order) { if (order != 1) { return Prev[level]; } return Next[level]; } public override string ToString() { return $"Pos: [{Position}] - Key: {Key}"; } } internal enum LockMode { Read, Write } [DebuggerStepThrough] internal struct PageAddress { public const int SIZE = 5; public static PageAddress Empty = new PageAddress(uint.MaxValue, byte.MaxValue); public readonly uint PageID; public readonly byte Index; public bool IsEmpty { get { if (PageID == uint.MaxValue) { return Index == byte.MaxValue; } return false; } } public override bool Equals(object obj) { PageAddress pageAddress = (PageAddress)obj; if (PageID == pageAddress.PageID) { return Index == pageAddress.Index; } return false; } public static bool operator ==(PageAddress lhs, PageAddress rhs) { if (lhs.PageID == rhs.PageID) { return lhs.Index == rhs.Index; } return false; } public static bool operator !=(PageAddress lhs, PageAddress rhs) { return !(lhs == rhs); } public override int GetHashCode() { return (int)((17 * 23 + PageID) * 23 + Index); } public PageAddress(uint pageID, byte index) { PageID = pageID; Index = index; } public override string ToString() { if (!IsEmpty) { uint pageID = PageID; string text = pageID.ToString().PadLeft(4, '0'); byte index = Index; return text + ":" + index.ToString().PadLeft(2, '0'); } return "(empty)"; } public BsonValue ToBsonValue() { if (IsEmpty) { return BsonValue.Null; } return new BsonDocument { ["pageID"] = (int)PageID, ["index"] = (int)Index }; } } internal class PageBuffer : BufferSlice { public readonly int UniqueID; public long Position; public FileOrigin Origin; public int ShareCounter; public long Timestamp; public PageBuffer(byte[] buffer, int offset, int uniqueID) : base(buffer, offset, 8192) { UniqueID = uniqueID; Position = long.MaxValue; Origin = FileOrigin.None; ShareCounter = 0; Timestamp = 0L; } public void Release() { Constants.ENSURE(ShareCounter > 0, "share counter must be > 0 in Release()"); Interlocked.Decrement(ref ShareCounter); } public override string ToString() { string text = ((Position == long.MaxValue) ? "" : Position.ToString()); string text2 = ((ShareCounter == Constants.BUFFER_WRITABLE) ? "" : ShareCounter.ToString()); uint num = this.ReadUInt32(0); byte b = base[4]; return string.Format("ID: {0} - Position: {1}/{2} - Shared: {3} - ({4}) :: Content: [{5}/{6}]", UniqueID, text, Origin, text2, base.ToString(), num.ToString("0:0000"), (PageType)b); } public unsafe bool IsBlank() { fixed (byte* ptr = base.Array) { ulong* ptr2 = (ulong*)(ptr + base.Offset); if (*ptr2 == 0L) { return ptr2[1] == 0; } return false; } } } internal struct PagePosition { public uint PageID; public long Position; public static PagePosition Empty => new PagePosition(uint.MaxValue, long.MaxValue); public bool IsEmpty { get { if (PageID == uint.MaxValue) { return Position == long.MaxValue; } return false; } } public override bool Equals(object obj) { PagePosition pagePosition = (PagePosition)obj; if (PageID == pagePosition.PageID) { return Position == pagePosition.PageID; } return false; } public override int GetHashCode() { return (int)((17 * 23 + PageID) * 23) + (int)Position; } public PagePosition(uint pageID, long position) { PageID = pageID; Position = position; } public override string ToString() { if (!IsEmpty) { return ((PageID == uint.MaxValue) ? "----" : PageID.ToString()) + ":" + Position; } return "----:----"; } } internal class Pragma { public string Name { get; set; } public Func Get { get; set; } public Action Set { get; set; } public Action Read { get; set; } public Action Validate { get; set; } public Action Write { get; set; } } internal class RebuildOptions { private string _buildId = Guid.NewGuid().ToString("d").ToLower() .Substring(6); public string Password { get; set; } public Collation Collation { get; set; } public bool IncludeErrorReport { get; set; } = true; internal IList Errors { get; } = new List(); public IEnumerable GetErrorReport() { return Errors.Select((FileReaderError x) => new BsonDocument { ["buildId"] = _buildId, ["created"] = x.Created, ["pageID"] = (int)x.PageID.Value, ["positionID"] = x.Position, ["origin"] = x.Origin.ToString(), ["pageType"] = x.PageType.ToString(), ["message"] = x.Message, ["exception"] = new BsonDocument { ["code"] = ((x.Exception is LiteException ex) ? ex.ErrorCode : (-1)), ["hresult"] = x.Exception.HResult, ["type"] = x.Exception.GetType().FullName, ["inner"] = x.Exception.InnerException?.Message, ["stacktrace"] = x.Exception.StackTrace } }); } } internal class TransactionPages { public int TransactionSize { get; set; } public Dictionary DirtyPages { get; } = new Dictionary(); public List NewPages { get; } = new List(); public uint FirstDeletedPageID { get; set; } = uint.MaxValue; public uint LastDeletedPageID { get; set; } = uint.MaxValue; public int DeletedPages { get; set; } public bool HeaderChanged { get { if (NewPages.Count <= 0 && DeletedPages <= 0) { return this.Commit != null; } return true; } } public event Action Commit; public void OnCommit(HeaderPage header) { this.Commit?.Invoke(header); } } internal enum TransactionState { Active, Committed, Aborted, Disposed } internal class SysDump : SystemCollection { private readonly HeaderPage _header; private readonly TransactionMonitor _monitor; public SysDump(HeaderPage header, TransactionMonitor monitor) : base("$dump") { _header = header; _monitor = monitor; } public override IEnumerable Input(BsonValue options) { BsonValue option = SystemCollection.GetOption(options, "pageID"); return DumpPages((option == null) ? null : new uint?((uint)option.AsInt32)); } private IEnumerable DumpPages(uint? pageID) { Dictionary collections = _header.GetCollections().ToDictionary((KeyValuePair x) => x.Value, (KeyValuePair x) => x.Key); TransactionService transaction = _monitor.GetThreadTransaction(); Snapshot snapshot = transaction.CreateSnapshot(LockMode.Read, "$", addIfNotExists: false); uint num = (pageID.HasValue ? pageID.Value : 0u); uint end = (pageID.HasValue ? pageID.Value : _header.LastPageID); for (uint i = num; i <= Math.Min(end, _header.LastPageID); i++) { FileOrigin origin; long position; int walVersion; BasePage page = snapshot.GetPage(i, out origin, out position, out walVersion); BsonDocument bsonDocument = new BsonDocument { ["pageID"] = (int)page.PageID, ["pageType"] = page.PageType.ToString(), ["_position"] = position, ["_origin"] = origin.ToString(), ["_version"] = walVersion, ["prevPageID"] = (int)page.PrevPageID, ["nextPageID"] = (int)page.NextPageID, ["slot"] = (int)page.PageListSlot, ["collection"] = collections.GetOrDefault(page.ColID, "-"), ["itemsCount"] = (int)page.ItemsCount, ["freeBytes"] = page.FreeBytes, ["usedBytes"] = (int)page.UsedBytes, ["fragmentedBytes"] = (int)page.FragmentedBytes, ["nextFreePosition"] = (int)page.NextFreePosition, ["highestIndex"] = (int)page.HighestIndex }; if (page.PageType == PageType.Collection) { CollectionPage collectionPage = new CollectionPage(page.Buffer); bsonDocument["dataPageList"] = new BsonArray(collectionPage.FreeDataPageList.Select((uint x) => new BsonValue((int)x))); bsonDocument["indexes"] = new BsonArray(from x in collectionPage.GetCollectionIndexes() select new BsonDocument { ["slot"] = (int)x.Slot, ["empty"] = x.IsEmpty, ["indexType"] = (int)x.IndexType, ["name"] = x.Name, ["expression"] = x.Expression, ["unique"] = x.Unique, ["head"] = x.Head.ToBsonValue(), ["tail"] = x.Tail.ToBsonValue(), ["freeIndexPageList"] = (int)x.FreeIndexPageList }); } if (pageID.HasValue) { bsonDocument["buffer"] = page.Buffer.ToArray(); } yield return bsonDocument; transaction.Safepoint(); } } } internal class SysFile : SystemCollection { private readonly Dictionary _formats = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["json"] = new SysFileJson(), ["csv"] = new SysFileCsv() }; public SysFile() : base("$file") { } public override IEnumerable Input(BsonValue options) { string format = GetFormat(options); if (_formats.TryGetValue(format, out var value)) { return value.Input(options); } throw new LiteException(0, "Unknow file format in $file: `" + format + "`"); } public override int Output(IEnumerable source, BsonValue options) { string format = GetFormat(options); if (_formats.TryGetValue(format, out var value)) { return value.Output(source, options); } throw new LiteException(0, "Unknow file format in $file: `" + format + "`"); } private string GetFormat(BsonValue options) { string path = SystemCollection.GetOption(options, "filename")?.AsString ?? throw new LiteException(0, "Collection $file requires string as 'filename' or a document field 'filename'"); string asString = SystemCollection.GetOption(options, "format", Path.GetExtension(path)).AsString; if (!asString.StartsWith(".")) { return asString; } return asString.Substring(1); } } internal class SysFileCsv : SystemCollection { private static readonly IFormatProvider _numberFormat = CultureInfo.InvariantCulture.NumberFormat; public SysFileCsv() : base("$file_csv") { } public override IEnumerable Input(BsonValue options) { string path = SystemCollection.GetOption(options, "filename")?.AsString ?? throw new LiteException(0, "Collection $" + base.Name + " requires string as 'filename' or a document field 'filename'"); string asString = SystemCollection.GetOption(options, "encoding", "utf-8").AsString; char delimiter = SystemCollection.GetOption(options, "delimiter", ",").AsString[0]; List header = new List(); if (options.IsDocument && options["header"].IsArray) { header.AddRange(options["header"].AsArray.Select((BsonValue x) => x.AsString)); } using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); using StreamReader reader = new StreamReader(fs, Encoding.GetEncoding(asString)); if (header.Count == 0) { bool newLine; do { string text = ReadString(reader, delimiter, out newLine); if (text == null) { break; } header.Add(text); } while (!newLine); } int num = 0; BsonDocument bsonDocument = new BsonDocument(); while (true) { bool newLine2; string text2 = ReadString(reader, delimiter, out newLine2); if (text2 != null) { if (num < header.Count) { string name = header[num++]; bsonDocument[name] = text2; } if (newLine2) { yield return bsonDocument; bsonDocument = new BsonDocument(); num = 0; } continue; } break; } } public override int Output(IEnumerable source, BsonValue options) { string path = SystemCollection.GetOption(options, "filename")?.AsString ?? throw new LiteException(0, "Collection $file_json requires string as 'filename' or a document field 'filename'"); bool asBoolean = SystemCollection.GetOption(options, "overwritten", false).AsBoolean; string asString = SystemCollection.GetOption(options, "encoding", "utf-8").AsString; char value = SystemCollection.GetOption(options, "delimiter", ",").AsString[0]; bool asBoolean2 = SystemCollection.GetOption(options, "header", true).AsBoolean; int num = 0; IList list = null; FileStream fileStream = null; StreamWriter streamWriter = null; try { foreach (BsonDocument item in source) { if (num++ == 0) { fileStream = new FileStream(path, (!asBoolean) ? FileMode.CreateNew : FileMode.OpenOrCreate); streamWriter = new StreamWriter(fileStream, Encoding.GetEncoding(asString)); list = item.Keys.ToList(); if (asBoolean2) { int num2 = 0; foreach (KeyValuePair item2 in item) { if (num2++ > 0) { streamWriter.Write(value); } streamWriter.Write(item2.Key); } streamWriter.WriteLine(); } } else { streamWriter.WriteLine(); } int num3 = 0; foreach (string item3 in list) { BsonValue value2 = item[item3]; if (num3++ > 0) { streamWriter.Write(value); } WriteValue(value2, streamWriter); } } if (num > 0) { streamWriter.Flush(); } } finally { streamWriter?.Dispose(); fileStream?.Dispose(); } return num; } private void WriteValue(BsonValue value, StreamWriter writer) { switch (value.Type) { case BsonType.MinValue: case BsonType.Null: case BsonType.Document: case BsonType.Array: case BsonType.MaxValue: writer.Write("null"); break; case BsonType.Int64: writer.Write(value.AsInt64.ToString(CultureInfo.InvariantCulture.NumberFormat)); break; case BsonType.Decimal: writer.Write(value.AsDecimal.ToString(CultureInfo.InvariantCulture.NumberFormat)); break; case BsonType.Binary: writer.Write("\"" + Convert.ToBase64String(value.AsBinary) + "\""); break; case BsonType.ObjectId: writer.Write("\"" + value.AsObjectId.ToString() + "\""); break; case BsonType.Guid: writer.Write("\"" + value.AsGuid.ToString() + "\""); break; case BsonType.DateTime: writer.Write("\"" + value.AsDateTime.ToUniversalTime().ToString("o") + "\""); break; default: writer.Write(value.ToString()); break; } } private string ReadString(TextReader reader, char delimiter, out bool newLine) { StringBuilder stringBuilder = new StringBuilder(); int num = reader.Read(); while (num == 10 || num == 13) { num = reader.Read(); } if (num == -1) { newLine = true; return null; } if (num == 34) { while (num != -1) { num = reader.Read(); if (num == 34) { int num2 = reader.Read(); if (num2 != 34) { num = num2; break; } stringBuilder.Append('"'); } else { stringBuilder.Append((char)num); } } } else { while (num != 10 && num != 13 && num != delimiter && num != -1) { stringBuilder.Append((char)num); num = reader.Read(); } } newLine = num == 10 || num == 13; return stringBuilder.ToString(); } } internal class SysFileJson : SystemCollection { public SysFileJson() : base("$file_json") { } public override IEnumerable Input(BsonValue options) { string path = SystemCollection.GetOption(options, "filename")?.AsString ?? throw new LiteException(0, "Collection $" + base.Name + " requires string as 'filename' or a document field 'filename'"); string asString = SystemCollection.GetOption(options, "encoding", "utf-8").AsString; using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); using StreamReader reader = new StreamReader(fs, Encoding.GetEncoding(asString)); IEnumerable enumerable = from x in new JsonReader(reader).DeserializeArray() select x.AsDocument; foreach (BsonDocument item in enumerable) { yield return item; } } public override int Output(IEnumerable source, BsonValue options) { string path = SystemCollection.GetOption(options, "filename")?.AsString ?? throw new LiteException(0, "Collection $file_json requires string as filename or a document field 'filename'"); bool asBoolean = SystemCollection.GetOption(options, "pretty", false).AsBoolean; int asInt = SystemCollection.GetOption(options, "indent", 4).AsInt32; string asString = SystemCollection.GetOption(options, "encoding", "utf-8").AsString; bool asBoolean2 = SystemCollection.GetOption(options, "overwritten", false).AsBoolean; int num = 0; FileStream fileStream = null; StreamWriter streamWriter = null; JsonWriter jsonWriter = null; try { foreach (BsonDocument item in source) { if (num++ == 0) { fileStream = new FileStream(path, (!asBoolean2) ? FileMode.CreateNew : FileMode.OpenOrCreate); streamWriter = new StreamWriter(fileStream, Encoding.GetEncoding(asString)); jsonWriter = new JsonWriter(streamWriter) { Pretty = asBoolean, Indent = asInt }; streamWriter.WriteLine("["); } else { streamWriter.WriteLine(","); } jsonWriter.Serialize(item); } if (num > 0) { streamWriter.WriteLine(); streamWriter.Write("]"); streamWriter.Flush(); } } finally { streamWriter?.Dispose(); fileStream?.Dispose(); } return num; } } internal class SysPageList : SystemCollection { private readonly HeaderPage _header; private readonly TransactionMonitor _monitor; private Dictionary _collections; public SysPageList(HeaderPage header, TransactionMonitor monitor) : base("$page_list") { _header = header; _monitor = monitor; } public override IEnumerable Input(BsonValue options) { BsonValue option = SystemCollection.GetOption(options, "pageID"); TransactionService threadTransaction = _monitor.GetThreadTransaction(); Snapshot snapshot = threadTransaction.CreateSnapshot(LockMode.Read, "$", addIfNotExists: false); _collections = _header.GetCollections().ToDictionary((KeyValuePair x) => x.Value, (KeyValuePair x) => x.Key); IEnumerable enumerable = ((option != null) ? GetList((uint)option.AsInt32, null, threadTransaction, snapshot) : GetAllList(threadTransaction, snapshot)); foreach (BsonDocument item in enumerable) { yield return item; } } private IEnumerable GetAllList(TransactionService transaction, Snapshot snapshot) { foreach (BsonDocument item in GetList(_header.FreeEmptyPageList, null, transaction, snapshot)) { yield return item; } foreach (KeyValuePair collection in _collections) { Snapshot snap = transaction.CreateSnapshot(LockMode.Read, collection.Value, addIfNotExists: false); for (int slot2 = 0; slot2 < 5; slot2++) { IEnumerable list = GetList(snap.CollectionPage.FreeDataPageList[slot2], null, transaction, snap); foreach (BsonDocument item2 in list) { yield return item2; } } CollectionIndex[] array = snap.CollectionPage.GetCollectionIndexes().ToArray(); CollectionIndex[] array2 = array; foreach (CollectionIndex collectionIndex in array2) { IEnumerable list2 = GetList(collectionIndex.FreeIndexPageList, collectionIndex.Name, transaction, snap); foreach (BsonDocument item3 in list2) { yield return item3; } } } } private IEnumerable GetList(uint pageID, string indexName, TransactionService transaction, Snapshot snapshot) { if (pageID == uint.MaxValue) { yield break; } BasePage page = snapshot.GetPage(pageID); while (page != null) { _collections.TryGetValue(page.ColID, out var value); yield return new BsonDocument { ["pageID"] = (int)page.PageID, ["pageType"] = page.PageType.ToString(), ["slot"] = (int)page.PageListSlot, ["collection"] = value, ["index"] = indexName, ["freeBytes"] = page.FreeBytes, ["itemsCount"] = (int)page.ItemsCount }; if (page.NextPageID != uint.MaxValue) { transaction.Safepoint(); page = snapshot.GetPage(page.NextPageID); continue; } break; } } } internal class SysQuery : SystemCollection { private readonly ILiteEngine _engine; public SysQuery(ILiteEngine engine) : base("$query") { _engine = engine; } public override IEnumerable Input(BsonValue options) { string source = options?.AsString ?? throw new LiteException(0, "Collection $query(sql) requires `sql` string parameter"); SqlParser sqlParser = new SqlParser(_engine, new Tokenizer(source), null); using IBsonDataReader reader = sqlParser.Execute(); while (reader.Read()) { BsonValue current = reader.Current; yield return current.IsDocument ? current.AsDocument : new BsonDocument { ["expr"] = current }; } } } internal class SystemCollection { private readonly string _name; private readonly Func> _input; public string Name => _name; public SystemCollection(string name) { if (!name.StartsWith("$")) { throw new ArgumentException("System collection name must starts with $"); } _name = name; } public SystemCollection(string name, Func> input) : this(name) { _input = input; } public virtual IEnumerable Input(BsonValue options) { return _input(); } public virtual int Output(IEnumerable source, BsonValue options) { throw new LiteException(0, _name + " do not support as output collection"); } protected static BsonValue GetOption(BsonValue options, string key) { return GetOption(options, key, null); } protected static BsonValue GetOption(BsonValue options, string key, BsonValue defaultValue) { if (options != null && options.IsDocument) { if (options.AsDocument.TryGetValue(key, out var value)) { if (defaultValue == null || value.Type == defaultValue.Type) { return value; } throw new LiteException(0, $"Parameter `{key}` expect {defaultValue.Type} value type"); } return defaultValue; } if (!(defaultValue == null)) { return defaultValue; } return options; } } } [CompilerGenerated] internal sealed class <2cf7ffe9-4344-4787-9bd6-3f59ca1da686> { [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 16)] internal struct __StaticArrayInitTypeSize=16 { } [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 20)] internal struct __StaticArrayInitTypeSize=20 { } internal static readonly __StaticArrayInitTypeSize=20 922EDBD4B9866F46F53D8ECEAC2CEF018353178303D75DB92D7C5B22C48B701F/* Not supported: data(0C 00 00 00 32 00 00 00 64 00 00 00 F4 01 00 00 E8 03 00 00) */; internal static readonly __StaticArrayInitTypeSize=16 EAA0F1FB54F6BEDA002754D2D7C470521C67E524EE1700421AA371706C1C8270/* Not supported: data(B0 1C 00 00 E8 17 00 00 20 13 00 00 90 09 00 00) */; } namespace Microsoft.CodeAnalysis { [<7e83054d-de25-4f31-945e-4e28123c60d7>Embedded] [CompilerGenerated] internal sealed class <7e83054d-de25-4f31-945e-4e28123c60d7>EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [<7e83054d-de25-4f31-945e-4e28123c60d7>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] [<7e83054d-de25-4f31-945e-4e28123c60d7>Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class <4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContextAttribute : Attribute { public readonly byte Flag; public <4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [<7e83054d-de25-4f31-945e-4e28123c60d7>Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class <5dd8b8d7-2199-41e5-9b09-11abdcf1620b>RefSafetyRulesAttribute : Attribute { public readonly int Version; public <5dd8b8d7-2199-41e5-9b09-11abdcf1620b>RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ServerSync { [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(1)] [Nullable(0)] [PublicAPI] internal abstract class OwnConfigEntryBase { [Nullable(2)] public object LocalBaseValue; public bool SynchronizedConfig = true; public abstract ConfigEntryBase BaseConfig { get; } } [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(1)] [Nullable(0)] [PublicAPI] internal class SyncedConfigEntry<[Nullable(2)] T> : OwnConfigEntryBase { public readonly ConfigEntry SourceConfig; public override ConfigEntryBase BaseConfig => (ConfigEntryBase)(object)SourceConfig; public T Value { get { return SourceConfig.Value; } set { SourceConfig.Value = value; } } public SyncedConfigEntry(ConfigEntry sourceConfig) { SourceConfig = sourceConfig; base..ctor(); } public void AssignLocalValue(T value) { if (LocalBaseValue == null) { Value = value; } else { LocalBaseValue = value; } } } [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(2)] [Nullable(0)] internal abstract class CustomSyncedValueBase { public object LocalBaseValue; [Nullable(1)] public readonly string Identifier; [Nullable(1)] public readonly Type Type; private object boxedValue; protected bool localIsOwner; public readonly int Priority; public object BoxedValue { get { return boxedValue; } set { boxedValue = value; this.ValueChanged?.Invoke(); } } public event Action ValueChanged; [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(1)] protected CustomSyncedValueBase(ConfigSync configSync, string identifier, Type type, int priority) { Priority = priority; Identifier = identifier; Type = type; configSync.AddCustomValue(this); localIsOwner = configSync.IsSourceOfTruth; configSync.SourceOfTruthChanged += delegate(bool truth) { localIsOwner = truth; }; } } [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(1)] [Nullable(0)] [PublicAPI] internal sealed class CustomSyncedValue<[Nullable(2)] T> : CustomSyncedValueBase { public T Value { get { return (T)base.BoxedValue; } set { base.BoxedValue = value; } } public CustomSyncedValue(ConfigSync configSync, string identifier, T value = default(T), int priority = 0) : base(configSync, identifier, typeof(T), priority) { Value = value; } public void AssignLocalValue(T value) { if (localIsOwner) { Value = value; } else { LocalBaseValue = value; } } } internal class ConfigurationManagerAttributes { [UsedImplicitly] public bool? ReadOnly = false; } [PublicAPI] [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(1)] [Nullable(0)] internal class ConfigSync { [HarmonyPatch(typeof(ZRpc), "HandlePackage")] [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] private static class SnatchCurrentlyHandlingRPC { [Nullable(2)] public static ZRpc currentRpc; [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(1)] [HarmonyPrefix] private static void Prefix(ZRpc __instance) { currentRpc = __instance; } } [HarmonyPatch(typeof(ZNet), "Awake")] [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] internal static class RegisterRPCPatch { [CompilerGenerated] private sealed class <g__WatchAdminListChanges|0_0>d : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private <>c__DisplayClass0_0 <>8__1; [Nullable(new byte[] { 0, 1 })] private List 5__2; [Nullable(new byte[] { 0, 1 })] private List 5__3; [Nullable(new byte[] { 0, 1 })] private List 5__4; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <g__WatchAdminListChanges|0_0>d(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; 5__2 = null; 5__3 = null; 5__4 = null; <>1__state = -2; } private bool MoveNext() { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected O, but got Unknown int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; if (!<>8__1.adminList.GetList().SequenceEqual(5__2)) { 5__2 = new List(<>8__1.adminList.GetList()); 5__3 = ZNet.instance.GetPeers().Where(delegate(ZNetPeer p) { string hostName = p.m_rpc.GetSocket().GetHostName(); return ((object)<>8__1.listContainsId == null) ? <>8__1.adminList.Contains(hostName) : ((bool)<>8__1.listContainsId.Invoke(ZNet.instance, new object[2] { <>8__1.adminList, hostName })); }).ToList(); 5__4 = ZNet.instance.GetPeers().Except(5__3).ToList(); g__SendAdmin|0_1(5__4, isAdmin: false); g__SendAdmin|0_1(5__3, isAdmin: true); 5__3 = null; 5__4 = null; } } else { <>1__state = -1; <>8__1 = new <>c__DisplayClass0_0(); <>8__1.listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); <>8__1.adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); 5__2 = new List(<>8__1.adminList.GetList()); } <>2__current = (object)new WaitForSeconds(30f); <>1__state = 1; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <>c__DisplayClass0_0 { public MethodInfo listContainsId; public SyncedList adminList; public Func <>9__2; internal bool b__2(ZNetPeer p) { string hostName = p.m_rpc.GetSocket().GetHostName(); return ((object)listContainsId == null) ? adminList.Contains(hostName) : ((bool)listContainsId.Invoke(ZNet.instance, new object[2] { adminList, hostName })); } } [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(1)] [HarmonyPostfix] private static void Postfix(ZNet __instance) { isServer = __instance.IsServer(); foreach (ConfigSync configSync in configSyncs) { ZRoutedRpc.instance.Register(configSync.Name + " ConfigSync", (Action)configSync.RPC_FromOtherClientConfigSync); if (isServer) { configSync.InitialSyncDone = true; Debug.Log((object)("Registered '" + configSync.Name + " ConfigSync' RPC - waiting for incoming connections")); } } if (isServer) { ((MonoBehaviour)__instance).StartCoroutine(WatchAdminListChanges()); } [IteratorStateMachine(typeof(<g__WatchAdminListChanges|0_0>d))] [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(1)] static IEnumerator WatchAdminListChanges() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <g__WatchAdminListChanges|0_0>d(0); } } [CompilerGenerated] [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(1)] internal static void g__SendAdmin|0_1(List peers, bool isAdmin) { ZPackage package = ConfigsToPackage(null, null, new PackageEntry[1] { new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = isAdmin } }); ConfigSync configSync = configSyncs.First(); if (configSync != null) { ((MonoBehaviour)ZNet.instance).StartCoroutine(configSync.sendZPackage(peers, package)); } } } [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class RegisterClientRPCPatch { [HarmonyPostfix] [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(1)] private static void Postfix(ZNet __instance, ZNetPeer peer) { if (__instance.IsServer()) { return; } foreach (ConfigSync configSync in configSyncs) { peer.m_rpc.Register(configSync.Name + " ConfigSync", (Action)configSync.RPC_FromServerConfigSync); } } } [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] private class ParsedConfigs { [Nullable(new byte[] { 1, 1, 2 })] public readonly Dictionary configValues = new Dictionary(); [Nullable(new byte[] { 1, 1, 2 })] public readonly Dictionary customValues = new Dictionary(); } [HarmonyPatch(typeof(ZNet), "Shutdown")] [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] private class ResetConfigsOnShutdown { [HarmonyPostfix] private static void Postfix() { ProcessingServerUpdate = true; foreach (ConfigSync configSync in configSyncs) { configSync.resetConfigsFromServer(); configSync.IsSourceOfTruth = true; configSync.InitialSyncDone = false; } ProcessingServerUpdate = false; } } [Nullable(0)] [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private class SendConfigsAfterLogin { [Nullable(0)] private class BufferingSocket : ZPlayFabSocket, ISocket { public volatile bool finished = false; public volatile int versionMatchQueued = -1; public readonly List Package = new List(); public readonly ISocket Original; public BufferingSocket(ISocket original) { Original = original; ((ZPlayFabSocket)this)..ctor(); } public bool IsConnected() { return Original.IsConnected(); } public ZPackage Recv() { return Original.Recv(); } public int GetSendQueueSize() { return Original.GetSendQueueSize(); } public int GetCurrentSendRate() { return Original.GetCurrentSendRate(); } public bool IsHost() { return Original.IsHost(); } public void Dispose() { Original.Dispose(); } public bool GotNewData() { return Original.GotNewData(); } public void Close() { Original.Close(); } public string GetEndPointString() { return Original.GetEndPointString(); } public void GetAndResetStats(out int totalSent, out int totalRecv) { Original.GetAndResetStats(ref totalSent, ref totalRecv); } public void GetConnectionQuality(out float localQuality, out float remoteQuality, out int ping, out float outByteSec, out float inByteSec) { Original.GetConnectionQuality(ref localQuality, ref remoteQuality, ref ping, ref outByteSec, ref inByteSec); } public ISocket Accept() { return Original.Accept(); } public int GetHostPort() { return Original.GetHostPort(); } public bool Flush() { return Original.Flush(); } public string GetHostName() { return Original.GetHostName(); } public void VersionMatch() { if (finished) { Original.VersionMatch(); } else { versionMatchQueued = Package.Count; } } public void Send(ZPackage pkg) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown int pos = pkg.GetPos(); pkg.SetPos(0); int num = pkg.ReadInt(); if ((num == StringExtensionMethods.GetStableHashCode("PeerInfo") || num == StringExtensionMethods.GetStableHashCode("RoutedRPC") || num == StringExtensionMethods.GetStableHashCode("ZDOData")) && !finished) { ZPackage val = new ZPackage(pkg.GetArray()); val.SetPos(pos); Package.Add(val); } else { pkg.SetPos(pos); Original.Send(pkg); } } } [CompilerGenerated] private sealed class <>c__DisplayClass2_0 { [Nullable(0)] public ZRpc rpc; [Nullable(0)] public ZNet __instance; [Nullable(new byte[] { 0, 1, 1 })] public Dictionary __state; [Nullable(0)] public ZNetPeer peer; } [HarmonyPriority(800)] [HarmonyPrefix] private static void Prefix([Nullable(new byte[] { 2, 1, 1 })] ref Dictionary __state, ZNet __instance, ZRpc rpc) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Invalid comparison between Unknown and I4 if (!__instance.IsServer()) { return; } BufferingSocket bufferingSocket = new BufferingSocket(rpc.GetSocket()); AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, bufferingSocket); object? obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); ZNetPeer val = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (val != null && (int)ZNet.m_onlineBackend > 0) { FieldInfo fieldInfo = AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket"); object? value = fieldInfo.GetValue(val); ZPlayFabSocket val2 = (ZPlayFabSocket)((value is ZPlayFabSocket) ? value : null); if (val2 != null) { typeof(ZPlayFabSocket).GetField("m_remotePlayerId").SetValue(bufferingSocket, val2.m_remotePlayerId); } fieldInfo.SetValue(val, bufferingSocket); } if (__state == null) { __state = new Dictionary(); } __state[Assembly.GetExecutingAssembly()] = bufferingSocket; } [HarmonyPostfix] private static void Postfix(Dictionary __state, ZNet __instance, ZRpc rpc) { <>c__DisplayClass2_0 CS$<>8__locals0 = new <>c__DisplayClass2_0(); CS$<>8__locals0.rpc = rpc; CS$<>8__locals0.__instance = __instance; CS$<>8__locals0.__state = __state; if (CS$<>8__locals0.__instance.IsServer()) { object obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(CS$<>8__locals0.__instance, new object[1] { CS$<>8__locals0.rpc }); CS$<>8__locals0.peer = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (CS$<>8__locals0.peer == null) { SendBufferedData(); } else { ((MonoBehaviour)CS$<>8__locals0.__instance).StartCoroutine(sendAsync()); } } void SendBufferedData() { if (CS$<>8__locals0.rpc.GetSocket() is BufferingSocket bufferingSocket) { AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(CS$<>8__locals0.rpc, bufferingSocket.Original); object? obj2 = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(CS$<>8__locals0.__instance, new object[1] { CS$<>8__locals0.rpc }); ZNetPeer val = (ZNetPeer)((obj2 is ZNetPeer) ? obj2 : null); if (val != null) { AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket").SetValue(val, bufferingSocket.Original); } } BufferingSocket bufferingSocket2 = CS$<>8__locals0.__state[Assembly.GetExecutingAssembly()]; bufferingSocket2.finished = true; for (int i = 0; i < bufferingSocket2.Package.Count; i++) { if (i == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } bufferingSocket2.Original.Send(bufferingSocket2.Package[i]); } if (bufferingSocket2.Package.Count == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } } [IteratorStateMachine(typeof(<>c__DisplayClass2_0.<g__sendAsync|1>d))] IEnumerator sendAsync() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <>c__DisplayClass2_0.<g__sendAsync|1>d(0) { <>4__this = CS$<>8__locals0 }; } } } [Nullable(0)] private class PackageEntry { public string section = null; public string key = null; public Type type = null; [Nullable(2)] public object value; } [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] [HarmonyPatch(typeof(ConfigEntryBase), "GetSerializedValue")] private static class PreventSavingServerInfo { [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(1)] [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, ref string __result) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || isWritableConfig(ownConfigEntryBase)) { return true; } __result = TomlTypeConverter.ConvertToString(ownConfigEntryBase.LocalBaseValue, __instance.SettingType); return false; } } [HarmonyPatch(typeof(ConfigEntryBase), "SetSerializedValue")] [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] private static class PreventConfigRereadChangingValues { [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(1)] [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, string value) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || ownConfigEntryBase.LocalBaseValue == null) { return true; } try { ownConfigEntryBase.LocalBaseValue = TomlTypeConverter.ConvertToValue(value, __instance.SettingType); } catch (Exception ex) { Debug.LogWarning((object)$"Config value of setting \"{__instance.Definition}\" could not be parsed and will be ignored. Reason: {ex.Message}; Value: {value}"); } return false; } } [Nullable(0)] private class InvalidDeserializationTypeException : Exception { public string expected = null; public string received = null; public string field = ""; } [CompilerGenerated] private sealed class <>c__DisplayClass55_0 { [Nullable(0)] public ZNetPeer peer; [Nullable(0)] public ConfigSync <>4__this; [Nullable(0)] public ZRoutedRpc rpc; } [CompilerGenerated] private sealed class <>c__DisplayClass57_0 { [Nullable(0)] public ConfigSync <>4__this; [Nullable(0)] public ZPackage package; [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] internal IEnumerator b__1(ZNetPeer p) { return <>4__this.distributeConfigToPeers(p, package); } } [CompilerGenerated] private sealed class d__55 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private bool <>2__current; [Nullable(0)] public ZNetPeer peer; [Nullable(0)] public ZPackage package; [Nullable(0)] public ConfigSync <>4__this; [Nullable(0)] private <>c__DisplayClass55_0 <>8__1; [Nullable(0)] private byte[] 5__2; private int 5__3; private long 5__4; private int 5__5; [Nullable(0)] private ZPackage 5__6; [Nullable(0)] private IEnumerator <>s__7; private bool 5__8; [Nullable(0)] private IEnumerator <>s__9; private bool 5__10; bool IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] [return: Nullable(0)] get { return <>2__current; } } [DebuggerHidden] public d__55(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { switch (<>1__state) { case -3: case 1: try { } finally { <>m__Finally1(); } break; case -4: case 3: try { } finally { <>m__Finally2(); } break; } <>8__1 = null; 5__2 = null; 5__6 = null; <>s__7 = null; <>s__9 = null; <>1__state = -2; } private bool MoveNext() { //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Expected O, but got Unknown try { int num; switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass55_0(); <>8__1.peer = peer; <>8__1.<>4__this = <>4__this; <>8__1.rpc = ZRoutedRpc.instance; if (<>8__1.rpc == null) { return false; } 5__2 = package.GetArray(); if (5__2 != null && 5__2.LongLength > 250000) { 5__3 = (int)(1 + (5__2.LongLength - 1) / 250000); 5__4 = ++packageCounter; 5__5 = 0; goto IL_0286; } <>s__9 = waitForQueue().GetEnumerator(); <>1__state = -4; goto IL_02f7; case 1: <>1__state = -3; goto IL_0162; case 2: <>1__state = -1; goto IL_026c; case 3: { <>1__state = -4; goto IL_02f7; } IL_0162: if (<>s__7.MoveNext()) { 5__8 = <>s__7.Current; <>2__current = 5__8; <>1__state = 1; return true; } <>m__Finally1(); <>s__7 = null; if (!<>8__1.peer.m_socket.IsConnected()) { return false; } 5__6 = new ZPackage(); 5__6.Write((byte)2); 5__6.Write(5__4); 5__6.Write(5__5); 5__6.Write(5__3); 5__6.Write(5__2.Skip(250000 * 5__5).Take(250000).ToArray()); SendPackage(5__6); if (5__5 != 5__3 - 1) { <>2__current = true; <>1__state = 2; return true; } goto IL_026c; IL_026c: 5__6 = null; num = 5__5 + 1; 5__5 = num; goto IL_0286; IL_02f7: if (<>s__9.MoveNext()) { 5__10 = <>s__9.Current; <>2__current = 5__10; <>1__state = 3; return true; } <>m__Finally2(); <>s__9 = null; SendPackage(package); break; IL_0286: if (5__5 < 5__3) { <>s__7 = waitForQueue().GetEnumerator(); <>1__state = -3; goto IL_0162; } break; } return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } void SendPackage(ZPackage pkg) { string text = ((<>c__DisplayClass55_0)this).<>4__this.Name + " ConfigSync"; if (isServer) { ((<>c__DisplayClass55_0)this).peer.m_rpc.Invoke(text, new object[1] { pkg }); } else { ((<>c__DisplayClass55_0)this).rpc.InvokeRoutedRPC(((<>c__DisplayClass55_0)this).peer.m_server ? 0 : ((<>c__DisplayClass55_0)this).peer.m_uid, text, new object[1] { pkg }); } } [IteratorStateMachine(typeof(<>c__DisplayClass55_0.<g__waitForQueue|0>d))] IEnumerable waitForQueue() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <>c__DisplayClass55_0.<g__waitForQueue|0>d(-2) { <>4__this = this }; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>s__7 != null) { <>s__7.Dispose(); } } private void <>m__Finally2() { <>1__state = -1; if (<>s__9 != null) { <>s__9.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__57 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; [Nullable(0)] private object <>2__current; [Nullable(new byte[] { 0, 1 })] public List peers; [Nullable(0)] public ZPackage package; [Nullable(0)] public ConfigSync <>4__this; [Nullable(0)] private <>c__DisplayClass57_0 <>8__1; [Nullable(0)] private byte[] 5__2; [Nullable(new byte[] { 0, 1 })] private List> 5__3; [Nullable(0)] private ZPackage 5__4; [Nullable(0)] private MemoryStream 5__5; [Nullable(0)] private DeflateStream 5__6; object IEnumerator.Current { [DebuggerHidden] [return: Nullable(0)] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] [return: Nullable(0)] get { return <>2__current; } } [DebuggerHidden] public d__57(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; 5__2 = null; 5__3 = null; 5__4 = null; 5__5 = null; 5__6 = null; <>1__state = -2; } private bool MoveNext() { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass57_0(); <>8__1.<>4__this = <>4__this; <>8__1.package = package; if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return false; } 5__2 = <>8__1.package.GetArray(); if (5__2 != null && 5__2.LongLength > 10000) { 5__4 = new ZPackage(); 5__4.Write((byte)4); 5__5 = new MemoryStream(); 5__6 = new DeflateStream(5__5, CompressionLevel.Optimal); try { 5__6.Write(5__2, 0, 5__2.Length); } finally { if (5__6 != null) { ((IDisposable)5__6).Dispose(); } } 5__6 = null; 5__4.Write(5__5.ToArray()); <>8__1.package = 5__4; 5__4 = null; 5__5 = null; } 5__3 = (from peer in peers where peer.IsReady() select peer into p select <>8__1.<>4__this.distributeConfigToPeers(p, <>8__1.package)).ToList(); 5__3.RemoveAll((IEnumerator writer) => !writer.MoveNext()); break; case 1: <>1__state = -1; 5__3.RemoveAll((IEnumerator writer) => !writer.MoveNext()); break; } if (5__3.Count > 0) { <>2__current = null; <>1__state = 1; return true; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public static bool ProcessingServerUpdate; public readonly string Name; [Nullable(2)] public string DisplayName; [Nullable(2)] public string CurrentVersion; [Nullable(2)] public string MinimumRequiredVersion; public bool ModRequired = false; private bool? forceConfigLocking; private bool isSourceOfTruth = true; private static readonly HashSet configSyncs; private readonly HashSet allConfigs = new HashSet(); private HashSet allCustomValues = new HashSet(); private static bool isServer; private static bool lockExempt; [Nullable(2)] private OwnConfigEntryBase lockedConfig = null; private const byte PARTIAL_CONFIGS = 1; private const byte FRAGMENTED_CONFIG = 2; private const byte COMPRESSED_CONFIG = 4; private readonly Dictionary> configValueCache = new Dictionary>(); [Nullable(new byte[] { 1, 0, 1 })] private readonly List> cacheExpirations = new List>(); private static long packageCounter; public bool IsLocked { get { bool? flag = forceConfigLocking; bool num; if (!flag.HasValue) { if (lockedConfig == null) { goto IL_0052; } num = ((IConvertible)lockedConfig.BaseConfig.BoxedValue).ToInt32(CultureInfo.InvariantCulture) != 0; } else { num = flag.GetValueOrDefault(); } if (!num) { goto IL_0052; } int result = ((!lockExempt) ? 1 : 0); goto IL_0053; IL_0053: return (byte)result != 0; IL_0052: result = 0; goto IL_0053; } set { forceConfigLocking = value; } } public bool IsAdmin => lockExempt || isSourceOfTruth; public bool IsSourceOfTruth { get { return isSourceOfTruth; } private set { if (value != isSourceOfTruth) { isSourceOfTruth = value; this.SourceOfTruthChanged?.Invoke(value); } } } public bool InitialSyncDone { get; private set; } = false; [Nullable(2)] [method: <4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(2)] [field: Nullable(2)] public event Action SourceOfTruthChanged; [Nullable(2)] [method: <4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(2)] [field: Nullable(2)] private event Action lockedConfigChanged; static ConfigSync() { ProcessingServerUpdate = false; configSyncs = new HashSet(); lockExempt = false; packageCounter = 0L; RuntimeHelpers.RunClassConstructor(typeof(VersionCheck).TypeHandle); } public ConfigSync(string name) { Name = name; configSyncs.Add(this); new VersionCheck(this); } public SyncedConfigEntry AddConfigEntry<[Nullable(2)] T>(ConfigEntry configEntry) { OwnConfigEntryBase ownConfigEntryBase = configData((ConfigEntryBase)(object)configEntry); SyncedConfigEntry syncedEntry = ownConfigEntryBase as SyncedConfigEntry; if (syncedEntry == null) { syncedEntry = new SyncedConfigEntry(configEntry); AccessTools.DeclaredField(typeof(ConfigDescription), "k__BackingField").SetValue(((ConfigEntryBase)configEntry).Description, new object[1] { new ConfigurationManagerAttributes() }.Concat(((ConfigEntryBase)configEntry).Description.Tags ?? Array.Empty()).Concat(new SyncedConfigEntry[1] { syncedEntry }).ToArray()); configEntry.SettingChanged += [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (object _, EventArgs _) => { if (!ProcessingServerUpdate && syncedEntry.SynchronizedConfig) { Broadcast(ZRoutedRpc.Everybody, (ConfigEntryBase)configEntry); } }; allConfigs.Add(syncedEntry); } return syncedEntry; } public SyncedConfigEntry AddLockingConfigEntry<[Nullable(0)] T>(ConfigEntry lockingConfig) where T : IConvertible { if (lockedConfig != null) { throw new Exception("Cannot initialize locking ConfigEntry twice"); } lockedConfig = AddConfigEntry(lockingConfig); lockingConfig.SettingChanged += [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (object _, EventArgs _) => { this.lockedConfigChanged?.Invoke(); }; return (SyncedConfigEntry)lockedConfig; } internal void AddCustomValue(CustomSyncedValueBase customValue) { if (allCustomValues.Select([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (CustomSyncedValueBase v) => v.Identifier).Concat(new string[1] { "serverversion" }).Contains(customValue.Identifier)) { throw new Exception("Cannot have multiple settings with the same name or with a reserved name (serverversion)"); } allCustomValues.Add(customValue); allCustomValues = new HashSet(allCustomValues.OrderByDescending([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (CustomSyncedValueBase v) => v.Priority)); customValue.ValueChanged += delegate { if (!ProcessingServerUpdate) { Broadcast(ZRoutedRpc.Everybody, customValue); } }; } private void RPC_FromServerConfigSync(ZRpc rpc, ZPackage package) { lockedConfigChanged += serverLockedSettingChanged; IsSourceOfTruth = false; if (HandleConfigSyncRPC(0L, package, clientUpdate: false)) { InitialSyncDone = true; } } private void RPC_FromOtherClientConfigSync(long sender, ZPackage package) { HandleConfigSyncRPC(sender, package, clientUpdate: true); } private bool HandleConfigSyncRPC(long sender, ZPackage package, bool clientUpdate) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Expected O, but got Unknown //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Expected O, but got Unknown try { if (isServer && IsLocked) { ZRpc currentRpc = SnatchCurrentlyHandlingRPC.currentRpc; object obj; if (currentRpc == null) { obj = null; } else { ISocket socket = currentRpc.GetSocket(); obj = ((socket != null) ? socket.GetHostName() : null); } string text = (string)obj; if (text != null) { MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList val = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); if (!(((object)methodInfo == null) ? val.Contains(text) : ((bool)methodInfo.Invoke(ZNet.instance, new object[2] { val, text })))) { return false; } } } cacheExpirations.RemoveAll(([Nullable(new byte[] { 0, 1 })] KeyValuePair kv) => { if (kv.Key < DateTimeOffset.Now.Ticks) { configValueCache.Remove(kv.Value); return true; } return false; }); byte b = package.ReadByte(); if ((b & 2u) != 0) { long num = package.ReadLong(); string text2 = sender.ToString() + num; if (!configValueCache.TryGetValue(text2, out var value)) { value = new SortedDictionary(); configValueCache[text2] = value; cacheExpirations.Add(new KeyValuePair(DateTimeOffset.Now.AddSeconds(60.0).Ticks, text2)); } int key = package.ReadInt(); int num2 = package.ReadInt(); value.Add(key, package.ReadByteArray()); if (value.Count < num2) { return false; } configValueCache.Remove(text2); package = new ZPackage(value.Values.SelectMany([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (byte[] a) => a).ToArray()); b = package.ReadByte(); } ProcessingServerUpdate = true; if ((b & 4u) != 0) { byte[] buffer = package.ReadByteArray(); MemoryStream stream = new MemoryStream(buffer); MemoryStream memoryStream = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress)) { deflateStream.CopyTo(memoryStream); } package = new ZPackage(memoryStream.ToArray()); b = package.ReadByte(); } if ((b & 1) == 0) { resetConfigsFromServer(); } ParsedConfigs parsedConfigs = ReadConfigsFromPackage(package); ConfigFile val2 = null; bool saveOnConfigSet = false; foreach (KeyValuePair configValue in parsedConfigs.configValues) { if (!isServer && configValue.Key.LocalBaseValue == null) { configValue.Key.LocalBaseValue = configValue.Key.BaseConfig.BoxedValue; } if (val2 == null) { val2 = configValue.Key.BaseConfig.ConfigFile; saveOnConfigSet = val2.SaveOnConfigSet; val2.SaveOnConfigSet = false; } configValue.Key.BaseConfig.BoxedValue = configValue.Value; } if (val2 != null) { val2.SaveOnConfigSet = saveOnConfigSet; val2.Save(); } foreach (KeyValuePair customValue in parsedConfigs.customValues) { if (!isServer) { CustomSyncedValueBase key2 = customValue.Key; if (key2.LocalBaseValue == null) { key2.LocalBaseValue = customValue.Key.BoxedValue; } } customValue.Key.BoxedValue = customValue.Value; } Debug.Log((object)string.Format("Received {0} configs and {1} custom values from {2} for mod {3}", parsedConfigs.configValues.Count, parsedConfigs.customValues.Count, (isServer || clientUpdate) ? $"client {sender}" : "the server", DisplayName ?? Name)); if (!isServer) { serverLockedSettingChanged(); } return true; } finally { ProcessingServerUpdate = false; } } private ParsedConfigs ReadConfigsFromPackage(ZPackage package) { ParsedConfigs parsedConfigs = new ParsedConfigs(); Dictionary dictionary = allConfigs.Where([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (OwnConfigEntryBase c) => c.SynchronizedConfig).ToDictionary([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (OwnConfigEntryBase c) => c.BaseConfig.Definition.Section + "_" + c.BaseConfig.Definition.Key, [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (OwnConfigEntryBase c) => c); Dictionary dictionary2 = allCustomValues.ToDictionary([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (CustomSyncedValueBase c) => c.Identifier, [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (CustomSyncedValueBase c) => c); int num = package.ReadInt(); for (int i = 0; i < num; i++) { string text = package.ReadString(); string text2 = package.ReadString(); string text3 = package.ReadString(); Type type = Type.GetType(text3); if (text3 == "" || type != null) { object obj; try { obj = ((text3 == "") ? null : ReadValueWithTypeFromZPackage(package, type)); } catch (InvalidDeserializationTypeException ex) { Debug.LogWarning((object)("Got unexpected struct internal type " + ex.received + " for field " + ex.field + " struct " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + ex.expected)); continue; } OwnConfigEntryBase value2; if (text == "Internal") { CustomSyncedValueBase value; if (text2 == "serverversion") { if (obj?.ToString() != CurrentVersion) { Debug.LogWarning((object)("Received server version is not equal: server version = " + (obj?.ToString() ?? "null") + "; local version = " + (CurrentVersion ?? "unknown"))); } } else if (text2 == "lockexempt") { if (obj is bool flag) { lockExempt = flag; } } else if (dictionary2.TryGetValue(text2, out value)) { if ((text3 == "" && (!value.Type.IsValueType || Nullable.GetUnderlyingType(value.Type) != null)) || GetZPackageTypeString(value.Type) == text3) { parsedConfigs.customValues[value] = obj; continue; } Debug.LogWarning((object)("Got unexpected type " + text3 + " for internal value " + text2 + " for mod " + (DisplayName ?? Name) + ", expecting " + value.Type.AssemblyQualifiedName)); } } else if (dictionary.TryGetValue(text + "_" + text2, out value2)) { Type type2 = configType(value2.BaseConfig); if ((text3 == "" && (!type2.IsValueType || Nullable.GetUnderlyingType(type2) != null)) || GetZPackageTypeString(type2) == text3) { parsedConfigs.configValues[value2] = obj; continue; } Debug.LogWarning((object)("Got unexpected type " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + type2.AssemblyQualifiedName)); } else { Debug.LogWarning((object)("Received unknown config entry " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ". This may happen if client and server versions of the mod do not match.")); } continue; } Debug.LogWarning((object)("Got invalid type " + text3 + ", abort reading of received configs")); return new ParsedConfigs(); } return parsedConfigs; } private static bool isWritableConfig(OwnConfigEntryBase config) { ConfigSync configSync = configSyncs.FirstOrDefault([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (ConfigSync cs) => cs.allConfigs.Contains(config)); if (configSync == null) { return true; } return configSync.IsSourceOfTruth || !config.SynchronizedConfig || config.LocalBaseValue == null || (!configSync.IsLocked && (config != configSync.lockedConfig || lockExempt)); } private void serverLockedSettingChanged() { foreach (OwnConfigEntryBase allConfig in allConfigs) { configAttribute(allConfig.BaseConfig).ReadOnly = !isWritableConfig(allConfig); } } private void resetConfigsFromServer() { ConfigFile val = null; bool saveOnConfigSet = false; foreach (OwnConfigEntryBase item in allConfigs.Where([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (OwnConfigEntryBase config) => config.LocalBaseValue != null)) { if (val == null) { val = item.BaseConfig.ConfigFile; saveOnConfigSet = val.SaveOnConfigSet; val.SaveOnConfigSet = false; } item.BaseConfig.BoxedValue = item.LocalBaseValue; item.LocalBaseValue = null; } if (val != null) { val.SaveOnConfigSet = saveOnConfigSet; } foreach (CustomSyncedValueBase item2 in allCustomValues.Where([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (CustomSyncedValueBase config) => config.LocalBaseValue != null)) { item2.BoxedValue = item2.LocalBaseValue; item2.LocalBaseValue = null; } lockedConfigChanged -= serverLockedSettingChanged; serverLockedSettingChanged(); } [IteratorStateMachine(typeof(d__55))] private IEnumerator distributeConfigToPeers(ZNetPeer peer, ZPackage package) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__55(0) { <>4__this = this, peer = peer, package = package }; } private IEnumerator sendZPackage(long target, ZPackage package) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return Enumerable.Empty().GetEnumerator(); } List list = (List)AccessTools.DeclaredField(typeof(ZRoutedRpc), "m_peers").GetValue(ZRoutedRpc.instance); if (target != ZRoutedRpc.Everybody) { list = list.Where([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (ZNetPeer p) => p.m_uid == target).ToList(); } return sendZPackage(list, package); } [IteratorStateMachine(typeof(d__57))] private IEnumerator sendZPackage(List peers, ZPackage package) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__57(0) { <>4__this = this, peers = peers, package = package }; } private void Broadcast(long target, params ConfigEntryBase[] configs) { if (!IsLocked || isServer) { ZPackage package = ConfigsToPackage(configs); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(sendZPackage(target, package)); } } } private void Broadcast(long target, params CustomSyncedValueBase[] customValues) { if (!IsLocked || isServer) { ZPackage package = ConfigsToPackage(null, customValues); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(sendZPackage(target, package)); } } } [return: Nullable(2)] private static OwnConfigEntryBase configData(ConfigEntryBase config) { return config.Description.Tags?.OfType().SingleOrDefault(); } [return: Nullable(new byte[] { 2, 1 })] public static SyncedConfigEntry ConfigData<[Nullable(2)] T>(ConfigEntry config) { return ((ConfigEntryBase)config).Description.Tags?.OfType>().SingleOrDefault(); } private static T configAttribute<[Nullable(2)] T>(ConfigEntryBase config) { return config.Description.Tags.OfType().First(); } private static Type configType(ConfigEntryBase config) { return configType(config.SettingType); } private static Type configType(Type type) { return type.IsEnum ? Enum.GetUnderlyingType(type) : type; } private static ZPackage ConfigsToPackage([Nullable(new byte[] { 2, 1 })] IEnumerable configs = null, [Nullable(new byte[] { 2, 1 })] IEnumerable customValues = null, [Nullable(new byte[] { 2, 1 })] IEnumerable packageEntries = null, bool partial = true) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown List list = configs?.Where([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (ConfigEntryBase config) => configData(config).SynchronizedConfig).ToList() ?? new List(); List list2 = customValues?.ToList() ?? new List(); ZPackage val = new ZPackage(); val.Write((byte)(partial ? 1 : 0)); val.Write(list.Count + list2.Count + (packageEntries?.Count() ?? 0)); foreach (PackageEntry item in packageEntries ?? Array.Empty()) { AddEntryToPackage(val, item); } foreach (CustomSyncedValueBase item2 in list2) { AddEntryToPackage(val, new PackageEntry { section = "Internal", key = item2.Identifier, type = item2.Type, value = item2.BoxedValue }); } foreach (ConfigEntryBase item3 in list) { AddEntryToPackage(val, new PackageEntry { section = item3.Definition.Section, key = item3.Definition.Key, type = configType(item3), value = item3.BoxedValue }); } return val; } private static void AddEntryToPackage(ZPackage package, PackageEntry entry) { package.Write(entry.section); package.Write(entry.key); package.Write((entry.value == null) ? "" : GetZPackageTypeString(entry.type)); AddValueToZPackage(package, entry.value); } private static string GetZPackageTypeString(Type type) { return type.AssemblyQualifiedName; } private static void AddValueToZPackage(ZPackage package, [Nullable(2)] object value) { Type type = value?.GetType(); if (value is Enum) { value = ((IConvertible)value).ToType(Enum.GetUnderlyingType(value.GetType()), CultureInfo.InvariantCulture); } else { if (value is ICollection collection) { package.Write(collection.Count); { foreach (object item in collection) { AddValueToZPackage(package, item); } return; } } if ((object)type != null && type.IsValueType && !type.IsPrimitive) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); package.Write(fields.Length); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { package.Write(GetZPackageTypeString(fieldInfo.FieldType)); AddValueToZPackage(package, fieldInfo.GetValue(value)); } return; } } ZRpc.Serialize(new object[1] { value }, ref package); } private static object ReadValueWithTypeFromZPackage(ZPackage package, Type type) { if ((object)type != null && type.IsValueType && !type.IsPrimitive && !type.IsEnum) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); int num = package.ReadInt(); if (num != fields.Length) { throw new InvalidDeserializationTypeException { received = $"(field count: {num})", expected = $"(field count: {fields.Length})" }; } object uninitializedObject = FormatterServices.GetUninitializedObject(type); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { string text = package.ReadString(); if (text != GetZPackageTypeString(fieldInfo.FieldType)) { throw new InvalidDeserializationTypeException { received = text, expected = GetZPackageTypeString(fieldInfo.FieldType), field = fieldInfo.Name }; } fieldInfo.SetValue(uninitializedObject, ReadValueWithTypeFromZPackage(package, fieldInfo.FieldType)); } return uninitializedObject; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<, >)) { int num2 = package.ReadInt(); IDictionary dictionary = (IDictionary)Activator.CreateInstance(type); Type type2 = typeof(KeyValuePair<, >).MakeGenericType(type.GenericTypeArguments); FieldInfo field = type2.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = type2.GetField("value", BindingFlags.Instance | BindingFlags.NonPublic); for (int j = 0; j < num2; j++) { object obj = ReadValueWithTypeFromZPackage(package, type2); dictionary.Add(field.GetValue(obj), field2.GetValue(obj)); } return dictionary; } if (type != typeof(List) && type.IsGenericType) { Type type3 = typeof(ICollection<>).MakeGenericType(type.GenericTypeArguments[0]); if ((object)type3 != null && type3.IsAssignableFrom(type)) { int num3 = package.ReadInt(); object obj2 = Activator.CreateInstance(type); MethodInfo method = type3.GetMethod("Add"); for (int k = 0; k < num3; k++) { method.Invoke(obj2, new object[1] { ReadValueWithTypeFromZPackage(package, type.GenericTypeArguments[0]) }); } return obj2; } } ParameterInfo parameterInfo = (ParameterInfo)FormatterServices.GetUninitializedObject(typeof(ParameterInfo)); AccessTools.DeclaredField(typeof(ParameterInfo), "ClassImpl").SetValue(parameterInfo, type); List source = new List(); ZRpc.Deserialize(new ParameterInfo[2] { null, parameterInfo }, package, ref source); return source.First(); } } [<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(1)] [Nullable(0)] [PublicAPI] [HarmonyPatch] internal class VersionCheck { private static readonly HashSet versionChecks; private static readonly Dictionary notProcessedNames; public string Name; [Nullable(2)] private string displayName; [Nullable(2)] private string currentVersion; [Nullable(2)] private string minimumRequiredVersion; public bool ModRequired = true; [Nullable(2)] private string ReceivedCurrentVersion; [Nullable(2)] private string ReceivedMinimumRequiredVersion; private readonly List ValidatedClients = new List(); [Nullable(2)] private ConfigSync ConfigSync; public string DisplayName { get { return displayName ?? Name; } set { displayName = value; } } public string CurrentVersion { get { return currentVersion ?? "0.0.0"; } set { currentVersion = value; } } public string MinimumRequiredVersion { get { return minimumRequiredVersion ?? (ModRequired ? CurrentVersion : "0.0.0"); } set { minimumRequiredVersion = value; } } private static void PatchServerSync() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown Patches patchInfo = PatchProcessor.GetPatchInfo((MethodBase)AccessTools.DeclaredMethod(typeof(ZNet), "Awake", (Type[])null, (Type[])null)); if (patchInfo != null && patchInfo.Postfixes.Count([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (Patch p) => p.PatchMethod.DeclaringType == typeof(ConfigSync.RegisterRPCPatch)) > 0) { return; } Harmony val = new Harmony("org.bepinex.helpers.ServerSync"); foreach (Type item in from t in typeof(ConfigSync).GetNestedTypes(BindingFlags.NonPublic).Concat(new Type[1] { typeof(VersionCheck) }) where t.IsClass select t) { val.PatchAll(item); } } static VersionCheck() { versionChecks = new HashSet(); notProcessedNames = new Dictionary(); typeof(ThreadingHelper).GetMethod("StartSyncInvoke").Invoke(ThreadingHelper.Instance, new object[1] { new Action(PatchServerSync) }); } public VersionCheck(string name) { Name = name; ModRequired = true; versionChecks.Add(this); } public VersionCheck(ConfigSync configSync) { ConfigSync = configSync; Name = ConfigSync.Name; versionChecks.Add(this); } public void Initialize() { ReceivedCurrentVersion = null; ReceivedMinimumRequiredVersion = null; if (ConfigSync != null) { Name = ConfigSync.Name; DisplayName = ConfigSync.DisplayName; CurrentVersion = ConfigSync.CurrentVersion; MinimumRequiredVersion = ConfigSync.MinimumRequiredVersion; ModRequired = ConfigSync.ModRequired; } } private bool IsVersionOk() { if (ReceivedMinimumRequiredVersion == null || ReceivedCurrentVersion == null) { return !ModRequired; } bool flag = new Version(CurrentVersion) >= new Version(ReceivedMinimumRequiredVersion); bool flag2 = new Version(ReceivedCurrentVersion) >= new Version(MinimumRequiredVersion); return flag && flag2; } private string ErrorClient() { if (ReceivedMinimumRequiredVersion == null) { return DisplayName + " is not installed on the server."; } return (new Version(CurrentVersion) >= new Version(ReceivedMinimumRequiredVersion)) ? (DisplayName + " may not be higher than version " + ReceivedCurrentVersion + ". You have version " + CurrentVersion + ".") : (DisplayName + " needs to be at least version " + ReceivedMinimumRequiredVersion + ". You have version " + CurrentVersion + "."); } private string ErrorServer(ZRpc rpc) { return "Disconnect: The client (" + rpc.GetSocket().GetHostName() + ") doesn't have the correct " + DisplayName + " version " + MinimumRequiredVersion; } private string Error([Nullable(2)] ZRpc rpc = null) { return (rpc == null) ? ErrorClient() : ErrorServer(rpc); } private static VersionCheck[] GetFailedClient() { return versionChecks.Where([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (VersionCheck check) => !check.IsVersionOk()).ToArray(); } private static VersionCheck[] GetFailedServer(ZRpc rpc) { return versionChecks.Where([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (VersionCheck check) => check.ModRequired && !check.ValidatedClients.Contains(rpc)).ToArray(); } private static void Logout() { Game.instance.Logout(true, true); AccessTools.DeclaredField(typeof(ZNet), "m_connectionStatus").SetValue(null, (object)(ConnectionStatus)3); } private static void DisconnectClient(ZRpc rpc) { rpc.Invoke("Error", new object[1] { 3 }); } private static void CheckVersion(ZRpc rpc, ZPackage pkg) { CheckVersion(rpc, pkg, null); } private static void CheckVersion(ZRpc rpc, ZPackage pkg, [Nullable(new byte[] { 2, 1, 1 })] Action original) { string text = pkg.ReadString(); string text2 = pkg.ReadString(); string text3 = pkg.ReadString(); bool flag = false; foreach (VersionCheck versionCheck in versionChecks) { if (!(text != versionCheck.Name)) { Debug.Log((object)("Received " + versionCheck.DisplayName + " version " + text3 + " and minimum version " + text2 + " from the " + (ZNet.instance.IsServer() ? "client" : "server") + ".")); versionCheck.ReceivedMinimumRequiredVersion = text2; versionCheck.ReceivedCurrentVersion = text3; if (ZNet.instance.IsServer() && versionCheck.IsVersionOk()) { versionCheck.ValidatedClients.Add(rpc); } flag = true; } } if (flag) { return; } pkg.SetPos(0); if (original != null) { original(rpc, pkg); if (pkg.GetPos() == 0) { notProcessedNames.Add(text, text3); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] [HarmonyPrefix] private static bool RPC_PeerInfo(ZRpc rpc, ZNet __instance) { VersionCheck[] array = (__instance.IsServer() ? GetFailedServer(rpc) : GetFailedClient()); if (array.Length == 0) { return true; } VersionCheck[] array2 = array; foreach (VersionCheck versionCheck in array2) { Debug.LogWarning((object)versionCheck.Error(rpc)); } if (__instance.IsServer()) { DisconnectClient(rpc); } else { Logout(); } return false; } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] [HarmonyPrefix] private static void RegisterAndCheckVersion(ZNetPeer peer, ZNet __instance) { //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Expected O, but got Unknown notProcessedNames.Clear(); IDictionary dictionary = (IDictionary)typeof(ZRpc).GetField("m_functions", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(peer.m_rpc); if (dictionary.Contains(StringExtensionMethods.GetStableHashCode("ServerSync VersionCheck"))) { object obj = dictionary[StringExtensionMethods.GetStableHashCode("ServerSync VersionCheck")]; Action action = (Action)obj.GetType().GetField("m_action", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(obj); peer.m_rpc.Register("ServerSync VersionCheck", (Action)([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (ZRpc rpc, [Nullable(1)] ZPackage pkg) => { CheckVersion(rpc, pkg, action); })); } else { peer.m_rpc.Register("ServerSync VersionCheck", (Action)CheckVersion); } foreach (VersionCheck versionCheck in versionChecks) { versionCheck.Initialize(); if (versionCheck.ModRequired || __instance.IsServer()) { Debug.Log((object)("Sending " + versionCheck.DisplayName + " version " + versionCheck.CurrentVersion + " and minimum version " + versionCheck.MinimumRequiredVersion + " to the " + (__instance.IsServer() ? "client" : "server") + ".")); ZPackage val = new ZPackage(); val.Write(versionCheck.Name); val.Write(versionCheck.MinimumRequiredVersion); val.Write(versionCheck.CurrentVersion); peer.m_rpc.Invoke("ServerSync VersionCheck", new object[1] { val }); } } } [HarmonyPatch(typeof(ZNet), "Disconnect")] [HarmonyPrefix] private static void RemoveDisconnected(ZNetPeer peer, ZNet __instance) { if (!__instance.IsServer()) { return; } foreach (VersionCheck versionCheck in versionChecks) { versionCheck.ValidatedClients.Remove(peer.m_rpc); } } [HarmonyPatch(typeof(FejdStartup), "ShowConnectError")] [HarmonyPostfix] private static void ShowConnectionError(FejdStartup __instance) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) if (!__instance.m_connectionFailedPanel.activeSelf || (int)ZNet.GetConnectionStatus() != 3) { return; } bool flag = false; VersionCheck[] failedClient = GetFailedClient(); if (failedClient.Length != 0) { string text = string.Join("\n", failedClient.Select([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (VersionCheck check) => check.Error())); TMP_Text connectionFailedError = __instance.m_connectionFailedError; connectionFailedError.text = connectionFailedError.text + "\n" + text; flag = true; } foreach (KeyValuePair item in notProcessedNames.OrderBy([<4eeaff95-ee3c-41f9-991d-aca2274c262d>NullableContext(0)] (KeyValuePair kv) => kv.Key)) { if (!__instance.m_connectionFailedError.text.Contains(item.Key)) { TMP_Text connectionFailedError2 = __instance.m_connectionFailedError; connectionFailedError2.text = connectionFailedError2.text + "\nServer expects you to have " + item.Key + " (Version: " + item.Value + ") installed."; flag = true; } } if (flag) { RectTransform component = ((Component)__instance.m_connectionFailedPanel.transform.Find("Image")).GetComponent(); Vector2 sizeDelta = component.sizeDelta; sizeDelta.x = 675f; component.sizeDelta = sizeDelta; __instance.m_connectionFailedError.ForceMeshUpdate(false, false); float num = __instance.m_connectionFailedError.renderedHeight + 105f; RectTransform component2 = ((Component)((Component)component).transform.Find("ButtonOk")).GetComponent(); component2.anchoredPosition = new Vector2(component2.anchoredPosition.x, component2.anchoredPosition.y - (num - component.sizeDelta.y) / 2f); sizeDelta = component.sizeDelta; sizeDelta.y = num; component.sizeDelta = sizeDelta; } } } } namespace Microsoft.CodeAnalysis { [CompilerGenerated] [<63f59ba0-4572-4a42-8708-15f9b37c5a36>Embedded] internal sealed class <63f59ba0-4572-4a42-8708-15f9b37c5a36>EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] [<63f59ba0-4572-4a42-8708-15f9b37c5a36>Embedded] internal sealed class <3341cca6-08b7-4096-b079-ff1fd6807803>NullableAttribute : Attribute { public readonly byte[] NullableFlags; public <3341cca6-08b7-4096-b079-ff1fd6807803>NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public <3341cca6-08b7-4096-b079-ff1fd6807803>NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] [<63f59ba0-4572-4a42-8708-15f9b37c5a36>Embedded] [CompilerGenerated] internal sealed class <18e53f63-e45f-404a-9901-dc42e79dd20f>NullableContextAttribute : Attribute { public readonly byte Flag; public <18e53f63-e45f-404a-9901-dc42e79dd20f>NullableContextAttribute(byte P_0) { Flag = P_0; } } [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] [CompilerGenerated] [<63f59ba0-4572-4a42-8708-15f9b37c5a36>Embedded] internal sealed class <25a40add-db06-4421-9da9-5887b6bd3027>RefSafetyRulesAttribute : Attribute { public readonly int Version; public <25a40add-db06-4421-9da9-5887b6bd3027>RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace FxResources.System.Buffers { internal static class SR { } } namespace System { [<18e53f63-e45f-404a-9901-dc42e79dd20f>NullableContext(1)] [<3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(0)] internal static class SR { private static readonly bool s_usingResourceKeys = AppContext.TryGetSwitch("System.Resources.UseSystemResourceKeys", out var isEnabled) && isEnabled; [<3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(0)] private static ResourceManager s_resourceManager; [<3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(0)] internal static ResourceManager ResourceManager { [<18e53f63-e45f-404a-9901-dc42e79dd20f>NullableContext(0)] get { return s_resourceManager ?? (s_resourceManager = new ResourceManager(typeof(FxResources.System.Buffers.SR))); } } [<3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(0)] internal static string ArgumentException_BufferNotFromPool { [<18e53f63-e45f-404a-9901-dc42e79dd20f>NullableContext(0)] get { return GetResourceString("ArgumentException_BufferNotFromPool"); } } private static bool UsingResourceKeys() { return s_usingResourceKeys; } internal static string GetResourceString(string resourceKey) { if (UsingResourceKeys()) { return resourceKey; } string result = null; try { result = ResourceManager.GetString(resourceKey); } catch (MissingManifestResourceException) { } return result; } internal static string GetResourceString(string resourceKey, string defaultString) { string resourceString = GetResourceString(resourceKey); if (!(resourceKey == resourceString) && resourceString != null) { return resourceString; } return defaultString; } internal static string Format(string resourceFormat, [<3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(2)] object p1) { if (UsingResourceKeys()) { return string.Join(", ", resourceFormat, p1); } return string.Format(resourceFormat, p1); } internal static string Format(string resourceFormat, [<3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(2)] object p1, [<3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(2)] object p2) { if (UsingResourceKeys()) { return string.Join(", ", resourceFormat, p1, p2); } return string.Format(resourceFormat, p1, p2); } [<18e53f63-e45f-404a-9901-dc42e79dd20f>NullableContext(2)] [return: <3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(1)] internal static string Format([<3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(1)] string resourceFormat, object p1, object p2, object p3) { if (UsingResourceKeys()) { return string.Join(", ", resourceFormat, p1, p2, p3); } return string.Format(resourceFormat, p1, p2, p3); } internal static string Format(string resourceFormat, [<3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(2)] params object[] args) { if (args != null) { if (UsingResourceKeys()) { return resourceFormat + ", " + string.Join(", ", args); } return string.Format(resourceFormat, args); } return resourceFormat; } internal static string Format([<3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(2)] IFormatProvider provider, string resourceFormat, [<3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(2)] object p1) { if (UsingResourceKeys()) { return string.Join(", ", resourceFormat, p1); } return string.Format(provider, resourceFormat, p1); } [<18e53f63-e45f-404a-9901-dc42e79dd20f>NullableContext(2)] [return: <3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(1)] internal static string Format(IFormatProvider provider, [<3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(1)] string resourceFormat, object p1, object p2) { if (UsingResourceKeys()) { return string.Join(", ", resourceFormat, p1, p2); } return string.Format(provider, resourceFormat, p1, p2); } [<18e53f63-e45f-404a-9901-dc42e79dd20f>NullableContext(2)] [return: <3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(1)] internal static string Format(IFormatProvider provider, [<3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(1)] string resourceFormat, object p1, object p2, object p3) { if (UsingResourceKeys()) { return string.Join(", ", resourceFormat, p1, p2, p3); } return string.Format(provider, resourceFormat, p1, p2, p3); } internal static string Format([<3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(2)] IFormatProvider provider, string resourceFormat, [<3341cca6-08b7-4096-b079-ff1fd6807803>Nullable(2)] params object[] args) { if (args != null) { if (UsingResourceKeys()) { return resourceFormat + ", " + string.Join(", ", args); } return string.Format(provider, resourceFormat, args); } return resourceFormat; } } } namespace System.Buffers { internal abstract class ArrayPool { private static ArrayPool s_sharedInstance; public static ArrayPool Shared { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Volatile.Read(ref s_sharedInstance) ?? EnsureSharedCreated(); } } [MethodImpl(MethodImplOptions.NoInlining)] private static ArrayPool EnsureSharedCreated() { Interlocked.CompareExchange(ref s_sharedInstance, Create(), null); return s_sharedInstance; } public static ArrayPool Create() { return new DefaultArrayPool(); } public static ArrayPool Create(int maxArrayLength, int maxArraysPerBucket) { return new DefaultArrayPool(maxArrayLength, maxArraysPerBucket); } public abstract T[] Rent(int minimumLength); public abstract void Return(T[] array, bool clearArray = false); } [EventSource(Name = "System.Buffers.ArrayPoolEventSource")] internal sealed class ArrayPoolEventSource : EventSource { internal enum BufferAllocatedReason { Pooled, OverMaximumSize, PoolExhausted } internal static readonly System.Buffers.ArrayPoolEventSource Log = new System.Buffers.ArrayPoolEventSource(); [Event(1, Level = EventLevel.Verbose)] internal unsafe void BufferRented(int bufferId, int bufferSize, int poolId, int bucketId) { EventData* ptr = stackalloc EventData[4]; *ptr = new EventData { Size = 4, DataPointer = (IntPtr)(&bufferId) }; ptr[1] = new EventData { Size = 4, DataPointer = (IntPtr)(&bufferSize) }; ptr[2] = new EventData { Size = 4, DataPointer = (IntPtr)(&poolId) }; ptr[3] = new EventData { Size = 4, DataPointer = (IntPtr)(&bucketId) }; WriteEventCore(1, 4, ptr); } [Event(2, Level = EventLevel.Informational)] internal unsafe void BufferAllocated(int bufferId, int bufferSize, int poolId, int bucketId, BufferAllocatedReason reason) { EventData* ptr = stackalloc EventData[5]; *ptr = new EventData { Size = 4, DataPointer = (IntPtr)(&bufferId) }; ptr[1] = new EventData { Size = 4, DataPointer = (IntPtr)(&bufferSize) }; ptr[2] = new EventData { Size = 4, DataPointer = (IntPtr)(&poolId) }; ptr[3] = new EventData { Size = 4, DataPointer = (IntPtr)(&bucketId) }; ptr[4] = new EventData { Size = 4, DataPointer = (IntPtr)(&reason) }; WriteEventCore(2, 5, ptr); } [Event(3, Level = EventLevel.Verbose)] internal void BufferReturned(int bufferId, int bufferSize, int poolId) { WriteEvent(3, bufferId, bufferSize, poolId); } } internal sealed class DefaultArrayPool : ArrayPool { private sealed class Bucket { internal readonly int _bufferLength; private readonly T[][] _buffers; private readonly int _poolId; private SpinLock _lock; private int _index; internal int Id => GetHashCode(); internal Bucket(int bufferLength, int numberOfBuffers, int poolId) { _lock = new SpinLock(Debugger.IsAttached); _buffers = new T[numberOfBuffers][]; _bufferLength = bufferLength; _poolId = poolId; } internal T[] Rent() { T[][] buffers = _buffers; T[] array = null; bool lockTaken = false; bool flag = false; try { _lock.Enter(ref lockTaken); if (_index < buffers.Length) { array = buffers[_index]; buffers[_index++] = null; flag = array == null; } } finally { if (lockTaken) { _lock.Exit(useMemoryBarrier: false); } } if (flag) { array = new T[_bufferLength]; System.Buffers.ArrayPoolEventSource log = System.Buffers.ArrayPoolEventSource.Log; if (log.IsEnabled()) { log.BufferAllocated(array.GetHashCode(), _bufferLength, _poolId, Id, System.Buffers.ArrayPoolEventSource.BufferAllocatedReason.Pooled); } } return array; } internal void Return(T[] array) { if (array.Length != _bufferLength) { throw new ArgumentException(SR.ArgumentException_BufferNotFromPool, "array"); } bool lockTaken = false; try { _lock.Enter(ref lockTaken); if (_index != 0) { _buffers[--_index] = array; } } finally { if (lockTaken) { _lock.Exit(useMemoryBarrier: false); } } } } private const int DefaultMaxArrayLength = 1048576; private const int DefaultMaxNumberOfArraysPerBucket = 50; private static T[] s_emptyArray; private readonly Bucket[] _buckets; private int Id => GetHashCode(); internal DefaultArrayPool() : this(1048576, 50) { } internal DefaultArrayPool(int maxArrayLength, int maxArraysPerBucket) { if (maxArrayLength <= 0) { throw new ArgumentOutOfRangeException("maxArrayLength"); } if (maxArraysPerBucket <= 0) { throw new ArgumentOutOfRangeException("maxArraysPerBucket"); } if (maxArrayLength > 1073741824) { maxArrayLength = 1073741824; } else if (maxArrayLength < 16) { maxArrayLength = 16; } int id = Id; int num = System.Buffers.Utilities.SelectBucketIndex(maxArrayLength); Bucket[] array = new Bucket[num + 1]; for (int i = 0; i < array.Length; i++) { array[i] = new Bucket(System.Buffers.Utilities.GetMaxSizeForBucket(i), maxArraysPerBucket, id); } _buckets = array; } public override T[] Rent(int minimumLength) { if (minimumLength < 0) { throw new ArgumentOutOfRangeException("minimumLength"); } if (minimumLength == 0) { return s_emptyArray ?? (s_emptyArray = new T[0]); } System.Buffers.ArrayPoolEventSource log = System.Buffers.ArrayPoolEventSource.Log; T[] array = null; int num = System.Buffers.Utilities.SelectBucketIndex(minimumLength); if (num < _buckets.Length) { int num2 = num; do { array = _buckets[num2].Rent(); if (array != null) { if (log.IsEnabled()) { log.BufferRented(array.GetHashCode(), array.Length, Id, _buckets[num2].Id); } return array; } } while (++num2 < _buckets.Length && num2 != num + 2); array = new T[_buckets[num]._bufferLength]; } else { array = new T[minimumLength]; } if (log.IsEnabled()) { int hashCode = array.GetHashCode(); int bucketId = -1; log.BufferRented(hashCode, array.Length, Id, bucketId); log.BufferAllocated(hashCode, array.Length, Id, bucketId, (num >= _buckets.Length) ? System.Buffers.ArrayPoolEventSource.BufferAllocatedReason.OverMaximumSize : System.Buffers.ArrayPoolEventSource.BufferAllocatedReason.PoolExhausted); } return array; } public override void Return(T[] array, bool clearArray = false) { if (array == null) { throw new ArgumentNullException("array"); } if (array.Length == 0) { return; } int num = System.Buffers.Utilities.SelectBucketIndex(array.Length); if (num < _buckets.Length) { if (clearArray) { Array.Clear(array, 0, array.Length); } _buckets[num].Return(array); } System.Buffers.ArrayPoolEventSource log = System.Buffers.ArrayPoolEventSource.Log; if (log.IsEnabled()) { log.BufferReturned(array.GetHashCode(), array.Length, Id); } } } internal static class Utilities { [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int SelectBucketIndex(int bufferSize) { uint num = (uint)(bufferSize - 1) >> 4; int num2 = 0; if (num > 65535) { num >>= 16; num2 = 16; } if (num > 255) { num >>= 8; num2 += 8; } if (num > 15) { num >>= 4; num2 += 4; } if (num > 3) { num >>= 2; num2 += 2; } if (num > 1) { num >>= 1; num2++; } return num2 + (int)num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int GetMaxSizeForBucket(int binIndex) { return 16 << binIndex; } } } namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [Embedded] [CompilerGenerated] internal sealed class <1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnlyAttribute : Attribute { } [Embedded] [CompilerGenerated] internal sealed class IsByRefLikeAttribute : Attribute { } [CompilerGenerated] [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; } } [Embedded] [CompilerGenerated] [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; } } [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] [Embedded] [CompilerGenerated] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace FxResources.System.Memory { internal static class SR { } } namespace System { [DebuggerDisplay("{ToString(),raw}")] [DebuggerTypeProxy(typeof(System.MemoryDebugView<>))] [<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] internal struct Memory { private readonly object _object; private readonly int _index; private readonly int _length; private const int RemoveFlagsBitMask = int.MaxValue; public static Memory Empty => default(Memory); public int Length => _length & 0x7FFFFFFF; public bool IsEmpty => (_length & 0x7FFFFFFF) == 0; public Span Span { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { Span result; if (_index < 0) { result = ((MemoryManager)_object).GetSpan(); return result.Slice(_index & 0x7FFFFFFF, _length); } if (typeof(T) == typeof(char) && _object is string text) { result = new Span(Unsafe.As>(text), MemoryExtensions.StringAdjustment, text.Length); return result.Slice(_index, _length); } if (_object != null) { return new Span((T[])_object, _index, _length & 0x7FFFFFFF); } result = default(Span); return result; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Memory(T[] array) { if (array == null) { this = default(Memory); return; } if (default(T) == null && array.GetType() != typeof(T[])) { System.ThrowHelper.ThrowArrayTypeMismatchException(); } _object = array; _index = 0; _length = array.Length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Memory(T[] array, int start) { if (array == null) { if (start != 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(); } this = default(Memory); return; } if (default(T) == null && array.GetType() != typeof(T[])) { System.ThrowHelper.ThrowArrayTypeMismatchException(); } if ((uint)start > (uint)array.Length) { System.ThrowHelper.ThrowArgumentOutOfRangeException(); } _object = array; _index = start; _length = array.Length - start; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Memory(T[] array, int start, int length) { if (array == null) { if (start != 0 || length != 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(); } this = default(Memory); return; } if (default(T) == null && array.GetType() != typeof(T[])) { System.ThrowHelper.ThrowArrayTypeMismatchException(); } if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) { System.ThrowHelper.ThrowArgumentOutOfRangeException(); } _object = array; _index = start; _length = length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Memory(MemoryManager manager, int length) { if (length < 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(); } _object = manager; _index = int.MinValue; _length = length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Memory(MemoryManager manager, int start, int length) { if (length < 0 || start < 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(); } _object = manager; _index = start | int.MinValue; _length = length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Memory(object obj, int start, int length) { _object = obj; _index = start; _length = length; } public static implicit operator Memory(T[] array) { return new Memory(array); } public static implicit operator Memory(ArraySegment segment) { return new Memory(segment.Array, segment.Offset, segment.Count); } public static implicit operator ReadOnlyMemory(Memory memory) { return Unsafe.As, ReadOnlyMemory>(ref memory); } public override string ToString() { if (typeof(T) == typeof(char)) { if (!(_object is string text)) { return Span.ToString(); } return text.Substring(_index, _length & 0x7FFFFFFF); } return $"System.Memory<{typeof(T).Name}>[{_length & 0x7FFFFFFF}]"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Memory Slice(int start) { int length = _length; int num = length & 0x7FFFFFFF; if ((uint)start > (uint)num) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } return new Memory(_object, _index + start, length - start); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Memory Slice(int start, int length) { int length2 = _length; int num = length2 & 0x7FFFFFFF; if ((uint)start > (uint)num || (uint)length > (uint)(num - start)) { System.ThrowHelper.ThrowArgumentOutOfRangeException(); } return new Memory(_object, _index + start, length | (length2 & int.MinValue)); } public void CopyTo(Memory destination) { Span.CopyTo(destination.Span); } public bool TryCopyTo(Memory destination) { return Span.TryCopyTo(destination.Span); } public unsafe MemoryHandle Pin() { if (_index < 0) { return ((MemoryManager)_object).Pin(_index & 0x7FFFFFFF); } if (typeof(T) == typeof(char) && _object is string value) { GCHandle handle = GCHandle.Alloc(value, GCHandleType.Pinned); void* pointer = Unsafe.Add((void*)handle.AddrOfPinnedObject(), _index); return new MemoryHandle(pointer, handle); } if (_object is T[] array) { if (_length < 0) { void* pointer2 = Unsafe.Add(Unsafe.AsPointer(ref MemoryMarshal.GetReference(array)), _index); return new MemoryHandle(pointer2); } GCHandle handle2 = GCHandle.Alloc(array, GCHandleType.Pinned); void* pointer3 = Unsafe.Add((void*)handle2.AddrOfPinnedObject(), _index); return new MemoryHandle(pointer3, handle2); } return default(MemoryHandle); } public T[] ToArray() { return Span.ToArray(); } [EditorBrowsable(EditorBrowsableState.Never)] public override bool Equals(object obj) { if (obj is ReadOnlyMemory readOnlyMemory) { return readOnlyMemory.Equals(this); } if (obj is Memory other) { return Equals(other); } return false; } public bool Equals(Memory other) { if (_object == other._object && _index == other._index) { return _length == other._length; } return false; } [EditorBrowsable(EditorBrowsableState.Never)] public override int GetHashCode() { if (_object == null) { return 0; } int hashCode = _object.GetHashCode(); int index = _index; int hashCode2 = index.GetHashCode(); index = _length; return CombineHashCodes(hashCode, hashCode2, index.GetHashCode()); } private static int CombineHashCodes(int left, int right) { return ((left << 5) + left) ^ right; } private static int CombineHashCodes(int h1, int h2, int h3) { return CombineHashCodes(CombineHashCodes(h1, h2), h3); } } internal sealed class MemoryDebugView { private readonly ReadOnlyMemory _memory; [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public T[] Items => _memory.ToArray(); public MemoryDebugView(Memory memory) { _memory = memory; } public MemoryDebugView(ReadOnlyMemory memory) { _memory = memory; } } internal static class MemoryExtensions { internal static readonly IntPtr StringAdjustment = MeasureStringAdjustment(); public static ReadOnlySpan Trim(this ReadOnlySpan span) { return span.TrimStart().TrimEnd(); } public static ReadOnlySpan TrimStart(this ReadOnlySpan span) { int i; for (i = 0; i < span.Length && char.IsWhiteSpace(span[i]); i++) { } return span.Slice(i); } public static ReadOnlySpan TrimEnd(this ReadOnlySpan span) { int num = span.Length - 1; while (num >= 0 && char.IsWhiteSpace(span[num])) { num--; } return span.Slice(0, num + 1); } public static ReadOnlySpan Trim(this ReadOnlySpan span, char trimChar) { return span.TrimStart(trimChar).TrimEnd(trimChar); } public static ReadOnlySpan TrimStart(this ReadOnlySpan span, char trimChar) { int i; for (i = 0; i < span.Length && span[i] == trimChar; i++) { } return span.Slice(i); } public static ReadOnlySpan TrimEnd(this ReadOnlySpan span, char trimChar) { int num = span.Length - 1; while (num >= 0 && span[num] == trimChar) { num--; } return span.Slice(0, num + 1); } public static ReadOnlySpan Trim(this ReadOnlySpan span, ReadOnlySpan trimChars) { return span.TrimStart(trimChars).TrimEnd(trimChars); } public static ReadOnlySpan TrimStart(this ReadOnlySpan span, ReadOnlySpan trimChars) { if (trimChars.IsEmpty) { return span.TrimStart(); } int i; for (i = 0; i < span.Length; i++) { int num = 0; while (num < trimChars.Length) { if (span[i] != trimChars[num]) { num++; continue; } goto IL_003c; } break; IL_003c:; } return span.Slice(i); } public static ReadOnlySpan TrimEnd(this ReadOnlySpan span, ReadOnlySpan trimChars) { if (trimChars.IsEmpty) { return span.TrimEnd(); } int num; for (num = span.Length - 1; num >= 0; num--) { int num2 = 0; while (num2 < trimChars.Length) { if (span[num] != trimChars[num2]) { num2++; continue; } goto IL_0044; } break; IL_0044:; } return span.Slice(0, num + 1); } public static bool IsWhiteSpace(this ReadOnlySpan span) { for (int i = 0; i < span.Length; i++) { if (!char.IsWhiteSpace(span[i])) { return false; } } return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOf(this Span span, T value) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.IndexOf(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); } if (typeof(T) == typeof(char)) { return System.SpanHelpers.IndexOf(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); } return System.SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOf(this Span span, ReadOnlySpan value) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.IndexOf(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(value)), value.Length); } return System.SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOf(this Span span, T value) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.LastIndexOf(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); } if (typeof(T) == typeof(char)) { return System.SpanHelpers.LastIndexOf(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); } return System.SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOf(this Span span, ReadOnlySpan value) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.LastIndexOf(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(value)), value.Length); } return System.SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool SequenceEqual(this Span span, ReadOnlySpan other) where T : IEquatable { int length = span.Length; if (default(T) != null && IsTypeComparableAsBytes(out var size)) { if (length == other.Length) { return System.SpanHelpers.SequenceEqual(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), ref Unsafe.As(ref MemoryMarshal.GetReference(other)), (NUInt)length * size); } return false; } if (length == other.Length) { return System.SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other), length); } return false; } public static int SequenceCompareTo(this Span span, ReadOnlySpan other) where T : IComparable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.SequenceCompareTo(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(other)), other.Length); } if (typeof(T) == typeof(char)) { return System.SpanHelpers.SequenceCompareTo(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(other)), other.Length); } return System.SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(other), other.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOf(this ReadOnlySpan span, T value) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.IndexOf(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); } if (typeof(T) == typeof(char)) { return System.SpanHelpers.IndexOf(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); } return System.SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.IndexOf(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(value)), value.Length); } return System.SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOf(this ReadOnlySpan span, T value) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.LastIndexOf(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); } if (typeof(T) == typeof(char)) { return System.SpanHelpers.LastIndexOf(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); } return System.SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOf(this ReadOnlySpan span, ReadOnlySpan value) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.LastIndexOf(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(value)), value.Length); } return System.SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOfAny(this Span span, T value0, T value1) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.IndexOfAny(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), span.Length); } return System.SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOfAny(this Span span, T value0, T value1, T value2) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.IndexOfAny(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), Unsafe.As(ref value2), span.Length); } return System.SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOfAny(this Span span, ReadOnlySpan values) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.IndexOfAny(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(values)), values.Length); } return System.SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOfAny(this ReadOnlySpan span, T value0, T value1) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.IndexOfAny(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), span.Length); } return System.SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOfAny(this ReadOnlySpan span, T value0, T value1, T value2) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.IndexOfAny(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), Unsafe.As(ref value2), span.Length); } return System.SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOfAny(this ReadOnlySpan span, ReadOnlySpan values) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.IndexOfAny(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(values)), values.Length); } return System.SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOfAny(this Span span, T value0, T value1) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.LastIndexOfAny(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), span.Length); } return System.SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOfAny(this Span span, T value0, T value1, T value2) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.LastIndexOfAny(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), Unsafe.As(ref value2), span.Length); } return System.SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOfAny(this Span span, ReadOnlySpan values) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.LastIndexOfAny(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(values)), values.Length); } return System.SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOfAny(this ReadOnlySpan span, T value0, T value1) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.LastIndexOfAny(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), span.Length); } return System.SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOfAny(this ReadOnlySpan span, T value0, T value1, T value2) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.LastIndexOfAny(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), Unsafe.As(ref value2), span.Length); } return System.SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOfAny(this ReadOnlySpan span, ReadOnlySpan values) where T : IEquatable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.LastIndexOfAny(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(values)), values.Length); } return System.SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool SequenceEqual(this ReadOnlySpan span, ReadOnlySpan other) where T : IEquatable { int length = span.Length; if (default(T) != null && IsTypeComparableAsBytes(out var size)) { if (length == other.Length) { return System.SpanHelpers.SequenceEqual(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), ref Unsafe.As(ref MemoryMarshal.GetReference(other)), (NUInt)length * size); } return false; } if (length == other.Length) { return System.SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other), length); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int SequenceCompareTo(this ReadOnlySpan span, ReadOnlySpan other) where T : IComparable { if (typeof(T) == typeof(byte)) { return System.SpanHelpers.SequenceCompareTo(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(other)), other.Length); } if (typeof(T) == typeof(char)) { return System.SpanHelpers.SequenceCompareTo(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(other)), other.Length); } return System.SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(other), other.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool StartsWith(this Span span, ReadOnlySpan value) where T : IEquatable { int length = value.Length; if (default(T) != null && IsTypeComparableAsBytes(out var size)) { if (length <= span.Length) { return System.SpanHelpers.SequenceEqual(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), ref Unsafe.As(ref MemoryMarshal.GetReference(value)), (NUInt)length * size); } return false; } if (length <= span.Length) { return System.SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), length); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool StartsWith(this ReadOnlySpan span, ReadOnlySpan value) where T : IEquatable { int length = value.Length; if (default(T) != null && IsTypeComparableAsBytes(out var size)) { if (length <= span.Length) { return System.SpanHelpers.SequenceEqual(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), ref Unsafe.As(ref MemoryMarshal.GetReference(value)), (NUInt)length * size); } return false; } if (length <= span.Length) { return System.SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), length); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EndsWith(this Span span, ReadOnlySpan value) where T : IEquatable { int length = span.Length; int length2 = value.Length; if (default(T) != null && IsTypeComparableAsBytes(out var size)) { if (length2 <= length) { return System.SpanHelpers.SequenceEqual(ref Unsafe.As(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), length - length2)), ref Unsafe.As(ref MemoryMarshal.GetReference(value)), (NUInt)length2 * size); } return false; } if (length2 <= length) { return System.SpanHelpers.SequenceEqual(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), length - length2), ref MemoryMarshal.GetReference(value), length2); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EndsWith(this ReadOnlySpan span, ReadOnlySpan value) where T : IEquatable { int length = span.Length; int length2 = value.Length; if (default(T) != null && IsTypeComparableAsBytes(out var size)) { if (length2 <= length) { return System.SpanHelpers.SequenceEqual(ref Unsafe.As(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), length - length2)), ref Unsafe.As(ref MemoryMarshal.GetReference(value)), (NUInt)length2 * size); } return false; } if (length2 <= length) { return System.SpanHelpers.SequenceEqual(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), length - length2), ref MemoryMarshal.GetReference(value), length2); } return false; } public static void Reverse(this Span span) { ref T reference = ref MemoryMarshal.GetReference(span); int num = 0; int num2 = span.Length - 1; while (num < num2) { T val = Unsafe.Add(ref reference, num); Unsafe.Add(ref reference, num) = Unsafe.Add(ref reference, num2); Unsafe.Add(ref reference, num2) = val; num++; num2--; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsSpan(this T[] array) { return new Span(array); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsSpan(this T[] array, int start, int length) { return new Span(array, start, length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsSpan(this ArraySegment segment) { return new Span(segment.Array, segment.Offset, segment.Count); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsSpan(this ArraySegment segment, int start) { if ((uint)start > segment.Count) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } return new Span(segment.Array, segment.Offset + start, segment.Count - start); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsSpan(this ArraySegment segment, int start, int length) { if ((uint)start > segment.Count) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } if ((uint)length > segment.Count - start) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.length); } return new Span(segment.Array, segment.Offset + start, length); } public static Memory AsMemory(this T[] array) { return new Memory(array); } public static Memory AsMemory(this T[] array, int start) { return new Memory(array, start); } public static Memory AsMemory(this T[] array, int start, int length) { return new Memory(array, start, length); } public static Memory AsMemory(this ArraySegment segment) { return new Memory(segment.Array, segment.Offset, segment.Count); } public static Memory AsMemory(this ArraySegment segment, int start) { if ((uint)start > segment.Count) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } return new Memory(segment.Array, segment.Offset + start, segment.Count - start); } public static Memory AsMemory(this ArraySegment segment, int start, int length) { if ((uint)start > segment.Count) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } if ((uint)length > segment.Count - start) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.length); } return new Memory(segment.Array, segment.Offset + start, length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CopyTo(this T[] source, Span destination) { new ReadOnlySpan(source).CopyTo(destination); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CopyTo(this T[] source, Memory destination) { source.CopyTo(destination.Span); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Overlaps(this Span span, ReadOnlySpan other) { return ((ReadOnlySpan)span).Overlaps(other); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Overlaps(this Span span, ReadOnlySpan other, out int elementOffset) { return ((ReadOnlySpan)span).Overlaps(other, out elementOffset); } public static bool Overlaps(this ReadOnlySpan span, ReadOnlySpan other) { if (span.IsEmpty || other.IsEmpty) { return false; } IntPtr intPtr = Unsafe.ByteOffset(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other)); if (Unsafe.SizeOf() == 4) { if ((uint)(int)intPtr >= (uint)(span.Length * Unsafe.SizeOf())) { return (uint)(int)intPtr > (uint)(-(other.Length * Unsafe.SizeOf())); } return true; } if ((ulong)(long)intPtr >= (ulong)((long)span.Length * (long)Unsafe.SizeOf())) { return (ulong)(long)intPtr > (ulong)(-((long)other.Length * (long)Unsafe.SizeOf())); } return true; } public static bool Overlaps(this ReadOnlySpan span, ReadOnlySpan other, out int elementOffset) { if (span.IsEmpty || other.IsEmpty) { elementOffset = 0; return false; } IntPtr intPtr = Unsafe.ByteOffset(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other)); if (Unsafe.SizeOf() == 4) { if ((uint)(int)intPtr < (uint)(span.Length * Unsafe.SizeOf()) || (uint)(int)intPtr > (uint)(-(other.Length * Unsafe.SizeOf()))) { if ((int)intPtr % Unsafe.SizeOf() != 0) { System.ThrowHelper.ThrowArgumentException_OverlapAlignmentMismatch(); } elementOffset = (int)intPtr / Unsafe.SizeOf(); return true; } elementOffset = 0; return false; } if ((ulong)(long)intPtr < (ulong)((long)span.Length * (long)Unsafe.SizeOf()) || (ulong)(long)intPtr > (ulong)(-((long)other.Length * (long)Unsafe.SizeOf()))) { if ((long)intPtr % Unsafe.SizeOf() != 0L) { System.ThrowHelper.ThrowArgumentException_OverlapAlignmentMismatch(); } elementOffset = (int)((long)intPtr / Unsafe.SizeOf()); return true; } elementOffset = 0; return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch(this Span span, IComparable comparable) { return span.BinarySearch>(comparable); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch(this Span span, TComparable comparable) where TComparable : IComparable { return BinarySearch((ReadOnlySpan)span, comparable); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch(this Span span, T value, TComparer comparer) where TComparer : IComparer { return ((ReadOnlySpan)span).BinarySearch(value, comparer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch(this ReadOnlySpan span, IComparable comparable) { return MemoryExtensions.BinarySearch>(span, comparable); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch(this ReadOnlySpan span, TComparable comparable) where TComparable : IComparable { return System.SpanHelpers.BinarySearch(span, comparable); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch(this ReadOnlySpan span, T value, TComparer comparer) where TComparer : IComparer { if (comparer == null) { System.ThrowHelper.ThrowArgumentNullException(System.ExceptionArgument.comparer); } System.SpanHelpers.ComparerComparable comparable = new System.SpanHelpers.ComparerComparable(value, comparer); return BinarySearch(span, comparable); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsTypeComparableAsBytes(out NUInt size) { if (typeof(T) == typeof(byte) || typeof(T) == typeof(sbyte)) { size = (NUInt)1; return true; } if (typeof(T) == typeof(char) || typeof(T) == typeof(short) || typeof(T) == typeof(ushort)) { size = (NUInt)2; return true; } if (typeof(T) == typeof(int) || typeof(T) == typeof(uint)) { size = (NUInt)4; return true; } if (typeof(T) == typeof(long) || typeof(T) == typeof(ulong)) { size = (NUInt)8; return true; } size = default(NUInt); return false; } public static Span AsSpan(this T[] array, int start) { return Span.Create(array, start); } public static bool Contains(this ReadOnlySpan span, ReadOnlySpan value, StringComparison comparisonType) { return span.IndexOf(value, comparisonType) >= 0; } public static bool Equals(this ReadOnlySpan span, ReadOnlySpan other, StringComparison comparisonType) { switch (comparisonType) { case StringComparison.Ordinal: return span.SequenceEqual(other); case StringComparison.OrdinalIgnoreCase: if (span.Length != other.Length) { return false; } return EqualsOrdinalIgnoreCase(span, other); default: return span.ToString().Equals(other.ToString(), comparisonType); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool EqualsOrdinalIgnoreCase(ReadOnlySpan span, ReadOnlySpan other) { if (other.Length == 0) { return true; } return CompareToOrdinalIgnoreCase(span, other) == 0; } public static int CompareTo(this ReadOnlySpan span, ReadOnlySpan other, StringComparison comparisonType) { return comparisonType switch { StringComparison.Ordinal => span.SequenceCompareTo(other), StringComparison.OrdinalIgnoreCase => CompareToOrdinalIgnoreCase(span, other), _ => string.Compare(span.ToString(), other.ToString(), comparisonType), }; } private unsafe static int CompareToOrdinalIgnoreCase(ReadOnlySpan strA, ReadOnlySpan strB) { int num = Math.Min(strA.Length, strB.Length); int num2 = num; fixed (char* ptr = &MemoryMarshal.GetReference(strA)) { fixed (char* ptr3 = &MemoryMarshal.GetReference(strB)) { char* ptr2 = ptr; char* ptr4 = ptr3; while (num != 0 && *ptr2 <= '\u007f' && *ptr4 <= '\u007f') { int num3 = *ptr2; int num4 = *ptr4; if (num3 == num4) { ptr2++; ptr4++; num--; continue; } if ((uint)(num3 - 97) <= 25u) { num3 -= 32; } if ((uint)(num4 - 97) <= 25u) { num4 -= 32; } if (num3 != num4) { return num3 - num4; } ptr2++; ptr4++; num--; } if (num == 0) { return strA.Length - strB.Length; } num2 -= num; return string.Compare(strA.Slice(num2).ToString(), strB.Slice(num2).ToString(), StringComparison.OrdinalIgnoreCase); } } } public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value, StringComparison comparisonType) { if (comparisonType == StringComparison.Ordinal) { return span.IndexOf(value); } return span.ToString().IndexOf(value.ToString(), comparisonType); } public static int ToLower(this ReadOnlySpan source, Span destination, CultureInfo culture) { if (culture == null) { System.ThrowHelper.ThrowArgumentNullException(System.ExceptionArgument.culture); } if (destination.Length < source.Length) { return -1; } string text = source.ToString(); string text2 = text.ToLower(culture); AsSpan(text2).CopyTo(destination); return source.Length; } public static int ToLowerInvariant(this ReadOnlySpan source, Span destination) { return source.ToLower(destination, CultureInfo.InvariantCulture); } public static int ToUpper(this ReadOnlySpan source, Span destination, CultureInfo culture) { if (culture == null) { System.ThrowHelper.ThrowArgumentNullException(System.ExceptionArgument.culture); } if (destination.Length < source.Length) { return -1; } string text = source.ToString(); string text2 = text.ToUpper(culture); AsSpan(text2).CopyTo(destination); return source.Length; } public static int ToUpperInvariant(this ReadOnlySpan source, Span destination) { return source.ToUpper(destination, CultureInfo.InvariantCulture); } public static bool EndsWith(this ReadOnlySpan span, ReadOnlySpan value, StringComparison comparisonType) { switch (comparisonType) { case StringComparison.Ordinal: return span.EndsWith(value); case StringComparison.OrdinalIgnoreCase: if (value.Length <= span.Length) { return EqualsOrdinalIgnoreCase(span.Slice(span.Length - value.Length), value); } return false; default: { string text = span.ToString(); string value2 = value.ToString(); return text.EndsWith(value2, comparisonType); } } } public static bool StartsWith(this ReadOnlySpan span, ReadOnlySpan value, StringComparison comparisonType) { switch (comparisonType) { case StringComparison.Ordinal: return span.StartsWith(value); case StringComparison.OrdinalIgnoreCase: if (value.Length <= span.Length) { return EqualsOrdinalIgnoreCase(span.Slice(0, value.Length), value); } return false; default: { string text = span.ToString(); string value2 = value.ToString(); return text.StartsWith(value2, comparisonType); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan AsSpan(this string text) { if (text == null) { return default(ReadOnlySpan); } return new ReadOnlySpan(Unsafe.As>(text), StringAdjustment, text.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan AsSpan(this string text, int start) { if (text == null) { if (start != 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } return default(ReadOnlySpan); } if ((uint)start > (uint)text.Length) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } return new ReadOnlySpan(Unsafe.As>(text), StringAdjustment + start * 2, text.Length - start); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan AsSpan(this string text, int start, int length) { if (text == null) { if (start != 0 || length != 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } return default(ReadOnlySpan); } if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start)) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } return new ReadOnlySpan(Unsafe.As>(text), StringAdjustment + start * 2, length); } public static ReadOnlyMemory AsMemory(this string text) { if (text == null) { return default(ReadOnlyMemory); } return new ReadOnlyMemory(text, 0, text.Length); } public static ReadOnlyMemory AsMemory(this string text, int start) { if (text == null) { if (start != 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } return default(ReadOnlyMemory); } if ((uint)start > (uint)text.Length) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } return new ReadOnlyMemory(text, start, text.Length - start); } public static ReadOnlyMemory AsMemory(this string text, int start, int length) { if (text == null) { if (start != 0 || length != 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } return default(ReadOnlyMemory); } if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start)) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } return new ReadOnlyMemory(text, start, length); } private unsafe static IntPtr MeasureStringAdjustment() { string text = "a"; fixed (char* source = text) { return Unsafe.ByteOffset(ref Unsafe.As>(text).Data, ref Unsafe.AsRef(source)); } } } internal struct MutableDecimal { public uint Flags; public uint High; public uint Low; public uint Mid; private const uint SignMask = 2147483648u; private const uint ScaleMask = 16711680u; private const int ScaleShift = 16; public bool IsNegative { get { return (Flags & 0x80000000u) != 0; } set { Flags = (Flags & 0x7FFFFFFFu) | (value ? 2147483648u : 0u); } } public int Scale { get { return (byte)(Flags >> 16); } set { Flags = (Flags & 0xFF00FFFFu) | (uint)(value << 16); } } } internal struct NUInt { private unsafe readonly void* _value; private unsafe NUInt(uint value) { _value = (void*)value; } private unsafe NUInt(ulong value) { _value = (void*)value; } public static implicit operator NUInt(uint value) { return new NUInt(value); } public unsafe static implicit operator IntPtr(NUInt value) { return (IntPtr)value._value; } public static explicit operator NUInt(int value) { return new NUInt((uint)value); } public unsafe static explicit operator void*(NUInt value) { return value._value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static NUInt operator *(NUInt left, NUInt right) { if (sizeof(IntPtr) != 4) { return new NUInt((ulong)left._value * (ulong)right._value); } return new NUInt((uint)((int)left._value * (int)right._value)); } } internal static class DecimalDecCalc { private static uint D32DivMod1E9(uint hi32, ref uint lo32) { ulong num = ((ulong)hi32 << 32) | lo32; lo32 = (uint)(num / 1000000000); return (uint)(num % 1000000000); } internal static uint DecDivMod1E9(ref MutableDecimal value) { return D32DivMod1E9(D32DivMod1E9(D32DivMod1E9(0u, ref value.High), ref value.Mid), ref value.Low); } internal static void DecAddInt32(ref MutableDecimal value, uint i) { if (D32AddCarry(ref value.Low, i) && D32AddCarry(ref value.Mid, 1u)) { D32AddCarry(ref value.High, 1u); } } private static bool D32AddCarry(ref uint value, uint i) { uint num = value; uint num2 = (value = num + i); if (num2 >= num) { return num2 < i; } return true; } internal static void DecMul10(ref MutableDecimal value) { MutableDecimal d = value; DecShiftLeft(ref value); DecShiftLeft(ref value); DecAdd(ref value, d); DecShiftLeft(ref value); } private static void DecShiftLeft(ref MutableDecimal value) { uint num = (((value.Low & 0x80000000u) != 0) ? 1u : 0u); uint num2 = (((value.Mid & 0x80000000u) != 0) ? 1u : 0u); value.Low <<= 1; value.Mid = (value.Mid << 1) | num; value.High = (value.High << 1) | num2; } private static void DecAdd(ref MutableDecimal value, MutableDecimal d) { if (D32AddCarry(ref value.Low, d.Low) && D32AddCarry(ref value.Mid, 1u)) { D32AddCarry(ref value.High, 1u); } if (D32AddCarry(ref value.Mid, d.Mid)) { D32AddCarry(ref value.High, 1u); } D32AddCarry(ref value.High, d.High); } } internal static class Number { private static class DoubleHelper { public unsafe static uint Exponent(double d) { return (*(uint*)((byte*)(&d) + 4) >> 20) & 0x7FFu; } public unsafe static ulong Mantissa(double d) { return *(uint*)(&d) | ((ulong)(uint)(*(int*)((byte*)(&d) + 4) & 0xFFFFF) << 32); } public unsafe static bool Sign(double d) { return *(uint*)((byte*)(&d) + 4) >> 31 != 0; } } internal const int DECIMAL_PRECISION = 29; private static readonly ulong[] s_rgval64Power10 = new ulong[30] { 11529215046068469760uL, 14411518807585587200uL, 18014398509481984000uL, 11258999068426240000uL, 14073748835532800000uL, 17592186044416000000uL, 10995116277760000000uL, 13743895347200000000uL, 17179869184000000000uL, 10737418240000000000uL, 13421772800000000000uL, 16777216000000000000uL, 10485760000000000000uL, 13107200000000000000uL, 16384000000000000000uL, 14757395258967641293uL, 11805916207174113035uL, 9444732965739290428uL, 15111572745182864686uL, 12089258196146291749uL, 9671406556917033399uL, 15474250491067253438uL, 12379400392853802751uL, 9903520314283042201uL, 15845632502852867522uL, 12676506002282294018uL, 10141204801825835215uL, 16225927682921336344uL, 12980742146337069075uL, 10384593717069655260uL }; private static readonly sbyte[] s_rgexp64Power10 = new sbyte[15] { 4, 7, 10, 14, 17, 20, 24, 27, 30, 34, 37, 40, 44, 47, 50 }; private static readonly ulong[] s_rgval64Power10By16 = new ulong[42] { 10240000000000000000uL, 11368683772161602974uL, 12621774483536188886uL, 14012984643248170708uL, 15557538194652854266uL, 17272337110188889248uL, 9588073174409622172uL, 10644899600020376798uL, 11818212630765741798uL, 13120851772591970216uL, 14567071740625403792uL, 16172698447808779622uL, 17955302187076837696uL, 9967194951097567532uL, 11065809325636130658uL, 12285516299433008778uL, 13639663065038175358uL, 15143067982934716296uL, 16812182738118149112uL, 9332636185032188787uL, 10361307573072618722uL, 16615349947311448416uL, 14965776766268445891uL, 13479973333575319909uL, 12141680576410806707uL, 10936253623915059637uL, 9850501549098619819uL, 17745086042373215136uL, 15983352577617880260uL, 14396524142538228461uL, 12967236152753103031uL, 11679847981112819795uL, 10520271803096747049uL, 9475818434452569218uL, 17070116948172427008uL, 15375394465392026135uL, 13848924157002783096uL, 12474001934591998882uL, 11235582092889474480uL, 10120112665365530972uL, 18230774251475056952uL, 16420821625123739930uL }; private static readonly short[] s_rgexp64Power10By16 = new short[21] { 54, 107, 160, 213, 266, 319, 373, 426, 479, 532, 585, 638, 691, 745, 798, 851, 904, 957, 1010, 1064, 1117 }; public static void RoundNumber(ref NumberBuffer number, int pos) { Span digits = number.Digits; int i; for (i = 0; i < pos && digits[i] != 0; i++) { } if (i == pos && digits[i] >= 53) { while (i > 0 && digits[i - 1] == 57) { i--; } if (i > 0) { digits[i - 1]++; } else { number.Scale++; digits[0] = 49; i = 1; } } else { while (i > 0 && digits[i - 1] == 48) { i--; } } if (i == 0) { number.Scale = 0; number.IsNegative = false; } digits[i] = 0; } internal static bool NumberBufferToDouble(ref NumberBuffer number, out double value) { double num = NumberToDouble(ref number); uint num2 = DoubleHelper.Exponent(num); ulong num3 = DoubleHelper.Mantissa(num); switch (num2) { case 2047u: value = 0.0; return false; case 0u: if (num3 == 0L) { num = 0.0; } break; } value = num; return true; } public unsafe static bool NumberBufferToDecimal(ref NumberBuffer number, ref decimal value) { MutableDecimal source = default(MutableDecimal); byte* ptr = number.UnsafeDigits; int num = number.Scale; if (*ptr == 0) { if (num > 0) { num = 0; } } else { if (num > 29) { return false; } while ((num > 0 || (*ptr != 0 && num > -28)) && (source.High < 429496729 || (source.High == 429496729 && (source.Mid < 2576980377u || (source.Mid == 2576980377u && (source.Low < 2576980377u || (source.Low == 2576980377u && *ptr <= 53))))))) { DecimalDecCalc.DecMul10(ref source); if (*ptr != 0) { DecimalDecCalc.DecAddInt32(ref source, (uint)(*(ptr++) - 48)); } num--; } if (*(ptr++) >= 53) { bool flag = true; if (*(ptr - 1) == 53 && *(ptr - 2) % 2 == 0) { int num2 = 20; while (*ptr == 48 && num2 != 0) { ptr++; num2--; } if (*ptr == 0 || num2 == 0) { flag = false; } } if (flag) { DecimalDecCalc.DecAddInt32(ref source, 1u); if ((source.High | source.Mid | source.Low) == 0) { source.High = 429496729u; source.Mid = 2576980377u; source.Low = 2576980378u; num++; } } } } if (num > 0) { return false; } if (num <= -29) { source.High = 0u; source.Low = 0u; source.Mid = 0u; source.Scale = 28; } else { source.Scale = -num; } source.IsNegative = number.IsNegative; value = Unsafe.As(ref source); return true; } public static void DecimalToNumber(decimal value, ref NumberBuffer number) { ref MutableDecimal reference = ref Unsafe.As(ref value); Span digits = number.Digits; number.IsNegative = reference.IsNegative; int num = 29; while ((reference.Mid != 0) | (reference.High != 0)) { uint num2 = DecimalDecCalc.DecDivMod1E9(ref reference); for (int i = 0; i < 9; i++) { digits[--num] = (byte)(num2 % 10 + 48); num2 /= 10; } } for (uint num3 = reference.Low; num3 != 0; num3 /= 10) { digits[--num] = (byte)(num3 % 10 + 48); } int num4 = 29 - num; number.Scale = num4 - reference.Scale; Span digits2 = number.Digits; int index = 0; while (--num4 >= 0) { digits2[index++] = digits[num++]; } digits2[index] = 0; } private static uint DigitsToInt(ReadOnlySpan digits, int count) { uint value; int bytesConsumed; bool flag = Utf8Parser.TryParse(digits.Slice(0, count), out value, out bytesConsumed, 'D'); return value; } private static ulong Mul32x32To64(uint a, uint b) { return (ulong)a * (ulong)b; } private static ulong Mul64Lossy(ulong a, ulong b, ref int pexp) { ulong num = Mul32x32To64((uint)(a >> 32), (uint)(b >> 32)) + (Mul32x32To64((uint)(a >> 32), (uint)b) >> 32) + (Mul32x32To64((uint)a, (uint)(b >> 32)) >> 32); if ((num & 0x8000000000000000uL) == 0L) { num <<= 1; pexp--; } return num; } private static int abs(int value) { if (value < 0) { return -value; } return value; } private unsafe static double NumberToDouble(ref NumberBuffer number) { ReadOnlySpan digits = number.Digits; int i = 0; int numDigits = number.NumDigits; int num = numDigits; for (; digits[i] == 48; i++) { num--; } if (num == 0) { return 0.0; } int num2 = Math.Min(num, 9); num -= num2; ulong num3 = DigitsToInt(digits, num2); if (num > 0) { num2 = Math.Min(num, 9); num -= num2; uint b = (uint)(s_rgval64Power10[num2 - 1] >> 64 - s_rgexp64Power10[num2 - 1]); num3 = Mul32x32To64((uint)num3, b) + DigitsToInt(digits.Slice(9), num2); } int num4 = number.Scale - (numDigits - num); int num5 = abs(num4); if (num5 >= 352) { ulong num6 = ((num4 > 0) ? 9218868437227405312uL : 0); if (number.IsNegative) { num6 |= 0x8000000000000000uL; } return *(double*)(&num6); } int pexp = 64; if ((num3 & 0xFFFFFFFF00000000uL) == 0L) { num3 <<= 32; pexp -= 32; } if ((num3 & 0xFFFF000000000000uL) == 0L) { num3 <<= 16; pexp -= 16; } if ((num3 & 0xFF00000000000000uL) == 0L) { num3 <<= 8; pexp -= 8; } if ((num3 & 0xF000000000000000uL) == 0L) { num3 <<= 4; pexp -= 4; } if ((num3 & 0xC000000000000000uL) == 0L) { num3 <<= 2; pexp -= 2; } if ((num3 & 0x8000000000000000uL) == 0L) { num3 <<= 1; pexp--; } int num7 = num5 & 0xF; if (num7 != 0) { int num8 = s_rgexp64Power10[num7 - 1]; pexp += ((num4 < 0) ? (-num8 + 1) : num8); ulong b2 = s_rgval64Power10[num7 + ((num4 < 0) ? 15 : 0) - 1]; num3 = Mul64Lossy(num3, b2, ref pexp); } num7 = num5 >> 4; if (num7 != 0) { int num9 = s_rgexp64Power10By16[num7 - 1]; pexp += ((num4 < 0) ? (-num9 + 1) : num9); ulong b3 = s_rgval64Power10By16[num7 + ((num4 < 0) ? 21 : 0) - 1]; num3 = Mul64Lossy(num3, b3, ref pexp); } if (((uint)(int)num3 & 0x400u) != 0) { ulong num10 = num3 + 1023 + (ulong)(((int)num3 >> 11) & 1); if (num10 < num3) { num10 = (num10 >> 1) | 0x8000000000000000uL; pexp++; } num3 = num10; } pexp += 1022; num3 = ((pexp <= 0) ? ((pexp == -52 && num3 >= 9223372036854775896uL) ? 1 : ((pexp > -52) ? (num3 >> -pexp + 11 + 1) : 0)) : ((pexp < 2047) ? ((ulong)((long)pexp << 52) + ((num3 >> 11) & 0xFFFFFFFFFFFFFL)) : 9218868437227405312uL)); if (number.IsNegative) { num3 |= 0x8000000000000000uL; } return *(double*)(&num3); } } internal ref struct NumberBuffer { public int Scale; public bool IsNegative; public const int BufferSize = 51; private byte _b0; private byte _b1; private byte _b2; private byte _b3; private byte _b4; private byte _b5; private byte _b6; private byte _b7; private byte _b8; private byte _b9; private byte _b10; private byte _b11; private byte _b12; private byte _b13; private byte _b14; private byte _b15; private byte _b16; private byte _b17; private byte _b18; private byte _b19; private byte _b20; private byte _b21; private byte _b22; private byte _b23; private byte _b24; private byte _b25; private byte _b26; private byte _b27; private byte _b28; private byte _b29; private byte _b30; private byte _b31; private byte _b32; private byte _b33; private byte _b34; private byte _b35; private byte _b36; private byte _b37; private byte _b38; private byte _b39; private byte _b40; private byte _b41; private byte _b42; private byte _b43; private byte _b44; private byte _b45; private byte _b46; private byte _b47; private byte _b48; private byte _b49; private byte _b50; public unsafe Span Digits => new Span(Unsafe.AsPointer(ref _b0), 51); public unsafe byte* UnsafeDigits => (byte*)Unsafe.AsPointer(ref _b0); public int NumDigits => Digits.IndexOf(0); [Conditional("DEBUG")] public void CheckConsistency() { } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append('['); stringBuilder.Append('"'); Span digits = Digits; for (int i = 0; i < 51; i++) { byte b = digits[i]; if (b == 0) { break; } stringBuilder.Append((char)b); } stringBuilder.Append('"'); stringBuilder.Append(", Scale = " + Scale); stringBuilder.Append(", IsNegative = " + IsNegative); stringBuilder.Append(']'); return stringBuilder.ToString(); } } [StructLayout(LayoutKind.Sequential)] internal sealed class Pinnable { public T Data; } [DebuggerDisplay("{ToString(),raw}")] [DebuggerTypeProxy(typeof(System.MemoryDebugView<>))] [<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] internal struct ReadOnlyMemory { private readonly object _object; private readonly int _index; private readonly int _length; internal const int RemoveFlagsBitMask = int.MaxValue; public static ReadOnlyMemory Empty => default(ReadOnlyMemory); public int Length => _length & 0x7FFFFFFF; public bool IsEmpty => (_length & 0x7FFFFFFF) == 0; public ReadOnlySpan Span { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (_index < 0) { return ((MemoryManager)_object).GetSpan().Slice(_index & 0x7FFFFFFF, _length); } ReadOnlySpan result; if (typeof(T) == typeof(char) && _object is string text) { result = new ReadOnlySpan(Unsafe.As>(text), MemoryExtensions.StringAdjustment, text.Length); return result.Slice(_index, _length); } if (_object != null) { return new ReadOnlySpan((T[])_object, _index, _length & 0x7FFFFFFF); } result = default(ReadOnlySpan); return result; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlyMemory(T[] array) { if (array == null) { this = default(ReadOnlyMemory); return; } _object = array; _index = 0; _length = array.Length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlyMemory(T[] array, int start, int length) { if (array == null) { if (start != 0 || length != 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(); } this = default(ReadOnlyMemory); return; } if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) { System.ThrowHelper.ThrowArgumentOutOfRangeException(); } _object = array; _index = start; _length = length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ReadOnlyMemory(object obj, int start, int length) { _object = obj; _index = start; _length = length; } public static implicit operator ReadOnlyMemory(T[] array) { return new ReadOnlyMemory(array); } public static implicit operator ReadOnlyMemory(ArraySegment segment) { return new ReadOnlyMemory(segment.Array, segment.Offset, segment.Count); } public override string ToString() { if (typeof(T) == typeof(char)) { if (!(_object is string text)) { return Span.ToString(); } return text.Substring(_index, _length & 0x7FFFFFFF); } return $"System.ReadOnlyMemory<{typeof(T).Name}>[{_length & 0x7FFFFFFF}]"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlyMemory Slice(int start) { int length = _length; int num = length & 0x7FFFFFFF; if ((uint)start > (uint)num) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } return new ReadOnlyMemory(_object, _index + start, length - start); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlyMemory Slice(int start, int length) { int length2 = _length; int num = _length & 0x7FFFFFFF; if ((uint)start > (uint)num || (uint)length > (uint)(num - start)) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } return new ReadOnlyMemory(_object, _index + start, length | (length2 & int.MinValue)); } public void CopyTo(Memory destination) { Span.CopyTo(destination.Span); } public bool TryCopyTo(Memory destination) { return Span.TryCopyTo(destination.Span); } public unsafe MemoryHandle Pin() { if (_index < 0) { return ((MemoryManager)_object).Pin(_index & 0x7FFFFFFF); } if (typeof(T) == typeof(char) && _object is string value) { GCHandle handle = GCHandle.Alloc(value, GCHandleType.Pinned); void* pointer = Unsafe.Add((void*)handle.AddrOfPinnedObject(), _index); return new MemoryHandle(pointer, handle); } if (_object is T[] array) { if (_length < 0) { void* pointer2 = Unsafe.Add(Unsafe.AsPointer(ref MemoryMarshal.GetReference(array)), _index); return new MemoryHandle(pointer2); } GCHandle handle2 = GCHandle.Alloc(array, GCHandleType.Pinned); void* pointer3 = Unsafe.Add((void*)handle2.AddrOfPinnedObject(), _index); return new MemoryHandle(pointer3, handle2); } return default(MemoryHandle); } public T[] ToArray() { return Span.ToArray(); } [EditorBrowsable(EditorBrowsableState.Never)] public override bool Equals(object obj) { if (obj is ReadOnlyMemory other) { return Equals(other); } if (obj is Memory memory) { return Equals(memory); } return false; } public bool Equals(ReadOnlyMemory other) { if (_object == other._object && _index == other._index) { return _length == other._length; } return false; } [EditorBrowsable(EditorBrowsableState.Never)] public override int GetHashCode() { if (_object == null) { return 0; } int hashCode = _object.GetHashCode(); int index = _index; int hashCode2 = index.GetHashCode(); index = _length; return CombineHashCodes(hashCode, hashCode2, index.GetHashCode()); } private static int CombineHashCodes(int left, int right) { return ((left << 5) + left) ^ right; } private static int CombineHashCodes(int h1, int h2, int h3) { return CombineHashCodes(CombineHashCodes(h1, h2), h3); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal object GetObjectStartLength(out int start, out int length) { start = _index; length = _length; return _object; } } [<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] [DebuggerTypeProxy(typeof(System.SpanDebugView<>))] [DebuggerTypeProxy(typeof(System.SpanDebugView<>))] [DebuggerDisplay("{ToString(),raw}")] internal ref struct ReadOnlySpan { public ref struct Enumerator { private readonly ReadOnlySpan _span; private int _index; [<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] public ref T Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] [return: <1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] get { return ref _span[_index]; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Enumerator(ReadOnlySpan span) { _span = span; _index = -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { int num = _index + 1; if (num < _span.Length) { _index = num; return true; } return false; } } private readonly Pinnable _pinnable; private readonly IntPtr _byteOffset; private readonly int _length; public int Length => _length; public bool IsEmpty => _length == 0; public static ReadOnlySpan Empty => default(ReadOnlySpan); [<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] public unsafe ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] [return: <1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] get { if ((uint)index >= (uint)_length) { System.ThrowHelper.ThrowIndexOutOfRangeException(); } if (_pinnable == null) { IntPtr byteOffset = _byteOffset; return ref Unsafe.Add(ref Unsafe.AsRef(byteOffset.ToPointer()), index); } return ref Unsafe.Add(ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset), index); } } internal Pinnable Pinnable => _pinnable; internal IntPtr ByteOffset => _byteOffset; public static bool operator !=(ReadOnlySpan left, ReadOnlySpan right) { return !(left == right); } [Obsolete("Equals() on ReadOnlySpan will always throw an exception. Use == instead.")] [EditorBrowsable(EditorBrowsableState.Never)] public override bool Equals(object obj) { throw new NotSupportedException(System.SR.NotSupported_CannotCallEqualsOnSpan); } [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("GetHashCode() on ReadOnlySpan will always throw an exception.")] public override int GetHashCode() { throw new NotSupportedException(System.SR.NotSupported_CannotCallGetHashCodeOnSpan); } public static implicit operator ReadOnlySpan(T[] array) { return new ReadOnlySpan(array); } public static implicit operator ReadOnlySpan(ArraySegment segment) { return new ReadOnlySpan(segment.Array, segment.Offset, segment.Count); } public Enumerator GetEnumerator() { return new Enumerator(this); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan(T[] array) { if (array == null) { this = default(ReadOnlySpan); return; } _length = array.Length; _pinnable = Unsafe.As>(array); _byteOffset = System.SpanHelpers.PerTypeValues.ArrayAdjustment; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan(T[] array, int start, int length) { if (array == null) { if (start != 0 || length != 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } this = default(ReadOnlySpan); return; } if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } _length = length; _pinnable = Unsafe.As>(array); _byteOffset = System.SpanHelpers.PerTypeValues.ArrayAdjustment.Add(start); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public unsafe ReadOnlySpan(void* pointer, int length) { if (System.SpanHelpers.IsReferenceOrContainsReferences()) { System.ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); } if (length < 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } _length = length; _pinnable = null; _byteOffset = new IntPtr(pointer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ReadOnlySpan(Pinnable pinnable, IntPtr byteOffset, int length) { _length = length; _pinnable = pinnable; _byteOffset = byteOffset; } [EditorBrowsable(EditorBrowsableState.Never)] [return: <1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] public unsafe ref T GetPinnableReference() { if (_length != 0) { if (_pinnable == null) { IntPtr byteOffset = _byteOffset; return ref Unsafe.AsRef(byteOffset.ToPointer()); } return ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset); } return ref Unsafe.AsRef(null); } public void CopyTo(Span destination) { if (!TryCopyTo(destination)) { System.ThrowHelper.ThrowArgumentException_DestinationTooShort(); } } public bool TryCopyTo(Span destination) { int length = _length; int length2 = destination.Length; if (length == 0) { return true; } if ((uint)length > (uint)length2) { return false; } ref T src = ref DangerousGetPinnableReference(); System.SpanHelpers.CopyTo(ref destination.DangerousGetPinnableReference(), length2, ref src, length); return true; } public static bool operator ==(ReadOnlySpan left, ReadOnlySpan right) { if (left._length == right._length) { return Unsafe.AreSame(ref left.DangerousGetPinnableReference(), ref right.DangerousGetPinnableReference()); } return false; } public unsafe override string ToString() { if (typeof(T) == typeof(char)) { if (_byteOffset == MemoryExtensions.StringAdjustment) { object obj = Unsafe.As(_pinnable); if (obj is string text && _length == text.Length) { return text; } } fixed (char* value = &Unsafe.As(ref DangerousGetPinnableReference())) { return new string(value, 0, _length); } } return $"System.ReadOnlySpan<{typeof(T).Name}>[{_length}]"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan Slice(int start) { if ((uint)start > (uint)_length) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } IntPtr byteOffset = _byteOffset.Add(start); int length = _length - start; return new ReadOnlySpan(_pinnable, byteOffset, length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan Slice(int start, int length) { if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } IntPtr byteOffset = _byteOffset.Add(start); return new ReadOnlySpan(_pinnable, byteOffset, length); } public T[] ToArray() { if (_length == 0) { return System.SpanHelpers.PerTypeValues.EmptyArray; } T[] array = new T[_length]; CopyTo(array); return array; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [EditorBrowsable(EditorBrowsableState.Never)] internal unsafe ref T DangerousGetPinnableReference() { if (_pinnable == null) { IntPtr byteOffset = _byteOffset; return ref Unsafe.AsRef(byteOffset.ToPointer()); } return ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset); } } [<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] internal struct SequencePosition : IEquatable { private readonly object _object; private readonly int _integer; public SequencePosition(object @object, int integer) { _object = @object; _integer = integer; } [EditorBrowsable(EditorBrowsableState.Never)] public object GetObject() { return _object; } [EditorBrowsable(EditorBrowsableState.Never)] public int GetInteger() { return _integer; } public bool Equals(SequencePosition other) { if (_integer == other._integer) { return object.Equals(_object, other._object); } return false; } [EditorBrowsable(EditorBrowsableState.Never)] public override bool Equals(object obj) { if (obj is SequencePosition other) { return Equals(other); } return false; } [EditorBrowsable(EditorBrowsableState.Never)] public override int GetHashCode() { return HashHelpers.Combine(_object?.GetHashCode() ?? 0, _integer); } } [DebuggerTypeProxy(typeof(System.SpanDebugView<>))] [DebuggerDisplay("{ToString(),raw}")] [DebuggerTypeProxy(typeof(System.SpanDebugView<>))] [<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] internal ref struct Span { public ref struct Enumerator { private readonly Span _span; private int _index; public ref T Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return ref _span[_index]; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Enumerator(Span span) { _span = span; _index = -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { int num = _index + 1; if (num < _span.Length) { _index = num; return true; } return false; } } private readonly Pinnable _pinnable; private readonly IntPtr _byteOffset; private readonly int _length; public int Length => _length; public bool IsEmpty => _length == 0; public static Span Empty => default(Span); public unsafe ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if ((uint)index >= (uint)_length) { System.ThrowHelper.ThrowIndexOutOfRangeException(); } if (_pinnable == null) { IntPtr byteOffset = _byteOffset; return ref Unsafe.Add(ref Unsafe.AsRef(byteOffset.ToPointer()), index); } return ref Unsafe.Add(ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset), index); } } internal Pinnable Pinnable => _pinnable; internal IntPtr ByteOffset => _byteOffset; public static bool operator !=(Span left, Span right) { return !(left == right); } [Obsolete("Equals() on Span will always throw an exception. Use == instead.")] [EditorBrowsable(EditorBrowsableState.Never)] public override bool Equals(object obj) { throw new NotSupportedException(System.SR.NotSupported_CannotCallEqualsOnSpan); } [Obsolete("GetHashCode() on Span will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override int GetHashCode() { throw new NotSupportedException(System.SR.NotSupported_CannotCallGetHashCodeOnSpan); } public static implicit operator Span(T[] array) { return new Span(array); } public static implicit operator Span(ArraySegment segment) { return new Span(segment.Array, segment.Offset, segment.Count); } public Enumerator GetEnumerator() { return new Enumerator(this); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span(T[] array) { if (array == null) { this = default(Span); return; } if (default(T) == null && array.GetType() != typeof(T[])) { System.ThrowHelper.ThrowArrayTypeMismatchException(); } _length = array.Length; _pinnable = Unsafe.As>(array); _byteOffset = System.SpanHelpers.PerTypeValues.ArrayAdjustment; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Span Create(T[] array, int start) { if (array == null) { if (start != 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } return default(Span); } if (default(T) == null && array.GetType() != typeof(T[])) { System.ThrowHelper.ThrowArrayTypeMismatchException(); } if ((uint)start > (uint)array.Length) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } IntPtr byteOffset = System.SpanHelpers.PerTypeValues.ArrayAdjustment.Add(start); int length = array.Length - start; return new Span(Unsafe.As>(array), byteOffset, length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span(T[] array, int start, int length) { if (array == null) { if (start != 0 || length != 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } this = default(Span); return; } if (default(T) == null && array.GetType() != typeof(T[])) { System.ThrowHelper.ThrowArrayTypeMismatchException(); } if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } _length = length; _pinnable = Unsafe.As>(array); _byteOffset = System.SpanHelpers.PerTypeValues.ArrayAdjustment.Add(start); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public unsafe Span(void* pointer, int length) { if (System.SpanHelpers.IsReferenceOrContainsReferences()) { System.ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); } if (length < 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } _length = length; _pinnable = null; _byteOffset = new IntPtr(pointer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Span(Pinnable pinnable, IntPtr byteOffset, int length) { _length = length; _pinnable = pinnable; _byteOffset = byteOffset; } [EditorBrowsable(EditorBrowsableState.Never)] public unsafe ref T GetPinnableReference() { if (_length != 0) { if (_pinnable == null) { IntPtr byteOffset = _byteOffset; return ref Unsafe.AsRef(byteOffset.ToPointer()); } return ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset); } return ref Unsafe.AsRef(null); } public unsafe void Clear() { int length = _length; if (length == 0) { return; } UIntPtr byteLength = (UIntPtr)(ulong)((uint)length * Unsafe.SizeOf()); if ((Unsafe.SizeOf() & (sizeof(IntPtr) - 1)) != 0) { if (_pinnable == null) { IntPtr byteOffset = _byteOffset; byte* ptr = (byte*)byteOffset.ToPointer(); System.SpanHelpers.ClearLessThanPointerSized(ptr, byteLength); } else { System.SpanHelpers.ClearLessThanPointerSized(ref Unsafe.As(ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset)), byteLength); } } else if (System.SpanHelpers.IsReferenceOrContainsReferences()) { UIntPtr pointerSizeLength = (UIntPtr)(ulong)(length * Unsafe.SizeOf() / sizeof(IntPtr)); System.SpanHelpers.ClearPointerSizedWithReferences(ref Unsafe.As(ref DangerousGetPinnableReference()), pointerSizeLength); } else { System.SpanHelpers.ClearPointerSizedWithoutReferences(ref Unsafe.As(ref DangerousGetPinnableReference()), byteLength); } } public unsafe void Fill(T value) { int length = _length; if (length == 0) { return; } if (Unsafe.SizeOf() == 1) { byte value2 = Unsafe.As(ref value); if (_pinnable == null) { IntPtr byteOffset = _byteOffset; Unsafe.InitBlockUnaligned(byteOffset.ToPointer(), value2, (uint)length); } else { Unsafe.InitBlockUnaligned(ref Unsafe.As(ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset)), value2, (uint)length); } return; } ref T source = ref DangerousGetPinnableReference(); int i; for (i = 0; i < (length & -8); i += 8) { Unsafe.Add(ref source, i) = value; Unsafe.Add(ref source, i + 1) = value; Unsafe.Add(ref source, i + 2) = value; Unsafe.Add(ref source, i + 3) = value; Unsafe.Add(ref source, i + 4) = value; Unsafe.Add(ref source, i + 5) = value; Unsafe.Add(ref source, i + 6) = value; Unsafe.Add(ref source, i + 7) = value; } if (i < (length & -4)) { Unsafe.Add(ref source, i) = value; Unsafe.Add(ref source, i + 1) = value; Unsafe.Add(ref source, i + 2) = value; Unsafe.Add(ref source, i + 3) = value; i += 4; } for (; i < length; i++) { Unsafe.Add(ref source, i) = value; } } public void CopyTo(Span destination) { if (!TryCopyTo(destination)) { System.ThrowHelper.ThrowArgumentException_DestinationTooShort(); } } public bool TryCopyTo(Span destination) { int length = _length; int length2 = destination._length; if (length == 0) { return true; } if ((uint)length > (uint)length2) { return false; } ref T src = ref DangerousGetPinnableReference(); System.SpanHelpers.CopyTo(ref destination.DangerousGetPinnableReference(), length2, ref src, length); return true; } public static bool operator ==(Span left, Span right) { if (left._length == right._length) { return Unsafe.AreSame(ref left.DangerousGetPinnableReference(), ref right.DangerousGetPinnableReference()); } return false; } public static implicit operator ReadOnlySpan(Span span) { return new ReadOnlySpan(span._pinnable, span._byteOffset, span._length); } public unsafe override string ToString() { if (typeof(T) == typeof(char)) { fixed (char* value = &Unsafe.As(ref DangerousGetPinnableReference())) { return new string(value, 0, _length); } } return $"System.Span<{typeof(T).Name}>[{_length}]"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span Slice(int start) { if ((uint)start > (uint)_length) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } IntPtr byteOffset = _byteOffset.Add(start); int length = _length - start; return new Span(_pinnable, byteOffset, length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span Slice(int start, int length) { if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } IntPtr byteOffset = _byteOffset.Add(start); return new Span(_pinnable, byteOffset, length); } public T[] ToArray() { if (_length == 0) { return System.SpanHelpers.PerTypeValues.EmptyArray; } T[] array = new T[_length]; CopyTo(array); return array; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [EditorBrowsable(EditorBrowsableState.Never)] internal unsafe ref T DangerousGetPinnableReference() { if (_pinnable == null) { IntPtr byteOffset = _byteOffset; return ref Unsafe.AsRef(byteOffset.ToPointer()); } return ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset); } } internal sealed class SpanDebugView { private readonly T[] _array; [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public T[] Items => _array; public SpanDebugView(Span span) { _array = span.ToArray(); } public SpanDebugView(ReadOnlySpan span) { _array = span.ToArray(); } } internal static class SpanHelpers { internal struct ComparerComparable : IComparable where TComparer : IComparer { private readonly T _value; private readonly TComparer _comparer; public ComparerComparable(T value, TComparer comparer) { _value = value; _comparer = comparer; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(T other) { return _comparer.Compare(_value, other); } } [StructLayout(LayoutKind.Sequential, Size = 64)] private struct Reg64 { } [StructLayout(LayoutKind.Sequential, Size = 32)] private struct Reg32 { } [StructLayout(LayoutKind.Sequential, Size = 16)] private struct Reg16 { } public static class PerTypeValues { public static readonly bool IsReferenceOrContainsReferences = IsReferenceOrContainsReferencesCore(typeof(T)); public static readonly T[] EmptyArray = new T[0]; public static readonly IntPtr ArrayAdjustment = MeasureArrayAdjustment(); private static IntPtr MeasureArrayAdjustment() { T[] array = new T[1]; return Unsafe.ByteOffset(ref Unsafe.As>(array).Data, ref array[0]); } } private const ulong XorPowerOfTwoToHighByte = 283686952306184uL; private const ulong XorPowerOfTwoToHighChar = 4295098372uL; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch(this ReadOnlySpan span, TComparable comparable) where TComparable : IComparable { if (comparable == null) { System.ThrowHelper.ThrowArgumentNullException(System.ExceptionArgument.comparable); } return BinarySearch(ref MemoryMarshal.GetReference(span), span.Length, comparable); } public static int BinarySearch(ref T spanStart, int length, TComparable comparable) where TComparable : IComparable { int num = 0; int num2 = length - 1; while (num <= num2) { int num3 = num2 + num >>> 1; ref TComparable reference = ref comparable; TComparable val = default(TComparable); if (val == null) { val = reference; reference = ref val; } int num4 = reference.CompareTo(Unsafe.Add(ref spanStart, num3)); if (num4 == 0) { return num3; } if (num4 > 0) { num = num3 + 1; } else { num2 = num3 - 1; } } return ~num; } public static int IndexOf(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength) { if (valueLength == 0) { return 0; } byte value2 = value; ref byte second = ref Unsafe.Add(ref value, 1); int num = valueLength - 1; int num2 = 0; while (true) { int num3 = searchSpaceLength - num2 - num; if (num3 <= 0) { break; } int num4 = IndexOf(ref Unsafe.Add(ref searchSpace, num2), value2, num3); if (num4 == -1) { break; } num2 += num4; if (SequenceEqual(ref Unsafe.Add(ref searchSpace, num2 + 1), ref second, num)) { return num2; } num2++; } return -1; } public static int IndexOfAny(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength) { if (valueLength == 0) { return 0; } int num = -1; for (int i = 0; i < valueLength; i++) { int num2 = IndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength); if ((uint)num2 < (uint)num) { num = num2; searchSpaceLength = num2; if (num == 0) { break; } } } return num; } public static int LastIndexOfAny(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength) { if (valueLength == 0) { return 0; } int num = -1; for (int i = 0; i < valueLength; i++) { int num2 = LastIndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength); if (num2 > num) { num = num2; } } return num; } public unsafe static int IndexOf(ref byte searchSpace, byte value, int length) { IntPtr intPtr = (IntPtr)0; IntPtr intPtr2 = (IntPtr)length; if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) { int num = (int)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); intPtr2 = (IntPtr)((Vector.Count - num) & (Vector.Count - 1)); } while (true) { if ((nuint)(void*)intPtr2 >= (nuint)8u) { intPtr2 -= 8; if (value == Unsafe.AddByteOffset(ref searchSpace, intPtr)) { goto IL_0242; } if (value == Unsafe.AddByteOffset(ref searchSpace, intPtr + 1)) { goto IL_024a; } if (value == Unsafe.AddByteOffset(ref searchSpace, intPtr + 2)) { goto IL_0258; } if (value != Unsafe.AddByteOffset(ref searchSpace, intPtr + 3)) { if (value != Unsafe.AddByteOffset(ref searchSpace, intPtr + 4)) { if (value != Unsafe.AddByteOffset(ref searchSpace, intPtr + 5)) { if (value != Unsafe.AddByteOffset(ref searchSpace, intPtr + 6)) { if (value == Unsafe.AddByteOffset(ref searchSpace, intPtr + 7)) { break; } intPtr += 8; continue; } return (int)(void*)(intPtr + 6); } return (int)(void*)(intPtr + 5); } return (int)(void*)(intPtr + 4); } goto IL_0266; } if ((nuint)(void*)intPtr2 >= (nuint)4u) { intPtr2 -= 4; if (value == Unsafe.AddByteOffset(ref searchSpace, intPtr)) { goto IL_0242; } if (value == Unsafe.AddByteOffset(ref searchSpace, intPtr + 1)) { goto IL_024a; } if (value == Unsafe.AddByteOffset(ref searchSpace, intPtr + 2)) { goto IL_0258; } if (value == Unsafe.AddByteOffset(ref searchSpace, intPtr + 3)) { goto IL_0266; } intPtr += 4; } while ((void*)intPtr2 != null) { intPtr2 -= 1; if (value != Unsafe.AddByteOffset(ref searchSpace, intPtr)) { intPtr += 1; continue; } goto IL_0242; } if (Vector.IsHardwareAccelerated && (int)(void*)intPtr < length) { intPtr2 = (IntPtr)((length - (int)(void*)intPtr) & ~(Vector.Count - 1)); Vector vector = GetVector(value); for (; (void*)intPtr2 > (void*)intPtr; intPtr += Vector.Count) { Vector vector2 = Vector.Equals(vector, Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, intPtr))); if (!Vector.Zero.Equals(vector2)) { return (int)(void*)intPtr + LocateFirstFoundByte(vector2); } } if ((int)(void*)intPtr < length) { intPtr2 = (IntPtr)(length - (int)(void*)intPtr); continue; } } return -1; IL_0266: return (int)(void*)(intPtr + 3); IL_0242: return (int)(void*)intPtr; IL_0258: return (int)(void*)(intPtr + 2); IL_024a: return (int)(void*)(intPtr + 1); } return (int)(void*)(intPtr + 7); } public static int LastIndexOf(ref byte searchSpace, int searchSpaceLength, ref byte value, int valueLength) { if (valueLength == 0) { return 0; } byte value2 = value; ref byte second = ref Unsafe.Add(ref value, 1); int num = valueLength - 1; int num2 = 0; while (true) { int num3 = searchSpaceLength - num2 - num; if (num3 <= 0) { break; } int num4 = LastIndexOf(ref searchSpace, value2, num3); if (num4 == -1) { break; } if (SequenceEqual(ref Unsafe.Add(ref searchSpace, num4 + 1), ref second, num)) { return num4; } num2 += num3 - num4; } return -1; } public unsafe static int LastIndexOf(ref byte searchSpace, byte value, int length) { IntPtr intPtr = (IntPtr)length; IntPtr intPtr2 = (IntPtr)length; if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) { int num = (int)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); intPtr2 = (IntPtr)(((length & (Vector.Count - 1)) + num) & (Vector.Count - 1)); } while (true) { if ((nuint)(void*)intPtr2 >= (nuint)8u) { intPtr2 -= 8; intPtr -= 8; if (value == Unsafe.AddByteOffset(ref searchSpace, intPtr + 7)) { break; } if (value != Unsafe.AddByteOffset(ref searchSpace, intPtr + 6)) { if (value != Unsafe.AddByteOffset(ref searchSpace, intPtr + 5)) { if (value != Unsafe.AddByteOffset(ref searchSpace, intPtr + 4)) { if (value != Unsafe.AddByteOffset(ref searchSpace, intPtr + 3)) { if (value != Unsafe.AddByteOffset(ref searchSpace, intPtr + 2)) { if (value != Unsafe.AddByteOffset(ref searchSpace, intPtr + 1)) { if (value != Unsafe.AddByteOffset(ref searchSpace, intPtr)) { continue; } goto IL_0254; } goto IL_025c; } goto IL_026a; } goto IL_0278; } return (int)(void*)(intPtr + 4); } return (int)(void*)(intPtr + 5); } return (int)(void*)(intPtr + 6); } if ((nuint)(void*)intPtr2 >= (nuint)4u) { intPtr2 -= 4; intPtr -= 4; if (value == Unsafe.AddByteOffset(ref searchSpace, intPtr + 3)) { goto IL_0278; } if (value == Unsafe.AddByteOffset(ref searchSpace, intPtr + 2)) { goto IL_026a; } if (value == Unsafe.AddByteOffset(ref searchSpace, intPtr + 1)) { goto IL_025c; } if (value == Unsafe.AddByteOffset(ref searchSpace, intPtr)) { goto IL_0254; } } while ((void*)intPtr2 != null) { intPtr2 -= 1; intPtr -= 1; if (value != Unsafe.AddByteOffset(ref searchSpace, intPtr)) { continue; } goto IL_0254; } if (Vector.IsHardwareAccelerated && (void*)intPtr != null) { intPtr2 = (IntPtr)((int)(void*)intPtr & ~(Vector.Count - 1)); Vector vector = GetVector(value); for (; (nuint)(void*)intPtr2 > (nuint)(Vector.Count - 1); intPtr2 -= Vector.Count) { Vector vector2 = Vector.Equals(vector, Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, intPtr - Vector.Count))); if (Vector.Zero.Equals(vector2)) { intPtr -= Vector.Count; continue; } return (int)intPtr - Vector.Count + LocateLastFoundByte(vector2); } if ((void*)intPtr != null) { intPtr2 = intPtr; continue; } } return -1; IL_0254: return (int)(void*)intPtr; IL_026a: return (int)(void*)(intPtr + 2); IL_0278: return (int)(void*)(intPtr + 3); IL_025c: return (int)(void*)(intPtr + 1); } return (int)(void*)(intPtr + 7); } public unsafe static int IndexOfAny(ref byte searchSpace, byte value0, byte value1, int length) { IntPtr intPtr = (IntPtr)0; IntPtr intPtr2 = (IntPtr)length; if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) { int num = (int)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); intPtr2 = (IntPtr)((Vector.Count - num) & (Vector.Count - 1)); } while (true) { if ((nuint)(void*)intPtr2 >= (nuint)8u) { intPtr2 -= 8; uint num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr); if (value0 == num2 || value1 == num2) { goto IL_02ff; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 1); if (value0 == num2 || value1 == num2) { goto IL_0307; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 2); if (value0 == num2 || value1 == num2) { goto IL_0315; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 3); if (value0 != num2 && value1 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 4); if (value0 != num2 && value1 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 5); if (value0 != num2 && value1 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 6); if (value0 != num2 && value1 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 7); if (value0 == num2 || value1 == num2) { break; } intPtr += 8; continue; } return (int)(void*)(intPtr + 6); } return (int)(void*)(intPtr + 5); } return (int)(void*)(intPtr + 4); } goto IL_0323; } if ((nuint)(void*)intPtr2 >= (nuint)4u) { intPtr2 -= 4; uint num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr); if (value0 == num2 || value1 == num2) { goto IL_02ff; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 1); if (value0 == num2 || value1 == num2) { goto IL_0307; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 2); if (value0 == num2 || value1 == num2) { goto IL_0315; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 3); if (value0 == num2 || value1 == num2) { goto IL_0323; } intPtr += 4; } while ((void*)intPtr2 != null) { intPtr2 -= 1; uint num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr); if (value0 != num2 && value1 != num2) { intPtr += 1; continue; } goto IL_02ff; } if (Vector.IsHardwareAccelerated && (int)(void*)intPtr < length) { intPtr2 = (IntPtr)((length - (int)(void*)intPtr) & ~(Vector.Count - 1)); Vector vector = GetVector(value0); Vector vector2 = GetVector(value1); for (; (void*)intPtr2 > (void*)intPtr; intPtr += Vector.Count) { Vector left = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, intPtr)); Vector vector3 = Vector.BitwiseOr(Vector.Equals(left, vector), Vector.Equals(left, vector2)); if (!Vector.Zero.Equals(vector3)) { return (int)(void*)intPtr + LocateFirstFoundByte(vector3); } } if ((int)(void*)intPtr < length) { intPtr2 = (IntPtr)(length - (int)(void*)intPtr); continue; } } return -1; IL_02ff: return (int)(void*)intPtr; IL_0315: return (int)(void*)(intPtr + 2); IL_0307: return (int)(void*)(intPtr + 1); IL_0323: return (int)(void*)(intPtr + 3); } return (int)(void*)(intPtr + 7); } public unsafe static int IndexOfAny(ref byte searchSpace, byte value0, byte value1, byte value2, int length) { IntPtr intPtr = (IntPtr)0; IntPtr intPtr2 = (IntPtr)length; if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) { int num = (int)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); intPtr2 = (IntPtr)((Vector.Count - num) & (Vector.Count - 1)); } while (true) { if ((nuint)(void*)intPtr2 >= (nuint)8u) { intPtr2 -= 8; uint num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr); if (value0 == num2 || value1 == num2 || value2 == num2) { goto IL_0393; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 1); if (value0 == num2 || value1 == num2 || value2 == num2) { goto IL_039b; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 2); if (value0 == num2 || value1 == num2 || value2 == num2) { goto IL_03a9; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 3); if (value0 != num2 && value1 != num2 && value2 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 4); if (value0 != num2 && value1 != num2 && value2 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 5); if (value0 != num2 && value1 != num2 && value2 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 6); if (value0 != num2 && value1 != num2 && value2 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 7); if (value0 == num2 || value1 == num2 || value2 == num2) { break; } intPtr += 8; continue; } return (int)(void*)(intPtr + 6); } return (int)(void*)(intPtr + 5); } return (int)(void*)(intPtr + 4); } goto IL_03b7; } if ((nuint)(void*)intPtr2 >= (nuint)4u) { intPtr2 -= 4; uint num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr); if (value0 == num2 || value1 == num2 || value2 == num2) { goto IL_0393; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 1); if (value0 == num2 || value1 == num2 || value2 == num2) { goto IL_039b; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 2); if (value0 == num2 || value1 == num2 || value2 == num2) { goto IL_03a9; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 3); if (value0 == num2 || value1 == num2 || value2 == num2) { goto IL_03b7; } intPtr += 4; } while ((void*)intPtr2 != null) { intPtr2 -= 1; uint num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr); if (value0 != num2 && value1 != num2 && value2 != num2) { intPtr += 1; continue; } goto IL_0393; } if (Vector.IsHardwareAccelerated && (int)(void*)intPtr < length) { intPtr2 = (IntPtr)((length - (int)(void*)intPtr) & ~(Vector.Count - 1)); Vector vector = GetVector(value0); Vector vector2 = GetVector(value1); Vector vector3 = GetVector(value2); for (; (void*)intPtr2 > (void*)intPtr; intPtr += Vector.Count) { Vector left = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, intPtr)); Vector vector4 = Vector.BitwiseOr(Vector.BitwiseOr(Vector.Equals(left, vector), Vector.Equals(left, vector2)), Vector.Equals(left, vector3)); if (!Vector.Zero.Equals(vector4)) { return (int)(void*)intPtr + LocateFirstFoundByte(vector4); } } if ((int)(void*)intPtr < length) { intPtr2 = (IntPtr)(length - (int)(void*)intPtr); continue; } } return -1; IL_0393: return (int)(void*)intPtr; IL_039b: return (int)(void*)(intPtr + 1); IL_03b7: return (int)(void*)(intPtr + 3); IL_03a9: return (int)(void*)(intPtr + 2); } return (int)(void*)(intPtr + 7); } public unsafe static int LastIndexOfAny(ref byte searchSpace, byte value0, byte value1, int length) { IntPtr intPtr = (IntPtr)length; IntPtr intPtr2 = (IntPtr)length; if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) { int num = (int)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); intPtr2 = (IntPtr)(((length & (Vector.Count - 1)) + num) & (Vector.Count - 1)); } while (true) { if ((nuint)(void*)intPtr2 >= (nuint)8u) { intPtr2 -= 8; intPtr -= 8; uint num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 7); if (value0 == num2 || value1 == num2) { break; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 6); if (value0 != num2 && value1 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 5); if (value0 != num2 && value1 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 4); if (value0 != num2 && value1 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 3); if (value0 != num2 && value1 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 2); if (value0 != num2 && value1 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 1); if (value0 != num2 && value1 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr); if (value0 != num2 && value1 != num2) { continue; } goto IL_0314; } goto IL_031c; } goto IL_032a; } goto IL_0338; } return (int)(void*)(intPtr + 4); } return (int)(void*)(intPtr + 5); } return (int)(void*)(intPtr + 6); } if ((nuint)(void*)intPtr2 >= (nuint)4u) { intPtr2 -= 4; intPtr -= 4; uint num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 3); if (value0 == num2 || value1 == num2) { goto IL_0338; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 2); if (value0 == num2 || value1 == num2) { goto IL_032a; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 1); if (value0 == num2 || value1 == num2) { goto IL_031c; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr); if (value0 == num2 || value1 == num2) { goto IL_0314; } } while ((void*)intPtr2 != null) { intPtr2 -= 1; intPtr -= 1; uint num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr); if (value0 != num2 && value1 != num2) { continue; } goto IL_0314; } if (Vector.IsHardwareAccelerated && (void*)intPtr != null) { intPtr2 = (IntPtr)((int)(void*)intPtr & ~(Vector.Count - 1)); Vector vector = GetVector(value0); Vector vector2 = GetVector(value1); for (; (nuint)(void*)intPtr2 > (nuint)(Vector.Count - 1); intPtr2 -= Vector.Count) { Vector left = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, intPtr - Vector.Count)); Vector vector3 = Vector.BitwiseOr(Vector.Equals(left, vector), Vector.Equals(left, vector2)); if (Vector.Zero.Equals(vector3)) { intPtr -= Vector.Count; continue; } return (int)intPtr - Vector.Count + LocateLastFoundByte(vector3); } if ((void*)intPtr != null) { intPtr2 = intPtr; continue; } } return -1; IL_0314: return (int)(void*)intPtr; IL_0338: return (int)(void*)(intPtr + 3); IL_031c: return (int)(void*)(intPtr + 1); IL_032a: return (int)(void*)(intPtr + 2); } return (int)(void*)(intPtr + 7); } public unsafe static int LastIndexOfAny(ref byte searchSpace, byte value0, byte value1, byte value2, int length) { IntPtr intPtr = (IntPtr)length; IntPtr intPtr2 = (IntPtr)length; if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) { int num = (int)Unsafe.AsPointer(ref searchSpace) & (Vector.Count - 1); intPtr2 = (IntPtr)(((length & (Vector.Count - 1)) + num) & (Vector.Count - 1)); } while (true) { if ((nuint)(void*)intPtr2 >= (nuint)8u) { intPtr2 -= 8; intPtr -= 8; uint num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 7); if (value0 == num2 || value1 == num2 || value2 == num2) { break; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 6); if (value0 != num2 && value1 != num2 && value2 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 5); if (value0 != num2 && value1 != num2 && value2 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 4); if (value0 != num2 && value1 != num2 && value2 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 3); if (value0 != num2 && value1 != num2 && value2 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 2); if (value0 != num2 && value1 != num2 && value2 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 1); if (value0 != num2 && value1 != num2 && value2 != num2) { num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr); if (value0 != num2 && value1 != num2 && value2 != num2) { continue; } goto IL_03ab; } goto IL_03b3; } goto IL_03c1; } goto IL_03cf; } return (int)(void*)(intPtr + 4); } return (int)(void*)(intPtr + 5); } return (int)(void*)(intPtr + 6); } if ((nuint)(void*)intPtr2 >= (nuint)4u) { intPtr2 -= 4; intPtr -= 4; uint num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 3); if (value0 == num2 || value1 == num2 || value2 == num2) { goto IL_03cf; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 2); if (value0 == num2 || value1 == num2 || value2 == num2) { goto IL_03c1; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr + 1); if (value0 == num2 || value1 == num2 || value2 == num2) { goto IL_03b3; } num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr); if (value0 == num2 || value1 == num2 || value2 == num2) { goto IL_03ab; } } while ((void*)intPtr2 != null) { intPtr2 -= 1; intPtr -= 1; uint num2 = Unsafe.AddByteOffset(ref searchSpace, intPtr); if (value0 != num2 && value1 != num2 && value2 != num2) { continue; } goto IL_03ab; } if (Vector.IsHardwareAccelerated && (void*)intPtr != null) { intPtr2 = (IntPtr)((int)(void*)intPtr & ~(Vector.Count - 1)); Vector vector = GetVector(value0); Vector vector2 = GetVector(value1); Vector vector3 = GetVector(value2); for (; (nuint)(void*)intPtr2 > (nuint)(Vector.Count - 1); intPtr2 -= Vector.Count) { Vector left = Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref searchSpace, intPtr - Vector.Count)); Vector vector4 = Vector.BitwiseOr(Vector.BitwiseOr(Vector.Equals(left, vector), Vector.Equals(left, vector2)), Vector.Equals(left, vector3)); if (Vector.Zero.Equals(vector4)) { intPtr -= Vector.Count; continue; } return (int)intPtr - Vector.Count + LocateLastFoundByte(vector4); } if ((void*)intPtr != null) { intPtr2 = intPtr; continue; } } return -1; IL_03ab: return (int)(void*)intPtr; IL_03cf: return (int)(void*)(intPtr + 3); IL_03c1: return (int)(void*)(intPtr + 2); IL_03b3: return (int)(void*)(intPtr + 1); } return (int)(void*)(intPtr + 7); } public unsafe static bool SequenceEqual(ref byte first, ref byte second, NUInt length) { if (Unsafe.AreSame(ref first, ref second)) { goto IL_013d; } IntPtr intPtr = (IntPtr)0; IntPtr intPtr2 = (IntPtr)(void*)length; if (Vector.IsHardwareAccelerated && (nuint)(void*)intPtr2 >= (nuint)Vector.Count) { intPtr2 -= Vector.Count; while (true) { if ((void*)intPtr2 > (void*)intPtr) { if (Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref first, intPtr)) != Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref second, intPtr))) { break; } intPtr += Vector.Count; continue; } return Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref first, intPtr2)) == Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref second, intPtr2)); } } else { if ((nuint)(void*)intPtr2 < (nuint)sizeof(UIntPtr)) { while ((void*)intPtr2 > (void*)intPtr) { if (Unsafe.AddByteOffset(ref first, intPtr) == Unsafe.AddByteOffset(ref second, intPtr)) { intPtr += 1; continue; } goto IL_013f; } goto IL_013d; } intPtr2 -= sizeof(UIntPtr); while (true) { if ((void*)intPtr2 > (void*)intPtr) { if (Unsafe.ReadUnaligned(ref Unsafe.AddByteOffset(ref first, intPtr)) != Unsafe.ReadUnaligned(ref Unsafe.AddByteOffset(ref second, intPtr))) { break; } intPtr += sizeof(UIntPtr); continue; } return Unsafe.ReadUnaligned(ref Unsafe.AddByteOffset(ref first, intPtr2)) == Unsafe.ReadUnaligned(ref Unsafe.AddByteOffset(ref second, intPtr2)); } } goto IL_013f; IL_013d: return true; IL_013f: return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LocateFirstFoundByte(Vector match) { Vector vector = Vector.AsVectorUInt64(match); ulong num = 0uL; int i; for (i = 0; i < Vector.Count; i++) { num = vector[i]; if (num != 0L) { break; } } return i * 8 + LocateFirstFoundByte(num); } public unsafe static int SequenceCompareTo(ref byte first, int firstLength, ref byte second, int secondLength) { if (!Unsafe.AreSame(ref first, ref second)) { IntPtr intPtr = (IntPtr)((firstLength < secondLength) ? firstLength : secondLength); IntPtr intPtr2 = (IntPtr)0; IntPtr intPtr3 = (IntPtr)(void*)intPtr; if (Vector.IsHardwareAccelerated && (nuint)(void*)intPtr3 > (nuint)Vector.Count) { intPtr3 -= Vector.Count; for (; (void*)intPtr3 > (void*)intPtr2 && !(Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref first, intPtr2)) != Unsafe.ReadUnaligned>(ref Unsafe.AddByteOffset(ref second, intPtr2))); intPtr2 += Vector.Count) { } } else if ((nuint)(void*)intPtr3 > (nuint)sizeof(UIntPtr)) { intPtr3 -= sizeof(UIntPtr); for (; (void*)intPtr3 > (void*)intPtr2 && !(Unsafe.ReadUnaligned(ref Unsafe.AddByteOffset(ref first, intPtr2)) != Unsafe.ReadUnaligned(ref Unsafe.AddByteOffset(ref second, intPtr2))); intPtr2 += sizeof(UIntPtr)) { } } for (; (void*)intPtr > (void*)intPtr2; intPtr2 += 1) { int num = Unsafe.AddByteOffset(ref first, intPtr2).CompareTo(Unsafe.AddByteOffset(ref second, intPtr2)); if (num != 0) { return num; } } } return firstLength - secondLength; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LocateLastFoundByte(Vector match) { Vector vector = Vector.AsVectorUInt64(match); ulong num = 0uL; int num2; for (num2 = Vector.Count - 1; num2 >= 0; num2--) { num = vector[num2]; if (num != 0L) { break; } } return num2 * 8 + LocateLastFoundByte(num); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LocateFirstFoundByte(ulong match) { ulong num = match ^ (match - 1); return (int)(num * 283686952306184L >> 57); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LocateLastFoundByte(ulong match) { int num = 7; while ((long)match > 0L) { match <<= 8; num--; } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector GetVector(byte vectorByte) { return Vector.AsVectorByte(new Vector((uint)(vectorByte * 16843009))); } public unsafe static int SequenceCompareTo(ref char first, int firstLength, ref char second, int secondLength) { int result = firstLength - secondLength; if (!Unsafe.AreSame(ref first, ref second)) { IntPtr intPtr = (IntPtr)((firstLength < secondLength) ? firstLength : secondLength); IntPtr intPtr2 = (IntPtr)0; if ((nuint)(void*)intPtr >= (nuint)(sizeof(UIntPtr) / 2)) { if (Vector.IsHardwareAccelerated && (nuint)(void*)intPtr >= (nuint)Vector.Count) { IntPtr intPtr3 = intPtr - Vector.Count; while (!(Unsafe.ReadUnaligned>(ref Unsafe.As(ref Unsafe.Add(ref first, intPtr2))) != Unsafe.ReadUnaligned>(ref Unsafe.As(ref Unsafe.Add(ref second, intPtr2))))) { intPtr2 += Vector.Count; if ((void*)intPtr3 < (void*)intPtr2) { break; } } } for (; (void*)intPtr >= (void*)(intPtr2 + sizeof(UIntPtr) / 2) && !(Unsafe.ReadUnaligned(ref Unsafe.As(ref Unsafe.Add(ref first, intPtr2))) != Unsafe.ReadUnaligned(ref Unsafe.As(ref Unsafe.Add(ref second, intPtr2)))); intPtr2 += sizeof(UIntPtr) / 2) { } } if (sizeof(UIntPtr) > 4 && (void*)intPtr >= (void*)(intPtr2 + 2) && Unsafe.ReadUnaligned(ref Unsafe.As(ref Unsafe.Add(ref first, intPtr2))) == Unsafe.ReadUnaligned(ref Unsafe.As(ref Unsafe.Add(ref second, intPtr2)))) { intPtr2 += 2; } for (; (void*)intPtr2 < (void*)intPtr; intPtr2 += 1) { int num = Unsafe.Add(ref first, intPtr2).CompareTo(Unsafe.Add(ref second, intPtr2)); if (num != 0) { return num; } } } return result; } public unsafe static int IndexOf(ref char searchSpace, char value, int length) { fixed (char* ptr = &searchSpace) { char* ptr2 = ptr; char* ptr3 = ptr2 + length; if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) { int num = ((int)ptr2 & (Unsafe.SizeOf>() - 1)) / 2; length = (Vector.Count - num) & (Vector.Count - 1); } while (true) { if (length >= 4) { length -= 4; if (*ptr2 == value) { break; } if (ptr2[1] != value) { if (ptr2[2] != value) { if (ptr2[3] != value) { ptr2 += 4; continue; } ptr2++; } ptr2++; } ptr2++; break; } while (length > 0) { length--; if (*ptr2 == value) { goto end_IL_0079; } ptr2++; } if (Vector.IsHardwareAccelerated && ptr2 < ptr3) { length = (int)((ptr3 - ptr2) & ~(Vector.Count - 1)); Vector left = new Vector(value); while (length > 0) { Vector vector = Vector.Equals(left, Unsafe.Read>(ptr2)); if (Vector.Zero.Equals(vector)) { ptr2 += Vector.Count; length -= Vector.Count; continue; } return (int)(ptr2 - ptr) + LocateFirstFoundChar(vector); } if (ptr2 < ptr3) { length = (int)(ptr3 - ptr2); continue; } } return -1; continue; end_IL_0079: break; } return (int)(ptr2 - ptr); } } public unsafe static int LastIndexOf(ref char searchSpace, char value, int length) { fixed (char* ptr = &searchSpace) { char* ptr2 = ptr + length; char* ptr3 = ptr; if (Vector.IsHardwareAccelerated && length >= Vector.Count * 2) { length = ((int)ptr2 & (Unsafe.SizeOf>() - 1)) / 2; } while (true) { if (length >= 4) { length -= 4; ptr2 -= 4; if (ptr2[3] == value) { break; } if (ptr2[2] != value) { if (ptr2[1] != value) { if (*ptr2 != value) { continue; } goto IL_011d; } return (int)(ptr2 - ptr3) + 1; } return (int)(ptr2 - ptr3) + 2; } while (length > 0) { length--; ptr2--; if (*ptr2 != value) { continue; } goto IL_011d; } if (Vector.IsHardwareAccelerated && ptr2 > ptr3) { length = (int)((ptr2 - ptr3) & ~(Vector.Count - 1)); Vector left = new Vector(value); while (length > 0) { char* ptr4 = ptr2 - Vector.Count; Vector vector = Vector.Equals(left, Unsafe.Read>(ptr4)); if (Vector.Zero.Equals(vector)) { ptr2 -= Vector.Count; length -= Vector.Count; continue; } return (int)(ptr4 - ptr3) + LocateLastFoundChar(vector); } if (ptr2 > ptr3) { length = (int)(ptr2 - ptr3); continue; } } return -1; IL_011d: return (int)(ptr2 - ptr3); } return (int)(ptr2 - ptr3) + 3; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LocateFirstFoundChar(Vector match) { Vector vector = Vector.AsVectorUInt64(match); ulong num = 0uL; int i; for (i = 0; i < Vector.Count; i++) { num = vector[i]; if (num != 0L) { break; } } return i * 4 + LocateFirstFoundChar(num); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LocateFirstFoundChar(ulong match) { ulong num = match ^ (match - 1); return (int)(num * 4295098372L >> 49); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LocateLastFoundChar(Vector match) { Vector vector = Vector.AsVectorUInt64(match); ulong num = 0uL; int num2; for (num2 = Vector.Count - 1; num2 >= 0; num2--) { num = vector[num2]; if (num != 0L) { break; } } return num2 * 4 + LocateLastFoundChar(num); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LocateLastFoundChar(ulong match) { int num = 3; while ((long)match > 0L) { match <<= 16; num--; } return num; } public unsafe static void CopyTo(ref T dst, int dstLength, ref T src, int srcLength) { IntPtr intPtr = Unsafe.ByteOffset(ref src, ref Unsafe.Add(ref src, srcLength)); IntPtr intPtr2 = Unsafe.ByteOffset(ref dst, ref Unsafe.Add(ref dst, dstLength)); IntPtr intPtr3 = Unsafe.ByteOffset(ref src, ref dst); bool num; if (sizeof(IntPtr) != 4) { if ((ulong)(long)intPtr3 >= (ulong)(long)intPtr) { num = (ulong)(long)intPtr3 > (ulong)(-(long)intPtr2); goto IL_006f; } } else if ((uint)(int)intPtr3 >= (uint)(int)intPtr) { num = (uint)(int)intPtr3 > (uint)(-(int)intPtr2); goto IL_006f; } goto IL_00de; IL_00de: bool flag = ((sizeof(IntPtr) == 4) ? ((uint)(int)intPtr3 > (uint)(-(int)intPtr2)) : ((ulong)(long)intPtr3 > (ulong)(-(long)intPtr2))); int num2 = (flag ? 1 : (-1)); int num3 = ((!flag) ? (srcLength - 1) : 0); int i; for (i = 0; i < (srcLength & -8); i += 8) { Unsafe.Add(ref dst, num3) = Unsafe.Add(ref src, num3); Unsafe.Add(ref dst, num3 + num2) = Unsafe.Add(ref src, num3 + num2); Unsafe.Add(ref dst, num3 + num2 * 2) = Unsafe.Add(ref src, num3 + num2 * 2); Unsafe.Add(ref dst, num3 + num2 * 3) = Unsafe.Add(ref src, num3 + num2 * 3); Unsafe.Add(ref dst, num3 + num2 * 4) = Unsafe.Add(ref src, num3 + num2 * 4); Unsafe.Add(ref dst, num3 + num2 * 5) = Unsafe.Add(ref src, num3 + num2 * 5); Unsafe.Add(ref dst, num3 + num2 * 6) = Unsafe.Add(ref src, num3 + num2 * 6); Unsafe.Add(ref dst, num3 + num2 * 7) = Unsafe.Add(ref src, num3 + num2 * 7); num3 += num2 * 8; } if (i < (srcLength & -4)) { Unsafe.Add(ref dst, num3) = Unsafe.Add(ref src, num3); Unsafe.Add(ref dst, num3 + num2) = Unsafe.Add(ref src, num3 + num2); Unsafe.Add(ref dst, num3 + num2 * 2) = Unsafe.Add(ref src, num3 + num2 * 2); Unsafe.Add(ref dst, num3 + num2 * 3) = Unsafe.Add(ref src, num3 + num2 * 3); num3 += num2 * 4; i += 4; } for (; i < srcLength; i++) { Unsafe.Add(ref dst, num3) = Unsafe.Add(ref src, num3); num3 += num2; } return; IL_006f: if (!num && !IsReferenceOrContainsReferences()) { ref byte source = ref Unsafe.As(ref dst); ref byte source2 = ref Unsafe.As(ref src); ulong num4 = (ulong)(long)intPtr; uint num6; for (ulong num5 = 0uL; num5 < num4; num5 += num6) { num6 = (uint)((num4 - num5 > uint.MaxValue) ? uint.MaxValue : (num4 - num5)); Unsafe.CopyBlock(ref Unsafe.Add(ref source, (IntPtr)(long)num5), ref Unsafe.Add(ref source2, (IntPtr)(long)num5), num6); } return; } goto IL_00de; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static IntPtr Add(this IntPtr start, int index) { if (sizeof(IntPtr) == 4) { uint num = (uint)(index * Unsafe.SizeOf()); return (IntPtr)((byte*)(void*)start + num); } ulong num2 = (ulong)index * (ulong)Unsafe.SizeOf(); return (IntPtr)((byte*)(void*)start + num2); } public static bool IsReferenceOrContainsReferences() { return PerTypeValues.IsReferenceOrContainsReferences; } private static bool IsReferenceOrContainsReferencesCore(Type type) { if (type.GetTypeInfo().IsPrimitive) { return false; } if (!type.GetTypeInfo().IsValueType) { return true; } Type underlyingType = Nullable.GetUnderlyingType(type); if (underlyingType != null) { type = underlyingType; } if (type.GetTypeInfo().IsEnum) { return false; } foreach (FieldInfo declaredField in type.GetTypeInfo().DeclaredFields) { if (!declaredField.IsStatic && IsReferenceOrContainsReferencesCore(declaredField.FieldType)) { return true; } } return false; } public unsafe static void ClearLessThanPointerSized(byte* ptr, UIntPtr byteLength) { if (sizeof(UIntPtr) == 4) { Unsafe.InitBlockUnaligned(ptr, 0, (uint)byteLength); return; } ulong num = (ulong)byteLength; uint num2 = (uint)(num & 0xFFFFFFFFu); Unsafe.InitBlockUnaligned(ptr, 0, num2); num -= num2; ptr += num2; while (num != 0) { num2 = (uint)((num >= uint.MaxValue) ? uint.MaxValue : num); Unsafe.InitBlockUnaligned(ptr, 0, num2); ptr += num2; num -= num2; } } public unsafe static void ClearLessThanPointerSized(ref byte b, UIntPtr byteLength) { if (sizeof(UIntPtr) == 4) { Unsafe.InitBlockUnaligned(ref b, 0, (uint)byteLength); return; } ulong num = (ulong)byteLength; uint num2 = (uint)(num & 0xFFFFFFFFu); Unsafe.InitBlockUnaligned(ref b, 0, num2); num -= num2; long num3 = num2; while (num != 0) { num2 = (uint)((num >= uint.MaxValue) ? uint.MaxValue : num); Unsafe.InitBlockUnaligned(ref Unsafe.Add(ref b, (IntPtr)num3), 0, num2); num3 += num2; num -= num2; } } public unsafe static void ClearPointerSizedWithoutReferences(ref byte b, UIntPtr byteLength) { IntPtr zero; for (zero = IntPtr.Zero; zero.LessThanEqual(byteLength - sizeof(Reg64)); zero += sizeof(Reg64)) { Unsafe.As(ref Unsafe.Add(ref b, zero)) = default(Reg64); } if (zero.LessThanEqual(byteLength - sizeof(Reg32))) { Unsafe.As(ref Unsafe.Add(ref b, zero)) = default(Reg32); zero += sizeof(Reg32); } if (zero.LessThanEqual(byteLength - sizeof(Reg16))) { Unsafe.As(ref Unsafe.Add(ref b, zero)) = default(Reg16); zero += sizeof(Reg16); } if (zero.LessThanEqual(byteLength - 8)) { Unsafe.As(ref Unsafe.Add(ref b, zero)) = 0L; zero += 8; } if (sizeof(IntPtr) == 4 && zero.LessThanEqual(byteLength - 4)) { Unsafe.As(ref Unsafe.Add(ref b, zero)) = 0; zero += 4; } } public static void ClearPointerSizedWithReferences(ref IntPtr ip, UIntPtr pointerSizeLength) { IntPtr intPtr = IntPtr.Zero; IntPtr zero = IntPtr.Zero; while ((zero = intPtr + 8).LessThanEqual(pointerSizeLength)) { Unsafe.Add(ref ip, intPtr + 0) = default(IntPtr); Unsafe.Add(ref ip, intPtr + 1) = default(IntPtr); Unsafe.Add(ref ip, intPtr + 2) = default(IntPtr); Unsafe.Add(ref ip, intPtr + 3) = default(IntPtr); Unsafe.Add(ref ip, intPtr + 4) = default(IntPtr); Unsafe.Add(ref ip, intPtr + 5) = default(IntPtr); Unsafe.Add(ref ip, intPtr + 6) = default(IntPtr); Unsafe.Add(ref ip, intPtr + 7) = default(IntPtr); intPtr = zero; } if ((zero = intPtr + 4).LessThanEqual(pointerSizeLength)) { Unsafe.Add(ref ip, intPtr + 0) = default(IntPtr); Unsafe.Add(ref ip, intPtr + 1) = default(IntPtr); Unsafe.Add(ref ip, intPtr + 2) = default(IntPtr); Unsafe.Add(ref ip, intPtr + 3) = default(IntPtr); intPtr = zero; } if ((zero = intPtr + 2).LessThanEqual(pointerSizeLength)) { Unsafe.Add(ref ip, intPtr + 0) = default(IntPtr); Unsafe.Add(ref ip, intPtr + 1) = default(IntPtr); intPtr = zero; } if ((intPtr + 1).LessThanEqual(pointerSizeLength)) { Unsafe.Add(ref ip, intPtr) = default(IntPtr); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static bool LessThanEqual(this IntPtr index, UIntPtr length) { if (sizeof(UIntPtr) != 4) { return (long)index <= (long)(ulong)length; } return (int)index <= (int)(uint)length; } public static int IndexOf(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) where T : IEquatable { if (valueLength == 0) { return 0; } T value2 = value; ref T second = ref Unsafe.Add(ref value, 1); int num = valueLength - 1; int num2 = 0; while (true) { int num3 = searchSpaceLength - num2 - num; if (num3 <= 0) { break; } int num4 = IndexOf(ref Unsafe.Add(ref searchSpace, num2), value2, num3); if (num4 == -1) { break; } num2 += num4; if (SequenceEqual(ref Unsafe.Add(ref searchSpace, num2 + 1), ref second, num)) { return num2; } num2++; } return -1; } public unsafe static int IndexOf(ref T searchSpace, T value, int length) where T : IEquatable { IntPtr intPtr = (IntPtr)0; while (true) { T val; if (length >= 8) { length -= 8; ref T reference = ref value; val = default(T); if (val == null) { val = reference; reference = ref val; } if (!reference.Equals(Unsafe.Add(ref searchSpace, intPtr))) { ref T reference2 = ref value; val = default(T); if (val == null) { val = reference2; reference2 = ref val; } if (reference2.Equals(Unsafe.Add(ref searchSpace, intPtr + 1))) { goto IL_0345; } ref T reference3 = ref value; val = default(T); if (val == null) { val = reference3; reference3 = ref val; } if (reference3.Equals(Unsafe.Add(ref searchSpace, intPtr + 2))) { goto IL_0353; } ref T reference4 = ref value; val = default(T); if (val == null) { val = reference4; reference4 = ref val; } if (!reference4.Equals(Unsafe.Add(ref searchSpace, intPtr + 3))) { ref T reference5 = ref value; val = default(T); if (val == null) { val = reference5; reference5 = ref val; } if (!reference5.Equals(Unsafe.Add(ref searchSpace, intPtr + 4))) { ref T reference6 = ref value; val = default(T); if (val == null) { val = reference6; reference6 = ref val; } if (!reference6.Equals(Unsafe.Add(ref searchSpace, intPtr + 5))) { ref T reference7 = ref value; val = default(T); if (val == null) { val = reference7; reference7 = ref val; } if (!reference7.Equals(Unsafe.Add(ref searchSpace, intPtr + 6))) { ref T reference8 = ref value; val = default(T); if (val == null) { val = reference8; reference8 = ref val; } if (reference8.Equals(Unsafe.Add(ref searchSpace, intPtr + 7))) { break; } intPtr += 8; continue; } return (int)(void*)(intPtr + 6); } return (int)(void*)(intPtr + 5); } return (int)(void*)(intPtr + 4); } goto IL_0361; } } else { if (length >= 4) { length -= 4; ref T reference9 = ref value; val = default(T); if (val == null) { val = reference9; reference9 = ref val; } if (reference9.Equals(Unsafe.Add(ref searchSpace, intPtr))) { goto IL_033d; } ref T reference10 = ref value; val = default(T); if (val == null) { val = reference10; reference10 = ref val; } if (reference10.Equals(Unsafe.Add(ref searchSpace, intPtr + 1))) { goto IL_0345; } ref T reference11 = ref value; val = default(T); if (val == null) { val = reference11; reference11 = ref val; } if (reference11.Equals(Unsafe.Add(ref searchSpace, intPtr + 2))) { goto IL_0353; } ref T reference12 = ref value; val = default(T); if (val == null) { val = reference12; reference12 = ref val; } if (reference12.Equals(Unsafe.Add(ref searchSpace, intPtr + 3))) { goto IL_0361; } intPtr += 4; } while (true) { if (length > 0) { ref T reference13 = ref value; val = default(T); if (val == null) { val = reference13; reference13 = ref val; } if (reference13.Equals(Unsafe.Add(ref searchSpace, intPtr))) { break; } intPtr += 1; length--; continue; } return -1; } } goto IL_033d; IL_033d: return (int)(void*)intPtr; IL_0353: return (int)(void*)(intPtr + 2); IL_0345: return (int)(void*)(intPtr + 1); IL_0361: return (int)(void*)(intPtr + 3); } return (int)(void*)(intPtr + 7); } public static int IndexOfAny(ref T searchSpace, T value0, T value1, int length) where T : IEquatable { int num = 0; while (true) { if (length - num >= 8) { T other = Unsafe.Add(ref searchSpace, num); if (!value0.Equals(other) && !value1.Equals(other)) { other = Unsafe.Add(ref searchSpace, num + 1); if (value0.Equals(other) || value1.Equals(other)) { goto IL_02cb; } other = Unsafe.Add(ref searchSpace, num + 2); if (value0.Equals(other) || value1.Equals(other)) { goto IL_02cf; } other = Unsafe.Add(ref searchSpace, num + 3); if (!value0.Equals(other) && !value1.Equals(other)) { other = Unsafe.Add(ref searchSpace, num + 4); if (!value0.Equals(other) && !value1.Equals(other)) { other = Unsafe.Add(ref searchSpace, num + 5); if (!value0.Equals(other) && !value1.Equals(other)) { other = Unsafe.Add(ref searchSpace, num + 6); if (!value0.Equals(other) && !value1.Equals(other)) { other = Unsafe.Add(ref searchSpace, num + 7); if (value0.Equals(other) || value1.Equals(other)) { break; } num += 8; continue; } return num + 6; } return num + 5; } return num + 4; } goto IL_02d3; } } else { if (length - num >= 4) { T other = Unsafe.Add(ref searchSpace, num); if (value0.Equals(other) || value1.Equals(other)) { goto IL_02c9; } other = Unsafe.Add(ref searchSpace, num + 1); if (value0.Equals(other) || value1.Equals(other)) { goto IL_02cb; } other = Unsafe.Add(ref searchSpace, num + 2); if (value0.Equals(other) || value1.Equals(other)) { goto IL_02cf; } other = Unsafe.Add(ref searchSpace, num + 3); if (value0.Equals(other) || value1.Equals(other)) { goto IL_02d3; } num += 4; } while (true) { if (num < length) { T other = Unsafe.Add(ref searchSpace, num); if (value0.Equals(other) || value1.Equals(other)) { break; } num++; continue; } return -1; } } goto IL_02c9; IL_02cf: return num + 2; IL_02cb: return num + 1; IL_02d3: return num + 3; IL_02c9: return num; } return num + 7; } public static int IndexOfAny(ref T searchSpace, T value0, T value1, T value2, int length) where T : IEquatable { int num = 0; while (true) { if (length - num >= 8) { T other = Unsafe.Add(ref searchSpace, num); if (!value0.Equals(other) && !value1.Equals(other) && !value2.Equals(other)) { other = Unsafe.Add(ref searchSpace, num + 1); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { goto IL_03c2; } other = Unsafe.Add(ref searchSpace, num + 2); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { goto IL_03c6; } other = Unsafe.Add(ref searchSpace, num + 3); if (!value0.Equals(other) && !value1.Equals(other) && !value2.Equals(other)) { other = Unsafe.Add(ref searchSpace, num + 4); if (!value0.Equals(other) && !value1.Equals(other) && !value2.Equals(other)) { other = Unsafe.Add(ref searchSpace, num + 5); if (!value0.Equals(other) && !value1.Equals(other) && !value2.Equals(other)) { other = Unsafe.Add(ref searchSpace, num + 6); if (!value0.Equals(other) && !value1.Equals(other) && !value2.Equals(other)) { other = Unsafe.Add(ref searchSpace, num + 7); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { break; } num += 8; continue; } return num + 6; } return num + 5; } return num + 4; } goto IL_03ca; } } else { if (length - num >= 4) { T other = Unsafe.Add(ref searchSpace, num); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { goto IL_03c0; } other = Unsafe.Add(ref searchSpace, num + 1); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { goto IL_03c2; } other = Unsafe.Add(ref searchSpace, num + 2); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { goto IL_03c6; } other = Unsafe.Add(ref searchSpace, num + 3); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { goto IL_03ca; } num += 4; } while (true) { if (num < length) { T other = Unsafe.Add(ref searchSpace, num); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { break; } num++; continue; } return -1; } } goto IL_03c0; IL_03c0: return num; IL_03c6: return num + 2; IL_03c2: return num + 1; IL_03ca: return num + 3; } return num + 7; } public static int IndexOfAny(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) where T : IEquatable { if (valueLength == 0) { return 0; } int num = -1; for (int i = 0; i < valueLength; i++) { int num2 = IndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength); if ((uint)num2 < (uint)num) { num = num2; searchSpaceLength = num2; if (num == 0) { break; } } } return num; } public static int LastIndexOf(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) where T : IEquatable { if (valueLength == 0) { return 0; } T value2 = value; ref T second = ref Unsafe.Add(ref value, 1); int num = valueLength - 1; int num2 = 0; while (true) { int num3 = searchSpaceLength - num2 - num; if (num3 <= 0) { break; } int num4 = LastIndexOf(ref searchSpace, value2, num3); if (num4 == -1) { break; } if (SequenceEqual(ref Unsafe.Add(ref searchSpace, num4 + 1), ref second, num)) { return num4; } num2 += num3 - num4; } return -1; } public static int LastIndexOf(ref T searchSpace, T value, int length) where T : IEquatable { while (true) { T val; if (length >= 8) { length -= 8; ref T reference = ref value; val = default(T); if (val == null) { val = reference; reference = ref val; } if (reference.Equals(Unsafe.Add(ref searchSpace, length + 7))) { break; } ref T reference2 = ref value; val = default(T); if (val == null) { val = reference2; reference2 = ref val; } if (reference2.Equals(Unsafe.Add(ref searchSpace, length + 6))) { return length + 6; } ref T reference3 = ref value; val = default(T); if (val == null) { val = reference3; reference3 = ref val; } if (reference3.Equals(Unsafe.Add(ref searchSpace, length + 5))) { return length + 5; } ref T reference4 = ref value; val = default(T); if (val == null) { val = reference4; reference4 = ref val; } if (reference4.Equals(Unsafe.Add(ref searchSpace, length + 4))) { return length + 4; } ref T reference5 = ref value; val = default(T); if (val == null) { val = reference5; reference5 = ref val; } if (reference5.Equals(Unsafe.Add(ref searchSpace, length + 3))) { goto IL_02fd; } ref T reference6 = ref value; val = default(T); if (val == null) { val = reference6; reference6 = ref val; } if (reference6.Equals(Unsafe.Add(ref searchSpace, length + 2))) { goto IL_02f9; } ref T reference7 = ref value; val = default(T); if (val == null) { val = reference7; reference7 = ref val; } if (reference7.Equals(Unsafe.Add(ref searchSpace, length + 1))) { goto IL_02f5; } ref T reference8 = ref value; val = default(T); if (val == null) { val = reference8; reference8 = ref val; } if (!reference8.Equals(Unsafe.Add(ref searchSpace, length))) { continue; } } else { if (length >= 4) { length -= 4; ref T reference9 = ref value; val = default(T); if (val == null) { val = reference9; reference9 = ref val; } if (reference9.Equals(Unsafe.Add(ref searchSpace, length + 3))) { goto IL_02fd; } ref T reference10 = ref value; val = default(T); if (val == null) { val = reference10; reference10 = ref val; } if (reference10.Equals(Unsafe.Add(ref searchSpace, length + 2))) { goto IL_02f9; } ref T reference11 = ref value; val = default(T); if (val == null) { val = reference11; reference11 = ref val; } if (reference11.Equals(Unsafe.Add(ref searchSpace, length + 1))) { goto IL_02f5; } ref T reference12 = ref value; val = default(T); if (val == null) { val = reference12; reference12 = ref val; } if (reference12.Equals(Unsafe.Add(ref searchSpace, length))) { goto IL_02f3; } } ref T reference13; do { if (length > 0) { length--; reference13 = ref value; val = default(T); if (val == null) { val = reference13; reference13 = ref val; } continue; } return -1; } while (!reference13.Equals(Unsafe.Add(ref searchSpace, length))); } goto IL_02f3; IL_02f5: return length + 1; IL_02f9: return length + 2; IL_02fd: return length + 3; IL_02f3: return length; } return length + 7; } public static int LastIndexOfAny(ref T searchSpace, T value0, T value1, int length) where T : IEquatable { while (true) { if (length >= 8) { length -= 8; T other = Unsafe.Add(ref searchSpace, length + 7); if (value0.Equals(other) || value1.Equals(other)) { break; } other = Unsafe.Add(ref searchSpace, length + 6); if (value0.Equals(other) || value1.Equals(other)) { return length + 6; } other = Unsafe.Add(ref searchSpace, length + 5); if (value0.Equals(other) || value1.Equals(other)) { return length + 5; } other = Unsafe.Add(ref searchSpace, length + 4); if (value0.Equals(other) || value1.Equals(other)) { return length + 4; } other = Unsafe.Add(ref searchSpace, length + 3); if (value0.Equals(other) || value1.Equals(other)) { goto IL_02cd; } other = Unsafe.Add(ref searchSpace, length + 2); if (value0.Equals(other) || value1.Equals(other)) { goto IL_02c9; } other = Unsafe.Add(ref searchSpace, length + 1); if (value0.Equals(other) || value1.Equals(other)) { goto IL_02c5; } other = Unsafe.Add(ref searchSpace, length); if (!value0.Equals(other) && !value1.Equals(other)) { continue; } } else { T other; if (length >= 4) { length -= 4; other = Unsafe.Add(ref searchSpace, length + 3); if (value0.Equals(other) || value1.Equals(other)) { goto IL_02cd; } other = Unsafe.Add(ref searchSpace, length + 2); if (value0.Equals(other) || value1.Equals(other)) { goto IL_02c9; } other = Unsafe.Add(ref searchSpace, length + 1); if (value0.Equals(other) || value1.Equals(other)) { goto IL_02c5; } other = Unsafe.Add(ref searchSpace, length); if (value0.Equals(other) || value1.Equals(other)) { goto IL_02c3; } } do { if (length > 0) { length--; other = Unsafe.Add(ref searchSpace, length); continue; } return -1; } while (!value0.Equals(other) && !value1.Equals(other)); } goto IL_02c3; IL_02c9: return length + 2; IL_02c5: return length + 1; IL_02c3: return length; IL_02cd: return length + 3; } return length + 7; } public static int LastIndexOfAny(ref T searchSpace, T value0, T value1, T value2, int length) where T : IEquatable { while (true) { if (length >= 8) { length -= 8; T other = Unsafe.Add(ref searchSpace, length + 7); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { break; } other = Unsafe.Add(ref searchSpace, length + 6); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { return length + 6; } other = Unsafe.Add(ref searchSpace, length + 5); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { return length + 5; } other = Unsafe.Add(ref searchSpace, length + 4); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { return length + 4; } other = Unsafe.Add(ref searchSpace, length + 3); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { goto IL_03da; } other = Unsafe.Add(ref searchSpace, length + 2); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { goto IL_03d5; } other = Unsafe.Add(ref searchSpace, length + 1); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { goto IL_03d0; } other = Unsafe.Add(ref searchSpace, length); if (!value0.Equals(other) && !value1.Equals(other) && !value2.Equals(other)) { continue; } } else { T other; if (length >= 4) { length -= 4; other = Unsafe.Add(ref searchSpace, length + 3); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { goto IL_03da; } other = Unsafe.Add(ref searchSpace, length + 2); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { goto IL_03d5; } other = Unsafe.Add(ref searchSpace, length + 1); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { goto IL_03d0; } other = Unsafe.Add(ref searchSpace, length); if (value0.Equals(other) || value1.Equals(other) || value2.Equals(other)) { goto IL_03cd; } } do { if (length > 0) { length--; other = Unsafe.Add(ref searchSpace, length); continue; } return -1; } while (!value0.Equals(other) && !value1.Equals(other) && !value2.Equals(other)); } goto IL_03cd; IL_03d0: return length + 1; IL_03d5: return length + 2; IL_03da: return length + 3; IL_03cd: return length; } return length + 7; } public static int LastIndexOfAny(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength) where T : IEquatable { if (valueLength == 0) { return 0; } int num = -1; for (int i = 0; i < valueLength; i++) { int num2 = LastIndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength); if (num2 > num) { num = num2; } } return num; } public static bool SequenceEqual(ref T first, ref T second, int length) where T : IEquatable { if (!Unsafe.AreSame(ref first, ref second)) { IntPtr intPtr = (IntPtr)0; while (true) { T val; if (length >= 8) { length -= 8; ref T reference = ref Unsafe.Add(ref first, intPtr); val = default(T); if (val == null) { val = reference; reference = ref val; } if (reference.Equals(Unsafe.Add(ref second, intPtr))) { ref T reference2 = ref Unsafe.Add(ref first, intPtr + 1); val = default(T); if (val == null) { val = reference2; reference2 = ref val; } if (reference2.Equals(Unsafe.Add(ref second, intPtr + 1))) { ref T reference3 = ref Unsafe.Add(ref first, intPtr + 2); val = default(T); if (val == null) { val = reference3; reference3 = ref val; } if (reference3.Equals(Unsafe.Add(ref second, intPtr + 2))) { ref T reference4 = ref Unsafe.Add(ref first, intPtr + 3); val = default(T); if (val == null) { val = reference4; reference4 = ref val; } if (reference4.Equals(Unsafe.Add(ref second, intPtr + 3))) { ref T reference5 = ref Unsafe.Add(ref first, intPtr + 4); val = default(T); if (val == null) { val = reference5; reference5 = ref val; } if (reference5.Equals(Unsafe.Add(ref second, intPtr + 4))) { ref T reference6 = ref Unsafe.Add(ref first, intPtr + 5); val = default(T); if (val == null) { val = reference6; reference6 = ref val; } if (reference6.Equals(Unsafe.Add(ref second, intPtr + 5))) { ref T reference7 = ref Unsafe.Add(ref first, intPtr + 6); val = default(T); if (val == null) { val = reference7; reference7 = ref val; } if (reference7.Equals(Unsafe.Add(ref second, intPtr + 6))) { ref T reference8 = ref Unsafe.Add(ref first, intPtr + 7); val = default(T); if (val == null) { val = reference8; reference8 = ref val; } if (reference8.Equals(Unsafe.Add(ref second, intPtr + 7))) { intPtr += 8; continue; } } } } } } } } } else { if (length < 4) { goto IL_03c0; } length -= 4; ref T reference9 = ref Unsafe.Add(ref first, intPtr); val = default(T); if (val == null) { val = reference9; reference9 = ref val; } if (reference9.Equals(Unsafe.Add(ref second, intPtr))) { ref T reference10 = ref Unsafe.Add(ref first, intPtr + 1); val = default(T); if (val == null) { val = reference10; reference10 = ref val; } if (reference10.Equals(Unsafe.Add(ref second, intPtr + 1))) { ref T reference11 = ref Unsafe.Add(ref first, intPtr + 2); val = default(T); if (val == null) { val = reference11; reference11 = ref val; } if (reference11.Equals(Unsafe.Add(ref second, intPtr + 2))) { ref T reference12 = ref Unsafe.Add(ref first, intPtr + 3); val = default(T); if (val == null) { val = reference12; reference12 = ref val; } if (reference12.Equals(Unsafe.Add(ref second, intPtr + 3))) { intPtr += 4; goto IL_03c0; } } } } } goto IL_03c6; IL_03c0: while (length > 0) { ref T reference13 = ref Unsafe.Add(ref first, intPtr); val = default(T); if (val == null) { val = reference13; reference13 = ref val; } if (reference13.Equals(Unsafe.Add(ref second, intPtr))) { intPtr += 1; length--; continue; } goto IL_03c6; } break; IL_03c6: return false; } } return true; } public static int SequenceCompareTo(ref T first, int firstLength, ref T second, int secondLength) where T : IComparable { int num = firstLength; if (num > secondLength) { num = secondLength; } for (int i = 0; i < num; i++) { ref T reference = ref Unsafe.Add(ref first, i); T val = default(T); if (val == null) { val = reference; reference = ref val; } int num2 = reference.CompareTo(Unsafe.Add(ref second, i)); if (num2 != 0) { return num2; } } return firstLength.CompareTo(secondLength); } } internal static class ThrowHelper { internal static void ThrowArgumentNullException(System.ExceptionArgument argument) { throw CreateArgumentNullException(argument); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateArgumentNullException(System.ExceptionArgument argument) { return new ArgumentNullException(argument.ToString()); } internal static void ThrowArrayTypeMismatchException() { throw CreateArrayTypeMismatchException(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateArrayTypeMismatchException() { return new ArrayTypeMismatchException(); } internal static void ThrowArgumentException_InvalidTypeWithPointersNotSupported(Type type) { throw CreateArgumentException_InvalidTypeWithPointersNotSupported(type); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateArgumentException_InvalidTypeWithPointersNotSupported(Type type) { return new ArgumentException(System.SR.Format(System.SR.Argument_InvalidTypeWithPointersNotSupported, type)); } internal static void ThrowArgumentException_DestinationTooShort() { throw CreateArgumentException_DestinationTooShort(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateArgumentException_DestinationTooShort() { return new ArgumentException(System.SR.Argument_DestinationTooShort); } internal static void ThrowIndexOutOfRangeException() { throw CreateIndexOutOfRangeException(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateIndexOutOfRangeException() { return new IndexOutOfRangeException(); } internal static void ThrowArgumentOutOfRangeException() { throw CreateArgumentOutOfRangeException(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateArgumentOutOfRangeException() { return new ArgumentOutOfRangeException(); } internal static void ThrowArgumentOutOfRangeException(System.ExceptionArgument argument) { throw CreateArgumentOutOfRangeException(argument); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateArgumentOutOfRangeException(System.ExceptionArgument argument) { return new ArgumentOutOfRangeException(argument.ToString()); } internal static void ThrowArgumentOutOfRangeException_PrecisionTooLarge() { throw CreateArgumentOutOfRangeException_PrecisionTooLarge(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateArgumentOutOfRangeException_PrecisionTooLarge() { return new ArgumentOutOfRangeException("precision", System.SR.Format(System.SR.Argument_PrecisionTooLarge, (byte)99)); } internal static void ThrowArgumentOutOfRangeException_SymbolDoesNotFit() { throw CreateArgumentOutOfRangeException_SymbolDoesNotFit(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateArgumentOutOfRangeException_SymbolDoesNotFit() { return new ArgumentOutOfRangeException("symbol", System.SR.Argument_BadFormatSpecifier); } internal static void ThrowInvalidOperationException() { throw CreateInvalidOperationException(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateInvalidOperationException() { return new InvalidOperationException(); } internal static void ThrowInvalidOperationException_OutstandingReferences() { throw CreateInvalidOperationException_OutstandingReferences(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateInvalidOperationException_OutstandingReferences() { return new InvalidOperationException(System.SR.OutstandingReferences); } internal static void ThrowInvalidOperationException_UnexpectedSegmentType() { throw CreateInvalidOperationException_UnexpectedSegmentType(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateInvalidOperationException_UnexpectedSegmentType() { return new InvalidOperationException(System.SR.UnexpectedSegmentType); } internal static void ThrowInvalidOperationException_EndPositionNotReached() { throw CreateInvalidOperationException_EndPositionNotReached(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateInvalidOperationException_EndPositionNotReached() { return new InvalidOperationException(System.SR.EndPositionNotReached); } internal static void ThrowArgumentOutOfRangeException_PositionOutOfRange() { throw CreateArgumentOutOfRangeException_PositionOutOfRange(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateArgumentOutOfRangeException_PositionOutOfRange() { return new ArgumentOutOfRangeException("position"); } internal static void ThrowArgumentOutOfRangeException_OffsetOutOfRange() { throw CreateArgumentOutOfRangeException_OffsetOutOfRange(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateArgumentOutOfRangeException_OffsetOutOfRange() { return new ArgumentOutOfRangeException("offset"); } internal static void ThrowObjectDisposedException_ArrayMemoryPoolBuffer() { throw CreateObjectDisposedException_ArrayMemoryPoolBuffer(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateObjectDisposedException_ArrayMemoryPoolBuffer() { return new ObjectDisposedException("ArrayMemoryPoolBuffer"); } internal static void ThrowFormatException_BadFormatSpecifier() { throw CreateFormatException_BadFormatSpecifier(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateFormatException_BadFormatSpecifier() { return new FormatException(System.SR.Argument_BadFormatSpecifier); } internal static void ThrowArgumentException_OverlapAlignmentMismatch() { throw CreateArgumentException_OverlapAlignmentMismatch(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateArgumentException_OverlapAlignmentMismatch() { return new ArgumentException(System.SR.Argument_OverlapAlignmentMismatch); } internal static void ThrowNotSupportedException() { throw CreateThrowNotSupportedException(); } [MethodImpl(MethodImplOptions.NoInlining)] private static Exception CreateThrowNotSupportedException() { return new NotSupportedException(); } public static bool TryFormatThrowFormatException(out int bytesWritten) { bytesWritten = 0; ThrowFormatException_BadFormatSpecifier(); return false; } public static bool TryParseThrowFormatException(out T value, out int bytesConsumed) { value = default(T); bytesConsumed = 0; ThrowFormatException_BadFormatSpecifier(); return false; } public static void ThrowArgumentValidationException(ReadOnlySequenceSegment startSegment, int startIndex, ReadOnlySequenceSegment endSegment) { throw CreateArgumentValidationException(startSegment, startIndex, endSegment); } private static Exception CreateArgumentValidationException(ReadOnlySequenceSegment startSegment, int startIndex, ReadOnlySequenceSegment endSegment) { if (startSegment == null) { return CreateArgumentNullException(System.ExceptionArgument.startSegment); } if (endSegment == null) { return CreateArgumentNullException(System.ExceptionArgument.endSegment); } if (startSegment != endSegment && startSegment.RunningIndex > endSegment.RunningIndex) { return CreateArgumentOutOfRangeException(System.ExceptionArgument.endSegment); } if ((uint)startSegment.Memory.Length < (uint)startIndex) { return CreateArgumentOutOfRangeException(System.ExceptionArgument.startIndex); } return CreateArgumentOutOfRangeException(System.ExceptionArgument.endIndex); } public static void ThrowArgumentValidationException(Array array, int start) { throw CreateArgumentValidationException(array, start); } private static Exception CreateArgumentValidationException(Array array, int start) { if (array == null) { return CreateArgumentNullException(System.ExceptionArgument.array); } if ((uint)start > (uint)array.Length) { return CreateArgumentOutOfRangeException(System.ExceptionArgument.start); } return CreateArgumentOutOfRangeException(System.ExceptionArgument.length); } public static void ThrowStartOrEndArgumentValidationException(long start) { throw CreateStartOrEndArgumentValidationException(start); } private static Exception CreateStartOrEndArgumentValidationException(long start) { if (start < 0) { return CreateArgumentOutOfRangeException(System.ExceptionArgument.start); } return CreateArgumentOutOfRangeException(System.ExceptionArgument.length); } } internal enum ExceptionArgument { length, start, minimumBufferSize, elementIndex, comparable, comparer, destination, offset, startSegment, endSegment, startIndex, endIndex, array, culture, manager } internal static class SR { private static readonly bool s_usingResourceKeys = AppContext.TryGetSwitch("System.Resources.UseSystemResourceKeys", out var isEnabled) && isEnabled; private static ResourceManager s_resourceManager; internal static ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new ResourceManager(typeof(FxResources.System.Memory.SR))); internal static string NotSupported_CannotCallEqualsOnSpan => GetResourceString("NotSupported_CannotCallEqualsOnSpan"); internal static string NotSupported_CannotCallGetHashCodeOnSpan => GetResourceString("NotSupported_CannotCallGetHashCodeOnSpan"); internal static string Argument_InvalidTypeWithPointersNotSupported => GetResourceString("Argument_InvalidTypeWithPointersNotSupported"); internal static string Argument_DestinationTooShort => GetResourceString("Argument_DestinationTooShort"); internal static string MemoryDisposed => GetResourceString("MemoryDisposed"); internal static string OutstandingReferences => GetResourceString("OutstandingReferences"); internal static string Argument_BadFormatSpecifier => GetResourceString("Argument_BadFormatSpecifier"); internal static string Argument_GWithPrecisionNotSupported => GetResourceString("Argument_GWithPrecisionNotSupported"); internal static string Argument_CannotParsePrecision => GetResourceString("Argument_CannotParsePrecision"); internal static string Argument_PrecisionTooLarge => GetResourceString("Argument_PrecisionTooLarge"); internal static string Argument_OverlapAlignmentMismatch => GetResourceString("Argument_OverlapAlignmentMismatch"); internal static string EndPositionNotReached => GetResourceString("EndPositionNotReached"); internal static string UnexpectedSegmentType => GetResourceString("UnexpectedSegmentType"); private static bool UsingResourceKeys() { return s_usingResourceKeys; } internal static string GetResourceString(string resourceKey) { if (UsingResourceKeys()) { return resourceKey; } string result = null; try { result = ResourceManager.GetString(resourceKey); } catch (MissingManifestResourceException) { } return result; } internal static string GetResourceString(string resourceKey, string defaultString) { string resourceString = GetResourceString(resourceKey); if (!(resourceKey == resourceString) && resourceString != null) { return resourceString; } return defaultString; } internal static string Format(string resourceFormat, object? p1) { if (UsingResourceKeys()) { return string.Join(", ", resourceFormat, p1); } return string.Format(resourceFormat, p1); } internal static string Format(string resourceFormat, object? p1, object? p2) { if (UsingResourceKeys()) { return string.Join(", ", resourceFormat, p1, p2); } return string.Format(resourceFormat, p1, p2); } internal static string Format(string resourceFormat, object? p1, object? p2, object? p3) { if (UsingResourceKeys()) { return string.Join(", ", resourceFormat, p1, p2, p3); } return string.Format(resourceFormat, p1, p2, p3); } internal static string Format(string resourceFormat, params object?[]? args) { if (args != null) { if (UsingResourceKeys()) { return resourceFormat + ", " + string.Join(", ", args); } return string.Format(resourceFormat, args); } return resourceFormat; } internal static string Format(IFormatProvider? provider, string resourceFormat, object? p1) { if (UsingResourceKeys()) { return string.Join(", ", resourceFormat, p1); } return string.Format(provider, resourceFormat, p1); } internal static string Format(IFormatProvider? provider, string resourceFormat, object? p1, object? p2) { if (UsingResourceKeys()) { return string.Join(", ", resourceFormat, p1, p2); } return string.Format(provider, resourceFormat, p1, p2); } internal static string Format(IFormatProvider? provider, string resourceFormat, object? p1, object? p2, object? p3) { if (UsingResourceKeys()) { return string.Join(", ", resourceFormat, p1, p2, p3); } return string.Format(provider, resourceFormat, p1, p2, p3); } internal static string Format(IFormatProvider? provider, string resourceFormat, params object?[]? args) { if (args != null) { if (UsingResourceKeys()) { return resourceFormat + ", " + string.Join(", ", args); } return string.Format(provider, resourceFormat, args); } return resourceFormat; } } } namespace System.Numerics.Hashing { internal static class HashHelpers { public static readonly int RandomSeed = Guid.NewGuid().GetHashCode(); public static int Combine(int h1, int h2) { uint num = (uint)(h1 << 5) | ((uint)h1 >> 27); return ((int)num + h1) ^ h2; } } } namespace System.Runtime.InteropServices { internal static class MemoryMarshal { public static bool TryGetArray(ReadOnlyMemory memory, out ArraySegment segment) { int start; int length; object objectStartLength = memory.GetObjectStartLength(out start, out length); if (start < 0) { if (((MemoryManager)objectStartLength).TryGetArray(out var segment2)) { segment = new ArraySegment(segment2.Array, segment2.Offset + (start & 0x7FFFFFFF), length); return true; } } else if (objectStartLength is T[] array) { segment = new ArraySegment(array, start, length & 0x7FFFFFFF); return true; } if ((length & 0x7FFFFFFF) == 0) { segment = new ArraySegment(System.SpanHelpers.PerTypeValues.EmptyArray); return true; } segment = default(ArraySegment); return false; } public static bool TryGetMemoryManager(ReadOnlyMemory memory, out TManager manager) where TManager : MemoryManager { int start; int length; TManager val = (manager = memory.GetObjectStartLength(out start, out length) as TManager); return manager != null; } public static bool TryGetMemoryManager(ReadOnlyMemory memory, out TManager manager, out int start, out int length) where TManager : MemoryManager { TManager val = (manager = memory.GetObjectStartLength(out start, out length) as TManager); start &= int.MaxValue; if (manager == null) { start = 0; length = 0; return false; } return true; } public static IEnumerable ToEnumerable(ReadOnlyMemory memory) { for (int i = 0; i < memory.Length; i++) { yield return memory.Span[i]; } } public static bool TryGetString(ReadOnlyMemory memory, out string text, out int start, out int length) { if (memory.GetObjectStartLength(out var start2, out var length2) is string text2) { text = text2; start = start2; length = length2; return true; } text = null; start = 0; length = 0; return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Read(ReadOnlySpan source) where T : struct { if (System.SpanHelpers.IsReferenceOrContainsReferences()) { System.ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); } if (Unsafe.SizeOf() > source.Length) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.length); } return Unsafe.ReadUnaligned(ref GetReference(source)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryRead(ReadOnlySpan source, out T value) where T : struct { if (System.SpanHelpers.IsReferenceOrContainsReferences()) { System.ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); } if (Unsafe.SizeOf() > (uint)source.Length) { value = default(T); return false; } value = Unsafe.ReadUnaligned(ref GetReference(source)); return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Write(Span destination, ref T value) where T : struct { if (System.SpanHelpers.IsReferenceOrContainsReferences()) { System.ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); } if ((uint)Unsafe.SizeOf() > (uint)destination.Length) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.length); } Unsafe.WriteUnaligned(ref GetReference(destination), value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryWrite(Span destination, ref T value) where T : struct { if (System.SpanHelpers.IsReferenceOrContainsReferences()) { System.ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); } if (Unsafe.SizeOf() > (uint)destination.Length) { return false; } Unsafe.WriteUnaligned(ref GetReference(destination), value); return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Memory CreateFromPinnedArray(T[] array, int start, int length) { if (array == null) { if (start != 0 || length != 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(); } return default(Memory); } if (default(T) == null && array.GetType() != typeof(T[])) { System.ThrowHelper.ThrowArrayTypeMismatchException(); } if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) { System.ThrowHelper.ThrowArgumentOutOfRangeException(); } return new Memory((object)array, start, length | int.MinValue); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsBytes(Span span) where T : struct { if (System.SpanHelpers.IsReferenceOrContainsReferences()) { System.ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); } int length = checked(span.Length * Unsafe.SizeOf()); return new Span(Unsafe.As>(span.Pinnable), span.ByteOffset, length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan AsBytes(ReadOnlySpan span) where T : struct { if (System.SpanHelpers.IsReferenceOrContainsReferences()) { System.ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(T)); } int length = checked(span.Length * Unsafe.SizeOf()); return new ReadOnlySpan(Unsafe.As>(span.Pinnable), span.ByteOffset, length); } public static Memory AsMemory(ReadOnlyMemory memory) { return Unsafe.As, Memory>(ref memory); } public unsafe static ref T GetReference(Span span) { if (span.Pinnable == null) { return ref Unsafe.AsRef(span.ByteOffset.ToPointer()); } return ref Unsafe.AddByteOffset(ref span.Pinnable.Data, span.ByteOffset); } public unsafe static ref T GetReference(ReadOnlySpan span) { if (span.Pinnable == null) { return ref Unsafe.AsRef(span.ByteOffset.ToPointer()); } return ref Unsafe.AddByteOffset(ref span.Pinnable.Data, span.ByteOffset); } public static Span Cast(Span span) where TFrom : struct where TTo : struct { if (System.SpanHelpers.IsReferenceOrContainsReferences()) { System.ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TFrom)); } if (System.SpanHelpers.IsReferenceOrContainsReferences()) { System.ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TTo)); } checked { int length = (int)unchecked(checked(unchecked((long)span.Length) * unchecked((long)Unsafe.SizeOf())) / Unsafe.SizeOf()); return new Span(Unsafe.As>(span.Pinnable), span.ByteOffset, length); } } public static ReadOnlySpan Cast(ReadOnlySpan span) where TFrom : struct where TTo : struct { if (System.SpanHelpers.IsReferenceOrContainsReferences()) { System.ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TFrom)); } if (System.SpanHelpers.IsReferenceOrContainsReferences()) { System.ThrowHelper.ThrowArgumentException_InvalidTypeWithPointersNotSupported(typeof(TTo)); } checked { int length = (int)unchecked(checked(unchecked((long)span.Length) * unchecked((long)Unsafe.SizeOf())) / Unsafe.SizeOf()); return new ReadOnlySpan(Unsafe.As>(span.Pinnable), span.ByteOffset, length); } } } internal static class SequenceMarshal { public static bool TryGetReadOnlySequenceSegment(ReadOnlySequence sequence, out ReadOnlySequenceSegment startSegment, out int startIndex, out ReadOnlySequenceSegment endSegment, out int endIndex) { return sequence.TryGetReadOnlySequenceSegment(out startSegment, out startIndex, out endSegment, out endIndex); } public static bool TryGetArray(ReadOnlySequence sequence, out ArraySegment segment) { return sequence.TryGetArray(out segment); } public static bool TryGetReadOnlyMemory(ReadOnlySequence sequence, out ReadOnlyMemory memory) { if (!sequence.IsSingleSegment) { memory = default(ReadOnlyMemory); return false; } memory = sequence.First; return true; } internal static bool TryGetString(ReadOnlySequence sequence, out string text, out int start, out int length) { return sequence.TryGetString(out text, out start, out length); } } } namespace System.Buffers { internal sealed class ArrayMemoryPool : MemoryPool { private sealed class ArrayMemoryPoolBuffer : IMemoryOwner, IDisposable { private T[] _array; public Memory Memory { get { T[] array = _array; if (array == null) { System.ThrowHelper.ThrowObjectDisposedException_ArrayMemoryPoolBuffer(); } return new Memory(array); } } public ArrayMemoryPoolBuffer(int size) { _array = ArrayPool.Shared.Rent(size); } public void Dispose() { T[] array = _array; if (array != null) { _array = null; ArrayPool.Shared.Return(array); } } } private const int s_maxBufferSize = int.MaxValue; public sealed override int MaxBufferSize => int.MaxValue; public sealed override IMemoryOwner Rent(int minimumBufferSize = -1) { if (minimumBufferSize == -1) { minimumBufferSize = 1 + 4095 / Unsafe.SizeOf(); } else if ((uint)minimumBufferSize > 2147483647u) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.minimumBufferSize); } return new ArrayMemoryPoolBuffer(minimumBufferSize); } protected sealed override void Dispose(bool disposing) { } } internal static class BuffersExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static SequencePosition? PositionOf([In][<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] this ref ReadOnlySequence source, T value) where T : IEquatable { if (source.IsSingleSegment) { int num = source.First.Span.IndexOf(value); if (num != -1) { return source.GetPosition(num); } return null; } return PositionOfMultiSegment(ref source, value); } private static SequencePosition? PositionOfMultiSegment([In][<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] ref ReadOnlySequence source, T value) where T : IEquatable { SequencePosition position = source.Start; SequencePosition origin = position; ReadOnlyMemory memory; while (source.TryGet(ref position, out memory)) { int num = memory.Span.IndexOf(value); if (num != -1) { return source.GetPosition(num, origin); } if (position.GetObject() == null) { break; } origin = position; } return null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CopyTo([In][<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] this ref ReadOnlySequence source, Span destination) { if (source.Length > destination.Length) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.destination); } if (source.IsSingleSegment) { source.First.Span.CopyTo(destination); } else { CopyToMultiSegment(ref source, destination); } } private static void CopyToMultiSegment([In][<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] ref ReadOnlySequence sequence, Span destination) { SequencePosition position = sequence.Start; ReadOnlyMemory memory; while (sequence.TryGet(ref position, out memory)) { ReadOnlySpan span = memory.Span; span.CopyTo(destination); if (position.GetObject() != null) { destination = destination.Slice(span.Length); continue; } break; } } public static T[] ToArray([In][<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] this ref ReadOnlySequence sequence) { T[] array = new T[sequence.Length]; CopyTo(ref sequence, array); return array; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Write(this IBufferWriter writer, ReadOnlySpan value) { Span span = writer.GetSpan(); if (value.Length <= span.Length) { value.CopyTo(span); writer.Advance(value.Length); } else { WriteMultiSegment(writer, ref value, span); } } private static void WriteMultiSegment(IBufferWriter writer, [In][<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] ref ReadOnlySpan source, Span destination) { ReadOnlySpan readOnlySpan = source; while (true) { int num = Math.Min(destination.Length, readOnlySpan.Length); readOnlySpan.Slice(0, num).CopyTo(destination); writer.Advance(num); readOnlySpan = readOnlySpan.Slice(num); if (readOnlySpan.Length > 0) { destination = writer.GetSpan(readOnlySpan.Length); continue; } break; } } } internal interface IBufferWriter { void Advance(int count); Memory GetMemory(int sizeHint = 0); Span GetSpan(int sizeHint = 0); } internal interface IMemoryOwner : IDisposable { Memory Memory { get; } } internal interface IPinnable { MemoryHandle Pin(int elementIndex); void Unpin(); } internal struct MemoryHandle : IDisposable { private unsafe void* _pointer; private GCHandle _handle; private IPinnable _pinnable; [CLSCompliant(false)] public unsafe void* Pointer => _pointer; [CLSCompliant(false)] public unsafe MemoryHandle(void* pointer, GCHandle handle = default(GCHandle), IPinnable pinnable = null) { _pointer = pointer; _handle = handle; _pinnable = pinnable; } public unsafe void Dispose() { if (_handle.IsAllocated) { _handle.Free(); } if (_pinnable != null) { _pinnable.Unpin(); _pinnable = null; } _pointer = null; } } internal abstract class MemoryManager : IMemoryOwner, IDisposable, IPinnable { public virtual Memory Memory => new Memory(this, GetSpan().Length); public abstract Span GetSpan(); public abstract MemoryHandle Pin(int elementIndex = 0); public abstract void Unpin(); [MethodImpl(MethodImplOptions.AggressiveInlining)] protected Memory CreateMemory(int length) { return new Memory(this, length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected Memory CreateMemory(int start, int length) { return new Memory(this, start, length); } protected internal virtual bool TryGetArray(out ArraySegment segment) { segment = default(ArraySegment); return false; } void IDisposable.Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected abstract void Dispose(bool disposing); } internal abstract class MemoryPool : IDisposable { private static readonly MemoryPool s_shared = new ArrayMemoryPool(); public static MemoryPool Shared => s_shared; public abstract int MaxBufferSize { get; } public abstract IMemoryOwner Rent(int minBufferSize = -1); public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected abstract void Dispose(bool disposing); } internal enum OperationStatus { Done, DestinationTooSmall, NeedMoreData, InvalidData } [<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] [DebuggerTypeProxy(typeof(ReadOnlySequenceDebugView<>))] [DebuggerDisplay("{ToString(),raw}")] internal struct ReadOnlySequence { public struct Enumerator { private readonly ReadOnlySequence _sequence; private SequencePosition _next; private ReadOnlyMemory _currentMemory; public ReadOnlyMemory Current => _currentMemory; public Enumerator([In][<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] ref ReadOnlySequence sequence) { _currentMemory = default(ReadOnlyMemory); _next = sequence.Start; _sequence = sequence; } public bool MoveNext() { if (_next.GetObject() == null) { return false; } return _sequence.TryGet(ref _next, out _currentMemory); } } private enum SequenceType { MultiSegment, Array, MemoryManager, String, Empty } private readonly SequencePosition _sequenceStart; private readonly SequencePosition _sequenceEnd; public static readonly ReadOnlySequence Empty = new ReadOnlySequence(System.SpanHelpers.PerTypeValues.EmptyArray); public long Length => GetLength(); public bool IsEmpty => Length == 0; public bool IsSingleSegment { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _sequenceStart.GetObject() == _sequenceEnd.GetObject(); } } public ReadOnlyMemory First => GetFirstBuffer(); public SequencePosition Start => _sequenceStart; public SequencePosition End => _sequenceEnd; [MethodImpl(MethodImplOptions.AggressiveInlining)] private ReadOnlySequence(object startSegment, int startIndexAndFlags, object endSegment, int endIndexAndFlags) { _sequenceStart = new SequencePosition(startSegment, startIndexAndFlags); _sequenceEnd = new SequencePosition(endSegment, endIndexAndFlags); } public ReadOnlySequence(ReadOnlySequenceSegment startSegment, int startIndex, ReadOnlySequenceSegment endSegment, int endIndex) { if (startSegment == null || endSegment == null || (startSegment != endSegment && startSegment.RunningIndex > endSegment.RunningIndex) || (uint)startSegment.Memory.Length < (uint)startIndex || (uint)endSegment.Memory.Length < (uint)endIndex || (startSegment == endSegment && endIndex < startIndex)) { System.ThrowHelper.ThrowArgumentValidationException(startSegment, startIndex, endSegment); } _sequenceStart = new SequencePosition(startSegment, ReadOnlySequence.SegmentToSequenceStart(startIndex)); _sequenceEnd = new SequencePosition(endSegment, ReadOnlySequence.SegmentToSequenceEnd(endIndex)); } public ReadOnlySequence(T[] array) { if (array == null) { System.ThrowHelper.ThrowArgumentNullException(System.ExceptionArgument.array); } _sequenceStart = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceStart(0)); _sequenceEnd = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceEnd(array.Length)); } public ReadOnlySequence(T[] array, int start, int length) { if (array == null || (uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) { System.ThrowHelper.ThrowArgumentValidationException(array, start); } _sequenceStart = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceStart(start)); _sequenceEnd = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceEnd(start + length)); } public ReadOnlySequence(ReadOnlyMemory memory) { ArraySegment segment; if (MemoryMarshal.TryGetMemoryManager>(memory, out var manager, out var start, out var length)) { _sequenceStart = new SequencePosition(manager, ReadOnlySequence.MemoryManagerToSequenceStart(start)); _sequenceEnd = new SequencePosition(manager, ReadOnlySequence.MemoryManagerToSequenceEnd(start + length)); } else if (MemoryMarshal.TryGetArray(memory, out segment)) { T[] array = segment.Array; int offset = segment.Offset; _sequenceStart = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceStart(offset)); _sequenceEnd = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceEnd(offset + segment.Count)); } else if (typeof(T) == typeof(char)) { if (!MemoryMarshal.TryGetString((ReadOnlyMemory)(object)memory, out var text, out var start2, out length)) { System.ThrowHelper.ThrowInvalidOperationException(); } _sequenceStart = new SequencePosition(text, ReadOnlySequence.StringToSequenceStart(start2)); _sequenceEnd = new SequencePosition(text, ReadOnlySequence.StringToSequenceEnd(start2 + length)); } else { System.ThrowHelper.ThrowInvalidOperationException(); _sequenceStart = default(SequencePosition); _sequenceEnd = default(SequencePosition); } } public ReadOnlySequence Slice(long start, long length) { if (start < 0 || length < 0) { System.ThrowHelper.ThrowStartOrEndArgumentValidationException(start); } int index = GetIndex(ref _sequenceStart); int index2 = GetIndex(ref _sequenceEnd); object @object = _sequenceStart.GetObject(); object object2 = _sequenceEnd.GetObject(); SequencePosition start2; SequencePosition end; if (@object != object2) { ReadOnlySequenceSegment readOnlySequenceSegment = (ReadOnlySequenceSegment)@object; int num = readOnlySequenceSegment.Memory.Length - index; if (num > start) { index += (int)start; start2 = new SequencePosition(@object, index); end = GetEndPosition(readOnlySequenceSegment, @object, index, object2, index2, length); } else { if (num < 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange(); } start2 = SeekMultiSegment(readOnlySequenceSegment.Next, object2, index2, start - num, System.ExceptionArgument.start); int index3 = GetIndex(ref start2); object object3 = start2.GetObject(); if (object3 != object2) { end = GetEndPosition((ReadOnlySequenceSegment)object3, object3, index3, object2, index2, length); } else { if (index2 - index3 < length) { System.ThrowHelper.ThrowStartOrEndArgumentValidationException(0L); } end = new SequencePosition(object3, index3 + (int)length); } } } else { if (index2 - index < start) { System.ThrowHelper.ThrowStartOrEndArgumentValidationException(-1L); } index += (int)start; start2 = new SequencePosition(@object, index); if (index2 - index < length) { System.ThrowHelper.ThrowStartOrEndArgumentValidationException(0L); } end = new SequencePosition(@object, index + (int)length); } return SliceImpl(ref start2, ref end); } public ReadOnlySequence Slice(long start, SequencePosition end) { if (start < 0) { System.ThrowHelper.ThrowStartOrEndArgumentValidationException(start); } uint index = (uint)GetIndex(ref end); object @object = end.GetObject(); uint index2 = (uint)GetIndex(ref _sequenceStart); object object2 = _sequenceStart.GetObject(); uint index3 = (uint)GetIndex(ref _sequenceEnd); object object3 = _sequenceEnd.GetObject(); if (object2 == object3) { if (!InRange(index, index2, index3)) { System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange(); } if (index - index2 < start) { System.ThrowHelper.ThrowStartOrEndArgumentValidationException(-1L); } } else { ReadOnlySequenceSegment readOnlySequenceSegment = (ReadOnlySequenceSegment)object2; ulong num = (ulong)(readOnlySequenceSegment.RunningIndex + index2); ulong num2 = (ulong)(((ReadOnlySequenceSegment)@object).RunningIndex + index); if (!InRange(num2, num, (ulong)(((ReadOnlySequenceSegment)object3).RunningIndex + index3))) { System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange(); } if ((ulong)((long)num + start) > num2) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.start); } int num3 = readOnlySequenceSegment.Memory.Length - (int)index2; if (num3 <= start) { if (num3 < 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange(); } SequencePosition start2 = SeekMultiSegment(readOnlySequenceSegment.Next, @object, (int)index, start - num3, System.ExceptionArgument.start); return SliceImpl(ref start2, ref end); } } SequencePosition start3 = new SequencePosition(object2, (int)index2 + (int)start); return SliceImpl(ref start3, ref end); } public ReadOnlySequence Slice(SequencePosition start, long length) { uint index = (uint)GetIndex(ref start); object @object = start.GetObject(); uint index2 = (uint)GetIndex(ref _sequenceStart); object object2 = _sequenceStart.GetObject(); uint index3 = (uint)GetIndex(ref _sequenceEnd); object object3 = _sequenceEnd.GetObject(); if (object2 == object3) { if (!InRange(index, index2, index3)) { System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange(); } if (length < 0) { System.ThrowHelper.ThrowStartOrEndArgumentValidationException(0L); } if (index3 - index < length) { System.ThrowHelper.ThrowStartOrEndArgumentValidationException(0L); } } else { ReadOnlySequenceSegment readOnlySequenceSegment = (ReadOnlySequenceSegment)@object; ulong num = (ulong)(readOnlySequenceSegment.RunningIndex + index); ulong start2 = (ulong)(((ReadOnlySequenceSegment)object2).RunningIndex + index2); ulong num2 = (ulong)(((ReadOnlySequenceSegment)object3).RunningIndex + index3); if (!InRange(num, start2, num2)) { System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange(); } if (length < 0) { System.ThrowHelper.ThrowStartOrEndArgumentValidationException(0L); } if ((ulong)((long)num + length) > num2) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.length); } int num3 = readOnlySequenceSegment.Memory.Length - (int)index; if (num3 < length) { if (num3 < 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange(); } SequencePosition end = SeekMultiSegment(readOnlySequenceSegment.Next, object3, (int)index3, length - num3, System.ExceptionArgument.length); return SliceImpl(ref start, ref end); } } SequencePosition end2 = new SequencePosition(@object, (int)index + (int)length); return SliceImpl(ref start, ref end2); } public ReadOnlySequence Slice(int start, int length) { return Slice((long)start, (long)length); } public ReadOnlySequence Slice(int start, SequencePosition end) { return Slice((long)start, end); } public ReadOnlySequence Slice(SequencePosition start, int length) { return Slice(start, (long)length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySequence Slice(SequencePosition start, SequencePosition end) { BoundsCheck((uint)GetIndex(ref start), start.GetObject(), (uint)GetIndex(ref end), end.GetObject()); return SliceImpl(ref start, ref end); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySequence Slice(SequencePosition start) { BoundsCheck(ref start); return SliceImpl(ref start, ref _sequenceEnd); } public ReadOnlySequence Slice(long start) { if (start < 0) { System.ThrowHelper.ThrowStartOrEndArgumentValidationException(start); } if (start == 0L) { return this; } SequencePosition start2 = Seek(ref _sequenceStart, ref _sequenceEnd, start, System.ExceptionArgument.start); return SliceImpl(ref start2, ref _sequenceEnd); } public override string ToString() { if (typeof(T) == typeof(char)) { ReadOnlySequence source = this; ReadOnlySequence sequence = Unsafe.As, ReadOnlySequence>(ref source); if (SequenceMarshal.TryGetString(sequence, out var text, out var start, out var length)) { return text.Substring(start, length); } if (Length < int.MaxValue) { return new string(BuffersExtensions.ToArray(ref sequence)); } } return $"System.Buffers.ReadOnlySequence<{typeof(T).Name}>[{Length}]"; } public Enumerator GetEnumerator() { return new Enumerator(ref this); } public SequencePosition GetPosition(long offset) { return GetPosition(offset, _sequenceStart); } public SequencePosition GetPosition(long offset, SequencePosition origin) { if (offset < 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException_OffsetOutOfRange(); } return Seek(ref origin, ref _sequenceEnd, offset, System.ExceptionArgument.offset); } public bool TryGet(ref SequencePosition position, out ReadOnlyMemory memory, bool advance = true) { SequencePosition next; bool result = TryGetBuffer(ref position, out memory, out next); if (advance) { position = next; } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool TryGetBuffer([In][<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] ref SequencePosition position, out ReadOnlyMemory memory, out SequencePosition next) { object @object = position.GetObject(); next = default(SequencePosition); if (@object == null) { memory = default(ReadOnlyMemory); return false; } SequenceType sequenceType = GetSequenceType(); object object2 = _sequenceEnd.GetObject(); int index = GetIndex(ref position); int index2 = GetIndex(ref _sequenceEnd); if (sequenceType == SequenceType.MultiSegment) { ReadOnlySequenceSegment readOnlySequenceSegment = (ReadOnlySequenceSegment)@object; if (readOnlySequenceSegment != object2) { ReadOnlySequenceSegment next2 = readOnlySequenceSegment.Next; if (next2 == null) { System.ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); } next = new SequencePosition(next2, 0); memory = readOnlySequenceSegment.Memory.Slice(index); } else { memory = readOnlySequenceSegment.Memory.Slice(index, index2 - index); } } else { if (@object != object2) { System.ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); } if (sequenceType == SequenceType.Array) { memory = new ReadOnlyMemory((T[])@object, index, index2 - index); } else if (typeof(T) == typeof(char) && sequenceType == SequenceType.String) { memory = (ReadOnlyMemory)(object)MemoryExtensions.AsMemory((string)@object, index, index2 - index); } else { memory = ((MemoryManager)@object).Memory.Slice(index, index2 - index); } } return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private ReadOnlyMemory GetFirstBuffer() { object @object = _sequenceStart.GetObject(); if (@object == null) { return default(ReadOnlyMemory); } int integer = _sequenceStart.GetInteger(); int integer2 = _sequenceEnd.GetInteger(); bool flag = @object != _sequenceEnd.GetObject(); if (integer >= 0) { if (integer2 >= 0) { ReadOnlyMemory memory = ((ReadOnlySequenceSegment)@object).Memory; if (flag) { return memory.Slice(integer); } return memory.Slice(integer, integer2 - integer); } if (flag) { System.ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); } return new ReadOnlyMemory((T[])@object, integer, (integer2 & 0x7FFFFFFF) - integer); } if (flag) { System.ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached(); } if (typeof(T) == typeof(char) && integer2 < 0) { return (ReadOnlyMemory)(object)MemoryExtensions.AsMemory((string)@object, integer & 0x7FFFFFFF, integer2 - integer); } integer &= 0x7FFFFFFF; return ((MemoryManager)@object).Memory.Slice(integer, integer2 - integer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private SequencePosition Seek([In][<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] ref SequencePosition start, [In][<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] ref SequencePosition end, long offset, System.ExceptionArgument argument) { int index = GetIndex(ref start); int index2 = GetIndex(ref end); object @object = start.GetObject(); object object2 = end.GetObject(); if (@object != object2) { ReadOnlySequenceSegment readOnlySequenceSegment = (ReadOnlySequenceSegment)@object; int num = readOnlySequenceSegment.Memory.Length - index; if (num <= offset) { if (num < 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange(); } return SeekMultiSegment(readOnlySequenceSegment.Next, object2, index2, offset - num, argument); } } else if (index2 - index < offset) { System.ThrowHelper.ThrowArgumentOutOfRangeException(argument); } return new SequencePosition(@object, index + (int)offset); } [MethodImpl(MethodImplOptions.NoInlining)] private static SequencePosition SeekMultiSegment(ReadOnlySequenceSegment currentSegment, object endObject, int endIndex, long offset, System.ExceptionArgument argument) { while (true) { if (currentSegment != null && currentSegment != endObject) { int length = currentSegment.Memory.Length; if (length > offset) { break; } offset -= length; currentSegment = currentSegment.Next; continue; } if (currentSegment == null || endIndex < offset) { System.ThrowHelper.ThrowArgumentOutOfRangeException(argument); } break; } return new SequencePosition(currentSegment, (int)offset); } private void BoundsCheck([In][<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] ref SequencePosition position) { uint index = (uint)GetIndex(ref position); uint index2 = (uint)GetIndex(ref _sequenceStart); uint index3 = (uint)GetIndex(ref _sequenceEnd); object @object = _sequenceStart.GetObject(); object object2 = _sequenceEnd.GetObject(); if (@object == object2) { if (!InRange(index, index2, index3)) { System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange(); } return; } ulong start = (ulong)(((ReadOnlySequenceSegment)@object).RunningIndex + index2); if (!InRange((ulong)(((ReadOnlySequenceSegment)position.GetObject()).RunningIndex + index), start, (ulong)(((ReadOnlySequenceSegment)object2).RunningIndex + index3))) { System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange(); } } private void BoundsCheck(uint sliceStartIndex, object sliceStartObject, uint sliceEndIndex, object sliceEndObject) { uint index = (uint)GetIndex(ref _sequenceStart); uint index2 = (uint)GetIndex(ref _sequenceEnd); object @object = _sequenceStart.GetObject(); object object2 = _sequenceEnd.GetObject(); if (@object == object2) { if (sliceStartObject != sliceEndObject || sliceStartObject != @object || sliceStartIndex > sliceEndIndex || sliceStartIndex < index || sliceEndIndex > index2) { System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange(); } return; } ulong num = (ulong)(((ReadOnlySequenceSegment)sliceStartObject).RunningIndex + sliceStartIndex); ulong num2 = (ulong)(((ReadOnlySequenceSegment)sliceEndObject).RunningIndex + sliceEndIndex); if (num > num2) { System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange(); } if (num < (ulong)(((ReadOnlySequenceSegment)@object).RunningIndex + index) || num2 > (ulong)(((ReadOnlySequenceSegment)object2).RunningIndex + index2)) { System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange(); } } private static SequencePosition GetEndPosition(ReadOnlySequenceSegment startSegment, object startObject, int startIndex, object endObject, int endIndex, long length) { int num = startSegment.Memory.Length - startIndex; if (num > length) { return new SequencePosition(startObject, startIndex + (int)length); } if (num < 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException_PositionOutOfRange(); } return SeekMultiSegment(startSegment.Next, endObject, endIndex, length - num, System.ExceptionArgument.length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private SequenceType GetSequenceType() { return (SequenceType)(-(2 * (_sequenceStart.GetInteger() >> 31) + (_sequenceEnd.GetInteger() >> 31))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetIndex([In][<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] ref SequencePosition position) { return position.GetInteger() & 0x7FFFFFFF; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private ReadOnlySequence SliceImpl([In][<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] ref SequencePosition start, [In][<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] ref SequencePosition end) { return new ReadOnlySequence(start.GetObject(), GetIndex(ref start) | (_sequenceStart.GetInteger() & int.MinValue), end.GetObject(), GetIndex(ref end) | (_sequenceEnd.GetInteger() & int.MinValue)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private long GetLength() { int index = GetIndex(ref _sequenceStart); int index2 = GetIndex(ref _sequenceEnd); object @object = _sequenceStart.GetObject(); object object2 = _sequenceEnd.GetObject(); if (@object != object2) { ReadOnlySequenceSegment readOnlySequenceSegment = (ReadOnlySequenceSegment)@object; ReadOnlySequenceSegment readOnlySequenceSegment2 = (ReadOnlySequenceSegment)object2; return readOnlySequenceSegment2.RunningIndex + index2 - (readOnlySequenceSegment.RunningIndex + index); } return index2 - index; } internal bool TryGetReadOnlySequenceSegment(out ReadOnlySequenceSegment startSegment, out int startIndex, out ReadOnlySequenceSegment endSegment, out int endIndex) { object @object = _sequenceStart.GetObject(); if (@object == null || GetSequenceType() != 0) { startSegment = null; startIndex = 0; endSegment = null; endIndex = 0; return false; } startSegment = (ReadOnlySequenceSegment)@object; startIndex = GetIndex(ref _sequenceStart); endSegment = (ReadOnlySequenceSegment)_sequenceEnd.GetObject(); endIndex = GetIndex(ref _sequenceEnd); return true; } internal bool TryGetArray(out ArraySegment segment) { if (GetSequenceType() != SequenceType.Array) { segment = default(ArraySegment); return false; } int index = GetIndex(ref _sequenceStart); segment = new ArraySegment((T[])_sequenceStart.GetObject(), index, GetIndex(ref _sequenceEnd) - index); return true; } internal bool TryGetString(out string text, out int start, out int length) { if (typeof(T) != typeof(char) || GetSequenceType() != SequenceType.String) { start = 0; length = 0; text = null; return false; } start = GetIndex(ref _sequenceStart); length = GetIndex(ref _sequenceEnd) - start; text = (string)_sequenceStart.GetObject(); return true; } private static bool InRange(uint value, uint start, uint end) { return value - start <= end - start; } private static bool InRange(ulong value, ulong start, ulong end) { return value - start <= end - start; } } internal static class ReadOnlySequence { public const int FlagBitMask = int.MinValue; public const int IndexBitMask = int.MaxValue; public const int SegmentStartMask = 0; public const int SegmentEndMask = 0; public const int ArrayStartMask = 0; public const int ArrayEndMask = int.MinValue; public const int MemoryManagerStartMask = int.MinValue; public const int MemoryManagerEndMask = 0; public const int StringStartMask = int.MinValue; public const int StringEndMask = int.MinValue; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int SegmentToSequenceStart(int startIndex) { return startIndex | 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int SegmentToSequenceEnd(int endIndex) { return endIndex | 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ArrayToSequenceStart(int startIndex) { return startIndex | 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ArrayToSequenceEnd(int endIndex) { return endIndex | int.MinValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int MemoryManagerToSequenceStart(int startIndex) { return startIndex | int.MinValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int MemoryManagerToSequenceEnd(int endIndex) { return endIndex | 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int StringToSequenceStart(int startIndex) { return startIndex | int.MinValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int StringToSequenceEnd(int endIndex) { return endIndex | int.MinValue; } } internal sealed class ReadOnlySequenceDebugView { [DebuggerDisplay("Count: {Segments.Length}", Name = "Segments")] public struct ReadOnlySequenceDebugViewSegments { [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public ReadOnlyMemory[] Segments { [<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] get; set; } } private readonly T[] _array; private readonly ReadOnlySequenceDebugViewSegments _segments; public ReadOnlySequenceDebugViewSegments BufferSegments => _segments; [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public T[] Items => _array; public ReadOnlySequenceDebugView(ReadOnlySequence sequence) { _array = BuffersExtensions.ToArray(ref sequence); int num = 0; ReadOnlySequence.Enumerator enumerator = sequence.GetEnumerator(); while (enumerator.MoveNext()) { ReadOnlyMemory current = enumerator.Current; num++; } ReadOnlyMemory[] array = new ReadOnlyMemory[num]; int num2 = 0; ReadOnlySequence.Enumerator enumerator2 = sequence.GetEnumerator(); while (enumerator2.MoveNext()) { ReadOnlyMemory current2 = enumerator2.Current; array[num2] = current2; num2++; } _segments = new ReadOnlySequenceDebugViewSegments { Segments = array }; } } internal abstract class ReadOnlySequenceSegment { public ReadOnlyMemory Memory { get; protected set; } public ReadOnlySequenceSegment Next { get; protected set; } public long RunningIndex { get; protected set; } } [<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] internal struct StandardFormat : IEquatable { public const byte NoPrecision = byte.MaxValue; public const byte MaxPrecision = 99; private readonly byte _format; private readonly byte _precision; public char Symbol => (char)_format; public byte Precision => _precision; public bool HasPrecision => _precision != byte.MaxValue; public bool IsDefault { get { if (_format == 0) { return _precision == 0; } return false; } } public StandardFormat(char symbol, byte precision = byte.MaxValue) { if (precision != byte.MaxValue && precision > 99) { System.ThrowHelper.ThrowArgumentOutOfRangeException_PrecisionTooLarge(); } if (symbol != (byte)symbol) { System.ThrowHelper.ThrowArgumentOutOfRangeException_SymbolDoesNotFit(); } _format = (byte)symbol; _precision = precision; } public static implicit operator StandardFormat(char symbol) { return new StandardFormat(symbol); } public static StandardFormat Parse(ReadOnlySpan format) { if (format.Length == 0) { return default(StandardFormat); } char symbol = format[0]; byte precision; if (format.Length == 1) { precision = byte.MaxValue; } else { uint num = 0u; for (int i = 1; i < format.Length; i++) { uint num2 = (uint)(format[i] - 48); if (num2 > 9) { throw new FormatException(System.SR.Format(System.SR.Argument_CannotParsePrecision, (byte)99)); } num = num * 10 + num2; if (num > 99) { throw new FormatException(System.SR.Format(System.SR.Argument_PrecisionTooLarge, (byte)99)); } } precision = (byte)num; } return new StandardFormat(symbol, precision); } public static StandardFormat Parse(string format) { if (format != null) { return Parse(MemoryExtensions.AsSpan(format)); } return default(StandardFormat); } public override bool Equals(object obj) { if (obj is StandardFormat other) { return Equals(other); } return false; } public override int GetHashCode() { byte format = _format; int hashCode = format.GetHashCode(); format = _precision; return hashCode ^ format.GetHashCode(); } public bool Equals(StandardFormat other) { if (_format == other._format) { return _precision == other._precision; } return false; } public unsafe override string ToString() { char* ptr = stackalloc char[4]; int length = 0; char symbol = Symbol; if (symbol != 0) { ptr[length++] = symbol; byte b = Precision; if (b != byte.MaxValue) { if (b >= 100) { ptr[length++] = (char)(48 + b / 100 % 10); b %= 100; } if (b >= 10) { ptr[length++] = (char)(48 + b / 10 % 10); b %= 10; } ptr[length++] = (char)(48 + b); } } return new string(ptr, 0, length); } public static bool operator ==(StandardFormat left, StandardFormat right) { return left.Equals(right); } public static bool operator !=(StandardFormat left, StandardFormat right) { return !left.Equals(right); } } } namespace System.Buffers.Text { internal static class Base64 { private static readonly sbyte[] s_decodingMap = new sbyte[256] { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; private static readonly byte[] s_encodingMap = new byte[64] { 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47 }; private const byte EncodingPad = 61; private const int MaximumEncodeLength = 1610612733; public static OperationStatus DecodeFromUtf8(ReadOnlySpan utf8, Span bytes, out int bytesConsumed, out int bytesWritten, bool isFinalBlock = true) { ref byte reference = ref MemoryMarshal.GetReference(utf8); ref byte reference2 = ref MemoryMarshal.GetReference(bytes); int num = utf8.Length & -4; int length = bytes.Length; int num2 = 0; int num3 = 0; if (utf8.Length != 0) { ref sbyte reference3 = ref s_decodingMap[0]; int num4 = (isFinalBlock ? 4 : 0); int num5 = 0; num5 = ((length < GetMaxDecodedFromUtf8Length(num)) ? (length / 3 * 4) : (num - num4)); while (true) { if (num2 < num5) { int num6 = Decode(ref Unsafe.Add(ref reference, num2), ref reference3); if (num6 >= 0) { WriteThreeLowOrderBytes(ref Unsafe.Add(ref reference2, num3), num6); num3 += 3; num2 += 4; continue; } } else { if (num5 != num - num4) { goto IL_0205; } if (num2 == num) { if (!isFinalBlock) { bytesConsumed = num2; bytesWritten = num3; return OperationStatus.NeedMoreData; } } else { int elementOffset = Unsafe.Add(ref reference, num - 4); int elementOffset2 = Unsafe.Add(ref reference, num - 3); int num7 = Unsafe.Add(ref reference, num - 2); int num8 = Unsafe.Add(ref reference, num - 1); elementOffset = Unsafe.Add(ref reference3, elementOffset); elementOffset2 = Unsafe.Add(ref reference3, elementOffset2); elementOffset <<= 18; elementOffset2 <<= 12; elementOffset |= elementOffset2; if (num8 != 61) { num7 = Unsafe.Add(ref reference3, num7); num8 = Unsafe.Add(ref reference3, num8); num7 <<= 6; elementOffset |= num8; elementOffset |= num7; if (elementOffset >= 0) { if (num3 <= length - 3) { WriteThreeLowOrderBytes(ref Unsafe.Add(ref reference2, num3), elementOffset); num3 += 3; goto IL_01eb; } goto IL_0205; } } else if (num7 != 61) { num7 = Unsafe.Add(ref reference3, num7); num7 <<= 6; elementOffset |= num7; if (elementOffset >= 0) { if (num3 <= length - 2) { Unsafe.Add(ref reference2, num3) = (byte)(elementOffset >> 16); Unsafe.Add(ref reference2, num3 + 1) = (byte)(elementOffset >> 8); num3 += 2; goto IL_01eb; } goto IL_0205; } } else if (elementOffset >= 0) { if (num3 <= length - 1) { Unsafe.Add(ref reference2, num3) = (byte)(elementOffset >> 16); num3++; goto IL_01eb; } goto IL_0205; } } } goto IL_022b; IL_01eb: num2 += 4; if (num == utf8.Length) { break; } goto IL_022b; IL_022b: bytesConsumed = num2; bytesWritten = num3; return OperationStatus.InvalidData; IL_0205: if (!(num != utf8.Length && isFinalBlock)) { bytesConsumed = num2; bytesWritten = num3; return OperationStatus.DestinationTooSmall; } goto IL_022b; } } bytesConsumed = num2; bytesWritten = num3; return OperationStatus.Done; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetMaxDecodedFromUtf8Length(int length) { if (length < 0) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.length); } return (length >> 2) * 3; } public static OperationStatus DecodeFromUtf8InPlace(Span buffer, out int bytesWritten) { int length = buffer.Length; int num = 0; int num2 = 0; if (length == (length >> 2) * 4) { if (length == 0) { goto IL_016d; } ref byte reference = ref MemoryMarshal.GetReference(buffer); ref sbyte reference2 = ref s_decodingMap[0]; while (num < length - 4) { int num3 = Decode(ref Unsafe.Add(ref reference, num), ref reference2); if (num3 >= 0) { WriteThreeLowOrderBytes(ref Unsafe.Add(ref reference, num2), num3); num2 += 3; num += 4; continue; } goto IL_0172; } int elementOffset = Unsafe.Add(ref reference, length - 4); int elementOffset2 = Unsafe.Add(ref reference, length - 3); int num4 = Unsafe.Add(ref reference, length - 2); int num5 = Unsafe.Add(ref reference, length - 1); elementOffset = Unsafe.Add(ref reference2, elementOffset); elementOffset2 = Unsafe.Add(ref reference2, elementOffset2); elementOffset <<= 18; elementOffset2 <<= 12; elementOffset |= elementOffset2; if (num5 != 61) { num4 = Unsafe.Add(ref reference2, num4); num5 = Unsafe.Add(ref reference2, num5); num4 <<= 6; elementOffset |= num5; elementOffset |= num4; if (elementOffset >= 0) { WriteThreeLowOrderBytes(ref Unsafe.Add(ref reference, num2), elementOffset); num2 += 3; goto IL_016d; } } else if (num4 != 61) { num4 = Unsafe.Add(ref reference2, num4); num4 <<= 6; elementOffset |= num4; if (elementOffset >= 0) { Unsafe.Add(ref reference, num2) = (byte)(elementOffset >> 16); Unsafe.Add(ref reference, num2 + 1) = (byte)(elementOffset >> 8); num2 += 2; goto IL_016d; } } else if (elementOffset >= 0) { Unsafe.Add(ref reference, num2) = (byte)(elementOffset >> 16); num2++; goto IL_016d; } } goto IL_0172; IL_016d: bytesWritten = num2; return OperationStatus.Done; IL_0172: bytesWritten = num2; return OperationStatus.InvalidData; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Decode(ref byte encodedBytes, ref sbyte decodingMap) { int elementOffset = encodedBytes; int elementOffset2 = Unsafe.Add(ref encodedBytes, 1); int elementOffset3 = Unsafe.Add(ref encodedBytes, 2); int elementOffset4 = Unsafe.Add(ref encodedBytes, 3); elementOffset = Unsafe.Add(ref decodingMap, elementOffset); elementOffset2 = Unsafe.Add(ref decodingMap, elementOffset2); elementOffset3 = Unsafe.Add(ref decodingMap, elementOffset3); elementOffset4 = Unsafe.Add(ref decodingMap, elementOffset4); elementOffset <<= 18; elementOffset2 <<= 12; elementOffset3 <<= 6; elementOffset |= elementOffset4; elementOffset2 |= elementOffset3; return elementOffset | elementOffset2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void WriteThreeLowOrderBytes(ref byte destination, int value) { destination = (byte)(value >> 16); Unsafe.Add(ref destination, 1) = (byte)(value >> 8); Unsafe.Add(ref destination, 2) = (byte)value; } public static OperationStatus EncodeToUtf8(ReadOnlySpan bytes, Span utf8, out int bytesConsumed, out int bytesWritten, bool isFinalBlock = true) { ref byte reference = ref MemoryMarshal.GetReference(bytes); ref byte reference2 = ref MemoryMarshal.GetReference(utf8); int length = bytes.Length; int length2 = utf8.Length; int num = 0; num = ((length > 1610612733 || length2 < GetMaxEncodedToUtf8Length(length)) ? ((length2 >> 2) * 3 - 2) : (length - 2)); int i = 0; int num2 = 0; int num3 = 0; ref byte encodingMap = ref s_encodingMap[0]; for (; i < num; i += 3) { num3 = Encode(ref Unsafe.Add(ref reference, i), ref encodingMap); Unsafe.WriteUnaligned(ref Unsafe.Add(ref reference2, num2), num3); num2 += 4; } if (num == length - 2) { if (isFinalBlock) { if (i == length - 1) { num3 = EncodeAndPadTwo(ref Unsafe.Add(ref reference, i), ref encodingMap); Unsafe.WriteUnaligned(ref Unsafe.Add(ref reference2, num2), num3); num2 += 4; i++; } else if (i == length - 2) { num3 = EncodeAndPadOne(ref Unsafe.Add(ref reference, i), ref encodingMap); Unsafe.WriteUnaligned(ref Unsafe.Add(ref reference2, num2), num3); num2 += 4; i += 2; } bytesConsumed = i; bytesWritten = num2; return OperationStatus.Done; } bytesConsumed = i; bytesWritten = num2; return OperationStatus.NeedMoreData; } bytesConsumed = i; bytesWritten = num2; return OperationStatus.DestinationTooSmall; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetMaxEncodedToUtf8Length(int length) { if ((uint)length > 1610612733u) { System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument.length); } return (length + 2) / 3 * 4; } public static OperationStatus EncodeToUtf8InPlace(Span buffer, int dataLength, out int bytesWritten) { int maxEncodedToUtf8Length = GetMaxEncodedToUtf8Length(dataLength); if (buffer.Length >= maxEncodedToUtf8Length) { int num = dataLength - dataLength / 3 * 3; int num2 = maxEncodedToUtf8Length - 4; int num3 = dataLength - num; int num4 = 0; ref byte encodingMap = ref s_encodingMap[0]; ref byte reference = ref MemoryMarshal.GetReference(buffer); switch (num) { case 1: num4 = EncodeAndPadTwo(ref Unsafe.Add(ref reference, num3), ref encodingMap); Unsafe.WriteUnaligned(ref Unsafe.Add(ref reference, num2), num4); num2 -= 4; break; default: num4 = EncodeAndPadOne(ref Unsafe.Add(ref reference, num3), ref encodingMap); Unsafe.WriteUnaligned(ref Unsafe.Add(ref reference, num2), num4); num2 -= 4; break; case 0: break; } for (num3 -= 3; num3 >= 0; num3 -= 3) { num4 = Encode(ref Unsafe.Add(ref reference, num3), ref encodingMap); Unsafe.WriteUnaligned(ref Unsafe.Add(ref reference, num2), num4); num2 -= 4; } bytesWritten = maxEncodedToUtf8Length; return OperationStatus.Done; } bytesWritten = 0; return OperationStatus.DestinationTooSmall; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Encode(ref byte threeBytes, ref byte encodingMap) { int num = (threeBytes << 16) | (Unsafe.Add(ref threeBytes, 1) << 8) | Unsafe.Add(ref threeBytes, 2); int num2 = Unsafe.Add(ref encodingMap, num >> 18); int num3 = Unsafe.Add(ref encodingMap, (num >> 12) & 0x3F); int num4 = Unsafe.Add(ref encodingMap, (num >> 6) & 0x3F); int num5 = Unsafe.Add(ref encodingMap, num & 0x3F); return num2 | (num3 << 8) | (num4 << 16) | (num5 << 24); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int EncodeAndPadOne(ref byte twoBytes, ref byte encodingMap) { int num = (twoBytes << 16) | (Unsafe.Add(ref twoBytes, 1) << 8); int num2 = Unsafe.Add(ref encodingMap, num >> 18); int num3 = Unsafe.Add(ref encodingMap, (num >> 12) & 0x3F); int num4 = Unsafe.Add(ref encodingMap, (num >> 6) & 0x3F); return num2 | (num3 << 8) | (num4 << 16) | 0x3D000000; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int EncodeAndPadTwo(ref byte oneByte, ref byte encodingMap) { int num = oneByte << 8; int num2 = Unsafe.Add(ref encodingMap, num >> 10); int num3 = Unsafe.Add(ref encodingMap, (num >> 4) & 0x3F); return num2 | (num3 << 8) | 0x3D0000 | 0x3D000000; } } internal static class FormattingHelpers { public enum HexCasing : uint { Uppercase = 0u, Lowercase = 8224u } internal const string HexTableLower = "0123456789abcdef"; internal const string HexTableUpper = "0123456789ABCDEF"; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int CountDigits(ulong value) { int num = 1; uint num2; if (value >= 10000000) { if (value >= 100000000000000L) { num2 = (uint)(value / 100000000000000L); num += 14; } else { num2 = (uint)(value / 10000000); num += 7; } } else { num2 = (uint)value; } if (num2 >= 10) { num = ((num2 < 100) ? (num + 1) : ((num2 < 1000) ? (num + 2) : ((num2 < 10000) ? (num + 3) : ((num2 < 100000) ? (num + 4) : ((num2 >= 1000000) ? (num + 6) : (num + 5)))))); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int CountDigits(uint value) { int num = 1; if (value >= 100000) { value /= 100000; num += 5; } if (value >= 10) { num = ((value < 100) ? (num + 1) : ((value < 1000) ? (num + 2) : ((value >= 10000) ? (num + 4) : (num + 3)))); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int CountHexDigits(ulong value) { int num = 1; if (value > uint.MaxValue) { num += 8; value >>= 32; } if (value > 65535) { num += 4; value >>= 16; } if (value > 255) { num += 2; value >>= 8; } if (value > 15) { num++; } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static char GetSymbolOrDefault([In][<1beca034-67a1-43c6-ac62-daf980b53811>IsReadOnly] ref StandardFormat format, char defaultSymbol) { char c = format.Symbol; if (c == '\0' && format.Precision == 0) { c = defaultSymbol; } return c; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void FillWithAsciiZeros(Span buffer) { for (int i = 0; i < buffer.Length; i++) { buffer[i] = 48; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteHexByte(byte value, Span buffer, int startingIndex = 0, HexCasing casing = HexCasing.Uppercase) { uint num = (uint)(((value & 0xF0) << 4) + (value & 0xF) - 35209); uint num2 = ((((0 - num) & 0x7070) >> 4) + num + 47545) | (uint)casing; buffer[startingIndex + 1] = (byte)num2; buffer[startingIndex] = (byte)(num2 >> 8); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteDigits(ulong value, Span buffer) { for (int num = buffer.Length - 1; num >= 1; num--) { ulong num2 = 48 + value; value /= 10; buffer[num] = (byte)(num2 - value * 10); } buffer[0] = (byte)(48 + value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteDigitsWithGroupSeparator(ulong value, Span buffer) { int num = 0; for (int num2 = buffer.Length - 1; num2 >= 1; num2--) { ulong num3 = 48 + value; value /= 10; buffer[num2] = (byte)(num3 - value * 10); if (num == 2) { buffer[--num2] = 44; num = 0; } else { num++; } } buffer[0] = (byte)(48 + value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteDigits(uint value, Span buffer) { for (int num = buffer.Length - 1; num >= 1; num--) { uint num2 = 48 + value; value /= 10; buffer[num] = (byte)(num2 - value * 10); } buffer[0] = (byte)(48 + value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteFourDecimalDigits(uint value, Span buffer, int startingIndex = 0) { uint num = 48 + value; value /= 10; buffer[startingIndex + 3] = (byte)(num - value * 10); num = 48 + value; value /= 10; buffer[startingIndex + 2] = (byte)(num - value * 10); num = 48 + value; value /= 10; buffer[startingIndex + 1] = (byte)(num - value * 10); buffer[startingIndex] = (byte)(48 + value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteTwoDecimalDigits(uint value, Span buffer, int startingIndex = 0) { uint num = 48 + value; value /= 10; buffer[startingIndex + 1] = (byte)(num - value * 10); buffer[startingIndex] = (byte)(48 + value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong DivMod(ulong numerator, ulong denominator, out ulong modulo) { ulong num = numerator / denominator; modulo = numerator - num * denominator; return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint DivMod(uint numerator, uint denominator, out uint modulo) { uint num = numerator / denominator; modulo = numerator - num * denominator; return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int CountDecimalTrailingZeros(uint value, out uint valueWithoutTrailingZeros) { int num = 0; if (value != 0) { while (true) { uint modulo; uint num2 = DivMod(value, 10u, out modulo); if (modulo != 0) { break; } value = num2; num++; } } valueWithoutTrailingZeros = value; return num; } } internal static class Utf8Constants { public const byte Colon = 58; public const byte Comma = 44; public const byte Minus = 45; public const byte Period = 46; public const byte Plus = 43; public const byte Slash = 47; public const byte Space = 32; public const byte Hyphen = 45; public const byte Separator = 44; public const int GroupSize = 3; public static readonly TimeSpan s_nullUtcOffset = TimeSpan.MinValue; public const int DateTimeMaxUtcOffsetHours = 14; public const int DateTimeNumFractionDigits = 7; public const int MaxDateTimeFraction = 9999999; public const ulong BillionMaxUIntValue = 4294967295000000000uL; public const uint Billion = 1000000000u; } internal static class Utf8Formatter { [StructLayout(LayoutKind.Explicit)] private struct DecomposedGuid { [FieldOffset(0)] public Guid Guid; [FieldOffset(0)] public byte Byte00; [FieldOffset(1)] public byte Byte01; [FieldOffset(2)] public byte Byte02; [FieldOffset(3)] public byte Byte03; [FieldOffset(4)] public byte Byte04; [FieldOffset(5)] public byte Byte05; [FieldOffset(6)] public byte Byte06; [FieldOffset(7)] public byte Byte07; [FieldOffset(8)] public byte Byte08; [FieldOffset(9)] public byte Byte09; [FieldOffset(10)] public byte Byte10; [FieldOffset(11)] public byte Byte11; [FieldOffset(12)] public byte Byte12; [FieldOffset(13)] public byte Byte13; [FieldOffset(14)] public byte Byte14; [FieldOffset(15)] public byte Byte15; } private const byte TimeMarker = 84; private const byte UtcMarker = 90; private const byte GMT1 = 71; private const byte GMT2 = 77; private const byte GMT3 = 84; private const byte GMT1Lowercase = 103; private const byte GMT2Lowercase = 109; private const byte GMT3Lowercase = 116; private static readonly uint[] DayAbbreviations = new uint[7] { 7238995u, 7237453u, 6649172u, 6579543u, 7694420u, 6910534u, 7627091u }; private static readonly uint[] DayAbbreviationsLowercase = new uint[7] { 7239027u, 7237485u, 6649204u, 6579575u, 7694452u, 6910566u, 7627123u }; private static readonly uint[] MonthAbbreviations = new uint[12] { 7233866u, 6448454u, 7496013u, 7499841u, 7954765u, 7238986u, 7107914u, 6780225u, 7365971u, 7627599u, 7761742u, 6513988u }; private static readonly uint[] MonthAbbreviationsLowercase = new uint[12] { 7233898u, 6448486u, 7496045u, 7499873u, 7954797u, 7239018u, 7107946u, 6780257u, 7366003u, 7627631u, 7761774u, 6514020u }; private const byte OpenBrace = 123; private const byte CloseBrace = 125; private const byte OpenParen = 40; private const byte CloseParen = 41; private const byte Dash = 45; public static bool TryFormat(bool value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { char symbolOrDefault = System.Buffers.Text.FormattingHelpers.GetSymbolOrDefault(ref format, 'G'); if (value) { if (symbolOrDefault == 'G') { if (BinaryPrimitives.TryWriteUInt32BigEndian(destination, 1416787301u)) { goto IL_0033; } } else { if (symbolOrDefault != 'l') { goto IL_0083; } if (BinaryPrimitives.TryWriteUInt32BigEndian(destination, 1953658213u)) { goto IL_0033; } } } else if (symbolOrDefault == 'G') { if (4u < (uint)destination.Length) { BinaryPrimitives.WriteUInt32BigEndian(destination, 1180789875u); goto IL_006e; } } else { if (symbolOrDefault != 'l') { goto IL_0083; } if (4u < (uint)destination.Length) { BinaryPrimitives.WriteUInt32BigEndian(destination, 1717660787u); goto IL_006e; } } bytesWritten = 0; return false; IL_0033: bytesWritten = 4; return true; IL_0083: return System.ThrowHelper.TryFormatThrowFormatException(out bytesWritten); IL_006e: destination[4] = 101; bytesWritten = 5; return true; } public static bool TryFormat(DateTimeOffset value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { TimeSpan offset = System.Buffers.Text.Utf8Constants.s_nullUtcOffset; char c = format.Symbol; if (format.IsDefault) { c = 'G'; offset = value.Offset; } return c switch { 'R' => TryFormatDateTimeR(value.UtcDateTime, destination, out bytesWritten), 'l' => TryFormatDateTimeL(value.UtcDateTime, destination, out bytesWritten), 'O' => TryFormatDateTimeO(value.DateTime, value.Offset, destination, out bytesWritten), 'G' => TryFormatDateTimeG(value.DateTime, offset, destination, out bytesWritten), _ => System.ThrowHelper.TryFormatThrowFormatException(out bytesWritten), }; } public static bool TryFormat(DateTime value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { return System.Buffers.Text.FormattingHelpers.GetSymbolOrDefault(ref format, 'G') switch { 'R' => TryFormatDateTimeR(value, destination, out bytesWritten), 'l' => TryFormatDateTimeL(value, destination, out bytesWritten), 'O' => TryFormatDateTimeO(value, System.Buffers.Text.Utf8Constants.s_nullUtcOffset, destination, out bytesWritten), 'G' => TryFormatDateTimeG(value, System.Buffers.Text.Utf8Constants.s_nullUtcOffset, destination, out bytesWritten), _ => System.ThrowHelper.TryFormatThrowFormatException(out bytesWritten), }; } private static bool TryFormatDateTimeG(DateTime value, TimeSpan offset, Span destination, out int bytesWritten) { int num = 19; if (offset != System.Buffers.Text.Utf8Constants.s_nullUtcOffset) { num += 7; } if (destination.Length < num) { bytesWritten = 0; return false; } bytesWritten = num; byte b = destination[18]; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Month, destination); destination[2] = 47; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Day, destination, 3); destination[5] = 47; System.Buffers.Text.FormattingHelpers.WriteFourDecimalDigits((uint)value.Year, destination, 6); destination[10] = 32; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Hour, destination, 11); destination[13] = 58; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Minute, destination, 14); destination[16] = 58; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Second, destination, 17); if (offset != System.Buffers.Text.Utf8Constants.s_nullUtcOffset) { byte b2; if (offset < default(TimeSpan)) { b2 = 45; offset = TimeSpan.FromTicks(-offset.Ticks); } else { b2 = 43; } System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)offset.Minutes, destination, 24); destination[23] = 58; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)offset.Hours, destination, 21); destination[20] = b2; destination[19] = 32; } return true; } private static bool TryFormatDateTimeL(DateTime value, Span destination, out int bytesWritten) { if (28u >= (uint)destination.Length) { bytesWritten = 0; return false; } uint num = DayAbbreviationsLowercase[(int)value.DayOfWeek]; destination[0] = (byte)num; num >>= 8; destination[1] = (byte)num; num >>= 8; destination[2] = (byte)num; destination[3] = 44; destination[4] = 32; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Day, destination, 5); destination[7] = 32; uint num2 = MonthAbbreviationsLowercase[value.Month - 1]; destination[8] = (byte)num2; num2 >>= 8; destination[9] = (byte)num2; num2 >>= 8; destination[10] = (byte)num2; destination[11] = 32; System.Buffers.Text.FormattingHelpers.WriteFourDecimalDigits((uint)value.Year, destination, 12); destination[16] = 32; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Hour, destination, 17); destination[19] = 58; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Minute, destination, 20); destination[22] = 58; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Second, destination, 23); destination[25] = 32; destination[26] = 103; destination[27] = 109; destination[28] = 116; bytesWritten = 29; return true; } private static bool TryFormatDateTimeO(DateTime value, TimeSpan offset, Span destination, out int bytesWritten) { int num = 27; DateTimeKind dateTimeKind = DateTimeKind.Local; if (offset == System.Buffers.Text.Utf8Constants.s_nullUtcOffset) { dateTimeKind = value.Kind; switch (dateTimeKind) { case DateTimeKind.Local: offset = TimeZoneInfo.Local.GetUtcOffset(value); num += 6; break; case DateTimeKind.Utc: num++; break; } } else { num += 6; } if (destination.Length < num) { bytesWritten = 0; return false; } bytesWritten = num; byte b = destination[26]; System.Buffers.Text.FormattingHelpers.WriteFourDecimalDigits((uint)value.Year, destination); destination[4] = 45; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Month, destination, 5); destination[7] = 45; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Day, destination, 8); destination[10] = 84; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Hour, destination, 11); destination[13] = 58; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Minute, destination, 14); destination[16] = 58; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Second, destination, 17); destination[19] = 46; System.Buffers.Text.FormattingHelpers.WriteDigits((uint)((ulong)value.Ticks % 10000000uL), destination.Slice(20, 7)); switch (dateTimeKind) { case DateTimeKind.Local: { byte b2; if (offset < default(TimeSpan)) { b2 = 45; offset = TimeSpan.FromTicks(-offset.Ticks); } else { b2 = 43; } System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)offset.Minutes, destination, 31); destination[30] = 58; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)offset.Hours, destination, 28); destination[27] = b2; break; } case DateTimeKind.Utc: destination[27] = 90; break; } return true; } private static bool TryFormatDateTimeR(DateTime value, Span destination, out int bytesWritten) { if (28u >= (uint)destination.Length) { bytesWritten = 0; return false; } uint num = DayAbbreviations[(int)value.DayOfWeek]; destination[0] = (byte)num; num >>= 8; destination[1] = (byte)num; num >>= 8; destination[2] = (byte)num; destination[3] = 44; destination[4] = 32; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Day, destination, 5); destination[7] = 32; uint num2 = MonthAbbreviations[value.Month - 1]; destination[8] = (byte)num2; num2 >>= 8; destination[9] = (byte)num2; num2 >>= 8; destination[10] = (byte)num2; destination[11] = 32; System.Buffers.Text.FormattingHelpers.WriteFourDecimalDigits((uint)value.Year, destination, 12); destination[16] = 32; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Hour, destination, 17); destination[19] = 58; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Minute, destination, 20); destination[22] = 58; System.Buffers.Text.FormattingHelpers.WriteTwoDecimalDigits((uint)value.Second, destination, 23); destination[25] = 32; destination[26] = 71; destination[27] = 77; destination[28] = 84; bytesWritten = 29; return true; } public static bool TryFormat(decimal value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { if (format.IsDefault) { format = 'G'; } switch (format.Symbol) { case 'G': case 'g': { if (format.Precision != byte.MaxValue) { throw new NotSupportedException(System.SR.Argument_GWithPrecisionNotSupported); } NumberBuffer number3 = default(NumberBuffer); System.Number.DecimalToNumber(value, ref number3); if (number3.Digits[0] == 0) { number3.IsNegative = false; } return TryFormatDecimalG(ref number3, destination, out bytesWritten); } case 'F': case 'f': { NumberBuffer number2 = default(NumberBuffer); System.Number.DecimalToNumber(value, ref number2); byte b2 = (byte)((format.Precision == byte.MaxValue) ? 2 : format.Precision); System.Number.RoundNumber(ref number2, number2.Scale + b2); return TryFormatDecimalF(ref number2, destination, out bytesWritten, b2); } case 'E': case 'e': { NumberBuffer number = default(NumberBuffer); System.Number.DecimalToNumber(value, ref number); byte b = (byte)((format.Precision == byte.MaxValue) ? 6 : format.Precision); System.Number.RoundNumber(ref number, b + 1); return TryFormatDecimalE(ref number, destination, out bytesWritten, b, (byte)format.Symbol); } default: return System.ThrowHelper.TryFormatThrowFormatException(out bytesWritten); } } private static bool TryFormatDecimalE(ref NumberBuffer number, Span destination, out int bytesWritten, byte precision, byte exponentSymbol) { int scale = number.Scale; ReadOnlySpan readOnlySpan = number.Digits; int num = (number.IsNegative ? 1 : 0) + 1 + ((precision != 0) ? (precision + 1) : 0) + 2 + 3; if (destination.Length < num) { bytesWritten = 0; return false; } int num2 = 0; int num3 = 0; if (number.IsNegative) { destination[num2++] = 45; } byte b = readOnlySpan[num3]; int num4; if (b == 0) { destination[num2++] = 48; num4 = 0; } else { destination[num2++] = b; num3++; num4 = scale - 1; } if (precision > 0) { destination[num2++] = 46; for (int i = 0; i < precision; i++) { byte b2 = readOnlySpan[num3]; if (b2 == 0) { while (i++ < precision) { destination[num2++] = 48; } break; } destination[num2++] = b2; num3++; } } destination[num2++] = exponentSymbol; if (num4 >= 0) { destination[num2++] = 43; } else { destination[num2++] = 45; num4 = -num4; } destination[num2++] = 48; destination[num2++] = (byte)(num4 / 10 + 48); destination[num2++] = (byte)(num4 % 10 + 48); bytesWritten = num; return true; } private static bool TryFormatDecimalF(ref NumberBuffer number, Span destination, out int bytesWritten, byte precision) { int scale = number.Scale; ReadOnlySpan readOnlySpan = number.Digits; int num = (number.IsNegative ? 1 : 0) + ((scale <= 0) ? 1 : scale) + ((precision != 0) ? (precision + 1) : 0); if (destination.Length < num) { bytesWritten = 0; return false; } int i = 0; int num2 = 0; if (number.IsNegative) { destination[num2++] = 45; } if (scale <= 0) { destination[num2++] = 48; } else { for (; i < scale; i++) { byte b = readOnlySpan[i]; if (b == 0) { int num3 = scale - i; for (int j = 0; j < num3; j++) { destination[num2++] = 48; } break; } destination[num2++] = b; } } if (precision > 0) { destination[num2++] = 46; int k = 0; if (scale < 0) { int num4 = Math.Min(precision, -scale); for (int l = 0; l < num4; l++) { destination[num2++] = 48; } k += num4; } for (; k < precision; k++) { byte b2 = readOnlySpan[i]; if (b2 == 0) { while (k++ < precision) { destination[num2++] = 48; } break; } destination[num2++] = b2; i++; } } bytesWritten = num; return true; } private static bool TryFormatDecimalG(ref NumberBuffer number, Span destination, out int bytesWritten) { int scale = number.Scale; ReadOnlySpan readOnlySpan = number.Digits; int numDigits = number.NumDigits; bool flag = scale < numDigits; int num; if (flag) { num = numDigits + 1; if (scale <= 0) { num += 1 + -scale; } } else { num = ((scale <= 0) ? 1 : scale); } if (number.IsNegative) { num++; } if (destination.Length < num) { bytesWritten = 0; return false; } int i = 0; int num2 = 0; if (number.IsNegative) { destination[num2++] = 45; } if (scale <= 0) { destination[num2++] = 48; } else { for (; i < scale; i++) { byte b = readOnlySpan[i]; if (b == 0) { int num3 = scale - i; for (int j = 0; j < num3; j++) { destination[num2++] = 48; } break; } destination[num2++] = b; } } if (flag) { destination[num2++] = 46; if (scale < 0) { int num4 = -scale; for (int k = 0; k < num4; k++) { destination[num2++] = 48; } } byte b2; while ((b2 = readOnlySpan[i++]) != 0) { destination[num2++] = b2; } } bytesWritten = num; return true; } public static bool TryFormat(double value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { return TryFormatFloatingPoint(value, destination, out bytesWritten, format); } public static bool TryFormat(float value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { return TryFormatFloatingPoint(value, destination, out bytesWritten, format); } private static bool TryFormatFloatingPoint(T value, Span destination, out int bytesWritten, StandardFormat format) where T : IFormattable { if (format.IsDefault) { format = 'G'; } switch (format.Symbol) { case 'G': case 'g': if (format.Precision != byte.MaxValue) { throw new NotSupportedException(System.SR.Argument_GWithPrecisionNotSupported); } break; default: return System.ThrowHelper.TryFormatThrowFormatException(out bytesWritten); case 'E': case 'F': case 'e': case 'f': break; } string text = format.ToString(); ref T reference = ref value; T val = default(T); if (val == null) { val = reference; reference = ref val; } string text2 = reference.ToString(text, CultureInfo.InvariantCulture); int length = text2.Length; if (length > destination.Length) { bytesWritten = 0; return false; } for (int i = 0; i < length; i++) { destination[i] = (byte)text2[i]; } bytesWritten = length; return true; } public static bool TryFormat(Guid value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { int num; switch (System.Buffers.Text.FormattingHelpers.GetSymbolOrDefault(ref format, 'D')) { case 'D': num = -2147483612; break; case 'B': num = -2139260122; break; case 'P': num = -2144786394; break; case 'N': num = 32; break; default: return System.ThrowHelper.TryFormatThrowFormatException(out bytesWritten); } if ((byte)num > destination.Length) { bytesWritten = 0; return false; } bytesWritten = (byte)num; num >>= 8; if ((byte)num != 0) { destination[0] = (byte)num; destination = destination.Slice(1); } num >>= 8; DecomposedGuid decomposedGuid = default(DecomposedGuid); decomposedGuid.Guid = value; byte b = destination[8]; System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte03, destination, 0, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte02, destination, 2, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte01, destination, 4, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte00, destination, 6, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); if (num < 0) { destination[8] = 45; destination = destination.Slice(9); } else { destination = destination.Slice(8); } byte b2 = destination[4]; System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte05, destination, 0, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte04, destination, 2, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); if (num < 0) { destination[4] = 45; destination = destination.Slice(5); } else { destination = destination.Slice(4); } byte b3 = destination[4]; System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte07, destination, 0, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte06, destination, 2, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); if (num < 0) { destination[4] = 45; destination = destination.Slice(5); } else { destination = destination.Slice(4); } byte b4 = destination[4]; System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte08, destination, 0, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte09, destination, 2, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); if (num < 0) { destination[4] = 45; destination = destination.Slice(5); } else { destination = destination.Slice(4); } byte b5 = destination[11]; System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte10, destination, 0, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte11, destination, 2, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte12, destination, 4, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte13, destination, 6, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte14, destination, 8, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); System.Buffers.Text.FormattingHelpers.WriteHexByte(decomposedGuid.Byte15, destination, 10, System.Buffers.Text.FormattingHelpers.HexCasing.Lowercase); if ((byte)num != 0) { destination[12] = (byte)num; } return true; } public static bool TryFormat(byte value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { return TryFormatUInt64(value, destination, out bytesWritten, format); } [CLSCompliant(false)] public static bool TryFormat(sbyte value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { return TryFormatInt64(value, 255uL, destination, out bytesWritten, format); } [CLSCompliant(false)] public static bool TryFormat(ushort value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { return TryFormatUInt64(value, destination, out bytesWritten, format); } public static bool TryFormat(short value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { return TryFormatInt64(value, 65535uL, destination, out bytesWritten, format); } [CLSCompliant(false)] public static bool TryFormat(uint value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { return TryFormatUInt64(value, destination, out bytesWritten, format); } public static bool TryFormat(int value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { return TryFormatInt64(value, 4294967295uL, destination, out bytesWritten, format); } [CLSCompliant(false)] public static bool TryFormat(ulong value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { return TryFormatUInt64(value, destination, out bytesWritten, format); } public static bool TryFormat(long value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { return TryFormatInt64(value, ulong.MaxValue, destination, out bytesWritten, format); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryFormatInt64(long value, ulong mask, Span destination, out int bytesWritten, StandardFormat format) { if (format.IsDefault) { return TryFormatInt64Default(value, destination, out bytesWritten); } switch (format.Symbol) { case 'G': case 'g': if (format.HasPrecision) { throw new NotSupportedException(System.SR.Argument_GWithPrecisionNotSupported); } return TryFormatInt64D(value, format.Precision, destination, out bytesWritten); case 'D': case 'd': return TryFormatInt64D(value, format.Precision, destination, out bytesWritten); case 'N': case 'n': return TryFormatInt64N(value, format.Precision, destination, out bytesWritten); case 'x': return TryFormatUInt64X((ulong)value & mask, format.Precision, useLower: true, destination, out bytesWritten); case 'X': return TryFormatUInt64X((ulong)value & mask, format.Precision, useLower: false, destination, out bytesWritten); default: return System.ThrowHelper.TryFormatThrowFormatException(out bytesWritten); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryFormatInt64D(long value, byte precision, Span destination, out int bytesWritten) { bool insertNegationSign = false; if (value < 0) { insertNegationSign = true; value = -value; } return TryFormatUInt64D((ulong)value, precision, destination, insertNegationSign, out bytesWritten); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryFormatInt64Default(long value, Span destination, out int bytesWritten) { if ((ulong)value < 10uL) { return TryFormatUInt32SingleDigit((uint)value, destination, out bytesWritten); } if (IntPtr.Size == 8) { return TryFormatInt64MultipleDigits(value, destination, out bytesWritten); } if (value <= int.MaxValue && value >= int.MinValue) { return TryFormatInt32MultipleDigits((int)value, destination, out bytesWritten); } if (value <= 4294967295000000000L && value >= -4294967295000000000L) { if (value >= 0) { return TryFormatUInt64LessThanBillionMaxUInt((ulong)value, destination, out bytesWritten); } return TryFormatInt64MoreThanNegativeBillionMaxUInt(-value, destination, out bytesWritten); } if (value >= 0) { return TryFormatUInt64MoreThanBillionMaxUInt((ulong)value, destination, out bytesWritten); } return TryFormatInt64LessThanNegativeBillionMaxUInt(-value, destination, out bytesWritten); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryFormatInt32Default(int value, Span destination, out int bytesWritten) { if ((uint)value < 10u) { return TryFormatUInt32SingleDigit((uint)value, destination, out bytesWritten); } return TryFormatInt32MultipleDigits(value, destination, out bytesWritten); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryFormatInt32MultipleDigits(int value, Span destination, out int bytesWritten) { if (value < 0) { value = -value; int num = System.Buffers.Text.FormattingHelpers.CountDigits((uint)value); if (num >= destination.Length) { bytesWritten = 0; return false; } destination[0] = 45; bytesWritten = num + 1; System.Buffers.Text.FormattingHelpers.WriteDigits((uint)value, destination.Slice(1, num)); return true; } return TryFormatUInt32MultipleDigits((uint)value, destination, out bytesWritten); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryFormatInt64MultipleDigits(long value, Span destination, out int bytesWritten) { if (value < 0) { value = -value; int num = System.Buffers.Text.FormattingHelpers.CountDigits((ulong)value); if (num >= destination.Length) { bytesWritten = 0; return false; } destination[0] = 45; bytesWritten = num + 1; System.Buffers.Text.FormattingHelpers.WriteDigits((ulong)value, destination.Slice(1, num)); return true; } return TryFormatUInt64MultipleDigits((ulong)value, destination, out bytesWritten); } private static bool TryFormatInt64MoreThanNegativeBillionMaxUInt(long value, Span destination, out int bytesWritten) { uint num = (uint)(value / 1000000000); uint value2 = (uint)(value - num * 1000000000); int num2 = System.Buffers.Text.FormattingHelpers.CountDigits(num); int num3 = num2 + 9; if (num3 >= destination.Length) { bytesWritten = 0; return false; } destination[0] = 45; bytesWritten = num3 + 1; System.Buffers.Text.FormattingHelpers.WriteDigits(num, destination.Slice(1, num2)); System.Buffers.Text.FormattingHelpers.WriteDigits(value2, destination.Slice(num2 + 1, 9)); return true; } private static bool TryFormatInt64LessThanNegativeBillionMaxUInt(long value, Span destination, out int bytesWritten) { ulong num = (ulong)value / 1000000000uL; uint value2 = (uint)((ulong)value - num * 1000000000); uint num2 = (uint)(num / 1000000000); uint value3 = (uint)(num - num2 * 1000000000); int num3 = System.Buffers.Text.FormattingHelpers.CountDigits(num2); int num4 = num3 + 18; if (num4 >= destination.Length) { bytesWritten = 0; return false; } destination[0] = 45; bytesWritten = num4 + 1; System.Buffers.Text.FormattingHelpers.WriteDigits(num2, destination.Slice(1, num3)); System.Buffers.Text.FormattingHelpers.WriteDigits(value3, destination.Slice(num3 + 1, 9)); System.Buffers.Text.FormattingHelpers.WriteDigits(value2, destination.Slice(num3 + 1 + 9, 9)); return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryFormatInt64N(long value, byte precision, Span destination, out int bytesWritten) { bool insertNegationSign = false; if (value < 0) { insertNegationSign = true; value = -value; } return TryFormatUInt64N((ulong)value, precision, destination, insertNegationSign, out bytesWritten); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryFormatUInt64(ulong value, Span destination, out int bytesWritten, StandardFormat format) { if (format.IsDefault) { return TryFormatUInt64Default(value, destination, out bytesWritten); } switch (format.Symbol) { case 'G': case 'g': if (format.HasPrecision) { throw new NotSupportedException(System.SR.Argument_GWithPrecisionNotSupported); } return TryFormatUInt64D(value, format.Precision, destination, insertNegationSign: false, out bytesWritten); case 'D': case 'd': return TryFormatUInt64D(value, format.Precision, destination, insertNegationSign: false, out bytesWritten); case 'N': case 'n': return TryFormatUInt64N(value, format.Precision, destination, insertNegationSign: false, out bytesWritten); case 'x': return TryFormatUInt64X(value, format.Precision, useLower: true, destination, out bytesWritten); case 'X': return TryFormatUInt64X(value, format.Precision, useLower: false, destination, out bytesWritten); default: return System.ThrowHelper.TryFormatThrowFormatException(out bytesWritten); } } private static bool TryFormatUInt64D(ulong value, byte precision, Span destination, bool insertNegationSign, out int bytesWritten) { int num = System.Buffers.Text.FormattingHelpers.CountDigits(value); int num2 = ((precision != byte.MaxValue) ? precision : 0) - num; if (num2 < 0) { num2 = 0; } int num3 = num + num2; if (insertNegationSign) { num3++; } if (num3 > destination.Length) { bytesWritten = 0; return false; } bytesWritten = num3; if (insertNegationSign) { destination[0] = 45; destination = destination.Slice(1); } if (num2 > 0) { System.Buffers.Text.FormattingHelpers.FillWithAsciiZeros(destination.Slice(0, num2)); } System.Buffers.Text.FormattingHelpers.WriteDigits(value, destination.Slice(num2, num)); return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryFormatUInt64Default(ulong value, Span destination, out int bytesWritten) { if (value < 10) { return TryFormatUInt32SingleDigit((uint)value, destination, out bytesWritten); } if (IntPtr.Size == 8) { return TryFormatUInt64MultipleDigits(value, destination, out bytesWritten); } if (value <= uint.MaxValue) { return TryFormatUInt32MultipleDigits((uint)value, destination, out bytesWritten); } if (value <= 4294967295000000000L) { return TryFormatUInt64LessThanBillionMaxUInt(value, destination, out bytesWritten); } return TryFormatUInt64MoreThanBillionMaxUInt(value, destination, out bytesWritten); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryFormatUInt32Default(uint value, Span destination, out int bytesWritten) { if (value < 10) { return TryFormatUInt32SingleDigit(value, destination, out bytesWritten); } return TryFormatUInt32MultipleDigits(value, destination, out bytesWritten); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryFormatUInt32SingleDigit(uint value, Span destination, out int bytesWritten) { if (destination.Length == 0) { bytesWritten = 0; return false; } destination[0] = (byte)(48 + value); bytesWritten = 1; return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryFormatUInt32MultipleDigits(uint value, Span destination, out int bytesWritten) { int num = System.Buffers.Text.FormattingHelpers.CountDigits(value); if (num > destination.Length) { bytesWritten = 0; return false; } bytesWritten = num; System.Buffers.Text.FormattingHelpers.WriteDigits(value, destination.Slice(0, num)); return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryFormatUInt64SingleDigit(ulong value, Span destination, out int bytesWritten) { if (destination.Length == 0) { bytesWritten = 0; return false; } destination[0] = (byte)(48 + value); bytesWritten = 1; return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryFormatUInt64MultipleDigits(ulong value, Span destination, out int bytesWritten) { int num = System.Buffers.Text.FormattingHelpers.CountDigits(value); if (num > destination.Length) { bytesWritten = 0; return false; } bytesWritten = num; System.Buffers.Text.FormattingHelpers.WriteDigits(value, destination.Slice(0, num)); return true; } private static bool TryFormatUInt64LessThanBillionMaxUInt(ulong value, Span destination, out int bytesWritten) { uint num = (uint)(value / 1000000000); uint value2 = (uint)(value - num * 1000000000); int num2 = System.Buffers.Text.FormattingHelpers.CountDigits(num); int num3 = num2 + 9; if (num3 > destination.Length) { bytesWritten = 0; return false; } bytesWritten = num3; System.Buffers.Text.FormattingHelpers.WriteDigits(num, destination.Slice(0, num2)); System.Buffers.Text.FormattingHelpers.WriteDigits(value2, destination.Slice(num2, 9)); return true; } private static bool TryFormatUInt64MoreThanBillionMaxUInt(ulong value, Span destination, out int bytesWritten) { ulong num = value / 1000000000; uint value2 = (uint)(value - num * 1000000000); uint num2 = (uint)(num / 1000000000); uint value3 = (uint)(num - num2 * 1000000000); int num3 = System.Buffers.Text.FormattingHelpers.CountDigits(num2); int num4 = num3 + 18; if (num4 > destination.Length) { bytesWritten = 0; return false; } bytesWritten = num4; System.Buffers.Text.FormattingHelpers.WriteDigits(num2, destination.Slice(0, num3)); System.Buffers.Text.FormattingHelpers.WriteDigits(value3, destination.Slice(num3, 9)); System.Buffers.Text.FormattingHelpers.WriteDigits(value2, destination.Slice(num3 + 9, 9)); return true; } private static bool TryFormatUInt64N(ulong value, byte precision, Span destination, bool insertNegationSign, out int bytesWritten) { int num = System.Buffers.Text.FormattingHelpers.CountDigits(value); int num2 = (num - 1) / 3; int num3 = ((precision == byte.MaxValue) ? 2 : precision); int num4 = num + num2; if (num3 > 0) { num4 += num3 + 1; } if (insertNegationSign) { num4++; } if (num4 > destination.Length) { bytesWritten = 0; return false; } bytesWritten = num4; if (insertNegationSign) { destination[0] = 45; destination = destination.Slice(1); } System.Buffers.Text.FormattingHelpers.WriteDigitsWithGroupSeparator(value, destination.Slice(0, num + num2)); if (num3 > 0) { destination[num + num2] = 46; System.Buffers.Text.FormattingHelpers.FillWithAsciiZeros(destination.Slice(num + num2 + 1, num3)); } return true; } private static bool TryFormatUInt64X(ulong value, byte precision, bool useLower, Span destination, out int bytesWritten) { int num = System.Buffers.Text.FormattingHelpers.CountHexDigits(value); int num2 = ((precision == byte.MaxValue) ? num : Math.Max(precision, num)); if (destination.Length < num2) { bytesWritten = 0; return false; } bytesWritten = num2; string text = (useLower ? "0123456789abcdef" : "0123456789ABCDEF"); while ((uint)(--num2) < (uint)destination.Length) { destination[num2] = (byte)text[(int)value & 0xF]; value >>= 4; } return true; } public static bool TryFormat(TimeSpan value, Span destination, out int bytesWritten, StandardFormat format = default(StandardFormat)) { char c = System.Buffers.Text.FormattingHelpers.GetSymbolOrDefault(ref format, 'c'); switch (c) { case 'T': case 't': c = 'c'; break; default: return System.ThrowHelper.TryFormatThrowFormatException(out bytesWritten); case 'G': case 'c': case 'g': break; } int num = 8; long ticks = value.Ticks; uint valueWithoutTrailingZeros; ulong num2; if (ticks < 0) { ticks = -ticks; if (ticks < 0) { valueWithoutTrailingZeros = 4775808u; num2 = 922337203685uL; goto IL_0082; } } num2 = System.Buffers.Text.FormattingHelpers.DivMod((ulong)Math.Abs(value.Ticks), 10000000uL, out var modulo); valueWithoutTrailingZeros = (uint)modulo; goto IL_0082; IL_0082: int num3 = 0; switch (c) { case 'c': if (valueWithoutTrailingZeros != 0) { num3 = 7; } break; case 'G': num3 = 7; break; default: if (valueWithoutTrailingZeros != 0) { num3 = 7 - System.Buffers.Text.FormattingHelpers.CountDecimalTrailingZeros(valueWithoutTrailingZeros, out valueWithoutTrailingZeros); } break; } if (num3 != 0) { num += num3 + 1; } ulong num4 = 0uL; ulong modulo2 = 0uL; if (num2 != 0) { num4 = System.Buffers.Text.FormattingHelpers.DivMod(num2, 60uL, out modulo2); } ulong num5 = 0uL; ulong modulo3 = 0uL; if (num4 != 0) { num5 = System.Buffers.Text.FormattingHelpers.DivMod(num4, 60uL, out modulo3); } uint num6 = 0u; uint modulo4 = 0u; if (num5 != 0) { num6 = System.Buffers.Text.FormattingHelpers.DivMod((uint)num5, 24u, out modulo4); } int num7 = 2; if (modulo4 < 10 && c == 'g') { num7--; num--; } int num8 = 0; if (num6 == 0) { if (c == 'G') { num += 2; num8 = 1; } } else { num8 = System.Buffers.Text.FormattingHelpers.CountDigits(num6); num += num8 + 1; } if (value.Ticks < 0) { num++; } if (destination.Length < num) { bytesWritten = 0; return false; } bytesWritten = num; int num9 = 0; if (value.Ticks < 0) { destination[num9++] = 45; } if (num8 > 0) { System.Buffers.Text.FormattingHelpers.WriteDigits(num6, destination.Slice(num9, num8)); num9 += num8; destination[num9++] = (byte)((c == 'c') ? 46 : 58); } System.Buffers.Text.FormattingHelpers.WriteDigits(modulo4, destination.Slice(num9, num7)); num9 += num7; destination[num9++] = 58; System.Buffers.Text.FormattingHelpers.WriteDigits((uint)modulo3, destination.Slice(num9, 2)); num9 += 2; destination[num9++] = 58; System.Buffers.Text.FormattingHelpers.WriteDigits((uint)modulo2, destination.Slice(num9, 2)); num9 += 2; if (num3 > 0) { destination[num9++] = 46; System.Buffers.Text.FormattingHelpers.WriteDigits(valueWithoutTrailingZeros, destination.Slice(num9, num3)); num9 += num3; } return true; } } internal static class ParserHelpers { public const int ByteOverflowLength = 3; public const int ByteOverflowLengthHex = 2; public const int UInt16OverflowLength = 5; public const int UInt16OverflowLengthHex = 4; public const int UInt32OverflowLength = 10; public const int UInt32OverflowLengthHex = 8; public const int UInt64OverflowLength = 20; public const int UInt64OverflowLengthHex = 16; public const int SByteOverflowLength = 3; public const int SByteOverflowLengthHex = 2; public const int Int16OverflowLength = 5; public const int Int16OverflowLengthHex = 4; public const int Int32OverflowLength = 10; public const int Int32OverflowLengthHex = 8; public const int Int64OverflowLength = 19; public const int Int64OverflowLengthHex = 16; public static readonly byte[] s_hexLookup = new byte[256] { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsDigit(int i) { return (uint)(i - 48) <= 9u; } } internal static class Utf8Parser { [Flags] private enum ParseNumberOptions { AllowExponent = 1 } private enum ComponentParseResult : byte { NoMoreData, Colon, Period, ParseFailure } private struct TimeSpanSplitter { public uint V1; public uint V2; public uint V3; public uint V4; public uint V5; public bool IsNegative; public uint Separators; public bool TrySplitTimeSpan(ReadOnlySpan source, bool periodUsedToSeparateDay, out int bytesConsumed) { int i = 0; byte b = 0; for (; i != source.Length; i++) { b = source[i]; if (b != 32 && b != 9) { break; } } if (i == source.Length) { bytesConsumed = 0; return false; } if (b == 45) { IsNegative = true; i++; if (i == source.Length) { bytesConsumed = 0; return false; } } if (!TryParseUInt32D(source.Slice(i), out V1, out var bytesConsumed2)) { bytesConsumed = 0; return false; } i += bytesConsumed2; ComponentParseResult componentParseResult = ParseComponent(source, periodUsedToSeparateDay, ref i, out V2); switch (componentParseResult) { case ComponentParseResult.ParseFailure: bytesConsumed = 0; return false; case ComponentParseResult.NoMoreData: bytesConsumed = i; return true; default: Separators |= (uint)componentParseResult << 24; componentParseResult = ParseComponent(source, neverParseAsFraction: false, ref i, out V3); switch (componentParseResult) { case ComponentParseResult.ParseFailure: bytesConsumed = 0; return false; case ComponentParseResult.NoMoreData: bytesConsumed = i; return true; default: Separators |= (uint)componentParseResult << 16; componentParseResult = ParseComponent(source, neverParseAsFraction: false, ref i, out V4); switch (componentParseResult) { case ComponentParseResult.ParseFailure: bytesConsumed = 0; return false; case ComponentParseResult.NoMoreData: bytesConsumed = i; return true; default: Separators |= (uint)componentParseResult << 8; componentParseResult = ParseComponent(source, neverParseAsFraction: false, ref i, out V5); switch (componentParseResult) { case ComponentParseResult.ParseFailure: bytesConsumed = 0; return false; case ComponentParseResult.NoMoreData: bytesConsumed = i; return true; default: Separators |= (uint)componentParseResult; if (i != source.Length && (source[i] == 46 || source[i] == 58)) { bytesConsumed = 0; return false; } bytesConsumed = i; return true; } } } } } private static ComponentParseResult ParseComponent(ReadOnlySpan source, bool neverParseAsFraction, ref int srcIndex, out uint value) { if (srcIndex == source.Length) { value = 0u; return ComponentParseResult.NoMoreData; } byte b = source[srcIndex]; if (b == 58 || (b == 46 && neverParseAsFraction)) { srcIndex++; if (!TryParseUInt32D(source.Slice(srcIndex), out value, out var bytesConsumed)) { value = 0u; return ComponentParseResult.ParseFailure; } srcIndex += bytesConsumed; if (b != 58) { return ComponentParseResult.Period; } return ComponentParseResult.Colon; } if (b == 46) { srcIndex++; if (!TryParseTimeSpanFraction(source.Slice(srcIndex), out value, out var bytesConsumed2)) { value = 0u; return ComponentParseResult.ParseFailure; } srcIndex += bytesConsumed2; return ComponentParseResult.Period; } value = 0u; return ComponentParseResult.NoMoreData; } } private const uint FlipCase = 32u; private const uint NoFlipCase = 0u; private static readonly int[] s_daysToMonth365 = new int[13] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; private static readonly int[] s_daysToMonth366 = new int[13] { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; public static bool TryParse(ReadOnlySpan source, out bool value, out int bytesConsumed, char standardFormat = '\0') { if (standardFormat != 0 && standardFormat != 'G' && standardFormat != 'l') { return System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); } if (source.Length >= 4) { if ((source[0] == 84 || source[0] == 116) && (source[1] == 82 || source[1] == 114) && (source[2] == 85 || source[2] == 117) && (source[3] == 69 || source[3] == 101)) { bytesConsumed = 4; value = true; return true; } if (source.Length >= 5 && (source[0] == 70 || source[0] == 102) && (source[1] == 65 || source[1] == 97) && (source[2] == 76 || source[2] == 108) && (source[3] == 83 || source[3] == 115) && (source[4] == 69 || source[4] == 101)) { bytesConsumed = 5; value = false; return true; } } bytesConsumed = 0; value = false; return false; } public static bool TryParse(ReadOnlySpan source, out DateTime value, out int bytesConsumed, char standardFormat = '\0') { switch (standardFormat) { case 'R': { if (!TryParseDateTimeOffsetR(source, 0u, out var dateTimeOffset, out bytesConsumed)) { value = default(DateTime); return false; } value = dateTimeOffset.DateTime; return true; } case 'l': { if (!TryParseDateTimeOffsetR(source, 32u, out var dateTimeOffset2, out bytesConsumed)) { value = default(DateTime); return false; } value = dateTimeOffset2.DateTime; return true; } case 'O': { if (!TryParseDateTimeOffsetO(source, out var value2, out bytesConsumed, out var kind)) { value = default(DateTime); bytesConsumed = 0; return false; } switch (kind) { case DateTimeKind.Local: value = value2.LocalDateTime; break; case DateTimeKind.Utc: value = value2.UtcDateTime; break; default: value = value2.DateTime; break; } return true; } case '\0': case 'G': { DateTimeOffset valueAsOffset; return TryParseDateTimeG(source, out value, out valueAsOffset, out bytesConsumed); } default: return System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); } } public static bool TryParse(ReadOnlySpan source, out DateTimeOffset value, out int bytesConsumed, char standardFormat = '\0') { DateTimeKind kind; DateTime value2; return standardFormat switch { 'R' => TryParseDateTimeOffsetR(source, 0u, out value, out bytesConsumed), 'l' => TryParseDateTimeOffsetR(source, 32u, out value, out bytesConsumed), 'O' => TryParseDateTimeOffsetO(source, out value, out bytesConsumed, out kind), '\0' => TryParseDateTimeOffsetDefault(source, out value, out bytesConsumed), 'G' => TryParseDateTimeG(source, out value2, out value, out bytesConsumed), _ => System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed), }; } private static bool TryParseDateTimeOffsetDefault(ReadOnlySpan source, out DateTimeOffset value, out int bytesConsumed) { if (source.Length < 26) { bytesConsumed = 0; value = default(DateTimeOffset); return false; } if (!TryParseDateTimeG(source, out var value2, out var _, out var _)) { bytesConsumed = 0; value = default(DateTimeOffset); return false; } if (source[19] != 32) { bytesConsumed = 0; value = default(DateTimeOffset); return false; } byte b = source[20]; if (b != 43 && b != 45) { bytesConsumed = 0; value = default(DateTimeOffset); return false; } uint num = (uint)(source[21] - 48); uint num2 = (uint)(source[22] - 48); if (num > 9 || num2 > 9) { bytesConsumed = 0; value = default(DateTimeOffset); return false; } int num3 = (int)(num * 10 + num2); if (source[23] != 58) { bytesConsumed = 0; value = default(DateTimeOffset); return false; } uint num4 = (uint)(source[24] - 48); uint num5 = (uint)(source[25] - 48); if (num4 > 9 || num5 > 9) { bytesConsumed = 0; value = default(DateTimeOffset); return false; } int num6 = (int)(num4 * 10 + num5); TimeSpan timeSpan = new TimeSpan(num3, num6, 0); if (b == 45) { timeSpan = -timeSpan; } if (!TryCreateDateTimeOffset(value2, b == 45, num3, num6, out value)) { bytesConsumed = 0; value = default(DateTimeOffset); return false; } bytesConsumed = 26; return true; } private static bool TryParseDateTimeG(ReadOnlySpan source, out DateTime value, out DateTimeOffset valueAsOffset, out int bytesConsumed) { if (source.Length < 19) { bytesConsumed = 0; value = default(DateTime); valueAsOffset = default(DateTimeOffset); return false; } uint num = (uint)(source[0] - 48); uint num2 = (uint)(source[1] - 48); if (num > 9 || num2 > 9) { bytesConsumed = 0; value = default(DateTime); valueAsOffset = default(DateTimeOffset); return false; } int month = (int)(num * 10 + num2); if (source[2] != 47) { bytesConsumed = 0; value = default(DateTime); valueAsOffset = default(DateTimeOffset); return false; } uint num3 = (uint)(source[3] - 48); uint num4 = (uint)(source[4] - 48); if (num3 > 9 || num4 > 9) { bytesConsumed = 0; value = default(DateTime); valueAsOffset = default(DateTimeOffset); return false; } int day = (int)(num3 * 10 + num4); if (source[5] != 47) { bytesConsumed = 0; value = default(DateTime); valueAsOffset = default(DateTimeOffset); return false; } uint num5 = (uint)(source[6] - 48); uint num6 = (uint)(source[7] - 48); uint num7 = (uint)(source[8] - 48); uint num8 = (uint)(source[9] - 48); if (num5 > 9 || num6 > 9 || num7 > 9 || num8 > 9) { bytesConsumed = 0; value = default(DateTime); valueAsOffset = default(DateTimeOffset); return false; } int year = (int)(num5 * 1000 + num6 * 100 + num7 * 10 + num8); if (source[10] != 32) { bytesConsumed = 0; value = default(DateTime); valueAsOffset = default(DateTimeOffset); return false; } uint num9 = (uint)(source[11] - 48); uint num10 = (uint)(source[12] - 48); if (num9 > 9 || num10 > 9) { bytesConsumed = 0; value = default(DateTime); valueAsOffset = default(DateTimeOffset); return false; } int hour = (int)(num9 * 10 + num10); if (source[13] != 58) { bytesConsumed = 0; value = default(DateTime); valueAsOffset = default(DateTimeOffset); return false; } uint num11 = (uint)(source[14] - 48); uint num12 = (uint)(source[15] - 48); if (num11 > 9 || num12 > 9) { bytesConsumed = 0; value = default(DateTime); valueAsOffset = default(DateTimeOffset); return false; } int minute = (int)(num11 * 10 + num12); if (source[16] != 58) { bytesConsumed = 0; value = default(DateTime); valueAsOffset = default(DateTimeOffset); return false; } uint num13 = (uint)(source[17] - 48); uint num14 = (uint)(source[18] - 48); if (num13 > 9 || num14 > 9) { bytesConsumed = 0; value = default(DateTime); valueAsOffset = default(DateTimeOffset); return false; } int second = (int)(num13 * 10 + num14); if (!TryCreateDateTimeOffsetInterpretingDataAsLocalTime(year, month, day, hour, minute, second, 0, out valueAsOffset)) { bytesConsumed = 0; value = default(DateTime); valueAsOffset = default(DateTimeOffset); return false; } bytesConsumed = 19; value = valueAsOffset.DateTime; return true; } private static bool TryCreateDateTimeOffset(DateTime dateTime, bool offsetNegative, int offsetHours, int offsetMinutes, out DateTimeOffset value) { if ((uint)offsetHours > 14u) { value = default(DateTimeOffset); return false; } if ((uint)offsetMinutes > 59u) { value = default(DateTimeOffset); return false; } if (offsetHours == 14 && offsetMinutes != 0) { value = default(DateTimeOffset); return false; } long num = ((long)offsetHours * 3600L + (long)offsetMinutes * 60L) * 10000000; if (offsetNegative) { num = -num; } try { value = new DateTimeOffset(dateTime.Ticks, new TimeSpan(num)); } catch (ArgumentOutOfRangeException) { value = default(DateTimeOffset); return false; } return true; } private static bool TryCreateDateTimeOffset(int year, int month, int day, int hour, int minute, int second, int fraction, bool offsetNegative, int offsetHours, int offsetMinutes, out DateTimeOffset value) { if (!TryCreateDateTime(year, month, day, hour, minute, second, fraction, DateTimeKind.Unspecified, out var value2)) { value = default(DateTimeOffset); return false; } if (!TryCreateDateTimeOffset(value2, offsetNegative, offsetHours, offsetMinutes, out value)) { value = default(DateTimeOffset); return false; } return true; } private static bool TryCreateDateTimeOffsetInterpretingDataAsLocalTime(int year, int month, int day, int hour, int minute, int second, int fraction, out DateTimeOffset value) { if (!TryCreateDateTime(year, month, day, hour, minute, second, fraction, DateTimeKind.Local, out var value2)) { value = default(DateTimeOffset); return false; } try { value = new DateTimeOffset(value2); } catch (ArgumentOutOfRangeException) { value = default(DateTimeOffset); return false; } return true; } private static bool TryCreateDateTime(int year, int month, int day, int hour, int minute, int second, int fraction, DateTimeKind kind, out DateTime value) { if (year == 0) { value = default(DateTime); return false; } if ((uint)(month - 1) >= 12u) { value = default(DateTime); return false; } uint num = (uint)(day - 1); if (num >= 28 && num >= DateTime.DaysInMonth(year, month)) { value = default(DateTime); return false; } if ((uint)hour > 23u) { value = default(DateTime); return false; } if ((uint)minute > 59u) { value = default(DateTime); return false; } if ((uint)second > 59u) { value = default(DateTime); return false; } int[] array = (DateTime.IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365); int num2 = year - 1; int num3 = num2 * 365 + num2 / 4 - num2 / 100 + num2 / 400 + array[month - 1] + day - 1; long num4 = num3 * 864000000000L; int num5 = hour * 3600 + minute * 60 + second; num4 += (long)num5 * 10000000L; num4 += fraction; value = new DateTime(num4, kind); return true; } private static bool TryParseDateTimeOffsetO(ReadOnlySpan source, out DateTimeOffset value, out int bytesConsumed, out DateTimeKind kind) { if (source.Length < 27) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } uint num = (uint)(source[0] - 48); uint num2 = (uint)(source[1] - 48); uint num3 = (uint)(source[2] - 48); uint num4 = (uint)(source[3] - 48); if (num > 9 || num2 > 9 || num3 > 9 || num4 > 9) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } int year = (int)(num * 1000 + num2 * 100 + num3 * 10 + num4); if (source[4] != 45) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } uint num5 = (uint)(source[5] - 48); uint num6 = (uint)(source[6] - 48); if (num5 > 9 || num6 > 9) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } int month = (int)(num5 * 10 + num6); if (source[7] != 45) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } uint num7 = (uint)(source[8] - 48); uint num8 = (uint)(source[9] - 48); if (num7 > 9 || num8 > 9) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } int day = (int)(num7 * 10 + num8); if (source[10] != 84) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } uint num9 = (uint)(source[11] - 48); uint num10 = (uint)(source[12] - 48); if (num9 > 9 || num10 > 9) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } int hour = (int)(num9 * 10 + num10); if (source[13] != 58) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } uint num11 = (uint)(source[14] - 48); uint num12 = (uint)(source[15] - 48); if (num11 > 9 || num12 > 9) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } int minute = (int)(num11 * 10 + num12); if (source[16] != 58) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } uint num13 = (uint)(source[17] - 48); uint num14 = (uint)(source[18] - 48); if (num13 > 9 || num14 > 9) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } int second = (int)(num13 * 10 + num14); if (source[19] != 46) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } uint num15 = (uint)(source[20] - 48); uint num16 = (uint)(source[21] - 48); uint num17 = (uint)(source[22] - 48); uint num18 = (uint)(source[23] - 48); uint num19 = (uint)(source[24] - 48); uint num20 = (uint)(source[25] - 48); uint num21 = (uint)(source[26] - 48); if (num15 > 9 || num16 > 9 || num17 > 9 || num18 > 9 || num19 > 9 || num20 > 9 || num21 > 9) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } int fraction = (int)(num15 * 1000000 + num16 * 100000 + num17 * 10000 + num18 * 1000 + num19 * 100 + num20 * 10 + num21); byte b = (byte)((source.Length > 27) ? source[27] : 0); if (b != 90 && b != 43 && b != 45) { if (!TryCreateDateTimeOffsetInterpretingDataAsLocalTime(year, month, day, hour, minute, second, fraction, out value)) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } bytesConsumed = 27; kind = DateTimeKind.Unspecified; return true; } if (b == 90) { if (!TryCreateDateTimeOffset(year, month, day, hour, minute, second, fraction, offsetNegative: false, 0, 0, out value)) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } bytesConsumed = 28; kind = DateTimeKind.Utc; return true; } if (source.Length < 33) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } uint num22 = (uint)(source[28] - 48); uint num23 = (uint)(source[29] - 48); if (num22 > 9 || num23 > 9) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } int offsetHours = (int)(num22 * 10 + num23); if (source[30] != 58) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } uint num24 = (uint)(source[31] - 48); uint num25 = (uint)(source[32] - 48); if (num24 > 9 || num25 > 9) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } int offsetMinutes = (int)(num24 * 10 + num25); if (!TryCreateDateTimeOffset(year, month, day, hour, minute, second, fraction, b == 45, offsetHours, offsetMinutes, out value)) { value = default(DateTimeOffset); bytesConsumed = 0; kind = DateTimeKind.Unspecified; return false; } bytesConsumed = 33; kind = DateTimeKind.Local; return true; } private static bool TryParseDateTimeOffsetR(ReadOnlySpan source, uint caseFlipXorMask, out DateTimeOffset dateTimeOffset, out int bytesConsumed) { if (source.Length < 29) { bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } uint num = source[0] ^ caseFlipXorMask; uint num2 = source[1]; uint num3 = source[2]; uint num4 = source[3]; DayOfWeek dayOfWeek; switch ((num << 24) | (num2 << 16) | (num3 << 8) | num4) { case 1400204844u: dayOfWeek = DayOfWeek.Sunday; break; case 1299148332u: dayOfWeek = DayOfWeek.Monday; break; case 1416979756u: dayOfWeek = DayOfWeek.Tuesday; break; case 1466262572u: dayOfWeek = DayOfWeek.Wednesday; break; case 1416131884u: dayOfWeek = DayOfWeek.Thursday; break; case 1181903148u: dayOfWeek = DayOfWeek.Friday; break; case 1398895660u: dayOfWeek = DayOfWeek.Saturday; break; default: bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } if (source[4] != 32) { bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } uint num5 = (uint)(source[5] - 48); uint num6 = (uint)(source[6] - 48); if (num5 > 9 || num6 > 9) { bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } int day = (int)(num5 * 10 + num6); if (source[7] != 32) { bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } uint num7 = source[8] ^ caseFlipXorMask; uint num8 = source[9]; uint num9 = source[10]; uint num10 = source[11]; int month; switch ((num7 << 24) | (num8 << 16) | (num9 << 8) | num10) { case 1247899168u: month = 1; break; case 1181049376u: month = 2; break; case 1298231840u: month = 3; break; case 1097888288u: month = 4; break; case 1298233632u: month = 5; break; case 1249209888u: month = 6; break; case 1249209376u: month = 7; break; case 1098213152u: month = 8; break; case 1399156768u: month = 9; break; case 1331917856u: month = 10; break; case 1315927584u: month = 11; break; case 1147495200u: month = 12; break; default: bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } uint num11 = (uint)(source[12] - 48); uint num12 = (uint)(source[13] - 48); uint num13 = (uint)(source[14] - 48); uint num14 = (uint)(source[15] - 48); if (num11 > 9 || num12 > 9 || num13 > 9 || num14 > 9) { bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } int year = (int)(num11 * 1000 + num12 * 100 + num13 * 10 + num14); if (source[16] != 32) { bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } uint num15 = (uint)(source[17] - 48); uint num16 = (uint)(source[18] - 48); if (num15 > 9 || num16 > 9) { bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } int hour = (int)(num15 * 10 + num16); if (source[19] != 58) { bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } uint num17 = (uint)(source[20] - 48); uint num18 = (uint)(source[21] - 48); if (num17 > 9 || num18 > 9) { bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } int minute = (int)(num17 * 10 + num18); if (source[22] != 58) { bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } uint num19 = (uint)(source[23] - 48); uint num20 = (uint)(source[24] - 48); if (num19 > 9 || num20 > 9) { bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } int second = (int)(num19 * 10 + num20); uint num21 = source[25]; uint num22 = source[26] ^ caseFlipXorMask; uint num23 = source[27] ^ caseFlipXorMask; uint num24 = source[28] ^ caseFlipXorMask; uint num25 = (num21 << 24) | (num22 << 16) | (num23 << 8) | num24; if (num25 != 541543764) { bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } if (!TryCreateDateTimeOffset(year, month, day, hour, minute, second, 0, offsetNegative: false, 0, 0, out dateTimeOffset)) { bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } if (dayOfWeek != dateTimeOffset.DayOfWeek) { bytesConsumed = 0; dateTimeOffset = default(DateTimeOffset); return false; } bytesConsumed = 29; return true; } public static bool TryParse(ReadOnlySpan source, out decimal value, out int bytesConsumed, char standardFormat = '\0') { ParseNumberOptions options; switch (standardFormat) { case '\0': case 'E': case 'G': case 'e': case 'g': options = ParseNumberOptions.AllowExponent; break; case 'F': case 'f': options = (ParseNumberOptions)0; break; default: return System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); } NumberBuffer number = default(NumberBuffer); if (!TryParseNumber(source, ref number, out bytesConsumed, options, out var textUsedExponentNotation)) { value = default(decimal); return false; } if (!textUsedExponentNotation && (standardFormat == 'E' || standardFormat == 'e')) { value = default(decimal); bytesConsumed = 0; return false; } if (number.Digits[0] == 0 && number.Scale == 0) { number.IsNegative = false; } value = default(decimal); if (!System.Number.NumberBufferToDecimal(ref number, ref value)) { value = default(decimal); bytesConsumed = 0; return false; } return true; } public static bool TryParse(ReadOnlySpan source, out float value, out int bytesConsumed, char standardFormat = '\0') { if (TryParseNormalAsFloatingPoint(source, out var value2, out bytesConsumed, standardFormat)) { value = (float)value2; if (float.IsInfinity(value)) { value = 0f; bytesConsumed = 0; return false; } return true; } return TryParseAsSpecialFloatingPoint(source, float.PositiveInfinity, float.NegativeInfinity, float.NaN, out value, out bytesConsumed); } public static bool TryParse(ReadOnlySpan source, out double value, out int bytesConsumed, char standardFormat = '\0') { if (TryParseNormalAsFloatingPoint(source, out value, out bytesConsumed, standardFormat)) { return true; } return TryParseAsSpecialFloatingPoint(source, double.PositiveInfinity, double.NegativeInfinity, double.NaN, out value, out bytesConsumed); } private static bool TryParseNormalAsFloatingPoint(ReadOnlySpan source, out double value, out int bytesConsumed, char standardFormat) { ParseNumberOptions options; switch (standardFormat) { case '\0': case 'E': case 'G': case 'e': case 'g': options = ParseNumberOptions.AllowExponent; break; case 'F': case 'f': options = (ParseNumberOptions)0; break; default: return System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); } NumberBuffer number = default(NumberBuffer); if (!TryParseNumber(source, ref number, out bytesConsumed, options, out var textUsedExponentNotation)) { value = 0.0; return false; } if (!textUsedExponentNotation && (standardFormat == 'E' || standardFormat == 'e')) { value = 0.0; bytesConsumed = 0; return false; } if (number.Digits[0] == 0) { number.IsNegative = false; } if (!System.Number.NumberBufferToDouble(ref number, out value)) { value = 0.0; bytesConsumed = 0; return false; } return true; } private static bool TryParseAsSpecialFloatingPoint(ReadOnlySpan source, T positiveInfinity, T negativeInfinity, T nan, out T value, out int bytesConsumed) { if (source.Length >= 8 && source[0] == 73 && source[1] == 110 && source[2] == 102 && source[3] == 105 && source[4] == 110 && source[5] == 105 && source[6] == 116 && source[7] == 121) { value = positiveInfinity; bytesConsumed = 8; return true; } if (source.Length >= 9 && source[0] == 45 && source[1] == 73 && source[2] == 110 && source[3] == 102 && source[4] == 105 && source[5] == 110 && source[6] == 105 && source[7] == 116 && source[8] == 121) { value = negativeInfinity; bytesConsumed = 9; return true; } if (source.Length >= 3 && source[0] == 78 && source[1] == 97 && source[2] == 78) { value = nan; bytesConsumed = 3; return true; } value = default(T); bytesConsumed = 0; return false; } public static bool TryParse(ReadOnlySpan source, out Guid value, out int bytesConsumed, char standardFormat = '\0') { switch (standardFormat) { case '\0': case 'D': return TryParseGuidCore(source, ends: false, ' ', ' ', out value, out bytesConsumed); case 'B': return TryParseGuidCore(source, ends: true, '{', '}', out value, out bytesConsumed); case 'P': return TryParseGuidCore(source, ends: true, '(', ')', out value, out bytesConsumed); case 'N': return TryParseGuidN(source, out value, out bytesConsumed); default: return System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); } } private static bool TryParseGuidN(ReadOnlySpan text, out Guid value, out int bytesConsumed) { if (text.Length < 32) { value = default(Guid); bytesConsumed = 0; return false; } if (!TryParseUInt32X(text.Slice(0, 8), out var value2, out var bytesConsumed2) || bytesConsumed2 != 8) { value = default(Guid); bytesConsumed = 0; return false; } if (!TryParseUInt16X(text.Slice(8, 4), out var value3, out bytesConsumed2) || bytesConsumed2 != 4) { value = default(Guid); bytesConsumed = 0; return false; } if (!TryParseUInt16X(text.Slice(12, 4), out var value4, out bytesConsumed2) || bytesConsumed2 != 4) { value = default(Guid); bytesConsumed = 0; return false; } if (!TryParseUInt16X(text.Slice(16, 4), out var value5, out bytesConsumed2) || bytesConsumed2 != 4) { value = default(Guid); bytesConsumed = 0; return false; } if (!TryParseUInt64X(text.Slice(20), out var value6, out bytesConsumed2) || bytesConsumed2 != 12) { value = default(Guid); bytesConsumed = 0; return false; } bytesConsumed = 32; value = new Guid((int)value2, (short)value3, (short)value4, (byte)(value5 >> 8), (byte)value5, (byte)(value6 >> 40), (byte)(value6 >> 32), (byte)(value6 >> 24), (byte)(value6 >> 16), (byte)(value6 >> 8), (byte)value6); return true; } private static bool TryParseGuidCore(ReadOnlySpan source, bool ends, char begin, char end, out Guid value, out int bytesConsumed) { int num = 36 + (ends ? 2 : 0); if (source.Length < num) { value = default(Guid); bytesConsumed = 0; return false; } if (ends) { if (source[0] != begin) { value = default(Guid); bytesConsumed = 0; return false; } source = source.Slice(1); } if (!TryParseUInt32X(source, out var value2, out var bytesConsumed2)) { value = default(Guid); bytesConsumed = 0; return false; } if (bytesConsumed2 != 8) { value = default(Guid); bytesConsumed = 0; return false; } if (source[bytesConsumed2] != 45) { value = default(Guid); bytesConsumed = 0; return false; } source = source.Slice(9); if (!TryParseUInt16X(source, out var value3, out bytesConsumed2)) { value = default(Guid); bytesConsumed = 0; return false; } if (bytesConsumed2 != 4) { value = default(Guid); bytesConsumed = 0; return false; } if (source[bytesConsumed2] != 45) { value = default(Guid); bytesConsumed = 0; return false; } source = source.Slice(5); if (!TryParseUInt16X(source, out var value4, out bytesConsumed2)) { value = default(Guid); bytesConsumed = 0; return false; } if (bytesConsumed2 != 4) { value = default(Guid); bytesConsumed = 0; return false; } if (source[bytesConsumed2] != 45) { value = default(Guid); bytesConsumed = 0; return false; } source = source.Slice(5); if (!TryParseUInt16X(source, out var value5, out bytesConsumed2)) { value = default(Guid); bytesConsumed = 0; return false; } if (bytesConsumed2 != 4) { value = default(Guid); bytesConsumed = 0; return false; } if (source[bytesConsumed2] != 45) { value = default(Guid); bytesConsumed = 0; return false; } source = source.Slice(5); if (!TryParseUInt64X(source, out var value6, out bytesConsumed2)) { value = default(Guid); bytesConsumed = 0; return false; } if (bytesConsumed2 != 12) { value = default(Guid); bytesConsumed = 0; return false; } if (ends && source[bytesConsumed2] != end) { value = default(Guid); bytesConsumed = 0; return false; } bytesConsumed = num; value = new Guid((int)value2, (short)value3, (short)value4, (byte)(value5 >> 8), (byte)value5, (byte)(value6 >> 40), (byte)(value6 >> 32), (byte)(value6 >> 24), (byte)(value6 >> 16), (byte)(value6 >> 8), (byte)value6); return true; } [CLSCompliant(false)] public static bool TryParse(ReadOnlySpan source, out sbyte value, out int bytesConsumed, char standardFormat = '\0') { switch (standardFormat) { case '\0': case 'D': case 'G': case 'd': case 'g': return TryParseSByteD(source, out value, out bytesConsumed); case 'N': case 'n': return TryParseSByteN(source, out value, out bytesConsumed); case 'X': case 'x': value = 0; return TryParseByteX(source, out Unsafe.As(ref value), out bytesConsumed); default: return System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); } } public static bool TryParse(ReadOnlySpan source, out short value, out int bytesConsumed, char standardFormat = '\0') { switch (standardFormat) { case '\0': case 'D': case 'G': case 'd': case 'g': return TryParseInt16D(source, out value, out bytesConsumed); case 'N': case 'n': return TryParseInt16N(source, out value, out bytesConsumed); case 'X': case 'x': value = 0; return TryParseUInt16X(source, out Unsafe.As(ref value), out bytesConsumed); default: return System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); } } public static bool TryParse(ReadOnlySpan source, out int value, out int bytesConsumed, char standardFormat = '\0') { switch (standardFormat) { case '\0': case 'D': case 'G': case 'd': case 'g': return TryParseInt32D(source, out value, out bytesConsumed); case 'N': case 'n': return TryParseInt32N(source, out value, out bytesConsumed); case 'X': case 'x': value = 0; return TryParseUInt32X(source, out Unsafe.As(ref value), out bytesConsumed); default: return System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); } } public static bool TryParse(ReadOnlySpan source, out long value, out int bytesConsumed, char standardFormat = '\0') { switch (standardFormat) { case '\0': case 'D': case 'G': case 'd': case 'g': return TryParseInt64D(source, out value, out bytesConsumed); case 'N': case 'n': return TryParseInt64N(source, out value, out bytesConsumed); case 'X': case 'x': value = 0L; return TryParseUInt64X(source, out Unsafe.As(ref value), out bytesConsumed); default: return System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); } } private static bool TryParseSByteD(ReadOnlySpan source, out sbyte value, out int bytesConsumed) { int num; int num2; int num4; int num3; if (source.Length >= 1) { num = 1; num2 = 0; num3 = source[num2]; if (num3 == 45) { num = -1; num2++; if ((uint)num2 >= (uint)source.Length) { goto IL_0123; } num3 = source[num2]; } else if (num3 == 43) { num2++; if ((uint)num2 >= (uint)source.Length) { goto IL_0123; } num3 = source[num2]; } num4 = 0; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { if (num3 != 48) { goto IL_009c; } while (true) { num2++; if ((uint)num2 >= (uint)source.Length) { break; } num3 = source[num2]; if (num3 == 48) { continue; } goto IL_0091; } goto IL_012b; } } goto IL_0123; IL_0123: bytesConsumed = 0; value = 0; return false; IL_0091: if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { goto IL_009c; } goto IL_012b; IL_012b: bytesConsumed = num2; value = (sbyte)(num4 * num); return true; IL_009c: num4 = num3 - 48; num2++; if ((uint)num2 < (uint)source.Length) { num3 = source[num2]; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num2++; num4 = 10 * num4 + num3 - 48; if ((uint)num2 < (uint)source.Length) { num3 = source[num2]; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num2++; num4 = num4 * 10 + num3 - 48; if ((uint)num4 > 127L + (long)((-1 * num + 1) / 2) || ((uint)num2 < (uint)source.Length && System.Buffers.Text.ParserHelpers.IsDigit(source[num2]))) { goto IL_0123; } } } } } goto IL_012b; } private static bool TryParseInt16D(ReadOnlySpan source, out short value, out int bytesConsumed) { int num; int num2; int num4; int num3; if (source.Length >= 1) { num = 1; num2 = 0; num3 = source[num2]; if (num3 == 45) { num = -1; num2++; if ((uint)num2 >= (uint)source.Length) { goto IL_0186; } num3 = source[num2]; } else if (num3 == 43) { num2++; if ((uint)num2 >= (uint)source.Length) { goto IL_0186; } num3 = source[num2]; } num4 = 0; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { if (num3 != 48) { goto IL_009c; } while (true) { num2++; if ((uint)num2 >= (uint)source.Length) { break; } num3 = source[num2]; if (num3 == 48) { continue; } goto IL_0091; } goto IL_018e; } } goto IL_0186; IL_0186: bytesConsumed = 0; value = 0; return false; IL_0091: if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { goto IL_009c; } goto IL_018e; IL_018e: bytesConsumed = num2; value = (short)(num4 * num); return true; IL_009c: num4 = num3 - 48; num2++; if ((uint)num2 < (uint)source.Length) { num3 = source[num2]; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num2++; num4 = 10 * num4 + num3 - 48; if ((uint)num2 < (uint)source.Length) { num3 = source[num2]; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num2++; num4 = 10 * num4 + num3 - 48; if ((uint)num2 < (uint)source.Length) { num3 = source[num2]; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num2++; num4 = 10 * num4 + num3 - 48; if ((uint)num2 < (uint)source.Length) { num3 = source[num2]; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num2++; num4 = num4 * 10 + num3 - 48; if ((uint)num4 > 32767L + (long)((-1 * num + 1) / 2) || ((uint)num2 < (uint)source.Length && System.Buffers.Text.ParserHelpers.IsDigit(source[num2]))) { goto IL_0186; } } } } } } } } } goto IL_018e; } private static bool TryParseInt32D(ReadOnlySpan source, out int value, out int bytesConsumed) { int num; int num2; int num4; int num3; if (source.Length >= 1) { num = 1; num2 = 0; num3 = source[num2]; if (num3 == 45) { num = -1; num2++; if ((uint)num2 >= (uint)source.Length) { goto IL_0281; } num3 = source[num2]; } else if (num3 == 43) { num2++; if ((uint)num2 >= (uint)source.Length) { goto IL_0281; } num3 = source[num2]; } num4 = 0; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { if (num3 != 48) { goto IL_009c; } while (true) { num2++; if ((uint)num2 >= (uint)source.Length) { break; } num3 = source[num2]; if (num3 == 48) { continue; } goto IL_0091; } goto IL_0289; } } goto IL_0281; IL_0281: bytesConsumed = 0; value = 0; return false; IL_0091: if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { goto IL_009c; } goto IL_0289; IL_0289: bytesConsumed = num2; value = num4 * num; return true; IL_009c: num4 = num3 - 48; num2++; if ((uint)num2 < (uint)source.Length) { num3 = source[num2]; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num2++; num4 = 10 * num4 + num3 - 48; if ((uint)num2 < (uint)source.Length) { num3 = source[num2]; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num2++; num4 = 10 * num4 + num3 - 48; if ((uint)num2 < (uint)source.Length) { num3 = source[num2]; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num2++; num4 = 10 * num4 + num3 - 48; if ((uint)num2 < (uint)source.Length) { num3 = source[num2]; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num2++; num4 = 10 * num4 + num3 - 48; if ((uint)num2 < (uint)source.Length) { num3 = source[num2]; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num2++; num4 = 10 * num4 + num3 - 48; if ((uint)num2 < (uint)source.Length) { num3 = source[num2]; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num2++; num4 = 10 * num4 + num3 - 48; if ((uint)num2 < (uint)source.Length) { num3 = source[num2]; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num2++; num4 = 10 * num4 + num3 - 48; if ((uint)num2 < (uint)source.Length) { num3 = source[num2]; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num2++; num4 = 10 * num4 + num3 - 48; if ((uint)num2 < (uint)source.Length) { num3 = source[num2]; if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num2++; if (num4 <= 214748364) { num4 = num4 * 10 + num3 - 48; if ((uint)num4 <= 2147483647L + (long)((-1 * num + 1) / 2) && ((uint)num2 >= (uint)source.Length || !System.Buffers.Text.ParserHelpers.IsDigit(source[num2]))) { goto IL_0289; } } goto IL_0281; } } } } } } } } } } } } } } } } } } goto IL_0289; } private static bool TryParseInt64D(ReadOnlySpan source, out long value, out int bytesConsumed) { if (source.Length < 1) { bytesConsumed = 0; value = 0L; return false; } int num = 0; int num2 = 1; if (source[0] == 45) { num = 1; num2 = -1; if (source.Length <= num) { bytesConsumed = 0; value = 0L; return false; } } else if (source[0] == 43) { num = 1; if (source.Length <= num) { bytesConsumed = 0; value = 0L; return false; } } int num3 = 19 + num; long num4 = source[num] - 48; if (num4 < 0 || num4 > 9) { bytesConsumed = 0; value = 0L; return false; } ulong num5 = (ulong)num4; if (source.Length < num3) { for (int i = num + 1; i < source.Length; i++) { long num6 = source[i] - 48; if (num6 < 0 || num6 > 9) { bytesConsumed = i; value = (long)num5 * (long)num2; return true; } num5 = num5 * 10 + (ulong)num6; } } else { for (int j = num + 1; j < num3 - 1; j++) { long num7 = source[j] - 48; if (num7 < 0 || num7 > 9) { bytesConsumed = j; value = (long)num5 * (long)num2; return true; } num5 = num5 * 10 + (ulong)num7; } for (int k = num3 - 1; k < source.Length; k++) { long num8 = source[k] - 48; if (num8 < 0 || num8 > 9) { bytesConsumed = k; value = (long)num5 * (long)num2; return true; } bool flag = num2 > 0; bool flag2 = num8 > 8 || (flag && num8 > 7); if (num5 > 922337203685477580L || (num5 == 922337203685477580L && flag2)) { bytesConsumed = 0; value = 0L; return false; } num5 = num5 * 10 + (ulong)num8; } } bytesConsumed = source.Length; value = (long)num5 * (long)num2; return true; } private static bool TryParseSByteN(ReadOnlySpan source, out sbyte value, out int bytesConsumed) { int num; int num2; int num4; int num3; if (source.Length >= 1) { num = 1; num2 = 0; num3 = source[num2]; if (num3 == 45) { num = -1; num2++; if ((uint)num2 >= (uint)source.Length) { goto IL_00f9; } num3 = source[num2]; } else if (num3 == 43) { num2++; if ((uint)num2 >= (uint)source.Length) { goto IL_00f9; } num3 = source[num2]; } if (num3 != 46) { if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num4 = num3 - 48; while (true) { num2++; if ((uint)num2 >= (uint)source.Length) { break; } num3 = source[num2]; if (num3 == 44) { continue; } if (num3 == 46) { goto IL_00d4; } if (!System.Buffers.Text.ParserHelpers.IsDigit(num3)) { break; } num4 = num4 * 10 + num3 - 48; if (num4 <= 127 + (-1 * num + 1) / 2) { continue; } goto IL_00f9; } goto IL_0101; } } else { num4 = 0; num2++; if ((uint)num2 < (uint)source.Length && source[num2] == 48) { goto IL_00d4; } } } goto IL_00f9; IL_00f9: bytesConsumed = 0; value = 0; return false; IL_0101: bytesConsumed = num2; value = (sbyte)(num4 * num); return true; IL_00d4: while (true) { num2++; if ((uint)num2 >= (uint)source.Length) { break; } num3 = source[num2]; if (num3 == 48) { continue; } goto IL_00f1; } goto IL_0101; IL_00f1: if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { goto IL_00f9; } goto IL_0101; } private static bool TryParseInt16N(ReadOnlySpan source, out short value, out int bytesConsumed) { int num; int num2; int num4; int num3; if (source.Length >= 1) { num = 1; num2 = 0; num3 = source[num2]; if (num3 == 45) { num = -1; num2++; if ((uint)num2 >= (uint)source.Length) { goto IL_00ff; } num3 = source[num2]; } else if (num3 == 43) { num2++; if ((uint)num2 >= (uint)source.Length) { goto IL_00ff; } num3 = source[num2]; } if (num3 != 46) { if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num4 = num3 - 48; while (true) { num2++; if ((uint)num2 >= (uint)source.Length) { break; } num3 = source[num2]; if (num3 == 44) { continue; } if (num3 == 46) { goto IL_00da; } if (!System.Buffers.Text.ParserHelpers.IsDigit(num3)) { break; } num4 = num4 * 10 + num3 - 48; if (num4 <= 32767 + (-1 * num + 1) / 2) { continue; } goto IL_00ff; } goto IL_0107; } } else { num4 = 0; num2++; if ((uint)num2 < (uint)source.Length && source[num2] == 48) { goto IL_00da; } } } goto IL_00ff; IL_00ff: bytesConsumed = 0; value = 0; return false; IL_0107: bytesConsumed = num2; value = (short)(num4 * num); return true; IL_00da: while (true) { num2++; if ((uint)num2 >= (uint)source.Length) { break; } num3 = source[num2]; if (num3 == 48) { continue; } goto IL_00f7; } goto IL_0107; IL_00f7: if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { goto IL_00ff; } goto IL_0107; } private static bool TryParseInt32N(ReadOnlySpan source, out int value, out int bytesConsumed) { int num; int num2; int num4; int num3; if (source.Length >= 1) { num = 1; num2 = 0; num3 = source[num2]; if (num3 == 45) { num = -1; num2++; if ((uint)num2 >= (uint)source.Length) { goto IL_010a; } num3 = source[num2]; } else if (num3 == 43) { num2++; if ((uint)num2 >= (uint)source.Length) { goto IL_010a; } num3 = source[num2]; } if (num3 != 46) { if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num4 = num3 - 48; while (true) { num2++; if ((uint)num2 >= (uint)source.Length) { break; } num3 = source[num2]; if (num3 == 44) { continue; } if (num3 == 46) { goto IL_00e5; } if (!System.Buffers.Text.ParserHelpers.IsDigit(num3)) { break; } if ((uint)num4 <= 214748364u) { num4 = num4 * 10 + num3 - 48; if ((uint)num4 <= 2147483647L + (long)((-1 * num + 1) / 2)) { continue; } } goto IL_010a; } goto IL_0112; } } else { num4 = 0; num2++; if ((uint)num2 < (uint)source.Length && source[num2] == 48) { goto IL_00e5; } } } goto IL_010a; IL_010a: bytesConsumed = 0; value = 0; return false; IL_0112: bytesConsumed = num2; value = num4 * num; return true; IL_00e5: while (true) { num2++; if ((uint)num2 >= (uint)source.Length) { break; } num3 = source[num2]; if (num3 == 48) { continue; } goto IL_0102; } goto IL_0112; IL_0102: if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { goto IL_010a; } goto IL_0112; } private static bool TryParseInt64N(ReadOnlySpan source, out long value, out int bytesConsumed) { int num; int num2; long num4; int num3; if (source.Length >= 1) { num = 1; num2 = 0; num3 = source[num2]; if (num3 == 45) { num = -1; num2++; if ((uint)num2 >= (uint)source.Length) { goto IL_0115; } num3 = source[num2]; } else if (num3 == 43) { num2++; if ((uint)num2 >= (uint)source.Length) { goto IL_0115; } num3 = source[num2]; } if (num3 != 46) { if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { num4 = num3 - 48; while (true) { num2++; if ((uint)num2 >= (uint)source.Length) { break; } num3 = source[num2]; if (num3 == 44) { continue; } if (num3 == 46) { goto IL_00f0; } if (!System.Buffers.Text.ParserHelpers.IsDigit(num3)) { break; } if ((ulong)num4 <= 922337203685477580uL) { num4 = num4 * 10 + num3 - 48; if ((ulong)num4 <= (ulong)(long.MaxValue + (-1 * num + 1) / 2)) { continue; } } goto IL_0115; } goto IL_011e; } } else { num4 = 0L; num2++; if ((uint)num2 < (uint)source.Length && source[num2] == 48) { goto IL_00f0; } } } goto IL_0115; IL_0115: bytesConsumed = 0; value = 0L; return false; IL_011e: bytesConsumed = num2; value = num4 * num; return true; IL_00f0: while (true) { num2++; if ((uint)num2 >= (uint)source.Length) { break; } num3 = source[num2]; if (num3 == 48) { continue; } goto IL_010d; } goto IL_011e; IL_010d: if (System.Buffers.Text.ParserHelpers.IsDigit(num3)) { goto IL_0115; } goto IL_011e; } public static bool TryParse(ReadOnlySpan source, out byte value, out int bytesConsumed, char standardFormat = '\0') { switch (standardFormat) { case '\0': case 'D': case 'G': case 'd': case 'g': return TryParseByteD(source, out value, out bytesConsumed); case 'N': case 'n': return TryParseByteN(source, out value, out bytesConsumed); case 'X': case 'x': return TryParseByteX(source, out value, out bytesConsumed); default: return System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); } } [CLSCompliant(false)] public static bool TryParse(ReadOnlySpan source, out ushort value, out int bytesConsumed, char standardFormat = '\0') { switch (standardFormat) { case '\0': case 'D': case 'G': case 'd': case 'g': return TryParseUInt16D(source, out value, out bytesConsumed); case 'N': case 'n': return TryParseUInt16N(source, out value, out bytesConsumed); case 'X': case 'x': return TryParseUInt16X(source, out value, out bytesConsumed); default: return System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); } } [CLSCompliant(false)] public static bool TryParse(ReadOnlySpan source, out uint value, out int bytesConsumed, char standardFormat = '\0') { switch (standardFormat) { case '\0': case 'D': case 'G': case 'd': case 'g': return TryParseUInt32D(source, out value, out bytesConsumed); case 'N': case 'n': return TryParseUInt32N(source, out value, out bytesConsumed); case 'X': case 'x': return TryParseUInt32X(source, out value, out bytesConsumed); default: return System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); } } [CLSCompliant(false)] public static bool TryParse(ReadOnlySpan source, out ulong value, out int bytesConsumed, char standardFormat = '\0') { switch (standardFormat) { case '\0': case 'D': case 'G': case 'd': case 'g': return TryParseUInt64D(source, out value, out bytesConsumed); case 'N': case 'n': return TryParseUInt64N(source, out value, out bytesConsumed); case 'X': case 'x': return TryParseUInt64X(source, out value, out bytesConsumed); default: return System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); } } private static bool TryParseByteD(ReadOnlySpan source, out byte value, out int bytesConsumed) { int num; int num3; int num2; if (source.Length >= 1) { num = 0; num2 = source[num]; num3 = 0; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { if (num2 != 48) { goto IL_0056; } while (true) { num++; if ((uint)num >= (uint)source.Length) { break; } num2 = source[num]; if (num2 == 48) { continue; } goto IL_004b; } goto IL_00dd; } } goto IL_00d5; IL_004b: if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { goto IL_0056; } goto IL_00dd; IL_0056: num3 = num2 - 48; num++; if ((uint)num < (uint)source.Length) { num2 = source[num]; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num++; num3 = 10 * num3 + num2 - 48; if ((uint)num < (uint)source.Length) { num2 = source[num]; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num++; num3 = num3 * 10 + num2 - 48; if ((uint)num3 > 255u || ((uint)num < (uint)source.Length && System.Buffers.Text.ParserHelpers.IsDigit(source[num]))) { goto IL_00d5; } } } } } goto IL_00dd; IL_00dd: bytesConsumed = num; value = (byte)num3; return true; IL_00d5: bytesConsumed = 0; value = 0; return false; } private static bool TryParseUInt16D(ReadOnlySpan source, out ushort value, out int bytesConsumed) { int num; int num3; int num2; if (source.Length >= 1) { num = 0; num2 = source[num]; num3 = 0; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { if (num2 != 48) { goto IL_0056; } while (true) { num++; if ((uint)num >= (uint)source.Length) { break; } num2 = source[num]; if (num2 == 48) { continue; } goto IL_004b; } goto IL_013d; } } goto IL_0135; IL_004b: if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { goto IL_0056; } goto IL_013d; IL_0056: num3 = num2 - 48; num++; if ((uint)num < (uint)source.Length) { num2 = source[num]; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num++; num3 = 10 * num3 + num2 - 48; if ((uint)num < (uint)source.Length) { num2 = source[num]; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num++; num3 = 10 * num3 + num2 - 48; if ((uint)num < (uint)source.Length) { num2 = source[num]; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num++; num3 = 10 * num3 + num2 - 48; if ((uint)num < (uint)source.Length) { num2 = source[num]; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num++; num3 = num3 * 10 + num2 - 48; if ((uint)num3 > 65535u || ((uint)num < (uint)source.Length && System.Buffers.Text.ParserHelpers.IsDigit(source[num]))) { goto IL_0135; } } } } } } } } } goto IL_013d; IL_013d: bytesConsumed = num; value = (ushort)num3; return true; IL_0135: bytesConsumed = 0; value = 0; return false; } private static bool TryParseUInt32D(ReadOnlySpan source, out uint value, out int bytesConsumed) { int num; int num3; int num2; if (source.Length >= 1) { num = 0; num2 = source[num]; num3 = 0; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { if (num2 != 48) { goto IL_0056; } while (true) { num++; if ((uint)num >= (uint)source.Length) { break; } num2 = source[num]; if (num2 == 48) { continue; } goto IL_004b; } goto IL_023d; } } goto IL_0235; IL_004b: if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { goto IL_0056; } goto IL_023d; IL_0056: num3 = num2 - 48; num++; if ((uint)num < (uint)source.Length) { num2 = source[num]; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num++; num3 = 10 * num3 + num2 - 48; if ((uint)num < (uint)source.Length) { num2 = source[num]; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num++; num3 = 10 * num3 + num2 - 48; if ((uint)num < (uint)source.Length) { num2 = source[num]; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num++; num3 = 10 * num3 + num2 - 48; if ((uint)num < (uint)source.Length) { num2 = source[num]; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num++; num3 = 10 * num3 + num2 - 48; if ((uint)num < (uint)source.Length) { num2 = source[num]; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num++; num3 = 10 * num3 + num2 - 48; if ((uint)num < (uint)source.Length) { num2 = source[num]; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num++; num3 = 10 * num3 + num2 - 48; if ((uint)num < (uint)source.Length) { num2 = source[num]; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num++; num3 = 10 * num3 + num2 - 48; if ((uint)num < (uint)source.Length) { num2 = source[num]; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num++; num3 = 10 * num3 + num2 - 48; if ((uint)num < (uint)source.Length) { num2 = source[num]; if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num++; if ((uint)num3 <= 429496729u && (num3 != 429496729 || num2 <= 53)) { num3 = num3 * 10 + num2 - 48; if ((uint)num >= (uint)source.Length || !System.Buffers.Text.ParserHelpers.IsDigit(source[num])) { goto IL_023d; } } goto IL_0235; } } } } } } } } } } } } } } } } } } goto IL_023d; IL_023d: bytesConsumed = num; value = (uint)num3; return true; IL_0235: bytesConsumed = 0; value = 0u; return false; } private static bool TryParseUInt64D(ReadOnlySpan source, out ulong value, out int bytesConsumed) { if (source.Length < 1) { bytesConsumed = 0; value = 0uL; return false; } ulong num = (uint)(source[0] - 48); if (num > 9) { bytesConsumed = 0; value = 0uL; return false; } ulong num2 = num; if (source.Length < 19) { for (int i = 1; i < source.Length; i++) { ulong num3 = (uint)(source[i] - 48); if (num3 > 9) { bytesConsumed = i; value = num2; return true; } num2 = num2 * 10 + num3; } } else { for (int j = 1; j < 18; j++) { ulong num4 = (uint)(source[j] - 48); if (num4 > 9) { bytesConsumed = j; value = num2; return true; } num2 = num2 * 10 + num4; } for (int k = 18; k < source.Length; k++) { ulong num5 = (uint)(source[k] - 48); if (num5 > 9) { bytesConsumed = k; value = num2; return true; } if (num2 > 1844674407370955161L || (num2 == 1844674407370955161L && num5 > 5)) { bytesConsumed = 0; value = 0uL; return false; } num2 = num2 * 10 + num5; } } bytesConsumed = source.Length; value = num2; return true; } private static bool TryParseByteN(ReadOnlySpan source, out byte value, out int bytesConsumed) { int num; int num3; int num2; if (source.Length >= 1) { num = 0; num2 = source[num]; if (num2 == 43) { num++; if ((uint)num >= (uint)source.Length) { goto IL_00ce; } num2 = source[num]; } if (num2 != 46) { if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num3 = num2 - 48; while (true) { num++; if ((uint)num >= (uint)source.Length) { break; } num2 = source[num]; if (num2 == 44) { continue; } if (num2 == 46) { goto IL_00a9; } if (!System.Buffers.Text.ParserHelpers.IsDigit(num2)) { break; } num3 = num3 * 10 + num2 - 48; if (num3 <= 255) { continue; } goto IL_00ce; } goto IL_00d6; } } else { num3 = 0; num++; if ((uint)num < (uint)source.Length && source[num] == 48) { goto IL_00a9; } } } goto IL_00ce; IL_00c6: if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { goto IL_00ce; } goto IL_00d6; IL_00ce: bytesConsumed = 0; value = 0; return false; IL_00d6: bytesConsumed = num; value = (byte)num3; return true; IL_00a9: while (true) { num++; if ((uint)num >= (uint)source.Length) { break; } num2 = source[num]; if (num2 == 48) { continue; } goto IL_00c6; } goto IL_00d6; } private static bool TryParseUInt16N(ReadOnlySpan source, out ushort value, out int bytesConsumed) { int num; int num3; int num2; if (source.Length >= 1) { num = 0; num2 = source[num]; if (num2 == 43) { num++; if ((uint)num >= (uint)source.Length) { goto IL_00ce; } num2 = source[num]; } if (num2 != 46) { if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num3 = num2 - 48; while (true) { num++; if ((uint)num >= (uint)source.Length) { break; } num2 = source[num]; if (num2 == 44) { continue; } if (num2 == 46) { goto IL_00a9; } if (!System.Buffers.Text.ParserHelpers.IsDigit(num2)) { break; } num3 = num3 * 10 + num2 - 48; if (num3 <= 65535) { continue; } goto IL_00ce; } goto IL_00d6; } } else { num3 = 0; num++; if ((uint)num < (uint)source.Length && source[num] == 48) { goto IL_00a9; } } } goto IL_00ce; IL_00c6: if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { goto IL_00ce; } goto IL_00d6; IL_00ce: bytesConsumed = 0; value = 0; return false; IL_00d6: bytesConsumed = num; value = (ushort)num3; return true; IL_00a9: while (true) { num++; if ((uint)num >= (uint)source.Length) { break; } num2 = source[num]; if (num2 == 48) { continue; } goto IL_00c6; } goto IL_00d6; } private static bool TryParseUInt32N(ReadOnlySpan source, out uint value, out int bytesConsumed) { int num; int num3; int num2; if (source.Length >= 1) { num = 0; num2 = source[num]; if (num2 == 43) { num++; if ((uint)num >= (uint)source.Length) { goto IL_00de; } num2 = source[num]; } if (num2 != 46) { if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num3 = num2 - 48; while (true) { num++; if ((uint)num >= (uint)source.Length) { break; } num2 = source[num]; if (num2 == 44) { continue; } if (num2 == 46) { goto IL_00b9; } if (!System.Buffers.Text.ParserHelpers.IsDigit(num2)) { break; } if ((uint)num3 <= 429496729u && (num3 != 429496729 || num2 <= 53)) { num3 = num3 * 10 + num2 - 48; continue; } goto IL_00de; } goto IL_00e6; } } else { num3 = 0; num++; if ((uint)num < (uint)source.Length && source[num] == 48) { goto IL_00b9; } } } goto IL_00de; IL_00d6: if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { goto IL_00de; } goto IL_00e6; IL_00de: bytesConsumed = 0; value = 0u; return false; IL_00e6: bytesConsumed = num; value = (uint)num3; return true; IL_00b9: while (true) { num++; if ((uint)num >= (uint)source.Length) { break; } num2 = source[num]; if (num2 == 48) { continue; } goto IL_00d6; } goto IL_00e6; } private static bool TryParseUInt64N(ReadOnlySpan source, out ulong value, out int bytesConsumed) { int num; long num3; int num2; if (source.Length >= 1) { num = 0; num2 = source[num]; if (num2 == 43) { num++; if ((uint)num >= (uint)source.Length) { goto IL_00eb; } num2 = source[num]; } if (num2 != 46) { if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { num3 = num2 - 48; while (true) { num++; if ((uint)num >= (uint)source.Length) { break; } num2 = source[num]; if (num2 == 44) { continue; } if (num2 == 46) { goto IL_00c6; } if (!System.Buffers.Text.ParserHelpers.IsDigit(num2)) { break; } if ((ulong)num3 <= 1844674407370955161uL && (num3 != 1844674407370955161L || num2 <= 53)) { num3 = num3 * 10 + num2 - 48; continue; } goto IL_00eb; } goto IL_00f4; } } else { num3 = 0L; num++; if ((uint)num < (uint)source.Length && source[num] == 48) { goto IL_00c6; } } } goto IL_00eb; IL_00e3: if (System.Buffers.Text.ParserHelpers.IsDigit(num2)) { goto IL_00eb; } goto IL_00f4; IL_00eb: bytesConsumed = 0; value = 0uL; return false; IL_00f4: bytesConsumed = num; value = (ulong)num3; return true; IL_00c6: while (true) { num++; if ((uint)num >= (uint)source.Length) { break; } num2 = source[num]; if (num2 == 48) { continue; } goto IL_00e3; } goto IL_00f4; } private static bool TryParseByteX(ReadOnlySpan source, out byte value, out int bytesConsumed) { if (source.Length < 1) { bytesConsumed = 0; value = 0; return false; } byte[] s_hexLookup = System.Buffers.Text.ParserHelpers.s_hexLookup; byte b = source[0]; byte b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = 0; value = 0; return false; } uint num = b2; if (source.Length <= 2) { for (int i = 1; i < source.Length; i++) { b = source[i]; b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = i; value = (byte)num; return true; } num = (num << 4) + b2; } } else { for (int j = 1; j < 2; j++) { b = source[j]; b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = j; value = (byte)num; return true; } num = (num << 4) + b2; } for (int k = 2; k < source.Length; k++) { b = source[k]; b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = k; value = (byte)num; return true; } if (num > 15) { bytesConsumed = 0; value = 0; return false; } num = (num << 4) + b2; } } bytesConsumed = source.Length; value = (byte)num; return true; } private static bool TryParseUInt16X(ReadOnlySpan source, out ushort value, out int bytesConsumed) { if (source.Length < 1) { bytesConsumed = 0; value = 0; return false; } byte[] s_hexLookup = System.Buffers.Text.ParserHelpers.s_hexLookup; byte b = source[0]; byte b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = 0; value = 0; return false; } uint num = b2; if (source.Length <= 4) { for (int i = 1; i < source.Length; i++) { b = source[i]; b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = i; value = (ushort)num; return true; } num = (num << 4) + b2; } } else { for (int j = 1; j < 4; j++) { b = source[j]; b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = j; value = (ushort)num; return true; } num = (num << 4) + b2; } for (int k = 4; k < source.Length; k++) { b = source[k]; b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = k; value = (ushort)num; return true; } if (num > 4095) { bytesConsumed = 0; value = 0; return false; } num = (num << 4) + b2; } } bytesConsumed = source.Length; value = (ushort)num; return true; } private static bool TryParseUInt32X(ReadOnlySpan source, out uint value, out int bytesConsumed) { if (source.Length < 1) { bytesConsumed = 0; value = 0u; return false; } byte[] s_hexLookup = System.Buffers.Text.ParserHelpers.s_hexLookup; byte b = source[0]; byte b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = 0; value = 0u; return false; } uint num = b2; if (source.Length <= 8) { for (int i = 1; i < source.Length; i++) { b = source[i]; b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = i; value = num; return true; } num = (num << 4) + b2; } } else { for (int j = 1; j < 8; j++) { b = source[j]; b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = j; value = num; return true; } num = (num << 4) + b2; } for (int k = 8; k < source.Length; k++) { b = source[k]; b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = k; value = num; return true; } if (num > 268435455) { bytesConsumed = 0; value = 0u; return false; } num = (num << 4) + b2; } } bytesConsumed = source.Length; value = num; return true; } private static bool TryParseUInt64X(ReadOnlySpan source, out ulong value, out int bytesConsumed) { if (source.Length < 1) { bytesConsumed = 0; value = 0uL; return false; } byte[] s_hexLookup = System.Buffers.Text.ParserHelpers.s_hexLookup; byte b = source[0]; byte b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = 0; value = 0uL; return false; } ulong num = b2; if (source.Length <= 16) { for (int i = 1; i < source.Length; i++) { b = source[i]; b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = i; value = num; return true; } num = (num << 4) + b2; } } else { for (int j = 1; j < 16; j++) { b = source[j]; b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = j; value = num; return true; } num = (num << 4) + b2; } for (int k = 16; k < source.Length; k++) { b = source[k]; b2 = s_hexLookup[b]; if (b2 == byte.MaxValue) { bytesConsumed = k; value = num; return true; } if (num > 1152921504606846975L) { bytesConsumed = 0; value = 0uL; return false; } num = (num << 4) + b2; } } bytesConsumed = source.Length; value = num; return true; } private static bool TryParseNumber(ReadOnlySpan source, ref NumberBuffer number, out int bytesConsumed, ParseNumberOptions options, out bool textUsedExponentNotation) { textUsedExponentNotation = false; if (source.Length == 0) { bytesConsumed = 0; return false; } Span digits = number.Digits; int i = 0; int num = 0; byte b = source[i]; if (b != 43) { if (b != 45) { goto IL_0055; } number.IsNegative = true; } i++; if (i == source.Length) { bytesConsumed = 0; return false; } b = source[i]; goto IL_0055; IL_0055: int num2 = i; for (; i != source.Length; i++) { b = source[i]; if (b != 48) { break; } } if (i == source.Length) { digits[0] = 0; number.Scale = 0; bytesConsumed = i; return true; } int num3 = i; for (; i != source.Length; i++) { b = source[i]; if ((uint)(b - 48) > 9u) { break; } } int num4 = i - num2; int num5 = i - num3; int num6 = Math.Min(num5, 50); source.Slice(num3, num6).CopyTo(digits); num = num6; number.Scale = num5; if (i == source.Length) { bytesConsumed = i; return true; } int num7 = 0; if (b == 46) { i++; int num8 = i; for (; i != source.Length; i++) { b = source[i]; if ((uint)(b - 48) > 9u) { break; } } num7 = i - num8; int j = num8; if (num == 0) { for (; j < i && source[j] == 48; j++) { number.Scale--; } } int num9 = Math.Min(i - j, 51 - num - 1); source.Slice(j, num9).CopyTo(digits.Slice(num)); num += num9; if (i == source.Length) { if (num4 == 0 && num7 == 0) { bytesConsumed = 0; return false; } bytesConsumed = i; return true; } } if (num4 == 0 && num7 == 0) { bytesConsumed = 0; return false; } if ((b & -33) != 69) { bytesConsumed = i; return true; } textUsedExponentNotation = true; i++; if ((options & ParseNumberOptions.AllowExponent) == 0) { bytesConsumed = 0; return false; } if (i == source.Length) { bytesConsumed = 0; return false; } bool flag = false; b = source[i]; if (b != 43) { if (b != 45) { goto IL_0229; } flag = true; } i++; if (i == source.Length) { bytesConsumed = 0; return false; } b = source[i]; goto IL_0229; IL_0229: if (!TryParseUInt32D(source.Slice(i), out var value, out var bytesConsumed2)) { bytesConsumed = 0; return false; } i += bytesConsumed2; if (flag) { if (number.Scale < int.MinValue + value) { number.Scale = int.MinValue; } else { number.Scale -= (int)value; } } else { if (number.Scale > 2147483647L - (long)value) { bytesConsumed = 0; return false; } number.Scale += (int)value; } bytesConsumed = i; return true; } private static bool TryParseTimeSpanBigG(ReadOnlySpan source, out TimeSpan value, out int bytesConsumed) { int i = 0; byte b = 0; for (; i != source.Length; i++) { b = source[i]; if (b != 32 && b != 9) { break; } } if (i == source.Length) { value = default(TimeSpan); bytesConsumed = 0; return false; } bool isNegative = false; if (b == 45) { isNegative = true; i++; if (i == source.Length) { value = default(TimeSpan); bytesConsumed = 0; return false; } } if (!TryParseUInt32D(source.Slice(i), out var value2, out var bytesConsumed2)) { value = default(TimeSpan); bytesConsumed = 0; return false; } i += bytesConsumed2; if (i == source.Length || source[i++] != 58) { value = default(TimeSpan); bytesConsumed = 0; return false; } if (!TryParseUInt32D(source.Slice(i), out var value3, out bytesConsumed2)) { value = default(TimeSpan); bytesConsumed = 0; return false; } i += bytesConsumed2; if (i == source.Length || source[i++] != 58) { value = default(TimeSpan); bytesConsumed = 0; return false; } if (!TryParseUInt32D(source.Slice(i), out var value4, out bytesConsumed2)) { value = default(TimeSpan); bytesConsumed = 0; return false; } i += bytesConsumed2; if (i == source.Length || source[i++] != 58) { value = default(TimeSpan); bytesConsumed = 0; return false; } if (!TryParseUInt32D(source.Slice(i), out var value5, out bytesConsumed2)) { value = default(TimeSpan); bytesConsumed = 0; return false; } i += bytesConsumed2; if (i == source.Length || source[i++] != 46) { value = default(TimeSpan); bytesConsumed = 0; return false; } if (!TryParseTimeSpanFraction(source.Slice(i), out var value6, out bytesConsumed2)) { value = default(TimeSpan); bytesConsumed = 0; return false; } i += bytesConsumed2; if (!TryCreateTimeSpan(isNegative, value2, value3, value4, value5, value6, out value)) { value = default(TimeSpan); bytesConsumed = 0; return false; } if (i != source.Length && (source[i] == 46 || source[i] == 58)) { value = default(TimeSpan); bytesConsumed = 0; return false; } bytesConsumed = i; return true; } private static bool TryParseTimeSpanC(ReadOnlySpan source, out TimeSpan value, out int bytesConsumed) { TimeSpanSplitter timeSpanSplitter = default(TimeSpanSplitter); if (!timeSpanSplitter.TrySplitTimeSpan(source, periodUsedToSeparateDay: true, out bytesConsumed)) { value = default(TimeSpan); return false; } bool isNegative = timeSpanSplitter.IsNegative; bool flag; switch (timeSpanSplitter.Separators) { case 0u: flag = TryCreateTimeSpan(isNegative, timeSpanSplitter.V1, 0u, 0u, 0u, 0u, out value); break; case 16777216u: flag = TryCreateTimeSpan(isNegative, 0u, timeSpanSplitter.V1, timeSpanSplitter.V2, 0u, 0u, out value); break; case 33619968u: flag = TryCreateTimeSpan(isNegative, timeSpanSplitter.V1, timeSpanSplitter.V2, timeSpanSplitter.V3, 0u, 0u, out value); break; case 16842752u: flag = TryCreateTimeSpan(isNegative, 0u, timeSpanSplitter.V1, timeSpanSplitter.V2, timeSpanSplitter.V3, 0u, out value); break; case 33620224u: flag = TryCreateTimeSpan(isNegative, timeSpanSplitter.V1, timeSpanSplitter.V2, timeSpanSplitter.V3, timeSpanSplitter.V4, 0u, out value); break; case 16843264u: flag = TryCreateTimeSpan(isNegative, 0u, timeSpanSplitter.V1, timeSpanSplitter.V2, timeSpanSplitter.V3, timeSpanSplitter.V4, out value); break; case 33620226u: flag = TryCreateTimeSpan(isNegative, timeSpanSplitter.V1, timeSpanSplitter.V2, timeSpanSplitter.V3, timeSpanSplitter.V4, timeSpanSplitter.V5, out value); break; default: value = default(TimeSpan); flag = false; break; } if (!flag) { bytesConsumed = 0; return false; } return true; } public static bool TryParse(ReadOnlySpan source, out TimeSpan value, out int bytesConsumed, char standardFormat = '\0') { switch (standardFormat) { case '\0': case 'T': case 'c': case 't': return TryParseTimeSpanC(source, out value, out bytesConsumed); case 'G': return TryParseTimeSpanBigG(source, out value, out bytesConsumed); case 'g': return TryParseTimeSpanLittleG(source, out value, out bytesConsumed); default: return System.ThrowHelper.TryParseThrowFormatException(out value, out bytesConsumed); } } private static bool TryParseTimeSpanFraction(ReadOnlySpan source, out uint value, out int bytesConsumed) { int num = 0; if (num == source.Length) { value = 0u; bytesConsumed = 0; return false; } uint num2 = (uint)(source[num] - 48); if (num2 > 9) { value = 0u; bytesConsumed = 0; return false; } num++; uint num3 = num2; int num4 = 1; while (num != source.Length) { num2 = (uint)(source[num] - 48); if (num2 > 9) { break; } num++; num4++; if (num4 > 7) { value = 0u; bytesConsumed = 0; return false; } num3 = 10 * num3 + num2; } switch (num4) { case 6: num3 *= 10; break; case 5: num3 *= 100; break; case 4: num3 *= 1000; break; case 3: num3 *= 10000; break; case 2: num3 *= 100000; break; default: num3 *= 1000000; break; case 7: break; } value = num3; bytesConsumed = num; return true; } private static bool TryCreateTimeSpan(bool isNegative, uint days, uint hours, uint minutes, uint seconds, uint fraction, out TimeSpan timeSpan) { if (hours > 23 || minutes > 59 || seconds > 59) { timeSpan = default(TimeSpan); return false; } long num = ((long)days * 3600L * 24 + (long)hours * 3600L + (long)minutes * 60L + seconds) * 1000; long ticks; if (isNegative) { num = -num; if (num < -922337203685477L) { timeSpan = default(TimeSpan); return false; } long num2 = num * 10000; if (num2 < long.MinValue + fraction) { timeSpan = default(TimeSpan); return false; } ticks = num2 - fraction; } else { if (num > 922337203685477L) { timeSpan = default(TimeSpan); return false; } long num3 = num * 10000; if (num3 > long.MaxValue - (long)fraction) { timeSpan = default(TimeSpan); return false; } ticks = num3 + fraction; } timeSpan = new TimeSpan(ticks); return true; } private static bool TryParseTimeSpanLittleG(ReadOnlySpan source, out TimeSpan value, out int bytesConsumed) { TimeSpanSplitter timeSpanSplitter = default(TimeSpanSplitter); if (!timeSpanSplitter.TrySplitTimeSpan(source, periodUsedToSeparateDay: false, out bytesConsumed)) { value = default(TimeSpan); return false; } bool isNegative = timeSpanSplitter.IsNegative; bool flag; switch (timeSpanSplitter.Separators) { case 0u: flag = TryCreateTimeSpan(isNegative, timeSpanSplitter.V1, 0u, 0u, 0u, 0u, out value); break; case 16777216u: flag = TryCreateTimeSpan(isNegative, 0u, timeSpanSplitter.V1, timeSpanSplitter.V2, 0u, 0u, out value); break; case 16842752u: flag = TryCreateTimeSpan(isNegative, 0u, timeSpanSplitter.V1, timeSpanSplitter.V2, timeSpanSplitter.V3, 0u, out value); break; case 16843008u: flag = TryCreateTimeSpan(isNegative, timeSpanSplitter.V1, timeSpanSplitter.V2, timeSpanSplitter.V3, timeSpanSplitter.V4, 0u, out value); break; case 16843264u: flag = TryCreateTimeSpan(isNegative, 0u, timeSpanSplitter.V1, timeSpanSplitter.V2, timeSpanSplitter.V3, timeSpanSplitter.V4, out value); break; case 16843010u: flag = TryCreateTimeSpan(isNegative, timeSpanSplitter.V1, timeSpanSplitter.V2, timeSpanSplitter.V3, timeSpanSplitter.V4, timeSpanSplitter.V5, out value); break; default: value = default(TimeSpan); flag = false; break; } if (!flag) { bytesConsumed = 0; return false; } return true; } } } namespace System.Buffers.Binary { internal static class BinaryPrimitives { [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static sbyte ReverseEndianness(sbyte value) { return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short ReverseEndianness(short value) { return (short)(((value & 0xFF) << 8) | ((value & 0xFF00) >> 8)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ReverseEndianness(int value) { return (int)ReverseEndianness((uint)value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long ReverseEndianness(long value) { return (long)ReverseEndianness((ulong)value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte ReverseEndianness(byte value) { return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static ushort ReverseEndianness(ushort value) { return (ushort)((value >> 8) + (value << 8)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static uint ReverseEndianness(uint value) { uint num = value & 0xFF00FFu; uint num2 = value & 0xFF00FF00u; return ((num >> 8) | (num << 24)) + ((num2 << 8) | (num2 >> 24)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static ulong ReverseEndianness(ulong value) { return ((ulong)ReverseEndianness((uint)value) << 32) + ReverseEndianness((uint)(value >> 32)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short ReadInt16BigEndian(ReadOnlySpan source) { short num = MemoryMarshal.Read(source); if (BitConverter.IsLittleEndian) { num = ReverseEndianness(num); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ReadInt32BigEndian(ReadOnlySpan source) { int num = MemoryMarshal.Read(source); if (BitConverter.IsLittleEndian) { num = ReverseEndianness(num); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long ReadInt64BigEndian(ReadOnlySpan source) { long num = MemoryMarshal.Read(source); if (BitConverter.IsLittleEndian) { num = ReverseEndianness(num); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static ushort ReadUInt16BigEndian(ReadOnlySpan source) { ushort num = MemoryMarshal.Read(source); if (BitConverter.IsLittleEndian) { num = ReverseEndianness(num); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static uint ReadUInt32BigEndian(ReadOnlySpan source) { uint num = MemoryMarshal.Read(source); if (BitConverter.IsLittleEndian) { num = ReverseEndianness(num); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static ulong ReadUInt64BigEndian(ReadOnlySpan source) { ulong num = MemoryMarshal.Read(source); if (BitConverter.IsLittleEndian) { num = ReverseEndianness(num); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryReadInt16BigEndian(ReadOnlySpan source, out short value) { bool result = MemoryMarshal.TryRead(source, out value); if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryReadInt32BigEndian(ReadOnlySpan source, out int value) { bool result = MemoryMarshal.TryRead(source, out value); if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryReadInt64BigEndian(ReadOnlySpan source, out long value) { bool result = MemoryMarshal.TryRead(source, out value); if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static bool TryReadUInt16BigEndian(ReadOnlySpan source, out ushort value) { bool result = MemoryMarshal.TryRead(source, out value); if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static bool TryReadUInt32BigEndian(ReadOnlySpan source, out uint value) { bool result = MemoryMarshal.TryRead(source, out value); if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static bool TryReadUInt64BigEndian(ReadOnlySpan source, out ulong value) { bool result = MemoryMarshal.TryRead(source, out value); if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short ReadInt16LittleEndian(ReadOnlySpan source) { short num = MemoryMarshal.Read(source); if (!BitConverter.IsLittleEndian) { num = ReverseEndianness(num); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ReadInt32LittleEndian(ReadOnlySpan source) { int num = MemoryMarshal.Read(source); if (!BitConverter.IsLittleEndian) { num = ReverseEndianness(num); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long ReadInt64LittleEndian(ReadOnlySpan source) { long num = MemoryMarshal.Read(source); if (!BitConverter.IsLittleEndian) { num = ReverseEndianness(num); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static ushort ReadUInt16LittleEndian(ReadOnlySpan source) { ushort num = MemoryMarshal.Read(source); if (!BitConverter.IsLittleEndian) { num = ReverseEndianness(num); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static uint ReadUInt32LittleEndian(ReadOnlySpan source) { uint num = MemoryMarshal.Read(source); if (!BitConverter.IsLittleEndian) { num = ReverseEndianness(num); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static ulong ReadUInt64LittleEndian(ReadOnlySpan source) { ulong num = MemoryMarshal.Read(source); if (!BitConverter.IsLittleEndian) { num = ReverseEndianness(num); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryReadInt16LittleEndian(ReadOnlySpan source, out short value) { bool result = MemoryMarshal.TryRead(source, out value); if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryReadInt32LittleEndian(ReadOnlySpan source, out int value) { bool result = MemoryMarshal.TryRead(source, out value); if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryReadInt64LittleEndian(ReadOnlySpan source, out long value) { bool result = MemoryMarshal.TryRead(source, out value); if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static bool TryReadUInt16LittleEndian(ReadOnlySpan source, out ushort value) { bool result = MemoryMarshal.TryRead(source, out value); if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static bool TryReadUInt32LittleEndian(ReadOnlySpan source, out uint value) { bool result = MemoryMarshal.TryRead(source, out value); if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static bool TryReadUInt64LittleEndian(ReadOnlySpan source, out ulong value) { bool result = MemoryMarshal.TryRead(source, out value); if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteInt16BigEndian(Span destination, short value) { if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } MemoryMarshal.Write(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteInt32BigEndian(Span destination, int value) { if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } MemoryMarshal.Write(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteInt64BigEndian(Span destination, long value) { if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } MemoryMarshal.Write(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static void WriteUInt16BigEndian(Span destination, ushort value) { if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } MemoryMarshal.Write(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static void WriteUInt32BigEndian(Span destination, uint value) { if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } MemoryMarshal.Write(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static void WriteUInt64BigEndian(Span destination, ulong value) { if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } MemoryMarshal.Write(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryWriteInt16BigEndian(Span destination, short value) { if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return MemoryMarshal.TryWrite(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryWriteInt32BigEndian(Span destination, int value) { if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return MemoryMarshal.TryWrite(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryWriteInt64BigEndian(Span destination, long value) { if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return MemoryMarshal.TryWrite(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static bool TryWriteUInt16BigEndian(Span destination, ushort value) { if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return MemoryMarshal.TryWrite(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static bool TryWriteUInt32BigEndian(Span destination, uint value) { if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return MemoryMarshal.TryWrite(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static bool TryWriteUInt64BigEndian(Span destination, ulong value) { if (BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return MemoryMarshal.TryWrite(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteInt16LittleEndian(Span destination, short value) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } MemoryMarshal.Write(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteInt32LittleEndian(Span destination, int value) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } MemoryMarshal.Write(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteInt64LittleEndian(Span destination, long value) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } MemoryMarshal.Write(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static void WriteUInt16LittleEndian(Span destination, ushort value) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } MemoryMarshal.Write(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static void WriteUInt32LittleEndian(Span destination, uint value) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } MemoryMarshal.Write(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static void WriteUInt64LittleEndian(Span destination, ulong value) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } MemoryMarshal.Write(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryWriteInt16LittleEndian(Span destination, short value) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return MemoryMarshal.TryWrite(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryWriteInt32LittleEndian(Span destination, int value) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return MemoryMarshal.TryWrite(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryWriteInt64LittleEndian(Span destination, long value) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return MemoryMarshal.TryWrite(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static bool TryWriteUInt16LittleEndian(Span destination, ushort value) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return MemoryMarshal.TryWrite(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static bool TryWriteUInt32LittleEndian(Span destination, uint value) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return MemoryMarshal.TryWrite(destination, ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] public static bool TryWriteUInt64LittleEndian(Span destination, ulong value) { if (!BitConverter.IsLittleEndian) { value = ReverseEndianness(value); } return MemoryMarshal.TryWrite(destination, ref value); } } } namespace System.Runtime.CompilerServices { internal static class Unsafe { [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static T Read(void* source) { return Unsafe.Read(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static T ReadUnaligned(void* source) { return Unsafe.ReadUnaligned(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static T ReadUnaligned(ref byte source) { return Unsafe.ReadUnaligned(ref source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static void Write(void* destination, T value) { Unsafe.Write(destination, value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static void WriteUnaligned(void* destination, T value) { Unsafe.WriteUnaligned(destination, value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static void WriteUnaligned(ref byte destination, T value) { Unsafe.WriteUnaligned(ref destination, value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static void Copy(void* destination, ref T source) { Unsafe.Write(destination, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static void Copy(ref T destination, void* source) { destination = Unsafe.Read(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static void* AsPointer(ref T value) { return Unsafe.AsPointer(ref value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static void SkipInit(out T value) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static int SizeOf() { return Unsafe.SizeOf(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static void CopyBlock(void* destination, void* source, uint byteCount) { // IL cpblk instruction Unsafe.CopyBlock(destination, source, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static void CopyBlock(ref byte destination, ref byte source, uint byteCount) { // IL cpblk instruction Unsafe.CopyBlock(ref destination, ref source, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static void CopyBlockUnaligned(void* destination, void* source, uint byteCount) { // IL cpblk instruction Unsafe.CopyBlockUnaligned(destination, source, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static void CopyBlockUnaligned(ref byte destination, ref byte source, uint byteCount) { // IL cpblk instruction Unsafe.CopyBlockUnaligned(ref destination, ref source, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static void InitBlock(void* startAddress, byte value, uint byteCount) { // IL initblk instruction Unsafe.InitBlock(startAddress, value, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static void InitBlock(ref byte startAddress, byte value, uint byteCount) { // IL initblk instruction Unsafe.InitBlock(ref startAddress, value, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static void InitBlockUnaligned(void* startAddress, byte value, uint byteCount) { // IL initblk instruction Unsafe.InitBlockUnaligned(startAddress, value, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static void InitBlockUnaligned(ref byte startAddress, byte value, uint byteCount) { // IL initblk instruction Unsafe.InitBlockUnaligned(ref startAddress, value, byteCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static T As(object o) where T : class { return (T)o; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static ref T AsRef(void* source) { return ref *(T*)source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static ref T AsRef(in T source) { return ref source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static ref TTo As(ref TFrom source) { return ref Unsafe.As(ref source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static ref T Unbox(object box) where T : struct { return ref (T)box; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static ref T Add(ref T source, int elementOffset) { return ref Unsafe.Add(ref source, elementOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static void* Add(void* source, int elementOffset) { return (byte*)source + (nint)elementOffset * (nint)Unsafe.SizeOf(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static ref T Add(ref T source, IntPtr elementOffset) { return ref Unsafe.Add(ref source, elementOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Add(ref T source, [System.Runtime.Versioning.NonVersionable] nuint elementOffset) { return ref Unsafe.Add(ref source, elementOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static ref T AddByteOffset(ref T source, IntPtr byteOffset) { return ref Unsafe.AddByteOffset(ref source, byteOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T AddByteOffset(ref T source, [System.Runtime.Versioning.NonVersionable] nuint byteOffset) { return ref Unsafe.AddByteOffset(ref source, byteOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static ref T Subtract(ref T source, int elementOffset) { return ref Unsafe.Subtract(ref source, elementOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static void* Subtract(void* source, int elementOffset) { return (byte*)source - (nint)elementOffset * (nint)Unsafe.SizeOf(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static ref T Subtract(ref T source, IntPtr elementOffset) { return ref Unsafe.Subtract(ref source, elementOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Subtract(ref T source, [System.Runtime.Versioning.NonVersionable] nuint elementOffset) { return ref Unsafe.Subtract(ref source, elementOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static ref T SubtractByteOffset(ref T source, IntPtr byteOffset) { return ref Unsafe.SubtractByteOffset(ref source, byteOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T SubtractByteOffset(ref T source, [System.Runtime.Versioning.NonVersionable] nuint byteOffset) { return ref Unsafe.SubtractByteOffset(ref source, byteOffset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static IntPtr ByteOffset(ref T origin, ref T target) { return Unsafe.ByteOffset(target: ref target, origin: ref origin); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static bool AreSame(ref T left, ref T right) { return Unsafe.AreSame(ref left, ref right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static bool IsAddressGreaterThan(ref T left, ref T right) { return Unsafe.IsAddressGreaterThan(ref left, ref right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public static bool IsAddressLessThan(ref T left, ref T right) { return Unsafe.IsAddressLessThan(ref left, ref right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static bool IsNullRef(ref T source) { return Unsafe.AsPointer(ref source) == null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.Versioning.NonVersionable] public unsafe static ref T NullRef() { return ref *(T*)null; } } } namespace System.Runtime.Versioning { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] internal sealed class NonVersionableAttribute : Attribute { } } namespace System.Runtime.CompilerServices { internal sealed class IsReadOnlyAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] [Microsoft.CodeAnalysis.Embedded] [CompilerGenerated] internal sealed class NativeIntegerAttribute : Attribute { public readonly bool[] TransformFlags; public NativeIntegerAttribute() { TransformFlags = new bool[1] { true }; } public NativeIntegerAttribute(bool[] A_0) { TransformFlags = A_0; } } } namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } }