mirror of
https://github.com/ZuneDev/ZuneModdingHelper.git
synced 2026-07-27 13:12:21 -07:00
Merge branch 'master' of https://github.com/ZuneDev/ZuneModdingHelper
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using OwlCore.AbstractUI.ViewModels;
|
||||
using OwlCore.AbstractUI.Models;
|
||||
|
||||
namespace OwlCore.Wpf.AbstractUI.Controls
|
||||
{
|
||||
@@ -16,7 +15,7 @@ namespace OwlCore.Wpf.AbstractUI.Controls
|
||||
/// Backing property for <see cref="ViewModel"/>.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ViewModelProperty =
|
||||
DependencyProperty.Register(nameof(ViewModel), typeof(AbstractUICollectionViewModel), typeof(AbstractUIGroupPresenter), new PropertyMetadata(null, (d, e) => ((AbstractUIGroupPresenter)d).OnViewModelChanged()));
|
||||
DependencyProperty.Register(nameof(ViewModel), typeof(AbstractUICollectionViewModel), typeof(AbstractUIGroupPresenter), new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Backing property for <see cref="TemplateSelector"/>.
|
||||
@@ -48,62 +47,6 @@ namespace OwlCore.Wpf.AbstractUI.Controls
|
||||
public AbstractUIGroupPresenter()
|
||||
{
|
||||
this.DefaultStyleKey = typeof(AbstractUIGroupPresenter);
|
||||
|
||||
AttachEvents();
|
||||
}
|
||||
|
||||
private void AttachEvents()
|
||||
{
|
||||
Loaded += OnLoaded;
|
||||
|
||||
DataContextChanged += OnDataContextChanged;
|
||||
}
|
||||
|
||||
private void DetachEvents()
|
||||
{
|
||||
DataContextChanged -= OnDataContextChanged;
|
||||
}
|
||||
|
||||
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (_dataContextBeingSet)
|
||||
return;
|
||||
|
||||
_dataContextBeingSet = true;
|
||||
|
||||
if (DataContext is AbstractUICollection elementGroup)
|
||||
ViewModel = new AbstractUICollectionViewModel(elementGroup);
|
||||
|
||||
if (DataContext is AbstractUICollectionViewModel elementGroupViewModel)
|
||||
ViewModel = elementGroupViewModel;
|
||||
|
||||
_dataContextBeingSet = false;
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Loaded -= OnLoaded;
|
||||
Unloaded += OnUnloaded;
|
||||
}
|
||||
|
||||
private void OnUnloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Unloaded -= OnUnloaded;
|
||||
|
||||
DetachEvents();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the <see cref="ViewModel"/> changes.
|
||||
/// </summary>
|
||||
public void OnViewModelChanged()
|
||||
{
|
||||
if (_dataContextBeingSet)
|
||||
return;
|
||||
|
||||
_dataContextBeingSet = true;
|
||||
DataContext = ViewModel;
|
||||
_dataContextBeingSet = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@
|
||||
<ControlTemplate TargetType="controls:AbstractUIGroupPresenter">
|
||||
<ContentControl ContentTemplate="{StaticResource DefaultAbstractUICollectionTemplate}"
|
||||
Content="{TemplateBinding ViewModel}"
|
||||
DataContext="{TemplateBinding ViewModel}"
|
||||
HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
|
||||
+131
-18
@@ -2,6 +2,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ZuneModCore.Mods;
|
||||
|
||||
@@ -9,20 +10,6 @@ namespace ZuneModCore
|
||||
{
|
||||
public abstract class Mod
|
||||
{
|
||||
/// <summary>
|
||||
/// A list of all available mods
|
||||
/// </summary>
|
||||
public static readonly IReadOnlyList<Mod> AvailableMods = new List<Mod>
|
||||
{
|
||||
new FeaturesOverrideMod(),
|
||||
new VideoSyncMod(),
|
||||
new WebservicesMod(),
|
||||
new BackgroundImageMod(),
|
||||
new MbidLocatorMod(),
|
||||
}.AsReadOnly();
|
||||
|
||||
public static string ZuneInstallDir { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Zune");
|
||||
|
||||
public abstract string Id { get; }
|
||||
|
||||
public abstract string Title { get; }
|
||||
@@ -31,13 +18,18 @@ namespace ZuneModCore
|
||||
|
||||
public abstract string Author { get; }
|
||||
|
||||
public delegate void StatusChangedHandler(Mod sender, bool status);
|
||||
public event StatusChangedHandler? StatusChanged;
|
||||
|
||||
public virtual AbstractUICollection? GetDefaultOptionsUI() => null;
|
||||
|
||||
public virtual Task Init() => Task.CompletedTask;
|
||||
|
||||
public abstract Task<string?> Apply();
|
||||
protected abstract Task<string?> ApplyCore();
|
||||
|
||||
public abstract Task<string?> Reset();
|
||||
protected abstract Task<string?> ResetCore();
|
||||
|
||||
protected virtual void FireStatusChanged(bool status) => StatusChanged?.Invoke(this, status);
|
||||
|
||||
private AbstractUICollection? _OptionsUI;
|
||||
public AbstractUICollection? OptionsUI
|
||||
@@ -51,17 +43,138 @@ namespace ZuneModCore
|
||||
set => _OptionsUI = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a directory that mods can use to store mod-specific information,
|
||||
/// such as backups for resetting.
|
||||
/// </summary>
|
||||
public string StorageDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
string dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ZuneModCore", Id);
|
||||
string dir = Path.Combine(ModManager.CoreStorageDir, 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<ModDependency>? DependentMods { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Applies this mod, without any of its dependencies.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The error message if not successful.
|
||||
/// </returns>
|
||||
public async Task<string?> Apply()
|
||||
{
|
||||
string? error = await ApplyCore();
|
||||
if (error == null)
|
||||
{
|
||||
ModManager.MarkApplied(Id);
|
||||
FireStatusChanged(true);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes any missing depedencies.
|
||||
/// </summary>
|
||||
public Task InitDependencies() => ForAllDependenciesAsync(async mod =>
|
||||
{
|
||||
bool isDepApplied = mod.CheckApplied();
|
||||
if (!isDepApplied)
|
||||
await mod.Init();
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Applies any missing depedencies.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Make sure to call <see cref="InitDependencies"/> first.
|
||||
/// </remarks>
|
||||
public Task ApplyDependencies() => ForAllDependenciesAsync(async mod =>
|
||||
{
|
||||
bool isDepApplied = mod.CheckApplied();
|
||||
if (!isDepApplied)
|
||||
await mod.Apply();
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Applies this mod and all its dependencies.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The error message if not successful.
|
||||
/// </returns>
|
||||
public async Task<string?> ApplyWithDependencies()
|
||||
{
|
||||
await ApplyDependencies();
|
||||
return await Apply();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets this mod, without resetting any dependencies.
|
||||
/// </summary>
|
||||
public async Task<string?> Reset()
|
||||
{
|
||||
string? error = await ResetCore();
|
||||
if (error == null)
|
||||
{
|
||||
ModManager.MarkReset(Id);
|
||||
FireStatusChanged(false);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
public bool CheckApplied() => ModManager.CheckStatus(Id);
|
||||
|
||||
public bool CheckDependenciesApplied() => TrueForAllDependencies(mod => mod.CheckApplied());
|
||||
|
||||
private void ForAllDependencies(Action<Mod> action)
|
||||
{
|
||||
// Check if there are any dependencies
|
||||
if (DependentMods == null)
|
||||
return;
|
||||
|
||||
foreach (var dep in DependentMods)
|
||||
{
|
||||
var depMod = ModManager.AvailableMods.First(m => m.Id == dep.Id);
|
||||
if (depMod != null)
|
||||
action(depMod);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ForAllDependenciesAsync(Func<Mod, Task> asyncAction)
|
||||
{
|
||||
// Check if there are any dependencies
|
||||
if (DependentMods == null)
|
||||
return;
|
||||
|
||||
foreach (var dep in DependentMods)
|
||||
{
|
||||
var depMod = ModManager.AvailableMods.First(m => m.Id == dep.Id);
|
||||
if (depMod != null)
|
||||
await asyncAction(depMod);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TrueForAllDependencies(Func<Mod, bool> action)
|
||||
{
|
||||
// Check if there are any dependencies
|
||||
if (DependentMods == null)
|
||||
return true;
|
||||
|
||||
foreach (var dep in DependentMods)
|
||||
{
|
||||
var depMod = ModManager.AvailableMods.First(m => m.Id == dep.Id);
|
||||
if (depMod != null)
|
||||
if (!action(depMod))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ToString() => Title;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ZuneModCore
|
||||
{
|
||||
public class ModDependency
|
||||
{
|
||||
public ModDependency(string id, ReleaseVersion? version = null)
|
||||
{
|
||||
Id = id;
|
||||
Version = version ?? ModManager.CurrentVersion;
|
||||
}
|
||||
|
||||
public string Id { get; set; }
|
||||
|
||||
public ReleaseVersion Version { get; set; }
|
||||
|
||||
public bool Status => CheckStatus();
|
||||
|
||||
/// <summary>
|
||||
/// DO NOT USE DIRECTLY. This constructor is only for
|
||||
/// JSON de/serialization.
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
internal ModDependency() { }
|
||||
|
||||
public bool CheckStatus() => ModManager.CheckStatus(Id);
|
||||
|
||||
public override string ToString() => $"{Id} / {Version}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using CommunityToolkit.Diagnostics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using ZuneModCore.Mods;
|
||||
|
||||
namespace ZuneModCore
|
||||
{
|
||||
public static class ModManager
|
||||
{
|
||||
/// <summary>
|
||||
/// A list of all available mods.
|
||||
/// </summary>
|
||||
public static readonly IReadOnlyList<Mod> AvailableMods = new List<Mod>
|
||||
{
|
||||
new FeaturesOverrideMod(),
|
||||
new VideoSyncMod(),
|
||||
new WebservicesMod(),
|
||||
new BackgroundImageMod(),
|
||||
new MbidLocatorMod(),
|
||||
};
|
||||
|
||||
public static string ZuneInstallDir { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Zune");
|
||||
|
||||
public static readonly ReleaseVersion CurrentVersion = new(2021, 12, 30, 0, Phase.Alpha);
|
||||
|
||||
internal static readonly string CoreStorageDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ZuneModCore");
|
||||
|
||||
private static readonly string CoreStatusFile = Path.Combine(CoreStorageDir, "ModStatus.json");
|
||||
|
||||
/// <summary>
|
||||
/// Marks the specified mod as applied in the status file.
|
||||
/// </summary>
|
||||
internal static void MarkApplied(string modId) => MarkStatus(modId, true);
|
||||
|
||||
/// <summary>
|
||||
/// Marks the specified mod as reset in the status file.
|
||||
/// </summary>
|
||||
internal static void MarkReset(string modId) => MarkStatus(modId, false);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the status of the specified mod in the status file.
|
||||
/// </summary>
|
||||
private static void MarkStatus(string modId, bool status)
|
||||
{
|
||||
var model = GetOrCreateStatusModel();
|
||||
|
||||
bool curStatus = model.InstalledMods.ContainsKey(modId);
|
||||
if (curStatus && !status)
|
||||
{
|
||||
model.InstalledMods.Remove(modId);
|
||||
}
|
||||
else if (!curStatus && status)
|
||||
{
|
||||
model.InstalledMods.Add(modId, CurrentVersion);
|
||||
}
|
||||
|
||||
string json = JsonSerializer.Serialize(model);
|
||||
File.WriteAllText(CoreStatusFile, json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status of the specified mod from the status file.
|
||||
/// </summary>
|
||||
internal static bool CheckStatus(string modId)
|
||||
{
|
||||
var model = GetOrCreateStatusModel();
|
||||
return model.InstalledMods.ContainsKey(modId);
|
||||
}
|
||||
|
||||
private static StatusModel GetOrCreateStatusModel()
|
||||
{
|
||||
StatusModel? model = null;
|
||||
if (File.Exists(CoreStatusFile))
|
||||
{
|
||||
string json = File.ReadAllText(CoreStatusFile);
|
||||
if (!string.IsNullOrWhiteSpace(json))
|
||||
model = JsonSerializer.Deserialize<StatusModel>(json);
|
||||
}
|
||||
|
||||
model ??= new()
|
||||
{
|
||||
Version = CurrentVersion,
|
||||
InstalledMods = new()
|
||||
};
|
||||
|
||||
Guard.IsNotNull(model, nameof(model));
|
||||
return model;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.Toolkit.Mvvm.Input;
|
||||
using OwlCore.AbstractUI.Models;
|
||||
using OwlCore.AbstractUI.ViewModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ZuneModCore
|
||||
{
|
||||
public class ModViewModel : ObservableObject, IDisposable
|
||||
{
|
||||
public ModViewModel(Mod mod)
|
||||
{
|
||||
Mod = mod;
|
||||
_loadCommand = new AsyncRelayCommand(LoadAsync);
|
||||
_applyCommand = new AsyncRelayCommand(Mod.ApplyWithDependencies);
|
||||
_resetCommand = new AsyncRelayCommand(Mod.Reset);
|
||||
|
||||
if (Mod.OptionsUI != null)
|
||||
OptionsViewModel = new(Mod.OptionsUI);
|
||||
|
||||
Mod.StatusChanged += OnStatusChanged;
|
||||
}
|
||||
|
||||
private Mod _mod;
|
||||
private AbstractUICollectionViewModel? _optionsVm;
|
||||
private IAsyncRelayCommand _loadCommand;
|
||||
private IAsyncRelayCommand? _actionCommand;
|
||||
private bool _hasOptions;
|
||||
private bool _hasDependencies;
|
||||
private string _actionButtonText;
|
||||
private readonly IAsyncRelayCommand _applyCommand;
|
||||
private readonly IAsyncRelayCommand _resetCommand;
|
||||
|
||||
public Mod Mod
|
||||
{
|
||||
get => _mod;
|
||||
set
|
||||
{
|
||||
HasDependencies = _mod?.DependentMods != null && _mod.DependentMods.Count > 0;
|
||||
SetProperty(ref _mod, value);
|
||||
}
|
||||
}
|
||||
|
||||
public AbstractUICollectionViewModel? OptionsViewModel
|
||||
{
|
||||
get => _optionsVm;
|
||||
set
|
||||
{
|
||||
HasOptions = _optionsVm != null;
|
||||
SetProperty(ref _optionsVm, value);
|
||||
}
|
||||
}
|
||||
|
||||
public IAsyncRelayCommand LoadCommand
|
||||
{
|
||||
get => _loadCommand;
|
||||
set => SetProperty(ref _loadCommand, value);
|
||||
}
|
||||
|
||||
public IAsyncRelayCommand? ActionCommand
|
||||
{
|
||||
get => _actionCommand;
|
||||
set => SetProperty(ref _actionCommand, value);
|
||||
}
|
||||
|
||||
public bool HasOptions
|
||||
{
|
||||
get => _hasOptions;
|
||||
set => SetProperty(ref _hasOptions, value);
|
||||
}
|
||||
|
||||
public bool HasDependencies
|
||||
{
|
||||
get => _hasDependencies;
|
||||
set => SetProperty(ref _hasDependencies, value);
|
||||
}
|
||||
|
||||
public string ActionButtonText
|
||||
{
|
||||
get => _actionButtonText;
|
||||
set => SetProperty(ref _actionButtonText, value);
|
||||
}
|
||||
|
||||
public async Task LoadAsync()
|
||||
{
|
||||
OnStatusChanged(_mod, _mod.CheckApplied());
|
||||
}
|
||||
|
||||
private void OnStatusChanged(Mod mod, bool status)
|
||||
{
|
||||
if (status)
|
||||
{
|
||||
ActionButtonText = "Reset";
|
||||
ActionCommand = _resetCommand;
|
||||
}
|
||||
else
|
||||
{
|
||||
ActionButtonText = "Apply";
|
||||
ActionCommand = _applyCommand;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Mod.StatusChanged -= OnStatusChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,9 +32,9 @@ namespace ZuneModCore.Mods
|
||||
};
|
||||
}
|
||||
|
||||
public override IReadOnlyList<Type>? DependentMods => null;
|
||||
public override IReadOnlyList<ModDependency>? DependentMods => null;
|
||||
|
||||
public override async Task<string?> Apply()
|
||||
protected override async Task<string?> ApplyCore()
|
||||
{
|
||||
string bgimgPath = ((AbstractTextBox)OptionsUI!.Items[0]).Value;
|
||||
FileInfo bgimgInfo = new(bgimgPath);
|
||||
@@ -43,7 +43,7 @@ namespace ZuneModCore.Mods
|
||||
return $"The file '{bgimgInfo.FullName}' does not exist.";
|
||||
}
|
||||
|
||||
FileInfo zsrDllInfo = new(Path.Combine(ZuneInstallDir, "ZuneShellResources.dll"));
|
||||
FileInfo zsrDllInfo = new(Path.Combine(ModManager.ZuneInstallDir, "ZuneShellResources.dll"));
|
||||
if (!zsrDllInfo.Exists)
|
||||
{
|
||||
return $"The file '{zsrDllInfo.FullName}' does not exist.";
|
||||
@@ -114,13 +114,13 @@ namespace ZuneModCore.Mods
|
||||
}
|
||||
}
|
||||
|
||||
public override Task<string?> Reset()
|
||||
protected override Task<string?> ResetCore()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Copy backup to application folder
|
||||
File.Copy(Path.Combine(StorageDirectory, "ZuneShellResources.original.dll"),
|
||||
Path.Combine(ZuneInstallDir, "ZuneShellResources.dll"), true);
|
||||
Path.Combine(ModManager.ZuneInstallDir, "ZuneShellResources.dll"), true);
|
||||
|
||||
return Task.FromResult<string?>(null);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using OwlCore.AbstractUI.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using ZuneModCore.Win32;
|
||||
|
||||
@@ -56,7 +57,7 @@ namespace ZuneModCore.Mods
|
||||
};
|
||||
}
|
||||
|
||||
public override IReadOnlyList<Type>? DependentMods => null;
|
||||
public override IReadOnlyList<ModDependency>? DependentMods => null;
|
||||
|
||||
public override Task Init()
|
||||
{
|
||||
@@ -72,10 +73,13 @@ namespace ZuneModCore.Mods
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override async Task<string?> Apply() => await Apply(false);
|
||||
protected override async Task<string?> ApplyCore() => await Apply(false);
|
||||
|
||||
public async Task<string?> Apply(bool applyAll = false)
|
||||
{
|
||||
// Save backup of current values
|
||||
ExportFeatureOverrides(Path.Combine(StorageDirectory, "FeatureOverrides.reg"));
|
||||
|
||||
// Use user choices from AbstractUI
|
||||
foreach (AbstractUIElement uiElem in OptionsUI!.Items)
|
||||
{
|
||||
@@ -84,7 +88,7 @@ namespace ZuneModCore.Mods
|
||||
bool isSuccess = SetFeatureOverride(boolElem.Id, true);
|
||||
if (!isSuccess)
|
||||
{
|
||||
string? resetStatus = await Reset();
|
||||
string? resetStatus = await ResetCore();
|
||||
if (resetStatus != null)
|
||||
{
|
||||
// The reset failed as well, return both errors
|
||||
@@ -101,13 +105,24 @@ namespace ZuneModCore.Mods
|
||||
return null;
|
||||
}
|
||||
|
||||
public override async Task<string?> Reset()
|
||||
protected override async Task<string?> ResetCore()
|
||||
{
|
||||
// Delete all values first, so if a backup exists it has a clean slate
|
||||
RegEdit.CurrentUserDeleteKey(ZUNE_FEATURESOVERRIDE_REGKEY);
|
||||
|
||||
FileInfo reg = new(Path.Combine(StorageDirectory, "FeatureOverrides.reg"));
|
||||
if (reg.Exists)
|
||||
{
|
||||
// Load backup of original values
|
||||
RegEdit.ImportKey(reg.FullName);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void ExportFeatureOverrides(string path) =>
|
||||
RegEdit.ExportKey(RegEdit.HIVE_CURRENTUSER, ZUNE_FEATURESOVERRIDE_REGKEY, path);
|
||||
|
||||
public static bool SetFeatureOverride(string feature, bool value) =>
|
||||
RegEdit.CurrentUserSetBoolValue(ZUNE_FEATURESOVERRIDE_REGKEY, feature, value);
|
||||
|
||||
|
||||
@@ -42,9 +42,9 @@ namespace ZuneModCore.Mods
|
||||
};
|
||||
}
|
||||
|
||||
public override IReadOnlyList<Type>? DependentMods => null;
|
||||
public override IReadOnlyList<ModDependency>? DependentMods => null;
|
||||
|
||||
public override async Task<string?> Apply()
|
||||
protected override async Task<string?> ApplyCore()
|
||||
{
|
||||
// Use user choices from AbstractUI
|
||||
string folderPath = ((AbstractTextBox)OptionsUI!.Items[0]).Value;
|
||||
@@ -82,7 +82,7 @@ namespace ZuneModCore.Mods
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Task<string?> Reset()
|
||||
protected override Task<string?> ResetCore()
|
||||
{
|
||||
return Task.FromResult<string?>(null);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
using OwlCore.AbstractUI.Models;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using ZuneModCore.Win32;
|
||||
|
||||
@@ -23,7 +20,7 @@ namespace ZuneModCore.Mods
|
||||
|
||||
public override string Id => nameof(VideoSyncMod);
|
||||
|
||||
public override async Task<string?> Apply()
|
||||
protected override async Task<string?> ApplyCore()
|
||||
{
|
||||
// Make a backup of the original file
|
||||
FileVersionInfo wmvDllVersionInfo = FileVersionInfo.GetVersionInfo(WMVCORE_PATH);
|
||||
@@ -56,7 +53,7 @@ namespace ZuneModCore.Mods
|
||||
}
|
||||
}
|
||||
|
||||
public override Task<string?> Reset()
|
||||
protected override Task<string?> ResetCore()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -71,6 +68,6 @@ namespace ZuneModCore.Mods
|
||||
}
|
||||
}
|
||||
|
||||
public override IReadOnlyList<Type>? DependentMods => null;
|
||||
public override IReadOnlyList<ModDependency>? DependentMods => null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,10 @@ namespace ZuneModCore.Mods
|
||||
};
|
||||
}
|
||||
|
||||
public override IReadOnlyList<Type>? DependentMods => null;
|
||||
public override IReadOnlyList<ModDependency>? DependentMods => new List<ModDependency>
|
||||
{
|
||||
new(nameof(FeaturesOverrideMod))
|
||||
};
|
||||
|
||||
public override Task Init()
|
||||
{
|
||||
@@ -87,10 +90,10 @@ namespace ZuneModCore.Mods
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override async Task<string?> Apply()
|
||||
protected override async Task<string?> ApplyCore()
|
||||
{
|
||||
// Verify that ZuneServices.dll exists
|
||||
FileInfo zsDllInfo = new(Path.Combine(ZuneInstallDir, "ZuneService.dll"));
|
||||
FileInfo zsDllInfo = new(Path.Combine(ModManager.ZuneInstallDir, "ZuneService.dll"));
|
||||
if (!zsDllInfo.Exists)
|
||||
{
|
||||
return $"The file '{zsDllInfo.FullName}' does not exist.";
|
||||
@@ -180,9 +183,9 @@ namespace ZuneModCore.Mods
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<string?> Reset()
|
||||
protected override async Task<string?> ResetCore()
|
||||
{
|
||||
string zsDllPath = Path.Combine(ZuneInstallDir, "ZuneService.dll");
|
||||
string zsDllPath = Path.Combine(ModManager.ZuneInstallDir, "ZuneService.dll");
|
||||
try
|
||||
{
|
||||
// Copy backup to application folder
|
||||
@@ -258,7 +261,7 @@ namespace ZuneModCore.Mods
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string?> Ping(string url)
|
||||
private static async Task<string?> Ping(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using CommunityToolkit.Diagnostics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ZuneModdingHelper
|
||||
namespace ZuneModCore
|
||||
{
|
||||
[JsonConverter(typeof(ReleaseVersionJsonConverter))]
|
||||
public sealed class ReleaseVersion : ICloneable, IComparable, IComparable<ReleaseVersion?>, IEquatable<ReleaseVersion?>
|
||||
{
|
||||
/// <summary>
|
||||
@@ -481,4 +480,17 @@ namespace ZuneModdingHelper
|
||||
ReleaseCandidate,
|
||||
Production
|
||||
}
|
||||
|
||||
internal class ReleaseVersionJsonConverter : JsonConverter<ReleaseVersion>
|
||||
{
|
||||
public override ReleaseVersion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return ReleaseVersion.Parse(reader.GetString()!);
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, ReleaseVersion value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ZuneModCore
|
||||
{
|
||||
internal class StatusModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The version of ZuneModCore that created this model.
|
||||
/// </summary>
|
||||
public ReleaseVersion Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of mods that are currently installed.
|
||||
/// <para>
|
||||
/// <c>Key</c> is mod ID and <c>Value</c> is installed version.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public Dictionary<string, ReleaseVersion> InstalledMods { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Win32;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ZuneModCore.Win32
|
||||
{
|
||||
@@ -7,6 +8,7 @@ namespace ZuneModCore.Win32
|
||||
|
||||
public class RegEdit
|
||||
{
|
||||
public const string HIVE_CURRENTUSER = "HKEY_CURRENT_USER";
|
||||
public const string ZUNE_REG_PATH = @"SOFTWARE\Microsoft\Zune\";
|
||||
|
||||
public static bool CurrentUserSetBoolValue(string key, string name, bool value)
|
||||
@@ -66,6 +68,46 @@ namespace ZuneModCore.Win32
|
||||
|
||||
regKey.DeleteValue(name, false);
|
||||
}
|
||||
|
||||
public static void ExportKey(string hive, string regKey, string savePath, bool overwrite = true)
|
||||
{
|
||||
string path = $"\"{savePath}\"";
|
||||
string key = $"\"{hive}\\{regKey}\"";
|
||||
|
||||
Process proc = null!;
|
||||
try
|
||||
{
|
||||
string cmd = $"reg export {key} {path}";
|
||||
|
||||
if (overwrite)
|
||||
cmd += " /y";
|
||||
|
||||
proc = Process.Start("cmd.exe", $"/c {cmd}");
|
||||
if (proc != null) proc.WaitForExit();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (proc != null) proc.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static void ImportKey(string savePath)
|
||||
{
|
||||
string path = "\"" + savePath + "\"";
|
||||
|
||||
Process proc = null!;
|
||||
try
|
||||
{
|
||||
string cmd = $"reg import {path}";
|
||||
|
||||
proc = Process.Start("cmd.exe", $"/c \"{cmd}\"");
|
||||
if (proc != null) proc.WaitForExit();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (proc != null) proc.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore CA1416 // Validate platform compatibility
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Diagnostics" Version="7.1.2" />
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Include="OwlCore" Version="0.0.43" />
|
||||
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
<mah:MetroWindow x:Class="ZuneModdingHelper.AbstractUIGroupDialog"
|
||||
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:absui="clr-namespace:OwlCore.Wpf.AbstractUI.Controls;assembly=OwlCore.Wpf"
|
||||
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
|
||||
mc:Ignorable="d"
|
||||
Title="Options" Height="450" Width="800"
|
||||
WindowStyle="ToolWindow">
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/OwlCore.Wpf;component/AbstractUI/Themes/AbstractUIGroupPresenterStyle.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid x:Name="OptionsUIGrid" Grid.RowSpan="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ScrollViewer>
|
||||
<absui:AbstractUIGroupPresenter x:Name="OptionsUIPresenter" ViewModel="{Binding}"
|
||||
TemplateSelector="{StaticResource GroupTemplateSelector}"/>
|
||||
</ScrollViewer>
|
||||
|
||||
<Border Grid.Row="1" BorderThickness="0,1,0,0" BorderBrush="{StaticResource MahApps.Brushes.Control.Border}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.Resources>
|
||||
<Style x:Key="PrimaryButton" TargetType="Button" BasedOn="{StaticResource MahApps.Styles.Button.Dialogs.Accent}">
|
||||
<Setter Property="Margin" Value="8"/>
|
||||
<Setter Property="Padding" Value="2"/>
|
||||
<Setter Property="MinWidth" Value="75"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
</Style>
|
||||
<Style x:Key="SecondaryButton" TargetType="Button" BasedOn="{StaticResource MahApps.Styles.Button.Dialogs}">
|
||||
<Setter Property="Margin" Value="8"/>
|
||||
<Setter Property="Padding" Value="2"/>
|
||||
<Setter Property="MinWidth" Value="75"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
</Style>
|
||||
</Grid.Resources>
|
||||
|
||||
<Button x:Name="OptionsUICancelButton" Content="Cancel" Click="OptionsUICancelButton_Click"
|
||||
Grid.Column="1" Style="{StaticResource SecondaryButton}"/>
|
||||
|
||||
<Button x:Name="OptionsUINextButton" Content="Next" Click="OptionsUINextButton_Click"
|
||||
Grid.Column="2" Style="{StaticResource PrimaryButton}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</mah:MetroWindow>
|
||||
@@ -2,6 +2,7 @@
|
||||
using Microsoft.AppCenter;
|
||||
using Microsoft.AppCenter.Analytics;
|
||||
using Microsoft.AppCenter.Crashes;
|
||||
using ZuneModCore;
|
||||
|
||||
namespace ZuneModdingHelper
|
||||
{
|
||||
@@ -12,7 +13,7 @@ namespace ZuneModdingHelper
|
||||
{
|
||||
public const string Title = "Zune Modding Helper";
|
||||
|
||||
public static readonly ReleaseVersion Version = new(2021, 12, 30, 0, Phase.Alpha);
|
||||
public static readonly ReleaseVersion Version = ModManager.CurrentVersion;
|
||||
public static readonly string VersionStr = Version.ToString();
|
||||
|
||||
public const string DonateLink = "http://josh.askharoun.com/donate";
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
using Microsoft.Xaml.Behaviors;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace ZuneModdingHelper.Behaviors
|
||||
{
|
||||
// https://stackoverflow.com/a/5910833
|
||||
public class FadeAnimateItemsBehavior : Behavior<ListBox>
|
||||
{
|
||||
public DoubleAnimation Animation { get; set; }
|
||||
public TimeSpan Tick { get; set; }
|
||||
|
||||
protected override void OnAttached()
|
||||
{
|
||||
base.OnAttached();
|
||||
AssociatedObject.Loaded += AssociatedObject_Loaded;
|
||||
}
|
||||
|
||||
void AssociatedObject_Loaded(object sender, System.Windows.RoutedEventArgs e)
|
||||
{
|
||||
IEnumerable<ListBoxItem> items;
|
||||
if (AssociatedObject.ItemsSource == null)
|
||||
{
|
||||
items = AssociatedObject.Items.Cast<ListBoxItem>();
|
||||
}
|
||||
else
|
||||
{
|
||||
var itemsSource = AssociatedObject.ItemsSource;
|
||||
if (itemsSource is INotifyCollectionChanged)
|
||||
{
|
||||
var collection = itemsSource as INotifyCollectionChanged;
|
||||
collection.CollectionChanged += (s, cce) =>
|
||||
{
|
||||
if (cce.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
var itemContainer = AssociatedObject.ItemContainerGenerator.ContainerFromItem(cce.NewItems[0]) as ListBoxItem;
|
||||
itemContainer.BeginAnimation(ListBoxItem.OpacityProperty, Animation);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
ListBoxItem[] itemsSub = new ListBoxItem[AssociatedObject.Items.Count];
|
||||
for (int i = 0; i < itemsSub.Length; i++)
|
||||
{
|
||||
itemsSub[i] = AssociatedObject.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
|
||||
}
|
||||
items = itemsSub;
|
||||
}
|
||||
foreach (var item in items)
|
||||
{
|
||||
item.Opacity = 0;
|
||||
}
|
||||
var enumerator = items.GetEnumerator();
|
||||
if (enumerator.MoveNext())
|
||||
{
|
||||
DispatcherTimer timer = new() { Interval = Tick };
|
||||
timer.Tick += (s, timerE) =>
|
||||
{
|
||||
var item = enumerator.Current;
|
||||
item.BeginAnimation(ListBoxItem.OpacityProperty, Animation);
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
timer.Stop();
|
||||
}
|
||||
};
|
||||
timer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace ZuneModdingHelper.Converters
|
||||
{
|
||||
internal class BoolToSymbolConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is not bool val)
|
||||
return null;
|
||||
|
||||
var resultsStr = parameter.ToString().Split(',');
|
||||
object trueReturnVal = null;
|
||||
object falseReturnVal = null;
|
||||
|
||||
if (targetType == typeof(string))
|
||||
{
|
||||
trueReturnVal = resultsStr[0];
|
||||
falseReturnVal = resultsStr[1];
|
||||
}
|
||||
else if (targetType == typeof(Brush))
|
||||
{
|
||||
var brushConverter = new BrushConverter();
|
||||
trueReturnVal = brushConverter.ConvertFrom(resultsStr[0]);
|
||||
falseReturnVal = brushConverter.ConvertFrom(resultsStr[1]);
|
||||
}
|
||||
|
||||
return val ? trueReturnVal : falseReturnVal;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var results = parameter.ToString().Split(',');
|
||||
return value.ToString() == results[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace ZuneModdingHelper.Converters
|
||||
{
|
||||
internal class NullToVisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value != null ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user