mirror of
https://github.com/ZuneDev/ZuneModdingHelper.git
synced 2026-07-27 13:12:21 -07:00
Update to net8.0-windows and C# 12
This commit is contained in:
+69
-72
@@ -6,85 +6,82 @@ using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using ZuneModCore.Mods;
|
||||
|
||||
namespace ZuneModCore
|
||||
namespace ZuneModCore;
|
||||
|
||||
public abstract class Mod
|
||||
{
|
||||
public abstract class Mod
|
||||
/// <summary>
|
||||
/// A list of all available mods
|
||||
/// </summary>
|
||||
public static readonly IReadOnlyList<Type> AvailableModTypes = new List<Type>
|
||||
{
|
||||
/// <summary>
|
||||
/// A list of all available mods
|
||||
/// </summary>
|
||||
public static readonly IReadOnlyList<Type> AvailableModTypes = new List<Type>
|
||||
typeof(FeaturesOverrideMod),
|
||||
typeof(VideoSyncMod),
|
||||
typeof(WebservicesMod),
|
||||
typeof(BackgroundImageMod),
|
||||
typeof(MbidLocatorMod),
|
||||
}.AsReadOnly();
|
||||
|
||||
public static string DefaultZuneInstallDir { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Zune");
|
||||
|
||||
public string ZuneInstallDir { get; set; } = DefaultZuneInstallDir;
|
||||
|
||||
private static List<Mod>? _mods;
|
||||
public static IReadOnlyList<Mod> AvailableMods
|
||||
{
|
||||
get
|
||||
{
|
||||
typeof(FeaturesOverrideMod),
|
||||
typeof(VideoSyncMod),
|
||||
typeof(WebservicesMod),
|
||||
typeof(BackgroundImageMod),
|
||||
typeof(MbidLocatorMod),
|
||||
}.AsReadOnly();
|
||||
|
||||
public static string DefaultZuneInstallDir { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Zune");
|
||||
|
||||
public string ZuneInstallDir { get; set; } = DefaultZuneInstallDir;
|
||||
|
||||
private static List<Mod>? _mods;
|
||||
public static IReadOnlyList<Mod> AvailableMods
|
||||
{
|
||||
get
|
||||
if (_mods is null)
|
||||
{
|
||||
if (_mods is null)
|
||||
_mods = new(AvailableModTypes.Count);
|
||||
var services = CommunityToolkit.Mvvm.DependencyInjection.Ioc.Default;
|
||||
foreach (var modType in AvailableModTypes)
|
||||
{
|
||||
_mods = new(AvailableModTypes.Count);
|
||||
var services = CommunityToolkit.Mvvm.DependencyInjection.Ioc.Default;
|
||||
foreach (var modType in AvailableModTypes)
|
||||
{
|
||||
var instance = ActivatorUtilities.CreateInstance(services, modType);
|
||||
if (instance is Mod mod)
|
||||
_mods.Add(mod);
|
||||
}
|
||||
var instance = ActivatorUtilities.CreateInstance(services, modType);
|
||||
if (instance is Mod mod)
|
||||
_mods.Add(mod);
|
||||
}
|
||||
return _mods;
|
||||
}
|
||||
return _mods;
|
||||
}
|
||||
|
||||
public abstract string Id { get; }
|
||||
|
||||
public abstract string Title { get; }
|
||||
|
||||
public abstract string Description { get; }
|
||||
|
||||
public abstract string Author { get; }
|
||||
|
||||
public virtual AbstractUICollection? GetDefaultOptionsUI() => null;
|
||||
|
||||
public virtual Task Init() => Task.CompletedTask;
|
||||
|
||||
public abstract Task<string?> Apply();
|
||||
|
||||
public abstract Task<string?> Reset();
|
||||
|
||||
private AbstractUICollection? _OptionsUI;
|
||||
public AbstractUICollection? OptionsUI
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_OptionsUI == null)
|
||||
_OptionsUI = GetDefaultOptionsUI();
|
||||
return _OptionsUI;
|
||||
}
|
||||
set => _OptionsUI = value;
|
||||
}
|
||||
|
||||
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; }
|
||||
}
|
||||
|
||||
public abstract string Id { get; }
|
||||
|
||||
public abstract string Title { get; }
|
||||
|
||||
public abstract string Description { get; }
|
||||
|
||||
public abstract string Author { get; }
|
||||
|
||||
public virtual AbstractUICollection? GetDefaultOptionsUI() => null;
|
||||
|
||||
public virtual Task Init() => Task.CompletedTask;
|
||||
|
||||
public abstract Task<string?> Apply();
|
||||
|
||||
public abstract Task<string?> Reset();
|
||||
|
||||
private AbstractUICollection? _OptionsUI;
|
||||
public AbstractUICollection? OptionsUI
|
||||
{
|
||||
get
|
||||
{
|
||||
return _OptionsUI ??= GetDefaultOptionsUI();
|
||||
}
|
||||
set => _OptionsUI = value;
|
||||
}
|
||||
|
||||
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; }
|
||||
}
|
||||
|
||||
@@ -7,126 +7,125 @@ using System.Threading.Tasks;
|
||||
using Vestris.ResourceLib;
|
||||
using ZuneModCore.Win32;
|
||||
|
||||
namespace ZuneModCore.Mods
|
||||
namespace ZuneModCore.Mods;
|
||||
|
||||
public class BackgroundImageMod : Mod
|
||||
{
|
||||
public class BackgroundImageMod : Mod
|
||||
public override string Id => nameof(BackgroundImageMod);
|
||||
|
||||
public override string Title => "Background Image";
|
||||
|
||||
public override string Description => "Replaces the \"Zero\" background with an image of your choice.";
|
||||
|
||||
public override string Author => "Joshua \"Yoshi\" Askharoun";
|
||||
|
||||
public override AbstractUICollection? GetDefaultOptionsUI()
|
||||
{
|
||||
public override string Id => nameof(BackgroundImageMod);
|
||||
|
||||
public override string Title => "Background Image";
|
||||
|
||||
public override string Description => "Replaces the \"Zero\" background with an image of your choice.";
|
||||
|
||||
public override string Author => "Joshua \"Yoshi\" Askharoun";
|
||||
|
||||
public override AbstractUICollection? GetDefaultOptionsUI()
|
||||
AbstractUICollection optionsUi = new(nameof(BackgroundImageMod))
|
||||
{
|
||||
AbstractUICollection optionsUi = new(nameof(BackgroundImageMod))
|
||||
{
|
||||
new AbstractTextBox("fileBox", Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)),
|
||||
};
|
||||
optionsUi.Title = "select background";
|
||||
optionsUi.Subtitle = "CHOOSE A BACKGROUND IMAGE OF YOUR CHOICE.";
|
||||
return optionsUi;
|
||||
new AbstractTextBox("fileBox", Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)),
|
||||
};
|
||||
optionsUi.Title = "select background";
|
||||
optionsUi.Subtitle = "CHOOSE A BACKGROUND IMAGE OF YOUR CHOICE.";
|
||||
return optionsUi;
|
||||
}
|
||||
|
||||
public override IReadOnlyList<Type>? DependentMods => null;
|
||||
|
||||
public override async Task<string?> Apply()
|
||||
{
|
||||
string bgimgPath = ((AbstractTextBox)OptionsUI![0]).Value;
|
||||
FileInfo bgimgInfo = new(bgimgPath);
|
||||
if (!bgimgInfo.Exists)
|
||||
{
|
||||
return $"The file '{bgimgInfo.FullName}' does not exist.";
|
||||
}
|
||||
|
||||
public override IReadOnlyList<Type>? DependentMods => null;
|
||||
|
||||
public override async Task<string?> Apply()
|
||||
FileInfo zsrDllInfo = new(Path.Combine(ZuneInstallDir, "ZuneShellResources.dll"));
|
||||
if (!zsrDllInfo.Exists)
|
||||
{
|
||||
string bgimgPath = ((AbstractTextBox)OptionsUI![0]).Value;
|
||||
FileInfo bgimgInfo = new(bgimgPath);
|
||||
if (!bgimgInfo.Exists)
|
||||
return $"The file '{zsrDllInfo.FullName}' does not exist.";
|
||||
}
|
||||
|
||||
// Make a backup if it doesn't already exist
|
||||
FileInfo zsrDllBackupInfo = new(Path.Combine(StorageDirectory, "ZuneShellResources.original.dll"));
|
||||
if (!zsrDllBackupInfo.Exists)
|
||||
{
|
||||
File.Copy(zsrDllInfo.FullName, zsrDllBackupInfo.FullName, true);
|
||||
}
|
||||
|
||||
string zsrDllTempPath = Path.Combine(StorageDirectory, zsrDllInfo.Name);
|
||||
|
||||
// Take ownership of DLL
|
||||
TokenManipulator.TakeOwnership(zsrDllInfo);
|
||||
|
||||
try
|
||||
{
|
||||
using (ResourceInfo vi = new())
|
||||
{
|
||||
return $"The file '{bgimgInfo.FullName}' does not exist.";
|
||||
}
|
||||
|
||||
FileInfo zsrDllInfo = new(Path.Combine(ZuneInstallDir, "ZuneShellResources.dll"));
|
||||
if (!zsrDllInfo.Exists)
|
||||
{
|
||||
return $"The file '{zsrDllInfo.FullName}' does not exist.";
|
||||
}
|
||||
|
||||
// Make a backup if it doesn't already exist
|
||||
FileInfo zsrDllBackupInfo = new(Path.Combine(StorageDirectory, "ZuneShellResources.original.dll"));
|
||||
if (!zsrDllBackupInfo.Exists)
|
||||
{
|
||||
File.Copy(zsrDllInfo.FullName, zsrDllBackupInfo.FullName, true);
|
||||
}
|
||||
|
||||
string zsrDllTempPath = Path.Combine(StorageDirectory, zsrDllInfo.Name);
|
||||
|
||||
// Take ownership of DLL
|
||||
TokenManipulator.TakeOwnership(zsrDllInfo);
|
||||
|
||||
try
|
||||
{
|
||||
using (ResourceInfo vi = new())
|
||||
// Load the RCDATA section
|
||||
vi.Load(zsrDllInfo.FullName);
|
||||
ResourceId? resId = vi.ResourceTypes.Find(id =>
|
||||
{
|
||||
// Load the RCDATA section
|
||||
vi.Load(zsrDllInfo.FullName);
|
||||
ResourceId? resId = vi.ResourceTypes.Find(id =>
|
||||
// This must be wrapped in a try-catch block,
|
||||
// otherwise resourcelib will error when attempting
|
||||
// to read the WAVE type (or any other non-default type).
|
||||
try
|
||||
{
|
||||
// This must be wrapped in a try-catch block,
|
||||
// otherwise resourcelib will error when attempting
|
||||
// to read the WAVE type (or any other non-default type).
|
||||
try
|
||||
{
|
||||
return id.ResourceType == Kernel32.ResourceTypes.RT_RCDATA;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (resId == null)
|
||||
return $"Failed to locate image resources in Zune software.";
|
||||
return id.ResourceType == Kernel32.ResourceTypes.RT_RCDATA;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (resId == null)
|
||||
return $"Failed to locate image resources in Zune software.";
|
||||
|
||||
// Locate the image resource
|
||||
// TODO: Allow users to specify which background they want to replace
|
||||
List<Resource> RCDATA = vi.Resources[resId];
|
||||
int idx = RCDATA.FindIndex(r => r.Name.Name == "FRAMEBACKGROUND06.PNG");
|
||||
if (idx < 0)
|
||||
return $"Failed to locate background image resource in Zune software.";
|
||||
GenericResource ogRes = (GenericResource)RCDATA[idx];
|
||||
// Locate the image resource
|
||||
// TODO: Allow users to specify which background they want to replace
|
||||
List<Resource> RCDATA = vi.Resources[resId];
|
||||
int idx = RCDATA.FindIndex(r => r.Name.Name == "FRAMEBACKGROUND06.PNG");
|
||||
if (idx < 0)
|
||||
return $"Failed to locate background image resource in Zune software.";
|
||||
GenericResource ogRes = (GenericResource)RCDATA[idx];
|
||||
|
||||
// Replace image
|
||||
byte[] data = await File.ReadAllBytesAsync(bgimgInfo.FullName);
|
||||
BitmapResource newRes = new(IntPtr.Zero, IntPtr.Zero, resId, ogRes.Name, ogRes.Language, data.Length);
|
||||
newRes.Bitmap = new(data);
|
||||
RCDATA[idx] = newRes;
|
||||
File.Copy(zsrDllInfo.FullName, zsrDllTempPath, true);
|
||||
newRes.SaveTo(zsrDllTempPath);
|
||||
}
|
||||
File.Copy(zsrDllTempPath, zsrDllInfo.FullName, true);
|
||||
// Replace image
|
||||
byte[] data = await File.ReadAllBytesAsync(bgimgInfo.FullName);
|
||||
BitmapResource newRes = new(IntPtr.Zero, IntPtr.Zero, resId, ogRes.Name, ogRes.Language, data.Length);
|
||||
newRes.Bitmap = new(data);
|
||||
RCDATA[idx] = newRes;
|
||||
File.Copy(zsrDllInfo.FullName, zsrDllTempPath, true);
|
||||
newRes.SaveTo(zsrDllTempPath);
|
||||
}
|
||||
File.Copy(zsrDllTempPath, zsrDllInfo.FullName, true);
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return $"Unable to replace '{zsrDllInfo.FullName}'. Verify that the Zune software is not running and try again.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.ToString());
|
||||
return ex.Message;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Task<string?> Reset()
|
||||
catch (IOException)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Copy backup to application folder
|
||||
File.Copy(Path.Combine(StorageDirectory, "ZuneShellResources.original.dll"),
|
||||
Path.Combine(ZuneInstallDir, "ZuneShellResources.dll"), true);
|
||||
return $"Unable to replace '{zsrDllInfo.FullName}'. Verify that the Zune software is not running and try again.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.ToString());
|
||||
return ex.Message;
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult<string?>(null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult(ex.Message)!;
|
||||
}
|
||||
public override Task<string?> Reset()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Copy backup to application folder
|
||||
File.Copy(Path.Combine(StorageDirectory, "ZuneShellResources.original.dll"),
|
||||
Path.Combine(ZuneInstallDir, "ZuneShellResources.dll"), true);
|
||||
|
||||
return Task.FromResult<string?>(null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult(ex.Message)!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,116 +4,115 @@ using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using ZuneModCore.Win32;
|
||||
|
||||
namespace ZuneModCore.Mods
|
||||
namespace ZuneModCore.Mods;
|
||||
|
||||
public class FeaturesOverrideMod : Mod
|
||||
{
|
||||
public class FeaturesOverrideMod : Mod
|
||||
private const string ZUNE_FEATURESOVERRIDE_REGKEY = RegEdit.ZUNE_REG_PATH + "FeaturesOverride";
|
||||
|
||||
public override string Id => nameof(FeaturesOverrideMod);
|
||||
|
||||
public override string Title => "Features Override";
|
||||
|
||||
public override string Description => "Re-enables access to some features disabled by Microsoft, such as the Social and Marketplace tabs.\r\n" +
|
||||
"Does not restore functionality of those features, but shows them in the software.";
|
||||
|
||||
public override string Author => "Rafael Rivera";
|
||||
|
||||
public override AbstractUICollection? GetDefaultOptionsUI()
|
||||
{
|
||||
private const string ZUNE_FEATURESOVERRIDE_REGKEY = RegEdit.ZUNE_REG_PATH + "FeaturesOverride";
|
||||
|
||||
public override string Id => nameof(FeaturesOverrideMod);
|
||||
|
||||
public override string Title => "Features Override";
|
||||
|
||||
public override string Description => "Re-enables access to some features disabled by Microsoft, such as the Social and Marketplace tabs.\r\n" +
|
||||
"Does not restore functionality of those features, but shows them in the software.";
|
||||
|
||||
public override string Author => "Rafael Rivera";
|
||||
|
||||
public override AbstractUICollection? GetDefaultOptionsUI()
|
||||
AbstractUICollection optionsUi = new(nameof(FeaturesOverrideMod))
|
||||
{
|
||||
AbstractUICollection optionsUi = new(nameof(FeaturesOverrideMod))
|
||||
{
|
||||
// We don't know what some of these overrides do exactly, so hide them from the user.
|
||||
// The ID is the name of the registry key, the label is the display name
|
||||
// We don't know what some of these overrides do exactly, so hide them from the user.
|
||||
// The ID is the name of the registry key, the label is the display name
|
||||
|
||||
new AbstractBoolean("Apps", "Apps"),
|
||||
new AbstractBoolean("Art", "Art"),
|
||||
new AbstractBoolean("Channels", "Channels"),
|
||||
new AbstractBoolean("FirstLaunchIntroVideo", "First Launch Intro Video"),
|
||||
new AbstractBoolean("Games", "Games"),
|
||||
new AbstractBoolean("Marketplace", "Marketplace"),
|
||||
new AbstractBoolean("MBRPreview", "[Marketplace] Media Preview"),
|
||||
new AbstractBoolean("MBRPurchase", "[Marketplace] Media Purchase"),
|
||||
new AbstractBoolean("MBRRental", "[Marketplace] Media Rental"),
|
||||
new AbstractBoolean("Music", "Music"),
|
||||
new AbstractBoolean("MusicVideos", "Music Videos"),
|
||||
new AbstractBoolean("Nowplaying", "Now Playing"),
|
||||
new AbstractBoolean("NowplayingArt", "Now Playing Art"),
|
||||
new AbstractBoolean("Picks", "Picks"),
|
||||
new AbstractBoolean("Podcasts", "Podcasts"),
|
||||
new AbstractBoolean("QuickMixLocal", "Quick Mix (Local)"),
|
||||
new AbstractBoolean("QuickMixZMP", "Quick Mix (ZMP)"),
|
||||
new AbstractBoolean("Quickplay", "Quickplay"),
|
||||
new AbstractBoolean("Sign In Available", "Sign In"),
|
||||
new AbstractBoolean("Social", "Social"),
|
||||
new AbstractBoolean("SocialMarketplace", "Social Marketplace"),
|
||||
new AbstractBoolean("SubscriptionFreeTracks", "Subscription Free Tracks"),
|
||||
new AbstractBoolean("Videos", "Videos"),
|
||||
};
|
||||
optionsUi.Title = "select features";
|
||||
optionsUi.Subtitle = "CHOOSE WHICH ZUNE FEATURES YOU WISH TO ENABLE.";
|
||||
return optionsUi;
|
||||
}
|
||||
new AbstractBoolean("Apps", "Apps"),
|
||||
new AbstractBoolean("Art", "Art"),
|
||||
new AbstractBoolean("Channels", "Channels"),
|
||||
new AbstractBoolean("FirstLaunchIntroVideo", "First Launch Intro Video"),
|
||||
new AbstractBoolean("Games", "Games"),
|
||||
new AbstractBoolean("Marketplace", "Marketplace"),
|
||||
new AbstractBoolean("MBRPreview", "[Marketplace] Media Preview"),
|
||||
new AbstractBoolean("MBRPurchase", "[Marketplace] Media Purchase"),
|
||||
new AbstractBoolean("MBRRental", "[Marketplace] Media Rental"),
|
||||
new AbstractBoolean("Music", "Music"),
|
||||
new AbstractBoolean("MusicVideos", "Music Videos"),
|
||||
new AbstractBoolean("Nowplaying", "Now Playing"),
|
||||
new AbstractBoolean("NowplayingArt", "Now Playing Art"),
|
||||
new AbstractBoolean("Picks", "Picks"),
|
||||
new AbstractBoolean("Podcasts", "Podcasts"),
|
||||
new AbstractBoolean("QuickMixLocal", "Quick Mix (Local)"),
|
||||
new AbstractBoolean("QuickMixZMP", "Quick Mix (ZMP)"),
|
||||
new AbstractBoolean("Quickplay", "Quickplay"),
|
||||
new AbstractBoolean("Sign In Available", "Sign In"),
|
||||
new AbstractBoolean("Social", "Social"),
|
||||
new AbstractBoolean("SocialMarketplace", "Social Marketplace"),
|
||||
new AbstractBoolean("SubscriptionFreeTracks", "Subscription Free Tracks"),
|
||||
new AbstractBoolean("Videos", "Videos"),
|
||||
};
|
||||
optionsUi.Title = "select features";
|
||||
optionsUi.Subtitle = "CHOOSE WHICH ZUNE FEATURES YOU WISH TO ENABLE.";
|
||||
return optionsUi;
|
||||
}
|
||||
|
||||
public override IReadOnlyList<Type>? DependentMods => null;
|
||||
public override IReadOnlyList<Type>? DependentMods => null;
|
||||
|
||||
public override Task Init()
|
||||
public override Task Init()
|
||||
{
|
||||
foreach (AbstractUIElement uiElem in OptionsUI!)
|
||||
{
|
||||
foreach (AbstractUIElement uiElem in OptionsUI!)
|
||||
if (uiElem is AbstractBoolean boolElem)
|
||||
{
|
||||
if (uiElem is AbstractBoolean boolElem)
|
||||
{
|
||||
bool? featureOverride = GetFeatureOverride(boolElem.Id);
|
||||
boolElem.State = featureOverride ?? false;
|
||||
}
|
||||
bool? featureOverride = GetFeatureOverride(boolElem.Id);
|
||||
boolElem.State = featureOverride ?? false;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override async Task<string?> Apply() => await Apply(false);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task<string?> Apply(bool applyAll = false)
|
||||
public override async Task<string?> Apply() => await Apply(false);
|
||||
|
||||
public async Task<string?> Apply(bool applyAll = false)
|
||||
{
|
||||
// Use user choices from AbstractUI
|
||||
foreach (AbstractUIElement uiElem in OptionsUI!)
|
||||
{
|
||||
// Use user choices from AbstractUI
|
||||
foreach (AbstractUIElement uiElem in OptionsUI!)
|
||||
if (uiElem is AbstractBoolean boolElem && (boolElem.State || applyAll))
|
||||
{
|
||||
if (uiElem is AbstractBoolean boolElem && (boolElem.State || applyAll))
|
||||
bool isSuccess = SetFeatureOverride(boolElem.Id, true);
|
||||
if (!isSuccess)
|
||||
{
|
||||
bool isSuccess = SetFeatureOverride(boolElem.Id, true);
|
||||
if (!isSuccess)
|
||||
string? resetStatus = await Reset();
|
||||
if (resetStatus != null)
|
||||
{
|
||||
string? resetStatus = await Reset();
|
||||
if (resetStatus != null)
|
||||
{
|
||||
// The reset failed as well, return both errors
|
||||
return "Failed to set registry keys. Unable to clean up partial overrides:\r\n" + resetStatus;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Failed to set registry keys. Automatically cleaned up partial changes.";
|
||||
}
|
||||
// The reset failed as well, return both errors
|
||||
return "Failed to set registry keys. Unable to clean up partial overrides:\r\n" + resetStatus;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Failed to set registry keys. Automatically cleaned up partial changes.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override async Task<string?> Reset()
|
||||
{
|
||||
RegEdit.CurrentUserDeleteKey(ZUNE_FEATURESOVERRIDE_REGKEY);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool SetFeatureOverride(string feature, bool value) =>
|
||||
RegEdit.CurrentUserSetBoolValue(ZUNE_FEATURESOVERRIDE_REGKEY, feature, value);
|
||||
|
||||
public static bool? GetFeatureOverride(string feature) =>
|
||||
RegEdit.CurrentUserGetBoolValue(ZUNE_FEATURESOVERRIDE_REGKEY, feature);
|
||||
|
||||
public static void ResetFeatureOverride(string feature) =>
|
||||
RegEdit.CurrentUserDeleteValue(ZUNE_FEATURESOVERRIDE_REGKEY, feature);
|
||||
return null;
|
||||
}
|
||||
|
||||
public override async Task<string?> Reset()
|
||||
{
|
||||
RegEdit.CurrentUserDeleteKey(ZUNE_FEATURESOVERRIDE_REGKEY);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool SetFeatureOverride(string feature, bool value) =>
|
||||
RegEdit.CurrentUserSetBoolValue(ZUNE_FEATURESOVERRIDE_REGKEY, feature, value);
|
||||
|
||||
public static bool? GetFeatureOverride(string feature) =>
|
||||
RegEdit.CurrentUserGetBoolValue(ZUNE_FEATURESOVERRIDE_REGKEY, feature);
|
||||
|
||||
public static void ResetFeatureOverride(string feature) =>
|
||||
RegEdit.CurrentUserDeleteValue(ZUNE_FEATURESOVERRIDE_REGKEY, feature);
|
||||
}
|
||||
|
||||
+139
-143
@@ -5,167 +5,163 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ZuneModCore.Mods
|
||||
namespace ZuneModCore.Mods;
|
||||
|
||||
public class MbidLocatorMod : Mod
|
||||
{
|
||||
public class MbidLocatorMod : Mod
|
||||
public const string ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME = "ZuneAlbumArtistMediaID";
|
||||
public const string ZUNE_ALBUM_MEDIA_ID_NAME = "ZuneAlbumMediaID";
|
||||
public const string ZUNE_MEDIA_ID_NAME = "ZuneMediaID";
|
||||
public const string ZUNE_COLLECTION_ID_NAME = "ZuneCollectionID";
|
||||
|
||||
private static readonly string[] KNOWN_EXTS = [".mp3", ".mp4", ".m4a", ".wav"];
|
||||
|
||||
public override string Id => nameof(MbidLocatorMod);
|
||||
|
||||
public override string Title => "MusicBrainz ID Locator";
|
||||
|
||||
public override string Description => "Puts MusicBrainz IDs added by MusicBrainz Picard where the Zune " +
|
||||
"software can use it to show additional information provided by Community Webservices.\r\n" +
|
||||
"Note that this will only have an effect if you have used MusicBrainz Picard on your music library.";
|
||||
|
||||
public override string Author => "Joshua \"Yoshi\" Askharoun";
|
||||
|
||||
public override AbstractUICollection? GetDefaultOptionsUI()
|
||||
{
|
||||
public const string ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME = "ZuneAlbumArtistMediaID";
|
||||
public const string ZUNE_ALBUM_MEDIA_ID_NAME = "ZuneAlbumMediaID";
|
||||
public const string ZUNE_MEDIA_ID_NAME = "ZuneMediaID";
|
||||
public const string ZUNE_COLLECTION_ID_NAME = "ZuneCollectionID";
|
||||
|
||||
private static readonly string[] KNOWN_EXTS = new[]
|
||||
AbstractUICollection optionsUi = new(nameof(MbidLocatorMod))
|
||||
{
|
||||
".mp3", ".mp4", ".m4a", ".wav"
|
||||
new AbstractTextBox("folderBox", Environment.GetFolderPath(Environment.SpecialFolder.MyMusic)),
|
||||
new AbstractBoolean("recursiveBox", "Search recursively")
|
||||
};
|
||||
optionsUi.Title = "select music";
|
||||
optionsUi.Subtitle = "CHOOSE YOUR MUSIC FOLDER.";
|
||||
return optionsUi;
|
||||
}
|
||||
|
||||
public override string Id => nameof(MbidLocatorMod);
|
||||
public override IReadOnlyList<Type>? DependentMods => null;
|
||||
|
||||
public override string Title => "MusicBrainz ID Locator";
|
||||
public override async Task<string?> Apply()
|
||||
{
|
||||
// Use user choices from AbstractUI
|
||||
string folderPath = ((AbstractTextBox)OptionsUI![0]).Value;
|
||||
bool recursive = ((AbstractBoolean)OptionsUI[1]).State;
|
||||
string errorString = string.Empty;
|
||||
|
||||
public override string Description => "Puts MusicBrainz IDs added by MusicBrainz Picard where the Zune " +
|
||||
"software can use it to show additional information provided by Community Webservices.\r\n" +
|
||||
"Note that this will only have an effect if you have used MusicBrainz Picard on your music library.";
|
||||
|
||||
public override string Author => "Joshua \"Yoshi\" Askharoun";
|
||||
|
||||
public override AbstractUICollection? GetDefaultOptionsUI()
|
||||
// Verify that the folder exists
|
||||
DirectoryInfo folder = new(folderPath);
|
||||
if (!folder.Exists)
|
||||
{
|
||||
AbstractUICollection optionsUi = new(nameof(MbidLocatorMod))
|
||||
{
|
||||
new AbstractTextBox("folderBox", Environment.GetFolderPath(Environment.SpecialFolder.MyMusic)),
|
||||
new AbstractBoolean("recursiveBox", "Search recursively")
|
||||
};
|
||||
optionsUi.Title = "select music";
|
||||
optionsUi.Subtitle = "CHOOSE YOUR MUSIC FOLDER.";
|
||||
return optionsUi;
|
||||
return $"The folder '{folder.FullName}' does not exist.";
|
||||
}
|
||||
|
||||
public override IReadOnlyList<Type>? DependentMods => null;
|
||||
|
||||
public override async Task<string?> Apply()
|
||||
foreach (FileInfo file in folder.GetFiles("*.*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
// Use user choices from AbstractUI
|
||||
string folderPath = ((AbstractTextBox)OptionsUI![0]).Value;
|
||||
bool recursive = ((AbstractBoolean)OptionsUI[1]).State;
|
||||
string errorString = string.Empty;
|
||||
|
||||
// Verify that the folder exists
|
||||
DirectoryInfo folder = new(folderPath);
|
||||
if (!folder.Exists)
|
||||
try
|
||||
{
|
||||
return $"The folder '{folder.FullName}' does not exist.";
|
||||
}
|
||||
|
||||
foreach (FileInfo file in folder.GetFiles("*.*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (KNOWN_EXTS.Contains(file.Extension))
|
||||
UpdateMbidInFile(file);
|
||||
if (KNOWN_EXTS.Contains(file.Extension))
|
||||
UpdateMbidInFile(file);
|
||||
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debug.WriteLine(file);
|
||||
System.Diagnostics.Debug.WriteLine(file);
|
||||
#endif
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorString += ex.Message + "\r\n";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (errorString.Length != 0)
|
||||
return errorString;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Task<string?> Reset()
|
||||
{
|
||||
return Task.FromResult<string?>(null);
|
||||
}
|
||||
|
||||
public static void UpdateMbidInFile(FileInfo file)
|
||||
{
|
||||
var tfile = TagLib.File.Create(file.FullName);
|
||||
|
||||
if (tfile.Tag is TagLib.Asf.Tag asfTag)
|
||||
catch (Exception ex)
|
||||
{
|
||||
var albumArtistIdDesc = asfTag.ExtendedContentDescriptionObject
|
||||
.FirstOrDefault(cd => cd.Name == ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME);
|
||||
if (albumArtistIdDesc == null && asfTag.MusicBrainzReleaseArtistId != null)
|
||||
{
|
||||
asfTag.ExtendedContentDescriptionObject.AddDescriptor(new TagLib.Asf.ContentDescriptor(
|
||||
ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME, asfTag.MusicBrainzReleaseArtistId));
|
||||
}
|
||||
|
||||
var albumIdDesc = asfTag.ExtendedContentDescriptionObject
|
||||
.FirstOrDefault(cd => cd.Name == ZUNE_ALBUM_MEDIA_ID_NAME);
|
||||
if (albumIdDesc == null && asfTag.MusicBrainzReleaseId != null)
|
||||
{
|
||||
asfTag.ExtendedContentDescriptionObject.AddDescriptor(new TagLib.Asf.ContentDescriptor(
|
||||
ZUNE_ALBUM_MEDIA_ID_NAME, asfTag.MusicBrainzReleaseId));
|
||||
}
|
||||
|
||||
var trackIdDesc = asfTag.ExtendedContentDescriptionObject
|
||||
.FirstOrDefault(cd => cd.Name == ZUNE_MEDIA_ID_NAME);
|
||||
if (trackIdDesc == null && asfTag.MusicBrainzTrackId != null)
|
||||
{
|
||||
asfTag.ExtendedContentDescriptionObject.AddDescriptor(new TagLib.Asf.ContentDescriptor(
|
||||
ZUNE_MEDIA_ID_NAME, asfTag.MusicBrainzTrackId));
|
||||
}
|
||||
errorString += ex.Message + "\r\n";
|
||||
continue;
|
||||
}
|
||||
else if (tfile.Tag is TagLib.NonContainer.Tag ncTag)
|
||||
{
|
||||
var tag = ncTag.Tags.FirstOrDefault(t => t.TagTypes == TagLib.TagTypes.Id3v2);
|
||||
if (tag is TagLib.Id3v2.Tag id3v2)
|
||||
{
|
||||
var frames = id3v2.GetFrames<TagLib.Id3v2.PrivateFrame>();
|
||||
|
||||
// Note the use of .ToList(): this is because calling RemoveFrame() will
|
||||
// modify the underlying collection, causing an exception to be thrown
|
||||
// when attempting the next iteration of the for loop
|
||||
|
||||
if (tag.MusicBrainzReleaseArtistId != null)
|
||||
{
|
||||
foreach (var oldAlbumArtistFrame in frames.Where(f => f.Owner == ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME).ToList())
|
||||
id3v2.RemoveFrame(oldAlbumArtistFrame);
|
||||
|
||||
TagLib.Id3v2.PrivateFrame albumArtistFrame = new(ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME)
|
||||
{
|
||||
PrivateData = new(new Guid(tag.MusicBrainzReleaseArtistId).ToByteArray())
|
||||
};
|
||||
id3v2.AddFrame(albumArtistFrame);
|
||||
}
|
||||
|
||||
if (tag.MusicBrainzReleaseId != null)
|
||||
{
|
||||
foreach (var oldAlbumFrame in frames.Where(f => f.Owner == ZUNE_ALBUM_MEDIA_ID_NAME).ToList())
|
||||
id3v2.RemoveFrame(oldAlbumFrame);
|
||||
|
||||
TagLib.Id3v2.PrivateFrame albumFrame = new(ZUNE_ALBUM_MEDIA_ID_NAME)
|
||||
{
|
||||
PrivateData = new(new Guid(tag.MusicBrainzReleaseId).ToByteArray())
|
||||
};
|
||||
id3v2.AddFrame(albumFrame);
|
||||
}
|
||||
|
||||
if (tag.MusicBrainzTrackId != null)
|
||||
{
|
||||
foreach (var oldTrackFrame in frames.Where(f => f.Owner == ZUNE_MEDIA_ID_NAME).ToList())
|
||||
id3v2.RemoveFrame(oldTrackFrame);
|
||||
|
||||
TagLib.Id3v2.PrivateFrame trackFrame = new(ZUNE_MEDIA_ID_NAME)
|
||||
{
|
||||
PrivateData = new(new Guid(tag.MusicBrainzTrackId).ToByteArray())
|
||||
};
|
||||
id3v2.AddFrame(trackFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tfile.Save();
|
||||
}
|
||||
|
||||
if (errorString.Length != 0)
|
||||
return errorString;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Task<string?> Reset()
|
||||
{
|
||||
return Task.FromResult<string?>(null);
|
||||
}
|
||||
|
||||
public static void UpdateMbidInFile(FileInfo file)
|
||||
{
|
||||
var tfile = TagLib.File.Create(file.FullName);
|
||||
|
||||
if (tfile.Tag is TagLib.Asf.Tag asfTag)
|
||||
{
|
||||
var albumArtistIdDesc = asfTag.ExtendedContentDescriptionObject
|
||||
.FirstOrDefault(cd => cd.Name == ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME);
|
||||
if (albumArtistIdDesc == null && asfTag.MusicBrainzReleaseArtistId != null)
|
||||
{
|
||||
asfTag.ExtendedContentDescriptionObject.AddDescriptor(new TagLib.Asf.ContentDescriptor(
|
||||
ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME, asfTag.MusicBrainzReleaseArtistId));
|
||||
}
|
||||
|
||||
var albumIdDesc = asfTag.ExtendedContentDescriptionObject
|
||||
.FirstOrDefault(cd => cd.Name == ZUNE_ALBUM_MEDIA_ID_NAME);
|
||||
if (albumIdDesc == null && asfTag.MusicBrainzReleaseId != null)
|
||||
{
|
||||
asfTag.ExtendedContentDescriptionObject.AddDescriptor(new TagLib.Asf.ContentDescriptor(
|
||||
ZUNE_ALBUM_MEDIA_ID_NAME, asfTag.MusicBrainzReleaseId));
|
||||
}
|
||||
|
||||
var trackIdDesc = asfTag.ExtendedContentDescriptionObject
|
||||
.FirstOrDefault(cd => cd.Name == ZUNE_MEDIA_ID_NAME);
|
||||
if (trackIdDesc == null && asfTag.MusicBrainzTrackId != null)
|
||||
{
|
||||
asfTag.ExtendedContentDescriptionObject.AddDescriptor(new TagLib.Asf.ContentDescriptor(
|
||||
ZUNE_MEDIA_ID_NAME, asfTag.MusicBrainzTrackId));
|
||||
}
|
||||
}
|
||||
else if (tfile.Tag is TagLib.NonContainer.Tag ncTag)
|
||||
{
|
||||
var tag = ncTag.Tags.FirstOrDefault(t => t.TagTypes == TagLib.TagTypes.Id3v2);
|
||||
if (tag is TagLib.Id3v2.Tag id3v2)
|
||||
{
|
||||
var frames = id3v2.GetFrames<TagLib.Id3v2.PrivateFrame>();
|
||||
|
||||
// Note the use of .ToList(): this is because calling RemoveFrame() will
|
||||
// modify the underlying collection, causing an exception to be thrown
|
||||
// when attempting the next iteration of the for loop
|
||||
|
||||
if (tag.MusicBrainzReleaseArtistId != null)
|
||||
{
|
||||
foreach (var oldAlbumArtistFrame in frames.Where(f => f.Owner == ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME).ToList())
|
||||
id3v2.RemoveFrame(oldAlbumArtistFrame);
|
||||
|
||||
TagLib.Id3v2.PrivateFrame albumArtistFrame = new(ZUNE_ALBUM_ARTIST_MEDIA_ID_NAME)
|
||||
{
|
||||
PrivateData = new(new Guid(tag.MusicBrainzReleaseArtistId).ToByteArray())
|
||||
};
|
||||
id3v2.AddFrame(albumArtistFrame);
|
||||
}
|
||||
|
||||
if (tag.MusicBrainzReleaseId != null)
|
||||
{
|
||||
foreach (var oldAlbumFrame in frames.Where(f => f.Owner == ZUNE_ALBUM_MEDIA_ID_NAME).ToList())
|
||||
id3v2.RemoveFrame(oldAlbumFrame);
|
||||
|
||||
TagLib.Id3v2.PrivateFrame albumFrame = new(ZUNE_ALBUM_MEDIA_ID_NAME)
|
||||
{
|
||||
PrivateData = new(new Guid(tag.MusicBrainzReleaseId).ToByteArray())
|
||||
};
|
||||
id3v2.AddFrame(albumFrame);
|
||||
}
|
||||
|
||||
if (tag.MusicBrainzTrackId != null)
|
||||
{
|
||||
foreach (var oldTrackFrame in frames.Where(f => f.Owner == ZUNE_MEDIA_ID_NAME).ToList())
|
||||
id3v2.RemoveFrame(oldTrackFrame);
|
||||
|
||||
TagLib.Id3v2.PrivateFrame trackFrame = new(ZUNE_MEDIA_ID_NAME)
|
||||
{
|
||||
PrivateData = new(new Guid(tag.MusicBrainzTrackId).ToByteArray())
|
||||
};
|
||||
id3v2.AddFrame(trackFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tfile.Save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,92 +4,91 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ZuneModCore.Mods
|
||||
namespace ZuneModCore.Mods;
|
||||
|
||||
public class VideoSyncMod : Mod
|
||||
{
|
||||
public class VideoSyncMod : Mod
|
||||
private const int ZUNEENCENG_WMVCORA_OFFSET = 0x161E;
|
||||
|
||||
public override string Title => "Fix Video Sync";
|
||||
|
||||
public override string Description =>
|
||||
"Resolves \"Error C00D11CD\" when attempting to sync video to a Zune device using Windows 10 1607 (Anniversary Update) or newer";
|
||||
|
||||
public override string Author => "sylvathemoth";
|
||||
|
||||
public override string Id => nameof(VideoSyncMod);
|
||||
|
||||
public override async Task<string?> Apply()
|
||||
{
|
||||
private const int ZUNEENCENG_WMVCORA_OFFSET = 0x161E;
|
||||
// Verify that ZuneEncEng.dll exists
|
||||
FileInfo zeeDllInfo = new(Path.Combine(ZuneInstallDir, "ZuneEncEng.dll"));
|
||||
if (!zeeDllInfo.Exists)
|
||||
return $"The file '{zeeDllInfo.FullName}' does not exist.";
|
||||
|
||||
public override string Title => "Fix Video Sync";
|
||||
// Make a backup if it doesn't already exist
|
||||
FileInfo zeeDllBackupInfo = new(Path.Combine(StorageDirectory, "ZuneEncEng.original.dll"));
|
||||
if (!zeeDllBackupInfo.Exists)
|
||||
File.Copy(zeeDllInfo.FullName, zeeDllBackupInfo.FullName, true);
|
||||
|
||||
public override string Description =>
|
||||
"Resolves \"Error C00D11CD\" when attempting to sync video to a Zune device using Windows 10 1607 (Anniversary Update) or newer";
|
||||
|
||||
public override string Author => "sylvathemoth";
|
||||
|
||||
public override string Id => nameof(VideoSyncMod);
|
||||
|
||||
public override async Task<string?> Apply()
|
||||
try
|
||||
{
|
||||
// Verify that ZuneEncEng.dll exists
|
||||
FileInfo zeeDllInfo = new(Path.Combine(ZuneInstallDir, "ZuneEncEng.dll"));
|
||||
if (!zeeDllInfo.Exists)
|
||||
return $"The file '{zeeDllInfo.FullName}' does not exist.";
|
||||
// Verify that the DLL is from v4.8 (other versions not tested)
|
||||
var zeeDllVersion = FileVersionInfo.GetVersionInfo(zeeDllInfo.FullName);
|
||||
if (zeeDllVersion is null || zeeDllVersion.FileMajorPart != 4 || zeeDllVersion.FileMinorPart != 8)
|
||||
return "This mod has not been tested on versions earlier than 4.8.";
|
||||
|
||||
// Make a backup if it doesn't already exist
|
||||
FileInfo zeeDllBackupInfo = new(Path.Combine(StorageDirectory, "ZuneEncEng.original.dll"));
|
||||
if (!zeeDllBackupInfo.Exists)
|
||||
File.Copy(zeeDllInfo.FullName, zeeDllBackupInfo.FullName, true);
|
||||
|
||||
try
|
||||
// Open the file
|
||||
using (FileStream zeeDll = zeeDllInfo.Open(FileMode.Open))
|
||||
using (BinaryWriter zeeDllWriter = new(zeeDll))
|
||||
{
|
||||
// Verify that the DLL is from v4.8 (other versions not tested)
|
||||
var zeeDllVersion = FileVersionInfo.GetVersionInfo(zeeDllInfo.FullName);
|
||||
if (zeeDllVersion is null || zeeDllVersion.FileMajorPart != 4 || zeeDllVersion.FileMinorPart != 8)
|
||||
return "This mod has not been tested on versions earlier than 4.8.";
|
||||
// Patch ZuneEncEng.dll to point to the local WMVCORE.DLL
|
||||
zeeDllWriter.Seek(ZUNEENCENG_WMVCORA_OFFSET, SeekOrigin.Begin);
|
||||
zeeDllWriter.Write((byte)'a');
|
||||
|
||||
// Open the file
|
||||
using (FileStream zeeDll = zeeDllInfo.Open(FileMode.Open))
|
||||
using (BinaryWriter zeeDllWriter = new(zeeDll))
|
||||
{
|
||||
// Patch ZuneEncEng.dll to point to the local WMVCORE.DLL
|
||||
zeeDllWriter.Seek(ZUNEENCENG_WMVCORA_OFFSET, SeekOrigin.Begin);
|
||||
zeeDllWriter.Write((byte)'a');
|
||||
|
||||
zeeDllWriter.Flush();
|
||||
}
|
||||
|
||||
// Get the working WMVCORE.dll
|
||||
byte[] wmvDllAniv = ModResources.WMVCORE;
|
||||
|
||||
// Copy the WMVCore files to the Zune directory.
|
||||
// Only ZuneEncEng.dll searches in System32 first; all other dependent
|
||||
// DLLs will search the Zune folder.
|
||||
await File.WriteAllBytesAsync(Path.Combine(ZuneInstallDir, "wmvcora.dll"), wmvDllAniv);
|
||||
await File.WriteAllBytesAsync(Path.Combine(ZuneInstallDir, "wmvcore.dll"), wmvDllAniv);
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return $"Unable to replace '{zeeDllInfo.FullName}'. Verify that the Zune software is not running and try again.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ex.Message;
|
||||
zeeDllWriter.Flush();
|
||||
}
|
||||
|
||||
// Get the working WMVCORE.dll
|
||||
byte[] wmvDllAniv = ModResources.WMVCORE;
|
||||
|
||||
// Copy the WMVCore files to the Zune directory.
|
||||
// Only ZuneEncEng.dll searches in System32 first; all other dependent
|
||||
// DLLs will search the Zune folder.
|
||||
await File.WriteAllBytesAsync(Path.Combine(ZuneInstallDir, "wmvcora.dll"), wmvDllAniv);
|
||||
await File.WriteAllBytesAsync(Path.Combine(ZuneInstallDir, "wmvcore.dll"), wmvDllAniv);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Task<string?> Reset()
|
||||
catch (IOException)
|
||||
{
|
||||
var zeeDllPath = Path.Combine(ZuneInstallDir, "ZuneEncEng.dll");
|
||||
try
|
||||
{
|
||||
// Copy backup to application folder
|
||||
File.Copy(Path.Combine(StorageDirectory, "ZuneEncEng.original.dll"), zeeDllPath, true);
|
||||
|
||||
// Delete WMVCore files
|
||||
File.Delete(Path.Combine(ZuneInstallDir, "wmvcora.dll"));
|
||||
File.Delete(Path.Combine(ZuneInstallDir, "wmvcore.dll"));
|
||||
|
||||
return Task.FromResult<string?>(null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult(ex.Message)!;
|
||||
}
|
||||
return $"Unable to replace '{zeeDllInfo.FullName}'. Verify that the Zune software is not running and try again.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ex.Message;
|
||||
}
|
||||
|
||||
public override IReadOnlyList<Type>? DependentMods => null;
|
||||
}
|
||||
|
||||
public override Task<string?> Reset()
|
||||
{
|
||||
var zeeDllPath = Path.Combine(ZuneInstallDir, "ZuneEncEng.dll");
|
||||
try
|
||||
{
|
||||
// Copy backup to application folder
|
||||
File.Copy(Path.Combine(StorageDirectory, "ZuneEncEng.original.dll"), zeeDllPath, true);
|
||||
|
||||
// Delete WMVCore files
|
||||
File.Delete(Path.Combine(ZuneInstallDir, "wmvcora.dll"));
|
||||
File.Delete(Path.Combine(ZuneInstallDir, "wmvcore.dll"));
|
||||
|
||||
return Task.FromResult<string?>(null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult(ex.Message)!;
|
||||
}
|
||||
}
|
||||
|
||||
public override IReadOnlyList<Type>? DependentMods => null;
|
||||
}
|
||||
|
||||
+233
-255
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
||||
namespace ZuneModCore.Services
|
||||
namespace ZuneModCore.Services;
|
||||
|
||||
public interface IModCoreConfig
|
||||
{
|
||||
public interface IModCoreConfig
|
||||
{
|
||||
string ZuneInstallDir { get; }
|
||||
}
|
||||
string ZuneInstallDir { get; }
|
||||
}
|
||||
|
||||
+120
-128
@@ -1,151 +1,143 @@
|
||||
using System;
|
||||
namespace ZuneModCore.Win32;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ZuneModCore.Win32
|
||||
public static partial class FileUtil
|
||||
{
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
static public class FileUtil
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct RM_UNIQUE_PROCESS
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct RM_UNIQUE_PROCESS
|
||||
public int dwProcessId;
|
||||
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
|
||||
}
|
||||
|
||||
const int RmRebootReasonNone = 0;
|
||||
const int CCH_RM_MAX_APP_NAME = 255;
|
||||
const int CCH_RM_MAX_SVC_NAME = 63;
|
||||
|
||||
enum RM_APP_TYPE
|
||||
{
|
||||
RmUnknownApp = 0,
|
||||
RmMainWindow = 1,
|
||||
RmOtherWindow = 2,
|
||||
RmService = 3,
|
||||
RmExplorer = 4,
|
||||
RmConsole = 5,
|
||||
RmCritical = 1000
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
struct RM_PROCESS_INFO
|
||||
{
|
||||
public RM_UNIQUE_PROCESS Process;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
|
||||
public string strAppName;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
|
||||
public string strServiceShortName;
|
||||
|
||||
public RM_APP_TYPE ApplicationType;
|
||||
public uint AppStatus;
|
||||
public uint TSSessionId;
|
||||
[MarshalAs(UnmanagedType.Bool)]
|
||||
public bool bRestartable;
|
||||
}
|
||||
|
||||
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
|
||||
static extern int RmRegisterResources(uint pSessionHandle,
|
||||
uint nFiles,
|
||||
string[] rgsFilenames,
|
||||
uint nApplications,
|
||||
[In] RM_UNIQUE_PROCESS[] rgApplications,
|
||||
uint nServices,
|
||||
string[] rgsServiceNames);
|
||||
|
||||
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
|
||||
static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
|
||||
|
||||
[LibraryImport("rstrtmgr.dll")]
|
||||
private static partial int RmEndSession(uint pSessionHandle);
|
||||
|
||||
[DllImport("rstrtmgr.dll")]
|
||||
static extern int RmGetList(uint dwSessionHandle,
|
||||
out uint pnProcInfoNeeded,
|
||||
ref uint pnProcInfo,
|
||||
[In, Out] RM_PROCESS_INFO[] rgAffectedApps,
|
||||
ref uint lpdwRebootReasons);
|
||||
|
||||
/// <summary>
|
||||
/// Find out what process(es) have a lock on the specified file.
|
||||
/// </summary>
|
||||
/// <param name="path">Path of the file.</param>
|
||||
/// <returns>Processes locking the file</returns>
|
||||
/// <remarks>See also:
|
||||
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
|
||||
/// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
|
||||
///
|
||||
/// </remarks>
|
||||
static public List<Process> WhoIsLocking(string path)
|
||||
{
|
||||
string key = Guid.NewGuid().ToString();
|
||||
List<Process> processes = [];
|
||||
|
||||
int res = RmStartSession(out uint handle, 0, key);
|
||||
if (res != 0)
|
||||
throw new Exception("Could not begin restart session. Unable to determine file locker.");
|
||||
|
||||
try
|
||||
{
|
||||
public int dwProcessId;
|
||||
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
|
||||
}
|
||||
const int ERROR_MORE_DATA = 234;
|
||||
uint pnProcInfo = 0,
|
||||
lpdwRebootReasons = RmRebootReasonNone;
|
||||
|
||||
const int RmRebootReasonNone = 0;
|
||||
const int CCH_RM_MAX_APP_NAME = 255;
|
||||
const int CCH_RM_MAX_SVC_NAME = 63;
|
||||
string[] resources = [path]; // Just checking on one resource.
|
||||
|
||||
enum RM_APP_TYPE
|
||||
{
|
||||
RmUnknownApp = 0,
|
||||
RmMainWindow = 1,
|
||||
RmOtherWindow = 2,
|
||||
RmService = 3,
|
||||
RmExplorer = 4,
|
||||
RmConsole = 5,
|
||||
RmCritical = 1000
|
||||
}
|
||||
res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
struct RM_PROCESS_INFO
|
||||
{
|
||||
public RM_UNIQUE_PROCESS Process;
|
||||
if (res != 0) throw new Exception("Could not register resource.");
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
|
||||
public string strAppName;
|
||||
//Note: there's a race condition here -- the first call to RmGetList() returns
|
||||
// the total number of process. However, when we call RmGetList() again to get
|
||||
// the actual processes this number may have increased.
|
||||
res = RmGetList(handle, out uint pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
|
||||
public string strServiceShortName;
|
||||
|
||||
public RM_APP_TYPE ApplicationType;
|
||||
public uint AppStatus;
|
||||
public uint TSSessionId;
|
||||
[MarshalAs(UnmanagedType.Bool)]
|
||||
public bool bRestartable;
|
||||
}
|
||||
|
||||
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
|
||||
static extern int RmRegisterResources(uint pSessionHandle,
|
||||
UInt32 nFiles,
|
||||
string[] rgsFilenames,
|
||||
UInt32 nApplications,
|
||||
[In] RM_UNIQUE_PROCESS[] rgApplications,
|
||||
UInt32 nServices,
|
||||
string[] rgsServiceNames);
|
||||
|
||||
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
|
||||
static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
|
||||
|
||||
[DllImport("rstrtmgr.dll")]
|
||||
static extern int RmEndSession(uint pSessionHandle);
|
||||
|
||||
[DllImport("rstrtmgr.dll")]
|
||||
static extern int RmGetList(uint dwSessionHandle,
|
||||
out uint pnProcInfoNeeded,
|
||||
ref uint pnProcInfo,
|
||||
[In, Out] RM_PROCESS_INFO[] rgAffectedApps,
|
||||
ref uint lpdwRebootReasons);
|
||||
|
||||
/// <summary>
|
||||
/// Find out what process(es) have a lock on the specified file.
|
||||
/// </summary>
|
||||
/// <param name="path">Path of the file.</param>
|
||||
/// <returns>Processes locking the file</returns>
|
||||
/// <remarks>See also:
|
||||
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
|
||||
/// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
|
||||
///
|
||||
/// </remarks>
|
||||
static public List<Process> WhoIsLocking(string path)
|
||||
{
|
||||
uint handle;
|
||||
string key = Guid.NewGuid().ToString();
|
||||
List<Process> processes = new List<Process>();
|
||||
|
||||
int res = RmStartSession(out handle, 0, key);
|
||||
if (res != 0) throw new Exception("Could not begin restart session. Unable to determine file locker.");
|
||||
|
||||
try
|
||||
if (res == ERROR_MORE_DATA)
|
||||
{
|
||||
const int ERROR_MORE_DATA = 234;
|
||||
uint pnProcInfoNeeded = 0,
|
||||
pnProcInfo = 0,
|
||||
lpdwRebootReasons = RmRebootReasonNone;
|
||||
// Create an array to store the process results
|
||||
RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
|
||||
pnProcInfo = pnProcInfoNeeded;
|
||||
|
||||
string[] resources = new string[] { path }; // Just checking on one resource.
|
||||
|
||||
res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);
|
||||
|
||||
if (res != 0) throw new Exception("Could not register resource.");
|
||||
|
||||
//Note: there's a race condition here -- the first call to RmGetList() returns
|
||||
// the total number of process. However, when we call RmGetList() again to get
|
||||
// the actual processes this number may have increased.
|
||||
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
|
||||
|
||||
if (res == ERROR_MORE_DATA)
|
||||
// Get the list
|
||||
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
|
||||
if (res == 0)
|
||||
{
|
||||
// Create an array to store the process results
|
||||
RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
|
||||
pnProcInfo = pnProcInfoNeeded;
|
||||
processes = new List<Process>((int)pnProcInfo);
|
||||
|
||||
// Get the list
|
||||
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
|
||||
if (res == 0)
|
||||
// Enumerate all of the results and add them to the
|
||||
// list to be returned
|
||||
for (int i = 0; i < pnProcInfo; i++)
|
||||
{
|
||||
processes = new List<Process>((int)pnProcInfo);
|
||||
|
||||
// Enumerate all of the results and add them to the
|
||||
// list to be returned
|
||||
for (int i = 0; i < pnProcInfo; i++)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
|
||||
}
|
||||
// catch the error -- in case the process is no longer running
|
||||
catch (ArgumentException) { }
|
||||
processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
|
||||
}
|
||||
// catch the error -- in case the process is no longer running
|
||||
catch (ArgumentException) { }
|
||||
}
|
||||
else throw new Exception("Could not list processes locking resource.");
|
||||
}
|
||||
else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
|
||||
else throw new Exception("Could not list processes locking resource.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
RmEndSession(handle);
|
||||
}
|
||||
|
||||
return processes;
|
||||
else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
RmEndSession(handle);
|
||||
}
|
||||
|
||||
return processes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,73 +1,65 @@
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace ZuneModCore.Win32
|
||||
namespace ZuneModCore.Win32;
|
||||
|
||||
public class RegEdit
|
||||
{
|
||||
public const string ZUNE_REG_PATH = @"SOFTWARE\Microsoft\Zune\";
|
||||
|
||||
#pragma warning disable CA1416 // Validate platform compatibility
|
||||
|
||||
public class RegEdit
|
||||
public static bool CurrentUserSetBoolValue(string key, string name, bool value)
|
||||
{
|
||||
public const string ZUNE_REG_PATH = @"SOFTWARE\Microsoft\Zune\";
|
||||
RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(key, true);
|
||||
regKey ??= Registry.CurrentUser.CreateSubKey(key, true);
|
||||
|
||||
public static bool CurrentUserSetBoolValue(string key, string name, bool value)
|
||||
{
|
||||
RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(key, true);
|
||||
if (regKey == null)
|
||||
regKey = Registry.CurrentUser.CreateSubKey(key, true);
|
||||
regKey.SetValue(name, value, RegistryValueKind.DWord);
|
||||
regKey.Close();
|
||||
regKey.Dispose();
|
||||
|
||||
regKey.SetValue(name, value, RegistryValueKind.DWord);
|
||||
regKey.Close();
|
||||
regKey.Dispose();
|
||||
|
||||
// Read the key to make sure it was set properly
|
||||
if (CurrentUserGetBoolValue(key, name) == null)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool? CurrentUserGetBoolValue(string key, string name)
|
||||
{
|
||||
using RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(key, true);
|
||||
if (regKey == null)
|
||||
return false;
|
||||
|
||||
// Return null if a boolean value couldn't be read from the key
|
||||
int? value = regKey.GetValue(name, false) as int?;
|
||||
if (!value.HasValue)
|
||||
return null;
|
||||
return value != 0;
|
||||
}
|
||||
|
||||
public static void CurrentUserDeleteKey(string key)
|
||||
{
|
||||
using RegistryKey? targetKey = Registry.CurrentUser.OpenSubKey(key, true);
|
||||
if (targetKey == null)
|
||||
// Key is already deleted
|
||||
return;
|
||||
|
||||
// Delete target subkeys
|
||||
if (targetKey.SubKeyCount > 0)
|
||||
targetKey.DeleteSubKeyTree(key, false);
|
||||
|
||||
// Delete target values
|
||||
string[] values = targetKey.GetValueNames();
|
||||
foreach (string value in values)
|
||||
targetKey.DeleteValue(value, false);
|
||||
|
||||
// Delete target key
|
||||
Registry.CurrentUser.DeleteSubKey(key);
|
||||
}
|
||||
|
||||
public static void CurrentUserDeleteValue(string key, string name)
|
||||
{
|
||||
using RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(key, true);
|
||||
if (regKey == null)
|
||||
return;
|
||||
|
||||
regKey.DeleteValue(name, false);
|
||||
}
|
||||
// Read the key to make sure it was set properly
|
||||
if (CurrentUserGetBoolValue(key, name) == null)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma warning restore CA1416 // Validate platform compatibility
|
||||
public static bool? CurrentUserGetBoolValue(string key, string name)
|
||||
{
|
||||
using RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(key, true);
|
||||
if (regKey == null)
|
||||
return false;
|
||||
|
||||
// Return null if a boolean value couldn't be read from the key
|
||||
int? value = regKey.GetValue(name, false) as int?;
|
||||
if (!value.HasValue)
|
||||
return null;
|
||||
return value != 0;
|
||||
}
|
||||
|
||||
public static void CurrentUserDeleteKey(string key)
|
||||
{
|
||||
using RegistryKey? targetKey = Registry.CurrentUser.OpenSubKey(key, true);
|
||||
if (targetKey == null)
|
||||
// Key is already deleted
|
||||
return;
|
||||
|
||||
// Delete target subkeys
|
||||
if (targetKey.SubKeyCount > 0)
|
||||
targetKey.DeleteSubKeyTree(key, false);
|
||||
|
||||
// Delete target values
|
||||
string[] values = targetKey.GetValueNames();
|
||||
foreach (string value in values)
|
||||
targetKey.DeleteValue(value, false);
|
||||
|
||||
// Delete target key
|
||||
Registry.CurrentUser.DeleteSubKey(key);
|
||||
}
|
||||
|
||||
public static void CurrentUserDeleteValue(string key, string name)
|
||||
{
|
||||
using RegistryKey? regKey = Registry.CurrentUser.OpenSubKey(key, true);
|
||||
if (regKey == null)
|
||||
return;
|
||||
|
||||
regKey.DeleteValue(name, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,123 +1,119 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace ZuneModCore.Win32
|
||||
namespace ZuneModCore.Win32;
|
||||
|
||||
public class TokenManipulator
|
||||
{
|
||||
public class TokenManipulator
|
||||
public static bool AddPrivilege(string privilege)
|
||||
{
|
||||
public static bool AddPrivilege(string privilege)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
bool retVal;
|
||||
var hproc = Kernel32.GetCurrentProcess();
|
||||
bool retVal;
|
||||
var hproc = Kernel32.GetCurrentProcess();
|
||||
|
||||
retVal = AdvApi32.OpenProcessToken(hproc,
|
||||
AdvApi32.TokenAccess.TOKEN_ADJUST_PRIVILEGES | AdvApi32.TokenAccess.TOKEN_QUERY, out var htok);
|
||||
retVal = AdvApi32.OpenProcessToken(hproc,
|
||||
AdvApi32.TokenAccess.TOKEN_ADJUST_PRIVILEGES | AdvApi32.TokenAccess.TOKEN_QUERY, out var htok);
|
||||
|
||||
retVal = AdvApi32.LookupPrivilegeValue(null, privilege, out var tpLuid);
|
||||
AdvApi32.TOKEN_PRIVILEGES tp = new(tpLuid, AdvApi32.PrivilegeAttributes.SE_PRIVILEGE_ENABLED);
|
||||
retVal = AdvApi32.LookupPrivilegeValue(null, privilege, out var tpLuid);
|
||||
AdvApi32.TOKEN_PRIVILEGES tp = new(tpLuid, AdvApi32.PrivilegeAttributes.SE_PRIVILEGE_ENABLED);
|
||||
|
||||
AdvApi32.AdjustTokenPrivileges(htok, false, in tp, out _).ThrowIfFailed();
|
||||
AdvApi32.AdjustTokenPrivileges(htok, false, in tp, out _).ThrowIfFailed();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
public static bool RemovePrivilege(string privilege)
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
bool retVal;
|
||||
var hproc = Kernel32.GetCurrentProcess();
|
||||
|
||||
retVal = AdvApi32.OpenProcessToken(hproc,
|
||||
AdvApi32.TokenAccess.TOKEN_ADJUST_PRIVILEGES | AdvApi32.TokenAccess.TOKEN_QUERY, out var htok);
|
||||
|
||||
retVal = AdvApi32.LookupPrivilegeValue(null, privilege, out var tpLuid);
|
||||
AdvApi32.TOKEN_PRIVILEGES tp = new(tpLuid, AdvApi32.PrivilegeAttributes.SE_PRIVILEGE_DISABLED);
|
||||
|
||||
AdvApi32.AdjustTokenPrivileges(htok, false, in tp, out _).ThrowIfFailed();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CA1416 // Validate platform compatibility
|
||||
public static void TakeOwnership(FileInfo file)
|
||||
{
|
||||
EscalateToAdmin();
|
||||
|
||||
// Get access control
|
||||
FileSecurity security = file.GetAccessControl();
|
||||
SecurityIdentifier? cu = WindowsIdentity.GetCurrent().User;
|
||||
|
||||
// Set owner to current user
|
||||
security.SetOwner(cu);
|
||||
security.SetAccessRule(new FileSystemAccessRule(cu, FileSystemRights.Modify, AccessControlType.Allow));
|
||||
|
||||
// Update the Access Control on the original file
|
||||
file.SetAccessControl(security);
|
||||
}
|
||||
|
||||
public static void TakeOwnership(DirectoryInfo dir)
|
||||
{
|
||||
EscalateToAdmin();
|
||||
|
||||
// Get access control
|
||||
DirectorySecurity security = dir.GetAccessControl();
|
||||
SecurityIdentifier? cu = WindowsIdentity.GetCurrent().User;
|
||||
|
||||
// Set owner to current user
|
||||
security.SetOwner(cu);
|
||||
security.SetAccessRule(new FileSystemAccessRule(cu, FileSystemRights.Modify, AccessControlType.Allow));
|
||||
|
||||
// Update the Access Control on the original file
|
||||
dir.SetAccessControl(security);
|
||||
}
|
||||
|
||||
public static void TakeOwnership(FileSystemInfo info)
|
||||
{
|
||||
if (info is FileInfo file)
|
||||
TakeOwnership(file);
|
||||
else if (info is DirectoryInfo dir)
|
||||
TakeOwnership(dir);
|
||||
}
|
||||
#pragma warning restore CA1416 // Validate platform compatibility
|
||||
|
||||
public static bool TryTakeOwnership(FileInfo file, out Exception? exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
TakeOwnership(file);
|
||||
|
||||
exception = null;
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EscalateToAdmin()
|
||||
{
|
||||
// Activate necessary admin privileges to make changes without NTFS perms
|
||||
AddPrivilege("SeRestorePrivilege"); // Necessary to set Owner Permissions
|
||||
AddPrivilege("SeBackupPrivilege"); // Necessary to bypass Traverse Checking
|
||||
AddPrivilege("SeTakeOwnershipPrivilege"); // Necessary to override FilePermissions
|
||||
throw;
|
||||
}
|
||||
}
|
||||
public static bool RemovePrivilege(string privilege)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool retVal;
|
||||
var hproc = Kernel32.GetCurrentProcess();
|
||||
|
||||
retVal = AdvApi32.OpenProcessToken(hproc,
|
||||
AdvApi32.TokenAccess.TOKEN_ADJUST_PRIVILEGES | AdvApi32.TokenAccess.TOKEN_QUERY, out var htok);
|
||||
|
||||
retVal = AdvApi32.LookupPrivilegeValue(null, privilege, out var tpLuid);
|
||||
AdvApi32.TOKEN_PRIVILEGES tp = new(tpLuid, AdvApi32.PrivilegeAttributes.SE_PRIVILEGE_DISABLED);
|
||||
|
||||
AdvApi32.AdjustTokenPrivileges(htok, false, in tp, out _).ThrowIfFailed();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static void TakeOwnership(FileInfo file)
|
||||
{
|
||||
EscalateToAdmin();
|
||||
|
||||
// Get access control
|
||||
FileSecurity security = file.GetAccessControl();
|
||||
SecurityIdentifier? cu = WindowsIdentity.GetCurrent().User;
|
||||
|
||||
// Set owner to current user
|
||||
security.SetOwner(cu);
|
||||
security.SetAccessRule(new FileSystemAccessRule(cu, FileSystemRights.Modify, AccessControlType.Allow));
|
||||
|
||||
// Update the Access Control on the original file
|
||||
file.SetAccessControl(security);
|
||||
}
|
||||
|
||||
public static void TakeOwnership(DirectoryInfo dir)
|
||||
{
|
||||
EscalateToAdmin();
|
||||
|
||||
// Get access control
|
||||
DirectorySecurity security = dir.GetAccessControl();
|
||||
SecurityIdentifier? cu = WindowsIdentity.GetCurrent().User;
|
||||
|
||||
// Set owner to current user
|
||||
security.SetOwner(cu);
|
||||
security.SetAccessRule(new FileSystemAccessRule(cu, FileSystemRights.Modify, AccessControlType.Allow));
|
||||
|
||||
// Update the Access Control on the original file
|
||||
dir.SetAccessControl(security);
|
||||
}
|
||||
|
||||
public static void TakeOwnership(FileSystemInfo info)
|
||||
{
|
||||
if (info is FileInfo file)
|
||||
TakeOwnership(file);
|
||||
else if (info is DirectoryInfo dir)
|
||||
TakeOwnership(dir);
|
||||
}
|
||||
|
||||
public static bool TryTakeOwnership(FileInfo file, out Exception? exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
TakeOwnership(file);
|
||||
|
||||
exception = null;
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EscalateToAdmin()
|
||||
{
|
||||
// Activate necessary admin privileges to make changes without NTFS perms
|
||||
AddPrivilege("SeRestorePrivilege"); // Necessary to set Owner Permissions
|
||||
AddPrivilege("SeBackupPrivilege"); // Necessary to bypass Traverse Checking
|
||||
AddPrivilege("SeTakeOwnershipPrivilege"); // Necessary to override FilePermissions
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>9</LangVersion>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<LangVersion>12</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<Platforms>AnyCPU;x64;x86</Platforms>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
||||
Reference in New Issue
Block a user