Refactor.

This commit is contained in:
2018-02-25 22:44:26 +00:00
parent 7bfb6744f2
commit c3cd0b5831
15 changed files with 224 additions and 245 deletions

View File

@@ -69,14 +69,8 @@ namespace libexeinfo
/// Header for this executable
/// </summary>
public COFFHeader Header { get; private set; }
/// <summary>
/// The <see cref="FileStream" /> that contains the executable represented by this instance
/// </summary>
public Stream BaseStream { get; }
public bool IsBigEndian { get; private set; }
/// <summary>
/// If true this instance correctly represents a Common Object File Format
/// </summary>
public bool Recognized { get; private set; }
public string Type { get; private set; }

View File

@@ -31,10 +31,10 @@ namespace libexeinfo
/// <summary>
/// Linear Executable signature, "LE"
/// </summary>
public const ushort Signature16 = 0x454C;
const ushort SIGNATURE16 = 0x454C;
/// <summary>
/// Linear eXecutable signature, "LX"
/// </summary>
public const ushort Signature = 0x584C;
const ushort SIGNATURE = 0x584C;
}
}

View File

@@ -30,16 +30,16 @@ namespace libexeinfo
{
public partial class LX
{
public string Information => GetInfo(Header);
public string Information => GetInfo(header);
public static string GetInfo(LXHeader header)
static string GetInfo(LXHeader header)
{
StringBuilder sb = new StringBuilder();
if(header.signature == Signature16) sb.AppendLine("Linear Executable (LE):");
else sb.AppendLine("Linear eXecutable (LX):");
sb.AppendLine(header.signature == SIGNATURE16 ? "Linear Executable (LE):" : "Linear eXecutable (LX):");
if(header.os_type == TargetOS.OS2)
switch(header.os_type)
{
case TargetOS.OS2:
sb.AppendLine("\tOS/2 application");
if(header.module_flags.HasFlag(ModuleFlags.PMIncompatible) &&
!header.module_flags.HasFlag(ModuleFlags.PMCompatible))
@@ -50,14 +50,21 @@ namespace libexeinfo
else if(header.module_flags.HasFlag(ModuleFlags.PMIncompatible) &&
header.module_flags.HasFlag(ModuleFlags.PMCompatible))
sb.AppendLine("\tApplication uses Presentation Manager");
}
else if(header.os_type == TargetOS.Windows || header.os_type == TargetOS.Win32 ||
header.os_type == TargetOS.Unknown)
break;
case TargetOS.Windows:
case TargetOS.Win32:
case TargetOS.Unknown:
switch(header.os_type)
{
if(header.os_type == TargetOS.Windows || header.os_type == TargetOS.Unknown)
case TargetOS.Windows:
case TargetOS.Unknown:
sb.AppendLine("\t16-bit Windows application");
else if(header.os_type == TargetOS.Win32)
break;
case TargetOS.Win32:
sb.AppendLine("\t32-bit Windows application");
break;
}
if(header.module_flags.HasFlag(ModuleFlags.PMIncompatible) &&
!header.module_flags.HasFlag(ModuleFlags.PMCompatible))
sb.AppendLine("\tApplication is full screen, unaware of Windows");
@@ -67,9 +74,8 @@ namespace libexeinfo
else if(header.module_flags.HasFlag(ModuleFlags.PMIncompatible) &&
header.module_flags.HasFlag(ModuleFlags.PMCompatible))
sb.AppendLine("\tApplication uses Windows");
}
else if(header.os_type == TargetOS.DOS)
{
break;
case TargetOS.DOS:
sb.AppendLine("\tDOS application");
if(header.module_flags.HasFlag(ModuleFlags.PMIncompatible) &&
!header.module_flags.HasFlag(ModuleFlags.PMCompatible))
@@ -80,9 +86,11 @@ namespace libexeinfo
else if(header.module_flags.HasFlag(ModuleFlags.PMIncompatible) &&
header.module_flags.HasFlag(ModuleFlags.PMCompatible))
sb.AppendLine("\tApplication uses Windows");
}
else
break;
default:
sb.AppendFormat("\tApplication for unknown OS {0}", (ushort)header.os_type).AppendLine();
break;
}
sb.AppendFormat("\tByte ordering: {0}", header.byte_order == 1 ? "Big-endian" : "Little-Endian")
.AppendLine();

View File

@@ -36,21 +36,11 @@ namespace libexeinfo
// TODO: Big-endian (really needed?)
public partial class LX : IExecutable
{
MZ BaseExecutable;
/// <summary>
/// The <see cref="FileStream" /> that contains the executable represented by this instance
/// </summary>
public Stream BaseStream { get; }
public bool IsBigEndian => false;
MZ baseExecutable;
/// <summary>
/// Header for this executable
/// </summary>
LXHeader Header;
/// <summary>
/// If true this instance correctly represents a Microsoft/IBM Linear EXecutable
/// </summary>
public bool Recognized { get; private set;}
public string Type { get; private set;}
LXHeader header;
/// <summary>
/// Initializes a new instance of the <see cref="T:libexeinfo.NE" /> class.
@@ -82,27 +72,36 @@ namespace libexeinfo
Initialize();
}
/// <summary>
/// The <see cref="FileStream" /> that contains the executable represented by this instance
/// </summary>
public Stream BaseStream { get; }
public bool IsBigEndian => false;
public bool Recognized { get; private set; }
public string Type { get; private set; }
void Initialize()
{
Recognized = false;
if(BaseStream == null) return;
BaseExecutable = new MZ(BaseStream);
if(!BaseExecutable.Recognized) return;
if(BaseExecutable.Header.new_offset >= BaseStream.Length) return;
baseExecutable = new MZ(BaseStream);
if(!baseExecutable.Recognized) return;
BaseStream.Seek(BaseExecutable.Header.new_offset, SeekOrigin.Begin);
if(baseExecutable.Header.new_offset >= BaseStream.Length) return;
BaseStream.Seek(baseExecutable.Header.new_offset, SeekOrigin.Begin);
byte[] buffer = new byte[Marshal.SizeOf(typeof(LXHeader))];
BaseStream.Read(buffer, 0, buffer.Length);
IntPtr hdrPtr = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, hdrPtr, buffer.Length);
Header = (LXHeader)Marshal.PtrToStructure(hdrPtr, typeof(LXHeader));
header = (LXHeader)Marshal.PtrToStructure(hdrPtr, typeof(LXHeader));
Marshal.FreeHGlobal(hdrPtr);
Recognized = Header.signature == Signature || Header.signature == Signature16;
Recognized = header.signature == SIGNATURE || header.signature == SIGNATURE16;
if(!Recognized) return;
Type = Header.signature == Signature16 ? "Linear Executable (LE)" : "Linear eXecutable (LX)";
Type = header.signature == SIGNATURE16 ? "Linear Executable (LE)" : "Linear eXecutable (LX)";
}
/// <summary>
@@ -112,21 +111,20 @@ namespace libexeinfo
/// <param name="path">Executable path.</param>
public static bool Identify(string path)
{
FileStream BaseStream = File.Open(path, FileMode.Open, FileAccess.Read);
MZ BaseExecutable = new MZ(BaseStream);
if(!BaseExecutable.Recognized) return false;
FileStream baseStream = File.Open(path, FileMode.Open, FileAccess.Read);
MZ baseExecutable = new MZ(baseStream);
if(!baseExecutable.Recognized) return false;
if(BaseExecutable.Header.new_offset >= BaseStream.Length) return false;
if(baseExecutable.Header.new_offset >= baseStream.Length) return false;
BaseStream.Seek(BaseExecutable.Header.new_offset, SeekOrigin.Begin);
baseStream.Seek(baseExecutable.Header.new_offset, SeekOrigin.Begin);
byte[] buffer = new byte[Marshal.SizeOf(typeof(LXHeader))];
BaseStream.Read(buffer, 0, buffer.Length);
baseStream.Read(buffer, 0, buffer.Length);
IntPtr hdrPtr = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, hdrPtr, buffer.Length);
LXHeader Header = (LXHeader)Marshal.PtrToStructure(hdrPtr, typeof(LXHeader));
LXHeader header = (LXHeader)Marshal.PtrToStructure(hdrPtr, typeof(LXHeader));
Marshal.FreeHGlobal(hdrPtr);
return Header.signature == Signature || Header.signature == Signature16;
return header.signature == SIGNATURE || header.signature == SIGNATURE16;
}
/// <summary>
@@ -136,21 +134,20 @@ namespace libexeinfo
/// <param name="stream">Stream containing the executable.</param>
public static bool Identify(FileStream stream)
{
FileStream BaseStream = stream;
MZ BaseExecutable = new MZ(BaseStream);
if(!BaseExecutable.Recognized) return false;
FileStream baseStream = stream;
MZ baseExecutable = new MZ(baseStream);
if(!baseExecutable.Recognized) return false;
if(BaseExecutable.Header.new_offset >= BaseStream.Length) return false;
if(baseExecutable.Header.new_offset >= baseStream.Length) return false;
BaseStream.Seek(BaseExecutable.Header.new_offset, SeekOrigin.Begin);
baseStream.Seek(baseExecutable.Header.new_offset, SeekOrigin.Begin);
byte[] buffer = new byte[Marshal.SizeOf(typeof(LXHeader))];
BaseStream.Read(buffer, 0, buffer.Length);
baseStream.Read(buffer, 0, buffer.Length);
IntPtr hdrPtr = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, hdrPtr, buffer.Length);
LXHeader Header = (LXHeader)Marshal.PtrToStructure(hdrPtr, typeof(LXHeader));
LXHeader header = (LXHeader)Marshal.PtrToStructure(hdrPtr, typeof(LXHeader));
Marshal.FreeHGlobal(hdrPtr);
return Header.signature == Signature || Header.signature == Signature16;
return header.signature == SIGNATURE || header.signature == SIGNATURE16;
}
}
}

View File

@@ -31,6 +31,6 @@ namespace libexeinfo
/// <summary>
/// MZ executable signature, "MZ"
/// </summary>
public const ushort Signature = 0x5A4D;
const ushort SIGNATURE = 0x5A4D;
}
}

View File

@@ -41,7 +41,7 @@ namespace libexeinfo
/// </summary>
/// <returns>Human readable information for given MZ header.</returns>
/// <param name="header">MZ executable header.</param>
public static string GetInfo(MZHeader header)
static string GetInfo(MZHeader header)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("DOS MZ executable:");

View File

@@ -94,7 +94,7 @@ namespace libexeinfo
Marshal.Copy(buffer, 0, hdrPtr, buffer.Length);
Header = (MZHeader)Marshal.PtrToStructure(hdrPtr, typeof(MZHeader));
Marshal.FreeHGlobal(hdrPtr);
Recognized = Header.signature == Signature;
Recognized = Header.signature == SIGNATURE;
if(!Recognized) return;
@@ -128,7 +128,7 @@ namespace libexeinfo
MZHeader mzHdr = (MZHeader)Marshal.PtrToStructure(hdrPtr, typeof(MZHeader));
Marshal.FreeHGlobal(hdrPtr);
return mzHdr.signature == Signature;
return mzHdr.signature == SIGNATURE;
}
}
}

View File

@@ -31,15 +31,15 @@ namespace libexeinfo
/// <summary>
/// New Executable signature, "NE"
/// </summary>
public const ushort Signature = 0x454E;
const ushort SIGNATURE = 0x454E;
/// <summary>
/// Signature for a <see cref="FixedFileInfo" />
/// </summary>
public static readonly string FixedFileInfoSig = "VS_VERSION_INFO";
const string FIXED_FILE_INFO_SIG = "VS_VERSION_INFO";
/// <summary>
/// Signature for list of name=value strings inside a version resource
/// </summary>
public static readonly string StringFileInfo = "StringFileInfo";
const string STRING_FILE_INFO = "StringFileInfo";
/// <summary>
/// Gets the name of a resource type according to its identifier
@@ -76,7 +76,7 @@ namespace libexeinfo
case (int)ResourceTypes.RT_TOOLBAR: return "RT_TOOLBAR";
case (int)ResourceTypes.RT_VERSION: return "RT_VERSION";
case (int)ResourceTypes.RT_VXD: return "RT_VXD";
default: return string.Format("{0}", id & 0x7FFF);
default: return $"{id & 0x7FFF}";
}
}
}

View File

@@ -73,16 +73,10 @@ namespace libexeinfo
Initialize();
}
/// <summary>
/// The <see cref="FileStream" /> that contains the executable represented by this instance
/// </summary>
public Stream BaseStream { get; }
public bool IsBigEndian => false;
/// <summary>
/// If true this instance correctly represents a Microsoft New Executable
/// </summary>
public bool Recognized { get; private set; }
public string Type { get; }
public string Type { get; private set; }
void Initialize()
{
@@ -102,9 +96,10 @@ namespace libexeinfo
Marshal.Copy(buffer, 0, hdrPtr, buffer.Length);
Header = (NEHeader)Marshal.PtrToStructure(hdrPtr, typeof(NEHeader));
Marshal.FreeHGlobal(hdrPtr);
if(Header.signature != Signature) return;
if(Header.signature != SIGNATURE) return;
Recognized = true;
Type = "New Executable (NE)";
if(Header.resource_entries <= 0) return;
Resources = GetResources(BaseStream, BaseExecutable.Header.new_offset, Header.resource_table_offset);
@@ -131,7 +126,7 @@ namespace libexeinfo
Marshal.Copy(buffer, 0, hdrPtr, buffer.Length);
NEHeader Header = (NEHeader)Marshal.PtrToStructure(hdrPtr, typeof(NEHeader));
Marshal.FreeHGlobal(hdrPtr);
return Header.signature == Signature;
return Header.signature == SIGNATURE;
}
/// <summary>
@@ -154,7 +149,7 @@ namespace libexeinfo
Marshal.Copy(buffer, 0, hdrPtr, buffer.Length);
NEHeader Header = (NEHeader)Marshal.PtrToStructure(hdrPtr, typeof(NEHeader));
Marshal.FreeHGlobal(hdrPtr);
return Header.signature == Signature;
return Header.signature == SIGNATURE;
}
}
}

View File

@@ -27,6 +27,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
@@ -40,17 +41,10 @@ namespace libexeinfo
/// <returns>The decoded version resources.</returns>
public List<Version> GetVersions()
{
List<Version> versions = new List<Version>();
foreach(ResourceType type in Resources.types)
if((type.id & 0x7FFF) == (int)ResourceTypes.RT_VERSION)
foreach(Resource resource in type.resources)
{
Version vers = new Version(resource.data, resource.name);
versions.Add(vers);
}
return versions;
return (from type in Resources.types
where (type.id & 0x7FFF) == (int)ResourceTypes.RT_VERSION
from resource in type.resources
select new Version(resource.data, resource.name)).ToList();
}
/// <summary>
@@ -129,7 +123,7 @@ namespace libexeinfo
/// <value>The resource name.</value>
public string Name { get; }
VersionNode GetNode(byte[] data, int startPosition, out int nodeLength)
static VersionNode GetNode(byte[] data, int startPosition, out int nodeLength)
{
nodeLength = 0;
@@ -172,21 +166,17 @@ namespace libexeinfo
void DecodeNode(VersionNode node, string parent, string grandparent)
{
if(node.szName == FixedFileInfoSig)
if(node.szName == FIXED_FILE_INFO_SIG)
{
IntPtr infoPtr = Marshal.AllocHGlobal(node.cbData);
Marshal.Copy(node.rgbData, 0, infoPtr, node.cbData);
FixedFileInfo info = (FixedFileInfo)Marshal.PtrToStructure(infoPtr, typeof(FixedFileInfo));
Marshal.FreeHGlobal(infoPtr);
FileVersion = string.Format("{0}.{1:D2}.{2}.{3}", (info.dwFileVersionMS & 0xFFFF0000) >> 16,
info.dwFileVersionMS & 0xFFFF,
(info.dwFileVersionLS & 0xFFFF0000) >> 16,
info.dwFileVersionLS & 0xFFFF);
ProductVersion = string.Format("{0}.{1:D2}.{2}.{3}", (info.dwProductVersionMS & 0xFFFF0000) >> 16,
info.dwProductVersionMS & 0xFFFF,
(info.dwProductVersionLS & 0xFFFF0000) >> 16,
info.dwProductVersionLS & 0xFFFF);
FileVersion =
$"{(info.dwFileVersionMS & 0xFFFF0000) >> 16}.{info.dwFileVersionMS & 0xFFFF:D2}.{(info.dwFileVersionLS & 0xFFFF0000) >> 16}.{info.dwFileVersionLS & 0xFFFF}";
ProductVersion =
$"{(info.dwProductVersionMS & 0xFFFF0000) >> 16}.{info.dwProductVersionMS & 0xFFFF:D2}.{(info.dwProductVersionLS & 0xFFFF0000) >> 16}.{info.dwProductVersionLS & 0xFFFF}";
FileFlags =
(VersionFileFlags)(info.dwFileFlags & info.dwFileFlagsMask);
FileOS = (VersionFileOS)info.dwFileOS;
@@ -195,13 +185,13 @@ namespace libexeinfo
FileDate = DateTime.FromFileTime(info.dwFileDateMS * 0x100000000 + info.dwFileDateLS);
}
if(parent == StringFileInfo)
if(parent == STRING_FILE_INFO)
{
Dictionary<string, string> strings = new Dictionary<string, string>();
StringsByLanguage.Add(node.szName, strings);
}
if(grandparent == StringFileInfo)
if(grandparent == STRING_FILE_INFO)
if(StringsByLanguage.TryGetValue(parent, out Dictionary<string, string> strings))
{
Encoding encoding;
@@ -212,9 +202,9 @@ namespace libexeinfo
strings.Add(node.szName, encoding.GetString(node.rgbData));
}
if(node.children != null)
for(int i = 0; i < node.children.Length; i++)
DecodeNode(node.children[i], node.szName, parent);
if(node.children == null) return;
foreach(VersionNode n in node.children) DecodeNode(n, node.szName, parent);
}
/// <summary>
@@ -235,7 +225,7 @@ namespace libexeinfo
case VersionFileType.VFT_STATIC_LIB: return "Static-link library";
case VersionFileType.VFT_UNKNOWN: return "Unknown";
case VersionFileType.VFT_VXD: return "Virtual device";
default: return string.Format("Unknown type code {0}", (uint)type);
default: return $"Unknown type code {(uint)type}";
}
}
@@ -263,7 +253,7 @@ namespace libexeinfo
case VersionFileSubtype.VFT2_DRV_VERSIONED_PRINTER: return "Versioned";
case VersionFileSubtype.VFT2_UNKNOWN: return "Unknown";
default:
return string.Format("Unknown type code {0}", (uint)subtype);
return $"Unknown type code {(uint)subtype}";
}
}
@@ -283,7 +273,7 @@ namespace libexeinfo
case VersionFileSubtype.VFT2_FONT_VECTOR: return "Vector";
case VersionFileSubtype.VFT2_UNKNOWN: return "Unknown";
default:
return string.Format("Unknown type code {0}", (uint)subtype);
return $"Unknown type code {(uint)subtype}";
}
}
@@ -330,7 +320,7 @@ namespace libexeinfo
return "16-bit Presentation Manager running under 32-bit OS/2";
case VersionFileOS.VOS_OS232_PM32:
return "32-bit Presentation Manager running under 32-bit OS/2";
default: return string.Format("Unknown OS code {0}", (uint)os);
default: return $"Unknown OS code {(uint)os}";
}
}
}

View File

@@ -31,8 +31,8 @@ namespace libexeinfo
/// <summary>
/// Portable Executable signature, "PE\0\0"
/// </summary>
public const ushort Signature = 0x00004550;
public const ushort PE32 = COFF.ZMAGIC;
public const ushort PE32Plus = 0x20b;
const ushort SIGNATURE = 0x00004550;
const ushort PE32 = COFF.ZMAGIC;
internal const ushort PE32Plus = 0x20b;
}
}

View File

@@ -25,6 +25,7 @@
// THE SOFTWARE.
using System;
// ReSharper disable InconsistentNaming
namespace libexeinfo
{

View File

@@ -32,7 +32,7 @@ namespace libexeinfo
{
public string Information => GetInfo(Header, WinHeader);
public static string GetInfo(PEHeader header, WindowsHeader64 winheader)
static string GetInfo(PEHeader header, WindowsHeader64 winheader)
{
StringBuilder sb = new StringBuilder();
sb.Append(COFF.GetInfo(header.coff));

View File

@@ -72,14 +72,8 @@ namespace libexeinfo
Initialize();
}
/// <summary>
/// The <see cref="FileStream" /> that contains the executable represented by this instance
/// </summary>
public Stream BaseStream { get; }
public bool IsBigEndian => false;
/// <summary>
/// If true this instance correctly represents a Microsoft Portable Executable
/// </summary>
public bool Recognized { get; private set; }
public string Type { get; private set; }
@@ -100,7 +94,7 @@ namespace libexeinfo
Marshal.Copy(buffer, 0, hdrPtr, buffer.Length);
Header = (PEHeader)Marshal.PtrToStructure(hdrPtr, typeof(PEHeader));
Marshal.FreeHGlobal(hdrPtr);
Recognized = Header.signature == Signature;
Recognized = Header.signature == SIGNATURE;
if(!Recognized) return;
@@ -135,20 +129,20 @@ namespace libexeinfo
/// <param name="path">Executable path.</param>
public static bool Identify(string path)
{
FileStream BaseStream = File.Open(path, FileMode.Open, FileAccess.Read);
MZ BaseExecutable = new MZ(BaseStream);
if(!BaseExecutable.Recognized) return false;
FileStream baseStream = File.Open(path, FileMode.Open, FileAccess.Read);
MZ baseExecutable = new MZ(baseStream);
if(!baseExecutable.Recognized) return false;
if(BaseExecutable.Header.new_offset >= BaseStream.Length) return false;
if(baseExecutable.Header.new_offset >= baseStream.Length) return false;
BaseStream.Seek(BaseExecutable.Header.new_offset, SeekOrigin.Begin);
baseStream.Seek(baseExecutable.Header.new_offset, SeekOrigin.Begin);
byte[] buffer = new byte[Marshal.SizeOf(typeof(PEHeader))];
BaseStream.Read(buffer, 0, buffer.Length);
baseStream.Read(buffer, 0, buffer.Length);
IntPtr hdrPtr = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, hdrPtr, buffer.Length);
PEHeader Header = (PEHeader)Marshal.PtrToStructure(hdrPtr, typeof(PEHeader));
PEHeader header = (PEHeader)Marshal.PtrToStructure(hdrPtr, typeof(PEHeader));
Marshal.FreeHGlobal(hdrPtr);
return Header.signature == Signature;
return header.signature == SIGNATURE;
}
/// <summary>
@@ -158,20 +152,20 @@ namespace libexeinfo
/// <param name="stream">Stream containing the executable.</param>
public static bool Identify(FileStream stream)
{
FileStream BaseStream = stream;
MZ BaseExecutable = new MZ(BaseStream);
if(!BaseExecutable.Recognized) return false;
FileStream baseStream = stream;
MZ baseExecutable = new MZ(baseStream);
if(!baseExecutable.Recognized) return false;
if(BaseExecutable.Header.new_offset >= BaseStream.Length) return false;
if(baseExecutable.Header.new_offset >= baseStream.Length) return false;
BaseStream.Seek(BaseExecutable.Header.new_offset, SeekOrigin.Begin);
baseStream.Seek(baseExecutable.Header.new_offset, SeekOrigin.Begin);
byte[] buffer = new byte[Marshal.SizeOf(typeof(PEHeader))];
BaseStream.Read(buffer, 0, buffer.Length);
baseStream.Read(buffer, 0, buffer.Length);
IntPtr hdrPtr = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, hdrPtr, buffer.Length);
PEHeader Header = (PEHeader)Marshal.PtrToStructure(hdrPtr, typeof(PEHeader));
PEHeader header = (PEHeader)Marshal.PtrToStructure(hdrPtr, typeof(PEHeader));
Marshal.FreeHGlobal(hdrPtr);
return Header.signature == Signature;
return header.signature == SIGNATURE;
}
static WindowsHeader64 ToPlus(WindowsHeader header)

View File

@@ -28,7 +28,7 @@ namespace libexeinfo
{
public partial class PE
{
public static string SubsystemToString(Subsystems subsystem)
static string SubsystemToString(Subsystems subsystem)
{
switch(subsystem)
{