Merge pull request #473 from Montellese/onplaybackspeedchanged
[vuplus_xbmc] / xbmc / Util.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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
19  *
20  */
21 #include "threads/SystemClock.h"
22 #include "system.h"
23 #ifdef __APPLE__
24 #include <sys/param.h>
25 #include <mach-o/dyld.h>
26 #endif
27
28 #if defined(__FreeBSD__)
29 #include <sys/param.h>
30 #include <sys/sysctl.h>
31 #endif
32
33 #ifdef _LINUX
34 #include <sys/types.h>
35 #include <dirent.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <sys/wait.h>
39 #endif
40
41 #include "Application.h"
42 #include "utils/AutoPtrHandle.h"
43 #include "Util.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"
54 #endif
55 #include "filesystem/MythDirectory.h"
56 #ifdef HAS_UPNP
57 #include "filesystem/UPnPDirectory.h"
58 #endif
59 #ifdef HAS_VIDEO_PLAYBACK
60 #include "cores/VideoRenderers/RenderManager.h"
61 #endif
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"
70 #ifdef _WIN32
71 #include <shlobj.h>
72 #include "WIN32Util.h"
73 #endif
74 #if defined(__APPLE__)
75 #include "osx/DarwinUtils.h"
76 #endif
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"
85 #endif
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"
97
98 using namespace std;
99 using namespace XFILE;
100
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;
104
105 using namespace AUTOPTR;
106 using namespace XFILE;
107 using namespace PLAYLIST;
108
109 #ifdef HAS_DX
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];
118 #endif
119
120 CUtil::CUtil(void)
121 {
122 }
123
124 CUtil::~CUtil(void)
125 {}
126
127 CStdString CUtil::GetTitleFromPath(const CStdString& strFileNameAndPath, bool bIsFolder /* = false */)
128 {
129   // use above to get the filename
130   CStdString path(strFileNameAndPath);
131   URIUtils::RemoveSlashAtEnd(path);
132   CStdString strFilename = URIUtils::GetFileName(path);
133
134   CURL url(strFileNameAndPath);
135   CStdString strHostname = url.GetHostName();
136
137 #ifdef HAS_UPNP
138   // UPNP
139   if (url.GetProtocol() == "upnp")
140     strFilename = CUPnPDirectory::GetFriendlyName(strFileNameAndPath.c_str());
141 #endif
142
143   if (url.GetProtocol() == "rss")
144   {
145     CRSSDirectory dir;
146     CFileItemList items;
147     if(dir.GetDirectory(strFileNameAndPath, items) && !items.m_strTitle.IsEmpty())
148       return items.m_strTitle;
149   }
150
151   // LastFM
152   if (url.GetProtocol() == "lastfm")
153   {
154     if (strFilename.IsEmpty())
155       strFilename = g_localizeStrings.Get(15200);
156     else
157       strFilename = g_localizeStrings.Get(15200) + " - " + strFilename;
158   }
159
160   // Shoutcast
161   else if (url.GetProtocol() == "shout")
162   {
163     const int genre = strFileNameAndPath.find_first_of('=');
164     if(genre <0)
165       strFilename = g_localizeStrings.Get(260);
166     else
167       strFilename = g_localizeStrings.Get(260) + " - " + strFileNameAndPath.substr(genre+1).c_str();
168   }
169
170   // Windows SMB Network (SMB)
171   else if (url.GetProtocol() == "smb" && strFilename.IsEmpty())
172   {
173     if (url.GetHostName().IsEmpty())
174     {
175       strFilename = g_localizeStrings.Get(20171);
176     }
177     else
178     {
179       strFilename = url.GetHostName();
180     }
181   }
182   // iTunes music share (DAAP)
183   else if (url.GetProtocol() == "daap" && strFilename.IsEmpty())
184     strFilename = g_localizeStrings.Get(20174);
185
186   // HDHomerun Devices
187   else if (url.GetProtocol() == "hdhomerun" && strFilename.IsEmpty())
188     strFilename = "HDHomerun Devices";
189
190   // Slingbox Devices
191   else if (url.GetProtocol() == "sling")
192     strFilename = "Slingbox";
193
194   // ReplayTV Devices
195   else if (url.GetProtocol() == "rtv")
196     strFilename = "ReplayTV Devices";
197
198   // HTS Tvheadend client
199   else if (url.GetProtocol() == "htsp")
200     strFilename = g_localizeStrings.Get(20256);
201
202   // VDR Streamdev client
203   else if (url.GetProtocol() == "vtp")
204     strFilename = g_localizeStrings.Get(20257);
205   
206   // MythTV client
207   else if (url.GetProtocol() == "myth")
208     strFilename = g_localizeStrings.Get(20258);
209
210   // SAP Streams
211   else if (url.GetProtocol() == "sap" && strFilename.IsEmpty())
212     strFilename = "SAP Streams";
213
214   // Root file views
215   else if (url.GetProtocol() == "sources")
216     strFilename = g_localizeStrings.Get(744);
217
218   // Music Playlists
219   else if (path.Left(24).Equals("special://musicplaylists"))
220     strFilename = g_localizeStrings.Get(136);
221
222   // Video Playlists
223   else if (path.Left(24).Equals("special://videoplaylists"))
224     strFilename = g_localizeStrings.Get(136);
225
226   else if (URIUtils::ProtocolHasParentInHostname(url.GetProtocol()) && strFilename.IsEmpty())
227     strFilename = URIUtils::GetFileName(url.GetHostName());
228
229   // now remove the extension if needed
230   if (!g_guiSettings.GetBool("filelists.showextensions") && !bIsFolder)
231   {
232     URIUtils::RemoveExtension(strFilename);
233     return strFilename;
234   }
235   
236   // URLDecode since the original path may be an URL
237   CURL::Decode(strFilename);
238   return strFilename;
239 }
240
241 bool CUtil::GetVolumeFromFileName(const CStdString& strFileName, CStdString& strFileTitle, CStdString& strVolumeNumber)
242 {
243   const CStdStringArray &regexps = g_advancedSettings.m_videoStackRegExps;
244
245   CStdString strFileNameTemp = strFileName;
246
247   CRegExp reg(true);
248
249   for (unsigned int i = 0; i < regexps.size(); i++)
250   {
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());
255       continue;
256     }
257 //    CLog::Log(LOGDEBUG, "Regexp:[%s]", regexps[i].c_str());
258
259     int iFoundToken = reg.RegFind(strFileName.c_str());
260     if (iFoundToken >= 0)
261     {
262       int iRegLength = reg.GetFindLen();
263       int iCount = reg.GetSubCount();
264
265       /*
266       reg.DumpOvector(LOGDEBUG);
267       CLog::Log(LOGDEBUG, "Subcount=%i", iCount);
268       for (int j = 0; j <= iCount; j++)
269       {
270         CStdString str = reg.GetMatch(j);
271         CLog::Log(LOGDEBUG, "Sub(%i):[%s]", j, str.c_str());
272       }
273       */
274
275       // simple regexp, only the volume is captured
276       if (iCount == 1)
277       {
278         strVolumeNumber = reg.GetMatch(1);
279         if (strVolumeNumber.IsEmpty()) return false;
280
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;
291
292         return true;
293       }
294
295       // advanced regexp with prefix (1), volume (2), and suffix (3)
296       else if (iCount == 3)
297       {
298         // second subpatten contains the stacking volume
299         strVolumeNumber = reg.GetMatch(2);
300         if (strVolumeNumber.IsEmpty()) return false;
301
302         // everything before the regexp match
303         strFileTitle = strFileName.Left(iFoundToken);
304
305         // first subpattern contains prefix
306         strFileTitle += reg.GetMatch(1);
307
308         // third subpattern contains suffix
309         strFileTitle += reg.GetMatch(3);
310
311         // everything after the regexp match
312         strFileTitle += strFileNameTemp.Mid(iFoundToken + iRegLength);
313
314         return true;
315       }
316
317       // unknown regexp format
318       else
319       {
320         CLog::Log(LOGERROR, "Incorrect movie stacking regexp format:[%s]", regexps[i].c_str());
321       }
322     }
323   }
324   return false;
325 }
326
327 void CUtil::CleanString(const CStdString& strFileName, CStdString& strTitle, CStdString& strTitleAndYear, CStdString& strYear, bool bRemoveExtension /* = false */, bool bCleanChars /* = true */)
328 {
329   strTitleAndYear = strFileName;
330
331   if (strFileName.Equals(".."))
332    return;
333
334   const CStdStringArray &regexps = g_advancedSettings.m_videoCleanStringRegExps;
335
336   CRegExp reTags(true);
337   CRegExp reYear;
338   CStdString strExtension;
339   URIUtils::GetExtension(strFileName, strExtension);
340
341   if (!reYear.RegComp(g_advancedSettings.m_videoCleanDateTimeRegExp))
342   {
343     CLog::Log(LOGERROR, "%s: Invalid datetime clean RegExp:'%s'", __FUNCTION__, g_advancedSettings.m_videoCleanDateTimeRegExp.c_str());
344   }
345   else
346   {
347     if (reYear.RegFind(strTitleAndYear.c_str()) >= 0)
348     {
349       strTitleAndYear = reYear.GetReplaceString("\\1");
350       strYear = reYear.GetReplaceString("\\2");
351     }
352   }
353
354   URIUtils::RemoveExtension(strTitleAndYear);
355
356   for (unsigned int i = 0; i < regexps.size(); i++)
357   {
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());
361       continue;
362     }
363     int j=0;
364     if ((j=reTags.RegFind(strFileName.c_str())) > 0)
365       strTitleAndYear = strTitleAndYear.Mid(0, j);
366   }
367
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.
373   if (bCleanChars)
374   {
375     bool initialDots = true;
376     bool alreadyContainsSpace = (strTitleAndYear.Find(' ') >= 0);
377
378     for (int i = 0; i < (int)strTitleAndYear.size(); i++)
379     {
380       char c = strTitleAndYear.GetAt(i);
381
382       if (c != '.')
383         initialDots = false;
384
385       if ((c == '_') || ((!alreadyContainsSpace) && !initialDots && (c == '.')))
386       {
387         strTitleAndYear.SetAt(i, ' ');
388       }
389     }
390   }
391
392   strTitle = strTitleAndYear.Trim();
393
394   // append year
395   if (!strYear.IsEmpty())
396     strTitleAndYear = strTitle + " (" + strYear + ")";
397
398   // restore extension if needed
399   if (!bRemoveExtension)
400     strTitleAndYear += strExtension;
401 }
402
403 void CUtil::GetQualifiedFilename(const CStdString &strBasePath, CStdString &strFilename)
404 {
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())
408     return;
409
410   // If the filename starts "x:" or "/" it's already fully qualified so return
411   if (strFilename.size() > 1)
412 #ifdef _LINUX
413     if ( (strFilename[1] == ':') || (strFilename[0] == '/') )
414 #else
415     if ( strFilename[1] == ':' )
416 #endif
417       return;
418
419   // add to base path and then clean
420   strFilename = URIUtils::AddFileToFolder(strBasePath, strFilename);
421
422   // get rid of any /./ or \.\ that happen to be there
423   strFilename.Replace("\\.\\", "\\");
424   strFilename.Replace("/./", "/");
425
426   // now find any "\\..\\" and remove them via GetParentPath
427   int pos;
428   while ((pos = strFilename.Find("/../")) > 0)
429   {
430     CStdString basePath = strFilename.Left(pos+1);
431     strFilename = strFilename.Mid(pos+4);
432     basePath = URIUtils::GetParentPath(basePath);
433     strFilename = URIUtils::AddFileToFolder(basePath, strFilename);
434   }
435   while ((pos = strFilename.Find("\\..\\")) > 0)
436   {
437     CStdString basePath = strFilename.Left(pos+1);
438     strFilename = strFilename.Mid(pos+4);
439     basePath = URIUtils::GetParentPath(basePath);
440     strFilename = URIUtils::AddFileToFolder(basePath, strFilename);
441   }
442 }
443
444 #ifdef UNIT_TESTING
445 bool CUtil::TestGetQualifiedFilename()
446 {
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;
455   return true;
456 }
457
458 bool CUtil::TestMakeLegalPath()
459 {
460   CStdString path;
461 #ifdef _WIN32
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;
466 #elif
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;
471 #endif
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;
476   return true;
477 }
478 #endif
479
480 void CUtil::RunShortcut(const char* szShortcutPath)
481 {
482 }
483
484 void CUtil::GetHomePath(CStdString& strPath, const CStdString& strTarget)
485 {
486   CStdString strHomePath;
487   strHomePath = ResolveExecutablePath();
488 #ifdef _WIN32
489   CStdStringW strPathW, strTargetW;
490   g_charsetConverter.utf8ToW(strTarget, strTargetW);
491   strPathW = _wgetenv(strTargetW);
492   g_charsetConverter.wToUTF8(strPathW,strPath);
493 #else
494   strPath = getenv(strTarget);
495 #endif
496
497   if (strPath != NULL && !strPath.IsEmpty())
498   {
499 #ifdef _WIN32
500     char tmp[1024];
501     //expand potential relative path to full path
502     if(GetFullPathName(strPath, 1024, tmp, 0) != 0)
503     {
504       strPath = tmp;
505     }
506 #endif
507   }
508   else
509   {
510 #ifdef __APPLE__
511     int      result = -1;
512     char     given_path[2*MAXPATHLEN];
513     uint32_t path_size =2*MAXPATHLEN;
514
515     result = GetDarwinExecutablePath(given_path, &path_size);
516     if (result == 0)
517     {
518       // Move backwards to last /.
519       for (int n=strlen(given_path)-1; given_path[n] != '/'; n--)
520         given_path[n] = '\0';
521
522       #if defined(__arm__)
523         strcat(given_path, "/XBMCData/XBMCHome/");
524       #else
525         // Assume local path inside application bundle.
526         strcat(given_path, "../Resources/XBMC/");
527       #endif
528
529       // Convert to real path.
530       char real_path[2*MAXPATHLEN];
531       if (realpath(given_path, real_path) != NULL)
532       {
533         strPath = real_path;
534         return;
535       }
536     }
537 #endif
538     size_t last_sep = strHomePath.find_last_of(PATH_SEPARATOR_CHAR);
539     if (last_sep != string::npos)
540       strPath = strHomePath.Left(last_sep);
541     else
542       strPath = strHomePath;
543   }
544
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
548    */
549   CStdString installPath = INSTALL_PATH;
550   CStdString binInstallPath = BIN_INSTALL_PATH;
551   if (!strTarget.compare("XBMC_HOME") && installPath.compare(binInstallPath))
552   {
553     int pos = strPath.length() - binInstallPath.length();
554     CStdString tmp = strPath;
555     tmp.erase(0, pos);
556     if (!tmp.compare(binInstallPath))
557     {
558       strPath.erase(pos, strPath.length());
559       strPath.append(installPath);
560     }
561   }
562 #endif
563 }
564
565 bool CUtil::IsPicture(const CStdString& strFile)
566 {
567   CStdString extension = URIUtils::GetExtension(strFile);
568
569   if (extension.IsEmpty())
570     return false;
571
572   extension.ToLower();
573   if (g_settings.m_pictureExtensions.Find(extension) != -1)
574     return true;
575
576   if (extension == ".tbn" || extension == ".dds")
577     return true;
578
579   return false;
580 }
581
582 bool CUtil::ExcludeFileOrFolder(const CStdString& strFileOrFolder, const CStdStringArray& regexps)
583 {
584   if (strFileOrFolder.IsEmpty())
585     return false;
586
587   CRegExp regExExcludes(true);  // case insensitive regex
588
589   for (unsigned int i = 0; i < regexps.size(); i++)
590   {
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());
594       continue;
595     }
596     if (regExExcludes.RegFind(strFileOrFolder) > -1)
597     {
598       CLog::Log(LOGDEBUG, "%s: File '%s' excluded. (Matches exclude rule RegExp:'%s')", __FUNCTION__, strFileOrFolder.c_str(), regexps[i].c_str());
599       return true;
600     }
601   }
602   return false;
603 }
604
605 void CUtil::GetFileAndProtocol(const CStdString& strURL, CStdString& strDir)
606 {
607   strDir = strURL;
608   if (!URIUtils::IsRemote(strURL)) return ;
609   if (URIUtils::IsDVD(strURL)) return ;
610
611   CURL url(strURL);
612   strDir.Format("%s://%s", url.GetProtocol().c_str(), url.GetFileName().c_str());
613 }
614
615 int CUtil::GetDVDIfoTitle(const CStdString& strFile)
616 {
617   CStdString strFilename = URIUtils::GetFileName(strFile);
618   if (strFilename.Equals("video_ts.ifo")) return 0;
619   //VTS_[TITLE]_0.IFO
620   return atoi(strFilename.Mid(4, 2).c_str());
621 }
622
623 CStdString CUtil::GetFileMD5(const CStdString& strPath)
624 {
625   CFile file;
626   CStdString result;
627   if (file.Open(strPath))
628   {
629     XBMC::XBMC_MD5 md5;
630     char temp[1024];
631     int pos=0;
632     int read=1;
633     while (read > 0)
634     {
635       read = file.Read(temp,1024);
636       pos += read;
637       md5.append(temp,read);
638     }
639     md5.getDigest(result);
640     file.Close();
641   }
642
643   return result;
644 }
645
646 bool CUtil::GetDirectoryName(const CStdString& strFileName, CStdString& strDescription)
647 {
648   CStdString strFName = URIUtils::GetFileName(strFileName);
649   strDescription = strFileName.Left(strFileName.size() - strFName.size());
650   URIUtils::RemoveSlashAtEnd(strDescription);
651
652   int iPos = strDescription.ReverseFind("\\");
653   if (iPos < 0)
654     iPos = strDescription.ReverseFind("/");
655   if (iPos >= 0)
656   {
657     CStdString strTmp = strDescription.Right(strDescription.size()-iPos-1);
658     strDescription = strTmp;//strDescription.Right(strDescription.size() - iPos - 1);
659   }
660   else if (strDescription.size() <= 0)
661     strDescription = strFName;
662   return true;
663 }
664
665 void CUtil::GetDVDDriveIcon( const CStdString& strPath, CStdString& strIcon )
666 {
667   if ( !g_mediaManager.IsDiscInDrive() )
668   {
669     strIcon = "DefaultDVDEmpty.png";
670     return ;
671   }
672
673   if ( URIUtils::IsDVD(strPath) )
674   {
675 #ifdef HAS_DVD_DRIVE
676     CCdInfo* pInfo = g_mediaManager.GetCdInfo();
677     //  xbox DVD
678     if ( pInfo != NULL && pInfo->IsUDFX( 1 ) )
679     {
680       strIcon = "DefaultXboxDVD.png";
681       return ;
682     }
683 #endif
684     strIcon = "DefaultDVDRom.png";
685     return ;
686   }
687
688   if ( URIUtils::IsISO9660(strPath) )
689   {
690 #ifdef HAS_DVD_DRIVE
691     CCdInfo* pInfo = g_mediaManager.GetCdInfo();
692     if ( pInfo != NULL && pInfo->IsVideoCd( 1 ) )
693     {
694       strIcon = "DefaultVCD.png";
695       return ;
696     }
697 #endif
698     strIcon = "DefaultDVDRom.png";
699     return ;
700   }
701
702   if ( URIUtils::IsCDDA(strPath) )
703   {
704     strIcon = "DefaultCDDA.png";
705     return ;
706   }
707 }
708
709 void CUtil::RemoveTempFiles()
710 {
711   CStdString searchPath = g_settings.GetDatabaseFolder();
712   CFileItemList items;
713   if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".tmp", false))
714     return;
715
716   for (int i = 0; i < items.Size(); ++i)
717   {
718     if (items[i]->m_bIsFolder)
719       continue;
720     XFILE::CFile::Delete(items[i]->GetPath());
721   }
722 }
723
724 void CUtil::ClearSubtitles()
725 {
726   //delete cached subs
727   CFileItemList items;
728   CDirectory::GetDirectory("special://temp/",items);
729   for( int i=0;i<items.Size();++i)
730   {
731     if (!items[i]->m_bIsFolder)
732     {
733       if ( items[i]->GetPath().Find("subtitle") >= 0 || items[i]->GetPath().Find("vobsub_queue") >= 0 )
734       {
735         CLog::Log(LOGDEBUG, "%s - Deleting temporary subtitle %s", __FUNCTION__, items[i]->GetPath().c_str());
736         CFile::Delete(items[i]->GetPath());
737       }
738     }
739   }
740 }
741
742 static const char * sub_exts[] = { ".utf", ".utf8", ".utf-8", ".sub", ".srt", ".smi", ".rt", ".txt", ".ssa", ".aqt", ".jss", ".ass", ".idx", NULL};
743
744 int64_t CUtil::ToInt64(uint32_t high, uint32_t low)
745 {
746   int64_t n;
747   n = high;
748   n <<= 32;
749   n += low;
750   return n;
751 }
752
753 bool CUtil::ThumbExists(const CStdString& strFileName, bool bAddCache)
754 {
755   return CThumbnailCache::GetThumbnailCache()->ThumbExists(strFileName, bAddCache);
756 }
757
758 void CUtil::ThumbCacheAdd(const CStdString& strFileName, bool bFileExists)
759 {
760   CThumbnailCache::GetThumbnailCache()->Add(strFileName, bFileExists);
761 }
762
763 void CUtil::ThumbCacheClear()
764 {
765   CThumbnailCache::GetThumbnailCache()->Clear();
766 }
767
768 bool CUtil::ThumbCached(const CStdString& strFileName)
769 {
770   return CThumbnailCache::GetThumbnailCache()->IsCached(strFileName);
771 }
772
773 void CUtil::PlayDVD(const CStdString& strProtocol, bool restart)
774 {
775 #if defined(HAS_DVDPLAYER) && defined(HAS_DVD_DRIVE)
776   CIoSupport::Dismount("Cdrom0");
777   CIoSupport::RemapDriveLetter('D', "Cdrom0");
778   CStdString strPath;
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);
786 #endif
787 }
788
789 CStdString CUtil::GetNextFilename(const CStdString &fn_template, int max)
790 {
791   if (!fn_template.Find("%03d"))
792     return "";
793
794   CStdString searchPath;
795   URIUtils::GetDirectory(fn_template, searchPath);
796   CStdString mask = URIUtils::GetExtension(fn_template);
797
798   CStdString name;
799   name.Format(fn_template.c_str(), 0);
800
801   CFileItemList items;
802   if (!CDirectory::GetDirectory(searchPath, items, mask, false))
803     return name;
804
805   items.SetFastLookup(true);
806   for (int i = 0; i <= max; i++)
807   {
808     CStdString name;
809     name.Format(fn_template.c_str(), i);
810     if (!items.Get(name))
811       return name;
812   }
813   return "";
814 }
815
816 CStdString CUtil::GetNextPathname(const CStdString &path_template, int max)
817 {
818   if (!path_template.Find("%04d"))
819     return "";
820   
821   for (int i = 0; i <= max; i++)
822   {
823     CStdString name;
824     name.Format(path_template.c_str(), i);
825     if (!CFile::Exists(name))
826       return name;
827   }
828   return "";
829 }
830
831 void CUtil::Tokenize(const CStdString& path, vector<CStdString>& tokens, const string& delimiters)
832 {
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);
838
839   while (string::npos != pos || string::npos != lastPos)
840   {
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);
847   }
848 }
849
850 void CUtil::TakeScreenshot(const CStdString &filename, bool sync)
851 {
852   int            width;
853   int            height;
854   int            stride;
855   unsigned char* outpixels = NULL;
856
857 #ifdef HAS_DX
858   LPDIRECT3DSURFACE9 lpSurface = NULL, lpBackbuffer = NULL;
859   g_graphicsContext.Lock();
860   if (g_application.IsPlayingVideo())
861   {
862 #ifdef HAS_VIDEO_PLAYBACK
863     g_renderManager.SetupScreenshot();
864 #endif
865   }
866   g_application.RenderNoPresent();
867
868   if (FAILED(g_Windowing.Get3DDevice()->CreateOffscreenPlainSurface(g_Windowing.GetWidth(), g_Windowing.GetHeight(), D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, &lpSurface, NULL)))
869     return;
870
871   if (FAILED(g_Windowing.Get3DDevice()->GetRenderTarget(0, &lpBackbuffer)))
872     return;
873
874   // now take screenshot
875   if (SUCCEEDED(g_Windowing.Get3DDevice()->GetRenderTargetData(lpBackbuffer, lpSurface)))
876   {
877     D3DLOCKED_RECT lr;
878     D3DSURFACE_DESC desc;
879     lpSurface->GetDesc(&desc);
880     if (SUCCEEDED(lpSurface->LockRect(&lr, NULL, D3DLOCK_READONLY)))
881     {
882       width = desc.Width;
883       height = desc.Height;
884       stride = lr.Pitch;
885       outpixels = new unsigned char[height * stride];
886       memcpy(outpixels, lr.pBits, height * stride);
887       lpSurface->UnlockRect();
888     }
889     else
890     {
891       CLog::Log(LOGERROR, "%s LockRect failed", __FUNCTION__);
892     }
893   }
894   else
895   {
896     CLog::Log(LOGERROR, "%s GetBackBuffer failed", __FUNCTION__);
897   }
898   lpSurface->Release();
899   lpBackbuffer->Release();
900
901   g_graphicsContext.Unlock();
902
903 #elif defined(HAS_GL) || defined(HAS_GLES)
904
905   g_graphicsContext.BeginPaint();
906   if (g_application.IsPlayingVideo())
907   {
908 #ifdef HAS_VIDEO_PLAYBACK
909     g_renderManager.SetupScreenshot();
910 #endif
911   }
912   g_application.RenderNoPresent();
913 #ifndef HAS_GLES
914   glReadBuffer(GL_BACK);
915 #endif
916   //get current viewport
917   GLint viewport[4];
918   glGetIntegerv(GL_VIEWPORT, viewport);
919
920   width  = viewport[2] - viewport[0];
921   height = viewport[3] - viewport[1];
922   stride = width * 4;
923   unsigned char* pixels = new unsigned char[stride * height];
924
925   //read pixels from the backbuffer
926 #if HAS_GLES == 2
927   glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)pixels);
928 #else
929   glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid*)pixels);
930 #endif
931   g_graphicsContext.EndPaint();
932
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);
937
938   delete [] pixels;
939
940 #else
941   //nothing to take a screenshot from
942   return;
943 #endif
944
945   if (!outpixels)
946   {
947     CLog::Log(LOGERROR, "Screenshot %s failed", filename.c_str());
948     return;
949   }
950
951   CLog::Log(LOGDEBUG, "Saving screenshot %s", filename.c_str());
952
953   //set alpha byte to 0xFF
954   for (int y = 0; y < height; y++)
955   {
956     unsigned char* alphaptr = outpixels - 1 + y * stride;
957     for (int x = 0; x < width; x++)
958       *(alphaptr += 4) = 0xFF;
959   }
960
961   //if sync is true, the png file needs to be completely written when this function returns
962   if (sync)
963   {
964     if (!CPicture::CreateThumbnailFromSurface(outpixels, width, height, stride, filename))
965       CLog::Log(LOGERROR, "Unable to write screenshot %s", filename.c_str());
966
967     delete [] outpixels;
968   }
969   else
970   {
971     //make sure the file exists to avoid concurrency issues
972     FILE* fp = fopen(filename.c_str(), "w");
973     if (fp)
974       fclose(fp);
975     else
976       CLog::Log(LOGERROR, "Unable to create file %s", filename.c_str());
977
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);
982   }
983 }
984
985 void CUtil::TakeScreenshot()
986 {
987   static bool savingScreenshots = false;
988   static vector<CStdString> screenShots;
989
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())
994   {
995     strDir = "special://temp/";
996     if (!savingScreenshots)
997     {
998       promptUser = true;
999       savingScreenshots = true;
1000       screenShots.clear();
1001     }
1002   }
1003   URIUtils::RemoveSlashAtEnd(strDir);
1004
1005   if (!strDir.IsEmpty())
1006   {
1007     CStdString file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(strDir, "screenshot%03d.png"), 999);
1008
1009     if (!file.IsEmpty())
1010     {
1011       TakeScreenshot(file, false);
1012       if (savingScreenshots)
1013         screenShots.push_back(file);
1014       if (promptUser)
1015       { // grab the real directory
1016         CStdString newDir = g_guiSettings.GetString("debug.screenshotpath");
1017         if (!newDir.IsEmpty())
1018         {
1019           for (unsigned int i = 0; i < screenShots.size(); i++)
1020           {
1021             CStdString file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(newDir, "screenshot%03d.png"), 999);
1022             CFile::Cache(screenShots[i], file);
1023           }
1024           screenShots.clear();
1025         }
1026         savingScreenshots = false;
1027       }
1028     }
1029     else
1030     {
1031       CLog::Log(LOGWARNING, "Too many screen shots or invalid folder");
1032     }
1033   }
1034 }
1035
1036 void CUtil::StatToStatI64(struct _stati64 *result, struct stat *stat)
1037 {
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;
1046
1047 #ifndef _LINUX
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);
1051 #else
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);
1055 #endif
1056 }
1057
1058 void CUtil::Stat64ToStatI64(struct _stati64 *result, struct __stat64 *stat)
1059 {
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;
1068 #ifndef _LINUX
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);
1072 #else
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);
1076 #endif
1077 }
1078
1079 void CUtil::StatI64ToStat64(struct __stat64 *result, struct _stati64 *stat)
1080 {
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;
1089 #ifndef _LINUX
1090   result->st_atime = stat->st_atime;
1091   result->st_mtime = stat->st_mtime;
1092   result->st_ctime = stat->st_ctime;
1093 #else
1094   result->st_atime = stat->_st_atime;
1095   result->st_mtime = stat->_st_mtime;
1096   result->st_ctime = stat->_st_ctime;
1097 #endif
1098 }
1099
1100 void CUtil::Stat64ToStat(struct stat *result, struct __stat64 *stat)
1101 {
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;
1109 #ifndef _LINUX
1110   if (stat->st_size <= LONG_MAX)
1111     result->st_size = (_off_t)stat->st_size;
1112 #else
1113   if (sizeof(stat->st_size) <= sizeof(result->st_size) )
1114     result->st_size = (off_t)stat->st_size;
1115 #endif
1116   else
1117   {
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");
1120   }
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);
1124 }
1125
1126 #ifdef _WIN32
1127 void CUtil::Stat64ToStat64i32(struct _stat64i32 *result, struct __stat64 *stat)
1128 {
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;
1136 #ifndef _LINUX
1137   if (stat->st_size <= LONG_MAX)
1138     result->st_size = (_off_t)stat->st_size;
1139 #else
1140   if (sizeof(stat->st_size) <= sizeof(result->st_size) )
1141     result->st_size = (off_t)stat->st_size;
1142 #endif
1143   else
1144   {
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");
1147   }
1148 #ifndef _LINUX
1149   result->st_atime = stat->st_atime;
1150   result->st_mtime = stat->st_mtime;
1151   result->st_ctime = stat->st_ctime;
1152 #else
1153   result->st_atime = stat->_st_atime;
1154   result->st_mtime = stat->_st_mtime;
1155   result->st_ctime = stat->_st_ctime;
1156 #endif
1157 }
1158 #endif
1159
1160 bool CUtil::CreateDirectoryEx(const CStdString& strPath)
1161 {
1162   // Function to create all directories at once instead
1163   // of calling CreateDirectory for every subdir.
1164   // Creates the directory and subdirectories if needed.
1165
1166   // return true if directory already exist
1167   if (CDirectory::Exists(strPath)) return true;
1168
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))
1171   {
1172     CLog::Log(LOGERROR,"%s called with an unsupported path: %s", __FUNCTION__, strPath.c_str());
1173     return false;
1174   }
1175
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 ++)
1180   {
1181     dir = URIUtils::AddFileToFolder(dir, *it);
1182     CDirectory::Create(dir);
1183   }
1184
1185   // was the final destination directory successfully created ?
1186   if (!CDirectory::Exists(strPath)) return false;
1187   return true;
1188 }
1189
1190 CStdString CUtil::MakeLegalFileName(const CStdString &strFile, int LegalType)
1191 {
1192   CStdString result = strFile;
1193
1194   result.Replace('/', '_');
1195   result.Replace('\\', '_');
1196   result.Replace('?', '_');
1197
1198   if (LegalType == LEGAL_WIN32_COMPAT)
1199   {
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(" ");
1210   }
1211   return result;
1212 }
1213
1214 // legalize entire path
1215 CStdString CUtil::MakeLegalPath(const CStdString &strPathAndFile, int LegalType)
1216 {
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
1223
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);
1234   return dir;
1235 }
1236
1237 CStdString CUtil::ValidatePath(const CStdString &path, bool bFixDoubleSlashes /* = false */)
1238 {
1239   CStdString result = path;
1240
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:") ))
1250     return result;
1251
1252   // check the path for incorrect slashes
1253 #ifdef _WIN32
1254   if (URIUtils::IsDOSPath(path))
1255   {
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.
1260     */
1261     if (bFixDoubleSlashes)
1262     {
1263       // Fixup for double back slashes (but ignore the \\ of unc-paths)
1264       for (int x = 1; x < result.GetLength() - 1; x++)
1265       {
1266         if (result[x] == '\\' && result[x+1] == '\\')
1267           result.Delete(x);
1268       }
1269     }
1270   }
1271   else if (path.Find("://") >= 0 || path.Find(":\\\\") >= 0)
1272 #endif
1273   {
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.
1278     */
1279     if (bFixDoubleSlashes)
1280     {
1281       // Fixup for double forward slashes(/) but don't touch the :// of URLs
1282       for (int x = 2; x < result.GetLength() - 1; x++)
1283       {
1284         if ( result[x] == '/' && result[x + 1] == '/' && !(result[x - 1] == ':' || (result[x - 1] == '/' && result[x - 2] == ':')) )
1285           result.Delete(x);
1286       }
1287     }
1288   }
1289   return result;
1290 }
1291
1292 bool CUtil::IsUsingTTFSubtitles()
1293 {
1294   return URIUtils::GetExtension(g_guiSettings.GetString("subtitles.font")).Equals(".ttf");
1295 }
1296
1297 #ifdef UNIT_TESTING
1298 bool CUtil::TestSplitExec()
1299 {
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")
1304     return false;
1305   params.clear();
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\"")
1308     return false;
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\\")
1311     return false;
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\\")
1314     return false;
1315   CUtil::SplitExecFunction("SetProperty(Foo,\"\")", function, params);
1316   if (function != "SetProperty" || params.size() != 2 || params[0] != "Foo" || params[1] != "")
1317    return false;
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)")
1320     return false;
1321   return true;
1322 }
1323 #endif
1324
1325 void CUtil::SplitExecFunction(const CStdString &execString, CStdString &function, vector<CStdString> &parameters)
1326 {
1327   CStdString paramString;
1328
1329   int iPos = execString.Find("(");
1330   int iPos2 = execString.ReverseFind(")");
1331   if (iPos > 0 && iPos2 > 0)
1332   {
1333     paramString = execString.Mid(iPos + 1, iPos2 - iPos - 1);
1334     function = execString.Left(iPos);
1335   }
1336   else
1337     function = execString;
1338
1339   // remove any whitespace, and the standard prefix (if it exists)
1340   function.Trim();
1341   if( function.Left(5).Equals("xbmc.", false) )
1342     function.Delete(0, 5);
1343
1344   SplitParams(paramString, parameters);
1345 }
1346
1347 void CUtil::SplitParams(const CStdString &paramString, std::vector<CStdString> &parameters)
1348 {
1349   bool inQuotes = false;
1350   bool lastEscaped = false; // only every second character can be escaped
1351   int inFunction = 0;
1352   size_t whiteSpacePos = 0;
1353   CStdString parameter;
1354   parameters.clear();
1355   for (size_t pos = 0; pos < paramString.size(); pos++)
1356   {
1357     char ch = paramString[pos];
1358     bool escaped = (pos > 0 && paramString[pos - 1] == '\\' && !lastEscaped);
1359     lastEscaped = escaped;
1360     if (inQuotes)
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
1364         inQuotes = false;
1365       }
1366     }
1367     else
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
1371         inQuotes = true;
1372       }
1373       if (inFunction && ch == ')')
1374       { // end of a function
1375         inFunction--;
1376       }
1377       if (ch == '(')
1378       { // start of function
1379         inFunction++;
1380       }
1381       if (!inFunction && ch == ',')
1382       { // not in a function, so a comma signfies the end of this parameter
1383         if (whiteSpacePos)
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);
1389         parameter.Empty();
1390         whiteSpacePos = 0;
1391         continue;
1392       }
1393     }
1394     if ((ch == '\"' || ch == '\\') && escaped)
1395     { // escaped quote or backslash
1396       parameter[parameter.size()-1] = ch;
1397       continue;
1398     }
1399     // whitespace handling - we skip any whitespace at the left or right of an unquoted parameter
1400     if (ch == ' ' && !inQuotes)
1401     {
1402       if (parameter.IsEmpty()) // skip whitespace on left
1403         continue;
1404       if (!whiteSpacePos) // make a note of where whitespace starts on the right
1405         whiteSpacePos = parameter.size();
1406     }
1407     else
1408       whiteSpacePos = 0;
1409     parameter += ch;
1410   }
1411   if (inFunction || inQuotes)
1412     CLog::Log(LOGWARNING, "%s(%s) - end of string while searching for ) or \"", __FUNCTION__, paramString.c_str());
1413   if (whiteSpacePos)
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);
1420 }
1421
1422 int CUtil::GetMatchingSource(const CStdString& strPath1, VECSOURCES& VECSOURCES, bool& bIsSourceName)
1423 {
1424   if (strPath1.IsEmpty())
1425     return -1;
1426
1427   //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, testing original path/name [%s]", strPath1.c_str());
1428
1429   // copy as we may change strPath
1430   CStdString strPath = strPath1;
1431
1432   // Check for special protocols
1433   CURL checkURL(strPath);
1434
1435   // stack://
1436   if (checkURL.GetProtocol() == "stack")
1437     strPath.Delete(0, 8); // remove the stack protocol
1438
1439   if (checkURL.GetProtocol() == "shout")
1440     strPath = checkURL.GetHostName();
1441   if (checkURL.GetProtocol() == "lastfm")
1442     return 1;
1443   if (checkURL.GetProtocol() == "tuxbox")
1444     return 1;
1445   if (checkURL.GetProtocol() == "plugin")
1446     return 1;
1447   if (checkURL.GetProtocol() == "multipath")
1448     strPath = CMultiPathDirectory::GetFirstPath(strPath);
1449
1450   //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, testing for matching name [%s]", strPath.c_str());
1451   bIsSourceName = false;
1452   int iIndex = -1;
1453   int iLength = -1;
1454   // we first test the NAME of a source
1455   for (int i = 0; i < (int)VECSOURCES.size(); ++i)
1456   {
1457     CMediaSource share = VECSOURCES.at(i);
1458     CStdString strName = share.strName;
1459
1460     // special cases for dvds
1461     if (URIUtils::IsOnDVD(share.strPath))
1462     {
1463       if (URIUtils::IsOnDVD(strPath))
1464         return i;
1465
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('(');
1470       if (iPos > 1)
1471         strName = strName.Mid(0, iPos - 1);
1472     }
1473     //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, comparing name [%s]", strName.c_str());
1474     if (strPath.Equals(strName))
1475     {
1476       bIsSourceName = true;
1477       return i;
1478     }
1479   }
1480
1481   // now test the paths
1482
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))
1490     strDest += "/";
1491   int iLenPath = strDest.size();
1492
1493   //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, testing url [%s]", strDest.c_str());
1494
1495   for (int i = 0; i < (int)VECSOURCES.size(); ++i)
1496   {
1497     CMediaSource share = VECSOURCES.at(i);
1498
1499     // does it match a source name?
1500     if (share.strPath.substr(0,8) == "shout://")
1501     {
1502       CURL url(share.strPath);
1503       if (strPath.Equals(url.GetHostName()))
1504         return i;
1505     }
1506
1507     // doesnt match a name, so try the source path
1508     vector<CStdString> vecPaths;
1509
1510     // add any concatenated paths if they exist
1511     if (share.vecPaths.size() > 0)
1512       vecPaths = share.vecPaths;
1513
1514     // add the actual share path at the front of the vector
1515     vecPaths.insert(vecPaths.begin(), share.strPath);
1516
1517     // test each path
1518     for (int j = 0; j < (int)vecPaths.size(); ++j)
1519     {
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))
1527         strShare += "/";
1528       int iLenShare = strShare.size();
1529       //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, comparing url [%s]", strShare.c_str());
1530
1531       if ((iLenPath >= iLenShare) && (strDest.Left(iLenShare).Equals(strShare)) && (iLenShare > iLength))
1532       {
1533         //CLog::Log(LOGDEBUG,"Found matching source at index %i: [%s], Len = [%i]", i, strShare.c_str(), iLenShare);
1534
1535         // if exact match, return it immediately
1536         if (iLenPath == iLenShare)
1537         {
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;
1543           return i;
1544         }
1545         iIndex = i;
1546         iLength = iLenShare;
1547       }
1548     }
1549   }
1550
1551   // return the index of the share with the longest match
1552   if (iIndex == -1)
1553   {
1554
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://") )
1558     {
1559       // get the hostname portion of the url since it contains the archive file
1560       strPath = checkURL.GetHostName();
1561
1562       bIsSourceName = false;
1563       bool bDummy;
1564       return GetMatchingSource(strPath, VECSOURCES, bDummy);
1565     }
1566
1567     CLog::Log(LOGWARNING,"CUtil::GetMatchingSource... no matching source found for [%s]", strPath1.c_str());
1568   }
1569   return iIndex;
1570 }
1571
1572 CStdString CUtil::TranslateSpecialSource(const CStdString &strSpecial)
1573 {
1574   CStdString strReturn=strSpecial;
1575   if (!strSpecial.IsEmpty() && strSpecial[0] == '$')
1576   {
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);
1600   }
1601   return strReturn;
1602 }
1603
1604 CStdString CUtil::MusicPlaylistsLocation()
1605 {
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);;
1613 }
1614
1615 CStdString CUtil::VideoPlaylistsLocation()
1616 {
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);;
1624 }
1625
1626 void CUtil::DeleteMusicDatabaseDirectoryCache()
1627 {
1628   CUtil::DeleteDirectoryCache("mdb-");
1629 }
1630
1631 void CUtil::DeleteVideoDatabaseDirectoryCache()
1632 {
1633   CUtil::DeleteDirectoryCache("vdb-");
1634 }
1635
1636 void CUtil::DeleteDirectoryCache(const CStdString &prefix)
1637 {
1638   CStdString searchPath = "special://temp/";
1639   CFileItemList items;
1640   if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".fi", false))
1641     return;
1642
1643   for (int i = 0; i < items.Size(); ++i)
1644   {
1645     if (items[i]->m_bIsFolder)
1646       continue;
1647     CStdString fileName = URIUtils::GetFileName(items[i]->GetPath());
1648     if (fileName.Left(prefix.GetLength()) == prefix)
1649       XFILE::CFile::Delete(items[i]->GetPath());
1650   }
1651 }
1652
1653 bool CUtil::SetSysDateTimeYear(int iYear, int iMonth, int iDay, int iHour, int iMinute)
1654 {
1655   TIME_ZONE_INFORMATION tziNew;
1656   SYSTEMTIME CurTime;
1657   SYSTEMTIME NewTime;
1658   GetLocalTime(&CurTime);
1659   GetLocalTime(&NewTime);
1660   int iRescBiases, iHourUTC;
1661   int iMinuteNew;
1662
1663   DWORD dwRet = GetTimeZoneInformation(&tziNew);  // Get TimeZone Informations
1664   float iGMTZone = (float(tziNew.Bias)/(60));     // Calc's the GMT Time
1665
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);
1671
1672   switch (dwRet)
1673   {
1674     case TIME_ZONE_ID_STANDARD:
1675       {
1676         iRescBiases   = tziNew.Bias + tziNew.StandardBias;
1677         CLog::Log(LOGDEBUG, "-   Timezone ID: 1, Standart");
1678       }
1679       break;
1680     case TIME_ZONE_ID_DAYLIGHT:
1681       {
1682         iRescBiases   = tziNew.Bias + tziNew.StandardBias + tziNew.DaylightBias;
1683         CLog::Log(LOGDEBUG, "-   Timezone ID: 2, Daylight");
1684       }
1685       break;
1686     case TIME_ZONE_ID_UNKNOWN:
1687       {
1688         iRescBiases   = tziNew.Bias + tziNew.StandardBias;
1689         CLog::Log(LOGDEBUG, "-   Timezone ID: 0, Unknown");
1690       }
1691       break;
1692     case TIME_ZONE_ID_INVALID:
1693       {
1694         iRescBiases   = tziNew.Bias + tziNew.StandardBias;
1695         CLog::Log(LOGDEBUG, "-   Timezone ID: Invalid");
1696       }
1697       break;
1698     default:
1699       iRescBiases   = tziNew.Bias + tziNew.StandardBias;
1700   }
1701     CLog::Log(LOGDEBUG, "--------------- END ---------------");
1702
1703   // Calculation
1704   iHourUTC = GMTZoneCalc(iRescBiases, iHour, iMinute, iMinuteNew);
1705   iMinute = iMinuteNew;
1706   if(iHourUTC <0)
1707   {
1708     iDay = iDay - 1;
1709     iHourUTC =iHourUTC + 24;
1710   }
1711   if(iHourUTC >23)
1712   {
1713     iDay = iDay + 1;
1714     iHourUTC =iHourUTC - 24;
1715   }
1716
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;
1723
1724   FILETIME stNewTime, stCurTime;
1725   SystemTimeToFileTime(&NewTime, &stNewTime);
1726   SystemTimeToFileTime(&CurTime, &stCurTime);
1727   return false;
1728 }
1729 int CUtil::GMTZoneCalc(int iRescBiases, int iHour, int iMinute, int &iMinuteNew)
1730 {
1731   int iHourUTC, iTemp;
1732   iMinuteNew = iMinute;
1733   iTemp = iRescBiases/60;
1734
1735   if (iRescBiases == 0 )return iHour;   // GMT Zone 0, no need calculate
1736   if (iRescBiases > 0)
1737     iHourUTC = iHour + abs(iTemp);
1738   else
1739     iHourUTC = iHour - abs(iTemp);
1740
1741   if ((iTemp*60) != iRescBiases)
1742   {
1743     if (iRescBiases > 0)
1744       iMinuteNew = iMinute + abs(iTemp*60 - iRescBiases);
1745     else
1746       iMinuteNew = iMinute - abs(iTemp*60 - iRescBiases);
1747
1748     if (iMinuteNew >= 60)
1749     {
1750       iMinuteNew = iMinuteNew -60;
1751       iHourUTC = iHourUTC + 1;
1752     }
1753     else if (iMinuteNew < 0)
1754     {
1755       iMinuteNew = iMinuteNew +60;
1756       iHourUTC = iHourUTC - 1;
1757     }
1758   }
1759   return iHourUTC;
1760 }
1761
1762 void CUtil::GetRecursiveListing(const CStdString& strPath, CFileItemList& items, const CStdString& strMask, bool bUseFileDirectories)
1763 {
1764   CFileItemList myItems;
1765   CDirectory::GetDirectory(strPath,myItems,strMask,bUseFileDirectories);
1766   for (int i=0;i<myItems.Size();++i)
1767   {
1768     if (myItems[i]->m_bIsFolder)
1769       CUtil::GetRecursiveListing(myItems[i]->GetPath(),items,strMask,bUseFileDirectories);
1770     else
1771       items.Add(myItems[i]);
1772   }
1773 }
1774
1775 void CUtil::GetRecursiveDirsListing(const CStdString& strPath, CFileItemList& item)
1776 {
1777   CFileItemList myItems;
1778   CDirectory::GetDirectory(strPath,myItems,"",false);
1779   for (int i=0;i<myItems.Size();++i)
1780   {
1781     if (myItems[i]->m_bIsFolder && !myItems[i]->GetPath().Equals(".."))
1782     {
1783       item.Add(myItems[i]);
1784       CUtil::GetRecursiveDirsListing(myItems[i]->GetPath(),item);
1785     }
1786   }
1787 }
1788
1789 void CUtil::ForceForwardSlashes(CStdString& strPath)
1790 {
1791   int iPos = strPath.ReverseFind('\\');
1792   while (iPos > 0)
1793   {
1794     strPath.at(iPos) = '/';
1795     iPos = strPath.ReverseFind('\\');
1796   }
1797 }
1798
1799 double CUtil::AlbumRelevance(const CStdString& strAlbumTemp1, const CStdString& strAlbum1, const CStdString& strArtistTemp1, const CStdString& strArtist1)
1800 {
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())
1811   {
1812     CStdString strArtistTemp = strArtistTemp1;
1813     strArtistTemp.MakeLower();
1814     CStdString strArtist = strArtist1;
1815     strArtist.MakeLower();
1816     fArtistPercentage = fstrcmp(strArtistTemp, strArtist, 0.0f);
1817   }
1818   double fRelevance = fAlbumPercentage * 0.5f + fArtistPercentage * 0.5f;
1819   return fRelevance;
1820 }
1821
1822 bool CUtil::MakeShortenPath(CStdString StrInput, CStdString& StrOutput, int iTextMaxLength)
1823 {
1824   int iStrInputSize = StrInput.size();
1825   if((iStrInputSize <= 0) || (iTextMaxLength >= iStrInputSize))
1826     return false;
1827
1828   char cDelim = '\0';
1829   size_t nGreaterDelim, nPos;
1830
1831   nPos = StrInput.find_last_of( '\\' );
1832   if ( nPos != CStdString::npos )
1833     cDelim = '\\';
1834   else
1835   {
1836     nPos = StrInput.find_last_of( '/' );
1837     if ( nPos != CStdString::npos )
1838       cDelim = '/';
1839   }
1840   if ( cDelim == '\0' )
1841     return false;
1842
1843   if (nPos == StrInput.size() - 1)
1844   {
1845     StrInput.erase(StrInput.size() - 1);
1846     nPos = StrInput.find_last_of( cDelim );
1847   }
1848   while( iTextMaxLength < iStrInputSize )
1849   {
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();
1857   }
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, ".."))
1863       break;
1864   // finally, truncate our string to force inside our max text length,
1865   // replacing the last 2 characters with ".."
1866
1867   // eg end up with:
1868   // "smb://../Playboy Swimsuit Cal.."
1869   if (iTextMaxLength > 2 && StrInput.size() > (unsigned int)iTextMaxLength)
1870   {
1871     StrInput = StrInput.Left(iTextMaxLength - 2);
1872     StrInput += "..";
1873   }
1874   StrOutput = StrInput;
1875   return true;
1876 }
1877
1878 bool CUtil::SupportsFileOperations(const CStdString& strPath)
1879 {
1880   // currently only hd, smb, nfs and afp support delete and rename
1881   if (URIUtils::IsHD(strPath))
1882     return true;
1883   if (URIUtils::IsSmb(strPath))
1884     return true;
1885   if (URIUtils::IsNfs(strPath))
1886     return true;
1887   if (URIUtils::IsAfp(strPath))
1888     return true;
1889   if (URIUtils::IsMythTV(strPath))
1890   {
1891     /*
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
1894      * items cached.
1895      */
1896     return CMythDirectory::SupportsFileOperations(strPath);
1897   }
1898   if (URIUtils::IsStack(strPath))
1899     return SupportsFileOperations(CStackDirectory::GetFirstStackedFile(strPath));
1900   if (URIUtils::IsMultiPath(strPath))
1901     return CMultiPathDirectory::SupportsFileOperations(strPath);
1902
1903   return false;
1904 }
1905
1906 CStdString CUtil::GetDefaultFolderThumb(const CStdString &folderThumb)
1907 {
1908   if (g_TextureManager.HasTexture(folderThumb))
1909     return folderThumb;
1910   return "";
1911 }
1912
1913 void CUtil::GetSkinThemes(vector<CStdString>& vecTheme)
1914 {
1915   CStdString strPath;
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)
1921   {
1922     CFileItemPtr pItem = items[i];
1923     if (!pItem->m_bIsFolder)
1924     {
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")))
1929       {
1930         CStdString strLabel = pItem->GetLabel();
1931         vecTheme.push_back(strLabel.Mid(0, strLabel.size() - 4));
1932       }
1933     }
1934   }
1935   sort(vecTheme.begin(), vecTheme.end(), sortstringbyname());
1936 }
1937
1938 void CUtil::InitRandomSeed()
1939 {
1940   // Init random seed
1941   int64_t now;
1942   now = CurrentHostCounter();
1943   unsigned int seed = (unsigned int)now;
1944 //  CLog::Log(LOGDEBUG, "%s - Initializing random seed with %u", __FUNCTION__, seed);
1945   srand(seed);
1946 }
1947
1948 #ifdef _LINUX
1949 bool CUtil::RunCommandLine(const CStdString& cmdLine, bool waitExit)
1950 {
1951   CStdStringArray args;
1952
1953   StringUtils::SplitString(cmdLine, ",", args);
1954
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++)
1960   {
1961     CStdString &s = args[i];
1962     CStdString stripd = s.Trim();
1963     if (stripd[0] == '"' || stripd[0] == '\'')
1964     {
1965       s = s.TrimLeft();
1966       s = s.Right(s.size() - 1);
1967     }
1968     if (stripd[stripd.size() - 1] == '"' || stripd[stripd.size() - 1] == '\'')
1969     {
1970       s = s.TrimRight();
1971       s = s.Left(s.size() - 1);
1972     }
1973   }
1974
1975   return Command(args, waitExit);
1976 }
1977
1978 //
1979 // FIXME, this should be merged with the function below.
1980 //
1981 bool CUtil::Command(const CStdStringArray& arrArgs, bool waitExit)
1982 {
1983 #ifdef _DEBUG
1984   printf("Executing: ");
1985   for (size_t i=0; i<arrArgs.size(); i++)
1986     printf("%s ", arrArgs[i].c_str());
1987   printf("\n");
1988 #endif
1989
1990   pid_t child = fork();
1991   int n = 0;
1992   if (child == 0)
1993   {
1994     close(0);
1995     close(1);
1996     close(2);
1997     if (arrArgs.size() > 0)
1998     {
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);
2004     }
2005   }
2006   else
2007   {
2008     if (waitExit) waitpid(child, &n, 0);
2009   }
2010
2011   return (waitExit) ? (WEXITSTATUS(n) == 0) : true;
2012 }
2013
2014 bool CUtil::SudoCommand(const CStdString &strCommand)
2015 {
2016   CLog::Log(LOGDEBUG, "Executing sudo command: <%s>", strCommand.c_str());
2017   pid_t child = fork();
2018   int n = 0;
2019   if (child == 0)
2020   {
2021     close(0); // close stdin to avoid sudo request password
2022     close(1);
2023     close(2);
2024     CStdStringArray arrArgs;
2025     StringUtils::SplitString(strCommand, " ", arrArgs);
2026     if (arrArgs.size() > 0)
2027     {
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++)
2033       {
2034         args[i+2] = (char *)arrArgs[i].c_str();
2035       }
2036       execvp("/usr/bin/sudo", args);
2037     }
2038   }
2039   else
2040     waitpid(child, &n, 0);
2041
2042   return WEXITSTATUS(n) == 0;
2043 }
2044 #endif
2045
2046 int CUtil::LookupRomanDigit(char roman_digit)
2047 {
2048   switch (roman_digit)
2049   {
2050     case 'i':
2051     case 'I':
2052       return 1;
2053     case 'v':
2054     case 'V':
2055       return 5;
2056     case 'x':
2057     case 'X':
2058       return 10;
2059     case 'l':
2060     case 'L':
2061       return 50;
2062     case 'c':
2063     case 'C':
2064       return 100;
2065     case 'd':
2066     case 'D':
2067       return 500;
2068     case 'm':
2069     case 'M':
2070       return 1000;
2071     default:
2072       return 0;
2073   }
2074 }
2075
2076 int CUtil::TranslateRomanNumeral(const char* roman_numeral)
2077 {
2078   
2079   int decimal = -1;
2080
2081   if (roman_numeral && roman_numeral[0])
2082   {
2083     int temp_sum  = 0,
2084         last      = 0,
2085         repeat    = 0,
2086         trend     = 1;
2087     decimal = 0;
2088     while (*roman_numeral)
2089     {
2090       int digit = CUtil::LookupRomanDigit(*roman_numeral);
2091       int test  = last;
2092       
2093       // General sanity checks
2094
2095       // numeral not in LUT
2096       if (!digit)
2097         return -1;
2098       
2099       while (test > 5)
2100         test /= 10;
2101       
2102       // N = 10^n may not precede (N+1) > 10^(N+1)
2103       if (test == 1 && digit > last * 10)
2104         return -1;
2105       
2106       // N = 5*10^n may not precede (N+1) >= N
2107       if (test == 5 && digit >= last) 
2108         return -1;
2109
2110       // End general sanity checks
2111
2112       if (last < digit)
2113       {
2114         // smaller numerals may not repeat before a larger one
2115         if (repeat) 
2116           return -1;
2117
2118         temp_sum += digit;
2119         
2120         repeat  = 0;
2121         trend   = 0;
2122       }
2123       else if (last == digit)
2124       {
2125         temp_sum += digit;
2126         repeat++;
2127         trend = 1;
2128       }
2129       else
2130       {
2131         if (!repeat)
2132           decimal += 2 * last - temp_sum;
2133         else
2134           decimal += temp_sum;
2135         
2136         temp_sum = digit;
2137
2138         trend   = 1;
2139         repeat  = 0;
2140       }
2141       // Post general sanity checks
2142
2143       // numerals may not repeat more than thrice
2144       if (repeat == 3)
2145         return -1;
2146
2147       last = digit;
2148       roman_numeral++;
2149     }
2150
2151     if (trend)
2152       decimal += temp_sum;
2153     else
2154       decimal += 2 * last - temp_sum;
2155   }
2156   return decimal;
2157 }
2158
2159 CStdString CUtil::ResolveExecutablePath()
2160 {
2161   CStdString strExecutablePath;
2162 #ifdef WIN32
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;
2170
2171   GetDarwinExecutablePath(given_path, &path_size);
2172   strExecutablePath = given_path;
2173 #elif defined(__FreeBSD__)                                                                                                                                                                   
2174   char buf[PATH_MAX];
2175   size_t buflen;
2176   int mib[4];
2177
2178   mib[0] = CTL_KERN;
2179   mib[1] = KERN_PROC;
2180   mib[2] = KERN_PROC_PATHNAME;
2181   mib[3] = getpid();
2182
2183   buflen = sizeof(buf) - 1;
2184   if(sysctl(mib, 4, buf, &buflen, NULL, 0) < 0)
2185     strExecutablePath = "";
2186   else
2187     strExecutablePath = buf;
2188 #else
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);
2193
2194   /* Now read the symbolic link */
2195   char buf[PATH_MAX];
2196   int ret = readlink(linkname, buf, PATH_MAX);
2197   buf[ret] = 0;
2198
2199   strExecutablePath = buf;
2200 #endif
2201   return strExecutablePath;
2202 }
2203
2204 CStdString CUtil::GetFrameworksPath(bool forPython)
2205 {
2206   CStdString strFrameworksPath;
2207 #if defined(__APPLE__)
2208   char     given_path[2*MAXPATHLEN];
2209   uint32_t path_size =2*MAXPATHLEN;
2210
2211   GetDarwinFrameworkPath(forPython, given_path, &path_size);
2212   strFrameworksPath = given_path;
2213 #endif
2214   return strFrameworksPath;
2215 }
2216
2217 void CUtil::ScanForExternalSubtitles(const CStdString& strMovie, std::vector<CStdString>& vecSubtitles )
2218 {
2219   unsigned int startTimer = XbmcThreads::SystemClockMillis();
2220   
2221   // new array for commons sub dirs
2222   const char * common_sub_dirs[] = {"subs",
2223     "Subs",
2224     "subtitles",
2225     "Subtitles",
2226     "vobsubs",
2227     "Vobsubs",
2228     "sub",
2229     "Sub",
2230     "vobsub",
2231     "Vobsub",
2232     "subtitle",
2233     "Subtitle",
2234     NULL};
2235   
2236   vector<CStdString> vecExtensionsCached;
2237   //strExtensionCached = "";
2238   
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 ;
2245   
2246   vector<CStdString> strLookInPaths;
2247   
2248   CStdString strMovieFileName;
2249   CStdString strPath;
2250   
2251   URIUtils::Split(strMovie, strPath, strMovieFileName);
2252   CStdString strMovieFileNameNoExt(URIUtils::ReplaceExtension(strMovieFileName, ""));
2253   strLookInPaths.push_back(strPath);
2254   
2255   if (!g_settings.iAdditionalSubtitleDirectoryChecked && !g_guiSettings.GetString("subtitles.custompath").IsEmpty()) // to avoid checking non-existent directories (network) every time..
2256   {
2257     if (!g_application.getNetwork().IsAvailable() && !URIUtils::IsHD(g_guiSettings.GetString("subtitles.custompath")))
2258     {
2259       CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonaccessible");
2260       g_settings.iAdditionalSubtitleDirectoryChecked = -1; // disabled
2261     }
2262     else if (!CDirectory::Exists(g_guiSettings.GetString("subtitles.custompath")))
2263     {
2264       CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonexistant");
2265       g_settings.iAdditionalSubtitleDirectoryChecked = -1; // disabled
2266     }
2267     
2268     g_settings.iAdditionalSubtitleDirectoryChecked = 1;
2269   }
2270   
2271   if (strMovie.Left(6) == "rar://") // <--- if this is found in main path then ignore it!
2272   {
2273     CURL url(strMovie);
2274     CStdString strArchive = url.GetHostName();
2275     URIUtils::Split(strArchive, strPath, strMovieFileName);
2276     strLookInPaths.push_back(strPath);
2277   }
2278   
2279   // checking if any of the common subdirs exist ..
2280   CStdStringArray directories;
2281   int nTokens = StringUtils::SplitString( strPath, "/", directories );
2282   if (nTokens == 1)
2283     StringUtils::SplitString( strPath, "\\", directories );
2284   
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
2287   {
2288     CStdString strPath2;
2289     URIUtils::GetParentPath(strPath,strPath2);
2290     strLookInPaths.push_back(strPath2);
2291   }
2292   int iSize = strLookInPaths.size();
2293   for (int i=0;i<iSize;++i)
2294   {
2295     for (int j=0; common_sub_dirs[j]; j++)
2296     {
2297       CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],common_sub_dirs[j]);
2298       if (CDirectory::Exists(strPath2))
2299         strLookInPaths.push_back(strPath2);
2300     }
2301   }
2302   // .. done checking for common subdirs
2303   
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
2307   {
2308     CStdString cdDir;
2309     cdDir.Format("cd%i",i+1);
2310     for (int i=0;i<iSize;++i)
2311     {
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++)
2316       {
2317         // if movie file is inside cd-dir, this directory can exist in vector already
2318         if (strLookInPaths[i].Equals( strPath2 ) )
2319           pathAlreadyAdded = true;
2320       }
2321       if (CDirectory::Exists(strPath2) && !pathAlreadyAdded) 
2322         strLookInPaths.push_back(strPath2);
2323     }
2324   }
2325   // .. done checking for cd-dirs
2326   
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)
2329   {
2330     strPath = g_guiSettings.GetString("subtitles.custompath");
2331     URIUtils::AddSlashAtEnd(strPath);
2332     strLookInPaths.push_back(strPath);
2333   }
2334   
2335   CStdString strLExt;
2336   CStdString strDest;
2337   CStdString strItem;
2338   
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++)
2342   {
2343     if (strLookInPaths[step].length() != 0)
2344     {
2345       CFileItemList items;
2346       
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();
2349       
2350       for (int j = 0; j < items.Size(); j++)
2351       {
2352         URIUtils::Split(items[j]->GetPath(), strPath, strItem);
2353         
2354         // is this a rar or zip-file
2355         if (URIUtils::IsRAR(strItem) || URIUtils::IsZIP(strItem))
2356         {
2357           ScanArchiveForSubtitles( items[j]->GetPath(), strMovieFileNameNoExt, vecSubtitles );
2358         }
2359         else    // not a rar/zip file
2360         {
2361           for (int i = 0; sub_exts[i]; i++)
2362           {
2363             //Cache subtitle with same name as movie
2364             if (URIUtils::GetExtension(strItem).Equals(sub_exts[i]) && strItem.Left(fnl).Equals(strMovieFileNameNoExt))
2365             {
2366               vecSubtitles.push_back( items[j]->GetPath() ); 
2367               CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, items[j]->GetPath().c_str() );
2368             }
2369           }
2370         }
2371       }
2372       g_directoryCache.ClearDirectory(strLookInPaths[step]);
2373     }
2374   }
2375
2376   iSize = vecSubtitles.size();
2377   for (int i = 0; i < iSize; i++)
2378   {
2379     if (URIUtils::GetExtension(vecSubtitles[i]).Equals(".smi"))
2380     {
2381       //Cache multi-language sami subtitle
2382       CDVDSubtitleStream* pStream = new CDVDSubtitleStream();
2383       if(pStream->Open(vecSubtitles[i]))
2384       {
2385         CDVDSubtitleTagSami TagConv;
2386         TagConv.LoadHead(pStream);
2387         if (TagConv.m_Langclass.size() >= 2)
2388         {
2389           for (unsigned int k = 0; k < TagConv.m_Langclass.size(); k++)
2390           {
2391             strDest.Format("special://temp/subtitle.%s.%d.smi", TagConv.m_Langclass[k].Name, i);
2392             if (CFile::Cache(vecSubtitles[i], strDest))
2393             {
2394               CLog::Log(LOGINFO, " cached subtitle %s->%s\n", vecSubtitles[i].c_str(), strDest.c_str());
2395               vecSubtitles.push_back(strDest);
2396             }
2397           }
2398         }
2399       }
2400       delete pStream;
2401     }
2402   }
2403   CLog::Log(LOGDEBUG,"%s: END (total time: %i ms)", __FUNCTION__, (int)(XbmcThreads::SystemClockMillis() - startTimer));
2404 }
2405
2406 int CUtil::ScanArchiveForSubtitles( const CStdString& strArchivePath, const CStdString& strMovieFileNameNoExt, std::vector<CStdString>& vecSubtitles )
2407 {
2408   int nSubtitlesAdded = 0;
2409   CFileItemList ItemList;
2410  
2411   // zip only gets the root dir
2412   if (URIUtils::GetExtension(strArchivePath).Equals(".zip"))
2413   {
2414    CStdString strZipPath;
2415    URIUtils::CreateArchivePath(strZipPath,"zip",strArchivePath,"");
2416    if (!CDirectory::GetDirectory(strZipPath,ItemList,"",false))
2417     return false;
2418   }
2419   else
2420   {
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, "") )
2425     return false;
2426  #else
2427    return false;
2428  #endif
2429   }
2430   for (int it= 0 ; it <ItemList.Size();++it)
2431   {
2432    CStdString strPathInRar = ItemList[it]->GetPath();
2433    CStdString strExt = URIUtils::GetExtension(strPathInRar);
2434    
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))
2439    {
2440     CStdString strRarInRar;
2441     if (URIUtils::GetExtension(strPathInRar).Equals(".rar"))
2442       URIUtils::CreateArchivePath(strRarInRar, "rar", strArchivePath, strPathInRar);
2443     else
2444       URIUtils::CreateArchivePath(strRarInRar, "zip", strArchivePath, strPathInRar);
2445     ScanArchiveForSubtitles(strRarInRar,strMovieFileNameNoExt,vecSubtitles);
2446    }
2447    // done checking if this is a rar-in-rar
2448    
2449    int iPos=0;
2450     while (sub_exts[iPos])
2451     {
2452      if (strExt.CompareNoCase(sub_exts[iPos]) == 0)
2453      {
2454       CStdString strSourceUrl;
2455       if (URIUtils::GetExtension(strArchivePath).Equals(".rar"))
2456        URIUtils::CreateArchivePath(strSourceUrl, "rar", strArchivePath, strPathInRar);
2457       else
2458        strSourceUrl = strPathInRar;
2459       
2460        CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, strSourceUrl.c_str() );
2461        vecSubtitles.push_back( strSourceUrl );
2462        nSubtitlesAdded++;
2463      }
2464      
2465      iPos++;
2466     }
2467   }
2468
2469   return nSubtitlesAdded;
2470 }
2471
2472 /*! \brief in a vector of subtitles finds the corresponding .sub file for a given .idx file
2473  */
2474 bool CUtil::FindVobSubPair( const std::vector<CStdString>& vecSubtitles, const CStdString& strIdxPath, CStdString& strSubPath )
2475 {
2476   if (URIUtils::GetExtension(strIdxPath) == ".idx")
2477   {
2478     CStdString strIdxFile;
2479     CStdString strIdxDirectory;
2480     URIUtils::Split(strIdxPath, strIdxDirectory, strIdxFile);
2481     for (unsigned int j=0; j < vecSubtitles.size(); j++)
2482     {
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,"")))
2488       {
2489         strSubPath = vecSubtitles[j];
2490         return true;
2491       }
2492     }
2493   }
2494   return false;
2495 }
2496
2497 /*! \brief checks if in the vector of subtitles the given .sub file has a corresponding idx and hence is a vobsub file
2498  */
2499 bool CUtil::IsVobSub( const std::vector<CStdString>& vecSubtitles, const CStdString& strSubPath )
2500 {
2501   if (URIUtils::GetExtension(strSubPath) == ".sub")
2502   {
2503     CStdString strSubFile;
2504     CStdString strSubDirectory;
2505     URIUtils::Split(strSubPath, strSubDirectory, strSubFile);
2506     for (unsigned int j=0; j < vecSubtitles.size(); j++)
2507     {
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,"")))
2513         return true;
2514     }
2515   }
2516   return false;
2517 }
2518