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