public static void CreateInstaller(InstallerLinkerArguments args)
        {
            args.Validate();

            args.WriteLine(string.Format("Creating \"{0}\" from \"{1}\"", args.output, args.template));
            System.IO.File.Copy(args.template, args.output, true);
            System.IO.File.SetAttributes(args.output, System.IO.FileAttributes.Normal);

            string configFilename = args.config;

            #region Version Information

            ConfigFile configfile = new ConfigFile();
            configfile.Load(configFilename);

            // \todo: check XML with XSD, warn if nodes are being dropped

            // filter the configuration
            string configTemp = null;
            if (!string.IsNullOrEmpty(args.processorArchitecture))
            {
                int configurationCount = configfile.ConfigurationCount;
                int componentCount = configfile.ComponentCount;
                args.WriteLine(string.Format("Applying processor architecture filter \"{0}\"", args.processorArchitecture));
                ProcessorArchitectureFilter filter = new ProcessorArchitectureFilter(args.processorArchitecture);
                XmlDocument filteredXml = configfile.GetXml(filter);
                configTemp = configFilename = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
                filteredXml.Save(configTemp);
                configfile.LoadXml(filteredXml);
                args.WriteLine(string.Format(" configurations: {0} => {1}", configurationCount, configfile.ConfigurationCount));
                args.WriteLine(string.Format(" components: {0} => {1}", componentCount, configfile.ComponentCount));
            }

            args.WriteLine(string.Format("Updating binary attributes in \"{0}\"", args.output));
            VersionResource rc = new VersionResource();
            rc.LoadFrom(args.output);

            // version information
            StringFileInfo stringFileInfo = (StringFileInfo)rc["StringFileInfo"];
            if (!string.IsNullOrEmpty(configfile.productversion))
            {
                rc.ProductVersion = configfile.productversion;
                stringFileInfo["ProductVersion"] = configfile.productversion;
            }

            if (!string.IsNullOrEmpty(configfile.fileversion))
            {
                rc.FileVersion = configfile.fileversion;
                stringFileInfo["FileVersion"] = configfile.fileversion;
            }

            foreach (FileAttribute attr in configfile.fileattributes)
            {
                args.WriteLine(string.Format(" {0}: {1}", attr.name, attr.value));
                stringFileInfo[attr.name] = attr.value;
            }

            rc.Language = ResourceUtil.NEUTRALLANGID;
            rc.SaveTo(args.output);

            #endregion

            #region Optional Icon
            // optional icon
            if (!string.IsNullOrEmpty(args.icon))
            {
                args.WriteLine(string.Format("Embedding icon \"{0}\"", args.icon));
                IconFile iconFile = new IconFile(args.icon);
                List<string> iconSizes = new List<string>();
                foreach (IconFileIcon icon in iconFile.Icons)
                    iconSizes.Add(icon.ToString());
                args.WriteLine(string.Format(" {0}", string.Join(", ", iconSizes.ToArray())));
                IconDirectoryResource iconDirectory = new IconDirectoryResource(iconFile);
                iconDirectory.Name = new ResourceId(128);
                iconDirectory.Language = ResourceUtil.NEUTRALLANGID;
                iconDirectory.SaveTo(args.output);
            }
            #endregion

            #region Manifest
            if (!string.IsNullOrEmpty(args.manifest))
            {
                args.WriteLine(string.Format("Embedding manifest \"{0}\"", args.manifest));
                ManifestResource manifest = new ManifestResource();
                manifest.Manifest.Load(args.manifest);
                manifest.SaveTo(args.output);
            }
            #endregion

            string supportdir = string.IsNullOrEmpty(args.apppath)
                ? Environment.CurrentDirectory
                : args.apppath;

            string templatepath = Path.GetDirectoryName(Path.GetFullPath(args.template));

            // create a temporary directory for CABs
            string cabtemp = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
            Directory.CreateDirectory(cabtemp);
            args.WriteLine(string.Format("Writing CABs to \"{0}\"", cabtemp));

            try
            {
                #region Prepare CABs

                long totalSize = 0;
                List<String> allFilesList = new List<string>();

                // embedded files
                if (args.embed)
                {
                    args.WriteLine(string.Format("Compressing files in \"{0}\"", supportdir));
                    Dictionary<string, EmbedFileCollection> all_files = configfile.GetFiles(string.Empty, supportdir);
                    // ensure at least one for additional command-line parameters
                    if (all_files.Count == 0) all_files.Add(string.Empty, new EmbedFileCollection(supportdir));
                    Dictionary<string, EmbedFileCollection>.Enumerator enumerator = all_files.GetEnumerator();

                    while (enumerator.MoveNext())
                    {
                        EmbedFileCollection c_files = enumerator.Current.Value;

                        // add additional command-line files to the root CAB
                        if (string.IsNullOrEmpty(enumerator.Current.Key))
                        {
                            if (args.embedFiles != null)
                            {
                                foreach (string filename in args.embedFiles)
                                {
                                    string fullpath = Path.Combine(args.apppath, filename);
                                    c_files.Add(new EmbedFilePair(fullpath, filename));
                                }
                            }

                            if (args.embedFolders != null)
                            {
                                foreach (string folder in args.embedFolders)
                                {
                                    c_files.AddDirectory(folder);
                                }
                            }
                        }

                        if (c_files.Count == 0)
                            continue;

                        c_files.CheckFilesExist(args);
                        c_files.CheckFileAttributes(args);

                        ArrayList files = c_files.GetFilePairs();

                        // compress new CABs
                        string cabname = string.IsNullOrEmpty(enumerator.Current.Key)
                            ? Path.Combine(cabtemp, "SETUP_%d.CAB")
                            : Path.Combine(cabtemp, string.Format("SETUP_{0}_%d.CAB", enumerator.Current.Key));

                        Compress cab = new Compress();
                        long currentSize = 0;
                        cab.evFilePlaced += delegate(string s_File, int s32_FileSize, bool bContinuation)
                        {
                            if (!bContinuation)
                            {
                                totalSize += s32_FileSize;
                                currentSize += s32_FileSize;
                                args.WriteLine(String.Format(" {0} - {1}", s_File, EmbedFileCollection.FormatBytes(s32_FileSize)));
                            }

                            return 0;
                        };
                        cab.CompressFileList(files, cabname, true, true, args.embedResourceSize);

                        StringBuilder fileslist = new StringBuilder();
                        fileslist.AppendLine(string.Format("{0} CAB size: {1}",
                            string.IsNullOrEmpty(enumerator.Current.Key) ? "*" : enumerator.Current.Key,
                            EmbedFileCollection.FormatBytes(currentSize)));

                        fileslist.Append(" " + String.Join("\r\n ", c_files.GetFileValuesWithSize(2)));
                        allFilesList.Add(fileslist.ToString());
                    }
                }
                #endregion

                #region Resources
                // embed resources

                IntPtr h = ResourceUpdate.BeginUpdateResource(args.output, false);

                if (h == IntPtr.Zero)
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }

                if (!string.IsNullOrEmpty(args.banner))
                {
                    args.WriteLine(string.Format("Embedding banner \"{0}\"", args.banner));
                    ResourceUpdate.WriteFile(h, new ResourceId("CUSTOM"), new ResourceId("RES_BANNER"),
                        ResourceUtil.NEUTRALLANGID, args.banner);
                }

                if (!string.IsNullOrEmpty(args.splash))
                {
                    args.WriteLine(string.Format("Embedding splash screen \"{0}\"", args.splash));
                    ResourceUpdate.WriteFile(h, new ResourceId("CUSTOM"), new ResourceId("RES_SPLASH"),
                        ResourceUtil.NEUTRALLANGID, args.splash);
                }

                args.WriteLine(string.Format("Embedding configuration \"{0}\"", configFilename));
                ResourceUpdate.WriteFile(h, new ResourceId("CUSTOM"), new ResourceId("RES_CONFIGURATION"),
                    ResourceUtil.NEUTRALLANGID, configFilename);

                #region Embed Resources

                EmbedFileCollection html_files = new EmbedFileCollection(args.apppath);

                if (args.htmlFiles != null)
                {
                    foreach (string filename in args.htmlFiles)
                    {
                        string fullpath = Path.GetFullPath(filename);
                        if (Directory.Exists(fullpath))
                        {
                            html_files.AddDirectory(fullpath);
                        }
                        else
                        {
                            html_files.Add(new EmbedFilePair(fullpath, Path.GetFileName(filename)));
                        }
                    }
                }

                IEnumerator<EmbedFilePair> html_files_enumerator = html_files.GetEnumerator();
                while (html_files_enumerator.MoveNext())
                {
                    EmbedFilePair pair = html_files_enumerator.Current;
                    String id = "";
                    for (int i = 0; i < pair.relativepath.Length; i++)
                    {
                        id += Char.IsLetterOrDigit(pair.relativepath[i]) ? pair.relativepath[i] : '_';
                    }

                    args.WriteLine(string.Format("Embedding HTML resource \"{0}\": {1}", id, pair.fullpath));
                    ResourceUpdate.WriteFile(h, new ResourceId("HTM"), new ResourceId(id.ToUpper()),
                        ResourceUtil.NEUTRALLANGID, pair.fullpath);
                }

                #endregion

                #region Embed CABs

                if (args.embed)
                {
                    args.WriteLine("Embedding CABs");
                    foreach (string cabfile in Directory.GetFiles(cabtemp))
                    {
                        args.WriteLine(string.Format(" {0} - {1}", Path.GetFileName(cabfile),
                            EmbedFileCollection.FormatBytes(new FileInfo(cabfile).Length)));

                        ResourceUpdate.WriteFile(h, new ResourceId("RES_CAB"), new ResourceId(Path.GetFileName(cabfile)),
                            ResourceUtil.NEUTRALLANGID, cabfile);
                    }

                    // cab directory

                    args.WriteLine("Embedding CAB directory");
                    StringBuilder filesDirectory = new StringBuilder();
                    filesDirectory.AppendLine(string.Format("Total CAB size: {0}\r\n", EmbedFileCollection.FormatBytes(totalSize)));
                    filesDirectory.AppendLine(string.Join("\r\n\r\n", allFilesList.ToArray()));
                    byte[] filesDirectory_b = Encoding.Unicode.GetBytes(filesDirectory.ToString());
                    ResourceUpdate.Write(h, new ResourceId("CUSTOM"), new ResourceId("RES_CAB_LIST"),
                        ResourceUtil.NEUTRALLANGID, filesDirectory_b);
                }

                #endregion

                // resource files
                ResourceFileCollection resources = configfile.GetResources(supportdir);
                foreach (ResourceFilePair r_pair in resources)
                {
                    args.WriteLine(string.Format("Embedding resource \"{0}\": {1}", r_pair.id, r_pair.path));
                    ResourceUpdate.WriteFile(h, new ResourceId("CUSTOM"), new ResourceId(r_pair.id),
                        ResourceUtil.NEUTRALLANGID, r_pair.path);
                }

                if (args.mslu)
                {
                    args.WriteLine("Embedding MSLU unicows.dll");

                    string unicowsdll = Path.Combine(templatepath, "unicows.dll");
                    if (! File.Exists(unicowsdll)) unicowsdll = Path.Combine(supportdir, "unicows.dll");
                    if (! File.Exists(unicowsdll)) unicowsdll = Path.Combine(Environment.CurrentDirectory, "unicows.dll");
                    if (! File.Exists(unicowsdll))
                    {
                        throw new Exception(string.Format("Error locating \"{0}\\unicows.dll\"", templatepath));
                    }

                    ResourceUpdate.WriteFile(h, new ResourceId("CUSTOM"), new ResourceId("RES_UNICOWS"),
                        ResourceUtil.NEUTRALLANGID, unicowsdll);
                }

                args.WriteLine(string.Format("Writing {0}", EmbedFileCollection.FormatBytes(totalSize)));

                if (!ResourceUpdate.EndUpdateResource(h, false))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                #endregion
            }
            finally
            {
                if (Directory.Exists(cabtemp))
                {
                    args.WriteLine(string.Format("Cleaning up \"{0}\"", cabtemp));
                    Directory.Delete(cabtemp, true);
                }

                if (!string.IsNullOrEmpty(configTemp))
                {
                    args.WriteLine(string.Format("Cleaning up \"{0}\"", configTemp));
                    File.Delete(configTemp);
                }
            }

            args.WriteLine(string.Format("Successfully created \"{0}\" ({1})",
                args.output, EmbedFileCollection.FormatBytes(new FileInfo(args.output).Length)));
        }
        public static void CreateInstaller(InstallerLinkerArguments args)
        {
            args.Validate();

            args.WriteLine(string.Format("Creating \"{0}\" from \"{1}\"", args.output, args.template));
            System.IO.File.Copy(args.template, args.output, true);
            System.IO.File.SetAttributes(args.output, System.IO.FileAttributes.Normal);

            string configFilename = args.config;

            #region Version Information

            ConfigFile configfile = new ConfigFile();
            configfile.Load(configFilename);

            // \todo: check XML with XSD, warn if nodes are being dropped

            // filter the configuration
            string configTemp = null;
            if (!string.IsNullOrEmpty(args.processorArchitecture))
            {
                int configurationCount = configfile.ConfigurationCount;
                int componentCount     = configfile.ComponentCount;
                args.WriteLine(string.Format("Applying processor architecture filter \"{0}\"", args.processorArchitecture));
                ProcessorArchitectureFilter filter = new ProcessorArchitectureFilter(args.processorArchitecture);
                XmlDocument filteredXml            = configfile.GetXml(filter);
                configTemp = configFilename = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
                filteredXml.Save(configTemp);
                configfile.LoadXml(filteredXml);
                args.WriteLine(string.Format(" configurations: {0} => {1}", configurationCount, configfile.ConfigurationCount));
                args.WriteLine(string.Format(" components: {0} => {1}", componentCount, configfile.ComponentCount));
            }

            args.WriteLine(string.Format("Updating binary attributes in \"{0}\"", args.output));
            VersionResource rc = new VersionResource();
            rc.LoadFrom(args.output);

            // version information
            StringFileInfo stringFileInfo = (StringFileInfo)rc["StringFileInfo"];
            if (!string.IsNullOrEmpty(configfile.productversion))
            {
                rc.ProductVersion = configfile.productversion;
                stringFileInfo["ProductVersion"] = configfile.productversion;
            }

            if (!string.IsNullOrEmpty(configfile.fileversion))
            {
                rc.FileVersion = configfile.fileversion;
                stringFileInfo["FileVersion"] = configfile.fileversion;
            }

            foreach (FileAttribute attr in configfile.fileattributes)
            {
                args.WriteLine(string.Format(" {0}: {1}", attr.name, attr.value));
                stringFileInfo[attr.name] = attr.value;
            }

            rc.Language = ResourceUtil.NEUTRALLANGID;
            rc.SaveTo(args.output);

            #endregion

            #region Optional Icon
            // optional icon
            if (!string.IsNullOrEmpty(args.icon))
            {
                args.WriteLine(string.Format("Embedding icon \"{0}\"", args.icon));
                IconFile      iconFile  = new IconFile(args.icon);
                List <string> iconSizes = new List <string>();
                foreach (IconFileIcon icon in iconFile.Icons)
                {
                    iconSizes.Add(icon.ToString());
                }
                args.WriteLine(string.Format(" {0}", string.Join(", ", iconSizes.ToArray())));
                IconDirectoryResource iconDirectory = new IconDirectoryResource(iconFile);
                iconDirectory.Name     = new ResourceId(128);
                iconDirectory.Language = ResourceUtil.NEUTRALLANGID;
                iconDirectory.SaveTo(args.output);
            }
            #endregion

            #region Manifest
            if (!string.IsNullOrEmpty(args.manifest))
            {
                args.WriteLine(string.Format("Embedding manifest \"{0}\"", args.manifest));
                ManifestResource manifest = new ManifestResource();
                manifest.Manifest.Load(args.manifest);
                manifest.SaveTo(args.output);
            }
            #endregion

            string supportdir = string.IsNullOrEmpty(args.apppath)
                ? Environment.CurrentDirectory
                : args.apppath;

            string templatepath = Path.GetDirectoryName(Path.GetFullPath(args.template));

            // create a temporary directory for CABs
            string cabtemp = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
            Directory.CreateDirectory(cabtemp);
            args.WriteLine(string.Format("Writing CABs to \"{0}\"", cabtemp));

            try
            {
                #region Prepare CABs

                long          totalSize    = 0;
                List <String> allFilesList = new List <string>();

                // embedded files
                if (args.embed)
                {
                    args.WriteLine(string.Format("Compressing files in \"{0}\"", supportdir));
                    Dictionary <string, EmbedFileCollection> all_files = configfile.GetFiles(string.Empty, supportdir);
                    // ensure at least one for additional command-line parameters
                    if (all_files.Count == 0)
                    {
                        all_files.Add(string.Empty, new EmbedFileCollection(supportdir));
                    }
                    using (Dictionary <string, EmbedFileCollection> .Enumerator enumerator = all_files.GetEnumerator())
                    {
                        while (enumerator.MoveNext())
                        {
                            EmbedFileCollection c_files = enumerator.Current.Value;

                            // add additional command-line files to the root CAB
                            if (string.IsNullOrEmpty(enumerator.Current.Key))
                            {
                                if (args.embedFiles != null)
                                {
                                    foreach (string filename in args.embedFiles)
                                    {
                                        string fullpath = Path.Combine(args.apppath, filename);
                                        c_files.Add(new EmbedFilePair(fullpath, filename));
                                    }
                                }

                                if (args.embedFolders != null)
                                {
                                    foreach (string folder in args.embedFolders)
                                    {
                                        c_files.AddDirectory(folder);
                                    }
                                }
                            }

                            if (c_files.Count == 0)
                            {
                                continue;
                            }

                            c_files.CheckFilesExist(args);
                            c_files.CheckFileAttributes(args);

                            ArrayList files = c_files.GetFilePairs();

                            // compress new CABs
                            string cabname = string.IsNullOrEmpty(enumerator.Current.Key)
                                ? Path.Combine(cabtemp, "SETUP_%d.CAB")
                                : Path.Combine(cabtemp, string.Format("SETUP_{0}_%d.CAB", enumerator.Current.Key));

                            Compress cab         = new Compress();
                            long     currentSize = 0;
                            cab.evFilePlaced += delegate(string s_File, int s32_FileSize, bool bContinuation)
                            {
                                if (!bContinuation)
                                {
                                    totalSize   += s32_FileSize;
                                    currentSize += s32_FileSize;
                                    args.WriteLine(String.Format(" {0} - {1}", s_File, EmbedFileCollection.FormatBytes(s32_FileSize)));
                                }

                                return(0);
                            };
                            cab.CompressFileList(files, cabname, true, true, args.embedResourceSize);

                            StringBuilder fileslist = new StringBuilder();
                            fileslist.AppendLine(string.Format("{0} CAB size: {1}",
                                                               string.IsNullOrEmpty(enumerator.Current.Key) ? "*" : enumerator.Current.Key,
                                                               EmbedFileCollection.FormatBytes(currentSize)));

                            fileslist.Append(" " + String.Join("\r\n ", c_files.GetFileValuesWithSize(2)));
                            allFilesList.Add(fileslist.ToString());
                        }
                    }
                }
                #endregion

                #region Resources
                // embed resources

                IntPtr h = ResourceUpdate.BeginUpdateResource(args.output, false);

                if (h == IntPtr.Zero)
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }

                if (!string.IsNullOrEmpty(args.banner))
                {
                    args.WriteLine(string.Format("Embedding banner \"{0}\"", args.banner));
                    ResourceUpdate.WriteFile(h, new ResourceId("CUSTOM"), new ResourceId("RES_BANNER"),
                                             ResourceUtil.NEUTRALLANGID, args.banner);
                }

                if (!string.IsNullOrEmpty(args.splash))
                {
                    args.WriteLine(string.Format("Embedding splash screen \"{0}\"", args.splash));
                    ResourceUpdate.WriteFile(h, new ResourceId("CUSTOM"), new ResourceId("RES_SPLASH"),
                                             ResourceUtil.NEUTRALLANGID, args.splash);
                }

                args.WriteLine(string.Format("Embedding configuration \"{0}\"", configFilename));
                ResourceUpdate.WriteFile(h, new ResourceId("CUSTOM"), new ResourceId("RES_CONFIGURATION"),
                                         ResourceUtil.NEUTRALLANGID, configFilename);

                #region Embed Resources

                EmbedFileCollection html_files = new EmbedFileCollection(args.apppath);

                if (args.htmlFiles != null)
                {
                    foreach (string filename in args.htmlFiles)
                    {
                        string fullpath = Path.GetFullPath(filename);
                        if (Directory.Exists(fullpath))
                        {
                            html_files.AddDirectory(fullpath);
                        }
                        else
                        {
                            html_files.Add(new EmbedFilePair(fullpath, Path.GetFileName(filename)));
                        }
                    }
                }

                using (IEnumerator <EmbedFilePair> html_files_enumerator = html_files.GetEnumerator())
                {
                    while (html_files_enumerator.MoveNext())
                    {
                        EmbedFilePair pair = html_files_enumerator.Current;
                        String        id   = "";
                        for (int i = 0; i < pair.relativepath.Length; i++)
                        {
                            id += Char.IsLetterOrDigit(pair.relativepath[i]) ? pair.relativepath[i] : '_';
                        }

                        args.WriteLine(string.Format("Embedding HTML resource \"{0}\": {1}", id, pair.fullpath));
                        ResourceUpdate.WriteFile(h, new ResourceId("HTM"), new ResourceId(id.ToUpper()),
                                                 ResourceUtil.NEUTRALLANGID, pair.fullpath);
                    }
                }

                #endregion

                #region Embed CABs

                if (args.embed)
                {
                    args.WriteLine("Embedding CABs");
                    foreach (string cabfile in Directory.GetFiles(cabtemp))
                    {
                        args.WriteLine(string.Format(" {0} - {1}", Path.GetFileName(cabfile),
                                                     EmbedFileCollection.FormatBytes(new FileInfo(cabfile).Length)));

                        ResourceUpdate.WriteFile(h, new ResourceId("RES_CAB"), new ResourceId(Path.GetFileName(cabfile)),
                                                 ResourceUtil.NEUTRALLANGID, cabfile);
                    }

                    // cab directory

                    args.WriteLine("Embedding CAB directory");
                    StringBuilder filesDirectory = new StringBuilder();
                    filesDirectory.AppendLine(string.Format("Total CAB size: {0}\r\n", EmbedFileCollection.FormatBytes(totalSize)));
                    filesDirectory.AppendLine(string.Join("\r\n\r\n", allFilesList.ToArray()));
                    byte[] filesDirectory_b = Encoding.Unicode.GetBytes(filesDirectory.ToString());
                    ResourceUpdate.Write(h, new ResourceId("CUSTOM"), new ResourceId("RES_CAB_LIST"),
                                         ResourceUtil.NEUTRALLANGID, filesDirectory_b);
                }

                #endregion

                // resource files
                ResourceFileCollection resources = configfile.GetResources(supportdir);
                foreach (ResourceFilePair r_pair in resources)
                {
                    args.WriteLine(string.Format("Embedding resource \"{0}\": {1}", r_pair.id, r_pair.path));
                    ResourceUpdate.WriteFile(h, new ResourceId("CUSTOM"), new ResourceId(r_pair.id),
                                             ResourceUtil.NEUTRALLANGID, r_pair.path);
                }

                args.WriteLine(string.Format("Writing {0}", EmbedFileCollection.FormatBytes(totalSize)));

                if (!ResourceUpdate.EndUpdateResource(h, false))
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }

                #endregion
            }
            finally
            {
                if (Directory.Exists(cabtemp))
                {
                    args.WriteLine(string.Format("Cleaning up \"{0}\"", cabtemp));
                    Directory.Delete(cabtemp, true);
                }

                if (!string.IsNullOrEmpty(configTemp))
                {
                    args.WriteLine(string.Format("Cleaning up \"{0}\"", configTemp));
                    File.Delete(configTemp);
                }
            }

            args.WriteLine(string.Format("Successfully created \"{0}\" ({1})",
                                         args.output, EmbedFileCollection.FormatBytes(new FileInfo(args.output).Length)));
        }
        public void TestProcessorArchitectureFilter()
        {
            // configurations
            SetupConfiguration nofilterConfiguration = new SetupConfiguration();
            SetupConfiguration x86Configuration = new SetupConfiguration();
            x86Configuration.processor_architecture_filter = "x86";
            SetupConfiguration x64Configuration = new SetupConfiguration();
            x64Configuration.processor_architecture_filter = "x64";
            SetupConfiguration mipsConfiguration = new SetupConfiguration();
            mipsConfiguration.processor_architecture_filter = "mips";
            // components
            ComponentCmd nofilterComponent = new ComponentCmd();
            ComponentCmd x86Component = new ComponentCmd();
            x86Component.processor_architecture_filter = "x86";
            ComponentCmd x64Component = new ComponentCmd();
            x64Component.processor_architecture_filter = "x64";
            ComponentCmd mipsComponent = new ComponentCmd();
            mipsComponent.processor_architecture_filter = "mips";
            // make a tree
            nofilterConfiguration.Children.Add(nofilterComponent);
            nofilterConfiguration.Children.Add(x86Component);
            nofilterConfiguration.Children.Add(x64Component);
            nofilterConfiguration.Children.Add(mipsComponent);
            x86Configuration.Children.Add(nofilterComponent);
            x86Configuration.Children.Add(x86Component);
            x86Configuration.Children.Add(x64Component);
            x86Configuration.Children.Add(mipsComponent);
            x64Configuration.Children.Add(nofilterComponent);
            x64Configuration.Children.Add(x86Component);
            x64Configuration.Children.Add(x64Component);
            x64Configuration.Children.Add(mipsComponent);
            mipsConfiguration.Children.Add(nofilterComponent);
            mipsConfiguration.Children.Add(x86Component);
            mipsConfiguration.Children.Add(x64Component);
            mipsConfiguration.Children.Add(mipsComponent);
            // configfile
            ConfigFile configFile = new ConfigFile();
            configFile.Children.Add(nofilterConfiguration);
            configFile.Children.Add(x86Configuration);
            configFile.Children.Add(x64Configuration);
            configFile.Children.Add(mipsConfiguration);

            // Console.WriteLine("Total : {0}x{1}", configFile.ConfigurationCount, configFile.ComponentCount);

            TestProcessorArchitectureFilter_TestData[] testdata =
            {
                new TestProcessorArchitectureFilter_TestData("", 4, 16),
                new TestProcessorArchitectureFilter_TestData("x86", 2, 4),
                new TestProcessorArchitectureFilter_TestData("x64", 2, 4),
                new TestProcessorArchitectureFilter_TestData("mips", 2, 4),
                new TestProcessorArchitectureFilter_TestData("sparc", 1, 1),
                new TestProcessorArchitectureFilter_TestData("x86,x64", 3, 9),
                new TestProcessorArchitectureFilter_TestData("alpha,x64", 2, 4),
            };

            foreach (TestProcessorArchitectureFilter_TestData test in testdata)
            {
                ProcessorArchitectureFilter filter = new ProcessorArchitectureFilter(test.Filter);
                XmlDocument filteredXml = configFile.GetXml(filter);
                ConfigFile filteredConfigFile = new ConfigFile();
                filteredConfigFile.LoadXml(filteredXml);
                //Console.WriteLine("{0} - {1}x{2}", test.Filter,
                //    filteredConfigFile.ConfigurationCount, filteredConfigFile.ComponentCount);
                Assert.AreEqual(test.ExpectedConfigs, filteredConfigFile.ConfigurationCount);
                Assert.AreEqual(test.ExpectedComponents, filteredConfigFile.ComponentCount);
            }
        }