/* * 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 "GameClientProperties.h" #include "FileItem.h" #include "GameClient.h" #include "ServiceBroker.h" #include "addons/AddonManager.h" #include "addons/GameResource.h" #include "addons/IAddon.h" #include "addons/addoninfo/AddonInfo.h" #include "addons/addoninfo/AddonType.h" #include "dialogs/GUIDialogYesNo.h" #include "filesystem/Directory.h" #include "filesystem/SpecialProtocol.h" #include "guilib/LocalizeStrings.h" #include "messaging/helpers/DialogOKHelper.h" #include "utils/StringUtils.h" #include "utils/Variant.h" #include "utils/log.h" #include using namespace KODI; using namespace ADDON; using namespace GAME; using namespace XFILE; #define GAME_CLIENT_RESOURCES_DIRECTORY "resources" CGameClientProperties::CGameClientProperties(const CGameClient& parent, AddonProps_Game& props) : m_parent(parent), m_properties(props) { } void CGameClientProperties::ReleaseResources(void) { for (auto& it : m_proxyDllPaths) delete[] it; m_proxyDllPaths.clear(); for (auto& it : m_resourceDirectories) delete[] it; m_resourceDirectories.clear(); for (auto& it : m_extensions) delete[] it; m_extensions.clear(); } bool CGameClientProperties::InitializeProperties(void) { ReleaseResources(); ADDON::VECADDONS addons; if (!GetProxyAddons(addons)) return false; m_properties.game_client_dll_path = GetLibraryPath(); m_properties.proxy_dll_paths = GetProxyDllPaths(addons); m_properties.proxy_dll_count = GetProxyDllCount(); m_properties.resource_directories = GetResourceDirectories(); m_properties.resource_directory_count = GetResourceDirectoryCount(); m_properties.profile_directory = GetProfileDirectory(); m_properties.supports_vfs = m_parent.SupportsVFS(); m_properties.extensions = GetExtensions(); m_properties.extension_count = GetExtensionCount(); return true; } const char* CGameClientProperties::GetLibraryPath(void) { if (m_strLibraryPath.empty()) { // Get the parent add-on's real path std::string strLibPath = m_parent.CAddonDll::LibPath(); m_strLibraryPath = CSpecialProtocol::TranslatePath(strLibPath); URIUtils::RemoveSlashAtEnd(m_strLibraryPath); } return m_strLibraryPath.c_str(); } const char** CGameClientProperties::GetProxyDllPaths(const ADDON::VECADDONS& addons) { if (m_proxyDllPaths.empty()) { for (const auto& addon : addons) AddProxyDll(std::static_pointer_cast(addon)); } if (!m_proxyDllPaths.empty()) return const_cast(m_proxyDllPaths.data()); return nullptr; } unsigned int CGameClientProperties::GetProxyDllCount(void) const { return static_cast(m_proxyDllPaths.size()); } const char** CGameClientProperties::GetResourceDirectories(void) { if (m_resourceDirectories.empty()) { // Add all other game resources const auto& dependencies = m_parent.GetDependencies(); for (auto it = dependencies.begin(); it != dependencies.end(); ++it) { const std::string& strAddonId = it->id; AddonPtr addon; if (CServiceBroker::GetAddonMgr().GetAddon(strAddonId, addon, AddonType::RESOURCE_GAMES, OnlyEnabled::CHOICE_YES)) { std::shared_ptr resource = std::static_pointer_cast(addon); std::string resourcePath = resource->GetFullPath(""); URIUtils::RemoveSlashAtEnd(resourcePath); char* resourceDir = new char[resourcePath.length() + 1]; std::strcpy(resourceDir, resourcePath.c_str()); m_resourceDirectories.push_back(resourceDir); } } // Add resource directories for profile and path std::string addonProfile = CSpecialProtocol::TranslatePath(m_parent.Profile()); std::string addonPath = m_parent.Path(); addonProfile = URIUtils::AddFileToFolder(addonProfile, GAME_CLIENT_RESOURCES_DIRECTORY); addonPath = URIUtils::AddFileToFolder(addonPath, GAME_CLIENT_RESOURCES_DIRECTORY); if (!CDirectory::Exists(addonProfile)) { CLog::Log(LOGDEBUG, "Creating resource directory: {}", addonProfile); CDirectory::Create(addonProfile); } // Only add user profile directory if non-empty CFileItemList items; if (CDirectory::GetDirectory(addonProfile, items, "", DIR_FLAG_DEFAULTS)) { if (!items.IsEmpty()) { char* addonProfileDir = new char[addonProfile.length() + 1]; std::strcpy(addonProfileDir, addonProfile.c_str()); m_resourceDirectories.push_back(addonProfileDir); } } char* addonPathDir = new char[addonPath.length() + 1]; std::strcpy(addonPathDir, addonPath.c_str()); m_resourceDirectories.push_back(addonPathDir); } if (!m_resourceDirectories.empty()) return const_cast(m_resourceDirectories.data()); return nullptr; } unsigned int CGameClientProperties::GetResourceDirectoryCount(void) const { return static_cast(m_resourceDirectories.size()); } const char* CGameClientProperties::GetProfileDirectory(void) { if (m_strProfileDirectory.empty()) { m_strProfileDirectory = CSpecialProtocol::TranslatePath(m_parent.Profile()); URIUtils::RemoveSlashAtEnd(m_strProfileDirectory); } return m_strProfileDirectory.c_str(); } const char** CGameClientProperties::GetExtensions(void) { for (auto& extension : m_parent.GetExtensions()) { char* ext = new char[extension.length() + 1]; std::strcpy(ext, extension.c_str()); m_extensions.push_back(ext); } return !m_extensions.empty() ? const_cast(m_extensions.data()) : nullptr; } unsigned int CGameClientProperties::GetExtensionCount(void) const { return static_cast(m_extensions.size()); } bool CGameClientProperties::GetProxyAddons(ADDON::VECADDONS& addons) { ADDON::VECADDONS ret; std::vector missingDependencies; // ID or name of missing dependencies for (const auto& dependency : m_parent.GetDependencies()) { AddonPtr addon; if (CServiceBroker::GetAddonMgr().GetAddon(dependency.id, addon, OnlyEnabled::CHOICE_NO)) { // If add-on is disabled, ask the user to enable it if (CServiceBroker::GetAddonMgr().IsAddonDisabled(dependency.id)) { // "Failed to play game" // "This game depends on a disabled add-on. Would you like to enable it?" if (CGUIDialogYesNo::ShowAndGetInput(CVariant{35210}, CVariant{35215})) { if (!CServiceBroker::GetAddonMgr().EnableAddon(dependency.id)) { CLog::Log(LOGERROR, "Failed to enable add-on {}", dependency.id); missingDependencies.emplace_back(addon->Name()); addon.reset(); } } else { CLog::Log(LOGERROR, "User chose to not enable add-on {}", dependency.id); missingDependencies.emplace_back(addon->Name()); addon.reset(); } } if (addon && addon->Type() == AddonType::GAMEDLL) ret.emplace_back(std::move(addon)); } else { if (dependency.optional) { CLog::Log(LOGDEBUG, "Missing optional dependency {}", dependency.id); } else { CLog::Log(LOGERROR, "Missing mandatory dependency {}", dependency.id); missingDependencies.emplace_back(dependency.id); } } } if (!missingDependencies.empty()) { std::string strDependencies = StringUtils::Join(missingDependencies, ", "); std::string dialogText = StringUtils::Format(g_localizeStrings.Get(35223), strDependencies); // "Failed to play game" // "Add-on is incompatible due to unmet dependencies." // "" // "Missing: {0:s}" MESSAGING::HELPERS::ShowOKDialogLines(CVariant{35210}, CVariant{24104}, CVariant{""}, CVariant{dialogText}); return false; } addons = std::move(ret); return true; } void CGameClientProperties::AddProxyDll(const GameClientPtr& gameClient) { // Get the add-on's real path std::string strLibPath = gameClient->CAddon::LibPath(); // Ignore add-on if it is already added if (!HasProxyDll(strLibPath)) { char* libPath = new char[strLibPath.length() + 1]; std::strcpy(libPath, strLibPath.c_str()); m_proxyDllPaths.push_back(libPath); } } bool CGameClientProperties::HasProxyDll(const std::string& strLibPath) const { for (const auto& it : m_proxyDllPaths) { if (strLibPath == it) return true; } return false; }