diff --git a/src/openrct2/config/Config.cpp b/src/openrct2/config/Config.cpp index a2fa8d867f..956a408de6 100644 --- a/src/openrct2/config/Config.cpp +++ b/src/openrct2/config/Config.cpp @@ -716,13 +716,24 @@ namespace OpenRCT2::Config } } - auto steamPath = Platform::GetSteamPath(); - if (!steamPath.empty()) + auto steamPaths = Platform::GetSteamPaths(); + if (steamPaths.isSteamPresent()) { - std::string location = Path::Combine(steamPath, Platform::GetRCT1SteamDir()); - if (RCT1DataPresentAtLocation(location)) + for (const auto& root : steamPaths.roots) { - return location; + auto nativePath = Path::Combine(root, steamPaths.nativeFolder, Platform::kSteamRCT1Data.nativeFolder); + if (RCT1DataPresentAtLocation(nativePath)) + { + return nativePath; + } + if (!steamPaths.downloadDepotFolder.empty()) + { + auto downloadDepotPath = steamPaths.getDownloadDepotFolder(root, Platform::kSteamRCT1Data); + if (RCT1DataPresentAtLocation(downloadDepotPath)) + { + return downloadDepotPath; + } + } } } @@ -753,19 +764,31 @@ namespace OpenRCT2::Config } } - auto steamPath = Platform::GetSteamPath(); - if (!steamPath.empty()) + auto steamPaths = Platform::GetSteamPaths(); + if (steamPaths.isSteamPresent()) { - std::string location = Path::Combine(steamPath, Platform::GetRCT2SteamDir()); - if (Platform::OriginalGameDataExists(location)) + const std::array gamesToCheck = { + Platform::kSteamRCT2Data, + Platform::kSteamRCTCData, + }; + for (const auto& root : steamPaths.roots) { - return location; - } - - std::string location2 = Path::Combine(steamPath, Platform::GetRCTClassicSteamDir()); - if (Platform::OriginalGameDataExists(location2)) - { - return location2; + for (const auto& game : gamesToCheck) + { + auto nativePath = Path::Combine(root, steamPaths.nativeFolder, game.nativeFolder); + if (Platform::OriginalGameDataExists(nativePath)) + { + return nativePath; + } + if (!steamPaths.downloadDepotFolder.empty()) + { + auto downloadDepotPath = steamPaths.getDownloadDepotFolder(root, game); + if (Platform::OriginalGameDataExists(downloadDepotPath)) + { + return downloadDepotPath; + } + } + } } } diff --git a/src/openrct2/platform/Platform.Android.cpp b/src/openrct2/platform/Platform.Android.cpp index 1ae2dbcaa4..dc0c0e4d6a 100644 --- a/src/openrct2/platform/Platform.Android.cpp +++ b/src/openrct2/platform/Platform.Android.cpp @@ -148,22 +148,7 @@ namespace OpenRCT2::Platform return isImperial == JNI_TRUE ? MeasurementFormat::Imperial : MeasurementFormat::Metric; } - std::string GetSteamPath() - { - return {}; - } - - u8string GetRCT1SteamDir() - { - return {}; - } - - u8string GetRCT2SteamDir() - { - return {}; - } - - u8string GetRCTClassicSteamDir() + SteamPaths GetSteamPaths() { return {}; } diff --git a/src/openrct2/platform/Platform.Common.cpp b/src/openrct2/platform/Platform.Common.cpp index e33cc7b7e3..f50c946328 100644 --- a/src/openrct2/platform/Platform.Common.cpp +++ b/src/openrct2/platform/Platform.Common.cpp @@ -213,4 +213,15 @@ namespace OpenRCT2::Platform return false; } + bool SteamPaths::isSteamPresent() const + { + return !roots.empty(); + } + + u8string SteamPaths::getDownloadDepotFolder(u8string_view steamroot, const SteamGameData& data) const + { + return Path::Combine( + steamroot, downloadDepotFolder, "app_" + std::to_string(data.appId), "depot_" + std::to_string(data.depotId)); + } + } // namespace OpenRCT2::Platform diff --git a/src/openrct2/platform/Platform.Emscripten.cpp b/src/openrct2/platform/Platform.Emscripten.cpp index 5eb5453bdd..24f73120de 100644 --- a/src/openrct2/platform/Platform.Emscripten.cpp +++ b/src/openrct2/platform/Platform.Emscripten.cpp @@ -97,22 +97,7 @@ namespace OpenRCT2::Platform return isImperial == 1 ? MeasurementFormat::Imperial : MeasurementFormat::Metric; } - std::string GetSteamPath() - { - return {}; - } - - u8string GetRCT1SteamDir() - { - return {}; - } - - u8string GetRCT2SteamDir() - { - return {}; - } - - u8string GetRCTClassicSteamDir() + SteamPaths GetSteamPaths() { return {}; } diff --git a/src/openrct2/platform/Platform.Linux.cpp b/src/openrct2/platform/Platform.Linux.cpp index 28ab83344f..5bcb8f9d5e 100644 --- a/src/openrct2/platform/Platform.Linux.cpp +++ b/src/openrct2/platform/Platform.Linux.cpp @@ -320,65 +320,52 @@ namespace OpenRCT2::Platform return MeasurementFormat::Metric; } - std::string GetSteamPath() + SteamPaths GetSteamPaths() { + SteamPaths ret = {}; + ret.nativeFolder = "steamapps/common"; + ret.downloadDepotFolder = "ubuntu12_32/steamapps/content"; + ret.manifests = "steamapps"; + const char* steamRoot = getenv("STEAMROOT"); if (steamRoot != nullptr) { - return Path::Combine(steamRoot, u8"ubuntu12_32/steamapps/content"); + ret.roots.emplace_back(steamRoot); } const char* localSharePath = getenv("XDG_DATA_HOME"); if (localSharePath != nullptr) { - auto steamPath = Path::Combine(localSharePath, u8"Steam/ubuntu12_32/steamapps/content"); - if (Path::DirectoryExists(steamPath)) + auto xdgDataHomeSteamPath = Path::Combine(localSharePath, u8"Steam"); + if (Path::DirectoryExists(xdgDataHomeSteamPath)) { - return steamPath; + ret.roots.emplace_back(xdgDataHomeSteamPath); } } const char* homeDir = getpwuid(getuid())->pw_dir; - if (homeDir == nullptr) + if (homeDir != nullptr) { - return {}; + auto localShareSteamPath = Path::Combine(homeDir, u8".local/share/Steam"); + if (Path::DirectoryExists(localShareSteamPath)) + { + ret.roots.emplace_back(localShareSteamPath); + } + + auto oldSteamPath = Path::Combine(homeDir, u8".steam/steam"); + if (Path::DirectoryExists(oldSteamPath)) + { + ret.roots.emplace_back(oldSteamPath); + } + + auto snapLocalShareSteamPath = Path::Combine(homeDir, u8"snap/steam/common/.local/share/Steam"); + if (Path::DirectoryExists(snapLocalShareSteamPath)) + { + ret.roots.emplace_back(snapLocalShareSteamPath); + } } - // Prefer new path for Steam, which is the default when using with Proton - auto steamPath = Path::Combine(homeDir, u8".local/share/Steam/steamapps/common"); - if (Path::DirectoryExists(steamPath)) - { - return steamPath; - } - - // Fallback paths - steamPath = Path::Combine(homeDir, u8".local/share/Steam/ubuntu12_32/steamapps/content"); - if (Path::DirectoryExists(steamPath)) - { - return steamPath; - } - - steamPath = Path::Combine(homeDir, u8".steam/steam/ubuntu12_32/steamapps/content"); - if (Path::DirectoryExists(steamPath)) - { - return steamPath; - } - return {}; - } - - u8string GetRCT1SteamDir() - { - return u8"Rollercoaster Tycoon Deluxe"; - } - - u8string GetRCT2SteamDir() - { - return u8"Rollercoaster Tycoon 2"; - } - - u8string GetRCTClassicSteamDir() - { - return u8"RollerCoaster Tycoon Classic"; + return ret; } std::vector GetSearchablePathsRCT1() diff --git a/src/openrct2/platform/Platform.Win32.cpp b/src/openrct2/platform/Platform.Win32.cpp index 23605cc3ca..1dd206642a 100644 --- a/src/openrct2/platform/Platform.Win32.cpp +++ b/src/openrct2/platform/Platform.Win32.cpp @@ -737,7 +737,7 @@ namespace OpenRCT2::Platform return isElevated; } - std::string GetSteamPath() + SteamPaths GetSteamPaths() { wchar_t* wSteamPath; HKEY hKey; @@ -759,12 +759,18 @@ namespace OpenRCT2::Platform result = RegQueryValueExW(hKey, L"SteamPath", nullptr, &type, reinterpret_cast(wSteamPath), &size); if (result == ERROR_SUCCESS) { - auto utf8SteamPath = String::toUtf8(wSteamPath); - outPath = Path::Combine(utf8SteamPath, u8"steamapps", u8"common"); + outPath = String::toUtf8(wSteamPath); } free(wSteamPath); RegCloseKey(hKey); - return outPath; + + SteamPaths ret = {}; + ret.roots.emplace_back(outPath); + ret.nativeFolder = "steamapps/common"; + ret.downloadDepotFolder = "steamapps/content"; + ret.manifests = "steamapps"; + + return ret; } std::string GetFontPath(const TTFFontDescriptor& font) @@ -796,21 +802,6 @@ namespace OpenRCT2::Platform return GetLogicalDrives(); } - u8string GetRCT1SteamDir() - { - return u8"Rollercoaster Tycoon Deluxe"; - } - - u8string GetRCT2SteamDir() - { - return u8"Rollercoaster Tycoon 2"; - } - - u8string GetRCTClassicSteamDir() - { - return u8"RollerCoaster Tycoon Classic"; - } - time_t FileGetModifiedTime(u8string_view path) { WIN32_FILE_ATTRIBUTE_DATA data{}; diff --git a/src/openrct2/platform/Platform.h b/src/openrct2/platform/Platform.h index 00603ad076..76037a58f2 100644 --- a/src/openrct2/platform/Platform.h +++ b/src/openrct2/platform/Platform.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #ifdef _WIN32 @@ -60,6 +61,49 @@ struct TTFFontDescriptor; namespace OpenRCT2::Platform { + struct SteamGameData + { + u8string nativeFolder; + uint32_t appId; + uint32_t depotId; + }; + const SteamGameData kSteamRCT1Data = { + .nativeFolder = u8"Rollercoaster Tycoon Deluxe", + .appId = 285310, + .depotId = 285311, + }; + const SteamGameData kSteamRCT2Data = { + .nativeFolder = u8"Rollercoaster Tycoon 2", + .appId = 285330, + .depotId = 285331, + }; + const SteamGameData kSteamRCTCData = { + .nativeFolder = u8"RollerCoaster Tycoon Classic", + .appId = 683900, + .depotId = 683901, + }; + + struct SteamPaths + { + sfl::static_vector roots{}; + /** + * Used by native applications and applications installed through Steam Play. + */ + u8string nativeFolder{}; + /** + * Used by applications downloaded through download_depot. Most likely used on macOS and Linux, + * though technically possible on Windows too. + */ + u8string downloadDepotFolder{}; + /** + * Directory that contains the manifests to trigger a download. + */ + u8string manifests{}; + + bool isSteamPresent() const; + u8string getDownloadDepotFolder(u8string_view steamroot, const SteamGameData& data) const; + }; + constexpr u8string_view kRCTClassicWindowsDataFolder = u8"Assets"; // clang-format off constexpr u8string_view kRCTClassicMacOSDataFolder = @@ -101,7 +145,7 @@ namespace OpenRCT2::Platform std::string GetUsername(); - std::string GetSteamPath(); + SteamPaths GetSteamPaths(); #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD__) || defined(__NetBSD__) std::string GetEnvironmentPath(const char* name); std::string GetHomePath(); @@ -138,9 +182,6 @@ namespace OpenRCT2::Platform bool LockSingleInstance(); - u8string GetRCT1SteamDir(); - u8string GetRCT2SteamDir(); - u8string GetRCTClassicSteamDir(); datetime64 GetDatetimeNowUTC(); uint32_t GetTicks(); diff --git a/src/openrct2/platform/Platform.macOS.mm b/src/openrct2/platform/Platform.macOS.mm index e4d280cf1f..cdc1b6812c 100644 --- a/src/openrct2/platform/Platform.macOS.mm +++ b/src/openrct2/platform/Platform.macOS.mm @@ -235,7 +235,7 @@ namespace OpenRCT2::Platform } } - std::string GetSteamPath() + SteamPaths GetSteamPaths() { const char* homeDir = getpwuid(getuid())->pw_dir; if (homeDir == nullptr) @@ -244,27 +244,18 @@ namespace OpenRCT2::Platform } auto steamPath = Path::Combine(homeDir, "Library/Application Support/Steam"); - if (Path::DirectoryExists(steamPath)) + if (!Path::DirectoryExists(steamPath)) { - return steamPath; + return {}; } - return {}; - } + SteamPaths ret = {}; + ret.roots.emplace_back(steamPath); + ret.nativeFolder = "steamapps/common"; + ret.downloadDepotFolder = "Steam.AppBundle/Steam/Contents/MacOS/steamapps/content"; + ret.manifests = "steamapps"; - u8string GetRCT1SteamDir() - { - return u8"Steam.AppBundle/Steam/Contents/MacOS/steamapps/content/app_285310/depot_285311"; - } - - u8string GetRCT2SteamDir() - { - return u8"Steam.AppBundle/Steam/Contents/MacOS/steamapps/content/app_285330/depot_285331"; - } - - u8string GetRCTClassicSteamDir() - { - return u8"steamapps/common/RollerCoaster Tycoon Classic"; + return ret; } std::string GetFontPath(const TTFFontDescriptor& font)