diff --git a/ZuneModCore/ModResources.Designer.cs b/ZuneModCore/ModResources.Designer.cs new file mode 100644 index 0000000..2e297b1 --- /dev/null +++ b/ZuneModCore/ModResources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ZuneModCore { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ModResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ModResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ZuneModCore.ModResources", typeof(ModResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] WMVCORE { + get { + object obj = ResourceManager.GetObject("WMVCORE", resourceCulture); + return ((byte[])(obj)); + } + } + } +} diff --git a/ZuneModCore/ModResources.resx b/ZuneModCore/ModResources.resx new file mode 100644 index 0000000..b4a4c4f --- /dev/null +++ b/ZuneModCore/ModResources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Resources\WMVCORE.DLL;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ZuneModCore/Mods/FeaturesOverrideMod.cs b/ZuneModCore/Mods/FeaturesOverrideMod.cs index db3ba34..af1a82b 100644 --- a/ZuneModCore/Mods/FeaturesOverrideMod.cs +++ b/ZuneModCore/Mods/FeaturesOverrideMod.cs @@ -28,27 +28,27 @@ namespace ZuneModCore.Mods // The ID is the name of the registry key, the label is the display name new AbstractBooleanUIElement("Apps", "Apps"), - //new AbstractBooleanUIElement("Art", "Art"), + new AbstractBooleanUIElement("Art", "Art"), new AbstractBooleanUIElement("Channels", "Channels"), - //new AbstractBooleanUIElement("FirstLaunchIntroVideo", "First Launch Intro Video"), + new AbstractBooleanUIElement("FirstLaunchIntroVideo", "First Launch Intro Video"), new AbstractBooleanUIElement("Games", "Games"), new AbstractBooleanUIElement("Marketplace", "Marketplace"), - //new AbstractBooleanUIElement("MBRPreview", "MBRPreview"), - //new AbstractBooleanUIElement("MBRPurchase", "MBRPurchase"), - //new AbstractBooleanUIElement("MBRRental", "MBRRental"), + new AbstractBooleanUIElement("MBRPreview", "[Marketplace] Media Preview"), + new AbstractBooleanUIElement("MBRPurchase", "[Marketplace] Media Purchase"), + new AbstractBooleanUIElement("MBRRental", "[Marketplace] Media Rental"), new AbstractBooleanUIElement("Music", "Music"), - new AbstractBooleanUIElement("MusicVideos", "MusicVideos"), + new AbstractBooleanUIElement("MusicVideos", "Music Videos"), new AbstractBooleanUIElement("Nowplaying", "Now Playing"), - //new AbstractBooleanUIElement("NowplayingArt", "Now Playing Art"), + new AbstractBooleanUIElement("NowplayingArt", "Now Playing Art"), new AbstractBooleanUIElement("Picks", "Picks"), new AbstractBooleanUIElement("Podcasts", "Podcasts"), new AbstractBooleanUIElement("QuickMixLocal", "Quick Mix (Local)"), - //new AbstractBooleanUIElement("QuickMixZMP", "Quick Mix (ZMP)"), + new AbstractBooleanUIElement("QuickMixZMP", "Quick Mix (ZMP)"), new AbstractBooleanUIElement("Quickplay", "Quickplay"), - //new AbstractBooleanUIElement("Sign In Available", "Sign In"), + new AbstractBooleanUIElement("Sign In Available", "Sign In"), new AbstractBooleanUIElement("Social", "Social"), - //new AbstractBooleanUIElement("SocialMarketplace", "Social Marketplace"), - //new AbstractBooleanUIElement("SubscriptionFreeTracks", "Subscription Free Tracks"), + new AbstractBooleanUIElement("SocialMarketplace", "Social Marketplace"), + new AbstractBooleanUIElement("SubscriptionFreeTracks", "Subscription Free Tracks"), new AbstractBooleanUIElement("Videos", "Videos"), } }; @@ -69,12 +69,14 @@ namespace ZuneModCore.Mods return Task.CompletedTask; } - public override async Task Apply() + public override async Task Apply() => await Apply(false); + + public async Task Apply(bool applyAll = false) { - // TODO: Use user choices from AbstractUI + // Use user choices from AbstractUI foreach (AbstractUIElement uiElem in OptionsUI.Items) { - if (uiElem is AbstractBooleanUIElement boolElem)// && boolElem.State) + if (uiElem is AbstractBooleanUIElement boolElem && (boolElem.State || applyAll)) { bool isSuccess = SetFeatureOverride(boolElem.Id, true); if (!isSuccess) @@ -96,13 +98,13 @@ namespace ZuneModCore.Mods return null; } - public override Task Reset() + public override async Task Reset() { foreach (AbstractUIElement uiElem in OptionsUI.Items) if (uiElem is AbstractBooleanUIElement boolElem) ResetFeatureOverride(boolElem.Id); - return Task.FromResult(null); + return null; } public static bool SetFeatureOverride(string feature, bool value) => diff --git a/ZuneModCore/Mods/VideoSyncMod.cs b/ZuneModCore/Mods/VideoSyncMod.cs index a2c7c3c..95714fd 100644 --- a/ZuneModCore/Mods/VideoSyncMod.cs +++ b/ZuneModCore/Mods/VideoSyncMod.cs @@ -26,7 +26,7 @@ namespace ZuneModCore.Mods public override AbstractUIElementGroup? OptionsUI => null; #pragma warning disable CA1416 // Validate platform compatibility - public override Task Apply() + public override async Task Apply() { // Make a backup of the original file FileVersionInfo wmvDllVersionInfo = FileVersionInfo.GetVersionInfo(WMVCORE_PATH); @@ -34,7 +34,7 @@ namespace ZuneModCore.Mods File.Copy(WMVCORE_PATH, Path.Combine(StorageDirectory, "WMVCORE.original.dll"), true); // Get the working WMVCORE.dll - string sourcePath = Path.Combine(AppContext.BaseDirectory, "Resources\\WMVCORE.dll"); + byte[] wmvDllAniv = ModResources.WMVCORE; // Get the original WMVCORE.dll FileInfo wmvDllInfo = new(WMVCORE_PATH); @@ -50,7 +50,7 @@ namespace ZuneModCore.Mods FileSecurity security = wmvDllInfo.GetAccessControl(); SecurityIdentifier? cu = WindowsIdentity.GetCurrent().User; if (cu == null) - return Task.FromResult("Failed to set permissions on WMVCORE.dll, current user was null"); + return "Failed to set permissions on WMVCORE.dll, current user was null."; // Set owner to current user security.SetOwner(cu); @@ -59,18 +59,18 @@ namespace ZuneModCore.Mods // Update the Access Control on the original WMVCORE.dll wmvDllInfo.SetAccessControl(security); - // Copy the pre-Anniversary Update WMVCORE.dll - File.Copy(sourcePath, WMVCORE_PATH, true); + // Replace with pre-Anniversary Update WMVCORE.dll + await File.WriteAllBytesAsync(wmvDllInfo.FullName, wmvDllAniv); - return Task.FromResult(null); + return null; } catch (IOException) { - return Task.FromResult($"Unable to replace '{wmvDllInfo.FullName}'. Verify that Zune and Windows Media Player are not running and try again."); + return $"Unable to replace '{wmvDllInfo.FullName}'. Verify that Zune and Windows Media Player are not running and try again."; } catch (Exception ex) { - return Task.FromResult(ex.Message)!; + return ex.Message; } } #pragma warning restore CA1416 // Validate platform compatibility diff --git a/ZuneModCore/Mods/WebservicesMod.cs b/ZuneModCore/Mods/WebservicesMod.cs index 632c7cd..93d40a8 100644 --- a/ZuneModCore/Mods/WebservicesMod.cs +++ b/ZuneModCore/Mods/WebservicesMod.cs @@ -21,17 +21,28 @@ namespace ZuneModCore.Mods public override string Author => "Joshua \"Yoshi\" Askharoun"; - public override AbstractUIElementGroup? OptionsUI => null; + public override AbstractUIElementGroup? OptionsUI => new(nameof(FeaturesOverrideMod)) + { + Title = string.Empty, + Items = + { + new AbstractTextBox("hostBox", "zunes.tk", "zune.net") + { + Title = "Host", + TooltipText = "The host where the replacement servers are located. Must be the same length as \"zune.net\"." + } + } + }; public override IReadOnlyList? DependentMods => null; - public override Task Apply() + public override async Task Apply() { // Verify that ZuneServices.dll exists FileInfo zsDllInfo = new(Path.Combine(ZuneInstallDir, "ZuneService.dll")); if (!zsDllInfo.Exists) { - return Task.FromResult($"The file '{zsDllInfo.FullName}' does not exist."); + return $"The file '{zsDllInfo.FullName}' does not exist."; } // Make a backup if it doesn't already exist @@ -53,17 +64,30 @@ namespace ZuneModCore.Mods var versionBytes = zsDllReader.ReadBytes(6); if (versionBytes[0] != '4' || versionBytes[2] != '.' || versionBytes[4] != '8') { - return Task.FromResult("This mod has not been tested on versions earlier than 4.8."); + return "This mod has not been tested on versions earlier than 4.8."; } - // Patch ZuneServices.dll to use zunes.tk instead of zune.net + // Get and validate replacement host + string oldHost = "zune.net"; + string newHost = ((AbstractTextBox)OptionsUI!.Items[0]).Value; + if (newHost.Length != oldHost.Length) + { + return $"The new host (\"{newHost}\") must have the same length as \"{oldHost}\"."; + } + var ping = await new System.Net.NetworkInformation.Ping().SendPingAsync(newHost); + if (ping.Status != System.Net.NetworkInformation.IPStatus.Success) + { + return $"Failed to reach \"{newHost}\". Ping status: {ping.Status}"; + } + + // Patch ZuneServices.dll to use the new host instead of zune.net zsDllReader.BaseStream.Position = ZUNESERVICES_ENDPOINTS_BLOCK_OFFSET; string endpointBlock = System.Text.Encoding.Unicode.GetString(zsDllReader.ReadBytes(ZUNESERVICES_ENDPOINTS_BLOCK_LENGTH)); - endpointBlock = endpointBlock.Replace("zune.net", "zunes.tk"); + endpointBlock = endpointBlock.Replace(oldHost, newHost); byte[] endpointBytes = System.Text.Encoding.Unicode.GetBytes(endpointBlock); if (endpointBytes.Length != ZUNESERVICES_ENDPOINTS_BLOCK_LENGTH) { - return Task.FromResult("Failed to safely overwrite strings in DLL."); + return "Failed to safely overwrite strings in DLL."; } zsDllWriter.Seek(ZUNESERVICES_ENDPOINTS_BLOCK_OFFSET, SeekOrigin.Begin); zsDllWriter.Write(endpointBytes); @@ -75,29 +99,36 @@ namespace ZuneModCore.Mods setOverrideSuccess &= SetFeatureOverride("Channels", true); setOverrideSuccess &= SetFeatureOverride("Games", true); setOverrideSuccess &= SetFeatureOverride("Marketplace", true); + setOverrideSuccess &= SetFeatureOverride("MBRPreview", true); + setOverrideSuccess &= SetFeatureOverride("MBRPurchase", true); + setOverrideSuccess &= SetFeatureOverride("MBRRental", true); setOverrideSuccess &= SetFeatureOverride("Music", true); setOverrideSuccess &= SetFeatureOverride("MusicVideos", true); + setOverrideSuccess &= SetFeatureOverride("Picks", true); setOverrideSuccess &= SetFeatureOverride("Podcasts", true); + setOverrideSuccess &= SetFeatureOverride("Sign In Available", true); setOverrideSuccess &= SetFeatureOverride("Social", true); + setOverrideSuccess &= SetFeatureOverride("SocialMarketplace", true); + setOverrideSuccess &= SetFeatureOverride("SubscriptionFreeTracks", true); setOverrideSuccess &= SetFeatureOverride("Videos", true); if (setOverrideSuccess != true) { - return Task.FromResult("Unable to set feature overrides. The mod was successful, but you may not be able to see it in the Zune Software."); + return "Unable to set feature overrides. The mod was successful, but you may not be able to see it in the Zune Software."; } - return Task.FromResult(null); + return null; } catch (IOException) { - return Task.FromResult($"Unable to replace '{zsDllInfo.FullName}'. Verify that the Zune software is not running and try again."); + return $"Unable to replace '{zsDllInfo.FullName}'. Verify that the Zune software is not running and try again."; } catch (Exception ex) { - return Task.FromResult(ex.Message); + return ex.Message; } } - public override Task Reset() + public override async Task Reset() { string zsDllPath = Path.Combine(ZuneInstallDir, "ZuneService.dll"); try @@ -111,25 +142,32 @@ namespace ZuneModCore.Mods setOverrideSuccess &= SetFeatureOverride("Channels", false); setOverrideSuccess &= SetFeatureOverride("Games", false); setOverrideSuccess &= SetFeatureOverride("Marketplace", false); + setOverrideSuccess &= SetFeatureOverride("MBRPreview", false); + setOverrideSuccess &= SetFeatureOverride("MBRPurchase", false); + setOverrideSuccess &= SetFeatureOverride("MBRRental", false); setOverrideSuccess &= SetFeatureOverride("Music", false); setOverrideSuccess &= SetFeatureOverride("MusicVideos", false); + setOverrideSuccess &= SetFeatureOverride("Picks", false); setOverrideSuccess &= SetFeatureOverride("Podcasts", false); + setOverrideSuccess &= SetFeatureOverride("Sign In Available", false); setOverrideSuccess &= SetFeatureOverride("Social", false); + setOverrideSuccess &= SetFeatureOverride("SocialMarketplace", false); + setOverrideSuccess &= SetFeatureOverride("SubscriptionFreeTracks", false); setOverrideSuccess &= SetFeatureOverride("Videos", false); if (setOverrideSuccess != true) { - return Task.FromResult("Unable to reset feature overrides. The mod was successfully removed, but you may still be able to see it in the Zune Software."); + return "Unable to reset feature overrides. The mod was successfully removed, but you may still be able to see it in the Zune Software."; } - return Task.FromResult(null); + return null; } catch (IOException) { - return Task.FromResult($"Unable to replace '{zsDllPath}'. Verify that the Zune software is not running and try again."); + return $"Unable to replace '{zsDllPath}'. Verify that the Zune software is not running and try again."; } catch (Exception ex) { - return Task.FromResult(ex.Message); + return ex.Message; } } } diff --git a/ZuneModCore/ZuneModCore.csproj b/ZuneModCore/ZuneModCore.csproj index 220bf66..bd600dc 100644 --- a/ZuneModCore/ZuneModCore.csproj +++ b/ZuneModCore/ZuneModCore.csproj @@ -1,4 +1,4 @@ - + net5.0 @@ -9,15 +9,26 @@ - + - - PreserveNewest - + + ResXFileCodeGenerator + ModResources.Designer.cs + + + True + True + ModResources.resx + + + + + Never + diff --git a/ZuneModdingHelper/MainWindow.xaml.cs b/ZuneModdingHelper/MainWindow.xaml.cs index 13bb110..1cbe2e1 100644 --- a/ZuneModdingHelper/MainWindow.xaml.cs +++ b/ZuneModdingHelper/MainWindow.xaml.cs @@ -47,51 +47,48 @@ namespace ZuneModdingHelper private async void ModInstallButton_Click(object sender, RoutedEventArgs e) { - var progDialog = await this.ShowProgressAsync("Getting ready...", "Preparing to apply mods", settings: defaultMetroDialogSettings); + var progDialog = await this.ShowProgressAsync("Getting ready...", "Preparing to apply mod", settings: defaultMetroDialogSettings); + Mod mod = (Mod)((FrameworkElement)sender).DataContext; Mod.ZuneInstallDir = ZuneInstallDirBox.Text; - var selectedMods = ModList.SelectedItems.Cast(); - progDialog.Maximum = selectedMods.Count() * 2; + progDialog.Maximum = 2; int numCompleted = 0; - progDialog.SetTitle("Installing mods..."); - foreach (Mod mod in selectedMods) + progDialog.SetTitle($"Installing '{mod.Title}'"); + progDialog.SetMessage($"Preparing to install..."); + await mod.Init(); + progDialog.SetProgress(++numCompleted); + + // TODO: Implement AbstractUI display for options + //if (mod.OptionsUI != null) + //{ + // var optionsDialog = new AbstractUIGroupDialog(); + // optionsDialog.OptionsUIPresenter.ViewModel = new AbstractUIElementGroupViewModel(mod.OptionsUI); + // optionsDialog.ShowDialog(); + //} + + progDialog.SetMessage($"Applying mod..."); + string applyResult = await mod.Apply(); + if (applyResult != null) { - progDialog.SetMessage($"Setting up '{mod.Title}'"); - await mod.Init(); - progDialog.SetProgress(++numCompleted); - - // TODO: Implement AbstractUI display for options - //if (mod.OptionsUI != null) - //{ - // var optionsDialog = new AbstractUIGroupDialog(); - // optionsDialog.OptionsUIPresenter.ViewModel = new AbstractUIElementGroupViewModel(mod.OptionsUI); - // optionsDialog.ShowDialog(); - //} - - progDialog.SetMessage($"Applying '{mod.Title}'"); - string applyResult = await mod.Apply(); - if (applyResult != null) - { - progDialog.SetMessage($"Failed to apply '{mod.Title}':\r\n{applyResult}"); - - Analytics.TrackEvent("Failed to apply mod", new Dictionary { - { "ModID", mod.Id }, - { "ErrorMessage", applyResult } - }); - - await Task.Delay(15000); - continue; - } - progDialog.SetProgress(++numCompleted); - - Analytics.TrackEvent("Applied mod", new Dictionary { + Analytics.TrackEvent("Failed to apply mod", new Dictionary { { "ModID", mod.Id }, + { "ErrorMessage", applyResult } }); + + await progDialog.CloseAsync(); + await this.ShowMessageAsync($"Failed to apply '{mod.Title}'", applyResult, settings: defaultMetroDialogSettings); + + return; } + progDialog.SetProgress(++numCompleted); + + Analytics.TrackEvent("Applied mod", new Dictionary { + { "ModID", mod.Id }, + }); await progDialog.CloseAsync(); - await this.ShowMessageAsync("Completed", "Finished installing selected mods", settings: defaultMetroDialogSettings); + await this.ShowMessageAsync("Completed", $"Installed '{mod.Title}'", settings: defaultMetroDialogSettings); } private async void ModResetButton_Click(object sender, RoutedEventArgs e)