public void Patch() { if (!File.Exists(_filePath)) { throw new FileNotFoundException(_filePath); } var fileBytes = File.ReadAllBytes(_filePath); var buildSettingsStartIndex = BitConverter.ToInt32(fileBytes, BuildSettingsStartAddressIndex) + BlockAddressOffset; var buildSettingsSize = BitConverter.ToInt32(fileBytes, BuildSettingsSizeIndex); var buildSettingsEndIndex = buildSettingsStartIndex + buildSettingsSize; var patchStartIndex = FindPatchStartIndex(fileBytes, buildSettingsStartIndex, buildSettingsEndIndex); var isAlreadyPatched = FindExistingPatch(fileBytes, patchStartIndex, buildSettingsEndIndex); if (isAlreadyPatched) { _writer.WriteLine("globalgamemanagers already patched."); return; } BackupFile(_filePath); var patchedBytes = CreatePatchedFileBytes(fileBytes, patchStartIndex); File.WriteAllBytes(_filePath, patchedBytes); _writer.WriteLine("Successfully patched globalgamemanagers."); }
public IModButton GetButton(string title) { var button = Buttons.FirstOrDefault(x => x.Title == title || x.Button.name == title); if (button == null) { _console.WriteLine("Warning: no button found with title or name: " + title); } return(button); }
private T GetTitleButton <T>(string title, List <T> buttons) where T : IModButton { var button = buttons.FirstOrDefault(x => x.Title == title || x.Button.name == title); if (button == null) { OwmlConsole.WriteLine("Warning: no button found with title or name: " + title); } return(button); }
private void SubscribeToEvent <T>(Common.Events ev) { var type = typeof(T); if (IsSubscribedTo(type, ev)) { _console.WriteLine($"Warning: already subscribed to {ev} of {type.Name}"); return; } AddToEventList(_subscribedEvents, type, ev); }
public IModConfigMenu GetModMenu(IModBehaviour modBehaviour) { _console.WriteLine("Registering " + modBehaviour.ModHelper.Manifest.UniqueName); var modConfigMenu = _modConfigMenus.FirstOrDefault(x => x.Mod == modBehaviour); if (modConfigMenu == null) { _console.WriteLine($"Error: {modBehaviour.ModHelper.Manifest.UniqueName} isn't added."); return(null); } return(modConfigMenu); }
public AssetBundle LoadBundle(string filename) { var path = _manifest.ModFolderPath + filename; _console.WriteLine("Loading asset bundle from " + path); var bundle = AssetBundle.LoadFromFile(path); if (bundle == null) { _console.WriteLine("Bundle is null"); } return(bundle); }
public GameObject Load3DObject(ModBehaviour modBehaviour, string objectFilename, string imageFilename) { var objectPath = modBehaviour.ModManifest.FolderPath + objectFilename; var imagePath = modBehaviour.ModManifest.FolderPath + imageFilename; _console.WriteLine("Loading object from " + objectPath); var go = new GameObject(); go.AddComponent <DontDestroyOnLoad>(); modBehaviour.StartCoroutine(LoadMesh(go, objectPath)); modBehaviour.StartCoroutine(LoadTexture(go, imagePath)); return(go); }
public IModInputCombination RegisterCombination(IModBehaviour mod, string name, string combination) { var combo = new ModInputCombination(mod.ModHelper.Manifest, _console, name, combination); switch (SwapCombination(combo, false)) { case RegistrationCode.InvalidCombination: _console.WriteLine($"Failed to register \"{combo.FullName}\": invalid combination!"); return(null); case RegistrationCode.CombinationTooLong: _console.WriteLine($"Failed to register \"{combo.FullName}\": too long!"); return(null); case RegistrationCode.CombinationTaken: _console.WriteLine($"Failed to register \"{combo.FullName}\": already in use by following mods:"); var collisions = GetCollisions(combo.Hashes); foreach (var collision in collisions) { _console.WriteLine($"\"{collision}\""); } return(null); case RegistrationCode.AllNormal: return(combo); default: return(null); } }
private void AddConfigInput(string key, object value, int index) { if (value is bool) { AddToggleInput(key, index); return; } if (value is string) { AddTextInput(key, index); return; } if (new[] { typeof(long), typeof(int), typeof(float), typeof(double) }.Contains(value.GetType())) { AddNumberInput(key, index); return; } if (value is JObject obj) { var type = (string)obj["type"]; if (type == "slider") { AddSliderInput(key, obj, index); return; } if (type == "toggle") { AddToggleInput(key, obj, index); return; } _console.WriteLine("Error: unrecognized complex setting: " + value); return; } _console.WriteLine("Error: unrecognized setting type: " + value.GetType()); }
public IList <IModData> SortMods(IList <IModData> mods) { var modDict = new Dictionary <string, IModData>(); var set = new HashSet <Edge>(); var modList = mods.Select(mod => mod.Manifest.UniqueName).ToList(); foreach (var mod in mods) { modDict.Add(mod.Manifest.UniqueName, mod); foreach (var dependency in mod.Manifest.Dependencies) { if (mod.Manifest.PriorityLoad && !modList.Contains(dependency)) { _console.WriteLine($"Error! {mod.Manifest.UniqueName} (priority load) depends on a normal mod! Removing from load..."); modDict.Remove(mod.Manifest.UniqueName); modList.Remove(mod.Manifest.UniqueName); } else { set.Add(new Edge(mod.Manifest.UniqueName, dependency)); } } } var sortedList = TopologicalSort( new HashSet <string>(modList), new HashSet <Edge>(set) ); if (sortedList == null) { // Sorting has failed, return the original mod list _console.WriteLine("Error - Cyclic dependency found. Returning original load order..."); return(mods); } sortedList.Reverse(); return(sortedList.Select(mod => modDict[mod]).ToList()); }
private void PatchAssembly() { var patcher = new dnpatch.Patcher($"{_owmlConfig.ManagedPath}/Assembly-CSharp.dll"); var target = new Target { Class = PatchClass, Method = PatchMethod }; var instructions = patcher.GetInstructions(target).ToList(); var patchedInstructions = GetPatchedInstructions(instructions); if (patchedInstructions.Count == 1) { _writer.WriteLine($"{PatchClass}.{PatchMethod} is already patched."); return; } if (patchedInstructions.Count > 1) { _writer.WriteLine($"Removing corrupted patch from {PatchClass}.{PatchMethod}."); foreach (var patchedInstruction in patchedInstructions) { instructions.Remove(patchedInstruction); } } _writer.WriteLine($"Adding patch in {PatchClass}.{PatchMethod}."); var newInstruction = Instruction.Create(OpCodes.Call, patcher.BuildCall(typeof(ModLoader.ModLoader), "LoadMods", typeof(void), new Type[] { })); instructions.Insert(instructions.Count - 1, newInstruction); target.Instructions = instructions.ToArray(); Patch(patcher, target); Save(patcher); }
private void PatchGlobalManager(bool enableVR) { var currentPath = _owmlConfig.DataPath + "/globalgamemanagers"; if (!File.Exists(currentPath)) { _writer.WriteLine("Error: can't find " + currentPath); return; } var currentChecksum = CalculateChecksum(currentPath); _writer.WriteLine("Current checksum: " + currentChecksum); var backupPath = $"{_owmlConfig.DataPath}/globalgamemanagers.{currentChecksum}.bak"; if (!File.Exists(backupPath)) { _writer.WriteLine("Taking backup of globalgamemanagers."); File.Copy(currentPath, backupPath, true); } var vrPath = $"{_owmlConfig.DataPath}/globalgamemanagers.{currentChecksum}.vr"; if (enableVR && !File.Exists(vrPath)) { _writer.WriteLine("Patching globalgamemanagers for VR..."); if (PatchChecksums.Contains(currentChecksum)) { var patchPath = $"{_owmlConfig.OWMLPath}VR/{currentChecksum}"; ApplyPatch(currentPath, vrPath, patchPath); } else { var patchedChecksum = PatchChecksums.FirstOrDefault(checksum => CalculateChecksum($"{_owmlConfig.DataPath}/globalgamemanagers.{checksum}.vr") == currentChecksum); if (!string.IsNullOrEmpty(patchedChecksum)) { _writer.WriteLine("Already patched! Original checksum: " + patchedChecksum); vrPath = $"{_owmlConfig.DataPath}/globalgamemanagers.{patchedChecksum}.vr"; } else { _writer.WriteLine($"Error: invalid checksum: {currentChecksum}. " + "VR patch for this version of Outer Wilds is not yet supported by OWML."); return; } } } var copyFrom = enableVR ? vrPath : backupPath; File.Copy(copyFrom, currentPath, true); }
public void AddPrefix <T>(string methodName, Type patchType, string patchMethodName) { var prefix = patchType.GetAnyMethod(patchMethodName); if (prefix == null) { _logger.Log("prefix is null"); _console.WriteLine("prefix is null"); return; } Patch <T>(methodName, prefix, null, null); }
public void LoadMods() { if (_owmlConfig.Verbose) { _console.WriteLine("Verbose mode is enabled"); Application.logMessageReceived += OnLogMessageReceived; } var normalMods = _modFinder.GetMods().Where(mod => !mod.Manifest.PriorityLoad).ToList(); var sortedNormal = _sorter.SortMods(normalMods); var priorityMods = _modFinder.GetMods().Where(mod => mod.Manifest.PriorityLoad).ToList(); var sortedPriority = _sorter.SortMods(priorityMods); var modNames = _modFinder.GetMods().Where(mod => mod.Config.Enabled).Select(mod => mod.Manifest.UniqueName).ToList(); var sortedMods = sortedPriority.Concat(sortedNormal); foreach (var mod in sortedMods) { foreach (var dependency in mod.Manifest.Dependencies) { if (!modNames.Contains(dependency)) { _console.WriteLine($"Error! {mod.Manifest.UniqueName} needs {dependency}, but it's disabled!"); } } var modType = LoadMod(mod); if (modType == null) { _logger.Log("Mod type is null, skipping"); _menus.ModsMenu.AddMod(mod, null); continue; } var helper = CreateModHelper(mod); var initMod = InitializeMod(modType, helper); _menus.ModsMenu.AddMod(mod, initMod); _modList.Add(initMod); } }
private HarmonyInstance CreateInstance() { HarmonyInstance harmony; try { _logger.Log("Creating harmony instance"); harmony = HarmonyInstance.Create("com.alek.owml"); _logger.Log("Created harmony instance"); } catch (Exception ex) { _console.WriteLine($"Exception while creating harmony instance: {ex}"); return(null); } if (harmony == null) { _console.WriteLine("Error: harmony instance is null"); } return(harmony); }
public IList <IModData> GetMods() { if (!Directory.Exists(_config.ModsPath)) { _console.WriteLine("Warning: Mods folder not found!"); return(new List <IModData>()); } var manifestFilenames = Directory.GetFiles(_config.ModsPath, "manifest.json", SearchOption.AllDirectories); var mods = new List <IModData>(); foreach (var manifestFilename in manifestFilenames) { var json = File.ReadAllText(manifestFilename); var manifest = JsonConvert.DeserializeObject <ModManifest>(json); manifest.ModFolderPath = manifestFilename.Substring(0, manifestFilename.IndexOf("manifest.json")); var modData = GetModData(manifest); mods.Add(modData); } return(mods); }
private List <long> StringToHashes(string combinations) { var hashes = new List <long>(); foreach (var combo in combinations.Split('/')) { var hash = ModInputLibrary.StringToHash(combo); if (hash <= 0) { _console.WriteLine($"Warning: Invalid part of combo in {FullName}: {combo}, " + ModInputLibrary.GetReadableMessage((RegistrationCode)hash)); continue; } hashes.Add(hash); if (hash < ModInputLibrary.MaxUsefulKey) { _singles.Add((KeyCode)hash); } } return(hashes); }
public void Run(string[] args) { _writer.WriteLine($"Started OWML v{_owmlManifest.Version}"); _writer.WriteLine("For detailed log, see Logs/OWML.Log.txt"); LocateGamePath(); CopyGameFiles(); CreateLogsDirectory(); var hasPortArgument = CommandLineArguments.HasArgument(Constants.ConsolePortArgument); if (!hasPortArgument) { ListenForOutput(); } var mods = _modFinder.GetMods(); ShowModList(mods); PatchGame(mods); StartGame(args); if (hasPortArgument) { ExitConsole(); } Console.ReadLine(); }