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