public void Create(ProgressHandler progress) { string modFilePath = GetFolderPath() + System.IO.Path.DirectorySeparatorChar + "Mod" + System.IO.Path.DirectorySeparatorChar + this.ID + ".dll"; if (!System.IO.File.Exists(modFilePath)) { Debug.Log("Mod: " + ID, "Couldn't find the compiled mod dll at \"" + modFilePath + "\".", Debug.Type.ERROR); SetProgress(progress, "Error.FileNotFound"); return; } string modInfoPath = GetFolderPath() + System.IO.Path.DirectorySeparatorChar + "ModInfo.xml"; if (!System.IO.File.Exists(modInfoPath)) { Debug.Log("Mod: " + ID, "Couldn't find the mod configuration at \"" + modInfoPath + "\".", Debug.Type.ERROR); SetProgress(progress, "Error.FileNotFound"); return; } string libraryFolder = Game.ModLibrary.GetLibraryFolder(); string baseModLibPath = libraryFolder + System.IO.Path.DirectorySeparatorChar + "BaseModLib.dll"; if (!System.IO.File.Exists(baseModLibPath)) { Debug.Log("Mod: " + ID, "Couldn't find BaseModLib.dll at \"" + baseModLibPath + "\".", Debug.Type.ERROR); SetProgress(progress, "Error.FileNotFound"); return; } ModuleDefinition modModule; ModuleDefinition baseModLib; try { SetProgress(progress, 0f, "Preparing"); baseModLib = ModuleDefinition.ReadModule(baseModLibPath); SetProgress(progress, 5f); Utils.CustomAssemblyResolver assemblyResolver = new Utils.CustomAssemblyResolver(); assemblyResolver.AddPath(ModAPI.Configurations.Configuration.GetPath("ModLib") + System.IO.Path.DirectorySeparatorChar + Game.GameConfiguration.ID + System.IO.Path.DirectorySeparatorChar); modModule = ModuleDefinition.ReadModule(modFilePath, new ReaderParameters() { AssemblyResolver = assemblyResolver }); SetProgress(progress, 10f); } catch (Exception e) { Debug.Log("Mod: " + ID, "One of the assemblies is corrupted: " + e.ToString(), Debug.Type.ERROR); SetProgress(progress, "Error.CorruptAssembly"); return; } Mod mod = new Mod(this.Game, ""); mod.header = new Mod.Header(mod, System.IO.File.ReadAllText(modInfoPath)); mod.module = modModule; MemoryStream stream = new MemoryStream(); mod.module.Write(stream); stream.Position = 0; mod.originalModule = ModuleDefinition.ReadModule(stream); SetProgress(progress, 15f); try { Dictionary <MethodReference, MethodReference> baseModLibRemap = new Dictionary <MethodReference, MethodReference>(); foreach (TypeDefinition baseModLibType in baseModLib.Types) { foreach (MethodDefinition method in baseModLibType.Methods) { if (method.HasCustomAttributes && method.CustomAttributes[0].AttributeType.Name == "AddModname") { foreach (MethodDefinition method2 in baseModLibType.Methods) { if (!method2.HasCustomAttributes && method2.Name == method.Name && method2.Parameters.Count > method.Parameters.Count) { bool add = true; for (int i = 0; i < method.Parameters.Count; i++) { ParameterDefinition param = method.Parameters[i]; if (param.ParameterType.FullName != method2.Parameters[i].ParameterType.FullName) { add = false; } } if (add) { baseModLibRemap.Add(method, method2); } } } } } } SetProgress(progress, 20f, "FetchingTypes"); Dictionary <string, string> injectableClasses = new Dictionary <string, string>(); Dictionary <string, Dictionary <string, TypeDefinition> > assemblyTypes = new Dictionary <string, Dictionary <string, TypeDefinition> >(); for (int i = 0; i < Game.GameConfiguration.IncludeAssemblies.Count; i++) { string assembly = libraryFolder + System.IO.Path.DirectorySeparatorChar + Game.ParsePath(Game.GameConfiguration.IncludeAssemblies[i]); ModuleDefinition module = ModuleDefinition.ReadModule(assembly); string key = System.IO.Path.GetFileNameWithoutExtension(assembly); assemblyTypes.Add(key, new Dictionary <string, TypeDefinition>()); foreach (TypeDefinition type in module.Types) { if (!ModLib.CheckName(type.Namespace, Game.GameConfiguration.ExcludeNamespaces) && !ModLib.CheckName(type.FullName, Game.GameConfiguration.ExcludeTypes) && !ModLib.CheckName(type.FullName, Game.GameConfiguration.NoFamily)) { assemblyTypes[key].Add(type.FullName, type); if (!injectableClasses.ContainsKey(type.FullName)) { injectableClasses.Add(type.FullName, key); } } } SetProgress(progress, 20f + ((float)i / (float)Game.GameConfiguration.IncludeAssemblies.Count) * 30f); } SetProgress(progress, 50f, "ConvertingClasses"); Dictionary <string, TypeDefinition> newClasses = new Dictionary <string, TypeDefinition>(); for (int i = 0; i < modModule.Types.Count; i++) { TypeDefinition type = modModule.Types[i]; if (type.FullName == "<Module>") { continue; } foreach (MethodDefinition method in type.Methods) { if (method != null && method.Body != null) { for (int j = 0; j < method.Body.Instructions.Count; j++) { ILProcessor methodIL = method.Body.GetILProcessor(); Instruction instruction = method.Body.Instructions[j]; if (instruction.OpCode == OpCodes.Call && instruction.Operand != null) { foreach (KeyValuePair <MethodReference, MethodReference> map in baseModLibRemap) { if (((MethodReference)instruction.Operand).FullName == map.Key.FullName) { instruction.Operand = type.Module.Import(map.Value); Instruction newInstruction = methodIL.Create(OpCodes.Ldstr, ID); methodIL.InsertBefore(instruction, newInstruction); } } } } } } string assemblyName = ""; if (type.BaseType != null && injectableClasses.ContainsKey(type.BaseType.FullName)) { assemblyName = injectableClasses[type.BaseType.FullName]; } if (assemblyName == "" || !assemblyTypes[assemblyName].ContainsKey(type.BaseType.FullName)) { Mod.Header.AddClass addClass = new Mod.Header.AddClass(mod); addClass.Type = type; mod.header.AddAddClass(addClass); } else { foreach (Mono.Cecil.FieldDefinition field in type.Fields) { Mod.Header.AddField addField = new Mod.Header.AddField(mod); addField.Field = field; addField.AssemblyName = assemblyName; mod.header.AddAddField(addField); } foreach (MethodDefinition method in type.Methods) { if (method == null) { continue; } int priority = int.MaxValue; if (method.CustomAttributes != null) { foreach (CustomAttribute attribute in method.CustomAttributes) { if (attribute.AttributeType.Name == "Priority") { priority = (int)attribute.ConstructorArguments[0].Value; } } } bool inject = false; if (method.IsVirtual || method.IsStatic || method.IsConstructor) { foreach (MethodDefinition _m in assemblyTypes[assemblyName][type.BaseType.FullName].Methods) { if (_m.Name == method.Name) { if ((_m.IsStatic && method.IsStatic) || (_m.IsConstructor && method.IsConstructor)) { if (method.Parameters.Count == _m.Parameters.Count) { bool ok = true; for (int pi = 0; pi < _m.Parameters.Count; pi++) { ParameterDefinition param = _m.Parameters[pi]; if (param.ParameterType.FullName != method.Parameters[pi].ParameterType.FullName) { ok = false; break; } } if (ok) { inject = true; } } } else if (!_m.IsStatic && !method.IsStatic) { inject = true; } break; } } } if (inject) { Mod.Header.InjectInto injectInto = new Mod.Header.InjectInto(mod); injectInto.Method = method; injectInto.Priority = priority; injectInto.AssemblyName = assemblyName; mod.header.AddInjectInto(injectInto); } else { Mod.Header.AddMethod addMethod = new Mod.Header.AddMethod(mod); addMethod.Method = method; addMethod.AssemblyName = assemblyName; mod.header.AddAddMethod(addMethod); } } } SetProgress(progress, 50f + ((float)i / (float)modModule.Types.Count) * 30f); } foreach (AssemblyNameReference aref in modModule.AssemblyReferences) { if (aref.Name == "mscorlib" || aref.Name == "System") { aref.Version = new System.Version("2.0.0.0"); aref.PublicKeyToken = new byte[] { 0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89 }; } if (aref.Name == "System.Core") { aref.Version = new System.Version("3.5.0.0"); aref.PublicKeyToken = new byte[] { 0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89 }; } if (aref.Name == "System.Xml") { aref.Version = new System.Version("2.0.0.0"); aref.PublicKeyToken = new byte[] { 0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89 }; } } } catch (Exception e) { Debug.Log("Mod: " + ID, "An unexpected error occured while parsing the assembly: " + e.ToString(), Debug.Type.ERROR); SetProgress(progress, "Error.UnexpectedError"); return; } string modResourcesPath = GetFolderPath() + System.IO.Path.DirectorySeparatorChar + "Resources/"; if (!System.IO.Directory.Exists(modResourcesPath)) { System.IO.Directory.CreateDirectory(modResourcesPath); } if (System.IO.Directory.GetFiles(modResourcesPath).Length > 0 || System.IO.Directory.GetDirectories(modResourcesPath).Length > 0) { ZipFile newZipFile = new ZipFile(); newZipFile.AddDirectory(modResourcesPath); newZipFile.Comment = "Automaticlly created resources zip file."; mod.Resources = newZipFile; } try { SetProgress(progress, 90f, "SavingMod"); string modFolder = System.IO.Path.GetFullPath(Configuration.GetPath("mods") + System.IO.Path.DirectorySeparatorChar + Game.GameConfiguration.ID); if (!System.IO.Directory.Exists(modFolder)) { System.IO.Directory.CreateDirectory(modFolder); } mod.FileName = System.IO.Path.GetFullPath(modFolder + System.IO.Path.DirectorySeparatorChar + mod.UniqueID + ".mod"); if (mod.Save()) { string key = mod.ID + "-" + mod.header.GetVersion(); if (Mod.Mods.ContainsKey(key)) { if (Mod.Mods[key].FileName != mod.FileName) { Mod.Mods[key].Remove(); } //Mod.Mods[key] = mod; } /*else * { * Mod.Mods.Add(key, mod); * }*/ SetProgress(progress, 100f, "Finish"); } else { Debug.Log("Mod: " + ID, "Could not save the mod.", Debug.Type.ERROR); SetProgress(progress, "Error.Save"); } } catch (Exception e) { Debug.Log("Mod: " + ID, "An error occured while saving the mod: " + e.ToString(), Debug.Type.ERROR); SetProgress(progress, "Error.Save"); } }
public void Create(ProgressHandler progress) { var modFilePath = GetFolderPath() + Path.DirectorySeparatorChar + "Mod" + Path.DirectorySeparatorChar + Id + ".dll"; if (!File.Exists(modFilePath)) { Debug.Log("Mod: " + Id, "Couldn't find the compiled mod dll at \"" + modFilePath + "\".", Debug.Type.Error); SetProgress(progress, "Error.FileNotFound"); return; } var modInfoPath = GetFolderPath() + Path.DirectorySeparatorChar + "ModInfo.xml"; if (!File.Exists(modInfoPath)) { Debug.Log("Mod: " + Id, "Couldn't find the mod configuration at \"" + modInfoPath + "\".", Debug.Type.Error); SetProgress(progress, "Error.FileNotFound"); return; } var libraryFolder = Game.ModLibrary.GetLibraryFolder(); var baseModLibPath = libraryFolder + Path.DirectorySeparatorChar + "BaseModLib.dll"; if (!File.Exists(baseModLibPath)) { Debug.Log("Mod: " + Id, "Couldn't find BaseModLib.dll at \"" + baseModLibPath + "\".", Debug.Type.Error); SetProgress(progress, "Error.FileNotFound"); return; } ModuleDefinition modModule; ModuleDefinition baseModLib; try { SetProgress(progress, 0f, "Preparing"); baseModLib = ModuleDefinition.ReadModule(baseModLibPath); SetProgress(progress, 5f); var assemblyResolver = new CustomAssemblyResolver(); assemblyResolver.AddPath(Configuration.GetPath("ModLib") + Path.DirectorySeparatorChar + Game.GameConfiguration.Id + Path.DirectorySeparatorChar); modModule = ModuleDefinition.ReadModule(modFilePath, new ReaderParameters { AssemblyResolver = assemblyResolver }); SetProgress(progress, 10f); } catch (Exception e) { Debug.Log("Mod: " + Id, "One of the assemblies is corrupted: " + e, Debug.Type.Error); SetProgress(progress, "Error.CorruptAssembly"); return; } var mod = new Mod(Game, ""); mod.HeaderData = new Mod.Header(mod, File.ReadAllText(modInfoPath)); mod.Module = modModule; var stream = new MemoryStream(); mod.Module.Write(stream); stream.Position = 0; mod.OriginalModule = ModuleDefinition.ReadModule(stream); SetProgress(progress, 15f); try { var baseModLibRemap = new Dictionary <MethodReference, MethodReference>(); foreach (var baseModLibType in baseModLib.Types) { foreach (var method in baseModLibType.Methods) { if (method.HasCustomAttributes && method.CustomAttributes[0].AttributeType.Name == "AddModname") { foreach (var method2 in baseModLibType.Methods) { if (!method2.HasCustomAttributes && method2.Name == method.Name && method2.Parameters.Count > method.Parameters.Count) { var add = true; for (var i = 0; i < method.Parameters.Count; i++) { var param = method.Parameters[i]; if (param.ParameterType.FullName != method2.Parameters[i].ParameterType.FullName) { add = false; } } if (add) { baseModLibRemap.Add(method, method2); } } } } } } SetProgress(progress, 20f, "FetchingTypes"); var injectableClasses = new Dictionary <string, string>(); var assemblyTypes = new Dictionary <string, Dictionary <string, TypeDefinition> >(); for (var i = 0; i < Game.GameConfiguration.IncludeAssemblies.Count; i++) { var assembly = libraryFolder + Path.DirectorySeparatorChar + Game.ParsePath(Game.GameConfiguration.IncludeAssemblies[i]); var module = ModuleDefinition.ReadModule(assembly); var key = Path.GetFileNameWithoutExtension(assembly); assemblyTypes.Add(key, new Dictionary <string, TypeDefinition>()); foreach (var type in module.Types) { if (!ModLib.CheckName(type.Namespace, Game.GameConfiguration.ExcludeNamespaces) && !ModLib.CheckName(type.FullName, Game.GameConfiguration.ExcludeTypes) && !ModLib.CheckName(type.FullName, Game.GameConfiguration.NoFamily)) { assemblyTypes[key].Add(type.FullName, type); if (!injectableClasses.ContainsKey(type.FullName)) { injectableClasses.Add(type.FullName, key); } } } SetProgress(progress, 20f + (i / (float)Game.GameConfiguration.IncludeAssemblies.Count) * 30f); } SetProgress(progress, 50f, "ConvertingClasses"); var newClasses = new Dictionary <string, TypeDefinition>(); for (var i = 0; i < modModule.Types.Count; i++) { var type = modModule.Types[i]; if (type.FullName == "<Module>") { continue; } foreach (var method in type.Methods) { if (method?.Body != null) { for (var j = 0; j < method.Body.Instructions.Count; j++) { var methodIl = method.Body.GetILProcessor(); var instruction = method.Body.Instructions[j]; if (instruction.OpCode == OpCodes.Call && instruction.Operand != null) { foreach (var map in baseModLibRemap) { if (((MethodReference)instruction.Operand).FullName == map.Key.FullName) { instruction.Operand = type.Module.Import(map.Value); var newInstruction = methodIl.Create(OpCodes.Ldstr, Id); methodIl.InsertBefore(instruction, newInstruction); } } } } } } var assemblyName = ""; if (type.BaseType != null && injectableClasses.ContainsKey(type.BaseType.FullName)) { assemblyName = injectableClasses[type.BaseType.FullName]; } if (assemblyName == "" || !assemblyTypes[assemblyName].ContainsKey(type.BaseType.FullName)) { var addClass = new Mod.Header.AddClass(mod) { Type = type }; mod.HeaderData.AddAddClass(addClass); } else { foreach (var field in type.Fields) { var addField = new Mod.Header.AddField(mod) { Field = field, AssemblyName = assemblyName }; mod.HeaderData.AddAddField(addField); } foreach (var method in type.Methods) { if (method == null) { continue; } var priority = int.MaxValue; if (method.CustomAttributes != null) { foreach (var attribute in method.CustomAttributes) { if (attribute.AttributeType.Name == "Priority") { priority = (int)attribute.ConstructorArguments[0].Value; } } } var inject = false; if (method.IsVirtual || method.IsStatic || method.IsConstructor) { foreach (var m in assemblyTypes[assemblyName][type.BaseType.FullName].Methods.Where(o => o.Name == method.Name)) { // Only compare methods with same parameter count if (method.Parameters.Count == m.Parameters.Count) { // No need to compare parameterless methods if (method.Parameters.Count == 0) { inject = true; break; } // Comapare parameters if (!m.Parameters.Where((param, pi) => param.ParameterType.FullName != method.Parameters[pi].ParameterType.FullName).Any()) { inject = true; break; } } } } if (inject) { var injectInto = new Mod.Header.InjectInto(mod) { Method = method, Priority = priority, AssemblyName = assemblyName }; mod.HeaderData.AddInjectInto(injectInto); } else { var addMethod = new Mod.Header.AddMethod(mod) { Method = method, AssemblyName = assemblyName }; mod.HeaderData.AddAddMethod(addMethod); } } } SetProgress(progress, 50f + (i / (float)modModule.Types.Count) * 30f); } foreach (var aref in modModule.AssemblyReferences) { if (aref.Name == "mscorlib" || aref.Name == "System") { aref.Version = new System.Version("2.0.0.0"); aref.PublicKeyToken = new byte[] { 0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89 }; } if (aref.Name == "System.Core") { aref.Version = new System.Version("3.5.0.0"); aref.PublicKeyToken = new byte[] { 0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89 }; } if (aref.Name == "System.Xml") { aref.Version = new System.Version("2.0.0.0"); aref.PublicKeyToken = new byte[] { 0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89 }; } } } catch (Exception e) { Debug.Log("Mod: " + Id, "An unexpected error occured while parsing the assembly: " + e, Debug.Type.Error); SetProgress(progress, "Error.UnexpectedError"); return; } var modResourcesPath = GetFolderPath() + Path.DirectorySeparatorChar + "Resources/"; if (!Directory.Exists(modResourcesPath)) { Directory.CreateDirectory(modResourcesPath); } if (Directory.GetFiles(modResourcesPath).Length > 0 || Directory.GetDirectories(modResourcesPath).Length > 0) { var newZipFile = new ZipFile(); newZipFile.AddDirectory(modResourcesPath); newZipFile.Comment = "Automaticlly created resources zip file."; mod.Resources = newZipFile; } try { SetProgress(progress, 90f, "SavingMod"); var modFolder = Path.GetFullPath(Configuration.GetPath("mods") + Path.DirectorySeparatorChar + Game.GameConfiguration.Id); if (!Directory.Exists(modFolder)) { Directory.CreateDirectory(modFolder); } mod.FileName = Path.GetFullPath(modFolder + Path.DirectorySeparatorChar + mod.UniqueId + ".mod"); if (mod.Save()) { var key = mod.Id + "-" + mod.HeaderData.GetVersion(); if (Mod.Mods.ContainsKey(key)) { if (Mod.Mods[key].FileName != mod.FileName) { Mod.Mods[key].Remove(); } //Mod.Mods[key] = mod; } /*else * { * Mod.Mods.Add(key, mod); * }*/ SetProgress(progress, 100f, "Finish"); } else { Debug.Log("Mod: " + Id, "Could not save the mod.", Debug.Type.Error); SetProgress(progress, "Error.Save"); } } catch (Exception e) { Debug.Log("Mod: " + Id, "An error occured while saving the mod: " + e, Debug.Type.Error); SetProgress(progress, "Error.Save"); } }