Пример #1
0
        public IEnumerable<RuleEntry> ReadRules()
        {
            int ruleNumber = 0;
            foreach (var element in document.Root.Elements("rule"))
            {
                RuleEntry entry = new RuleEntry();

                var nameElements = element.Elements("name").ToArray();
                entry.Name = nameElements.Length > 0 ? matchIllegalRuleNameCharactersRegex.Replace(nameElements[0].Value, "_") : ruleNumber.ToString();
                ruleNumber++;

                //Log.Fine("Reading rule " + entry.Name);

                if (element.Elements("description").Any())
                    entry.Description = element.Elements("description").First().Value;

                if (element.Elements("from").Any())
                    entry.From = new RuleEntry.RuleEntryFrom() { FormKind = element.Elements("from").First().Value.ToUpper() };

                var whereElements = element.Elements("where").Take(2).ToArray();
                if (whereElements.Length > 0)
                {
                    entry.Where = new RuleEntry.RuleEntryWhere() { Code = whereElements[0].Value };

                    if (whereElements.Length > 1)
                    {
                        Log.Warning("Extra where elements will be ignored");
                    }
                }

                var selectElements = element.Elements("select").Take(2).ToArray();
                if (selectElements.Length > 0)
                {
                    entry.Select = new RuleEntry.RuleEntrySelect() { Code = selectElements[0].Value };

                    if (selectElements.Length > 1)
                    {
                        Log.Warning("Extra select elements will be ignored");
                    }
                }

                var updateElements = element.Elements("update").Take(2).ToArray();
                if (updateElements.Length > 0)
                {
                    entry.Update = new RuleEntry.RuleEntryUpdate() { Code = updateElements[0].Value };

                    if (updateElements.Length > 1)
                    {
                        Log.Warning("Extra update elements will be ignored");
                    }
                }

                var insertElements = element.Elements("insert");
                if (insertElements.Any())
                {
                    List<RuleEntry.RuleEntryInsert> inserts = new List<RuleEntry.RuleEntryInsert>();

                    foreach (var insertElement in insertElements)
                    {
                        var intoAttribute = insertElement.Attribute("into");
                        if (intoAttribute == null)
                            throw new InvalidDataException("Insert is missing mandatory attribute: into");

                        string insertFormKind = intoAttribute.Value;

                        var copy = false;
                        var copyAttribute = insertElement.Attribute("copy");
                        if (copyAttribute != null)
                        {
                            if (!bool.TryParse(copyAttribute.Value.ToLower(), out copy))
                            {
                                Log.Warning("Attribute 'copy' present on insert but the value '{0}' could not be parsed. Expected 'true' or 'false'.", copyAttribute.Value);
                            }

                            // Warn when copy attribute will be ignored - non-query and incompatible inserts
                            if (entry.From == null || !entry.From.FormKind.Equals(insertFormKind))
                            {
                                Log.Warning("Attribute 'copy' present on a non-query incompatible insert will be ignored.");
                            }
                        }

                        uint insertedFormId = 0;
                        var asAttribute = insertElement.Attribute("as");
                        if (asAttribute != null)
                        {
                            if (!uint.TryParse(asAttribute.Value.ToLower(), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out insertedFormId))
                            {
                                Log.Warning("Attribute 'as' present on insert but the value '{0}' could not be parsed.", asAttribute.Value);
                            }
                        }

                        inserts.Add(new RuleEntry.RuleEntryInsert() { InsertedFormId = insertedFormId, InsertedFormKind = insertFormKind, Copy = copy, Code = insertElement.Value });
                    }

                    entry.Inserts = inserts;
                }

                yield return entry;
            }
        }
Пример #2
0
        public void Add(RuleEntry entry, RuleMetadata metadata, bool debug)
        {
            string className = "Rule_" + ruleClassCounter++;

            var unit = new RuleCompilationUnit()
            {
                ClassName = className,
                ClassFullName = string.Format("{0}.{1}", NamespaceName, className),
                IsDebugModeEnabled = debug,
                Rule = new CompiledRule(engine, metadata)
                {
                    From = entry.From == null ? FormKind.Any : (FormKind)entry.From.FormKind
                },
            };

            string comment = string.Format("//\n// Source file for rule {0}\\{1}@{2} by {3}\n//\n", metadata.PluginFileName, metadata.RuleFileName, entry.Name, Program.GetProgramFullVersionInfo());
            CodeBuilder builder = new CodeBuilder(NamespaceName, className, comment);

            builder.Usings.Add("Patcher.Rules.Compiled.Helpers");
            builder.Usings.Add("Patcher.Rules.Compiled.Helpers." + engine.Context.GameTitle);
            builder.Usings.Add("Patcher.Rules.Compiled.Extensions");
            builder.Usings.Add("Patcher.Rules.Compiled.Extensions." + engine.Context.GameTitle);
            builder.Usings.Add("Patcher.Rules.Compiled.Constants");
            builder.Usings.Add("Patcher.Rules.Compiled.Constants." + engine.Context.GameTitle);

            Type sourceProxyType = entry.From != null ? engine.ProxyProvider.GetInterface((FormKind)entry.From.FormKind) : typeof(object);

            if (entry.Where != null)
            {
                string code = PreprocessCode(entry.Where.Code);
                if (StripComments(code).Length > 0)
                {
                    // TODO: optimize HasTag() with index similarly

                    var match = Regex.Match(code, @"^\s*Source\.EditorId\s*==\s*""([^""]*)""\s*$");
                    if (match.Success)
                    {
                        unit.Rule.WhereEditorId = match.Groups[1].Value;

                        Log.Fine("Where criteria EditorID == '" + match.Groups[1].Value + "' will use an index.");
                    }
                    else
                    {
                        builder.BeginMethod("Where", sourceProxyType);
                        builder.WriteCodeAsReturn(code);
                        builder.EndMethod();

                        unit.Rule.Where = new WhereMethod();
                    }
                }
            }

            if (entry.Select != null)
            {
                string code = PreprocessCode(entry.Select.Code);
                if (StripComments(code).Length > 0)
                {
                    builder.BeginMethod("Select", sourceProxyType);
                    builder.WriteCode(code);
                    builder.ReturnTrue();
                    builder.EndMethod();

                    unit.Rule.Select = new SelectMethod();
                }
            }

            if (entry.Update != null)
            {
                string code = PreprocessCode(entry.Update.Code);
                if (StripComments(code).Length > 0)
                {
                    builder.BeginMethod("Update", sourceProxyType, sourceProxyType);
                    builder.WriteCode(code);
                    builder.ReturnTrue();
                    builder.EndMethod();

                    unit.Rule.Update = new UpdateMethod();
                }
            }

            if (entry.Inserts != null)
            {
                List<InsertMethod> methods = new List<InsertMethod>();
                foreach (var insert in entry.Inserts)
                {
                    string code = PreprocessCode(insert.Code);
                    if (StripComments(code).Length > 0)
                    {
                        Type targetProxyType = engine.ProxyProvider.GetInterface((FormKind)insert.InsertedFormKind);
                        builder.BeginMethod("Insert_" + methods.Count, sourceProxyType, targetProxyType);
                        builder.WriteCode(code);
                        builder.ReturnTrue();
                        builder.EndMethod();
                    }

                    methods.Add(new InsertMethod()
                    {
                        InsertedFormId = insert.InsertedFormId,
                        InsertedFormKind = (FormKind)insert.InsertedFormKind,
                        Copy = insert.Copy
                    });
                }
                unit.Rule.Inserts = methods.ToArray();
            }

            // Declare static helper fields
            foreach (var helper in engine.HelperProvider.Helpers)
            {
                // Skip declaration of helpers that are not used in debug mode if debug mode is disabled
                if (helper.DebugModeOnly && !debug)
                    continue;

                builder.WriteCode(string.Format("static readonly {0} {1} = null;", helper.InterfaceType.FullName, helper.Name));
            }

            string finalCode = builder.ToString();
            if (!debug)
            {
                finalCode = StripDebug(finalCode);
            }

            // Write to memory stream
            using (var memoryStream = new MemoryStream(finalCode.Length))
            {
                var writer = new StreamWriter(memoryStream);
                writer.Write(finalCode);
                writer.Flush();
                memoryStream.Position = 0;

                // Find available file name for the new source file.
                int ruleNumber = 0;
                string sourceFilePath;
                do
                {
                    sourceFilePath = string.Format("{0}@{1:00}.cs", Path.Combine(cachePath, unit.Rule.Metadata.RuleFileName), ruleNumber++);
                } while (units.Any(u => u.Source == sourceFilePath));

                // Update source if necessary
                var sourceFile = engine.Context.DataFileProvider.GetDataFile(FileMode.Create, sourceFilePath);
                bool updated = sourceFile.CopyFrom(memoryStream, true);

                unit.Source = sourceFilePath;
                unit.Updated = updated;
            }

            units.Add(unit);
        }