Update to net8.0-windows and C# 12

This commit is contained in:
Yoshi Askharoun
2024-05-20 21:53:52 -05:00
parent a2cf29e1ed
commit 49479d4cd9
11 changed files with 989 additions and 1042 deletions
+104 -105
View File
@@ -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)!;
}
}
}
+89 -90
View File
@@ -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
View File
@@ -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();
}
}
+74 -75
View File
@@ -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;
}
File diff suppressed because it is too large Load Diff