From 9ec84b9e37070c1d402c0f9e2c114bec825adbe6 Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Mon, 11 Dec 2017 09:49:35 +0100 Subject: [PATCH] Added Razer-SDK support --- NuGet/Metapackages/RGB.NET.Devices.nuspec | 1 + NuGet/RGB.NET.Devices.Razer.nuspec | 32 +++ .../ChromaLink/RazerChromaLinkRGBDevice.cs | 63 +++++ .../RazerChromaLinkRGBDeviceInfo.cs | 32 +++ RGB.NET.Devices.Razer/Enum/DeviceType.cs | 13 ++ RGB.NET.Devices.Razer/Enum/RazerError.cs | 93 ++++++++ .../Enum/RazerLogicalKeyboardLayout.cs | 15 ++ .../Enum/RazerPhysicalKeyboardLayout.cs | 15 ++ .../Exceptions/RazerException.cs | 37 +++ RGB.NET.Devices.Razer/Generic/Devices.cs | 61 +++++ .../Generic/IRazerRGBDevice.cs | 13 ++ RGB.NET.Devices.Razer/Generic/RazerLedId.cs | 94 ++++++++ .../Generic/RazerRGBDevice.cs | 143 ++++++++++++ .../Generic/RazerRGBDeviceInfo.cs | 56 +++++ .../Headset/RazerHeadsetRGBDevice.cs | 63 +++++ .../Headset/RazerHeadsetRGBDeviceInfo.cs | 32 +++ .../Keyboard/RazerKeyboardRGBDevice.cs | 67 ++++++ .../Keyboard/RazerKeyboardRGBDeviceInfo.cs | 66 ++++++ .../Keypad/RazerKeypadRGBDevice.cs | 66 ++++++ .../Keypad/RazerKeypadRGBDeviceInfo.cs | 32 +++ .../Layouts/DeviceLayout.xsd | 63 +++++ .../Mouse/RazerMouseRGBDevice.cs | 66 ++++++ .../Mouse/RazerMouseRGBDeviceInfo.cs | 32 +++ .../Mousepad/RazerMousepadRGBDevice.cs | 63 +++++ .../Mousepad/RazerMousepadRGBDeviceInfo.cs | 32 +++ .../Native/_ChromaLinkCustomEffect.cs | 11 + RGB.NET.Devices.Razer/Native/_Color.cs | 30 +++ RGB.NET.Devices.Razer/Native/_Defines.cs | 25 ++ RGB.NET.Devices.Razer/Native/_DeviceInfo.cs | 27 +++ .../Native/_HeadsetCustomEffect.cs | 11 + .../Native/_KeyboardCustomEffect.cs | 11 + .../Native/_KeypadCustomEffect.cs | 11 + .../Native/_MouseCustomEffect.cs | 11 + .../Native/_MousepadCustomEffect.cs | 11 + RGB.NET.Devices.Razer/Native/_RazerSDK.cs | 146 ++++++++++++ .../Properties/AssemblyInfo.cs | 35 +++ .../RGB.NET.Devices.Razer.csproj | 105 +++++++++ .../RGB.NET.Devices.Razer.csproj.DotSettings | 11 + RGB.NET.Devices.Razer/RazerDeviceProvider.cs | 218 ++++++++++++++++++ .../libs/x64/RzChromaSDK.dll | Bin 0 -> 119424 bytes .../libs/x86/RzChromaSDK.dll | Bin 0 -> 103040 bytes RGB.NET.Devices.Razer/packages.config | 4 + .../targets/RGB.NET.Devices.Razer.targets | 150 ++++++++++++ RGB.NET.sln | 11 +- 44 files changed, 2075 insertions(+), 3 deletions(-) create mode 100644 NuGet/RGB.NET.Devices.Razer.nuspec create mode 100644 RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDevice.cs create mode 100644 RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDeviceInfo.cs create mode 100644 RGB.NET.Devices.Razer/Enum/DeviceType.cs create mode 100644 RGB.NET.Devices.Razer/Enum/RazerError.cs create mode 100644 RGB.NET.Devices.Razer/Enum/RazerLogicalKeyboardLayout.cs create mode 100644 RGB.NET.Devices.Razer/Enum/RazerPhysicalKeyboardLayout.cs create mode 100644 RGB.NET.Devices.Razer/Exceptions/RazerException.cs create mode 100644 RGB.NET.Devices.Razer/Generic/Devices.cs create mode 100644 RGB.NET.Devices.Razer/Generic/IRazerRGBDevice.cs create mode 100644 RGB.NET.Devices.Razer/Generic/RazerLedId.cs create mode 100644 RGB.NET.Devices.Razer/Generic/RazerRGBDevice.cs create mode 100644 RGB.NET.Devices.Razer/Generic/RazerRGBDeviceInfo.cs create mode 100644 RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDevice.cs create mode 100644 RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDeviceInfo.cs create mode 100644 RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDevice.cs create mode 100644 RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDeviceInfo.cs create mode 100644 RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDevice.cs create mode 100644 RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDeviceInfo.cs create mode 100644 RGB.NET.Devices.Razer/Layouts/DeviceLayout.xsd create mode 100644 RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDevice.cs create mode 100644 RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDeviceInfo.cs create mode 100644 RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDevice.cs create mode 100644 RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDeviceInfo.cs create mode 100644 RGB.NET.Devices.Razer/Native/_ChromaLinkCustomEffect.cs create mode 100644 RGB.NET.Devices.Razer/Native/_Color.cs create mode 100644 RGB.NET.Devices.Razer/Native/_Defines.cs create mode 100644 RGB.NET.Devices.Razer/Native/_DeviceInfo.cs create mode 100644 RGB.NET.Devices.Razer/Native/_HeadsetCustomEffect.cs create mode 100644 RGB.NET.Devices.Razer/Native/_KeyboardCustomEffect.cs create mode 100644 RGB.NET.Devices.Razer/Native/_KeypadCustomEffect.cs create mode 100644 RGB.NET.Devices.Razer/Native/_MouseCustomEffect.cs create mode 100644 RGB.NET.Devices.Razer/Native/_MousepadCustomEffect.cs create mode 100644 RGB.NET.Devices.Razer/Native/_RazerSDK.cs create mode 100644 RGB.NET.Devices.Razer/Properties/AssemblyInfo.cs create mode 100644 RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj create mode 100644 RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj.DotSettings create mode 100644 RGB.NET.Devices.Razer/RazerDeviceProvider.cs create mode 100644 RGB.NET.Devices.Razer/libs/x64/RzChromaSDK.dll create mode 100644 RGB.NET.Devices.Razer/libs/x86/RzChromaSDK.dll create mode 100644 RGB.NET.Devices.Razer/packages.config create mode 100644 RGB.NET.Devices.Razer/targets/RGB.NET.Devices.Razer.targets diff --git a/NuGet/Metapackages/RGB.NET.Devices.nuspec b/NuGet/Metapackages/RGB.NET.Devices.nuspec index 2d578be..35e6773 100644 --- a/NuGet/Metapackages/RGB.NET.Devices.nuspec +++ b/NuGet/Metapackages/RGB.NET.Devices.nuspec @@ -21,6 +21,7 @@ + \ No newline at end of file diff --git a/NuGet/RGB.NET.Devices.Razer.nuspec b/NuGet/RGB.NET.Devices.Razer.nuspec new file mode 100644 index 0000000..eb3e065 --- /dev/null +++ b/NuGet/RGB.NET.Devices.Razer.nuspec @@ -0,0 +1,32 @@ + + + + RGB.NET.Devices.Razer + RGB.NET.Devices.Razer + 0.0.1 + Darth Affe + Darth Affe + https://github.com/DarthAffe/RGB.NET + https://raw.githubusercontent.com/DarthAffe/RGB.NET/master/LICENSE + true + Razer-Device-Implementations of RGB.NET + + Razer-Device-Implementations of RGB.NET, a C# (.NET) library for accessing various RGB-peripherals + Copyright © Wyrez 2017 + en-US + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDevice.cs b/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDevice.cs new file mode 100644 index 0000000..36c6741 --- /dev/null +++ b/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDevice.cs @@ -0,0 +1,63 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a razer chroma link. + /// + public class RazerChromaLinkRGBDevice : RazerRGBDevice + { + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The specific information provided by CUE for the chroma link. + internal RazerChromaLinkRGBDevice(RazerChromaLinkRGBDeviceInfo info) + : base(info) + { } + + #endregion + + #region Methods + + /// + protected override void InitializeLayout() + { + string model = DeviceInfo.Model.Replace(" ", string.Empty).ToUpper(); + ApplyLayoutFromFile(PathHelper.GetAbsolutePath( + $@"Layouts\Razer\ChromaLink\{model}.xml"), null, PathHelper.GetAbsolutePath(@"Images\Razer\ChromaLink")); + + if (LedMapping.Count == 0) + for (int i = 0; i < _Defines.CHROMALINK_MAX_LEDS; i++) + InitializeLed(new RazerLedId(this, i), new Rectangle(i * 11, 0, 10, 10)); + } + + /// + protected override IntPtr CreateEffectParams(IEnumerable leds) + { + _Color[] colors = new _Color[_Defines.CHROMALINK_MAX_LEDS]; + + foreach (Led led in leds) + colors[((RazerLedId)led.Id).Index] = new _Color(led.Color.R, led.Color.G, led.Color.B); + + _ChromaLinkCustomEffect effectParams = new _ChromaLinkCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDeviceInfo.cs b/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDeviceInfo.cs new file mode 100644 index 0000000..34746d5 --- /dev/null +++ b/RGB.NET.Devices.Razer/ChromaLink/RazerChromaLinkRGBDeviceInfo.cs @@ -0,0 +1,32 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a generic information for a . + /// + public class RazerChromaLinkRGBDeviceInfo : RazerRGBDeviceInfo + { + #region Constructors + + /// + /// + /// Internal constructor of managed . + /// + /// The Id of the . + /// The model of the . + internal RazerChromaLinkRGBDeviceInfo(Guid deviceId, string model) + : base(deviceId, RGBDeviceType.LedStripe, model) + { + string modelName = Model.Replace(" ", string.Empty).ToUpper(); + Image = new Uri(PathHelper.GetAbsolutePath($@"Images\Razer\ChromaLinks\{modelName}.png"), UriKind.Absolute); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Enum/DeviceType.cs b/RGB.NET.Devices.Razer/Enum/DeviceType.cs new file mode 100644 index 0000000..d262656 --- /dev/null +++ b/RGB.NET.Devices.Razer/Enum/DeviceType.cs @@ -0,0 +1,13 @@ +namespace RGB.NET.Devices.Razer +{ + internal enum DeviceType + { + Keyboard = 1, + Mouse = 2, + Headset = 3, + Mousepad = 4, + Keypad = 5, + System = 6, + Invalid + } +} diff --git a/RGB.NET.Devices.Razer/Enum/RazerError.cs b/RGB.NET.Devices.Razer/Enum/RazerError.cs new file mode 100644 index 0000000..06321a7 --- /dev/null +++ b/RGB.NET.Devices.Razer/Enum/RazerError.cs @@ -0,0 +1,93 @@ +namespace RGB.NET.Devices.Razer +{ + /// + /// Razer-SDK: Error codes for Chroma SDK. If the error is not defined here, refer to WinError.h from the Windows SDK. + /// + public enum RazerError : long + { + /// + /// Razer-SDK: Invalid. + /// + Invalid = -1, + + /// + /// Razer-SDK: Success. + /// + Success = 0, + + /// + /// Razer-SDK: Access denied. + /// + AccessDenied = 5, + + /// + /// Razer-SDK: Invalid handle. + /// + InvalidHandle = 6, + + /// + /// Razer-SDK: Not supported. + /// + NotSupported = 50, + + /// + /// Razer-SDK: Invalid parameter. + /// + InvalidParameter = 87, + + /// + /// Razer-SDK: The service has not been started. + /// + ServiceNotActive = 1062, + + /// + /// Razer-SDK: Cannot start more than one instance of the specified program. + /// + SingleInstanceApp = 1152, + + /// + /// Razer-SDK: Device not connected. + /// + DeviceNotConnected = 1167, + + /// + /// Razer-SDK: Element not found. + /// + NotFound = 1168, + + /// + /// Razer-SDK: Request aborted. + /// + RequestAborted = 1235, + + /// + /// Razer-SDK: An attempt was made to perform an initialization operation when initialization has already been completed. + /// + AlreadyInitialized = 1247, + + /// + /// Razer-SDK: Resource not available or disabled. + /// + ResourceDisabled = 4309, + + /// + /// Razer-SDK: Device not available or supported. + /// + DeviceNotAvailable = 4319, + + /// + /// Razer-SDK: The group or resource is not in the correct state to perform the requested operation. + /// + NotValidState = 5023, + + /// + /// Razer-SDK: No more items. + /// + NoMoreItems = 259, + + /// + /// Razer-SDK: General failure. + /// + Failed = 2147500037 + } +} diff --git a/RGB.NET.Devices.Razer/Enum/RazerLogicalKeyboardLayout.cs b/RGB.NET.Devices.Razer/Enum/RazerLogicalKeyboardLayout.cs new file mode 100644 index 0000000..1ec9382 --- /dev/null +++ b/RGB.NET.Devices.Razer/Enum/RazerLogicalKeyboardLayout.cs @@ -0,0 +1,15 @@ +// ReSharper disable InconsistentNaming +// ReSharper disable UnusedMember.Global + +#pragma warning disable 1591 // Missing XML comment for publicly visible type or member + +namespace RGB.NET.Devices.Razer +{ + /// + /// Contains list of available logical layouts for razer keyboards. + /// + public enum RazerLogicalKeyboardLayout + { + TODO + }; +} diff --git a/RGB.NET.Devices.Razer/Enum/RazerPhysicalKeyboardLayout.cs b/RGB.NET.Devices.Razer/Enum/RazerPhysicalKeyboardLayout.cs new file mode 100644 index 0000000..090a2db --- /dev/null +++ b/RGB.NET.Devices.Razer/Enum/RazerPhysicalKeyboardLayout.cs @@ -0,0 +1,15 @@ +// ReSharper disable UnusedMember.Global +// ReSharper disable InconsistentNaming + +#pragma warning disable 1591 // Missing XML comment for publicly visible type or member + +namespace RGB.NET.Devices.Razer +{ + /// + /// Contains list of available physical layouts for razer keyboards. + /// + public enum RazerPhysicalKeyboardLayout + { + TODO + } +} diff --git a/RGB.NET.Devices.Razer/Exceptions/RazerException.cs b/RGB.NET.Devices.Razer/Exceptions/RazerException.cs new file mode 100644 index 0000000..edd6bac --- /dev/null +++ b/RGB.NET.Devices.Razer/Exceptions/RazerException.cs @@ -0,0 +1,37 @@ +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable MemberCanBePrivate.Global + +using System; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents an exception thrown by the Razer-SDK. + /// + public class RazerException : ApplicationException + { + #region Properties & Fields + + /// + /// Gets the error code provided by the SDK. + /// + public RazerError ErrorCode { get; } + + #endregion + + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The error code provided by the SDK. + public RazerException(RazerError errorCode) + { + this.ErrorCode = errorCode; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Generic/Devices.cs b/RGB.NET.Devices.Razer/Generic/Devices.cs new file mode 100644 index 0000000..a167aa9 --- /dev/null +++ b/RGB.NET.Devices.Razer/Generic/Devices.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; + +namespace RGB.NET.Devices.Razer +{ + internal class Devices + { + public static readonly List<(Guid guid, string model)> KEYBOARDS = new List<(Guid guid, string model)> + { + (new Guid("2EA1BB63-CA28-428D-9F06-196B88330BBB"), "Blackwidow Chroma"), + (new Guid("ED1C1B82-BFBE-418F-B49D-D03F05B149DF"), "Razer Blackwidow Chroma Tournament Edition"), + (new Guid("18C5AD9B-4326-4828-92C4-2669A66D2283"), "Razer Deathstalker "), + (new Guid("872AB2A9-7959-4478-9FED-15F6186E72E4"), "Overwatch Keyboard"), + (new Guid("5AF60076-ADE9-43D4-B574-52599293B554"), "Razer Blackwidow X Chroma"), + (new Guid("2D84DD51-3290-4AAC-9A89-D8AFDE38B57C"), "Razer Blackwidow X TE Chroma"), + (new Guid("803378C1-CC48-4970-8539-D828CC1D420A"), "Razer Omata Chroma"), + (new Guid("C83BDFE8-E7FC-40E0-99DB-872E23F19891"), "Razer Blade Stealth"), + (new Guid("F2BEDFAF-A0FE-4651-9D41-B6CE603A3DDD"), "Razer Blade"), + (new Guid("A73AC338-F0E5-4BF7-91AE-DD1F7E1737A5"), "Razer Blade Pro"), + (new Guid("608E743F-B402-44BD-A7A6-7AA9F574ECF4"), "Razer Blackwidow Chroma v2") + }; + + public static readonly List<(Guid guid, string model)> MICE = new List<(Guid guid, string model)> + { + (new Guid("7EC00450-E0EE-4289-89D5-0D879C19061A"), "Razer Mamba Chroma Tournament Edition"), + (new Guid("AEC50D91-B1F1-452F-8E16-7B73F376FDF3"), "Razer Deathadder Chroma "), + (new Guid("FF8A5929-4512-4257-8D59-C647BF9935D0"), "Razer Diamondback"), + (new Guid("D527CBDC-EB0A-483A-9E89-66D50463EC6C"), "Razer Mamba"), + (new Guid("D714C50B-7158-4368-B99C-601ACB985E98"), "Razer Naga Epic"), + (new Guid("F1876328-6CA4-46AE-BE04-BE812B414433"), "Razer Naga"), + (new Guid("52C15681-4ECE-4DD9-8A52-A1418459EB34"), "Razer Orochi Chroma"), + (new Guid("195D70F5-F285-4CFF-99F2-B8C0E9658DB4"), "Razer Naga Hex Chroma"), + (new Guid("77834867-3237-4A9F-AD77-4A46C4183003"), "Razer DeathAdder Elite Chroma") + }; + + public static readonly List<(Guid guid, string model)> HEADSETS = new List<(Guid guid, string model)> + { + (new Guid("DF3164D7-5408-4A0E-8A7F-A7412F26BEBF"), "Razer ManO'War"), + (new Guid("CD1E09A5-D5E6-4A6C-A93B-E6D9BF1D2092"), "Razer Kraken 7.1 Chroma"), + (new Guid("7FB8A36E-9E74-4BB3-8C86-CAC7F7891EBD"), "Razer Kraken 7.1 Chroma Refresh") + }; + + public static readonly List<(Guid guid, string model)> MOUSEMATS = new List<(Guid guid, string model)> + { + (new Guid("80F95A94-73D2-48CA-AE9A-0986789A9AF2"), "Razer Firefly") + }; + + public static readonly List<(Guid guid, string model)> KEYPADS = new List<(Guid guid, string model)> + { + (new Guid("9D24B0AB-0162-466C-9640-7A924AA4D9FD"), "Razer Orbweaver"), + (new Guid("00F0545C-E180-4AD1-8E8A-419061CE505E"), "Razer Tartarus") + }; + + public static readonly List<(Guid guid, string model)> CHROMALINKS = new List<(Guid guid, string model)> + { + (new Guid("0201203B-62F3-4C50-83DD-598BABD208E0"), "Core Chroma"), + (new Guid("35F6F18D-1AE5-436C-A575-AB44A127903A"), "Lenovo Y900"), + (new Guid("47DB1FA7-6B9B-4EE6-B6F4-4071A3B2053B"), "Lenovo Y27") + }; + } +} diff --git a/RGB.NET.Devices.Razer/Generic/IRazerRGBDevice.cs b/RGB.NET.Devices.Razer/Generic/IRazerRGBDevice.cs new file mode 100644 index 0000000..879537f --- /dev/null +++ b/RGB.NET.Devices.Razer/Generic/IRazerRGBDevice.cs @@ -0,0 +1,13 @@ +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// Represents a razer RGB-device. + /// + internal interface IRazerRGBDevice : IRGBDevice + { + void Initialize(); + void Reset(); + } +} diff --git a/RGB.NET.Devices.Razer/Generic/RazerLedId.cs b/RGB.NET.Devices.Razer/Generic/RazerLedId.cs new file mode 100644 index 0000000..d8fc56d --- /dev/null +++ b/RGB.NET.Devices.Razer/Generic/RazerLedId.cs @@ -0,0 +1,94 @@ +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a Id of a on a . + /// + public class RazerLedId : ILedId + { + #region Properties & Fields + + internal int Index { get; } + + /// + public IRGBDevice Device { get; } + + /// + public bool IsValid => true; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The the belongs to. + /// The index representing the led-location in the grid. + public RazerLedId(IRGBDevice device, int index) + { + this.Device = device; + this.Index = index; + } + + #endregion + + #region Methods + + /// + /// Converts the Id of this to a human-readable string. + /// + /// A string that contains the Id of this . For example "Enter". + public override string ToString() => Index.ToString(); + + /// + /// Tests whether the specified object is a and is equivalent to this . + /// + /// The object to test. + /// true if is a equivalent to this ; otherwise, false. + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (!(obj is RazerLedId other)) return false; + + return (Index == other.Index) && Equals(Device, other.Device); + } + + /// + /// Returns a hash code for this . + /// + /// An integer value that specifies the hash code for this . + public override int GetHashCode() + { + unchecked + { + return (Index * 397) ^ (Device != null ? Device.GetHashCode() : 0); + } + } + + #endregion + + #region Operators + + /// + /// Returns a value that indicates whether two specified are equal. + /// + /// The first to compare. + /// The second to compare. + /// true if and are equal; otherwise, false. + public static bool operator ==(RazerLedId ledId1, RazerLedId ledId2) => ReferenceEquals(ledId1, null) ? ReferenceEquals(ledId2, null) : ledId1.Equals(ledId2); + + /// + /// Returns a value that indicates whether two specified are equal. + /// + /// The first to compare. + /// The second to compare. + /// true if and are not equal; otherwise, false. + public static bool operator !=(RazerLedId ledId1, RazerLedId ledId2) => !(ledId1 == ledId2); + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Generic/RazerRGBDevice.cs b/RGB.NET.Devices.Razer/Generic/RazerRGBDevice.cs new file mode 100644 index 0000000..66cd55c --- /dev/null +++ b/RGB.NET.Devices.Razer/Generic/RazerRGBDevice.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using RGB.NET.Core; +using RGB.NET.Core.Layout; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// + /// Represents a generic razer-device. (keyboard, mouse, headset, mousepad). + /// + public abstract class RazerRGBDevice : AbstractRGBDevice, IRazerRGBDevice + where TDeviceInfo : RazerRGBDeviceInfo + { + #region Properties & Fields + + private Guid? _lastEffect; + + /// + /// + /// Gets information about the . + /// + public override TDeviceInfo DeviceInfo { get; } + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The generic information provided by razer for the device. + protected RazerRGBDevice(TDeviceInfo info) + { + this.DeviceInfo = info; + } + + #endregion + + #region Methods + + /// + /// Initializes the device. + /// + public void Initialize() + { + InitializeLayout(); + + if (Size == Size.Invalid) + { + Rectangle ledRectangle = new Rectangle(this.Select(x => x.LedRectangle)); + Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y); + } + } + + /// + /// Initializes the and of the device. + /// + protected abstract void InitializeLayout(); + + /// + /// Applies the given layout. + /// + /// The file containing the layout. + /// The name of the layout used to get the images of the leds. + /// The path images for this device are collected in. + protected void ApplyLayoutFromFile(string layoutPath, string imageLayout, string imageBasePath) + { + DeviceLayout layout = DeviceLayout.Load(layoutPath); + if (layout != null) + { + LedImageLayout ledImageLayout = layout.LedImageLayouts.FirstOrDefault(x => string.Equals(x.Layout, imageLayout, StringComparison.OrdinalIgnoreCase)); + + Size = new Size(layout.Width, layout.Height); + + if (layout.Leds != null) + foreach (LedLayout layoutLed in layout.Leds) + { + if (Enum.TryParse(layoutLed.Id, true, out int ledIndex)) + { + if (LedMapping.TryGetValue(new RazerLedId(this, ledIndex), out Led led)) + { + led.LedRectangle.Location = new Point(layoutLed.X, layoutLed.Y); + led.LedRectangle.Size = new Size(layoutLed.Width, layoutLed.Height); + + led.Shape = layoutLed.Shape; + led.ShapeData = layoutLed.ShapeData; + + LedImage image = ledImageLayout?.LedImages.FirstOrDefault(x => x.Id == layoutLed.Id); + led.Image = (!string.IsNullOrEmpty(image?.Image)) + ? new Uri(Path.Combine(imageBasePath, image.Image), UriKind.Absolute) + : new Uri(Path.Combine(imageBasePath, "Missing.png"), UriKind.Absolute); + } + } + } + } + } + + /// + protected override void UpdateLeds(IEnumerable ledsToUpdate) + { + List leds = ledsToUpdate.Where(x => x.Color.A > 0).ToList(); + + if (leds.Count <= 0) return; + + IntPtr effectParams = CreateEffectParams(leds); + Guid effectId = Guid.NewGuid(); + _RazerSDK.CreateEffect(DeviceInfo.DeviceId, _Defines.EFFECT_ID, effectParams, ref effectId); + + _RazerSDK.SetEffect(effectId); + + if (_lastEffect.HasValue) + _RazerSDK.DeleteEffect(_lastEffect.Value); + + _lastEffect = effectId; + } + + /// + /// Creates the device-specific effect parameters for the led-update. + /// + /// The leds to be updated. + /// An pointing to the effect parameter struct. + protected abstract IntPtr CreateEffectParams(IEnumerable leds); + + /// + /// Resets the device. + /// + public void Reset() + { + if (_lastEffect.HasValue) + { + _RazerSDK.DeleteEffect(_lastEffect.Value); + _lastEffect = null; + } + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Generic/RazerRGBDeviceInfo.cs b/RGB.NET.Devices.Razer/Generic/RazerRGBDeviceInfo.cs new file mode 100644 index 0000000..84b307e --- /dev/null +++ b/RGB.NET.Devices.Razer/Generic/RazerRGBDeviceInfo.cs @@ -0,0 +1,56 @@ +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a generic information for a Razer-. + /// + public class RazerRGBDeviceInfo : IRGBDeviceInfo + { + #region Properties & Fields + + /// + /// Gets the Id of the . + /// + public Guid DeviceId { get; } + + /// + public RGBDeviceType DeviceType { get; } + + /// + public string Manufacturer => "Razer"; + + /// + public string Model { get; } + + /// + public Uri Image { get; protected set; } + + /// + public bool SupportsSyncBack => false; + + /// + public RGBDeviceLighting Lighting => RGBDeviceLighting.Key; + + #endregion + + #region Constructors + + /// + /// Internal constructor of managed . + /// + /// The Id of the . + /// The type of the . + /// The model of the . + internal RazerRGBDeviceInfo(Guid deviceId, RGBDeviceType deviceType, string model) + { + this.DeviceId = deviceId; + this.DeviceType = deviceType; + this.Model = model; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDevice.cs b/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDevice.cs new file mode 100644 index 0000000..a62cb07 --- /dev/null +++ b/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDevice.cs @@ -0,0 +1,63 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a razer headset. + /// + public class RazerHeadsetRGBDevice : RazerRGBDevice + { + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The specific information provided by CUE for the headset. + internal RazerHeadsetRGBDevice(RazerHeadsetRGBDeviceInfo info) + : base(info) + { } + + #endregion + + #region Methods + + /// + protected override void InitializeLayout() + { + string model = DeviceInfo.Model.Replace(" ", string.Empty).ToUpper(); + ApplyLayoutFromFile(PathHelper.GetAbsolutePath( + $@"Layouts\Razer\Headset\{model}.xml"), null, PathHelper.GetAbsolutePath(@"Images\Razer\Headset")); + + if (LedMapping.Count == 0) + for (int i = 0; i < _Defines.HEADSET_MAX_LEDS; i++) + InitializeLed(new RazerLedId(this, i), new Rectangle(i * 11, 0, 10, 10)); + } + + /// + protected override IntPtr CreateEffectParams(IEnumerable leds) + { + _Color[] colors = new _Color[_Defines.HEADSET_MAX_LEDS]; + + foreach (Led led in leds) + colors[((RazerLedId)led.Id).Index] = new _Color(led.Color.R, led.Color.G, led.Color.B); + + _HeadsetCustomEffect effectParams = new _HeadsetCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDeviceInfo.cs b/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDeviceInfo.cs new file mode 100644 index 0000000..34604cb --- /dev/null +++ b/RGB.NET.Devices.Razer/Headset/RazerHeadsetRGBDeviceInfo.cs @@ -0,0 +1,32 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a generic information for a . + /// + public class RazerHeadsetRGBDeviceInfo : RazerRGBDeviceInfo + { + #region Constructors + + /// + /// + /// Internal constructor of managed . + /// + /// The Id of the . + /// The model of the . + internal RazerHeadsetRGBDeviceInfo(Guid deviceId, string model) + : base(deviceId, RGBDeviceType.Headset, model) + { + string modelName = Model.Replace(" ", string.Empty).ToUpper(); + Image = new Uri(PathHelper.GetAbsolutePath($@"Images\Razer\Headsets\{modelName}.png"), UriKind.Absolute); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDevice.cs b/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDevice.cs new file mode 100644 index 0000000..51a7fae --- /dev/null +++ b/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDevice.cs @@ -0,0 +1,67 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a razer keyboard. + /// + public class RazerKeyboardRGBDevice : RazerRGBDevice + { + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The specific information provided by CUE for the keyboard. + internal RazerKeyboardRGBDevice(RazerKeyboardRGBDeviceInfo info) + : base(info) + { } + + #endregion + + #region Methods + + /// + protected override void InitializeLayout() + { + //string model = DeviceInfo.Model.Replace(" ", string.Empty).ToUpper(); + //ApplyLayoutFromFile(PathHelper.GetAbsolutePath( + // $@"Layouts\Razer\Keyboards\{model}\{DeviceInfo.PhysicalLayout.ToString().ToUpper()}.xml"), + // DeviceInfo.LogicalLayout.ToString(), PathHelper.GetAbsolutePath(@"Images\Razer\Keyboards")); + + if (LedMapping.Count == 0) + { + for (int i = 0; i < _Defines.KEYBOARD_MAX_ROW; i++) + for (int j = 0; j < _Defines.KEYBOARD_MAX_COLUMN; j++) + InitializeLed(new RazerLedId(this, (i * _Defines.KEYBOARD_MAX_COLUMN) + j), new Rectangle(j * 20, i * 20, 19, 19)); + } + } + + /// + protected override IntPtr CreateEffectParams(IEnumerable leds) + { + _Color[] colors = new _Color[_Defines.KEYBOARD_MAX_LEDS]; + + foreach (Led led in leds) + colors[((RazerLedId)led.Id).Index] = new _Color(led.Color.R, led.Color.G, led.Color.B); + + _KeyboardCustomEffect effectParams = new _KeyboardCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDeviceInfo.cs b/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDeviceInfo.cs new file mode 100644 index 0000000..d9173fc --- /dev/null +++ b/RGB.NET.Devices.Razer/Keyboard/RazerKeyboardRGBDeviceInfo.cs @@ -0,0 +1,66 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Globalization; +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a generic information for a . + /// + public class RazerKeyboardRGBDeviceInfo : RazerRGBDeviceInfo + { + #region Properties & Fields + + /// + /// Gets the physical layout of the keyboard. + /// + public RazerPhysicalKeyboardLayout PhysicalLayout { get; private set; } + + /// + /// Gets the logical layout of the keyboard as set in CUE settings. + /// + public RazerLogicalKeyboardLayout LogicalLayout { get; private set; } + + #endregion + + #region Constructors + + /// + /// + /// Internal constructor of managed . + /// + /// The Id of the . + /// The model of the . + /// The of the layout this keyboard is using. + internal RazerKeyboardRGBDeviceInfo(Guid deviceId, string model, CultureInfo culture) + : base(deviceId, RGBDeviceType.Keyboard, model) + { + SetLayouts(culture.KeyboardLayoutId); + + Image = new Uri(PathHelper.GetAbsolutePath($@"Images\Razer\Keyboards\{Model.Replace(" ", string.Empty).ToUpper()}.png"), + UriKind.Absolute); + } + + #endregion + + #region Methods + + private void SetLayouts(int keyboardLayoutId) + { + switch (keyboardLayoutId) + { + //TODO DarthAffe 07.10.2017: Implement + default: + PhysicalLayout = RazerPhysicalKeyboardLayout.TODO; + LogicalLayout = RazerLogicalKeyboardLayout.TODO; + break; + } + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDevice.cs b/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDevice.cs new file mode 100644 index 0000000..2c528ee --- /dev/null +++ b/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDevice.cs @@ -0,0 +1,66 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a razer keypad. + /// + public class RazerKeypadRGBDevice : RazerRGBDevice + { + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The specific information provided by CUE for the keypad. + internal RazerKeypadRGBDevice(RazerKeypadRGBDeviceInfo info) + : base(info) + { } + + #endregion + + #region Methods + + /// + protected override void InitializeLayout() + { + string model = DeviceInfo.Model.Replace(" ", string.Empty).ToUpper(); + ApplyLayoutFromFile(PathHelper.GetAbsolutePath( + $@"Layouts\Razer\Keypad\{model}.xml"), null, PathHelper.GetAbsolutePath(@"Images\Razer\Keypad")); + + if (LedMapping.Count == 0) + { + for (int i = 0; i < _Defines.KEYPAD_MAX_ROW; i++) + for (int j = 0; j < _Defines.KEYPAD_MAX_COLUMN; j++) + InitializeLed(new RazerLedId(this, (i * _Defines.KEYPAD_MAX_COLUMN) + j), new Rectangle(j * 20, i * 20, 19, 19)); + } + } + + /// + protected override IntPtr CreateEffectParams(IEnumerable leds) + { + _Color[] colors = new _Color[_Defines.KEYPAD_MAX_LEDS]; + + foreach (Led led in leds) + colors[((RazerLedId)led.Id).Index] = new _Color(led.Color.R, led.Color.G, led.Color.B); + + _KeypadCustomEffect effectParams = new _KeypadCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDeviceInfo.cs b/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDeviceInfo.cs new file mode 100644 index 0000000..a41ccc5 --- /dev/null +++ b/RGB.NET.Devices.Razer/Keypad/RazerKeypadRGBDeviceInfo.cs @@ -0,0 +1,32 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a generic information for a . + /// + public class RazerKeypadRGBDeviceInfo : RazerRGBDeviceInfo + { + #region Constructors + + /// + /// + /// Internal constructor of managed . + /// + /// The Id of the . + /// The model of the . + internal RazerKeypadRGBDeviceInfo(Guid deviceId, string model) + : base(deviceId, RGBDeviceType.Keypad, model) + { + string modelName = Model.Replace(" ", string.Empty).ToUpper(); + Image = new Uri(PathHelper.GetAbsolutePath($@"Images\Razer\Keypads\{modelName}.png"), UriKind.Absolute); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Layouts/DeviceLayout.xsd b/RGB.NET.Devices.Razer/Layouts/DeviceLayout.xsd new file mode 100644 index 0000000..92690b3 --- /dev/null +++ b/RGB.NET.Devices.Razer/Layouts/DeviceLayout.xsd @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDevice.cs b/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDevice.cs new file mode 100644 index 0000000..944d00c --- /dev/null +++ b/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDevice.cs @@ -0,0 +1,66 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a razer mouse. + /// + public class RazerMouseRGBDevice : RazerRGBDevice + { + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The specific information provided by CUE for the mouse. + internal RazerMouseRGBDevice(RazerMouseRGBDeviceInfo info) + : base(info) + { } + + #endregion + + #region Methods + + /// + protected override void InitializeLayout() + { + string model = DeviceInfo.Model.Replace(" ", string.Empty).ToUpper(); + ApplyLayoutFromFile(PathHelper.GetAbsolutePath( + $@"Layouts\Razer\Mice\{model}.xml"), null, PathHelper.GetAbsolutePath(@"Images\Razer\Mice")); + + if (LedMapping.Count == 0) + { + for (int i = 0; i < _Defines.MOUSE_MAX_ROW; i++) + for (int j = 0; j < _Defines.MOUSE_MAX_COLUMN; j++) + InitializeLed(new RazerLedId(this, (i * _Defines.MOUSE_MAX_COLUMN) + j), new Rectangle(j * 11, i * 11, 10, 10)); + } + } + + /// + protected override IntPtr CreateEffectParams(IEnumerable leds) + { + _Color[] colors = new _Color[_Defines.MOUSE_MAX_LEDS]; + + foreach (Led led in leds) + colors[((RazerLedId)led.Id).Index] = new _Color(led.Color.R, led.Color.G, led.Color.B); + + _MouseCustomEffect effectParams = new _MouseCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDeviceInfo.cs b/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDeviceInfo.cs new file mode 100644 index 0000000..72782d6 --- /dev/null +++ b/RGB.NET.Devices.Razer/Mouse/RazerMouseRGBDeviceInfo.cs @@ -0,0 +1,32 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a generic information for a . + /// + public class RazerMouseRGBDeviceInfo : RazerRGBDeviceInfo + { + #region Constructors + + /// + /// + /// Internal constructor of managed . + /// + /// The Id of the . + /// The model of the . + internal RazerMouseRGBDeviceInfo(Guid deviceId, string model) + : base(deviceId, RGBDeviceType.Mouse, model) + { + string modelName = Model.Replace(" ", string.Empty).ToUpper(); + Image = new Uri(PathHelper.GetAbsolutePath($@"Images\Razer\Mice\{modelName}.png"), UriKind.Absolute); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDevice.cs b/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDevice.cs new file mode 100644 index 0000000..e42b97d --- /dev/null +++ b/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDevice.cs @@ -0,0 +1,63 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a razer mousepad. + /// + public class RazerMousepadRGBDevice : RazerRGBDevice + { + #region Constructors + + /// + /// + /// Initializes a new instance of the class. + /// + /// The specific information provided by CUE for the mousepad. + internal RazerMousepadRGBDevice(RazerMousepadRGBDeviceInfo info) + : base(info) + { } + + #endregion + + #region Methods + + /// + protected override void InitializeLayout() + { + string model = DeviceInfo.Model.Replace(" ", string.Empty).ToUpper(); + ApplyLayoutFromFile(PathHelper.GetAbsolutePath( + $@"Layouts\Razer\Mousepad\{model}.xml"), null, PathHelper.GetAbsolutePath(@"Images\Razer\Mousepad")); + + if (LedMapping.Count == 0) + for (int i = 0; i < _Defines.MOUSEPAD_MAX_LEDS; i++) + InitializeLed(new RazerLedId(this, i), new Rectangle(i * 11, 0, 10, 10)); + } + + /// + protected override IntPtr CreateEffectParams(IEnumerable leds) + { + _Color[] colors = new _Color[_Defines.MOUSEPAD_MAX_LEDS]; + + foreach (Led led in leds) + colors[((RazerLedId)led.Id).Index] = new _Color(led.Color.R, led.Color.G, led.Color.B); + + _MousepadCustomEffect effectParams = new _MousepadCustomEffect { Color = colors }; + + IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(effectParams)); + Marshal.StructureToPtr(effectParams, ptr, false); + + return ptr; + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDeviceInfo.cs b/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDeviceInfo.cs new file mode 100644 index 0000000..b21d49a --- /dev/null +++ b/RGB.NET.Devices.Razer/Mousepad/RazerMousepadRGBDeviceInfo.cs @@ -0,0 +1,32 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global + +using System; +using RGB.NET.Core; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a generic information for a . + /// + public class RazerMousepadRGBDeviceInfo : RazerRGBDeviceInfo + { + #region Constructors + + /// + /// + /// Internal constructor of managed . + /// + /// The Id of the . + /// The model of the . + internal RazerMousepadRGBDeviceInfo(Guid deviceId, string model) + : base(deviceId, RGBDeviceType.Mousepad, model) + { + string modelName = Model.Replace(" ", string.Empty).ToUpper(); + Image = new Uri(PathHelper.GetAbsolutePath($@"Images\Razer\Mousepads\{modelName}.png"), UriKind.Absolute); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Native/_ChromaLinkCustomEffect.cs b/RGB.NET.Devices.Razer/Native/_ChromaLinkCustomEffect.cs new file mode 100644 index 0000000..267dbe0 --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_ChromaLinkCustomEffect.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + [StructLayout(LayoutKind.Sequential)] + internal struct _ChromaLinkCustomEffect + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _Defines.CHROMALINK_MAX_LEDS)] + public _Color[] Color; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_Color.cs b/RGB.NET.Devices.Razer/Native/_Color.cs new file mode 100644 index 0000000..c3c5794 --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_Color.cs @@ -0,0 +1,30 @@ +#pragma warning disable 169 // Field 'x' is never used +#pragma warning disable 414 // Field 'x' is assigned but its value never used +#pragma warning disable 649 // Field 'x' is never assigned +// ReSharper disable MemberCanBePrivate.Global + +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + // ReSharper disable once InconsistentNaming + [StructLayout(LayoutKind.Sequential, Size = sizeof(uint))] + internal struct _Color + { + #region Properties & Fields + + public uint Color; + + #endregion + + #region Constructors + + public _Color(byte red, byte green, byte blue) + : this() + { + Color = red + ((uint)green << 8) + ((uint)blue << 16); + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Native/_Defines.cs b/RGB.NET.Devices.Razer/Native/_Defines.cs new file mode 100644 index 0000000..2c0c724 --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_Defines.cs @@ -0,0 +1,25 @@ +namespace RGB.NET.Devices.Razer.Native +{ + internal static class _Defines + { + internal const int EFFECT_ID = 7; + + internal const int KEYBOARD_MAX_ROW = 6; + internal const int KEYBOARD_MAX_COLUMN = 22; + internal const int KEYBOARD_MAX_LEDS = KEYBOARD_MAX_ROW * KEYBOARD_MAX_COLUMN; + + internal const int MOUSE_MAX_ROW = 9; + internal const int MOUSE_MAX_COLUMN = 7; + internal const int MOUSE_MAX_LEDS = KEYBOARD_MAX_ROW * KEYBOARD_MAX_COLUMN; + + internal const int HEADSET_MAX_LEDS = 5; + + internal const int MOUSEPAD_MAX_LEDS = 15; + + internal const int KEYPAD_MAX_ROW = 4; + internal const int KEYPAD_MAX_COLUMN = 5; + internal const int KEYPAD_MAX_LEDS = KEYPAD_MAX_ROW * KEYPAD_MAX_COLUMN; + + internal const int CHROMALINK_MAX_LEDS = 5; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_DeviceInfo.cs b/RGB.NET.Devices.Razer/Native/_DeviceInfo.cs new file mode 100644 index 0000000..0b465c3 --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_DeviceInfo.cs @@ -0,0 +1,27 @@ +#pragma warning disable 169 // Field 'x' is never used +#pragma warning disable 414 // Field 'x' is assigned but its value never used +#pragma warning disable 649 // Field 'x' is never assigned +// ReSharper disable MemberCanBePrivate.Global + +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + // ReSharper disable once InconsistentNaming + /// + /// Razer-SDK: Device info. + /// + [StructLayout(LayoutKind.Sequential)] + internal struct _DeviceInfo + { + /// + /// Razer-SDK: Device types. + /// + internal DeviceType Type; + + /// + /// Razer-SDK: Number of devices connected. + /// + internal int Connected; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_HeadsetCustomEffect.cs b/RGB.NET.Devices.Razer/Native/_HeadsetCustomEffect.cs new file mode 100644 index 0000000..d3bcdb3 --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_HeadsetCustomEffect.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + [StructLayout(LayoutKind.Sequential)] + internal struct _HeadsetCustomEffect + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _Defines.HEADSET_MAX_LEDS)] + public _Color[] Color; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_KeyboardCustomEffect.cs b/RGB.NET.Devices.Razer/Native/_KeyboardCustomEffect.cs new file mode 100644 index 0000000..5e9294f --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_KeyboardCustomEffect.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + [StructLayout(LayoutKind.Sequential)] + internal struct _KeyboardCustomEffect + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _Defines.KEYBOARD_MAX_LEDS)] + public _Color[] Color; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_KeypadCustomEffect.cs b/RGB.NET.Devices.Razer/Native/_KeypadCustomEffect.cs new file mode 100644 index 0000000..a08a175 --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_KeypadCustomEffect.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + [StructLayout(LayoutKind.Sequential)] + internal struct _KeypadCustomEffect + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _Defines.KEYPAD_MAX_LEDS)] + public _Color[] Color; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_MouseCustomEffect.cs b/RGB.NET.Devices.Razer/Native/_MouseCustomEffect.cs new file mode 100644 index 0000000..a5e1599 --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_MouseCustomEffect.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + [StructLayout(LayoutKind.Sequential)] + internal struct _MouseCustomEffect + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _Defines.MOUSE_MAX_LEDS)] + public _Color[] Color; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_MousepadCustomEffect.cs b/RGB.NET.Devices.Razer/Native/_MousepadCustomEffect.cs new file mode 100644 index 0000000..01368bf --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_MousepadCustomEffect.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace RGB.NET.Devices.Razer.Native +{ + [StructLayout(LayoutKind.Sequential)] + internal struct _MousepadCustomEffect + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _Defines.MOUSEPAD_MAX_LEDS)] + public _Color[] Color; + } +} diff --git a/RGB.NET.Devices.Razer/Native/_RazerSDK.cs b/RGB.NET.Devices.Razer/Native/_RazerSDK.cs new file mode 100644 index 0000000..af6310b --- /dev/null +++ b/RGB.NET.Devices.Razer/Native/_RazerSDK.cs @@ -0,0 +1,146 @@ +// ReSharper disable UnusedMethodReturnValue.Global +// ReSharper disable UnusedMember.Global + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using RGB.NET.Core.Exceptions; + +namespace RGB.NET.Devices.Razer.Native +{ + // ReSharper disable once InconsistentNaming + internal static class _RazerSDK + { + #region Libary Management + + private static IntPtr _dllHandle = IntPtr.Zero; + + /// + /// Gets the loaded architecture (x64/x86). + /// + internal static string LoadedArchitecture { get; private set; } + + /// + /// Reloads the SDK. + /// + internal static void Reload() + { + UnloadRazerSDK(); + LoadRazerSDK(); + } + + private static void LoadRazerSDK() + { + if (_dllHandle != IntPtr.Zero) return; + + // HACK: Load library at runtime to support both, x86 and x64 with one managed dll + List possiblePathList = Environment.Is64BitProcess ? RazerDeviceProvider.PossibleX64NativePaths : RazerDeviceProvider.PossibleX86NativePaths; + string dllPath = possiblePathList.FirstOrDefault(File.Exists); + if (dllPath == null) throw new RGBDeviceException($"Can't find the Razer-SDK at one of the expected locations:\r\n '{string.Join("\r\n", possiblePathList.Select(Path.GetFullPath))}'"); + + _dllHandle = LoadLibrary(dllPath); + + _initPointer = (InitPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "Init"), typeof(InitPointer)); + _unInitPointer = (UnInitPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "UnInit"), typeof(UnInitPointer)); + _queryDevicePointer = (QueryDevicePointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "QueryDevice"), typeof(QueryDevicePointer)); + _createEffectPointer = (CreateEffectPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "CreateEffect"), typeof(CreateEffectPointer)); + _setEffectPointer = (SetEffectPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "SetEffect"), typeof(SetEffectPointer)); + _deleteEffectPointer = (DeleteEffectPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "DeleteEffect"), typeof(DeleteEffectPointer)); + } + + private static void UnloadRazerSDK() + { + if (_dllHandle == IntPtr.Zero) return; + + // ReSharper disable once EmptyEmbeddedStatement - DarthAffe 09.11.2017: We might need to reduce the internal reference counter more than once to set the library free + while (FreeLibrary(_dllHandle)) ; + _dllHandle = IntPtr.Zero; + } + + [DllImport("kernel32.dll")] + private static extern IntPtr LoadLibrary(string dllToLoad); + + [DllImport("kernel32.dll")] + private static extern bool FreeLibrary(IntPtr dllHandle); + + [DllImport("kernel32.dll")] + private static extern IntPtr GetProcAddress(IntPtr dllHandle, string name); + + #endregion + + #region SDK-METHODS + + #region Pointers + + private static InitPointer _initPointer; + private static UnInitPointer _unInitPointer; + private static QueryDevicePointer _queryDevicePointer; + private static CreateEffectPointer _createEffectPointer; + private static SetEffectPointer _setEffectPointer; + private static DeleteEffectPointer _deleteEffectPointer; + + #endregion + + #region Delegates + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate RazerError InitPointer(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate RazerError UnInitPointer(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate RazerError QueryDevicePointer(Guid deviceId, IntPtr deviceInfo); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate RazerError CreateEffectPointer(Guid deviceId, int effectType, IntPtr param, ref Guid effectId); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate RazerError SetEffectPointer(Guid effectId); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate RazerError DeleteEffectPointer(Guid effectId); + + #endregion + + // ReSharper disable EventExceptionNotDocumented + + /// + /// Razer-SDK: Initialize Chroma SDK. + /// + internal static RazerError Init() => _initPointer(); + + /// + /// Razer-SDK: UnInitialize Chroma SDK. + /// + internal static RazerError UnInit() => _unInitPointer(); + + /// + /// Razer-SDK: Query for device information. + /// + internal static RazerError QueryDevice(Guid deviceId, out _DeviceInfo deviceInfo) + { + int structSize = Marshal.SizeOf(typeof(_DeviceInfo)); + IntPtr deviceInfoPtr = Marshal.AllocHGlobal(structSize); + + RazerError error = _queryDevicePointer(deviceId, deviceInfoPtr); + + deviceInfo = (_DeviceInfo)Marshal.PtrToStructure(deviceInfoPtr, typeof(_DeviceInfo)); + Marshal.FreeHGlobal(deviceInfoPtr); + + return error; + } + + internal static RazerError CreateEffect(Guid deviceId, int effectType, IntPtr param, ref Guid effectId) => _createEffectPointer(deviceId, effectType, param, ref effectId); + + internal static RazerError SetEffect(Guid effectId) => _setEffectPointer(effectId); + + internal static RazerError DeleteEffect(Guid effectId) => _deleteEffectPointer(effectId); + + // ReSharper restore EventExceptionNotDocumented + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/Properties/AssemblyInfo.cs b/RGB.NET.Devices.Razer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..220a7ac --- /dev/null +++ b/RGB.NET.Devices.Razer/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RGB.NET.Devices.Razer")] +[assembly: AssemblyDescription("Razer-Device-Implementations of RGB.NET")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Wyrez")] +[assembly: AssemblyProduct("RGB.NET.Devices.Razer")] +[assembly: AssemblyCopyright("Copyright © Wyrez 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("24ff4acb-d679-4b2d-86d4-50ab6c02d816")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj b/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj new file mode 100644 index 0000000..11919f7 --- /dev/null +++ b/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj @@ -0,0 +1,105 @@ + + + + + Debug + AnyCPU + {24FF4ACB-D679-4B2D-86D4-50AB6C02D816} + Library + Properties + RGB.NET.Devices.Razer + RGB.NET.Devices.Razer + v4.5 + 512 + + + true + full + false + ..\bin\ + DEBUG;TRACE + prompt + 4 + ..\bin\RGB.NET.Devices.Razer.xml + + + pdbonly + true + ..\bin\ + TRACE + prompt + 4 + ..\bin\RGB.NET.Devices.Razer.xml + + + + + + ..\packages\System.ValueTuple.4.3.1\lib\netstandard1.0\System.ValueTuple.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {5a4f9a75-75fe-47cd-90e5-914d5b20d232} + RGB.NET.Core + + + + + + + + + Designer + + + + + + + + + + \ No newline at end of file diff --git a/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj.DotSettings b/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj.DotSettings new file mode 100644 index 0000000..1e5aa14 --- /dev/null +++ b/RGB.NET.Devices.Razer/RGB.NET.Devices.Razer.csproj.DotSettings @@ -0,0 +1,11 @@ + + True + True + True + True + True + True + True + True + True + True \ No newline at end of file diff --git a/RGB.NET.Devices.Razer/RazerDeviceProvider.cs b/RGB.NET.Devices.Razer/RazerDeviceProvider.cs new file mode 100644 index 0000000..1114dd0 --- /dev/null +++ b/RGB.NET.Devices.Razer/RazerDeviceProvider.cs @@ -0,0 +1,218 @@ +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Linq; +using RGB.NET.Core; +using RGB.NET.Devices.Razer.Native; + +namespace RGB.NET.Devices.Razer +{ + /// + /// + /// Represents a device provider responsible for razer devices. + /// + public class RazerDeviceProvider : IRGBDeviceProvider + { + #region Properties & Fields + + private static RazerDeviceProvider _instance; + /// + /// Gets the singleton instance. + /// + public static RazerDeviceProvider Instance => _instance ?? new RazerDeviceProvider(); + + /// + /// Gets a modifiable list of paths used to find the native SDK-dlls for x86 applications. + /// The first match will be used. + /// + public static List PossibleX86NativePaths { get; } = new List { "x86/RzChromaSDK.dll" }; + + /// + /// Gets a modifiable list of paths used to find the native SDK-dlls for x64 applications. + /// The first match will be used. + /// + public static List PossibleX64NativePaths { get; } = new List { "x64/RzChromaSDK.dll", "x64/RzChromaSDK64.dll" }; + + /// + /// + /// Indicates if the SDK is initialized and ready to use. + /// + public bool IsInitialized { get; private set; } + + /// + /// Gets the loaded architecture (x64/x86). + /// + public string LoadedArchitecture => _RazerSDK.LoadedArchitecture; + + /// + /// + /// Gets whether the application has exclusive access to the SDK or not. + /// + public bool HasExclusiveAccess => false; + + /// + public IEnumerable Devices { get; private set; } + + /// + /// Gets or sets a function to get the culture for a specific device. + /// + public Func GetCulture { get; set; } = CultureHelper.GetCurrentCulture; + + /// + /// Forces to load the devices represented by the emulator even if they aren't reported to exist. + /// + public bool LoadEmulatorDevices { get; set; } = false; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// Thrown if this constructor is called even if there is already an instance of this class. + public RazerDeviceProvider() + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(RazerDeviceProvider)}"); + _instance = this; + } + + #endregion + + #region Methods + + /// + public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool exclusiveAccessIfPossible = false, bool throwExceptions = false) + { + if (IsInitialized) + TryUnInit(); + + IsInitialized = false; + + try + { + _RazerSDK.Reload(); + + RazerError error; + if ((error = _RazerSDK.Init()) != RazerError.Success) + ThrowRazerError(error); + + IList devices = new List(); + + if (loadFilter.HasFlag(RGBDeviceType.Keyboard)) + foreach ((Guid guid, string model) in Razer.Devices.KEYBOARDS) + try + { + if (((_RazerSDK.QueryDevice(guid, out _DeviceInfo deviceInfo) != RazerError.Success) || (deviceInfo.Connected < 1)) + && (!LoadEmulatorDevices || (Razer.Devices.KEYBOARDS.FirstOrDefault().guid != guid))) continue; + + RazerKeyboardRGBDevice device = new RazerKeyboardRGBDevice(new RazerKeyboardRGBDeviceInfo(guid, model, GetCulture())); + device.Initialize(); + devices.Add(device); + } + catch { if (throwExceptions) throw; } + + if (loadFilter.HasFlag(RGBDeviceType.Mouse)) + foreach ((Guid guid, string model) in Razer.Devices.MICE) + try + { + if (((_RazerSDK.QueryDevice(guid, out _DeviceInfo deviceInfo) != RazerError.Success) || (deviceInfo.Connected < 1)) + && (!LoadEmulatorDevices || (Razer.Devices.MICE.FirstOrDefault().guid != guid))) continue; + + RazerMouseRGBDevice device = new RazerMouseRGBDevice(new RazerMouseRGBDeviceInfo(guid, model)); + device.Initialize(); + devices.Add(device); + } + catch { if (throwExceptions) throw; } + + if (loadFilter.HasFlag(RGBDeviceType.Headset)) + foreach ((Guid guid, string model) in Razer.Devices.HEADSETS) + try + { + if (((_RazerSDK.QueryDevice(guid, out _DeviceInfo deviceInfo) != RazerError.Success) || (deviceInfo.Connected < 1)) + && (!LoadEmulatorDevices || (Razer.Devices.HEADSETS.FirstOrDefault().guid != guid))) continue; + + RazerHeadsetRGBDevice device = new RazerHeadsetRGBDevice(new RazerHeadsetRGBDeviceInfo(guid, model)); + device.Initialize(); + devices.Add(device); + } + catch { if (throwExceptions) throw; } + + if (loadFilter.HasFlag(RGBDeviceType.Mousepad)) + foreach ((Guid guid, string model) in Razer.Devices.MOUSEMATS) + try + { + if (((_RazerSDK.QueryDevice(guid, out _DeviceInfo deviceInfo) != RazerError.Success) || (deviceInfo.Connected < 1)) + && (!LoadEmulatorDevices || (Razer.Devices.MOUSEMATS.FirstOrDefault().guid != guid))) continue; + + RazerMousepadRGBDevice device = new RazerMousepadRGBDevice(new RazerMousepadRGBDeviceInfo(guid, model)); + device.Initialize(); + devices.Add(device); + } + catch { if (throwExceptions) throw; } + + if (loadFilter.HasFlag(RGBDeviceType.Keypad)) + foreach ((Guid guid, string model) in Razer.Devices.KEYPADS) + try + { + if (((_RazerSDK.QueryDevice(guid, out _DeviceInfo deviceInfo) != RazerError.Success) || (deviceInfo.Connected < 1)) + && (!LoadEmulatorDevices || (Razer.Devices.KEYPADS.FirstOrDefault().guid != guid))) continue; + + RazerKeypadRGBDevice device = new RazerKeypadRGBDevice(new RazerKeypadRGBDeviceInfo(guid, model)); + device.Initialize(); + devices.Add(device); + } + catch { if (throwExceptions) throw; } + + if (loadFilter.HasFlag(RGBDeviceType.Keyboard)) + foreach ((Guid guid, string model) in Razer.Devices.CHROMALINKS) + try + { + if (((_RazerSDK.QueryDevice(guid, out _DeviceInfo deviceInfo) != RazerError.Success) || (deviceInfo.Connected < 1)) + && (!LoadEmulatorDevices || (Razer.Devices.CHROMALINKS.FirstOrDefault().guid != guid))) continue; + + RazerChromaLinkRGBDevice device = new RazerChromaLinkRGBDevice(new RazerChromaLinkRGBDeviceInfo(guid, model)); + device.Initialize(); + devices.Add(device); + } + catch { if (throwExceptions) throw; } + + Devices = new ReadOnlyCollection(devices); + IsInitialized = true; + } + catch + { + TryUnInit(); + if (throwExceptions) throw; + return false; + } + + return true; + } + + /// + public void ResetDevices() + { + foreach (IRGBDevice device in Devices) + ((IRazerRGBDevice)device).Reset(); + } + + private void ThrowRazerError(RazerError errorCode) => throw new RazerException(errorCode); + + private void TryUnInit() + { + try + { + _RazerSDK.UnInit(); + } + catch { /* We tried our best */ } + } + + #endregion + } +} diff --git a/RGB.NET.Devices.Razer/libs/x64/RzChromaSDK.dll b/RGB.NET.Devices.Razer/libs/x64/RzChromaSDK.dll new file mode 100644 index 0000000000000000000000000000000000000000..0ec6585d755cd47b95887b0bafee7f66485d2756 GIT binary patch literal 119424 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

^`6yKa2I1N(916`ERItq3AT6?|Vh1>e37?fF* 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 &KiWR$Kj4$*5XR7boI1P)oE4H_a_$GrIZXNcg>; zcw7tX_AEISWi_mF9!_UnZ5G0dQr`!JTz_F&q%Q(*hadHHDeFltL0|0_^(2?in9=6x zy-q)|ZmvGZznns)xZ)wD>+f76oGPl~dOi8fmn<9CB7pY77ymo$zPivTNTt%jzta$K z^iU%wZjdU~FVz&CEhk=B7#pPOb8c;tx!;5wI`^Slku^|>V1Nym3IuJ~KP_2{*%<`5 z#xKB=-x$*rktD%K z@~6&cd?Y{FkZ_L5{a*~s5SD%$D1;cSd5Tdl5a?HPl|1#80)w5`1PWG+klk&*6D;ib z=^4C7VZL4X@#{0VvTD9sML=Tnauq>|gR8f6CLnFgp=hKOmM*^spXH8OelL511Jx1w z&QZ$lx*zdt=jA4l7VW}Uzi)Z>S9CwuulsNuen38WulEyi1@}V4yMu(STQ>63BxqT@ zLCrg>c$Xv7y-)1vdY+mn8dnOgnWKdKzRR(ML9igt^#O#OY#mD&M#3VU&=4;^KaV^; zv-L=k-Jd`&g$%N6yt`MH!DJb&rwU{VhWHkj*>iY;zYMNL1{zh+5c1e>BuY}MSvl@l zYO&XRk4~+RLMglT!8K)fQxH_WdaOxnhC2g(7u*84JK*-fy$3wD{!&nXe@3s{Tm4<@ ztG~CfLB;xe6u$tf*PrWDcZ3)uqy1~NELk_=MV!ufCTERJh38mWMx$h)6}8-X7#a}d z{FdFZxxtXS|ZFtT6~b^IVDk< zNU!xd1GB>=a5urd4)-x!EUM#NxB|G9aH>8(x(ns$*&ahI8`mR4990x@RyCLZlK5`~ zemK$<;m?yoU#3J9>X@zqRBYGTqD%@y)prdchBm6#WS8$m{(E1syS;@2;`2Gc*zmneQGD>hrNkTA_Ne5%)*?ry^y zQ0VZCjq8;OCT$;F2i(_i-@rCE~sVW%Ls`Q`l!-f1IZB z6*}^vKh@FH*&LxMU)(FkxbeD}D@Ta8NqkgWbt=E5`FksTYp)5_`2gGjxFc}Gpp+!I zd2sp>iL&osu!YpazG2p>eHrsWqqUBPwZgu9HrUf{^-(~|U8{MvV;w1~A3})wt(&!m zTUbUTl_UKX$-wF({Ur<-^1-~3Zv(|!#$tf;6wUE7)eGn6Kdg8Dza|;@|Hwd}{Hv_J z@>lQU)Xmz-tp~hN=RfShD|FtNmnWfvXa$np7YYp|4Eb6RdtLO(7tO|^wJ-8dA&!f zgO2RB@7f#PSqCAI#`lgBj?nRzs4YgQaG<^J-`?teo=^*VNVDgVIKFz-GaRwJeD;o1 zQYX7q@aXgo4&=onO4U*Cd z|D%Z6V_pvobKPTJ10NUxcb)RdZ`EGa3WibFGUc8S6Z=~~-W_$%^<bnWZWHBPi|jp>$eZQFg;?`a zmJ8*C$%%4)Px;*Dc|tcJ|m}2Az-miQ1M~V!y5%%I2 zRANG~K!4VXNcT&19p{g4v%mF*J?SU=Td&%a-nTc*ngTj>Yf}jtqvnCmGZF?V&eNf1 z|Ld=Oodee*_SzaRGI?WozZ?ek4cilX)0evnP^&z|g>aeGG_81aT#z>PAtlMIwBuB*pJ)w;?MdeTs-rMZDYGzX1#z zqs9Q`j(LI+tqtZMHsOLuus+;L`edFWqjyf0Fs{0@A!EJ7H4f3!xghWHGMz9i@ zplfRYLtoz-bw6V;F-)|(htER-ypf(X{2n0dJ~^M&g^ss7T$Jrz9`19I7nT+;Cq%8fA{>`fY@Oo;Zr1*5&K zwJQnXy+C_oUL8v*qP|7|hT(qK8*>vejZqH+!^y7zrM>9~kcl zp|?cnk-yB?Wyah0E_}MW+irO*su`TB|0#aZFJX7Hsp|)2Po91HZNfnDaH>PEP^E;<0Sqzj1`X%PZ%E z(d=H~-JaLHe`7zj2)Ag^c?tyud;z}mlPF*N@th_Y;X4wIQ4cgiQ;@&&5aK;SkOw)K zJs%+?ilxf>g$j3m2#;(lmZ#0XZv^??=Qsvf(syq%Z$#Mh6@>CU1u~_D!Jh3h5DuOF z0p6~!Hme_l*UPNmsd2xDg_ULFAK4VDP!*`p@%Zq=S&;MR>w!B5sez+zR|BUt0yk9P zU>04=&^oKDP&V#RU6L#Y#OtZT!1I+h<^|5gS zqV51*r@2k|2Soh=km^ttnn{dp0N!UAF&8-hc^_2wONh*_hIxL zjqj(sV%>B5XU*JI8R1S13*ME5k6&O0^HM_BAyhu5=Tynyy49n#-_aKV;|>K361E=8 zt4p%ZHoxZ?isqPf09uT!|Dd{`ePhg>Zc){kbxNArSuX;kZGO)cQh%WOl+CYtJTMo4 zPXL6ma)PS&moeXBQirk4z0aN$vk(bI_p;Ak_mh{uSNy7{5vjWHoshRsVbc)raRQOF zcSEb)l8#=8>qPPsZRa6QXnr_2wDpIB-Zt5)h{s}idfithR|5Z?iY-X2y=ilT!m|UL z@t7~J1+Y9V+oJmLNv8!wTnygEToO95+{Y**+8DmyAnt~#@65Z8@JHYGdPZVQhS}mv zADtf7>^AIdZ$7vweYDm3jHzzc(#=4+BIJO?)!rU+4p5ZE^LLQex4M>jcEbKqRxg{u znJAE_P$I4*B8CW;K*WqVOAtl?cvcB(uO90ulWgK)FP&$K_ipfA-isr(PRJD1^MOj?kBJ8FwXCs@v<7T8015b@Cnrwn)Cs5oG1%kgECufSLBP|NbGGaz!>|*7;b`kEWlWExuluS2 z>m5`>=SoH@VQ-j=4~f{rJXXxzaG7?Mh!57M7Q6Rj z0@z@~pkuBOF*xMG??ebMmN)o}jozmy{^5P`??8Wxq_#f!PxJF1);Iq@jqXGKTJl#D zBKDog>vw`yyc^iL4AHV}2sI)$pRxGqR(uxeeZJu0Ne9XIyZxU+dc%{BGY|1&4XhX4 zw(GvOb1@O``IPIT#+ZeLEEo~tA`f3YPka&oqA$MFOd0A{g;5dRqmay!VGCn>!Ln9- z&I261o||#}(9{(K#n-82JLZX{BX#-3w=t|}khp=~3XbmgW;b8>VZ{$f&JQwqilYx6f8zZ5U9DBnCW`psrQ6OzC-1CtO)eW2A^~ell3bgqG zY)Iu`ZEpq4V;}8}d1W{TsH0epsyp&ldt*ko>nY9MquW{ZbuUv};u8>TQ(`EIRg;+bI)?}2TT<&DvuBO$t}WZr@y#joI|x#Y zb%%>=+3D%-ed+E4wsnUc<*riOx+A6Kj!fIS?sAu-EF-c#-Tgtj+dK!E+SYw*t9F%S zM84;KD?RePbw}nn%Bz$1x{szu9uS?kWgF%)>F&Mh4cSNCZ4H-y*RbH{40MXPiW{{Y zS>mL+Eo4mh`CdzUgZW<=t!K4{;%p;fRzYZx<{B}}Nif9RjJr{FN8?uxwPQZVKa|sQ z|L&?tBsU=?f7c=dJx<)ZPc%!nW!;aOr9i7>YzSiifY=r^d#zzX2L$vXdnG?>n;}R>oz-BUv*3? z!Jm{7ZBN=|e-^D44>*lNAW6?`=5EZE@RL904a^hb+xg=*4d!bZL~nwd!i`ZQ)+68@ zvk$qk&x#s=aOVM3B)W(g_{F-#^T8p^LhHgpJos20JwZs1)eC4QZ6rC?X!`? zl-&Sbr`p{!xle$Z-5Z|A&~aeZQMVh{+dr=SqQ{`YzUUZUH%ML^kKyGw5wDk6DE7tV zDf(TmdVWlAOijS6R-P|Vx%RqcIV|fX&;IJITeb+^c?osP7IQg?*SexX&zHLp&o+*q zIo{K+0IBxOiPg9it82z-aBKBcZ_ME#sQzc#*6r1_ezll4T-df@@2bR z1=O8)OtbjEP5i%A{J&28uMz*R6#o~C|2xG07V-aK@&7yVKNJ?`9TO)0eoe$!smmNn|l%%a_M-Gk9$)1t8?w+CMk_Ps6G;)<|5 zZ40jJ=*|kp;tZMWy{z*^d{mTmEarSz374Q}b)rGl#LIblruz~IbR?T!T2N-;fsK8j$6FBO7}VVs_+toTF`z36gQ% zD-12*pJ9Ky?(66*tjV%yhuKLVWMS(tHfwx)wk7o~N0xiom}qD!d*)k5s!m&KTlcBf zo!xkshEdq|O0YoV_r%>ujoh(*dpY{Z?)}~|2Voom+3ok|=j{lLu{UncK@Oe8dw!*= z&~7(78LodzhVgz1rgr$XEO$m?U55z+a8#Wm@mIS$+v+I*jqEM_Jn`*=(b0MuyL&+m zI@cPz_a)C~?}NBO46JA~Z@%YCDpKS!NrL!MuFIXM*;xM4Rwceu-O3M??L=kpS!5DYo2VZ<^^;yHOpE>E6!wcB zc7co1UF<6;T;t{x(ye)1;6)mz^e-abrNy4HljYLFrreRh0eCej3_%%PX(YB5w$Gk7XW8-;YXs z0r8r|Q@@?JN_;f&o@PNW>ST??^E+&v?Gi8KDVF$V;#(wM)LWLs^Il=+R*5el|73~p zCVrE|mk>Wp;-iD$cS^jI_z;OtBz}>^*AV|TtD|vqJo#89zLxm6B|eAv1c?`VdS2pd ziI0}}CelA3@vX!k{fpqah4||wp3DBu4vF7Nyi4M(#J5TOcH$RH{8sX5miT7k(93XkMEYIutM*;M@OSuVBHw+)uP~aD_O44oht+Dib(eb= zCeOYxXLGhy=&thy^saui=NXLao3q7c!sVTnzPLom&W%wXi$7ir#l5%4ueq<1;dC#+ z!CQ8L=kMt5o4_>@8TG7;(T4*I%lZ_~(mi{?OKJ_RTaO5i1S>s9?q z`hTnh4d!Mb-zUlc1`HpZNwoPRAC!@g>yZJxguW!dkFN3YTP>690Y>rTT1REJKt^uU zBah)%J8LZS!Vnx3571zaM9?(|Qbj1K@H-V_uEkC#Cfs;H7%mAPUqKwkyRq+pnNUrN36e{R$3K^i3-K zu!5hebb5Ya3V)`87b~d8zuSYpN$KxW1-B`)csU`il;kLdq6*t|>f&3Q>eYpsN5_sH)m&7oCl<@jHwC25m!m8clLGGD%#<)yCD z!m`rUjW=#qot@Wfet*lDPSJbd{BbtK!QYE91)? z<;z?pWIfV+OC07>+QHl8-r&#onKZ0+w1jOYEgp~$<^)H0Z!F?&@S&1AfA`{rrdJiK+Fe7hIiLu;bbr{x&(z zc6OWn-p|*bw&Wjgx$e4q;&TU@hs@qNe^lh#y6l=ZA*V z|DV4VPdXHqcgoz&YagJUe_lCv z_RFz}!T)kz9=G9$H|vfgEv=o7#wXWZylejJ7rvLhdH#L2`b)Z!6N)w-exPi}>=!~_ zSU295o)r1l!GBwHbxGQ@cNYwKb;}i7K7948cOtrG*zajr`c6pE*JWcb+3X#du`spq zl2_)uc*m6XoRHRQ|Jl)y+VD=)#=GM}hujqP*Byr+J166YQ?IG|cEvB>rtF^a&==i5 zTyWElkIuOEv}q6i<_6C^e!XDEtdBg0X14wO_YUhFA8edB>hPAEcGV=+ z+V?v%H%vd6uzz@J#6=&~PiUI_=-fLS4*uoil&99L(7wO4^UrUmJ>{Bz$<4PsmG`H| z$8Ef1RZaT6|A_fMw!HH5yZ-#w*fY}->^HxBX6YZ7kE~libl5*$c=1NtlYeMG-~3qS zp`z#Ro0zb#G4HMU zAMPzUlS$Jqfa9z4;TXU+!c7Ur&OQdONDNq)VNcB*rfFxO%Y33gj*qY)I|nyqS7Ezx zKs@3TH0=#sj2>#$v}bZO?LG|e!L`WiKJa;!2YM#F7Gu&1@&4&>El3-?AxPVOb}){i zLbP)t%-SuNglaKbn3g*@LK}q)`eDNJM8aV0jHPkf04-j-=%NJ8`|4!;oP||u+?J!6 zwm7tKtyUY6ai8|%Ij@QpD~jQJdX8Ty7^^sX*#PM{ei1VTzGPq~VT|JVm4T5f9X^g$ z>7e15l?q=PW;fFj*2k}&{8JH^3W`Z04adjn;4xi{l*#bT7J1shXBu*wfN&CWWU6GO zkuDLjI{yre7y9_mLS2S9|ClI8#LEM4`Z&q)pW~woBTT$D3y!0T{v69?BIXKC{K=>H zd~Bd80>3K!9iKnPtHolhJw89?csx$M%W)ZOHxK%okN=m$Q6mnNm1A_N_}{ayEIWt$$*m(rj2Bv23cR6%Wxhu9rVe9b_#HbzWVA*-aN>fsm;M3 zXLu);xi3B$+J&&f6Q@s0E2*qlUYMJH!HgN4d+X)zi?30iV?@s&Q%r2qn-tut;L)PLZhf-; zzU@+8=Y4HmHOV@aTp`tRHR6QLh!NVZ{WB?)Pztx$Hk24?%UA;j^4 zvjwh_StC>6m{WS6`e&=96gK?lxWfK{^9O33VXoF`|I|23mmy>!mMiUa@kcA_OFz^q zOH8}wEJn_4l7N%;$v%R0PAg|iX71!i#~fMjr0$D3*#5ZxkPgVcknN{0 z{?tEfOzM;MOX+Esl%HkS=LD>u2?(*gtTAb2Y$FkMpxCEkVrE z#$sgcjYh-B$^8!Qhj14taL&e&udn>|@w9ioCzm(*pJ;c6vro%g7p;j)B$)remRP+<^*&el$-pbd!d^KeAA88rhK?&$hpa+wNhbt7{huaY-#k< zQO}7;X9eVQJi6{1PL8hSBtrwGX;QJ@it5a8J9Z zeunj+OF(#0k9>LA>}R)M&wP!;dHu0=ppy9jQ*1?iG-&K z;Z}t>UcTDB$MPfnG5MY(UBOAxHJv0~>q*jepCn!U*51pT1HAsQ-WDNjRS18KL@b@! zaS46wF0O=oyUwJ&T`H;cIBYFx*jCc4_26@5U>xne0GZ?=F0V%%uBHY&w+4j%&jrF- z<$AFv$HZcZPf@UEyy*0Z?3Q$43O>0nnmD)Gdd$l zfS)$xG{kR$zh~Y|Kwdf*WB%lCe2gLfR_Q^+_-yJC*5gQ3V2DcrKm9T0>om|XJ_X^vcru={HyhIUxniz>JSZRb zUPv176!cM^Eol43XHk!^9!IJ~L)@l5%40RqFl`vZeeq;G%TsGeW0Zz6Q$Fksk<|Ch z&rQ$${L8~Helhj=xf`A=7;^bpe?G{uFk{BzbjJ!uS%q^sUQ$`Stkktw?!qa6&A7$I zjunfSl$I~9nVP(KzN5@hSmnsiDJ;6GaG7KA{MEiqyQ#?&oyAL3j^_Z<@fis>CoL}v z+v1uP{)sL}jcasqDZH8$xcKQNguNXq_GF$vWUwG z#4R9hQ6TP8;tB$AnSx(QATEt`&On?ea6Zy6V;4T&Uwi$7GlCB*%As^G`NxBZ;4h})yHB!EoZCK}+^zdlh z@x$`~+AVPoNz?YAN$Un$)jdvgAQwKY9P7s=5!Vi!!A6pa<2@)R&W2RlsRGw3*H#lH zdpV9VKZc9VTPmr7l*wQnV^vZ}IZbmAm<5f0L})6OMMl$H3CeN2{6%~IS? zv)~7+Et4aiCcGUMIyorO6cM6DBu8rzkB1hFFb8R7mql~pFmBL5jFYI75y)5Tfg3O| zNDDs_l(>2hbiE4S`@+Mv%EvlLju&?Witu0=7Wq{MCC1^lNk7w@ng?l}SfIou_R~%^ zy{q->FgrsEf)j&suy=rpZvx+Kh$rtR@Q%jeApIe%Ts#K>8IBtpq76wNNO_!0SAuk_ z5zn}-NY{+>KKefdQAB}3R06Q;%B#qSvCHU_$lnLMDwG&oEfTnv}jPa6!};K|X>xH0{- zVM}7WPwg1oI?y?wAR75le^#YGuoZF=N=^D9aeO$2Gwx-`fm#tx#~lRD3Y`A5qwFaN zSak$o&|LrCLH9_R-?CAXU>_j$#B_W<0;KN?K<4$G!bgJ#^NfoK(jq2@=ICXJiwxEx zQ~Txc7oBySDFn8k3YiC*wSh}caYhyN&xu65(SCA%C-Z9_BlGWnCYa;d0Z4h`MzdaW zM7^+n6F^@Hff&~eS=!H%{x5l8jOWNW86I#(n07|-U~OpLAni2RN92(ZC*{&qeu?8H z-5x+bac8S^36WYt@-QvdG+6839nlfi9@-k>3@)HuGX23d(%xH@N+;u z?Gq$F=R^_I)&r9Mc|cMOfF7vV(*PO%1dw|Dy9)mit)BD)28Y0gBDB!TAR&LV%D-FX zlZc*xbT_CTAtOnK1NaIxa0kI)kY;w33F+WuBD^UEzVLI|3bb&0-nShjU zIw0lB0z{M0@&UOHtpr5#)HVQOh}O0Ma*TZhkn8Ig0MWf_Zvt|i`Z=KS*{bCE0FaLd zkQDy{WWGV@NSUt%kogV+WWHkoneS9U=9>n{d~*Pq@8y8Z_ewzKyAqIVw0i-mC)wYH z(srG3(;~Got_9kN;yi75Ual6GJYVY%TQhgaJ}3pc(QQr3lJa+8q#&Pf*tqb#FkOZR zoNCsh)`vU83jA##TIn_#X`^RIx*GubG@L8Laj1(?#rFn}O1>v}lTLh|47ZhX5HT``7uJKea4dE7Grr zqPH%VbT?ft)7=8dbc+gP_+vnZpDdK&xUv1TL1-I2`E3EeJ4v!c($6T8^g98WK5iV- zjXWlu6X}AWT5GYS`Rvvm@O|q>IL6g#7y!A^bLYeyafb7kL> zh?)iG$hJ|S!exML8#O9i56HIBq{4RtvTbZv;U@vvHdfLwUA0P0T@CLzpw+BFj6 z1!VZTHL{;e_?_(M{s_oa2LKsA>ss0GeE`UQ?~=7LyaAB$-GHp?Ew!>AJawH6rvfs* z8IW~<*?QS8z6Z##xlYEf0HiGpt(X1e9e@n~6_DwtZjk-v2Y~E1FS$;JHvlsJdqCRC zT^sSP2w>bM8O{J?{L_H6oy!`af57(u88&ZL`2*6HhPpKkQ%vm+K!*Pc$n;6qV+=z0 zZ-9dVa~oxNEg<8+0;H{Nyg}1W1=Mbo;j;l5ej4@6Eg;**-S=V7 z958m93}*l`{vp5#z|XxOYZ1U#02w|4$oQfM_FMAsMa&Wc+7XgaeihcLOt#1!&2a{ZIx*n0r}k0D#MKDW1RmpA7j3?26&y(eeep%)-9FWf_6=pmi zV?N|-%qOvze3Eg_X%#8uzeJUf^{+CYY(PG_D$IC3#(c=vm`_+Q`P|(jpB$A>(5d|GsXlO)8(nS7ko40Qsb;Fyr|c^C4eTScn!jU#`qXaBt(g{#wN6k(wE6r5vQa60R9} zjX>JKa3~hD7}-Fp^`Mn&`Eab|v7S%HdLCyLM^4f(1rD9C`)PnW93`2S#X7 zlQFL@2+fguP2pO>#~5c1VJ@q98N+I?#wlo%L$y}W#dc`gt%^1vY>+PpG-03kvDXF`Q6_Qau~x( zj;)CQ7o4t-fUrT1L=VOsIK93C!+tpe@JbHrSDN-EoL)ZxVI@axm!`b~r^^u#HpmhE zHRkYex*UOFzZ?O0B}e=}G1kNBas-5x90lKK+EZ}4906g29NnPV-tCtoFzlBj0I%fG zj%eERaJn1;VO@@Iu@->y$zco|l)UF(XdUg=n}ttPT1RO?ETeGwA|x1BUqbSh)vQgFUq7f6M-Qz|ZJl@$3NPTw_2S z*T17pT+fnrD`<&tR{i+%fb7xR0FjgSHefJdhl+<+Jk7sKem?{9IS;cKrrQX}bT0xj z-4B4!6mFVJKGy;={$4=Rw~GJRUjr2K^(af5+7FC2$@r@Q`J94HiQeeD6@7cKjGu%( zPCl;#_C_DeUyi}k6e{Bn1M<0{U+?r*MPCpu<97k_xgw%>`VvJS-(SYB1LSjhRPXdn zioW9%8E>;l`i}v7lfOmLZyg}xO#>zUJ%ED#G&APBgSAN0KrO^6>%0SL7}t$D&B1(? z{zgDPpR2Ige?Ji96npK!j~E`J4KE&`h2bnBKE|Y#!a29f@g*RvY`f`HlXefBuvyV2 zLPU56aNAWn#v8-NG$y<>6kZvWBlf75NZT@nVGlDEe&{@$IB_;J{M=%vrO9H z9I2xL;;bgjcUGg*jXV!?G`R7KE_?{){K*z=NF`1coP!EG%!>fi1U(xm|qQ?9GPPhIts_UfU!yj>Ms%gxCku{V{#nkV{usH#G(I+n;g?2 z=vW5>>BDr+DrGqcxqCiAKy(8idw*bDH1ND@!H4!q7z_6^^wsmx?}QlXo>zGqX>~aY zdgMhK_EojO_a=+ZgLf|Y81q@&OFo_+bnO3nKIF8$7dc4R-UnX4)AYgv(ylF_rAt7# z7;Y;Z+alW$7P(qeBF;)lxH{369 z18{bGCfszm3*ZXiUI%_TU?tqOa9iNE!TkxYKYI0W>|{k^w?6_s{wa_v3OixJ8qXmN zHxecmmz8N5<<-k^#i6vwp`}$i3SAC)VIixu{HlzlOC3cng$@YV9fieJ4p#u~0>`Q) z6@`_>J(14BKvZ@`b(JH4NMQVA>9|AT(kaAiK^Ij!Dpx5X=R1~_R=FIN87puhC)07`9goLPayR+(Sq{IK8c|d>qF9p;MMx?fZhvGZ-4URrUKW#1P*87`jhn+_(9kH zupjq0am6@KHKd)~j5Y?ZNY#s~F z!Z%dP@adL{BJ7F>&2hN03aeZhm6a8hnB~aW*_955))_p{=_sFFTIR?ubUN`-i-lN? zsPQBayE_;Z7neF#&Rfa|?Kbm*^5p@TwOU$PMU}%|SYC|ymkeAM;w^Uc{Y;lrQHrle zLyE)Zg_Wf)hv2ovG`p<2s)YX3Y7A+WRoFA5)WqcC^EDYYS}DWX6~)zMBF%+`%N>yQ zm*9ETE@!nX-La&4S+4jj2e4NLXI2T!GDl@jrK1YtkT!(r(=mF^uBcpI=mOQR#N?GO z_hTYWa%5w+3$^y(?1~j4vxS<4v}x6qm0*=qSyAM`rKv#%TwV!A=3?w}C=O{T8%Djd zTnx|r?#&lM&tgPG&W|vqVv~tf-v~(r3k09m$8(OKpnlH0u&~qxv2#&BWsZ4EuH=|M zD8$DguOb)nARF?EHb~{8(EhG?>T+If&T~{QFD-{Y6zZa2!`FfWrMHzA=fXD9DypH! znZcM^IMW^Y)=9-G%_c&uB27DA_~-MBK`^^>MMxN#mLmKT|0hG)$q8>a^_?^7{U@QD z(xR(G_Pi-EA|$7}3=K{*vw zu573acRxWL6O>uCu(Z6mVx^`n$jz9q(>0p>+CUvvLm`;nR25a0I$dl-NhX#ox3pM$ zBZ#w_a+sc@Dzn^HR0gi`nkXxj3)LAIfm}O8`eRw97Z#Oi8-g%*VuQp?EdzzQnPD-t zy11~c8m$1l^N14-4Mf^DhQQDtKRAdCa?`R4%TdHiq&QV#9F?3xBAgv0SmsJZuB*^h zT?Iws4Kl%2;Z-x%Z0Q%B`xeCiM zYt?==$)-6QZ^zGBkeNOir;Iw)oN8*NqRgSPxI@b+bd`|aNe|?mCNMT9idDQ2Kc_?V ze8+OM&5RnCqnw5hswtAHxY&Tq6gal#j2cWys}^cEsnq4@J`99fu03lZiYbi59dv&F zB7BM}zmSG(t#TDxt(VvqExJ^@kueq3uKbFn`IUv`%N)MQoQ%|T>!sKUA%*-PRlbAW z?J*SDxfiG9OiE6a_81ei0OgnZ%5_{_4U@(PyU-?baNRXo(#+D9JC?J!SS{q|r{z~U z9Yvf|=a-0{7S@;u{EDJVSH8b$dUQan-w~ifVe)-n-4F4y*PN5VvHM1+BLun#FGD3@JW89t z;KIDj>PS9UJS9#QJ$D2S}W+FBf*ell^13DSF;c z$VYEqhR>0eXy+L{C*01}@+;N)DPP4btuB`%4rywem!rk`{Q3C8hqH9T@~R0dOUox9<%CL%0vI$Wluep2 zX@WH49uycFFj5%_dh&o7mR1-EdT=Pgz+jBlc7)7#m8BIrQLp@#9q-?3PZ5_@QE^qZ zbG9lq`(ar0^NhK;w9-{wShk=Xf}@AI8S@p{V9-Mp$}0u`0Bu_%XJ`b=Z231T=Zd$d4JdoVc5QMkf!4Bi`p+|_9IM~q%*L@M-rZzDgN z@^X%eaxNqrAo{-t^l{ZzT#P9X+DKxE=;w1Bl}kD0K~F4(P!xDDY1M2-4*#4vd_-*S zDlFZXb5>-lV#i6JmS-5DbF5Xri%~j?zaH}mZs}DKTFzrd2%o&&>f&{(rTaXEU*^cGe%PNmccQNyo zqr9KAFMd7gq_+PL{>O%B931!4@eNPyT;;a{ehJ6-Gr9pwaapSye&ag=d{>|oE@YEQ zz^Q|+f4A+0tTIi{a5&Tum7Pq++Bp9clCe8rK}eCc?s)T zxNi83_w9KPzaK7K4Q{|4N5FWv1o#P`hofw*faWu#%+Y}N$KxIsXqo{#N8&Cs{Jj6Z zXEfx2za8+rF|zDdz}w*#Ll3-@o^zI_mBCNA7moVjopatF-vU43%eVm5PCVe)$r#z( zzymHx0uB7UdwmmJJN&%&{O4qx$HLzX`0iBruQzGDR~$A2d)e^wuJDd?v6t5f8o(Gj_>V@!4rPo+x@u^_6I-j+)hJplyC!d4j8cv<$|AgV|mYY3;cv3%Mm6X(0(=Y zyAkCAe0L?n@OJ>#uSOZ+=lw= zxKES_`1O;xzXpFd;MY&1Ec64eYk_XzZvrfM4zHHLPgo1bx+AncFa3lC%1>CU{DfPT zpRiT=2|JXZQ2T!rPr&`k-ws&w0@ggpw-)e5IM!(s;Nx)Hfo}y|+lu=mqygLl*9QMq z!1G^{cq`zKaFm&Mm}23qx5Ewr`@bw{c;_eqZaeUafam>Hr3G9B7md3;1%Ml0k$B$e z*$!6&d^6y>HiZYw#JwBl#rrrVa2=p=0^(D0LQdYFvD4wsi}Ah-@4=k0A8iA8!f|j1 z;V0xB77zT!yDYrp68Rd+gL^K7w%5^q;Wyr8;T@NuZ=jC=p75e}v=jJ^cUgGHW!#&n zKi~-q51_8$H{NC89hVtz!RCM`y!vhE8h+zl7T$50`wsL1JmI=`Q5N`(cUgGH<+8t_ zj)5n<=^*q2|5iZ#ZVTa6??H#a6W;ee+5_&9Gy}f$f$S5E_gHwp<;9QCzkr5tA6yFj zgdK2p_>Ff}c!%W?I4AIgPs7#2Z@i-t+kx`J-3>h91#r#q=KzL%BJTkZp8qLq8hFCF za0lThl zen~s5s2E4ZWZ6Y?%e*dNfJ0P1&42ru{+b_hJ-#c;{+7Xa#aObFkHO9!5? z3vLno-GKW262e3OLf-*A;a70A@Dpm^qd$b7P`^_`c)<@y4?N+;aL>cP74Q%o`%HY3 zPCNgo9KVeBN_gkw0Nj4i6CQ%=fZuq}g!fIB!hHukVI^GHJ>U=cDjfT|cEI<4lKu|B zpWv9U_HWoc9OopwhjP&`xU+?{1%P+KG3{2shkuo07VnZonDFi!Xn4=#u^^m9!QTqF zFBth?{NjC%tIaqkz8AIxxEc2}i08eHNnuC}ycO``aEa&r3{xcT!T=u)IHbSC^B%^o zDBM8+z8&zxQzV}EE!IWj{W0L10Pnz7>L>bOp zYk?=+0JjBx<9!C>T?gKI_!91U&=7XRwZl*N6I=)UJik3}EO7Aa=e9?Q9}n8IO&a}# zYbQv*ex6G_&vmz-Bk?@noscN~JPTg0{5%`}RQY*6ycB1^tQVdWKLp2mHf zXoJ)4Gim97wQ!_w0(@Ed32#fnSukj}06qdoK92&FKD)az%aX(o{}79b#kU;{+OmI&DU;9x@$8y0M% zf+)7JqN6x=vHbg_KyZ9B|NGZ^>#aBc%UV18+;dMqXMgvcyU*PRcsmeE=OS>Dr`V0S zp~P(hJV4?`e4@l12fP^w%}ZN=_e%T`Ck>ON1AH_PDqAFQLoe`$dYJ&<3*?CE3;ZS! z3WK`nw{^b)Rf8KBg2#;k+68Vn=K#k6Dg`(CuJ|O-<5K8r;OM(r z49bA`PM{IsM*HNS0%0|fA8?fjC^xt@fja=Zp+L#tjs!j(=pwkYfR_V#tbp+k zd@GOu+-QFh+9MPO?SnYl2ZYLiINIxj+=!$7IyWH<;wylHQJI0C0uqf+;N&r4Hw8FL z;)W-|e}Df^TYwDpRt8D{asvti8U+*%6e;1+5ot1;tBANuu=u$`h&&rbX*n= z*&NS<(lAA~#!)V^c1TfbEYVk!2wSxvo-`k1g`5lbQ2tp^juc2QLsUrF__DU}rPlLN zAPjVA18Gfwv^bF3KpN zGT|H^wvTNdn``SkTus@V$>p-s6Vmc1kU$34#}?GV!?>(ub~=;ml%B%kWO6g7@ti;_ zJdDXrr)JY^DWD)rnFf0A39`9CQWT0ek290Y3jt-cBsI&wNR44BiU%>dlC2|>Yw+P9 zB|oUx5+gY&*`QxbVsra%%H9qX>29IgVQesN24`HszmF{wwwPpRPG@s$DKk_2SZEuP zkL@%j?1r(WIQOUH?fj2(@pc|mCU58d%0YB*XKA~^1?qtR)ZbKCLqgfHdXM_x`pEj^ z`j+~(`iu3Q^;r$M4FwH?hD{B-8rm8zHgq=Nvc$H-a)kxLQsENe3ZX!_Nw`b6PuL=C z6J8W{3Ojf9?tH%!R{-lBNYg+-5!eYZ0j>qst8g7GNER#+>=Qf|XbL@qQz4E2)JFUJ zlw0(m2g4{B-ymIH$-X4GQ_PEoa|Sl%)66kbbel-lliWsXe*o5Wv?Y|{?}wS` ztI_B*I-Q1zezE#$bc{x4V04D7r>kcy7B5rS@ln$3l*u|#c>t_DHjCn)$;ry(h`s}W zM5yBIZ2NDc3@NY|O`M}-4;mM;rZ`c8=olYwDceBMVMIP&13DP5OyJ|;E$FtNLdJzI zH_=w^w9Z><8Ev0=bDzy|f9~|wBh<*)KAqO=MgDke|3=}xz}JtPW*y_5v+QWB#u46) z{_RL(Q^1hv?Nb81E(M(T*XHW;%TpR1&aO1u#xb@TJ;QuAt-EgwlMuCG%bO5Kx!k}8 z=d$yURzCVtaWC4}=Vay0QSWCl?&m4@_0BByTmAI}am>ms9ck;TQs%RV7R_s&s&l;U zw3kj-OJT3M8t+!6<<>Lr`gpT#AHEx9oWJqdTBjWgdNw@VVyicY`EJDwtwZ-Bg`p27 ze|(_6(Q(}tHszPjn#XOk8^VJFz8c;OP^eVtNG>jzoSK^$*gkdS+;KW%PYb_my=V#<4HEy z5X@3~jp0l5l6kzWVb0E(EN&K+CmK3bcu0!Im?;^L|4LHC$uQkh zD#Ho?lnlGTKoa$Y!(Yj8R7z3?n}g)o9M8?+=@2K$_y5~v04xcyI)LcKz%u|_kI;r8H3G$P{p zJdgcaC#-%?jOo>-XNE@$ca+u>I_%BbL+^I8uIiW3C+&(~8M|qXU8K7H^O_q>ufQ>; z7qlm+R0e(8y5sFMpP+47oJW<9rnGDBKVFnrZP;vGaQE?L%Z7^w2tNvQswT0IJ=f_Ng>)j##O9SJRHOrClt?e0(&tR1~I)2ERz`;0r=b z4_#R!(fAXQQ7FK;QgH$SPr}%kz4RIc+)N^)Jg$>S#&ft-t^}b)g6`fQmQdm_5SE$3 zq<$0BnaAZiv6xOtbSjI@!Msu7%t@{o1EUF?1x~WD5g`0YY=DCQt(4Pcz-{EolsffM zI%ZeHy2i5fedFB@sXfHZQ8BDY2AE#K_t9_gQx+Z~@b=xSaj88ntaot&=AK(uY^#uI zu;-TbO~I9F;+B$d>vgpwh$h|Vf6Y5KT45_>Mha6p zK~f;ghrqV@3^q+4(?y|_b=1c)e+0A|ALU00<0Vq1H;CWf_)92Th&9U%)%Q+&H~dj! z^0gs&-JI@S`&7FNuAJ+MpJz#R4IMGQahi_JtfGqy1=5Z(K|NDd$duxnh`ljj%xU&|jt-tQDdb-E$rX=Qe6WvB_ zo^MQdkIMXQ4G-Esd|Bp^S$T9W<;Yzkc@rgZk1}iKkFRW)hgx=(%lW#P&rV~UEo23F z-|!vnRl`iD)jnRSu*#uzm5*N;t#8Q91^QJh7-xrNWj~oo=l+no?e%(}inJz;iVa%6 zLya~}(!P4!#TkGDpVjJ9dx)6}lsd3Eko38Q9G{?l^2GtF_t z#ad52clFPy`avJlJX>z5Jh051^CZ=NelY^$sa>O-_u6{#b+CsTU-Rfe^bhHa+a}>s_G-HZMmOEl_LpnXH+`#lxNhCTt^2vZerfUR zJD+RXIInZ@CYyH=>WZ2V9azk9q}HyUx^W-9xd(Q(Pu{jsCv(`B$H!FnM8Dk=@pMWzrjzaf7gI80LXMM1@SVV-2OyG0cr34Jl3n zCPDBW7lk>Kmz>FgkERkS@TD~+Gb1gJ7Kr(yxJ0tIGzvwEgOM{6(o$Fycw>~#5YZY4P&V1j|*lP-DPSO+3d#;7Er{tbdaCZ%6OHTFEUhGQ;WZbliPe@pwma)tISf zGv3|{YP=CM+A1Nn@kp%kn0Iw*206(GJKeTATSRuPef4OHZMaJ7@;0Tcs?A+-YT@;2 zO9hS3bj~m3jH8~fyAkkYtg)}3_nA@mx3oO=+!}Sx;^Mgr`Kunbo{LH4>pu5!yI^u+ zxx$b|FJ~R5Ir(JV+`ICJb)n2(9AS{(xDQdn(y@r&g}fiXF$t2Sr`~iGEF} zK61o~<&_IAMQvO;X`XBJFFjQWOp5X;EN6(;=!Bx556nvD)1I@>-2QNN;U3qU6%LuH zoVcmQtAAImj&GST78iW(m-h{GZaH_5?|W=2zB+Orxun&2O;4;x_o^FG*en9r%*ULi zC<3r!g~2LFdMyh(^Z}gv?n*69jG1hV1@NhYUV4uQ|m%+~O zLaXd-G5;6##@6m<2YWH4SN@(K2|F1y#y3bI0$1`pBeRsv?(YQ6^*iU9GNZn%sfbtI zFw-no->UobGb7@7&%PPUPO)P*F2XJc#4>MWWp-J}wNkkULS9~~`ZD=+dpz+@*;-$o zS;sq%^3B&S9CUSN#B8dZaH(yy7yn^ivEC=UUAu!`nXVuHeE#(f`SlYTe76@RjK-GHnNXw!6Ve{_~)0%Rv-nwj++;r#0)A%skSt{EMVT_yX~>s_pY3*z(r#A!X}R3309D3dI z5;VUzUzK^WC*b3QFvI?LYf40#s?2*m5@sTEr9xAOHP^sX*d{1EQBc?ldcI+B zIH04m3hY zhm-<36|w#=L3BQ_^epSRer*goC;A5nmjOH%^l2`lxZl$zp?eZ-nXEX#8#odlb{P4r zKW=DNZWp_5<`>79)NS>@EjoHT_+io%oMK}5aPKpn{KNycl&X3qv)W$IA*NTW73Ps& z&v{VKphr%)cYV?DJmsDOdF_2CBE3|@N7;5&zctZN;zg)r>zdhbru|m?>yj_gO6k`} z2bvVwYo6YpUJ$5}`z|x%B}1TN^i^Z^9EQV|NAoq$@+~4flh@o@_x{zi;@M7HOzymy zZ*pPRhuD=#`}S=pee$59Y-C5oLyM%xg`e%t)NNk!+q}HxOH!?@ih5(HYWXEL&aa)S zo`FR&9cv`J$&cFq6J;|+Ail=?GIJG-AkI8zP#@2{wH$;S6;Q^Gd|w3x)k6v zw?vhESjZ=+V0?nYz%Y@g@$v5gyI&&#;ry##>JT*l@K#AlUd}4H|1r zKeay7KX7E4JLUI9xw>4Tg_@9LJnqu|_#Wdsf!xmz_-~TBW;={({zU8ztn0PbJ>(U| z=>A3C``Xv8`5zx_wdba6E@8P66*tZcB86udSIoBD^{_a)w0QTCF-@jVRTn()%kZ3X zasFk4GlvRdeX`$Ah;om6H2h5p_lik*<Nd_30K@*G$5+!QXsAxdJfFzI!Q3*}}6BGq)5r;^X%M2Hlo0Acy zhoQ7pi`7>A(ALjOOIxv#Voj8ipopjx@slcTu|3m7jF(EH==|?md!IA=B$I2~=jrpq zGnsw%S$plZ-nI7Hmvi=>@*6hUQf)Sy1D|-@W~;?F{VU?XUH|deZMH$fcMP&U*YEW+ zYwc5BKXZnE{*s)9i|@Q`@h#uU`SvY$+;L|h=UcbtEDqk0Gyjg9l53~ueCN)&w_Y@G zVE(>M5o_K7V_9mY=Tw`t;+Rzv!QP<^Ah9f7<+S`>Ae2 zQ=ivnn_|zf`7b+iy^(jsmU89*`yiX`EGT@V$Q?NhX%Esx_@)%4Q*5?$y23x>+qNI~ z0dD9mJ8ly(2#MDGmY06Jtr2${=i6=B#B!V6R+dI|^50!{+nQ{o>+CiiqU2QCZ3~Ti z%6BvJiEIj3ZUIJ@;|8fS z2H)q9z=?lFxO0&pvu*B&{57~wbp0ww*y_#MT>7;a3=@}OTv_GQr&@WB31YQ^FLjKVf;Cl z*gd+Vzuk8ID=+M?K|23ryUqU|)ybQ_2ccu1;mL9GPWccD)LyPPlt<)i}vLT^X>a<+B za5bPh$EROsCs~|YsfTl1K7EprlR2BS(tY|!k!7h7c!ZjrmDTCmC;Fdkc3ZT(HBy>c zonEnnB0z(;6@P77mc4G7%BZAdp1IbckYa&obq~u9@(U6gz;jya#uX3=As*&+53pEYI$l`xpuC z3pM9H=2~O-6#T&#^?p8gD`onROtO2bZT{glx2+*4Pye}xeV(eG2Hv$KqE`O-+{i%E z>v~xHmCiXOqXu4&-3%t7)_7nrsyx7rrcw0Ng_3nY}w)0NR4Od4*|nQJp({8ZIN@ zBW>|`l>hqt6H?sZ>>pQR_xRzNYk(Phk2`z}wM4^*LEFH8ow)6bhL0k{N7DEgqD*`{ zF$0vc(6d~KX`j^^+k&~|YhDzAp}gR_`~-z5kfDGRRD4G-hrem%CkkHIA3cG#2y+{b zFTaKV-<|OPwarB;`Yn}JiFTlQUB9P3T3hK1wWj$AMEAs&Xjq57u&6$EknlF|P|`k_ zE$>L&^pR+l@r91~$aq#WD@x@;yKWss5NEr$a$fi-oJsO zfAH*(o|0OJ>oh7cFqk!-s}ALnL+qIs*}(aJ8uy?|*pP&IW}pxzT#JlKpMEK%)gbJC zTvBwzKcD^){)Sd%Iv1yCmxY$|w=Ei_S}H1i{w-`9V(6#wgioJ306e1P5ush#T1%+S zxp-i4MSMH))IUM>1$7PVR2kqqFT!ZN8%i@tKf+DkSnSZ!n^Lsh zWDIBZ$cm`9E>eEN2L=$zrl0AOeooDp)a2&iYIJWABp}qv?~+0 za;UW+Boc>VGw_$ghI@FCtx}YLJh`&em+&PjRg#;HB4r2+=)o?EmfC%iv?ezn>?HMG zNiFL@siD63+f&0|tbU@(KMt}WCr8q5VOq+`_Ep8-);8qk1D83oXBF(#Hyszvg2r#9 znbaoxp-o<<=L6^@{@qVYFW561&ix8Hph)^`GmOGNX!r>>7=4y$Q!^Rhv z0A}o}%q6GBmwL2)Rr(~%qQmjP?Hs)>)&@0Q7>)-&rK`v)&tebBI|uS=y9GyTo6sCN z#9!NoqJyH22|f&!zb{&+QFJgBofmN}*LI@-Q==?p%rTfLeF*eNub`DX$HO&NFHMhU zJqJNk>&DXrXYr+9)DI8HJgxXdVf|uxaY19K&9OvYgxW?J1+~TseexIN5Du!D{hJ!t z+HX37u_$xN$?-*=N_`C6BmBi|_X8M0IFSmT6j}io7I_fW`xVrMznB-Xt$6KTR7_5U;4v_RWio zo2Xw0Ye;xpHtnEtr%=7qiA{5*P4DDZ?t*QWhVJIh=yYk`A#C1%7&dBWUR0@=mjnQd z!DQa3;~L*I{g;?&VZ36LZf{{xT(Pjrh<*Zp%&na8?|29QZcXOj4ITWu75*Kss?_UX zr#Wl|ZK_ABN4s$y-ybP=7QV7f_426WRJ5g;6+XnhuhA3N)CI0#7##5O6u9ntfy}7m zbRXJNRn$@J)AwV_2=(BEsjsn)Q$34G$G2vo1!R`NYo!;zMVGV4PQtn1$M6UAhb?M< z=tAU_0YA|lMs97`tvU^hSoXq(f_MI#-*WS^M-;Pd#<~@7il`#@_r#;j2qe zt|fnsjyo03bZBYXOMEriF2^0{)*J=zHC3B!W~9<)&Wx6gC~RDYwn4cS`teV>IYeiU z2>ta)WYUSI0-yd@k*6JMItKzpyv`!X=iBW)k)odtkRx8b_Kx8;&np@PhV5?B(MAzf zhgo2(3k*!H^HogOH#r4dWEx zg0{d(qDfrsn-Qm&VoH!Jx#fiI^o&voN#fAaKf??p+6sq6rT`9efkX|Uu)#an`loMkHmu* z;bH1GtwJxP(S#WE>je2844sE!mRwfPvk|nv3hg>q)SHPBZV+=*r~U$Z5?JPXo~zV) z!gi0@+Zye)yZO5RDrLUDA!5weH_4QouhR{huRjJ6JDIOXkMVqcUMKVQ*&mtnb*^#- z#0ICWnOM5VcT2m73Up1Gc~wwVTl<^Z|{ttSyxqv1mc@D2PIBH6l{!iOlZg^z+B z(|!EK#E331vBF51tVkKLw?1!P`)9GpcDwqib~aw(2XJhKjrP;8l=4aThmj)l;NY z`sFN8MKXbiRdtF*ARhgSsW(l-E?M8jYBA_xGn2+1BX4d=A(4 zxX%@;^F+c2fT5r_rpvst66G8I1JDHX=MYM47nFxVAKK-?0t*tPfMLzbc8f6^aZlJ# zF`lrdYC~dUEXE}4mc2&U?Fbocv0*pL)jcTS)6f4niQVXab*crX_;yhNEtWt*)D;iy zZV5HUTio@fuCj35YNs}+lHhO{{zRlov!u?A=h%~)fPz?C5#x!O3I7i@=Or6YXyb1L zv-msOum$+W6vpSzz6LX?b->0jdl7^}h(!p3ANp^`ZTLv4Z8&T?mn3PyP}k3a>o67? z_hP-#SZLg%8K62|8P5uUf+FVQ$Ph6_#uwN6NO=p*?~(+Vud&F2@qD)d=5vG<5nwiO38(&X z6O8f>gHuP~Je&Z>PjN@TlH+f-ejmC~Q$W}RGS)0cS%^&YR>{LT1w6JUc<3K|NdUJs zGr$FP?M#c+a0ks7Fg|=-wP#sUdyZy%9&B&VXO!v|a&_NDbpkb5H4&KCBjn4#Jl0Pa z`X8DlZgdq7j*zWTZ)?SR)Xns)nRx9fr}@q-YEUEa?oeFc>p{_exFmrt*T z(KT-~z#)H%Iu3>E9DxC!xmVd}nfyr884!z8V0V)7peFH9NJA@V&2Wb!V61tM7J6IU z;SEsnF|js|f%g|p>Do35&hd8&ce}$h>5@U%9i}II(egtgM1zA(pS8_qtKgCK+F#pj zNZ2*+VRQzApoas#)!;o$bei`NIUPN*#SLC8@gAZZF*osVbq2kLG~Xb%Q)&K;o3g`j zhiN4y(Obm+K=)cx+~IqXFt|ay&bj@NN^tjMcZcu81x6T(ZmWMObW7Er_?4mC0gG-V z0jk=M%&Sek_ETol6`@vpaI_F3cwr|B1v4L`9_j^PU;{oS^;_vO^i{v;B>U;_jW`5D z7iPP|L(su#+=AdHu|^8~@Ds#DZn9Y@ZTLw3aq^K;1SZY<415HFnUCJQ+qBX2)D3y5 zzW71S`<$7oMT#H19sUrA;K7@BC+Fb7+p9)S9p~V=Tdl}R9uU;Ql^E|LJ~WoySfj>6vWcQe`{$OU$(@2eV7V6oA?pOI zy8KGZ03@{1Y1djRg98u+E40D!X+xX(X^01!GhUHHk#SF8psYf(SHyjII%5O>b|NzD z^ti=h=j47$PUK`ZDnw97*Iz+Hgbd;`Lw^pzBDyh8Tsrk9FtUnn94RjQ z>GwTPmt(|be|=dkUD9(Guo|HIchF^-xOD07K1~Yva7q8P-3|B6km-i|W}2xRjr)nG zdt?S>XwIHHU3(YP*YOm&w9pikfeEp^`%VL2a0MZ%SsP6;Ox!%!z**vEiCTs(L^cU; zQqzUV9%p8w2D%X0q`65=7b1I2o!rY{$3z1FF6h$_u z=|W_a!Axqp5ZOb`Y*5pM$j&yifeBr3_V}Br(+$~h4J4x)yT_zeFk+|{p2B(TJ$J68 zyoE*{JJ-YaP+p#q$G-QYyD9Go+>pz%Yi_-Z@-`cJU+|glB&l4U}%AKi4QR;8&ke40!FYJX#99#BD3k zzxD&v89N6&#k^yVb9!RZ5gZ^M+sD#zUmH!&O{42PNKY3_^znn3=!+bLM|wCqUCcPt z+x&RZzaHX%rUW#IfcN4hCw|JDK~I_M%xTRDSR%5HMAH{TTy5AIWYD}2WsaO<<7E78 z&anIU=ZlQUI%-^aKroDao`%(d(f;)(+iY^gJ3a*{1c&=K5h8b(h}iT;F(dQoFZ_iN z$*dH7tNh#YWIb6_Ih+ff(cAjBe+C4Uxz?ngS`J!kj;)z7NgR`O}yS%y=Ja_9Pu|3r}^T zOB9~^6Gfs7mgr0&ksf>8iu|4@6y&|l$zb<3OUwrbK^ttwh?>wAW9m+z_F>f(^C>KD z=yoq+#T%pTW=u#7OcF@(UbS?jv;Fp%5O9kCSDqW*OmlDhs^}=vzAY7;t zKB^F=Cm1?FSEkY}Q|K^_x*Bgh7|Nu8cBo2wjY$h&6FBae33%PZ*8tM)@$6^fS$~Ft zQ_H&v{RvMd6L>zP(lsh{gr}qNhSE+Q%6*|I`=_H;xD&e&XA>k<* z5}vKY6h+Qe=z!;!c%jswB|P`4v|k-El(yno)*jEa&i&J2;<-p=y~SiL{FBK9o>!@K z%N07pQ_>NhXR35Qg^qAyI*C*3P*s10kZ_U=38(!kUHlzGcb>k#g?9-ITEgjZmG*6u z){4^@;M8pc9&7^SQ+cj7c?y7-OaPRp(p4#R1dya7fNUz=#R?rcoTMX%J9N5&PliHA zKr)>K@|P;%`v(&M5s;E00l7}4dtRZVbr8G+VbBtgvsBvenzU9R^8n;L_wW+b*j+pv zV!~-xIgd0s3pklf!1?ZJiuz84j^LDZ1m~|*x+8x~XisoTI)XEz($y+-1Siu;IB!-7 zzo!rqoRT5IIYyFKZ=T4Qj>1{)4E1u3?;CY6L=PfGhO(tvMpG+q39IMj#6*|IG(h;76Rk|XD zj_{OpglFs&Mg44rj__nUiD#Wk`0-nY?x9w~Q!*qxH>-3F3LWtLaVqeXw1npZmG)ti z){19qX^;BrP%9yjMu1jT69R6P`IRPf1n>kM3Gw$K3Ian+IxOIxCD`$Js$QjPc{8Ct z;VEk+JRef&8WlRilj$U$3su5L6+*&OG9*09RJvsf9TrWmrirbjrNP`#mG&Bw){3Xz z8$8_!JYULEa5{!%R-?reo|2C6{E zEM)p~F!qmzw_wnYNe_+h^{s!VC11Q^p!@WnRPtPB8ND?VejG#%OzKJLhjDvG@U*oP zvQa*GS!lJx9vtbXiyic*DT`X+@X!QT@HD=cS?!AY(yOA;C&5I2@JCdIlNJs{-82jI z$(5izw_|GI@#^lu>9rqt6lh$gx_C%VVs9t0wG%I_M z$0U{REQKx|jXojXP?hYf*AwC)mhHn+0i$XsShlFX@`g!kg=f79&wCx6#DM2Y6P`y@ zo_Cu(1w4X`;R&d8(-k^unv#xWoTAcQpwJOKl8)e^S8;j$gWk);e)j1lMu1( zWQFw5UIQYl+RM3#6v^>g7phM;Rewz7yT;@zR2O8d`U;h9rb0)mOFB}$LZut2(2?qr zj#NKSrE@BDQ2jm3{v|Cr?>8qYs{iS=34{NPnX zdx}J72|{k+?+#W#s#VAkZZ$j@m!#DhsQX!!c7sW4^`$4-mwJlllmwpFs@yL!xeGic z9rZZps&wfl9plMljHg|tIM9bB%a$;!tW`Bgr{IgP8U(h z{0bTHEW{&xqi(|UCY5%aNo&P(bFc6$N#GfGE0%q~F@XTJJV{4*zN6ATZ_+WIOvZTr zN~PLlQVIW*+z8KzO6OPT2v4Syc;2iMPEZI5Pr;Dz9IKLLD`dd4iQZ+9brYTgRodnT zLt`tR{tkH3zO2sLdsza{Uk+06+-Pza{we7Q&vh!@Jd=*`WHQF{7L}^Vq!M^aZiMGp zl`dPMBRrW-;^|QdKi+L9kM`~kzl`VJf|%RISL*12a=BZgHu(y)<0O(ehrVtB`t|~%%!OP zx=CtPy9{c#>kqon|Hg#=D)WUVbD_T=6Z)%kWeOeXFX>o+l`co2BmE^E>#x$a?y~BS z=kAi0^&g<>Z<1Q|AJb9)?VS&WQqvrAJ^L25?JYE!3;mg_iuJd&=Q4$k{F&+C&(w@O zD#PIlA^iE%&!L!PO2f*ds_%dHiUEREvpmzG|CCfAA!g| zo=O)q>A1l&88`SMmFilP3a{iqaVC?BpP~|;qYy&zw$GrrteO;WQoaAnmy;`=ElgU1 zyF9+xkW_sZ*JIk#(w?`NoQ3jCR#i>PTiWw-g$~kQL()oGl6Jf*?G%&NY9=S7wT{QS zSg*FZoYMd7s=Kt*8%T)t878aZ#~Sad=r$^Jv^pW_NY-DebXycUG8fZHbFEhimnnoa zuayi5sGC)~YZW@oRnGbpKuKDfPK{D&N0_u$pjz=Bk1%Lg{_ITg7z-}ns1Utgmw<>u zkEEm852|!em~`wQOve7aO{H3GQVD-%G5|!1uTu#t6+(E(d6=>qRZ}#ZrP7{n(pnYQ zp?LS}%c41FB-D?qpzeFgs=lNn_1{tHerD3K`b;LqLn_r8lS=g8k{gAwh)Oq8p(8(H zIti1-KQ2}X$&VyMf@qA&Fhilkfaw45=-QyA^%0j!``+&jm920d0-ToTZNIZ69}i^& zCq*Ya@d9PF$F~DRSG*61WeKd2{P8{%;DZOW*Zem38Z^mjyZ(}D%h_h-j3X2ASmYs< zYK%$6!d4k(z!mZE${b>r%%fJ)Q z{pZXsczu@cHF@HtEAhV7?TPnS);3RZC~qUQd*A9^&FhgXmv|k)p9j)N_!&&vqv2Y5 zr=6EDkLXAClHXzp^9Wvna$%VhW*ZtUKeB_CJfjO;jKU>S;$w~_9}CJcc%2EabYzdu z$*|c1gU7#?j<5cFA)Xr!{T9XI+c>`&ow)9}u4&1X%!_sIS*y?xR%S}$3@-&%;Eyb?7Mq={Fewh;;;s=3`OQFKGTo^wY9N^ED!@N>xKSE49^ zIy!qLYDBdI;~pGCz^^?Kv3&1lyGZw7KZv%SOoY|m;lfN=Q4z}9uI8&yt+mhy?~!=q8+^i_o>u*d7Pri|Te|e8 zNO&)nP)Rrhh9mzj&v^|d@ng#QntUQh&XnN;qzi2VqXdNPSt}%BqZNvD zk6Iylw`hgrh_V&(o3W#(T3R7{>vkeSE1U?a_~}Z~gw9&wVoaDtD?IxKn`ab0lkk~_ z&n@^Y#Ah`=8}a!uK0m|f*ZBM%pI7lYh|gc}Ifl=d_@vLWd5rgQr)!6?>jqn7u_Nj_ z?RH0GL9T7K0VZ2WH#_-b-!e!yw>O~CV8~GT@mj#9EO6%jUjX_xEF+9xeYHn4vl*E!?)^u(T)5j_Cr(H zw*Y%a(+E7++m9DIaYl@GK-=dYoPv!?-Xnm2q9-yjTfeZVgNte9&3NSF`3M>E+XQ6cE0ld>jKjkx(xQ!B@yq2sCTn5W_V^7*}ytxI7=BbB<5ei-i;ilkvoWy zL-`{S?`FhyL>@$&cvFrte!xj6j$m4a_9n%nl;Gg%anXt^;2m3Z|6OV@a*6}fLXA$i z#`qQBt+>W8keb2ByD3>D@@|rjam!Vp4Q`5h?`OxjzsHVozu_22^vE$bAn3p=Hp__9 zide5thg-rKj!?gU2@;#JBP`Th0xM;95cxCqWq>chwqCyJW>u_5AJ zulPis=;nOUimB~Kk7;7`c#<)CEZ?LTEm!>nC%!!elZF~S@RO`%s6+9Sf6}$_HobRE zQa@QE{bcN9&<58;y*2D7H9hu|8pBVJ=!u_fC%A>Hte39BTRxmuCtPJbJYjvPd8YQ7 zhS?i=&}KSm5#R8aBJvaXOVLPt!(WPuD7qX%bSd)b(>isV=Y9bNrQ3*ja(BXC);7~X zt0yjlvHpILM#2vAny?3d4af1-b#$D>d}O(GKJsyo<|DsjugT;WGcr+CBuW*o#g@=D zw!nP%Z}+Ti%feVI=%V$&TdQ-Uz7b8gt!=}JjJDwUlvjacCd1h%gDxi$9?A*4#b}|m9n^qZ(5@Y?BLDeC1j8Rk$ z9I_^0b;!9OH61eSMCoKc$v321{!a-?Y-h@_3-mmSqC^+|X`Rauu`!Ag=^lA0nOBUW z2yyACykF_svV@~zE<@fB-A+UpMUBD*kD_*r?<{=fKa=o7WBeoh@pq~}mSAb5C;qq+ zMtqLea{kTpnM>eS^0lElu&4RVLRz3n%x88XI})aw%BaUDhUORn4Q?u-5w}ZuU8n$u zD)4CB*@T!!{Ue6<11RRvFdswvbeb`zQ_Qnt#_R%^G-GZ=75Y_6*vpP{g*MD&Fmcx0 z0oNDx_TXx*9&_iWrN+$^8|9}2Z=?K_rrAcmgYzB1YboE+RBGfqIo}z)obsJb7a92; z&i9~6Am7t8#K=e61OJ?02Ia%g3*GLT9S~N(e-RK;0&rk4u8ja3u7w^+{he+|fB$tc zN^ED!@J@o3W(|}8`_2Y*R(~gA!{0@^NB&MeB>bH$D*c`J>BvR*Ch z%-_fTFEp}t*R&oKI@%Iy9wR(_SEM9&l(E0t<8@Y6xd-FGy1-qJ(Fqk{aVp7ow9&j- z1?Tno_<%e7!4h9dZa#*fyvXncy0CDaac`(emG299CCS@N;;vZ;@0QCD3q6T7h=Ejt ze(Yk>1xH0r$sL75@$SZqSttvkUAYh>Q3spMvl7wit!cgu-}4y!tE*Y(1?4ld$1inVo{D(l!s2S*b^zv z#-U-gh)s%CPy9Tg6%W-~F<KJd< z0e2c#y9uLWERgQi&o=YL_%+m=V}^U$oQ1;^g}-}sIvVJxoXU!Z>1~!ik6CTA{tfy_ zNIfDR)P~Y^_|CWp>$EUX-E5Ydf$GcUH-RCZLUCD@Ye0l~} zLlTT0=$?_VKl`C%L^(eF44=wqipdDz3wzV?q@&GjbG}l3u{obz+f0vJO$V?}Y26;o zD_6>MgT)@;Xqc9Fv994B9HkUoJ-Q#?yLr7j1^%9aJ^;($y8U(lq;yGtrfaiH3LuP& zZNkQKax_eN{-M}H>eF)()wS)EfjM&}_GHpo-{PDw?NInVcg=%njnytZ=@Yl6V{t{S zu1{~6Os^l=v@&O?t>3co@!aTw%~-?Cpve~2CpM7t zc5iG|CL_BC^OtXT_zL}=Vjw~xse$If6T30c%AfYdK+A3u1`5D^RRfW$h&BkE z954`N$OgC-or$)cGCDNC2-9_GpngdWMBF+WD78BS5nxFSM7uD1X`sAcsRr7F^$Zw@ z+I`Oq^ap6wje!n7(ia0Ayj2+JZM@Ffse#h121>z*qVEQxUyA71f|463t&awx?VpN) ze$j&vx9*p!fj(Z!2BJXRGXv$Kif#;4_;6ngGWk8r%y5 zW%SlS1z!NA4D@Pz?hNL{^$-r)B{uQd{#AOF4kYr z*82TBO}X%)zL>K5Tf&sL;SJtSP1)aS$_Fp~S4=si7pCmjTT@1PG*Iw`{w=s@Y-x^Z zHQMB=w7y^uOx6zzMD&OvO1-!^qu#RXq4iT{;P{=NJPH{f!@kxHPAmtc3~hI8gJdLuEM zpgppRIcD|19Cu-TJzAfdMdKKzOZ`6Z*G6dOP&$~!5#2^@ga^$MywZj=bwL>&YRl*Gh z>UUoV%QFa**4FbFE|vhj6>q*W=BuhDnwUtiD#`nrL>nBw#V^mRX1LI3y;27Pc3K%aklx1bmQu2foA5pWFlJ zkDb~r^t->`cj$MVr$CPdST)*w10?zkJ-yS=aR{6w>YLH#WPKon=``9j8qf=WpV>u- zN7W4aj~-V+k7I=dJ#JPHKwp8w4!Q~P6`T4Fx)C8PdwA?YN zcZg@{51-o}@#aK)uQd>F7vFmV{m`u{^flEC{V6?w{+l7)LVs{$-=Tl?90mGpEA)e4 zmOevY+B@h^(Q_b))!7y3pERKFxwAj@n2PvG^BHjjk8Z~^8_*)UarU{kZ(|`EO6+%g&#w z*yqk;?1%LL`=4T)c{kp^^t*k>zWOW$`!lTA{{k>{gMH_r{mR~fK1{DWvpwiaeDNEg z_dLE_{Sy`OUt>NDZ^xXpTW`zdRGN|A^&=JWzszRDF&*d@@ozxW4dSCheMdYG zdYXvmSrK0VYjlHn=Z`fVX}z71o~z$|T6@sN`WG+ZPMr#{>SEvO4}ryF=RCskvO<3hNc0(c8~Ul<`TK?X*O-}i3OyaC(8EU_usVaj?)xg}AJ1ga@kpUt(C33g zH~wC{vhSdehMp$qBdwsXfI<2UI=!aZJJ2uI*JERgYn3t1a!PVzpGGVK=0b5 zLLaDN=r8F3^vBRcbOZhFyZa9P4y-|&&|hkW{u?0CXXxogu--v`i9Qg*bP7Ek`_Myw zM~D#XzZ+H15GLrOte{7LL7zeA{UIIR-0Eu4y+GfL z>3OH1(`ghP{Jpx1(WL?X(5MQ1%?yTqbPu5K!RYef@+8n7!SiGw*~MdSe!BXqhoC27 zJm0*`3jH9MrO(jwMw?ziKUyCKVUj^lqf5MVaBLD@l}!8nr9e&FooxI33oCF3ui)c% z?T6b=z0>B2-C^^5f=?Shc3k(vXD~j)<%ejDy(#p~@9VdQnq8IADY;JlPt(9}O0Emr zIphOxyLO$kaR174v8#PVXsm738T0u~KKw{sYJFg^+p{JQ!#Mmja={na&IMWNkXF!U z;Aimo<(HCN{Q55cW*Pt3%x?azEc&H$@elR$&kN(XHRL%DXJ~KWga_WfYwX9%PrSML zF$m?}-67(b2e&xp!AURt(k~d!sf7eKP73|jHt&Rp<{P*tM;ZYLd(k!_6&p)s z!<32rG&`uIyX55vXLKwr%g{*!(Qu-^43y%-EpQMHSVyBf!A^gD0y(@h_73i<)J~$N ztrtE%`XUyFv>RGKM7+h$pnz46d3|E%ko1$IOMF%fKyM*_ysX8_i?} zq_j2NNGBgv2@2xabdAb_0t!y+K^B-;2-FNR!9HU)MGt zKoy<)(?rxCeX#}W+vHDQ5p80#i@W9@$ne*(bw*+mLH_i~Is%YNus>0@7XBQUk?<%^ zx6&;fgL+CNJccuDcy&gcI1yV9euVJpT9|Shn1Tk`k+2W6G5RrD0PYTBEfF4=pdmj( zJBI_(j<`s;ggMiOyoPWQ(u8j$Tt+-{iAS3~aDeu6jp$e)5~Tu?_&5q`%z<51nt}+D z6cqe-p$MHrBK+2p##=VAeUtNfPh~iV z)3li}5+28C+9(+b&*XFtrvsd(?UHCZoX+F4jnny@9?I#FoF2pJQJkK^=`oyM$mwyM z-oWW1q=iP*o+9BVksCXNB>g_-`NEcHT0kZmK7o6D%!U612z+|MIFO-bQd`66Hvs6tVKx*Au-W4 zgy}F1=_hEgbj}G~HJzey8ZN=xm$|Lc&1jf>myU*^wpKR@hBMlldkHSk)|_?`_~l>f zf0`GXjj2`HE2epKDzOXZ0DkV!+e{mQv+>5c_o%z(F^FJkQW#m=s*A7@o*L8RHK@pY zfLfCGe%h3dU^4{2VcOu`OtMG3k2iRqM8eioPCyQi@+}IF)tSB>81UNY$6=qlg5hsE z57?(+(?7QQ1G0W#!g!rTEpDLT%JIp&3)N${4gCP0-ar$B@>bd$lD$}{Jv4E?&Dv)A zG3-uME+dtnVU@8}C>s|$@tue7z4#u3@5Anz)2O@}Q~DvpUrVBM*dQ2w5+WGzlcDIK zD3oa4ZD?Wcb$daD-U%S0ccQq0Yg_Dj{K4;siX1S5huX>KX!9!{B*PJeaF%&tBn36& zvBE4&7*pXhG;gigN{!zhZQ$lhlo8$n+;kOLr9@;?iKa0o1BR3LGdGQh8X~cI#`$)j zQK0LhcB`K>mMtVS2J-YniTZdB?~MFU7YgJ{Y>#X~OvmR+=zJL8=i&Qle4Fw7b8t27 zVT=#NPOVRX_!eo2ZPF5T(h~deU55Kd$T??{bGEo^7PPkw%?Z(-Z; z_G0hjbmG{$Ge8R41h(2dn)k_uFvTzf&og06xx3rDt%30ocv9fvV}sa4@1Y`bmkP$n z+EIfQgXr_^@KI)zf}H;PRF3Z%gTaUELK1^otD97jMRIUlIl%5?*S&Ie-lR@u2!n@B-wbt7VFXNH>IQkTx70W1Rc>R)EPiBORk7 zSjg8#biAj9mowcXM0Y;ZVa#wQ5`s211WAkm&3YJ9I(ixdVbUPF1ts-eusU_OI=~bX z3aJQOoH`WSZ|38?9xcqWd22O=uhBYO@^{B$cv{XvQwC(P#WrvTqzfT|Ol})@skL#7 z_SQAsDnY`LhDr_OG7)cM(`3vTp^jRR_($+d;G*Fi#kmd`sG$iRZ90v%?Q;r^=_ofm zF(UIG6~=?pl#$c?8gp~gX>P%ra^y5QgD?ks?g_LTSne!*PD1(-eDB5g<@ml2-)e_m zseKYH9f>Vt^c%6-zS*%0oQWT21tISHvBlp8q6X5(<}-f0xYUVd2>g_B4&^qK=3>r< zuRL*;FTPwhThWAuQjg8H=E{cBOqFQ&?4r-z*Dm@zwBm$))u2l4 z4INDwTbOAQtzFexxNqgzIBx`qugbvC!eeW~p$HFM@ba6jsf~sE+*9^dpu|A4gsej0 ze+tCb`j1`gTsbuS#j0%0UY$}o4ZpyR<$wVpoO_ias?sCiVG4-yI4?}Ak9b?BS8CW~ z+7eyZqRn{!t=1OWk)8fGs(d8b9_9>x) zAHmq=jLdWZL?Boy5rndYpyq9jjG>bV;ph&VPHkwfe~>L>js76Y3XvcW4#tRjJ-&F* zrT+rXVf~3|mKTRkea@>G+9%j_eFC3WUzJ{rNJGbsj!h4Gs_pTmPHa;jKg4FM;SJp< zw0-elYn49WREQaLL1uYeB2FiuVX{p~;xZ*M<-zZ-uSx7Lu1NN4v5{L_@M;e9vFUe1 zdLzxV0(@)Pome=AJo)VI>I6zz^aN5%g|#0=m+8eYQq6XhOyp`j5=ShL+88x9um z(7^&YuK8dw&RNKg6z6I6szvU)NYx1VL7%#w>+>2=O`=cMGu`M@fY{ejpO3Is41Ff4 z`q1yMTJ)hq6`&U#-av;r&;be~=>P?-s3?NN6D-<2a-pi7alY%3p6hoF;hy+ScG0|E zsr_en`uzr>q@#ZHJOKJ#tLoQwW)Jm)o<-2pNA`r8WYL>dOKwB2qWUJKg*|N}&Hek` zwXfd|NUHCL&$hQGet*|#i4SMeJ6i4H!!RsBTjRr5Bfww82Mkd`AOw3>bd*_fMOE)Z~dhyf5mj| zCLBDYwP8Xu4P}|_8u6dh^3SHsl<#x}qFEj@yLalLkNquCpbDsMS z6!}|Sy*~U?_e{qh)?7zt*x%6jncFit^E3DL^`E&1m!HsITLPy<7NX|$-V>3@+4>8} zDcD12+)f0aq1xoUhRHmBLtlN0Htc@ve$_BSd>evlyCMFkk!5-DYjb3zP@P${M?8R_ zAF-~|Cb|kHItwN`3Q`K*iliT3gu|R&5s!9VW&`#7Hf=0U-<6L6JT}cX9ozWEt;2zq z*WlbUuRMQ$?7DG4B$$tpf%cAgnApG289bGK`nt{$qO+vwNZ)L&VHOQfz{l&+KG0qw z3xR#zEb*AZIIfuDh3xrJ(d}9}JvqtNxRQdmj-$^Xq^+Z}5y$tNTr#2HtX!s2f`Z|T;cZ$p7~@BNJ|)C+&3N=$#F zveMr;xt7gM{zi$;{OwYj62srVL_jtD?O9x?{>V+Sz`K zK8S+bYIwKlY?o5^_(kv~4sP2J+^(I|iLU_A`AKHgDjw_bn#kZE&lcK zjotd!`wRZ<{-uGXHJ+_N&VR9g&Fq7J&FHOv%{2XMhV-u)k>VNbUo$%MuSNZYf1QV3 z%Ji>hOh;7z+L_ZA|MGY3U&k@0qxkix>R*ddv&Fx@hgTev4$czsYgP5X-M=mXORImK zik$yq|60%o|MK_NzZRJO<(K~Dj}-gazxf3NiLR;A-@^I+^F0#9?QoIok?9x8UOneF7|I3|KHZ$zg|Gjf0ch($Nxux zb??VxWT9S+$EXs+#nw?-IUeKWTKY@AwvG~=x!eC>)xGN$oVh!H~M9~ z-uoL_sB?d7(=srbcKh_g8HxectaM?i#TmE;%M-i#Nzd`W_!r`+NcX1t-2+I-cv3jL;U6K5Az0d~_WKxCP-{unO= z{4?o7fJ=WF1x&-ZXdz%5aumaCv#Xw4fOT(pF8QmRKu3Znps^Q|;ZBww>ac=Q>e8<# z$yx_5#iOYr+vGyWi*o&994A6JzaFx?tL%-vJBYJ{f@M~ZtRypfGW+lE>lqfY< zGo1R@-(n?7c{RfsDb3cKki(DH0cwK;b-ESRXQu(xLqL_z&D;+FvD*fCy4USPI_h{) zVme*#2VMXKuVj=v$4+%FoiuNgmadIjk9DAYW53XSv_65*aatz}5br~~_9w$X*bie37XwEs)pg?D>{SBGg z)Vv6cn5R|d&VQBotgWYGuTmRBFUOY;6~BLmFwE;Qk*WD>V?-1BhkZp%TSZf%wAh2h zg_mhC^@)_yvQcCu4)LXBp$M*IyMPD& z9JyIG6IzceE_6htKjWDY7UoFVlEw;j1aF?eGv5Yb6PL06gXb~gLD6540)ErAceP1* z4U_UAI98VfXQ6S8rzJPTzp(Q54j>XMZ*w9`@@CI6o|T&Ti>2zGw#Pj*FaHm<$M1kQ zf{M}}XCbGX_Bia?9=697vwP4U<&fDY9ruMo$h9QYn&n3v|x;=h^ z*@jpJ{5w))JpAj}esZXx{LAep>UyjG&uMGxb#xUS+7IJF?T6}0(ta4YB<+WwwX`25 z>}~tG>ZYExpEKzV)OPLXSq!d3``L<=fnP8EpVkd};{T%vo!<3F-vMu{U(7;IjNbC< zygwQ?Me+XzkQ)8bACOl2qZPBP{(nWHKN?}gi&9$ZIj&&|51x-;Dc%0>)^4Z~-Ow+u z>9yN$gaB6c?mv@fc3%O zW0n6oe~*H<)gkUiPB;EOzO;w_{?K(j@ON4YgTMRP-~CBpJ`s{UmK*9YL!$gLO=(jC6W5A6`*mw1lqb!^y_;f5Q5xXbW#6 z1q}P*|GxjA|AV*HFEr$I=l?}L^?yu!yY+usj}-pTtH9*{l<3O;C-mO`A&FJbt;p%d z|ISw&%h;_&l0p2|7XHp`~TE`!T(cD z|3^yrKYn}}ud-F!@N(3&3jN`0Fbj6Z7dk?F9N43+(~8+1?AaKKMYA$`6;xdf;`W2` z)372;KLw=Ulqj8!(nq@~y>~B_0^#F$D7%ebhj;EvvEIlV+&6&y3$N22 z7*Or1)W0Qm5aLDrgmjE2fU|yp^|XRH*giWfyDLI-))gZ2`b90pXqhGoq#O$k^GDV!SlpQ0&4I zgRP+$%b0jhM7(FqIwX7)54Ectn*FBu%?`?Ayim!Cbi&1@%5qGR<#Lk616eK-vYbn@ zoCQvJk)K|-)&|F?;k|swGmzvVVW74Phhy=VQTyJ#E%D+^%(YFEYxQQ5i69R_2#g!j zrx3mas)ttUL&l;V?sB%nk_yayj8;0ef!k{Ao=PmGKb9S~dkXI(09IY)rwjc%F!Gd_!78uIE^%$nVCcwClmnB_Q+Lw4xm(DLMzJjL)r^mCl zp`A3?hM?+r)=s47)KYr(O|Q`Fv3NChNZ~h&PpX_=p)V>Yom~ZZ*AaKC=wXl#RUivL zcr~;mqgf`W!f%#*YTQ4|tO)n%0=yt4&{>1yeq2}C5xfc&_@P9scD@bkFB(!j< z-6MbBKx`C}`-H@Altt1JuEIo%cfYXl3A-@&N4!U|bp#tlqUHPbU8CtYDT3A2u6XbW z)3ii=c$#(4t4GUq{Q+#>hC@ZYI#=Yc-9+cbU}s`Aev-libf|(rzL76}15N%$R)ib8 zN8ZCu?J@?3YJDGUEeCbbUXE8fdd z#lev(WD+HMN|>Fz`pFJNlJFjP&G*1+zMVGnh3cJ^ysNL;mRj%AXHAA7u6697Tbzw{ zZW5+8>JI(;BwCa9-`UB~Cy0MiSfn`-N3g zZNuS(bEy#C!VV0ojAuQA3w)7!vk|$((;&lNY7_plwz<}6vsG4g@ELhV$2k1#4DWx& zBpEK-5T*>;_8u+Yg>wyXfmx+Zcc3*!`JUV;FCB-UQz5H#dYg~}kg0iFwDEKl416&a51%w|vo@N{EUph~UfsNN)XY0# z<{dWk4w-oe%)I?(-d;1W(ahUrW+&TM7u6q0PpzlED1N1|JzM}i zBuyZQf^IkJZRdBed;Oo&yD++^qUT2M`S?>X?W?^@F6h$US2{+_iTc$qrE%f(vi|??}Z5nEcQ;y&a}4}Pu9Y)+}=N#jY%;qeW( zcS#7l+t2G@_xXG1b{D4K-wV_89I40lA35z`>hDXSOc(QyES$b#^!KR9Uv^P)`^)^K z_80eHf1V|Bd!G%q9j$6`Z_lM8rPcll1Qz=z70R^#4`4CT-g*34jW-VJ>W?^RM$r$F zk_TY2B?hNP!{i@gK@`)&6m6lhNO!BLC;# z*prU;WPVcni+iv?pP?k|54IhxYH)AQr6VQVAL}z^dFK9itj~0qYGXYWCmXqI9wH;_ zc6a#CNOZCgQwWg&#HdadVq7CkAUrc*OFNc0wqYq?>zat&gCPuN=QPjMYe2`pm+oJ_ zj!YiU8V9O))=d0G5kGzLE&oDdb9U3X_-JAg&w7sNWSSRG(kLGSgGa~GI+eb}zn{KR z^En|Va7DG>Ut2xahU5S-9u>?0=JB&rB(9ssGI9{jS?uabNbk* z^t(qup$~7;{yOWG(D)fxWW>AD{+ltw(ih_ny(QYH-##H8-%9uN$0ouM!CI}Q6K)qK zxKX_ybY5?&Ldg#Vx?+0}i-%H3@${g|kRYMRuiMkmK}K{e*h7gM!ah>ZKw+i6wgim@ z%TK`z@ro>Ih#lNn*kZE>8Z!!2`cF|D&9otn!2zsPMV3>`>3qt{Yle6%7?G7}vN9YA75HPQEiH%@{lH;C|3{qO*0e9wmL5!}rzcH+2(@Jd z9k`G42h&K9P_swdRS^&5=HQ@TJV?6N8;_fuFOi%r;+L7?|1zog%du?5;@`;$dvF-u z@vgZFUdAH1H(ZG%sWCZ*byb{wn;LkZq-L_S5@e?^S?a2s*gz&r5B7r!C&l7el_D+h zILKY|DK4N1$<$txS~!BK$!$5pAQnx=pp^W512)HlN%eUWEeG`L2 zn2%B=AA~BTWrx)pkAr8~cR+9WJCNm`8#aB}D-_k`p$!ib{O`j=9 zCb^S*2Kzgx`6h>dIU=O+e@+sfJi`g8!hNVqs&F?|%PK5lX;c;FB*X|LSSm; zq<2|t0CP(Tjz-UYg_crSzj_d=TX`|6a>b6}>DPEV;x#bb%4f03Cmmp;69rnNcYCxp zqO{q}#>+yMX68518kq#Gz`W}He~K14nP;SPU>4py7!fMEjH zq0WHM%5tX3QXqUu?0-=fr)#=vd@Ou=@C>Tyhg=gFxHnElzGVKO=)DP)_E1h5r$G7X zDM=h}IZDa?<0w?^=y`nLY#Um|#i)jXJ%|ZAG{)PHK-QxwD4TJ&FYEQ9yFgJ{v8Crlku}KYN$E z=8qlVZ^;yu68uF}ZX&2fxiMzBa48rI$lW#H2M^%lu366sG=tSh#QKBU2#1SeCq5OJ zUq~!J#)V+{AyO<#;8)S&3(bgjyD0?NPEhfE4h3SPReb-)V77iUQiSg&d;?!Xsy)7y zV-$STWG$Asx!Hnmve@aaDdX`2@ST7J@I8x^dKXFyr97x#;=7BGIF-pIoS)%6M?n@t zN|1$*qm%3g&fkGL0q3)Pl@??;>D*+&@v69D#aNC4P;0#;{W zIeI+b0uZpyRdWD&7k+~d)LpZOAby?;riv!L2~>zY6mG(zKwy9%4sQgV3>7o1p`sOs zi3p607=0C$MVPpPgcz6Lc}Y8-SC~8lMHC+jDLz~l>(9mEzhk8Tjvzjte{9MYLlVlE zvVDpk6*v=O?Qwn-9&a{|sZvr4&SfYiApQb)bnK!Y0CVn{HgEzEhByJN??M7tk0SBD zgV_TIZTCicrCoY)6>_a;(Qh{iv{Hj!Dmaw&vUKogfp5%#eCgyT;hM1rCTTQFK_6Xn z0z>0iEBE9wMAOf*#9lu};)3ut0iHYjHnORr-{1~QxN3=%?gc8a9V{$>j4=UeP6|j3 z*NM^M-JAd*i#Y*6<|6?>&ZFu|WOXU-?|n=?IE@&~NED1uL(~sm$n7CMBRld7}nGP(_guN zg<${32?+KkCm>HFC-5sE7-{Fk9&08IG6J7M#FJ=XG?3)v`Nn;8tDR)iRmoD4N#=xP za=dqMQqjr`(Iyz8T_z-Q*IdX6IBqT{AnfT#K(ZlZx0=(xW?6-FdQ(T~wqK&)HWx2u zcfhT6vNC`EMMwF7*puA#0n|i6#R2TJ`s@HjX(lXi==9gbZn%e%VsR3cF;v=o_4XbL{2>6nhZ~D0Zc(*bo0LcGm4iq3! z+AEN!D)7#LBnlitDRutem{k63Lt3|xc7-YJhcQFiTS?lRV+W97NINaIo3GNu%;p7? zb}Z52E%emD#j&kSA}#f*X{ilNOqRNr2*^?^kbt5ja(hK5T&!5?7+xLhV5vrwqW&eA zueMZp2I531kAmDa^piv6<>5Sx7-A!lYY9cQ28&bLvk2D5Qrc5L**WzOV+Sx^vhdz) zP)}~h8(qxLc?AM;{uBv%(e^>kKpS0+kv%ud8qUGW zTevsn{(gBfs?#l0u(dmU58`s{4DiJRfc)AI(0k6AjfV~)yw^!kM_)J6k z2l%YS=UIFX;By8(^aQF~ys3)?-VPV=z788R0;lm9d*idn$0-x52FPK}PjC(4QV4PE zWzk=mDiNnb?wVC(B${?@474ja*HX}~f7d7|tV=6w8lDTeHx^RCF9)x3twkfjkT*;# zY?{vj%LfJloIpPSj}eB#&sUwIUm^J!!qMKNR6W;P%Li;ds)eS4mRe};(yW zYKMNnFd1VsM5r1eUce}WF$%6U3QVg`)3;g*e2WUK!tV~6#J&V^y1vvPCQJgO36JF$ zL+{n+8&tmP4BaQGj6BlRsb3*;P3PD&uD{*02%o3$`4S(`0K4aAe7=v*@9{Z`k9a!S zoqsso>uR7Q{NpJ32TQjU)jLGHOVOh~rQHQ)C2l$N#qySs$9ux`8!ZJ$J;}?i7bhu@ zs$U>;P4(t&`T=Bl5}zCKc?F+k_;jz&bR|P%>u>l z1QfJD%23c!H00t!1jS3%B0sd;PSLlhw*$+Cc8e&Q5^Ddiy>|hNs`~oJ&%mH4D5!Y9 zjf#plICH-mYR4gDy>1S z@RC|u{6FiQ8D=oe@B2Rg-~ac#&;Oly_St8zz1G@$uf6u#m$UadHZHE#={7VwjHD*C zrZgfZso4ndNTX5IlTx#xHL>=vp*2WN*Ali9ZXw(zxX-#nwJB;gv*4cp0`MKre*xcdt?4Q2m zcdNjM>rrhRE}CNwM#aTO ztiTp$5S?~x{F{968XWrI;;n+V_B|ThE*ZSB;u)1Dda}~6Si+I?w2{O&RNFPa+PFF<1+rqO*;C-jSt3rv8OzYy=?)3#tt@kkrxans78XiTT z<)ba7+aXW$6^I( zFNw&V9{1vFPPm_Us|zoNKqw4}0WY`u|5^;!Acp^1;gQLbB~*UCY;KOSHJqT$LZL0q z=``{=Op+24Z3k#kFbSyr9AJE&B$G)m_d?$>9;axn2HfHNK)MCQ^=+>}S7?`% z*Mg-GSNbvlaZImS`m%j3me+gn=OdA2I=B=xdFimwtjI|oK z6Ka>^N)fXuq6Gschz>5{?n>O9j~kbIE?H0UCG)63TVLbINAu)yR%~{sm$BY-&rG$_ z)9pmO`OVU!II!OG>$92MbS-XKKXF1WPT=yDQQx)-GN`m=y}&oFR8Hk8{X!}ySrvcZ z{p=T1%x-b4u73SQc9X)nHm$XN?40S^-5_NfsK3KyGoR5jxi@ zua9u%dsGM4Gq`CorOwawj`kZ%N4!8pJIvGEymYX;>tIi9mD#iSh@bYGf*YKLFFkVe z59@I(3BU@vN-8Q-*W*Df6L)Y4GKSf~O`-OT#amiY`dOXV`z?V}hgaEF?h;I_v@Yj} zEKRL9Adj0Jtg{i0w(iC{5G<;Ncv-h2uuD*hS<{zbx}YM_YZai!0A0YRX5G)v~)eAO-2iXRbr>Y)?T!Ym8V+qkN*t79T@rRzJq49%rQsI#}tuU5PNw z)<2-D$be`1QWU|eA#kNN79P#1mGCJ)!33_=;!3qN+a2HeIqtRz1OjVk6vx^FG03A( zQUQ)We4sh#ni1Vid^4MqeDN($>SWRM+=~Ae6wI>D=F{$v&5nKe^yy?K$pf5j#;32L zEWHlUgX+Xi6@ORU4CPu>S;4({w5mgtW3Fl8GkW%>RT6d&-{`}3*3FwYA0m1l0207< z2>?ZB2vduxS2zT6D$_kZISr|NLL0VBkC}Z@WARoIHA0Mc!G!fVOz%B!9|fYru-hSW zzXt{rY3e1Yb?<^esM3Cl8*n&!l$AD~B~sq|78m+5vwC2QFic@EyoyM~&7>9kNv{_3(ks%Fz!^&udY>}Yth@Cc z)HV4V;3p>9q|Av3pHgxo@u1lN;cOLN)PVka67X(%@RA-zG>|Kr&zW9f?py^CuaA*u z9$Xz^YcI5`+9IXFtB5YG=;9`=2)fByXQR*<0mQ@e+FL=R1nS(a)F={fdKb^em+Mwl zAST+{9)iP1sXUoX@AM*)_)wT9sK|B_8*~S6ov5t=ne`3yI7`BQ{6Mf|!p~GA#rg$Y zxtqeUz5<#PZWIPL#!8#x@L4aDwJYS7-nkdJDv*8aX_$r+Lf=n%0)P$g_0~%~fJI-B z?uUyt6PibrRnX5miDx28_odhl6dTUP@(EFIEV+!l*s9AK3c!NPUJJ>vSlJAM_tjzRGFDHp8 z5wo7q@I6hu59H>^h>aAomg4d961+vAK^WM517R!zx>bkV7x(5QWY1t{WRSCj*5j0+ zMCr)|6<({LbXFQpR9Xjuq`u&qwUlBh@d`?uzGqxI+Y_%B_EPI9*|}6B^kk7sw_adr zUR>{jF~|h(n_^sI;yG0I zpC{?fk@!7>gyl#mmd`T2Wh*?<U>6?zKL2{ z-`fX_ooAP)PNfg%j-w&)6%fVHcdcZ zQ5IImzun^wcDvW5Vn~HwViNz|w^4Vf0QCjvHlbFe73p1C!%Wa<$ND)`0ouh`Nq))_ z6NgF5GJs*k`O&)cei=8FNIwOIi;FIi@1Pd_Do5#AvXJz>q-+UYS1q9H8%1>ea28!Z zEyOi#>8eefSGI=p-mq}q!aUB~oW*(Xro&tJ^1^lSO2dTs#58Wo=%ySuC%7EYG^RA@ zab|SM!ew0S);Q<|G6Djq@QJXkZShgv6Ja~ZwBl>h^=(5jf{H6ksDE%7A4(@laeO*m zMo;>8B|np+Ps;LBIn;Oyuf3Ht9_!&oh2%b9OVTUuy$3-ChY|4UyO3sGy&=2I=GF`t zBN}65BfVl%9p=NuEhx%GdNMA)q$|;s&908|+E99TTuIZCX~L3`?j`Y_rD4^jQP+^0 zdABoiOhZe~F9jvs4ZbMF17<&LS{jC2;O!MP5JySa ztyz-HrU}xMe5z#=s!Lo;@`WYC-AiJS(gJl@a&Q%Zo}w-v1Icl z7oKpVQUj9#oN>2|AIE3U$M z5F&?I(WdJ}8wrX>z*Qn1j+V)AFicXY8KEzNQ@i;9jRu-rd-o|e-9v3WyAVW6t8Y7tzbOm5 z1*B_)6jg^W;Yy=!NwE}BrTwy?>Hd-6ebam47kCBM;TEliFwEf^Z;z@HZnFH~s}gRK z%#YU6D^RfdwsqiB$=zGWL2D8#_f-L2X`P586{QivVs~LlgwXmkED{%TL8x-y$`YW& z#2vIu5O{w=7wz5r4)LXd=v>Dei9XHMz^iQ zAy;mMc%q34w`C)IO2YAWf8mL6PkJ*hJ_{ZJ!)<->13Yf4n2r|}QL}=5@JLSpyuNJ^ zg2&zR7Gq3rw8y16NE?8eXb4sw~o1 zIG~Cfd{&hn?akCd!>aC>JxUKJt}MmTO5Zz`gU~dXh0M*LVd}ZBq2BTG9#n;cXE(dh zCKrZ+XDHZWKk2+ZHqC2J1r7SMW z#98Tmp$H%ID(-TCD#m&R7iq=ExE56c_damH@LI41Vesi%4nFXEZ{_&q z3}WIad%1OYypG&PzH+}x^SXRC&FkhlN?r!Q_i0{_uEW2S=5_Z9eB3_*Eceeeud|QS zywXmmc}+SAqKj!>-+c+f&(pkq`vo|)fPDej?||Kglxm6}GIM%?35b5X0Kgffa0y>I zne(;B5sOVYBIFW~Iz)X-QFkgo##o!N(#2k;;#(prx7SBP-{L>rE$$2E|Aw*@_+1?2 zjk1*Z_~OYi`WY&2E1V(Yu5{|GgLNasdljE35mi1}!GsT~*ejTf7X_*MQB@IFezW z_W9>x_;4-v*_hMIP&7P~ z?d}zS65!7c-ypE(c>>?@N^G%)^AiqNmTmGyp&6p*5S`gHpb0s4V1Rev)%Q;4+V3V@ zokv_a0$aNTA6+Po^>r^B>{&XjT3ThT#FmPEMKGJS+hHl4(NC3??Ze~#+O|0EW+!=Z z4Qrsv&yK0HNnPubB0n?D$U0AB) z)1%M^d&rMMEA1hcLW}Gn6@?1zp&$xnQpm1-RQfRV`}>NhRD+;cjcVr<^snYG)NdGN z%U-`!!F`@(=v7T~Mvs^Hgwq zdYb?N)f=GV(|d*J(}{lOKk3gul)yo)SlV|C*ondK-r6< ze2BY_L|H+U0-mxgYFVwHGyVc#-MwzlX)W&d-6H(2ux%BRDT^wqeK^ypa2D7}kW@5~ z>?M*8Mv%M-lCm;)Ak-dnCZXjqkd!r$6cb4(kxb!JaZ~~9$8FFrbf(_{2L9fF5>0w{TD{58EBkWe(^n2Xux5I@bYBbwD#5&=C%3j0382K=lr2PY1NG z1M2C3wsJu4zuO?QN7Lz+D_e~Ndffs2%mMw<0o~((?sq`nbU-&cpi3OkG6xhNl(*O0 zGzWCN18Q_Y!yVA!4k+t@${o;74rq4=ROo;vb#;HF%hjMNtJS*Xq+VSin z!CNkC*KCJW(T^_U{ByNSIR8p*5$9j4Erf5+*NgP}Ot6e5mOfaTs-@#N_E=7;a*8U) z!c=Y7CsR9hYJD=E!edd~(hAckdg^wzFewEtj=(K}^M>mL7X+68XNIHSk^+<)ZZ91DM4+YLf~lYZtPflc z?(YFV8Ga$$5x~yD{R-!e@Ibf-IQp#xEE6sgj((kN{t?vWF1T;t%HgbV^b7CB>tQQz zOuXrD`)%~$;r8%<;?o`p+B+eP7kHa(^ivoUV`g%2&0?gCgz;xmahJuh8=8}0$uMSR z%rJ%I=A`02Hz&?$&M;2OGBGh(CR5%&z#%b27;l_n3eL*$g~Q7Q1y3hpLYQE{vY5fZB8({TOl1nV6!1vT_8Wq5F4CnG#xpaSP)5dt zF=EiF83n^KgBTT~L>+|VP6l4#2oHkKB0d;*YGBI|6Ao+{Tqv+Z0Hf^(G2onrJWK<3 zGjcEX>Qmv1`5|NA1JeNp}>T-<0YdRM3Lk4N|Nn2@DtrWFZX?oNu1EWVmpy8UbSnzy@O z9`gE>pGNGx7xco$-EP{l+xO+yJ7tAzF4!KrzV9;4_FcYjOcG>;uMV2AV%VmOe~pi= zV~W22YS>FJ1}`&y5j}C`*guNC&i^c8=h|kA3fHc^le2N(T+5pu3}3qFvl9f zRs5ZMY|CLU&V4&LpwGcWo11kz{nPoZVcWDnT|CrPv|@5Z@ie96tzqv@8y0@F1MB*G z-h_?|@6<=Wc4uEjt*LC!ytu=ozZh{b>c!FPgBK*;l=-DBzP%}HXZS(k!FdCML!}<-*Gyb<<@3Jho8Qz#yjhZVUYK$#t6$=a^{vC=2bU!tA9Cn5`I%^8 z#hhPjN(PslZ?Sk?$0nVYw>Y-*_WJ>0FSVPQe|PF%cU6^|x3AYd7`}Yx)$ViJD>uLQ z-N8G5myZg6BY5u@$r}At>v!6!$3NEuy>@xAf3MqXmLD#Z7Db%O8@fvD~83Gs_qakNjymh7Iz^E-F><=~;0gZI2~#^Cn;(C<<{S?}-nS=r#Xnsl79MC$R$ z|JL{azv}x*JFJ4uB`X{ZpB&j?g2-_v14Im4OLjO67C01lWOYMXRdQow;CWef|aYuDbbrizY zh77(KVbX;#)D+bV^}W>Zs(~-#aOHrp4fX!?dKibijYeNK1b-TdJU_3`;uFSB8< z(;Y*j53o4yicx!j55r8yOK@6=5bwt@UrHFJa}dLP6ph)d2{=VaYd!dU4*s+$V6cEO zt`;CqE=<3LE==V>SH_hQG6Buqm{$^;Fl`xkCdQ{Z(+deS#kAl9KOd%hT1S*c#Ecr{ z$JBo=!Q01!n6eGgj9`t4X~q;WJ;K&Ae*}Dv=f?arw6RQQ!um*2?qFy*jY|hZ<5{$Y zP+NN-3rE&Z&9#Ft+)02R&b1S=o-EfEXe|3II~We^Fz`?UlW%2W+>`wzJFCK<+8}R- z2^NYx1cQd!rviUUPma*D_$$GMY&DILgSothab@E96HLtUr_nUEXC?Bi0!)fOS$oQx z8o7BEe;Jo=vfY$>;!4sX3qbiNt4() zZWzZ$!r_uENS7!jepaN!9nF$LZ2T#OP~1};ll3Oa^7AD$a2}NQ+1Ulp3E` zDv^w<3DR)N|$9f}-~rqbAew?_n_kR1NA z_){(W`hr1R|#YQ~oq=%xC<#(PKaKI8Vc2(CgE> z0dN!RljBeleth$Ee*amT&s+7NkuZ&7>|+(O15f|e1viG*1yjF?E!8$KI(7b6 z#C!H|O(h?$3E}-Z^0AZVw;{amMH`avOAd4ANd$bjP9(+n!2pM@ovFuw#L$M1alWQ2 zY7!|-dss5zb|eaz9R{9-J&vBoW4OU|7%N~p1T~JMjg+Ans|-XOUhs?%Hfh^tj@s)Kz9A9dnIIJ{IF^Kzym zUKZfpbK-?6VT2gZ`oKB!BRoone$II#+I7!JYbki7!tqr6mGTq`*a6Vk;m-VG5yw7- zj?ylAf)}MrX;I_kA;W`WiJ={#VPvD{g1WXZJIa~!? z4IIM?7!h0$9KQU-;OlY>_DwURV?txLZ|Sw^v31^v*E75B^4c{ipegberWqeSiCg#75H702cTD7E__cH#LLeSirpT9u6Ls%`Kz`K`s@rDLkx@ zVZ;LFdIR0`RH9>G3w$7K|BO_l#puiJ*+#p?i~}&1O-aKf2*)t^aLDLb--M80BJ0u^ z7D+HsW7rsi`8I})B^VBUIPn|G@f+9}#@SGu5OK!iXl0FHksK}Th!b8Uho@@Qh>h{*${Wk!MFX}D#?{|Ua>BExIOq}KaADtADi@xwFat@JJdBpL z8Q4N*7JM5lzmO;A?DtVLej9woj2WquAZjLWf~_oOON#H%U@kw{C(DT@H1;KFOk@1D+Z!0V8HPeOoQ`pzc;CU32KzcbTKcB;2hUa30KRGQU zHQ#qS2P?Og#gfJ02%W;j8+`}^meB32eBZHrS#j!)DJPgY-<;yhigC-71GW{)Y=d!S zBe?Ugr`szXiPt7)@mL zFz>+kfWHsEUH-DV^TPPo^egInW->uvn^vUv}5 z*@7S4cA`eCt!=fWqQebE+P1)pXp<+kv4__V&c*A_ku zpQaV3Y~c&=am0xE!501nKCOj5w1r*$1>RV;WLm+k+VCdq>Y_T^10P<)k6;c0csojbAKUnPrvBIYia_JcH-5W=Z!F>{?bQDU-@+&O_TAy& zyTiu!z<-19nJ4%*eb>QPXyWth10UIL`28pH^#e^%0|gGDJMkTFRG1E1*2;gim7 zg-`Zo7kq5(<#e~&=2JMQqriI}98eg0{28Zsf){me9DnYK_(%T|f2NDT`!_h^a~*qr z$iIcMP~ING_rYF30_uwRII!mONNX4|Hhla1+&ON)EzZs}2z;oV(eUkj5#;9CyLr!YEfY}QB&)~}4vFEJ0fMGoWYk~Wg z*vp6bWfZrDVJ;Fq++T35Fy8M2r-mC2mkjp>;MwraaC6|+z-@rr2Iq-UWivc&^1!U9 zIc6BNUep4mc12E`FwXPV)T}HfET>GbJo7&6HxXfg1%P zOvcoFlcf=CxM}*NT%$SlNnoC_F(@jxAm7voiGw)MLNU{}*fF@rilYil=IORfjy6rs z$hVlxVN)@2ACYUxNXxKoI*Vz@knL&Ym>lynks*@~Q>fx?hu5%j+h_Wf3pC? ztEaemlpVIhjCkN(al(5D*kj;{oM466&lm)lyA$kN!2AH~-~Ye>z5(nSU=}CXeZX)Qky+pb8;JdpIG1EERwMs^_qSrc zi+!yByT8@3zjdc;B=&Y@VY_H<3aq!w5R)a+m~RO)n{&+!vzw0%H=9h%Pp%{LOgZ5h zS*9psULH2~#pA1YHXG6yTHy+exD3;@k!cjcEOZ-_lidh1mkG(r%{N6Db5aq1#EDBh z^H_-XPIS?^892m_Tzu~qZ_cooI9@9R;aLUw>2yE10A|dLRS6yCnjkUQDjQbFq)<32 zH?<&(qZwh$HX*Nnx{fTcGfFm|Iqe#iJC#c;9y=0%8&Y64gH^OSH^qd-l@?C0*mPLq zRPl844;ZmlKW!f_v>YoNOkrx4Xp~Gc3q< z4C*J#G;&fV*?Dha1B2My7~}`Z5U=J=xoW^*tTvwZd`@zUHJP(Bav%>ID8kigXL94* z2j`^5KsQ2i3m`{{E4q`sP!slh=1yl+T!^ZOVHDi`XxgO=wUcroPRW=6?w*Hla4I`- z!j9Bur;K{P%gH7qWeS(P0_$SkgwX|A=#)r24D*$(WKjiJu!Xdi3TE= zl}rt@Z-++xayl5|2CaN%w9z3WdzCZU&@5XZ*Bp)xxWH_(YglhV7^(J?RnKg2jWijj zn*IT=qc&kH#RlX$zag~aX*CX6OeR_IZGdlODDN9~c?PGZLeY7N2pr2cfr40o!Tm`HTwG?HM3hm9T)7AckZ z(}0A@bBWH)w?rXjtg|COtz3rYV+}qvcN$)y6%#hvj#nmd=oeZ=0aOfwg!~k9MxKSN zl2kyt5|fe2eCa}R$w6Z_mZ8R!EM}n#Mj&M3 zFy0A+daR%@C!RQCRsrk^c*hcqGhIMpHc$u*9q~Rc#2_Xl%9w*9nnBTyhnUPX5JNc1 zg<~1R17a*ti+ofUo{4g7ZE%}mWP(HEf}@ApBVTI6#{CH-$jXDPg*$I(}iHM9x zF*zkEbulC*;Lt&mkxWBSz9ls%C^0x8VJ!19#pD)Pl5*3M%*LF_riRGq!J$E8nU^S@ zKZKBEqVDU7_^6n;kZ7@t<#o~1Wem#B%PPk7LIDf}j#@Cx=x97_knuP}m~2xv6_sI` zq@<}SW=oPo{72?@LWYwX&Qc_$xEr!`m{dihBb zfy2#^6R!zoESbm|8{;J2Mww+`kL=}OXFu`@lR&%Y_#9ZK=b4f+a?)~>Y$+vWp$f1X z8Zvs!h}fY~VGVN7Fu>!s$Gc6@QC2x8XY=-vP z6d*L4VCV4E%ahMZMy@TBNmKK4rsd&0fF%t~+Wa%t5sBwHX*A$~o@R4eKsyAvayfIe z;nTzVB=nwHID(SS1T#tbru3vV&S)o*(dmfT+?=$G$*{gjIY#Kkv?QZ>@>EWqj;3Re zGUZIoFz4pbvqSV!R8sp9oTS_wQ(=ZB$-*7KVFH;XvuzGyk4Y=Y;d^jjswvnli^(3v z6Lm*kphK`sV1ylyNh#b7c9d}GK=RqxE7EYEgx$1x8H2L(2TjY!83f8fX4qyJ+d*04 zLE=HYB0qtFt`SJ zj?PmhL79_yQ)WYJgcrg+xTX6s}#3?2tZ z>t@8`e}DgDB~bJ~Hs}AF@&6Yk;6mGbFq>eCI>1-Khv)R;*N0&jikyJB#o1?E)1Lxw z7V!@thu=(y^<>=99tmGsM7RB_J`gaosROe8d|hR@i^C4eBwXn%OAOAnjD$T(fw*Z+IfS(0>GikTzWN^#C?opG|DbQpN zhjz5mj#=_2;YzmB1GFLB`PMw{tjcuc#>kyfA@w#v^D%Vd zmCnLCay=R+aGcZPQ*xZnUI~q$-7+RfiYTe{c{qbhxu1;M$MEqfFcJ3brr>;w2>GBr z)U+R&zf%C_Tu!^3Fq;MUEW|oE(|IpCYegrWX(uh6?;(jsaAg{4!=zfIoZ0J1CV*zs31oY&Ne=ci z$iC29>UGV)pp(;C@SXLfnUG${g!Mo}VRn{iD%J)uJJt6U`?GyTrks@AR1A&veaFOx z4^s6N(eyagh;zv%ec$P({Jw_37R`0W{CrdPq^#*8Fv!W*_r+93lb@1q$~NW?%FakJ z=jP|8Sq5R+r7`Ab`%e}36=BgNBMq~;IOo*BN+i-*%mw+Dp_oY7xb=JvH)&6f4wU(} zMGspT5Hn3tvFwr>ZO)jADfMJienX^lbQm}9K(QlD*aV3)`MB!)8nHMsHFt{1+*eeP z5u8FxlKQ@BMy!nW6$Lc#(FHs^6>l0IeX=~n5x(xcKd(r=`{OaGL5%KFRXvM^byELXNhwn=tOc2;&t_N%O3<|%I_? ztM{wFQvag9t9I3hH2#_i8mU&T{Xu(Q>#FnCb=C#x!gZ^4>vSLKKGikVx6pSC>>ju} zaBE;Y!$3oZ0Vae=7U1=(>^CUgA8bAQy7+zZW3gBgE*T?9ljKWgOBPF(N-j#Ql3ygQ zQg>+|=@4n8bd(f_!=+`?*QHye?@2$D9+G|~{Ym;z+E*r#8D-hBeA#qaiEKH_`IhVh z*&*2}**V!|+4r*FQCc^78+liGFS)-w6y=>D&zCQeAC#YzKbH4ah!w$#p^9ilykd%C znqq-sh2m4iPl|_%=E~N}&dT0OjdF-`g>s$ppz@URd*!dn$I7NEZ&f>$NTpH@hJ2D# zFQ{gz7OD2Cs#KR$Kd5f1epj_pcU1RL%hVC-(duNiMSW6zPW_YmceR_wL(^6>3KE;4 zS)C#uEZ%Ow_Kb0Plo|5*Fsbs@s zBW1C&iL&*wZP1)MvPZJE(3-w-say@s87u!l{;B*+`DOWa`CoDug{LA@F+;IPaa}1_ zPta`9{;mrPTxCFqhrS1U09d(XykxM{C`*vXC^jkYsX{eb+7w-iULUA1Trak~Wk3$cLa^O_JxxnYDK9C@RfkpIBK@}N9_kErKg}>rnr6P{4b3*qx0<)LRoZJ>2CcZSZoICG zUawEo7wDJi*Xh5~zZ$qN@LXVBpogKoA=1DU;TaL~<;f0Whq5MiHe13TWEYFKijN|9 z55+?0+8>fmQiXK1G*dcNx?MU&wp6xNc2xGG>^GUO{Aam4au%dWP^2neQzR;9C|4_Y zD?d|yqx4XHuDYdiSNp1UsN>mcy=EfX%WBO|&BvPGG#_cJwfD5%sArijL)T9~OrNHo zuYW_oO<$+~D6l&4UZA&OfI(@aE%x+FjZ`+I`vs+6wJaNcWQV zy0(eVQ`grZF(ew23`{w%_np|@>{vFLUBfOwyIm??CSECCgSPvj_&%(oOk#okn*|HF z0D87mx>mYbdK|6vO|;C0M$-<~CPDiXY_*^6IBbi%ey{$x{-J(q;3Cwn!VqpKB)RRPaVliTzRP~bwif$H zyGk|E`O*dQGWk35JMwGFUzE+E#i5!gO{QkIW|Qt+-DkSidLKhqgT^r5z*J!W4d~~w zXW1}usyG`Zxgai*+?TYKc9Z5v+sb4zy=K{3oFnNi4V6YgVn?N?(PJoOHL^Ld{Gn)-<%(|=O_ibAFEHO&#Qk_|Ej*DexUwKEzmU8w9<6e z_-gv24^(NwVAWrQmY&sor@5=C=OnGx#%MR7_P^A&)#-JkbXmITx|O=0bzb@|us^Im zTt8Od6s>8CVXxt&;YR~g16@VmC}Bg{B$VM4dy&1z4iV>yXNgOo5nIH2#V5r-iY1az zSnHI-I9Nu8^n zr7l*lR&PPye^%X7Bh!r3tkN9NoJ7qiwMp76?R0ICcC~hkcE9$lwkO)=NZl&kUfnU& zmQ1hKk48z8^^^74db55O+UpYiOZt`iH}#wK+tGu4q;DP=5Ez8I%nDo*_;TPofyV>C z3A`G}7@8V-8U`6;hLMH@L%v~!VXNVU;fCQp)ps4edjOw-hedqME)yRY4}mof!l>tv zEJj`;7b)TtKP$Q_mnx4aKUUSL+%#H^n^vn`fj;4m-Y+m1R-Q)vFcHi)_B*yt{6Ml? zIzV<(mLy-rQ8!Z>l=GFll;0{@Rh()o`gg1Pmby-TPyJB+Sj}jJ8h1?x)VxsVj`r-W z^U-xc|0B}*>QuS}r@qErKL{=Cv7QMO2D%4&26hNk1r9W@D3!{fGXxnzQMR8Acc@I* z(?wfqIFT6ComE` zE4d{39wVmT(8m6f2&JAhcaRnjk{=cJdU-%G91 z-=z1^V+my*vNp2LvR*QOnN+5hg~+00$ug5{j%=sw6WL){mb&YFl_&ZnKefL)NIguwRsEs*iuxMb+8oWh znol+7G}krvHIFnt+K#Yd`PyaLwc4%PkI}kX=(_7J>OA!+`kDH9`qlcK`aSxi`Wt#d zpd@f)peZmfuqbe2;K9JJ0)GwELgF(>)*|eOgV!5vZ3J3d4m%6|`v$hZSSr?v=Zlxa zW__lY$wTC& z@>k@q%aat76>AmS6#EqS6k=tra=Y?NnazuMxCjihrZ^L z+C@{X>7*T|eNp?FR*3dDS+`bqNtcB-v)k}BX%{Qt+DmIz!j5KVz&cu4fw+aZjkt$+ zpjd)3hl?Y{Msb$d3{6{v9%zGjoA_Of9S@1W7JnnYLYt|Bcpg5IE)tPspd?w6DoK}Q zLGNx!>Ll*aI&Y~jv~Hl3MXOg~v>Jp_R)juMAFYqoCt%c?tY@NeCJn5puc}tuQq-Y@ z4;9`@AEmF-Psu7HlnKi5ut#~yLgg}LxpI?ok8+>#0D4}s&2>1cE>yXzJXJobfsjLl zDpHlKO11UFYtTw}sCJ>Ho>JAoay`at3xw(p(5QjxCHk$&|@hR~+Nbj2XmiQiea-qaiLh|#4 z{A3axEP13P7UPgq$k8I1C0T%x`AW$;$tKAT$sTCfQCRtNnCD%CjPFSvH?(bENLhxq z9g4Y8taQ9IRhlKWK>wDYA2^EEU4ypWMK%zl_#l}@wm`NFGow>z!&Z!LeC0BEkUT;@ z3$1pIyj*@xeobD7KDdiQhh8yK5vv%luqX=ATK93|i5i8KtEb1Prw+=2N|{op3`I>v zDr1%DsHb%p8SPS5DX*dLrn6sNASPeTV-r{lCzmU*&|OG>{`dD(32+O&E-nl^ze9GD zX8jjMEPBvF(A1@Teus$wjdc+S#9nNRCe4x;MF>o;LKlG>!;Wv#d_WU{P%vNVA`q63 zU?bV~P7p7)nJf1F3OBeEF|7CrTA^rH!F$ur{}nWUc}qplTVavEC#4HUj$SDF>hF}L zXL?1YmY2_OeS)3u`YT*~xvPu7#l?HhD>om_IzA%t#_+Y%&NX?gC7!mN3fz#Nd8J|x zwpkO`F~TNoT*d~A+p=va;NHeFp4N}dIie6_o=NP(wkB}XHXfk`=1E38_0GyNais&a zd7CD&>BeamlejC}nb17i`0xNxh{^2Om@DqicBN?7HtlTDv=s;oYuU6tP!tl(c5B}P zHccWCv)pfN`xX*bERnJjn!2jTvJ;&$J0`}ST=O|yeJMoHJ7mxLAHwcgKbl!-Io0#Ro|TNt zgQ#B)?fEFwcgpFcFzr{NUxc*IZ$G~@W6yxkU+cER+__KG3*FxrUwt&n=n}K#o!^HJ zbSn(o98mJb&#(Rb_tGEY4f^9Nt_*uPQ+jQB^G9_BOM=(cA9Ee`+B+Ar-g+ftv8iA2 z!XwE(N2^Y1eJ)kZtLxsv^6iSA+dq5o$TF+%PY;H5p8Iy?n}c?~cw@^?@APdq+xTG3 z3#|_P5WRl*^$CAoZ~6AXx85;{&Q`CsR?XTxA|kZD;}4-tS9n}VUr;n5voJO6baMEd z*u!mR9h-jpaUcYfE7}p-0oMO&$q7C3R%2}a8g8hNR>2ufKLQl52JqNDtY)j(m zMJWlC}E)=gnhWo4x+>^xx(t zvhVzs{^6fLHtYJy$oWksD3i8~eyd&CjV>#ih35R8eKTPeYyBgmtXIBA>)avRcB)%F z?En7jyE;|AbTe@^^X?a(QM=EGx44h*KjZNaGrGn{ZZ&nNogOr)_v)MzCzP4RJ=|Xk zzH#+pyrJu~p^N*sYcg?*Dzj(njiWji&)Lh)7j8t0FSWJ!mS5geRsDTPo+GTzT+_Kz zw6j7}i~m2}hWIwjidBu;FqRc;ZFtgusSV4ZNL)<}cwQTh$(WpDGE+O2J+~ciNbyjI z?)#+I+o3~0X<5I%&e%NWm0fEtzdO)t*s+}YMY@3d!Z8u{x&)&5q;dfiD)sB0_99TC5N=aNk>7y5TQJ^cHt zDd*dlNXG4&_}bX5Z}>&GZ2#-(%SLV3sIFhOj`LVC)RVU-zz>Ia8BC_!nxJg8#IYOPq{b`0>g71Q{jiF{e+^8@7x}KbV`Aa6$>GzCUKV3S&f9C!xqGoMgxS82;+?UqP zZ2Z8Ip@#im&OLc3uxENo%-T;gEIW>M)y-W|Q+c3EwXgtY>NL#Mhc+|SLAI|$NmJL> zA0GY8d?R@6L}xRFm-4Z5|4U}VHnQ+&)6QWg=y958gvl~3*F5EEV}Xz?JcbfUMKKY< z61f5+hg1v=U__k8A_~?%6k#zI5vwr?Q`+sAsc{C|u&tmJ7&tW#!Apy=Jvt}DXdfUo zZPR2ZHuUD0#O>L(gw@=qgW`6i=hv)~>V-)ORY_NQ-U zPwrP(886)Z^O)0h@)gzh`qZ+=S4_Ilvh@#dbV_)X+_pS_W?7337YD4HqWbQm$;NlP zwB6I%Vo11p!(;J|&DT%=@%JiK?ux^6M2Eh2ZL?LBy1RKw!Hm0oj0Y-qmAV;Z-DhP< zKcANps=I86(ylgUi`QCTYx??tBd_a&OT>?Sue{j)l{M1OHF;BO3ncl2Gk4tEpkJEx zk=N2KtqlD-Z5h}4e9Z?JwktpHFI!zd)qhyY#eI+ZyY{J<$`s`{mvv)b+@l>S@if{^ z*#t-`ksHZ&Z6hQ*)~Jhzy&Axq4va)tQF*uWE{i(Z3{U=4GEOOz$T9^WK!XM&A&Z1# zF&Bz!(q!^HPYl^HR_|rETP}ij?TI`eqnK1D@|3)Mws!-P3)|82sc7arkp!sEyhZ}bf9u_@?@*2x*PCj0hnZ#PDDzf-idDs4pgR4{~YmEhuDp)zdQX%{ORxJBY*Zb zibj-o?cX%B8f**ZR*mcjs>X*0nc`&l2yZ286QjD>}BbRP$jQvxW|M7dL zC+v9JCs*^gwbFBU{QcbtA;-S!)&B6AvBozSSwkx3wR^M0U(t%w9sl@^o$rkyxTbYUR}4(7WIEZWbC}p8XF9|DQMFYdjT@Y@C+0U%}MdM&yrxv>=B~a zMz&1Ds>q0Vv=RHI^b<)~Rw1%u@+>e2!kHeCvA~j^YsLYZR1r>EigI(Zri;Va5TbQ$ zqqCEU>~=7^U{Y2_iU>OfvW@2Hji+v+V4jy{It{So+(b=^sTx1VBjYGRkZl;`{a?4< z&zs4;THK9k_ujzwzji-*A^nw6$=zPK|HF_ymlL9TP0HMJXl&q>wFfIL)YQ^?| z9?_THy!&%f-w_^1Uae}D_sX_Q<6Df_)M9!0o?AX&EH}scf3fj$Xzl3EhG5-i!>+wk zVO4LBIo0FLsW0cg{?n0D37PZT{;F4e+2z=)O?_XwJ@cS=kUry+D#d|-VnLc0qtLd}v{y1N@TYhEffZR;;_~Zra?s%@8SP?gxi8yuk;pW-j z23%-!O|^Bw`S5*h79HvQ#*MM6tFK?So6TaF&ADuV-HO2M^kOj!vWK0`j>KW-o;p*@ z;%%mhO@aCJphb^G55oja+q+`nvcXKjaN6zev}x10Q6V&oGreRx{p<<5xUgM1Jqwe} zCrhby7xrWOvc1cDmG@ZG-EJPmj(i5>r5L4yCd)aSPHPXC2)1K=?1{L#G~Bs1?k_MU z>__OWgy=0cb;B}PC$<9sXgICxBaGfv_8KmX&Qx4(Mj?+GVQPjsy*dDCF&cHx1lblW#yej*Q$ zCTv|1_f=JtcK%P(7qolqx9j~Oce`!~{B`lgEps=;Z8p5Sz`x(Ovkq*1XQN&yxbg8T z8(K#9?AJbW@ik4Wq;EF$E_vN;O2D3zg2=uzJ$7_->sg-M)^O?Mqo~N2ADJgl`>XHD z6CcZ4E*biF*|G?VpMPu2&963vRD(HNofl51;lO9(!=Wf{9*R zDlKl!e|xpEr04tHr``SZbjZ2ek=qO(mM(ZN_0Ace9^(0K9bsVGx(Ed9yjT9Oj|N@5 zpEh1~TG%U}FBrmhY*<@s<|_7dTGtb@U7SK5;+B|mIo1K&qW#$y<(dh_J%yspS02AR zATYCPm|>Ry6Mj_IYaX z26z7b-t?6i+Gj}n_;T=UcRcnQ()UruH`=S%IK=N<9FIMM^oH>9+@43rS4U_EA$_Za z_7G-ZPaVJilfLvp@2Tg%(K5baHy*u9elYNI;Ky@5eY1w>|7Ay^b92B7oN&H?c`^Lh z;xiw`_?hJ=u5ORZbb0CU+T1gW@2a|04s6~2$KmRg`+gq%-j~zr&TiP>N;LS+>duFS3JGDbzb1!Sugb-JN<9Nve7d{;VpiwA3uM?i~YYn z{Z&@aVc#9y)^X+9xju!v^`CsxyIojFo_}@f<$$*)oIH`6u&LeUtZO@tpMS9M;~p1c zx*a)prt_g=%Z_~MV{wtX4GB%j_W5elu^yF&jtzbL^$S0L5p*JJUP;cF6Y8&jZz7-d z!^}RNJGQ#p`p6c)G087~eeQ>g9-L9Wx^>TTwI>b`ZCLwRKNSHm$|IYFem%qO8PV`%^{eE6E@xh)EeHR&W zI!YG2vge&8H=7^2@NWMjtIiBhwXD6ZU0*-yQPSl7zt4~C71m|;+xwSU){bd=dd|hT zm&VC||L(2BAEl01F)QOYS!VldRlh{`Dfpn5YhLHfQIP>VzaQ_~?YHT^AN~1v*PMtC ymStzJT=(m1b0gl{_HFV{fs?+<>d`;B-Ok;2XDmOy-1Gg%0mZEcO|m_W_ + + + \ No newline at end of file diff --git a/RGB.NET.Devices.Razer/targets/RGB.NET.Devices.Razer.targets b/RGB.NET.Devices.Razer/targets/RGB.NET.Devices.Razer.targets new file mode 100644 index 0000000..6338df1 --- /dev/null +++ b/RGB.NET.Devices.Razer/targets/RGB.NET.Devices.Razer.targets @@ -0,0 +1,150 @@ + + + + + + + + + + + False + + + False + + + + + + + + %(RecursiveDir)%(FileName)%(Extension) + Always + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bin\%(RecursiveDir)%(Filename)%(Extension) + + + + + + + + + + + + + + + + + + + + + + + $(PostBuildEventDependsOn); + CopyRazerSDKFiles; + + + $(BuildDependsOn); + CopyRazerSDKFiles; + + + $(CleanDependsOn); + CleanRazerSDKFiles; + + + + + + + + CollectRazerSDKFiles; + $(PipelineCollectFilesPhaseDependsOn); + + + diff --git a/RGB.NET.sln b/RGB.NET.sln index 5944be8..1869c69 100644 --- a/RGB.NET.sln +++ b/RGB.NET.sln @@ -34,12 +34,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuGet", "NuGet", "{06416566 NuGet\RGB.NET.Devices.Logitech.nuspec = NuGet\RGB.NET.Devices.Logitech.nuspec NuGet\RGB.NET.Devices.Msi.nuspec = NuGet\RGB.NET.Devices.Msi.nuspec NuGet\RGB.NET.Devices.Novation.nuspec = NuGet\RGB.NET.Devices.Novation.nuspec - NuGet\RGB.NET.Devices.nuspec = NuGet\RGB.NET.Devices.nuspec + NuGet\RGB.NET.Devices.Razer.nuspec = NuGet\RGB.NET.Devices.Razer.nuspec NuGet\RGB.NET.Groups.nuspec = NuGet\RGB.NET.Groups.nuspec NuGet\RGB.NET.Input.Corsair.nuspec = NuGet\RGB.NET.Input.Corsair.nuspec NuGet\RGB.NET.Input.nuspec = NuGet\RGB.NET.Input.nuspec - NuGet\RGB.NET.nuspec = NuGet\RGB.NET.nuspec - NuGet\RGB.NET.Presets.nuspec = NuGet\RGB.NET.Presets.nuspec NuGet\RGB.NET.WPF.nuspec = NuGet\RGB.NET.WPF.nuspec EndProjectSection EndProject @@ -70,6 +68,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Metapackages", "Metapackage NuGet\Metapackages\RGB.NET.Presets.nuspec = NuGet\Metapackages\RGB.NET.Presets.nuspec EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RGB.NET.Devices.Razer", "RGB.NET.Devices.Razer\RGB.NET.Devices.Razer.csproj", "{24FF4ACB-D679-4B2D-86D4-50AB6C02D816}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -128,6 +128,10 @@ Global {4EFD77C7-FDB4-4C6B-970C-0EF66D24BE09}.Debug|Any CPU.Build.0 = Debug|Any CPU {4EFD77C7-FDB4-4C6B-970C-0EF66D24BE09}.Release|Any CPU.ActiveCfg = Release|Any CPU {4EFD77C7-FDB4-4C6B-970C-0EF66D24BE09}.Release|Any CPU.Build.0 = Release|Any CPU + {24FF4ACB-D679-4B2D-86D4-50AB6C02D816}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24FF4ACB-D679-4B2D-86D4-50AB6C02D816}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24FF4ACB-D679-4B2D-86D4-50AB6C02D816}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24FF4ACB-D679-4B2D-86D4-50AB6C02D816}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -146,6 +150,7 @@ Global {4F2F3FBD-A1E4-4968-A2AD-0514959E5E59} = {33D5E279-1C4E-4AB6-9D1E-6D18109A6C25} {4EFD77C7-FDB4-4C6B-970C-0EF66D24BE09} = {33D5E279-1C4E-4AB6-9D1E-6D18109A6C25} {C191D5BE-E1B2-47E4-AB39-D954B277188C} = {06416566-481F-4571-80EE-7BB05B1E0299} + {24FF4ACB-D679-4B2D-86D4-50AB6C02D816} = {33D5E279-1C4E-4AB6-9D1E-6D18109A6C25} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CDECA6C7-8D18-4AF3-94F7-C70A69B8571B}