Created
July 17, 2025 06:32
-
-
Save Aetopia/e1149311f8dd10584550be5ed5231e89 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.IO; | |
| using System.Linq; | |
| using System.Net; | |
| using System.Net.Http; | |
| using System.Runtime.CompilerServices; | |
| using System.Runtime.InteropServices; | |
| using System.Runtime.Serialization.Json; | |
| using System.Text; | |
| using System.Threading; | |
| using System.Threading.Tasks; | |
| using System.Xml; | |
| using System.Xml.Linq; | |
| using Windows.ApplicationModel; | |
| using Windows.Globalization; | |
| using Windows.Management.Deployment; | |
| using Windows.System; | |
| sealed class MicrosoftStore | |
| { | |
| readonly struct _ : INotifyCompletion | |
| { | |
| public void OnCompleted(Action @delegate) | |
| { | |
| var @this = SynchronizationContext.Current; | |
| try { SynchronizationContext.SetSynchronizationContext(null); @delegate(); } | |
| finally { SynchronizationContext.SetSynchronizationContext(@this); } | |
| } | |
| public bool IsCompleted => SynchronizationContext.Current is null; | |
| public _ GetAwaiter() => this; | |
| public void GetResult() { } | |
| } | |
| const string DeliveryUri = "https://fe3cr.delivery.mp.microsoft.com/ClientWebService/client.asmx"; | |
| const string DeliverySecuredUri = $"{DeliveryUri}/secured"; | |
| const string DisplayCatalogUri = "https://displaycatalog.mp.microsoft.com/v7.0/products/{0}?languages=iv&market={1}"; | |
| const string TraceUri = "https://cloudflare.com/cdn-cgi/trace"; | |
| static readonly PackageManager _packageManager = new(); | |
| static readonly HttpClient _httpClient = new(); | |
| MicrosoftStore(string syncUpdates, string displayCatalogUri) => (_syncUpdates, _displayCatalogUri) = (syncUpdates, displayCatalogUri); | |
| readonly string _syncUpdates, _displayCatalogUri; | |
| static IEnumerable<XElement> LocalDescendants(XElement @this, string name) | |
| { | |
| foreach (var element in @this.Descendants().AsParallel()) | |
| { | |
| if (element.Name.LocalName == name) | |
| yield return element; | |
| } | |
| } | |
| static XElement LocalElement(XElement @this, string name) | |
| { | |
| foreach (var element in @this.Elements().AsParallel()) | |
| if (element.Name.LocalName == name) return element; | |
| return null; | |
| } | |
| static async Task<string> GetGeographicRegionAsync() | |
| { | |
| using StreamReader reader = new(await _httpClient.GetStreamAsync(TraceUri)); | |
| string @string; while ((@string = await reader.ReadLineAsync()) is not null) | |
| { | |
| var strings = @string.Split('='); | |
| if (strings[0] is "loc") return strings[1]; | |
| } | |
| return new GeographicRegion().CodeTwoLetter; | |
| } | |
| static async Task<Uri> GetAppxPackageUriAsync(Tuple<bool, string, string> appxPackage) | |
| { | |
| using StringContent content = new(string.Format(EmbeddedResources.GetExtendedUpdateInfo2, appxPackage.Item2, appxPackage.Item3), Encoding.UTF8, "application/soap+xml"); | |
| using var message = await _httpClient.PostAsync(DeliverySecuredUri, content); | |
| message.EnsureSuccessStatusCode(); | |
| using var stream = await message.Content.ReadAsStreamAsync(); | |
| foreach (var element in LocalDescendants(XElement.Load(stream), "Url")) | |
| { | |
| Uri uri = new(element.Value); | |
| if (uri.Host is "tlu.dl.delivery.mp.microsoft.com") return uri; | |
| } | |
| return null; | |
| } | |
| static async Task<string> GetCookieAsync() | |
| { | |
| using StringContent content = new(EmbeddedResources.GetCookie, Encoding.UTF8, "application/soap+xml"); | |
| using var message = await _httpClient.PostAsync(DeliveryUri, content); | |
| message.EnsureSuccessStatusCode(); | |
| using var stream = await message.Content.ReadAsStreamAsync(); | |
| return LocalDescendants(XElement.Load(stream), "EncryptedData").First().Value; | |
| } | |
| internal static async Task<MicrosoftStore> CreateAsync() | |
| { | |
| await new _(); | |
| if (RuntimeInformation.OSArchitecture is not Architecture.X64) | |
| throw new PlatformNotSupportedException(); | |
| var displayCatalogUri = string.Format(DisplayCatalogUri, "{0}", await GetGeographicRegionAsync()); | |
| var syncUpdates = string.Format(EmbeddedResources.SyncUpdates, await GetCookieAsync(), "{0}"); | |
| return new(syncUpdates, displayCatalogUri); | |
| } | |
| async Task<string> GetWuCategoryIdAsync(string productId) | |
| { | |
| var displayCatalogUri = string.Format(_displayCatalogUri, productId); | |
| using var stream = await _httpClient.GetStreamAsync(displayCatalogUri); | |
| using var reader = JsonReaderWriterFactory.CreateJsonReader(stream, XmlDictionaryReaderQuotas.Max); | |
| return XElement.Load(reader).Descendants("WuCategoryId").First().Value; | |
| } | |
| internal async Task<List<Lazy<Task<Uri>>>> GetAsync(string productId) | |
| { | |
| await new _(); | |
| List<Lazy<Task<Uri>>> packageUris = []; | |
| var syncUpdates = await SyncUpdatesAsync(await GetWuCategoryIdAsync(productId)); | |
| var appxPackages = GetAppxPackages(syncUpdates, GetAppxPackageInstallData(syncUpdates)); | |
| foreach (var appxPackage in appxPackages) | |
| packageUris.Add(new(() => GetAppxPackageUriAsync(appxPackage))); | |
| return packageUris; | |
| } | |
| async Task<XElement> SyncUpdatesAsync(string wuCategoryId) | |
| { | |
| using StringContent content = new(string.Format(_syncUpdates, wuCategoryId), Encoding.UTF8, "application/soap+xml"); | |
| using var message = await _httpClient.PostAsync(DeliveryUri, content); | |
| message.EnsureSuccessStatusCode(); | |
| return XElement.Parse(WebUtility.HtmlDecode(await message.Content.ReadAsStringAsync())); | |
| } | |
| Dictionary<string, bool> GetAppxPackageInstallData(XElement syncUpdates) | |
| { | |
| Dictionary<string, bool> appxPackageInstallData = []; | |
| foreach (var element in LocalDescendants(syncUpdates, "AppxPackageInstallData")) | |
| { | |
| var packageFileName = element.Attribute("PackageFileName").Value; | |
| if (packageFileName[packageFileName.LastIndexOf('.') + 1] is 'e') continue; | |
| var id = LocalElement(element.Parent.Parent.Parent, "ID").Value; | |
| var mainPackage = bool.Parse(element.Attribute("MainPackage").Value); | |
| appxPackageInstallData.Add(id, mainPackage); | |
| } | |
| return appxPackageInstallData; | |
| } | |
| List<Tuple<bool, string, string>> GetAppxPackages(XElement syncUpdates, Dictionary<string, bool> appxPackageInstallData) | |
| { | |
| Dictionary<string, StrongBox<(int PackageRank, string PackageVersion, bool MainPackage, XElement UpdateInfo)>> appxPackages = []; | |
| foreach (var updateInfo in LocalDescendants(syncUpdates, "UpdateInfo")) | |
| { | |
| var id = LocalElement(updateInfo, "ID").Value; | |
| if (!appxPackageInstallData.TryGetValue(id, out var mainPackage)) | |
| continue; | |
| var packageMoniker = LocalDescendants(updateInfo, "AppxMetadata").First().Attribute("PackageMoniker").Value; | |
| var packageIdentity = packageMoniker.Split('_'); | |
| var architecture = packageIdentity[2]; | |
| if (architecture is not "neutral" && architecture is not "x64") continue; | |
| var packageFamilyName = $"{packageIdentity[0]}_{packageIdentity[4]}"; | |
| var packageRank = int.Parse(LocalDescendants(updateInfo, "Properties").First().Attribute("PackageRank").Value); | |
| if (!appxPackages.TryGetValue(packageFamilyName, out var strongBox)) appxPackages.Add(packageFamilyName, new() | |
| { | |
| Value = new() | |
| { | |
| PackageRank = packageRank, | |
| PackageVersion = packageIdentity[1], | |
| MainPackage = mainPackage, | |
| UpdateInfo = updateInfo, | |
| } | |
| }); | |
| else if (strongBox.Value.PackageRank < packageRank) | |
| { | |
| strongBox.Value.PackageRank = packageRank; | |
| strongBox.Value.PackageVersion = packageIdentity[1]; | |
| strongBox.Value.UpdateInfo = updateInfo; | |
| } | |
| } | |
| return GetInstallableAppxPackages(appxPackages); | |
| } | |
| List<Tuple<bool, string, string>> GetInstallableAppxPackages(Dictionary<string, StrongBox<(int PackageRank, string PackageVersion, bool MainPackage, XElement XElement)>> appxPackages) | |
| { | |
| List<Tuple<bool, string, string>> updateIdentities = []; | |
| foreach (var appxPackage in appxPackages) | |
| { | |
| var (_, packageVersion, mainPackage, updateInfo) = appxPackage.Value.Value; | |
| var packages = _packageManager.FindPackagesForUser(string.Empty, appxPackage.Key); | |
| var package = packages.FirstOrDefault(_ => _.Id.Architecture is ProcessorArchitecture.X64 || mainPackage); | |
| if (package is not null) | |
| { | |
| if (package.SignatureKind is not PackageSignatureKind.Store) | |
| return []; | |
| var installedPackageVersion = package.Id.Version; | |
| Version installedVersion = new(installedPackageVersion.Major, installedPackageVersion.Minor, installedPackageVersion.Build, installedPackageVersion.Revision); | |
| if (new Version(packageVersion) <= installedVersion) | |
| if (mainPackage) return []; else continue; | |
| } | |
| var updateIdentity = LocalDescendants(updateInfo, "UpdateIdentity").First(); | |
| var updateId = updateIdentity.Attribute("UpdateID").Value; | |
| var revisionNumber = updateIdentity.Attribute("RevisionNumber").Value; | |
| updateIdentities.Add(new(mainPackage, updateId, revisionNumber)); | |
| } | |
| updateIdentities.Sort((x, y) => x.Item1 ? 1 : -1); return updateIdentities; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment