diff --git a/StardewXnbHack/Framework/PlatformContext.cs b/StardewXnbHack/Framework/PlatformContext.cs index 184fe84..1a0fbd6 100644 --- a/StardewXnbHack/Framework/PlatformContext.cs +++ b/StardewXnbHack/Framework/PlatformContext.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using StardewModdingAPI.Toolkit; @@ -26,35 +27,81 @@ public bool Is(params Platform[] platforms) return platforms.Contains(this.Platform); } - /// Detect the game folder, if any. - public string DetectGameFolder() + /// Get the absolute paths to the game and content folders, if found. + /// The game path specified by the user, if any. + /// The absolute path to the game folder, if found. + /// The absolute path to the content folder, if found. + /// Returns whether both the game and content folders were found. + public bool TryDetectGamePaths(string specifiedPath, out string gamePath, out string contentPath) { - string assemblyDirPath = AppDomain.CurrentDomain.BaseDirectory; + gamePath = null; + contentPath = null; - return this.IsGameFolder(assemblyDirPath) - ? assemblyDirPath - : new ModToolkit().GetGameFolders().FirstOrDefault()?.FullName; - } + // check possible game paths + foreach (string candidate in this.GetCandidateGamePaths(specifiedPath)) + { + // detect paths + string curGamePath = this.TryGamePath(candidate); + string curContentPath = this.FindContentPath(curGamePath); - /// Get the relative path from the game folder to the content folder. - public string GetRelativeContentPath() - { - return this.Platform == Platform.Mac - ? "../../Resources/Content" - : "Content"; + // valid game install found + if (curGamePath != null && curContentPath != null) + { + gamePath = curGamePath; + contentPath = curContentPath; + return true; + } + + // if game folder exists without a content folder, track the first found game path (i.e. the highest-priority one) + gamePath = gamePath ?? curGamePath; + } + + return false; } /********* ** Private methods *********/ - /// Get whether a folder contains the game files. - /// The absolute folder path to check. - private bool IsGameFolder(string path) + /// Get the possible game paths. + /// The game path specified by the user, if any. + private IEnumerable GetCandidateGamePaths(string specifiedPath = null) { - return - File.Exists(Path.Combine(path, this.GetExecutableFileName())) - && Directory.Exists(Path.Combine(path, this.GetRelativeContentPath())); + // specified path + if (!string.IsNullOrWhiteSpace(specifiedPath)) + yield return specifiedPath; + + // current working directory + yield return AppDomain.CurrentDomain.BaseDirectory; + + // detected game path + string detectedPath = new ModToolkit().GetGameFolders().FirstOrDefault()?.FullName; + if (detectedPath != null) + yield return detectedPath; + } + + /// Get the absolute path to the game folder, if it's valid. + /// The path to check for a game install. + private string TryGamePath(string path) + { + // game path exists + if (path == null) + return null; + DirectoryInfo gameDir = new DirectoryInfo(path); + if (!gameDir.Exists) + return null; + + // has game files + bool hasExecutable = File.Exists(Path.Combine(gameDir.FullName, this.GetExecutableFileName())); + if (!hasExecutable) + return null; + + // isn't the build folder when compiled directly + bool isCompileFolder = File.Exists(Path.Combine(gameDir.FullName, "StardewXnbHack.exe.config")); + if (isCompileFolder) + return null; + + return gameDir.FullName; } /// Get the filename for the Stardew Valley executable. @@ -64,5 +111,44 @@ private string GetExecutableFileName() ? "Stardew Valley.exe" : "StardewValley.exe"; } + + /// Get the absolute path to the content folder for a given game, if found. + /// The absolute path to the game folder. + private string FindContentPath(string gamePath) + { + if (gamePath == null) + return null; + + foreach (string relativePath in this.GetPossibleRelativeContentPaths()) + { + DirectoryInfo folder = new DirectoryInfo(Path.Combine(gamePath, relativePath)); + if (folder.Exists) + return folder.FullName; + } + + return null; + } + + /// Get the possible relative paths for the current platform. + private IEnumerable GetPossibleRelativeContentPaths() + { + // under game folder on most platforms + if (this.Platform != Platform.Mac) + yield return "Content"; + + // MacOS + else + { + // Steam paths + // - game path: StardewValley/Contents/MacOS + // - content: StardewValley/Contents/Resources/Content + yield return "../Resources/Content"; + + // GOG paths + // - game path: Stardew Valley.app/Contents/MacOS + // - content: Stardew Valley.app/Resources/Content + yield return "../../Resources/Content"; + } + } } } diff --git a/StardewXnbHack/Program.cs b/StardewXnbHack/Program.cs index 7854d26..a4bfe79 100644 --- a/StardewXnbHack/Program.cs +++ b/StardewXnbHack/Program.cs @@ -85,18 +85,24 @@ public static void Run(Game1 game = null, string gamePath = null, FuncMIT https://github.com/Pathoschild/StardewXnbHack git - 1.0.1 + 1.0.2 net452 @@ -24,7 +24,7 @@ - + diff --git a/release-notes.md b/release-notes.md index 993fd4c..48b6975 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,10 +1,18 @@ [← back to readme](README.md) # Release notes +## 1.0.2 +Released 07 December 2020. + +* Assets on MacOS are now unpacked into the game folder instead of resources, for consistency with other platforms. +* Improved error if the game's content folder is missing. +* Fixed duplicate tile index properties in some cases. +* Fixed unpack error on MacOS with Steam. + ## 1.0.1 Released 21 November 2020. -* Updated TMXTile to 1.5.8 to fix `.tmx` map files losing tile index properties. +* Fixed `.tmx` map files losing tile index properties. ## 1.0 Released 04 October 2020.