diff --git a/build/common.props b/build/common.props index 9dd8182..a91402d 100644 --- a/build/common.props +++ b/build/common.props @@ -7,9 +7,9 @@ - 1.10.4 + 1.11.0 2.2.2 - 3.0.0.122 + 3.0.0.124 4.0.150 3.1.0.68 1.0.1.44 diff --git a/changelog.txt b/changelog.txt index 685e29e..e605480 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,8 @@ --------------------------------------------------------------------------------------------------- +Version: 1.11.0 +Game Versions: v1.0.0,v1.0.1,v1.0.2 +* Added warning when module is installed in /Modules and subscribed on Workshop at the same time +--------------------------------------------------------------------------------------------------- Version: 1.10.4 Game Versions: v1.0.0,v1.0.1,v1.0.2 * Improved the obfuscation check diff --git a/src/Bannerlord.BUTRLoader.LauncherEx/Helpers/ModuleChecker.cs b/src/Bannerlord.BUTRLoader.LauncherEx/Helpers/ModuleChecker.cs new file mode 100644 index 0000000..0ee58fe --- /dev/null +++ b/src/Bannerlord.BUTRLoader.LauncherEx/Helpers/ModuleChecker.cs @@ -0,0 +1,60 @@ +using Bannerlord.BUTR.Shared.Helpers; +using Bannerlord.ModuleManager; + +using Mono.Cecil; +using Mono.Cecil.Rocks; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using TaleWorlds.Library; +using TaleWorlds.MountAndBlade; + +namespace Bannerlord.BUTRLoader.Helpers +{ + internal static class ModuleChecker + { + private static readonly HashSet MainModules = ModuleInfoHelper.GetPhysicalModules().Select(x => x.Id).ToHashSet(); + private static readonly HashSet ExternalModules = ModuleInfoHelper.GetPlatformModules().Select(x => x.Id).ToHashSet(); + + public static bool IsInstalledInMainAndExternalModuleDirectory(ModuleInfoExtendedWithMetadata moduleInfoExtended) => + MainModules.Contains(moduleInfoExtended.Id) && ExternalModules.Contains(moduleInfoExtended.Id); + + public static bool IsObfuscated(ModuleInfoExtendedWithMetadata moduleInfoExtended) + { + static bool CanBeLoaded(SubModuleInfoExtended x) => + ModuleInfoHelper.CheckIfSubModuleCanBeLoaded(x, ApplicationPlatform.CurrentPlatform, ApplicationPlatform.CurrentRuntimeLibrary, DedicatedServerType.None, false); + + foreach (var subModule in moduleInfoExtended.SubModules.Where(CanBeLoaded)) + { + var asm = Path.GetFullPath(Path.Combine(moduleInfoExtended.Path, "bin", "Win64_Shipping_Client", subModule.DLLName)); + + try + { + using var moduleDefinition = ModuleDefinition.ReadModule(asm); + + var hasObfuscationAttributeUsed = moduleDefinition.GetCustomAttributes().Any(x => x.Constructor.DeclaringType.Name switch + { + "ConfusedByAttribute" => true, + _ => false + }); + var hasObfuscationAttributeDeclared = moduleDefinition.Types.Any(x => x.Name switch + { + "ConfusedByAttribute" => true, + _ => false + }); + // Every module should have a module initializer. If it's missing, someone is hiding it + var hasModuleInitializer = moduleDefinition.GetAllTypes().Any(x => x.Name == ""); + + return hasObfuscationAttributeUsed || hasObfuscationAttributeDeclared || !hasModuleInitializer; + } + // Failing to read the metadata is a direct sign of metadata manipulation + catch (Exception) { return true; } + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Bannerlord.BUTRLoader.LauncherEx/Patches/Mixins/LauncherModuleVMMixin.cs b/src/Bannerlord.BUTRLoader.LauncherEx/Patches/Mixins/LauncherModuleVMMixin.cs index 2f9aac6..cccfa1f 100644 --- a/src/Bannerlord.BUTRLoader.LauncherEx/Patches/Mixins/LauncherModuleVMMixin.cs +++ b/src/Bannerlord.BUTRLoader.LauncherEx/Patches/Mixins/LauncherModuleVMMixin.cs @@ -2,19 +2,9 @@ using Bannerlord.BUTR.Shared.Utils; using Bannerlord.BUTRLoader.Extensions; using Bannerlord.BUTRLoader.Helpers; -using Bannerlord.ModuleManager; using HarmonyLib.BUTR.Extensions; -using Mono.Cecil; -using Mono.Cecil.Rocks; - -using System; -using System.IO; -using System.Linq; - -using TaleWorlds.Library; -using TaleWorlds.MountAndBlade; using TaleWorlds.MountAndBlade.Launcher.Library; namespace Bannerlord.BUTRLoader.Patches.Mixins @@ -67,16 +57,16 @@ public bool IsNoUpdateAvailable } private bool _isNoUpdateAvailable; - public LauncherHintVM DependencyHint2 { get; } + public LauncherHintVM? DependencyHint2 { get; } public bool AnyDependencyAvailable2 { get; } public bool IsDangerous2 { get; } + public bool IsDisabled2 { get; } private readonly LauncherModuleVM _launcherModuleVM; - private readonly string _moduleId; public LauncherModuleVMMixin(LauncherModuleVM launcherModuleVM) { @@ -97,80 +87,46 @@ void SetVMProperty(string property) SetVMProperty(nameof(IsDisabled2)); SetVMProperty(nameof(IsDangerous2)); - var id = _launcherModuleVM.Info.Id ?? string.Empty; - var moduleInfoExtended = ModuleInfoHelper.LoadFromId(id); - if (moduleInfoExtended is not null && ModuleInfoHelper2.GetDependencyHint(moduleInfoExtended) is { } str) + if (ModuleInfoHelper.LoadFromId(_launcherModuleVM.Info.Id) is { } moduleInfoExtended) { - DependencyHint2 = new LauncherHintVM(str); - AnyDependencyAvailable2 = !string.IsNullOrEmpty(str); - SetVMProperty(nameof(DependencyHint2)); - SetVMProperty(nameof(AnyDependencyAvailable2)); - } + if (ModuleInfoHelper2.GetDependencyHint(moduleInfoExtended) is { } str) + { + DependencyHint2 = new LauncherHintVM(str); + AnyDependencyAvailable2 = !string.IsNullOrEmpty(str); + SetVMProperty(nameof(DependencyHint2)); + SetVMProperty(nameof(AnyDependencyAvailable2)); + } - _moduleId = _launcherModuleVM.Info.Id ?? string.Empty; + var dangerous = string.Empty; + if (ModuleChecker.IsInstalledInMainAndExternalModuleDirectory(moduleInfoExtended)) + dangerous += "The Module is installed in the game's /Modules folder and on Steam Workshop!\nThe /Modules version will be used!\n"; + if (ModuleChecker.IsObfuscated(moduleInfoExtended)) + dangerous += "The DLL is obfuscated!\nThere is no guarantee that the code is safe!\nThe BUTR Team warns of consequences arising from running obfuscated code!\n"; + if (!string.IsNullOrEmpty(dangerous)) + { + IsDangerous2 = true; + _launcherModuleVM.DangerousHint = new LauncherHintVM(dangerous); + } + } IsDisabled2 = LauncherModuleVMPatch.AreAllDepenenciesPresentReferences.TryGetValue(launcherModuleVM, out var del) ? !(bool) del.DynamicInvoke(_launcherModuleVM.Info) : true; - UpdateIssues(); + UpdateIssues(_launcherModuleVM.Info.Id); - if (CheckModuleDangerous(moduleInfoExtended)) - { - IsDangerous2 = true; - _launcherModuleVM.DangerousHint = new LauncherHintVM( - "The DLL is obfuscated!\nThere is no guarantee that the code is safe!\nThe BUTR Team warns of consequences arising from running obfuscated code!"); - } _launcherModuleVM.PropertyChanged += (_, e) => { if (e.PropertyName == "Refresh_Command") - UpdateIssues(); + UpdateIssues(_launcherModuleVM.Info.Id); }; } - public void UpdateIssues() + private void UpdateIssues(string id) { - IssuesText = IssueStorage.Issues.TryGetValue(_moduleId, out var issues) && issues.Count > 0 + IssuesText = IssueStorage.Issues.TryGetValue(id, out var issues) && issues.Count > 0 ? string.Join("\n", issues) : string.Empty; } - - private static bool CheckModuleDangerous(ModuleInfoExtended? moduleInfoExtended) - { - static bool CanBeLoaded(SubModuleInfoExtended x) => - ModuleInfoHelper.CheckIfSubModuleCanBeLoaded(x, ApplicationPlatform.CurrentPlatform, ApplicationPlatform.CurrentRuntimeLibrary, DedicatedServerType.None, false); - - if (moduleInfoExtended is not ModuleInfoExtendedWithMetadata moduleInfoExtendedWithMetadata) - return false; - - foreach (var subModule in moduleInfoExtended.SubModules.Where(CanBeLoaded)) - { - var asm = Path.GetFullPath(Path.Combine(moduleInfoExtendedWithMetadata.Path, "bin", "Win64_Shipping_Client", subModule.DLLName)); - - try - { - using var moduleDefinition = ModuleDefinition.ReadModule(asm); - - var hasObfuscationAttributeUsed = moduleDefinition.GetCustomAttributes().Any(x => x.Constructor.DeclaringType.Name switch - { - "ConfusedByAttribute" => true, - _ => false - }); - var hasObfuscationAttributeDeclared = moduleDefinition.Types.Any(x => x.Name switch - { - "ConfusedByAttribute" => true, - _ => false - }); - // Every module should have a module initializer. If it's missing, someone is hiding it - var hasModuleInitializer = moduleDefinition.GetAllTypes().Any(x => x.Name == ""); - - return hasObfuscationAttributeUsed || hasObfuscationAttributeDeclared || !hasModuleInitializer; - } - // Failing to read the metadata is a direct sign of metadata manipulation - catch (Exception) { return true; } - } - - return false; - } } } \ No newline at end of file