diff --git a/PartIDRedirectionPlugin .cs b/PartIDRedirectionPlugin .cs new file mode 100644 index 0000000..de767d6 --- /dev/null +++ b/PartIDRedirectionPlugin .cs @@ -0,0 +1,184 @@ +using BepInEx; +using BepInEx.Logging; +using HarmonyLib; +using System; +using System.Collections.Generic; +using System.IO; +using UnityEngine; + +namespace PartIDRedirection +{ + [BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)] + public class PartIDRedirectionPlugin : BaseUnityPlugin + { + private const string REDIRECT_CONFIG_FILENAME = "PartID.redirects.cfg"; + + internal static Dictionary IdentifierMappings { get; private set; } + internal static ManualLogSource PluginLogger { get; private set; } + + private void Awake() + { + PluginLogger = Logger; + + string configFilePath = Path.Combine(Paths.ConfigPath, REDIRECT_CONFIG_FILENAME); + IdentifierMappings = LoadRedirectMappings(configFilePath); + + Logger.LogInfo($"Loaded {IdentifierMappings.Count} PartID redirect(s) from: {configFilePath}"); + + try + { + Harmony harmony = new Harmony(PluginInfo.PLUGIN_GUID); + harmony.PatchAll(typeof(PartsDatabasePatch)); + harmony.PatchAll(typeof(PartInstancePatch)); + Logger.LogInfo("Successfully patched PartsDatabase.GetDesc method"); + } + catch (Exception ex) + { + Logger.LogError($"Failed to apply Harmony patches: {ex}"); + } + } + + private static Dictionary LoadRedirectMappings(string filePath) + { + var mappings = new Dictionary(StringComparer.OrdinalIgnoreCase); + + if (!File.Exists(filePath)) + { + PluginLogger?.LogWarning($"Redirect configuration file not found: {filePath}"); + return mappings; + } + + try + { + string[] lines = File.ReadAllLines(filePath); + int lineNumber = 0; + + foreach (string rawLine in lines) + { + lineNumber++; + string line = rawLine.Trim(); + + if (string.IsNullOrEmpty(line) || line.StartsWith("#")) + continue; + + int separatorIndex = line.IndexOf('='); + + if (separatorIndex <= 0 || separatorIndex >= line.Length - 1) + { + PluginLogger?.LogWarning($"Invalid format at line {lineNumber}: {line}"); + continue; + } + + string oldID = line.Substring(0, separatorIndex).Trim(); + string newID = line.Substring(separatorIndex + 1).Trim(); + + if (string.IsNullOrEmpty(oldID) || string.IsNullOrEmpty(newID)) + { + PluginLogger?.LogWarning($"Empty ID at line {lineNumber}: {line}"); + continue; + } + + if (mappings.ContainsKey(oldID)) + { + PluginLogger?.LogWarning($"Duplicate mapping for '{oldID}' at line {lineNumber}. Using latest definition."); + } + + mappings[oldID] = newID; + } + + PluginLogger?.LogInfo($"Successfully parsed {mappings.Count} redirect mapping(s)"); + } + catch (Exception ex) + { + PluginLogger?.LogError($"Error reading redirect file: {ex.Message}"); + } + + return mappings; + } + } + + internal static class PluginInfo + { + public const string PLUGIN_GUID = "com.anonymus637.partidredirection"; + public const string PLUGIN_NAME = "PartID Redirection"; + public const string PLUGIN_VERSION = "1.0.0"; + } + + [HarmonyPatch(typeof(PartsDatabase))] + internal static class PartsDatabasePatch + { + [HarmonyPatch("GetDesc", new Type[] { typeof(string), typeof(GetString) })] + [HarmonyPrefix] + private static void GetDescPrefix(ref string partName) + { + RedirectPartName(ref partName); + } + + [HarmonyPatch("GetPartDescription")] + [HarmonyPrefix] + private static void GetPartDescriptionPrefix(ref string partName) + { + RedirectPartName(ref partName); + } + + private static void RedirectPartName(ref string partName) + { + if (string.IsNullOrEmpty(partName)) + return; + + string cleanPartName = partName; + int newlineIndex = partName.IndexOfAny(new char[] { '\n', '\r' }); + if (newlineIndex > 0) + { + cleanPartName = partName.Substring(0, newlineIndex).Trim(); + } + + if (PartIDRedirectionPlugin.IdentifierMappings.TryGetValue(cleanPartName, out string redirectedIdentifier)) + { + PartIDRedirectionPlugin.PluginLogger?.LogInfo( + $"Redirecting part identifier: '{cleanPartName}' → '{redirectedIdentifier}'" + ); + + if (newlineIndex > 0) + { + partName = redirectedIdentifier + partName.Substring(newlineIndex); + } + else + { + partName = redirectedIdentifier; + } + } + } + } + + [HarmonyPatch(typeof(PartInstance))] + internal static class PartInstancePatch + { + [HarmonyPatch("name", MethodType.Getter)] + [HarmonyPostfix] + private static void NameGetterPostfix(PartInstance __instance, ref string __result) + { + if (string.IsNullOrEmpty(__result)) + return; + + string cleanPartName = __result; + int newlineIndex = __result.IndexOfAny(new char[] { '\n', '\r' }); + if (newlineIndex > 0) + { + cleanPartName = __result.Substring(0, newlineIndex).Trim(); + } + + if (PartIDRedirectionPlugin.IdentifierMappings.TryGetValue(cleanPartName, out string redirectedIdentifier)) + { + if (newlineIndex > 0) + { + __result = redirectedIdentifier + __result.Substring(newlineIndex); + } + else + { + __result = redirectedIdentifier; + } + } + } + } +} \ No newline at end of file diff --git a/PartIDRedirectionPlugin.csproj b/PartIDRedirectionPlugin.csproj new file mode 100644 index 0000000..930d4c8 --- /dev/null +++ b/PartIDRedirectionPlugin.csproj @@ -0,0 +1,62 @@ + + + + + Debug + AnyCPU + {9C7CFC83-908F-49F8-86A7-5E16FE6898F3} + Library + Properties + PartIDRedirectionPlugin + PartID Redirection Plugin + v4.0 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + D:\Steam\steamapps\common\PC Building Simulator\BepInEx\core\0Harmony.dll + + + D:\Steam\steamapps\common\PC Building Simulator\PCBS_Data\Managed\Assembly-CSharp-firstpass.dll + + + D:\Steam\steamapps\common\PC Building Simulator\BepInEx\core\BepInEx.dll + + + + + + + + + + D:\Steam\steamapps\common\PC Building Simulator\PCBS_Data\Managed\UnityEngine.dll + + + D:\Steam\steamapps\common\PC Building Simulator\PCBS_Data\Managed\UnityEngine.CoreModule.dll + + + + + + + + \ No newline at end of file diff --git a/PartIDRedirectionPlugin.sln b/PartIDRedirectionPlugin.sln new file mode 100644 index 0000000..8a27319 --- /dev/null +++ b/PartIDRedirectionPlugin.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36603.0 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PartIDRedirectionPlugin", "PartIDRedirectionPlugin.csproj", "{9C7CFC83-908F-49F8-86A7-5E16FE6898F3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9C7CFC83-908F-49F8-86A7-5E16FE6898F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C7CFC83-908F-49F8-86A7-5E16FE6898F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C7CFC83-908F-49F8-86A7-5E16FE6898F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C7CFC83-908F-49F8-86A7-5E16FE6898F3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9C91F2F5-4A20-4113-8765-BA03EDA3E091} + EndGlobalSection +EndGlobal