1
0
mirror of https://github.com/livingcomputermuseum/ContrAlto.git synced 2026-01-20 09:54:35 +00:00

Switched from PCap.net to SharpPcap for ethernet encapsulation; SharpPcap is cross-platform, this allows raw ethernet encapsulation to work on Linux and OS X.

This commit is contained in:
Josh Dersch 2017-06-13 11:19:25 -07:00
parent aa9a2651ef
commit c671b04ee8
12 changed files with 214 additions and 214 deletions

View File

@ -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
/// </summary>
public static string AudioDACCapturePath;
/// <summary>
/// The components to enable debug logging for.
/// </summary>
public static LogComponent LogComponents;
/// <summary>
/// The types of logging to enable.
/// </summary>
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;
}
}
}
}

View File

@ -101,29 +101,15 @@
<HintPath>..\packages\NAudio.1.8.0\lib\net35\NAudio.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PcapDotNet.Base, Version=1.0.4.25027, Culture=neutral, PublicKeyToken=06a20bc2fabb1931, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Pcap.Net.x86.1.0.4.1\lib\net45\PcapDotNet.Base.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PcapDotNet.Core, Version=1.0.4.25067, Culture=neutral, PublicKeyToken=06a20bc2fabb1931, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Pcap.Net.x86.1.0.4.1\lib\net45\PcapDotNet.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PcapDotNet.Core.Extensions, Version=1.0.4.25069, Culture=neutral, PublicKeyToken=06a20bc2fabb1931, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Pcap.Net.x86.1.0.4.1\lib\net45\PcapDotNet.Core.Extensions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PcapDotNet.Packets, Version=1.0.4.25028, Culture=neutral, PublicKeyToken=06a20bc2fabb1931, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Pcap.Net.x86.1.0.4.1\lib\net45\PcapDotNet.Packets.dll</HintPath>
<Private>True</Private>
<Reference Include="PacketDotNet, Version=0.13.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sharp_Pcap.4.2.0\lib\PacketDotNet.dll</HintPath>
</Reference>
<Reference Include="SDL2-CS, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\SDL2-CS.dll.2.0.0.0\lib\net20\SDL2-CS.dll</HintPath>
</Reference>
<Reference Include="SharpPcap, Version=4.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sharp_Pcap.4.2.0\lib\SharpPcap.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />

View File

@ -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.

View File

@ -15,20 +15,18 @@
along with ContrAlto. If not, see <http://www.gnu.org/licenses/>.
*/
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<EthernetInterface> EnumerateDevices()
{
List<EthernetInterface> interfaces = new List<EthernetInterface>();
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;
}
/// <summary>
@ -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
/// </summary>
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
/// <summary>
/// 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.
/// </summary>
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 });
}
}

View File

@ -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;
}
}

View File

@ -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<EthernetInterface> interfaces = EthernetInterface.EnumerateDevices();
Configuration.HostRawEthernetInterfacesAvailable = true;
}
catch
{
Configuration.HostRawEthernetInterfacesAvailable = false;
}
}
else
{
Configuration.HostRawEthernetInterfacesAvailable = false;
}
}
private static AltoSystem _system;
}
}

View File

@ -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);
}

View File

@ -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))

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NAudio" version="1.8.0" targetFramework="net452" />
<package id="Pcap.Net.x86" version="1.0.4.1" targetFramework="net452" />
<package id="SDL2-CS.dll" version="2.0.0.0" targetFramework="net452" />
<package id="Sharp_Pcap" version="4.2.0" targetFramework="net452" />
</packages>

View File

@ -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
----

View File

@ -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
----

View File

@ -17,18 +17,18 @@
-->
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="CE5F3D08-3BD6-4BDF-9C64-0C2B852B899C" Name="ContrAlto" Language="1033" Version="1.1.0" Manufacturer="Living Computers: Museum+Labs" UpgradeCode="38d6b09f-6e7b-4854-844a-5d4ab707a357">
<Package Id="*" InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<Product Id="CE5F3D08-3BD6-4BDF-9C64-0C2B852B899C" Name="ContrAlto" Language="1033" Version="1.1.0" Manufacturer="Living Computers: Museum+Labs" UpgradeCode="38d6b09f-6e7b-4854-844a-5d4ab707a357">
<Package Id="*" InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<Upgrade Id="38d6b09f-6e7b-4854-844a-5d4ab707a357">
<UpgradeVersion OnlyDetect="yes" Property="SELFFOUND"
Minimum="1.1.0" IncludeMinimum="yes"
Maximum="1.1.0" IncludeMaximum="yes" />
Minimum="1.2.0" IncludeMinimum="yes"
Maximum="1.2.0" IncludeMaximum="yes" />
<UpgradeVersion OnlyDetect="yes" Property="NEWERFOUND"
Minimum="1.1.0" IncludeMinimum="no" />
Minimum="1.2.0" IncludeMinimum="no" />
</Upgrade>
<CustomAction Id="AlreadyUpdated" Error="[ProductName] 1.0 has already been updated to 1.1.0 or newer." />
<CustomAction Id="AlreadyUpdated" Error="[ProductName] 1.1 has already been updated to 1.2.0 or newer." />
<CustomAction Id="NoDowngrade" Error="A later version of [ProductName] is already installed." />
<InstallExecuteSequence>
@ -38,8 +38,8 @@
<Media Id="1" Cabinet="ContrAlto.cab" EmbedCab="yes"/>
<Feature Id="ProductFeature" Title="ContrAlto" Description="The ContrAlto Alto Emulator" Level="1" ConfigurableDirectory="INSTALLFOLDER">
<ComponentGroupRef Id="ProductComponents" />
<Feature Id="ProductFeature" Title="ContrAlto" Description="The ContrAlto Alto Emulator" Level="1" ConfigurableDirectory="INSTALLFOLDER">
<ComponentGroupRef Id="ProductComponents" />
<ComponentGroupRef Id="MicrocodeComponentsShared" />
<ComponentGroupRef Id="MicrocodeComponentsAltoI" />
<ComponentGroupRef Id="MicrocodeComponentsAltoII" />
@ -47,7 +47,7 @@
<ComponentGroupRef Id="DiskComponents" /> -->
<ComponentGroupRef Id="CodeComponents" />
<ComponentRef Id="ProgramMenuDir" />
</Feature>
</Feature>
<Icon Id="ContrAlto.exe" SourceFile="$(var.Contralto.TargetPath)"/>
@ -58,13 +58,13 @@
<WixVariable Id="WixUILicenseRtf" Value="EULA.rtf" />
</Product>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="LCM" Name="Living Computers">
<Directory Id="INSTALLFOLDER" Name="Contralto">
<Directory Id="INSTALLFOLDER" Name="Contralto">
<Directory Id="ROMFOLDER" Name="ROM">
<Directory Id="ROMFOLDERALTOI" Name="AltoI"/>
<Directory Id="ROMFOLDERALTOII" Name="AltoII"/>
@ -72,7 +72,7 @@
<Directory Id="DISASSEMBLYFOLDER" Name="Disassembly"/>
</Directory>
</Directory>
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder" Name="Programs">
<Directory Id="ProgramMenuDir" Name="ContrAlto">
<Component Id="ProgramMenuDir" Guid="007ADB59-BCE4-43CB-9AEC-DD5962136A7D">
@ -90,12 +90,12 @@
</Component>
</Directory>
</Directory> -->
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<!-- Main Contralto EXE and dependencies-->
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<!-- Main Contralto EXE and dependencies-->
<Component Id="ContraltoExecutable" Guid="0AF4F077-3858-4CEA-A3CD-CF8585F98AAA">
<File Id="exe" Name="ContrAlto.exe" Source="$(var.Contralto.TargetPath)" KeyPath="yes">
<Shortcut Id="shortcut" Directory="ProgramMenuDir" Name="ContrAlto"
@ -103,10 +103,8 @@
</File>
<!-- PCAP libs -->
<File Source="$(var.Contralto.TargetDir)\PcapDotNet.Base.dll"/>
<File Source="$(var.Contralto.TargetDir)\PcapDotNet.Core.dll"/>
<File Source="$(var.Contralto.TargetDir)\PcapDotNet.Core.Extensions.dll"/>
<File Source="$(var.Contralto.TargetDir)\PcapDotNet.Packets.dll"/>
<File Source="$(var.Contralto.TargetDir)\PacketDotNet.dll"/>
<File Source="$(var.Contralto.TargetDir)\SharpPcap.dll"/>
<!-- SDL2-CS libs - at this time we don't use SDL2 on Windows but the SDL2-CS wrapper
still must be present. -->
@ -120,7 +118,7 @@
<Shortcut Id="docshortcut" Directory="ProgramMenuDir" Name="ContrAlto Readme" Advertise="yes" />
</File>
</Component>
</ComponentGroup>
</ComponentGroup>
<ComponentGroup Id="MicrocodeComponentsShared" Directory="ROMFOLDER">
<!-- Microcode and other ROM files shared between all Alto revisions -->
@ -225,6 +223,6 @@
<File Id="MesaROM" Name="MesaROM.mu" Source="$(var.Contralto.TargetDir)\Disassembly\MesaROM.mu"/>
</Component>
</ComponentGroup>
</Fragment>
</Fragment>
</Wix>