Ejemplo n.º 1
0
        /// <summary>
        /// Creates a new, empty addon and encapsulates it within a realtime instance.
        /// </summary>
        /// <param name="filename">The path of the addon file to create.</param>
        /// <returns>A RealtimeAddon instance.</returns>
        /// <exception cref="UnauthorizedAccessException">The specified file already exists on the local filesystem.</exception>
        /// <exception cref="IOException">There was an error creating a specified file.</exception>
        public static RealtimeAddon New(string filename)
        {
            if (File.Exists(filename))
            {
                throw new UnauthorizedAccessException("The file already exists.");
            }

            if (Path.GetExtension(filename) != "gma")
            {
                filename = Path.GetFileNameWithoutExtension(filename);
                filename += ".gma";
            }

            FileStream fs;
            try
            {
                fs = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
            }
            catch (IOException)
            {
                throw;
            }

            Addon addon = new Addon();

            RealtimeAddon realtime = new RealtimeAddon(addon, fs);
            return realtime;
        }
Ejemplo n.º 2
0
 /// <summary>
 /// Creates the RealtimeAddon instance with the specified Addon to encapsulate and the FileStream pointing to the
 /// local filesystem. This method cannot be called externally.
 /// </summary>
 /// <param name="addon">The addon to encapsulate.</param>
 /// <param name="stream">The FileStream pointing to the GMA file on the local filesystem.</param>
 protected RealtimeAddon(Addon addon, FileStream stream)
     : this()
 {
     OpenAddon = addon;
     AddonStream = stream;
 }
Ejemplo n.º 3
0
        /// <summary>
        /// Compiles the specified addon into the specified stream.
        /// </summary>
        /// <param name="addon">The addon to compile.</param>
        /// <param name="stream">The stream which the result should be written to.</param>
        /// <exception cref="IOExecption">Happens if there is a problem with the specified stream.</exception>
        public static void Create(Addon addon, Stream stream)
        {
            BinaryWriter writer = new BinaryWriter(stream);
            writer.BaseStream.Seek(0, SeekOrigin.Begin);
            writer.BaseStream.SetLength(0);

            // Header (5)
            writer.Write(Addon.Ident.ToCharArray()); // Ident (4)
            writer.Write((char)Addon.Version); // Version (1)
            // SteamID (8) [unused]
            writer.Write((ulong)0);
            // TimeStamp (8)
            writer.Write((ulong)
                (((TimeSpan)(DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0).ToLocalTime())).TotalSeconds));
            // Required content (a list of strings)
            writer.Write((char)0); // signifies nothing
            // Addon Name (n)
            writer.WriteNullTerminatedString(addon.Title);
            // Addon Description (n)
            writer.WriteNullTerminatedString(addon.DescriptionJSON);
            // Addon Author (n) [unused]
            writer.WriteNullTerminatedString("Author Name");
            // Addon version (4) [unused]
            writer.Write((int)1);

            // File list
            uint fileNum = 0;

            foreach (ContentFile f in addon.Files)
            {
                // Remove prefix / from filename
                string file = f.Path.TrimStart('/');

                fileNum++;

                writer.Write(fileNum); // File number (4)
                writer.WriteNullTerminatedString(file.ToLowerInvariant()); // File name (all lower case!) (n)
                writer.Write(f.Size); // File size (8) unsigned long
                writer.Write(f.CRC); // File CRC (4) long long
            }
            writer.Flush();

            // Zero to signify the end of files
            fileNum = 0;
            writer.Write(fileNum);

            // The files
            foreach (ContentFile f in addon.Files)
            {
                writer.Write(f.Content);
                writer.Flush();
            }

            // CRC what we've written (to verify that the download isn't shitted) (4)
            writer.Seek(0, SeekOrigin.Begin);
            byte[] buffer_whole = new byte[writer.BaseStream.Length];
            writer.BaseStream.Read(buffer_whole, 0, (int)writer.BaseStream.Length);
            ulong addonCRC = System.Cryptography.CRC32.ComputeChecksum(buffer_whole);
            writer.Write(addonCRC);
            writer.Flush();

            writer.BaseStream.Seek(0, SeekOrigin.Begin);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Loads the specified addon from the local filesystem and encapsulates it within a realtime instance.
        /// </summary>
        /// <param name="filename">The path to the file on the local filesystem.</param>
        /// <returns>A RealtimeAddon instance.</returns>
        /// <exception cref="FileNotFoundException">Happens if the specified file does not exist.</exception>
        /// <exception cref="IOException">Thrown if there is a problem opening the specified file.</exception>
        /// <exception cref="ReaderException">Thrown if the addon reader and parser encounters an error.</exception>
        /// <exception cref="ArgumentException">Happens if a file with the same path is already added.</exception>
        /// <exception cref="WhitelistException">There is a file prohibited from storing by the global whitelist.</exception>
        /// <exception cref="IgnoredException">There is a file prohibited from storing by the addon's ignore list.</exception>
        public static RealtimeAddon Load(string filename)
        {
            if (!File.Exists(filename))
            {
                throw new FileNotFoundException("The specified file " + filename + " does not exist.");
            }

            FileStream fs;
            try
            {
                fs = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
            }
            catch (IOException)
            {
                throw;
            }

            Reader r;
            try
            {
                r = new Reader(fs);
            }
            catch (IOException)
            {
                throw;
            }
            catch (ReaderException)
            {
                throw;
            }

            Addon addon;
            try
            {
                addon = new Addon(r);
            }
            catch (ArgumentException)
            {
                throw;
            }
            catch (WhitelistException)
            {
                throw;
            }
            catch (IgnoredException)
            {
                throw;
            }

            RealtimeAddon realtime = new RealtimeAddon(addon, fs);
            realtime.AddonReader = r;
            return realtime;
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Creates a JSON string using the properties of the provided Addon.
        /// </summary>
        /// <param name="addon">The addon which metadata is to be used.</param>
        /// <returns>The compiled JSON string.</returns>
        /// <exception cref="AddonJSONException">Errors regarding creating the JSON.</exception>
        public static string BuildDescription(Addon addon)
        {
            DescriptionJSON tree = new DescriptionJSON();
            tree.Description = addon.Description;

            // Load the addon type
            if (addon.Type.ToLowerInvariant() == String.Empty || addon.Type.ToLowerInvariant() == null)
                throw new AddonJSONException("type is empty!");
            else
            {
                if (!SharpGMad.Tags.TypeExists(addon.Type.ToLowerInvariant()))
                    throw new AddonJSONException("type isn't a supported type!");
                else
                    tree.Type = addon.Type.ToLowerInvariant();
            }

            // Parse the tags
            tree.Tags = new List<string>();
            if (addon.Tags.Count > 2)
                throw new AddonJSONException("too many tags - specify 2 only!");
            else
            {
                foreach (string tag in addon.Tags)
                {
                    if (tag == String.Empty || tag == null) continue;

                    if (!SharpGMad.Tags.TagExists(tag.ToLowerInvariant()))
                        throw new AddonJSONException("tag isn't a supported word!");
                    else
                        tree.Tags.Add(tag.ToLowerInvariant());
                }
            }

            string strOutput;

            using (MemoryStream stream = new MemoryStream())
            {
                DataContractJsonSerializer jsonFormatter = new DataContractJsonSerializer(typeof(DescriptionJSON));

                try
                {
                    jsonFormatter.WriteObject(stream, tree);
                }
                catch (SerializationException ex)
                {
                    throw new AddonJSONException("Couldn't create json", ex);
                }

                stream.Seek(0, SeekOrigin.Begin);
                byte[] bytes = new byte[stream.Length];
                stream.Read(bytes, 0, (int)stream.Length);
                strOutput = Encoding.ASCII.GetString(bytes);
            }

            return strOutput;
        }
Ejemplo n.º 6
0
        private void btnExtract_Click(object sender, EventArgs e)
        {
            List<string> extractFailures = new List<string>();

            //
            // If an out path hasn't been provided, make our own
            //
            if (txtFolder.Text == String.Empty)
            {
                txtFolder.Text = Path.GetFileNameWithoutExtension(txtFile.Text);
            }

            //
            // Remove slash, add slash (enforces a slash)
            //
            txtFolder.Text = txtFolder.Text.TrimEnd('/');
            txtFolder.Text = txtFolder.Text + '/';
            Addon addon;
            try
            {
                FileStream fs = new FileStream(txtFile.Text, FileMode.Open, FileAccess.ReadWrite);
                addon = new Addon(new Reader(fs));
            }
            catch (Exception ex)
            {
                MessageBox.Show("Can't open the selected file.\nError happened: " + ex.Message,
                    "Failed to extract addon", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return;
            }

            foreach (ContentFile entry in addon.Files)
            {
                // Make sure folder exists
                try
                {
                    Directory.CreateDirectory(txtFolder.Text + Path.GetDirectoryName(entry.Path));
                }
                catch (Exception)
                {
                    // Noop
                }
                // Write the file to the disk
                try
                {
                    using (FileStream file = new FileStream(txtFolder.Text + entry.Path, FileMode.Create, FileAccess.Write))
                    {
                        file.Write(entry.Content, 0, (int)entry.Size);
                    }
                }
                catch (Exception)
                {
                    extractFailures.Add(entry.Path);
                }
            }

            if (extractFailures.Count == 0)
                MessageBox.Show("Successfully extracted the addon.", "Extract addon",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            else if (extractFailures.Count == 1)
                MessageBox.Show("Failed to extract " + extractFailures[0], "Extract addon",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
            else if (extractFailures.Count > 1)
            {
                DialogResult showFailedFiles = MessageBox.Show(extractFailures.Count + " files failed to extract." +
                    "\n\nShow a list of failures?", "Extract addon", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);

                if (showFailedFiles == DialogResult.Yes)
                {
                    string temppath = ContentFile.GenerateExternalPath(
                            new Random().Next() + "_failedExtracts") + ".txt";

                    try
                    {
                        File.WriteAllText(temppath,
                            "These files failed to extract:\r\n\r\n" +
                            String.Join("\r\n", extractFailures.ToArray()));
                    }
                    catch (Exception)
                    {
                        MessageBox.Show("Can't show the list, an error happened generating it.", "Extract addon",
                            MessageBoxButtons.OK, MessageBoxIcon.Stop);
                        return;
                    }

                    // The file will be opened by the user's default text file handler (Notepad?)
                    System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(temppath)
                    {
                        UseShellExecute = true,
                    });
                }
            }

            btnAbort_Click(sender, e); // Close the form
            return;
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Sets the type of an addon.
        /// </summary>
        /// <param name="addon">The addon to modify.</param>
        /// <param name="type">Optional. The new type the addon should have.</param>
        private static void SetType(Addon addon, string type = null)
        {
            if (type == String.Empty || type == null)
            {
                while (!Tags.TypeExists(type))
                {
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.WriteLine("Type? ");
                    Console.ResetColor();
                    Console.Write("Please choose ONE from the following: ");
                    Console.WriteLine(String.Join(" ", Tags.Type));
                    type = Console.ReadLine();

                    if (!Tags.TypeExists(type))
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("The specified type is not valid.");
                        Console.ResetColor();
                    }
                }
            }
            else
            {
                if (!Tags.TypeExists(type))
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("The specified type is not valid.");
                    Console.ResetColor();
                    return;
                }
            }

            addon.Type = type;
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Sets the tags of an addon.
        /// </summary>
        /// <param name="addon">The addon to modify.</param>
        /// <param name="tagsInput">Optional. The new tags the addon should have.</param>
        private static void SetTags(Addon addon, string[] tagsInput = null)
        {
            List<string> tags = new List<string>(2);
            if (tagsInput == null || tagsInput.Length == 0 || tagsInput[0] == String.Empty)
            {
                bool allTagsValid = false;
                while (!allTagsValid)
                {
                    tags.Clear();

                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.WriteLine("Tags? ");
                    Console.ResetColor();
                    Console.Write("Please choose ZERO, ONE or TWO from the following: ");
                    Console.WriteLine(String.Join(" ", Tags.Misc));

                    tagsInput = Console.ReadLine().Split(' ');

                    allTagsValid = true;
                    if (tagsInput[0] != String.Empty)
                    {
                        // More than zero (one or two) elements: add the first one.
                        if (tagsInput.Length > 0)
                        {
                            if (!Tags.TagExists(tagsInput[0]))
                            {
                                Console.ForegroundColor = ConsoleColor.Red;
                                Console.WriteLine("The specified tag \"" + tagsInput[0] + "\" is not valid.");
                                Console.ResetColor();
                                allTagsValid = false;
                                continue;
                            }
                            else
                                tags.Add(tagsInput[0]);
                        }

                        // More than one (two) elements: add the second one too.
                        if (tagsInput.Length > 1)
                        {
                            if (!Tags.TagExists(tagsInput[1]))
                            {
                                Console.ForegroundColor = ConsoleColor.Red;
                                Console.WriteLine("The specified tag \"" + tagsInput[1] + "\" is not valid.");
                                Console.ResetColor();
                                allTagsValid = false;
                                continue;
                            }
                            else
                                tags.Add(tagsInput[1]);
                        }

                        if (tagsInput.Length > 2)
                        {
                            Console.ForegroundColor = ConsoleColor.DarkYellow;
                            Console.WriteLine("More than two tags specified. Only the first two is saved.");
                            Console.ResetColor();
                        }
                    }
                }
            }
            else
            {
                if (tagsInput[0] != String.Empty)
                {
                    // More than zero (one or two) elements: add the first one.
                    if (tagsInput.Length > 0)
                    {
                        if (!Tags.TagExists(tagsInput[0]))
                        {
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine("The specified tag \"" + tagsInput[0] + "\" is not valid.");
                            Console.ResetColor();
                            return;
                        }
                        else
                            tags.Add(tagsInput[0]);
                    }

                    // More than one (two) elements: add the second one too.
                    if (tagsInput.Length > 1)
                    {
                        if (!Tags.TagExists(tagsInput[1]))
                        {
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine("The specified tag \"" + tagsInput[1] + "\" is not valid.");
                            Console.ResetColor();
                            return;
                        }
                        else
                            tags.Add(tagsInput[1]);
                    }

                    if (tagsInput.Length > 2)
                    {
                        Console.ForegroundColor = ConsoleColor.DarkYellow;
                        Console.WriteLine("More than two tags specified. Only the first two is saved.");
                        Console.ResetColor();
                    }
                }
            }

            addon.Tags = tags;
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Legacy GMAD operation to extract an addon file to a specified folder.
        /// </summary>
        /// <param name="strFile">The file path of the GMA to extract.</param>
        /// <param name="strOutPath">The folder where the addon is to be extracted to.</param>
        /// <returns>Integer error code: 0 if success, 1 if error.</returns>
        static int ExtractAddonFile(string strFile, string strOutPath = "")
        {
            Console.WriteLine("Opening " + strFile);

            //
            // If an out path hasn't been provided, make our own
            //
            if (strOutPath == String.Empty)
            {
                strOutPath = Path.GetFileNameWithoutExtension(strFile);
            }

            //
            // Remove slash, add slash (enforces a slash)
            //
            strOutPath = strOutPath.TrimEnd('/');
            strOutPath = strOutPath + '/';
            Addon addon;
            try
            {
                FileStream fs = new FileStream(strFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
                addon = new Addon(new Reader(fs));
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("There was a problem opening or parsing the file");
                Console.ResetColor();
                Console.WriteLine(ex.Message);
                return 1;
            }

            Console.WriteLine("Extracting Files:");
            foreach (ContentFile entry in addon.Files)
            {
                Console.WriteLine("\t" + entry.Path + " [" + ((int)entry.Size).HumanReadableSize() + "]");
                // Make sure folder exists
                try
                {
                    Directory.CreateDirectory(strOutPath + Path.GetDirectoryName(entry.Path));
                }
                catch (Exception)
                {
                    // Noop
                }
                // Write the file to the disk
                try
                {
                    using (FileStream file = new FileStream(strOutPath + entry.Path, FileMode.Create, FileAccess.Write))
                    {
                        file.Write(entry.Content, 0, (int)entry.Size);
                    }
                }
                catch (Exception)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("\t\tCouldn't extract!");
                    Console.ResetColor();
                }
            }
            Console.WriteLine("Done!");
            return 0;
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Legacy GMAD operation to create an addon file using contents of a specified folder.
        /// </summary>
        /// <param name="strFolder">The folder containing the raw content.</param>
        /// <param name="strOutfile">The path of the addon file to write.</param>
        /// <param name="warnInvalid">Whether there should be a warning for files failing to validate
        /// instead of a full exception halt.</param>
        /// <returns>Integer error code: 0 if success, 1 if error.</returns>
        static int CreateAddonFile(string strFolder, string strOutfile, bool warnInvalid, bool gmod12convert = false)
        {
            //
            // Make sure there's a slash on the end
            //
            strFolder = strFolder.TrimEnd('/');
            strFolder = strFolder + "/";
            //
            // Make sure OutFile ends in .gma
            //
            strOutfile = Path.GetFileNameWithoutExtension(strOutfile);
            strOutfile += ".gma";
            Console.WriteLine("Looking in folder \"" + strFolder + "\"");

            Addon addon = null;
            if (!gmod12convert)
            {
                //
                // Load the Addon Info file
                //
                Json addonInfo;
                try
                {
                    addonInfo = new Json(strFolder + "addon.json");
                }
                catch (AddonJSONException ex)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine(strFolder + "addon.json error: " + ex.Message);
                    Console.ResetColor();
                    return 1;
                }

                addon = new Addon(addonInfo);
            }
            else if (gmod12convert)
            {
                // Load the addon metadata from the old file structure: info.txt or addon.txt.
                string legacyInfoFile;
                if (File.Exists(strFolder + "\\info.txt"))
                    legacyInfoFile = "info.txt";
                else if (File.Exists(strFolder + "\\addon.txt"))
                    legacyInfoFile = "addon.txt";
                else
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("Failed to find legacy addon metadata file \"info.txt\" or \"addon.txt\"");
                    Console.ResetColor();
                    return 1;
                }

                string legacyInfo;
                try
                {
                    legacyInfo = File.ReadAllText(strFolder + Path.DirectorySeparatorChar + legacyInfoFile);
                }
                catch (Exception ex)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("Failed to read metadata.");
                    Console.ResetColor();
                    Console.WriteLine(ex.Message);
                    return 1;
                }

                addon = new Addon();

                // Parse the read data
                Regex regex = new System.Text.RegularExpressions.Regex("\"([A-Za-z_\r\n])*\"", RegexOptions.IgnoreCase);
                MatchCollection matches = regex.Matches(legacyInfo);

                foreach (Match keyMatch in matches)
                {
                    if (keyMatch.Value.ToLowerInvariant() == "\"name\"")
                        addon.Title = keyMatch.NextMatch().Value;
                    else if (keyMatch.Value.ToLowerInvariant() == "\"info\"")
                        addon.Description = keyMatch.NextMatch().Value;
                    else if (keyMatch.Value.ToLowerInvariant() == "\"author_name\"")
                        addon.Author = keyMatch.NextMatch().Value;
                    // Current GMAD writer only writes "Author Name", not real value
                }

                Console.WriteLine(addon.Title + " by " + addon.Author);
                Console.WriteLine("You need to set the title, and optionally, the tags for this addon!");

                SetType(addon);
                SetTags(addon);
            }

            //
            // Get a list of files in the specified folder
            //
            foreach (string f in Directory.GetFiles(strFolder, "*", SearchOption.AllDirectories))
            {
                string file = f;
                file = file.Replace(strFolder, String.Empty);
                file = file.Replace('\\', '/');

                Console.WriteLine("\t" + file);

                try
                {
                    addon.CheckRestrictions(file);
                    addon.AddFile(file, File.ReadAllBytes(f));
                }
                catch (IOException)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("Unable to read file " + file);
                    Console.ResetColor();
                    continue;
                }
                catch (IgnoredException)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("\t\t[Ignored]");
                    Console.ResetColor();
                    continue;
                }
                catch (WhitelistException)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("\t\t[Not allowed by whitelist]");
                    Console.ResetColor();
                    if (!warnInvalid)
                        return 1;
                }
            }
            //
            // Sort the list into alphabetical order, no real reason - we're just ODC
            //
            addon.Sort();

            //
            // Create an addon file in a buffer
            //
            //
            // Save the buffer to the provided name
            //
            FileStream fs;
            try
            {
                fs = new FileStream(strOutfile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
            }
            catch (Exception)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Couldn't save to file \"" + strOutfile + "\"");
                Console.ResetColor();
                return 1;
            }

            fs.SetLength(0);
            try
            {
                Writer.Create(addon, fs);
            }
            catch (Exception)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Failed to create the addon.");
                Console.ResetColor();
                return 1;
            }

            fs.Flush();

            //
            // Success!
            //
            Console.WriteLine("Successfully saved to \"" + strOutfile + "\" [" + ((int)fs.Length).HumanReadableSize() + "]");
            fs.Dispose();
            return 0;
        }
Ejemplo n.º 11
0
        private void btnCreate_Click(object sender, EventArgs e)
        {
            if (cmbTag1.SelectedItem == cmbTag2.SelectedItem &&
                !(cmbTag1.SelectedItem.ToString() == "(empty)" && cmbTag2.SelectedItem.ToString() == "(empty)"))
            {
                MessageBox.Show("You selected the same tag twice!", "Update metadata",
                    MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
                return;
            }

            List<CreateError> errors = new List<CreateError>();

            //
            // Make sure there's a slash on the end
            //
            txtFolder.Text = txtFolder.Text.TrimEnd('/');
            txtFolder.Text = txtFolder.Text + "/";
            //
            // Make sure OutFile ends in .gma
            //
            txtFile.Text = Path.GetFileNameWithoutExtension(txtFile.Text);
            txtFile.Text += ".gma";

            Addon addon = null;
            if (chkConvertNeeded.Checked == false)
            {
                //
                // Load the Addon Info file
                //
                Json addonInfo;
                try
                {
                    addonInfo = new Json(txtFolder.Text + "addon.json");
                }
                catch (AddonJSONException ex)
                {
                    MessageBox.Show(ex.Message,
                        "addon.json parse error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                    return;
                }

                addon = new Addon(addonInfo);
            }
            else if (chkConvertNeeded.Checked == true)
            {
                // Load the addon metadata from the old file structure: info.txt or addon.txt.
                string legacyInfoFile;
                if (File.Exists(txtFolder.Text + "\\info.txt"))
                    legacyInfoFile = "info.txt";
                else if (File.Exists(txtFolder.Text + "\\addon.txt"))
                    legacyInfoFile = "addon.txt";
                else
                {
                    MessageBox.Show("A legacy metadata file \"info.txt\" or \"addon.txt\" could not be found!",
                        "Failed to create the addon", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                    return;
                }

                string legacyInfo;
                try
                {
                    legacyInfo = File.ReadAllText(txtFolder.Text + Path.DirectorySeparatorChar + legacyInfoFile);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("There was an error reading the metadata.\n\n" + ex.Message,
                        "Failed to create the addon", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                    return;
                }

                addon = new Addon();

                // Parse the read data
                Regex regex = new System.Text.RegularExpressions.Regex("\"([A-Za-z_\r\n])*\"", RegexOptions.IgnoreCase);
                MatchCollection matches = regex.Matches(legacyInfo);

                foreach (Match keyMatch in matches)
                {
                    if (keyMatch.Value.ToLowerInvariant() == "\"name\"")
                        addon.Title = keyMatch.NextMatch().Value;
                    else if (keyMatch.Value.ToLowerInvariant() == "\"info\"")
                        addon.Description = keyMatch.NextMatch().Value;
                    else if (keyMatch.Value.ToLowerInvariant() == "\"author_name\"")
                        addon.Author = keyMatch.NextMatch().Value;
                    // Current GMAD writer only writes "Author Name", not real value
                }

                if (cmbType.SelectedItem != null && Tags.TypeExists(cmbType.SelectedItem.ToString()))
                    addon.Type = cmbType.SelectedItem.ToString();
                else
                {
                    // This should not happen in normal operation
                    // nontheless we check against it
                    MessageBox.Show("The selected type is invalid!", "Update metadata",
                        MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
                    return;
                }

                if (((cmbTag1.SelectedItem.ToString() != "(empty)") && !Tags.TagExists(cmbTag1.SelectedItem.ToString()))
                    || ((cmbTag2.SelectedItem.ToString() != "(empty)") && !Tags.TagExists(cmbTag2.SelectedItem.ToString())))
                {
                    // This should not happen in normal operation
                    // nontheless we check against it
                    MessageBox.Show("The selected tags are invalid!", "Update metadata",
                        MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
                    return;
                }

                addon.Tags = new List<string>(2);
                if (cmbTag1.SelectedItem.ToString() != "(empty)")
                    addon.Tags.Add(cmbTag1.SelectedItem.ToString());
                if (cmbTag2.SelectedItem.ToString() != "(empty)")
                    addon.Tags.Add(cmbTag2.SelectedItem.ToString());
            }

            //
            // Get a list of files in the specified folder
            //
            foreach (string f in Directory.GetFiles(txtFolder.Text, "*", SearchOption.AllDirectories))
            {
                string file = f;
                file = file.Replace(txtFolder.Text, String.Empty);
                file = file.Replace('\\', '/');

                try
                {
                    addon.CheckRestrictions(file);
                    addon.AddFile(file, File.ReadAllBytes(f));
                }
                catch (IOException)
                {
                    errors.Add(new CreateError() { Path = file, Type = CreateErrorType.FileRead });
                    continue;
                }
                catch (IgnoredException)
                {
                    errors.Add(new CreateError() { Path = file, Type = CreateErrorType.Ignored });
                    continue;
                }
                catch (WhitelistException)
                {
                    errors.Add(new CreateError() { Path = file, Type = CreateErrorType.WhitelistFailure });

                    if (!chkWarnInvalid.Checked)
                    {
                        MessageBox.Show("The following file is not allowed by the whitelist:\n" + file,
                            "Failed to create the addon", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                        return;
                    }
                }
            }

            //
            // Sort the list into alphabetical order, no real reason - we're just ODC
            //
            addon.Sort();

            //
            // Create an addon file in a buffer
            //
            //
            // Save the buffer to the provided name
            //
            FileStream gmaFS;
            try
            {
                gmaFS = new FileStream(txtFile.Text, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
                gmaFS.SetLength(0); // Truncate the file

                Writer.Create(addon, gmaFS);
            }
            catch (Exception be)
            {
                MessageBox.Show("An exception happened while compiling the addon.\n\n" + be.Message,
                    "Failed to create the addon", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return;
            }

            gmaFS.Flush();

            //
            // Success!
            //
            if (errors.Count == 0)
                MessageBox.Show("Successfully extracted the addon.", "Create addon",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            else if (errors.Count == 1)
                MessageBox.Show("Successfully created the addon.\nThe file " + errors[0] + " was not added.", "Create addon",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
            else if (errors.Count > 1)
            {
                DialogResult showFailedFiles = MessageBox.Show(errors.Count + " files failed to add." +
                    "\n\nShow a list of failures?", "Create addon", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);

                if (showFailedFiles == DialogResult.Yes)
                {
                    string temppath = ContentFile.GenerateExternalPath(
                            new Random().Next() + "_failedCreations") + ".txt";

                    string msgboxMessage = String.Empty;

                    foreach (CreateError err in errors)
                    {
                        msgboxMessage += err.Path + ", ";
                        switch (err.Type)
                        {
                            case CreateErrorType.FileRead:
                                msgboxMessage += "failed to read the file";
                                break;
                            case CreateErrorType.Ignored:
                                msgboxMessage += "the file is ignored by the addon's configuration";
                                break;
                            case CreateErrorType.WhitelistFailure:
                                msgboxMessage += "the file is not allowed by the global whitelist";
                                break;
                        }
                        msgboxMessage += "\r\n";
                    }
                    msgboxMessage = msgboxMessage.TrimEnd('\r', '\n');

                    try
                    {
                        File.WriteAllText(temppath, "These files failed to add:\r\n\r\n" + msgboxMessage);
                    }
                    catch (Exception)
                    {
                        MessageBox.Show("Can't show the list, an error happened generating it.", "Extract addon",
                            MessageBoxButtons.OK, MessageBoxIcon.Stop);
                        return;
                    }

                    // The file will be opened by the user's default text file handler (Notepad?)
                    System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(temppath)
                    {
                        UseShellExecute = true,
                    });
                }
            }

            gmaFS.Dispose();
            btnAbort_Click(sender, e); // Close the form
            return;
        }