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 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 string StorageDirectory => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"ZuneModCore", Id);
public string StorageDirectory
{
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; }
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;
}
public override Task<bool> Apply()
public override Task<string?> Apply()
{
// TODO: Use user choices from AbstractUI
foreach (AbstractUIElement uiElem in OptionsUI.Items)
if (uiElem is AbstractBooleanUIElement boolElem)// && boolElem.State)
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)
if (uiElem is AbstractBooleanUIElement boolElem)
ResetFeatureOverride(boolElem.Id);
return Task.FromResult(true);
return Task.FromResult<string?>(null);
}
public static void SetFeatureOverride(string feature, bool value) =>
+7 -26
View File
@@ -8,16 +8,6 @@ namespace ZuneModCore.Mods
{
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 Description =>
@@ -25,30 +15,21 @@ namespace ZuneModCore.Mods
public override string Id => nameof(VideoSyncMod);
public override AbstractUIElementGroup OptionsUI => new(nameof(VideoSyncMod))
{
Title = Title,
Items =
{
new AbstractBooleanUIElement("test", "Is this is test?"),
testButton
}
};
public override AbstractUIElementGroup? OptionsUI => null;
private readonly AbstractButton testButton = new(nameof(testButton), "Howdy Zune!", type: AbstractButtonType.Generic);
public override Task<bool> Apply()
public override Task<string?> Apply()
{
var writer = File.CreateText(Path.Combine(StorageDirectory, "1.log"));
var writer = new StreamWriter(OpenFileInStorageDirectory("1.log"));
writer.WriteLine("Hello world");
writer.Flush();
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;
+54 -36
View File
@@ -5,10 +5,6 @@ using System.IO;
using System.Threading.Tasks;
using static ZuneModCore.Mods.FeaturesOverrideMod;
#if DEBUG
using System.Diagnostics;
#endif
namespace ZuneModCore.Mods
{
public class WebservicesMod : Mod
@@ -31,48 +27,70 @@ namespace ZuneModCore.Mods
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"));
if (!zsDllInfo.Exists)
return Task.FromResult(true);
using FileStream zsDll = zsDllInfo.Open(FileMode.Open);
using BinaryWriter zsDllWriter = new(zsDll);
using BinaryReader zsDllReader = new(zsDll);
{
return Task.FromResult<string?>($"The file '{zsDllInfo.FullName}' does not exist.");
}
// Verify that the DLL is from v4.8 (other versions not tested)
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(true);
// Make a backup of the file
File.Copy(zsDllInfo.FullName, Path.Combine(StorageDirectory, "ZuneService.original.dll"));
// 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(false);
zsDllWriter.Seek(ZUNESERVICES_ENDPOINTS_BLOCK_OFFSET, SeekOrigin.Begin);
zsDllWriter.Write(endpointBytes);
try
{
// Open the file
using FileStream zsDll = zsDllInfo.Open(FileMode.Open);
using BinaryWriter zsDllWriter = new(zsDll);
using BinaryReader zsDllReader = new(zsDll);
// Verify that the DLL is from v4.8 (other versions not tested)
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
SetFeatureOverride("Apps", true);
SetFeatureOverride("Channels", true);
SetFeatureOverride("Games", true);
SetFeatureOverride("Marketplace", true);
SetFeatureOverride("Music", true);
SetFeatureOverride("MusicVideos", true);
SetFeatureOverride("Podcasts", true);
SetFeatureOverride("Social", true);
SetFeatureOverride("Videos", true);
// Enable all feature overrides affected by new servers
SetFeatureOverride("Apps", true);
SetFeatureOverride("Channels", true);
SetFeatureOverride("Games", true);
SetFeatureOverride("Marketplace", true);
SetFeatureOverride("Music", true);
SetFeatureOverride("MusicVideos", true);
SetFeatureOverride("Podcasts", true);
SetFeatureOverride("Social", 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();
}
+30 -13
View File
@@ -1,15 +1,15 @@
<mah:MetroWindow x:Class="ZuneModdingHelper.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ZuneModdingHelper"
xmlns:core="clr-namespace:ZuneModCore;assembly=ZuneModCore"
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
mc:Ignorable="d"
Title="Zune Modding Helper" Height="450" Width="800"
Loaded="Window_Loaded"
Background="LightGray">
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ZuneModdingHelper"
xmlns:core="clr-namespace:ZuneModCore;assembly=ZuneModCore"
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
mc:Ignorable="d"
Title="Zune Modding Helper" Height="450" Width="800"
Loaded="Window_Loaded"
Background="#F0F0F0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
@@ -53,8 +53,25 @@
<TextBlock Text="Zune Software install directory:" Margin="4,4,0,0"/>
<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"
VerticalAlignment="Center" HorizontalAlignment="Center" Padding="4" Margin="4"/>
<Button x:Name="InstallModsButton" Content="Install" ToolTip="Install selected mods" Click="InstallModsButton_Click"
Grid.RowSpan="2" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="4"/>
</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>
</mah:MetroWindow>
+28 -4
View File
@@ -36,17 +36,21 @@ namespace ZuneModdingHelper
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;
var selectedMods = ModList.SelectedItems.Cast<Mod>();
double progTotal = selectedMods.Count() * 2;
double progCompleted = 0;
double progPercent = 0;
foreach (Mod mod in selectedMods)
{
ProgressDesc.Text = $"Setting up '{mod.Title}'...";
await mod.Init();
progPercent = ++progCompleted / progTotal;
Progress.Value = ++progCompleted / progTotal * 100;
// TODO: Implement AbstractUI display for options
//if (mod.OptionsUI != null)
@@ -56,9 +60,29 @@ namespace ZuneModdingHelper
// optionsDialog.ShowDialog();
//}
await mod.Apply();
progPercent = ++progCompleted / progTotal;
ProgressDesc.Text = $"Applying '{mod.Title}'...";
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;
}
}
}