diff --git a/Confuser.Core/ConfuserContext.cs b/Confuser.Core/ConfuserContext.cs
index 56b7c806a..d8265384e 100644
--- a/Confuser.Core/ConfuserContext.cs
+++ b/Confuser.Core/ConfuserContext.cs
@@ -74,6 +74,12 @@ public ServiceRegistry Registry {
/// The output directory.
public string OutputDirectory { get; internal set; }
+ ///
+ /// Gets the input symbol map (optional).
+ ///
+ /// The input symbol map.
+ public string InputSymbolMap { get; internal set; }
+
///
/// Gets the packer.
///
diff --git a/Confuser.Core/ConfuserEngine.cs b/Confuser.Core/ConfuserEngine.cs
index 211da3a56..3e0697cf1 100644
--- a/Confuser.Core/ConfuserEngine.cs
+++ b/Confuser.Core/ConfuserEngine.cs
@@ -91,6 +91,10 @@ static void RunInternal(ConfuserParameters parameters, CancellationToken token)
context.Resolver = asmResolver;
context.BaseDirectory = Path.Combine(Environment.CurrentDirectory, parameters.Project.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar);
context.OutputDirectory = Path.Combine(parameters.Project.BaseDirectory, parameters.Project.OutputDirectory.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar);
+
+ if (!string.IsNullOrWhiteSpace(parameters.Project.InputSymbolMap))
+ context.InputSymbolMap = Path.Combine(parameters.Project.BaseDirectory, parameters.Project.InputSymbolMap);
+
foreach (string probePath in parameters.Project.ProbePaths)
asmResolver.PostSearchPaths.Insert(0, Path.Combine(context.BaseDirectory, probePath));
diff --git a/Confuser.Core/Project/ConfuserPrj.xsd b/Confuser.Core/Project/ConfuserPrj.xsd
index bdad775b7..32372d38f 100644
--- a/Confuser.Core/Project/ConfuserPrj.xsd
+++ b/Confuser.Core/Project/ConfuserPrj.xsd
@@ -68,6 +68,7 @@
+
diff --git a/Confuser.Core/Project/ConfuserProject.cs b/Confuser.Core/Project/ConfuserProject.cs
index d6307fadd..b2cdad2f1 100644
--- a/Confuser.Core/Project/ConfuserProject.cs
+++ b/Confuser.Core/Project/ConfuserProject.cs
@@ -449,6 +449,12 @@ public ConfuserProject() {
/// The base directory.
public string BaseDirectory { get; set; }
+ ///
+ /// Gets or sets the input symbol map file.
+ ///
+ /// The file with input symbol map.
+ public string InputSymbolMap { get; set; }
+
///
/// Gets a list of protection rules that applies globally.
///
@@ -556,6 +562,11 @@ public void Load(XmlDocument doc) {
else
Seed = null;
+ if (docElem.Attributes["inputSymbolMap"] != null)
+ InputSymbolMap = docElem.Attributes["inputSymbolMap"].Value.NullIfEmpty();
+ else
+ InputSymbolMap = null;
+
if (docElem.Attributes["debug"] != null)
Debug = bool.Parse(docElem.Attributes["debug"].Value);
else
@@ -597,6 +608,7 @@ public void Load(XmlDocument doc) {
public ConfuserProject Clone() {
var ret = new ConfuserProject();
ret.Seed = Seed;
+ ret.InputSymbolMap = InputSymbolMap;
ret.Debug = Debug;
ret.OutputDirectory = OutputDirectory;
ret.BaseDirectory = BaseDirectory;
diff --git a/Confuser.Renamer/NameService.cs b/Confuser.Renamer/NameService.cs
index e35402373..16b62f815 100644
--- a/Confuser.Renamer/NameService.cs
+++ b/Confuser.Renamer/NameService.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Text;
using Confuser.Core;
@@ -58,6 +59,8 @@ internal class NameService : INameService {
public NameService(ConfuserContext context) {
this.context = context;
+ if (context.InputSymbolMap != null)
+ LoadInputSymbolMap(context.InputSymbolMap);
storage = new VTableStorage(context.Logger);
random = context.Registry.GetService().GetRandomGenerator(NameProtection._FullId);
nameSeed = random.NextBytes(20);
@@ -71,6 +74,23 @@ public NameService(ConfuserContext context) {
};
}
+ private void LoadInputSymbolMap(string inputSymbolMapPath)
+ {
+ var lineNum = 0;
+ foreach (var line in File.ReadLines(inputSymbolMapPath))
+ {
+ lineNum++;
+ if (string.IsNullOrWhiteSpace(line)) continue;
+ var fields = line.Split('\t');
+ if (fields.Length != 2)
+ throw new FileFormatException(string.Format("Cannot read input symbol map {0}:{1}", inputSymbolMapPath, lineNum));
+ var key = fields[0];
+ var value = fields[1];
+ nameMap2.Add(key, value);
+ nameMap1.Add(value, key);
+ }
+ }
+
public IList Renamers { get; private set; }
public VTableStorage GetVTables() {
@@ -227,7 +247,7 @@ public string ObfuscateName(string name, RenameMode mode) {
byte[] hash = Utils.Xor(Utils.SHA1(Encoding.UTF8.GetBytes(name)), nameSeed);
for (int i = 0; i < 100; i++) {
newName = ObfuscateNameInternal(hash, mode);
- if (!identifiers.Contains(MakeGenericName(newName, count)))
+ if (!identifiers.Contains(MakeGenericName(newName, count)) && !nameMap2.ContainsKey(newName))
break;
hash = Utils.SHA1(hash);
}
diff --git a/docs/ProjectFormat.md b/docs/ProjectFormat.md
index 0618cce5f..b8b29b432 100644
--- a/docs/ProjectFormat.md
+++ b/docs/ProjectFormat.md
@@ -27,6 +27,11 @@ The seed of the random generator in protection process.
Indicates whether the debug symbols (*.pdb) are generated.
Currently unused.
+`inputSymbolMap`
+Path to symbols map relative to `baseDir`.
+It if is set, rename protection will respect name mappings specified there and only generate additional ones as needed. Does not make sense for non-reversible rename modes.
+Symbol map file can be taken from previous obfuscation or created manually. Every non-empty line must contain exactly one tab character that separates two values: the first is obfuscated name and the second is original one. No duplications in either original or obfuscated names are allowed.
+
**Elements:**
`rule`: