Decompiled source of WristHub Quest v4.0.1

Mods\WristHub.Core.dll

Decompiled 3 hours ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using BoneHub.Api;
using BoneHub.Avatars;
using BoneHub.Configuration;
using BoneHub.Downloads;
using BoneHub.Installation;
using BoneHub.Interaction;
using BoneHub.Models;
using BoneHub.Persistence;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]
[assembly: AssemblyCompany("WristHub.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("4.0.1.0")]
[assembly: AssemblyInformationalVersion("4.0.1")]
[assembly: AssemblyProduct("WristHub.Core")]
[assembly: AssemblyTitle("WristHub.Core")]
[assembly: AssemblyVersion("4.0.1.0")]
[module: RefSafetyRules(11)]
namespace BoneHub
{
	public sealed class BoneHubService : IDisposable
	{
		private readonly IModIoClient _client;

		private readonly DownloadManager _downloads;

		private readonly BoneHubOptions _options;

		private readonly SearchCache _cache;

		private readonly JsonFileStore<BoneHubState> _stateStore;

		private readonly SemaphoreSlim _stateGate = new SemaphoreSlim(1, 1);

		private BoneHubState _state = new BoneHubState();

		public int FavoriteCount => _state.FavoriteModIds.Count;

		public IReadOnlyCollection<BoneHub.Persistence.InstalledModRecord> Installed => (IReadOnlyCollection<BoneHub.Persistence.InstalledModRecord>)(object)_state.InstalledMods.Values.ToArray();

		public BoneHubPreferences Preferences => _state.Preferences;

		public IReadOnlyList<string> RecentlyEquippedAvatarBarcodes => _state.RecentlyEquippedAvatarBarcodes.ToArray();

		public string LastEquippedAvatarBarcode
		{
			get
			{
				if (!string.IsNullOrWhiteSpace(_state.LastEquippedAvatarBarcode))
				{
					return _state.LastEquippedAvatarBarcode;
				}
				return _state.RecentlyEquippedAvatarBarcodes.FirstOrDefault() ?? string.Empty;
			}
		}

		public TrashedModRecord? LastTrashed => _state.TrashHistory.LastOrDefault();

		public IReadOnlyCollection<long> PendingDeletionModIds => (IReadOnlyCollection<long>)(object)_state.PendingDeletions.Select((PendingDeletionRecord item) => item.Mod.ModId).ToArray();

		public bool ModIoReady => _options.CanUseModIo;

		public string ApiKeyFilePath => _options.ApiKeyFilePath;

		public event Action<DownloadJob>? DownloadChanged;

		public event Action? InstalledChanged;

		public BoneHubService(IModIoClient client, DownloadManager downloads, BoneHubOptions options)
		{
			_client = client;
			_downloads = downloads;
			_options = options;
			_cache = new SearchCache(Path.Combine(options.DataDirectory, "cache", "search"));
			_stateStore = new JsonFileStore<BoneHubState>(Path.Combine(options.DataDirectory, "state.json"));
			_downloads.JobChanged += OnJobChanged;
		}

		public async Task InitializeAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			_state = await _stateStore.LoadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			if (_state.Preferences.MiniMapRange == MiniMapRange.TwentyFiveMeters)
			{
				_state.Preferences.MiniMapRange = MiniMapRange.ThirtyMeters;
			}
		}

		public async Task<ModIoPage<ModIoMod>> SearchAsync(ModSearchRequest request, CancellationToken cancellationToken = default(CancellationToken))
		{
			ModSearchRequest normalized = request with
			{
				Query = request.Query.Trim(),
				Limit = Math.Clamp(request.Limit, 1, 100),
				Offset = Math.Max(request.Offset, 0)
			};
			string key = $"{normalized.Query}|{normalized.Tag}|{normalized.Creator}|{normalized.Sort}|{normalized.Offset}|{normalized.Limit}|{_options.TargetPlatform}";
			ModIoPage<ModIoMod> modIoPage = await _cache.ReadAsync(key, TimeSpan.FromMinutes(_options.CacheMinutes), cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			if (modIoPage != null)
			{
				return modIoPage;
			}
			try
			{
				ModIoPage<ModIoMod> page = await _client.SearchAsync(normalized, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
				await _cache.WriteAsync(key, page, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
				return page;
			}
			catch when (!cancellationToken.IsCancellationRequested)
			{
				ModIoPage<ModIoMod> modIoPage2 = await _cache.ReadAsync(key, TimeSpan.FromDays(7.0), cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
				if (modIoPage2 != null)
				{
					return modIoPage2;
				}
				throw;
			}
		}

		public DownloadJob Install(ModIoMod mod, CancellationToken cancellationToken = default(CancellationToken))
		{
			return _downloads.Enqueue(mod, cancellationToken);
		}

		public async Task<ModInstallPlan> GetInstallPlanAsync(ModIoMod root, CancellationToken cancellationToken = default(CancellationToken))
		{
			if (!ModIoReady)
			{
				throw new InvalidOperationException("WristHub's online avatar service is unavailable.");
			}
			List<ModIoMod> ordered = new List<ModIoMod>();
			HashSet<long> installed = new HashSet<long>();
			HashSet<long> visited = new HashSet<long>();
			HashSet<long> visiting = new HashSet<long>();
			await VisitAsync(root, isRoot: true).ConfigureAwait(continueOnCapturedContext: false);
			return new ModInstallPlan(root, ordered, installed.OrderBy((long id) => id).ToArray());
			async Task VisitAsync(ModIoMod mod, bool isRoot)
			{
				cancellationToken.ThrowIfCancellationRequested();
				if (!visited.Contains(mod.Id))
				{
					if (!visiting.Add(mod.Id))
					{
						throw new InvalidDataException($"mod.io reported a dependency cycle involving mod {mod.Id}.");
					}
					if (visited.Count + visiting.Count > 32)
					{
						throw new InvalidDataException("This dependency graph exceeds WristHub's 32-mod safety limit.");
					}
					foreach (ModIoDependency item in (await _client.GetDependenciesAsync(mod.Id, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)).Data.Where((ModIoDependency item) => item.ModId > 0).DistinctBy((ModIoDependency item) => item.ModId))
					{
						if (_state.InstalledMods.ContainsKey(item.ModId))
						{
							installed.Add(item.ModId);
						}
						else
						{
							await VisitAsync(await _client.GetModAsync(item.ModId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false), isRoot: false).ConfigureAwait(continueOnCapturedContext: false);
						}
					}
					visiting.Remove(mod.Id);
					visited.Add(mod.Id);
					if (isRoot || !_state.InstalledMods.ContainsKey(mod.Id))
					{
						ordered.Add(mod);
					}
					else
					{
						installed.Add(mod.Id);
					}
				}
			}
		}

		public async Task<IReadOnlyList<DownloadJob>> InstallWithDependenciesAsync(ModIoMod root, CancellationToken cancellationToken = default(CancellationToken))
		{
			return (await GetInstallPlanAsync(root, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)).OrderedMods.Select((ModIoMod mod) => Install(mod, cancellationToken)).ToArray();
		}

		public bool CancelDownload(Guid jobId)
		{
			return _downloads.Cancel(jobId);
		}

		public bool IsFavorite(long modId)
		{
			return _state.FavoriteModIds.Contains(modId);
		}

		public bool IsInstalled(long modId)
		{
			return _state.InstalledMods.ContainsKey(modId);
		}

		public async Task ConfigureApiKeyAsync(string value, CancellationToken cancellationToken = default(CancellationToken))
		{
			string normalized = value?.Trim() ?? string.Empty;
			if (!BoneHubOptions.IsValidApiKey(normalized))
			{
				throw new InvalidDataException("The mod.io API key must contain exactly 32 letters or numbers.");
			}
			if (string.IsNullOrWhiteSpace(_options.ApiKeyFilePath))
			{
				throw new InvalidOperationException("WristHub's API-key file path is unavailable.");
			}
			string path = Path.GetFullPath(_options.ApiKeyFilePath);
			Directory.CreateDirectory(Path.GetDirectoryName(path) ?? throw new InvalidOperationException("The API-key file has no parent directory."));
			string temporary = path + ".tmp";
			await File.WriteAllTextAsync(temporary, normalized, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			File.Move(temporary, path, overwrite: true);
			_options.ApiKey = normalized;
		}

		public async Task SetFavoriteAsync(long modId, bool favorite, CancellationToken cancellationToken = default(CancellationToken))
		{
			await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			try
			{
				if (favorite)
				{
					_state.FavoriteModIds.Add(modId);
				}
				else
				{
					_state.FavoriteModIds.Remove(modId);
				}
				await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			}
			finally
			{
				_stateGate.Release();
			}
		}

		public async Task<IReadOnlyList<ModIoMod>> GetFavoriteModsAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			long[] array;
			try
			{
				array = _state.FavoriteModIds.Take(50).ToArray();
			}
			finally
			{
				_stateGate.Release();
			}
			List<ModIoMod> results = new List<ModIoMod>();
			long[] array2 = array;
			foreach (long modId in array2)
			{
				cancellationToken.ThrowIfCancellationRequested();
				try
				{
					List<ModIoMod> list = results;
					list.Add(await _client.GetModAsync(modId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false));
				}
				catch when (!cancellationToken.IsCancellationRequested)
				{
				}
			}
			return results.OrderBy((ModIoMod mod) => mod.Name).ToArray();
		}

		public Task<ModIoMod> GetModDetailsAsync(long modId, CancellationToken cancellationToken = default(CancellationToken))
		{
			return _client.GetModAsync(modId, cancellationToken);
		}

		public async Task MarkViewedAsync(long modId, CancellationToken cancellationToken = default(CancellationToken))
		{
			await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			try
			{
				_state.RecentlyViewedModIds.Remove(modId);
				_state.RecentlyViewedModIds.Insert(0, modId);
				if (_state.RecentlyViewedModIds.Count > 50)
				{
					_state.RecentlyViewedModIds.RemoveRange(50, _state.RecentlyViewedModIds.Count - 50);
				}
				await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			}
			finally
			{
				_stateGate.Release();
			}
		}

		public async Task MarkAvatarEquippedAsync(string barcode, CancellationToken cancellationToken = default(CancellationToken))
		{
			string normalized = barcode?.Trim() ?? string.Empty;
			if (normalized.Length == 0)
			{
				return;
			}
			await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			try
			{
				_state.RecentlyEquippedAvatarBarcodes.RemoveAll((string item) => string.Equals(item, normalized, StringComparison.OrdinalIgnoreCase));
				_state.RecentlyEquippedAvatarBarcodes.Insert(0, normalized);
				_state.LastEquippedAvatarBarcode = normalized;
				if (_state.RecentlyEquippedAvatarBarcodes.Count > 50)
				{
					_state.RecentlyEquippedAvatarBarcodes.RemoveRange(50, _state.RecentlyEquippedAvatarBarcodes.Count - 50);
				}
				await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			}
			finally
			{
				_stateGate.Release();
			}
		}

		public async Task ForgetAvatarBarcodesAsync(IEnumerable<string> barcodes, CancellationToken cancellationToken = default(CancellationToken))
		{
			HashSet<string> removed = (from item in barcodes
				where !string.IsNullOrWhiteSpace(item)
				select item.Trim()).ToHashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (removed.Count == 0)
			{
				return;
			}
			await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			try
			{
				_state.RecentlyEquippedAvatarBarcodes.RemoveAll(removed.Contains);
				if (removed.Contains(_state.LastEquippedAvatarBarcode))
				{
					_state.LastEquippedAvatarBarcode = _state.RecentlyEquippedAvatarBarcodes.FirstOrDefault() ?? string.Empty;
				}
				await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			}
			finally
			{
				_stateGate.Release();
			}
		}

		public async Task SavePreferencesAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			try
			{
				if (!Enum.IsDefined(_state.Preferences.ComfortPreset))
				{
					_state.Preferences.ComfortPreset = BoneHubComfortPreset.Default;
				}
				if (!Enum.IsDefined(_state.Preferences.AvatarBrowserStartupMode))
				{
					_state.Preferences.AvatarBrowserStartupMode = BrowserStartupMode.AllInstalled;
				}
				if (!Enum.IsDefined(_state.Preferences.LastAvatarBrowseScope))
				{
					_state.Preferences.LastAvatarBrowseScope = AvatarBrowseScope.AllInstalled;
				}
				if (!Enum.IsDefined(_state.Preferences.MiniMapRange))
				{
					_state.Preferences.MiniMapRange = MiniMapRange.ThirtyMeters;
				}
				if (!Enum.IsDefined(_state.Preferences.MiniMapOrientation))
				{
					_state.Preferences.MiniMapOrientation = MiniMapOrientation.HeadingUp;
				}
				if (!Enum.IsDefined(_state.Preferences.HologramTheme))
				{
					_state.Preferences.HologramTheme = WristHubTheme.Cyan;
				}
				_state.Preferences.WatchScale = Math.Clamp(_state.Preferences.WatchScale, 0.65f, 1.6f);
				_state.Preferences.DwellSeconds = 1f;
				_state.Preferences.OutwardOffset = Math.Clamp(_state.Preferences.OutwardOffset, 0.015f, 0.06f);
				_state.Preferences.FingerOffset = Math.Clamp(_state.Preferences.FingerOffset, -0.055f, 0.025f);
				_state.Preferences.WatchForearmOffset = Math.Clamp(_state.Preferences.WatchForearmOffset, -0.08f, 0.03f);
				_state.Preferences.CarouselDistance = Math.Clamp(_state.Preferences.CarouselDistance, 0.45f, 0.95f);
				_state.Preferences.HologramScale = Math.Clamp(_state.Preferences.HologramScale, 0.7f, 1.4f);
				_state.Preferences.EquipZoneScale = Math.Clamp(_state.Preferences.EquipZoneScale, 0.7f, 1.6f);
				_state.Preferences.HologramBrightness = Math.Clamp(_state.Preferences.HologramBrightness, 0.35f, 1.5f);
				foreach (CompassCalibrationRecord compassCalibration in _state.Preferences.CompassCalibrations)
				{
					compassCalibration.NorthYawDegrees = CompassHeadingMath.Normalize(compassCalibration.NorthYawDegrees);
				}
				while (_state.Preferences.CompassCalibrations.Count > 64)
				{
					CompassCalibrationRecord item = _state.Preferences.CompassCalibrations.OrderBy((CompassCalibrationRecord record) => record.LastUsedUtc).First();
					_state.Preferences.CompassCalibrations.Remove(item);
				}
				TrimDistinct(_state.Preferences.FavoriteSpawnableBarcodes, 128);
				TrimDistinct(_state.Preferences.RecentSpawnableBarcodes, 64);
				await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			}
			finally
			{
				_stateGate.Release();
			}
		}

		private static void TrimDistinct(List<string> values, int maximum)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			for (int num = values.Count - 1; num >= 0; num--)
			{
				string text = values[num]?.Trim() ?? string.Empty;
				if (text.Length == 0 || !hashSet.Add(text))
				{
					values.RemoveAt(num);
				}
				else
				{
					values[num] = text;
				}
			}
			if (values.Count > maximum)
			{
				values.RemoveRange(maximum, values.Count - maximum);
			}
		}

		public InstalledModHealth VerifyInstalled(long modId)
		{
			if (!_state.InstalledMods.TryGetValue(modId, out BoneHub.Persistence.InstalledModRecord value))
			{
				return new InstalledModHealth(Healthy: false, new string[1] { "WristHub has no installation record for this mod." });
			}
			List<string> list = new List<string>();
			foreach (string installedDirectory in value.InstalledDirectories)
			{
				string text;
				try
				{
					text = ValidateInstalledPath(installedDirectory);
				}
				catch (Exception ex)
				{
					list.Add(ex.Message);
					continue;
				}
				if (!Directory.Exists(text))
				{
					list.Add("Missing folder: " + Path.GetFileName(text));
				}
				else if (!PalletManifestLocator.HasManifest(text))
				{
					list.Add("Missing pallet manifest: " + Path.GetFileName(text));
				}
			}
			return new InstalledModHealth(list.Count == 0, list);
		}

		public async Task<ModUpdateInfo> CheckForUpdateAsync(long modId, CancellationToken cancellationToken = default(CancellationToken))
		{
			if (!_state.InstalledMods.TryGetValue(modId, out BoneHub.Persistence.InstalledModRecord installed))
			{
				throw new InvalidOperationException("This mod is not installed.");
			}
			ModIoMod mod = await _client.GetModAsync(modId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			ModIoFile modIoFile = await ModIoFileResolver.ResolveAsync(_client, mod, _options, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			return new ModUpdateInfo(mod, modIoFile.Id != installed.FileId, installed.FileId, modIoFile.Id);
		}

		public async Task<IReadOnlyList<ModUpdateInfo>> CheckAllUpdatesAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			List<ModUpdateInfo> results = new List<ModUpdateInfo>();
			foreach (BoneHub.Persistence.InstalledModRecord item in _state.InstalledMods.Values.OrderBy((BoneHub.Persistence.InstalledModRecord item) => item.Name))
			{
				cancellationToken.ThrowIfCancellationRequested();
				List<ModUpdateInfo> list = results;
				list.Add(await CheckForUpdateAsync(item.ModId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false));
			}
			return results;
		}

		public async Task<DownloadJob> RepairOrUpdateAsync(long modId, CancellationToken cancellationToken = default(CancellationToken))
		{
			return Install((await CheckForUpdateAsync(modId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)).Mod, cancellationToken);
		}

		public async Task<int> RepairSharingRegistrationsAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			BoneHub.Persistence.InstalledModRecord[] array = _state.InstalledMods.Values.Where((BoneHub.Persistence.InstalledModRecord installedModRecord) => installedModRecord.ModId > 0 && installedModRecord.InstalledDirectories.Any(Directory.Exists) && (installedModRecord.Sharing == null || installedModRecord.Sharing.RegistrationVersion < 3 || installedModRecord.Sharing.SidecarManifestPaths.Count == 0 || installedModRecord.Sharing.SidecarManifestPaths.Any((string path) => !File.Exists(path)))).ToArray();
			int repaired = 0;
			BoneHub.Persistence.InstalledModRecord[] array2 = array;
			foreach (BoneHub.Persistence.InstalledModRecord record in array2)
			{
				cancellationToken.ThrowIfCancellationRequested();
				try
				{
					ModIoMod mod = await _client.GetModAsync(record.ModId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
					ModIoFile modIoFile = ((record.FileId <= 0) ? (await ModIoFileResolver.ResolveAsync(_client, mod, _options, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)) : (await _client.GetFileAsync(record.ModId, record.FileId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)));
					ModIoFile file = modIoFile;
					ModIoSharingMetadata sharing = await NetworkerRegistrationWriter.WriteAsync(mod, file, await ModIoFileResolver.ResolvePlatformFilesAsync(_client, record.ModId, cancellationToken).ConfigureAwait(continueOnCapturedContext: false), record.InstalledDirectories, _options.ModsDirectory, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
					await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
					try
					{
						if (_state.InstalledMods.TryGetValue(record.ModId, out BoneHub.Persistence.InstalledModRecord value))
						{
							value.Sharing = sharing;
						}
						await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
					}
					finally
					{
						_stateGate.Release();
					}
					repaired++;
				}
				catch when (!cancellationToken.IsCancellationRequested)
				{
				}
			}
			if (repaired > 0)
			{
				this.InstalledChanged?.Invoke();
			}
			return repaired;
		}

		public Task<TrashedModRecord> UninstallAsync(long modId, CancellationToken cancellationToken = default(CancellationToken))
		{
			return UninstallCoreAsync(modId, null, null, wasSubscribed: false, cancellationToken);
		}

		public Task<TrashedModRecord> UninstallAsync(long modId, string? localName, IReadOnlyList<string>? localDirectories, CancellationToken cancellationToken = default(CancellationToken))
		{
			return UninstallCoreAsync(modId, localName, localDirectories, wasSubscribed: false, cancellationToken);
		}

		public Task<TrashedModRecord> UninstallSubscribedAsync(long modId, string? localName, IReadOnlyList<string>? localDirectories, bool wasSubscribed, CancellationToken cancellationToken = default(CancellationToken))
		{
			return UninstallCoreAsync(modId, localName, localDirectories, wasSubscribed, cancellationToken);
		}

		public async Task<PendingDeletionRecord> QueueDeletionAsync(long modId, string? localName, IReadOnlyList<string>? localDirectories, bool wasSubscribed, IEnumerable<string>? avatarBarcodes, CancellationToken cancellationToken = default(CancellationToken))
		{
			await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			PendingDeletionRecord queued;
			try
			{
				PendingDeletionRecord pendingDeletionRecord = _state.PendingDeletions.FirstOrDefault((PendingDeletionRecord item) => item.Mod.ModId == modId);
				if (pendingDeletionRecord != null)
				{
					return pendingDeletionRecord;
				}
				_state.InstalledMods.TryGetValue(modId, out BoneHub.Persistence.InstalledModRecord value);
				IEnumerable<string> enumerable = localDirectories;
				object obj = enumerable;
				if (obj == null)
				{
					enumerable = value?.InstalledDirectories;
					obj = enumerable ?? Array.Empty<string>();
				}
				List<string> list = ((IEnumerable<string>)obj).Where((string path) => !string.IsNullOrWhiteSpace(path)).Select(ValidateInstalledPath).Where(Directory.Exists)
					.Distinct<string>(StringComparer.OrdinalIgnoreCase)
					.ToList();
				if (list.Count == 0)
				{
					throw new DirectoryNotFoundException("No installed avatar-pack folders were found to queue.");
				}
				if (value == null)
				{
					value = new BoneHub.Persistence.InstalledModRecord
					{
						ModId = modId,
						Name = (string.IsNullOrWhiteSpace(localName) ? "Local avatar pack" : localName.Trim()),
						InstalledDirectories = list,
						InstalledAt = DateTimeOffset.UtcNow
					};
				}
				List<string> sidecarFiles = (from path in (value.Sharing?.SidecarManifestPaths ?? new List<string>()).Concat(FindRegistrationSidecars(list))
					where !string.IsNullOrWhiteSpace(path)
					select path).Select(ValidateInstalledFile).Where(File.Exists).Distinct<string>(StringComparer.OrdinalIgnoreCase)
					.ToList();
				List<string> barcodes = (from text in avatarBarcodes ?? Array.Empty<string>()
					where !string.IsNullOrWhiteSpace(text)
					select text.Trim()).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList();
				queued = new PendingDeletionRecord
				{
					Mod = value,
					Directories = list,
					SidecarFiles = sidecarFiles,
					AvatarBarcodes = barcodes,
					WasSubscribed = wasSubscribed
				};
				_state.PendingDeletions.Add(queued);
				_state.InstalledMods.Remove(modId);
				_state.RecentlyEquippedAvatarBarcodes.RemoveAll((string value2) => barcodes.Contains<string>(value2, StringComparer.OrdinalIgnoreCase));
				if (barcodes.Contains<string>(_state.LastEquippedAvatarBarcode, StringComparer.OrdinalIgnoreCase))
				{
					_state.LastEquippedAvatarBarcode = _state.RecentlyEquippedAvatarBarcodes.FirstOrDefault() ?? string.Empty;
				}
				await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			}
			finally
			{
				_stateGate.Release();
			}
			this.InstalledChanged?.Invoke();
			return queued;
		}

		public async Task<PendingDeletionProcessResult> ProcessPendingDeletionsAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			int completed = 0;
			List<string> problems = new List<string>();
			await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			try
			{
				PendingDeletionRecord[] array = _state.PendingDeletions.ToArray();
				foreach (PendingDeletionRecord pending in array)
				{
					cancellationToken.ThrowIfCancellationRequested();
					string text = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(_options.ModsDirectory).TrimEnd(Path.DirectorySeparatorChar)) ?? throw new InvalidOperationException("Mods directory has no parent."), ".wristhub-trash", $"{pending.Mod.ModId}-{DateTimeOffset.UtcNow:yyyyMMddHHmmss}-{Guid.NewGuid():N}");
					Directory.CreateDirectory(text);
					List<TrashedDirectoryRecord> list = new List<TrashedDirectoryRecord>();
					List<TrashedFileRecord> list2 = new List<TrashedFileRecord>();
					try
					{
						for (int j = 0; j < pending.Directories.Count; j++)
						{
							string text2 = ValidateInstalledPath(pending.Directories[j]);
							if (Directory.Exists(text2))
							{
								string text3 = Path.Combine(text, $"{j:D2}-{Path.GetFileName(text2)}");
								Directory.Move(text2, text3);
								list.Add(new TrashedDirectoryRecord
								{
									OriginalPath = text2,
									TrashPath = text3
								});
							}
						}
						for (int k = 0; k < pending.SidecarFiles.Count; k++)
						{
							string text4 = ValidateInstalledFile(pending.SidecarFiles[k]);
							if (File.Exists(text4))
							{
								string text5 = Path.Combine(text, $"manifest-{k:D2}-{Path.GetFileName(text4)}");
								File.Move(text4, text5);
								list2.Add(new TrashedFileRecord
								{
									OriginalPath = text4,
									TrashPath = text5
								});
							}
						}
						if (list.Count > 0 || list2.Count > 0)
						{
							_state.TrashHistory.Add(new TrashedModRecord
							{
								Mod = pending.Mod,
								Directories = list,
								Files = list2,
								WasSubscribed = pending.WasSubscribed
							});
							if (_state.TrashHistory.Count > 20)
							{
								_state.TrashHistory.RemoveRange(0, _state.TrashHistory.Count - 20);
							}
						}
						else if (Directory.Exists(text))
						{
							Directory.Delete(text, recursive: false);
						}
						_state.PendingDeletions.Remove(pending);
						_state.InstalledMods.Remove(pending.Mod.ModId);
						_state.RecentlyEquippedAvatarBarcodes.RemoveAll((string value) => pending.AvatarBarcodes.Contains<string>(value, StringComparer.OrdinalIgnoreCase));
						if (pending.AvatarBarcodes.Contains<string>(_state.LastEquippedAvatarBarcode, StringComparer.OrdinalIgnoreCase))
						{
							_state.LastEquippedAvatarBarcode = _state.RecentlyEquippedAvatarBarcodes.FirstOrDefault() ?? string.Empty;
						}
						completed++;
					}
					catch (Exception ex)
					{
						for (int num = list.Count - 1; num >= 0; num--)
						{
							TrashedDirectoryRecord trashedDirectoryRecord = list[num];
							if (Directory.Exists(trashedDirectoryRecord.TrashPath) && !Directory.Exists(trashedDirectoryRecord.OriginalPath))
							{
								Directory.Move(trashedDirectoryRecord.TrashPath, trashedDirectoryRecord.OriginalPath);
							}
						}
						for (int num2 = list2.Count - 1; num2 >= 0; num2--)
						{
							TrashedFileRecord trashedFileRecord = list2[num2];
							if (File.Exists(trashedFileRecord.TrashPath) && !File.Exists(trashedFileRecord.OriginalPath))
							{
								File.Move(trashedFileRecord.TrashPath, trashedFileRecord.OriginalPath);
							}
						}
						try
						{
							if (Directory.Exists(text) && !Directory.EnumerateFileSystemEntries(text).Any())
							{
								Directory.Delete(text);
							}
						}
						catch
						{
						}
						problems.Add(pending.Mod.Name + ": " + ex.Message);
					}
				}
				await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			}
			finally
			{
				_stateGate.Release();
			}
			if (completed > 0)
			{
				this.InstalledChanged?.Invoke();
			}
			return new PendingDeletionProcessResult(completed, _state.PendingDeletions.Count, problems);
		}

		private async Task<TrashedModRecord> UninstallCoreAsync(long modId, string? localName, IReadOnlyList<string>? localDirectories, bool wasSubscribed, CancellationToken cancellationToken)
		{
			await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			try
			{
				BoneHub.Persistence.InstalledModRecord installed;
				bool wasTracked = _state.InstalledMods.TryGetValue(modId, out installed);
				if (!wasTracked)
				{
					List<string> list = (localDirectories ?? Array.Empty<string>()).Where((string path) => !string.IsNullOrWhiteSpace(path)).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList();
					if (list.Count == 0)
					{
						throw new InvalidOperationException("WristHub could not identify this avatar pack's local folder.");
					}
					installed = new BoneHub.Persistence.InstalledModRecord
					{
						ModId = modId,
						Name = (string.IsNullOrWhiteSpace(localName) ? "Local avatar pack" : localName.Trim()),
						InstalledDirectories = list,
						InstalledAt = DateTimeOffset.UtcNow
					};
				}
				BoneHub.Persistence.InstalledModRecord installedModRecord = installed ?? throw new InvalidOperationException("The avatar pack record is unavailable.");
				string text = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(_options.ModsDirectory).TrimEnd(Path.DirectorySeparatorChar)) ?? throw new InvalidOperationException("Mods directory has no parent."), ".wristhub-trash", $"{modId}-{DateTimeOffset.UtcNow:yyyyMMddHHmmss}-{Guid.NewGuid():N}");
				Directory.CreateDirectory(text);
				List<TrashedDirectoryRecord> moved = new List<TrashedDirectoryRecord>();
				List<TrashedFileRecord> movedFiles = new List<TrashedFileRecord>();
				TrashedModRecord stateRecord = null;
				try
				{
					for (int num = 0; num < installedModRecord.InstalledDirectories.Count; num++)
					{
						cancellationToken.ThrowIfCancellationRequested();
						string text2 = ValidateInstalledPath(installedModRecord.InstalledDirectories[num]);
						if (Directory.Exists(text2))
						{
							string text3 = Path.Combine(text, $"{num:D2}-{Path.GetFileName(text2)}");
							Directory.Move(text2, text3);
							moved.Add(new TrashedDirectoryRecord
							{
								OriginalPath = text2,
								TrashPath = text3
							});
						}
					}
					string[] array = (installedModRecord.Sharing?.SidecarManifestPaths ?? new List<string>()).Concat(FindRegistrationSidecars(installedModRecord.InstalledDirectories)).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();
					for (int num2 = 0; num2 < array.Length; num2++)
					{
						cancellationToken.ThrowIfCancellationRequested();
						string text4 = ValidateInstalledFile(array[num2]);
						if (File.Exists(text4))
						{
							string text5 = Path.Combine(text, $"manifest-{num2:D2}-{Path.GetFileName(text4)}");
							File.Move(text4, text5);
							movedFiles.Add(new TrashedFileRecord
							{
								OriginalPath = text4,
								TrashPath = text5
							});
						}
					}
					if (moved.Count == 0)
					{
						throw new DirectoryNotFoundException("No installed avatar-pack folders were found to remove.");
					}
					TrashedModRecord record = new TrashedModRecord
					{
						Mod = installedModRecord,
						Directories = moved,
						Files = movedFiles,
						WasSubscribed = wasSubscribed
					};
					stateRecord = record;
					_state.InstalledMods.Remove(modId);
					_state.TrashHistory.Add(record);
					if (_state.TrashHistory.Count > 20)
					{
						_state.TrashHistory.RemoveRange(0, _state.TrashHistory.Count - 20);
					}
					await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
					this.InstalledChanged?.Invoke();
					return record;
				}
				catch
				{
					if (wasTracked)
					{
						_state.InstalledMods[modId] = installed;
					}
					else
					{
						_state.InstalledMods.Remove(modId);
					}
					if (stateRecord != null)
					{
						_state.TrashHistory.Remove(stateRecord);
					}
					for (int num3 = moved.Count - 1; num3 >= 0; num3--)
					{
						TrashedDirectoryRecord trashedDirectoryRecord = moved[num3];
						if (Directory.Exists(trashedDirectoryRecord.TrashPath) && !Directory.Exists(trashedDirectoryRecord.OriginalPath))
						{
							Directory.Move(trashedDirectoryRecord.TrashPath, trashedDirectoryRecord.OriginalPath);
						}
					}
					for (int num4 = movedFiles.Count - 1; num4 >= 0; num4--)
					{
						TrashedFileRecord trashedFileRecord = movedFiles[num4];
						if (File.Exists(trashedFileRecord.TrashPath) && !File.Exists(trashedFileRecord.OriginalPath))
						{
							File.Move(trashedFileRecord.TrashPath, trashedFileRecord.OriginalPath);
						}
					}
					throw;
				}
			}
			finally
			{
				_stateGate.Release();
			}
		}

		public async Task<BoneHub.Persistence.InstalledModRecord> UndoLastUninstallAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			await _stateGate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			try
			{
				TrashedModRecord record = _state.TrashHistory.LastOrDefault() ?? throw new InvalidOperationException("There is no WristHub uninstall to undo.");
				if (_state.InstalledMods.ContainsKey(record.Mod.ModId))
				{
					throw new InvalidOperationException("That mod is already installed again.");
				}
				List<TrashedDirectoryRecord> restored = new List<TrashedDirectoryRecord>();
				List<TrashedFileRecord> restoredFiles = new List<TrashedFileRecord>();
				try
				{
					foreach (TrashedDirectoryRecord directory in record.Directories)
					{
						cancellationToken.ThrowIfCancellationRequested();
						string text = ValidateInstalledPath(directory.OriginalPath);
						string text2 = ValidateTrashPath(directory.TrashPath);
						if (Directory.Exists(text))
						{
							throw new IOException("Cannot restore " + Path.GetFileName(text) + " because that folder already exists.");
						}
						if (!Directory.Exists(text2))
						{
							throw new DirectoryNotFoundException("Trash data is missing for " + record.Mod.Name + ".");
						}
						Directory.Move(text2, text);
						restored.Add(new TrashedDirectoryRecord
						{
							OriginalPath = text,
							TrashPath = text2
						});
					}
					foreach (TrashedFileRecord file in record.Files)
					{
						cancellationToken.ThrowIfCancellationRequested();
						string text3 = ValidateInstalledFile(file.OriginalPath);
						string text4 = ValidateTrashPath(file.TrashPath);
						if (File.Exists(text3))
						{
							throw new IOException("Cannot restore " + Path.GetFileName(text3) + " because it already exists.");
						}
						if (!File.Exists(text4))
						{
							throw new FileNotFoundException("Trash sidecar data is missing.", text4);
						}
						File.Move(text4, text3);
						restoredFiles.Add(new TrashedFileRecord
						{
							OriginalPath = text3,
							TrashPath = text4
						});
					}
					_state.InstalledMods[record.Mod.ModId] = record.Mod;
					_state.TrashHistory.Remove(record);
					await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
					this.InstalledChanged?.Invoke();
					return record.Mod;
				}
				catch
				{
					_state.InstalledMods.Remove(record.Mod.ModId);
					if (!_state.TrashHistory.Contains(record))
					{
						_state.TrashHistory.Add(record);
					}
					for (int num = restored.Count - 1; num >= 0; num--)
					{
						TrashedDirectoryRecord trashedDirectoryRecord = restored[num];
						if (Directory.Exists(trashedDirectoryRecord.OriginalPath) && !Directory.Exists(trashedDirectoryRecord.TrashPath))
						{
							Directory.Move(trashedDirectoryRecord.OriginalPath, trashedDirectoryRecord.TrashPath);
						}
					}
					for (int num2 = restoredFiles.Count - 1; num2 >= 0; num2--)
					{
						TrashedFileRecord trashedFileRecord = restoredFiles[num2];
						if (File.Exists(trashedFileRecord.OriginalPath) && !File.Exists(trashedFileRecord.TrashPath))
						{
							File.Move(trashedFileRecord.OriginalPath, trashedFileRecord.TrashPath);
						}
					}
					throw;
				}
			}
			finally
			{
				_stateGate.Release();
			}
		}

		private void OnJobChanged(DownloadJob job)
		{
			if (job.State == DownloadState.Completed && job.Result != null)
			{
				PersistCompletedJobAsync(job, job.Result);
			}
			else
			{
				this.DownloadChanged?.Invoke(job);
			}
		}

		private async Task PersistCompletedJobAsync(DownloadJob job, BoneHub.Downloads.InstalledModRecord result)
		{
			await _stateGate.WaitAsync().ConfigureAwait(continueOnCapturedContext: false);
			try
			{
				_state.InstalledMods[result.ModId] = new BoneHub.Persistence.InstalledModRecord
				{
					ModId = result.ModId,
					FileId = result.FileId,
					Name = result.Name,
					Version = result.Version,
					Md5 = result.Md5,
					InstalledDirectories = result.InstalledDirectories.ToList(),
					Sharing = result.Sharing,
					InstalledAt = DateTimeOffset.UtcNow
				};
				await _stateStore.SaveAsync(_state).ConfigureAwait(continueOnCapturedContext: false);
				this.DownloadChanged?.Invoke(job);
				this.InstalledChanged?.Invoke();
			}
			finally
			{
				_stateGate.Release();
			}
		}

		private string ValidateInstalledPath(string path)
		{
			string text = Path.GetFullPath(_options.ModsDirectory).TrimEnd(Path.DirectorySeparatorChar);
			string text2 = Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar);
			if (!text2.StartsWith(text + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) || string.Equals(text2, text, StringComparison.OrdinalIgnoreCase))
			{
				throw new InvalidDataException("An installed-mod path falls outside BONELAB's Mods directory.");
			}
			return text2;
		}

		private string ValidateInstalledFile(string path)
		{
			string text = Path.GetFullPath(_options.ModsDirectory).TrimEnd(Path.DirectorySeparatorChar);
			string fullPath = Path.GetFullPath(path);
			if (!fullPath.StartsWith(text + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) || !string.Equals(Path.GetExtension(fullPath), ".manifest", StringComparison.OrdinalIgnoreCase))
			{
				throw new InvalidDataException("A mod registration sidecar falls outside BONELAB's Mods directory.");
			}
			return fullPath;
		}

		private IReadOnlyList<string> FindRegistrationSidecars(IEnumerable<string> packageDirectories)
		{
			string[] source = packageDirectories.Select(Path.GetFullPath).ToArray();
			List<string> list = new List<string>();
			if (!Directory.Exists(_options.ModsDirectory))
			{
				return list;
			}
			foreach (string item in Directory.EnumerateFiles(_options.ModsDirectory, "*.manifest", SearchOption.TopDirectoryOnly))
			{
				try
				{
					using JsonDocument jsonDocument = JsonDocument.Parse(File.ReadAllText(item));
					JsonElement rootElement = jsonDocument.RootElement;
					string propertyName = rootElement.GetProperty("root").GetProperty("ref").GetString() ?? "1";
					string text = rootElement.GetProperty("objects").GetProperty(propertyName).GetProperty("palletPath")
						.GetString();
					if (!string.IsNullOrWhiteSpace(text))
					{
						string fullPallet = Path.GetFullPath(text);
						if (source.Any((string package) => fullPallet.StartsWith(package.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)))
						{
							list.Add(item);
						}
					}
				}
				catch
				{
				}
			}
			return list;
		}

		private string ValidateTrashPath(string path)
		{
			string? path2 = Path.GetDirectoryName(Path.GetFullPath(_options.ModsDirectory).TrimEnd(Path.DirectorySeparatorChar)) ?? throw new InvalidOperationException("Mods directory has no parent.");
			string text = Path.GetFullPath(Path.Combine(path2, ".wristhub-trash")).TrimEnd(Path.DirectorySeparatorChar);
			string text2 = Path.GetFullPath(Path.Combine(path2, ".bonehub-trash")).TrimEnd(Path.DirectorySeparatorChar);
			string text3 = Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar);
			bool num = text3.StartsWith(text + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) && !string.Equals(text3, text, StringComparison.OrdinalIgnoreCase);
			bool flag = text3.StartsWith(text2 + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) && !string.Equals(text3, text2, StringComparison.OrdinalIgnoreCase);
			if (!num && !flag)
			{
				throw new InvalidDataException("A restore path falls outside WristHub Trash.");
			}
			return text3;
		}

		public void Dispose()
		{
			_downloads.JobChanged -= OnJobChanged;
			_downloads.Dispose();
			(_client as IDisposable)?.Dispose();
			_stateGate.Dispose();
		}
	}
}
namespace BoneHub.Persistence
{
	public sealed class BoneHubState
	{
		public HashSet<long> FavoriteModIds { get; init; } = new HashSet<long>();

		public List<long> RecentlyViewedModIds { get; init; } = new List<long>();

		public List<string> RecentlyEquippedAvatarBarcodes { get; init; } = new List<string>();

		public string LastEquippedAvatarBarcode { get; set; } = string.Empty;

		public Dictionary<long, InstalledModRecord> InstalledMods { get; init; } = new Dictionary<long, InstalledModRecord>();

		public BoneHubPreferences Preferences { get; init; } = new BoneHubPreferences();

		public List<TrashedModRecord> TrashHistory { get; init; } = new List<TrashedModRecord>();

		public List<PendingDeletionRecord> PendingDeletions { get; init; } = new List<PendingDeletionRecord>();
	}
	public sealed class BoneHubPreferences
	{
		public BoneHubComfortPreset ComfortPreset { get; set; }

		public bool WatchEnabled { get; set; } = true;

		public bool RaiseToOpen { get; set; } = true;

		public bool UseLeftWrist { get; set; } = true;

		public float WatchScale { get; set; } = 1f;

		public float DwellSeconds { get; set; } = 1f;

		public float OutwardOffset { get; set; } = 0.029f;

		public float FingerOffset { get; set; } = -0.018f;

		public float WatchForearmOffset { get; set; }

		public bool ReducedMotion { get; set; }

		public bool HologramEffects { get; set; } = true;

		public bool HapticsEnabled { get; set; } = true;

		public bool SoundsEnabled { get; set; } = true;

		public float CarouselDistance { get; set; } = 0.62f;

		public float HologramScale { get; set; } = 1f;

		public float EquipZoneScale { get; set; } = 1f;

		public float HologramBrightness { get; set; } = 1f;

		public WristHubTheme HologramTheme { get; set; }

		public bool Use24HourClock { get; set; } = true;

		public BrowserStartupMode AvatarBrowserStartupMode { get; set; } = BrowserStartupMode.AllInstalled;

		public AvatarBrowseScope LastAvatarBrowseScope { get; set; }

		public List<CompassCalibrationRecord> CompassCalibrations { get; init; } = new List<CompassCalibrationRecord>();

		public MiniMapRange MiniMapRange { get; set; } = MiniMapRange.ThirtyMeters;

		public MiniMapOrientation MiniMapOrientation { get; set; }

		public List<string> FavoriteSpawnableBarcodes { get; init; } = new List<string>();

		public List<string> RecentSpawnableBarcodes { get; init; } = new List<string>();
	}
	public enum WristHubTheme
	{
		Cyan,
		Graphite,
		Crimson,
		Emerald,
		Violet,
		Amber
	}
	public sealed class CompassCalibrationRecord
	{
		public string SceneKey { get; init; } = string.Empty;

		public float NorthYawDegrees { get; set; }

		public DateTimeOffset LastUsedUtc { get; set; } = DateTimeOffset.UtcNow;
	}
	public enum BoneHubComfortPreset
	{
		Default,
		Seated,
		LargeTargets,
		ReducedMotion
	}
	public sealed class InstalledModRecord
	{
		public long ModId { get; init; }

		public long FileId { get; init; }

		public string Name { get; init; } = string.Empty;

		public string Version { get; init; } = string.Empty;

		public string Md5 { get; init; } = string.Empty;

		public List<string> InstalledDirectories { get; init; } = new List<string>();

		public ModIoSharingMetadata? Sharing { get; set; }

		public DateTimeOffset InstalledAt { get; init; }
	}
	public sealed class TrashedModRecord
	{
		public Guid Id { get; init; } = Guid.NewGuid();

		public InstalledModRecord Mod { get; init; } = new InstalledModRecord();

		public List<TrashedDirectoryRecord> Directories { get; init; } = new List<TrashedDirectoryRecord>();

		public List<TrashedFileRecord> Files { get; init; } = new List<TrashedFileRecord>();

		public bool WasSubscribed { get; init; }

		public DateTimeOffset TrashedAt { get; init; } = DateTimeOffset.UtcNow;
	}
	public sealed class TrashedFileRecord
	{
		public string OriginalPath { get; init; } = string.Empty;

		public string TrashPath { get; init; } = string.Empty;
	}
	public sealed class TrashedDirectoryRecord
	{
		public string OriginalPath { get; init; } = string.Empty;

		public string TrashPath { get; init; } = string.Empty;
	}
	public sealed class PendingDeletionRecord
	{
		public InstalledModRecord Mod { get; init; } = new InstalledModRecord();

		public List<string> Directories { get; init; } = new List<string>();

		public List<string> SidecarFiles { get; init; } = new List<string>();

		public List<string> AvatarBarcodes { get; init; } = new List<string>();

		public bool WasSubscribed { get; init; }

		public DateTimeOffset QueuedAt { get; init; } = DateTimeOffset.UtcNow;
	}
	public sealed record PendingDeletionProcessResult(int Completed, int Remaining, IReadOnlyList<string> Problems);
	public sealed class JsonFileStore<T> where T : class, new()
	{
		private readonly string _path;

		private readonly SemaphoreSlim _gate = new SemaphoreSlim(1, 1);

		private readonly JsonSerializerOptions _json = new JsonSerializerOptions
		{
			WriteIndented = true,
			PropertyNameCaseInsensitive = true
		};

		public JsonFileStore(string path)
		{
			_path = Path.GetFullPath(path);
		}

		public async Task<T> LoadAsync(CancellationToken cancellationToken = default(CancellationToken))
		{
			await _gate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			try
			{
				if (!File.Exists(_path))
				{
					return new T();
				}
				T result;
				await using (FileStream stream = File.OpenRead(_path))
				{
					result = (await JsonSerializer.DeserializeAsync<T>(stream, _json, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)) ?? new T();
				}
				return result;
			}
			catch (JsonException)
			{
				string destFileName = _path + $".corrupt-{DateTimeOffset.UtcNow:yyyyMMddHHmmss}";
				File.Move(_path, destFileName, overwrite: false);
				return new T();
			}
			finally
			{
				_gate.Release();
			}
		}

		public async Task SaveAsync(T value, CancellationToken cancellationToken = default(CancellationToken))
		{
			await _gate.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(_path));
				string temporary = _path + ".tmp";
				await using (FileStream stream = new FileStream(temporary, FileMode.Create, FileAccess.Write, FileShare.None))
				{
					await JsonSerializer.SerializeAsync(stream, value, _json, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
					await stream.FlushAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
				}
				File.Move(temporary, _path, overwrite: true);
			}
			finally
			{
				_gate.Release();
			}
		}
	}
	public sealed class SearchCache
	{
		public sealed class CachedSearch
		{
			public DateTimeOffset SavedAt { get; init; }

			public ModIoPage<ModIoMod> Page { get; init; } = new ModIoPage<ModIoMod>();
		}

		private readonly string _directory;

		public SearchCache(string directory)
		{
			_directory = Path.GetFullPath(directory);
		}

		public async Task<ModIoPage<ModIoMod>?> ReadAsync(string key, TimeSpan maximumAge, CancellationToken cancellationToken = default(CancellationToken))
		{
			CachedSearch cachedSearch = await new JsonFileStore<CachedSearch>(PathFor(key)).LoadAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
			return (cachedSearch.SavedAt != default(DateTimeOffset) && DateTimeOffset.UtcNow - cachedSearch.SavedAt <= maximumAge) ? cachedSearch.Page : null;
		}

		public Task WriteAsync(string key, ModIoPage<ModIoMod> page, CancellationToken cancellationToken = default(CancellationToken))
		{
			return new JsonFileStore<CachedSearch>(PathFor(key)).SaveAsync(new CachedSearch
			{
				SavedAt = DateTimeOffset.UtcNow,
				Page = page
			}, cancellationToken);
		}

		private string PathFor(string key)
		{
			Directory.CreateDirectory(_directory);
			byte[] inArray = SHA256.HashData(Encoding.UTF8.GetBytes(key));
			return Path.Combine(_directory, Convert.ToHexString(inArray).ToLowerInvariant() + ".json");
		}
	}
}
namespace BoneHub.Models
{
	public sealed class ModIoPage<T>
	{
		[JsonPropertyName("data")]
		public List<T> Data { get; init; } = new List<T>();

		[JsonPropertyName("result_count")]
		public int ResultCount { get; init; }

		[JsonPropertyName("result_offset")]
		public int ResultOffset { get; init; }

		[JsonPropertyName("result_limit")]
		public int ResultLimit { get; init; }

		[JsonPropertyName("result_total")]
		public int ResultTotal { get; init; }
	}
	public sealed class ModIoMod
	{
		[JsonPropertyName("id")]
		public long Id { get; init; }

		[JsonPropertyName("name")]
		public string Name { get; init; } = string.Empty;

		[JsonPropertyName("name_id")]
		public string NameId { get; init; } = string.Empty;

		[JsonPropertyName("summary")]
		public string Summary { get; init; } = string.Empty;

		[JsonPropertyName("description_plaintext")]
		public string Description { get; init; } = string.Empty;

		[JsonPropertyName("profile_url")]
		public string ProfileUrl { get; init; } = string.Empty;

		[JsonPropertyName("date_updated")]
		public long DateUpdated { get; init; }

		[JsonPropertyName("submitted_by")]
		public ModIoUser Author { get; init; } = new ModIoUser();

		[JsonPropertyName("logo")]
		public ModIoLogo Logo { get; init; } = new ModIoLogo();

		[JsonPropertyName("modfile")]
		public ModIoFile? Modfile { get; init; }

		[JsonPropertyName("stats")]
		public ModIoStats Stats { get; init; } = new ModIoStats();

		[JsonPropertyName("tags")]
		public List<ModIoTag> Tags { get; init; } = new List<ModIoTag>();

		[JsonPropertyName("maturity_option")]
		public int MaturityOption { get; init; }

		public bool HasTag(string value)
		{
			return Tags.Any((ModIoTag tag) => string.Equals(tag.Name, value, StringComparison.OrdinalIgnoreCase));
		}
	}
	public sealed class ModIoUser
	{
		[JsonPropertyName("username")]
		public string Username { get; init; } = string.Empty;

		[JsonPropertyName("display_name_portal")]
		public string? DisplayNamePortal { get; init; }

		public string DisplayName
		{
			get
			{
				if (!string.IsNullOrWhiteSpace(DisplayNamePortal))
				{
					return DisplayNamePortal;
				}
				return Username;
			}
		}
	}
	public sealed class ModIoLogo
	{
		[JsonPropertyName("original")]
		public string Original { get; init; } = string.Empty;

		[JsonPropertyName("thumb_320x180")]
		public string Thumbnail { get; init; } = string.Empty;
	}
	public sealed class ModIoStats
	{
		[JsonPropertyName("downloads_total")]
		public long DownloadsTotal { get; init; }

		[JsonPropertyName("subscribers_total")]
		public long SubscribersTotal { get; init; }

		[JsonPropertyName("ratings_weighted_aggregate")]
		public double Rating { get; init; }
	}
	public sealed class ModIoTag
	{
		[JsonPropertyName("name")]
		public string Name { get; init; } = string.Empty;
	}
	public sealed class ModIoFile
	{
		[JsonPropertyName("id")]
		public long Id { get; init; }

		[JsonPropertyName("mod_id")]
		public long ModId { get; init; }

		[JsonPropertyName("filename")]
		public string Filename { get; init; } = string.Empty;

		[JsonPropertyName("version")]
		public string Version { get; init; } = string.Empty;

		[JsonPropertyName("date_added")]
		public long DateAdded { get; init; }

		[JsonPropertyName("filesize")]
		public long FileSize { get; init; }

		[JsonPropertyName("filesize_uncompressed")]
		public long UncompressedSize { get; init; }

		[JsonPropertyName("virus_status")]
		public int VirusStatus { get; init; }

		[JsonPropertyName("virus_positive")]
		public int VirusPositive { get; init; }

		[JsonPropertyName("filehash")]
		public ModIoFileHash Hash { get; init; } = new ModIoFileHash();

		[JsonPropertyName("download")]
		public ModIoDownload Download { get; init; } = new ModIoDownload();

		[JsonPropertyName("platforms")]
		public List<ModIoPlatform> Platforms { get; init; } = new List<ModIoPlatform>();
	}
	public sealed class ModIoFileHash
	{
		[JsonPropertyName("md5")]
		public string Md5 { get; init; } = string.Empty;
	}
	public sealed class ModIoDownload
	{
		[JsonPropertyName("binary_url")]
		public string BinaryUrl { get; init; } = string.Empty;

		[JsonPropertyName("date_expires")]
		public long DateExpires { get; init; }
	}
	public sealed class ModIoPlatform
	{
		[JsonPropertyName("platform")]
		public string Platform { get; init; } = string.Empty;

		[JsonPropertyName("status")]
		public int Status { get; init; }
	}
	public sealed class ModIoDependency
	{
		[JsonPropertyName("mod_id")]
		public long ModId { get; init; }

		[JsonPropertyName("date_added")]
		public long DateAdded { get; init; }
	}
	public enum ModSort
	{
		Trending,
		Newest,
		MostDownloaded,
		RecentlyUpdated
	}
	public sealed record ModSearchRequest(string Query, string? Tag = null, string? Creator = null, ModSort Sort = ModSort.Trending, int Offset = 0, int Limit = 20);
	public sealed record ModUpdateInfo(ModIoMod Mod, bool UpdateAvailable, long InstalledFileId, long LatestFileId);
	public sealed record ModInstallPlan(ModIoMod Root, IReadOnlyList<ModIoMod> OrderedMods, IReadOnlyList<long> AlreadyInstalledIds);
	public sealed record InstalledModHealth(bool Healthy, IReadOnlyList<string> Problems);
}
namespace BoneHub.Interaction
{
	public enum AvatarBrowseScope
	{
		AllInstalled,
		Groups
	}
	public enum BrowserStartupMode
	{
		RememberLast,
		AllInstalled,
		Groups
	}
	public static class AvatarBrowseStartup
	{
		public static AvatarBrowseScope Resolve(BrowserStartupMode mode, AvatarBrowseScope lastScope)
		{
			return mode switch
			{
				BrowserStartupMode.RememberLast => Enum.IsDefined(lastScope) ? lastScope : AvatarBrowseScope.AllInstalled, 
				BrowserStartupMode.Groups => AvatarBrowseScope.Groups, 
				_ => AvatarBrowseScope.AllInstalled, 
			};
		}
	}
	public enum AvatarBrowserViewState
	{
		PushPrompt,
		Hub,
		Library,
		Pack,
		Downloads,
		Compass,
		CodeMods,
		MiniMap,
		Portals,
		PortalLevels,
		FusionServers,
		Spawnables,
		PackOptions,
		Options,
		Keyboard,
		Searching,
		Result,
		Downloading,
		PreparingPreview,
		Error
	}
	public enum AvatarPackSort
	{
		RecentlyUsed,
		Name,
		Creator
	}
	public sealed record AvatarPackEntry(long ModId, string Name, string Creator, string ThumbnailUrl, IReadOnlyList<AvatarEntry> Avatars, bool Installed, AvatarDownloadState DownloadState = AvatarDownloadState.None, double DownloadProgress = 0.0, string? Status = null)
	{
		public string Identity => "pack:" + ModId;

		public int AvatarCount => Avatars.Count((AvatarEntry entry) => entry.Installed && !string.IsNullOrWhiteSpace(entry.Barcode));
	}
	public static class AvatarPackLibrary
	{
		public static IReadOnlyList<AvatarPackEntry> Group(IEnumerable<AvatarEntry> entries, IEnumerable<string>? recentBarcodes = null, AvatarPackSort sort = AvatarPackSort.RecentlyUsed)
		{
			Dictionary<string, int> recent = (recentBarcodes ?? Array.Empty<string>()).Select((string barcode, int index) => (barcode: barcode, index: index)).ToDictionary<(string, int), string, int>(((string barcode, int index) item) => item.barcode, ((string barcode, int index) item) => item.index, StringComparer.OrdinalIgnoreCase);
			IEnumerable<AvatarPackEntry> source = (from entry in entries
				group entry by entry.ModId).Select(delegate(IGrouping<long, AvatarEntry> group)
			{
				AvatarEntry[] array = group.OrderBy<AvatarEntry, string>((AvatarEntry item) => item.AvatarName, StringComparer.OrdinalIgnoreCase).ToArray();
				AvatarEntry avatarEntry = array.OrderByDescending((AvatarEntry item) => item.Installed).First();
				AvatarDownloadState downloadState = array.Select((AvatarEntry item) => item.DownloadState).OrderByDescending(DownloadRank).FirstOrDefault();
				return new AvatarPackEntry(group.Key, avatarEntry.ModName, avatarEntry.Creator, avatarEntry.ThumbnailUrl, array, array.Any((AvatarEntry item) => item.Installed), downloadState, array.Max((AvatarEntry item) => item.DownloadProgress), array.Select((AvatarEntry item) => item.Status).FirstOrDefault((string value) => !string.IsNullOrWhiteSpace(value)));
			});
			return sort switch
			{
				AvatarPackSort.Name => source.OrderBy<AvatarPackEntry, string>((AvatarPackEntry pack) => pack.Name, StringComparer.OrdinalIgnoreCase).ToArray(), 
				AvatarPackSort.Creator => source.OrderBy<AvatarPackEntry, string>((AvatarPackEntry pack) => pack.Creator, StringComparer.OrdinalIgnoreCase).ThenBy<AvatarPackEntry, string>((AvatarPackEntry pack) => pack.Name, StringComparer.OrdinalIgnoreCase).ToArray(), 
				_ => source.OrderBy((AvatarPackEntry pack) => RecentIndex(pack, recent)).ThenBy<AvatarPackEntry, string>((AvatarPackEntry pack) => pack.Name, StringComparer.OrdinalIgnoreCase).ToArray(), 
			};
		}

		private static int RecentIndex(AvatarPackEntry pack, IReadOnlyDictionary<string, int> recent)
		{
			int[] array = (from item in pack.Avatars
				where recent.ContainsKey(item.Barcode)
				select recent[item.Barcode]).ToArray();
			if (array.Length != 0)
			{
				return array.Min();
			}
			return int.MaxValue;
		}

		private static int DownloadRank(AvatarDownloadState state)
		{
			return state switch
			{
				AvatarDownloadState.Failed => 7, 
				AvatarDownloadState.Installing => 6, 
				AvatarDownloadState.Downloading => 5, 
				AvatarDownloadState.Queued => 4, 
				AvatarDownloadState.Cancelled => 3, 
				AvatarDownloadState.Installed => 2, 
				_ => 1, 
			};
		}
	}
	public sealed class FourRowPageCursor
	{
		public const int RowsPerPage = 4;

		public int Page { get; private set; }

		public int Count { get; private set; }

		public int PageCount => Math.Max(1, (Count + 4 - 1) / 4);

		public string PositionText => $"{Page + 1} / {PageCount}";

		public int Reset(int count)
		{
			Count = Math.Max(0, count);
			Page = 0;
			return Page;
		}

		public int Keep(int count)
		{
			Count = Math.Max(0, count);
			Page = Math.Clamp(Page, 0, PageCount - 1);
			return Page;
		}

		public int Move(int direction)
		{
			if (PageCount <= 1 || direction == 0)
			{
				return Page;
			}
			Page = (Page + Math.Sign(direction) + PageCount) % PageCount;
			return Page;
		}
	}
	public sealed class FocusedResultCursor
	{
		public int Index { get; private set; }

		public int Count { get; private set; }

		public string PositionText
		{
			get
			{
				if (Count != 0)
				{
					return $"{Index + 1} / {Count}";
				}
				return "0 / 0";
			}
		}

		public bool CanMovePrevious => Count > 1;

		public bool CanMoveNext => Count > 1;

		public int Reset(int count)
		{
			Count = Math.Max(0, count);
			Index = 0;
			return Index;
		}

		public int Keep(int count, int preferredIndex)
		{
			Count = Math.Max(0, count);
			Index = ((Count != 0) ? Math.Clamp(preferredIndex, 0, Count - 1) : 0);
			return Index;
		}

		public int Move(int direction)
		{
			if (Count <= 1 || direction == 0)
			{
				return Index;
			}
			Index = (Index + Math.Sign(direction) + Count) % Count;
			return Index;
		}
	}
	public readonly record struct TabletRect(float X, float Y, float Width, float Height)
	{
		public float Left => X - Width * 0.5f;

		public float Right => X + Width * 0.5f;

		public float Bottom => Y - Height * 0.5f;

		public float Top => Y + Height * 0.5f;

		public bool IsInside(TabletRect parent)
		{
			if (Left >= parent.Left && Right <= parent.Right && Bottom >= parent.Bottom)
			{
				return Top <= parent.Top;
			}
			return false;
		}

		public bool Overlaps(TabletRect other)
		{
			if (Left < other.Right && Right > other.Left && Bottom < other.Top)
			{
				return Top > other.Bottom;
			}
			return false;
		}
	}
	public static class AvatarTabletLayout
	{
		public static readonly TabletRect Tablet = new TabletRect(0f, 0f, 340f, 210f);

		public static readonly TabletRect Search = new TabletRect(-34f, 82f, 150f, 30f);

		public static readonly TabletRect Close = new TabletRect(148f, 82f, 30f, 30f);

		public static readonly TabletRect Artwork = new TabletRect(-66f, -4f, 124f, 128f);

		public static readonly TabletRect Details = new TabletRect(65f, 3f, 126f, 112f);

		public static readonly TabletRect Previous = new TabletRect(-151f, 14f, 26f, 54f);

		public static readonly TabletRect Next = new TabletRect(151f, 14f, 26f, 54f);

		public static readonly TabletRect Action = new TabletRect(65f, -69f, 126f, 30f);

		public static readonly TabletRect Trash = new TabletRect(-66f, -91f, 116f, 18f);

		public static readonly TabletRect Progress = new TabletRect(65f, -91f, 126f, 14f);

		public static IReadOnlyList<TabletRect> ContentRects => new TabletRect[9] { Search, Close, Artwork, Details, Previous, Next, Action, Trash, Progress };

		public static TabletRect PhysicalTouchRect(TabletRect rect)
		{
			return new TabletRect(rect.X, rect.Y, rect.Width * 1.1f, rect.Height * 1.2f);
		}
	}
	public static class ArcDashboardLayout
	{
		public static readonly TabletRect Presentation = new TabletRect(0f, 0f, 300f, 190f);

		public static readonly TabletRect Stage = new TabletRect(0f, 7f, 170f, 124f);

		public static readonly TabletRect LeftWing = new TabletRect(-122f, 16f, 50f, 146f);

		public static readonly TabletRect RightWing = new TabletRect(122f, 16f, 50f, 146f);

		public static readonly TabletRect Back = new TabletRect(-122f, 72f, 42f, 22f);

		public static readonly TabletRect Search = new TabletRect(-122f, 44f, 46f, 24f);

		public static readonly TabletRect Previous = new TabletRect(-122f, 7f, 42f, 42f);

		public static readonly TabletRect Scope = new TabletRect(-122f, -35f, 46f, 25f);

		public static readonly TabletRect Close = new TabletRect(122f, 72f, 30f, 26f);

		public static readonly TabletRect Pack = new TabletRect(122f, 44f, 46f, 24f);

		public static readonly TabletRect Next = new TabletRect(122f, 7f, 42f, 42f);

		public static readonly TabletRect Options = new TabletRect(122f, -35f, 46f, 25f);

		public static readonly TabletRect Name = new TabletRect(0f, 82f, 166f, 20f);

		public static readonly TabletRect Creator = new TabletRect(-45f, 65f, 78f, 14f);

		public static readonly TabletRect PackName = new TabletRect(45f, 65f, 92f, 14f);

		public static readonly TabletRect Height = new TabletRect(-54f, -53f, 94f, 16f);

		public static readonly TabletRect Position = new TabletRect(0f, -53f, 48f, 16f);

		public static readonly TabletRect Status = new TabletRect(54f, -53f, 94f, 18f);

		public static readonly TabletRect Action = new TabletRect(0f, -80f, 116f, 28f);

		public static readonly TabletRect Progress = Action;

		public static IReadOnlyList<TabletRect> TouchControls => new TabletRect[9] { Back, Search, Previous, Scope, Close, Pack, Next, Options, Action };
	}
	public static class WristOsHomeHeaderLayout
	{
		public static readonly TabletRect Download = new TabletRect(-86f, 70f, 28f, 23f);

		public static readonly TabletRect Wordmark = new TabletRect(-25f, 70f, 88f, 20f);

		public static readonly TabletRect OsMark = new TabletRect(31f, 70f, 20f, 20f);

		public static readonly TabletRect AccentRail = new TabletRect(-19f, 56f, 120f, 1.5f);

		public static readonly TabletRect Settings = new TabletRect(66f, 70f, 28f, 23f);

		public static readonly TabletRect Close = new TabletRect(106f, 70f, 24f, 24f);

		public static IReadOnlyList<TabletRect> InteractiveControls => new TabletRect[3] { Download, Settings, Close };
	}
	public static class AvatarPackTabletLayout
	{
		public static readonly IReadOnlyList<TabletRect> HeaderControls = new TabletRect[5]
		{
			new TabletRect(-145f, 83f, 42f, 28f),
			new TabletRect(-90f, 83f, 64f, 28f),
			new TabletRect(-34f, 83f, 46f, 28f),
			new TabletRect(25f, 83f, 68f, 28f),
			new TabletRect(148f, 83f, 26f, 28f)
		};

		public static readonly IReadOnlyList<TabletRect> Rows = (from index in Enumerable.Range(0, 4)
			select new TabletRect(-36f, 50f - (float)index * 34f, 228f, 30f)).ToArray();

		public static readonly IReadOnlyList<TabletRect> DeleteRows = (from index in Enumerable.Range(0, 4)
			select new TabletRect(125f, 50f - (float)index * 34f, 52f, 30f)).ToArray();

		public static readonly TabletRect Status = new TabletRect(0f, -72f, 250f, 10f);

		public static readonly IReadOnlyList<TabletRect> FooterControls = new TabletRect[3]
		{
			new TabletRect(-135f, -91f, 50f, 26f),
			new TabletRect(0f, -91f, 90f, 24f),
			new TabletRect(135f, -91f, 50f, 26f)
		};

		public static IReadOnlyList<TabletRect> AllControls => HeaderControls.Concat(Rows).Concat(DeleteRows).Append(Status)
			.Concat(FooterControls)
			.ToArray();
	}
	public sealed class PresentationGeneration
	{
		public int Value { get; private set; }

		public int Next()
		{
			return ++Value;
		}

		public bool IsCurrent(int generation)
		{
			return generation == Value;
		}
	}
	public sealed class SurfaceInputRearmGate
	{
		private readonly double _clearSeconds;

		private double _clearSince = -1.0;

		public bool Required { get; private set; }

		public SurfaceInputRearmGate(double clearSeconds = 0.1)
		{
			_clearSeconds = Math.Max(0.0, clearSeconds);
		}

		public void Require()
		{
			Required = true;
			_clearSince = -1.0;
		}

		public bool CanInteract(bool touchingAnyActiveControl, double nowSeconds)
		{
			if (!Required)
			{
				return true;
			}
			if (touchingAnyActiveControl)
			{
				_clearSince = -1.0;
				return false;
			}
			if (_clearSince < 0.0)
			{
				_clearSince = nowSeconds;
				return _clearSeconds <= 0.0;
			}
			if (nowSeconds - _clearSince < _clearSeconds)
			{
				return false;
			}
			Required = false;
			_clearSince = -1.0;
			return true;
		}
	}
	public sealed class AvatarSearchInputSession
	{
		public int Generation { get; private set; }

		public bool IsOpen { get; private set; }

		public string InitialValue { get; private set; } = string.Empty;

		public int Open(string? initialValue)
		{
			InitialValue = initialValue?.Trim() ?? string.Empty;
			IsOpen = true;
			return ++Generation;
		}

		public bool TrySubmit(int generation, string? value, out string query)
		{
			query = value?.Trim() ?? string.Empty;
			if (!IsOpen || generation != Generation)
			{
				return false;
			}
			IsOpen = false;
			return true;
		}

		public bool Cancel(int generation)
		{
			if (!IsOpen || generation != Generation)
			{
				return false;
			}
			IsOpen = false;
			return true;
		}
	}
	public sealed class TabletKeyboardBuffer
	{
		public const int MaximumLength = 64;

		public string Value { get; private set; } = string.Empty;

		public bool Shifted { get; private set; }

		public void Set(string? value)
		{
			Value = (((value ?? string.Empty).Length <= 64) ? (value ?? string.Empty) : value.Substring(0, 64));
		}

		public bool Append(char character)
		{
			if (Value.Length >= 64 || char.IsControl(character))
			{
				return false;
			}
			Value += (Shifted ? char.ToUpperInvariant(character) : char.ToLowerInvariant(character));
			return true;
		}

		public bool AppendSpace()
		{
			if (Value.Length >= 64 || Value.EndsWith(' '))
			{
				return false;
			}
			Value += " ";
			return true;
		}

		public bool Backspace()
		{
			if (Value.Length == 0)
			{
				return false;
			}
			string value = Value;
			Value = value.Substring(0, value.Length - 1);
			return true;
		}

		public void Clear()
		{
			Value = string.Empty;
		}

		public void ToggleShift()
		{
			Shifted = !Shifted;
		}

		public string VisibleValue(int maximumCharacters = 36)
		{
			int num = Math.Clamp(maximumCharacters, 4, 64);
			if (Value.Length > num)
			{
				string value = Value;
				int num2 = num - 1;
				int length = value.Length;
				int num3 = length - num2;
				return "…" + value.Substring(num3, length - num3);
			}
			return Value;
		}
	}
	public static class PreviewDragMath
	{
		public static bool IsInside(Vector3 localPoint, Vector3 halfExtents)
		{
			if (MathF.Abs(localPoint.X) <= MathF.Max(0f, halfExtents.X) && MathF.Abs(localPoint.Y) <= MathF.Max(0f, halfExtents.Y))
			{
				return MathF.Abs(localPoint.Z) <= MathF.Max(0f, halfExtents.Z);
			}
			return false;
		}

		public static bool PinchActive(float thumbIndexDistance, bool alreadyDragging, float beginDistance = 0.03f, float releaseDistance = 0.045f)
		{
			if (float.IsFinite(thumbIndexDistance))
			{
				return thumbIndexDistance <= (alreadyDragging ? MathF.Max(beginDistance, releaseDistance) : beginDistance);
			}
			return false;
		}

		public static bool GripActive(float gripForce, bool alreadyDragging, float beginForce = 0.62f, float releaseForce = 0.28f)
		{
			if (float.IsFinite(gripForce))
			{
				return gripForce >= (alreadyDragging ? MathF.Min(beginForce, releaseForce) : beginForce);
			}
			return false;
		}

		public static bool IsChestRelease(Vector3 previewPosition, Vector3 chestPosition, float radius)
		{
			if (radius > 0f)
			{
				return Vector3.Distance(previewPosition, chestPosition) <= radius;
			}
			return false;
		}

		public static Vector3 LimitAnchorOffset(Vector3 offset, float maximumDistance)
		{
			if (!float.IsFinite(offset.X) || !float.IsFinite(offset.Y) || !float.IsFinite(offset.Z))
			{
				return Vector3.Zero;
			}
			float num = Math.Clamp(maximumDistance, 0.01f, 0.2f);
			float num2 = offset.LengthSquared();
			if (!float.IsFinite(num2) || num2 <= 0f)
			{
				return Vector3.Zero;
			}
			if (num2 <= num * num)
			{
				return offset;
			}
			return Vector3.Normalize(offset) * num;
		}
	}
	public static class ChestEquipGuideMath
	{
		public static bool ResolveLocked(float distance, bool wasLocked, float radius, float releaseMultiplier = 1.12f)
		{
			if (!float.IsFinite(distance) || !float.IsFinite(radius) || radius <= 0f)
			{
				return false;
			}
			float num = (wasLocked ? (radius * Math.Clamp(releaseMultiplier, 1f, 1.5f)) : radius);
			return distance <= num;
		}

		public static float Proximity(float distance, float radius, float visibleRadiusMultiplier = 2.75f)
		{
			if (!float.IsFinite(distance) || !float.IsFinite(radius) || radius <= 0f)
			{
				return 0f;
			}
			float num = radius * Math.Clamp(visibleRadiusMultiplier, 1.1f, 6f);
			if (distance <= radius)
			{
				return 1f;
			}
			return Math.Clamp(1f - (distance - radius) / (num - radius), 0f, 1f);
		}
	}
	public sealed class LookAwayDismissal
	{
		public double AwaySeconds { get; private set; }

		public bool Update(bool observed, double deltaSeconds, double delaySeconds = 0.35)
		{
			if (observed)
			{
				AwaySeconds = 0.0;
				return false;
			}
			AwaySeconds += Math.Clamp(deltaSeconds, 0.0, 0.25);
			return AwaySeconds >= Math.Max(0.05, delaySeconds);
		}

		public void Reset()
		{
			AwaySeconds = 0.0;
		}
	}
	public readonly record struct AvatarPreviewMetrics(string Barcode, float GameplayHeightMeters, bool Valid)
	{
		public string DisplayText
		{
			get
			{
				if (!Valid || !float.IsFinite(GameplayHeightMeters) || !(GameplayHeightMeters > 0f))
				{
					return "Height unavailable";
				}
				if (!(GameplayHeightMeters < 10f))
				{
					if (GameplayHeightMeters < 100f)
					{
						return $"{GameplayHeightMeters:0.0} m";
					}
					return $"{GameplayHeightMeters:#,##0} m";
				}
				return $"{GameplayHeightMeters:0.00} m";
			}
		}

		public string HeightDisplayText
		{
			get
			{
				if (!Valid)
				{
					return "Avatar height unavailable";
				}
				return "Avatar height " + DisplayText;
			}
		}
	}
	public enum AvatarPreviewVisualSource
	{
		Unavailable,
		FullColor,
		NativePreviewMesh
	}
	public enum AvatarPreviewHeightSource
	{
		Unavailable,
		AvatarMetadata,
		RendererBounds,
		PreviewMeshBounds
	}
	public readonly record struct AvatarResolvedHeight(float Meters, AvatarPreviewHeightSource Source)
	{
		public bool Valid => AvatarPreviewMath.IsDisplayableGameplayHeight(Meters);
	}
	public readonly record struct AvatarPreviewRouteMetadata(AvatarPreviewVisualSource VisualSource, AvatarPreviewHeightSource HeightSource, bool InteractionReady);
	public readonly record struct AvatarPreviewPlacement(float UniformScale, Vector3 Offset)
	{
		public bool Valid
		{
			get
			{
				if (float.IsFinite(UniformScale) && UniformScale > 0f && float.IsFinite(Offset.X) && float.IsFinite(Offset.Y))
				{
					return float.IsFinite(Offset.Z);
				}
				return false;
			}
		}
	}
	public static class AvatarPreviewMath
	{
		public const float DisplayHeightMeters = 0.16f;

		public const float DisplayWidthMeters = 0.135f;

		public const float DisplayDepthMeters = 0.1f;

		public const float FrontFacingYawDegrees = 180f;

		public static float NormalizeScale(float visualHeightMeters, float displayHeightMeters = 0.16f)
		{
			if (!IsUsableHeight(visualHeightMeters))
			{
				return 1f;
			}
			return Math.Clamp(displayHeightMeters, 0.05f, 0.3f) / visualHeightMeters;
		}

		public static bool IsUsableHeight(float value)
		{
			if (float.IsFinite(value))
			{
				if (value >= 0.05f)
				{
					return value <= 100f;
				}
				return false;
			}
			return false;
		}

		public static bool IsDisplayableGameplayHeight(float value)
		{
			if (float.IsFinite(value))
			{
				if (value >= 0.01f)
				{
					return value <= 10000f;
				}
				return false;
			}
			return false;
		}

		public static float NormalizeBoundsScale(Vector3 visualSize)
		{
			if (!float.IsFinite(visualSize.Y) || visualSize.Y <= 0.0001f)
			{
				return 1f;
			}
			return 0.16f / visualSize.Y;
		}

		public static AvatarPreviewPlacement FitBounds(Vector3 visualCenter, Vector3 visualSize)
		{
			float num = NormalizeBoundsScale(visualSize);
			return new AvatarPreviewPlacement(num, -visualCenter * num);
		}

		public static AvatarResolvedHeight ResolveHeight(float avatarMetadataHeight, float rendererBoundsHeight, float previewMeshBoundsHeight = 0f)
		{
			if (IsUsableHeight(avatarMetadataHeight))
			{
				return new AvatarResolvedHeight(avatarMetadataHeight, AvatarPreviewHeightSource.AvatarMetadata);
			}
			if (IsUsableHeight(rendererBoundsHeight))
			{
				return new AvatarResolvedHeight(rendererBoundsHeight, AvatarPreviewHeightSource.RendererBounds);
			}
			if (IsUsableHeight(previewMeshBoundsHeight))
			{
				return new AvatarResolvedHeight(previewMeshBoundsHeight, AvatarPreviewHeightSource.PreviewMeshBounds);
			}
			return new AvatarResolvedHeight(0f, AvatarPreviewHeightSource.Unavailable);
		}

		public static AvatarResolvedHeight ResolveGameplayHeight(float selectedAvatarHeight, float fallbackAvatarHeight)
		{
			if (IsDisplayableGameplayHeight(selectedAvatarHeight))
			{
				return new AvatarResolvedHeight(selectedAvatarHeight, AvatarPreviewHeightSource.AvatarMetadata);
			}
			if (IsDisplayableGameplayHeight(fallbackAvatarHeight))
			{
				return new AvatarResolvedHeight(fallbackAvatarHeight, AvatarPreviewHeightSource.AvatarMetadata);
			}
			return new AvatarResolvedHeight(0f, AvatarPreviewHeightSource.Unavailable);
		}

		public static bool IsVisualRendererType(string? typeName)
		{
			if (!string.IsNullOrWhiteSpace(typeName) && !typeName.Contains("Particle", StringComparison.OrdinalIgnoreCase) && !typeName.Contains("Trail", StringComparison.OrdinalIgnoreCase))
			{
				return !typeName.Contains("LineRenderer", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		public static bool IsSafePreview(float visualHeightMeters, int rendererCount, int vertexCount, bool constrained)
		{
			int num = (constrained ? 24 : 64);
			int num2 = (constrained ? 350000 : 1250000);
			if (IsUsableHeight(visualHeightMeters) && rendererCount > 0 && rendererCount <= num && vertexCount >= 0)
			{
				return vertexCount <= num2;
			}
			return false;
		}
	}
	public static class AvatarUiScaleMath
	{
		public const float ReferencePlayerHeightMeters = 1.75f;

		public static float ResolveMenuScale(float playerHeightMeters)
		{
			if (!AvatarPreviewMath.IsUsableHeight(playerHeightMeters))
			{
				return 1f;
			}
			return playerHeightMeters / 1.75f;
		}

		public static float ResolveDetachedContentScale(float tabletWorldScale)
		{
			if (!float.IsFinite(tabletWorldScale) || !(tabletWorldScale > 0.0001f))
			{
				return 1f;
			}
			return tabletWorldScale;
		}
	}
	public readonly record struct NativeMenuPlacement(Vector3 Position, Vector3 Forward, float DistanceMeters);
	public static class NativeMenuPlacementMath
	{
		public const float ReferenceDistanceMeters = 1.05f;

		public static Vector3 ResolveForward(Vector3 headForward, Vector3 fallbackForward)
		{
			Vector3 value = new Vector3(headForward.X, 0f, headForward.Z);
			if (value.LengthSquared() < 0.04f)
			{
				value = new Vector3(fallbackForward.X, 0f, fallbackForward.Z);
			}
			if (value.LengthSquared() < 0.0001f)
			{
				value = Vector3.UnitZ;
			}
			return Vector3.Normalize(value);
		}

		public static NativeMenuPlacement Resolve(Vector3 headPosition, Vector3 headForward, float avatarHeightMeters)
		{
			Vector3 vector = ResolveForward(headForward, Vector3.UnitZ);
			float num = Math.Clamp(AvatarUiScaleMath.ResolveMenuScale(avatarHeightMeters), 0.65f, 1.75f);
			float num2 = 1.05f * num;
			return new NativeMenuPlacement(headPosition + vector * num2 - Vector3.UnitY * (0.06f * num), vector, num2);
		}
	}
	public static class DownloadPresentationMath
	{
		public static double Smooth(double displayed, double authoritative, double deltaSeconds, double speed = 8.0)
		{
			double num = Math.Clamp(displayed, 0.0, 1.0);
			double num2 = Math.Clamp(authoritative, 0.0, 1.0);
			if (num2 <= num || deltaSeconds <= 0.0)
			{
				return Math.Min(num, (num2 < num) ? num : num2);
			}
			double num3 = 1.0 - Math.Exp((0.0 - Math.Max(0.1, speed)) * Math.Min(deltaSeconds, 0.25));
			double num4 = num + (num2 - num) * num3;
			if (!(num2 - num4 < 0.0005))
			{
				return Math.Min(num4, num2);
			}
			return num2;
		}
	}
	public static class FingertipProbeMath
	{
		public static Vector3 ControllerTip(Vector3 position, Vector3 forward, float reachMeters = 0.075f)
		{
			Vector3 vector = ((forward.LengthSquared() > 0.0001f) ? Vector3.Normalize(forward) : Vector3.UnitZ);
			return position + vector * Math.Clamp(reachMeters, 0f, 0.15f);
		}

		public static Vector3 Extrapolate(Vector3 intermediate, Vector3 distal, float distalFraction = 0.65f)
		{
			Vector3 value = distal - intermediate;
			float num = value.Length();
			if (!(num > 0.0001f))
			{
				return distal;
			}
			return distal + Vector3.Normalize(value) * num * Math.Clamp(distalFraction, 0f, 1.5f);
		}
	}
	public static class WorldTextFacingMath
	{
		public const float FrontFacingLocalYawDegrees = 0f;

		public static bool IsFrontFacing(float localYawDegrees)
		{
			return Math.Abs(Math.IEEERemainder(localYawDegrees - 0f, 360.0)) < 0.0010000000474974513;
		}

		public static bool FacesViewer(Vector3 rootForward, Vector3 directionToViewer, float localYawDegrees)
		{
			if (rootForward.LengthSquared() < 0.0001f || directionToViewer.LengthSquared() < 0.0001f)
			{
				return false;
			}
			Vector3 vector = Vector3.Normalize(rootForward);
			Vector3 value = Vector3.Cross(Vector3.UnitY, vector);
			value = ((!(value.LengthSquared() < 0.0001f)) ? Vector3.Normalize(value) : Vector3.UnitX);
			float x = localYawDegrees * (float)Math.PI / 180f;
			return Vector3.Dot(Vector3.Normalize(-vector * MathF.Cos(x) - value * MathF.Sin(x)), Vector3.Normalize(directionToViewer)) > 0.999f;
		}
	}
	public enum AvatarRestoreDecision
	{
		NativeSaveOwnsSelection,
		WaitForNativeSave,
		RestorePrivateFallback,
		NoRestore
	}
	public static class AvatarRestorePolicy
	{
		public static AvatarRestoreDecision Decide(bool nativeSaveReadable, bool nativeSaveWaitExpired, string? privateFallbackBarcode)
		{
			if (nativeSaveReadable)
			{
				return AvatarRestoreDecision.NativeSaveOwnsSelection;
			}
			if (!nativeSaveWaitExpired)
			{
				return AvatarRestoreDecision.WaitForNativeSave;
			}
			if (!string.IsNullOrWhiteSpace(privateFallbackBarcode))
			{
				return AvatarRestoreDecision.RestorePrivateFallback;
			}
			return AvatarRestoreDecision.NoRestore;
		}
	}
	public enum AvatarRigCompatibility
	{
		UnverifiedAllowed,
		Verified,
		Invalid
	}
	public readonly record struct AvatarRigValidationResult(AvatarRigCompatibility Compatibility, string Reason)
	{
		public bool CanEquip => Compatibility != AvatarRigCompatibility.Invalid;

		public bool IsVerified => Compatibility == AvatarRigCompatibility.Verified;

		public static AvatarRigValidationResult Ready()
		{
			return new AvatarRigValidationResult(AvatarRigCompatibility.Verified, string.Empty);
		}

		public static AvatarRigValidationResult Allow(string reason)
		{
			return new AvatarRigValidationResult(AvatarRigCompatibility.UnverifiedAllowed, reason);
		}

		public static AvatarRigValidationResult Reject(string reason)
		{
			return new AvatarRigValidationResult(AvatarRigCompatibility.Invalid, reason);
		}
	}
	public enum AvatarSharingState
	{
		Unregistered,
		Registering,
		Ready,
		Announced,
		PlatformLimited,
		Failed
	}
	public readonly record struct AvatarPlatformAvailability(long? WindowsFileId, long? AndroidFileId)
	{
		public bool HasWindows
		{
			get
			{
				long? windowsFileId = WindowsFileId;
				if (windowsFileId.HasValue)
				{
					return windowsFileId.GetValueOrDefault() > 0;
				}
				return false;
			}
		}

		public bool HasAndroid
		{
			get
			{
				long? androidFileId = AndroidFileId;
				if (androidFileId.HasValue)
				{
					return androidFileId.GetValueOrDefault() > 0;
				}
				return false;
			}
		}

		public bool HasAny
		{
			get
			{
				if (!HasWindows)
				{
					return HasAndroid;
				}
				return true;
			}
		}

		public bool IsLimited
		{
			get
			{
				if (HasAny)
				{
					if (HasWindows)
					{
						return !HasAndroid;
					}
					return true;
				}
				return false;
			}
		}

		public string Badge
		{
			get
			{
				if (!HasWindows || !HasAndroid)
				{
					if (!HasWindows)
					{
						if (!HasAndroid)
						{
							return "LOCAL ONLY";
						}
						return "QUEST ONLY";
					}
					return "PC ONLY";
				}
				return "PC + QUEST";
			}
		}
	}
	public readonly record struct CompassPresentation(float HeadingDegrees, float SmoothedHeadingDegrees, float NorthOffsetDegrees, string Cardinal, string SceneKey, bool HasCalibration);
	public static class CompassHeadingMath
	{
		private static readonly string[] Cardinals = new string[8] { "N", "NE", "E", "SE", "S", "SW", "W", "NW" };

		public static float Normalize(float degrees)
		{
			if (!float.IsFinite(degrees))
			{
				return 0f;
			}
			float num = degrees % 360f;
			if (!(num < 0f))
			{
				return num;
			}
			return num + 360f;
		}

		public static float Heading(float worldYawDegrees, float northOffsetDegrees)
		{
			return Normalize(worldYawDegrees - northOffsetDegrees);
		}

		public static float SignedDelta(float fromDegrees, float toDegrees)
		{
			float num = Normalize(toDegrees) - Normalize(fromDegrees);
			if (num > 180f)
			{
				num -= 360f;
			}
			if (num < -180f)
			{
				num += 360f;
			}
			return num;
		}

		public static float Smooth(float current, float target, float deltaSeconds, float response = 12f)
		{
			if (!float.IsFinite(deltaSeconds) || deltaSeconds <= 0f)
			{
				return Normalize(current);
			}
			float num = 1f - MathF.Exp((0f - MathF.Max(0.01f, response)) * deltaSeconds);
			return Normalize(current + SignedDelta(current, target) * num);
		}

		public static string Cardinal(float headingDegrees)
		{
			int num = (int)MathF.Floor((Normalize(headingDegrees) + 22.5f) / 45f) % Cardinals.Length;
			return Cardinals[num];
		}

		public static float NeedleRotation(float headingDegrees)
		{
			return 0f - Normalize(headingDegrees);
		}
	}
	public static class CompassCalibrationStore
	{
		public const int MaximumRecords = 64;

		public static CompassCalibrationRecord? Find(IReadOnlyList<CompassCalibrationRecord> records, string sceneKey)
		{
			return records.FirstOrDefault((CompassCalibrationRecord record) => string.Equals(record.SceneKey, sceneKey, StringComparison.OrdinalIgnoreCase));
		}

		public static void Upsert(List<CompassCalibrationRecord> records, string sceneKey, float northYaw, DateTimeOffset usedAt)
		{
			string sceneKey2 = (string.IsNullOrWhiteSpace(sceneKey) ? "unknown-scene" : sceneKey.Trim());
			CompassCalibrationRecord compassCalibrationRecord = Find(records, sceneKey2);
			if (compassCalibrationRecord != null)
			{
				compassCalibrationRecord.NorthYawDegrees = CompassHeadingMath.Normalize(northYaw);
				compassCalibrationRecord.LastUsedUtc = usedAt;
			}
			else
			{
				records.Add(new CompassCalibrationRecord
				{
					SceneKey = sceneKey2,
					NorthYawDegrees = CompassHeadingMath.Normalize(northYaw),
					LastUsedUtc = usedAt
				});
			}
			while (records.Count > 64)
			{
				CompassCalibrationRecord item = records.OrderBy((CompassCalibrationRecord record) => record.LastUsedUtc).First();
				records.Remove(item);
			}
		}
	}
	public enum HologramCarouselRole
	{
		Previous,
		Selected,
		Next
	}
	public enum HologramAvatarMaterialMode
	{
		BlueHologram,
		FullColor
	}
	public readonly record struct HologramCarouselSlot(HologramCarouselRole Role, int Index, string Identity, float HorizontalOffset, float Scale, HologramAvatarMaterialMode MaterialMode, bool InteractionReady);
	public static class HologramCarouselPresentation
	{
		public const float SideOffsetMeters = 0.105f;

		public const float SideScale = 0.62f;

		public static IReadOnlyList<HologramCarouselSlot> Build(IReadOnlyList<string> identities, int selectedIndex)
		{
			if (identities.Count == 0)
			{
				return Array.Empty<HologramCarouselSlot>();
			}
			int num = Wrap(selectedIndex, identities.Count);
			if (identities.Count != 1)
			{
				int index = Wrap(num - 1, identities.Count);
				int index2 = Wrap(num + 1, identities.Count);
				return new HologramCarouselSlot[3]
				{
					Slot(HologramCarouselRole.Previous, index, identities[index], -0.105f, 0.62f, interactive: false),
					Slot(HologramCarouselRole.Selected, num, identities[num], 0f, 1f, interactive: true),
					Slot(HologramCarouselRole.Next, index2, identities[index2], 0.105f, 0.62f, interactive: false)
				};
			}
			return new HologramCarouselSlot[1] { Slot(HologramCarouselRole.Selected, num, identities[num], 0f, 1f, interactive: true) };
		}

		public static int Wrap(int index, int count)
		{
			if (count <= 0)
			{
				return 0;
			}
			int num = index % count;
			if (num >= 0)
			{
				return num;
			}
			return num + count;
		}

		private static HologramCarouselSlot Slot(HologramCarouselRole role, int index, string identity, float offset, float scale, bool interactive)
		{
			return new HologramCarouselSlot(role, index, identity, offset, scale, interactive ? HologramAvatarMaterialMode.FullColor : HologramAvatarMaterialMode.BlueHologram, interactive);
		}
	}
	public enum CinematicProjectionPhase
	{
		Hidden,
		Charging,
		RingExpansion,
		SideAvatarAssembly,
		CenterAvatarAssembly,
		Stable,
		CarouselTransition,
		PanelTransition,
		Collapsing,
		TrackingGrace
	}
	public readonly record struct CinematicProjectionProgress(CinematicProjectionPhase Phase, float Emitter, float Rings, float SideAvatars, float CenterAvatar, float Controls);
	public static class CinematicProjectionTimeline
	{
		public const float ChargeEnd = 0.15f;

		public const float RingEnd = 0.35f;

		public const float SideEnd = 0.45f;

		public const float CenterEnd = 0.55f;

		public const float CollapseSeconds = 0.35f;

		public static CinematicProjectionProgress Resolve(float elapsed, bool reducedMotion)
		{
			if (reducedMotion)
			{
				return new CinematicProjectionProgress(CinematicProjectionPhase.Stable, 1f, 1f, 1f, 1f, 1f);
			}
			float num = Math.Max(0f, elapsed);
			return new CinematicProjectionProgress((num < 0.15f) ? CinematicProjectionPhase.Charging : ((num < 0.35f) ? CinematicProjectionPhase.RingExpansion : ((num < 0.45f) ? CinematicProjectionPhase.SideAvatarAssembly : ((num < 0.55f) ? CinematicProjectionPhase.CenterAvatarAssembly : CinematicProjectionPhase.Stable))), Smooth(num / 0.15f), Smooth((num - 0.0825f) / 0.26749998f), Smooth((num - 0.22749999f) / 0.2225f), Smooth((num - 0.35f) / 0.20000002f), Smooth((num - 0.35099998f) / 0.19900003f));
		}

		private static float Smooth(float value)
		{
			float num = Math.Clamp(value, 0f, 1f);
			return num * num * (3f - 2f * num);
		}
	}
	public static class HoloInteractionMath
	{
		public static int WrapIndex(int current, int delta, int count)
		{
			if (count <= 0)
			{
				return 0;
			}
			return ((current + delta) % count + count) % count;
		}

		public static float SmoothStep01(float value)
		{
			float num = Math.Clamp(value, 0f, 1f);
			return num * num * (3f - 2f * num);
		}

		public static bool IsIntentionalFlick(float lateralVelocity, float threshold)
		{
			return Math.Abs(lateralVelocity) >= Math.Max(0f, threshold);
		}
	}
	public enum HoloPerformanceTier
	{
		Full,
		Balanced,
		Minimal
	}
	public sealed class HoloPerformanceGovernor
	{
		private readonly bool _constrained;

		private double _averageFrameMs;

		private int _slowSamples;

		private int _fastSamples;

		public HoloPerformanceTier Tier { get; private set; }

		public int RendererLimit => Tier switch
		{
			HoloPerformanceTier.Full => 64, 
			HoloPerformanceTier.Balanced => 24, 
			_ => 12, 
		};

		public bool EffectsAllowed => Tier != HoloPerformanceTier.Minimal;

		public double AverageFrameMs => _averageFrameMs;

		public HoloPerformanceGovernor(bool constrained)
		{
			_constrained = constrained;
			Tier = (constrained ? HoloPerformanceTier.Balanced : HoloPerformanceTier.Full);
		}

		public bool Sample(double frameMilliseconds)
		{
			if (!_constrained || double.IsNaN(frameMilliseconds) || double.IsInfinity(frameMilliseconds))
			{
				return false;
			}
			double num = Math.Clamp(frameMilliseconds, 1.0, 100.0);
			_averageFrameMs = ((_averageFrameMs <= 0.0) ? num : (_averageFrameMs * 0.94 + num * 0.06));
			if (_averageFrameMs >= 21.0)
			{
				_slowSamples++;
				_fastSamples = 0;
			}
			else if (_averageFrameMs <= 15.5)
			{
				_fastSamples++;
				_slowSamples = 0;
			}
			else
			{
				_slowSamples = Math.Max(0, _slowSamples - 1);
				_fastSamples = Math.Max(0, _fastSamples - 1);
			}
			if (Tier == HoloPerformanceTier.Balanced && _slowSamples >= 45)
			{
				Tier = HoloPerformanceTier.Minimal;
				_slowSamples = 0;
				return true;
			}
			if (Tier == HoloPerformanceTier.Minimal && _fastSamples >= 240)
			{
				Tier = HoloPerformanceTier.Balanced;
				_fastSamples = 0;
				return true;
			}
			return false;
		}
	}
	public enum InstalledPackFocusState
	{
		None,
		Downloading,
		WaitingForPallet,
		Ready,
		TimedOut
	}
	public sealed class InstalledPackFocus
	{
		public long ModId { get; private set; }

		public int Generation { get; private set; }

		public double DeadlineSeconds { get; private set; }

		public InstalledPackFocusState State { get; private set; }

		public string SelectedBarcode { get; private set; } = string.Empty;

		public IReadOnlyList<string> InstallDirectories { get; private set; } = Array.Empty<string>();

		public bool Active => State != InstalledPackFocusState.None;

		public int Begin(long modId, double nowSeconds, IEnumerable<string>? installDirectories = null, double timeoutSeconds = 12.0)
		{
			Generation++;
			ModId = modId;
			DeadlineSeconds = nowSeconds + Math.Max(1.0, timeoutSeconds);
			State = InstalledPackFocusState.WaitingForPallet;
			SelectedBarcode = string.Empty;
			InstallDirectories = (installDirectories ?? Array.Empty<string>()).Where((string path) => !string.IsNullOrWhiteSpace(path)).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();
			return Generation;
		}

		public int BeginDownload(long modId)
		{
			Generation++;
			ModId = modId;
			DeadlineSeconds = double.PositiveInfinity;
			State = InstalledPackFocusState.Downloading;
			SelectedBarcode = string.Empty;
			InstallDirectories = Array.Empty<string>();
			return Generation;
		}

		public void MarkDownloaded(long modId, double nowSeconds, IEnumerable<string>? installDirectories, double timeoutSeconds = 12.0)
		{
			if (modId == ModId && State != InstalledPackFocusState.None)
			{
				UpdateDirectories(modId, installDirectories);
				DeadlineSeconds = nowSeconds + Math.Max(1.0, timeoutSeconds);
				State = InstalledPackFocusState.WaitingForPallet;
			}
		}

		public void UpdateDirectories(long modId, IEnumerable<string>? installDirectories)
		{
			if (modId == ModId && State != InstalledPackFocusState.None)
			{
				InstallDirectories = (installDirectories ?? Array.Empty<string>()).Where((string path) => !string.IsNullOrWhiteSpace(path)).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();
			}
		}

		public bool TryBind(long modId, IEnumerable<string?> barcodes)
		{
			if (modId != ModId || State == InstalledPackFocusState.None)
			{
				return false;
			}
			string text = barcodes.FirstOrDefault((string value) => !string.IsNullOrWhiteSpace(value))?.Trim();
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			SelectedBarcode = text;
			State = InstalledPackFocusState.Ready;
			return true;
		}

		public bool UpdateTimeout(double nowSeconds)
		{
			if (State != InstalledPackFocusState.WaitingForPallet || nowSeconds < DeadlineSeconds)
			{
				return false;
			}
			State = InstalledPackFocusState.TimedOut;
			return true;
		}

		public void Retry(double nowSeconds, double timeoutSeconds = 12.0)
		{
			if (Active)
			{
				DeadlineSeconds = nowSeconds + Math.Max(1.0, timeoutSeconds);
				State = InstalledPackFocusState.WaitingForPallet;
			}
		}

		public void Clear()
		{
			ModId = 0L;
			DeadlineSeconds = 0.0;
			State = InstalledPackFocusState.None;
			SelectedBarcode = string.Empty;
			InstallDirectories = Array.Empty<string>();
		}
	}
	public sealed class LibraryRefreshGate
	{
		public bool Pending { get; private set; }

		public double NotBeforeSeconds { get; private set; }

		public LibraryRefreshGate(bool initiallyPending = true)
		{
			Pending = initiallyPending;
		}

		public void Request(double nowSeconds, double debounceSeconds = 0.35)
		{
			double num = nowSeconds + Math.Max(0.0, debounceSeconds);
			NotBeforeSeconds = (Pending ? Math.Max(NotBeforeSeconds, num) : num);
			Pending = true;
		}

		public bool TryConsume(double nowSeconds)
		{
			if (!Pending || nowSeconds < NotBeforeSeconds)
			{
				return false;
			}
			Pending = false;
			NotBeforeSeconds = 0.0;
			return true;
		}
	}
	public readonly record struct MiniMapOverlayColor(byte R, byte G, byte B, byte A = byte.MaxValue)
	{
		public static readonly MiniMapOverlayColor Outline = new MiniMapOverlayColor(0, 15, 24, 235);
	}
	public enum MiniMapPeerRole
	{
		Player,
		Operator,
		Owner
	}
	public static class MiniMapPeerColors
	{
		public static readonly MiniMapOverlayColor Cyan = new MiniMapOverlayColor(44, 220, byte.MaxValue);

		public static readonly MiniMapOverlayColor OperatorViolet = new MiniMapOverlayColor(190, 112, byte.MaxValue);

		public static readonly MiniMapOverlayColor OwnerAmber = new MiniMapOverlayColor(byte.MaxValue, 184, 46);

		public static readonly MiniMapOverlayColor Above = new MiniMapOverlayColor(117, byte.MaxValue, 184);

		public static readonly MiniMapOverlayColor Below = new MiniMapOverlayColor(92, 158, byte.MaxValue, 235);

		public static MiniMapOverlayColor Resolve(MiniMapPeerRole role, MiniMapAltitudeBand altitude)
		{
			return role switch
			{
				MiniMapPeerRole.Owner => OwnerAmber, 
				MiniMapPeerRole.Operator => OperatorViolet, 
				_ => altitude switch
				{
					MiniMapAltitudeBand.Above => Above, 
					MiniMapAltitudeBand.Below => Below, 
					_ => Cyan, 
				}, 
			};
		}
	}
	public readonly record struct MiniMapOverlayMarker(Vector2 NormalizedPosition, float RotationDegrees, MiniMapOverlayColor Color, float Scale = 1f);
	public static class MiniMapPeerOverlayRaster
	{
		public const int DefaultTextureSize = 192;

		private const int ReferenceTextureSize = 96;

		private const int AntiAliasSamples = 3;

		public static int Render(Span<byte> rgba, int width, int height, ReadOnlySpan<MiniMapOverlayMarker> markers)
		{
			if (width <= 0 || height <= 0 || rgba.Length < width * height * 4)
			{
				throw new ArgumentOutOfRangeException("rgba");
			}
			rgba.Slice(0, width * height * 4).Clear();
			int written = 0;
			float num = MathF.Min(width, height) * 0.4f;
			float num2 = MathF.Min(width, height) / 96f;
			for (int i = 0; i < markers.Length; i++)
			{
				MiniMapOverlayMarker miniMapOverlayMarker = markers[i];
				Vector2 normalizedPosition = miniMapOverlayMarker.NormalizedPosition;
				float num3 = normalizedPosition.Length();
				if (float.IsFinite(num3))
				{
					if (num3 > 1f && num3 > float.Epsilon)
					{
						normalizedPosition /= num3;
					}
					float centerX = (float)(width - 1) * 0.5f + normalizedPosition.X * num;
					float centerY = (float)(height - 1) * 0.5f + normalizedPosition.Y * num;
					float scale = Math.Clamp(float.IsFinite(miniMapOverlayMarker.Scale) ? miniMapOverlayMarker.Scale : 1f, 0.45f, 1.65f) * num2;
					DrawPlane(rgba, width, height, centerX, centerY, miniMapOverlayMarker.RotationDegrees, scale, miniMapOverlayMarker.Color, ref written);
				}
			}
			return written;
		}

		private static void DrawPlane(Span<byte> pixels, int width, int height, float centerX, float centerY, float rotationDegrees, float scale, MiniMapOverlayColor color, ref int written)
		{
			float x = rotationDegrees * (float)Math.PI / 180f;
			float cos = MathF.Cos(x);
			float sin = MathF.Sin(x);
			Vector2 a = Rotate(new Vector2(0f, 6.4f) * scale, cos, sin) + new Vector2(centerX, centerY);
			Vector2 b = Rotate(new Vector2(-5f, -4.8f) * scale, cos, sin) + new Vector2(centerX, centerY);
			Vector2 vector = Rotate(new Vector2(0f, -1.4f) * scale, cos, sin) + new Vector2(centerX, centerY);
			Vector2 c = Rotate(new Vector2(5f, -4.8f) * scale, cos, sin) + new Vector2(centerX, centerY);
			DrawTriangle(pixels, width, height, a, b, vector, color, ref written);
			DrawTriangle(pixels, width, height, a, vector, c, color, ref written);
		}

		private static Vector2 Rotate(Vector2 value, float cos, float sin)
		{
			return new Vector2(value.X * cos - value.Y * sin, value.X * sin + value.Y * cos);
		}

		private static void DrawTriangle(Span<byte> pixels, int width, int height, Vector2 a, Vector2 b, Vector2 c, MiniMapOverlayColor color, ref int written)
		{
			int num = Math.Clamp((int)MathF.Floor(MathF.Min(a.X, MathF.Min(b.X, c.X))), 0, width - 1);
			int num2 = Math.Clamp((int)MathF.Ceiling(MathF.Max(a.X, MathF.Max(b.X, c.X))), 0, width - 1);
			int num3 = Math.Clamp((int)MathF.Floor(MathF.Min(a.Y, MathF.Min(b.Y, c.Y))), 0, height - 1);
			int num4 = Math.Clamp((int)MathF.Ceiling(MathF.Max(a.Y, MathF.Max(b.Y, c.Y))), 0, height - 1);
			for (int i = num3; i <= num4; i++)
			{
				for (int j = num; j <= num2; j++)
				{
					int num5 = 0;
					for (int k = 0; k < 3; k++)
					{
						for (int l = 0; l < 3; l++)
						{
							if (InsideTriangle(new Vector2((float)j + ((float)l + 0.5f) / 3f, (float)i + ((float)k + 0.5f) / 3f), a, b, c))
							{
								num5++;
							}
						}
					}
					if (num5 != 0)
					{
						int offset = (i * width + j) * 4;
						int sourceAlpha = (color.A * num5 + 4) / 9;
						BlendPixel(pixels, offset, color, sourceAlpha, ref written);
					}
				}
			}
		}

		private static bool InsideTriangle(Vector2 point, Vector2 a, Vector2 b, Vector2 c)
		{
			float num = Cross(b - a, point - a);
			float num2 = Cross(c - b, point - b);
			float num3 = Cross(a - c, point - c);
			if (!(num >= 0f) || !(num2 >= 0f) || !(num3 >= 0f))
			{
				if (num <= 0f && num2 <= 0f)
				{
					return num3 <= 0f;
				}
				return false;
			}
			return true;
		}

		private static void BlendPixel(Span<byte> pixels, int offset, MiniMapOverlayColor color, int sourceAlpha, ref int written)
		{
			if (sourceAlpha > 0)
			{
				byte b = pixels[offset + 3];
				if (b == 0)
				{
					written++;
				}
				int num = 255 - sourceAlpha;
				int num2 = sourceAlpha + (b * num + 127) / 255;
				if (num2 > 0)
				{
					pixels[offset] = BlendChannel(color.R, pixels[offset], sourceAlpha, b, num, num2);
					pixels[offset + 1] = BlendChannel(color.G, pixels[offset + 1], sourceAlpha, b, num, num2);
					pixels[offset + 2] = BlendChannel(color.B, pixels[offset + 2], sourceAlpha, b, num, num2);
					pixels[offset + 3] = (byte)num2;
				}
			}
		}

		private static byte BlendChannel(byte source, byte destination, int sourceAlpha, int destinationAlpha, int inverseAlpha, int outputAlpha)
		{
			return (byte)Math.Clamp((source * sourceAlpha + (destination * destinationAlpha * inverseAlpha + 127) / 255 + outputAlpha / 2) / outputAlpha, 0, 255);
		}

		private static float Cross(Vector2 left, Vector2 right)
		{
			return left.X * right.Y - left.Y * right.X;
		}
	}
	public enum MiniMapRange
	{
		TenMeters = 10,
		TwentyMeters = 20,
		TwentyFiveMeters = 25,
		ThirtyMeters = 30,
		FortyMeters = 40,
		FiftyMeters = 50
	}
	public enum MiniMapOrientation
	{
		HeadingUp,
		NorthUp
	}
	public enum MiniMapMarkerKind
	{
		LocalPlayer,
		FusionPeer,
		Waypoint
	}
	public enum MiniMap

Mods\WristHub.dll

Decompiled 3 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using BoneHub.Api;
using BoneHub.Avatars;
using BoneHub.Compatibility;
using BoneHub.Configuration;
using BoneHub.Downloads;
using BoneHub.Installation;
using BoneHub.Interaction;
using BoneHub.Mod;
using BoneHub.Models;
using BoneHub.Persistence;
using BoneLib;
using BoneLib.BoneMenu;
using Il2CppCysharp.Threading.Tasks;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Bonelab;
using Il2CppSLZ.Bonelab.SaveData;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.Data;
using Il2CppSLZ.Marrow.Forklift.Model;
using Il2CppSLZ.Marrow.Pool;
using Il2CppSLZ.Marrow.SaveData;
using Il2CppSLZ.Marrow.SceneStreaming;
using Il2CppSLZ.Marrow.Utilities;
using Il2CppSLZ.Marrow.Warehouse;
using Il2CppSLZ.VRMK;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppTMPro;
using LabFusion.Data;
using LabFusion.Entities;
using LabFusion.Marrow;
using LabFusion.Marrow.Patching;
using LabFusion.Menu;
using LabFusion.Network;
using LabFusion.Player;
using LabFusion.RPC;
using LabFusion.Representation;
using LabFusion.SDK.Lobbies;
using LabFusion.SDK.Modules;
using LabFusion.Scene;
using MelonLoader;
using MelonLoader.Utils;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using WristHub.SDK;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(BoneHubMod), "WristHubV4", "4.0.1", "WristHub contributors", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: AssemblyMetadata("WristHubPlatform", "QuestStandalone")]
[assembly: TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]
[assembly: AssemblyCompany("WristHub")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("4.0.1.0")]
[assembly: AssemblyInformationalVersion("4.0.1")]
[assembly: AssemblyProduct("WristHub")]
[assembly: AssemblyTitle("WristHub")]
[assembly: AssemblyVersion("4.0.1.0")]
[module: RefSafetyRules(11)]
namespace BoneHub.Mod;

internal sealed class BoneHubAvatarBrowser : IDisposable
{
	private enum ButtonIconKind
	{
		None,
		Back,
		Forward,
		Close,
		Search,
		Settings,
		Download,
		Avatar,
		Apps,
		Map,
		Compass,
		Portal,
		Fusion,
		Players,
		Gamemode,
		QuickJoin,
		Home,
		Refresh,
		Equip,
		Delete,
		Join
	}

	private sealed class TouchButton
	{
		public Transform Space { get; init; }

		public GameObject Root { get; init; }

		public Image Panel { get; init; }

		public TMP_Text Label { get; init; }

		public Vector3 Center { get; init; }

		public Vector3 Half { get; init; }

		public Action Action { get; init; }

		public TouchDebounce Debounce { get; } = new TouchDebounce();

		public bool IsAlive
		{
			get
			{
				try
				{
					return (Object)(object)Root != (Object)null && (Object)(object)Space != (Object)null && (Object)(object)Panel != (Object)null && (Object)(object)Label != (Object)null;
				}
				catch
				{
					return false;
				}
			}
		}

		public bool Contains(Vector3 worldPoint)
		{
			//IL_0033: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_0069: 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_0081: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)Root == (Object)null || (Object)(object)Space == (Object)null || !Root.activeInHierarchy)
				{
					return false;
				}
				Vector3 val = Space.InverseTransformPoint(worldPoint) - Center;
				return Mathf.Abs(val.x) <= Half.x && Mathf.Abs(val.y) <= Half.y && Mathf.Abs(val.z) <= Half.z;
			}
			catch
			{
				return false;
			}
		}
	}

	private sealed class PackRow
	{
		public TouchButton Button { get; init; }

		public TouchButton DeleteButton { get; init; }

		public RawImage Artwork { get; init; }

		public TMP_Text Name { get; init; }

		public TMP_Text Creator { get; init; }

		public TMP_Text State { get; init; }

		public string Identity { get; set; } = string.Empty;

		public int Generation { get; set; }

		public bool IsAlive
		{
			get
			{
				if (Button.IsAlive && DeleteButton.IsAlive && (Object)(object)Artwork != (Object)null && (Object)(object)Name != (Object)null && (Object)(object)Creator != (Object)null)
				{
					return (Object)(object)State != (Object)null;
				}
				return false;
			}
		}
	}

	private enum IconPrimitiveType
	{
		Line,
		Ring,
		Disc,
		Box
	}

	private readonly record struct IconPrimitive(IconPrimitiveType Type, Vector2 A, Vector2 B, float Radius, float Stroke);

	private enum PortalPlacementStage
	{
		None,
		LocalEntrance,
		LocalExit,
		WorldEntrance,
		FusionServerEntrance
	}

	private enum WristKeyboardPurpose
	{
		Avatars,
		CodeMods,
		Spawnables,
		PortalLevels,
		FusionServerCode,
		CodeModString,
		SdkText
	}

	private enum SpawnableBrowseMode
	{
		All,
		Groups,
		Recent,
		Favorites
	}

	private enum CodeModCatalogMode
	{
		All,
		Community,
		Managers,
		BoneMenu,
		Loaded
	}

	private sealed class OsControlRow
	{
		public TouchButton Main { get; init; }

		public TouchButton Minus { get; init; }

		public TouchButton Plus { get; init; }

		public TMP_Text Detail { get; init; }

		public object? Source { get; set; }
	}

	private sealed class MiniMapPeerVisual
	{
		public int PeerId { get; set; } = -1;

		public bool Active { get; set; }

		public Vector3 TargetWorldPosition { get; set; }

		public Vector3 WorldPosition { get; set; }

		public float TargetHeadingDegrees { get; set; }

		public float HeadingDegrees { get; set; }

		public MiniMapPeerRole Role { get; set; }

		public Transform? TrackingTransform { get; set; }

		public bool HasDisplayPose { get; set; }

		public float LastSeenAt { get; set; }
	}

	private sealed record CodeModSnapshot(string Name, string Detail, object? Page, object? Element = null);

	private sealed record LoadedCodeModSnapshot(string Name, string Author, string Version);

	private sealed class ReflectedManagerControl
	{
		private readonly Func<string> _detail;

		private readonly Action _activate;

		private readonly Action<int>? _adjust;

		public string Label { get; }

		public string Detail => _detail();

		public bool Adjustable => _adjust != null;

		public ReflectedManagerControl(string label, Func<string> detail, Action activate, Action<int>? adjust = null)
		{
			Label = label;
			_detail = detail;
			_activate = activate;
			_adjust = adjust;
		}

		public void Activate()
		{
			_activate();
		}

		public void Adjust(int direction)
		{
			_adjust?.Invoke(direction);
		}
	}

	private sealed class RuntimeSdkHost : IWristHubHost
	{
		private readonly BoneHubAvatarBrowser _owner;

		public WristHubRuntimeInfo Runtime => new WristHubRuntimeInfo(true, _owner._constrainedRendering, IsFusionConnected(), ((object)_owner._preferences.HologramTheme/*cast due to .constrained prefix*/).ToString(), 2);

		public RuntimeSdkHost(BoneHubAvatarBrowser owner)
		{
			_owner = owner;
		}

		public void Notify(string appId, string message, WristHubNotificationKind kind)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			_owner._sdkNotifications.Enqueue(new SdkNotification(appId, message, kind));
		}

		public void RequestOpen(string appId)
		{
			_owner._sdkOpenRequests.Enqueue(appId);
		}
	}

	private readonly record struct SdkNotification(string AppId, string Message, WristHubNotificationKind Kind)
	{
		[CompilerGenerated]
		public void Deconstruct(out string AppId, out string Message, out WristHubNotificationKind Kind)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected I4, but got Unknown
			AppId = this.AppId;
			Message = this.Message;
			Kind = (WristHubNotificationKind)(int)this.Kind;
		}
	}

	private sealed class RuntimeWristOsApp : IWristOsApp
	{
		private readonly Action _open;

		public WristOsAppId Id { get; }

		public string DisplayName { get; }

		public bool IsAvailable => true;

		public int NotificationCount => 0;

		public RuntimeWristOsApp(WristOsAppId id, string name, Action open)
		{
			//IL_0007: 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)
			Id = id;
			DisplayName = name;
			_open = open;
		}

		public void Open()
		{
			_open();
		}

		public void Close()
		{
		}

		public void Tick(double nowSeconds)
		{
		}

		public bool HandleBack()
		{
			return false;
		}
	}

	private const float ChestRadius = 0.22f;

	private static readonly Color Glass = new Color(0.012f, 0.027f, 0.04f, 1f);

	private static readonly Color Surface = new Color(0.025f, 0.075f, 0.105f, 0.96f);

	private static readonly Color Green = new Color(0.32f, 0.9f, 0.62f, 1f);

	private static readonly Color Amber = new Color(1f, 0.67f, 0.22f, 1f);

	private static readonly Color Red = new Color(1f, 0.38f, 0.46f, 1f);

	private readonly BoneHubService _service;

	private readonly BoneHubRuntime _runtime;

	private readonly BoneHubThumbnailCache _thumbnails;

	private readonly ConcurrentQueue<Action> _mainThread;

	private readonly BoneHubPreferences _preferences;

	private readonly bool _constrainedRendering;

	private readonly Instance _log;

	private readonly LobbyAvatarSharingBridge _sharing;

	private readonly List<TouchButton> _buttons = new List<TouchButton>();

	private readonly Dictionary<int, TouchButton> _touchCaptures = new Dictionary<int, TouchButton>();

	private readonly HashSet<int> _activeProbeIds = new HashSet<int>();

	private readonly List<int> _staleCaptureIds = new List<int>(2);

	private readonly Dictionary<long, ModIoMod> _onlineMods = new Dictionary<long, ModIoMod>();

	private readonly Dictionary<long, DownloadJob> _jobs = new Dictionary<long, DownloadJob>();

	private readonly Dictionary<long, IReadOnlyList<Guid>> _downloadBatches = new Dictionary<long, IReadOnlyList<Guid>>();

	private readonly Dictionary<string, AvatarPreviewMetrics> _previewMetrics = new Dictionary<string, AvatarPreviewMetrics>(StringComparer.OrdinalIgnoreCase);

	private readonly Dictionary<string, AvatarPreviewRouteMetadata> _previewRoutes = new Dictionary<string, AvatarPreviewRouteMetadata>(StringComparer.OrdinalIgnoreCase);

	private readonly Dictionary<string, AvatarRigValidationResult> _rigValidations = new Dictionary<string, AvatarRigValidationResult>(StringComparer.OrdinalIgnoreCase);

	private readonly HashSet<long> _startingDownloads = new HashSet<long>();

	private readonly BoneHubFingertipProbeProvider _fingertips = BoneHubFingertipProbeProvider.Shared;

	private readonly TabletKeyboardBuffer _keyboard = new TabletKeyboardBuffer();

	private readonly LookAwayDismissal _promptDismissal = new LookAwayDismissal();

	private readonly FocusedResultCursor _cursor = new FocusedResultCursor();

	private readonly FourRowPageCursor _packCursor = new FourRowPageCursor();

	private readonly PresentationGeneration _artworkGeneration = new PresentationGeneration();

	private readonly ErrorLogThrottle _runtimeErrorThrottle = new ErrorLogThrottle();

	private readonly SurfaceInputRearmGate _inputRearm = new SurfaceInputRearmGate(0.1);

	private readonly InstalledPackFocus _installedFocus = new InstalledPackFocus();

	private readonly BoneHubForearmProjectionAnchorProvider _projectionAnchors = new BoneHubForearmProjectionAnchorProvider();

	private readonly List<PackRow> _packRows = new List<PackRow>();

	private readonly Action _projectionTickAction;

	private readonly Action _projectionRecoveryAction;

	private readonly Action _touchTickAction;

	private readonly Action _touchRecoveryAction;

	private readonly Action _installationFocusTickAction;

	private readonly Action _previewTickAction;

	private readonly Action _previewRecoveryAction;

	private readonly Action _downloadTickAction;

	private readonly Action _sharingTickAction;

	private readonly Action _compassTickAction;

	private readonly Action<PortalLinkState> _portalStateReceiver;

	private readonly Action<int> _portalRemoveReceiver;

	private IReadOnlyList<AvatarEntry> _entries = Array.Empty<AvatarEntry>();

	private IReadOnlyList<AvatarEntry> _mergedEntries = Array.Empty<AvatarEntry>();

	private IReadOnlyList<AvatarEntry> _onlineEntries = Array.Empty<AvatarEntry>();

	private IReadOnlyList<AvatarPackEntry> _packs = Array.Empty<AvatarPackEntry>();

	private GameObject? _root;

	private GameObject? _launcher;

	private GameObject? _hub;

	private GameObject? _browser;

	private GameObject? _canvasRoot;

	private GameObject? _packPage;

	private GameObject? _detailPage;

	private GameObject? _optionsPage;

	private GameObject? _watchPlacementPage;

	private GameObject? _downloadsPage;

	private GameObject? _packOptionsPage;

	private GameObject? _keyboardPage;

	private GameObject? _deletePage;

	private GameObject? _compassPage;

	private GameObject? _denseGlassChrome;

	private GameObject? _denseGlassBackground;

	private GameObject? _denseGlassOverlay;

	private bool _wristLocalPoseValid;

	private int _wristLocalHandId;

	private Vector3 _wristLocalPosition;

	private float _wristLocalHeightRatio;

	private float _wristLocalWatchScale;

	private float _wristLocalOutwardOffset;

	private float _wristLocalFingerOffset;

	private GameObject? _leftArcWing;

	private GameObject? _rightArcWing;

	private GameObject? _metadataArc;

	private CanvasGroup? _canvasGroup;

	private TMP_Text? _packTitle;

	private TMP_Text? _packStatus;

	private TMP_Text? _packPosition;

	private TMP_Text? _packSearchText;

	private TMP_Text? _sortText;

	private TMP_Text? _wristText;

	private TMP_Text? _scaleText;

	private TMP_Text? _motionText;

	private TMP_Text? _themeText;

	private TMP_Text? _clockText;

	private TMP_Text? _startingViewText;

	private TMP_Text? _watchForearmText;

	private TMP_Text? _scopeText;

	private TMP_Text? _downloadsSummary;

	private TMP_Text? _deleteTitle;

	private TMP_Text? _deleteDetails;

	private TMP_Text? _keyboardText;

	private TMP_Text? _keyboardTitle;

	private TMP_Text? _keyboardShiftText;

	private TMP_Text? _searchText;

	private TMP_Text? _compassHeadingText;

	private TMP_Text? _compassCalibrationText;

	private RectTransform? _compassNeedle;

	private RectTransform? _compassDecorRing;

	private TMP_Text? _positionText;

	private TMP_Text? _selectedName;

	private TMP_Text? _creatorText;

	private TMP_Text? _modText;

	private TMP_Text? _statusText;

	private TMP_Text? _heightText;

	private GameObject? _heightRuler;

	private GameObject? _progressRoot;

	private RectTransform? _progressFill;

	private TMP_Text? _progressText;

	private TMP_Text? _actionLabel;

	private Image? _actionPanel;

	private TouchButton? _trashButton;

	private RawImage? _centralArtwork;

	private Texture2D? _placeholderTexture;

	private Texture2D? _roundedTexture;

	private Sprite? _roundedSprite;

	private BoneHubForearmProjection? _hologramProjection;

	private BoneHubHologramCarousel? _hologramCarousel;

	private BoneHubChestEquipGuide? _chestEquipGuide;

	private TouchButton? _actionButton;

	private GameObject? _previewShell;

	private GameObject? _previewFallback;

	private GameObject? _previewModel;

	private readonly List<Mesh> _ownedPreviewMeshes = new List<Mesh>();

	private BoneHubAvatar? _previewAvatar;

	private BoneHubAvatar? _pendingPreviewAvatar;

	private string _loadedPreviewBarcode = string.Empty;

	private string _requestedPreviewBarcode = string.Empty;

	private float _previewLoadAt;

	private bool _previewReady;

	private bool _previewInteractionReady;

	private AvatarPreviewVisualSource _previewVisualSource;

	private bool _previewHeld;

	private Hand? _dragHand;

	private bool _dragIsLeft;

	private bool _dragUsesPinch;

	private Vector3 _dragLocalOffset;

	private Quaternion _dragRotationOffset = Quaternion.identity;

	private float _dragTrackingLostAt = -1f;

	private bool _dragWasInsideChest;

	private bool _previewReturning;

	private float _returnStarted;

	private Vector3 _returnFrom;

	private Quaternion _returnRotation;

	private int _previewGeneration;

	private int _searchGeneration;

	private string _query = string.Empty;

	private bool _downloadsOnly;

	private string _selectedIdentity = string.Empty;

	private string _trashConfirmIdentity = string.Empty;

	private bool _deleteReturnToPackPage;

	private bool _trashInProgress;

	private long? _selectedPackId;

	private AvatarPackSort _packSort;

	private AvatarBrowseScope _scope;

	private AvatarBrowseScope _scopeBeforeSearch;

	private Guid _progressJobId;

	private double _displayedProgress;

	private double _targetProgress;

	private int _lastProgressPercent = -1;

	private AvatarDownloadState? _lastProgressState;

	private AvatarBrowserViewState _viewState = (AvatarBrowserViewState)2;

	private AvatarBrowserViewState _viewStateBeforeKeyboard = (AvatarBrowserViewState)2;

	private bool _disposed;

	private bool _wasPlayerDead;

	private AvatarSharingState _sharingState;

	private BoneHubAvatar? _pendingSharingAvatar;

	private ModIoSharingMetadata? _pendingSharingMetadata;

	private string _sharingRetryBarcode = string.Empty;

	private float _nextSharingCheckAt;

	private float _sharingDeadline;

	private bool _sharingRefreshRequested;

	private string _carouselIdentity = string.Empty;

	private float _hubControlsReadyAt;

	private HologramDashboardLifecycle _lifecycle;

	private float _lifecycleStartedAt;

	private bool _closeNotificationPending;

	private HologramDashboardProgress _closeVisualStart = new HologramDashboardProgress((HologramProjectionPhase)5, 1f, 1f, 1f, 1f, 1f, true);

	private string _compassSceneKey = "unknown-scene";

	private float _compassSmoothedHeading;

	private bool _compassHeadingValid;

	private int _lastCompassDegree = -1;

	private GameObject? _fusionHomePage;

	private GameObject? _fusionPlayersPage;

	private TMP_Text? _fusionHomeTitle;

	private TMP_Text? _fusionHomeSummary;

	private TMP_Text? _fusionHomeStatus;

	private TouchButton? _fusionDisconnectButton;

	private readonly List<TouchButton> _fusionPlayerRows = new List<TouchButton>(4);

	private readonly List<RawImage> _fusionPlayerImages = new List<RawImage>(4);

	private readonly int[] _fusionPlayerImageGenerations = new int[4];

	private readonly long[] _fusionPlayerImageModIds = new long[4];

	private FusionServerEntry? _fusionPlayersSource;

	private int _fusionPlayerPageIndex;

	private TMP_Text? _fusionPlayerPosition;

	private TMP_Text? _fusionPlayerStatus;

	private readonly Dictionary<ButtonIconKind, Sprite> _buttonIconSprites = new Dictionary<ButtonIconKind, Sprite>();

	private readonly List<Texture2D> _buttonIconTextures = new List<Texture2D>();

	private Sprite? _miniMapRingSprite;

	private Sprite? _miniMapMaskSprite;

	private readonly RaycastHit[] _portalRayHits = (RaycastHit[])(object)new RaycastHit[12];

	private readonly PortalTraversalGuard _portalEntranceGuard = new PortalTraversalGuard();

	private readonly PortalTraversalGuard _portalExitGuard = new PortalTraversalGuard();

	private readonly List<PortalDestinationEntry> _portalLevels = new List<PortalDestinationEntry>();

	private readonly List<PortalDestinationEntry> _installedPortalLevels = new List<PortalDestinationEntry>();

	private readonly List<FusionServerEntry> _fusionServers = new List<FusionServerEntry>();

	private readonly List<TouchButton> _fusionServerRows = new List<TouchButton>();

	private readonly List<RawImage> _fusionServerRowArtwork = new List<RawImage>();

	private readonly string[] _fusionServerRowArtworkCodes = new string[4];

	private readonly int[] _fusionServerRowArtworkGenerations = new int[4];

	private readonly Dictionary<string, LevelCrate> _portalLevelCrates = new Dictionary<string, LevelCrate>(StringComparer.OrdinalIgnoreCase);

	private readonly Dictionary<long, ModIoMod> _portalOnlineMods = new Dictionary<long, ModIoMod>();

	private GameObject? _portalsPage;

	private GameObject? _portalLevelsPage;

	private GameObject? _fusionServersPage;

	private GameObject? _fusionServerDetailsPage;

	private TMP_Text? _portalStatus;

	private TMP_Text? _portalModeText;

	private TouchButton? _portalLocalButton;

	private TouchButton? _portalWorldButton;

	private TouchButton? _portalRemoveButton;

	private TouchButton? _portalLevelsButton;

	private TouchButton? _portalServersButton;

	private TouchButton? _portalLevelActionButton;

	private TMP_Text? _portalLevelName;

	private TMP_Text? _portalLevelDetails;

	private TMP_Text? _portalLevelPosition;

	private TMP_Text? _portalLevelStatus;

	private TouchButton? _fusionServerActionButton;

	private TouchButton? _fusionServerJoinButton;

	private RawImage? _fusionServerArtwork;

	private TMP_Text? _fusionServerListPosition;

	private TMP_Text? _fusionServerListStatus;

	private TMP_Text? _fusionServerName;

	private TMP_Text? _fusionServerDetails;

	private TMP_Text? _fusionServerPosition;

	private TMP_Text? _fusionServerStatus;

	private TMP_Text? _fusionServerDescription;

	private TMP_Text? _fusionServerMembers;

	private PortalPlacementStage _portalPlacementStage;

	private PortalEndpoint? _pendingPortalEntrance;

	private PortalLinkState? _activePortal;

	private bool _activePortalLocallyCreated;

	private int _portalGeneration;

	private bool _portalTriggerHeld;

	private float _portalPointerReadyAt;

	private LineRenderer? _portalPointerLine;

	private GameObject? _portalPointerReticle;

	private Renderer? _portalPointerRenderer;

	private Material? _portalMaterial;

	private MaterialPropertyBlock? _portalPropertyBlock;

	private BoneHubPortalVisual? _portalVisual;

	private Vector3 _previousPortalHead;

	private bool _hasPreviousPortalHead;

	private float _nextPortalLevelScan;

	private string _selectedPortalLevelBarcode = string.Empty;

	private int _portalLevelIndex;

	private string _portalLevelQuery = string.Empty;

	private int _portalLevelSearchGeneration;

	private int _fusionServerRequestGeneration;

	private int _fusionServerIndex;

	private int _fusionServerPageIndex;

	private string _selectedFusionServerCode = string.Empty;

	private string _fusionServerCodeInput = string.Empty;

	private int _fusionServerArtworkGeneration;

	private string _fusionServerArtworkCode = string.Empty;

	private long _pendingPortalLevelModId;

	private float _pendingPortalLevelDeadline;

	private float _nextPortalInstallScan;

	private readonly HashSet<string> _portalLevelBarcodesBeforeDownload = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	private static TMP_FontAsset? _osWordmarkFont;

	private static bool _osWordmarkFontResolved;

	private readonly List<OsControlRow> _codeModRows = new List<OsControlRow>(4);

	private readonly List<CodeModSnapshot> _codeModItems = new List<CodeModSnapshot>();

	private readonly List<LoadedCodeModSnapshot> _loadedCodeMods = new List<LoadedCodeModSnapshot>();

	private readonly ConcurrentQueue<string> _sdkOpenRequests = new ConcurrentQueue<string>();

	private readonly ConcurrentQueue<SdkNotification> _sdkNotifications = new ConcurrentQueue<SdkNotification>();

	private const int MiniMapTextureSize = 32;

	private readonly Color[] _miniMapPixels = (Color[])(object)new Color[1024];

	private readonly float[] _miniMapHeights = new float[1024];

	private readonly byte[] _miniMapCellState = new byte[1024];

	private readonly List<MiniMapPeerVisual> _miniMapPeerVisuals = new List<MiniMapPeerVisual>(16);

	private readonly List<GameObject> _miniMapPortalDots = new List<GameObject>(2);

	private readonly List<SpawnableEntry> _spawnableEntries = new List<SpawnableEntry>();

	private readonly List<SpawnableEntry> _spawnableCatalog = new List<SpawnableEntry>();

	private readonly Dictionary<string, SpawnableCrate> _spawnableCrates = new Dictionary<string, SpawnableCrate>(StringComparer.OrdinalIgnoreCase);

	private readonly RaycastHit[] _miniMapHits = (RaycastHit[])(object)new RaycastHit[12];

	private readonly RaycastHit[] _spawnPointerHits = (RaycastHit[])(object)new RaycastHit[16];

	private readonly List<GameObject> _spawnedObjects = new List<GameObject>(32);

	private readonly WristOsRouter _osRouter = new WristOsRouter();

	private readonly FusionMiniMapBridge _miniMapFusion = new FusionMiniMapBridge();

	private readonly List<FusionMiniMapPeer> _miniMapPeers = new List<FusionMiniMapPeer>(16);

	private readonly List<Renderer> _miniMapSuppressedRenderers = new List<Renderer>(64);

	private GameObject? _codeModsPage;

	private GameObject? _miniMapPage;

	private GameObject? _spawnablesPage;

	private GameObject? _notificationsPage;

	private TMP_Text? _osStatusText;

	private TMP_Text? _codeModTitle;

	private TMP_Text? _codeModPosition;

	private TouchButton? _codeModCommunityButton;

	private TMP_Text? _miniMapHeading;

	private TMP_Text? _miniMapRangeText;

	private TMP_Text? _miniMapScanText;

	private TouchButton? _miniMapModeButton;

	private TMP_Text? _miniMapRangeValue;

	private TMP_Text? _spawnableName;

	private TMP_Text? _spawnableDetails;

	private TMP_Text? _spawnablePosition;

	private TMP_Text? _spawnableStatus;

	private TMP_Text? _notificationText;

	private GameObject? _spawnablePreview;

	private GameObject? _spawnableNativePreview;

	private GameObject? _spawnPlacementPreview;

	private GameObject? _spawnPointerReticle;

	private LineRenderer? _spawnPointerLine;

	private TouchButton? _spawnActionButton;

	private TouchButton? _spawnableModeButton;

	private TouchButton? _spawnableFavoriteButton;

	private TouchButton? _spawnDeleteButton;

	private Mesh? _spawnablePreviewMesh;

	private MiniMapPeerOverlay? _miniMapPeerOverlay;

	private RectTransform? _miniMapViewport;

	private RectTransform? _miniMapGeometryRoot;

	private Image? _miniMapLayoutImage;

	private Texture2D? _miniMapLayoutTexture;

	private Sprite? _miniMapLayoutSprite;

	private GameObject? _miniMapCaptureObject;

	private Camera? _miniMapCaptureCamera;

	private RenderTexture? _miniMapCaptureTexture;

	private RenderTexture? _miniMapCaptureBackTexture;

	private RawImage? _miniMapSceneImage;

	private RectTransform? _miniMapBearingRoot;

	private RectTransform? _miniMapMarkerRoot;

	private object? _activeBoneMenuPage;

	private object? _managerControlCachePage;

	private IReadOnlyList<ReflectedManagerControl> _managerControlCache = Array.Empty<ReflectedManagerControl>();

	private float _managerControlCacheUntil;

	private object? _activeCodeModStringElement;

	private WristHubApp? _activeSdkApp;

	private WristHubPage? _activeSdkPage;

	private WristHubTextInput? _activeSdkText;

	private RuntimeSdkHost? _sdkHost;

	private bool _showLegacyBoneMenu;

	private bool _optionsReturnToHub;

	private string _latestSdkNotification = string.Empty;

	private WristKeyboardPurpose _wristKeyboardPurpose;

	private string _codeModQuery = string.Empty;

	private CodeModCatalogMode _codeModCatalogMode;

	private string _spawnableQuery = string.Empty;

	private TouchButton? _keyboardSubmitButton;

	private int _codeModPageIndex;

	private int _spawnableIndex;

	private int _spawnablePreviewGeneration;

	private int _spawnableManifestCount = -1;

	private bool _spawnPlacementActive;

	private bool _spawnDeleteActive;

	private bool _spawnTriggerHeld;

	private Hand? _spawnPlacementHand;

	private float _spawnPlacementYaw;

	private float _spawnPlacementDistance = 1.5f;

	private GameObject? _spawnDeleteTarget;

	private GameObject? _pendingGrabObject;

	private Hand? _pendingGrabHand;

	private float _pendingGrabDeadline;

	private float _nextPendingGrabAttempt;

	private float _spawnPointerReadyAt;

	private Material? _spawnPointerMaterial;

	private MaterialPropertyBlock? _spawnPointerBlock;

	private Renderer? _spawnPointerRenderer;

	private SpawnableBrowseMode _spawnableMode;

	private float _nextCodeModRefresh;

	private float _nextLoadedCodeModScan;

	private string _lastCodeModCatalogSignature = string.Empty;

	private float _nextMiniMapPeerRefresh;

	private float _nextMiniMapOverlayRender;

	private float _nextMiniMapOverlayRecovery;

	private bool _miniMapOverlayDiagnosticLogged;

	private bool _miniMapOverlayRecovering;

	private float _nextSpawnableRefresh;

	private int _lastMiniMapDegree = -1;

	private int _lastMiniMapPeerCount = -1;

	private int _miniMapRasterCursor;

	private int _miniMapRasterSamplesRemaining;

	private bool _miniMapTextureDirty;

	private float _nextMiniMapTextureUpload;

	private bool _miniMapCaptureValid;

	private Vector3 _miniMapCaptureCenter;

	private Vector3 _miniMapPendingCaptureCenter;

	private Vector3 _miniMapScanOrigin;

	private float _miniMapReferenceFloorY;

	private bool _miniMapScanOriginValid;

	private Color Cyan => WristHubThemePalette.Accent(_preferences.HologramTheme);

	private Color DimCyan => WristHubThemePalette.Dim(_preferences.HologramTheme);

	public bool IsOpen
	{
		get
		{
			if ((Object)(object)_browser != (Object)null)
			{
				return _browser.activeSelf;
			}
			return false;
		}
	}

	public bool IsLauncherOpen
	{
		get
		{
			if ((Object)(object)_root != (Object)null)
			{
				return _root.activeSelf;
			}
			return false;
		}
	}

	public bool IsClosing => (int)_lifecycle == 3;

	public string Query => _query;

	public event Action? Closed;

	public BoneHubAvatarBrowser(BoneHubService service, BoneHubRuntime runtime, BoneHubThumbnailCache thumbnails, ConcurrentQueue<Action> mainThread, BoneHubPreferences preferences, bool constrainedRendering, Instance log, LobbyAvatarSharingBridge? sharing = null)
	{
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Expected O, but got Unknown
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Expected O, but got Unknown
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Expected O, but got Unknown
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Expected O, but got Unknown
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: Expected O, but got Unknown
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Expected O, but got Unknown
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Expected O, but got Unknown
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Expected O, but got Unknown
		//IL_0159: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: 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_019f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01db: Unknown result type (might be due to invalid IL or missing references)
		//IL_0246: Unknown result type (might be due to invalid IL or missing references)
		//IL_0250: Expected O, but got Unknown
		//IL_0251: Unknown result type (might be due to invalid IL or missing references)
		//IL_025b: Expected O, but got Unknown
		//IL_03db: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e5: Expected O, but got Unknown
		_service = service;
		_runtime = runtime;
		_thumbnails = thumbnails;
		_mainThread = mainThread;
		_preferences = preferences;
		_constrainedRendering = constrainedRendering;
		_log = log;
		_sharing = sharing ?? new LobbyAvatarSharingBridge(log);
		_projectionTickAction = TickProjectionStage;
		_projectionRecoveryAction = RecoverProjectionStage;
		_touchTickAction = TickTouchStage;
		_touchRecoveryAction = ResetTouchButtons;
		_installationFocusTickAction = UpdateInstalledPackFocus;
		_previewTickAction = ProcessPendingPreview;
		_previewRecoveryAction = RecoverPreviewStage;
		_downloadTickAction = UpdateDownloadProgress;
		_sharingTickAction = TickSharingRegistration;
		_compassTickAction = TickCompass;
		_portalStateReceiver = ReceivePortalState;
		_portalRemoveReceiver = ReceivePortalRemoval;
	}

	public void OpenLauncher()
	{
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		EnsureBuilt();
		if (!((Object)(object)_root == (Object)null) && !((Object)(object)_hub == (Object)null) && !((Object)(object)_browser == (Object)null))
		{
			_wristLocalPoseValid = false;
			FollowWrist(immediate: true);
			_root.SetActive(true);
			if ((Object)(object)_launcher != (Object)null)
			{
				_launcher.SetActive(false);
			}
			_hub.SetActive(true);
			_browser.SetActive(false);
			_hologramCarousel?.SetVisible(visible: false);
			_viewState = (AvatarBrowserViewState)1;
			_osRouter.ShowHome();
			_promptDismissal.Reset();
			RefreshEntries();
			StartHologramProjection();
			_hubControlsReadyAt = Time.unscaledTime + (_preferences.ReducedMotion ? 0f : 0.55f);
			RequireInputRearm();
			_log.Msg("WristHub compact module hub projection opened.");
		}
	}

	public void Open()
	{
		//IL_0080: 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_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		OpenLauncher();
		if (!((Object)(object)_browser == (Object)null))
		{
			if ((Object)(object)_launcher != (Object)null)
			{
				_launcher.SetActive(false);
			}
			if ((Object)(object)_hub != (Object)null)
			{
				_hub.SetActive(false);
			}
			_browser.SetActive(true);
			StartHologramProjection();
			_query = string.Empty;
			_downloadsOnly = false;
			_selectedPackId = null;
			_scope = AvatarBrowseStartup.Resolve(_preferences.AvatarBrowserStartupMode, _preferences.LastAvatarBrowseScope);
			_scopeBeforeSearch = _scope;
			_viewState = (AvatarBrowserViewState)2;
			RememberScope();
			RefreshEntries();
			ShowScopePage();
		}
	}

	public void OpenSearch()
	{
		//IL_006e: 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_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_008a: 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)
		OpenLauncher();
		if (!((Object)(object)_browser == (Object)null))
		{
			if ((Object)(object)_launcher != (Object)null)
			{
				_launcher.SetActive(false);
			}
			if ((Object)(object)_hub != (Object)null)
			{
				_hub.SetActive(false);
			}
			_browser.SetActive(true);
			StartHologramProjection();
			_selectedPackId = null;
			_scope = AvatarBrowseStartup.Resolve(_preferences.AvatarBrowserStartupMode, _preferences.LastAvatarBrowseScope);
			_scopeBeforeSearch = _scope;
			RefreshEntries();
			OpenKeyboard();
		}
	}

	public void Close()
	{
		BeginClose(_preferences.ReducedMotion);
	}

	private void BeginClose(bool immediate)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: 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_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Invalid comparison between Unknown and I4
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		HologramDashboardLifecycle lifecycle = _lifecycle;
		if (((int)lifecycle != 0 && (int)lifecycle != 3) || 1 == 0)
		{
			if (_previewHeld)
			{
				EndPreviewDrag(equipIfAtChest: false);
			}
			CancelSpawnPlacement();
			_osRouter.BeginClose();
			ResetTouchButtons();
			ResetTrashConfirmation();
			CancelPendingSharingRegistration();
			_closeVisualStart = CurrentVisibleProgress();
			_lifecycle = (HologramDashboardLifecycle)3;
			_lifecycleStartedAt = Time.unscaledTime;
			_closeNotificationPending = true;
			_hologramProjection?.Hide(immediate);
			_chestEquipGuide?.Hide(immediate: true);
			_artworkGeneration.Next();
			_previewGeneration++;
			_requestedPreviewBarcode = string.Empty;
			_pendingPreviewAvatar = null;
			_promptDismissal.Reset();
			if (immediate)
			{
				FinalizeClose();
			}
		}
	}

	private void FinalizeClose()
	{
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_root != (Object)null)
		{
			_root.SetActive(false);
		}
		if ((Object)(object)_browser != (Object)null)
		{
			_browser.SetActive(false);
		}
		if ((Object)(object)_launcher != (Object)null)
		{
			_launcher.SetActive(false);
		}
		if ((Object)(object)_hub != (Object)null)
		{
			_hub.SetActive(false);
		}
		if ((Object)(object)_previewShell != (Object)null)
		{
			_previewShell.SetActive(false);
		}
		_hologramCarousel?.SetVisible(visible: false);
		_hologramProjection?.Hide(immediate: true);
		_loadedPreviewBarcode = string.Empty;
		_previewReady = false;
		_previewInteractionReady = false;
		_lifecycle = (HologramDashboardLifecycle)0;
		if (_closeNotificationPending)
		{
			_closeNotificationPending = false;
			this.Closed?.Invoke();
		}
	}

	public void SetQuery(string value)
	{
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: 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)
		if ((Object)(object)_canvasGroup != (Object)null)
		{
			_canvasGroup.alpha = 1f;
		}
		if ((Object)(object)_root != (Object)null)
		{
			_root.SetActive(true);
		}
		if ((Object)(object)_launcher != (Object)null)
		{
			_launcher.SetActive(false);
		}
		if ((Object)(object)_hub != (Object)null)
		{
			_hub.SetActive(false);
		}
		if ((Object)(object)_browser != (Object)null)
		{
			_browser.SetActive(true);
		}
		_query = value?.Trim() ?? string.Empty;
		_downloadsOnly = false;
		_onlineEntries = Array.Empty<AvatarEntry>();
		_viewState = (AvatarBrowserViewState)((_query.Length == 0) ? 2 : 15);
		_log.Msg($"Avatar search submitted ({_query.Length} characters). Query text is not logged.");
		RefreshEntries();
		if (_query.Length == 0)
		{
			ShowScopePage();
		}
		else
		{
			_scope = (AvatarBrowseScope)0;
			_selectedPackId = null;
			RefreshEntries();
			ShowDetailPage();
		}
		int generation = ++_searchGeneration;
		if (_query.Length != 0 && _service.ModIoReady)
		{
			SetStatus("Searching BONELAB mod.io…", Cyan);
			SearchOnlineAsync(_query, generation);
		}
	}

	public void Refresh()
	{
		RefreshEntries();
		RefreshVisiblePage();
	}

	public void Select(string identity)
	{
		AvatarEntry val = ((IEnumerable<AvatarEntry>)_entries).FirstOrDefault((Func<AvatarEntry, bool>)((AvatarEntry item) => string.Equals(item.Identity, identity, StringComparison.OrdinalIgnoreCase)));
		if (!(val == (AvatarEntry)null))
		{
			_selectedIdentity = val.Identity;
			_cursor.Keep(_entries.Count, IndexOf(val.Identity));
			ShowDetailPage();
			RefreshSelection(val);
		}
	}

	public void OnDownloadChanged(DownloadJob job)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Invalid comparison between Unknown and I4
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Invalid comparison between Unknown and I4
		//IL_007f: 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_0085: 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_0089: Invalid comparison between Unknown and I4
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: Invalid comparison between Unknown and I4
		//IL_0123: Unknown result type (might be due to invalid IL or missing references)
		//IL_0128: 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_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_012d: Invalid comparison between Unknown and I4
		//IL_01a3: 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_01a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bd: Expected I4, but got Unknown
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d6: 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_01d3: Unknown result type (might be due to invalid IL or missing references)
		_jobs[job.Mod.Id] = job;
		if ((int)job.State == 6)
		{
			_log.Warning("WristHub download failed: " + Readable(job.Error ?? "Unknown download failure."));
		}
		else if ((int)job.State == 5)
		{
			_log.Msg("WristHub download and safe installation completed.");
		}
		GameObject? downloadsPage = _downloadsPage;
		if (downloadsPage != null && downloadsPage.activeSelf)
		{
			RefreshDownloadsPage();
		}
		DownloadState state = job.State;
		if (state - 5 <= 2)
		{
			_startingDownloads.Remove(job.Mod.Id);
		}
		if ((int)job.State == 5 && _installedFocus.Active && _installedFocus.ModId == job.Mod.Id)
		{
			InstalledPackFocus installedFocus = _installedFocus;
			long id = job.Mod.Id;
			double num = Time.unscaledTime;
			InstalledModRecord result = job.Result;
			installedFocus.MarkDownloaded(id, num, (IEnumerable<string>)((result != null) ? result.InstalledDirectories : null), 12.0);
			_log.Msg("Downloaded root pack completed; waiting for its runtime avatar crates.");
		}
		else
		{
			state = job.State;
			bool flag = state - 6 <= 1;
			if (flag && _installedFocus.ModId == job.Mod.Id)
			{
				_installedFocus.Clear();
			}
		}
		RefreshEntries();
		AvatarEntry? obj = Selected();
		if (((obj != null) ? new long?(obj.ModId) : ((long?)null)) == job.Mod.Id)
		{
			state = job.State;
			_viewState = (AvatarBrowserViewState)((state - 5) switch
			{
				1 => 19, 
				0 => 18, 
				2 => 16, 
				_ => 17, 
			});
			RefreshVisiblePage();
		}
	}

	public void OnRuntimeStatusChanged(string status)
	{
		//IL_006e: 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_005f: Unknown result type (might be due to invalid IL or missing references)
		GameObject? browser = _browser;
		if (browser != null && browser.activeSelf && !string.IsNullOrWhiteSpace(status))
		{
			bool flag = status.Contains("failed", StringComparison.OrdinalIgnoreCase) || status.Contains("could not", StringComparison.OrdinalIgnoreCase) || status.Contains("rejected", StringComparison.OrdinalIgnoreCase);
			SetStatus(status, flag ? Red : (status.Contains("sharing", StringComparison.OrdinalIgnoreCase) ? Cyan : Green));
		}
	}

	public void OnEquipFinished(BoneHubAvatar avatar, bool success)
	{
		//IL_0051: 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)
		if (success)
		{
			AvatarEntry? obj = Selected();
			if (string.Equals((obj != null) ? obj.Barcode : null, avatar.Barcode, StringComparison.OrdinalIgnoreCase) && _previewMetrics.TryGetValue(avatar.Barcode, out var value))
			{
				SetHeight(((AvatarPreviewMetrics)(ref value)).HeightDisplayText, ((AvatarPreviewMetrics)(ref value)).Valid ? Green : Red);
			}
		}
	}

	public void OnAvatarsChanged(long modId, IReadOnlyList<BoneHubAvatar> avatars)
	{
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
		_previewMetrics.Clear();
		_previewRoutes.Clear();
		_rigValidations.Clear();
		if (avatars.Any((BoneHubAvatar avatar) => string.Equals(avatar.Barcode, _loadedPreviewBarcode, StringComparison.OrdinalIgnoreCase)))
		{
			_loadedPreviewBarcode = string.Empty;
			_requestedPreviewBarcode = string.Empty;
			_pendingPreviewAvatar = null;
			_previewReady = false;
			_previewInteractionReady = false;
			_previewVisualSource = (AvatarPreviewVisualSource)0;
		}
		_fingertips.Invalidate();
		_projectionAnchors.Invalidate();
		_hologramProjection?.BeginTrackingGrace();
		RefreshEntries();
		if (_installedFocus.TryBind(modId, avatars.Select((BoneHubAvatar avatar) => avatar.Barcode)))
		{
			_selectedPackId = modId;
			_query = string.Empty;
			_onlineEntries = Array.Empty<AvatarEntry>();
			_downloadsOnly = false;
			IReadOnlyList<BoneHubAvatar> avatars2 = _runtime.GetAvatars(modId);
			_entries = ((avatars2.Count > 0) ? avatars2 : avatars).Select((BoneHubAvatar avatar) => avatar.ToEntry()).OrderBy<AvatarEntry, string>((AvatarEntry entry) => entry.AvatarName, StringComparer.OrdinalIgnoreCase).ToArray();
			_selectedIdentity = _entries.First((AvatarEntry entry) => string.Equals(entry.Barcode, _installedFocus.SelectedBarcode, StringComparison.OrdinalIgnoreCase)).Identity;
			_cursor.Keep(_entries.Count, IndexOf(_selectedIdentity));
			ShowDetailPage();
			SetStatus("Downloaded - ready to equip", Green);
			_log.Msg($"Downloaded avatar pack bound to {avatars.Count} runtime crate{((avatars.Count == 1) ? string.Empty : "s")}; Equip is ready.");
			return;
		}
		if (avatars.Count > 0)
		{
			AvatarEntry? obj = Selected();
			if (obj != null && obj.ModId == modId)
			{
				AvatarEntry? obj2 = Selected();
				if (string.IsNullOrWhiteSpace((obj2 != null) ? obj2.Barcode : null))
				{
					_selectedIdentity = avatars[0].ToEntry().Identity;
					RefreshEntries();
				}
			}
		}
		RefreshVisiblePage();
	}

	public void OnSceneChanged(string sceneName = "")
	{
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		_compassSceneKey = NormalizeSceneKey(sceneName);
		_compassHeadingValid = false;
		_lastCompassDegree = -1;
		_fingertips.Invalidate();
		_projectionAnchors.Invalidate();
		_wristLocalPoseValid = false;
		_hologramProjection?.BeginTrackingGrace();
		ResetTouchButtons();
		OnWristOsSceneChanged();
		if ((Object)(object)_root == (Object)null)
		{
			return;
		}
		bool reportRecovery = PreviewShellRecovery.RequiresRebuild((Object)(object)_previewShell != (Object)null, (Object)(object)_previewFallback != (Object)null);
		if (EnsurePreviewShell(reportRecovery))
		{
			ClearPreviewModel();
			_loadedPreviewBarcode = string.Empty;
			_requestedPreviewBarcode = string.Empty;
			_pendingPreviewAvatar = null;
			_previewReady = false;
			_previewInteractionReady = false;
			_previewVisualSource = (AvatarPreviewVisualSource)0;
			GameObject? detailPage = _detailPage;
			if (detailPage != null && detailPage.activeSelf)
			{
				RefreshFocusedResult();
			}
		}
	}

	public void Tick()
	{
		//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Invalid comparison between Unknown and I4
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		_hologramProjection?.Tick(_preferences.ReducedMotion);
		BoneHubHologramCarousel? hologramCarousel = _hologramCarousel;
		if (hologramCarousel != null)
		{
			float assemblyProgress = _hologramProjection?.SideAvatarProgress ?? 1f;
			bool reducedMotion = _preferences.ReducedMotion;
			BoneHubForearmProjection? hologramProjection = _hologramProjection;
			hologramCarousel.Tick(assemblyProgress, reducedMotion, (HoloPerformanceTier)((hologramProjection == null) ? (_constrainedRendering ? 1 : 0) : ((int)hologramProjection.PerformanceTier)));
		}
		_chestEquipGuide?.Tick(_preferences.ReducedMotion);
		if (IsLocalPlayerDead())
		{
			if (!_wasPlayerDead && _activePortal != (PortalLinkState)null)
			{
				RemovePortal(IsFusionHost(), hideImmediately: true);
			}
			if (!_wasPlayerDead && IsLauncherOpen)
			{
				_log.Msg("WristHub closed because the local player died.");
				BeginClose(immediate: true);
			}
			_wasPlayerDead = true;
			return;
		}
		_wasPlayerDead = false;
		try
		{
			FusionPortalBridge.Attach(_portalStateReceiver, _portalRemoveReceiver);
			_portalVisual?.Tick(Time.unscaledTime, _preferences.ReducedMotion);
			if (_activePortal != (PortalLinkState)null)
			{
				TickPortals();
			}
		}
		catch (Exception ex)
		{
			string text = "portal-global|" + ex.GetType().FullName;
			if (_runtimeErrorThrottle.ShouldLog(text, (double)Environment.TickCount64 / 1000.0, 5.0))
			{
				_log.Error("WristHub portal update recovered (repeats suppressed): " + ex.Message);
			}
		}
		if (!IsLauncherOpen || (Object)(object)Player.Head == (Object)null)
		{
			return;
		}
		if ((int)_lifecycle == 3)
		{
			FollowWrist();
			UpdateLifecycleVisuals();
			if ((double)(Time.unscaledTime - _lifecycleStartedAt) >= 0.35)
			{
				FinalizeClose();
			}
		}
		else
		{
			RunGuarded("projection", _projectionTickAction, _projectionRecoveryAction);
			RunGuarded("touch", _touchTickAction, _touchRecoveryAction);
			RunGuarded("installation-focus", _installationFocusTickAction);
			RunGuarded("preview", _previewTickAction, _previewRecoveryAction);
			RunGuarded("download", _downloadTickAction);
			RunGuarded("sharing", _sharingTickAction, CancelPendingSharingRegistration);
			RunGuarded("compass", _compassTickAction);
			RunGuarded("wrist-os", TickWristOs);
		}
	}

	private void TickProjectionStage()
	{
		FollowWrist();
		UpdateHologramProjection();
		UpdateLifecycleVisuals();
	}

	private void RecoverProjectionStage()
	{
		_projectionAnchors.Invalidate();
		_hologramProjection?.BeginTrackingGrace();
	}

	private void TickTouchStage()
	{
		UpdatePushPromptVisibility();
		PollButtons();
	}

	private void RecoverPreviewStage()
	{
		EndPreviewDrag(equipIfAtChest: false);
	}

	public void LateTick()
	{
		if (!IsLauncherOpen || (Object)(object)Player.Head == (Object)null)
		{
			return;
		}
		try
		{
			FollowWrist(immediate: true);
			UpdateHologramProjection(finalPoseOnly: true);
			UpdatePreview();
		}
		catch (Exception ex)
		{
			string text = "late-drag|" + ex.GetType().FullName;
			if (_runtimeErrorThrottle.ShouldLog(text, (double)Environment.TickCount64 / 1000.0, 5.0))
			{
				_log.Error($"WristHub preview drag recovered: {ex}");
			}
			EndPreviewDrag(equipIfAtChest: false);
		}
	}

	private void RunGuarded(string subsystem, Action action, Action? recover = null)
	{
		try
		{
			action();
		}
		catch (Exception ex)
		{
			string text = ex.StackTrace?.Split('\n').FirstOrDefault()?.Trim() ?? "no-stack";
			string text2 = subsystem + "|" + ex.GetType().FullName + "|" + text;
			if (_runtimeErrorThrottle.ShouldLog(text2, (double)Environment.TickCount64 / 1000.0, 5.0))
			{
				_log.Error($"WristHub {subsystem} update recovered (repeats suppressed): {ex}");
			}
			try
			{
				recover?.Invoke();
			}
			catch
			{
			}
		}
	}

	public void Dispose()
	{
		if (!_disposed)
		{
			_disposed = true;
			_searchGeneration++;
			DisposeWristOs();
			ResetUiReferences(destroyOwnedObjects: true);
		}
	}

	private void EnsureBuilt()
	{
		if (!((Object)(object)_root != (Object)null))
		{
			ResetUiReferences(destroyOwnedObjects: true);
			BuildTablet();
		}
	}

	private void ResetUiReferences(bool destroyOwnedObjects)
	{
		//IL_043e: Unknown result type (might be due to invalid IL or missing references)
		ResetWristOsReferences(destroyOwnedObjects);
		foreach (PackRow packRow in _packRows)
		{
			packRow.Generation++;
		}
		ResetTouchButtons();
		_buttons.Clear();
		_packRows.Clear();
		_touchCaptures.Clear();
		_activeProbeIds.Clear();
		_staleCaptureIds.Clear();
		_artworkGeneration.Next();
		_previewGeneration++;
		if (destroyOwnedObjects)
		{
			try
			{
				if ((Object)(object)_previewModel != (Object)null)
				{
					Object.Destroy((Object)(object)_previewModel);
				}
			}
			catch
			{
			}
			foreach (Mesh ownedPreviewMesh in _ownedPreviewMeshes)
			{
				try
				{
					if ((Object)(object)ownedPreviewMesh != (Object)null)
					{
						Object.Destroy((Object)(object)ownedPreviewMesh);
					}
				}
				catch
				{
				}
			}
			try
			{
				if ((Object)(object)_previewShell != (Object)null)
				{
					Object.Destroy((Object)(object)_previewShell);
				}
			}
			catch
			{
			}
			try
			{
				if ((Object)(object)_root != (Object)null)
				{
					Object.Destroy((Object)(object)_root);
				}
			}
			catch
			{
			}
			try
			{
				if ((Object)(object)_placeholderTexture != (Object)null)
				{
					Object.Destroy((Object)(object)_placeholderTexture);
				}
			}
			catch
			{
			}
			try
			{
				if ((Object)(object)_roundedSprite != (Object)null)
				{
					Object.Destroy((Object)(object)_roundedSprite);
				}
			}
			catch
			{
			}
			try
			{
				if ((Object)(object)_roundedTexture != (Object)null)
				{
					Object.Destroy((Object)(object)_roundedTexture);
				}
			}
			catch
			{
			}
			DisposeButtonIconSprites();
			try
			{
				_hologramProjection?.Dispose();
			}
			catch
			{
			}
			try
			{
				_hologramCarousel?.Dispose();
			}
			catch
			{
			}
			try
			{
				_chestEquipGuide?.Dispose();
			}
			catch
			{
			}
		}
		_ownedPreviewMeshes.Clear();
		_hologramProjection = null;
		_hologramCarousel = null;
		_chestEquipGuide = null;
		_root = null;
		_launcher = null;
		_hub = null;
		_browser = null;
		_canvasRoot = null;
		_packPage = null;
		_detailPage = null;
		_optionsPage = null;
		_watchPlacementPage = null;
		_downloadsPage = null;
		_packOptionsPage = null;
		_keyboardPage = null;
		_deletePage = null;
		_compassPage = null;
		_denseGlassChrome = null;
		_denseGlassBackground = null;
		_denseGlassOverlay = null;
		_wristLocalPoseValid = false;
		_wristLocalHandId = 0;
		_leftArcWing = null;
		_rightArcWing = null;
		_metadataArc = null;
		_canvasGroup = null;
		_carouselIdentity = string.Empty;
		_packTitle = null;
		_packStatus = null;
		_packPosition = null;
		_packSearchText = null;
		_sortText = null;
		_wristText = null;
		_scaleText = null;
		_motionText = null;
		_themeText = null;
		_clockText = null;
		_startingViewText = null;
		_watchForearmText = null;
		_scopeText = null;
		_downloadsSummary = null;
		_deleteTitle = null;
		_deleteDetails = null;
		_keyboardText = null;
		_keyboardTitle = null;
		_keyboardShiftText = null;
		_searchText = null;
		_compassHeadingText = null;
		_compassCalibrationText = null;
		_compassNeedle = null;
		_compassDecorRing = null;
		_positionText = null;
		_selectedName = null;
		_creatorText = null;
		_modText = null;
		_statusText = null;
		_heightText = null;
		_heightRuler = null;
		_progressRoot = null;
		_progressFill = null;
		_progressText = null;
		_actionLabel = null;
		_actionPanel = null;
		_trashButton = null;
		_centralArtwork = null;
		_placeholderTexture = null;
		_roundedTexture = null;
		_roundedSprite = null;
		_actionButton = null;
		_previewShell = null;
		_previewFallback = null;
		_previewModel = null;
		_previewAvatar = null;
		_pendingPreviewAvatar = null;
		_loadedPreviewBarcode = string.Empty;
		_requestedPreviewBarcode = string.Empty;
		_previewReady = false;
		_previewInteractionReady = false;
		_previewHeld = false;
		_dragHand = null;
		_dragWasInsideChest = false;
		_previewReturning = false;
		_lifecycle = (HologramDashboardLifecycle)0;
		_closeNotificationPending = false;
		CancelPendingSharingRegistration();
	}

	private void BuildTablet()
	{
		//IL_0034: 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_0063: 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_0092: Expected O, but got Unknown
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Expected O, but got Unknown
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: Expected O, but got Unknown
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: 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_0183: Expected O, but got Unknown
		//IL_01be: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0207: 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_0243: Unknown result type (might be due to invalid IL or missing references)
		//IL_029a: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_02dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_031a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0324: Expected O, but got Unknown
		//IL_0365: Unknown result type (might be due to invalid IL or missing references)
		//IL_036f: Unknown result type (might be due to invalid IL or missing references)
		//IL_03af: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0410: Unknown result type (might be due to invalid IL or missing references)
		//IL_0424: 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_047d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0491: Unknown result type (might be due to invalid IL or missing references)
		//IL_0497: Unknown result type (might be due to invalid IL or missing references)
		//IL_04de: Unknown result type (might be due to invalid IL or missing references)
		//IL_04f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_04f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_053f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0553: Unknown result type (might be due to invalid IL or missing references)
		//IL_0558: Unknown result type (might be due to invalid IL or missing references)
		//IL_05b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_05cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_05d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0612: Unknown result type (might be due to invalid IL or missing references)
		//IL_061d: Unknown result type (might be due to invalid IL or missing references)
		//IL_065d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0671: Unknown result type (might be due to invalid IL or missing references)
		//IL_0677: Unknown result type (might be due to invalid IL or missing references)
		//IL_06b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_06d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_06f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_06fb: Expected O, but got Unknown
		//IL_071d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0727: Expected O, but got Unknown
		//IL_0754: Unknown result type (might be due to invalid IL or missing references)
		//IL_076d: Unknown result type (might be due to invalid IL or missing references)
		//IL_07b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_07be: Unknown result type (might be due to invalid IL or missing references)
		//IL_07c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_07d4: 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_07f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_07fe: Expected O, but got Unknown
		//IL_082b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0844: Unknown result type (might be due to invalid IL or missing references)
		//IL_088f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0895: Unknown result type (might be due to invalid IL or missing references)
		//IL_08a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_08ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_08ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_08cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_08d5: Expected O, but got Unknown
		//IL_0916: Unknown result type (might be due to invalid IL or missing references)
		//IL_092f: Unknown result type (might be due to invalid IL or missing references)
		//IL_095f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0978: 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_09ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_09d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_09d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_09de: Unknown result type (might be due to invalid IL or missing references)
		//IL_09e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_09e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a21: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a26: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a2b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a30: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a36: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a7c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a81: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a86: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a8b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a91: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ac9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ace: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ad3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ad8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ade: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b24: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b29: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b2e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b33: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b39: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b71: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b76: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b7b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b80: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b85: Unknown result type (might be due to invalid IL or missing references)
		//IL_0bbd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0bc2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0bc7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0bcc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0bd2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0c0a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0c0f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0c14: Unknown result type (might be due to invalid IL or missing references)
		//IL_0c19: Unknown result type (might be due to invalid IL or missing references)
		//IL_0c1f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0c60: Unknown result type (might be due to invalid IL or missing references)
		//IL_0c6b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0c97: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ca1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0cc7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0cd2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0cf8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d16: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d3c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d47: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d73: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d7e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0db0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0db5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0dba: Unknown result type (might be due to invalid IL or missing references)
		//IL_0dbf: Unknown result type (might be due to invalid IL or missing references)
		//IL_0dc5: Unknown result type (might be due to invalid IL or missing references)
		_roundedTexture = CreateRoundedTexture();
		_roundedSprite = Sprite.Create(_roundedTexture, new Rect(0f, 0f, (float)((Texture)_roundedTexture).width, (float)((Texture)_roundedTexture).height), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4(16f, 16f, 16f, 16f));
		((Object)_roundedSprite).name = "WristHub Rounded UI Sprite";
		_root = new GameObject("[WristHub] Wrist Avatar Browser");
		Object.DontDestroyOnLoad((Object)(object)_root);
		BuildHologramProjection();
		_hub = new GameObject("WristHub Module Hub");
		_hub.transform.SetParent(_root.transform, false);
		BuildModuleStage(_hub.transform);
		GameObject val = CreateCanvas(_hub.transform, "Radial Module UI", 230f, 176f, 0.009f);
		BuildWristOsHome(val.transform);
		_hub.SetActive(false);
		_placeholderTexture = CreatePlaceholderTexture();
		_browser = new GameObject("Compact 30x19cm Arc Hologram Dashboard");
		_browser.transform.SetParent(_root.transform, false);
		_browser.transform.localScale = Vector3.one * 0.88f;
		_denseGlassChrome = new GameObject("Dense Glass Panel Chrome");
		_denseGlassChrome.transform.SetParent(_browser.transform, false);
		CreatePanel(_denseGlassChrome.transform, "Glass Panel Shadow", new Vector3(0.006f, -0.007f, -0.006f), new Vector3(0.35f, 0.22f, 0.009f), new Color(0f, 0f, 0f, 0.62f), (PrimitiveType)3);
		CreatePanel(_denseGlassChrome.transform, "Curved Glass Panel", Vector3.zero, new Vector3(0.34f, 0.21f, 0.007f), new Color(Glass.r, Glass.g, Glass.b, 0.94f), (PrimitiveType)3);
		_canvasRoot = CreateCanvas(_browser.transform, "WristHub Tablet Canvas", 340f, 210f, 0.006f);
		_canvasGroup = _canvasRoot.AddComponent<CanvasGroup>();
		RawImage val2 = CreateRawImage(_canvasRoot.transform, "WristHub Background", AvatarTabletLayout.Tablet, BoneHubTheme.Background ?? _placeholderTexture, Color.white);
		_denseGlassBackground = ((Component)val2).gameObject;
		((Graphic)val2).raycastTarget = false;
		Image val3 = CreateImage(_canvasRoot.transform, "Calm Dark Overlay", AvatarTabletLayout.Tablet, new Color(0.005f, 0.024f, 0.038f, 0.72f));
		_denseGlassOverlay = ((Component)val3).gameObject;
		((Graphic)val3).raycastTarget = false;
		_packPage = new GameObject("Mod Pack Library Page");
		_packPage.transform.SetParent(_canvasRoot.transform, false);
		_packTitle = CreateText(_packPage.transform, "GROUPS", new TabletRect(92f, 83f, 64f, 25f), 15f, Color.white, (TextAlignmentOptions)514, (FontStyles)1);
		CreateButton(_browser.transform, _packPage.transform, "BACK", new Vector3(-0.145f, 0.083f, 0.007f), new Vector3(0.042f, 0.028f, 0.014f), DimCyan, BackFromPackPage, 9f);
		TouchButton touchButton = CreateButton(_browser.transform, _packPage.transform, "SEARCH", new Vector3(-0.09f, 0.083f, 0.007f), new Vector3(0.064f, 0.028f, 0.014f), DimCyan, OpenKeyboard, 11f);
		_packSearchText = touchButton.Label;
		CreateButton(_browser.transform, _packPage.transform, "ALL", new Vector3(-0.034f, 0.083f, 0.007f), new Vector3(0.046f, 0.028f, 0.014f), Cyan, ShowAllAvatars, 12f);
		CreateButton(_browser.transform, _packPage.transform, "OPTIONS", new Vector3(0.025f, 0.083f, 0.007f), new Vector3(0.068f, 0.028f, 0.014f), DimCyan, ShowOptionsPage, 9f);
		CreateButton(_browser.transform, _packPage.transform, "X", new Vector3(0.148f, 0.083f, 0.007f), new Vector3(0.026f, 0.028f, 0.014f), Red, Close, 19f);
		for (int i = 0; i < 4; i++)
		{
			CreatePackRow(i);
		}
		CreateButton(_browser.transform, _packPage.transform, "<", new Vector3(-0.135f, -0.091f, 0.007f), new Vector3(0.05f, 0.026f, 0.014f), DimCyan, delegate
		{
			MovePackPage(-1);
		}, 22f);
		_packPosition = CreateText(_packPage.transform, "1 / 1", new TabletRect(0f, -91f, 90f, 24f), 14f, Cyan, (TextAlignmentOptions)514, (FontStyles)1);
		CreateButton(_browser.transform, _packPage.transform, ">", new Vector3(0.135f, -0.091f, 0.007f), new Vector3(0.05f, 0.026f, 0.014f), DimCyan, delegate
		{
			MovePackPage(1);
		}, 22f);
		_packStatus = CreateText(_packPage.transform, string.Empty, new TabletRect(0f, -72f, 250f, 10f), 11f, new Color(0.7f, 0.82f, 0.88f, 1f), (TextAlignmentOptions)514, (FontStyles)0);
		_detailPage = new GameObject("Arc Dashboard Avatar Page");
		_detailPage.transform.SetParent(_canvasRoot.transform, false);
		_leftArcWing = new GameObject("Left Curved Glass Wing");
		_leftArcWing.transform.SetParent(_detailPage.transform, false);
		CreateImage(_leftArcWing.transform, "Left Wing Glass", ArcDashboardLayout.LeftWing, new Color(0.006f, 0.026f, 0.038f, 0.92f));
		CreateImage(_leftArcWing.transform, "Left Wing Cyan Edge", new TabletRect(((TabletRect)(ref ArcDashboardLayout.LeftWing)).Right - 1.5f, ((TabletRect)(ref ArcDashboardLayout.LeftWing)).Y, 3f, ((TabletRect)(ref ArcDashboardLayout.LeftWing)).Height - 14f), new Color(Cyan.r, Cyan.g, Cyan.b, 0.55f));
		_rightArcWing = new GameObject("Right Curved Glass Wing");
		_rightArcWing.transform.SetParent(_detailPage.transform, false);
		CreateImage(_rightArcWing.transform, "Right Wing Glass", ArcDashboardLayout.RightWing, new Color(0.006f, 0.026f, 0.038f, 0.92f));
		CreateImage(_rightArcWing.transform, "Right Wing Cyan Edge", new TabletRect(((TabletRect)(ref ArcDashboardLayout.RightWing)).Left + 1.5f, ((TabletRect)(ref ArcDashboardLayout.RightWing)).Y, 3f, ((TabletRect)(ref ArcDashboardLayout.RightWing)).Height - 14f), new Color(Cyan.r, Cyan.g, Cyan.b, 0.55f));
		_metadataArc = new GameObject("Floating Avatar Metadata Arc");
		_metadataArc.transform.SetParent(_detailPage.transform, false);
		CreateImage(_metadataArc.transform, "Upper Metadata Glass", new TabletRect(0f, 73f, 184f, 38f), new Color(0.006f, 0.026f, 0.038f, 0.78f));
		CreateImage(_metadataArc.transform, "Lower Metadata Glass", new TabletRect(0f, -53f, 206f, 24f), new Color(0.006f, 0.026f, 0.038f, 0.82f));
		CreateImage(_detailPage.transform, "Circular Hologram Stage", ArcDashboardLayout.Stage, new Color(0.01f, 0.18f, 0.24f, 0.1f));
		CreateButton(_browser.transform, _detailPage.transform, "BACK", PixelCenter(ArcDashboardLayout.Back), PixelSize(ArcDashboardLayout.Back), DimCyan, BackFromDetailPage, 8f);
		TouchButton touchButton2 = CreateButton(_browser.transform, _detailPage.transform, "SEARCH", PixelCenter(ArcDashboardLayout.Search), PixelSize(ArcDashboardLayout.Search), DimCyan, OpenKeyboard, 9f);
		_searchText = touchButton2.Label;
		CreateButton(_browser.transform, _detailPage.transform, "PACK", PixelCenter(ArcDashboardLayout.Pack), PixelSize(ArcDashboardLayout.Pack), DimCyan, ShowPackOptionsPage, 9f);
		TouchButton touchButton3 = CreateButton(_browser.transform, _detailPage.transform, "ALL", PixelCenter(ArcDashboardLayout.Scope), PixelSize(ArcDashboardLayout.Scope), Cyan, ToggleScopeOrDownloads, 10f);
		_scopeText = touchButton3.Label;
		CreateButton(_browser.transform, _detailPage.transform, "OPTIONS", PixelCenter(ArcDashboardLayout.Options), PixelSize(ArcDashboardLayout.Options), DimCyan, ShowOptionsPage, 8f);
		CreateButton(_browser.transform, _detailPage.transform, "X", PixelCenter(ArcDashboardLayout.Close), PixelSize(ArcDashboardLayout.Close), Red, Close, 18f);
		CreateButton(_browser.transform, _detailPage.transform, "<", PixelCenter(ArcDashboardLayout.Previous), PixelSize(ArcDashboardLayout.Previous), DimCyan, delegate
		{
			MoveFocused(-1);
		}, 25f);
		CreateButton(_browser.transform, _detailPage.transform, ">", PixelCenter(ArcDashboardLayout.Next), PixelSize(ArcDashboardLayout.Next), DimCyan, delegate
		{
			MoveFocused(1);
		}, 25f);
		_centralArtwork = CreateRawImage(_detailPage.transform, "Selected Artwork", new TabletRect(0f, 5f, 88f, 96f), _placeholderTexture, Color.white);
		((Graphic)_centralArtwork).raycastTarget = false;
		_selectedName = CreateText(_metadataArc.transform, "Select an avatar", ArcDashboardLayout.Name, 15f, Color.white, (TextAlignmentOptions)514, (FontStyles)1);
		_creatorText = CreateText(_metadataArc.transform, string.Empty, ArcDashboardLayout.Creator, 9f, Cyan, (TextAlignmentOptions)514, (FontStyles)0);
		_modText = CreateText(_metadataArc.transform, string.Empty, ArcDashboardLayout.PackName, 9f, new Color(0.72f, 0.82f, 0.88f, 1f), (TextAlignmentOptions)514, (FontStyles)0);
		_positionText = CreateText(_metadataArc.transform, "0 / 0", ArcDashboardLayout.Position, 9f, Cyan, (TextAlignmentOptions)514, (FontStyles)1);
		CreateTabletHeightRuler();
		_statusText = CreateText(_metadataArc.transform, "Ready", ArcDashboardLayout.Status, 8.5f, Cyan, (TextAlignmentOptions)514, (FontStyles)1);
		_actionButton = CreateButton(_browser.transform, _detailPage.transform, "EQUIP", PixelCenter(ArcDashboardLayout.Action), PixelSize(ArcDashboardLayout.Action), Cyan, ActivateSelected, 16f);
		_actionLabel = _actionButton.Label;
		_actionPanel = _actionButton.Panel;
		_trashButton = null;
		CreateTabletProgressBar();
		BuildOptionsPage();
		BuildWatchPlacementPage();
		BuildDownloadsPage();
		BuildPackOptionsPage();
		BuildCompassPage();
		BuildWristOsPages();
		BuildKeyboardPage();
		BuildDeletePage();
		_packPage.SetActive(true);
		_detailPage.SetActive(false);
		GameObject? optionsPage = _optionsPage;
		if (optionsPage != null)
		{
			optionsPage.SetActive(false);
		}
		GameObject? watchPlacementPage = _watchPlacementPage;
		if (watchPlacementPage != null)
		{
			watchPlacementPage.SetActive(false);
		}
		GameObject? downloadsPage = _downloadsPage;
		if (downloadsPage != null)
		{
			downloadsPage.SetActive(false);
		}
		GameObject? packOptionsPage = _packOptionsPage;
		if (packOptionsPage != null)
		{
			packOptionsPage.SetActive(false);
		}
		GameObject? compassPage = _compassPage;
		if (compassPage != null)
		{
			compassPage.SetActive(false);
		}
		HideWristOsAppPages();
		GameObject? keyboardPage = _keyboardPage;
		if (keyboardPage != null)
		{
			keyboardPage.SetActive(false);
		}
		GameObject? deletePage = _deletePage;
		if (deletePage != null)
		{
			deletePage.SetActive(false);
		}
		_browser.SetActive(false);
		CreatePreviewShell();
		if ((Object)(object)_root != (Object)null)
		{
			_hologramCarousel = null;
			_root.SetActive(false);
		}
	}

	private void BuildHologramProjection()
	{
		if (_hologramProjection == null)
		{
			_hologramProjection = new BoneHubForearmProjection(_log, _constrainedRendering);
		}
	}

	private void BuildModuleStage(Transform parent)
	{
		//IL_003f: 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_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: 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_0066: 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)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 0; i < 32; i++)
		{
			float num = (float)i / 32f * (float)Math.PI * 2f;
			((Component)CreatePanel(parent, "Projected Module Ring", new Vector3(Mathf.Sin(num) * 0.061f, Mathf.Cos(num) * 0.061f, 0.002f), new Vector3(0.007f, 0.0011f, 0.0011f), (i % 4 == 0) ? Cyan : DimCyan, (PrimitiveType)3)).transform.localRotation = Quaternion.Euler(0f, 0f, (0f - num) * 57.29578f);
		}
		((Component)CreatePanel(parent, "Module Emitter Core", new Vector3(0f, 0f, -0.002f), new Vector3(0.012f, 0.012f, 0.001f), new Color(0.08f, 0.6f, 0.72f, 0.45f), (PrimitiveType)2)).transform.localRotation = Quaternion.Euler(90f, 0f, 0f);
	}

	private void BuildDownloadsPage()
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Expected O, but got Unknown
		//IL_006e: 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_00b6: 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_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0171: 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_01b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0201: 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_021a: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)_browser == (Object)null) && !((Object)(object)_canvasRoot == (Object)null))
		{
			_downloadsPage = new GameObject("WristHub Downloads Glass Panel");
			_downloadsPage.transform.SetParent(_canvasRoot.transform, false);
			CreateImage(_downloadsPage.transform, "Downloads Glass", new TabletRect(0f, 0f, 286f, 168f), new Color(0.008f, 0.03f, 0.043f, 0.88f));
			CreateText(_downloadsPage.transform, "DOWNLOADS", new TabletRect(-55f, 66f, 150f, 26f), 18f, Color.white, (TextAlignmentOptions)4097, (FontStyles)1);
			CreateButton(_browser.transform, _downloadsPage.transform, "BACK", new Vector3(0.092f, 0.066f, 0.007f), new Vector3(0.06f, 0.026f, 0.014f), DimCyan, ShowHub, 10f);
			CreateButton(_browser.transform, _downloadsPage.transform, "X", new Vector3(0.13f, 0.066f, 0.007f), new Vector3(0.024f, 0.026f, 0.014f), Red, Close, 17f);
			_downloadsSummary = CreateText(_downloadsPage.transform, "No downloads yet", new TabletRect(0f, 5f, 252f, 88f), 12f, Color.white, (TextAlignmentOptions)257, (FontStyles)0);
			CreateButton(_browser.transform, _downloadsPage.transform, "CANCEL ACTIVE", new Vector3(0f, -0.065f, 0.007f), new Vector3(0.13f, 0.03f, 0.014f), Amber, CancelActiveDownload, 10f);
			_downloadsPage.SetActive(false);
		}
	}

	private void BuildPackOptionsPage()
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Expected O, but got Unknown
		//IL_006e: 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_00b6: 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_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0171: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: 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_01d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d7: 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_0232: Unknown result type (might be due to invalid IL or missing references)
		//IL_0237: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)_browser == (Object)null) && !((Object)(object)_canvasRoot == (Object)null))
		{
			_packOptionsPage = new GameObject("Avatar Pack Options Glass Panel");
			_packOptionsPage.transform.SetParent(_canvasRoot.transform, false);
			CreateImage(_packOptionsPage.transform, "Pack Options Glass", new TabletRect(0f, 0f, 250f, 150f), new Color(0.008f, 0.03f, 0.043f, 0.9f));
			CreateText(_packOptionsPage.transform, "PACK OPTIONS", new TabletRect(0f, 53f, 190f, 25f), 17f, Color.white, (TextAlignmentOptions)514, (FontStyles)1);
			CreateButton(_browser.transform, _packOptionsPage.transform, "BACK", new Vector3(-0.076f, -0.052f, 0.007f), new Vector3(0.08f, 0.03f, 0.014f), DimCyan, ShowDetailPage, 11f);
			CreateButton(_browser.transform, _packOptionsPage.transform, "SETTINGS", new Vector3(0f, 0.012f, 0.007f), new Vector3(0.15f, 0.034f, 0.014f), DimCyan, ShowOptionsPage, 12f);
			CreateButton(_browser.transform, _packOptionsPage.transform, "DELETE PACK", new Vector3(0f, -0.029f, 0.007f), new Vector3(0.15f, 0.034f, 0.014f), Red, ShowDeleteConfirmation, 11f);
			CreateButton(_browser.transform, _packOptionsPage.transform, "X", new Vector3(0.108f, 0.054f, 0.007f), new Vector3(0.024f, 0.026f, 0.014f), Red, Close, 17f);
			_packOptionsPage.SetActive(false);
		}
	}

	private void BuildCompassPage()
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Expected O, but got Unknown
		//IL_006e: 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_00b6: 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_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_022b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0236: Unknown result type (might be due to invalid IL or missing references)
		//IL_026b: 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_02aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_03b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_03fa: Expected O, but got Unknown
		//IL_0437: Unknown result type (might be due to invalid IL or missing references)
		//IL_0461: Unknown result type (might be due to invalid IL or missing references)
		//IL_0467: Unknown result type (might be due to invalid IL or missing references)
		//IL_0492: Unknown result type (might be due to invalid IL or missing references)
		//IL_04ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_04d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_04db: 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_04f1: Expected O, but got Unknown
		//IL_052e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0558: 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_0569: Unknown result type (might be due to invalid IL or missing references)
		//IL_0574: 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_05b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_05be: Unknown result type (might be due to invalid IL or missing references)
		//IL_05f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0616: Unknown result type (might be due to invalid IL or missing references)
		//IL_0656: Unknown result type (might be due to invalid IL or missing references)
		//IL_066a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0670: Unknown result type (might be due to invalid IL or missing references)
		//IL_06b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_06cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_06d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0717: Unknown result type (might be due to invalid IL or missing references)
		//IL_072b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0744: 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_01d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)_browser == (Object)null) && !((Object)(object)_canvasRoot == (Object)null))
		{
			_compassPage = new GameObject("Projected Compass App");
			_compassPage.transform.SetParent(_canvasRoot.transform, false);
			CreateImage(_compassPage.transform, "Compact Compass Glass", new TabletRect(0f, 0f, 154f, 178f), new Color(0.004f, 0.022f, 0.032f, 0.9f));
			CreateText(_compassPage.transform, "COMPASS", new TabletRect(0f, 78f, 90f, 18f), 13f, Color.white, (TextAlignmentOptions)514, (FontStyles)1);
			CreateImage(_compassPage.transform, "Eleven Centimeter Dial", new TabletRect(0f, 7f, 110f, 110f), new Color(0.008f, 0.075f, 0.095f, 0.72f));
			for (int i = 0; i < 32; i++)
			{
				float num = (float)i / 32f * (float)Math.PI * 2f;
				float num2 = 51f;
				bool flag = i % 4 == 0;
				((Transform)((Graphic)CreateImage(_compassPage.transform, flag ? "Major Bearing Tick" : "Bearing Tick", new TabletRect(Mathf.Sin(num) * num2, 7f + Mathf.Cos(num) * num2, flag ? 2.2f : 1.2f, flag ? 8f : 5f), (Color)(flag ? Cyan : new Color(Cyan.r, Cyan.g, Cyan.b, 0.46f)))).rectTransform).localRotation = Quaternion.Euler(0f, 0f, (0f - num) * 57.29578f);
			}
			CreateText(_compassPage.transform, "N", new TabletRect(0f, 48f, 18f, 16f), 12f, Cyan, (TextAlignmentOptions)514, (FontStyles)1);
			CreateText(_compassPage.transform, "E", new TabletRect(44f, 7f, 18f, 16f), 10f, Color.white, (TextAlignmentOptions)514, (FontStyles)1);
			CreateText(_compassPage.transform, "S", new TabletRect(0f, -34f, 18f, 16f), 10f, Color.white, (TextAlignmentOptions)514, (FontStyles)1);
			CreateText(_compassPage.transform, "W", new TabletRect(-44f, 7f, 18f, 16f), 10f, Color.white, (TextAlignmentOptions)514, (FontStyles)1);
			(string, float, float)[] array = new(string, float, float)[4]
			{
				("NE", 31f, 36f),
				("SE", 31f, -22f),
				("SW", -31f, -22f),
				("NW", -31f, 36f)
			};
			for (int j = 0; j < array.Length; j++)
			{
				(string, float, float) tuple = array[j];
				CreateText(_compassPage.transform, tuple.Item1, new TabletRect(tuple.Item2, tuple.Item3, 17f, 10f), 6.5f, new Color(0.6f, 0.82f, 0.88f, 1f), (TextAlignmentOptions)514, (FontStyles)1);
			}
			GameObject val = new GameObject("Smooth North Needle");
			val.transform.SetParent(_compassPage.transform, false);
			_compassNeedle = val.AddComponent<RectTransform>();
			SetRect(_compassNeedle, new TabletRect(0f, 7f, 110f, 110f));
			CreateImage((Transform)(object)_compassNeedle, "North Arrow", new TabletRect(0f, 22f, 4f, 40f), Cyan);
			CreateImage((Transform)(object)_compassNeedle, "Needle Tail", new TabletRect(0f, -15f, 2f, 27f), new Color(0.2f, 0.48f, 0.54f, 0.8f));
			CreateImage((Transform)(object)_compassNeedle, "Needle Hub", new TabletRect(0f, 0f, 9f, 9f), Color.white);
			GameObject val2 = new GameObject("Slow Compass Detail Ring");
			val2.transform.SetParent(_compassPage.transform, false);
			_compassDecorRing = val2.AddComponent<RectTransform>();
			SetRect(_compassDecorRing, new TabletRect(0f, 7f, 116f, 116f));
			CreateImage((Transform)(object)_compassDecorRing, "Decor North Arc", new TabletRect(0f, 55f, 30f, 2f), new Color(Cyan.r, Cyan.g, Cyan.b, 0.7f));
			_compassHeadingText = CreateText(_compassPage.transform, "N  •  000°", new TabletRect(0f, -52f, 118f, 18f), 12f, Cyan, (TextAlignmentOptions)514, (FontStyles)1);
			_compassCalibrationText = CreateText(_compassPage.transform, "WORLD NORTH", new TabletRect(0f, -65f, 112f, 10f), 7f, new Color(0.62f, 0.78f, 0.84f, 1f), (TextAlignmentOptions)514, (FontStyles)1);
			CreateButton(_browser.transform, _compassPage.transform, "BACK", new Vector3(-0.058f, 0.077f, 0.007f), new Vector3(0.048f, 0.022f, 0.014f), DimCyan, ShowHub, 8f);
			CreateButton(_browser.transform, _compassPage.transform, "X", new Vector3(0.061f, 0.077f, 0.007f), new Vector3(0.026f, 0.022f, 0.014f), Red, Close, 14f);
			CreateButton(_browser.transform, _compassPage.transform, "SET NORTH", new Vector3(0f, -0.08f, 0.007f), new Vector3(0.084f, 0.024f, 0.014f), new Color(0.025f, 0.28f, 0.34f, 1f), SetCompassNorth, 8f);
			_compassPage.SetActive(false);
		}
	}

	private void StartHologramProjection()
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		UpdatePresentationMode();
		_lifecycleStartedAt = Time.unscaledTime;
		_lifecycle = (HologramDashboardLifecycle)((!_preferences.ReducedMotion) ? 1 : 2);
		_closeNotificationPending = false;
		if (!_preferences.HologramEffects)
		{
			if ((Object)(object)_canvasGroup != (Object)null)
			{
				_canvasGroup.alpha = 1f;
			}
			_hologramProjection?.Hide(immediate: true);
			_lifecycle = (HologramDashboardLifecycle)2;
			UpdateLifecycleVisuals();
		}
		else
		{
			if ((Object)(object)_canvasGroup != (Object)null)
			{
				_canvasGroup.alpha = (_preferences.ReducedMotion ? 1f : 0.02f);
			}
			_hologramProjection?.Show();
			UpdateLifecycleVisuals();
		}
	}

	private void UpdateHologramProjection(bool finalPoseOnly = false)
	{
		//IL_00d2: 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_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_0139: 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_0082: 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)
		if (_hologramProjection == null)
		{
			return;
		}
		GameObject? browser = _browser;
		object obj;
		if (browser == null || !browser.activeSelf)
		{
			GameObject? hub = _hub;
			obj = ((hub != null && hub.activeSelf) ? _hub.transform : null);
		}
		else
		{
			obj = _browser.transform;
		}
		Transform val = (Transform)obj;
		if (!_preferences.HologramEffects)
		{
			if ((Object)(object)_canvasGroup != (Object)null)
			{
				_canvasGroup.alpha = 1f;
			}
			if ((Object)(object)val != (Object)null)
			{
				val.localPosition = Vector3.zero;
				val.localScale = Vector3.one;
			}
			_hologramProjection.Hide(immediate: true);
			_hologramCarousel?.SetVisible(visible: false);
			return;
		}
		if ((Object)(object)val == (Object)null)
		{
			_hologramProjection.Hide();
			return;
		}
		_hologramProjection.SetAccent(Cyan);
		float num = AvatarUiScaleMath.ResolveMenuScale(CurrentPlayerHeight());
		if (!_projectionAnchors.TryGet(_preferences.UseLeftWrist, num, out var anchor))
		{
			_hologramProjection.BeginTrackingGrace();
			_hologramProjection.UpdateTrackingLost();
			return;
		}
		anchor = anchor with
		{
			EmitterPosition = anchor.EmitterPosition + anchor.ArmDirection * (_preferences.WatchForearmOffset * num)
		};
		_hologramProjection.UpdatePose(anchor, val, _preferences.ReducedMotion, _preferences.HologramBrightness, finalPoseOnly);
		if (!finalPoseOnly)
		{
			float num2 = (_preferences.ReducedMotion ? 1f : _hologramProjection.RevealProgress);
			if ((Object)(object)_canvasGroup != (Object)null)
			{
				_canvasGroup.alpha = Mathf.Lerp(0.02f, 1f, num2);
			}
		}
	}

	private void UpdateLifecycleVisuals()
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Invalid comparison between Unknown and I4
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Invalid comparison between Unknown and I4
		//IL_0024: 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_002a: 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_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Invalid comparison between Unknown and I4
		//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_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: Unknown result type (might be due to invalid IL or missing references)
		//IL_017b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_022d: Unknown result type (might be due to invalid IL or missing references)
		//IL_026b: Unknown result type (might be due to invalid IL or missing references)
		HologramDashboardProgress val3 = default(HologramDashboardProgress);
		if ((int)_lifecycle == 3)
		{
			HologramDashboardProgress val = HologramDashboardTimeline.Closing((double)(Time.unscaledTime - _lifecycleStartedAt), _preferences.ReducedMotion);
			HologramDashboardProgress val2 = val;
			((HologramDashboardProgress)(ref val2)).set_Rays(((HologramDashboardProgress)(ref val)).Rays * ((HologramDashboardProgress)(ref _closeVisualStart)).Rays);
			((HologramDashboardProgress)(ref val2)).set_Stage(((HologramDashboardProgress)(ref val)).Stage * ((HologramDashboardProgress)(ref _closeVisualStart)).Stage);
			((HologramDashboardProgress)(ref val2)).set_Wings(((HologramDashboardProgress)(ref val)).Wings * ((HologramDashboardProgress)(ref _closeVisualStart)).Wings);
			((HologramDashboardProgress)(ref val2)).set_Avatars(((HologramDashboardProgress)(ref val)).Avatars * ((HologramDashboardProgress)(ref _closeVisualStart)).Avatars);
			((HologramDashboardProgress)(ref val2)).set_Labels(((HologramDashboardProgress)(ref val)).Labels * ((HologramDashboardProgress)(ref _closeVisualStart)).Labels);
			val3 = val2;
		}
		else if ((int)_lifecycle == 1)
		{
			val3 = HologramDashboardTimeline.Opening((double)(Time.unscaledTime - _lifecycleStartedAt), _preferences.ReducedMotion);
			if (((HologramDashboardProgress)(ref val3)).ControlsReady)
			{
				_lifecycle = (HologramDashboardLifecycle)2;
			}
		}
		else
		{
			((HologramDashboardProgress)(ref val3))..ctor((HologramProjectionPhase)5, 1f, 1f, 1f, 1f, 1f, (int)_lifecycle == 2);
		}
		float num = Mathf.Lerp(0.015f, 1f, ((HologramDashboardProgress)(ref val3)).Stage);
		GameObject val4 = ActiveVisualPage();
		if ((Object)(object)val4 != (Object)null)
		{
			val4.transform.localScale = new Vector3(num, num, 1f);
			val4.transform.localPosition = new Vector3(0f, Mathf.Lerp(-54f, 0f, ((HologramDashboardProgress)(ref val3)).Stage), 0f);
		}
		if ((Object)(object)_hub != (Object)null && _hub.activeSelf)
		{
			_hub.transform.localScale = Vector3.one * num;
		}
		if ((Object)(object)_leftArcWing != (Object)null)
		{
			_leftArcWing.transform.localScale = new Vector3(Mathf.Max(0.02f, ((HologramDashboardProgress)(ref val3)).Wings), 1f, 1f);
		}
		if ((Object)(object)_rightArcWing != (Object)null)
		{
			_rightArcWing.transform.localScale = new Vector3(Mathf.Max(0.02f, ((HologramDashboardProgress)(ref val3)).Wings), 1f, 1f);
		}
		if ((Object)(object)_metadataArc != (Object)null)
		{
			_metadataArc.transform.localScale = new Vector3(Mathf.Max(0.02f, ((HologramDashboardProgress)(ref val3)).Labels), 1f, 1f);
		}
		if ((Object)(object)_canvasGroup != (Object)null)
		{
			_canvasGroup.alpha = Mathf.Clamp01(Mathf.Max(((HologramDashboardProgress)(ref val3)).Stage * 0.82f, ((HologramDashboardProgress)(ref val3)).Labels));
		}
		BoneHubHologramCarousel? hologramCarousel = _hologramCarousel;
		if (hologram

Mods\WristHub.SDK.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("WristHub")]
[assembly: InternalsVisibleTo("BoneHub.Tests")]
[assembly: TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]
[assembly: AssemblyCompany("WristHub.SDK")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Stable public app and control API for WristHubOS.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("4.0.1")]
[assembly: AssemblyProduct("WristHub.SDK")]
[assembly: AssemblyTitle("WristHub.SDK")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace WristHub.SDK;

public static class WristHubApi
{
	public const int ApiVersion = 2;

	private static readonly object Sync = new object();

	private static readonly Dictionary<string, WristHubApp> Apps = new Dictionary<string, WristHubApp>(StringComparer.OrdinalIgnoreCase);

	private static IWristHubHost? _host;

	public static bool IsWristHubAvailable
	{
		get
		{
			lock (Sync)
			{
				return _host != null;
			}
		}
	}

	public static WristHubRuntimeInfo Runtime
	{
		get
		{
			lock (Sync)
			{
				return _host?.Runtime ?? WristHubRuntimeInfo.Unavailable;
			}
		}
	}

	public static int RegisteredAppCount
	{
		get
		{
			lock (Sync)
			{
				return Apps.Count;
			}
		}
	}

	public static event Action? AppsChanged;

	public static event Action<WristHubRuntimeInfo>? RuntimeChanged;

	public static WristHubApp RegisterApp(WristHubAppDefinition definition)
	{
		ArgumentNullException.ThrowIfNull(definition, "definition");
		definition.Validate();
		WristHubApp wristHubApp;
		lock (Sync)
		{
			if (Apps.ContainsKey(definition.Id))
			{
				throw new InvalidOperationException("A WristHub app with id '" + definition.Id + "' is already registered.");
			}
			wristHubApp = new WristHubApp(definition, OnAppChanged, Unregister);
			Apps.Add(definition.Id, wristHubApp);
		}
		SafeRaise(WristHubApi.AppsChanged);
		return wristHubApp;
	}

	public static WristHubApp RegisterApp(string id, string name, string author, string version, string description = "", string icon = "APP")
	{
		return RegisterApp(new WristHubAppDefinition(id, name, author, version, description, icon));
	}

	public static IReadOnlyList<WristHubApp> GetRegisteredApps()
	{
		lock (Sync)
		{
			return new ReadOnlyCollection<WristHubApp>(Apps.Values.OrderBy<WristHubApp, string>((WristHubApp app) => app.Name, StringComparer.OrdinalIgnoreCase).ToArray());
		}
	}

	public static void Notify(string appId, string message, WristHubNotificationKind kind = WristHubNotificationKind.Info)
	{
		if (string.IsNullOrWhiteSpace(message))
		{
			return;
		}
		IWristHubHost host;
		lock (Sync)
		{
			host = _host;
		}
		try
		{
			host?.Notify(appId?.Trim() ?? string.Empty, message.Trim(), kind);
		}
		catch
		{
		}
	}

	public static void OpenApp(string appId)
	{
		IWristHubHost host;
		lock (Sync)
		{
			host = _host;
		}
		try
		{
			host?.RequestOpen(appId?.Trim() ?? string.Empty);
		}
		catch
		{
		}
	}

	internal static void AttachHost(IWristHubHost host)
	{
		ArgumentNullException.ThrowIfNull(host, "host");
		lock (Sync)
		{
			_host = host;
		}
		SafeRaise(WristHubApi.RuntimeChanged, host.Runtime);
		SafeRaise(WristHubApi.AppsChanged);
	}

	internal static void DetachHost(IWristHubHost host)
	{
		lock (Sync)
		{
			if (_host != host)
			{
				return;
			}
			_host = null;
		}
		SafeRaise(WristHubApi.RuntimeChanged, WristHubRuntimeInfo.Unavailable);
	}

	private static void Unregister(WristHubApp app)
	{
		lock (Sync)
		{
			if (Apps.TryGetValue(app.Id, out WristHubApp value) && value == app)
			{
				Apps.Remove(app.Id);
			}
		}
		SafeRaise(WristHubApi.AppsChanged);
	}

	private static void OnAppChanged()
	{
		SafeRaise(WristHubApi.AppsChanged);
	}

	private static void SafeRaise(Action? callback)
	{
		if (callback == null)
		{
			return;
		}
		Delegate[] invocationList = callback.GetInvocationList();
		for (int i = 0; i < invocationList.Length; i++)
		{
			Action action = (Action)invocationList[i];
			try
			{
				action();
			}
			catch
			{
			}
		}
	}

	private static void SafeRaise<T>(Action<T>? callback, T value)
	{
		if (callback == null)
		{
			return;
		}
		Delegate[] invocationList = callback.GetInvocationList();
		for (int i = 0; i < invocationList.Length; i++)
		{
			Action<T> action = (Action<T>)invocationList[i];
			try
			{
				action(value);
			}
			catch
			{
			}
		}
	}
}
internal interface IWristHubHost
{
	WristHubRuntimeInfo Runtime { get; }

	void Notify(string appId, string message, WristHubNotificationKind kind);

	void RequestOpen(string appId);
}
public readonly record struct WristHubRuntimeInfo(bool Available, bool IsQuest, bool IsFusionConnected, string Theme, int ApiVersion)
{
	public static WristHubRuntimeInfo Unavailable => new WristHubRuntimeInfo(Available: false, IsQuest: false, IsFusionConnected: false, "Cyan", 2);
}
public enum WristHubNotificationKind
{
	Info,
	Success,
	Warning,
	Error
}
public enum WristHubAccent
{
	Theme,
	Neutral,
	Success,
	Warning,
	Danger
}
public enum WristHubCommunityCategory
{
	Utility,
	Gameplay,
	Social,
	Accessibility,
	Developer,
	Other
}
public sealed record WristHubCommunityProfile(WristHubCommunityCategory Category, string Summary, string Homepage = "", string SupportContact = "", IReadOnlyList<string>? Tags = null)
{
	internal void Validate()
	{
		WristHubAppDefinition.ValidateText(Summary, "Summary", 160);
		ValidateOptional(Homepage, "Homepage", 240);
		ValidateOptional(SupportContact, "SupportContact", 96);
		IReadOnlyList<string> tags = Tags;
		if (tags != null && tags.Count > 8)
		{
			throw new ArgumentOutOfRangeException("Tags");
		}
		if (Tags == null)
		{
			return;
		}
		foreach (string tag in Tags)
		{
			WristHubAppDefinition.ValidateText(tag, "Tags", 24);
		}
	}

	private static void ValidateOptional(string? value, string name, int length)
	{
		if (!string.IsNullOrEmpty(value) && value.Length > length)
		{
			throw new ArgumentOutOfRangeException(name);
		}
	}
}
public sealed record WristHubAppDefinition(string Id, string Name, string Author, string Version, string Description = "", string Icon = "APP")
{
	internal void Validate()
	{
		ValidateToken(Id, "Id", 64);
		ValidateText(Name, "Name", 48);
		ValidateText(Author, "Author", 48);
		ValidateText(Version, "Version", 24);
		string description = Description;
		if (description != null && description.Length > 240)
		{
			throw new ArgumentOutOfRangeException("Description");
		}
		string icon = Icon;
		if (icon != null && icon.Length > 8)
		{
			throw new ArgumentOutOfRangeException("Icon");
		}
	}

	internal static void ValidateToken(string value, string name, int length)
	{
		ValidateText(value, name, length);
		if (value.Any(delegate(char character)
		{
			bool flag = char.IsLetterOrDigit(character);
			if (!flag)
			{
				bool flag2 = ((character == '-' || character == '.' || character == '_') ? true : false);
				flag = flag2;
			}
			return !flag;
		}))
		{
			throw new ArgumentException("Use only letters, numbers, dots, dashes, and underscores.", name);
		}
	}

	internal static void ValidateText(string value, string name, int length)
	{
		if (string.IsNullOrWhiteSpace(value))
		{
			throw new ArgumentException("Value is required.", name);
		}
		if (value.Length > length)
		{
			throw new ArgumentOutOfRangeException(name);
		}
	}
}
public sealed class WristHubApp : IDisposable
{
	private readonly Action _changed;

	private readonly Action<WristHubApp> _dispose;

	private int _disposed;

	public string Id { get; }

	public string Name { get; }

	public string Author { get; }

	public string Version { get; }

	public string Description { get; }

	public string Icon { get; }

	public WristHubPage Root { get; }

	public bool IsDisposed => _disposed != 0;

	public Func<bool>? IsAvailable { get; set; }

	public Func<int>? NotificationCount { get; set; }

	public WristHubCommunityProfile? Community { get; private set; }

	public bool Available
	{
		get
		{
			try
			{
				return !IsDisposed && (IsAvailable?.Invoke() ?? true);
			}
			catch
			{
				return false;
			}
		}
	}

	public int Notifications
	{
		get
		{
			try
			{
				return Math.Clamp(NotificationCount?.Invoke() ?? 0, 0, 99);
			}
			catch
			{
				return 0;
			}
		}
	}

	internal WristHubApp(WristHubAppDefinition definition, Action changed, Action<WristHubApp> dispose)
	{
		Id = definition.Id.Trim();
		Name = definition.Name.Trim();
		Author = definition.Author.Trim();
		Version = definition.Version.Trim();
		Description = definition.Description?.Trim() ?? string.Empty;
		Icon = (string.IsNullOrWhiteSpace(definition.Icon) ? "APP" : definition.Icon.Trim());
		_changed = changed;
		_dispose = dispose;
		Root = new WristHubPage("root", Name, null, changed);
	}

	public WristHubApp PublishToCommunity(WristHubCommunityProfile profile)
	{
		ArgumentNullException.ThrowIfNull(profile, "profile");
		profile.Validate();
		Community = profile with
		{
			Summary = profile.Summary.Trim(),
			Homepage = (profile.Homepage?.Trim() ?? string.Empty),
			SupportContact = (profile.SupportContact?.Trim() ?? string.Empty),
			Tags = profile.Tags?.Select((string tag) => tag.Trim()).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray()
		};
		_changed();
		return this;
	}

	public void RemoveFromCommunity()
	{
		if (!(Community == null))
		{
			Community = null;
			_changed();
		}
	}

	public void Refresh()
	{
		_changed();
	}

	public void Dispose()
	{
		if (Interlocked.Exchange(ref _disposed, 1) == 0)
		{
			_dispose(this);
		}
	}
}
public sealed class WristHubPage
{
	private readonly object _sync = new object();

	private readonly List<WristHubControl> _controls = new List<WristHubControl>();

	private readonly Action _changed;

	public string Id { get; }

	public string Title { get; set; }

	public WristHubPage? Parent { get; }

	public IReadOnlyList<WristHubControl> Controls
	{
		get
		{
			lock (_sync)
			{
				return _controls.ToArray();
			}
		}
	}

	internal WristHubPage(string id, string title, WristHubPage? parent, Action changed)
	{
		WristHubAppDefinition.ValidateToken(id, "id", 64);
		WristHubAppDefinition.ValidateText(title, "title", 64);
		Id = id.Trim();
		Title = title.Trim();
		Parent = parent;
		_changed = changed;
	}

	public WristHubPage AddPage(string id, string title, string description = "")
	{
		WristHubPage wristHubPage = new WristHubPage(id, title, this, _changed);
		Add(new WristHubPageLink(id, title, wristHubPage, description));
		return wristHubPage;
	}

	public WristHubAction AddButton(string id, string label, Action action, string description = "", WristHubAccent accent = WristHubAccent.Theme)
	{
		return Add(new WristHubAction(id, label, action, description, accent));
	}

	public WristHubToggle AddToggle(string id, string label, Func<bool> read, Action<bool> write, string description = "")
	{
		return Add(new WristHubToggle(id, label, read, write, description));
	}

	public WristHubSlider AddSlider(string id, string label, double minimum, double maximum, double step, Func<double> read, Action<double> write, string description = "")
	{
		return Add(new WristHubSlider(id, label, minimum, maximum, step, read, write, description));
	}

	public WristHubChoice AddChoice(string id, string label, IReadOnlyList<string> choices, Func<int> read, Action<int> write, string description = "")
	{
		return Add(new WristHubChoice(id, label, choices, read, write, description));
	}

	public WristHubTextInput AddText(string id, string label, Func<string> read, Action<string> write, string description = "")
	{
		return Add(new WristHubTextInput(id, label, read, write, description));
	}

	public WristHubColorInput AddColor(string id, string label, Func<WristHubColorValue> read, Action<WristHubColorValue> write, IReadOnlyList<WristHubColorValue>? palette = null, string description = "")
	{
		return Add(new WristHubColorInput(id, label, read, write, palette, description));
	}

	public WristHubLabel AddLabel(string id, string label, Func<string> read, string description = "")
	{
		return Add(new WristHubLabel(id, label, read, description));
	}

	public T Add<T>(T control) where T : WristHubControl
	{
		ArgumentNullException.ThrowIfNull(control, "control");
		lock (_sync)
		{
			if (_controls.Any((WristHubControl existing) => existing.Id.Equals(control.Id, StringComparison.OrdinalIgnoreCase)))
			{
				throw new InvalidOperationException($"Control id '{control.Id}' already exists on page '{Id}'.");
			}
			_controls.Add(control);
		}
		_changed();
		return control;
	}

	public bool Remove(string id)
	{
		bool flag;
		lock (_sync)
		{
			flag = _controls.RemoveAll((WristHubControl control) => control.Id.Equals(id, StringComparison.OrdinalIgnoreCase)) > 0;
		}
		if (flag)
		{
			_changed();
		}
		return flag;
	}
}
public abstract class WristHubControl
{
	public string Id { get; }

	public string Label { get; set; }

	public string Description { get; set; }

	public WristHubAccent Accent { get; set; }

	public Func<bool>? IsEnabled { get; set; }

	public bool Enabled
	{
		get
		{
			try
			{
				return IsEnabled?.Invoke() ?? true;
			}
			catch
			{
				return false;
			}
		}
	}

	protected WristHubControl(string id, string label, string description, WristHubAccent accent = WristHubAccent.Theme)
	{
		WristHubAppDefinition.ValidateToken(id, "id", 64);
		WristHubAppDefinition.ValidateText(label, "label", 64);
		Id = id.Trim();
		Label = label.Trim();
		Description = description?.Trim() ?? string.Empty;
		Accent = accent;
	}
}
public sealed class WristHubAction : WristHubControl
{
	private readonly Action _action;

	internal WristHubAction(string id, string label, Action action, string description, WristHubAccent accent)
		: base(id, label, description, accent)
	{
		_action = action ?? throw new ArgumentNullException("action");
	}

	public void Invoke()
	{
		_action();
	}
}
public sealed class WristHubToggle : WristHubControl
{
	private readonly Func<bool> _read;

	private readonly Action<bool> _write;

	public bool Value => _read();

	internal WristHubToggle(string id, string label, Func<bool> read, Action<bool> write, string description)
		: base(id, label, description)
	{
		_read = read;
		_write = write;
	}

	public void Toggle()
	{
		_write(!Value);
	}
}
public sealed class WristHubSlider : WristHubControl
{
	private readonly Func<double> _read;

	private readonly Action<double> _write;

	public double Minimum { get; }

	public double Maximum { get; }

	public double Step { get; }

	public double Value => Math.Clamp(_read(), Minimum, Maximum);

	internal WristHubSlider(string id, string label, double min, double max, double step, Func<double> read, Action<double> write, string description)
		: base(id, label, description)
	{
		if (!double.IsFinite(min) || !double.IsFinite(max) || max <= min)
		{
			throw new ArgumentOutOfRangeException("max");
		}
		if (!double.IsFinite(step) || step <= 0.0)
		{
			throw new ArgumentOutOfRangeException("step");
		}
		Minimum = min;
		Maximum = max;
		Step = step;
		_read = read;
		_write = write;
	}

	public void Adjust(int direction)
	{
		_write(Math.Clamp(Value + (double)Math.Sign(direction) * Step, Minimum, Maximum));
	}
}
public sealed class WristHubChoice : WristHubControl
{
	private readonly Func<int> _read;

	private readonly Action<int> _write;

	public IReadOnlyList<string> Choices { get; }

	public int Index => Math.Clamp(_read(), 0, Choices.Count - 1);

	public string Value => Choices[Index];

	internal WristHubChoice(string id, string label, IReadOnlyList<string> choices, Func<int> read, Action<int> write, string description)
		: base(id, label, description)
	{
		Choices = (from choice in choices?.Where((string choice) => !string.IsNullOrWhiteSpace(choice))
			select choice.Trim()).ToArray() ?? throw new ArgumentNullException("choices");
		if (Choices.Count == 0)
		{
			throw new ArgumentException("At least one choice is required.", "choices");
		}
		_read = read;
		_write = write;
	}

	public void Adjust(int direction)
	{
		_write((Index + Math.Sign(direction) + Choices.Count) % Choices.Count);
	}
}
public sealed class WristHubTextInput : WristHubControl
{
	private readonly Func<string> _read;

	private readonly Action<string> _write;

	public string Value => _read() ?? string.Empty;

	internal WristHubTextInput(string id, string label, Func<string> read, Action<string> write, string description)
		: base(id, label, description)
	{
		_read = read;
		_write = write;
	}

	public void Set(string value)
	{
		_write(value ?? string.Empty);
	}
}
public readonly record struct WristHubColorValue(byte Red, byte Green, byte Blue, byte Alpha = byte.MaxValue)
{
	public string Hex => $"#{Red:X2}{Green:X2}{Blue:X2}{Alpha:X2}";

	public static WristHubColorValue Cyan => new WristHubColorValue(38, 221, byte.MaxValue);

	public static WristHubColorValue Graphite => new WristHubColorValue(173, 184, 194);

	public static WristHubColorValue Crimson => new WristHubColorValue(byte.MaxValue, 61, 79);

	public static WristHubColorValue Emerald => new WristHubColorValue(61, 240, 140);

	public static WristHubColorValue Violet => new WristHubColorValue(173, 107, byte.MaxValue);

	public static WristHubColorValue Amber => new WristHubColorValue(byte.MaxValue, 168, 46);
}
public sealed class WristHubColorInput : WristHubControl
{
	private static readonly WristHubColorValue[] DefaultPalette = new WristHubColorValue[6]
	{
		WristHubColorValue.Cyan,
		WristHubColorValue.Graphite,
		WristHubColorValue.Crimson,
		WristHubColorValue.Emerald,
		WristHubColorValue.Violet,
		WristHubColorValue.Amber
	};

	private readonly Func<WristHubColorValue> _read;

	private readonly Action<WristHubColorValue> _write;

	public IReadOnlyList<WristHubColorValue> Palette { get; }

	public WristHubColorValue Value => _read();

	internal WristHubColorInput(string id, string label, Func<WristHubColorValue> read, Action<WristHubColorValue> write, IReadOnlyList<WristHubColorValue>? palette, string description)
		: base(id, label, description)
	{
		_read = read;
		_write = write;
		Palette = ((palette != null && palette.Count > 0) ? palette.ToArray() : DefaultPalette);
	}

	public void Set(WristHubColorValue value)
	{
		_write(value);
	}

	public void Adjust(int direction)
	{
		WristHubColorValue value = Value;
		int val = -1;
		for (int i = 0; i < Palette.Count; i++)
		{
			if (Palette[i].Equals(value))
			{
				val = i;
				break;
			}
		}
		val = (Math.Max(0, val) + Math.Sign(direction) + Palette.Count) % Palette.Count;
		_write(Palette[val]);
	}
}
public sealed class WristHubLabel : WristHubControl
{
	private readonly Func<string> _read;

	public string Value => _read() ?? string.Empty;

	internal WristHubLabel(string id, string label, Func<string> read, string description)
		: base(id, label, description, WristHubAccent.Neutral)
	{
		_read = read;
	}
}
public sealed class WristHubPageLink : WristHubControl
{
	public WristHubPage Page { get; }

	internal WristHubPageLink(string id, string label, WristHubPage page, string description)
		: base(id, label, description)
	{
		Page = page;
	}
}