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");
        }
Example #4
0
 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);
 }
Example #5
0
        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));
            }
        }
Example #6
0
        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", );
        }