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 = null) { string libraryPath = GetLibraryFolder(); System.IO.Directory.CreateDirectory(libraryPath); /** Remove old files **/ SetProgress(progress, 0, "RemovingOldFiles"); string[] oldFiles = System.IO.Directory.GetFiles(libraryPath); int removedFiles = 0; foreach (string file in oldFiles) { System.IO.FileAttributes attr = System.IO.File.GetAttributes(@file); if ((attr & System.IO.FileAttributes.Directory) == System.IO.FileAttributes.Directory) { System.IO.Directory.Delete(file, true); } else { System.IO.File.Delete(file); } removedFiles++; } Debug.Log("Modlib: " + this.Game.GameConfiguration.ID, "Removed " + removedFiles + " files and directories."); SetProgress(progress, 1f, "CreatingModToolkit"); string baseModLibPath = Configurations.Configuration.GetPath("Libraries") + System.IO.Path.DirectorySeparatorChar + "BaseModLib.dll"; if (!System.IO.File.Exists(baseModLibPath)) { Debug.Log("Modlib: " + this.Game.GameConfiguration.ID, "Couldn't find BaseModLib.dll.", Debug.Type.ERROR); SetProgress(progress, "Error.BaseModLibNotFound"); return; } ModuleDefinition baseModLib = ModuleDefinition.ReadModule(baseModLibPath); foreach (TypeDefinition type in baseModLib.Types) { List <string> ChangeMethods = new List <string>(); foreach (MethodDefinition method in type.Methods) { if (method.HasCustomAttributes) { if (method.CustomAttributes[0].AttributeType.Name == "AddModname") { ChangeMethods.Add(method.Name); } } } foreach (MethodDefinition method in type.Methods) { if (ChangeMethods.Contains(method.Name)) { method.IsPrivate = false; method.IsFamily = false; method.IsAssembly = false; method.IsFamilyAndAssembly = false; method.IsPublic = true; Debug.Log("Modlib: " + this.Game.GameConfiguration.ID, "Changed the accessibility of " + method.FullName + " in BaseModLib.dll"); } } } byte[] mscorlibPublicKeyToken = new byte[] { 0x7C, 0xEC, 0x85, 0xD7, 0xBE, 0xA7, 0x79, 0x8E }; byte[] systemXmlPublicKeyToken = new byte[] { 0x31, 0xBF, 0x38, 0x56, 0xAD, 0x36, 0x4E, 0x35 }; foreach (AssemblyNameReference assemblyReference in baseModLib.AssemblyReferences) { if ((assemblyReference.Name.StartsWith("System") || assemblyReference.Name.StartsWith("mscorlib"))) { assemblyReference.Version = new System.Version("2.0.5.0"); if (assemblyReference.Name == "System.Xml.Linq") { assemblyReference.PublicKeyToken = systemXmlPublicKeyToken; } else { assemblyReference.PublicKeyToken = mscorlibPublicKeyToken; } Debug.Log("ModLib: " + this.Game.GameConfiguration.ID, "Changed assembly reference token of " + assemblyReference.Name + " in BaseModLib.dll."); } } CreationTime = DateTime.Now; GameVersion = Game.BackupVersion.ID; ModAPIVersion = ModAPI.Version.Descriptor; XDocument metaXML = new XDocument(); XElement rootElement = new XElement("Meta"); rootElement.Add(new XElement("GameVersion", GameVersion)); rootElement.Add(new XElement("ModAPIVersion", ModAPIVersion)); rootElement.Add(new XElement("CreationTime", CreationTime.Ticks + "")); metaXML.Add(rootElement); EmbeddedResource metaResource = new EmbeddedResource("Meta", ManifestResourceAttributes.Public, System.Text.Encoding.UTF8.GetBytes(metaXML.ToString())); baseModLib.Resources.Add(metaResource); baseModLib.Write(libraryPath + System.IO.Path.DirectorySeparatorChar + "BaseModLib.dll"); Debug.Log("ModLib: " + this.Game.GameConfiguration.ID, "Successfully parsed BaseModLib.dll and copied in mod library."); SetProgress(progress, 5f, "ModifyingAssemblies"); Utils.CustomAssemblyResolver assemblyResolver = new Utils.CustomAssemblyResolver(); assemblyResolver.AddPath(ModAPI.Configurations.Configuration.GetPath("OriginalGameFiles") + System.IO.Path.DirectorySeparatorChar + Game.GameConfiguration.ID + System.IO.Path.DirectorySeparatorChar); assemblyResolver.AddPath(libraryPath); List <string> SearchFolders = new List <string>(); for (int i = 0; i < Game.GameConfiguration.IncludeAssemblies.Count; i++) { string assemblyPath = ModAPI.Configurations.Configuration.GetPath("OriginalGameFiles") + System.IO.Path.DirectorySeparatorChar + Game.GameConfiguration.ID + System.IO.Path.DirectorySeparatorChar + Game.ParsePath(Game.GameConfiguration.IncludeAssemblies[i]); string folder = System.IO.Path.GetDirectoryName(assemblyPath); if (!SearchFolders.Contains(folder)) { Debug.Log("ModLib: " + this.Game.GameConfiguration.ID, "Added folder \"" + folder + "\" to assembly resolver."); SearchFolders.Add(folder); } } for (int i = 0; i < Game.GameConfiguration.CopyAssemblies.Count; i++) { string assemblyPath = ModAPI.Configurations.Configuration.GetPath("OriginalGameFiles") + System.IO.Path.DirectorySeparatorChar + Game.GameConfiguration.ID + System.IO.Path.DirectorySeparatorChar + Game.ParsePath(Game.GameConfiguration.CopyAssemblies[i]); string folder = System.IO.Path.GetDirectoryName(assemblyPath); if (!SearchFolders.Contains(folder)) { Debug.Log("ModLib: " + this.Game.GameConfiguration.ID, "Added folder \"" + folder + "\" to assembly resolver."); SearchFolders.Add(folder); } } for (int i = 0; i < SearchFolders.Count; i++) { assemblyResolver.AddPath(SearchFolders[i]); } for (int i = 0; i < Game.GameConfiguration.IncludeAssemblies.Count; i++) { string assemblyPath = ModAPI.Configurations.Configuration.GetPath("OriginalGameFiles") + System.IO.Path.DirectorySeparatorChar + Game.GameConfiguration.ID + System.IO.Path.DirectorySeparatorChar + Game.ParsePath(Game.GameConfiguration.IncludeAssemblies[i]); if (System.IO.File.Exists(assemblyPath)) { try { ModuleDefinition module = ModuleDefinition.ReadModule(assemblyPath, new ReaderParameters() { AssemblyResolver = assemblyResolver }); foreach (TypeDefinition type in module.Types) { if (!CheckName(type.Namespace, Game.GameConfiguration.ExcludeNamespaces) && !CheckName(type.Name, Game.GameConfiguration.ExcludeTypes) && !CheckName(type.FullName, Game.GameConfiguration.NoFamily)) { if (type.IsAbstract && type.IsSealed) { type.IsAbstract = false; type.IsSealed = false; type.IsBeforeFieldInit = true; MethodDefinition constructor = new MethodDefinition(".ctor", MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Public | MethodAttributes.HideBySig, type.Module.TypeSystem.Void); type.Methods.Add(constructor); } foreach (MethodDefinition m in type.Methods) { if (!m.IsConstructor) { if (!m.IsGetter && !m.IsSetter && !m.IsStatic) { m.IsVirtual = true; } if (m.IsPrivate) { m.IsFamily = true; } Debug.Log("ModLib: " + this.Game.GameConfiguration.ID, "Changed private method " + m.FullName + " to protectded"); } } foreach (FieldDefinition f in type.Fields) { if (f.IsPrivate) { f.IsFamily = true; Debug.Log("ModLib: " + this.Game.GameConfiguration.ID, "Changed private field " + f.FullName + " to protectded"); } } } } string savePath = System.IO.Path.GetFullPath(libraryPath + System.IO.Path.DirectorySeparatorChar + Game.ParsePath(Game.GameConfiguration.IncludeAssemblies[i])); System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(savePath)); module.Write(savePath); Debug.Log("ModLib: " + this.Game.GameConfiguration.ID, "Saved modified \"" + module.Name + "\"."); } catch (Exception e) { Debug.Log("ModLib: " + this.Game.GameConfiguration.ID, "File couldnt be parsed: \"" + assemblyPath + "\". Exception: " + e.ToString(), Debug.Type.ERROR); SetProgress(progress, "Error.ModifyAssemblyException"); return; } Debug.Log("ModLib: " + this.Game.GameConfiguration.ID, "Successfully parsed file: \"" + assemblyPath + "\" and copied in mod library."); } else { SetProgress(progress, "Error.ModifyAssemblyFileNotFound"); Debug.Log("ModLib: " + this.Game.GameConfiguration.ID, "File not found: \"" + assemblyPath + "\".", Debug.Type.ERROR); return; } SetProgress(progress, 5f + ((float)i / (float)Game.GameConfiguration.IncludeAssemblies.Count) * 75f); } SetProgress(progress, 80f, "CopyingAssemblies"); for (int i = 0; i < Game.GameConfiguration.CopyAssemblies.Count; i++) { string copyFrom = Configurations.Configuration.GetPath("OriginalGameFiles") + System.IO.Path.DirectorySeparatorChar + Game.GameConfiguration.ID + System.IO.Path.DirectorySeparatorChar + Game.ParsePath(Game.GameConfiguration.CopyAssemblies[i]); string copyTo = System.IO.Path.GetFullPath(libraryPath + System.IO.Path.DirectorySeparatorChar + Game.ParsePath(Game.GameConfiguration.CopyAssemblies[i])); if (System.IO.File.Exists(copyFrom)) { System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(copyTo)); System.IO.File.Copy(copyFrom, copyTo, true); } else { SetProgress(progress, "Error.CopyAssemblyFileNotFound"); Debug.Log("ModLib: " + this.Game.GameConfiguration.ID, "File not found: \"" + copyFrom + "\".", Debug.Type.ERROR); return; } SetProgress(progress, 80f + ((float)i / (float)Game.GameConfiguration.CopyAssemblies.Count) * 20f); } Exists = true; SetProgress(progress, 100f, "Finish"); }