public void PatchGame() { GameManager.SaveAllMetadata(); GameManager.appID = appidBox.Text; DebugLogger.Log("Run Game"); GameManager.PatchGame(); if (int.TryParse(appidBox.Text, out int id) == false) { MessageBox.Show(this, "Mods applied! No/Incorrect APPID was entered, so the game can't be automatically launched. You can now launch the game yourself, as you normally would", "Mod Results", MessageBoxType.Information); } else { DialogResult result = MessageBox.Show(this, "Mods applied! Would you like to launch the game?", "Mod results", MessageBoxButtons.YesNo); if (result == DialogResult.Yes) { foreach (Control c in Children) { c.Enabled = false; } GameManager.StartGame(); foreach (Control c in Children) { c.Enabled = true; } } } }
public static void PatchGame() { try { PatchManager.PatchGame(); } catch (System.Exception e) { DebugLogger.Log(e); } }
public static void LoadModMetas() { modMetas.Clear(); string gameDirectory = Directory.GetParent(exePath).FullName; string managedPath = Path.Combine(gameDirectory, Path.GetFileNameWithoutExtension(exePath) + "_Data", "Managed"); //Load mods string modFolder = Path.Combine(gameDirectory, "Mods"); string dependenciesFolder = Path.Combine(gameDirectory, "ModDependencies"); string hashesFolder = Path.Combine(gameDirectory, "PartialityHashes"); string oldMetaChecker = Path.Combine(gameDirectory, "PartialityHashes", "newMeta"); if (!Directory.Exists(modFolder)) { File.Create(Path.Combine(Directory.CreateDirectory(modFolder).FullName, "mods go here")).Dispose(); } if (!Directory.Exists(dependenciesFolder)) { File.Create(Path.Combine(Directory.CreateDirectory(dependenciesFolder).FullName, "mod dependencies go here")).Dispose(); } if (!Directory.Exists(hashesFolder)) { File.Create(Path.Combine(Directory.CreateDirectory(hashesFolder).FullName, "IGNORE THIS FOLDER! It's just data for Partiality!")).Dispose(); } if (!File.Exists(oldMetaChecker)) { ClearMetas(true); File.Create(oldMetaChecker); DebugLogger.Log("Clearing old metadata files!"); } string[] files = Directory.GetFiles(modFolder); foreach (string s in files) { DebugLogger.Log("Checking if file is mod " + s); if (Path.GetExtension(s) == ".dll") { try { ModMetadata md = ModMetadata.GetForMod(s); if (md.isStandalone) { modMetas.Insert(0, md); } else { modMetas.Add(md); } } catch (Exception e) { DebugLogger.Log(e); } } } DebugLogger.Log("Loaded metadata for " + modMetas.Count + " mods"); }
public static bool SameHash(byte[] a, byte[] b) { if (a.Length != b.Length) { return(false); } for (int i = 0; i < a.Length; i++) { if (a[i] != b[i]) { DebugLogger.Log("Failed hash check at index " + i + " bytes where " + a[i] + " and " + b[i]); return(false); } } return(true); }
public static ModMetadata GetForMod(string file) { string metaFilePath = Path.Combine(Directory.GetParent(file).FullName, Path.GetFileNameWithoutExtension(file) + ".modMeta"); string hashFilePath = Path.Combine(Directory.GetParent(file).FullName, Path.GetFileNameWithoutExtension(file) + ".modHash"); DebugLogger.Log("Checking for modmeta at " + metaFilePath); if (!File.Exists(metaFilePath)) { GenerateHashForMod(file, hashFilePath); return(GenerateForMod(file)); } else { return(ReadFromFile(metaFilePath, hashFilePath, file)); } }
public static ModMetadata GenerateForMod(string modFile) { ModMetadata meta = new ModMetadata(); meta.isEnabled = false; meta.modPath = modFile; ModuleDefinition modDef = ModuleDefinition.ReadModule(modFile); IEnumerable <TypeDefinition> getTypes = modDef.GetTypes(); Type patchType = typeof(MonoMod.MonoModPatch); Type constructorType = typeof(MonoMod.MonoModConstructor); Type originalNameType = typeof(MonoMod.MonoModOriginalName); Type ignoreType = typeof(MonoMod.MonoModIgnore); string origPrefix = "orig_"; DebugLogger.Log(string.Empty); DebugLogger.Log("Generating metadata for " + Path.GetFileName(modFile)); //Foreach type in the mod dll foreach (TypeDefinition checkType in getTypes) { //If the type has a custom attribute if (checkType.HasCustomAttributes) { HashSet <CustomAttribute> attributes = new HashSet <CustomAttribute>(checkType.CustomAttributes); //Foreach custom attribute foreach (CustomAttribute ct in attributes) { try { //If the attribute is [MonoModPatch] if (ct.AttributeType.Name == patchType.Name) { string originalClassName = ct.ConstructorArguments[0].Value as string; HashSet <string> changedFunctions = new HashSet <string>(); DebugLogger.Log(string.Empty); DebugLogger.Log("Adding " + originalClassName); List <MethodDefinition> methods = new List <MethodDefinition>(checkType.Methods); //Foreach method in this type foreach (MethodDefinition methodDef in methods) { if (methodDef.IsConstructor) { continue; } if (methodDef.Name.StartsWith(origPrefix)) { string methodEntry = originalClassName + "->" + methodDef.Name.Remove(0, origPrefix.Length); DebugLogger.Log("Adding method " + methodEntry); changedFunctions.Add(methodEntry); } else if (methodDef.HasCustomAttributes) { HashSet <CustomAttribute> methodAttributes = new HashSet <CustomAttribute>(methodDef.CustomAttributes); //Foreach custom attribute on the method foreach (CustomAttribute c in methodAttributes) { if (c.AttributeType.Name == ignoreType.Name) { string methodEntry = originalClassName + "->" + methodDef.Name; DebugLogger.Log("Ignoring Method " + methodEntry); break; } else if (c.AttributeType.Name == constructorType.Name) { string methodEntry = originalClassName + "->" + "ctor_" + originalClassName; DebugLogger.Log("Adding Constructor " + methodEntry); changedFunctions.Add(methodEntry); break; } else if (c.AttributeType.Name == originalNameType.Name) { string methodEntry = originalClassName + "->" + methodDef.Name; DebugLogger.Log("Adding Original Method " + methodEntry); changedFunctions.Add(methodEntry); break; } } } } meta.modifiedClasses.Add(originalClassName, changedFunctions); //We're done looking through attributes of this class, break break; } } catch (System.Exception e) { DebugLogger.Log(e); } } } } meta.isPatch = meta.modifiedClasses.Count > 0; if (meta.isPatch) { meta.isDirty = true; } modDef.Dispose(); return(meta); }
public static void PatchGame() { string executablePath = Assembly.GetEntryAssembly().Location; string executableDirectory = Directory.GetParent(executablePath).FullName; string monoModPath = Path.Combine(executableDirectory, "MonoMod.exe"); string hookGenPath = Path.Combine(executableDirectory, "MonoMod.RuntimeDetour.HookGen.exe"); string runtimeDetourDLL = "MonoMod.RuntimeDetour.dll"; string mmUtilsDLL = "MonoMod.Utils.dll"; string jsonDLL = "YamlDotNet.dll"; string gameDirectory = Directory.GetParent(GameManager.exePath).FullName; string hashesFolder = Path.Combine(gameDirectory, "DustDevilHashes"); string modDependencies = Path.Combine(gameDirectory, "ModDependencies"); string dataDirectory = Path.Combine(gameDirectory, Path.GetFileNameWithoutExtension(GameManager.exePath) + "_Data"); string managedFolder = Path.Combine(dataDirectory, "Managed"); string codeDll = Path.Combine(managedFolder, "Assembly-CSharp.dll"); string hookGenDLL = Path.Combine(managedFolder, "HOOKS-Assembly-CSharp.dll"); string engineDll = Path.Combine(managedFolder, "UnityEngine.dll"); string coreModuleDLL = Path.Combine(managedFolder, "UnityEngine.CoreModule.dll"); string backupFolder = managedFolder + "_backup"; Process currentProcess = Process.GetCurrentProcess(); Process monomodProcess = new Process(); monomodProcess.StartInfo.FileName = monoModPath; //monomodProcess.StartInfo.CreateNoWindow = true; monomodProcess.StartInfo.UseShellExecute = false; monomodProcess.StartInfo.RedirectStandardOutput = true; //Create backup if there isn't one if (!Directory.Exists(backupFolder)) { Directory.CreateDirectory(backupFolder); CopyFilesRecursively(managedFolder, backupFolder); } //Install the default patch for Partiality { string engineDLLName = "UnityEngine.dll"; if (File.Exists(coreModuleDLL)) { engineDLLName = "UnityEngine.CoreModule.dll"; engineDll = coreModuleDLL; } string moddedDLL = Path.Combine(Path.GetDirectoryName(engineDll), "patched" + engineDLLName); string defaultPatchLocation = Path.Combine(executableDirectory, "PartialityPatch.dll"); string partialityModLocation = Path.Combine(Path.GetDirectoryName(engineDll), "Partiality.dll"); bool shouldPatch = false; if (!File.Exists(Path.Combine(hashesFolder, "ENGINEHASH.hash"))) { shouldPatch = true; } else { shouldPatch = !ModMetadata.CompareHashes(defaultPatchLocation, Path.Combine(hashesFolder, "ENGINEHASH.hash")); } //Delete mod if it exists if (File.Exists(partialityModLocation)) { File.Delete(partialityModLocation); } //Copy mod to folder with assembly-chsharp.dll File.Copy(Path.Combine(executableDirectory, "Partiality.dll"), partialityModLocation); if (shouldPatch) { //Restore backup File.Delete(engineDll); File.Copy(Path.Combine(backupFolder, engineDLLName), engineDll); //Set monomod arguments to "[UnityEngine.dll] [PartialityPatch.dll] [patched_UnityEngine.dll]" monomodProcess.StartInfo.Arguments = ('"' + engineDll + '"') + " " + ('"' + defaultPatchLocation + '"') + " " + ('"' + moddedDLL + '"'); monomodProcess.Start(); string mmoutput = monomodProcess.StandardOutput.ReadToEnd(); Console.WriteLine(mmoutput); monomodProcess.WaitForExit(); int exitCode = monomodProcess.ExitCode; Console.WriteLine("MMEC:" + exitCode); Console.WriteLine(mmoutput); //Replace file if (File.Exists(moddedDLL)) { //Move modded .dll over original .dll File.Delete(engineDll); File.Copy(moddedDLL, engineDll); File.Delete(moddedDLL); } byte[] newHash = ChecksumHasher.ComputeHash(File.ReadAllBytes(defaultPatchLocation)); File.WriteAllBytes(Path.Combine(hashesFolder, "ENGINEHASH.hash"), newHash); } } //Install custom patches { string[] files = Directory.GetFiles(modDependencies); //Copy mod dependencies foreach (string dependency in files) { string fileName = Path.GetFileName(dependency); //Delete the dependency if it already exists if (File.Exists(Path.Combine(managedFolder, fileName))) { File.Delete(Path.Combine(managedFolder, fileName)); } //Copy the file File.Copy(dependency, Path.Combine(managedFolder, fileName)); } bool shouldPatch = false; string moddedDLL = Path.Combine(Path.GetDirectoryName(engineDll), "patched_Assembly-CSharp.dll"); string epListLocation = Path.Combine(hashesFolder, "ENABLEDPATCHES.enp"); //Check if we have the same enabled/disabled mods as last time, if we do, then { string totalEnabledPatches = "Partiality+"; foreach (ModMetadata md in GameManager.modMetas) { if ((md.isStandalone || md.isPatch) && md.isEnabled) { totalEnabledPatches += Path.GetFileNameWithoutExtension(md.modPath) + "+"; } } DebugLogger.Log(totalEnabledPatches); if (File.Exists(epListLocation)) { string getList = File.ReadAllText(epListLocation); shouldPatch = getList != totalEnabledPatches; if (shouldPatch) { File.WriteAllText(epListLocation, totalEnabledPatches); } } else { shouldPatch = true; File.WriteAllText(epListLocation, totalEnabledPatches); } } //If all the same mods are enabled, check if any mods are dirty. If they are, we gotta re-patch. if (!shouldPatch) { foreach (ModMetadata md in GameManager.modMetas) { if (md.isDirty) { shouldPatch = true; break; } } } if (shouldPatch) { DebugLogger.Log("Patching Assembly-CSharp"); string backupDll = Path.Combine(backupFolder, "Assembly-CSharp.dll"); foreach (ModMetadata md in GameManager.modMetas) { if (md.isStandalone && md.isEnabled) { backupDll = md.modPath; } } //Restore backup File.Delete(codeDll); File.Copy(backupDll, codeDll); List <string> failedPatches = new List <string>(); foreach (ModMetadata md in GameManager.modMetas) { if (md.isPatch && md.isEnabled) { monomodProcess.StartInfo.Arguments = ('"' + codeDll + '"') + " " + ('"' + md.modPath + '"') + " " + ('"' + moddedDLL + '"'); monomodProcess.Start(); string mmoutput = monomodProcess.StandardOutput.ReadToEnd(); monomodProcess.WaitForExit(); int exitCode = monomodProcess.ExitCode; DebugLogger.Log("MMEC:" + exitCode); DebugLogger.Log(mmoutput); if (exitCode != 0) { failedPatches.Add(Path.GetFileNameWithoutExtension(md.modPath)); } //Replace file if (File.Exists(moddedDLL)) { //Move modded .dll over original .dll File.Delete(codeDll); File.Copy(moddedDLL, codeDll); File.Delete(moddedDLL); } } } if (failedPatches.Count > 0) { Eto.Forms.MessageBox.Show("Some mods failed to apply correctly! Please send your LOG.txt (in the Partiality folder) to someone who can help, probably from the people who made the mod."); } //Set mods to all not be dirty, and save them. foreach (ModMetadata md in GameManager.modMetas) { md.isDirty = false; } try { GameManager.SaveAllMetadata(); } catch (System.Exception e) { DebugLogger.Log(e); } } } //HookGen stuff { //Delete Legacy DLL if (File.Exists(Path.Combine(managedFolder, "HOOKS-Assembly-CSharp.dll"))) { File.Delete(Path.Combine(managedFolder, "HOOKS-Assembly-CSharp.dll")); } if (File.Exists(hookGenDLL)) { File.Delete(hookGenDLL); } //Delete files if they existed, so we can update them. if (File.Exists(Path.Combine(managedFolder, runtimeDetourDLL))) { File.Delete(Path.Combine(managedFolder, runtimeDetourDLL)); } if (File.Exists(Path.Combine(managedFolder, mmUtilsDLL))) { File.Delete(Path.Combine(managedFolder, mmUtilsDLL)); } if (File.Exists(Path.Combine(managedFolder, jsonDLL))) { File.Delete(Path.Combine(managedFolder, jsonDLL)); } //Copy files File.Copy(Path.Combine(executableDirectory, runtimeDetourDLL), Path.Combine(managedFolder, runtimeDetourDLL)); File.Copy(Path.Combine(executableDirectory, mmUtilsDLL), Path.Combine(managedFolder, mmUtilsDLL)); File.Copy(Path.Combine(executableDirectory, jsonDLL), Path.Combine(managedFolder, jsonDLL)); string pathIn = codeDll; string pathOut = hookGenDLL; using (MonoModder mm = new MonoModder { InputPath = pathIn, OutputPath = pathOut }) { mm.Read(); mm.MapDependencies(); if (File.Exists(pathOut)) { mm.Log(string.Format("Clearing {0}", pathOut)); File.Delete(pathOut); } mm.Log("[HookGen] Starting HookGenerator"); HookGenerator gen = new HookGenerator(mm, Path.GetFileName(pathOut)); using (ModuleDefinition mOut = gen.OutputModule) { gen.HookPrivate = true; gen.Generate(); mOut.Write(pathOut); } mm.Log("[HookGen] Done."); } } //File.WriteAllText( gameDirectory + "\\PARTIALITY_OUTPUT.txt", ); }