using OwlCore.AbstractUI.Models; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading.Tasks; using Vestris.ResourceLib; using ZuneModCore.Services; using ZuneModCore.Win32; namespace ZuneModCore.Mods; public class BackgroundImageModFactory : DIModFactoryBase { public override ModMetadata Metadata { get; } = new() { Id = nameof(BackgroundImageMod), Title = "Background Image", Author = "Joshua \"Yoshi\" Askharoun", Version = new(1, 1), Description = "Replaces the \"Zero\" background with an image of your choice.", }; } public class BackgroundImageMod(ModMetadata metadata, IModCoreConfig modConfig) : Mod(metadata) { private const string FRAMEBACKGROUND_NAME = "FRAMEBACKGROUND06.PNG"; public string ZuneInstallDir => modConfig.ZuneInstallDir; public override AbstractUICollection? GetDefaultOptionsUI() { 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; } public override IReadOnlyList? DependentMods => null; public override async Task Apply() { string bgimgPath = ((AbstractTextBox)OptionsUI![0]).Value; FileInfo bgimgInfo = new(bgimgPath); if (!bgimgInfo.Exists) { 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 => { // 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."; // Locate the image resource // TODO: Allow users to specify which background they want to replace List RCDATA = vi.Resources[resId]; int idx = RCDATA.FindIndex(r => r.Name.Name == FRAMEBACKGROUND_NAME); 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); // Configure Zune to use the "Zero" background var resourceUri = $"res://ZuneShellResources.dll!{FRAMEBACKGROUND_NAME}"; RegEdit.CurrentUserSetStringValue(RegEdit.ZUNE_REG_PATH + "Shell", "BackgroundImage", resourceUri); 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; } } public override Task Reset() { try { // Copy backup to application folder File.Copy(Path.Combine(StorageDirectory, "ZuneShellResources.original.dll"), Path.Combine(ZuneInstallDir, "ZuneShellResources.dll"), true); return Task.FromResult(null); } catch (Exception ex) { return Task.FromResult(ex.Message)!; } } }