diff --git a/HardwareInformation/HardwareInformation.csproj b/HardwareInformation/HardwareInformation.csproj index 6b7598c..62829ad 100644 --- a/HardwareInformation/HardwareInformation.csproj +++ b/HardwareInformation/HardwareInformation.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + netstandard2.0 true L3tum LICENSE @@ -42,4 +42,8 @@ + + + + diff --git a/HardwareInformation/Mentions.md b/HardwareInformation/Mentions.md new file mode 100644 index 0000000..95dcb82 --- /dev/null +++ b/HardwareInformation/Mentions.md @@ -0,0 +1,23 @@ +# Marked as "Taken from https://github.com/pruggitorg/detect-windows-version" + +MIT License + +Copyright (c) 2019 pruggitorg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/HardwareInformation/Providers/WindowsInformationProvider.cs b/HardwareInformation/Providers/WindowsInformationProvider.cs index 3b477aa..ae91538 100644 --- a/HardwareInformation/Providers/WindowsInformationProvider.cs +++ b/HardwareInformation/Providers/WindowsInformationProvider.cs @@ -5,393 +5,662 @@ using System.Linq; using System.Management; using System.Runtime.InteropServices; +using System.Security; using HardwareInformation.Information; #endregion namespace HardwareInformation.Providers { - internal class WindowsInformationProvider : InformationProvider - { - public void GatherInformation(ref MachineInformation information) - { - var mos = new ManagementObjectSearcher( - "select Name,NumberOfEnabledCore,NumberOfLogicalProcessors,SocketDesignation,MaxClockSpeed from Win32_Processor"); - - foreach (var managementBaseObject in mos.Get()) - { - if (managementBaseObject == null || managementBaseObject.Properties == null || - managementBaseObject.Properties.Count == 0) - { - continue; - } - - foreach (var propertyData in managementBaseObject.Properties) - { - if (propertyData == null || propertyData.Value == null || propertyData.Name == null) - { - continue; - } - - switch (propertyData.Name) - { - case "Name": - { - if (information.Cpu.Name == default || information.Cpu.Name == information.Cpu.Caption) - { - information.Cpu.Name = propertyData.Value.ToString().Trim(); - } - - break; - } - - // MIND THE SSSSSSSS - case "NumberOfEnabledCore": - { - var val = uint.Parse(propertyData.Value.ToString()); - - // Safety check - if (information.Cpu.PhysicalCores == default || - information.Cpu.PhysicalCores == information.Cpu.LogicalCores || - val != 0 && val != information.Cpu.PhysicalCores) - { - information.Cpu.PhysicalCores = val; - } - - break; - } - - case "NumberOfLogicalProcessors": - { - var val = uint.Parse(propertyData.Value.ToString()); - - if (information.Cpu.LogicalCores == default || - val != 0 && val != information.Cpu.LogicalCores) - { - information.Cpu.LogicalCores = val; - } - - break; - } - - case "SocketDesignation": - { - if (information.Cpu.Socket == default) - { - information.Cpu.Socket = propertyData.Value.ToString().Trim(); - } - - break; - } - - case "MaxClockSpeed": - { - if (information.Cpu.NormalClockSpeed == default) - { - information.Cpu.NormalClockSpeed = uint.Parse(propertyData.Value.ToString()); - } - - break; - } - } - } - } - - // There is currently no other way to gather RAM information so we don't need to check if it's already set - mos = new ManagementObjectSearcher( - "select ConfiguredClockSpeed,Manufacturer,Capacity,DeviceLocator,PartNumber,FormFactor from Win32_PhysicalMemory"); - - foreach (var managementBaseObject in mos.Get()) - { - if (managementBaseObject == null || managementBaseObject.Properties == null || - managementBaseObject.Properties.Count == 0) - { - continue; - } - - var ram = new RAM(); - - foreach (var propertyData in managementBaseObject.Properties) - { - if (propertyData == null || propertyData.Value == null || propertyData.Name == null) - { - continue; - } - - switch (propertyData.Name) - { - case "ConfiguredClockSpeed": - { - ram.Speed = uint.Parse(propertyData.Value.ToString()); - - break; - } - - case "Manufacturer": - { - ram.Manufacturer = propertyData.Value.ToString(); - - break; - } - - case "Capacity": - { - ram.Capacity += ulong.Parse(propertyData.Value.ToString()); - - break; - } - - case "DeviceLocator": - { - ram.Name = propertyData.Value.ToString(); - - break; - } - - case "PartNumber": - { - ram.PartNumber = propertyData.Value.ToString(); - - break; - } - - case "FormFactor": - { - ram.FormFactor = (RAM.FormFactors) Enum.Parse( - typeof(RAM.FormFactors), propertyData.Value.ToString()); - - break; - } - } - } - - ram.CapacityHRF = Util.FormatBytes(ram.Capacity); - - information.RAMSticks.Add(ram); - } - - mos = new ManagementObjectSearcher("select Name,Manufacturer,Version from Win32_BIOS"); - - foreach (var managementBaseObject in mos.Get()) - { - if (managementBaseObject == null || managementBaseObject.Properties == null || - managementBaseObject.Properties.Count == 0) - { - continue; - } - - foreach (var propertyData in managementBaseObject.Properties) - { - if (propertyData == null || propertyData.Value == null || propertyData.Name == null) - { - continue; - } - - switch (propertyData.Name) - { - case "Name": - { - information.SmBios.BIOSVersion = propertyData.Value.ToString(); - - break; - } - - case "Manufacturer": - { - information.SmBios.BIOSVendor = propertyData.Value.ToString(); - - break; - } - - case "Version": - { - information.SmBios.BIOSCodename = propertyData.Value.ToString(); - - break; - } - } - } - } - - mos = new ManagementObjectSearcher("select Product,Manufacturer,Version from Win32_BaseBoard"); - - foreach (var managementBaseObject in mos.Get()) - { - if (managementBaseObject == null || managementBaseObject.Properties == null || - managementBaseObject.Properties.Count == 0) - { - continue; - } - - foreach (var propertyData in managementBaseObject.Properties) - { - if (propertyData == null || propertyData.Value == null || propertyData.Name == null) - { - continue; - } - - switch (propertyData.Name) - { - case "Product": - { - information.SmBios.BoardName = propertyData.Value.ToString(); - - break; - } - - case "Manufacturer": - { - information.SmBios.BoardVendor = propertyData.Value.ToString(); - - break; - } - - case "Version": - { - information.SmBios.BoardVersion = propertyData.Value.ToString(); - - break; - } - } - } - } - - mos = new ManagementObjectSearcher("select Model,Size,Caption from Win32_DiskDrive"); - - foreach (var managementBaseObject in mos.Get()) - { - var disk = new Disk(); - - foreach (var propertyData in managementBaseObject.Properties) - { - switch (propertyData.Name) - { - case "Model": - { - disk.Model = propertyData.Value.ToString(); - break; - } - case "Size": - { - disk.Capacity = ulong.Parse(propertyData.Value.ToString()); - disk.CapacityHRF = Util.FormatBytes(disk.Capacity); - break; - } - case "Caption": - { - disk.Caption = propertyData.Value.ToString(); - break; - } - } - } - - information.Disks.Add(disk); - } - - mos = new ManagementObjectSearcher( - "select AdapterCompatibility,Caption,Description,DriverDate,DriverVersion,Name,Status from Win32_VideoController"); - - foreach (var managementBaseObject in mos.Get()) - { - var gpu = new GPU(); - - foreach (var propertyData in managementBaseObject.Properties) - { - switch (propertyData.Name) - { - case "AdapterCompatibility": - { - gpu.Vendor = propertyData.Value.ToString(); - - break; - } - case "Caption": - { - gpu.Caption = propertyData.Value.ToString(); - - break; - } - case "DriverDate": - { - gpu.DriverDate = propertyData.Value.ToString(); - - break; - } - case "DriverVersion": - { - gpu.DriverVersion = propertyData.Value.ToString(); - - break; - } - case "Description": - { - gpu.Description = propertyData.Value.ToString(); - - break; - } - case "Name": - { - gpu.Name = propertyData.Value.ToString(); - - break; - } - case "Status": - { - gpu.Status = propertyData.Value.ToString(); - - break; - } - } - } - - information.Gpus.Add(gpu); - } - - mos = new ManagementObjectSearcher("root\\wmi", - "select ManufacturerName,UserFriendlyName from WmiMonitorID"); - - foreach (var managementBaseObject in mos.Get()) - { - try - { - var display = new Display(); - - foreach (var propertyData in managementBaseObject.Properties) - { - switch (propertyData.Name) - { - case "ManufacturerName": - { - display.Manufacturer = string.Join("", ((IEnumerable) propertyData.Value) - .Select(u => char.ConvertFromUtf32(u)).Where(s => s != "\u0000").ToList()); - break; - } - case "UserFriendlyName": - { - display.Name = string.Join("", ((IEnumerable) propertyData.Value) - .Select(u => char.ConvertFromUtf32(u)).Where(s => s != "\u0000").ToList()); - break; - } - } - } - - information.Displays.Add(display); - } - catch - { - // Intentionally left blank - } - } - } - - public bool Available(MachineInformation information) - { - return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - } - - public void PostProviderUpdateInformation(ref MachineInformation information) - { - // Intentionally left blank - } - } + #region Win32API + + // Taken from https://github.com/pruggitorg/detect-windows-version + internal enum ProductType : byte + { + /// + /// The operating system is Windows 10, Windows 8, Windows 7,... + /// + /// VER_NT_WORKSTATION + Workstation = 0x0000001, + + /// + /// The system is a domain controller and the operating system is Windows Server. + /// + /// VER_NT_DOMAIN_CONTROLLER + DomainController = 0x0000002, + + /// + /// The operating system is Windows Server. Note that a server that is also a domain controller + /// is reported as VER_NT_DOMAIN_CONTROLLER, not VER_NT_SERVER. + /// + /// VER_NT_SERVER + Server = 0x0000003 + } + + + // Taken from https://github.com/pruggitorg/detect-windows-version + [Flags] + internal enum SuiteMask : ushort + { + /// + /// Microsoft BackOffice components are installed. + /// + VER_SUITE_BACKOFFICE = 0x00000004, + + /// + /// Windows Server 2003, Web Edition is installed + /// + VER_SUITE_BLADE = 0x00000400, + + /// + /// Windows Server 2003, Compute Cluster Edition is installed. + /// + VER_SUITE_COMPUTE_SERVER = 0x00004000, + + /// + /// Windows Server 2008 Datacenter, Windows Server 2003, Datacenter Edition, or Windows 2000 Datacenter Server is + /// installed. + /// + VER_SUITE_DATACENTER = 0x00000080, + + /// + /// Windows Server 2008 Enterprise, Windows Server 2003, Enterprise Edition, or Windows 2000 Advanced Server is + /// installed. + /// Refer to the Remarks section for more information about this bit flag. + /// + VER_SUITE_ENTERPRISE = 0x00000002, + + /// + /// Windows XP Embedded is installed. + /// + VER_SUITE_EMBEDDEDNT = 0x00000040, + + /// + /// Windows Vista Home Premium, Windows Vista Home Basic, or Windows XP Home Edition is installed. + /// + VER_SUITE_PERSONAL = 0x00000200, + + /// + /// Remote Desktop is supported, but only one interactive session is supported. This value is set unless the system is + /// running in application server mode. + /// + VER_SUITE_SINGLEUSERTS = 0x00000100, + + /// + /// Microsoft Small Business Server was once installed on the system, but may have been upgraded to another version of + /// Windows. + /// Refer to the Remarks section for more information about this bit flag. + /// + VER_SUITE_SMALLBUSINESS = 0x00000001, + + /// + /// Microsoft Small Business Server is installed with the restrictive client license in force. Refer to the Remarks + /// section for more information about this bit flag. + /// + VER_SUITE_SMALLBUSINESS_RESTRICTED = 0x00000020, + + /// + /// Windows Storage Server 2003 R2 or Windows Storage Server 2003is installed. + /// + VER_SUITE_STORAGE_SERVER = 0x00002000, + + /// + /// Terminal Services is installed. This value is always set. + /// If VER_SUITE_TERMINAL is set but VER_SUITE_SINGLEUSERTS is not set, the system is running in application server + /// mode. + /// + VER_SUITE_TERMINAL = 0x00000010, + + /// + /// Windows Home Server is installed. + /// + VER_SUITE_WH_SERVER = 0x00008000 + + //VER_SUITE_MULTIUSERTS = 0x00020000 + } + + // Taken from https://github.com/pruggitorg/detect-windows-version + internal enum NTSTATUS : uint + { + /// + /// The operation completed successfully. + /// + STATUS_SUCCESS = 0x00000000 + } + + // Taken from https://github.com/pruggitorg/detect-windows-version + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal struct OSVERSIONINFOEX + { + // The OSVersionInfoSize field must be set to Marshal.SizeOf(typeof(OSVERSIONINFOEX)) + public int OSVersionInfoSize; + public int MajorVersion; + public int MinorVersion; + public int BuildNumber; + public int PlatformId; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + public string CSDVersion; + + public ushort ServicePackMajor; + public ushort ServicePackMinor; + public SuiteMask SuiteMask; + public ProductType ProductType; + public byte Reserved; + } + + internal static class Win32APIProvider + { + private const string NTDLL = "ntdll.dll"; + + [SecurityCritical] + [DllImport(NTDLL, EntryPoint = "RtlGetVersion", SetLastError = true, CharSet = CharSet.Unicode)] + internal static extern NTSTATUS ntdll_RtlGetVersion(ref OSVERSIONINFOEX versionInfo); + } + + #endregion + + internal class WindowsInformationProvider : InformationProvider + { + public void GatherInformation(ref MachineInformation information) + { + var win10 = false; + + try + { + var osVersionInfoEx = new OSVERSIONINFOEX(); + var success = Win32APIProvider.ntdll_RtlGetVersion(ref osVersionInfoEx); + + if (success == NTSTATUS.STATUS_SUCCESS) + { + if (osVersionInfoEx.MajorVersion >= 10) + { + win10 = true; + } + } + } + catch + { + // Intentionally left blank + } + + try + { + GatherWin32ProcessorInformation(ref information, win10); + } + catch + { + // Intentionally left blank + } + + try + { + GatherWin32PhysicalMemory(ref information, win10); + } + catch + { + // Intentionally left blank + } + + try + { + GatherWin32Bios(ref information, win10); + } + catch + { + // Intentionally left blank + } + + try + { + GatherWin32BaseBoard(ref information, win10); + } + catch + { + // Intentionally left blank + } + + try + { + GatherWin32DiskDrive(ref information, win10); + } + catch + { + // Intentionally left blank + } + + try + { + GatherWin32VideoController(ref information, win10); + } + catch + { + // Intentionally left blank + } + + try + { + GatherWmiMonitorId(ref information, win10); + } + catch + { + // Intentionally left blank + } + } + + public bool Available(MachineInformation information) + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + } + + public void PostProviderUpdateInformation(ref MachineInformation information) + { + // Intentionally left blank + } + + private void GatherWin32BaseBoard(ref MachineInformation information, bool win10) + { + var mos = new ManagementObjectSearcher("select Product,Manufacturer,Version from Win32_BaseBoard"); + + foreach (var managementBaseObject in mos.Get()) + { + if (managementBaseObject?.Properties == null || managementBaseObject.Properties.Count == 0) + { + continue; + } + + foreach (var propertyData in managementBaseObject.Properties) + { + if (propertyData?.Value == null) + { + continue; + } + + switch (propertyData.Name) + { + case "Product": + { + information.SmBios.BoardName = propertyData.Value.ToString(); + + break; + } + + case "Manufacturer": + { + information.SmBios.BoardVendor = propertyData.Value.ToString(); + + break; + } + + case "Version": + { + information.SmBios.BoardVersion = propertyData.Value.ToString(); + + break; + } + } + } + } + } + + private void GatherWin32DiskDrive(ref MachineInformation information, bool win10) + { + var mos = new ManagementObjectSearcher("select Model,Size,Caption from Win32_DiskDrive"); + + foreach (var managementBaseObject in mos.Get()) + { + var disk = new Disk(); + + foreach (var propertyData in managementBaseObject.Properties) + { + switch (propertyData.Name) + { + case "Model": + { + disk.Model = propertyData.Value.ToString(); + break; + } + case "Size": + { + disk.Capacity = ulong.Parse(propertyData.Value.ToString()); + disk.CapacityHRF = Util.FormatBytes(disk.Capacity); + break; + } + case "Caption": + { + disk.Caption = propertyData.Value.ToString(); + break; + } + } + } + + information.Disks.Add(disk); + } + } + + private void GatherWin32VideoController(ref MachineInformation information, bool win10) + { + var mos = new ManagementObjectSearcher( + "select AdapterCompatibility,Caption,Description,DriverDate,DriverVersion,Name,Status from Win32_VideoController"); + + foreach (var managementBaseObject in mos.Get()) + { + var gpu = new GPU(); + + foreach (var propertyData in managementBaseObject.Properties) + { + switch (propertyData.Name) + { + case "AdapterCompatibility": + { + gpu.Vendor = propertyData.Value.ToString(); + + break; + } + case "Caption": + { + gpu.Caption = propertyData.Value.ToString(); + + break; + } + case "DriverDate": + { + gpu.DriverDate = propertyData.Value.ToString(); + + break; + } + case "DriverVersion": + { + gpu.DriverVersion = propertyData.Value.ToString(); + + break; + } + case "Description": + { + gpu.Description = propertyData.Value.ToString(); + + break; + } + case "Name": + { + gpu.Name = propertyData.Value.ToString(); + + break; + } + case "Status": + { + gpu.Status = propertyData.Value.ToString(); + + break; + } + } + } + + information.Gpus.Add(gpu); + } + } + + private void GatherWmiMonitorId(ref MachineInformation information, bool win10) + { + var mos = new ManagementObjectSearcher("root\\wmi", + "select ManufacturerName,UserFriendlyName from WmiMonitorID"); + + foreach (var managementBaseObject in mos.Get()) + { + try + { + var display = new Display(); + + foreach (var propertyData in managementBaseObject.Properties) + { + switch (propertyData.Name) + { + case "ManufacturerName": + { + display.Manufacturer = string.Join("", ((IEnumerable) propertyData.Value) + .Select(u => char.ConvertFromUtf32(u)).Where(s => s != "\u0000").ToList()); + break; + } + case "UserFriendlyName": + { + display.Name = string.Join("", ((IEnumerable) propertyData.Value) + .Select(u => char.ConvertFromUtf32(u)).Where(s => s != "\u0000").ToList()); + break; + } + } + } + + information.Displays.Add(display); + } + catch + { + // Intentionally left blank + } + } + } + + private void GatherWin32ProcessorInformation(ref MachineInformation information, bool win10) + { + ManagementObjectSearcher mos; + + if (win10) + { + mos = new ManagementObjectSearcher( + "select Name,NumberOfEnabledCore,NumberOfLogicalProcessors,SocketDesignation,MaxClockSpeed from Win32_Processor"); + } + else + { + mos = new ManagementObjectSearcher( + "select Name,NumberOfLogicalProcessors,SocketDesignation,MaxClockSpeed from Win32_Processor"); + } + + foreach (var managementBaseObject in mos.Get()) + { + if (managementBaseObject?.Properties == null || managementBaseObject.Properties.Count == 0) + { + continue; + } + + foreach (var propertyData in managementBaseObject.Properties) + { + if (propertyData?.Value == null || propertyData.Name == null) + { + continue; + } + + switch (propertyData.Name) + { + case "Name": + { + if (information.Cpu.Name == default || information.Cpu.Name == information.Cpu.Caption) + { + information.Cpu.Name = propertyData.Value.ToString().Trim(); + } + + break; + } + + // MIND THE SSSSSSSS + case "NumberOfEnabledCore": + { + var val = uint.Parse(propertyData.Value.ToString()); + + // Safety check + if (information.Cpu.PhysicalCores == default || + information.Cpu.PhysicalCores == information.Cpu.LogicalCores || + val != 0 && val != information.Cpu.PhysicalCores) + { + information.Cpu.PhysicalCores = val; + } + + break; + } + + case "NumberOfLogicalProcessors": + { + var val = uint.Parse(propertyData.Value.ToString()); + + if (information.Cpu.LogicalCores == default || + val != 0 && val != information.Cpu.LogicalCores) + { + information.Cpu.LogicalCores = val; + } + + break; + } + + case "SocketDesignation": + { + if (information.Cpu.Socket == default) + { + information.Cpu.Socket = propertyData.Value.ToString().Trim(); + } + + break; + } + + case "MaxClockSpeed": + { + if (information.Cpu.NormalClockSpeed == default) + { + information.Cpu.NormalClockSpeed = uint.Parse(propertyData.Value.ToString()); + } + + break; + } + } + } + } + } + + private void GatherWin32PhysicalMemory(ref MachineInformation information, bool win10) + { + ManagementObjectSearcher mos; + + if (win10) + { + mos = new ManagementObjectSearcher( + "select ConfiguredClockSpeed,Manufacturer,Capacity,DeviceLocator,PartNumber,FormFactor from Win32_PhysicalMemory"); + } + else + { + mos = new ManagementObjectSearcher( + "select Manufacturer,Capacity,DeviceLocator,PartNumber,FormFactor from Win32_PhysicalMemory"); + } + + // There is currently no other way to gather RAM information so we don't need to check if it's already set + foreach (var managementBaseObject in mos.Get()) + { + if (managementBaseObject?.Properties == null || managementBaseObject.Properties.Count == 0) + { + continue; + } + + var ram = new RAM(); + + foreach (var propertyData in managementBaseObject.Properties) + { + if (propertyData?.Value == null) + { + continue; + } + + switch (propertyData.Name) + { + case "ConfiguredClockSpeed": + { + ram.Speed = uint.Parse(propertyData.Value.ToString()); + + break; + } + + case "Manufacturer": + { + ram.Manufacturer = propertyData.Value.ToString(); + + break; + } + + case "Capacity": + { + ram.Capacity += ulong.Parse(propertyData.Value.ToString()); + + break; + } + + case "DeviceLocator": + { + ram.Name = propertyData.Value.ToString(); + + break; + } + + case "PartNumber": + { + ram.PartNumber = propertyData.Value.ToString(); + + break; + } + + case "FormFactor": + { + ram.FormFactor = (RAM.FormFactors) Enum.Parse( + typeof(RAM.FormFactors), propertyData.Value.ToString()); + + break; + } + } + } + + ram.CapacityHRF = Util.FormatBytes(ram.Capacity); + + information.RAMSticks.Add(ram); + } + } + + private void GatherWin32Bios(ref MachineInformation information, bool win10) + { + var mos = new ManagementObjectSearcher("select Name,Manufacturer,Version from Win32_BIOS"); + + foreach (var managementBaseObject in mos.Get()) + { + if (managementBaseObject?.Properties == null || managementBaseObject.Properties.Count == 0) + { + continue; + } + + foreach (var propertyData in managementBaseObject.Properties) + { + if (propertyData?.Value == null) + { + continue; + } + + switch (propertyData.Name) + { + case "Name": + { + information.SmBios.BIOSVersion = propertyData.Value.ToString(); + + break; + } + + case "Manufacturer": + { + information.SmBios.BIOSVendor = propertyData.Value.ToString(); + + break; + } + + case "Version": + { + information.SmBios.BIOSCodename = propertyData.Value.ToString(); + + break; + } + } + } + } + } + } } \ No newline at end of file diff --git a/LICENSE b/LICENSE index 688c54b..930d2c2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 L3tum +Copyright (c) 2019-2020 L3tum Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal