Improved mod interface; Basic mod install process

This commit is contained in:
Joshua Askharoun
2021-04-26 04:28:43 -05:00
parent c8cf308cc3
commit 7161797d74
6 changed files with 141 additions and 87 deletions
+18 -4
View File
@@ -29,15 +29,29 @@ namespace ZuneModCore
public virtual Task Init() => Task.CompletedTask; public virtual Task Init() => Task.CompletedTask;
public abstract Task<bool> Apply(); public abstract Task<string?> Apply();
public abstract Task<bool> Reset(); public abstract Task<string?> Reset();
public abstract AbstractUIElementGroup? OptionsUI { get; } public abstract AbstractUIElementGroup? OptionsUI { get; }
public string StorageDirectory => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), public string StorageDirectory
"ZuneModCore", Id); {
get
{
string dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ZuneModCore", Id);
// Create the directory just in case the consumer assumes the folder exists already
Directory.CreateDirectory(dir);
return dir;
}
}
public abstract IReadOnlyList<Type>? DependentMods { get; } public abstract IReadOnlyList<Type>? DependentMods { get; }
internal FileStream OpenFileInStorageDirectory(string filename)
{
FileInfo info = new(Path.Combine(StorageDirectory, filename));
return File.Create(info.FullName);
}
} }
} }
+4 -4
View File
@@ -61,23 +61,23 @@ namespace ZuneModCore.Mods
return Task.CompletedTask; return Task.CompletedTask;
} }
public override Task<bool> Apply() public override Task<string?> Apply()
{ {
// TODO: Use user choices from AbstractUI // TODO: Use user choices from AbstractUI
foreach (AbstractUIElement uiElem in OptionsUI.Items) foreach (AbstractUIElement uiElem in OptionsUI.Items)
if (uiElem is AbstractBooleanUIElement boolElem)// && boolElem.State) if (uiElem is AbstractBooleanUIElement boolElem)// && boolElem.State)
SetFeatureOverride(boolElem.Id, true); SetFeatureOverride(boolElem.Id, true);
return Task.FromResult(true); return Task.FromResult<string?>(null);
} }
public override Task<bool> Reset() public override Task<string?> Reset()
{ {
foreach (AbstractUIElement uiElem in OptionsUI.Items) foreach (AbstractUIElement uiElem in OptionsUI.Items)
if (uiElem is AbstractBooleanUIElement boolElem) if (uiElem is AbstractBooleanUIElement boolElem)
ResetFeatureOverride(boolElem.Id); ResetFeatureOverride(boolElem.Id);
return Task.FromResult(true); return Task.FromResult<string?>(null);
} }
public static void SetFeatureOverride(string feature, bool value) => public static void SetFeatureOverride(string feature, bool value) =>
+7 -26
View File
@@ -8,16 +8,6 @@ namespace ZuneModCore.Mods
{ {
public class VideoSyncMod : Mod public class VideoSyncMod : Mod
{ {
public VideoSyncMod()
{
testButton.Clicked += TestButton_Clicked;
}
private void TestButton_Clicked(object? sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("Howdy, Zune! How are ya?");
}
public override string Title => "Fix Video Sync"; public override string Title => "Fix Video Sync";
public override string Description => public override string Description =>
@@ -25,30 +15,21 @@ namespace ZuneModCore.Mods
public override string Id => nameof(VideoSyncMod); public override string Id => nameof(VideoSyncMod);
public override AbstractUIElementGroup OptionsUI => new(nameof(VideoSyncMod)) public override AbstractUIElementGroup? OptionsUI => null;
{
Title = Title,
Items =
{
new AbstractBooleanUIElement("test", "Is this is test?"),
testButton
}
};
private readonly AbstractButton testButton = new(nameof(testButton), "Howdy Zune!", type: AbstractButtonType.Generic); public override Task<string?> Apply()
public override Task<bool> Apply()
{ {
var writer = File.CreateText(Path.Combine(StorageDirectory, "1.log")); var writer = new StreamWriter(OpenFileInStorageDirectory("1.log"));
writer.WriteLine("Hello world"); writer.WriteLine("Hello world");
writer.Flush(); writer.Flush();
writer.Close(); writer.Close();
return Task.FromResult(true);
return Task.FromResult<string?>(null);
} }
public override Task<bool> Reset() public override Task<string?> Reset()
{ {
return Task.FromResult(true); return Task.FromResult<string?>(null);
} }
public override IReadOnlyList<Type>? DependentMods => null; public override IReadOnlyList<Type>? DependentMods => null;
+54 -36
View File
@@ -5,10 +5,6 @@ using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using static ZuneModCore.Mods.FeaturesOverrideMod; using static ZuneModCore.Mods.FeaturesOverrideMod;
#if DEBUG
using System.Diagnostics;
#endif
namespace ZuneModCore.Mods namespace ZuneModCore.Mods
{ {
public class WebservicesMod : Mod public class WebservicesMod : Mod
@@ -31,48 +27,70 @@ namespace ZuneModCore.Mods
public override IReadOnlyList<Type>? DependentMods => null; public override IReadOnlyList<Type>? DependentMods => null;
public override Task<bool> Apply() public override Task<string?> Apply()
{ {
// Open ZuneServices.dll // Verify that ZuneServices.dll exists
FileInfo zsDllInfo = new(Path.Combine(ZuneInstallDir, "ZuneService.dll")); FileInfo zsDllInfo = new(Path.Combine(ZuneInstallDir, "ZuneService.dll"));
if (!zsDllInfo.Exists) if (!zsDllInfo.Exists)
return Task.FromResult(true); {
using FileStream zsDll = zsDllInfo.Open(FileMode.Open); return Task.FromResult<string?>($"The file '{zsDllInfo.FullName}' does not exist.");
using BinaryWriter zsDllWriter = new(zsDll); }
using BinaryReader zsDllReader = new(zsDll);
// Verify that the DLL is from v4.8 (other versions not tested) // Make a backup of the file
zsDllReader.BaseStream.Position = 0x12C824; File.Copy(zsDllInfo.FullName, Path.Combine(StorageDirectory, "ZuneService.original.dll"));
var versionBytes = zsDllReader.ReadBytes(ZUNE_4_8_VERSION_BYTES.Length * 2);
if (versionBytes[0] != '4' || versionBytes[2] != '.' || versionBytes[4] != '8')
return Task.FromResult(true);
// Patch ZuneServices.dll to use zunes.tk instead of zune.net try
zsDllReader.BaseStream.Position = ZUNESERVICES_ENDPOINTS_BLOCK_OFFSET; {
string endpointBlock = System.Text.Encoding.Unicode.GetString(zsDllReader.ReadBytes(ZUNESERVICES_ENDPOINTS_BLOCK_LENGTH)); // Open the file
endpointBlock = endpointBlock.Replace("zune.net", "zunes.tk"); using FileStream zsDll = zsDllInfo.Open(FileMode.Open);
byte[] endpointBytes = System.Text.Encoding.Unicode.GetBytes(endpointBlock); using BinaryWriter zsDllWriter = new(zsDll);
if (endpointBytes.Length != ZUNESERVICES_ENDPOINTS_BLOCK_LENGTH) using BinaryReader zsDllReader = new(zsDll);
return Task.FromResult(false);
zsDllWriter.Seek(ZUNESERVICES_ENDPOINTS_BLOCK_OFFSET, SeekOrigin.Begin); // Verify that the DLL is from v4.8 (other versions not tested)
zsDllWriter.Write(endpointBytes); zsDllReader.BaseStream.Position = 0x12C824;
var versionBytes = zsDllReader.ReadBytes(ZUNE_4_8_VERSION_BYTES.Length * 2);
if (versionBytes[0] != '4' || versionBytes[2] != '.' || versionBytes[4] != '8')
{
return Task.FromResult<string?>("This mod has not been tested on versions earlier than 4.8.");
}
// Patch ZuneServices.dll to use zunes.tk 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");
byte[] endpointBytes = System.Text.Encoding.Unicode.GetBytes(endpointBlock);
if (endpointBytes.Length != ZUNESERVICES_ENDPOINTS_BLOCK_LENGTH)
{
return Task.FromResult<string?>("Failed to safely overwrite strings in DLL.");
}
zsDllWriter.Seek(ZUNESERVICES_ENDPOINTS_BLOCK_OFFSET, SeekOrigin.Begin);
zsDllWriter.Write(endpointBytes);
// Enable all feature overrides affected by new servers // Enable all feature overrides affected by new servers
SetFeatureOverride("Apps", true); SetFeatureOverride("Apps", true);
SetFeatureOverride("Channels", true); SetFeatureOverride("Channels", true);
SetFeatureOverride("Games", true); SetFeatureOverride("Games", true);
SetFeatureOverride("Marketplace", true); SetFeatureOverride("Marketplace", true);
SetFeatureOverride("Music", true); SetFeatureOverride("Music", true);
SetFeatureOverride("MusicVideos", true); SetFeatureOverride("MusicVideos", true);
SetFeatureOverride("Podcasts", true); SetFeatureOverride("Podcasts", true);
SetFeatureOverride("Social", true); SetFeatureOverride("Social", true);
SetFeatureOverride("Videos", true); SetFeatureOverride("Videos", true);
return Task.FromResult(true); return Task.FromResult<string?>(null);
}
catch (UnauthorizedAccessException)
{
return Task.FromResult<string?>($"Failed to open '{zsDllInfo.FullName}'. Verify that the Zune software is not running and try again.");
}
catch (Exception ex)
{
return Task.FromResult<string?>(ex.Message);
}
} }
public override Task<bool> Reset() public override Task<string?> Reset()
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
+30 -13
View File
@@ -1,15 +1,15 @@
<mah:MetroWindow x:Class="ZuneModdingHelper.MainWindow" <mah:MetroWindow x:Class="ZuneModdingHelper.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ZuneModdingHelper" xmlns:local="clr-namespace:ZuneModdingHelper"
xmlns:core="clr-namespace:ZuneModCore;assembly=ZuneModCore" xmlns:core="clr-namespace:ZuneModCore;assembly=ZuneModCore"
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro" xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
mc:Ignorable="d" mc:Ignorable="d"
Title="Zune Modding Helper" Height="450" Width="800" Title="Zune Modding Helper" Height="450" Width="800"
Loaded="Window_Loaded" Loaded="Window_Loaded"
Background="LightGray"> Background="#F0F0F0">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
@@ -53,8 +53,25 @@
<TextBlock Text="Zune Software install directory:" Margin="4,4,0,0"/> <TextBlock Text="Zune Software install directory:" Margin="4,4,0,0"/>
<TextBox x:Name="ZuneInstallDirBox" Grid.Row="1" Margin="4,0,0,4"/> <TextBox x:Name="ZuneInstallDirBox" Grid.Row="1" Margin="4,0,0,4"/>
<Button Content="Install" ToolTip="Install selected mods" Click="InstallModsButton_Click" Grid.RowSpan="2" Grid.Column="1" <Button x:Name="InstallModsButton" Content="Install" ToolTip="Install selected mods" Click="InstallModsButton_Click"
VerticalAlignment="Center" HorizontalAlignment="Center" Padding="4" Margin="4"/> Grid.RowSpan="2" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="4"/>
</Grid> </Grid>
<Border x:Name="ProgressBorder" Padding="8" Margin="8" Background="White" Visibility="Collapsed" Grid.RowSpan="2">
<Grid>
<StackPanel Margin="8" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock x:Name="ProgressDesc" Text="Preparing..." FontSize="14" FontWeight="SemiBold"
HorizontalAlignment="Center" TextAlignment="Center"/>
<mah:MetroProgressBar x:Name="Progress" Value="0" MinWidth="200"/>
</StackPanel>
<Button x:Name="ProgressCloseButton" Content="Close" Visibility="Collapsed" Click="ProgressCloseButton_Click"
HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
</Grid>
<Border.Effect>
<DropShadowEffect BlurRadius="8" ShadowDepth="2" Direction="-90" Opacity="0.25"/>
</Border.Effect>
</Border>
</Grid> </Grid>
</mah:MetroWindow> </mah:MetroWindow>
+28 -4
View File
@@ -36,17 +36,21 @@ namespace ZuneModdingHelper
private async void InstallModsButton_Click(object sender, RoutedEventArgs e) private async void InstallModsButton_Click(object sender, RoutedEventArgs e)
{ {
ProgressCloseButton.Visibility = Visibility.Collapsed;
ProgressBorder.Visibility = Visibility.Visible;
ModList.IsEnabled = false;
InstallModsButton.IsEnabled = false;
Mod.ZuneInstallDir = ZuneInstallDirBox.Text; Mod.ZuneInstallDir = ZuneInstallDirBox.Text;
var selectedMods = ModList.SelectedItems.Cast<Mod>(); var selectedMods = ModList.SelectedItems.Cast<Mod>();
double progTotal = selectedMods.Count() * 2; double progTotal = selectedMods.Count() * 2;
double progCompleted = 0; double progCompleted = 0;
double progPercent = 0;
foreach (Mod mod in selectedMods) foreach (Mod mod in selectedMods)
{ {
ProgressDesc.Text = $"Setting up '{mod.Title}'...";
await mod.Init(); await mod.Init();
progPercent = ++progCompleted / progTotal; Progress.Value = ++progCompleted / progTotal * 100;
// TODO: Implement AbstractUI display for options // TODO: Implement AbstractUI display for options
//if (mod.OptionsUI != null) //if (mod.OptionsUI != null)
@@ -56,9 +60,29 @@ namespace ZuneModdingHelper
// optionsDialog.ShowDialog(); // optionsDialog.ShowDialog();
//} //}
await mod.Apply(); ProgressDesc.Text = $"Applying '{mod.Title}'...";
progPercent = ++progCompleted / progTotal; string applyResult = await mod.Apply();
if (applyResult != null)
{
ProgressDesc.Text = $"Failed to apply '{mod.Title}':\r\n{applyResult}";
await Task.Delay(15000);
continue;
}
Progress.Value = ++progCompleted / progTotal * 100;
} }
ProgressDesc.Text = "Completed";
ProgressCloseButton.Visibility = Visibility.Visible;
ModList.IsEnabled = true;
InstallModsButton.IsEnabled = true;
}
private void ProgressCloseButton_Click(object sender, RoutedEventArgs e)
{
ProgressCloseButton.Visibility = Visibility.Collapsed;
ProgressBorder.Visibility = Visibility.Collapsed;
ProgressDesc.Text = "Preparing...";
Progress.Value = 0;
} }
} }
} }