diff --git a/Controls/AppTile.xaml.cs b/Controls/AppTile.xaml.cs index 036dad0..292b5e2 100644 --- a/Controls/AppTile.xaml.cs +++ b/Controls/AppTile.xaml.cs @@ -109,7 +109,7 @@ await progress.CreateAsync(async () => { await WinDurangoPatcher.UnpatchPackage(_package, progress); }); - if (!progress.failed) + if (!progress.Failed) { NoticeDialog good = new NoticeDialog($"WinDurango has been uninstalled from package {_Name}", "Uninstalled"); await good.ShowAsync(); @@ -126,7 +126,7 @@ await progress.CreateAsync(async () => await WinDurangoPatcher.PatchPackage(_package, false, progress); }); - if (!progress.failed) + if (!progress.Failed) { NoticeDialog good = new NoticeDialog($"WinDurango has been installed in package {_Name}", "Installed"); await good.ShowAsync(); @@ -134,6 +134,7 @@ await progress.CreateAsync(async () => App.MainWindow.ReloadAppList(); } + // TODO: This can probably be improved public AppTile(string familyName) { _familyName = familyName; @@ -152,7 +153,45 @@ public AppTile(string familyName) _Version = $"{_package.Id.Version.Major.ToString() ?? "U"}.{_package.Id.Version.Minor.ToString() ?? "U"}.{_package.Id.Version.Build.ToString() ?? "U"}.{_package.Id.Version.Revision.ToString() ?? "U"}"; _Logo = _package.Logo; - string ss = Packages.GetSplashScreenPath(_package); + ManifestInfo mfInfo = _package.GetProperties(); + + string ss = String.Empty; + + // TODO: This seems slow. + if (!string.IsNullOrEmpty(mfInfo.SplashScreen)) + { + ss = Path.Combine(_package.InstalledPath, mfInfo.SplashScreen); + // if it doesn't exist it probably has some scale thing + if (!File.Exists(ss)) + { + for (int i = 100; i < 400; i += 100) + { + string path = Path.Combine(_package.InstalledPath, Path.GetDirectoryName(mfInfo.SplashScreen), Path.GetFileNameWithoutExtension(mfInfo.SplashScreen) + $".scale-{i}.png"); + if (File.Exists(path)) + { + ss = path; + break; + } + } + } + } else if (!string.IsNullOrEmpty(mfInfo.WideLogo)) + { + ss = Path.Combine(_package.InstalledPath, mfInfo.WideLogo); + // if it doesn't exist it probably has some scale thing + if (!File.Exists(ss)) + { + for (int i = 100; i < 400; i += 100) + { + string path = Path.Combine(_package.InstalledPath, Path.GetDirectoryName(mfInfo.WideLogo), Path.GetFileNameWithoutExtension(mfInfo.WideLogo) + $".scale-{i}.png"); + if (File.Exists(path)) + { + ss = path; + break; + } + } + } + } + IReadOnlyList appListEntries = null; try { @@ -167,7 +206,7 @@ public AppTile(string familyName) if (firstAppListEntry == null) Logger.WriteWarning($"Could not get the applist entry of \"{_Name}\""); - if (ss == null || !File.Exists(ss)) + if (String.IsNullOrEmpty(ss) || !File.Exists(ss)) { try { diff --git a/Dialogs/AppListDialog.xaml.cs b/Dialogs/AppListDialog.xaml.cs index e68359e..8e7a9ac 100644 --- a/Dialogs/AppListDialog.xaml.cs +++ b/Dialogs/AppListDialog.xaml.cs @@ -38,6 +38,7 @@ private void AppListView_Loaded(object sender, RoutedEventArgs e) foreach (Package pkg in Packages) { // if we already have the package "installed" we will skip it (not show it in the AppListView) + // maybe this behavior should change? if (App.InstalledPackages.GetPackages().Find(p => p.FamilyName == pkg.Id.FamilyName) != null) continue; ListViewItem item = new() { MinWidth = 200 }; diff --git a/Dialogs/ProgressController.cs b/Dialogs/ProgressController.cs index 02ffdd7..0eda20d 100644 --- a/Dialogs/ProgressController.cs +++ b/Dialogs/ProgressController.cs @@ -70,7 +70,7 @@ public async Task Fail(string title, Exception ex) }); } - public bool failed + public bool Failed { get => hasFailed; } @@ -87,7 +87,7 @@ public void Show() { _dialog.DispatcherQueue.TryEnqueue(() => { - _dialog.ShowAsync(); + _ = _dialog.ShowAsync(); }); } diff --git a/Pages/AboutPage.xaml.cs b/Pages/AboutPage.xaml.cs index 43e3a38..6df43ce 100644 --- a/Pages/AboutPage.xaml.cs +++ b/Pages/AboutPage.xaml.cs @@ -3,6 +3,7 @@ using System; using System.IO; using WinDurango.UI.Controls; +using WinDurango.UI.Utils; namespace WinDurango.UI.Pages diff --git a/README.md b/README.md index 6f291ca..e1bb894 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ For building you'll need Visual Studio 2022 with the following: - [X] Scan for already installed EraOS/XUWP stuff - [X] Allow for any existing installed package to be added to the applist - [ ] Built in updater + - [ ] Fitting place for extra xbox-specific info - [ ] Resize content to fit to screen - [X] Allow for search @@ -29,6 +30,7 @@ For building you'll need Visual Studio 2022 with the following: - [X] Fix icon in the titlebar - [ ] Repo contributors on the about screen - [ ] Get Fluent Thin working + - [ ] Use InfoBar for many progress related things (such as registering package, etc) - [ ] Add versioning to the InstalledPackages json (as in versioning the JSON file itself) - [ ] Make the Package stuff not rely on UI so much, handle that somewhere else. - [X] Fix crash when installation errors diff --git a/Utils/Packages.cs b/Utils/Packages.cs index 0628025..49fe5f3 100644 --- a/Utils/Packages.cs +++ b/Utils/Packages.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Windows.ApplicationModel.DynamicDependency; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -7,6 +8,7 @@ using System.Xml.Linq; using Windows.ApplicationModel; using Windows.Management.Deployment; +using Windows.Networking; using WinDurango.UI.Dialogs; using WinUI3Localizer; using static WinDurango.UI.Localization.Locale; @@ -17,14 +19,35 @@ namespace WinDurango.UI.Utils // this class sucks so much that we literally had to stop bc we didn't know how to rewrite a function and make it still work with the UI stuff // update: this was fixed same commit. #nullable enable + public class ManifestInfo { public string? DisplayName { get; set; } public string? PublisherDisplayName { get; set; } + public string? StoreLogo { get; set; } public string? Logo { get; set; } + public string? WideLogo { get; set; } + public string? SmallLogo { get; set; } public string? Description { get; set; } + public string? SplashScreen { get; set; } + public (string? Name, string? Version)? OSPackageDependency { get; set; } + } + + public class XbManifestInfo + { + public string[]? Ratings { get; set; } + public string? TitleId { get; set; } + public string? OsName { get; set; } + public string? ApplicationEnvironment { get; set; } + public bool? IsBackCompat { get; set; } + + public bool IsEra(ManifestInfo mfInfo) + { + return (this.OsName?.Equals("era", StringComparison.OrdinalIgnoreCase) ?? false) || + (mfInfo.OSPackageDependency?.Name == "Microsoft.GameOs"); + } } - public abstract class Packages + public static class Packages { // TODO: Make these methods not use the GUI, instead just throw an exception and catch it in the area where the method is actually invoked. /// @@ -56,85 +79,85 @@ public static ManifestInfo GetPropertiesFromManifest(string manifestPath) } XDocument doc = XDocument.Parse(manifest); - XElement? package = doc.Descendants().FirstOrDefault(e => e.Name.LocalName == "Package"); - - XElement? properties = package?.Descendants().FirstOrDefault(e => e.Name.LocalName == "Properties"); + XElement? defaultTile = doc?.Descendants().FirstOrDefault(e => e.Name.LocalName == "DefaultTile"); + XElement? visualElements = doc?.Descendants().FirstOrDefault(e => e.Name.LocalName == "VisualElements"); + + manifestInfo.WideLogo = defaultTile?.Attribute("WideLogo")?.Value; + manifestInfo.StoreLogo = doc?.Descendants().FirstOrDefault(e => e.Name.LocalName == "Logo")?.Value; + manifestInfo.Logo = visualElements?.Attribute("Logo")?.Value; + manifestInfo.SmallLogo = visualElements?.Attribute("SmallLogo")?.Value; + manifestInfo.DisplayName = doc?.Descendants().FirstOrDefault(e => e.Name.LocalName == "DisplayName")?.Value; + manifestInfo.PublisherDisplayName = doc?.Descendants().FirstOrDefault(e => e.Name.LocalName == "PublisherDisplayName")?.Value; + manifestInfo.Description = doc?.Descendants().FirstOrDefault(e => e.Name.LocalName == "Description")?.Value; + manifestInfo.SplashScreen = doc?.Descendants().FirstOrDefault(e => e.Name.LocalName == "SplashScreen")?.Attribute("Image")?.Value; + manifestInfo.OSPackageDependency = (doc?.Descendants().FirstOrDefault(e => e.Name.LocalName == "OSPackageDependency")?.Attribute("Name")?.Value, doc?.Descendants().FirstOrDefault(e => e.Name.LocalName == "OSPackageDependency")?.Attribute("Version")?.Value); - manifestInfo.Logo = properties?.Descendants().FirstOrDefault(e => e.Name.LocalName == "Logo")?.Value; - manifestInfo.DisplayName = properties?.Descendants().FirstOrDefault(e => e.Name.LocalName == "DisplayName")?.Value; - manifestInfo.PublisherDisplayName = properties?.Descendants().FirstOrDefault(e => e.Name.LocalName == "PublisherDisplayName")?.Value; - manifestInfo.Description = properties?.Descendants().FirstOrDefault(e => e.Name.LocalName == "Description")?.Value; return manifestInfo; } - /// - /// Installs an Xbox Package with the folder given to it - /// - /// - /// This is simply meant for being able to pass a folder containing the Mount directory. - /// - public static async Task InstallXPackageAsync(string dir, ProgressController controller, bool addInstalledPackage = true) + public static ManifestInfo GetProperties(this Package pkg) { - string mountDir = Path.Combine(dir, "Mount"); + string installPath = pkg.InstalledPath; + string manifestPath = Path.Combine(installPath, "AppxManifest.xml"); - if (!Directory.Exists(mountDir)) - { - await new NoticeDialog(GetLocalizedText($"/Errors/NotFound", mountDir), "Error").ShowAsync(); - return; - } + return GetPropertiesFromManifest(manifestPath); + } - await InstallPackageAsync(new Uri(mountDir + "\\AppxManifest.xml", UriKind.Absolute), controller, addInstalledPackage); + public static XbManifestInfo GetXbProperties(this Package pkg) + { + string installPath = pkg.InstalledPath; + string manifestPath = Path.Combine(installPath, "AppxManifest.xml"); + + return GetXbProperties(manifestPath); } /// - /// Gets a Package's splash screen, if none is found... it returns null. + /// Gets Xbox specific properties from the provided AppxManifest /// - /// - /// It checks the package's AppxManifest for Package.Applications.Application.VisualElements.SplashScreen.Image - /// - public static string GetSplashScreenPath(Package pkg) + public static XbManifestInfo GetXbProperties(string manifestPath) { - try - { - string installPath = pkg.InstalledPath; - string manifestPath = Path.Combine(installPath, "AppxManifest.xml"); - - if (!File.Exists(manifestPath)) - return null; + XbManifestInfo info = new(); - string manifest; - using (var stream = File.OpenRead(manifestPath)) - { - var reader = new StreamReader(stream); - manifest = reader.ReadToEnd(); - } + if (!File.Exists(manifestPath)) + return info; - XDocument doc = XDocument.Parse(manifest); - XElement package = doc.Descendants().FirstOrDefault(e => e.Name.LocalName == "Package"); - if (package == null) return null; + string manifest; + using (var stream = File.OpenRead(manifestPath)) + { + var reader = new StreamReader(stream); + manifest = reader.ReadToEnd(); + } - XElement applications = package.Descendants().FirstOrDefault(e => e.Name.LocalName == "Applications"); - if (applications == null) return null; + XDocument doc = XDocument.Parse(manifest); - XElement application = applications.Descendants().FirstOrDefault(e => e.Name.LocalName == "Application"); - if (application == null) return null; + info.Ratings = doc?.Descendants().FirstOrDefault(e => e.Name.LocalName == "Ratings")?.Descendants().Select(e => e.Value).ToArray(); + info.TitleId = doc?.Descendants().FirstOrDefault(e => e.Name.LocalName == "XboxLive")?.Attribute("TitleId")?.Value; + info.OsName = doc?.Descendants().FirstOrDefault(e => e.Name.LocalName == "OSName")?.Value; + info.ApplicationEnvironment = doc?.Descendants().FirstOrDefault(e => e.Name.LocalName == "ApplicationEnvironment")?.Value; + bool.TryParse(doc?.Descendants().FirstOrDefault(e => e.Name.LocalName == "XboxFission")?.Attribute("IsFissionApp")?.Value, out bool isBackCompat); - XElement visualElements = application.Descendants().FirstOrDefault(e => e.Name.LocalName == "VisualElements"); - if (visualElements == null) return null; + info.IsBackCompat = isBackCompat; + return info; + } - XElement splashScreen = visualElements.Descendants().FirstOrDefault(e => e.Name.LocalName == "SplashScreen"); - if (splashScreen == null) return null; - string imagePath = splashScreen.Attribute("Image")?.Value; - if (imagePath == null) return null; + /// + /// Installs an Xbox Package with the folder given to it + /// + /// + /// This is simply meant for being able to pass a folder containing the Mount directory. + /// + public static async Task InstallXPackageAsync(string dir, ProgressController controller, bool addInstalledPackage = true) + { + string mountDir = Path.Combine(dir, "Mount"); - string splashScreenPath = Path.Combine(installPath, imagePath); - return splashScreenPath; - } - catch + if (!Directory.Exists(mountDir)) { - return null; + await new NoticeDialog(GetLocalizedText($"/Errors/NotFound", mountDir), "Error").ShowAsync(); + return; } + + await InstallPackageAsync(new Uri(mountDir + "\\AppxManifest.xml", UriKind.Absolute), controller, addInstalledPackage); } /// @@ -245,7 +268,7 @@ public static async Task RemovePackage(Package package, ProgressController contr /// /// Gets a Package by it's Family Name /// - public static Package GetPackageByFamilyName(string familyName) + public static Package? GetPackageByFamilyName(string familyName) { var packageManager = new PackageManager(); var packages = packageManager.FindPackagesForUser(null, familyName); @@ -256,7 +279,7 @@ public static Package GetPackageByFamilyName(string familyName) /// /// Gets the most recent installed package /// - public static Package GetMostRecentInstalledPackage() + public static Package? GetMostRecentInstalledPackage() { var sid = WindowsIdentity.GetCurrent().User?.Value; var pm = new PackageManager(); @@ -270,4 +293,4 @@ public static Package GetMostRecentInstalledPackage() return newestPackage; } } -} \ No newline at end of file +} diff --git a/Utils/XHandler.cs b/Utils/XHandler.cs index 5be1020..2fb319c 100644 --- a/Utils/XHandler.cs +++ b/Utils/XHandler.cs @@ -8,84 +8,16 @@ namespace WinDurango.UI.Utils { public class XHandler { - // unused because sucks - public static (string? DisplayName, string? Description, string? SplashScreen, string? SmallLogo, string? WideLogo, string? Logo) GetVisualElementsInfo(Package package) - { - string installPath = package.InstalledPath; - string manifestPath = Path.Combine(installPath, "AppxManifest.xml"); - - if (!File.Exists(manifestPath)) - return (null, null, null, null, null, null); - - string manifest; - using (var stream = File.OpenRead(manifestPath)) - { - var reader = new StreamReader(stream); - manifest = reader.ReadToEnd(); - } - - XDocument doc = XDocument.Parse(manifest); - XElement xmlPackage = doc.Descendants().FirstOrDefault(e => e.Name.LocalName == "Package"); - XElement application = xmlPackage?.Descendants().LastOrDefault(e => e.Name.LocalName == "Application"); - XElement visualElements = application?.Descendants().FirstOrDefault(e => e.Name.LocalName == "VisualElements"); - if (visualElements == null) - return (null, null, null, null, null, null); - - XElement ss = visualElements?.Descendants().FirstOrDefault(e => e.Name.LocalName == "SplashScreen") ?? null; - XElement defaultTile = visualElements?.Descendants().FirstOrDefault(e => e.Name.LocalName == "DefaultTile") ?? null; - - // holy hell this is ew - - string? DisplayName = visualElements?.Attribute("DisplayName")?.Value ?? null; - string? Description = visualElements?.Attribute("Description")?.Value ?? null; - string? SplashScreen = ss?.Attribute("SplashScreen")?.Value ?? null; - string? SmallLogo = visualElements?.Attribute("SmallLogo")?.Value ?? null; - string? WideLogo = defaultTile?.Attribute("WideLogo")?.Value ?? null; - string? Logo = visualElements?.Attribute("Logo")?.Value ?? null; - - return ( - DisplayName == null || DisplayName.StartsWith("ms-resource:") ? null : DisplayName, - Description == null || Description.StartsWith("ms-resource:") ? null : Description, - SplashScreen == null || SplashScreen.StartsWith("ms-resource:") ? null : SplashScreen, - SmallLogo == null || SmallLogo.StartsWith("ms-resource:") ? null : SmallLogo, - WideLogo == null || WideLogo.StartsWith("ms-resource:") ? null : WideLogo, - Logo == null || Logo.StartsWith("ms-resource:") ? null : Logo - ); - - } - public static List GetXPackages(List packages) { // first try implementation and it worked hell yeah List result = []; foreach (Package package in packages) { - string installPath = package.InstalledPath; - string manifestPath = Path.Combine(installPath, "AppxManifest.xml"); - - if (!File.Exists(manifestPath)) - continue; - - string manifest; - using (FileStream stream = File.OpenRead(manifestPath)) - { - StreamReader reader = new(stream); - manifest = reader.ReadToEnd(); - } - - XDocument doc = XDocument.Parse(manifest); - XElement xmlPackage = doc.Descendants().FirstOrDefault(e => e.Name.LocalName == "Package"); - if (xmlPackage == null) continue; - - XElement prerequisites = xmlPackage.Descendants().FirstOrDefault(e => e.Name.LocalName == "Prerequisites"); - XElement dependencies = xmlPackage?.Descendants().FirstOrDefault(e => e.Name.LocalName == "Dependencies"); - XElement osName = prerequisites?.Descendants().FirstOrDefault(e => e.Name.LocalName == "OSName"); - XElement osPackageDependency = dependencies?.Descendants().FirstOrDefault(e => e.Name.LocalName == "OSPackageDependency"); - - if ((osName != null && osName.Value.Equals("era", System.StringComparison.InvariantCultureIgnoreCase)) || (osPackageDependency != null && osPackageDependency.Attribute("Name")?.Value == "Microsoft.GameOs")) - { + XbManifestInfo xbManifestInfo = package.GetXbProperties(); + ManifestInfo manifestInfo = package.GetProperties(); + if (xbManifestInfo.IsEra(manifestInfo)) result.Add(package); - } } Logger.WriteInformation($"Found {result.Count} Era/XbUWP packages"); return result; diff --git a/WinDurango.csproj b/WinDurango.csproj index 7473fa7..c9b7f35 100644 --- a/WinDurango.csproj +++ b/WinDurango.csproj @@ -15,8 +15,8 @@ False Assets\icon.ico 10.0.19041.0 - 0.1.2 - 0.1.2 + 0.1.2.1 + 0.1.2.1 WinDurango UI true @@ -65,6 +65,7 @@ WinDurango https://github.com/WinDurango/WinDurango.UI disable + 0.1.2.1