コード例 #1
0
        private static void ProcessMacroEntities(ExpansionSettings settings, Logger logger)
        {
            // Default to .map, if no extension was specified (unless there's an extensionless file that matches):
            var inputPath = settings.InputPath;

            if (!Path.HasExtension(settings.InputPath) && !File.Exists(settings.InputPath))
            {
                inputPath = Path.ChangeExtension(settings.InputPath, ".map");
            }

            if (settings.OutputPath == null)
            {
                settings.OutputPath = inputPath;
            }

            if (settings.Directory == null)
            {
                settings.Directory = Path.GetDirectoryName(settings.InputPath);
            }


            logger.Info($"Starting to expand macros in '{settings.InputPath}'.");

            var expandedMap = MacroExpander.ExpandMacros(inputPath, settings, logger);

            // TODO: Create a backup if the target file already exists! -- how many backups to make? -- make a setting for this behavior?
            using (var file = File.Create(settings.OutputPath))
            {
                logger.Info($"Finished macro expansion. Saving to '{settings.OutputPath}'.");
                MapFormat.Save(expandedMap, file);
            }
        }
コード例 #2
0
        static int Main(string[] args)
        {
            var settings          = new ExpansionSettings();
            var commandLineParser = GetCommandLineParser(settings);

            try
            {
                // NOTE: The -repl argument enables a different mode, so required arguments are no longer required,
                //       but the cmd-line parser doesn't support that, so here's a quick hacky workaround:
                if (args.Any(arg => arg == "-repl"))
                {
                    RunMScriptREPL();
                    return(0);
                }

                commandLineParser.Parse(args);

                using (var logger = new Logger(new StreamWriter(Console.OpenStandardOutput()), settings.LogLevel))
                {
                    if (settings.LogLevel != LogLevel.Off)
                    {
                        ShowToolInfo();
                        Console.WriteLine("----- BEGIN MESS -----");
                        Console.WriteLine($"Command line: {Environment.CommandLine}");
                        Console.WriteLine($"Arguments: {string.Join(" ", Environment.GetCommandLineArgs())}");
                    }

                    try
                    {
                        ProcessMacroEntities(settings, logger);
                        return(0);
                    }
                    catch (Exception ex)
                    {
                        logger.Error("Failed to process macro entities", ex);
                        // TODO: Show more error details here?
                        return(-1);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Failed to parse command line arguments: {ex.GetType().Name}: '{ex.Message}'.");
                ShowHelp(commandLineParser);
                return(-1);
            }
        }
コード例 #3
0
 /// <summary>
 /// Returns a command-line parser that will fill the given settings object when it parses command-line arguments.
 /// </summary>
 private static CommandLine GetCommandLineParser(ExpansionSettings settings)
 {
     return(new CommandLine()
            .Option(
                "-repl",
                () => { }, // This argument is taken care of in Main.
                $"Enables the interactive MScript interpreter mode. This starts a REPL (read-evaluate-print loop). All other arguments will be ignored.")
            .Option(
                "-dir",
                s => { settings.Directory = Path.GetFullPath(s); },
                $"The directory to use for resolving relative template map paths. If not specified, the input map file directory will be used.")
            .Option(
                "-fgd",
                s => { settings.GameDataPaths = s.Split(';').Select(Path.GetFullPath).ToArray(); },
                $"The .fgd file(s) that contains entity rewrite directives. Multiple paths must be separated by semicolons.")
            .Option(
                "-vars",
                s => { settings.Variables = ParseVariables(s); },
                $"These variables are used when evaluating expressions in the given map's properties and entities. Input format is \"name1 = expression; name2: expression; ...\".")
            .Option(
                "-maxrecursion",
                s => { settings.RecursionLimit = Math.Max(1, int.Parse(s)); },
                $"Limits recursion depth (templates that insert other templates). This protects against accidentally triggering infinite recursion. Default value is {settings.RecursionLimit}.")
            .Option(
                "-maxinstances",
                s => { settings.InstanceLimit = Math.Max(1, int.Parse(s)); },
                $"Limits the total number of instantiations. This protects against acidentally triggering an excessive amount of instantiation. Default value is {settings.InstanceLimit}.")
            .Option(
                "-log",
                s => { settings.LogLevel = (LogLevel)Enum.Parse(typeof(LogLevel), s, true); },
                $"Sets the log level. Valid options are: {string.Join(", ", Enum.GetValues(typeof(LogLevel)).OfType<LogLevel>().Select(level => level.ToString().ToLowerInvariant()))}. Default value is {settings.LogLevel.ToString().ToLowerInvariant()}.")
            .Argument(
                s => { settings.InputPath = Path.GetFullPath(s); },
                "Input map file.")
            .OptionalArgument(
                s => { settings.OutputPath = Path.GetFullPath(s); },
                "Output map file. If not specified, the input map file will be overwritten."));
 }
コード例 #4
0
        /// <summary>
        /// Fills the given settings object by reading settings from the 'mess.config' file.
        /// Does nothing if the config file does not exist, but will throw an exception if the config file is not structured correctly.
        /// </summary>
        public static void ReadSettings(string path, ExpansionSettings settings)
        {
            if (!File.Exists(path))
            {
                return;
            }

            var evaluationContext = Evaluation.DefaultContext();

            evaluationContext.Bind("EXE_DIR", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));

            string currentSegment = null;
            var    fgdPaths       = new List <string>();
            var    lines          = File.ReadLines(path, Encoding.UTF8).ToArray();

            for (int i = 0; i < lines.Length; i++)
            {
                try
                {
                    var line = lines[i].Trim();
                    if (line.StartsWith("//") || line == "")
                    {
                        continue;
                    }

                    var nameValueSeparatorIndex = line.IndexOf(':');
                    if (nameValueSeparatorIndex == -1)
                    {
                        switch (currentSegment)
                        {
                        case "rewrite-fgds":
                            settings.GameDataPaths.Add(Path.GetFullPath(Evaluation.EvaluateInterpolatedString(ReadString(line), evaluationContext)));
                            break;

                        case "variables":
                            // TODO: This approach does not support comments at the end of a line!
                            var assignments = Parser.ParseAssignments(Tokenizer.Tokenize(line + ";"));
                            foreach (var assignment in assignments)
                            {
                                settings.Variables[assignment.Identifier] = Evaluator.Evaluate(assignment.Value, evaluationContext);
                            }
                            break;

                        default:
                            Console.WriteLine($"Warning: config line #{i + 1} in '{path}' is formatted incorrectly and will be skipped.");
                            break;
                        }
                    }
                    else
                    {
                        currentSegment = null;

                        var name = line.Substring(0, nameValueSeparatorIndex).Trim();
                        var rest = RemoveTrailingComments(line.Substring(nameValueSeparatorIndex + 1));
                        switch (name)
                        {
                        case "template-directory":
                            settings.Directory = Path.GetFullPath(Evaluation.EvaluateInterpolatedString(ReadString(rest), evaluationContext));
                            break;

                        case "max-recursion":
                            settings.RecursionLimit = ReadInteger(rest);
                            break;

                        case "max-instances":
                            settings.InstanceLimit = ReadInteger(rest);
                            break;

                        case "log-level":
                            settings.LogLevel = (LogLevel)Enum.Parse(typeof(LogLevel), ReadString(rest), true);
                            break;

                        case "rewrite-fgds":
                        case "variables":
                            currentSegment = name;
                            break;

                        case "inverted-pitch-predicate":
                            settings.InvertedPitchPredicate = ReadString(rest);
                            break;

                        default:
                            Console.WriteLine($"Unknown setting on config line #{i + 1}: '{name}'.");
                            break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Failed to read config line #{i + 1} in '{path}': {ex.GetType().Name}: '{ex.Message}'.");
                    continue;
                }
            }


            string ReadString(string part) => part;

            int ReadInteger(string part) => int.Parse(part);

            string RemoveTrailingComments(string part)
            {
                var commentStartIndex = part.IndexOf("//");

                if (commentStartIndex != -1)
                {
                    part = part.Substring(0, commentStartIndex);
                }

                return(part.Trim());
            }
        }
コード例 #5
0
ファイル: Program.cs プロジェクト: pwitvoet/mess
        static int Main(string[] args)
        {
            var stopwatch         = Stopwatch.StartNew();
            var settings          = new ExpansionSettings();
            var commandLineParser = GetCommandLineParser(settings);

            try
            {
                if (args.Length == 0)
                {
                    Console.WriteLine("MESS requires at least one argument (the input map file).");
                    Console.WriteLine("");
                    ShowHelp(commandLineParser);
                    return(-1);
                }

                // NOTE: The -repl argument enables a different mode, so required arguments are no longer required,
                //       but the cmd-line parser doesn't support that, so here's a quick hacky workaround:
                if (args.Any(arg => arg == "-repl"))
                {
                    RunMScriptREPL();
                    return(0);
                }

                ConfigFile.ReadSettings(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "mess.config"), settings);
                commandLineParser.Parse(args);

                using (var logger = new MultiLogger(new ConsoleLogger(settings.LogLevel), new FileLogger(settings.InputPath + ".mess.log", settings.LogLevel)))
                {
                    logger.Minimal($"MESS v{Assembly.GetExecutingAssembly().GetName().Version}: Macro Entity Substitution System");
                    logger.Minimal("----- BEGIN MESS -----");
                    logger.Minimal($"Command line: {Environment.CommandLine}");
                    logger.Minimal($"Arguments: {string.Join(" ", Environment.GetCommandLineArgs())}");
                    logger.Minimal("");

                    try
                    {
                        ProcessMacroEntities(settings, logger);
                        return(0);
                    }
                    catch (Exception ex)
                    {
                        logger.Error("Failed to process macro entities", ex);
                        var innerException = ex.InnerException;
                        while (innerException != null)
                        {
                            logger.Error("Inner exception:", innerException);
                            innerException = innerException.InnerException;
                        }
                        // TODO: Show more error details here?
                        return(-1);
                    }
                    finally
                    {
                        // TODO: Log a small summary as well? Number of templates/instances/etc?
                        logger.Minimal("");
                        logger.Minimal($"Finished in {stopwatch.ElapsedMilliseconds / 1000f:0.##} seconds.");
                        logger.Minimal("");
                        logger.Minimal("----- END MESS -----");
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Failed to read config file or to parse command line arguments: {ex.GetType().Name}: '{ex.Message}'.");
                ShowHelp(commandLineParser);
                return(-1);
            }
        }