/// <summary>
        ///   Saves the ZipFile instance to a self-extracting zip archive, using the specified
        ///   save options.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   This method saves a self extracting archive, using the specified save
        ///   options. These options include the flavor of the SFX, the default extract
        ///   directory, the icon file, and so on.  See the documentation
        ///   for <see cref="SaveSelfExtractor(string , SelfExtractorFlavor)"/> for more
        ///   details.
        /// </para>
        ///
        /// <para>
        ///   The user who runs the SFX will have the opportunity to change the extract
        ///   directory before extracting. If at the time of extraction, the specified
        ///   directory does not exist, the SFX will create the directory before
        ///   extracting the files.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <example>
        ///   This example saves a self-extracting archive that will use c:\ExtractHere
        ///   as the default extract location.
        /// <code>
        /// string DirectoryPath = "c:\\Documents\\Project7";
        /// using (ZipFile zip = new ZipFile())
        /// {
        ///     zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath));
        ///     zip.Comment = "This will be embedded into a self-extracting console-based exe";
        ///     SelfExtractorOptions options = new SelfExtractorOptions();
        ///     options.Flavor = SelfExtractorFlavor.ConsoleApplication;
        ///     options.DefaultExtractDirectory = "%USERPROFILE%\\ExtractHere";
        ///     options.PostExtractCommandLine = ExeToRunAfterExtract;
        ///     options.RemoveUnpackedFilesAfterExecute = true;
        ///     zip.SaveSelfExtractor("archive.exe", options);
        /// }
        /// </code>
        /// <code lang="VB">
        /// Dim DirectoryPath As String = "c:\Documents\Project7"
        /// Using zip As New ZipFile()
        ///     zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath))
        ///     zip.Comment = "This will be embedded into a self-extracting console-based exe"
        ///     Dim options As New SelfExtractorOptions()
        ///     options.Flavor = SelfExtractorFlavor.ConsoleApplication
        ///     options.DefaultExtractDirectory = "%USERPROFILE%\\ExtractHere"
        ///     options.PostExtractCommandLine = ExeToRunAfterExtract
        ///     options.RemoveUnpackedFilesAfterExecute = True
        ///     zip.SaveSelfExtractor("archive.exe", options)
        /// End Using
        /// </code>
        /// </example>
        ///
        /// <param name="exeToGenerate">The name of the EXE to generate.</param>
        /// <param name="options">provides the options for how to save the Self-extracting archive.</param>
        public void SaveSelfExtractor(string exeToGenerate, SelfExtractorSaveOptions options)
        {
            // Save an SFX that is both an EXE and a ZIP.

            // Check for the case where we are re-saving a zip archive
            // that was originally instantiated with a stream.  In that case,
            // the _name will be null. If so, we set _writestream to null,
            // which insures that we'll cons up a new WriteStream (with a filesystem
            // file backing it) in the Save() method.
            if (_name == null)
            {
                _writestream = null;
            }

            _SavingSfx = true;
            _name      = exeToGenerate;
            if (Directory.Exists(_name))
            {
                throw new ZipException("Bad Directory", new System.ArgumentException("That name specifies an existing directory. Please specify a filename.", "exeToGenerate"));
            }
            _contentsChanged   = true;
            _fileAlreadyExists = File.Exists(_name);

            _SaveSfxStub(exeToGenerate, options);

            Save();
            _SavingSfx = false;
        }
        //string _defaultExtractLocation;
        //string _postExtractCmdLine;
        //         string _SetDefaultLocationCode =
        //         "namespace PMDCP.Compression.Zip { public partial class WinFormsSelfExtractorStub { partial void _SetDefaultExtractLocation() {" +
        //         " txtExtractDirectory.Text = \"@@VALUE\"; } }}";



        /// <summary>
        /// Saves the ZipFile instance to a self-extracting zip archive.
        /// </summary>
        ///
        /// <remarks>
        ///
        /// <para>
        /// The generated exe image will execute on any machine that has the .NET Framework 2.0
        /// installed on it.  The generated exe image is also a valid ZIP file, readable with DotNetZip
        /// or another Zip library or tool such as WinZip.
        /// </para>
        ///
        /// <para>
        /// There are two "flavors" of self-extracting archive.  The <c>WinFormsApplication</c>
        /// version will pop up a GUI and allow the user to select a target directory into which
        /// to extract. There's also a checkbox allowing the user to specify to overwrite
        /// existing files, and another checkbox to allow the user to request that Explorer be
        /// opened to see the extracted files after extraction.  The other flavor is
        /// <c>ConsoleApplication</c>.  A self-extractor generated with that flavor setting will
        /// run from the command line. It accepts command-line options to set the overwrite
        /// behavior, and to specify the target extraction directory.
        /// </para>
        ///
        /// <para>
        /// There are a few temporary files created during the saving to a self-extracting zip.
        /// These files are created in the directory pointed to by <see
        /// cref="ZipFile.TempFileFolder"/>, which defaults to <see
        /// cref="System.IO.Path.GetTempPath"/>.  These temporary files are removed upon
        /// successful completion of this method.
        /// </para>
        ///
        /// <para>
        /// When a user runs the WinForms SFX, the user's personal directory (<see
        /// cref="Environment.SpecialFolder.Personal"/>) will be used as the default extract
        /// location.  The user who runs the SFX will have the opportunity to change the extract
        /// directory before extracting. When the user runs the Command-Line SFX, the user must
        /// explicitly specify the directory to which to extract.  The .NET Framework 2.0 is
        /// required on the computer when the self-extracting archive is run.
        /// </para>
        ///
        /// <para>
        /// NB: This method is not available in the version of DotNetZip
        /// build for the .NET Compact Framework, nor in the "Reduced" DotNetZip library.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <example>
        /// <code>
        /// string DirectoryPath = "c:\\Documents\\Project7";
        /// using (ZipFile zip = new ZipFile())
        /// {
        ///     zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath));
        ///     zip.Comment = "This will be embedded into a self-extracting console-based exe";
        ///     zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication);
        /// }
        /// </code>
        /// <code lang="VB">
        /// Dim DirectoryPath As String = "c:\Documents\Project7"
        /// Using zip As New ZipFile()
        ///     zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath))
        ///     zip.Comment = "This will be embedded into a self-extracting console-based exe"
        ///     zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication)
        /// End Using
        /// </code>
        /// </example>
        ///
        /// <param name="exeToGenerate">a pathname, possibly fully qualified, to be created. Typically it will end in an .exe extension.</param>
        /// <param name="flavor">Indicates whether a Winforms or Console self-extractor is desired.</param>
        public void SaveSelfExtractor(string exeToGenerate, SelfExtractorFlavor flavor)
        {
            SelfExtractorSaveOptions options = new SelfExtractorSaveOptions();

            options.Flavor = flavor;
            SaveSelfExtractor(exeToGenerate, options);
        }
        private void _SaveSfxStub(string exeToGenerate, SelfExtractorSaveOptions options)
        {
            string nameOfIconFile = null;
            string StubExe        = null;
            string TempDir        = null;

            try
            {
                if (File.Exists(exeToGenerate))
                {
                    if (Verbose)
                    {
                        StatusMessageTextWriter.WriteLine("The existing file ({0}) will be overwritten.", exeToGenerate);
                    }
                }
                if (!exeToGenerate.EndsWith(".exe"))
                {
                    if (Verbose)
                    {
                        StatusMessageTextWriter.WriteLine("Warning: The generated self-extracting file will not have an .exe extension.");
                    }
                }

                StubExe = GenerateTempPathname("exe");

                // get the PMDCP.Compression.Zip assembly
                Assembly a1 = typeof(ZipFile).Assembly;

                Microsoft.CSharp.CSharpCodeProvider csharp = new Microsoft.CSharp.CSharpCodeProvider();

                // Perfect opportunity for a linq query, but I cannot use it.
                // The DotNetZip library can compile into 2.0, but needs to run on .NET 2.0.
                // Using LINQ would break that. Here's what it would look like:
                //
                //      var settings = (from x in SettingsList
                //                      where x.Flavor == flavor
                //                      select x).First();

                ExtractorSettings settings = null;
                foreach (var x in SettingsList)
                {
                    if (x.Flavor == options.Flavor)
                    {
                        settings = x;
                        break;
                    }
                }

                if (settings == null)
                {
                    throw new BadStateException(String.Format("While saving a Self-Extracting Zip, Cannot find that flavor ({0})?", options.Flavor));
                }

                // This is the list of referenced assemblies.  PMDCP.Compression.Zip is needed here.
                // Also if it is the winforms (gui) extractor, we need other referenced assemblies,
                // like System.Windows.Forms.dll, etc.
                System.CodeDom.Compiler.CompilerParameters cp = new System.CodeDom.Compiler.CompilerParameters();
                cp.ReferencedAssemblies.Add(a1.Location);
                if (settings.ReferencedAssemblies != null)
                {
                    foreach (string ra in settings.ReferencedAssemblies)
                    {
                        cp.ReferencedAssemblies.Add(ra);
                    }
                }

                cp.GenerateInMemory        = false;
                cp.GenerateExecutable      = true;
                cp.IncludeDebugInformation = false;
                cp.CompilerOptions         = "";

                Assembly a2 = Assembly.GetExecutingAssembly();

                // Use this to concatenate all the source code resources into a single module
                var sb = new System.Text.StringBuilder();

                // In case there are compiler errors later, we allocate a
                // source file name now.
                string sourceFile = GenerateTempPathname("cs");



                // // debugging: enumerate the resources in this assembly
                // Console.WriteLine("Resources in this assembly:");
                // foreach (string rsrc in a2.GetManifestResourceNames())
                //   {
                //     Console.WriteLine(rsrc);
                //   }
                // Console.WriteLine();


                // all the source code is embedded in the DLL as a zip file.
                using (ZipFile zip = ZipFile.Read(a2.GetManifestResourceStream("PMDCP.Compression.Zip.Resources.ZippedResources.zip")))
                {
                    // // debugging: enumerate the files in the embedded zip
                    // Console.WriteLine("Entries in the embbedded zip:");
                    // foreach (ZipEntry entry in zip)
                    //   {
                    //     Console.WriteLine(entry.FileName);
                    //   }
                    // Console.WriteLine();

                    TempDir = GenerateTempPathname("tmp");

                    if (String.IsNullOrEmpty(options.IconFile))
                    {
                        // Use the embedded ico file. But we must unpack it to the
                        // filesystem, in order to specify it on the cmdline of csc.exe.  We
                        // will remove this file later.
                        System.IO.Directory.CreateDirectory(TempDir);
                        ZipEntry e = zip["zippedFile.ico"];
                        // Must not extract a readonly file - it will be impossible to
                        // delete later.
                        if ((e.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                        {
                            e.Attributes ^= FileAttributes.ReadOnly;
                        }
                        e.Extract(TempDir);
                        nameOfIconFile      = Path.Combine(TempDir, "zippedFile.ico");
                        cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", nameOfIconFile);
                    }
                    else
                    {
                        cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", options.IconFile);
                    }

                    cp.OutputAssembly = StubExe;

                    if (options.Flavor == SelfExtractorFlavor.WinFormsApplication)
                    {
                        cp.CompilerOptions += " /target:winexe";
                    }

                    if (cp.CompilerOptions == "")
                    {
                        cp.CompilerOptions = null;
                    }

                    if ((settings.CopyThroughResources != null) && (settings.CopyThroughResources.Count != 0))
                    {
                        if (!Directory.Exists(TempDir))
                        {
                            System.IO.Directory.CreateDirectory(TempDir);
                        }
                        foreach (string re in settings.CopyThroughResources)
                        {
                            string filename = Path.Combine(TempDir, re);

                            ExtractResourceToFile(a2, re, filename);
                            // add the file into the target assembly as an embedded resource
                            cp.EmbeddedResources.Add(filename);
                        }
                    }

                    // add the Ionic.Utils.Zip DLL as an embedded resource
                    cp.EmbeddedResources.Add(a1.Location);

                    // file header
                    sb.Append("// " + Path.GetFileName(sourceFile) + "\n")
                    .Append("// --------------------------------------------\n//\n")
                    .Append("// This SFX source file was generated by DotNetZip ")
                    .Append(ZipFile.LibraryVersion.ToString())
                    .Append("\n//         at ")
                    .Append(System.DateTime.Now.ToString("yyyy MMMM dd  HH:mm:ss"))
                    .Append("\n//\n// --------------------------------------------\n\n\n");

                    // assembly attributes
                    if (!String.IsNullOrEmpty(options.Description))
                    {
                        sb.Append("[assembly: System.Reflection.AssemblyTitle(\""
                                  + options.Description.Replace("\"", "")
                                  + "\")]\n");
                    }
                    else
                    {
                        sb.Append("[assembly: System.Reflection.AssemblyTitle(\"DotNetZip SFX Archive\")]\n");
                    }

                    if (!String.IsNullOrEmpty(options.ProductVersion))
                    {
                        sb.Append("[assembly: System.Reflection.AssemblyInformationalVersion(\""
                                  + options.ProductVersion.Replace("\"", "")
                                  + "\")]\n");
                    }

                    string copyright = "Extractor: Copyright © Dino Chiesa 2008, 2009";
                    if (!String.IsNullOrEmpty(options.Copyright))
                    {
                        copyright += "Contents: " + options.Copyright.Replace("\"", "");
                    }


                    if (!String.IsNullOrEmpty(options.ProductName))
                    {
                        sb.Append("[assembly: System.Reflection.AssemblyProduct(\"")
                        .Append(options.ProductName.Replace("\"", ""))
                        .Append("\")]\n");
                    }
                    else
                    {
                        sb.Append("[assembly: System.Reflection.AssemblyProduct(\"DotNetZip\")]\n");
                    }


                    sb.Append("[assembly: System.Reflection.AssemblyCopyright(\"" + copyright + "\")]\n")
                    .Append(String.Format("[assembly: System.Reflection.AssemblyVersion(\"{0}\")]\n", ZipFile.LibraryVersion.ToString()));
                    if (options.FileVersion != null)
                    {
                        sb.Append(String.Format("[assembly: System.Reflection.AssemblyFileVersion(\"{0}\")]\n",
                                                options.FileVersion.ToString()));
                    }

                    sb.Append("\n\n\n");

                    // Set the default extract location if it is available
                    string extractLoc = options.DefaultExtractDirectory;
                    if (extractLoc != null)
                    {
                        // remove double-quotes and replace slash with double-slash.
                        // This, because the value is going to be embedded into a
                        // cs file as a quoted string, and it needs to be escaped.
                        extractLoc = extractLoc.Replace("\"", "").Replace("\\", "\\\\");
                    }

                    string postExCmdLine = options.PostExtractCommandLine;
                    if (postExCmdLine != null)
                    {
                        postExCmdLine = postExCmdLine.Replace("\\", "\\\\");
                        postExCmdLine = postExCmdLine.Replace("\"", "\\\"");
                    }


                    foreach (string rc in settings.ResourcesToCompile)
                    {
                        // Console.WriteLine("  trying to read entry: ({0})", rc);
                        using (Stream s = zip[rc].OpenReader())
                        {
                            if (s == null)
                            {
                                throw new ZipException(String.Format("missing resource '{0}'", rc));
                            }
                            using (StreamReader sr = new StreamReader(s))
                            {
                                while (sr.Peek() >= 0)
                                {
                                    string line = sr.ReadLine();
                                    if (extractLoc != null)
                                    {
                                        line = line.Replace("@@EXTRACTLOCATION", extractLoc);
                                    }

                                    line = line.Replace("@@REMOVE_AFTER_EXECUTE", options.RemoveUnpackedFilesAfterExecute.ToString());
                                    line = line.Replace("@@QUIET", options.Quiet.ToString());
                                    line = line.Replace("@@EXTRACT_EXISTING_FILE", ((int)options.ExtractExistingFile).ToString());

                                    if (postExCmdLine != null)
                                    {
                                        line = line.Replace("@@POST_UNPACK_CMD_LINE", postExCmdLine);
                                    }

                                    sb.Append(line).Append("\n");
                                }
                            }
                            sb.Append("\n\n");
                        }
                    }
                }

                string LiteralSource = sb.ToString();

                #if DEBUGSFX
                // for debugging only
                string sourceModule = GenerateTempPathname("cs");
                using (StreamWriter sw = File.CreateText(sourceModule))
                {
                    sw.Write(LiteralSource);
                }
                Console.WriteLine("source: {0}", sourceModule);
                #endif

                System.CodeDom.Compiler.CompilerResults cr = csharp.CompileAssemblyFromSource(cp, LiteralSource);


                if (cr == null)
                {
                    throw new SfxGenerationException("Cannot compile the extraction logic!");
                }

                if (Verbose)
                {
                    foreach (string output in cr.Output)
                    {
                        StatusMessageTextWriter.WriteLine(output);
                    }
                }

                if (cr.Errors.Count != 0)
                {
                    using (TextWriter tw = new StreamWriter(sourceFile))
                    {
                        // first, the source we compiled
                        tw.Write(LiteralSource);

                        // now, append the compile errors
                        tw.Write("\n\n\n// ------------------------------------------------------------------\n");
                        tw.Write("// Errors during compilation: \n//\n");
                        string p = Path.GetFileName(sourceFile);

                        foreach (System.CodeDom.Compiler.CompilerError error in cr.Errors)
                        {
                            tw.Write(String.Format("//   {0}({1},{2}): {3} {4}: {5}\n//\n",
                                                   p,                                   // 0
                                                   error.Line,                          // 1
                                                   error.Column,                        // 2
                                                   error.IsWarning?"Warning":"error",   // 3
                                                   error.ErrorNumber,                   // 4
                                                   error.ErrorText));                   // 5
                        }
                    }
                    throw new SfxGenerationException(String.Format("Errors compiling the extraction logic!  {0}", sourceFile));
                }

                OnSaveEvent(ZipProgressEventType.Saving_AfterCompileSelfExtractor);

                // Now, copy the resulting EXE image to the _writestream.
                // Because this stub exe is being saved first, the effect will be to
                // concatenate the exe and the zip data together.
                using (System.IO.Stream input = System.IO.File.OpenRead(StubExe))
                {
                    byte[] buffer = new byte[4000];
                    int    n      = 1;
                    while (n != 0)
                    {
                        n = input.Read(buffer, 0, buffer.Length);
                        if (n != 0)
                        {
                            WriteStream.Write(buffer, 0, n);
                        }
                    }
                }

                OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive);
            }
            finally
            {
                try
                {
                    if (Directory.Exists(TempDir))
                    {
                        try { Directory.Delete(TempDir, true); }
                        catch (Exception exc1)
                        {
                            Console.WriteLine("Exception: {0}", exc1.ToString());
                        }
                    }
                    if (File.Exists(StubExe))
                    {
                        try { File.Delete(StubExe); }
                        catch { }
                    }
                }
                catch { }
            }

            return;
        }
        private void _SaveSfxStub(string exeToGenerate, SelfExtractorSaveOptions options)
        {
            string nameOfIconFile= null;
            string StubExe = null;
            string TempDir = null;
            try
            {
                if (File.Exists(exeToGenerate))
                {
                    if (Verbose) StatusMessageTextWriter.WriteLine("The existing file ({0}) will be overwritten.", exeToGenerate);
                }
                if (!exeToGenerate.EndsWith(".exe"))
                {
                    if (Verbose) StatusMessageTextWriter.WriteLine("Warning: The generated self-extracting file will not have an .exe extension.");
                }

                StubExe = GenerateTempPathname("exe");

                // get the PMDCP.Compression.Zip assembly
                Assembly a1 = typeof(ZipFile).Assembly;

                Microsoft.CSharp.CSharpCodeProvider csharp = new Microsoft.CSharp.CSharpCodeProvider();

                // Perfect opportunity for a linq query, but I cannot use it.
                // The DotNetZip library can compile into 2.0, but needs to run on .NET 2.0.
                // Using LINQ would break that. Here's what it would look like:
                //
                //      var settings = (from x in SettingsList
                //                      where x.Flavor == flavor
                //                      select x).First();

                ExtractorSettings settings = null;
                foreach (var x in SettingsList)
                {
                    if (x.Flavor == options.Flavor)
                    {
                        settings = x;
                        break;
                    }
                }

                if (settings == null)
                    throw new BadStateException(String.Format("While saving a Self-Extracting Zip, Cannot find that flavor ({0})?", options.Flavor));

                // This is the list of referenced assemblies.  PMDCP.Compression.Zip is needed here.
                // Also if it is the winforms (gui) extractor, we need other referenced assemblies,
                // like System.Windows.Forms.dll, etc.
                System.CodeDom.Compiler.CompilerParameters cp = new System.CodeDom.Compiler.CompilerParameters();
                cp.ReferencedAssemblies.Add(a1.Location);
                if (settings.ReferencedAssemblies != null)
                    foreach (string ra in settings.ReferencedAssemblies)
                        cp.ReferencedAssemblies.Add(ra);

                cp.GenerateInMemory = false;
                cp.GenerateExecutable = true;
                cp.IncludeDebugInformation = false;
                cp.CompilerOptions = "";

                Assembly a2 = Assembly.GetExecutingAssembly();

                // Use this to concatenate all the source code resources into a single module
                var sb = new System.Text.StringBuilder();

                // In case there are compiler errors later, we allocate a
                // source file name now.
                string sourceFile = GenerateTempPathname("cs");

                // // debugging: enumerate the resources in this assembly
                // Console.WriteLine("Resources in this assembly:");
                // foreach (string rsrc in a2.GetManifestResourceNames())
                //   {
                //     Console.WriteLine(rsrc);
                //   }
                // Console.WriteLine();

                // all the source code is embedded in the DLL as a zip file.
                using (ZipFile zip = ZipFile.Read(a2.GetManifestResourceStream("PMDCP.Compression.Zip.Resources.ZippedResources.zip")))
                {
                    // // debugging: enumerate the files in the embedded zip
                    // Console.WriteLine("Entries in the embbedded zip:");
                    // foreach (ZipEntry entry in zip)
                    //   {
                    //     Console.WriteLine(entry.FileName);
                    //   }
                    // Console.WriteLine();

                    TempDir = GenerateTempPathname("tmp");

                    if (String.IsNullOrEmpty(options.IconFile))
                    {
                        // Use the embedded ico file. But we must unpack it to the
                        // filesystem, in order to specify it on the cmdline of csc.exe.  We
                        // will remove this file later.
                        System.IO.Directory.CreateDirectory(TempDir);
                        ZipEntry e = zip["zippedFile.ico"];
                        // Must not extract a readonly file - it will be impossible to
                        // delete later.
                        if ((e.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                            e.Attributes ^= FileAttributes.ReadOnly;
                        e.Extract(TempDir);
                        nameOfIconFile = Path.Combine(TempDir, "zippedFile.ico");
                        cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", nameOfIconFile);
                    }
                    else
                        cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", options.IconFile);

                    cp.OutputAssembly = StubExe;

                    if (options.Flavor == SelfExtractorFlavor.WinFormsApplication)
                        cp.CompilerOptions += " /target:winexe";

                    if (cp.CompilerOptions == "")
                        cp.CompilerOptions = null;

                    if ((settings.CopyThroughResources != null) && (settings.CopyThroughResources.Count != 0))
                    {
                        if (!Directory.Exists(TempDir)) System.IO.Directory.CreateDirectory(TempDir);
                        foreach (string re in settings.CopyThroughResources)
                        {
                            string filename = Path.Combine(TempDir, re);

                            ExtractResourceToFile(a2, re, filename);
                            // add the file into the target assembly as an embedded resource
                            cp.EmbeddedResources.Add(filename);
                        }
                    }

                    // add the Ionic.Utils.Zip DLL as an embedded resource
                    cp.EmbeddedResources.Add(a1.Location);

                    // file header
                    sb.Append("// " + Path.GetFileName(sourceFile) + "\n")
                        .Append("// --------------------------------------------\n//\n")
                        .Append("// This SFX source file was generated by DotNetZip ")
                        .Append(ZipFile.LibraryVersion.ToString())
                        .Append("\n//         at ")
                        .Append(System.DateTime.Now.ToString("yyyy MMMM dd  HH:mm:ss"))
                        .Append("\n//\n// --------------------------------------------\n\n\n");

                    // assembly attributes
                    if (!String.IsNullOrEmpty(options.Description))
                        sb.Append("[assembly: System.Reflection.AssemblyTitle(\""
                                  + options.Description.Replace("\"", "")
                                  + "\")]\n");
                    else
                        sb.Append("[assembly: System.Reflection.AssemblyTitle(\"DotNetZip SFX Archive\")]\n");

                    if (!String.IsNullOrEmpty(options.ProductVersion))
                        sb.Append("[assembly: System.Reflection.AssemblyInformationalVersion(\""
                                  + options.ProductVersion.Replace("\"", "")
                                  + "\")]\n");

                    string copyright = "Extractor: Copyright © Dino Chiesa 2008, 2009";
                    if (!String.IsNullOrEmpty(options.Copyright))
                        copyright += "Contents: " + options.Copyright.Replace("\"", "");

                    if (!String.IsNullOrEmpty(options.ProductName))
                        sb.Append("[assembly: System.Reflection.AssemblyProduct(\"")
                            .Append(options.ProductName.Replace("\"", ""))
                            .Append("\")]\n");
                    else
                        sb.Append("[assembly: System.Reflection.AssemblyProduct(\"DotNetZip\")]\n");

                    sb.Append("[assembly: System.Reflection.AssemblyCopyright(\"" + copyright + "\")]\n")
                        .Append(String.Format("[assembly: System.Reflection.AssemblyVersion(\"{0}\")]\n", ZipFile.LibraryVersion.ToString()));
                    if (options.FileVersion != null)
                        sb.Append(String.Format("[assembly: System.Reflection.AssemblyFileVersion(\"{0}\")]\n",
                                                options.FileVersion.ToString()));

                    sb.Append("\n\n\n");

                    // Set the default extract location if it is available
                    string extractLoc = options.DefaultExtractDirectory;
                    if (extractLoc != null)
                    {
                        // remove double-quotes and replace slash with double-slash.
                        // This, because the value is going to be embedded into a
                        // cs file as a quoted string, and it needs to be escaped.
                        extractLoc = extractLoc.Replace("\"", "").Replace("\\", "\\\\");
                    }

                    string postExCmdLine = options.PostExtractCommandLine;
                    if (postExCmdLine  != null)
                    {
                        postExCmdLine = postExCmdLine.Replace("\\","\\\\");
                        postExCmdLine = postExCmdLine.Replace("\"","\\\"");
                    }

                    foreach (string rc in settings.ResourcesToCompile)
                    {
                        // Console.WriteLine("  trying to read entry: ({0})", rc);
                        using (Stream s = zip[rc].OpenReader())
                        {
                            if (s == null)
                                throw new ZipException(String.Format("missing resource '{0}'", rc));
                            using (StreamReader sr = new StreamReader(s))
                            {
                                while (sr.Peek() >= 0)
                                {
                                    string line = sr.ReadLine();
                                    if (extractLoc != null)
                                        line = line.Replace("@@EXTRACTLOCATION", extractLoc);

                                    line = line.Replace("@@REMOVE_AFTER_EXECUTE", options.RemoveUnpackedFilesAfterExecute.ToString());
                                    line = line.Replace("@@QUIET", options.Quiet.ToString());
                                    line = line.Replace("@@EXTRACT_EXISTING_FILE", ((int)options.ExtractExistingFile).ToString());

                                    if (postExCmdLine != null)
                                        line = line.Replace("@@POST_UNPACK_CMD_LINE", postExCmdLine);

                                    sb.Append(line).Append("\n");
                                }
                            }
                            sb.Append("\n\n");
                        }
                    }
                }

                string LiteralSource = sb.ToString();

                #if DEBUGSFX
                // for debugging only
                string sourceModule = GenerateTempPathname("cs");
                using (StreamWriter sw = File.CreateText(sourceModule))
                {
                    sw.Write(LiteralSource);
                }
                Console.WriteLine("source: {0}", sourceModule);
                #endif

                System.CodeDom.Compiler.CompilerResults cr = csharp.CompileAssemblyFromSource(cp, LiteralSource);

                if (cr == null)
                    throw new SfxGenerationException("Cannot compile the extraction logic!");

                if (Verbose)
                    foreach (string output in cr.Output)
                        StatusMessageTextWriter.WriteLine(output);

                if (cr.Errors.Count != 0)
                {
                    using (TextWriter tw = new StreamWriter(sourceFile))
                    {
                        // first, the source we compiled
                        tw.Write(LiteralSource);

                        // now, append the compile errors
                        tw.Write("\n\n\n// ------------------------------------------------------------------\n");
                        tw.Write("// Errors during compilation: \n//\n");
                        string p = Path.GetFileName(sourceFile);

                        foreach( System.CodeDom.Compiler.CompilerError error in cr.Errors)
                        {
                            tw.Write(String.Format("//   {0}({1},{2}): {3} {4}: {5}\n//\n",
                                                   p,                                   // 0
                                                   error.Line,                          // 1
                                                   error.Column,                        // 2
                                                   error.IsWarning?"Warning":"error",   // 3
                                                   error.ErrorNumber,                   // 4
                                                   error.ErrorText ));                  // 5
                        }
                    }
                    throw new SfxGenerationException(String.Format("Errors compiling the extraction logic!  {0}", sourceFile));
                }

                OnSaveEvent(ZipProgressEventType.Saving_AfterCompileSelfExtractor);

                // Now, copy the resulting EXE image to the _writestream.
                // Because this stub exe is being saved first, the effect will be to
                // concatenate the exe and the zip data together.
                using (System.IO.Stream input = System.IO.File.OpenRead(StubExe))
                {
                    byte[] buffer = new byte[4000];
                    int n = 1;
                    while (n != 0)
                    {
                        n = input.Read(buffer, 0, buffer.Length);
                        if (n != 0)
                            WriteStream.Write(buffer, 0, n);
                    }
                }

                OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive);
            }
            finally
            {
                try
                {
                    if (Directory.Exists(TempDir))
                    {
                        try { Directory.Delete(TempDir, true); }
                        catch (Exception exc1)
                        {
                            Console.WriteLine("Exception: {0}", exc1.ToString());
                        }
                    }
                    if (File.Exists(StubExe))
                    {
                        try { File.Delete(StubExe); }
                        catch { }
                    }
                }
                catch { }

            }

            return;
        }
        /// <summary>
        ///   Saves the ZipFile instance to a self-extracting zip archive, using the specified 
        ///   save options. 
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   This method saves a self extracting archive, using the specified save
        ///   options. These options include the flavor of the SFX, the default extract
        ///   directory, the icon file, and so on.  See the documentation
        ///   for <see cref="SaveSelfExtractor(string , SelfExtractorFlavor)"/> for more
        ///   details.
        /// </para>
        ///
        /// <para>
        ///   The user who runs the SFX will have the opportunity to change the extract
        ///   directory before extracting. If at the time of extraction, the specified
        ///   directory does not exist, the SFX will create the directory before
        ///   extracting the files.
        /// </para>
        /// 
        /// </remarks>
        ///
        /// <example>
        ///   This example saves a self-extracting archive that will use c:\ExtractHere
        ///   as the default extract location.
        /// <code>
        /// string DirectoryPath = "c:\\Documents\\Project7";
        /// using (ZipFile zip = new ZipFile())
        /// {
        ///     zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath));
        ///     zip.Comment = "This will be embedded into a self-extracting console-based exe";
        ///     SelfExtractorOptions options = new SelfExtractorOptions();
        ///     options.Flavor = SelfExtractorFlavor.ConsoleApplication;
        ///     options.DefaultExtractDirectory = "%USERPROFILE%\\ExtractHere";
        ///     options.PostExtractCommandLine = ExeToRunAfterExtract;
        ///     options.RemoveUnpackedFilesAfterExecute = true;
        ///     zip.SaveSelfExtractor("archive.exe", options);
        /// }
        /// </code>
        /// <code lang="VB">
        /// Dim DirectoryPath As String = "c:\Documents\Project7"
        /// Using zip As New ZipFile()
        ///     zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath))
        ///     zip.Comment = "This will be embedded into a self-extracting console-based exe"
        ///     Dim options As New SelfExtractorOptions()
        ///     options.Flavor = SelfExtractorFlavor.ConsoleApplication
        ///     options.DefaultExtractDirectory = "%USERPROFILE%\\ExtractHere"
        ///     options.PostExtractCommandLine = ExeToRunAfterExtract
        ///     options.RemoveUnpackedFilesAfterExecute = True
        ///     zip.SaveSelfExtractor("archive.exe", options)
        /// End Using
        /// </code>
        /// </example>
        /// 
        /// <param name="exeToGenerate">The name of the EXE to generate.</param>
        /// <param name="options">provides the options for how to save the Self-extracting archive.</param>
        public void SaveSelfExtractor(string exeToGenerate, SelfExtractorSaveOptions options)
        {
            // Save an SFX that is both an EXE and a ZIP.

            // Check for the case where we are re-saving a zip archive
            // that was originally instantiated with a stream.  In that case,
            // the _name will be null. If so, we set _writestream to null,
            // which insures that we'll cons up a new WriteStream (with a filesystem
            // file backing it) in the Save() method.
            if (_name == null)
                _writestream = null;

            _SavingSfx = true;
            _name = exeToGenerate;
            if (Directory.Exists(_name))
                throw new ZipException("Bad Directory", new System.ArgumentException("That name specifies an existing directory. Please specify a filename.", "exeToGenerate"));
            _contentsChanged = true;
            _fileAlreadyExists = File.Exists(_name);

            _SaveSfxStub(exeToGenerate, options);

            Save();
            _SavingSfx = false;
        }
 //string _defaultExtractLocation;
 //string _postExtractCmdLine;
 //         string _SetDefaultLocationCode =
 //         "namespace PMDCP.Compression.Zip { public partial class WinFormsSelfExtractorStub { partial void _SetDefaultExtractLocation() {" +
 //         " txtExtractDirectory.Text = \"@@VALUE\"; } }}";
 /// <summary>
 /// Saves the ZipFile instance to a self-extracting zip archive.
 /// </summary>
 /// 
 /// <remarks>
 /// 
 /// <para>
 /// The generated exe image will execute on any machine that has the .NET Framework 2.0
 /// installed on it.  The generated exe image is also a valid ZIP file, readable with DotNetZip
 /// or another Zip library or tool such as WinZip. 
 /// </para>
 /// 
 /// <para>
 /// There are two "flavors" of self-extracting archive.  The <c>WinFormsApplication</c>
 /// version will pop up a GUI and allow the user to select a target directory into which
 /// to extract. There's also a checkbox allowing the user to specify to overwrite
 /// existing files, and another checkbox to allow the user to request that Explorer be
 /// opened to see the extracted files after extraction.  The other flavor is
 /// <c>ConsoleApplication</c>.  A self-extractor generated with that flavor setting will
 /// run from the command line. It accepts command-line options to set the overwrite
 /// behavior, and to specify the target extraction directory.
 /// </para>
 /// 
 /// <para>
 /// There are a few temporary files created during the saving to a self-extracting zip.
 /// These files are created in the directory pointed to by <see
 /// cref="ZipFile.TempFileFolder"/>, which defaults to <see
 /// cref="System.IO.Path.GetTempPath"/>.  These temporary files are removed upon
 /// successful completion of this method.
 /// </para>
 ///
 /// <para>
 /// When a user runs the WinForms SFX, the user's personal directory (<see
 /// cref="Environment.SpecialFolder.Personal"/>) will be used as the default extract
 /// location.  The user who runs the SFX will have the opportunity to change the extract
 /// directory before extracting. When the user runs the Command-Line SFX, the user must
 /// explicitly specify the directory to which to extract.  The .NET Framework 2.0 is
 /// required on the computer when the self-extracting archive is run.
 /// </para>
 ///
 /// <para>
 /// NB: This method is not available in the version of DotNetZip
 /// build for the .NET Compact Framework, nor in the "Reduced" DotNetZip library.  
 /// </para>
 /// 
 /// </remarks>
 /// 
 /// <example>
 /// <code>
 /// string DirectoryPath = "c:\\Documents\\Project7";
 /// using (ZipFile zip = new ZipFile())
 /// {
 ///     zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath));
 ///     zip.Comment = "This will be embedded into a self-extracting console-based exe";
 ///     zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication);
 /// }
 /// </code>
 /// <code lang="VB">
 /// Dim DirectoryPath As String = "c:\Documents\Project7"
 /// Using zip As New ZipFile()
 ///     zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath))
 ///     zip.Comment = "This will be embedded into a self-extracting console-based exe"
 ///     zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication)
 /// End Using
 /// </code>
 /// </example>
 /// 
 /// <param name="exeToGenerate">a pathname, possibly fully qualified, to be created. Typically it will end in an .exe extension.</param>
 /// <param name="flavor">Indicates whether a Winforms or Console self-extractor is desired.</param>
 public void SaveSelfExtractor(string exeToGenerate, SelfExtractorFlavor flavor)
 {
     SelfExtractorSaveOptions options = new SelfExtractorSaveOptions();
     options.Flavor = flavor;
     SaveSelfExtractor(exeToGenerate, options);
 }