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:
@@ -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
Reference in New Issue
Block a user