diff --git a/RGB.NET.Core/Positioning/Scale.cs b/RGB.NET.Core/Positioning/Scale.cs
index 973bf9cf..18644e6c 100644
--- a/RGB.NET.Core/Positioning/Scale.cs
+++ b/RGB.NET.Core/Positioning/Scale.cs
@@ -50,6 +50,12 @@ public Scale(float horizontal, float vertical)
#region Methods
+ ///
+ /// Converts the and value of this to a human-readable string.
+ ///
+ /// A string that contains the and value of this . For example "[Horizontal: 1, Vertical: 0.5]".
+ public override string ToString() => $"[Horizontal: {Horizontal}, Vertical: {Vertical}]\"";
+
///
/// Tests whether the specified is equivalent to this .
///
diff --git a/RGB.NET.Layout/DeviceLayout.cs b/RGB.NET.Layout/DeviceLayout.cs
index 24efd371..e33e7609 100644
--- a/RGB.NET.Layout/DeviceLayout.cs
+++ b/RGB.NET.Layout/DeviceLayout.cs
@@ -174,7 +174,7 @@ public class DeviceLayout : IDeviceLayout
/// The deserialized custom data object.
protected virtual object? GetCustomData(object? customData, Type? type)
{
- XmlNode? node = (customData as XmlNode) ?? (customData as IEnumerable)?.FirstOrDefault()?.ParentNode; //HACK DarthAffe 16.01.2021: This gives us the CustomData-Node
+ XmlNode? node = (customData as XmlNode) ?? (customData as IEnumerable)?.FirstOrDefault(x => x.ParentNode != null)?.ParentNode; //HACK DarthAffe 16.01.2021: This gives us the CustomData-Node
if ((node == null) || (type == null)) return null;
using MemoryStream ms = new();
diff --git a/RGB.NET.Layout/LayoutExtension.cs b/RGB.NET.Layout/LayoutExtension.cs
index a6666372..915380b1 100644
--- a/RGB.NET.Layout/LayoutExtension.cs
+++ b/RGB.NET.Layout/LayoutExtension.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
+using System.Xml.Serialization;
using RGB.NET.Core;
namespace RGB.NET.Layout;
@@ -51,4 +53,48 @@ public static void ApplyTo(this IDeviceLayout layout, IRGBDevice device, bool cr
device.RemoveLed(led);
}
}
+
+ ///
+ /// Saves the specified layout to the given location.
+ ///
+ /// The layout to save.
+ /// The location to save to.
+ public static void Save(this IDeviceLayout layout, string targetFile)
+ {
+ using FileStream fs = new(targetFile, FileMode.Create);
+ layout.Save(fs);
+ }
+
+ ///
+ /// Saves the specified layout to the given stream.
+ ///
+ /// The layout to save.
+ /// The stream to save to.
+ public static void Save(this IDeviceLayout layout, Stream stream)
+ {
+ Type? customDataType = layout.CustomData?.GetType();
+ Type? customLedDataType = layout.Leds.FirstOrDefault(x => x.CustomData != null)?.GetType();
+
+ Type[] customTypes;
+ if ((customDataType != null) && (customLedDataType != null))
+ customTypes = new[] { customDataType, customLedDataType };
+ else if (customDataType != null)
+ customTypes = new[] { customDataType };
+ else if (customLedDataType != null)
+ customTypes = new[] { customLedDataType };
+ else
+ customTypes = Array.Empty();
+
+ if (layout is DeviceLayout deviceLayout)
+ {
+ deviceLayout.InternalCustomData = deviceLayout.CustomData;
+
+ foreach (ILedLayout led in deviceLayout.Leds)
+ if (led is LedLayout ledLayout)
+ ledLayout.InternalCustomData = ledLayout.CustomData;
+ }
+
+ XmlSerializer serializer = new(typeof(DeviceLayout), null, customTypes, null, null);
+ serializer.Serialize(stream, layout);
+ }
}
\ No newline at end of file