예제 #1
0
파일: Writer.cs 프로젝트: uwx/HSNXT.Greed
        /// <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)
        {
            var writer = new BinaryWriter(stream);

            writer.BaseStream.Seek(0, SeekOrigin.Begin);
            writer.BaseStream.SetLength(0);

            // Header (5)
            writer.Write(Addon.Magic.ToCharArray()); // Ident (4)
            writer.Write(Addon.Version);             // Version (1)
            // SteamID (8) [unused]
            writer.Write((ulong)0);
            // TimeStamp (8)
            writer.Write((ulong)
                         (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(1);

            // File list
            uint fileNum = 0;

            foreach (var f in addon.Files)
            {
                // Remove prefix / from filename
                var 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 (var f in addon.Files)
            {
                writer.Write(f.Content);
                writer.Flush();
            }

            // CRC what we've written (to verify that the download isn't s*****d) (4)
            writer.Seek(0, SeekOrigin.Begin);
            var buffer_whole = new byte[writer.BaseStream.Length];

            writer.BaseStream.Read(buffer_whole, 0, (int)writer.BaseStream.Length);
            ulong addonCRC = CRC32.ComputeChecksum(buffer_whole);

            writer.Write(addonCRC);
            writer.Flush();

            writer.BaseStream.Seek(0, SeekOrigin.Begin);
        }
예제 #2
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)
        {
            var tree = new DescriptionJson {
                Description = addon.Description
            };

            // Load the addon type
            if (addon.Type.ToLowerInvariant() == string.Empty || addon.Type.ToLowerInvariant() == null)
            {
                throw new AddonJsonException("type is empty!");
            }
            if (!Greed.Tags.TypeExists(addon.Type.ToLowerInvariant()))
            {
                throw new AddonJsonException("type isn't a supported type!");
            }
            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!");
            }
            foreach (var tag in addon.Tags)
            {
                if (string.IsNullOrEmpty(tag))
                {
                    continue;
                }

                if (!Greed.Tags.TagExists(tag.ToLowerInvariant()))
                {
                    throw new AddonJsonException("tag isn't a supported word!");
                }
                tree.Tags.Add(tag.ToLowerInvariant());
            }

            string strOutput;

            using (var stream = new MemoryStream())
            {
                var 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);
                var bytes = new byte[stream.Length];
                stream.Read(bytes, 0, (int)stream.Length);
                strOutput = Encoding.ASCII.GetString(bytes);
                strOutput = strOutput.Replace("\\u000d", "").Replace("\\u0009", "\\t").Replace("\\u000a", "\\n");
            }

            return(strOutput);
        }
예제 #3
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>
        /// <param name="gmod12">True if the extract should also create a legacy info.txt file.</param>
        /// <returns>Integer error code: 0 if success, 1 if error.</returns>
        private static int ExtractAddonFile(string strFile, string strOutPath = "", bool gmod12 = false)
        {
            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
            {
                var fs = new FileStream(strFile, FileMode.Open, FileAccess.Read, 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 (var 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 (var 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();
                }
            }

            if (gmod12) // Write a legacy info.txt schema
            {
                // The description has paramteres if the addon was created by a conversion.
                // Extract them out.

                var regex = new Regex("^# ([\\s\\S]*?): ([\\s\\S]*?)$",
                                      RegexOptions.IgnoreCase | RegexOptions.Multiline);
                var matches = regex.Matches(addon.Description);

                // info.txt/addon.txt files usually have these values not directly mapped into GMAs as well.
                var AuthorName  = string.Empty;
                var AuthorEmail = string.Empty;
                var AuthorURL   = string.Empty;
                var Version     = string.Empty;
                var Date        = string.Empty;

                foreach (Match keyMatch in matches)
                {
                    if (keyMatch.Groups.Count == 3)
                    {
                        // All match should have 2 groups matched (the 0th group is the whole match.)
                        switch (keyMatch.Groups[1].Value.ToLowerInvariant())
                        {
                        case "version":
                            Version = keyMatch.Groups[2].Value.TrimEnd('\n', '\r', '\t');
                            break;

                        case "date":
                            Date = keyMatch.Groups[2].Value.TrimEnd('\n', '\r', '\t');
                            break;

                        case "authorname":
                            AuthorName = keyMatch.Groups[2].Value.TrimEnd('\n', '\r', '\t');
                            break;

                        case "authoremail":
                            AuthorEmail = keyMatch.Groups[2].Value.TrimEnd('\n', '\r', '\t');
                            break;

                        case "authorurl":
                            AuthorURL = keyMatch.Groups[2].Value.TrimEnd('\n', '\r', '\t');
                            break;
                        }
                    }
                }

                var endConversionInfo = "## End conversion info";
                var description       = addon.Description;
                if (addon.Description.IndexOf(endConversionInfo) > 0)
                {
                    description = addon.Description.Substring(addon.Description.IndexOf(endConversionInfo) +
                                                              endConversionInfo.Length);
                    description = description.TrimStart('\r', '\n');
                }

                File.WriteAllText(strOutPath + "info.txt", "\"AddonInfo\"\n" +
                                  "{\n" +
                                  "\t" + "\"name\"" + "\t" + "\"" + addon.Title + "\"\n" +
                                  "\t" + "\"version\"" + "\t" + "\"" + Version + "\"\n" +
                                  "\t" + "\"up_date\"" + "\t" + "\"" + (string.IsNullOrWhiteSpace(Date) ?
                                                                        addon.Timestamp.ToString("ddd MM dd hh:mm:ss yyyy", CultureInfo.InvariantCulture) :
                                                                        DateTime.Now.ToString("ddd MM dd hh:mm:ss yyyy", CultureInfo.InvariantCulture) +
                                                                        " (+" + TimeZoneInfo.Local.BaseUtcOffset.ToString("hhmm") + ")") + "\"\n" +
                                  "\t" + "\"author_name\"" + "\t" + "\"" + AuthorName + "\"\n" + // addon.Author would be nice
                                  "\t" + "\"author_email\"" + "\t" + "\"" + AuthorEmail + "\"\n" +
                                  "\t" + "\"author_url\"" + "\t" + "\"" + AuthorURL + "\"\n" +
                                  "\t" + "\"info\"" + "\t" + "\"" + description + "\"\n" +
                                  "\t" + "\"override\"" + "\t" + "\"1\"\n" +
                                  "}");
            }

            Console.WriteLine("Done!");
            return(0);
        }
예제 #4
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)
        {
            var tags = new List <string>(2);

            if (tagsInput == null || tagsInput.Length == 0 || tagsInput[0] == string.Empty)
            {
                var 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;
                            }

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

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

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

                        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;
        }
예제 #5
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>
        private static int CreateAddonFile(string strFolder, string strOutfile, bool warnInvalid)
        {
            //
            // 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 (File.Exists(strFolder + Path.DirectorySeparatorChar + "addon.json"))
            {
                // Use addon.json for metadata if it exists

                if (File.Exists(strFolder + Path.DirectorySeparatorChar + "info.txt") ||
                    File.Exists(strFolder + Path.DirectorySeparatorChar + "addon.txt"))
                {
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.WriteLine("Both addon.json and legacy info.txt/addon.txt found in source folder.");
                    Console.WriteLine("addon.json takes priority");
                    Console.ResetColor();
                }

                //
                // 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 (File.Exists(strFolder + Path.DirectorySeparatorChar + "info.txt") ||
                     File.Exists(strFolder + Path.DirectorySeparatorChar + "addon.txt"))
            {
                // Load the addon metadata from the old file structure: info.txt/addon.txt

                var legacyInfo = string.Empty;
                try
                {
                    if (File.Exists(strFolder + Path.DirectorySeparatorChar + "info.txt"))
                    {
                        legacyInfo = File.ReadAllText(strFolder + Path.DirectorySeparatorChar + "info.txt");
                    }
                    else if (File.Exists(strFolder + Path.DirectorySeparatorChar + "addon.txt"))
                    {
                        legacyInfo = File.ReadAllText(strFolder + Path.DirectorySeparatorChar + "addon.txt");
                    }
                }
                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
                var regex = new Regex("\"([A-Za-z_\r\n]*)\"[\\s]*\"([\\s\\S]*?)\"",
                                      RegexOptions.IgnoreCase | RegexOptions.Multiline);
                var matches = regex.Matches(legacyInfo);

                // info.txt/addon.txt files usually have these values not directly mapped into GMAs as well.
                var AuthorName  = string.Empty;
                var AuthorEmail = string.Empty;
                var AuthorURL   = string.Empty;
                var Version     = string.Empty;
                var Date        = string.Empty;

                foreach (Match keyMatch in matches)
                {
                    if (keyMatch.Groups.Count == 3)
                    {
                        // All match should have 2 groups matched (the 0th group is the whole match.)
                        switch (keyMatch.Groups[1].Value.ToLowerInvariant())
                        {
                        case "name":
                            addon.Title = keyMatch.Groups[2].Value;
                            break;

                        case "version":
                            Version = keyMatch.Groups[2].Value;
                            break;

                        case "up_date":
                            Date = keyMatch.Groups[2].Value;
                            break;

                        case "author_name":
                            //addon.Author = keyMatch.Groups[2].Value;
                            // GMAD writer only writes "Author Name" right now...
                            AuthorName = keyMatch.Groups[2].Value;
                            break;

                        case "author_email":
                            AuthorEmail = keyMatch.Groups[2].Value;
                            break;

                        case "author_url":
                            AuthorURL = keyMatch.Groups[2].Value;
                            break;

                        case "info":
                            addon.Description = keyMatch.Groups[2].Value;
                            break;
                        }
                    }
                }

                // Prettify the loaded Description.
                var newDescription    = string.Empty;
                var hasNewDescription = !string.IsNullOrWhiteSpace(AuthorName) || !string.IsNullOrWhiteSpace(AuthorEmail) ||
                                        !string.IsNullOrWhiteSpace(AuthorURL) || !string.IsNullOrWhiteSpace(Version) ||
                                        !string.IsNullOrWhiteSpace(Date);

                if (hasNewDescription)
                {
                    newDescription = "## Converted by SharpGMad " + Shared.PrettyVersion + " at " +
                                     DateTime.Now.ToString("yyyy. MM. dd. hh:mm:ss") +
                                     " (+" + TimeZoneInfo.Local.BaseUtcOffset.ToString("hhmm") + ")";
                }

                if (!string.IsNullOrWhiteSpace(AuthorName))
                {
                    newDescription += "\n# AuthorName: " + AuthorName;
                }

                if (!string.IsNullOrWhiteSpace(AuthorEmail))
                {
                    newDescription += "\n# AuthorEmail: " + AuthorEmail;
                }

                if (!string.IsNullOrWhiteSpace(AuthorURL))
                {
                    newDescription += "\n# AuthorURL: " + AuthorURL;
                }

                if (!string.IsNullOrWhiteSpace(Version))
                {
                    newDescription += "\n# Version: " + Version;
                }

                if (!string.IsNullOrWhiteSpace(Date))
                {
                    newDescription += "\n# Date: " + Date;
                }

                if (hasNewDescription)
                {
                    // If anything was added to the prettifiction
                    newDescription   += "\n## End conversion info";
                    addon.Description = newDescription +
                                        (!string.IsNullOrWhiteSpace(addon.Description) ? Environment.NewLine + addon.Description : null);
                }

                Console.WriteLine("Addon: " + addon.Title);
                if (hasNewDescription)
                {
                    Console.WriteLine(newDescription);
                }
                Console.ForegroundColor = ConsoleColor.Magenta;
                Console.WriteLine("You need to set the title, and optionally, the tags for this addon!");
                Console.ResetColor();

                SetType(addon);
                SetTags(addon);
            }
            else
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Failed to find addon metadata file \"addon.json\", \"info.txt\" or \"addon.txt\"");
                Console.ResetColor();
                return(1);
            }

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

                if (file == "addon.json" || file == "info.txt")
                {
                    continue; // Don't read the metadata file
                }
                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();
                }
                catch (IgnoredException)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("\t\t[Ignored]");
                    Console.ResetColor();
                }
                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);
        }