diff --git a/Contralto/Configuration.cs b/Contralto/Configuration.cs
index 1024d49..be6f8f0 100644
--- a/Contralto/Configuration.cs
+++ b/Contralto/Configuration.cs
@@ -111,6 +111,9 @@ namespace Contralto
break;
}
+ // See if PCap is available.
+ TestPCap();
+
ReadConfiguration();
// Special case: On first startup, AlternateBoot will come back as "None" which
@@ -201,6 +204,17 @@ namespace Contralto
///
public static string AudioDACCapturePath;
+ ///
+ /// The components to enable debug logging for.
+ ///
+ public static LogComponent LogComponents;
+
+ ///
+ /// The types of logging to enable.
+ ///
+ public static LogType LogTypes;
+
+
public static string GetAltoIRomPath(string romFileName)
{
return Path.Combine("ROM", "AltoI", romFileName);
@@ -262,7 +276,7 @@ namespace Contralto
private static void ReadConfigurationWindows()
{
- Properties.Settings.Default.AudioDACCapturePath = Properties.Settings.Default.AudioDACCapturePath;
+ AudioDACCapturePath = Properties.Settings.Default.AudioDACCapturePath;
Drive0Image = Properties.Settings.Default.Drive0Image;
Drive1Image = Properties.Settings.Default.Drive1Image;
SystemType = (SystemType)Properties.Settings.Default.SystemType;
@@ -462,6 +476,23 @@ namespace Contralto
}
}
}
+
+ private static void TestPCap()
+ {
+ // Just try enumerating interfaces, if this fails for any reason we assume
+ // PCap is not properly installed.
+ try
+ {
+ SharpPcap.CaptureDeviceList devices = SharpPcap.CaptureDeviceList.Instance;
+ Configuration.HostRawEthernetInterfacesAvailable = true;
+ }
+ catch
+ {
+ Configuration.HostRawEthernetInterfacesAvailable = false;
+ }
+ }
+
+
}
}
diff --git a/Contralto/Contralto.csproj b/Contralto/Contralto.csproj
index 843d199..df2439c 100644
--- a/Contralto/Contralto.csproj
+++ b/Contralto/Contralto.csproj
@@ -101,29 +101,15 @@
..\packages\NAudio.1.8.0\lib\net35\NAudio.dll
True
-
- False
- ..\packages\Pcap.Net.x86.1.0.4.1\lib\net45\PcapDotNet.Base.dll
- True
-
-
- False
- ..\packages\Pcap.Net.x86.1.0.4.1\lib\net45\PcapDotNet.Core.dll
- True
-
-
- False
- ..\packages\Pcap.Net.x86.1.0.4.1\lib\net45\PcapDotNet.Core.Extensions.dll
- True
-
-
- False
- ..\packages\Pcap.Net.x86.1.0.4.1\lib\net45\PcapDotNet.Packets.dll
- True
+
+ ..\packages\Sharp_Pcap.4.2.0\lib\PacketDotNet.dll
..\packages\SDL2-CS.dll.2.0.0.0\lib\net20\SDL2-CS.dll
+
+ ..\packages\Sharp_Pcap.4.2.0\lib\SharpPcap.dll
+
diff --git a/Contralto/IO/EthernetController.cs b/Contralto/IO/EthernetController.cs
index 6bba325..43613c1 100644
--- a/Contralto/IO/EthernetController.cs
+++ b/Contralto/IO/EthernetController.cs
@@ -62,9 +62,10 @@ namespace Contralto.IO
break;
}
}
- catch
- {
+ catch(Exception e)
+ {
_hostInterface = null;
+ Log.Write(LogComponent.HostNetworkInterface, "Unable to configure network interface. Error {0}", e.Message);
}
// More words than the Alto will ever send.
diff --git a/Contralto/IO/HostEthernetEncapsulation.cs b/Contralto/IO/HostEthernetEncapsulation.cs
index 2e86bf1..48c2308 100644
--- a/Contralto/IO/HostEthernetEncapsulation.cs
+++ b/Contralto/IO/HostEthernetEncapsulation.cs
@@ -15,20 +15,18 @@
along with ContrAlto. If not, see .
*/
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using SharpPcap;
+using SharpPcap.WinPcap;
+using SharpPcap.LibPcap;
+using SharpPcap.AirPcap;
+using PacketDotNet;
+
+using System;
+using System.Net.NetworkInformation;
+
-using PcapDotNet.Base;
-using PcapDotNet.Core;
-using PcapDotNet.Core.Extensions;
-using PcapDotNet.Packets;
-using PcapDotNet.Packets.Ethernet;
-using System.IO;
using Contralto.Logging;
-using System.Threading;
+
namespace Contralto.IO
{
@@ -39,29 +37,17 @@ namespace Contralto.IO
{
public EthernetInterface(string name, string description)
{
- Name = name;
- Description = description;
- }
-
- public static List EnumerateDevices()
- {
- List interfaces = new List();
-
- foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine)
- {
- interfaces.Add(new EthernetInterface(device.Name, device.Description));
- }
-
- return interfaces;
- }
+ Name = name;
+ Description = description;
+ }
public override string ToString()
{
return String.Format("{0} ({1})", Name, Description);
}
- public string Name;
- public string Description;
+ public string Name;
+ public string Description;
}
///
@@ -73,20 +59,35 @@ namespace Contralto.IO
public class HostEthernetEncapsulation : IPacketEncapsulation
{
public HostEthernetEncapsulation(string name)
- {
+ {
// Find the specified device by name
- foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine)
+ foreach (ICaptureDevice device in CaptureDeviceList.Instance)
{
- if (device.GetNetworkInterface().Name.ToLowerInvariant() == Configuration.HostPacketInterfaceName.ToLowerInvariant())
+ if (device is WinPcapDevice)
{
- AttachInterface(device);
- break;
+ //
+ // We use the friendly name to make it easier to specify in config files.
+ //
+ if (((WinPcapDevice)device).Interface.FriendlyName.ToLowerInvariant() == name.ToLowerInvariant())
+ {
+ AttachInterface(device);
+ break;
+ }
+ }
+ else
+ {
+ if (device.Name.ToLowerInvariant() == name.ToLowerInvariant())
+ {
+ AttachInterface(device);
+ break;
+ }
}
}
if (_interface == null)
{
- throw new InvalidOperationException("Specified ethernet interface does not exist or is not compatible with WinPCAP.");
+ Log.Write(LogComponent.HostNetworkInterface, "Specified ethernet interface does not exist or is not compatible with ContrAlto.");
+ throw new InvalidOperationException("Specified ethernet interface does not exist or is not compatible with ContrAlto.");
}
}
@@ -95,20 +96,31 @@ namespace Contralto.IO
_callback = callback;
// Now that we have a callback we can start receiving stuff.
- Open(false /* not promiscuous */, int.MaxValue);
+ Open(false /* not promiscuous */, 0);
BeginReceive();
}
public void Shutdown()
{
- if (_communicator != null)
+ if (_interface != null)
{
- _communicator.Break();
- }
-
- if (_receiveThread != null)
- {
- _receiveThread.Abort();
+ try
+ {
+ if (_interface.Started)
+ {
+ _interface.StopCapture();
+ }
+ }
+ catch
+ {
+ // Eat exceptions. The Pcap libs seem to throw on StopCapture on
+ // Unix platforms, we don't really care about them (since we're shutting down anyway)
+ // but this prevents debug spew from appearing on the console.
+ }
+ finally
+ {
+ _interface.Close();
+ }
}
}
@@ -159,50 +171,48 @@ namespace Contralto.IO
Conversion.ToOctal(destinationHost),
length);
- MacAddress destinationMac = new MacAddress(_10mbitBroadcast);
- MacAddress sourceMac = new MacAddress((UInt48)(_10mbitMACPrefix | Configuration.HostAddress));
+ _10mbitMACPrefix[5] = Configuration.HostAddress; // Stuff our current Alto host address into the 10mbit MAC
- // Build the outgoing packet; place the source/dest addresses, type field and the raw data.
- EthernetLayer ethernetLayer = new EthernetLayer
- {
- Source = sourceMac,
- Destination = destinationMac,
- EtherType = (EthernetType)_3mbitFrameType,
- };
+ EthernetPacket p = new EthernetPacket(
+ new PhysicalAddress(_10mbitMACPrefix), // Source address
+ _10mbitBroadcast, // Destnation (broadcast)
+ (EthernetPacketType)_3mbitFrameType);
- PayloadLayer payloadLayer = new PayloadLayer
- {
- Data = new Datagram(packetBytes),
- };
-
- PacketBuilder builder = new PacketBuilder(ethernetLayer, payloadLayer);
+ p.PayloadData = packetBytes;
// Send it over the 'net!
- _communicator.SendPacket(builder.Build(DateTime.Now));
+ _interface.SendPacket(p);
Log.Write(LogComponent.HostNetworkInterface, "Encapsulated 3mbit packet sent.");
}
- private void ReceiveCallback(Packet p)
+ private void ReceiveCallback(object sender, CaptureEventArgs e)
{
//
// Filter out packets intended for the emulator, forward them on, drop everything else.
//
- if ((int)p.Ethernet.EtherType == _3mbitFrameType && // encapsulated 3mbit frames
- (p.Ethernet.Source.ToValue() != (UInt48)(_10mbitMACPrefix | Configuration.HostAddress))) // and not sent by this emulator
+ if (e.Packet.LinkLayerType == PacketDotNet.LinkLayers.Ethernet)
{
- Log.Write(LogComponent.HostNetworkInterface, "Received encapsulated 3mbit packet.");
- _callback(p.Ethernet.Payload.ToMemoryStream());
- }
- else
- {
- // Not for us, discard the packet.
+ EthernetPacket packet = (EthernetPacket)PacketDotNet.Packet.ParsePacket(PacketDotNet.LinkLayers.Ethernet, e.Packet.Data);
+
+ _10mbitMACPrefix[5] = Configuration.HostAddress;
+
+ if ((int)packet.Type == _3mbitFrameType && // encapsulated 3mbit frames
+ (packet.SourceHwAddress != new System.Net.NetworkInformation.PhysicalAddress(_10mbitMACPrefix))) // and not sent by this emulator
+ {
+ Log.Write(LogComponent.HostNetworkInterface, "Received encapsulated 3mbit packet.");
+ _callback(new System.IO.MemoryStream(packet.PayloadData));
+ }
+ else
+ {
+ // Not for us, discard the packet.
+ }
}
}
- private void AttachInterface(LivePacketDevice iface)
+ private void AttachInterface(ICaptureDevice iface)
{
- _interface = iface;
+ _interface = iface;
if (_interface == null)
{
@@ -214,10 +224,18 @@ namespace Contralto.IO
private void Open(bool promiscuous, int timeout)
{
- _communicator = _interface.Open(65536, promiscuous ? PacketDeviceOpenAttributes.MaximumResponsiveness | PacketDeviceOpenAttributes.Promiscuous : PacketDeviceOpenAttributes.MaximumResponsiveness, timeout);
-
- // Set this to 1 so we'll get packets as soon as they arrive, no buffering.
- _communicator.SetKernelMinimumBytesToCopy(1);
+ if (_interface is WinPcapDevice)
+ {
+ ((WinPcapDevice)_interface).Open(promiscuous ? OpenFlags.MaxResponsiveness | OpenFlags.Promiscuous : OpenFlags.MaxResponsiveness, timeout);
+ }
+ else if (_interface is LibPcapLiveDevice)
+ {
+ ((LibPcapLiveDevice)_interface).Open(promiscuous ? DeviceMode.Promiscuous : DeviceMode.Normal, timeout);
+ }
+ else if (_interface is AirPcapDevice)
+ {
+ ((AirPcapDevice)_interface).Open(promiscuous ? OpenFlags.MaxResponsiveness | OpenFlags.Promiscuous : OpenFlags.MaxResponsiveness, timeout);
+ }
Log.Write(LogComponent.HostNetworkInterface, "Host interface opened and receiving packets.");
}
@@ -227,38 +245,23 @@ namespace Contralto.IO
///
private void BeginReceive()
{
- // Kick off receive thread.
- _receiveThread = new Thread(ReceiveThread);
- _receiveThread.Start();
+ // Kick off receiver.
+ _interface.OnPacketArrival += ReceiveCallback;
+ _interface.StartCapture();
}
- private void ReceiveThread()
- {
- // Just call ReceivePackets, that's it. This will never return.
- // (probably need to make this more elegant so we can tear down the thread
- // properly.)
- Log.Write(LogComponent.HostNetworkInterface, "Receiver thread started.");
-
- _communicator.ReceivePackets(-1, ReceiveCallback);
- }
-
- private LivePacketDevice _interface;
- private PacketCommunicator _communicator;
+ private ICaptureDevice _interface;
private ReceivePacketDelegate _callback;
-
- // Thread used for receive
- private Thread _receiveThread;
-
private const int _3mbitFrameType = 0xbeef; // easy to identify, ostensibly unused by anything of any import
///
/// On output, these bytes are prepended to the Alto's 3mbit (1 byte) address to form a full
/// 6 byte Ethernet MAC.
- /// On input, ethernet frames are checked for this prefix
+ /// On input, ethernet frames are checked for this prefix.
///
- private UInt48 _10mbitMACPrefix = 0x0000aa010200; // 00-00-AA is the Xerox vendor code, used just to be cute.
+ private byte[] _10mbitMACPrefix = { 0x00, 0x00, 0xaa, 0x01, 0x02, 0x00 }; // 00-00-AA is the Xerox vendor code, used just to be cute.
- private UInt48 _10mbitBroadcast = (UInt48)0xffffffffffff;
+ private PhysicalAddress _10mbitBroadcast = new PhysicalAddress(new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff });
}
}
diff --git a/Contralto/Logging/Log.cs b/Contralto/Logging/Log.cs
index c803337..db43779 100644
--- a/Contralto/Logging/Log.cs
+++ b/Contralto/Logging/Log.cs
@@ -75,11 +75,9 @@ namespace Contralto.Logging
{
static Log()
{
- // TODO: make configurable
- _components = LogComponent.DAC;
- _type = LogType.Normal | LogType.Warning | LogType.Error | LogType.Verbose;
+ _components = Configuration.LogComponents;
+ _type = Configuration.LogTypes;
- //_logStream = new StreamWriter("log.txt");
}
public static LogComponent LogComponents
@@ -110,11 +108,6 @@ namespace Contralto.Logging
// My log has something to tell you...
// TODO: color based on type, etc.
Console.WriteLine(component.ToString() + ": " + message, args);
-
- if (_logStream != null)
- {
- _logStream.WriteLine(component.ToString() + ": " + message, args);
- }
}
}
#else
@@ -132,6 +125,5 @@ namespace Contralto.Logging
private static LogComponent _components;
private static LogType _type;
- private static StreamWriter _logStream;
}
}
diff --git a/Contralto/Program.cs b/Contralto/Program.cs
index e552b55..d878630 100644
--- a/Contralto/Program.cs
+++ b/Contralto/Program.cs
@@ -40,10 +40,7 @@ namespace Contralto
}
// Handle command-line args
- PrintHerald();
-
- // See if WinPCap is installed and working
- TestPCap();
+ PrintHerald();
_system = new AltoSystem();
@@ -133,29 +130,7 @@ namespace Contralto
Console.WriteLine("Bug reports to joshd@livingcomputers.org");
Console.WriteLine();
}
-
- private static void TestPCap()
- {
- if (Configuration.Platform == PlatformType.Windows)
- {
- // Just try enumerating interfaces, if this fails for any reason we assume
- // PCap is not properly installed.
- try
- {
- List interfaces = EthernetInterface.EnumerateDevices();
- Configuration.HostRawEthernetInterfacesAvailable = true;
- }
- catch
- {
- Configuration.HostRawEthernetInterfacesAvailable = false;
- }
- }
- else
- {
- Configuration.HostRawEthernetInterfacesAvailable = false;
- }
- }
-
+
private static AltoSystem _system;
}
}
diff --git a/Contralto/SdlUI/SdlConsole.cs b/Contralto/SdlUI/SdlConsole.cs
index 1fd1af8..3afc195 100644
--- a/Contralto/SdlUI/SdlConsole.cs
+++ b/Contralto/SdlUI/SdlConsole.cs
@@ -154,7 +154,7 @@ namespace Contralto.SdlUI
{
if (_controller.IsRunning)
{
- Console.WriteLine("Alto is already running. Use 'stop' to stop the Alto first.");
+ _controller.Reset(AlternateBootType.Disk);
}
else
{
@@ -169,10 +169,10 @@ namespace Contralto.SdlUI
{
if (_controller.IsRunning)
{
- Console.WriteLine("Alto is already running. Use 'stop' to stop the Alto first.");
+ _controller.Reset(AlternateBootType.Ethernet);
}
else
- {
+ {
_controller.StartExecution(AlternateBootType.Ethernet);
}
diff --git a/Contralto/UI/SystemOptions.cs b/Contralto/UI/SystemOptions.cs
index c58f801..7d4af17 100644
--- a/Contralto/UI/SystemOptions.cs
+++ b/Contralto/UI/SystemOptions.cs
@@ -16,10 +16,9 @@
*/
using Contralto.IO;
-using PcapDotNet.Core;
-using PcapDotNet.Core.Extensions;
+using SharpPcap;
+using SharpPcap.WinPcap;
using System;
-using System.Collections.Generic;
using System.IO;
using System.Net.NetworkInformation;
using System.Windows.Forms;
@@ -79,7 +78,7 @@ namespace Contralto.UI
if (!Configuration.HostRawEthernetInterfacesAvailable)
{
- // If PCAP isn't installed, the RAW Ethernet option is not available.
+ // If PCAP isn't installed, the RAW Ethernet option is not available.
RawEthernetRadioButton.Enabled = false;
// Ensure the option isn't set in the configuration.
@@ -102,7 +101,7 @@ namespace Contralto.UI
case PacketInterfaceType.None:
NoEncapsulationRadioButton.Checked = true;
break;
- }
+ }
PopulateNetworkAdapterList(Configuration.HostPacketInterfaceType);
@@ -153,22 +152,21 @@ namespace Contralto.UI
case PacketInterfaceType.EthernetEncapsulation:
if (Configuration.HostRawEthernetInterfacesAvailable)
{
- foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine)
- {
- EthernetInterfaceListBox.Items.Add(new EthernetInterface(device.GetNetworkInterface().Name, device.GetNetworkInterface().Description));
- }
- }
+ foreach (WinPcapDevice device in CaptureDeviceList.Instance)
+ {
+ EthernetInterfaceListBox.Items.Add(new EthernetInterface(device.Interface.FriendlyName, device.Interface.Description));
+ }
+ }
break;
case PacketInterfaceType.None:
// Add nothing.
break;
- }
-
+ }
//
// Select the one that is already selected (if any)
- //
+ //
EthernetInterfaceListBox.SelectedIndex = 0;
if (!string.IsNullOrEmpty(Configuration.HostPacketInterfaceName))
diff --git a/Contralto/packages.config b/Contralto/packages.config
index 0d96e65..400781d 100644
--- a/Contralto/packages.config
+++ b/Contralto/packages.config
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/Contralto/readme-mono.txt b/Contralto/readme-mono.txt
index 7e92b76..41571fa 100644
--- a/Contralto/readme-mono.txt
+++ b/Contralto/readme-mono.txt
@@ -282,8 +282,8 @@ Drive0Image and Drive1Image: Specifies a disk image to be loaded into the
HostPacketInterfaceType: Specifies the type of interface to be used on the
host for Ethernet emulation. One of:
- UDPEncapsulation: Transmits Alto Ethernet packets over UDP broadcasts
- - RAWEncapsulation: Transmits Alto Ethernet packets over raw Ethernet packets.
- (not yet available on Unix / OS X platforms.)
+ - EthernetEncapsulation: Transmits Alto Ethernet packets over raw Ethernet packets.
+ (See Section 4.1 for configuration details)
- None: No packet encapsulation.
HostPacketInterfaceName: Specifies the name of the host network interface
@@ -295,6 +295,24 @@ BootFile: The file number to use with a Keyboard Net Boot (again, Section 5.0
AlternateBootType: The type of boot to default to (Section 5.0)
+4.1 Ethernet Encapsulation Setup
+================================
+
+Encapsulation of Alto (3mbit) Ethernet packets in Ethernet broadcasts is supported
+on Linux and OS X using libpcap. While it is tested and works well, it may require some extra
+configuration on your system before it will work properly for you.
+
+- Ensure that the latest libpcap libraries are installed. These should be present by default
+ on OS X; on other platforms check your distribution's documentation for details.
+
+- On many systems, libpcap requires additional privileges in order to capture packets.
+ You can either run ContrAlto as root, or setuid ContrAlto to root. Depending on
+ your operating system, there may be other options. See (for example)
+ http://www.tcpdump.org/manpages/pcap.3pcap.html.
+
+- You may need to modify SharpPcap.dll.config to point to the specific libpcap
+ version you have installed on your system.
+
5.0 Console Interface
=====================
@@ -388,7 +406,7 @@ At the moment, the following issues are known and being worked on. If you find
an issue not listed here, see section 7.0 to report a new bug.
- Smalltalk-80 does not run.
-- Audio and RAW ethernet packets are not available on Unix / OS X.
+- Audio is not available on Unix / OS X.
7.0 Reporting Bugs
@@ -424,6 +442,9 @@ Contributions are welcome!
ContrAlto would not have been possible without the amazing preservation work of
the Computer History Museum.
+Ethernet encapsulation is provided courtesy of SharpPcap, a WinPcap/LibPcap wrapper.
+See: https://github.com/chmorgan/sharppcap.
+
Audio output and capture on Windows is provided using the NAudio libraries, see:
https://github.com/naudio/NAudio.
@@ -442,6 +463,7 @@ V1.2
- Initial implementation of Orbit rasterization device; Dover ROS is implemented
but not working properly.
- Added ability to load a configuration file at startup
+- Switched to cross-platform SharpPcap library for Ethernet encapsulation.
V1.1
----
diff --git a/Contralto/readme.txt b/Contralto/readme.txt
index 54725a1..b8763d9 100644
--- a/Contralto/readme.txt
+++ b/Contralto/readme.txt
@@ -247,19 +247,9 @@ encapsulation. ContrAlto can encapsulate the Alto's 3mbit ("experimental")
Ethernet packets in either UDP datagrams or raw Ethernet packets on a network
interface on the "host" computer (the computer running ContrAlto).
-Raw packet encapsulation requires WinPCAP and the Microsoft Visual C++ 2010
-redistributable to be installed; these can be acquired from:
+Raw packet encapsulation requires WinPCAP libraries to be installed. See:
+http://www.winpcap.org/.
-http://www.winpcap.org/
-and
-http://www.microsoft.com/en-us/download/details.aspx?id=5555
-
-ContrAlto uses binaries from the Pcap.NET project to expose WinPCAP
-functionality to the emulator:
-
-https://github.com/PcapDotNet/Pcap.Net
-
-Pcap.NET is released under the BSD license.
4.2.1 Host Address
------------------
@@ -443,8 +433,8 @@ CPU Registers:
General Registers:
Shows the contents of the 32 R and 32 S registers (in octal).
- (The extra 7 sets of R and S registers on 3K CRAM machines are not yet
- displayed.)
+ (The extra 7 sets of R and S registers on 3K CRAM machines are not yet
+ displayed.)
Reserved Memory:
Shows the contents of most "well known" memory locations. See the
@@ -493,6 +483,9 @@ Contributions are welcome!
ContrAlto would not have been possible without the amazing preservation work of
the Computer History Museum.
+Ethernet encapsulation is provided courtesy of SharpPcap, a WinPcap/LibPcap wrapper.
+See: https://github.com/chmorgan/sharppcap.
+
Audio output and capture on Windows is provided using the NAudio libraries, see:
https://github.com/naudio/NAudio.
@@ -511,6 +504,7 @@ V1.2
- Initial implementation of Orbit rasterization device; Dover ROS is implemented
but not working properly.
- Added ability to load a configuration file at startup
+- Switched to cross-platform SharpPcap library for Ethernet encapsulation.
V1.1
----
diff --git a/ContraltoSetup/Product.wxs b/ContraltoSetup/Product.wxs
index a51a29b..6349803 100644
--- a/ContraltoSetup/Product.wxs
+++ b/ContraltoSetup/Product.wxs
@@ -17,18 +17,18 @@
-->
-
-
+
+
+ Minimum="1.2.0" IncludeMinimum="yes"
+ Maximum="1.2.0" IncludeMaximum="yes" />
+ Minimum="1.2.0" IncludeMinimum="no" />
-
+
@@ -38,8 +38,8 @@
-
-
+
+
@@ -47,7 +47,7 @@
-->
-
+
@@ -58,13 +58,13 @@
-
+
-
-
-
+
+
+
-
+
@@ -72,7 +72,7 @@
-
+
-->
-
+
-
-
-
+
+
+
-
-
-
-
+
+
@@ -120,7 +118,7 @@
-
+
@@ -225,6 +223,6 @@
-
+
\ No newline at end of file