Decompiled source of WristHub v2.0.1
Mods\WristHub.Core.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
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; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("WristHub.Core")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("2.0.1.0")] [assembly: AssemblyInformationalVersion("2.0.1")] [assembly: AssemblyProduct("WristHub.Core")] [assembly: AssemblyTitle("WristHub.Core")] [assembly: AssemblyVersion("2.0.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace 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); } 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 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; } _state.Preferences.WatchScale = Math.Clamp(_state.Preferences.WatchScale, 0.65f, 1.6f); _state.Preferences.DwellSeconds = Math.Clamp(_state.Preferences.DwellSeconds, 1.5f, 1.5f); _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.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); await _stateStore.SaveAsync(_state, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } finally { _stateGate.Release(); } } 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; } = 1.5f; public float OutwardOffset { get; set; } = 0.029f; public float FingerOffset { get; set; } = -0.018f; 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 BrowserStartupMode AvatarBrowserStartupMode { get; set; } = BrowserStartupMode.AllInstalled; public AvatarBrowseScope LastAvatarBrowseScope { get; set; } } 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, 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 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 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 VisualHeightMeters, bool Valid) { public string DisplayText { get { if (!Valid || !float.IsFinite(VisualHeightMeters) || !(VisualHeightMeters > 0f)) { return "Height unavailable"; } if (VisualHeightMeters < 10f) { return $"{VisualHeightMeters:0.00} m"; } return $"{VisualHeightMeters:0.0} m"; } } public string GameDisplayText { get { if (!Valid) { return "Game height unavailable"; } return "Game 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.IsUsableHeight(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 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 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 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 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; } = true; public double NotBeforeSeconds { get; private set; } 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 TouchBounds(float CenterX, float CenterY, float CenterZ, float HalfX, float HalfY, float HalfZ) { public bool Contains(float x, float y, float z) { if (Math.Abs(x - CenterX) <= Math.Max(0f, HalfX) && Math.Abs(y - CenterY) <= Math.Max(0f, HalfY)) { return Math.Abs(z - CenterZ) <= Math.Max(0f, HalfZ); } return false; } } public sealed class TouchDebounce { public bool Latched { get; private set; } public bool Update(bool touching) { if (!touching) { Latched = false; return false; } if (Latched) { return false; } Latched = true; return true; } } public sealed class ErrorLogThrottle { private readonly Dictionary<string, double> _lastLogged = new Dictionary<string, double>(StringComparer.Ordinal); public bool ShouldLog(string signature, double nowSeconds, double intervalSeconds = 5.0) { if (string.IsNullOrWhiteSpace(signature)) { signature = "unknown"; } if (_lastLogged.TryGetValue(signature, out var value) && nowSeconds - value < Math.Max(0.1, intervalSeconds)) { return false; } _lastLogged[signature] = nowSeconds; if (_lastLogged.Count > 64) { double cutoff = nowSeconds - Math.Max(30.0, intervalSeconds * 4.0); string[] array = (from item in _lastLogged where item.Value < cutoff select item.Key).ToArray(); foreach (string key in array) { _lastLogged.Remove(key); } } return true; } } public static class PreviewShellRecovery { public static bool RequiresRebuild(bool shellAlive, bool fallbackAlive) { if (shellAlive) { return !fallbackAlive; } return true; } public static bool ShouldSchedule(string? selectedBarcode, string? loadedBarcode, string? requestedBarcode) { if (string.IsNullOrWhiteSpace(selectedBarcode)) { return false; } if (!string.Equals(selectedBarcode, loadedBarcode, StringComparison.OrdinalIgnoreCase)) { return !string.Equals(selectedBarcode, requestedBarcode, StringComparison.OrdinalIgnoreCase); } return false; } } public readonly record struct WristActivationState(bool Armed, double RingProgress, bool Activate); public static class WristActivationMath { public const double ActivationSeconds = 1.5; public static WristActivationState Evaluate(double observedSeconds, double armSeconds) { double num = Math.Max(0.0, observedSeconds); double num2 = ((double.IsFinite(armSeconds) && armSeconds > 0.0) ? armSeconds : 1.5); double num3 = Math.Clamp(num / num2, 0.0, 1.0); return new WristActivationState(num > 0.0, num3, num3 >= 1.0); } } public sealed class WristRearmGate { public const double RequiredLookAwaySeconds = 0.25; private double _lookAwaySeconds; public bool RequiresLookAway { get; private set; } public void RequireLookAway() { RequiresLookAway = true; _lookAwaySeconds = 0.0; } public bool Update(bool observed, double deltaSeconds) { if (!RequiresLookAway) { return true; } if (observed) { _lookAwaySeconds = 0.0; } else { _lookAwaySeconds += Math.Max(0.0, deltaSeconds); } if (_lookAwaySeconds >= 0.25) { RequiresLookAway = false; _lookAwaySeconds = 0.0; } return !RequiresLookAway; } } } namespace BoneHub.Installation { public static class NetworkerRegistrationWriter { private sealed record PalletInfo(string Barcode, string Version, string Author); private const long GameId = 3809L; private static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions { WriteIndented = true }; public static async Task<ModIoSharingMetadata> WriteAsync(ModIoMod mod, ModIoFile installedFile, ModIoFileResolver.PlatformFiles platformFiles, IReadOnlyList<string> installedDirectories, string modsDirectory, CancellationToken cancellationToken = default(CancellationToken)) { string root = Path.GetFullPath(modsDirectory).TrimEnd(Path.DirectorySeparatorChar); List<string> sidecars = new List<string>(); List<string> pallets = new List<string>(); foreach (string installedDirectory in installedDirectories) { string package = ValidateChild(installedDirectory, root); await WriteModInfoAsync(package, mod, installedFile, platformFiles, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); foreach (string palletPath in PalletManifestLocator.FindAll(package, SearchOption.TopDirectoryOnly)) { cancellationToken.ThrowIfCancellationRequested(); PalletInfo pallet = await ReadPalletAsync(palletPath, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); string manifestPath = Path.Combine(root, SafeFileName(pallet.Barcode) + ".manifest"); string catalogPath = Directory.EnumerateFiles(package, "catalog_*.json", SearchOption.TopDirectoryOnly).FirstOrDefault() ?? Path.Combine(package, "catalog_" + pallet.Barcode + ".json"); JsonObject value = BuildManifest(pallet, palletPath, catalogPath, mod, installedFile, platformFiles); await WriteAtomicAsync(manifestPath, value, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); sidecars.Add(manifestPath); pallets.Add(pallet.Barcode); } } string warning = ((platformFiles.Windows == null || platformFiles.Android == null) ? ("This pack has no active file for " + ((platformFiles.Windows == null) ? "PC" : "Quest") + "; Fusion sharing is disabled for that platform.") : null); return new ModIoSharingMetadata { RegistrationVersion = 3, ModId = mod.Id, ModSlug = NonEmpty(mod.NameId, SafeSlug(mod.Name, mod.Id)), WindowsFileId = platformFiles.Windows?.Id, AndroidFileId = platformFiles.Android?.Id, SidecarManifestPaths = sidecars, PalletBarcodes = pallets, Warning = warning }; } private static JsonObject BuildManifest(PalletInfo pallet, string palletPath, string catalogPath, ModIoMod mod, ModIoFile installedFile, ModIoFileResolver.PlatformFiles files) { JsonObject jsonObject = new JsonObject(); int num = 3; string text = null; string text2 = null; JsonObject jsonObject2 = new JsonObject(); if (files.Windows != null) { text = num++.ToString(); jsonObject["pc"] = Ref(text, "mod-target-modio#0"); } if (files.Android != null) { text2 = num++.ToString(); jsonObject["android"] = Ref(text2, "mod-target-modio#0"); } string text3 = text ?? text2; if (text3 != null) { jsonObject[NetworkerKey(mod, installedFile)] = Ref(text3, "mod-target-modio#0"); } string text4 = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString(); jsonObject2["1"] = new JsonObject { ["palletBarcode"] = pallet.Barcode, ["palletPath"] = Path.GetFullPath(palletPath), ["catalogPath"] = Path.GetFullPath(catalogPath), ["version"] = NonEmpty(pallet.Version, "0.0.0"), ["installedDate"] = text4, ["updateDate"] = text4, ["modListing"] = Ref("2", "mod-listing#0"), ["active"] = true, ["isa"] = Type("pallet-manifest#0") }; jsonObject2["2"] = new JsonObject { ["barcode"] = pallet.Barcode, ["title"] = NonEmpty(mod.NameId, SafeSlug(mod.Name, mod.Id)), ["description"] = NonEmpty(mod.Summary, string.Empty), ["author"] = NonEmpty(mod.Author.DisplayName, NonEmpty(pallet.Author, "Unknown")), ["version"] = NonEmpty(installedFile.Version, "0.0.0"), ["thumbnailUrl"] = NonEmpty(mod.Logo.Thumbnail, string.Empty), ["targets"] = jsonObject, ["isa"] = Type("mod-listing#0") }; if (files.Windows != null && text != null) { jsonObject2[text] = Target(mod.Id, files.Windows.Id); } if (files.Android != null && text2 != null) { jsonObject2[text2] = Target(mod.Id, files.Android.Id); } return new JsonObject { ["version"] = 2, ["root"] = Ref("1", "pallet-manifest#0"), ["objects"] = jsonObject2 }; } private static async Task WriteModInfoAsync(string package, ModIoMod mod, ModIoFile installedFile, ModIoFileResolver.PlatformFiles files, CancellationToken cancellationToken) { JsonArray value = new JsonArray(((IEnumerable<ModIoTag>)mod.Tags).Select((Func<ModIoTag, JsonNode>)((ModIoTag tag) => Sanitize(tag.Name))).ToArray()); JsonObject value2 = new JsonObject { ["isValidMod"] = true, ["downloading"] = false, ["mature"] = mod.MaturityOption != 0, ["isTracked"] = true, ["modId"] = NonEmpty(mod.NameId, SafeSlug(mod.Name, mod.Id)), ["thumbnailLink"] = NonEmpty(mod.Logo.Thumbnail, string.Empty), ["modName"] = NonEmpty(mod.Name, "Avatar Pack"), ["modSummary"] = NonEmpty(mod.Summary, string.Empty), ["fileName"] = NonEmpty(installedFile.Filename, "modfile.zip"), ["windowsDownloadLink"] = files.Windows?.Download.BinaryUrl ?? "nothing", ["androidDownloadLink"] = files.Android?.Download.BinaryUrl ?? "nothing", ["directDownloadLink"] = "nothing", ["modDownloadPercentage"] = 100, ["numericalId"] = mod.Id.ToString(), ["version"] = NonEmpty(installedFile.Version, "0.0.0"), ["tags"] = value, ["author"] = NonEmpty(mod.Author.DisplayName, "Unknown"), ["structureVersion"] = 4, ["temp"] = false }; await WriteAtomicAsync(Path.Combine(package, "modinfo.json"), value2, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } private static async Task<PalletInfo> ReadPalletAsync(string path, CancellationToken cancellationToken) { JsonNode? obj = JsonNode.Parse(await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)) ?? throw new InvalidDataException("The pallet manifest is empty."); string propertyName = obj["root"]?["ref"]?.GetValue<string>() ?? "1"; JsonNode jsonNode = obj["objects"]?[propertyName] ?? throw new InvalidDataException("The pallet root is missing."); string obj2 = jsonNode["barcode"]?.GetValue<string>()?.Trim(); if (string.IsNullOrWhiteSpace(obj2)) { throw new InvalidDataException("The pallet barcode is missing."); } return new PalletInfo(obj2, jsonNode["version"]?.GetValue<string>() ?? "0.0.0", jsonNode["author"]?.GetValue<string>() ?? "Unknown"); } private static string NetworkerKey(ModIoMod mod, ModIoFile file) { string[] array = (from tag in mod.Tags select Sanitize(tag.Name) into tag where tag.Length > 0 select tag).ToArray(); return $"networker;{mod.MaturityOption != 0};False;{file.FileSize};{Sanitize(file.Filename)};4;{Sanitize(mod.Name)};{array.Length}" + ((array.Length == 0) ? string.Empty : (";" + string.Join(";", array))); } private static JsonObject Ref(string value, string type) { return new JsonObject { ["ref"] = value, ["type"] = type }; } private static JsonObject Type(string type) { return new JsonObject { ["type"] = type }; } private static JsonObject Target(long modId, long fileId) { return new JsonObject { ["thumbnailOverride"] = string.Empty, ["gameId"] = 3809L, ["modId"] = modId, ["modfileId"] = fileId, ["isa"] = Type("mod-target-modio#0") }; } private static string NonEmpty(string? value, string fallback) { if (!string.IsNullOrWhiteSpace(value)) { return value.Trim(); } return fallback; } private static string SafeSlug(string? name, long modId) { string text = new string((from character in (name ?? string.Empty).Trim().ToLowerInvariant() select (!char.IsLetterOrDigit(character)) ? '-' : character).ToArray()).Trim('-'); if (text.Length != 0) { return text; } return "wristhub-mod-" + modId; } private static string Sanitize(string? value) { return (value ?? string.Empty).Replace(';', ' ').Trim(); } private static string SafeFileName(string value) { return string.Concat(value.Select((char character) => (!Path.GetInvalidFileNameChars().Contains(character)) ? character : '_')); } private static string ValidateChild(string path, string root) { string text = Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar); if (!text.StartsWith(root + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) { throw new InvalidDataException("An installed directory is outside BONELAB's Mods directory."); } return text; } private static async Task WriteAtomicAsync(string path, JsonNode value, CancellationToken cancellationToken) { string temp = path + ".wristhub.tmp"; try { await File.WriteAllTextAsync(temp, value.ToJsonString(JsonOptions), cancellationToken).ConfigureAwait(continueOnCapturedContext: false); File.Move(temp, path, overwrite: true); } finally { if (File.Exists(temp)) { File.Delete(temp); } } } } public static class PalletManifestLocator { public static bool IsManifestFileName(string? fileName) { if (string.IsNullOrWhiteSpace(fileName)) { return false; } if (!fileName.Equals("pallet.json", StringComparison.OrdinalIgnoreCase)) { return fileName.EndsWith(".pallet.json", StringComparison.OrdinalIgnoreCase); } return true; } public static IReadOnlyList<string> FindAll(string directory, SearchOption searchOption) { if (!Directory.Exists(directory)) { return Array.Empty<string>(); } return (from path in Directory.EnumerateFiles(directory, "*.json", searchOption) where IsManifestFileName(Path.GetFileName(path)) select path).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray(); } public static bool HasManifest(string directory) { return FindAll(directory, SearchOption.TopDirectoryOnly).Count > 0; } } public sealed class SafeModInstaller { private static readonly HashSet<string> BlockedExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".exe", ".dll", ".com", ".bat", ".cmd", ".ps1", ".vbs", ".js", ".jar", ".sh", ".so", ".dylib" }; private readonly BoneHubOptions _options; public SafeModInstaller(BoneHubOptions options) { _options = options; } public async Task<IReadOnlyList<string>> InstallAsync(string zipPath, long modId, CancellationToken cancellationToken = default(CancellationToken)) { string modsRoot = EnsureDirectory(_options.ModsDirectory); string path = $"{modId}-{Guid.NewGuid():N}"; string? directoryName = Path.GetDirectoryName(modsRoot.TrimEnd(Path.DirectorySeparatorChar)); string text = Path.Combine(directoryName, ".bonehub-staging"); string text2 = Path.Combine(directoryName, ".bonehub-backups"); RecoverInterruptedTransactions(modsRoot, text2); DeleteStaleStaging(text); string stagingRoot = Path.Combine(text, path); string backupRoot = Path.Combine(text2, path); Directory.CreateDirectory(stagingRoot); try { await ExtractValidatedAsync(zipPath, stagingRoot, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); List<string> list = FindPackageRoots(stagingRoot); if (list.Count == 0) { throw new InvalidDataException("The archive has no pallet manifest (pallet.json or *.pallet.json) and is not a BONELAB content mod."); } Directory.CreateDirectory(backupRoot); List<string> list2 = new List<string>(); List<(string, string)> list3 = new List<(string, string)>(); try { foreach (string item in list) { cancellationToken.ThrowIfCancellationRequested(); string fileName = Path.GetFileName(item.TrimEnd(Path.DirectorySeparatorChar)); ValidateFolderName(fileName); string text3 = Path.Combine(modsRoot, fileName); string text4 = null; if (Directory.Exists(text3)) { text4 = Path.Combine(backupRoot, fileName); Directory.Move(text3, text4); } try { Directory.Move(item, text3); list3.Add((text3, text4)); } catch { if (text4 != null && Directory.Exists(text4) && !Directory.Exists(text3)) { Directory.Move(text4, text3); } throw; } list2.Add(text3); } return list2; } catch { for (int num = list3.Count - 1; num >= 0; num--) { (string, string) tuple = list3[num]; if (Directory.Exists(tuple.Item1)) { Directory.Delete(tuple.Item1, recursive: true); } if (tuple.Item2 != null && Directory.Exists(tuple.Item2)) { Directory.Move(tuple.Item2, tuple.Item1); } } throw; } } finally { TryDeleteDirectory(stagingRoot); TryDeleteDirectory(backupRoot); } } private async Task ExtractValidatedAsync(string zipPath, string destination, CancellationToken cancellationToken) { await using FileStream zipStream = File.OpenRead(zipPath); using ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Read, leaveOpen: false); long extractedBytes = 0L; string destinationPrefix = Path.GetFullPath(destination) + Path.DirectorySeparatorChar; foreach (ZipArchiveEntry entry in archive.Entries) { cancellationToken.ThrowIfCancellationRequested(); if (IsSymbolicLink(entry)) { throw new InvalidDataException("Archive contains a symbolic link: " + entry.FullName); } string fullPath = Path.GetFullPath(Path.Combine(destination, entry.FullName)); if (!fullPath.StartsWith(destinationPrefix, StringComparison.OrdinalIgnoreCase)) { throw new InvalidDataException("Archive path escapes the install directory: " + entry.FullName); } if (string.IsNullOrEmpty(entry.Name)) { Directory.CreateDirectory(fullPath); continue; } if (BlockedExtensions.Contains(Path.GetExtension(entry.Name))) { throw new InvalidDataException("Archive contains blocked executable content: " + entry.FullName); } extractedBytes = checked(extractedBytes + entry.Length); if (extractedBytes > _options.MaximumExtractedBytes) { throw new InvalidDataException("Archive exceeds the configured extracted-size limit."); } Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); await using Stream input = entry.Open(); await using FileStream output = new FileStream(fullPath, FileMode.CreateNew, FileAccess.Write, FileShare.None); byte[] buffer = new byte[131072]; long writtenForEntry = 0L; while (true) { int num = await input.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken).ConfigureAwait(continueOnCapturedContext: false); if (num != 0) { writtenForEntry = checked(writtenForEntry + num); if (extractedBytes - entry.Length + writtenForEntry > _options.MaximumExtractedBytes) { throw new InvalidDataException("Archive exceeds the configured extracted-size limit."); } await output.WriteAsync(buffer.AsMemory(0, num), cancellationToken).ConfigureAwait(continueOnCapturedContext: false); continue; } break; } } } private static List<string> FindPackageRoots(string stagingRoot) { List<string> roots = (from path in PalletManifestLocator.FindAll(stagingRoot, SearchOption.AllDirectories) select Path.GetDirectoryName(path) into path where !string.Equals(path, stagingRoot, StringComparison.OrdinalIgnoreCase) select path).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList(); return roots.Where((string candidate) => !roots.Any((string other) => !string.Equals(candidate, other, StringComparison.OrdinalIgnoreCase) && candidate.StartsWith(other + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))).ToList(); } private static bool IsSymbolicLink(ZipArchiveEntry entry) { return ((entry.ExternalAttributes >> 16) & 0xF000) == 40960; } private static string EnsureDirectory(string path) { string fullPath = Path.GetFullPath(path); Directory.CreateDirectory(fullPath); return fullPath; } private static void ValidateFolderName(string name) { bool flag = string.IsNullOrWhiteSpace(name); if (!flag) { bool flag2 = ((name == "
Mods\WristHub.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.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.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.Forklift.Model; using Il2CppSLZ.Marrow.SaveData; using Il2CppSLZ.Marrow.Warehouse; using Il2CppSLZ.VRMK; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppTMPro; using MelonLoader; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(BoneHubMod), "WristHub", "2.0.1", "WristHub contributors", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("WristHub")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("2.0.1.0")] [assembly: AssemblyInformationalVersion("2.0.1")] [assembly: AssemblyProduct("WristHub")] [assembly: AssemblyTitle("WristHub")] [assembly: AssemblyVersion("2.0.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BoneHub.Mod { internal sealed class BoneHubAvatarBrowser : IDisposable { 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 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 Cyan = new Color(0.2f, 0.9f, 1f, 1f); private static readonly Color DimCyan = new Color(0.055f, 0.25f, 0.32f, 1f); private static readonly Color Green = new Color(0.32f, 0.9f, 0.62f, 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 = new BoneHubFingertipProbeProvider(); 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 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? _keyboardPage; private GameObject? _deletePage; 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? _startingViewText; private TMP_Text? _scopeText; private TMP_Text? _deleteTitle; private TMP_Text? _deleteDetails; private TMP_Text? _keyboardText; private TMP_Text? _keyboardShiftText; private TMP_Text? _searchText; 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 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 _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; 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 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) _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; } public void OpenLauncher() { //IL_0078: Unknown result type (might be due to invalid IL or missing references) EnsureBuilt(); if (!((Object)(object)_root == (Object)null) && !((Object)(object)_launcher == (Object)null) && !((Object)(object)_browser == (Object)null)) { FollowWrist(immediate: true); _root.SetActive(true); _launcher.SetActive(true); if ((Object)(object)_hub != (Object)null) { _hub.SetActive(false); } _browser.SetActive(false); _viewState = (AvatarBrowserViewState)0; _promptDismissal.Reset(); RefreshEntries(); RequireInputRearm(); } } 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)_launcher == (Object)null) && !((Object)(object)_browser == (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)_launcher == (Object)null) && !((Object)(object)_browser == (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() { if (_previewHeld) { EndPreviewDrag(equipIfAtChest: false); } ResetTouchButtons(); ResetTrashConfirmation(); CancelPendingSharingRegistration(); 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); } _hologramProjection?.Hide(); _artworkGeneration.Next(); _previewGeneration++; _requestedPreviewBarcode = string.Empty; _pendingPreviewAvatar = null; _promptDismissal.Reset(); this.Closed?.Invoke(); } public void SetQuery(string value) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_017d: 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 : 6); _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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Invalid comparison between Unknown and I4 //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Expected I4, but got Unknown //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) _jobs[job.Mod.Id] = job; 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 => 10, 0 => 9, 2 => 7, _ => 8, }); RefreshVisiblePage(); } } public void OnRuntimeStatusChanged(string status) { //IL_006d: 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_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 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() { //IL_0097: Unknown result type (might be due to invalid IL or missing references) _fingertips.Invalidate(); _projectionAnchors.Invalidate(); _hologramProjection?.BeginTrackingGrace(); ResetTouchButtons(); 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? browser = _browser; if (browser != null && browser.activeSelf) { RefreshFocusedResult(); } } } public void Tick() { _hologramProjection?.Tick(_preferences.ReducedMotion); if (IsLocalPlayerDead()) { if (!_wasPlayerDead && IsLauncherOpen) { _log.Msg("WristHub closed because the local player died."); Close(); } _wasPlayerDead = true; return; } _wasPlayerDead = false; if (IsLauncherOpen && !((Object)(object)Player.Head == (Object)null)) { RunGuarded("projection", _projectionTickAction, _projectionRecoveryAction); RunGuarded("touch", _touchTickAction, _touchRecoveryAction); RunGuarded("installation-focus", _installationFocusTickAction); RunGuarded("preview", _previewTickAction, _previewRecoveryAction); RunGuarded("download", _downloadTickAction); RunGuarded("sharing", _sharingTickAction, CancelPendingSharingRegistration); } } private void TickProjectionStage() { FollowWrist(); UpdateHologramProjection(); } 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 { 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++; ResetUiReferences(destroyOwnedObjects: true); } } private void EnsureBuilt() { if (!((Object)(object)_root != (Object)null)) { ResetUiReferences(destroyOwnedObjects: true); BuildTablet(); } } private void ResetUiReferences(bool 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 { } try { _hologramProjection?.Dispose(); } catch { } } _ownedPreviewMeshes.Clear(); _hologramProjection = null; _root = null; _launcher = null; _hub = null; _browser = null; _canvasRoot = null; _packPage = null; _detailPage = null; _optionsPage = null; _keyboardPage = null; _deletePage = null; _canvasGroup = null; _packTitle = null; _packStatus = null; _packPosition = null; _packSearchText = null; _sortText = null; _wristText = null; _scaleText = null; _motionText = null; _startingViewText = null; _scopeText = null; _deleteTitle = null; _deleteDetails = null; _keyboardText = null; _keyboardShiftText = null; _searchText = 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; _previewReturning = 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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0187: 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_01b3: Expected O, but got Unknown //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Unknown result type (might be due to invalid IL or missing references) //IL_0358: Unknown result type (might be due to invalid IL or missing references) //IL_039a: Unknown result type (might be due to invalid IL or missing references) //IL_03ae: Unknown result type (might be due to invalid IL or missing references) //IL_03c7: Unknown result type (might be due to invalid IL or missing references) //IL_0410: Unknown result type (might be due to invalid IL or missing references) //IL_0444: Unknown result type (might be due to invalid IL or missing references) //IL_044e: Expected O, but got Unknown //IL_0489: Unknown result type (might be due to invalid IL or missing references) //IL_049d: Unknown result type (might be due to invalid IL or missing references) //IL_04b6: Unknown result type (might be due to invalid IL or missing references) //IL_04d2: Unknown result type (might be due to invalid IL or missing references) //IL_04e6: 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_0542: Unknown result type (might be due to invalid IL or missing references) //IL_0556: Unknown result type (might be due to invalid IL or missing references) //IL_0577: Unknown result type (might be due to invalid IL or missing references) //IL_0590: Unknown result type (might be due to invalid IL or missing references) //IL_05a6: Unknown result type (might be due to invalid IL or missing references) //IL_05b0: Expected O, but got Unknown //IL_05f1: Unknown result type (might be due to invalid IL or missing references) //IL_05fb: Unknown result type (might be due to invalid IL or missing references) //IL_063b: Unknown result type (might be due to invalid IL or missing references) //IL_064f: Unknown result type (might be due to invalid IL or missing references) //IL_0654: Unknown result type (might be due to invalid IL or missing references) //IL_069b: Unknown result type (might be due to invalid IL or missing references) //IL_06af: Unknown result type (might be due to invalid IL or missing references) //IL_06b4: Unknown result type (might be due to invalid IL or missing references) //IL_0707: Unknown result type (might be due to invalid IL or missing references) //IL_071b: Unknown result type (might be due to invalid IL or missing references) //IL_0720: Unknown result type (might be due to invalid IL or missing references) //IL_0767: Unknown result type (might be due to invalid IL or missing references) //IL_077b: Unknown result type (might be due to invalid IL or missing references) //IL_0780: Unknown result type (might be due to invalid IL or missing references) //IL_07c7: Unknown result type (might be due to invalid IL or missing references) //IL_07db: Unknown result type (might be due to invalid IL or missing references) //IL_07e0: Unknown result type (might be due to invalid IL or missing references) //IL_083f: Unknown result type (might be due to invalid IL or missing references) //IL_0853: Unknown result type (might be due to invalid IL or missing references) //IL_0858: Unknown result type (might be due to invalid IL or missing references) //IL_0899: Unknown result type (might be due to invalid IL or missing references) //IL_08a3: Unknown result type (might be due to invalid IL or missing references) //IL_08e3: Unknown result type (might be due to invalid IL or missing references) //IL_08f7: Unknown result type (might be due to invalid IL or missing references) //IL_08fc: Unknown result type (might be due to invalid IL or missing references) //IL_093d: Unknown result type (might be due to invalid IL or missing references) //IL_095b: Unknown result type (might be due to invalid IL or missing references) //IL_0976: Unknown result type (might be due to invalid IL or missing references) //IL_0980: Expected O, but got Unknown //IL_09c1: Unknown result type (might be due to invalid IL or missing references) //IL_09da: Unknown result type (might be due to invalid IL or missing references) //IL_0a10: Unknown result type (might be due to invalid IL or missing references) //IL_0a24: Unknown result type (might be due to invalid IL or missing references) //IL_0a29: Unknown result type (might be due to invalid IL or missing references) //IL_0a70: Unknown result type (might be due to invalid IL or missing references) //IL_0a84: Unknown result type (might be due to invalid IL or missing references) //IL_0a89: 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_0af2: Unknown result type (might be due to invalid IL or missing references) //IL_0af7: Unknown result type (might be due to invalid IL or missing references) //IL_0b3e: Unknown result type (might be due to invalid IL or missing references) //IL_0b52: Unknown result type (might be due to invalid IL or missing references) //IL_0b57: Unknown result type (might be due to invalid IL or missing references) //IL_0b9d: Unknown result type (might be due to invalid IL or missing references) //IL_0ba2: Unknown result type (might be due to invalid IL or missing references) //IL_0ba7: Unknown result type (might be due to invalid IL or missing references) //IL_0bac: Unknown result type (might be due to invalid IL or missing references) //IL_0bb1: Unknown result type (might be due to invalid IL or missing references) //IL_0be9: Unknown result type (might be due to invalid IL or missing references) //IL_0bee: Unknown result type (might be due to invalid IL or missing references) //IL_0bf3: Unknown result type (might be due to invalid IL or missing references) //IL_0bf8: Unknown result type (might be due to invalid IL or missing references) //IL_0bfd: Unknown result type (might be due to invalid IL or missing references) //IL_0c35: Unknown result type (might be due to invalid IL or missing references) //IL_0c3a: Unknown result type (might be due to invalid IL or missing references) //IL_0c3f: Unknown result type (might be due to invalid IL or missing references) //IL_0c44: Unknown result type (might be due to invalid IL or missing references) //IL_0c49: Unknown result type (might be due to invalid IL or missing references) //IL_0c76: Unknown result type (might be due to invalid IL or missing references) //IL_0c8f: Unknown result type (might be due to invalid IL or missing references) //IL_0cbf: Unknown result type (might be due to invalid IL or missing references) //IL_0cca: Unknown result type (might be due to invalid IL or missing references) //IL_0cf6: Unknown result type (might be due to invalid IL or missing references) //IL_0d0f: Unknown result type (might be due to invalid IL or missing references) //IL_0d3f: Unknown result type (might be due to invalid IL or missing references) //IL_0d49: Unknown result type (might be due to invalid IL or missing references) //IL_0d83: Unknown result type (might be due to invalid IL or missing references) //IL_0d8d: Unknown result type (might be due to invalid IL or missing references) //IL_0dc7: Unknown result type (might be due to invalid IL or missing references) //IL_0de5: Unknown result type (might be due to invalid IL or missing references) //IL_0e1f: Unknown result type (might be due to invalid IL or missing references) //IL_0e29: Unknown result type (might be due to invalid IL or missing references) //IL_0e69: Unknown result type (might be due to invalid IL or missing references) //IL_0e73: Unknown result type (might be due to invalid IL or missing references) //IL_0ea5: Unknown result type (might be due to invalid IL or missing references) //IL_0eaa: Unknown result type (might be due to invalid IL or missing references) //IL_0eaf: Unknown result type (might be due to invalid IL or missing references) //IL_0eb4: Unknown result type (might be due to invalid IL or missing references) //IL_0eb9: Unknown result type (might be due to invalid IL or missing references) //IL_0f18: Unknown result type (might be due to invalid IL or missing references) //IL_0f1d: Unknown result type (might be due to invalid IL or missing references) //IL_0f22: Unknown result type (might be due to invalid IL or missing references) //IL_0f27: Unknown result type (might be due to invalid IL or missing references) //IL_0f2c: 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(); _launcher = new GameObject("Push Prompt"); _launcher.transform.SetParent(_root.transform, false); _launcher.transform.localPosition = new Vector3(0f, -0.085f, 0f); CreatePanel(_launcher.transform, "Push Prompt Glass", Vector3.zero, new Vector3(0.15f, 0.07f, 0.006f), Glass, (PrimitiveType)3); GameObject val = CreateCanvas(_launcher.transform, "Push Prompt UI", 150f, 70f, 0.007f); CreateButton(_launcher.transform, val.transform, "PUSH ME", Vector3.forward * 0.007f, new Vector3(0.13f, 0.05f, 0.014f), Cyan, ShowHub, 24f); _hub = new GameObject("WristHub Module Hub"); _hub.transform.SetParent(_root.transform, false); CreatePanel(_hub.transform, "Hub Glass", Vector3.zero, new Vector3(0.25f, 0.164f, 0.008f), Glass, (PrimitiveType)3); GameObject val2 = CreateCanvas(_hub.transform, "Hub UI", 250f, 164f, 0.009f); CreateText(val2.transform, "WRISTHUB", new TabletRect(-62f, 64f, 100f, 22f), 19f, Color.white, (TextAlignmentOptions)4097, (FontStyles)1); CreateButton(_hub.transform, val2.transform, "X", new Vector3(0.108f, 0.064f, 0.008f), new Vector3(0.026f, 0.026f, 0.014f), Red, Close, 19f); CreateButton(_hub.transform, val2.transform, "AVATARS", new Vector3(0f, 0.027f, 0.008f), new Vector3(0.214f, 0.038f, 0.014f), Cyan, Open, 19f); CreateButton(_hub.transform, val2.transform, "SEARCH AVATARS", new Vector3(0f, -0.02f, 0.008f), new Vector3(0.214f, 0.038f, 0.014f), DimCyan, OpenSearch, 18f); TouchButton touchButton = CreateButton(_hub.transform, val2.transform, "MORE COMING SOON", new Vector3(0f, -0.066f, 0.008f), new Vector3(0.214f, 0.032f, 0.014f), new Color(0.06f, 0.1f, 0.12f, 1f), delegate { }, 14f); ((Graphic)touchButton.Label).color = new Color(0.45f, 0.55f, 0.6f, 1f); _buttons.Remove(touchButton); _hub.SetActive(false); _placeholderTexture = CreatePlaceholderTexture(); _browser = new GameObject("Avatar Browser Tablet 34x21cm"); _browser.transform.SetParent(_root.transform, false); CreatePanel(_browser.transform, "Tablet Shadow", new Vector3(0.006f, -0.007f, -0.006f), new Vector3(0.35f, 0.22f, 0.009f), new Color(0f, 0f, 0f, 0.7f), (PrimitiveType)3); CreatePanel(_browser.transform, "Tablet Bezel", Vector3.zero, new Vector3(0.34f, 0.21f, 0.01f), Glass, (PrimitiveType)3); _canvasRoot = CreateCanvas(_browser.transform, "WristHub Tablet Canvas", 340f, 210f, 0.006f); _canvasGroup = _canvasRoot.AddComponent<CanvasGroup>(); ((Graphic)CreateRawImage(_canvasRoot.transform, "WristHub Background", AvatarTabletLayout.Tablet, BoneHubTheme.Background ?? _placeholderTexture, Color.white)).raycastTarget = false; ((Graphic)CreateImage(_canvasRoot.transform, "Calm Dark Overlay", AvatarTabletLayout.Tablet, new Color(0.005f, 0.024f, 0.038f, 0.72f))).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 touchButton2 = 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 = touchButton2.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 num = 0; num < 4; num++) { CreatePackRow(num); } 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("Focused Avatar Page"); _detailPage.transform.SetParent(_canvasRoot.transform, false); CreateImage(_detailPage.transform, "Header", new TabletRect(0f, 82f, 324f, 34f), new Color(0.02f, 0.09f, 0.13f, 0.96f)); CreateButton(_browser.transform, _detailPage.transform, "BACK", new Vector3(-0.145f, 0.083f, 0.007f), new Vector3(0.042f, 0.028f, 0.014f), DimCyan, BackFromDetailPage, 9f); TouchButton touchButton3 = CreateButton(_browser.transform, _detailPage.transform, "SEARCH", new Vector3(-0.09f, 0.083f, 0.007f), new Vector3(0.064f, 0.028f, 0.014f), DimCyan, OpenKeyboard, 11f); _searchText = touchButton3.Label; CreateButton(_browser.transform, _detailPage.transform, "OPTIONS", new Vector3(-0.027f, 0.083f, 0.007f), new Vector3(0.058f, 0.028f, 0.014f), DimCyan, ShowOptionsPage, 8f); TouchButton touchButton4 = CreateButton(_browser.transform, _detailPage.transform, "ALL", new Vector3(0.048f, 0.083f, 0.007f), new Vector3(0.088f, 0.028f, 0.014f), Cyan, ToggleScopeOrDownloads, 10f); _scopeText = touchButton4.Label; CreateButton(_browser.transform, _detailPage.transform, "X", PixelCenter(AvatarTabletLayout.Close), PixelSize(AvatarTabletLayout.Close), Red, Close, 21f); CreateButton(_browser.transform, _detailPage.transform, "<", PixelCenter(AvatarTabletLayout.Previous), PixelSize(AvatarTabletLayout.Previous), DimCyan, delegate { MoveFocused(-1); }, 30f); CreateButton(_browser.transform, _detailPage.transform, ">", PixelCenter(AvatarTabletLayout.Next), PixelSize(AvatarTabletLayout.Next), DimCyan, delegate { MoveFocused(1); }, 30f); CreateImage(_detailPage.transform, "Preview Well", AvatarTabletLayout.Artwork, new Color(0.018f, 0.07f, 0.095f, 0.94f)); _centralArtwork = CreateRawImage(_detailPage.transform, "Selected Artwork", new TabletRect(-66f, -4f, 116f, 120f), _placeholderTexture, Color.white); ((Graphic)_centralArtwork).raycastTarget = false; CreateImage(_detailPage.transform, "Details Surface", AvatarTabletLayout.Details, new Color(0.02f, 0.065f, 0.09f, 0.94f)); _selectedName = CreateText(_detailPage.transform, "Select an avatar", new TabletRect(65f, 45f, 112f, 32f), 18f, Color.white, (TextAlignmentOptions)257, (FontStyles)1); _creatorText = CreateText(_detailPage.transform, string.Empty, new TabletRect(65f, 22f, 112f, 16f), 12f, Cyan, (TextAlignmentOptions)4097, (FontStyles)0); _modText = CreateText(_detailPage.transform, string.Empty, new TabletRect(65f, 3f, 112f, 20f), 11f, new Color(0.72f, 0.82f, 0.88f, 1f), (TextAlignmentOptions)257, (FontStyles)0); _positionText = CreateText(_detailPage.transform, "0 / 0", new TabletRect(65f, -18f, 112f, 16f), 12f, Cyan, (TextAlignmentOptions)4097, (FontStyles)1); CreateTabletHeightRuler(); _statusText = CreateText(_detailPage.transform, "Ready", new TabletRect(65f, -42f, 112f, 26f), 12f, Cyan, (TextAlignmentOptions)257, (FontStyles)1); _actionButton = CreateButton(_browser.transform, _detailPage.transform, "EQUIP", PixelCenter(AvatarTabletLayout.Action), PixelSize(AvatarTabletLayout.Action), Cyan, ActivateSelected, 18f); _actionLabel = _actionButton.Label; _actionPanel = _actionButton.Panel; _trashButton = CreateButton(_browser.transform, _detailPage.transform, "DELETE PACK", PixelCenter(AvatarTabletLayout.Trash), PixelSize(AvatarTabletLayout.Trash), Red, ShowDeleteConfirmation, 8f); ((Object)_trashButton.Root).name = "Trash Avatar Pack"; CreateTabletProgressBar(); BuildOptionsPage(); BuildKeyboardPage(); BuildDeletePage(); _packPage.SetActive(true); _detailPage.SetActive(false); GameObject? optionsPage = _optionsPage; if (optionsPage != null) { optionsPage.SetActive(false); } GameObject? keyboardPage = _keyboardPage; if (keyboardPage != null) { keyboardPage.SetActive(false); } GameObject? deletePage = _deletePage; if (deletePage != null) { deletePage.SetActive(false); } _browser.SetActive(false); CreatePreviewShell(); _root.SetActive(false); } private void BuildHologramProjection() { if (_hologramProjection == null) { _hologramProjection = new BoneHubForearmProjection(_log); } } private void StartHologramProjection() { if (!_preferences.HologramEffects) { if ((Object)(object)_canvasGroup != (Object)null) { _canvasGroup.alpha = 1f; } _hologramProjection?.Hide(immediate: true); } else { if ((Object)(object)_canvasGroup != (Object)null) { _canvasGroup.alpha = (_preferences.ReducedMotion ? 1f : 0.35f); } _hologramProjection?.Show(); } } private void UpdateHologramProjection() { //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) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) 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); return; } if ((Object)(object)val == (Object)null) { _hologramProjection.Hide(); return; } float heightRatio = AvatarUiScaleMath.ResolveMenuScale(CurrentPlayerHeight()); if (!_projectionAnchors.TryGet(_preferences.UseLeftWrist, heightRatio, out var anchor)) { _hologramProjection.BeginTrackingGrace(); _hologramProjection.UpdateTrackingLost(); return; } _hologramProjection.UpdatePose(anchor, val, _preferences.ReducedMotion, _preferences.HologramBrightness); float num = (_preferences.ReducedMotion ? 1f : _hologramProjection.RevealProgress); if ((Object)(object)_canvasGroup != (Object)null) { _canvasGroup.alpha = Mathf.Lerp(0.35f, 1f, num); } val.localPosition = new Vector3(0f, -0.004f * (1f - num), 0f); val.localScale = Vector3.one * Mathf.Lerp(0.985f, 1f, num); } private void BuildDeletePage() { //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_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_015c: 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_01ac: 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_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_browser == (Object)null) && !((Object)(object)_canvasRoot == (Object)null)) { _deletePage = new GameObject("Delete Pack Confirmation Page"); _deletePage.transform.SetParent(_canvasRoot.transform, false); CreateImage(_deletePage.transform, "Delete Header", new TabletRect(0f, 76f, 310f, 38f), new Color(0.3f, 0.035f, 0.055f, 0.98f)); _deleteTitle = CreateText(_deletePage.transform, "DELETE AVATAR PACK?", new TabletRect(0f, 76f, 280f, 28f), 20f, Color.white, (TextAlignmentOptions)514, (FontStyles)1); _deleteDetails = CreateText(_deletePage.transform, string.Empty, new TabletRect(0f, 15f, 280f, 72f), 15f, Color.white, (TextAlignmentOptions)514, (FontStyles)0); CreateText(_deletePage.transform, "Locked packs finish safely on the next BONELAB launch. Undo stays available.", new TabletRect(0f, -37f, 280f, 34f), 11f, new Color(0.75f, 0.82f, 0.86f, 1f), (TextAlignmentOptions)514, (FontStyles)0); CreateButton(_browser.transform, _deletePage.transform, "CANCEL", new Vector3(-0.082f, -0.077f, 0.007f), new Vector3(0.13f, 0.038f, 0.014f), DimCyan, CancelDelete, 16f); CreateButton(_browser.transform, _deletePage.transform, "DELETE PACK", new Vector3(0.082f, -0.077f, 0.007f), new Vector3(0.13f, 0.038f, 0.014f), Red, TrashSelected, 16f); _deletePage.SetActive(false); } } private void ShowHub() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_launcher != (Object)null) { _launcher.SetActive(false); } if ((Object)(object)_hub != (Object)null) { _hub.SetActive(true); } if ((Object)(object)_browser != (Object)null) { _browser.SetActive(false); } _viewState = (AvatarBrowserViewState)1; StartHologramProjection(); ResetTouchButtons(); RequireInputRearm(); _log.Msg("WristHub wrist module hub opened."); } private void CreatePackRow(int index) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_0223: 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) //IL_023c: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_browser == (Object)null) && !((Object)(object)_packPage == (Object)null) && !((Object)(object)_placeholderTexture == (Object)null)) { float num = 50f - (float)index * 34f; TouchButton touchButton = CreateButton(_browser.transform, _packPage.transform, string.Empty, new Vector3(-0.036f, num / 1000f, 0.007f), new Vector3(0.228f, 0.03f, 0.014f), Surface, delegate { OpenPackRow(index); }, 1f); RawImage artwork = CreateRawImage(touchButton.Root.transform, $"Pack {index + 1} Artwork", new TabletRect(-96f, 0f, 26f, 24f), _placeholderTexture, Color.white); TMP_Text name = CreateText(touchButton.Root.transform, string.Empty, new TabletRect(-28f, 5f, 104f, 13f), 12f, Color.white, (TextAlignmentOptions)4097, (FontStyles)1); TMP_Text creator = CreateText(touchButton.Root.transform, string.Empty, new TabletRect(-24f, -7f, 112f, 10f), 9f, new Color(0.65f, 0.8f, 0.86f, 1f), (TextAlignmentOptions)4097, (FontStyles)0); TMP_Text state = CreateText(touchButton.Root.transform, string.Empty, new TabletRect(78f, 0f, 54f, 18f), 9f, Cyan, (TextAlignmentOptions)4100, (FontStyles)1); TouchButton deleteButton = CreateButton(_browser.transform, _packPage.transform, "DELETE", new Vector3(0.125f, num / 1000f, 0.007f), new Vector3(0.052f, 0.03f, 0.014f), Red, delegate { ShowDeleteConfirmationForPack(index); }, 8f); _packRows.Add(new PackRow { Button = touchButton, DeleteButton = deleteButton, Artwork = artwork, Name = name, Creator = creator, State = state }); } } private void BuildOptionsPage() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_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_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_016d: 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_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_0211: 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_025b: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_0359: Unknown result type (might be due to invalid IL or missing references) //IL_0363: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03b7: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Unknown result type (might be due to invalid IL or missing references) //IL_0403: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Unknown result type (might be due to invalid IL or missing references) //IL_041c: Unknown result type (might be due to invalid IL or missing references) //IL_045d: 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_04a7: Unknown result type (might be due to invalid IL or missing references) //IL_04bb: Unknown result type (might be due to invalid IL or missing references) //IL_04c0: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_browser == (Object)null) && !((Object)(object)_canvasRoot == (Object)null)) { _optionsPage = new GameObject("WristHub Options Page"); _optionsPage.transform.SetParent(_canvasRoot.transform, false); CreateText(_optionsPage.transform, "SETTINGS", new TabletRect(-92f, 82f, 112f, 28f), 18f, Color.white, (TextAlignmentOptions)4097, (FontStyles)1); CreateButton(_browser.transform, _optionsPage.transform, "BACK", new Vector3(0.108f, 0.082f, 0.007f), new Vector3(0.064f, 0.028f, 0.014f), DimCyan, ShowScopePage, 12f); CreateButton(_browser.transform, _optionsPage.transform, "X", new Vector3(0.151f, 0.082f, 0.007f), new Vector3(0.026f, 0.028f, 0.014f), Red, Close, 19f); _startingViewText = CreateText(_optionsPage.transform, string.Empty, new TabletRect(-58f, 49f, 194f, 20f), 13f, Color.white, (TextAlignmentOptions)4097, (FontStyles)1); CreateButton(_browser.transform, _optionsPage.transform, "CHANGE", new Vector3(0.112f, 0.049f, 0.007f), new Vector3(0.072f, 0.026f, 0.014f), Cyan, CycleStartingView, 10f); _sortText = CreateText(_optionsPage.transform, string.Empty, new TabletRect(-58f, 19f, 194f, 20f), 13f, Color.white, (TextAlignmentOptions)4097, (FontStyles)0); CreateButton(_browser.transform, _optionsPage.transform, "CHANGE", new Vector3(0.112f, 0.019f, 0.007f), new Vector3(0.072f, 0.026f, 0.014f), DimCyan, CyclePackSort, 10f); _wristText = CreateText(_optionsPage.transform, string.Empty, new TabletRect(-58f, -11f, 194f, 20f), 13f, Color.white, (TextAlignmentOptions)4097, (FontStyles)0); CreateButton(_browser.transform, _optionsPage.transform, "SWITCH", new Vector3(0.112f, -0.011f, 0.007f), new Vector3(0.072f, 0.026f, 0.014f), DimCyan, ToggleWrist, 10f); _scaleText = CreateText(_optionsPage.transform, string.Empty, new TabletRect(-58f, -41f, 194f, 20f), 13f, Color.white, (TextAlignmentOptions)4097, (FontStyles)0); CreateButton(_browser.transform, _optionsPage.transform, "-", new Vector3(0.086f, -0.041f, 0.007f), new Vector3(0.036f, 0.026f, 0.014f), DimCyan, delegate { ChangeScale(-0.1f); }, 18f); CreateButton(_browser.transform, _optionsPage.transform, "+", new Vector3(0.138f, -0.041f, 0.007f), new Vector3(0.036f, 0.026f, 0.014f), DimCyan, delegate { ChangeScale(0.1f); }, 18f); _motionText = CreateText(_optionsPage.transform, string.Empty, new TabletRect(-58f, -71f, 194f, 20f), 13f, Color.white, (TextAlignmentOptions)4097, (FontStyles)0); CreateButton(_browser.transform, _optionsPage.transform, "TOGGLE", new Vector3(0.112f, -0.071f, 0.007f), new Vector3(0.072f, 0.026f, 0.014f), DimCyan, ToggleMotion, 10f); RefreshOptionLabels(); } } private void BuildKeyboardPage() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_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_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_0315: Unknown result type (might be due to invalid IL or missing references) //IL_0329: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Unknown result type (might be due to invalid IL or missing references) //IL_0375: Unknown result type (might be due to invalid IL or missing references) //IL_0389: Unknown result type (might be due to invalid IL or missing references) //IL_038e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_browser == (Object)null) && !((Object)(object)_canvasRoot == (Object)null)) { _keyboardPage = new GameObject("WristHub Tablet Keyboard"); _keyboardPage.transform.SetParent(_canvasRoot.transform, false); CreateText(_keyboardPage.transform, "SEARCH AVATARS", new TabletRect(-70f, 84f, 180f, 24f), 17f, Color.white, (TextAlignmentOptions)4097, (FontStyles)1); CreateButton(_browser.transform, _keyboardPage.transform, "X", new Vector3(0.151f, 0.084f, 0.007f), new Vector3(0.026f, 0.026f, 0.014f), Red, CancelKeyboard, 19f); CreateImage(_keyboardPage.transform, "Search Input", new TabletRect(-35f, 55f, 236f, 30f), Surface); _keyboardText = CreateText(_keyboardPage.transform, "Type an avatar name", new TabletRect(-35f, 55f, 218f, 24f), 15f, Color.white, (TextAlignmentOptions)4097, (FontStyles)0); CreateButton(_browser.transform, _keyboardPage.transform, "SEARCH", new Vector3(0.125f, 0.055f, 0.007f), new Vector3(0.064f, 0.03f, 0.014f), Cyan, SubmitKeyboard, 10f); CreateKeyboardRow("QWERTYUIOP", -135f, 24f, 27f, 3f); CreateKeyboardRow("ASDFGHJKL", -120f, -6f, 27f, 3f); CreateKeyboardRow("ZXCVBNM", -105f, -36f, 27f, 3f); TouchButton touchButton = CreateButton(_browser.transform, _keyboardPage.transform, "SHIFT", new Vector3(-0.139f, -0.074f, 0.007f), new Vector3(0.052f, 0.026f, 0.014f), DimCyan, ToggleKeyboardShift, 10f); _keyboardShiftText = touchButton.Label; CreateButton(_browser.transform, _keyboardPage.transform, "SPACE", new Vector3(-0.073f, -0.074f, 0.007f), new Vector3(0.072f, 0.026f, 0.014f), DimCyan, AppendKeyboardSpace, 10f); CreateButton(_browser.transform, _keyboardPage.transform, "BACK", new Vector3(0f, -0.074f, 0.007f), new Vector3(0.062f, 0.026f, 0.014f), DimCyan, BackspaceKeyboard, 10f); CreateButton(_browser.transform, _keyboardPage.transform, "CLEAR", new Vector3(0.061f, -0.074f, 0.007f), new Vector3(0.05f, 0.026f, 0.014f), DimCyan, ClearKeyboard, 9f); _keyboardPage.SetActive(false); } } private void CreateKeyboardRow(string keys, float startX, float y, float width, float gap) { //IL_007e: 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_009a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_browser == (Object)null || (Object)(object)_keyboardPage == (Object)null) { return; } for (int i = 0; i < keys.Length; i++) { char key = keys[i]; float num = startX + (float)i * (width + gap); CreateButton(_browser.transform, _keyboardPage.transform, key.ToString(), new Vector3(num / 1000f, y / 1000f, 0.007f), new Vector3(width / 1000f, 0.026f, 0.014f), DimCyan, delegate { AppendKeyboardCharacter(key); }, 12f); } } private void HandlePackSearch() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 bool flag = _query.Length > 0; if (!flag) { AvatarBrowserViewState viewState = _viewState; bool flag2 = viewState - 6 <= 1; flag = flag2; } if (flag) { OpenKeyboard(); } else { OpenSearchPage(); } } private void OpenSearchPage() { OpenKeyboard(); } private void BackFromPackPage() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 //IL_0055: Unknown result type (might be due to invalid IL or missing references) bool flag = _query.Length > 0; if (!flag) { AvatarBrowserViewState viewState = _viewState; bool flag2 = viewState - 6 <= 1; flag = flag2; } if (flag) { _query = string.Empty; _downloadsOnly = false; _onlineEntries = Array.Empty<AvatarEntry>(); _selectedPackId = null; _viewState = (AvatarBrowserViewState)2; RefreshEntries(); ShowPackPage(); } else { ShowHub(); } } private void BackFromDetailPage() { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Invalid comparison between Unknown and I4 //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (_query.Length > 0) { _query = string.Empty; _downloadsOnly = false; _onlineEntries = Array.Empty<AvatarEntry>(); _scope = _scopeBeforeSearch; _selectedPackId = null; _viewState = (AvatarBrowserViewState)2; RefreshEntries(); ShowScopePage(); } else if ((int)_scope == 1 && _selectedPackId.HasValue) { ShowPackPage(); } else { ShowHub(); } } private void ShowScopePage() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)_scope == 1) { ShowPackPage(); } else { ShowAllAvatars(); } } private void ShowAllAvatars() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) _scope = (AvatarBrowseScope)0; _selectedPackId = null; RememberScope(); RefreshEntries(); if (!_entries.Any((AvatarEntry entry) => entry.Identity == _selectedIdentity)) { AvatarEntry? obj = _entries.FirstOrDefault(); _selectedIdentity = ((obj != null) ? obj.Identity : null) ?? string.Empty; } _cursor.Keep(_entries.Count, IndexOf(_selectedIdentity)); ShowDetailPage(); } private void ShowGroups() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) _scope = (AvatarBrowseScope)1; _selectedPackId = null; RememberScope(); ShowPackPage(); } private void ToggleBrowseScope() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)_scope == 0) { ShowGroups(); } else { ShowAllAvatars(); } } private void ToggleScopeOrDownloads() { if (_query.Length == 0) { ToggleBrowseScope(); return; } _downloadsOnly = !_downloadsOnly; _selectedPackId = null; RefreshEntries(); AvatarEntry? obj = _entries.FirstOrDefault(); _selectedIdentity = ((obj != null) ? obj.Identity : null) ?? string.Empty; _cursor.Reset(_entries.Count); RefreshFocusedResult(); _log.Msg(_downloadsOnly ? $"Downloadable search section opened with {_entries.Count} results." : $"All search results reopened with {_entries.Count} results."); } private void RememberScope() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (_query.Length <= 0) { _preferences.LastAvatarBrowseScope = _scope; SavePreferences(); } } private void ShowPackPage() { if (!((Object)(object)_browser == (Object)null) && _browser.activeSelf && !_previewHeld) { GameObject? packPage = _packPage; if (packPage != null) { packPage.SetActive(true); } GameObject? detailPage = _detailPage; if (detailPage != null) { detailPage.SetActive(false); } GameObject? optionsPage = _optionsPage; if (optionsPage != null) { optionsPage.SetActive(false); } GameObject? keyboardPage = _keyboardPage; if (keyboardPage != null) { keyboardPage.SetActive(false); } GameObject? deletePage = _deletePage; if (deletePage != null) { deletePage.SetActive(false); } _previewGeneration++; _loadedPreviewBarcode = string.Empty; _requestedPreviewBarcode = string.Empty; _pendingPreviewAvatar = null; ClearPreviewModel(); if ((Object)(object)_previewShell != (Object)null) { _previewShell.SetActive(false); } RefreshEntries(); RefreshPackRows(); RequireInputRearm(); } } private void ShowDetailPage() { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Invalid comparison between Unknown and I4 //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_browser == (Object)null) && _browser.activeSelf) { GameObject? packPage = _packPage; if (packPage != null) { packPage.SetActive(false); } GameObject? detailPage = _detailPage; if (detailPage != null) { detailPage.SetActive(true); } GameObject? optionsPage = _optionsPage; if (optionsPage != null) { optionsPage.SetActive(false); } GameObject? keyboardPage = _keyboardPage; if (keyboardPage != null) { keyboardPage.SetActive(false); } GameObject? deletePage = _deletePage; if (deletePage != null) { deletePage.SetActive(false); } if (_query.Length == 0) { _viewState = (AvatarBrowserViewState)3; } else if ((int)_viewState != 6) { _viewState = (AvatarBrowserViewState)7; } RefreshFocusedResult(); RequireInputRearm(); } } private void ShowOptionsPage() { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) GameObject? packPage = _packPage; if (packPage != null) { packPage.SetActive(false); } GameObject? detailPage = _detailPage; if (detailPage != null) { detailPage.SetActive(false); } GameObject? optionsPage = _optionsPage; if (optionsPage != null) { optionsPage.SetActive(true); } GameObject? keyboardPage = _keyboardPage; if (keyboardPage != null) { keyboardPage.SetActive(false); } GameObject? deletePage = _deletePage; if (deletePage != null) { deletePage.SetActive(false); } _previewGeneration++; _loadedPreviewBarcode = string.Empty; _requestedPreviewBarcode = string.Empty; _pendingPreviewAvatar = null; ClearPreviewModel(); if ((Object)(object)_previewShell != (Object)null) { _previewShell.SetActive(false); } _viewState = (AvatarBrowserViewState)4; RefreshOptionLabels(); RequireInputRearm(); } private void RefreshVisiblePage() { if ((Object)(object)_detailPage != (Object)null && _detailPage.activeSelf) { RefreshFocusedResult(); } else if ((Object)(object)_packPage != (Object)null && _packPage.activeSelf) { RefreshPackRows(); } else if ((Object)(object)_optionsPage != (Object)null && _optionsPage.activeSelf) { RefreshOptionLabels(); } } private void RefreshPackRows() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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_0060: Invalid comparison between Unknown and I4 //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Invalid comparison between Unknown and I4 //IL_03e5: Unknown result type (might be due to invalid IL or missing references) //IL_03eb: Invalid comparison between Unknown and I4 //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_0402: Unknown result type (might be due to invalid IL or missing references) //IL_03fb: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_packPage == (Object)null || !_packPage.activeSelf || (Object)(object)_placeholderTexture == (Object)null) { return; } _packCursor.Keep(_packs.Count); bool flag = _query.Length > 0; if (!flag) { AvatarBrowserViewState viewState = _viewState; bool flag2 = viewState - 6 <= 1; flag = flag2; } bool flag3 = flag; if ((Object)(object)_packTitle != (Object)null) { _packTitle.text = ((_query.Length > 0) ? "RESULTS" : "GROUPS"); } if ((Object)(object)_packSearchText != (Object)null) { _packSearchText.text = "SEARCH"; } if ((Object)(object)_packPosition != (Object)null) { _packPosition.text = _packCursor.PositionText; } if ((Object)(object)_packStatus != (Object)null) { _packStatus.text = ((flag3 && _query.Length == 0) ? "Touch the search field, type, and submit to search automatically" : (((int)_viewState == 6 && _query.Length > 0) ? "Searching BONELAB mod.io..." : ((_packs.Count != 0) ? $"{_packs.Count} pack{((_packs.Count == 1) ? string.Empty : "s")} - touch a row to open" : ((_query.Length == 0) ? "No installed avatar packs found" : "No matching avatars found")))); } int num = _packCursor.Page * 4; int num2 = Math.Min(4, _packRows.Count); for (int i = 0; i < num2; i++) { PackRow row = _packRows[i]; if (!row.IsAlive) { continue; } row.Generation++; int num3 = num + i; if (num3 >= _packs.Count) { row.Identity = string.Empty; row.Button.Root.SetActive(false); row.DeleteButton.Root.SetActive(false); continue; } AvatarPackEntry pack = _packs[num3]; row.Identity = pack.Identity; row.Button.Root.SetActive(true); row.DeleteButton.Root.SetActive(pack.Installed && pack.AvatarCount > 0); row.Name.text = pack.Name; row.Creator.text = "By " + pack.Creator; row.State.text = (pack.Installed ? $"{pack.AvatarCount} AVATAR{((pack.AvatarCount == 1) ? string.Empty : "S")}" : CardStatus(pack.Avatars[0])); ((Graphic)row.State).color = (((int)pack.DownloadState == 5) ? Red : (pack.Installed ? Green : Cyan)); row.Artwork.texture = (Texture)(object)_placeholderTexture; int generation = row.Generation; if (string.IsNullOrWhiteSpace(pack.ThumbnailUrl)) { continue; } _thumbnails.Load(pack.ThumbnailUrl, delegate(Texture2D texture) { if (!_disposed && row.IsAlive && row.Generation == generation && row.Identity == pack.Identity && (Object)(object)texture != (Object)null) { row.Artwork.texture = (Texture)(object)texture; } }); } } private void OpenPackRow(int slot) { int num = _packCursor.Page * 4 + slot; if (num >= 0 && num < _packs.Count) { AvatarPackEntry val = _packs[num]; _selectedPackId = val.ModId; _entries = val.Avatars; AvatarEntry? obj = _entries.FirstOrDefault(); _selectedIdentity = ((obj != null) ? obj.Identity : null) ?? string.Empty; _cursor.Reset(_entries.Count); _log.Msg($"Avatar pack opened ({val.AvatarCount} installed avatars). Pack name is not logged."); ShowDetailPage(); } } private void ShowDeleteConfirmationForPack(int slot) { int num = _packCursor.Page * 4 + slot; if (num >= 0 && num < _packs.Count) { AvatarPackEntry val = _packs[num]; AvatarEntry val2 = ((IEnumerable<AvatarEntry>)val.Avatars).FirstOrDefault((Func<AvatarEntry, bool>)((AvatarEntry entry) => entry.Installed && !string.IsNullOrWhiteSpace(entry.Barcode))); if (!(val2 == (AvatarEntry)null)) { _selectedPackId = val.ModId; _entries = val.Avatars; _selectedIdentity = val2.Identity; _cursor.Reset(_entries.Count); _deleteReturnToPackPage = true; ShowDeleteConfirmation(); } } } private void MovePackPage(int direction) { _packCursor.Move(direction); RefreshPackRows(); } private void SetPackSort(AvatarPackSort sort) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _packSort = sort; _selectedPackId = null; RefreshEntries(); ShowPackPage(); } private void CyclePackSort() { //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_0011: 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_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) AvatarPackSort packSort = _packSort; AvatarPackSort packSort2 = (((int)packSort == 0) ? ((AvatarPackSort)1) : (((int)packSort != 1) ? ((AvatarPackSort)0) : ((AvatarPackSort)2))); SetPackSort(packSort2); ShowOptionsPage(); } private void CycleStartingView() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) BoneHubPreferences preferences = _preferences; BrowserStartupMode avatarBrowserStartupMode = _preferences.AvatarBrowserStartupMode; BrowserStartupMode avatarBrowserStartupMode2 = (((int)avatarBrowserStartupMode == 1) ? ((BrowserStartupMode)2) : (((int)avatarBrowserStartupMode != 2) ? ((BrowserStartupMode)1) : ((BrowserStartupMode)0))); preferences.AvatarBrowserStartupMode = avatarBrowserStartupMode2; RefreshOptionLabels(); SavePreferences(); } private void ToggleWrist() { _preferences.UseLeftWrist = !_preferences.UseLeftWrist; _fingertips.Invalidate(); FollowWrist(immediate: true); RefreshOptionLabels(); SavePreferences(); } private void ChangeScale(float amount) { _preferences.WatchScale = Mathf.Clamp(_preferences.WatchScale + amount, 0.65f, 1.6f); FollowWrist(immediate: true); RefreshOptionLabels(); SavePreferences(); } private void ToggleMotion() { _preferences.ReducedMotion = !_preferences.ReducedMotion; RefreshOptionLabels(); SavePreferences(); } private void RefreshOptionLabels() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 if ((Object)(object)_startingViewText != (Object)null) { TMP_Text startingViewText = _startingViewText; BrowserStartupMode avatarBrowserStartupMode = _preferences.AvatarBrowserStartupMode; string text = (((int)avatarBrowserStartupMode == 0) ? "Remember last" : (((int)avatarBrowserStartupMode != 2) ? "All avatars" : "Groups")); startingViewText.text = "Starting view: " + text; } if ((Object)(object)_sortText != (Object)null) { _sortText.text = "Library order: " + (((int)_packSort == 0) ? "Recent" : ((object)Unsafe.As<AvatarPackSort, AvatarPackSort>(ref _packSort)/*cast due to .constrained prefix*/).ToString()); } if ((Object)(object)_wristText != (Object)null) { _wristText.text = "Wrist: " + (_preferences.UseLeftWrist ? "Left" : "Right"); } if ((Object)(object)_scaleText != (Object)null) { _scaleText.text = $"UI scale: {_preferences.WatchScale:0.0}x"; } if ((Object)(object)_motionText != (Object)null) { _motionText.text = "Reduced motion: " + (_preferences.ReducedMotion ? "On" : "Off"); } } private void SavePreferences() { SavePreferencesGuardedAsync(); } private async Task SavePreferencesGuardedAsync() { try { await _service.SavePreferencesAsync(default(CancellationToken)).ConfigureAwait(continueOnCapturedContext: false); } catch (Exception ex) { Exception exception = ex; _mainThread.Enqueue(delegate { _log.Error("Could not save WristHub tablet options: " + exception.Message); }); } } private void CreateTabletHeightRuler() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_detailPage == (Object)null)) { _heightRuler = new GameObject("Avatar Height Ruler"); _heightRuler.transform.SetParent(_detailPage.transform, false); CreateImage(_heightRuler.transform, "Ruler Line", new TabletRect(-10f, -2f, 2f, 108f), DimCyan); TabletRect bounds = default(TabletRect); for (int i = 0; i < 7; i++) { float num = -56f + (float)i * 18f; Transform transform = _heightRuler.transform; bool flag = ((i == 0 || i == 6) ? true : false); float num2 = (flag ? (-15f) : (-14f)); float num3 = num; bool flag2 = ((i == 0 || i == 6) ? true : false); ((TabletRect)(ref bounds))..ctor(num2, num3, flag2 ? 12f : 8f, 2f); bool flag3 = ((i == 0 || i == 6) ? true : false); CreateImage(transform, "Ruler Tick", bounds, flag3 ? Cyan : DimCyan); } _heightText = CreateText(_heightRuler.transform, "Height after download", new TabletRect(-66f, -72f, 116f, 16f), 10f, Cyan, (TextAlignmentOptions)514, (FontStyles)1); } } private void CreateTabletProgressBar() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_detailPage == (Object)null)) { _progressRoot = new GameObject("Download Progress Capsule"); _progressRoot.transform.SetParent(_detailPage.transform, false); TabletRect progress = AvatarTabletLayout.Progress; CreateImage(_progressRoot.transform, "Progress Track", progress, new Color(0.025f, 0.105f, 0.145f, 0.98f)); Image val = CreateImage(_progressRoot.transform, "Progress Fill", new TabletRect(((TabletRect)(ref progress)).Left + 0.25f, ((TabletRect)(ref progress)).Y, 0.5f, ((TabletRect)(ref progress)).Height), new Color(Cyan.r, Cyan.g, Cyan.b, 0.82f)); _progressFill = ((Graphic)val).rectTransform; _progressText = CreateText(_progressRoot.transform, string.Empty, new TabletRect(((TabletRect)(ref progress)).X, ((TabletRect)(ref progress)).Y, ((TabletRect)(ref progress)).Width - 8f, ((TabletRect)(ref progress)).Height), 8.5f, Color.white, (TextAlignmentOptions)514, (FontStyles)1); _progressRoot.SetActive(false); } } private void RefreshEntries() { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) AvatarEntry[] array = (from avatar in _runtime.GetAllAvatars() select avatar.ToEntry()).ToArray(); IEnumerable<AvatarEntry> enumerable = ((IEnumerable<AvatarEntry>)_onlineEntries).Select((Func<AvatarEntry, AvatarEntry>)delegate(AvatarEntry entry) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (_runtime.IsModInstalled(entry.ModId)) { AvatarEntry obj = entry.<Clone>$(); obj.set_Installed(true); obj.set_DownloadState((AvatarDownloadState)4); return obj; } DownloadJob value; return (!_jobs.TryGetValue(entry.ModId, out value)) ? entry : entry.WithDownload(Map(value.State), value.Progress, value.Error); }); _mergedEntries = AvatarLibrary.Merge((IEnumerable<AvatarEntry>)array, enumerable, _query, (IEnumerable<string>)_service.RecentlyEquippedAvatarBarcodes); _packs = AvatarPackLibrary.Group((IEnumerable<AvatarEntry>)_mergedEntries, (IEnumerable<string>)_service.RecentlyEquippedAvatarBarcodes, _packSort); AvatarPackEntry val = (_selectedPackId.HasValue ? ((IEnumerable<AvatarPackEntry>)_packs).FirstOrDefault((Func<AvatarPackEntry, bool>)((AvatarPackEntry pack) => pack.ModId == _selectedPackId.Value)) : null); AvatarEntry[] array2 = (_selectedPackId.HasValue ? (from avatar in _runtime.GetAvatars(_selectedPackId.Value) select avatar.ToEntry()).OrderBy<AvatarEntry, string>((AvatarEntry entry) => entry.AvatarName, StringComparer.OrdinalIgnoreCase).ToArray() : Array.Empty<AvatarEntry>()); if (val == (AvatarPackEntry)null && array2.Length != 0) { val = AvatarPackLibrary.Group((IEnumerable<AvatarEntry>)array2, (IEnumerable<string>)_service.RecentlyEquippedAvatarBarcodes, _packSort).FirstOrDefault(); } IReadOnlyList<AvatarEntry> readOnlyList = (IReadOnlyList<AvatarEntry>)((_query.Length > 0 && _downloadsOnly) ? ((IEnumerable)AvatarLibrary.Downloadable((IEnumerable<AvatarEntry>)_mergedEntries)) : ((IEnumerable)_mergedEntries)); object entries; if (array2.Length == 0) { entries = ((val != null) ? val.Avatars : null) ?? readOnlyList; } else { IReadOnlyList<AvatarEntry> readOnlyList2 = array2; entries = readOnlyList2; } _entries = (IReadOnlyList<AvatarEntry>)entries; if (_entries.Count == 0) { _selectedIdentity = string.Empty; } else if (!_entries.Any((AvatarEntry entry) => entry.Identity == _selectedIdentity)) { _selectedIdentity = _entries[0].Identity; } _cursor.Keep(_entries.Count, IndexOf(_selectedIdentity)); if ((Object)(object)_searchText != (Object)null) { _searchText.text = "SEARCH"; } } private void RefreshFocusedResult() { //IL_005d: 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_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Invalid comparison between Unknown and I4 //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Invalid comparison between Unknown and I4 //IL_0277: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_browser == (Object)null || !_browser.activeSelf) { return; } if ((Object)(object)_searchText != (Object)null) { _searchText.text = "SEARCH"; } if ((Object)(object)_scopeText != (Object)null) { _scopeText.text = ((_query.Length <= 0) ? (((int)_scope == 0) ? "ALL AVATARS" : "GROUPS") : (_downloadsOnly ? "ALL RESULTS" : "DOWNLOADS")); } if ((Object)(object)_positionText != (Object)null) { _positionText.text = _cursor.PositionText; } AvatarEntry val = Selected(); if (val != (AvatarEntry)null) { if (_actionButton != null) { _actionButton.Root.SetActive(true); } RefreshSelection(val); return; } _artworkGeneration.Next(); _previewGeneration++; _previewAvatar = null; _loadedPreviewBarcode = string.Empty; _requestedPreviewBarcode = string.Empty; _pendingPreviewAvatar = null; _previewReady = false; _previewInteractionReady = false; _previewVisualSource = (AvatarPreviewVisualSource)0; ClearPreviewModel(); if ((Object)(object)_previewShell != (Object)null) { _previewShell.SetActive(false); } if ((Object)(object)_centralArtwork != (Object)null) { ((Component)_centralArtwork).gameObject.SetActive(true); _centralArtwork.texture = (Texture)(object)_placeholderTexture; } if ((Object)(object)_selectedName != (Object)null) { _selectedName.text = (((int)_viewState == 6) ? "Searching..." : (_downloadsOnly ? "No downloads found" : "No avatars found")); } if ((Object)(object)_creatorText !=