[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / settings / MediaSourceSettings.cpp
blobb587e1c8cdfdadffbebb2bbb5a426b0322199892
1 /*
2 * Copyright (C) 2013-2020 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "MediaSourceSettings.h"
11 #include "ServiceBroker.h"
12 #include "URL.h"
13 #include "Util.h"
14 #include "media/MediaLockState.h"
15 #include "network/WakeOnAccess.h"
16 #include "profiles/ProfileManager.h"
17 #include "settings/SettingsComponent.h"
18 #include "utils/FileUtils.h"
19 #include "utils/StringUtils.h"
20 #include "utils/URIUtils.h"
21 #include "utils/XBMCTinyXML2.h"
22 #include "utils/XMLUtils.h"
23 #include "utils/log.h"
25 #include <cstdlib>
26 #include <cstring>
28 #define SOURCES_FILE "sources.xml"
29 #define XML_SOURCES "sources"
30 #define XML_SOURCE "source"
32 CMediaSourceSettings::CMediaSourceSettings()
34 Clear();
37 CMediaSourceSettings::~CMediaSourceSettings() = default;
39 CMediaSourceSettings& CMediaSourceSettings::GetInstance()
41 static CMediaSourceSettings sMediaSourceSettings;
42 return sMediaSourceSettings;
45 std::string CMediaSourceSettings::GetSourcesFile()
47 const std::shared_ptr<CProfileManager> profileManager =
48 CServiceBroker::GetSettingsComponent()->GetProfileManager();
50 std::string file;
51 if (profileManager->GetCurrentProfile().hasSources())
52 file = profileManager->GetProfileUserDataFolder();
53 else
54 file = profileManager->GetUserDataFolder();
56 return URIUtils::AddFileToFolder(file, SOURCES_FILE);
59 void CMediaSourceSettings::OnSettingsLoaded()
61 Load();
64 void CMediaSourceSettings::OnSettingsUnloaded()
66 Clear();
69 bool CMediaSourceSettings::Load()
71 return Load(GetSourcesFile());
74 bool CMediaSourceSettings::Load(const std::string& file)
76 Clear();
78 if (!CFileUtils::Exists(file))
79 return false;
81 CLog::Log(LOGINFO, "CMediaSourceSettings: loading media sources from {}", file);
83 // load xml file
84 CXBMCTinyXML2 xmlDoc;
85 if (!xmlDoc.LoadFile(file))
87 CLog::Log(LOGERROR, "CMediaSourceSettings: error loading {}: Line {}, {}", file,
88 xmlDoc.ErrorLineNum(), xmlDoc.ErrorStr());
89 return false;
92 auto* rootElement = xmlDoc.RootElement();
93 if (!rootElement || !StringUtils::EqualsNoCase(rootElement->Value(), XML_SOURCES))
94 CLog::Log(LOGERROR, "CMediaSourceSettings: sources.xml file does not contain <sources>");
96 // parse sources
97 std::string dummy;
98 GetSources(rootElement, "video", m_videoSources, dummy);
99 GetSources(rootElement, "programs", m_programSources, m_defaultProgramSource);
100 GetSources(rootElement, "pictures", m_pictureSources, m_defaultPictureSource);
101 GetSources(rootElement, "files", m_fileSources, m_defaultFileSource);
102 GetSources(rootElement, "music", m_musicSources, m_defaultMusicSource);
103 GetSources(rootElement, "games", m_gameSources, dummy);
105 return true;
108 bool CMediaSourceSettings::Save()
110 return Save(GetSourcesFile());
113 bool CMediaSourceSettings::Save(const std::string& file) const
115 CXBMCTinyXML2 doc;
116 auto* element = doc.NewElement(XML_SOURCES);
117 auto* rootNode = doc.InsertFirstChild(element);
119 if (!rootNode)
120 return false;
122 // ok, now run through and save each sources section
123 SetSources(rootNode, "programs", m_programSources, m_defaultProgramSource);
124 SetSources(rootNode, "video", m_videoSources, "");
125 SetSources(rootNode, "music", m_musicSources, m_defaultMusicSource);
126 SetSources(rootNode, "pictures", m_pictureSources, m_defaultPictureSource);
127 SetSources(rootNode, "files", m_fileSources, m_defaultFileSource);
128 SetSources(rootNode, "games", m_gameSources, "");
130 CWakeOnAccess::GetInstance().QueueMACDiscoveryForAllRemotes();
132 return doc.SaveFile(file);
135 void CMediaSourceSettings::Clear()
137 m_programSources.clear();
138 m_pictureSources.clear();
139 m_fileSources.clear();
140 m_musicSources.clear();
141 m_videoSources.clear();
142 m_gameSources.clear();
145 VECSOURCES* CMediaSourceSettings::GetSources(const std::string& type)
147 if (type == "programs" || type == "myprograms")
148 return &m_programSources;
149 else if (type == "files")
150 return &m_fileSources;
151 else if (type == "music")
152 return &m_musicSources;
153 else if (type == "video" || type == "videos")
154 return &m_videoSources;
155 else if (type == "pictures")
156 return &m_pictureSources;
157 else if (type == "games")
158 return &m_gameSources;
160 return NULL;
163 const std::string& CMediaSourceSettings::GetDefaultSource(const std::string& type) const
165 if (type == "programs" || type == "myprograms")
166 return m_defaultProgramSource;
167 else if (type == "files")
168 return m_defaultFileSource;
169 else if (type == "music")
170 return m_defaultMusicSource;
171 else if (type == "pictures")
172 return m_defaultPictureSource;
174 return StringUtils::Empty;
177 void CMediaSourceSettings::SetDefaultSource(const std::string& type, const std::string& source)
179 if (type == "programs" || type == "myprograms")
180 m_defaultProgramSource = source;
181 else if (type == "files")
182 m_defaultFileSource = source;
183 else if (type == "music")
184 m_defaultMusicSource = source;
185 else if (type == "pictures")
186 m_defaultPictureSource = source;
189 // NOTE: This function does NOT save the sources.xml file - you need to call SaveSources() separately.
190 bool CMediaSourceSettings::UpdateSource(const std::string& strType,
191 const std::string& strOldName,
192 const std::string& strUpdateChild,
193 const std::string& strUpdateValue)
195 VECSOURCES* pShares = GetSources(strType);
196 if (pShares == NULL)
197 return false;
199 for (IVECSOURCES it = pShares->begin(); it != pShares->end(); ++it)
201 if (it->strName == strOldName)
203 if (strUpdateChild == "name")
204 it->strName = strUpdateValue;
205 else if (strUpdateChild == "lockmode")
206 it->m_iLockMode = (LockType)std::strtol(strUpdateValue.c_str(), NULL, 10);
207 else if (strUpdateChild == "lockcode")
208 it->m_strLockCode = strUpdateValue;
209 else if (strUpdateChild == "badpwdcount")
210 it->m_iBadPwdCount = (int)std::strtol(strUpdateValue.c_str(), NULL, 10);
211 else if (strUpdateChild == "thumbnail")
212 it->m_strThumbnailImage = strUpdateValue;
213 else if (strUpdateChild == "path")
215 it->vecPaths.clear();
216 it->strPath = strUpdateValue;
217 it->vecPaths.push_back(strUpdateValue);
219 else
220 return false;
222 return true;
226 return false;
229 bool CMediaSourceSettings::DeleteSource(const std::string& strType,
230 const std::string& strName,
231 const std::string& strPath,
232 bool virtualSource /* = false */)
234 VECSOURCES* pShares = GetSources(strType);
235 if (pShares == NULL)
236 return false;
238 bool found = false;
240 for (IVECSOURCES it = pShares->begin(); it != pShares->end(); ++it)
242 if (it->strName == strName && it->strPath == strPath)
244 CLog::Log(LOGDEBUG, "CMediaSourceSettings: found share, removing!");
245 pShares->erase(it);
246 found = true;
247 break;
251 if (virtualSource)
252 return found;
254 return Save();
257 bool CMediaSourceSettings::AddShare(const std::string& type, const CMediaSource& share)
259 VECSOURCES* pShares = GetSources(type);
260 if (pShares == NULL)
261 return false;
263 // translate dir and add to our current shares
264 std::string strPath1 = share.strPath;
265 if (strPath1.empty())
267 CLog::Log(LOGERROR, "CMediaSourceSettings: unable to add empty path");
268 return false;
270 StringUtils::ToUpper(strPath1);
272 CMediaSource shareToAdd = share;
273 if (strPath1.at(0) == '$')
275 shareToAdd.strPath = CUtil::TranslateSpecialSource(strPath1);
276 if (!share.strPath.empty())
277 CLog::Log(LOGDEBUG, "CMediaSourceSettings: translated ({}) to path ({})", strPath1,
278 shareToAdd.strPath);
279 else
281 CLog::Log(LOGDEBUG, "CMediaSourceSettings: skipping invalid special directory token ({})",
282 strPath1);
283 return false;
286 pShares->push_back(shareToAdd);
288 if (!share.m_ignore)
289 return Save();
291 return true;
294 bool CMediaSourceSettings::UpdateShare(const std::string& type,
295 const std::string& oldName,
296 const CMediaSource& share)
298 VECSOURCES* pShares = GetSources(type);
299 if (pShares == NULL)
300 return false;
302 // update our current share list
303 CMediaSource* pShare = NULL;
304 for (IVECSOURCES it = pShares->begin(); it != pShares->end(); ++it)
306 if (it->strName == oldName)
308 it->strName = share.strName;
309 it->strPath = share.strPath;
310 it->vecPaths = share.vecPaths;
311 pShare = &(*it);
312 break;
316 if (pShare == NULL)
317 return false;
319 // Update our XML file as well
320 return Save();
323 bool CMediaSourceSettings::GetSource(const std::string& category,
324 const tinyxml2::XMLNode* source,
325 CMediaSource& share)
327 const auto* nodeName = source->FirstChildElement("name");
328 std::string name;
329 if (nodeName && nodeName->FirstChild())
330 name = nodeName->FirstChild()->Value();
332 if (name.empty())
333 return false;
335 // get multiple paths
336 std::vector<std::string> vecPaths;
337 const auto* pathName = source->FirstChildElement("path");
338 while (pathName)
340 if (pathName->FirstChild())
342 std::string path = pathName->FirstChild()->Value();
344 // make sure there are no virtualpaths or stack paths defined in sources.xml
345 if (!URIUtils::IsStack(path))
347 // translate special tags
348 if (!path.empty() && path.at(0) == '$')
349 path = CUtil::TranslateSpecialSource(path);
351 // need to check path validity again as CUtil::TranslateSpecialSource() may have failed
352 if (!path.empty())
354 URIUtils::AddSlashAtEnd(path);
355 vecPaths.push_back(path);
358 else
359 CLog::Log(LOGERROR, "CMediaSourceSettings: invalid path type ({}) in source", path);
362 pathName = pathName->NextSiblingElement("path");
365 if (vecPaths.empty())
366 return false;
368 const auto* lockModeElement = source->FirstChildElement("lockmode");
369 const auto* lockCodeElement = source->FirstChildElement("lockcode");
370 const auto* badPwdCountElement = source->FirstChildElement("badpwdcount");
371 const auto* thumbnailNodeElement = source->FirstChildElement("thumbnail");
373 std::vector<std::string> verifiedPaths;
374 // disallowed for files, or there's only a single path in the vector
375 if (StringUtils::EqualsNoCase(category, "files") || vecPaths.size() == 1)
377 verifiedPaths.push_back(vecPaths[0]);
379 else // multiple paths?
381 // validate the paths
382 for (auto path = vecPaths.begin(); path != vecPaths.end(); ++path)
384 CURL url(*path);
385 bool isInvalid = false;
387 // for my programs
388 if (StringUtils::EqualsNoCase(category, "programs") ||
389 StringUtils::EqualsNoCase(category, "myprograms"))
391 // only allow HD and plugins
392 if (url.IsLocal() || url.IsProtocol("plugin"))
393 verifiedPaths.push_back(*path);
394 else
395 isInvalid = true;
397 // for others allow everything (if the user does something silly, we can't stop them)
398 else
399 verifiedPaths.push_back(*path);
401 // error message
402 if (isInvalid)
403 CLog::Log(LOGERROR, "CMediaSourceSettings: invalid path type ({}) for multipath source",
404 *path);
407 // no valid paths? skip to next source
408 if (verifiedPaths.empty())
410 CLog::Log(LOGERROR,
411 "CMediaSourceSettings: missing or invalid <name> and/or <path> in source");
412 return false;
416 share.FromNameAndPaths(category, name, verifiedPaths);
418 share.m_iBadPwdCount = 0;
419 if (lockModeElement)
421 share.m_iLockMode =
422 static_cast<LockType>(std::strtol(lockModeElement->FirstChild()->Value(), nullptr, 10));
423 share.m_iHasLock = LOCK_STATE_LOCKED;
426 if (lockCodeElement && lockCodeElement->FirstChild())
427 share.m_strLockCode = lockCodeElement->FirstChild()->Value();
429 if (badPwdCountElement && badPwdCountElement->FirstChild())
431 share.m_iBadPwdCount =
432 static_cast<int>(std::strtol(badPwdCountElement->FirstChild()->Value(), nullptr, 10));
435 if (thumbnailNodeElement && thumbnailNodeElement->FirstChild())
436 share.m_strThumbnailImage = thumbnailNodeElement->FirstChild()->Value();
438 XMLUtils::GetBoolean(source, "allowsharing", share.m_allowSharing);
440 return true;
443 void CMediaSourceSettings::GetSources(const tinyxml2::XMLNode* rootElement,
444 const std::string& tagName,
445 VECSOURCES& items,
446 std::string& defaultString)
449 defaultString = "";
450 items.clear();
452 const auto* childElement = rootElement->FirstChildElement(tagName.c_str());
453 if (!childElement)
455 CLog::Log(LOGDEBUG, "CMediaSourceSettings: <{}> tag is missing or sources.xml is malformed",
456 tagName);
457 return;
460 auto child = childElement->FirstChild();
461 while (child)
463 std::string value = child->Value();
464 if (value == XML_SOURCE ||
465 value == "bookmark") // "bookmark" left in for backwards compatibility
467 CMediaSource share;
468 if (GetSource(tagName, child, share))
469 items.push_back(share);
470 else
471 CLog::Log(LOGERROR,
472 "CMediaSourceSettings: Missing or invalid <name> and/or <path> in source");
474 else if (value == "default")
476 const auto* valueNode = child->FirstChild();
477 if (valueNode)
479 const char* text = child->FirstChild()->Value();
480 if (strcmp(text, "\0") != 0)
481 defaultString = text;
482 CLog::Log(LOGDEBUG, "CMediaSourceSettings: Setting <default> source to : {}",
483 defaultString);
487 child = child->NextSibling();
491 bool CMediaSourceSettings::SetSources(tinyxml2::XMLNode* root,
492 const char* section,
493 const VECSOURCES& shares,
494 const std::string& defaultPath) const
496 auto* doc = root->GetDocument();
497 auto* newElement = doc->NewElement(section);
498 auto* sectionNode = root->InsertEndChild(newElement);
500 if (!sectionNode)
501 return false;
503 XMLUtils::SetPath(sectionNode, "default", defaultPath);
504 for (CIVECSOURCES it = shares.begin(); it != shares.end(); ++it)
506 const CMediaSource& share = *it;
507 if (share.m_ignore)
508 continue;
510 auto* sourceElement = doc->NewElement(XML_SOURCE);
512 XMLUtils::SetString(sourceElement, "name", share.strName);
514 for (unsigned int i = 0; i < share.vecPaths.size(); i++)
515 XMLUtils::SetPath(sourceElement, "path", share.vecPaths[i]);
517 if (share.m_iHasLock)
519 XMLUtils::SetInt(sourceElement, "lockmode", share.m_iLockMode);
520 XMLUtils::SetString(sourceElement, "lockcode", share.m_strLockCode);
521 XMLUtils::SetInt(sourceElement, "badpwdcount", share.m_iBadPwdCount);
524 if (!share.m_strThumbnailImage.empty())
525 XMLUtils::SetPath(sourceElement, "thumbnail", share.m_strThumbnailImage);
527 XMLUtils::SetBoolean(sourceElement, "allowsharing", share.m_allowSharing);
529 sectionNode->InsertEndChild(sourceElement);
532 return true;