2 * Copyright (C) 2005-2008 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
21 #include "threads/SystemClock.h"
24 #include <sys/param.h>
25 #include <mach-o/dyld.h>
28 #if defined(__FreeBSD__)
29 #include <sys/param.h>
30 #include <sys/sysctl.h>
34 #include <sys/types.h>
41 #include "Application.h"
42 #include "utils/AutoPtrHandle.h"
44 #include "addons/Addon.h"
45 #include "storage/IoSupport.h"
46 #include "filesystem/StackDirectory.h"
47 #include "filesystem/MultiPathDirectory.h"
48 #include "filesystem/DirectoryCache.h"
49 #include "filesystem/SpecialProtocol.h"
50 #include "filesystem/RSSDirectory.h"
51 #include "ThumbnailCache.h"
52 #ifdef HAS_FILESYSTEM_RAR
53 #include "filesystem/RarManager.h"
55 #include "filesystem/MythDirectory.h"
57 #include "filesystem/UPnPDirectory.h"
59 #ifdef HAS_VIDEO_PLAYBACK
60 #include "cores/VideoRenderers/RenderManager.h"
62 #include "utils/RegExp.h"
63 #include "settings/GUISettings.h"
64 #include "guilib/TextureManager.h"
65 #include "utils/fstrcmp.h"
66 #include "storage/MediaManager.h"
67 #include "guilib/DirectXGraphics.h"
68 #include "network/DNSNameCache.h"
69 #include "guilib/GUIWindowManager.h"
72 #include "WIN32Util.h"
74 #if defined(__APPLE__)
75 #include "osx/DarwinUtils.h"
77 #include "GUIUserMessages.h"
78 #include "filesystem/File.h"
79 #include "utils/Crc32.h"
80 #include "settings/Settings.h"
81 #include "utils/StringUtils.h"
82 #include "settings/AdvancedSettings.h"
83 #ifdef HAS_IRSERVERSUITE
84 #include "input/windows/IRServerSuite.h"
86 #include "guilib/LocalizeStrings.h"
87 #include "utils/md5.h"
88 #include "utils/TimeUtils.h"
89 #include "utils/URIUtils.h"
90 #include "utils/log.h"
91 #include "pictures/Picture.h"
92 #include "utils/JobManager.h"
93 #include "cores/dvdplayer/DVDSubtitles/DVDSubtitleTagSami.h"
94 #include "cores/dvdplayer/DVDSubtitles/DVDSubtitleStream.h"
95 #include "windowing/WindowingFactory.h"
96 #include "video/VideoInfoTag.h"
99 using namespace XFILE;
101 #define clamp(x) (x) > 255.f ? 255 : ((x) < 0 ? 0 : (BYTE)(x+0.5f)) // Valid ranges: brightness[-1 -> 1 (0 is default)] contrast[0 -> 2 (1 is default)] gamma[0.5 -> 3.5 (1 is default)] default[ramp is linear]
102 static const __int64 SECS_BETWEEN_EPOCHS = 11644473600LL;
103 static const __int64 SECS_TO_100NS = 10000000;
105 using namespace AUTOPTR;
106 using namespace XFILE;
107 using namespace PLAYLIST;
110 static D3DGAMMARAMP oldramp, flashramp;
111 #elif defined(HAS_SDL_2D)
112 static uint16_t oldrampRed[256];
113 static uint16_t oldrampGreen[256];
114 static uint16_t oldrampBlue[256];
115 static uint16_t flashrampRed[256];
116 static uint16_t flashrampGreen[256];
117 static uint16_t flashrampBlue[256];
127 CStdString CUtil::GetTitleFromPath(const CStdString& strFileNameAndPath, bool bIsFolder /* = false */)
129 // use above to get the filename
130 CStdString path(strFileNameAndPath);
131 URIUtils::RemoveSlashAtEnd(path);
132 CStdString strFilename = URIUtils::GetFileName(path);
134 CURL url(strFileNameAndPath);
135 CStdString strHostname = url.GetHostName();
139 if (url.GetProtocol() == "upnp")
140 strFilename = CUPnPDirectory::GetFriendlyName(strFileNameAndPath.c_str());
143 if (url.GetProtocol() == "rss")
147 if(dir.GetDirectory(strFileNameAndPath, items) && !items.m_strTitle.IsEmpty())
148 return items.m_strTitle;
152 if (url.GetProtocol() == "lastfm")
154 if (strFilename.IsEmpty())
155 strFilename = g_localizeStrings.Get(15200);
157 strFilename = g_localizeStrings.Get(15200) + " - " + strFilename;
161 else if (url.GetProtocol() == "shout")
163 const int genre = strFileNameAndPath.find_first_of('=');
165 strFilename = g_localizeStrings.Get(260);
167 strFilename = g_localizeStrings.Get(260) + " - " + strFileNameAndPath.substr(genre+1).c_str();
170 // Windows SMB Network (SMB)
171 else if (url.GetProtocol() == "smb" && strFilename.IsEmpty())
173 if (url.GetHostName().IsEmpty())
175 strFilename = g_localizeStrings.Get(20171);
179 strFilename = url.GetHostName();
182 // iTunes music share (DAAP)
183 else if (url.GetProtocol() == "daap" && strFilename.IsEmpty())
184 strFilename = g_localizeStrings.Get(20174);
187 else if (url.GetProtocol() == "hdhomerun" && strFilename.IsEmpty())
188 strFilename = "HDHomerun Devices";
191 else if (url.GetProtocol() == "sling")
192 strFilename = "Slingbox";
195 else if (url.GetProtocol() == "rtv")
196 strFilename = "ReplayTV Devices";
198 // HTS Tvheadend client
199 else if (url.GetProtocol() == "htsp")
200 strFilename = g_localizeStrings.Get(20256);
202 // VDR Streamdev client
203 else if (url.GetProtocol() == "vtp")
204 strFilename = g_localizeStrings.Get(20257);
207 else if (url.GetProtocol() == "myth")
208 strFilename = g_localizeStrings.Get(20258);
211 else if (url.GetProtocol() == "sap" && strFilename.IsEmpty())
212 strFilename = "SAP Streams";
215 else if (url.GetProtocol() == "sources")
216 strFilename = g_localizeStrings.Get(744);
219 else if (path.Left(24).Equals("special://musicplaylists"))
220 strFilename = g_localizeStrings.Get(136);
223 else if (path.Left(24).Equals("special://videoplaylists"))
224 strFilename = g_localizeStrings.Get(136);
226 else if (URIUtils::ProtocolHasParentInHostname(url.GetProtocol()) && strFilename.IsEmpty())
227 strFilename = URIUtils::GetFileName(url.GetHostName());
229 // now remove the extension if needed
230 if (!g_guiSettings.GetBool("filelists.showextensions") && !bIsFolder)
232 URIUtils::RemoveExtension(strFilename);
236 // URLDecode since the original path may be an URL
237 CURL::Decode(strFilename);
241 bool CUtil::GetVolumeFromFileName(const CStdString& strFileName, CStdString& strFileTitle, CStdString& strVolumeNumber)
243 const CStdStringArray ®exps = g_advancedSettings.m_videoStackRegExps;
245 CStdString strFileNameTemp = strFileName;
249 for (unsigned int i = 0; i < regexps.size(); i++)
251 CStdString strRegExp = regexps[i];
252 if (!reg.RegComp(strRegExp.c_str()))
253 { // invalid regexp - complain in logs
254 CLog::Log(LOGERROR, "Invalid RegExp:[%s]", regexps[i].c_str());
257 // CLog::Log(LOGDEBUG, "Regexp:[%s]", regexps[i].c_str());
259 int iFoundToken = reg.RegFind(strFileName.c_str());
260 if (iFoundToken >= 0)
262 int iRegLength = reg.GetFindLen();
263 int iCount = reg.GetSubCount();
266 reg.DumpOvector(LOGDEBUG);
267 CLog::Log(LOGDEBUG, "Subcount=%i", iCount);
268 for (int j = 0; j <= iCount; j++)
270 CStdString str = reg.GetMatch(j);
271 CLog::Log(LOGDEBUG, "Sub(%i):[%s]", j, str.c_str());
275 // simple regexp, only the volume is captured
278 strVolumeNumber = reg.GetMatch(1);
279 if (strVolumeNumber.IsEmpty()) return false;
281 // Remove the extension (if any). We do this on the base filename, as the regexp
282 // match may include some of the extension (eg the "." in particular).
283 // The extension will then be added back on at the end - there is no reason
284 // to clean it off here. It will be cleaned off during the display routine, if
285 // the settings to hide extensions are turned on.
286 CStdString strFileNoExt = strFileNameTemp;
287 URIUtils::RemoveExtension(strFileNoExt);
288 CStdString strFileExt = strFileNameTemp.Right(strFileNameTemp.length() - strFileNoExt.length());
289 CStdString strFileRight = strFileNoExt.Mid(iFoundToken + iRegLength);
290 strFileTitle = strFileName.Left(iFoundToken) + strFileRight + strFileExt;
295 // advanced regexp with prefix (1), volume (2), and suffix (3)
296 else if (iCount == 3)
298 // second subpatten contains the stacking volume
299 strVolumeNumber = reg.GetMatch(2);
300 if (strVolumeNumber.IsEmpty()) return false;
302 // everything before the regexp match
303 strFileTitle = strFileName.Left(iFoundToken);
305 // first subpattern contains prefix
306 strFileTitle += reg.GetMatch(1);
308 // third subpattern contains suffix
309 strFileTitle += reg.GetMatch(3);
311 // everything after the regexp match
312 strFileTitle += strFileNameTemp.Mid(iFoundToken + iRegLength);
317 // unknown regexp format
320 CLog::Log(LOGERROR, "Incorrect movie stacking regexp format:[%s]", regexps[i].c_str());
327 void CUtil::CleanString(const CStdString& strFileName, CStdString& strTitle, CStdString& strTitleAndYear, CStdString& strYear, bool bRemoveExtension /* = false */, bool bCleanChars /* = true */)
329 strTitleAndYear = strFileName;
331 if (strFileName.Equals(".."))
334 const CStdStringArray ®exps = g_advancedSettings.m_videoCleanStringRegExps;
336 CRegExp reTags(true);
338 CStdString strExtension;
339 URIUtils::GetExtension(strFileName, strExtension);
341 if (!reYear.RegComp(g_advancedSettings.m_videoCleanDateTimeRegExp))
343 CLog::Log(LOGERROR, "%s: Invalid datetime clean RegExp:'%s'", __FUNCTION__, g_advancedSettings.m_videoCleanDateTimeRegExp.c_str());
347 if (reYear.RegFind(strTitleAndYear.c_str()) >= 0)
349 strTitleAndYear = reYear.GetReplaceString("\\1");
350 strYear = reYear.GetReplaceString("\\2");
354 URIUtils::RemoveExtension(strTitleAndYear);
356 for (unsigned int i = 0; i < regexps.size(); i++)
358 if (!reTags.RegComp(regexps[i].c_str()))
359 { // invalid regexp - complain in logs
360 CLog::Log(LOGERROR, "%s: Invalid string clean RegExp:'%s'", __FUNCTION__, regexps[i].c_str());
364 if ((j=reTags.RegFind(strFileName.c_str())) > 0)
365 strTitleAndYear = strTitleAndYear.Mid(0, j);
368 // final cleanup - special characters used instead of spaces:
369 // all '_' tokens should be replaced by spaces
370 // if the file contains no spaces, all '.' tokens should be replaced by
371 // spaces - one possibility of a mistake here could be something like:
372 // "Dr..StrangeLove" - hopefully no one would have anything like this.
375 bool initialDots = true;
376 bool alreadyContainsSpace = (strTitleAndYear.Find(' ') >= 0);
378 for (int i = 0; i < (int)strTitleAndYear.size(); i++)
380 char c = strTitleAndYear.GetAt(i);
385 if ((c == '_') || ((!alreadyContainsSpace) && !initialDots && (c == '.')))
387 strTitleAndYear.SetAt(i, ' ');
392 strTitle = strTitleAndYear.Trim();
395 if (!strYear.IsEmpty())
396 strTitleAndYear = strTitle + " (" + strYear + ")";
398 // restore extension if needed
399 if (!bRemoveExtension)
400 strTitleAndYear += strExtension;
403 void CUtil::GetQualifiedFilename(const CStdString &strBasePath, CStdString &strFilename)
405 // Check if the filename is a fully qualified URL such as protocol://path/to/file
406 CURL plItemUrl(strFilename);
407 if (!plItemUrl.GetProtocol().IsEmpty())
410 // If the filename starts "x:" or "/" it's already fully qualified so return
411 if (strFilename.size() > 1)
413 if ( (strFilename[1] == ':') || (strFilename[0] == '/') )
415 if ( strFilename[1] == ':' )
419 // add to base path and then clean
420 strFilename = URIUtils::AddFileToFolder(strBasePath, strFilename);
422 // get rid of any /./ or \.\ that happen to be there
423 strFilename.Replace("\\.\\", "\\");
424 strFilename.Replace("/./", "/");
426 // now find any "\\..\\" and remove them via GetParentPath
428 while ((pos = strFilename.Find("/../")) > 0)
430 CStdString basePath = strFilename.Left(pos+1);
431 strFilename = strFilename.Mid(pos+4);
432 basePath = URIUtils::GetParentPath(basePath);
433 strFilename = URIUtils::AddFileToFolder(basePath, strFilename);
435 while ((pos = strFilename.Find("\\..\\")) > 0)
437 CStdString basePath = strFilename.Left(pos+1);
438 strFilename = strFilename.Mid(pos+4);
439 basePath = URIUtils::GetParentPath(basePath);
440 strFilename = URIUtils::AddFileToFolder(basePath, strFilename);
445 bool CUtil::TestGetQualifiedFilename()
447 CStdString file = "../foo"; GetQualifiedFilename("smb://", file);
448 if (file != "foo") return false;
449 file = "C:\\foo\\bar"; GetQualifiedFilename("smb://", file);
450 if (file != "C:\\foo\\bar") return false;
451 file = "../foo/./bar"; GetQualifiedFilename("smb://my/path", file);
452 if (file != "smb://my/foo/bar") return false;
453 file = "smb://foo/bar/"; GetQualifiedFilename("upnp://", file);
454 if (file != "smb://foo/bar/") return false;
458 bool CUtil::TestMakeLegalPath()
462 path = "C:\\foo\\bar"; path = MakeLegalPath(path);
463 if (path != "C:\\foo\\bar") return false;
464 path = "C:\\foo:\\bar\\"; path = MakeLegalPath(path);
465 if (path != "C:\\foo_\\bar\\") return false;
467 path = "/foo/bar/"; path = MakeLegalPath(path);
468 if (path != "/foo/bar/") return false;
469 path = "/foo?/bar"; path = MakeLegalPath(path);
470 if (path != "/foo_/bar") return false;
472 path = "smb://foo/bar"; path = MakeLegalPath(path);
473 if (path != "smb://foo/bar") return false;
474 path = "smb://foo/bar?/"; path = MakeLegalPath(path);
475 if (path != "smb://foo/bar_/") return false;
480 void CUtil::RunShortcut(const char* szShortcutPath)
484 void CUtil::GetHomePath(CStdString& strPath, const CStdString& strTarget)
486 CStdString strHomePath;
487 strHomePath = ResolveExecutablePath();
489 CStdStringW strPathW, strTargetW;
490 g_charsetConverter.utf8ToW(strTarget, strTargetW);
491 strPathW = _wgetenv(strTargetW);
492 g_charsetConverter.wToUTF8(strPathW,strPath);
494 strPath = getenv(strTarget);
497 if (strPath != NULL && !strPath.IsEmpty())
501 //expand potential relative path to full path
502 if(GetFullPathName(strPath, 1024, tmp, 0) != 0)
512 char given_path[2*MAXPATHLEN];
513 uint32_t path_size =2*MAXPATHLEN;
515 result = GetDarwinExecutablePath(given_path, &path_size);
518 // Move backwards to last /.
519 for (int n=strlen(given_path)-1; given_path[n] != '/'; n--)
520 given_path[n] = '\0';
523 strcat(given_path, "/XBMCData/XBMCHome/");
525 // Assume local path inside application bundle.
526 strcat(given_path, "../Resources/XBMC/");
529 // Convert to real path.
530 char real_path[2*MAXPATHLEN];
531 if (realpath(given_path, real_path) != NULL)
538 size_t last_sep = strHomePath.find_last_of(PATH_SEPARATOR_CHAR);
539 if (last_sep != string::npos)
540 strPath = strHomePath.Left(last_sep);
542 strPath = strHomePath;
545 #if defined(_LINUX) && !defined(__APPLE__)
546 /* Change strPath accordingly when target is XBMC_HOME and when INSTALL_PATH
547 * and BIN_INSTALL_PATH differ
549 CStdString installPath = INSTALL_PATH;
550 CStdString binInstallPath = BIN_INSTALL_PATH;
551 if (!strTarget.compare("XBMC_HOME") && installPath.compare(binInstallPath))
553 int pos = strPath.length() - binInstallPath.length();
554 CStdString tmp = strPath;
556 if (!tmp.compare(binInstallPath))
558 strPath.erase(pos, strPath.length());
559 strPath.append(installPath);
565 bool CUtil::IsPicture(const CStdString& strFile)
567 CStdString extension = URIUtils::GetExtension(strFile);
569 if (extension.IsEmpty())
573 if (g_settings.m_pictureExtensions.Find(extension) != -1)
576 if (extension == ".tbn" || extension == ".dds")
582 bool CUtil::ExcludeFileOrFolder(const CStdString& strFileOrFolder, const CStdStringArray& regexps)
584 if (strFileOrFolder.IsEmpty())
587 CRegExp regExExcludes(true); // case insensitive regex
589 for (unsigned int i = 0; i < regexps.size(); i++)
591 if (!regExExcludes.RegComp(regexps[i].c_str()))
592 { // invalid regexp - complain in logs
593 CLog::Log(LOGERROR, "%s: Invalid exclude RegExp:'%s'", __FUNCTION__, regexps[i].c_str());
596 if (regExExcludes.RegFind(strFileOrFolder) > -1)
598 CLog::Log(LOGDEBUG, "%s: File '%s' excluded. (Matches exclude rule RegExp:'%s')", __FUNCTION__, strFileOrFolder.c_str(), regexps[i].c_str());
605 void CUtil::GetFileAndProtocol(const CStdString& strURL, CStdString& strDir)
608 if (!URIUtils::IsRemote(strURL)) return ;
609 if (URIUtils::IsDVD(strURL)) return ;
612 strDir.Format("%s://%s", url.GetProtocol().c_str(), url.GetFileName().c_str());
615 int CUtil::GetDVDIfoTitle(const CStdString& strFile)
617 CStdString strFilename = URIUtils::GetFileName(strFile);
618 if (strFilename.Equals("video_ts.ifo")) return 0;
620 return atoi(strFilename.Mid(4, 2).c_str());
623 CStdString CUtil::GetFileMD5(const CStdString& strPath)
627 if (file.Open(strPath))
635 read = file.Read(temp,1024);
637 md5.append(temp,read);
639 md5.getDigest(result);
646 bool CUtil::GetDirectoryName(const CStdString& strFileName, CStdString& strDescription)
648 CStdString strFName = URIUtils::GetFileName(strFileName);
649 strDescription = strFileName.Left(strFileName.size() - strFName.size());
650 URIUtils::RemoveSlashAtEnd(strDescription);
652 int iPos = strDescription.ReverseFind("\\");
654 iPos = strDescription.ReverseFind("/");
657 CStdString strTmp = strDescription.Right(strDescription.size()-iPos-1);
658 strDescription = strTmp;//strDescription.Right(strDescription.size() - iPos - 1);
660 else if (strDescription.size() <= 0)
661 strDescription = strFName;
665 void CUtil::GetDVDDriveIcon( const CStdString& strPath, CStdString& strIcon )
667 if ( !g_mediaManager.IsDiscInDrive() )
669 strIcon = "DefaultDVDEmpty.png";
673 if ( URIUtils::IsDVD(strPath) )
676 CCdInfo* pInfo = g_mediaManager.GetCdInfo();
678 if ( pInfo != NULL && pInfo->IsUDFX( 1 ) )
680 strIcon = "DefaultXboxDVD.png";
684 strIcon = "DefaultDVDRom.png";
688 if ( URIUtils::IsISO9660(strPath) )
691 CCdInfo* pInfo = g_mediaManager.GetCdInfo();
692 if ( pInfo != NULL && pInfo->IsVideoCd( 1 ) )
694 strIcon = "DefaultVCD.png";
698 strIcon = "DefaultDVDRom.png";
702 if ( URIUtils::IsCDDA(strPath) )
704 strIcon = "DefaultCDDA.png";
709 void CUtil::RemoveTempFiles()
711 CStdString searchPath = g_settings.GetDatabaseFolder();
713 if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".tmp", false))
716 for (int i = 0; i < items.Size(); ++i)
718 if (items[i]->m_bIsFolder)
720 XFILE::CFile::Delete(items[i]->GetPath());
724 void CUtil::ClearSubtitles()
728 CDirectory::GetDirectory("special://temp/",items);
729 for( int i=0;i<items.Size();++i)
731 if (!items[i]->m_bIsFolder)
733 if ( items[i]->GetPath().Find("subtitle") >= 0 || items[i]->GetPath().Find("vobsub_queue") >= 0 )
735 CLog::Log(LOGDEBUG, "%s - Deleting temporary subtitle %s", __FUNCTION__, items[i]->GetPath().c_str());
736 CFile::Delete(items[i]->GetPath());
742 static const char * sub_exts[] = { ".utf", ".utf8", ".utf-8", ".sub", ".srt", ".smi", ".rt", ".txt", ".ssa", ".aqt", ".jss", ".ass", ".idx", NULL};
744 int64_t CUtil::ToInt64(uint32_t high, uint32_t low)
753 bool CUtil::ThumbExists(const CStdString& strFileName, bool bAddCache)
755 return CThumbnailCache::GetThumbnailCache()->ThumbExists(strFileName, bAddCache);
758 void CUtil::ThumbCacheAdd(const CStdString& strFileName, bool bFileExists)
760 CThumbnailCache::GetThumbnailCache()->Add(strFileName, bFileExists);
763 void CUtil::ThumbCacheClear()
765 CThumbnailCache::GetThumbnailCache()->Clear();
768 bool CUtil::ThumbCached(const CStdString& strFileName)
770 return CThumbnailCache::GetThumbnailCache()->IsCached(strFileName);
773 void CUtil::PlayDVD(const CStdString& strProtocol, bool restart)
775 #if defined(HAS_DVDPLAYER) && defined(HAS_DVD_DRIVE)
776 CIoSupport::Dismount("Cdrom0");
777 CIoSupport::RemapDriveLetter('D', "Cdrom0");
779 strPath.Format("%s://1", strProtocol.c_str());
780 CFileItem item(strPath, false);
781 item.SetLabel(g_mediaManager.GetDiskLabel());
782 item.GetVideoInfoTag()->m_strFileNameAndPath = "removable://"; // need to put volume label for resume point in videoInfoTag
783 item.GetVideoInfoTag()->m_strFileNameAndPath += g_mediaManager.GetDiskLabel();
784 if (!restart) item.m_lStartOffset = STARTOFFSET_RESUME;
785 g_application.PlayFile(item, restart);
789 CStdString CUtil::GetNextFilename(const CStdString &fn_template, int max)
791 if (!fn_template.Find("%03d"))
794 CStdString searchPath;
795 URIUtils::GetDirectory(fn_template, searchPath);
796 CStdString mask = URIUtils::GetExtension(fn_template);
799 name.Format(fn_template.c_str(), 0);
802 if (!CDirectory::GetDirectory(searchPath, items, mask, false))
805 items.SetFastLookup(true);
806 for (int i = 0; i <= max; i++)
809 name.Format(fn_template.c_str(), i);
810 if (!items.Get(name))
816 CStdString CUtil::GetNextPathname(const CStdString &path_template, int max)
818 if (!path_template.Find("%04d"))
821 for (int i = 0; i <= max; i++)
824 name.Format(path_template.c_str(), i);
825 if (!CFile::Exists(name))
831 void CUtil::Tokenize(const CStdString& path, vector<CStdString>& tokens, const string& delimiters)
833 // Tokenize ripped from http://www.linuxselfhelp.com/HOWTO/C++Programming-HOWTO-7.html
834 // Skip delimiters at beginning.
835 string::size_type lastPos = path.find_first_not_of(delimiters, 0);
836 // Find first "non-delimiter".
837 string::size_type pos = path.find_first_of(delimiters, lastPos);
839 while (string::npos != pos || string::npos != lastPos)
841 // Found a token, add it to the vector.
842 tokens.push_back(path.substr(lastPos, pos - lastPos));
843 // Skip delimiters. Note the "not_of"
844 lastPos = path.find_first_not_of(delimiters, pos);
845 // Find next "non-delimiter"
846 pos = path.find_first_of(delimiters, lastPos);
850 void CUtil::TakeScreenshot(const CStdString &filename, bool sync)
855 unsigned char* outpixels = NULL;
858 LPDIRECT3DSURFACE9 lpSurface = NULL, lpBackbuffer = NULL;
859 g_graphicsContext.Lock();
860 if (g_application.IsPlayingVideo())
862 #ifdef HAS_VIDEO_PLAYBACK
863 g_renderManager.SetupScreenshot();
866 g_application.RenderNoPresent();
868 if (FAILED(g_Windowing.Get3DDevice()->CreateOffscreenPlainSurface(g_Windowing.GetWidth(), g_Windowing.GetHeight(), D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, &lpSurface, NULL)))
871 if (FAILED(g_Windowing.Get3DDevice()->GetRenderTarget(0, &lpBackbuffer)))
874 // now take screenshot
875 if (SUCCEEDED(g_Windowing.Get3DDevice()->GetRenderTargetData(lpBackbuffer, lpSurface)))
878 D3DSURFACE_DESC desc;
879 lpSurface->GetDesc(&desc);
880 if (SUCCEEDED(lpSurface->LockRect(&lr, NULL, D3DLOCK_READONLY)))
883 height = desc.Height;
885 outpixels = new unsigned char[height * stride];
886 memcpy(outpixels, lr.pBits, height * stride);
887 lpSurface->UnlockRect();
891 CLog::Log(LOGERROR, "%s LockRect failed", __FUNCTION__);
896 CLog::Log(LOGERROR, "%s GetBackBuffer failed", __FUNCTION__);
898 lpSurface->Release();
899 lpBackbuffer->Release();
901 g_graphicsContext.Unlock();
903 #elif defined(HAS_GL) || defined(HAS_GLES)
905 g_graphicsContext.BeginPaint();
906 if (g_application.IsPlayingVideo())
908 #ifdef HAS_VIDEO_PLAYBACK
909 g_renderManager.SetupScreenshot();
912 g_application.RenderNoPresent();
914 glReadBuffer(GL_BACK);
916 //get current viewport
918 glGetIntegerv(GL_VIEWPORT, viewport);
920 width = viewport[2] - viewport[0];
921 height = viewport[3] - viewport[1];
923 unsigned char* pixels = new unsigned char[stride * height];
925 //read pixels from the backbuffer
927 glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)pixels);
929 glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid*)pixels);
931 g_graphicsContext.EndPaint();
933 //make a new buffer and copy the read image to it with the Y axis inverted
934 outpixels = new unsigned char[stride * height];
935 for (int y = 0; y < height; y++)
936 memcpy(outpixels + y * stride, pixels + (height - y - 1) * stride, stride);
941 //nothing to take a screenshot from
947 CLog::Log(LOGERROR, "Screenshot %s failed", filename.c_str());
951 CLog::Log(LOGDEBUG, "Saving screenshot %s", filename.c_str());
953 //set alpha byte to 0xFF
954 for (int y = 0; y < height; y++)
956 unsigned char* alphaptr = outpixels - 1 + y * stride;
957 for (int x = 0; x < width; x++)
958 *(alphaptr += 4) = 0xFF;
961 //if sync is true, the png file needs to be completely written when this function returns
964 if (!CPicture::CreateThumbnailFromSurface(outpixels, width, height, stride, filename))
965 CLog::Log(LOGERROR, "Unable to write screenshot %s", filename.c_str());
971 //make sure the file exists to avoid concurrency issues
972 FILE* fp = fopen(filename.c_str(), "w");
976 CLog::Log(LOGERROR, "Unable to create file %s", filename.c_str());
978 //write .png file asynchronous with CThumbnailWriter, prevents stalling of the render thread
979 //outpixels is deleted from CThumbnailWriter
980 CThumbnailWriter* thumbnailwriter = new CThumbnailWriter(outpixels, width, height, stride, filename);
981 CJobManager::GetInstance().AddJob(thumbnailwriter, NULL);
985 void CUtil::TakeScreenshot()
987 static bool savingScreenshots = false;
988 static vector<CStdString> screenShots;
990 bool promptUser = false;
991 // check to see if we have a screenshot folder yet
992 CStdString strDir = g_guiSettings.GetString("debug.screenshotpath", false);
993 if (strDir.IsEmpty())
995 strDir = "special://temp/";
996 if (!savingScreenshots)
999 savingScreenshots = true;
1000 screenShots.clear();
1003 URIUtils::RemoveSlashAtEnd(strDir);
1005 if (!strDir.IsEmpty())
1007 CStdString file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(strDir, "screenshot%03d.png"), 999);
1009 if (!file.IsEmpty())
1011 TakeScreenshot(file, false);
1012 if (savingScreenshots)
1013 screenShots.push_back(file);
1015 { // grab the real directory
1016 CStdString newDir = g_guiSettings.GetString("debug.screenshotpath");
1017 if (!newDir.IsEmpty())
1019 for (unsigned int i = 0; i < screenShots.size(); i++)
1021 CStdString file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(newDir, "screenshot%03d.png"), 999);
1022 CFile::Cache(screenShots[i], file);
1024 screenShots.clear();
1026 savingScreenshots = false;
1031 CLog::Log(LOGWARNING, "Too many screen shots or invalid folder");
1036 void CUtil::StatToStatI64(struct _stati64 *result, struct stat *stat)
1038 result->st_dev = stat->st_dev;
1039 result->st_ino = stat->st_ino;
1040 result->st_mode = stat->st_mode;
1041 result->st_nlink = stat->st_nlink;
1042 result->st_uid = stat->st_uid;
1043 result->st_gid = stat->st_gid;
1044 result->st_rdev = stat->st_rdev;
1045 result->st_size = (int64_t)stat->st_size;
1048 result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
1049 result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
1050 result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
1052 result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
1053 result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
1054 result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
1058 void CUtil::Stat64ToStatI64(struct _stati64 *result, struct __stat64 *stat)
1060 result->st_dev = stat->st_dev;
1061 result->st_ino = stat->st_ino;
1062 result->st_mode = stat->st_mode;
1063 result->st_nlink = stat->st_nlink;
1064 result->st_uid = stat->st_uid;
1065 result->st_gid = stat->st_gid;
1066 result->st_rdev = stat->st_rdev;
1067 result->st_size = stat->st_size;
1069 result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
1070 result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
1071 result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
1073 result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
1074 result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
1075 result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
1079 void CUtil::StatI64ToStat64(struct __stat64 *result, struct _stati64 *stat)
1081 result->st_dev = stat->st_dev;
1082 result->st_ino = stat->st_ino;
1083 result->st_mode = stat->st_mode;
1084 result->st_nlink = stat->st_nlink;
1085 result->st_uid = stat->st_uid;
1086 result->st_gid = stat->st_gid;
1087 result->st_rdev = stat->st_rdev;
1088 result->st_size = stat->st_size;
1090 result->st_atime = stat->st_atime;
1091 result->st_mtime = stat->st_mtime;
1092 result->st_ctime = stat->st_ctime;
1094 result->st_atime = stat->_st_atime;
1095 result->st_mtime = stat->_st_mtime;
1096 result->st_ctime = stat->_st_ctime;
1100 void CUtil::Stat64ToStat(struct stat *result, struct __stat64 *stat)
1102 result->st_dev = stat->st_dev;
1103 result->st_ino = stat->st_ino;
1104 result->st_mode = stat->st_mode;
1105 result->st_nlink = stat->st_nlink;
1106 result->st_uid = stat->st_uid;
1107 result->st_gid = stat->st_gid;
1108 result->st_rdev = stat->st_rdev;
1110 if (stat->st_size <= LONG_MAX)
1111 result->st_size = (_off_t)stat->st_size;
1113 if (sizeof(stat->st_size) <= sizeof(result->st_size) )
1114 result->st_size = (off_t)stat->st_size;
1118 result->st_size = 0;
1119 CLog::Log(LOGWARNING, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
1121 result->st_atime = (time_t)(stat->st_atime & 0xFFFFFFFF);
1122 result->st_mtime = (time_t)(stat->st_mtime & 0xFFFFFFFF);
1123 result->st_ctime = (time_t)(stat->st_ctime & 0xFFFFFFFF);
1127 void CUtil::Stat64ToStat64i32(struct _stat64i32 *result, struct __stat64 *stat)
1129 result->st_dev = stat->st_dev;
1130 result->st_ino = stat->st_ino;
1131 result->st_mode = stat->st_mode;
1132 result->st_nlink = stat->st_nlink;
1133 result->st_uid = stat->st_uid;
1134 result->st_gid = stat->st_gid;
1135 result->st_rdev = stat->st_rdev;
1137 if (stat->st_size <= LONG_MAX)
1138 result->st_size = (_off_t)stat->st_size;
1140 if (sizeof(stat->st_size) <= sizeof(result->st_size) )
1141 result->st_size = (off_t)stat->st_size;
1145 result->st_size = 0;
1146 CLog::Log(LOGWARNING, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
1149 result->st_atime = stat->st_atime;
1150 result->st_mtime = stat->st_mtime;
1151 result->st_ctime = stat->st_ctime;
1153 result->st_atime = stat->_st_atime;
1154 result->st_mtime = stat->_st_mtime;
1155 result->st_ctime = stat->_st_ctime;
1160 bool CUtil::CreateDirectoryEx(const CStdString& strPath)
1162 // Function to create all directories at once instead
1163 // of calling CreateDirectory for every subdir.
1164 // Creates the directory and subdirectories if needed.
1166 // return true if directory already exist
1167 if (CDirectory::Exists(strPath)) return true;
1169 // we currently only allow HD and smb, nfs and afp paths
1170 if (!URIUtils::IsHD(strPath) && !URIUtils::IsSmb(strPath) && !URIUtils::IsNfs(strPath) && !URIUtils::IsAfp(strPath))
1172 CLog::Log(LOGERROR,"%s called with an unsupported path: %s", __FUNCTION__, strPath.c_str());
1176 CStdStringArray dirs = URIUtils::SplitPath(strPath);
1177 CStdString dir(dirs.front());
1178 URIUtils::AddSlashAtEnd(dir);
1179 for (CStdStringArray::iterator it = dirs.begin() + 1; it != dirs.end(); it ++)
1181 dir = URIUtils::AddFileToFolder(dir, *it);
1182 CDirectory::Create(dir);
1185 // was the final destination directory successfully created ?
1186 if (!CDirectory::Exists(strPath)) return false;
1190 CStdString CUtil::MakeLegalFileName(const CStdString &strFile, int LegalType)
1192 CStdString result = strFile;
1194 result.Replace('/', '_');
1195 result.Replace('\\', '_');
1196 result.Replace('?', '_');
1198 if (LegalType == LEGAL_WIN32_COMPAT)
1200 // just filter out some illegal characters on windows
1201 result.Replace(':', '_');
1202 result.Replace('*', '_');
1203 result.Replace('?', '_');
1204 result.Replace('\"', '_');
1205 result.Replace('<', '_');
1206 result.Replace('>', '_');
1207 result.Replace('|', '_');
1208 result.TrimRight(".");
1209 result.TrimRight(" ");
1214 // legalize entire path
1215 CStdString CUtil::MakeLegalPath(const CStdString &strPathAndFile, int LegalType)
1217 if (URIUtils::IsStack(strPathAndFile))
1218 return MakeLegalPath(CStackDirectory::GetFirstStackedFile(strPathAndFile));
1219 if (URIUtils::IsMultiPath(strPathAndFile))
1220 return MakeLegalPath(CMultiPathDirectory::GetFirstPath(strPathAndFile));
1221 if (!URIUtils::IsHD(strPathAndFile) && !URIUtils::IsSmb(strPathAndFile) && !URIUtils::IsNfs(strPathAndFile) && !URIUtils::IsAfp(strPathAndFile))
1222 return strPathAndFile; // we don't support writing anywhere except HD, SMB, NFS and AFP - no need to legalize path
1224 bool trailingSlash = URIUtils::HasSlashAtEnd(strPathAndFile);
1225 CStdStringArray dirs = URIUtils::SplitPath(strPathAndFile);
1226 // we just add first token to path and don't legalize it - possible values:
1227 // "X:" (local win32), "" (local unix - empty string before '/') or
1228 // "protocol://domain"
1229 CStdString dir(dirs.front());
1230 URIUtils::AddSlashAtEnd(dir);
1231 for (CStdStringArray::iterator it = dirs.begin() + 1; it != dirs.end(); it ++)
1232 dir = URIUtils::AddFileToFolder(dir, MakeLegalFileName(*it, LegalType));
1233 if (trailingSlash) URIUtils::AddSlashAtEnd(dir);
1237 CStdString CUtil::ValidatePath(const CStdString &path, bool bFixDoubleSlashes /* = false */)
1239 CStdString result = path;
1241 // Don't do any stuff on URLs containing %-characters or protocols that embed
1242 // filenames. NOTE: Don't use IsInZip or IsInRar here since it will infinitely
1243 // recurse and crash XBMC
1244 if (URIUtils::IsURL(path) &&
1245 (path.Find('%') >= 0 ||
1246 path.Left(4).Equals("zip:") ||
1247 path.Left(4).Equals("rar:") ||
1248 path.Left(6).Equals("stack:") ||
1249 path.Left(10).Equals("multipath:") ))
1252 // check the path for incorrect slashes
1254 if (URIUtils::IsDOSPath(path))
1256 result.Replace('/', '\\');
1257 /* The double slash correction should only be used when *absolutely*
1258 necessary! This applies to certain DLLs or use from Python DLLs/scripts
1259 that incorrectly generate double (back) slashes.
1261 if (bFixDoubleSlashes)
1263 // Fixup for double back slashes (but ignore the \\ of unc-paths)
1264 for (int x = 1; x < result.GetLength() - 1; x++)
1266 if (result[x] == '\\' && result[x+1] == '\\')
1271 else if (path.Find("://") >= 0 || path.Find(":\\\\") >= 0)
1274 result.Replace('\\', '/');
1275 /* The double slash correction should only be used when *absolutely*
1276 necessary! This applies to certain DLLs or use from Python DLLs/scripts
1277 that incorrectly generate double (back) slashes.
1279 if (bFixDoubleSlashes)
1281 // Fixup for double forward slashes(/) but don't touch the :// of URLs
1282 for (int x = 2; x < result.GetLength() - 1; x++)
1284 if ( result[x] == '/' && result[x + 1] == '/' && !(result[x - 1] == ':' || (result[x - 1] == '/' && result[x - 2] == ':')) )
1292 bool CUtil::IsUsingTTFSubtitles()
1294 return URIUtils::GetExtension(g_guiSettings.GetString("subtitles.font")).Equals(".ttf");
1298 bool CUtil::TestSplitExec()
1300 CStdString function;
1301 vector<CStdString> params;
1302 CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\test\\foo\")", function, params);
1303 if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo")
1306 CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\test\\foo\\\")", function, params);
1307 if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo\"")
1309 CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\\\test\\\\foo\\\\\")", function, params);
1310 if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo\\")
1312 CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\\\\\\\test\\\\\\foo\\\\\")", function, params);
1313 if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\\\test\\\\foo\\")
1315 CUtil::SplitExecFunction("SetProperty(Foo,\"\")", function, params);
1316 if (function != "SetProperty" || params.size() != 2 || params[0] != "Foo" || params[1] != "")
1318 CUtil::SplitExecFunction("SetProperty(foo,ba(\"ba black )\",sheep))", function, params);
1319 if (function != "SetProperty" || params.size() != 2 || params[0] != "foo" || params[1] != "ba(\"ba black )\",sheep)")
1325 void CUtil::SplitExecFunction(const CStdString &execString, CStdString &function, vector<CStdString> ¶meters)
1327 CStdString paramString;
1329 int iPos = execString.Find("(");
1330 int iPos2 = execString.ReverseFind(")");
1331 if (iPos > 0 && iPos2 > 0)
1333 paramString = execString.Mid(iPos + 1, iPos2 - iPos - 1);
1334 function = execString.Left(iPos);
1337 function = execString;
1339 // remove any whitespace, and the standard prefix (if it exists)
1341 if( function.Left(5).Equals("xbmc.", false) )
1342 function.Delete(0, 5);
1344 SplitParams(paramString, parameters);
1347 void CUtil::SplitParams(const CStdString ¶mString, std::vector<CStdString> ¶meters)
1349 bool inQuotes = false;
1350 bool lastEscaped = false; // only every second character can be escaped
1352 size_t whiteSpacePos = 0;
1353 CStdString parameter;
1355 for (size_t pos = 0; pos < paramString.size(); pos++)
1357 char ch = paramString[pos];
1358 bool escaped = (pos > 0 && paramString[pos - 1] == '\\' && !lastEscaped);
1359 lastEscaped = escaped;
1361 { // if we're in a quote, we accept everything until the closing quote
1362 if (ch == '\"' && !escaped)
1363 { // finished a quote - no need to add the end quote to our string
1368 { // not in a quote, so check if we should be starting one
1369 if (ch == '\"' && !escaped)
1370 { // start of quote - no need to add the quote to our string
1373 if (inFunction && ch == ')')
1374 { // end of a function
1378 { // start of function
1381 if (!inFunction && ch == ',')
1382 { // not in a function, so a comma signfies the end of this parameter
1384 parameter = parameter.Left(whiteSpacePos);
1385 // trim off start and end quotes
1386 if (parameter.GetLength() > 1 && parameter[0] == '\"' && parameter[parameter.GetLength() - 1] == '\"')
1387 parameter = parameter.Mid(1,parameter.GetLength() - 2);
1388 parameters.push_back(parameter);
1394 if ((ch == '\"' || ch == '\\') && escaped)
1395 { // escaped quote or backslash
1396 parameter[parameter.size()-1] = ch;
1399 // whitespace handling - we skip any whitespace at the left or right of an unquoted parameter
1400 if (ch == ' ' && !inQuotes)
1402 if (parameter.IsEmpty()) // skip whitespace on left
1404 if (!whiteSpacePos) // make a note of where whitespace starts on the right
1405 whiteSpacePos = parameter.size();
1411 if (inFunction || inQuotes)
1412 CLog::Log(LOGWARNING, "%s(%s) - end of string while searching for ) or \"", __FUNCTION__, paramString.c_str());
1414 parameter = parameter.Left(whiteSpacePos);
1415 // trim off start and end quotes
1416 if (parameter.GetLength() > 1 && parameter[0] == '\"' && parameter[parameter.GetLength() - 1] == '\"')
1417 parameter = parameter.Mid(1,parameter.GetLength() - 2);
1418 if (!parameter.IsEmpty() || parameters.size())
1419 parameters.push_back(parameter);
1422 int CUtil::GetMatchingSource(const CStdString& strPath1, VECSOURCES& VECSOURCES, bool& bIsSourceName)
1424 if (strPath1.IsEmpty())
1427 //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, testing original path/name [%s]", strPath1.c_str());
1429 // copy as we may change strPath
1430 CStdString strPath = strPath1;
1432 // Check for special protocols
1433 CURL checkURL(strPath);
1436 if (checkURL.GetProtocol() == "stack")
1437 strPath.Delete(0, 8); // remove the stack protocol
1439 if (checkURL.GetProtocol() == "shout")
1440 strPath = checkURL.GetHostName();
1441 if (checkURL.GetProtocol() == "lastfm")
1443 if (checkURL.GetProtocol() == "tuxbox")
1445 if (checkURL.GetProtocol() == "plugin")
1447 if (checkURL.GetProtocol() == "multipath")
1448 strPath = CMultiPathDirectory::GetFirstPath(strPath);
1450 //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, testing for matching name [%s]", strPath.c_str());
1451 bIsSourceName = false;
1454 // we first test the NAME of a source
1455 for (int i = 0; i < (int)VECSOURCES.size(); ++i)
1457 CMediaSource share = VECSOURCES.at(i);
1458 CStdString strName = share.strName;
1460 // special cases for dvds
1461 if (URIUtils::IsOnDVD(share.strPath))
1463 if (URIUtils::IsOnDVD(strPath))
1466 // not a path, so we need to modify the source name
1467 // since we add the drive status and disc name to the source
1468 // "Name (Drive Status/Disc Name)"
1469 int iPos = strName.ReverseFind('(');
1471 strName = strName.Mid(0, iPos - 1);
1473 //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, comparing name [%s]", strName.c_str());
1474 if (strPath.Equals(strName))
1476 bIsSourceName = true;
1481 // now test the paths
1483 // remove user details, and ensure path only uses forward slashes
1484 // and ends with a trailing slash so as not to match a substring
1485 CURL urlDest(strPath);
1486 urlDest.SetOptions("");
1487 CStdString strDest = urlDest.GetWithoutUserDetails();
1488 ForceForwardSlashes(strDest);
1489 if (!URIUtils::HasSlashAtEnd(strDest))
1491 int iLenPath = strDest.size();
1493 //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, testing url [%s]", strDest.c_str());
1495 for (int i = 0; i < (int)VECSOURCES.size(); ++i)
1497 CMediaSource share = VECSOURCES.at(i);
1499 // does it match a source name?
1500 if (share.strPath.substr(0,8) == "shout://")
1502 CURL url(share.strPath);
1503 if (strPath.Equals(url.GetHostName()))
1507 // doesnt match a name, so try the source path
1508 vector<CStdString> vecPaths;
1510 // add any concatenated paths if they exist
1511 if (share.vecPaths.size() > 0)
1512 vecPaths = share.vecPaths;
1514 // add the actual share path at the front of the vector
1515 vecPaths.insert(vecPaths.begin(), share.strPath);
1518 for (int j = 0; j < (int)vecPaths.size(); ++j)
1520 // remove user details, and ensure path only uses forward slashes
1521 // and ends with a trailing slash so as not to match a substring
1522 CURL urlShare(vecPaths[j]);
1523 urlShare.SetOptions("");
1524 CStdString strShare = urlShare.GetWithoutUserDetails();
1525 ForceForwardSlashes(strShare);
1526 if (!URIUtils::HasSlashAtEnd(strShare))
1528 int iLenShare = strShare.size();
1529 //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, comparing url [%s]", strShare.c_str());
1531 if ((iLenPath >= iLenShare) && (strDest.Left(iLenShare).Equals(strShare)) && (iLenShare > iLength))
1533 //CLog::Log(LOGDEBUG,"Found matching source at index %i: [%s], Len = [%i]", i, strShare.c_str(), iLenShare);
1535 // if exact match, return it immediately
1536 if (iLenPath == iLenShare)
1538 // if the path EXACTLY matches an item in a concatentated path
1539 // set source name to true to load the full virtualpath
1540 bIsSourceName = false;
1541 if (vecPaths.size() > 1)
1542 bIsSourceName = true;
1546 iLength = iLenShare;
1551 // return the index of the share with the longest match
1555 // rar:// and zip://
1556 // if archive wasn't mounted, look for a matching share for the archive instead
1557 if( strPath.Left(6).Equals("rar://") || strPath.Left(6).Equals("zip://") )
1559 // get the hostname portion of the url since it contains the archive file
1560 strPath = checkURL.GetHostName();
1562 bIsSourceName = false;
1564 return GetMatchingSource(strPath, VECSOURCES, bDummy);
1567 CLog::Log(LOGWARNING,"CUtil::GetMatchingSource... no matching source found for [%s]", strPath1.c_str());
1572 CStdString CUtil::TranslateSpecialSource(const CStdString &strSpecial)
1574 CStdString strReturn=strSpecial;
1575 if (!strSpecial.IsEmpty() && strSpecial[0] == '$')
1577 if (strSpecial.Left(5).Equals("$HOME"))
1578 URIUtils::AddFileToFolder("special://home/", strSpecial.Mid(5), strReturn);
1579 else if (strSpecial.Left(10).Equals("$SUBTITLES"))
1580 URIUtils::AddFileToFolder("special://subtitles/", strSpecial.Mid(10), strReturn);
1581 else if (strSpecial.Left(9).Equals("$USERDATA"))
1582 URIUtils::AddFileToFolder("special://userdata/", strSpecial.Mid(9), strReturn);
1583 else if (strSpecial.Left(9).Equals("$DATABASE"))
1584 URIUtils::AddFileToFolder("special://database/", strSpecial.Mid(9), strReturn);
1585 else if (strSpecial.Left(11).Equals("$THUMBNAILS"))
1586 URIUtils::AddFileToFolder("special://thumbnails/", strSpecial.Mid(11), strReturn);
1587 else if (strSpecial.Left(11).Equals("$RECORDINGS"))
1588 URIUtils::AddFileToFolder("special://recordings/", strSpecial.Mid(11), strReturn);
1589 else if (strSpecial.Left(12).Equals("$SCREENSHOTS"))
1590 URIUtils::AddFileToFolder("special://screenshots/", strSpecial.Mid(12), strReturn);
1591 else if (strSpecial.Left(15).Equals("$MUSICPLAYLISTS"))
1592 URIUtils::AddFileToFolder("special://musicplaylists/", strSpecial.Mid(15), strReturn);
1593 else if (strSpecial.Left(15).Equals("$VIDEOPLAYLISTS"))
1594 URIUtils::AddFileToFolder("special://videoplaylists/", strSpecial.Mid(15), strReturn);
1595 else if (strSpecial.Left(7).Equals("$CDRIPS"))
1596 URIUtils::AddFileToFolder("special://cdrips/", strSpecial.Mid(7), strReturn);
1597 // this one will be removed post 2.0
1598 else if (strSpecial.Left(10).Equals("$PLAYLISTS"))
1599 URIUtils::AddFileToFolder(g_guiSettings.GetString("system.playlistspath",false), strSpecial.Mid(10), strReturn);
1604 CStdString CUtil::MusicPlaylistsLocation()
1606 vector<CStdString> vec;
1607 CStdString strReturn;
1608 URIUtils::AddFileToFolder(g_guiSettings.GetString("system.playlistspath"), "music", strReturn);
1609 vec.push_back(strReturn);
1610 URIUtils::AddFileToFolder(g_guiSettings.GetString("system.playlistspath"), "mixed", strReturn);
1611 vec.push_back(strReturn);
1612 return XFILE::CMultiPathDirectory::ConstructMultiPath(vec);;
1615 CStdString CUtil::VideoPlaylistsLocation()
1617 vector<CStdString> vec;
1618 CStdString strReturn;
1619 URIUtils::AddFileToFolder(g_guiSettings.GetString("system.playlistspath"), "video", strReturn);
1620 vec.push_back(strReturn);
1621 URIUtils::AddFileToFolder(g_guiSettings.GetString("system.playlistspath"), "mixed", strReturn);
1622 vec.push_back(strReturn);
1623 return XFILE::CMultiPathDirectory::ConstructMultiPath(vec);;
1626 void CUtil::DeleteMusicDatabaseDirectoryCache()
1628 CUtil::DeleteDirectoryCache("mdb-");
1631 void CUtil::DeleteVideoDatabaseDirectoryCache()
1633 CUtil::DeleteDirectoryCache("vdb-");
1636 void CUtil::DeleteDirectoryCache(const CStdString &prefix)
1638 CStdString searchPath = "special://temp/";
1639 CFileItemList items;
1640 if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".fi", false))
1643 for (int i = 0; i < items.Size(); ++i)
1645 if (items[i]->m_bIsFolder)
1647 CStdString fileName = URIUtils::GetFileName(items[i]->GetPath());
1648 if (fileName.Left(prefix.GetLength()) == prefix)
1649 XFILE::CFile::Delete(items[i]->GetPath());
1653 bool CUtil::SetSysDateTimeYear(int iYear, int iMonth, int iDay, int iHour, int iMinute)
1655 TIME_ZONE_INFORMATION tziNew;
1658 GetLocalTime(&CurTime);
1659 GetLocalTime(&NewTime);
1660 int iRescBiases, iHourUTC;
1663 DWORD dwRet = GetTimeZoneInformation(&tziNew); // Get TimeZone Informations
1664 float iGMTZone = (float(tziNew.Bias)/(60)); // Calc's the GMT Time
1666 CLog::Log(LOGDEBUG, "------------ TimeZone -------------");
1667 CLog::Log(LOGDEBUG, "- GMT Zone: GMT %.1f",iGMTZone);
1668 CLog::Log(LOGDEBUG, "- Bias: %lu minutes",tziNew.Bias);
1669 CLog::Log(LOGDEBUG, "- DaylightBias: %lu",tziNew.DaylightBias);
1670 CLog::Log(LOGDEBUG, "- StandardBias: %lu",tziNew.StandardBias);
1674 case TIME_ZONE_ID_STANDARD:
1676 iRescBiases = tziNew.Bias + tziNew.StandardBias;
1677 CLog::Log(LOGDEBUG, "- Timezone ID: 1, Standart");
1680 case TIME_ZONE_ID_DAYLIGHT:
1682 iRescBiases = tziNew.Bias + tziNew.StandardBias + tziNew.DaylightBias;
1683 CLog::Log(LOGDEBUG, "- Timezone ID: 2, Daylight");
1686 case TIME_ZONE_ID_UNKNOWN:
1688 iRescBiases = tziNew.Bias + tziNew.StandardBias;
1689 CLog::Log(LOGDEBUG, "- Timezone ID: 0, Unknown");
1692 case TIME_ZONE_ID_INVALID:
1694 iRescBiases = tziNew.Bias + tziNew.StandardBias;
1695 CLog::Log(LOGDEBUG, "- Timezone ID: Invalid");
1699 iRescBiases = tziNew.Bias + tziNew.StandardBias;
1701 CLog::Log(LOGDEBUG, "--------------- END ---------------");
1704 iHourUTC = GMTZoneCalc(iRescBiases, iHour, iMinute, iMinuteNew);
1705 iMinute = iMinuteNew;
1709 iHourUTC =iHourUTC + 24;
1714 iHourUTC =iHourUTC - 24;
1717 // Set the New-,Detected Time Values to System Time!
1718 NewTime.wYear = (WORD)iYear;
1719 NewTime.wMonth = (WORD)iMonth;
1720 NewTime.wDay = (WORD)iDay;
1721 NewTime.wHour = (WORD)iHourUTC;
1722 NewTime.wMinute = (WORD)iMinute;
1724 FILETIME stNewTime, stCurTime;
1725 SystemTimeToFileTime(&NewTime, &stNewTime);
1726 SystemTimeToFileTime(&CurTime, &stCurTime);
1729 int CUtil::GMTZoneCalc(int iRescBiases, int iHour, int iMinute, int &iMinuteNew)
1731 int iHourUTC, iTemp;
1732 iMinuteNew = iMinute;
1733 iTemp = iRescBiases/60;
1735 if (iRescBiases == 0 )return iHour; // GMT Zone 0, no need calculate
1736 if (iRescBiases > 0)
1737 iHourUTC = iHour + abs(iTemp);
1739 iHourUTC = iHour - abs(iTemp);
1741 if ((iTemp*60) != iRescBiases)
1743 if (iRescBiases > 0)
1744 iMinuteNew = iMinute + abs(iTemp*60 - iRescBiases);
1746 iMinuteNew = iMinute - abs(iTemp*60 - iRescBiases);
1748 if (iMinuteNew >= 60)
1750 iMinuteNew = iMinuteNew -60;
1751 iHourUTC = iHourUTC + 1;
1753 else if (iMinuteNew < 0)
1755 iMinuteNew = iMinuteNew +60;
1756 iHourUTC = iHourUTC - 1;
1762 void CUtil::GetRecursiveListing(const CStdString& strPath, CFileItemList& items, const CStdString& strMask, bool bUseFileDirectories)
1764 CFileItemList myItems;
1765 CDirectory::GetDirectory(strPath,myItems,strMask,bUseFileDirectories);
1766 for (int i=0;i<myItems.Size();++i)
1768 if (myItems[i]->m_bIsFolder)
1769 CUtil::GetRecursiveListing(myItems[i]->GetPath(),items,strMask,bUseFileDirectories);
1771 items.Add(myItems[i]);
1775 void CUtil::GetRecursiveDirsListing(const CStdString& strPath, CFileItemList& item)
1777 CFileItemList myItems;
1778 CDirectory::GetDirectory(strPath,myItems,"",false);
1779 for (int i=0;i<myItems.Size();++i)
1781 if (myItems[i]->m_bIsFolder && !myItems[i]->GetPath().Equals(".."))
1783 item.Add(myItems[i]);
1784 CUtil::GetRecursiveDirsListing(myItems[i]->GetPath(),item);
1789 void CUtil::ForceForwardSlashes(CStdString& strPath)
1791 int iPos = strPath.ReverseFind('\\');
1794 strPath.at(iPos) = '/';
1795 iPos = strPath.ReverseFind('\\');
1799 double CUtil::AlbumRelevance(const CStdString& strAlbumTemp1, const CStdString& strAlbum1, const CStdString& strArtistTemp1, const CStdString& strArtist1)
1801 // case-insensitive fuzzy string comparison on the album and artist for relevance
1802 // weighting is identical, both album and artist are 50% of the total relevance
1803 // a missing artist means the maximum relevance can only be 0.50
1804 CStdString strAlbumTemp = strAlbumTemp1;
1805 strAlbumTemp.MakeLower();
1806 CStdString strAlbum = strAlbum1;
1807 strAlbum.MakeLower();
1808 double fAlbumPercentage = fstrcmp(strAlbumTemp, strAlbum, 0.0f);
1809 double fArtistPercentage = 0.0f;
1810 if (!strArtist1.IsEmpty())
1812 CStdString strArtistTemp = strArtistTemp1;
1813 strArtistTemp.MakeLower();
1814 CStdString strArtist = strArtist1;
1815 strArtist.MakeLower();
1816 fArtistPercentage = fstrcmp(strArtistTemp, strArtist, 0.0f);
1818 double fRelevance = fAlbumPercentage * 0.5f + fArtistPercentage * 0.5f;
1822 bool CUtil::MakeShortenPath(CStdString StrInput, CStdString& StrOutput, int iTextMaxLength)
1824 int iStrInputSize = StrInput.size();
1825 if((iStrInputSize <= 0) || (iTextMaxLength >= iStrInputSize))
1829 size_t nGreaterDelim, nPos;
1831 nPos = StrInput.find_last_of( '\\' );
1832 if ( nPos != CStdString::npos )
1836 nPos = StrInput.find_last_of( '/' );
1837 if ( nPos != CStdString::npos )
1840 if ( cDelim == '\0' )
1843 if (nPos == StrInput.size() - 1)
1845 StrInput.erase(StrInput.size() - 1);
1846 nPos = StrInput.find_last_of( cDelim );
1848 while( iTextMaxLength < iStrInputSize )
1850 nPos = StrInput.find_last_of( cDelim, nPos );
1851 nGreaterDelim = nPos;
1852 if ( nPos != CStdString::npos )
1853 nPos = StrInput.find_last_of( cDelim, nPos - 1 );
1854 if ( nPos == CStdString::npos ) break;
1855 if ( nGreaterDelim > nPos ) StrInput.replace( nPos + 1, nGreaterDelim - nPos - 1, ".." );
1856 iStrInputSize = StrInput.size();
1858 // replace any additional /../../ with just /../ if necessary
1859 CStdString replaceDots;
1860 replaceDots.Format("..%c..", cDelim);
1861 while (StrInput.size() > (unsigned int)iTextMaxLength)
1862 if (!StrInput.Replace(replaceDots, ".."))
1864 // finally, truncate our string to force inside our max text length,
1865 // replacing the last 2 characters with ".."
1868 // "smb://../Playboy Swimsuit Cal.."
1869 if (iTextMaxLength > 2 && StrInput.size() > (unsigned int)iTextMaxLength)
1871 StrInput = StrInput.Left(iTextMaxLength - 2);
1874 StrOutput = StrInput;
1878 bool CUtil::SupportsFileOperations(const CStdString& strPath)
1880 // currently only hd, smb, nfs and afp support delete and rename
1881 if (URIUtils::IsHD(strPath))
1883 if (URIUtils::IsSmb(strPath))
1885 if (URIUtils::IsNfs(strPath))
1887 if (URIUtils::IsAfp(strPath))
1889 if (URIUtils::IsMythTV(strPath))
1892 * Can't use CFile::Exists() to check whether the myth:// path supports file operations because
1893 * it hits the directory cache on the way through, which has the Live Channels and Guide
1896 return CMythDirectory::SupportsFileOperations(strPath);
1898 if (URIUtils::IsStack(strPath))
1899 return SupportsFileOperations(CStackDirectory::GetFirstStackedFile(strPath));
1900 if (URIUtils::IsMultiPath(strPath))
1901 return CMultiPathDirectory::SupportsFileOperations(strPath);
1906 CStdString CUtil::GetDefaultFolderThumb(const CStdString &folderThumb)
1908 if (g_TextureManager.HasTexture(folderThumb))
1913 void CUtil::GetSkinThemes(vector<CStdString>& vecTheme)
1916 URIUtils::AddFileToFolder(g_graphicsContext.GetMediaDir(),"media",strPath);
1917 CFileItemList items;
1918 CDirectory::GetDirectory(strPath, items);
1919 // Search for Themes in the Current skin!
1920 for (int i = 0; i < items.Size(); ++i)
1922 CFileItemPtr pItem = items[i];
1923 if (!pItem->m_bIsFolder)
1925 CStdString strExtension;
1926 URIUtils::GetExtension(pItem->GetPath(), strExtension);
1927 if ((strExtension == ".xpr" && pItem->GetLabel().CompareNoCase("Textures.xpr")) ||
1928 (strExtension == ".xbt" && pItem->GetLabel().CompareNoCase("Textures.xbt")))
1930 CStdString strLabel = pItem->GetLabel();
1931 vecTheme.push_back(strLabel.Mid(0, strLabel.size() - 4));
1935 sort(vecTheme.begin(), vecTheme.end(), sortstringbyname());
1938 void CUtil::InitRandomSeed()
1942 now = CurrentHostCounter();
1943 unsigned int seed = (unsigned int)now;
1944 // CLog::Log(LOGDEBUG, "%s - Initializing random seed with %u", __FUNCTION__, seed);
1949 bool CUtil::RunCommandLine(const CStdString& cmdLine, bool waitExit)
1951 CStdStringArray args;
1953 StringUtils::SplitString(cmdLine, ",", args);
1955 // Strip quotes and whitespace around the arguments, or exec will fail.
1956 // This allows the python invocation to be written more naturally with any amount of whitespace around the args.
1957 // But it's still limited, for example quotes inside the strings are not expanded, etc.
1958 // TODO: Maybe some python library routine can parse this more properly ?
1959 for (size_t i=0; i<args.size(); i++)
1961 CStdString &s = args[i];
1962 CStdString stripd = s.Trim();
1963 if (stripd[0] == '"' || stripd[0] == '\'')
1966 s = s.Right(s.size() - 1);
1968 if (stripd[stripd.size() - 1] == '"' || stripd[stripd.size() - 1] == '\'')
1971 s = s.Left(s.size() - 1);
1975 return Command(args, waitExit);
1979 // FIXME, this should be merged with the function below.
1981 bool CUtil::Command(const CStdStringArray& arrArgs, bool waitExit)
1984 printf("Executing: ");
1985 for (size_t i=0; i<arrArgs.size(); i++)
1986 printf("%s ", arrArgs[i].c_str());
1990 pid_t child = fork();
1997 if (arrArgs.size() > 0)
1999 char **args = (char **)alloca(sizeof(char *) * (arrArgs.size() + 3));
2000 memset(args, 0, (sizeof(char *) * (arrArgs.size() + 3)));
2001 for (size_t i=0; i<arrArgs.size(); i++)
2002 args[i] = (char *)arrArgs[i].c_str();
2003 execvp(args[0], args);
2008 if (waitExit) waitpid(child, &n, 0);
2011 return (waitExit) ? (WEXITSTATUS(n) == 0) : true;
2014 bool CUtil::SudoCommand(const CStdString &strCommand)
2016 CLog::Log(LOGDEBUG, "Executing sudo command: <%s>", strCommand.c_str());
2017 pid_t child = fork();
2021 close(0); // close stdin to avoid sudo request password
2024 CStdStringArray arrArgs;
2025 StringUtils::SplitString(strCommand, " ", arrArgs);
2026 if (arrArgs.size() > 0)
2028 char **args = (char **)alloca(sizeof(char *) * (arrArgs.size() + 3));
2029 memset(args, 0, (sizeof(char *) * (arrArgs.size() + 3)));
2030 args[0] = (char *)"/usr/bin/sudo";
2031 args[1] = (char *)"-S";
2032 for (size_t i=0; i<arrArgs.size(); i++)
2034 args[i+2] = (char *)arrArgs[i].c_str();
2036 execvp("/usr/bin/sudo", args);
2040 waitpid(child, &n, 0);
2042 return WEXITSTATUS(n) == 0;
2046 int CUtil::LookupRomanDigit(char roman_digit)
2048 switch (roman_digit)
2076 int CUtil::TranslateRomanNumeral(const char* roman_numeral)
2081 if (roman_numeral && roman_numeral[0])
2088 while (*roman_numeral)
2090 int digit = CUtil::LookupRomanDigit(*roman_numeral);
2093 // General sanity checks
2095 // numeral not in LUT
2102 // N = 10^n may not precede (N+1) > 10^(N+1)
2103 if (test == 1 && digit > last * 10)
2106 // N = 5*10^n may not precede (N+1) >= N
2107 if (test == 5 && digit >= last)
2110 // End general sanity checks
2114 // smaller numerals may not repeat before a larger one
2123 else if (last == digit)
2132 decimal += 2 * last - temp_sum;
2134 decimal += temp_sum;
2141 // Post general sanity checks
2143 // numerals may not repeat more than thrice
2152 decimal += temp_sum;
2154 decimal += 2 * last - temp_sum;
2159 CStdString CUtil::ResolveExecutablePath()
2161 CStdString strExecutablePath;
2163 wchar_t szAppPathW[MAX_PATH] = L"";
2164 ::GetModuleFileNameW(0, szAppPathW, sizeof(szAppPathW) - 1);
2165 CStdStringW strPathW = szAppPathW;
2166 g_charsetConverter.wToUTF8(strPathW,strExecutablePath);
2167 #elif defined(__APPLE__)
2168 char given_path[2*MAXPATHLEN];
2169 uint32_t path_size =2*MAXPATHLEN;
2171 GetDarwinExecutablePath(given_path, &path_size);
2172 strExecutablePath = given_path;
2173 #elif defined(__FreeBSD__)
2180 mib[2] = KERN_PROC_PATHNAME;
2183 buflen = sizeof(buf) - 1;
2184 if(sysctl(mib, 4, buf, &buflen, NULL, 0) < 0)
2185 strExecutablePath = "";
2187 strExecutablePath = buf;
2189 /* Get our PID and build the name of the link in /proc */
2190 pid_t pid = getpid();
2191 char linkname[64]; /* /proc/<pid>/exe */
2192 snprintf(linkname, sizeof(linkname), "/proc/%i/exe", pid);
2194 /* Now read the symbolic link */
2196 int ret = readlink(linkname, buf, PATH_MAX);
2199 strExecutablePath = buf;
2201 return strExecutablePath;
2204 CStdString CUtil::GetFrameworksPath(bool forPython)
2206 CStdString strFrameworksPath;
2207 #if defined(__APPLE__)
2208 char given_path[2*MAXPATHLEN];
2209 uint32_t path_size =2*MAXPATHLEN;
2211 GetDarwinFrameworkPath(forPython, given_path, &path_size);
2212 strFrameworksPath = given_path;
2214 return strFrameworksPath;
2217 void CUtil::ScanForExternalSubtitles(const CStdString& strMovie, std::vector<CStdString>& vecSubtitles )
2219 unsigned int startTimer = XbmcThreads::SystemClockMillis();
2221 // new array for commons sub dirs
2222 const char * common_sub_dirs[] = {"subs",
2236 vector<CStdString> vecExtensionsCached;
2237 //strExtensionCached = "";
2239 CFileItem item(strMovie, false);
2240 if (item.IsInternetStream()) return ;
2241 if (item.IsHDHomeRun()) return ;
2242 if (item.IsSlingbox()) return ;
2243 if (item.IsPlayList()) return ;
2244 if (!item.IsVideo()) return ;
2246 vector<CStdString> strLookInPaths;
2248 CStdString strMovieFileName;
2251 URIUtils::Split(strMovie, strPath, strMovieFileName);
2252 CStdString strMovieFileNameNoExt(URIUtils::ReplaceExtension(strMovieFileName, ""));
2253 strLookInPaths.push_back(strPath);
2255 if (!g_settings.iAdditionalSubtitleDirectoryChecked && !g_guiSettings.GetString("subtitles.custompath").IsEmpty()) // to avoid checking non-existent directories (network) every time..
2257 if (!g_application.getNetwork().IsAvailable() && !URIUtils::IsHD(g_guiSettings.GetString("subtitles.custompath")))
2259 CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonaccessible");
2260 g_settings.iAdditionalSubtitleDirectoryChecked = -1; // disabled
2262 else if (!CDirectory::Exists(g_guiSettings.GetString("subtitles.custompath")))
2264 CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonexistant");
2265 g_settings.iAdditionalSubtitleDirectoryChecked = -1; // disabled
2268 g_settings.iAdditionalSubtitleDirectoryChecked = 1;
2271 if (strMovie.Left(6) == "rar://") // <--- if this is found in main path then ignore it!
2274 CStdString strArchive = url.GetHostName();
2275 URIUtils::Split(strArchive, strPath, strMovieFileName);
2276 strLookInPaths.push_back(strPath);
2279 // checking if any of the common subdirs exist ..
2280 CStdStringArray directories;
2281 int nTokens = StringUtils::SplitString( strPath, "/", directories );
2283 StringUtils::SplitString( strPath, "\\", directories );
2285 // if it's inside a cdX dir, add parent path
2286 if (directories[directories.size()-2].size() == 3 && directories[directories.size()-2].Left(2).Equals("cd")) // SplitString returns empty token as last item, hence size-2
2288 CStdString strPath2;
2289 URIUtils::GetParentPath(strPath,strPath2);
2290 strLookInPaths.push_back(strPath2);
2292 int iSize = strLookInPaths.size();
2293 for (int i=0;i<iSize;++i)
2295 for (int j=0; common_sub_dirs[j]; j++)
2297 CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],common_sub_dirs[j]);
2298 if (CDirectory::Exists(strPath2))
2299 strLookInPaths.push_back(strPath2);
2302 // .. done checking for common subdirs
2304 // check if there any cd-directories in the paths we have added so far
2305 iSize = strLookInPaths.size();
2306 for (int i=0;i<9;++i) // 9 cd's
2309 cdDir.Format("cd%i",i+1);
2310 for (int i=0;i<iSize;++i)
2312 CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],cdDir);
2313 URIUtils::AddSlashAtEnd(strPath2);
2314 bool pathAlreadyAdded = false;
2315 for (unsigned int i=0; i<strLookInPaths.size(); i++)
2317 // if movie file is inside cd-dir, this directory can exist in vector already
2318 if (strLookInPaths[i].Equals( strPath2 ) )
2319 pathAlreadyAdded = true;
2321 if (CDirectory::Exists(strPath2) && !pathAlreadyAdded)
2322 strLookInPaths.push_back(strPath2);
2325 // .. done checking for cd-dirs
2327 // this is last because we dont want to check any common subdirs or cd-dirs in the alternate <subtitles> dir.
2328 if (g_settings.iAdditionalSubtitleDirectoryChecked == 1)
2330 strPath = g_guiSettings.GetString("subtitles.custompath");
2331 URIUtils::AddSlashAtEnd(strPath);
2332 strLookInPaths.push_back(strPath);
2339 // 2 steps for movie directory and alternate subtitles directory
2340 CLog::Log(LOGDEBUG,"%s: Searching for subtitles...", __FUNCTION__);
2341 for (unsigned int step = 0; step < strLookInPaths.size(); step++)
2343 if (strLookInPaths[step].length() != 0)
2345 CFileItemList items;
2347 CDirectory::GetDirectory(strLookInPaths[step], items,".utf|.utf8|.utf-8|.sub|.srt|.smi|.rt|.txt|.ssa|.text|.ssa|.aqt|.jss|.ass|.idx|.ifo|.rar|.zip",false);
2348 int fnl = strMovieFileNameNoExt.size();
2350 for (int j = 0; j < items.Size(); j++)
2352 URIUtils::Split(items[j]->GetPath(), strPath, strItem);
2354 // is this a rar or zip-file
2355 if (URIUtils::IsRAR(strItem) || URIUtils::IsZIP(strItem))
2357 ScanArchiveForSubtitles( items[j]->GetPath(), strMovieFileNameNoExt, vecSubtitles );
2359 else // not a rar/zip file
2361 for (int i = 0; sub_exts[i]; i++)
2363 //Cache subtitle with same name as movie
2364 if (URIUtils::GetExtension(strItem).Equals(sub_exts[i]) && strItem.Left(fnl).Equals(strMovieFileNameNoExt))
2366 vecSubtitles.push_back( items[j]->GetPath() );
2367 CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, items[j]->GetPath().c_str() );
2372 g_directoryCache.ClearDirectory(strLookInPaths[step]);
2376 iSize = vecSubtitles.size();
2377 for (int i = 0; i < iSize; i++)
2379 if (URIUtils::GetExtension(vecSubtitles[i]).Equals(".smi"))
2381 //Cache multi-language sami subtitle
2382 CDVDSubtitleStream* pStream = new CDVDSubtitleStream();
2383 if(pStream->Open(vecSubtitles[i]))
2385 CDVDSubtitleTagSami TagConv;
2386 TagConv.LoadHead(pStream);
2387 if (TagConv.m_Langclass.size() >= 2)
2389 for (unsigned int k = 0; k < TagConv.m_Langclass.size(); k++)
2391 strDest.Format("special://temp/subtitle.%s.%d.smi", TagConv.m_Langclass[k].Name, i);
2392 if (CFile::Cache(vecSubtitles[i], strDest))
2394 CLog::Log(LOGINFO, " cached subtitle %s->%s\n", vecSubtitles[i].c_str(), strDest.c_str());
2395 vecSubtitles.push_back(strDest);
2403 CLog::Log(LOGDEBUG,"%s: END (total time: %i ms)", __FUNCTION__, (int)(XbmcThreads::SystemClockMillis() - startTimer));
2406 int CUtil::ScanArchiveForSubtitles( const CStdString& strArchivePath, const CStdString& strMovieFileNameNoExt, std::vector<CStdString>& vecSubtitles )
2408 int nSubtitlesAdded = 0;
2409 CFileItemList ItemList;
2411 // zip only gets the root dir
2412 if (URIUtils::GetExtension(strArchivePath).Equals(".zip"))
2414 CStdString strZipPath;
2415 URIUtils::CreateArchivePath(strZipPath,"zip",strArchivePath,"");
2416 if (!CDirectory::GetDirectory(strZipPath,ItemList,"",false))
2421 #ifdef HAS_FILESYSTEM_RAR
2422 // get _ALL_files in the rar, even those located in subdirectories because we set the bMask to false.
2423 // so now we dont have to find any subdirs anymore, all files in the rar is checked.
2424 if( !g_RarManager.GetFilesInRar(ItemList, strArchivePath, false, "") )
2430 for (int it= 0 ; it <ItemList.Size();++it)
2432 CStdString strPathInRar = ItemList[it]->GetPath();
2433 CStdString strExt = URIUtils::GetExtension(strPathInRar);
2435 CLog::Log(LOGDEBUG, "ScanArchiveForSubtitles:: Found file %s", strPathInRar.c_str());
2436 // always check any embedded rar archives
2437 // checking for embedded rars, I moved this outside the sub_ext[] loop. We only need to check this once for each file.
2438 if (URIUtils::IsRAR(strPathInRar) || URIUtils::IsZIP(strPathInRar))
2440 CStdString strRarInRar;
2441 if (URIUtils::GetExtension(strPathInRar).Equals(".rar"))
2442 URIUtils::CreateArchivePath(strRarInRar, "rar", strArchivePath, strPathInRar);
2444 URIUtils::CreateArchivePath(strRarInRar, "zip", strArchivePath, strPathInRar);
2445 ScanArchiveForSubtitles(strRarInRar,strMovieFileNameNoExt,vecSubtitles);
2447 // done checking if this is a rar-in-rar
2450 while (sub_exts[iPos])
2452 if (strExt.CompareNoCase(sub_exts[iPos]) == 0)
2454 CStdString strSourceUrl;
2455 if (URIUtils::GetExtension(strArchivePath).Equals(".rar"))
2456 URIUtils::CreateArchivePath(strSourceUrl, "rar", strArchivePath, strPathInRar);
2458 strSourceUrl = strPathInRar;
2460 CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, strSourceUrl.c_str() );
2461 vecSubtitles.push_back( strSourceUrl );
2469 return nSubtitlesAdded;
2472 /*! \brief in a vector of subtitles finds the corresponding .sub file for a given .idx file
2474 bool CUtil::FindVobSubPair( const std::vector<CStdString>& vecSubtitles, const CStdString& strIdxPath, CStdString& strSubPath )
2476 if (URIUtils::GetExtension(strIdxPath) == ".idx")
2478 CStdString strIdxFile;
2479 CStdString strIdxDirectory;
2480 URIUtils::Split(strIdxPath, strIdxDirectory, strIdxFile);
2481 for (unsigned int j=0; j < vecSubtitles.size(); j++)
2483 CStdString strSubFile;
2484 CStdString strSubDirectory;
2485 URIUtils::Split(vecSubtitles[j], strSubDirectory, strSubFile);
2486 if (URIUtils::GetExtension(strSubFile) == ".sub" &&
2487 URIUtils::ReplaceExtension(strIdxFile,"").Equals(URIUtils::ReplaceExtension(strSubFile,"")))
2489 strSubPath = vecSubtitles[j];
2497 /*! \brief checks if in the vector of subtitles the given .sub file has a corresponding idx and hence is a vobsub file
2499 bool CUtil::IsVobSub( const std::vector<CStdString>& vecSubtitles, const CStdString& strSubPath )
2501 if (URIUtils::GetExtension(strSubPath) == ".sub")
2503 CStdString strSubFile;
2504 CStdString strSubDirectory;
2505 URIUtils::Split(strSubPath, strSubDirectory, strSubFile);
2506 for (unsigned int j=0; j < vecSubtitles.size(); j++)
2508 CStdString strIdxFile;
2509 CStdString strIdxDirectory;
2510 URIUtils::Split(vecSubtitles[j], strIdxDirectory, strIdxFile);
2511 if (URIUtils::GetExtension(strIdxFile) == ".idx" &&
2512 URIUtils::ReplaceExtension(strIdxFile,"").Equals(URIUtils::ReplaceExtension(strSubFile,"")))