diff --git a/CHANGELOG.md b/CHANGELOG.md index 20697e169..260584fa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ All notable changes to this project will be documented in this file. - [Policy] Fix #3518 rewrite de-indexing policy (#3993 by: JonnyOThan; reviewed: HebaruSan) - [Netkan] Fix null reference exception in swinfo transformer (#3999 by: HebaruSan) - [Netkan] Improve netkan relationship error message (#4020, #4021 by: HebaruSan) +- [Core] Get KSP2 version from game assembly (#4034 by: HebaruSan) ## v1.34.4 (Niven) diff --git a/Core/CKAN-core.csproj b/Core/CKAN-core.csproj index ac3d1cd5d..b3f998f10 100644 --- a/Core/CKAN-core.csproj +++ b/Core/CKAN-core.csproj @@ -28,6 +28,7 @@ + diff --git a/Core/Games/KerbalSpaceProgram2.cs b/Core/Games/KerbalSpaceProgram2.cs index 46c3ff14f..a10029b1a 100644 --- a/Core/Games/KerbalSpaceProgram2.cs +++ b/Core/Games/KerbalSpaceProgram2.cs @@ -8,6 +8,7 @@ using log4net; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Mono.Cecil; using CKAN.DLC; using CKAN.Versioning; @@ -21,7 +22,7 @@ public class KerbalSpaceProgram2 : IGame public bool GameInFolder(DirectoryInfo where) => InstanceAnchorFiles.Any(f => File.Exists(Path.Combine(where.FullName, f))) - && Directory.Exists(Path.Combine(where.FullName, "KSP2_x64_Data")); + && Directory.Exists(Path.Combine(where.FullName, DataDir)); /// /// Get the default non-Steam path to KSP on macOS @@ -53,7 +54,7 @@ public string PrimaryModDirectory(GameInstance inst) public string[] StockFolders => new string[] { - "KSP2_x64_Data", + DataDir, "MonoBleedingEdge", "PDLauncher", }; @@ -84,8 +85,7 @@ public bool AllowInstallationIn(string name, out string path) public void RebuildSubdirectories(string absGameRoot) { - const string dataDir = "KSP2_x64_Data"; - var path = Path.Combine(absGameRoot, dataDir); + var path = Path.Combine(absGameRoot, DataDir); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); @@ -151,17 +151,39 @@ public GameVersion[] ParseBuildsJson(JToken json) => json.ToObject(); public GameVersion DetectVersion(DirectoryInfo where) - => VersionFromFile(Path.Combine(where.FullName, "KSP2_x64.exe")); - - private GameVersion VersionFromFile(string path) - => File.Exists(path) - && GameVersion.TryParse(FileVersionInfo.GetVersionInfo(path).ProductVersion + => VersionFromAssembly(Path.Combine(where.FullName, + DataDir, + "Managed", + "Assembly-CSharp.dll")) + ?? VersionFromExecutable(Path.Combine(where.FullName, + "KSP2_x64.exe")) + // Fall back to the most recent version + ?? KnownVersions.Last(); + + private static GameVersion VersionFromAssembly(string assemblyPath) + => File.Exists(assemblyPath) + && GameVersion.TryParse( + AssemblyDefinition.ReadAssembly(assemblyPath) + .Modules + .SelectMany(m => m.GetTypes()) + .Where(t => t.Name == "VersionID") + .SelectMany(t => t.Fields) + .Where(f => f.Name == "VERSION_TEXT") + .Select(f => (string)f.Constant) + .Select(ver => string.Join(".", ver.Split('.').Take(4))) + .FirstOrDefault(), + out GameVersion v) + ? v + : null; + + private static GameVersion VersionFromExecutable(string exePath) + => File.Exists(exePath) + && GameVersion.TryParse(FileVersionInfo.GetVersionInfo(exePath).ProductVersion // Fake instances have an EXE containing just the version string - ?? File.ReadAllText(path), + ?? File.ReadAllText(exePath), out GameVersion v) ? v - // Fall back to the most recent version - : KnownVersions.Last(); + : null; public string CompatibleVersionsFile => "compatible_game_versions.json"; @@ -185,6 +207,8 @@ private GameVersion VersionFromFile(string path) public Uri MetadataBugtrackerURL => new Uri("https://github.com/KSP-CKAN/KSP2-NetKAN/issues/new/choose"); + private const string DataDir = "KSP2_x64_Data"; + // Key: Allowed value of install_to // Value: Relative path // (PrimaryModDirectoryRelative is allowed implicitly)