summaryrefslogtreecommitdiffstats
path: root/xbmc/games/GameUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xbmc/games/GameUtils.cpp314
1 files changed, 314 insertions, 0 deletions
diff --git a/xbmc/games/GameUtils.cpp b/xbmc/games/GameUtils.cpp
new file mode 100644
index 0000000..5eba00b
--- /dev/null
+++ b/xbmc/games/GameUtils.cpp
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2012-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "GameUtils.h"
+
+#include "FileItem.h"
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "addons/Addon.h"
+#include "addons/AddonInstaller.h"
+#include "addons/AddonManager.h"
+#include "addons/BinaryAddonCache.h"
+#include "addons/addoninfo/AddonType.h"
+#include "cores/RetroPlayer/savestates/ISavestate.h"
+#include "cores/RetroPlayer/savestates/SavestateDatabase.h"
+#include "filesystem/SpecialProtocol.h"
+#include "games/addons/GameClient.h"
+#include "games/dialogs/GUIDialogSelectGameClient.h"
+#include "games/dialogs/GUIDialogSelectSavestate.h"
+#include "games/tags/GameInfoTag.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/log.h"
+
+#include <algorithm>
+
+using namespace KODI;
+using namespace GAME;
+
+bool CGameUtils::FillInGameClient(CFileItem& item, std::string& savestatePath)
+{
+ using namespace ADDON;
+
+ if (item.GetGameInfoTag()->GetGameClient().empty())
+ {
+ // If the fileitem is an add-on, fall back to that
+ if (item.HasAddonInfo() && item.GetAddonInfo()->Type() == AddonType::GAMEDLL)
+ {
+ item.GetGameInfoTag()->SetGameClient(item.GetAddonInfo()->ID());
+ }
+ else
+ {
+ if (!CGUIDialogSelectSavestate::ShowAndGetSavestate(item.GetPath(), savestatePath))
+ return false;
+
+ if (!savestatePath.empty())
+ {
+ RETRO::CSavestateDatabase db;
+ std::unique_ptr<RETRO::ISavestate> save = RETRO::CSavestateDatabase::AllocateSavestate();
+ db.GetSavestate(savestatePath, *save);
+ item.GetGameInfoTag()->SetGameClient(save->GameClientID());
+ }
+ else
+ {
+ // No game client specified, need to ask the user
+ GameClientVector candidates;
+ GameClientVector installable;
+ bool bHasVfsGameClient;
+ GetGameClients(item, candidates, installable, bHasVfsGameClient);
+
+ if (candidates.empty() && installable.empty())
+ {
+ // if: "This game can only be played directly from a hard drive or partition. Compressed files must be extracted."
+ // else: "This game isn't compatible with any available emulators."
+ int errorTextId = bHasVfsGameClient ? 35214 : 35212;
+
+ // "Failed to play game"
+ KODI::MESSAGING::HELPERS::ShowOKDialogText(CVariant{35210}, CVariant{errorTextId});
+ }
+ else if (candidates.size() == 1 && installable.empty())
+ {
+ // Only 1 option, avoid prompting the user
+ item.GetGameInfoTag()->SetGameClient(candidates[0]->ID());
+ }
+ else
+ {
+ std::string gameClient = CGUIDialogSelectGameClient::ShowAndGetGameClient(
+ item.GetPath(), candidates, installable);
+
+ if (!gameClient.empty())
+ item.GetGameInfoTag()->SetGameClient(gameClient);
+ }
+ }
+ }
+ }
+
+ const std::string gameClient = item.GetGameInfoTag()->GetGameClient();
+ if (gameClient.empty())
+ return false;
+
+ if (Install(gameClient))
+ {
+ // If the addon is disabled we need to enable it
+ if (!Enable(gameClient))
+ {
+ CLog::Log(LOGDEBUG, "Failed to enable game client {}", gameClient);
+ item.GetGameInfoTag()->SetGameClient("");
+ }
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, "Failed to install game client: {}", gameClient);
+ item.GetGameInfoTag()->SetGameClient("");
+ }
+
+ return !item.GetGameInfoTag()->GetGameClient().empty();
+}
+
+void CGameUtils::GetGameClients(const CFileItem& file,
+ GameClientVector& candidates,
+ GameClientVector& installable,
+ bool& bHasVfsGameClient)
+{
+ using namespace ADDON;
+
+ bHasVfsGameClient = false;
+
+ // Try to resolve path to a local file, as not all game clients support VFS
+ CURL translatedUrl(CSpecialProtocol::TranslatePath(file.GetPath()));
+
+ // Get local candidates
+ VECADDONS localAddons;
+ CBinaryAddonCache& addonCache = CServiceBroker::GetBinaryAddonCache();
+ addonCache.GetAddons(localAddons, AddonType::GAMEDLL);
+
+ bool bVfs = false;
+ GetGameClients(localAddons, translatedUrl, candidates, bVfs);
+ bHasVfsGameClient |= bVfs;
+
+ // Get remote candidates
+ VECADDONS remoteAddons;
+ if (CServiceBroker::GetAddonMgr().GetInstallableAddons(remoteAddons, AddonType::GAMEDLL))
+ {
+ GetGameClients(remoteAddons, translatedUrl, installable, bVfs);
+ bHasVfsGameClient |= bVfs;
+ }
+
+ // Sort by name
+ //! @todo Move to presentation code
+ auto SortByName = [](const GameClientPtr& lhs, const GameClientPtr& rhs) {
+ std::string lhsName = lhs->Name();
+ std::string rhsName = rhs->Name();
+
+ StringUtils::ToLower(lhsName);
+ StringUtils::ToLower(rhsName);
+
+ return lhsName < rhsName;
+ };
+
+ std::sort(candidates.begin(), candidates.end(), SortByName);
+ std::sort(installable.begin(), installable.end(), SortByName);
+}
+
+void CGameUtils::GetGameClients(const ADDON::VECADDONS& addons,
+ const CURL& translatedUrl,
+ GameClientVector& candidates,
+ bool& bHasVfsGameClient)
+{
+ bHasVfsGameClient = false;
+
+ const std::string extension = URIUtils::GetExtension(translatedUrl.Get());
+
+ const bool bIsLocalFile =
+ (translatedUrl.GetProtocol() == "file" || translatedUrl.GetProtocol().empty());
+
+ for (auto& addon : addons)
+ {
+ GameClientPtr gameClient = std::static_pointer_cast<CGameClient>(addon);
+
+ // Filter by extension
+ if (!gameClient->IsExtensionValid(extension))
+ continue;
+
+ // Filter by VFS
+ if (!bIsLocalFile && !gameClient->SupportsVFS())
+ {
+ bHasVfsGameClient = true;
+ continue;
+ }
+
+ candidates.push_back(gameClient);
+ }
+}
+
+bool CGameUtils::HasGameExtension(const std::string& path)
+{
+ using namespace ADDON;
+
+ // Get filename from CURL so that top-level zip directories will become
+ // normal paths:
+ //
+ // zip://%2Fpath_to_zip_file.zip/ -> /path_to_zip_file.zip
+ //
+ std::string filename = CURL(path).GetFileNameWithoutPath();
+
+ // Get the file extension
+ std::string extension = URIUtils::GetExtension(filename);
+ if (extension.empty())
+ return false;
+
+ StringUtils::ToLower(extension);
+
+ // Look for a game client that supports this extension
+ VECADDONS gameClients;
+ CBinaryAddonCache& addonCache = CServiceBroker::GetBinaryAddonCache();
+ addonCache.GetInstalledAddons(gameClients, AddonType::GAMEDLL);
+ for (auto& gameClient : gameClients)
+ {
+ GameClientPtr gc(std::static_pointer_cast<CGameClient>(gameClient));
+ if (gc->IsExtensionValid(extension))
+ return true;
+ }
+
+ // Check remote add-ons
+ gameClients.clear();
+ if (CServiceBroker::GetAddonMgr().GetInstallableAddons(gameClients, AddonType::GAMEDLL))
+ {
+ for (auto& gameClient : gameClients)
+ {
+ GameClientPtr gc(std::static_pointer_cast<CGameClient>(gameClient));
+ if (gc->IsExtensionValid(extension))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+std::set<std::string> CGameUtils::GetGameExtensions()
+{
+ using namespace ADDON;
+
+ std::set<std::string> extensions;
+
+ VECADDONS gameClients;
+ CBinaryAddonCache& addonCache = CServiceBroker::GetBinaryAddonCache();
+ addonCache.GetAddons(gameClients, AddonType::GAMEDLL);
+ for (auto& gameClient : gameClients)
+ {
+ GameClientPtr gc(std::static_pointer_cast<CGameClient>(gameClient));
+ extensions.insert(gc->GetExtensions().begin(), gc->GetExtensions().end());
+ }
+
+ // Check remote add-ons
+ gameClients.clear();
+ if (CServiceBroker::GetAddonMgr().GetInstallableAddons(gameClients, AddonType::GAMEDLL))
+ {
+ for (auto& gameClient : gameClients)
+ {
+ GameClientPtr gc(std::static_pointer_cast<CGameClient>(gameClient));
+ extensions.insert(gc->GetExtensions().begin(), gc->GetExtensions().end());
+ }
+ }
+
+ return extensions;
+}
+
+bool CGameUtils::IsStandaloneGame(const ADDON::AddonPtr& addon)
+{
+ using namespace ADDON;
+
+ switch (addon->Type())
+ {
+ case AddonType::GAMEDLL:
+ {
+ return std::static_pointer_cast<GAME::CGameClient>(addon)->SupportsStandalone();
+ }
+ case AddonType::SCRIPT:
+ {
+ return addon->HasType(AddonType::GAME);
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool CGameUtils::Install(const std::string& gameClient)
+{
+ // If the addon isn't installed we need to install it
+ bool installed = CServiceBroker::GetAddonMgr().IsAddonInstalled(gameClient);
+ if (!installed)
+ {
+ ADDON::AddonPtr installedAddon;
+ installed = ADDON::CAddonInstaller::GetInstance().InstallModal(
+ gameClient, installedAddon, ADDON::InstallModalPrompt::CHOICE_NO);
+ if (!installed)
+ {
+ CLog::Log(LOGERROR, "Game utils: Failed to install {}", gameClient);
+ // "Error"
+ // "Failed to install add-on."
+ MESSAGING::HELPERS::ShowOKDialogText(CVariant{257}, CVariant{35256});
+ }
+ }
+
+ return installed;
+}
+
+bool CGameUtils::Enable(const std::string& gameClient)
+{
+ bool bSuccess = true;
+
+ if (CServiceBroker::GetAddonMgr().IsAddonDisabled(gameClient))
+ bSuccess = CServiceBroker::GetAddonMgr().EnableAddon(gameClient);
+
+ return bSuccess;
+}