Code changes to make external python work in windows. Changes credit to WiSo
[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 "system.h"
22 #ifdef __APPLE__
23 #include <sys/param.h>
24 #include <mach-o/dyld.h>
25 #endif
26
27 #ifdef _LINUX
28 #include <sys/types.h>
29 #include <dirent.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <sys/wait.h>
33 #endif
34
35 #include "Application.h"
36 #include "utils/AutoPtrHandle.h"
37 #include "Util.h"
38 #include "addons/Addon.h"
39 #include "storage/IoSupport.h"
40 #include "filesystem/StackDirectory.h"
41 #include "filesystem/VirtualPathDirectory.h"
42 #include "filesystem/MultiPathDirectory.h"
43 #include "filesystem/DirectoryCache.h"
44 #include "filesystem/SpecialProtocol.h"
45 #include "filesystem/RSSDirectory.h"
46 #include "ThumbnailCache.h"
47 #ifdef HAS_FILESYSTEM_RAR
48 #include "filesystem/RarManager.h"
49 #endif
50 #include "filesystem/MythDirectory.h"
51 #ifdef HAS_UPNP
52 #include "filesystem/UPnPDirectory.h"
53 #endif
54 #ifdef HAS_CREDITS
55 #include "Credits.h"
56 #endif
57 #ifdef HAS_VIDEO_PLAYBACK
58 #include "cores/VideoRenderers/RenderManager.h"
59 #endif
60 #include "utils/RegExp.h"
61 #include "settings/GUISettings.h"
62 #include "guilib/TextureManager.h"
63 #include "utils/fstrcmp.h"
64 #include "storage/MediaManager.h"
65 #include "guilib/DirectXGraphics.h"
66 #include "network/DNSNameCache.h"
67 #include "guilib/GUIWindowManager.h"
68 #ifdef _WIN32
69 #include <shlobj.h>
70 #include "WIN32Util.h"
71 #endif
72 #if defined(__APPLE__)
73 #include "osx/DarwinUtils.h"
74 #endif
75 #include "GUIUserMessages.h"
76 #include "filesystem/File.h"
77 #include "utils/Crc32.h"
78 #include "settings/Settings.h"
79 #include "utils/StringUtils.h"
80 #include "settings/AdvancedSettings.h"
81 #ifdef HAS_IRSERVERSUITE
82   #include "input/windows/IRServerSuite.h"
83 #endif
84 #include "guilib/LocalizeStrings.h"
85 #include "utils/md5.h"
86 #include "utils/TimeUtils.h"
87 #include "utils/URIUtils.h"
88 #include "utils/log.h"
89 #include "pictures/Picture.h"
90 #include "utils/JobManager.h"
91 #include "cores/dvdplayer/DVDSubtitles/DVDSubtitleTagSami.h"
92 #include "cores/dvdplayer/DVDSubtitles/DVDSubtitleStream.h"
93 #include "windowing/WindowingFactory.h"
94
95 using namespace std;
96 using namespace XFILE;
97
98 #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]
99 static const __int64 SECS_BETWEEN_EPOCHS = 11644473600LL;
100 static const __int64 SECS_TO_100NS = 10000000;
101
102 using namespace AUTOPTR;
103 using namespace XFILE;
104 using namespace PLAYLIST;
105
106 #ifdef HAS_DX
107 static D3DGAMMARAMP oldramp, flashramp;
108 #elif defined(HAS_SDL_2D)
109 static uint16_t oldrampRed[256];
110 static uint16_t oldrampGreen[256];
111 static uint16_t oldrampBlue[256];
112 static uint16_t flashrampRed[256];
113 static uint16_t flashrampGreen[256];
114 static uint16_t flashrampBlue[256];
115 #endif
116
117 CUtil::CUtil(void)
118 {
119 }
120
121 CUtil::~CUtil(void)
122 {}
123
124 CStdString CUtil::GetTitleFromPath(const CStdString& strFileNameAndPath, bool bIsFolder /* = false */)
125 {
126   // use above to get the filename
127   CStdString path(strFileNameAndPath);
128   URIUtils::RemoveSlashAtEnd(path);
129   CStdString strFilename = URIUtils::GetFileName(path);
130
131   CURL url(strFileNameAndPath);
132   CStdString strHostname = url.GetHostName();
133
134 #ifdef HAS_UPNP
135   // UPNP
136   if (url.GetProtocol() == "upnp")
137     strFilename = CUPnPDirectory::GetFriendlyName(strFileNameAndPath.c_str());
138 #endif
139
140   if (url.GetProtocol() == "rss")
141   {
142     CRSSDirectory dir;
143     CFileItemList items;
144     if(dir.GetDirectory(strFileNameAndPath, items) && !items.m_strTitle.IsEmpty())
145       return items.m_strTitle;
146   }
147
148   // LastFM
149   if (url.GetProtocol() == "lastfm")
150   {
151     if (strFilename.IsEmpty())
152       strFilename = g_localizeStrings.Get(15200);
153     else
154       strFilename = g_localizeStrings.Get(15200) + " - " + strFilename;
155   }
156
157   // Shoutcast
158   else if (url.GetProtocol() == "shout")
159   {
160     const int genre = strFileNameAndPath.find_first_of('=');
161     if(genre <0)
162       strFilename = g_localizeStrings.Get(260);
163     else
164       strFilename = g_localizeStrings.Get(260) + " - " + strFileNameAndPath.substr(genre+1).c_str();
165   }
166
167   // Windows SMB Network (SMB)
168   else if (url.GetProtocol() == "smb" && strFilename.IsEmpty())
169   {
170     if (url.GetHostName().IsEmpty())
171     {
172       strFilename = g_localizeStrings.Get(20171);
173     }
174     else
175     {
176       strFilename = url.GetHostName();
177     }
178   }
179   // XBMSP Network
180   else if (url.GetProtocol() == "xbms" && strFilename.IsEmpty())
181     strFilename = "XBMSP Network";
182
183   // iTunes music share (DAAP)
184   else if (url.GetProtocol() == "daap" && strFilename.IsEmpty())
185     strFilename = g_localizeStrings.Get(20174);
186
187   // HDHomerun Devices
188   else if (url.GetProtocol() == "hdhomerun" && strFilename.IsEmpty())
189     strFilename = "HDHomerun Devices";
190
191   // ReplayTV Devices
192   else if (url.GetProtocol() == "rtv")
193     strFilename = "ReplayTV Devices";
194
195   // HTS Tvheadend client
196   else if (url.GetProtocol() == "htsp")
197     strFilename = g_localizeStrings.Get(20256);
198
199   // VDR Streamdev client
200   else if (url.GetProtocol() == "vtp")
201     strFilename = g_localizeStrings.Get(20257);
202   
203   // MythTV client
204   else if (url.GetProtocol() == "myth")
205     strFilename = g_localizeStrings.Get(20258);
206
207   // SAP Streams
208   else if (url.GetProtocol() == "sap" && strFilename.IsEmpty())
209     strFilename = "SAP Streams";
210
211   // Root file views
212   else if (url.GetProtocol() == "sources")
213     strFilename = g_localizeStrings.Get(744);
214
215   // Music Playlists
216   else if (path.Left(24).Equals("special://musicplaylists"))
217     strFilename = g_localizeStrings.Get(136);
218
219   // Video Playlists
220   else if (path.Left(24).Equals("special://videoplaylists"))
221     strFilename = g_localizeStrings.Get(136);
222
223   else if ((url.GetProtocol() == "rar" || url.GetProtocol() == "zip") && strFilename.IsEmpty())
224     strFilename = URIUtils::GetFileName(url.GetHostName());
225
226   // now remove the extension if needed
227   if (!g_guiSettings.GetBool("filelists.showextensions") && !bIsFolder)
228   {
229     URIUtils::RemoveExtension(strFilename);
230     return strFilename;
231   }
232   
233   // URLDecode since the original path may be an URL
234   CURL::Decode(strFilename);
235   return strFilename;
236 }
237
238 bool CUtil::GetVolumeFromFileName(const CStdString& strFileName, CStdString& strFileTitle, CStdString& strVolumeNumber)
239 {
240   const CStdStringArray &regexps = g_advancedSettings.m_videoStackRegExps;
241
242   CStdString strFileNameTemp = strFileName;
243
244   CRegExp reg(true);
245
246   for (unsigned int i = 0; i < regexps.size(); i++)
247   {
248     CStdString strRegExp = regexps[i];
249     if (!reg.RegComp(strRegExp.c_str()))
250     { // invalid regexp - complain in logs
251       CLog::Log(LOGERROR, "Invalid RegExp:[%s]", regexps[i].c_str());
252       continue;
253     }
254 //    CLog::Log(LOGDEBUG, "Regexp:[%s]", regexps[i].c_str());
255
256     int iFoundToken = reg.RegFind(strFileName.c_str());
257     if (iFoundToken >= 0)
258     {
259       int iRegLength = reg.GetFindLen();
260       int iCount = reg.GetSubCount();
261
262       /*
263       reg.DumpOvector(LOGDEBUG);
264       CLog::Log(LOGDEBUG, "Subcount=%i", iCount);
265       for (int j = 0; j <= iCount; j++)
266       {
267         CStdString str = reg.GetMatch(j);
268         CLog::Log(LOGDEBUG, "Sub(%i):[%s]", j, str.c_str());
269       }
270       */
271
272       // simple regexp, only the volume is captured
273       if (iCount == 1)
274       {
275         strVolumeNumber = reg.GetMatch(1);
276         if (strVolumeNumber.IsEmpty()) return false;
277
278         // Remove the extension (if any).  We do this on the base filename, as the regexp
279         // match may include some of the extension (eg the "." in particular).
280         // The extension will then be added back on at the end - there is no reason
281         // to clean it off here. It will be cleaned off during the display routine, if
282         // the settings to hide extensions are turned on.
283         CStdString strFileNoExt = strFileNameTemp;
284         URIUtils::RemoveExtension(strFileNoExt);
285         CStdString strFileExt = strFileNameTemp.Right(strFileNameTemp.length() - strFileNoExt.length());
286         CStdString strFileRight = strFileNoExt.Mid(iFoundToken + iRegLength);
287         strFileTitle = strFileName.Left(iFoundToken) + strFileRight + strFileExt;
288
289         return true;
290       }
291
292       // advanced regexp with prefix (1), volume (2), and suffix (3)
293       else if (iCount == 3)
294       {
295         // second subpatten contains the stacking volume
296         strVolumeNumber = reg.GetMatch(2);
297         if (strVolumeNumber.IsEmpty()) return false;
298
299         // everything before the regexp match
300         strFileTitle = strFileName.Left(iFoundToken);
301
302         // first subpattern contains prefix
303         strFileTitle += reg.GetMatch(1);
304
305         // third subpattern contains suffix
306         strFileTitle += reg.GetMatch(3);
307
308         // everything after the regexp match
309         strFileTitle += strFileNameTemp.Mid(iFoundToken + iRegLength);
310
311         return true;
312       }
313
314       // unknown regexp format
315       else
316       {
317         CLog::Log(LOGERROR, "Incorrect movie stacking regexp format:[%s]", regexps[i].c_str());
318       }
319     }
320   }
321   return false;
322 }
323
324 void CUtil::CleanString(const CStdString& strFileName, CStdString& strTitle, CStdString& strTitleAndYear, CStdString& strYear, bool bRemoveExtension /* = false */, bool bCleanChars /* = true */)
325 {
326   strTitleAndYear = strFileName;
327
328   if (strFileName.Equals(".."))
329    return;
330
331   const CStdStringArray &regexps = g_advancedSettings.m_videoCleanStringRegExps;
332
333   CRegExp reTags(true);
334   CRegExp reYear;
335   CStdString strExtension;
336   URIUtils::GetExtension(strFileName, strExtension);
337
338   if (!reYear.RegComp(g_advancedSettings.m_videoCleanDateTimeRegExp))
339   {
340     CLog::Log(LOGERROR, "%s: Invalid datetime clean RegExp:'%s'", __FUNCTION__, g_advancedSettings.m_videoCleanDateTimeRegExp.c_str());
341   }
342   else
343   {
344     if (reYear.RegFind(strTitleAndYear.c_str()) >= 0)
345     {
346       strTitleAndYear = reYear.GetReplaceString("\\1");
347       strYear = reYear.GetReplaceString("\\2");
348     }
349   }
350
351   URIUtils::RemoveExtension(strTitleAndYear);
352
353   for (unsigned int i = 0; i < regexps.size(); i++)
354   {
355     if (!reTags.RegComp(regexps[i].c_str()))
356     { // invalid regexp - complain in logs
357       CLog::Log(LOGERROR, "%s: Invalid string clean RegExp:'%s'", __FUNCTION__, regexps[i].c_str());
358       continue;
359     }
360     int j=0;
361     if ((j=reTags.RegFind(strFileName.c_str())) > 0)
362       strTitleAndYear = strTitleAndYear.Mid(0, j);
363   }
364
365   // final cleanup - special characters used instead of spaces:
366   // all '_' tokens should be replaced by spaces
367   // if the file contains no spaces, all '.' tokens should be replaced by
368   // spaces - one possibility of a mistake here could be something like:
369   // "Dr..StrangeLove" - hopefully no one would have anything like this.
370   if (bCleanChars)
371   {
372     bool initialDots = true;
373     bool alreadyContainsSpace = (strTitleAndYear.Find(' ') >= 0);
374
375     for (int i = 0; i < (int)strTitleAndYear.size(); i++)
376     {
377       char c = strTitleAndYear.GetAt(i);
378
379       if (c != '.')
380         initialDots = false;
381
382       if ((c == '_') || ((!alreadyContainsSpace) && !initialDots && (c == '.')))
383       {
384         strTitleAndYear.SetAt(i, ' ');
385       }
386     }
387   }
388
389   strTitle = strTitleAndYear.Trim();
390
391   // append year
392   if (!strYear.IsEmpty())
393     strTitleAndYear = strTitle + " (" + strYear + ")";
394
395   // restore extension if needed
396   if (!bRemoveExtension)
397     strTitleAndYear += strExtension;
398 }
399
400 void CUtil::GetQualifiedFilename(const CStdString &strBasePath, CStdString &strFilename)
401 {
402   //Make sure you have a full path in the filename, otherwise adds the base path before.
403   CURL plItemUrl(strFilename);
404   CURL plBaseUrl(strBasePath);
405   int iDotDotLoc, iBeginCut, iEndCut;
406
407   if (plBaseUrl.IsLocal()) //Base in local directory
408   {
409     if (plItemUrl.IsLocal() ) //Filename is local or not qualified
410     {
411 #ifdef _LINUX
412       if (!( (strFilename.c_str()[1] == ':') || (strFilename.c_str()[0] == '/') ) ) //Filename not fully qualified
413 #else
414       if (!( strFilename.c_str()[1] == ':')) //Filename not fully qualified
415 #endif
416       {
417         if (strFilename.c_str()[0] == '/' || strFilename.c_str()[0] == '\\' || URIUtils::HasSlashAtEnd(strBasePath))
418         {
419           strFilename = strBasePath + strFilename;
420           strFilename.Replace('/', '\\');
421         }
422         else
423         {
424           strFilename = strBasePath + '\\' + strFilename;
425           strFilename.Replace('/', '\\');
426         }
427       }
428     }
429     strFilename.Replace("\\.\\", "\\");
430     while ((iDotDotLoc = strFilename.Find("\\..\\")) > 0)
431     {
432       iEndCut = iDotDotLoc + 4;
433       iBeginCut = strFilename.Left(iDotDotLoc).ReverseFind('\\') + 1;
434       strFilename.Delete(iBeginCut, iEndCut - iBeginCut);
435     }
436   }
437   else //Base is remote
438   {
439     if (plItemUrl.IsLocal()) //Filename is local
440     {
441 #ifdef _LINUX
442       if ( (strFilename.c_str()[1] == ':') || (strFilename.c_str()[0] == '/') )  //Filename not fully qualified
443 #else
444       if (strFilename[1] == ':') // already fully qualified
445 #endif
446         return;
447       if (strFilename.c_str()[0] == '/' || strFilename.c_str()[0] == '\\' || URIUtils::HasSlashAtEnd(strBasePath)) //Begins with a slash.. not good.. but we try to make the best of it..
448
449       {
450         strFilename = strBasePath + strFilename;
451         strFilename.Replace('\\', '/');
452       }
453       else
454       {
455         strFilename = strBasePath + '/' + strFilename;
456         strFilename.Replace('\\', '/');
457       }
458     }
459     strFilename.Replace("/./", "/");
460     while ((iDotDotLoc = strFilename.Find("/../")) > 0)
461     {
462       iEndCut = iDotDotLoc + 4;
463       iBeginCut = strFilename.Left(iDotDotLoc).ReverseFind('/') + 1;
464       strFilename.Delete(iBeginCut, iEndCut - iBeginCut);
465     }
466   }
467 }
468
469 void CUtil::RunShortcut(const char* szShortcutPath)
470 {
471 }
472
473 void CUtil::GetHomePath(CStdString& strPath, const CStdString& strTarget)
474 {
475   CStdString strHomePath;
476   strHomePath = ResolveExecutablePath();
477 #ifdef _WIN32
478   CStdStringW strPathW, strTargetW;
479   g_charsetConverter.utf8ToW(strTarget, strTargetW);
480   strPathW = _wgetenv(strTargetW);
481   g_charsetConverter.wToUTF8(strPathW,strPath);
482 #else
483   strPath = getenv(strTarget);
484 #endif
485
486   if (strPath != NULL && !strPath.IsEmpty())
487   {
488 #ifdef _WIN32
489     char tmp[1024];
490     //expand potential relative path to full path
491     if(GetFullPathName(strPath, 1024, tmp, 0) != 0)
492     {
493       strPath = tmp;
494     }
495 #endif
496   }
497   else
498   {
499 #ifdef __APPLE__
500     int      result = -1;
501     char     given_path[2*MAXPATHLEN];
502     uint32_t path_size =2*MAXPATHLEN;
503
504     result = GetDarwinExecutablePath(given_path, &path_size);
505     if (result == 0)
506     {
507       // Move backwards to last /.
508       for (int n=strlen(given_path)-1; given_path[n] != '/'; n--)
509         given_path[n] = '\0';
510
511       #if defined(__arm__)
512         strcat(given_path, "/XBMCData/XBMCHome/");
513       #else
514         // Assume local path inside application bundle.
515         strcat(given_path, "../Resources/XBMC/");
516       #endif
517
518       // Convert to real path.
519       char real_path[2*MAXPATHLEN];
520       if (realpath(given_path, real_path) != NULL)
521       {
522         strPath = real_path;
523         return;
524       }
525     }
526 #endif
527     size_t last_sep = strHomePath.find_last_of(PATH_SEPARATOR_CHAR);
528     if (last_sep != string::npos)
529       strPath = strHomePath.Left(last_sep);
530     else
531       strPath = strHomePath;
532   }
533
534 #if defined(_LINUX) && !defined(__APPLE__)
535   /* Change strPath accordingly when target is XBMC_HOME and when INSTALL_PATH
536    * and BIN_INSTALL_PATH differ
537    */
538   CStdString installPath = INSTALL_PATH;
539   CStdString binInstallPath = BIN_INSTALL_PATH;
540   if (!strTarget.compare("XBMC_HOME") && installPath.compare(binInstallPath))
541   {
542     int pos = strPath.length() - binInstallPath.length();
543     CStdString tmp = strPath;
544     tmp.erase(0, pos);
545     if (!tmp.compare(binInstallPath))
546     {
547       strPath.erase(pos, strPath.length());
548       strPath.append(installPath);
549     }
550   }
551 #endif
552 }
553
554 bool CUtil::IsPicture(const CStdString& strFile)
555 {
556   CStdString extension = URIUtils::GetExtension(strFile);
557
558   if (extension.IsEmpty())
559     return false;
560
561   extension.ToLower();
562   if (g_settings.m_pictureExtensions.Find(extension) != -1)
563     return true;
564
565   if (extension == ".tbn" || extension == ".dds")
566     return true;
567
568   return false;
569 }
570
571 bool CUtil::ExcludeFileOrFolder(const CStdString& strFileOrFolder, const CStdStringArray& regexps)
572 {
573   if (strFileOrFolder.IsEmpty())
574     return false;
575
576   CRegExp regExExcludes(true);  // case insensitive regex
577
578   for (unsigned int i = 0; i < regexps.size(); i++)
579   {
580     if (!regExExcludes.RegComp(regexps[i].c_str()))
581     { // invalid regexp - complain in logs
582       CLog::Log(LOGERROR, "%s: Invalid exclude RegExp:'%s'", __FUNCTION__, regexps[i].c_str());
583       continue;
584     }
585     if (regExExcludes.RegFind(strFileOrFolder) > -1)
586     {
587       CLog::Log(LOGDEBUG, "%s: File '%s' excluded. (Matches exclude rule RegExp:'%s')", __FUNCTION__, strFileOrFolder.c_str(), regexps[i].c_str());
588       return true;
589     }
590   }
591   return false;
592 }
593
594 void CUtil::GetFileAndProtocol(const CStdString& strURL, CStdString& strDir)
595 {
596   strDir = strURL;
597   if (!URIUtils::IsRemote(strURL)) return ;
598   if (URIUtils::IsDVD(strURL)) return ;
599
600   CURL url(strURL);
601   strDir.Format("%s://%s", url.GetProtocol().c_str(), url.GetFileName().c_str());
602 }
603
604 int CUtil::GetDVDIfoTitle(const CStdString& strFile)
605 {
606   CStdString strFilename = URIUtils::GetFileName(strFile);
607   if (strFilename.Equals("video_ts.ifo")) return 0;
608   //VTS_[TITLE]_0.IFO
609   return atoi(strFilename.Mid(4, 2).c_str());
610 }
611
612 CStdString CUtil::GetFileMD5(const CStdString& strPath)
613 {
614   CFile file;
615   CStdString result;
616   if (file.Open(strPath))
617   {
618     XBMC::XBMC_MD5 md5;
619     char temp[1024];
620     int pos=0;
621     int read=1;
622     while (read > 0)
623     {
624       read = file.Read(temp,1024);
625       pos += read;
626       md5.append(temp,read);
627     }
628     md5.getDigest(result);
629     file.Close();
630   }
631
632   return result;
633 }
634
635 bool CUtil::GetDirectoryName(const CStdString& strFileName, CStdString& strDescription)
636 {
637   CStdString strFName = URIUtils::GetFileName(strFileName);
638   strDescription = strFileName.Left(strFileName.size() - strFName.size());
639   URIUtils::RemoveSlashAtEnd(strDescription);
640
641   int iPos = strDescription.ReverseFind("\\");
642   if (iPos < 0)
643     iPos = strDescription.ReverseFind("/");
644   if (iPos >= 0)
645   {
646     CStdString strTmp = strDescription.Right(strDescription.size()-iPos-1);
647     strDescription = strTmp;//strDescription.Right(strDescription.size() - iPos - 1);
648   }
649   else if (strDescription.size() <= 0)
650     strDescription = strFName;
651   return true;
652 }
653
654 void CUtil::GetDVDDriveIcon( const CStdString& strPath, CStdString& strIcon )
655 {
656   if ( !g_mediaManager.IsDiscInDrive() )
657   {
658     strIcon = "DefaultDVDEmpty.png";
659     return ;
660   }
661
662   if ( URIUtils::IsDVD(strPath) )
663   {
664 #ifdef HAS_DVD_DRIVE
665     CCdInfo* pInfo = g_mediaManager.GetCdInfo();
666     //  xbox DVD
667     if ( pInfo != NULL && pInfo->IsUDFX( 1 ) )
668     {
669       strIcon = "DefaultXboxDVD.png";
670       return ;
671     }
672 #endif
673     strIcon = "DefaultDVDRom.png";
674     return ;
675   }
676
677   if ( URIUtils::IsISO9660(strPath) )
678   {
679 #ifdef HAS_DVD_DRIVE
680     CCdInfo* pInfo = g_mediaManager.GetCdInfo();
681     if ( pInfo != NULL && pInfo->IsVideoCd( 1 ) )
682     {
683       strIcon = "DefaultVCD.png";
684       return ;
685     }
686 #endif
687     strIcon = "DefaultDVDRom.png";
688     return ;
689   }
690
691   if ( URIUtils::IsCDDA(strPath) )
692   {
693     strIcon = "DefaultCDDA.png";
694     return ;
695   }
696 }
697
698 void CUtil::RemoveTempFiles()
699 {
700   CStdString searchPath = g_settings.GetDatabaseFolder();
701   CFileItemList items;
702   if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".tmp", false))
703     return;
704
705   for (int i = 0; i < items.Size(); ++i)
706   {
707     if (items[i]->m_bIsFolder)
708       continue;
709     XFILE::CFile::Delete(items[i]->m_strPath);
710   }
711 }
712
713 void CUtil::ClearSubtitles()
714 {
715   //delete cached subs
716   CFileItemList items;
717   CDirectory::GetDirectory("special://temp/",items);
718   for( int i=0;i<items.Size();++i)
719   {
720     if (!items[i]->m_bIsFolder)
721     {
722       if ( items[i]->m_strPath.Find("subtitle") >= 0 || items[i]->m_strPath.Find("vobsub_queue") >= 0 )
723       {
724         CLog::Log(LOGDEBUG, "%s - Deleting temporary subtitle %s", __FUNCTION__, items[i]->m_strPath.c_str());
725         CFile::Delete(items[i]->m_strPath);
726       }
727     }
728   }
729 }
730
731 static const char * sub_exts[] = { ".utf", ".utf8", ".utf-8", ".sub", ".srt", ".smi", ".rt", ".txt", ".ssa", ".aqt", ".jss", ".ass", ".idx", NULL};
732
733 int64_t CUtil::ToInt64(uint32_t high, uint32_t low)
734 {
735   int64_t n;
736   n = high;
737   n <<= 32;
738   n += low;
739   return n;
740 }
741
742 bool CUtil::ThumbExists(const CStdString& strFileName, bool bAddCache)
743 {
744   return CThumbnailCache::GetThumbnailCache()->ThumbExists(strFileName, bAddCache);
745 }
746
747 void CUtil::ThumbCacheAdd(const CStdString& strFileName, bool bFileExists)
748 {
749   CThumbnailCache::GetThumbnailCache()->Add(strFileName, bFileExists);
750 }
751
752 void CUtil::ThumbCacheClear()
753 {
754   CThumbnailCache::GetThumbnailCache()->Clear();
755 }
756
757 bool CUtil::ThumbCached(const CStdString& strFileName)
758 {
759   return CThumbnailCache::GetThumbnailCache()->IsCached(strFileName);
760 }
761
762 void CUtil::PlayDVD(const CStdString& strProtocol)
763 {
764 #if defined(HAS_DVDPLAYER) && defined(HAS_DVD_DRIVE)
765   CIoSupport::Dismount("Cdrom0");
766   CIoSupport::RemapDriveLetter('D', "Cdrom0");
767   CStdString strPath;
768   strPath.Format("%s://1", strProtocol.c_str());
769   CFileItem item(strPath, false);
770   item.SetLabel(g_mediaManager.GetDiskLabel());
771   g_application.PlayFile(item);
772 #endif
773 }
774
775 CStdString CUtil::GetNextFilename(const CStdString &fn_template, int max)
776 {
777   if (!fn_template.Find("%03d"))
778     return "";
779
780   CStdString searchPath;
781   URIUtils::GetDirectory(fn_template, searchPath);
782   CStdString mask = URIUtils::GetExtension(fn_template);
783
784   CStdString name;
785   name.Format(fn_template.c_str(), 0);
786
787   CFileItemList items;
788   if (!CDirectory::GetDirectory(searchPath, items, mask, false))
789     return name;
790
791   items.SetFastLookup(true);
792   for (int i = 0; i <= max; i++)
793   {
794     CStdString name;
795     name.Format(fn_template.c_str(), i);
796     if (!items.Get(name))
797       return name;
798   }
799   return "";
800 }
801
802 CStdString CUtil::GetNextPathname(const CStdString &path_template, int max)
803 {
804   if (!path_template.Find("%04d"))
805     return "";
806   
807   for (int i = 0; i <= max; i++)
808   {
809     CStdString name;
810     name.Format(path_template.c_str(), i);
811     if (!CFile::Exists(name))
812       return name;
813   }
814   return "";
815 }
816
817 void CUtil::Tokenize(const CStdString& path, vector<CStdString>& tokens, const string& delimiters)
818 {
819   // Tokenize ripped from http://www.linuxselfhelp.com/HOWTO/C++Programming-HOWTO-7.html
820   // Skip delimiters at beginning.
821   string::size_type lastPos = path.find_first_not_of(delimiters, 0);
822   // Find first "non-delimiter".
823   string::size_type pos = path.find_first_of(delimiters, lastPos);
824
825   while (string::npos != pos || string::npos != lastPos)
826   {
827     // Found a token, add it to the vector.
828     tokens.push_back(path.substr(lastPos, pos - lastPos));
829     // Skip delimiters.  Note the "not_of"
830     lastPos = path.find_first_not_of(delimiters, pos);
831     // Find next "non-delimiter"
832     pos = path.find_first_of(delimiters, lastPos);
833   }
834 }
835
836 void CUtil::TakeScreenshot(const CStdString &filename, bool sync)
837 {
838   int            width;
839   int            height;
840   int            stride;
841   unsigned char* outpixels = NULL;
842
843 #ifdef HAS_DX
844   LPDIRECT3DSURFACE9 lpSurface = NULL, lpBackbuffer = NULL;
845   g_graphicsContext.Lock();
846   if (g_application.IsPlayingVideo())
847   {
848 #ifdef HAS_VIDEO_PLAYBACK
849     g_renderManager.SetupScreenshot();
850 #endif
851   }
852   g_application.RenderNoPresent();
853
854   if (FAILED(g_Windowing.Get3DDevice()->CreateOffscreenPlainSurface(g_Windowing.GetWidth(), g_Windowing.GetHeight(), D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, &lpSurface, NULL)))
855     return;
856
857   if (FAILED(g_Windowing.Get3DDevice()->GetRenderTarget(0, &lpBackbuffer)))
858     return;
859
860   // now take screenshot
861   if (SUCCEEDED(g_Windowing.Get3DDevice()->GetRenderTargetData(lpBackbuffer, lpSurface)))
862   {
863     D3DLOCKED_RECT lr;
864     D3DSURFACE_DESC desc;
865     lpSurface->GetDesc(&desc);
866     if (SUCCEEDED(lpSurface->LockRect(&lr, NULL, D3DLOCK_READONLY)))
867     {
868       width = desc.Width;
869       height = desc.Height;
870       stride = lr.Pitch;
871       outpixels = new unsigned char[height * stride];
872       memcpy(outpixels, lr.pBits, height * stride);
873       lpSurface->UnlockRect();
874     }
875     else
876     {
877       CLog::Log(LOGERROR, "%s LockRect failed", __FUNCTION__);
878     }
879   }
880   else
881   {
882     CLog::Log(LOGERROR, "%s GetBackBuffer failed", __FUNCTION__);
883   }
884   lpSurface->Release();
885   lpBackbuffer->Release();
886
887   g_graphicsContext.Unlock();
888
889 #elif defined(HAS_GL) || defined(HAS_GLES)
890
891   g_graphicsContext.BeginPaint();
892   if (g_application.IsPlayingVideo())
893   {
894 #ifdef HAS_VIDEO_PLAYBACK
895     g_renderManager.SetupScreenshot();
896 #endif
897   }
898   g_application.RenderNoPresent();
899 #ifndef HAS_GLES
900   glReadBuffer(GL_BACK);
901 #endif
902   //get current viewport
903   GLint viewport[4];
904   glGetIntegerv(GL_VIEWPORT, viewport);
905
906   width  = viewport[2] - viewport[0];
907   height = viewport[3] - viewport[1];
908   stride = width * 4;
909   unsigned char* pixels = new unsigned char[stride * height];
910
911   //read pixels from the backbuffer
912 #if HAS_GLES == 2
913   glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)pixels);
914 #else
915   glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid*)pixels);
916 #endif
917   g_graphicsContext.EndPaint();
918
919   //make a new buffer and copy the read image to it with the Y axis inverted
920   outpixels = new unsigned char[stride * height];
921   for (int y = 0; y < height; y++)
922     memcpy(outpixels + y * stride, pixels + (height - y - 1) * stride, stride);
923
924   delete [] pixels;
925
926 #else
927   //nothing to take a screenshot from
928   return;
929 #endif
930
931   if (!outpixels)
932   {
933     CLog::Log(LOGERROR, "Screenshot %s failed", filename.c_str());
934     return;
935   }
936
937   CLog::Log(LOGDEBUG, "Saving screenshot %s", filename.c_str());
938
939   //set alpha byte to 0xFF
940   for (int y = 0; y < height; y++)
941   {
942     unsigned char* alphaptr = outpixels - 1 + y * stride;
943     for (int x = 0; x < width; x++)
944       *(alphaptr += 4) = 0xFF;
945   }
946
947   //if sync is true, the png file needs to be completely written when this function returns
948   if (sync)
949   {
950     if (!CPicture::CreateThumbnailFromSurface(outpixels, width, height, stride, filename))
951       CLog::Log(LOGERROR, "Unable to write screenshot %s", filename.c_str());
952
953     delete [] outpixels;
954   }
955   else
956   {
957     //make sure the file exists to avoid concurrency issues
958     FILE* fp = fopen(filename.c_str(), "w");
959     if (fp)
960       fclose(fp);
961     else
962       CLog::Log(LOGERROR, "Unable to create file %s", filename.c_str());
963
964     //write .png file asynchronous with CThumbnailWriter, prevents stalling of the render thread
965     //outpixels is deleted from CThumbnailWriter
966     CThumbnailWriter* thumbnailwriter = new CThumbnailWriter(outpixels, width, height, stride, filename);
967     CJobManager::GetInstance().AddJob(thumbnailwriter, NULL);
968   }
969 }
970
971 void CUtil::TakeScreenshot()
972 {
973   static bool savingScreenshots = false;
974   static vector<CStdString> screenShots;
975
976   bool promptUser = false;
977   // check to see if we have a screenshot folder yet
978   CStdString strDir = g_guiSettings.GetString("debug.screenshotpath", false);
979   if (strDir.IsEmpty())
980   {
981     strDir = "special://temp/";
982     if (!savingScreenshots)
983     {
984       promptUser = true;
985       savingScreenshots = true;
986       screenShots.clear();
987     }
988   }
989   URIUtils::RemoveSlashAtEnd(strDir);
990
991   if (!strDir.IsEmpty())
992   {
993     CStdString file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(strDir, "screenshot%03d.png"), 999);
994
995     if (!file.IsEmpty())
996     {
997       TakeScreenshot(file, false);
998       if (savingScreenshots)
999         screenShots.push_back(file);
1000       if (promptUser)
1001       { // grab the real directory
1002         CStdString newDir = g_guiSettings.GetString("debug.screenshotpath");
1003         if (!newDir.IsEmpty())
1004         {
1005           for (unsigned int i = 0; i < screenShots.size(); i++)
1006           {
1007             CStdString file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(newDir, "screenshot%03d.png"), 999);
1008             CFile::Cache(screenShots[i], file);
1009           }
1010           screenShots.clear();
1011         }
1012         savingScreenshots = false;
1013       }
1014     }
1015     else
1016     {
1017       CLog::Log(LOGWARNING, "Too many screen shots or invalid folder");
1018     }
1019   }
1020 }
1021
1022 void CUtil::StatToStatI64(struct _stati64 *result, struct stat *stat)
1023 {
1024   result->st_dev = stat->st_dev;
1025   result->st_ino = stat->st_ino;
1026   result->st_mode = stat->st_mode;
1027   result->st_nlink = stat->st_nlink;
1028   result->st_uid = stat->st_uid;
1029   result->st_gid = stat->st_gid;
1030   result->st_rdev = stat->st_rdev;
1031   result->st_size = (int64_t)stat->st_size;
1032
1033 #ifndef _LINUX
1034   result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
1035   result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
1036   result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
1037 #else
1038   result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
1039   result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
1040   result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
1041 #endif
1042 }
1043
1044 void CUtil::Stat64ToStatI64(struct _stati64 *result, struct __stat64 *stat)
1045 {
1046   result->st_dev = stat->st_dev;
1047   result->st_ino = stat->st_ino;
1048   result->st_mode = stat->st_mode;
1049   result->st_nlink = stat->st_nlink;
1050   result->st_uid = stat->st_uid;
1051   result->st_gid = stat->st_gid;
1052   result->st_rdev = stat->st_rdev;
1053   result->st_size = stat->st_size;
1054 #ifndef _LINUX
1055   result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
1056   result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
1057   result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
1058 #else
1059   result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
1060   result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
1061   result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
1062 #endif
1063 }
1064
1065 void CUtil::StatI64ToStat64(struct __stat64 *result, struct _stati64 *stat)
1066 {
1067   result->st_dev = stat->st_dev;
1068   result->st_ino = stat->st_ino;
1069   result->st_mode = stat->st_mode;
1070   result->st_nlink = stat->st_nlink;
1071   result->st_uid = stat->st_uid;
1072   result->st_gid = stat->st_gid;
1073   result->st_rdev = stat->st_rdev;
1074   result->st_size = stat->st_size;
1075 #ifndef _LINUX
1076   result->st_atime = stat->st_atime;
1077   result->st_mtime = stat->st_mtime;
1078   result->st_ctime = stat->st_ctime;
1079 #else
1080   result->st_atime = stat->_st_atime;
1081   result->st_mtime = stat->_st_mtime;
1082   result->st_ctime = stat->_st_ctime;
1083 #endif
1084 }
1085
1086 void CUtil::Stat64ToStat(struct stat *result, struct __stat64 *stat)
1087 {
1088   result->st_dev = stat->st_dev;
1089   result->st_ino = stat->st_ino;
1090   result->st_mode = stat->st_mode;
1091   result->st_nlink = stat->st_nlink;
1092   result->st_uid = stat->st_uid;
1093   result->st_gid = stat->st_gid;
1094   result->st_rdev = stat->st_rdev;
1095 #ifndef _LINUX
1096   if (stat->st_size <= LONG_MAX)
1097     result->st_size = (_off_t)stat->st_size;
1098 #else
1099   if (sizeof(stat->st_size) <= sizeof(result->st_size) )
1100     result->st_size = (off_t)stat->st_size;
1101 #endif
1102   else
1103   {
1104     result->st_size = 0;
1105     CLog::Log(LOGWARNING, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
1106   }
1107   result->st_atime = (time_t)(stat->st_atime & 0xFFFFFFFF);
1108   result->st_mtime = (time_t)(stat->st_mtime & 0xFFFFFFFF);
1109   result->st_ctime = (time_t)(stat->st_ctime & 0xFFFFFFFF);
1110 }
1111
1112 #ifdef _WIN32
1113 void CUtil::Stat64ToStat64i32(struct _stat64i32 *result, struct __stat64 *stat)
1114 {
1115   result->st_dev = stat->st_dev;
1116   result->st_ino = stat->st_ino;
1117   result->st_mode = stat->st_mode;
1118   result->st_nlink = stat->st_nlink;
1119   result->st_uid = stat->st_uid;
1120   result->st_gid = stat->st_gid;
1121   result->st_rdev = stat->st_rdev;
1122 #ifndef _LINUX
1123   if (stat->st_size <= LONG_MAX)
1124     result->st_size = (_off_t)stat->st_size;
1125 #else
1126   if (sizeof(stat->st_size) <= sizeof(result->st_size) )
1127     result->st_size = (off_t)stat->st_size;
1128 #endif
1129   else
1130   {
1131     result->st_size = 0;
1132     CLog::Log(LOGWARNING, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
1133   }
1134 #ifndef _LINUX
1135   result->st_atime = stat->st_atime;
1136   result->st_mtime = stat->st_mtime;
1137   result->st_ctime = stat->st_ctime;
1138 #else
1139   result->st_atime = stat->_st_atime;
1140   result->st_mtime = stat->_st_mtime;
1141   result->st_ctime = stat->_st_ctime;
1142 #endif
1143 }
1144 #endif
1145
1146 bool CUtil::CreateDirectoryEx(const CStdString& strPath)
1147 {
1148   // Function to create all directories at once instead
1149   // of calling CreateDirectory for every subdir.
1150   // Creates the directory and subdirectories if needed.
1151
1152   // return true if directory already exist
1153   if (CDirectory::Exists(strPath)) return true;
1154
1155   // we currently only allow HD and smb paths
1156   if (!URIUtils::IsHD(strPath) && !URIUtils::IsSmb(strPath))
1157   {
1158     CLog::Log(LOGERROR,"%s called with an unsupported path: %s", __FUNCTION__, strPath.c_str());
1159     return false;
1160   }
1161
1162   CURL url(strPath);
1163   // silly CStdString can't take a char in the constructor
1164   CStdString sep(1, url.GetDirectorySeparator());
1165
1166   // split the filename portion of the URL up into separate dirs
1167   CStdStringArray dirs;
1168   StringUtils::SplitString(url.GetFileName(), sep, dirs);
1169
1170   // we start with the root path
1171   CStdString dir = url.GetWithoutFilename();
1172   unsigned int i = 0;
1173   if (dir.IsEmpty())
1174   { // local directory - start with the first dirs member so that
1175     // we ensure CUtil::AddFileToFolder() below has something to work with
1176     dir = dirs[i++] + sep;
1177   }
1178   // and append the rest of the directories successively, creating each dir
1179   // as we go
1180   for (; i < dirs.size(); i++)
1181   {
1182     dir = URIUtils::AddFileToFolder(dir, dirs[i]);
1183     CDirectory::Create(dir);
1184   }
1185
1186   // was the final destination directory successfully created ?
1187   if (!CDirectory::Exists(strPath)) return false;
1188   return true;
1189 }
1190
1191 CStdString CUtil::MakeLegalFileName(const CStdString &strFile, int LegalType)
1192 {
1193   CStdString result = strFile;
1194
1195   result.Replace('/', '_');
1196   result.Replace('\\', '_');
1197   result.Replace('?', '_');
1198
1199   if (LegalType == LEGAL_WIN32_COMPAT)
1200   {
1201     // just filter out some illegal characters on windows
1202     result.Replace(':', '_');
1203     result.Replace('*', '_');
1204     result.Replace('?', '_');
1205     result.Replace('\"', '_');
1206     result.Replace('<', '_');
1207     result.Replace('>', '_');
1208     result.Replace('|', '_');
1209     result.TrimRight(".");
1210     result.TrimRight(" ");
1211   }
1212   return result;
1213 }
1214
1215 // same as MakeLegalFileName, but we assume that we're passed a complete path,
1216 // and just legalize the filename
1217 CStdString CUtil::MakeLegalPath(const CStdString &strPathAndFile, int LegalType)
1218 {
1219   CStdString strPath;
1220   URIUtils::GetDirectory(strPathAndFile,strPath);
1221   CStdString strFileName = URIUtils::GetFileName(strPathAndFile);
1222   return strPath + MakeLegalFileName(strFileName, LegalType);
1223 }
1224
1225 CStdString CUtil::ValidatePath(const CStdString &path, bool bFixDoubleSlashes /* = false */)
1226 {
1227   CStdString result = path;
1228
1229   // Don't do any stuff on URLs containing %-characters or protocols that embed
1230   // filenames. NOTE: Don't use IsInZip or IsInRar here since it will infinitely
1231   // recurse and crash XBMC
1232   if (URIUtils::IsURL(path) && 
1233      (path.Find('%') >= 0 ||
1234       path.Left(4).Equals("zip:") ||
1235       path.Left(4).Equals("rar:") ||
1236       path.Left(6).Equals("stack:") ||
1237       path.Left(10).Equals("multipath:") ))
1238     return result;
1239
1240   // check the path for incorrect slashes
1241 #ifdef _WIN32
1242   if (URIUtils::IsDOSPath(path))
1243   {
1244     result.Replace('/', '\\');
1245     /* The double slash correction should only be used when *absolutely*
1246        necessary! This applies to certain DLLs or use from Python DLLs/scripts
1247        that incorrectly generate double (back) slashes.
1248     */
1249     if (bFixDoubleSlashes)
1250     {
1251       // Fixup for double back slashes (but ignore the \\ of unc-paths)
1252       for (int x = 1; x < result.GetLength() - 1; x++)
1253       {
1254         if (result[x] == '\\' && result[x+1] == '\\')
1255           result.Delete(x);
1256       }
1257     }
1258   }
1259   else if (path.Find("://") >= 0 || path.Find(":\\\\") >= 0)
1260 #endif
1261   {
1262     result.Replace('\\', '/');
1263     /* The double slash correction should only be used when *absolutely*
1264        necessary! This applies to certain DLLs or use from Python DLLs/scripts
1265        that incorrectly generate double (back) slashes.
1266     */
1267     if (bFixDoubleSlashes)
1268     {
1269       // Fixup for double forward slashes(/) but don't touch the :// of URLs
1270       for (int x = 2; x < result.GetLength() - 1; x++)
1271       {
1272         if ( result[x] == '/' && result[x + 1] == '/' && !(result[x - 1] == ':' || (result[x - 1] == '/' && result[x - 2] == ':')) )
1273           result.Delete(x);
1274       }
1275     }
1276   }
1277   return result;
1278 }
1279
1280 bool CUtil::IsUsingTTFSubtitles()
1281 {
1282   return URIUtils::GetExtension(g_guiSettings.GetString("subtitles.font")).Equals(".ttf");
1283 }
1284
1285 #ifdef UNIT_TESTING
1286 bool CUtil::TestSplitExec()
1287 {
1288   CStdString function;
1289   vector<CStdString> params;
1290   CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\test\\foo\")", function, params);
1291   if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo")
1292     return false;
1293   params.clear();
1294   CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\test\\foo\\\")", function, params);
1295   if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo\"")
1296     return false;
1297   CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\\\test\\\\foo\\\\\")", function, params);
1298   if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo\\")
1299     return false;
1300   CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\\\\\\\test\\\\\\foo\\\\\")", function, params);
1301   if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\\\test\\\\foo\\")
1302     return false;
1303   return true;
1304 }
1305 #endif
1306
1307 void CUtil::SplitExecFunction(const CStdString &execString, CStdString &function, vector<CStdString> &parameters)
1308 {
1309   CStdString paramString;
1310
1311   int iPos = execString.Find("(");
1312   int iPos2 = execString.ReverseFind(")");
1313   if (iPos > 0 && iPos2 > 0)
1314   {
1315     paramString = execString.Mid(iPos + 1, iPos2 - iPos - 1);
1316     function = execString.Left(iPos);
1317   }
1318   else
1319     function = execString;
1320
1321   // remove any whitespace, and the standard prefix (if it exists)
1322   function.Trim();
1323   if( function.Left(5).Equals("xbmc.", false) )
1324     function.Delete(0, 5);
1325
1326   // now split up our parameters - we may have quotes to deal with as well as brackets and whitespace
1327   bool inQuotes = false;
1328   bool lastEscaped = false; // only every second character can be escaped
1329   int inFunction = 0;
1330   size_t whiteSpacePos = 0;
1331   CStdString parameter;
1332   parameters.clear();
1333   for (size_t pos = 0; pos < paramString.size(); pos++)
1334   {
1335     char ch = paramString[pos];
1336     bool escaped = (pos > 0 && paramString[pos - 1] == '\\' && !lastEscaped);
1337     lastEscaped = escaped;
1338     if (inQuotes)
1339     { // if we're in a quote, we accept everything until the closing quote
1340       if (ch == '\"' && !escaped)
1341       { // finished a quote - no need to add the end quote to our string
1342         inQuotes = false;
1343         continue;
1344       }
1345     }
1346     else
1347     { // not in a quote, so check if we should be starting one
1348       if (ch == '\"' && !escaped)
1349       { // start of quote - no need to add the quote to our string
1350         inQuotes = true;
1351         continue;
1352       }
1353       if (inFunction && ch == ')')
1354       { // end of a function
1355         inFunction--;
1356       }
1357       if (ch == '(')
1358       { // start of function
1359         inFunction++;
1360       }
1361       if (!inFunction && !URIUtils::IsStack(paramString) && ch == ',')
1362       { // not in a function, so a comma signfies the end of this parameter
1363         if (whiteSpacePos)
1364           parameter = parameter.Left(whiteSpacePos);
1365         parameters.push_back(parameter);
1366         parameter.Empty();
1367         whiteSpacePos = 0;
1368         continue;
1369       }
1370     }
1371     if ((ch == '\"' || ch == '\\') && escaped)
1372     { // escaped quote or backslash
1373       parameter[parameter.size()-1] = ch;
1374       continue;
1375     }
1376     // whitespace handling - we skip any whitespace at the left or right of an unquoted parameter
1377     if (ch == ' ' && !inQuotes)
1378     {
1379       if (parameter.IsEmpty()) // skip whitespace on left
1380         continue;
1381       if (!whiteSpacePos) // make a note of where whitespace starts on the right
1382         whiteSpacePos = parameter.size();
1383     }
1384     else
1385       whiteSpacePos = 0;
1386     parameter += ch;
1387   }
1388   if (inFunction || inQuotes)
1389     CLog::Log(LOGWARNING, "%s(%s) - end of string while searching for ) or \"", __FUNCTION__, execString.c_str());
1390   if (whiteSpacePos)
1391     parameter = parameter.Left(whiteSpacePos);
1392   if (!parameter.IsEmpty())
1393     parameters.push_back(parameter);
1394 }
1395
1396 int CUtil::GetMatchingSource(const CStdString& strPath1, VECSOURCES& VECSOURCES, bool& bIsSourceName)
1397 {
1398   if (strPath1.IsEmpty())
1399     return -1;
1400
1401   //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, testing original path/name [%s]", strPath1.c_str());
1402
1403   // copy as we may change strPath
1404   CStdString strPath = strPath1;
1405
1406   // Check for special protocols
1407   CURL checkURL(strPath);
1408
1409   // stack://
1410   if (checkURL.GetProtocol() == "stack")
1411     strPath.Delete(0, 8); // remove the stack protocol
1412
1413   if (checkURL.GetProtocol() == "shout")
1414     strPath = checkURL.GetHostName();
1415   if (checkURL.GetProtocol() == "lastfm")
1416     return 1;
1417   if (checkURL.GetProtocol() == "tuxbox")
1418     return 1;
1419   if (checkURL.GetProtocol() == "plugin")
1420     return 1;
1421   if (checkURL.GetProtocol() == "multipath")
1422     strPath = CMultiPathDirectory::GetFirstPath(strPath);
1423
1424   //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, testing for matching name [%s]", strPath.c_str());
1425   bIsSourceName = false;
1426   int iIndex = -1;
1427   int iLength = -1;
1428   // we first test the NAME of a source
1429   for (int i = 0; i < (int)VECSOURCES.size(); ++i)
1430   {
1431     CMediaSource share = VECSOURCES.at(i);
1432     CStdString strName = share.strName;
1433
1434     // special cases for dvds
1435     if (URIUtils::IsOnDVD(share.strPath))
1436     {
1437       if (URIUtils::IsOnDVD(strPath))
1438         return i;
1439
1440       // not a path, so we need to modify the source name
1441       // since we add the drive status and disc name to the source
1442       // "Name (Drive Status/Disc Name)"
1443       int iPos = strName.ReverseFind('(');
1444       if (iPos > 1)
1445         strName = strName.Mid(0, iPos - 1);
1446     }
1447     //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, comparing name [%s]", strName.c_str());
1448     if (strPath.Equals(strName))
1449     {
1450       bIsSourceName = true;
1451       return i;
1452     }
1453   }
1454
1455   // now test the paths
1456
1457   // remove user details, and ensure path only uses forward slashes
1458   // and ends with a trailing slash so as not to match a substring
1459   CURL urlDest(strPath);
1460   urlDest.SetOptions("");
1461   CStdString strDest = urlDest.GetWithoutUserDetails();
1462   ForceForwardSlashes(strDest);
1463   if (!URIUtils::HasSlashAtEnd(strDest))
1464     strDest += "/";
1465   int iLenPath = strDest.size();
1466
1467   //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, testing url [%s]", strDest.c_str());
1468
1469   for (int i = 0; i < (int)VECSOURCES.size(); ++i)
1470   {
1471     CMediaSource share = VECSOURCES.at(i);
1472
1473     // does it match a source name?
1474     if (share.strPath.substr(0,8) == "shout://")
1475     {
1476       CURL url(share.strPath);
1477       if (strPath.Equals(url.GetHostName()))
1478         return i;
1479     }
1480
1481     // doesnt match a name, so try the source path
1482     vector<CStdString> vecPaths;
1483
1484     // add any concatenated paths if they exist
1485     if (share.vecPaths.size() > 0)
1486       vecPaths = share.vecPaths;
1487
1488     // add the actual share path at the front of the vector
1489     vecPaths.insert(vecPaths.begin(), share.strPath);
1490
1491     // test each path
1492     for (int j = 0; j < (int)vecPaths.size(); ++j)
1493     {
1494       // remove user details, and ensure path only uses forward slashes
1495       // and ends with a trailing slash so as not to match a substring
1496       CURL urlShare(vecPaths[j]);
1497       urlShare.SetOptions("");
1498       CStdString strShare = urlShare.GetWithoutUserDetails();
1499       ForceForwardSlashes(strShare);
1500       if (!URIUtils::HasSlashAtEnd(strShare))
1501         strShare += "/";
1502       int iLenShare = strShare.size();
1503       //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, comparing url [%s]", strShare.c_str());
1504
1505       if ((iLenPath >= iLenShare) && (strDest.Left(iLenShare).Equals(strShare)) && (iLenShare > iLength))
1506       {
1507         //CLog::Log(LOGDEBUG,"Found matching source at index %i: [%s], Len = [%i]", i, strShare.c_str(), iLenShare);
1508
1509         // if exact match, return it immediately
1510         if (iLenPath == iLenShare)
1511         {
1512           // if the path EXACTLY matches an item in a concatentated path
1513           // set source name to true to load the full virtualpath
1514           bIsSourceName = false;
1515           if (vecPaths.size() > 1)
1516             bIsSourceName = true;
1517           return i;
1518         }
1519         iIndex = i;
1520         iLength = iLenShare;
1521       }
1522     }
1523   }
1524
1525   // return the index of the share with the longest match
1526   if (iIndex == -1)
1527   {
1528
1529     // rar:// and zip://
1530     // if archive wasn't mounted, look for a matching share for the archive instead
1531     if( strPath.Left(6).Equals("rar://") || strPath.Left(6).Equals("zip://") )
1532     {
1533       // get the hostname portion of the url since it contains the archive file
1534       strPath = checkURL.GetHostName();
1535
1536       bIsSourceName = false;
1537       bool bDummy;
1538       return GetMatchingSource(strPath, VECSOURCES, bDummy);
1539     }
1540
1541     CLog::Log(LOGWARNING,"CUtil::GetMatchingSource... no matching source found for [%s]", strPath1.c_str());
1542   }
1543   return iIndex;
1544 }
1545
1546 CStdString CUtil::TranslateSpecialSource(const CStdString &strSpecial)
1547 {
1548   CStdString strReturn=strSpecial;
1549   if (!strSpecial.IsEmpty() && strSpecial[0] == '$')
1550   {
1551     if (strSpecial.Left(5).Equals("$HOME"))
1552       URIUtils::AddFileToFolder("special://home/", strSpecial.Mid(5), strReturn);
1553     else if (strSpecial.Left(10).Equals("$SUBTITLES"))
1554       URIUtils::AddFileToFolder("special://subtitles/", strSpecial.Mid(10), strReturn);
1555     else if (strSpecial.Left(9).Equals("$USERDATA"))
1556       URIUtils::AddFileToFolder("special://userdata/", strSpecial.Mid(9), strReturn);
1557     else if (strSpecial.Left(9).Equals("$DATABASE"))
1558       URIUtils::AddFileToFolder("special://database/", strSpecial.Mid(9), strReturn);
1559     else if (strSpecial.Left(11).Equals("$THUMBNAILS"))
1560       URIUtils::AddFileToFolder("special://thumbnails/", strSpecial.Mid(11), strReturn);
1561     else if (strSpecial.Left(11).Equals("$RECORDINGS"))
1562       URIUtils::AddFileToFolder("special://recordings/", strSpecial.Mid(11), strReturn);
1563     else if (strSpecial.Left(12).Equals("$SCREENSHOTS"))
1564       URIUtils::AddFileToFolder("special://screenshots/", strSpecial.Mid(12), strReturn);
1565     else if (strSpecial.Left(15).Equals("$MUSICPLAYLISTS"))
1566       URIUtils::AddFileToFolder("special://musicplaylists/", strSpecial.Mid(15), strReturn);
1567     else if (strSpecial.Left(15).Equals("$VIDEOPLAYLISTS"))
1568       URIUtils::AddFileToFolder("special://videoplaylists/", strSpecial.Mid(15), strReturn);
1569     else if (strSpecial.Left(7).Equals("$CDRIPS"))
1570       URIUtils::AddFileToFolder("special://cdrips/", strSpecial.Mid(7), strReturn);
1571     // this one will be removed post 2.0
1572     else if (strSpecial.Left(10).Equals("$PLAYLISTS"))
1573       URIUtils::AddFileToFolder(g_guiSettings.GetString("system.playlistspath",false), strSpecial.Mid(10), strReturn);
1574   }
1575   return strReturn;
1576 }
1577
1578 CStdString CUtil::MusicPlaylistsLocation()
1579 {
1580   vector<CStdString> vec;
1581   CStdString strReturn;
1582   URIUtils::AddFileToFolder(g_guiSettings.GetString("system.playlistspath"), "music", strReturn);
1583   vec.push_back(strReturn);
1584   URIUtils::AddFileToFolder(g_guiSettings.GetString("system.playlistspath"), "mixed", strReturn);
1585   vec.push_back(strReturn);
1586   return XFILE::CMultiPathDirectory::ConstructMultiPath(vec);;
1587 }
1588
1589 CStdString CUtil::VideoPlaylistsLocation()
1590 {
1591   vector<CStdString> vec;
1592   CStdString strReturn;
1593   URIUtils::AddFileToFolder(g_guiSettings.GetString("system.playlistspath"), "video", strReturn);
1594   vec.push_back(strReturn);
1595   URIUtils::AddFileToFolder(g_guiSettings.GetString("system.playlistspath"), "mixed", strReturn);
1596   vec.push_back(strReturn);
1597   return XFILE::CMultiPathDirectory::ConstructMultiPath(vec);;
1598 }
1599
1600 void CUtil::DeleteMusicDatabaseDirectoryCache()
1601 {
1602   CUtil::DeleteDirectoryCache("mdb-");
1603 }
1604
1605 void CUtil::DeleteVideoDatabaseDirectoryCache()
1606 {
1607   CUtil::DeleteDirectoryCache("vdb-");
1608 }
1609
1610 void CUtil::DeleteDirectoryCache(const CStdString &prefix)
1611 {
1612   CStdString searchPath = "special://temp/";
1613   CFileItemList items;
1614   if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".fi", false))
1615     return;
1616
1617   for (int i = 0; i < items.Size(); ++i)
1618   {
1619     if (items[i]->m_bIsFolder)
1620       continue;
1621     CStdString fileName = URIUtils::GetFileName(items[i]->m_strPath);
1622     if (fileName.Left(prefix.GetLength()) == prefix)
1623       XFILE::CFile::Delete(items[i]->m_strPath);
1624   }
1625 }
1626
1627 bool CUtil::SetSysDateTimeYear(int iYear, int iMonth, int iDay, int iHour, int iMinute)
1628 {
1629   TIME_ZONE_INFORMATION tziNew;
1630   SYSTEMTIME CurTime;
1631   SYSTEMTIME NewTime;
1632   GetLocalTime(&CurTime);
1633   GetLocalTime(&NewTime);
1634   int iRescBiases, iHourUTC;
1635   int iMinuteNew;
1636
1637   DWORD dwRet = GetTimeZoneInformation(&tziNew);  // Get TimeZone Informations
1638   float iGMTZone = (float(tziNew.Bias)/(60));     // Calc's the GMT Time
1639
1640   CLog::Log(LOGDEBUG, "------------ TimeZone -------------");
1641   CLog::Log(LOGDEBUG, "-      GMT Zone: GMT %.1f",iGMTZone);
1642   CLog::Log(LOGDEBUG, "-          Bias: %lu minutes",tziNew.Bias);
1643   CLog::Log(LOGDEBUG, "-  DaylightBias: %lu",tziNew.DaylightBias);
1644   CLog::Log(LOGDEBUG, "-  StandardBias: %lu",tziNew.StandardBias);
1645
1646   switch (dwRet)
1647   {
1648     case TIME_ZONE_ID_STANDARD:
1649       {
1650         iRescBiases   = tziNew.Bias + tziNew.StandardBias;
1651         CLog::Log(LOGDEBUG, "-   Timezone ID: 1, Standart");
1652       }
1653       break;
1654     case TIME_ZONE_ID_DAYLIGHT:
1655       {
1656         iRescBiases   = tziNew.Bias + tziNew.StandardBias + tziNew.DaylightBias;
1657         CLog::Log(LOGDEBUG, "-   Timezone ID: 2, Daylight");
1658       }
1659       break;
1660     case TIME_ZONE_ID_UNKNOWN:
1661       {
1662         iRescBiases   = tziNew.Bias + tziNew.StandardBias;
1663         CLog::Log(LOGDEBUG, "-   Timezone ID: 0, Unknown");
1664       }
1665       break;
1666     case TIME_ZONE_ID_INVALID:
1667       {
1668         iRescBiases   = tziNew.Bias + tziNew.StandardBias;
1669         CLog::Log(LOGDEBUG, "-   Timezone ID: Invalid");
1670       }
1671       break;
1672     default:
1673       iRescBiases   = tziNew.Bias + tziNew.StandardBias;
1674   }
1675     CLog::Log(LOGDEBUG, "--------------- END ---------------");
1676
1677   // Calculation
1678   iHourUTC = GMTZoneCalc(iRescBiases, iHour, iMinute, iMinuteNew);
1679   iMinute = iMinuteNew;
1680   if(iHourUTC <0)
1681   {
1682     iDay = iDay - 1;
1683     iHourUTC =iHourUTC + 24;
1684   }
1685   if(iHourUTC >23)
1686   {
1687     iDay = iDay + 1;
1688     iHourUTC =iHourUTC - 24;
1689   }
1690
1691   // Set the New-,Detected Time Values to System Time!
1692   NewTime.wYear     = (WORD)iYear;
1693   NewTime.wMonth    = (WORD)iMonth;
1694   NewTime.wDay      = (WORD)iDay;
1695   NewTime.wHour     = (WORD)iHourUTC;
1696   NewTime.wMinute   = (WORD)iMinute;
1697
1698   FILETIME stNewTime, stCurTime;
1699   SystemTimeToFileTime(&NewTime, &stNewTime);
1700   SystemTimeToFileTime(&CurTime, &stCurTime);
1701   return false;
1702 }
1703 int CUtil::GMTZoneCalc(int iRescBiases, int iHour, int iMinute, int &iMinuteNew)
1704 {
1705   int iHourUTC, iTemp;
1706   iMinuteNew = iMinute;
1707   iTemp = iRescBiases/60;
1708
1709   if (iRescBiases == 0 )return iHour;   // GMT Zone 0, no need calculate
1710   if (iRescBiases > 0)
1711     iHourUTC = iHour + abs(iTemp);
1712   else
1713     iHourUTC = iHour - abs(iTemp);
1714
1715   if ((iTemp*60) != iRescBiases)
1716   {
1717     if (iRescBiases > 0)
1718       iMinuteNew = iMinute + abs(iTemp*60 - iRescBiases);
1719     else
1720       iMinuteNew = iMinute - abs(iTemp*60 - iRescBiases);
1721
1722     if (iMinuteNew >= 60)
1723     {
1724       iMinuteNew = iMinuteNew -60;
1725       iHourUTC = iHourUTC + 1;
1726     }
1727     else if (iMinuteNew < 0)
1728     {
1729       iMinuteNew = iMinuteNew +60;
1730       iHourUTC = iHourUTC - 1;
1731     }
1732   }
1733   return iHourUTC;
1734 }
1735
1736 void CUtil::GetRecursiveListing(const CStdString& strPath, CFileItemList& items, const CStdString& strMask, bool bUseFileDirectories)
1737 {
1738   CFileItemList myItems;
1739   CDirectory::GetDirectory(strPath,myItems,strMask,bUseFileDirectories);
1740   for (int i=0;i<myItems.Size();++i)
1741   {
1742     if (myItems[i]->m_bIsFolder)
1743       CUtil::GetRecursiveListing(myItems[i]->m_strPath,items,strMask,bUseFileDirectories);
1744     else
1745       items.Add(myItems[i]);
1746   }
1747 }
1748
1749 void CUtil::GetRecursiveDirsListing(const CStdString& strPath, CFileItemList& item)
1750 {
1751   CFileItemList myItems;
1752   CDirectory::GetDirectory(strPath,myItems,"",false);
1753   for (int i=0;i<myItems.Size();++i)
1754   {
1755     if (myItems[i]->m_bIsFolder && !myItems[i]->m_strPath.Equals(".."))
1756     {
1757       item.Add(myItems[i]);
1758       CUtil::GetRecursiveDirsListing(myItems[i]->m_strPath,item);
1759     }
1760   }
1761 }
1762
1763 void CUtil::ForceForwardSlashes(CStdString& strPath)
1764 {
1765   int iPos = strPath.ReverseFind('\\');
1766   while (iPos > 0)
1767   {
1768     strPath.at(iPos) = '/';
1769     iPos = strPath.ReverseFind('\\');
1770   }
1771 }
1772
1773 double CUtil::AlbumRelevance(const CStdString& strAlbumTemp1, const CStdString& strAlbum1, const CStdString& strArtistTemp1, const CStdString& strArtist1)
1774 {
1775   // case-insensitive fuzzy string comparison on the album and artist for relevance
1776   // weighting is identical, both album and artist are 50% of the total relevance
1777   // a missing artist means the maximum relevance can only be 0.50
1778   CStdString strAlbumTemp = strAlbumTemp1;
1779   strAlbumTemp.MakeLower();
1780   CStdString strAlbum = strAlbum1;
1781   strAlbum.MakeLower();
1782   double fAlbumPercentage = fstrcmp(strAlbumTemp, strAlbum, 0.0f);
1783   double fArtistPercentage = 0.0f;
1784   if (!strArtist1.IsEmpty())
1785   {
1786     CStdString strArtistTemp = strArtistTemp1;
1787     strArtistTemp.MakeLower();
1788     CStdString strArtist = strArtist1;
1789     strArtist.MakeLower();
1790     fArtistPercentage = fstrcmp(strArtistTemp, strArtist, 0.0f);
1791   }
1792   double fRelevance = fAlbumPercentage * 0.5f + fArtistPercentage * 0.5f;
1793   return fRelevance;
1794 }
1795
1796 CStdString CUtil::SubstitutePath(const CStdString& strFileName)
1797 {
1798   for (CAdvancedSettings::StringMapping::iterator i = g_advancedSettings.m_pathSubstitutions.begin(); 
1799                                                   i != g_advancedSettings.m_pathSubstitutions.end(); i++)
1800   {
1801     if (strncmp(strFileName.c_str(), i->first.c_str(), i->first.size()) == 0)
1802       return URIUtils::AddFileToFolder(i->second, strFileName.Mid(i->first.size()));
1803   }
1804   return strFileName;
1805 }
1806
1807 bool CUtil::MakeShortenPath(CStdString StrInput, CStdString& StrOutput, int iTextMaxLength)
1808 {
1809   int iStrInputSize = StrInput.size();
1810   if((iStrInputSize <= 0) || (iTextMaxLength >= iStrInputSize))
1811     return false;
1812
1813   char cDelim = '\0';
1814   size_t nGreaterDelim, nPos;
1815
1816   nPos = StrInput.find_last_of( '\\' );
1817   if ( nPos != CStdString::npos )
1818     cDelim = '\\';
1819   else
1820   {
1821     nPos = StrInput.find_last_of( '/' );
1822     if ( nPos != CStdString::npos )
1823       cDelim = '/';
1824   }
1825   if ( cDelim == '\0' )
1826     return false;
1827
1828   if (nPos == StrInput.size() - 1)
1829   {
1830     StrInput.erase(StrInput.size() - 1);
1831     nPos = StrInput.find_last_of( cDelim );
1832   }
1833   while( iTextMaxLength < iStrInputSize )
1834   {
1835     nPos = StrInput.find_last_of( cDelim, nPos );
1836     nGreaterDelim = nPos;
1837     if ( nPos != CStdString::npos )
1838       nPos = StrInput.find_last_of( cDelim, nPos - 1 );
1839     if ( nPos == CStdString::npos ) break;
1840     if ( nGreaterDelim > nPos ) StrInput.replace( nPos + 1, nGreaterDelim - nPos - 1, ".." );
1841     iStrInputSize = StrInput.size();
1842   }
1843   // replace any additional /../../ with just /../ if necessary
1844   CStdString replaceDots;
1845   replaceDots.Format("..%c..", cDelim);
1846   while (StrInput.size() > (unsigned int)iTextMaxLength)
1847     if (!StrInput.Replace(replaceDots, ".."))
1848       break;
1849   // finally, truncate our string to force inside our max text length,
1850   // replacing the last 2 characters with ".."
1851
1852   // eg end up with:
1853   // "smb://../Playboy Swimsuit Cal.."
1854   if (iTextMaxLength > 2 && StrInput.size() > (unsigned int)iTextMaxLength)
1855   {
1856     StrInput = StrInput.Left(iTextMaxLength - 2);
1857     StrInput += "..";
1858   }
1859   StrOutput = StrInput;
1860   return true;
1861 }
1862
1863 bool CUtil::SupportsFileOperations(const CStdString& strPath)
1864 {
1865   // currently only hd and smb support delete and rename
1866   if (URIUtils::IsHD(strPath))
1867     return true;
1868   if (URIUtils::IsSmb(strPath))
1869     return true;
1870   if (URIUtils::IsMythTV(strPath))
1871   {
1872     /*
1873      * Can't use CFile::Exists() to check whether the myth:// path supports file operations because
1874      * it hits the directory cache on the way through, which has the Live Channels and Guide
1875      * items cached.
1876      */
1877     return CMythDirectory::SupportsFileOperations(strPath);
1878   }
1879   if (URIUtils::IsStack(strPath))
1880   {
1881     CStackDirectory dir;
1882     return SupportsFileOperations(dir.GetFirstStackedFile(strPath));
1883   }
1884   if (URIUtils::IsMultiPath(strPath))
1885     return CMultiPathDirectory::SupportsFileOperations(strPath);
1886
1887   return false;
1888 }
1889
1890 CStdString CUtil::GetCachedAlbumThumb(const CStdString& album, const CStdString& artist)
1891 {
1892   if (album.IsEmpty())
1893     return GetCachedMusicThumb("unknown"+artist);
1894   if (artist.IsEmpty())
1895     return GetCachedMusicThumb(album+"unknown");
1896   return GetCachedMusicThumb(album+artist);
1897 }
1898
1899 CStdString CUtil::GetCachedMusicThumb(const CStdString& path)
1900 {
1901   Crc32 crc;
1902   CStdString noSlashPath(path);
1903   URIUtils::RemoveSlashAtEnd(noSlashPath);
1904   crc.ComputeFromLowerCase(noSlashPath);
1905   CStdString hex;
1906   hex.Format("%08x", (unsigned __int32) crc);
1907   CStdString thumb;
1908   thumb.Format("%c/%s.tbn", hex[0], hex.c_str());
1909   return URIUtils::AddFileToFolder(g_settings.GetMusicThumbFolder(), thumb);
1910 }
1911
1912 CStdString CUtil::GetDefaultFolderThumb(const CStdString &folderThumb)
1913 {
1914   if (g_TextureManager.HasTexture(folderThumb))
1915     return folderThumb;
1916   return "";
1917 }
1918
1919 void CUtil::GetSkinThemes(vector<CStdString>& vecTheme)
1920 {
1921   CStdString strPath;
1922   URIUtils::AddFileToFolder(g_graphicsContext.GetMediaDir(),"media",strPath);
1923   CFileItemList items;
1924   CDirectory::GetDirectory(strPath, items);
1925   // Search for Themes in the Current skin!
1926   for (int i = 0; i < items.Size(); ++i)
1927   {
1928     CFileItemPtr pItem = items[i];
1929     if (!pItem->m_bIsFolder)
1930     {
1931       CStdString strExtension;
1932       URIUtils::GetExtension(pItem->m_strPath, strExtension);
1933       if ((strExtension == ".xpr" && pItem->GetLabel().CompareNoCase("Textures.xpr")) ||
1934           (strExtension == ".xbt" && pItem->GetLabel().CompareNoCase("Textures.xbt")))
1935       {
1936         CStdString strLabel = pItem->GetLabel();
1937         vecTheme.push_back(strLabel.Mid(0, strLabel.size() - 4));
1938       }
1939     }
1940   }
1941   sort(vecTheme.begin(), vecTheme.end(), sortstringbyname());
1942 }
1943
1944 void CUtil::InitRandomSeed()
1945 {
1946   // Init random seed
1947   int64_t now;
1948   now = CurrentHostCounter();
1949   unsigned int seed = (unsigned int)now;
1950 //  CLog::Log(LOGDEBUG, "%s - Initializing random seed with %u", __FUNCTION__, seed);
1951   srand(seed);
1952 }
1953
1954 #ifdef _LINUX
1955 bool CUtil::RunCommandLine(const CStdString& cmdLine, bool waitExit)
1956 {
1957   CStdStringArray args;
1958
1959   StringUtils::SplitString(cmdLine, ",", args);
1960
1961   // Strip quotes and whitespace around the arguments, or exec will fail.
1962   // This allows the python invocation to be written more naturally with any amount of whitespace around the args.
1963   // But it's still limited, for example quotes inside the strings are not expanded, etc.
1964   // TODO: Maybe some python library routine can parse this more properly ?
1965   for (size_t i=0; i<args.size(); i++)
1966   {
1967     CStdString &s = args[i];
1968     CStdString stripd = s.Trim();
1969     if (stripd[0] == '"' || stripd[0] == '\'')
1970     {
1971       s = s.TrimLeft();
1972       s = s.Right(s.size() - 1);
1973     }
1974     if (stripd[stripd.size() - 1] == '"' || stripd[stripd.size() - 1] == '\'')
1975     {
1976       s = s.TrimRight();
1977       s = s.Left(s.size() - 1);
1978     }
1979   }
1980
1981   return Command(args, waitExit);
1982 }
1983
1984 //
1985 // FIXME, this should be merged with the function below.
1986 //
1987 bool CUtil::Command(const CStdStringArray& arrArgs, bool waitExit)
1988 {
1989 #ifdef _DEBUG
1990   printf("Executing: ");
1991   for (size_t i=0; i<arrArgs.size(); i++)
1992     printf("%s ", arrArgs[i].c_str());
1993   printf("\n");
1994 #endif
1995
1996   pid_t child = fork();
1997   int n = 0;
1998   if (child == 0)
1999   {
2000     close(0);
2001     close(1);
2002     close(2);
2003     if (arrArgs.size() > 0)
2004     {
2005       char **args = (char **)alloca(sizeof(char *) * (arrArgs.size() + 3));
2006       memset(args, 0, (sizeof(char *) * (arrArgs.size() + 3)));
2007       for (size_t i=0; i<arrArgs.size(); i++)
2008         args[i] = (char *)arrArgs[i].c_str();
2009       execvp(args[0], args);
2010     }
2011   }
2012   else
2013   {
2014     if (waitExit) waitpid(child, &n, 0);
2015   }
2016
2017   return (waitExit) ? (WEXITSTATUS(n) == 0) : true;
2018 }
2019
2020 bool CUtil::SudoCommand(const CStdString &strCommand)
2021 {
2022   CLog::Log(LOGDEBUG, "Executing sudo command: <%s>", strCommand.c_str());
2023   pid_t child = fork();
2024   int n = 0;
2025   if (child == 0)
2026   {
2027     close(0); // close stdin to avoid sudo request password
2028     close(1);
2029     close(2);
2030     CStdStringArray arrArgs;
2031     StringUtils::SplitString(strCommand, " ", arrArgs);
2032     if (arrArgs.size() > 0)
2033     {
2034       char **args = (char **)alloca(sizeof(char *) * (arrArgs.size() + 3));
2035       memset(args, 0, (sizeof(char *) * (arrArgs.size() + 3)));
2036       args[0] = (char *)"/usr/bin/sudo";
2037       args[1] = (char *)"-S";
2038       for (size_t i=0; i<arrArgs.size(); i++)
2039       {
2040         args[i+2] = (char *)arrArgs[i].c_str();
2041       }
2042       execvp("/usr/bin/sudo", args);
2043     }
2044   }
2045   else
2046     waitpid(child, &n, 0);
2047
2048   return WEXITSTATUS(n) == 0;
2049 }
2050 #endif
2051
2052 int CUtil::LookupRomanDigit(char roman_digit)
2053 {
2054   switch (roman_digit)
2055   {
2056     case 'i':
2057     case 'I':
2058       return 1;
2059     case 'v':
2060     case 'V':
2061       return 5;
2062     case 'x':
2063     case 'X':
2064       return 10;
2065     case 'l':
2066     case 'L':
2067       return 50;
2068     case 'c':
2069     case 'C':
2070       return 100;
2071     case 'd':
2072     case 'D':
2073       return 500;
2074     case 'm':
2075     case 'M':
2076       return 1000;
2077     default:
2078       return 0;
2079   }
2080 }
2081
2082 int CUtil::TranslateRomanNumeral(const char* roman_numeral)
2083 {
2084   
2085   int decimal = -1;
2086
2087   if (roman_numeral && roman_numeral[0])
2088   {
2089     int temp_sum  = 0,
2090         last      = 0,
2091         repeat    = 0,
2092         trend     = 1;
2093     decimal = 0;
2094     while (*roman_numeral)
2095     {
2096       int digit = CUtil::LookupRomanDigit(*roman_numeral);
2097       int test  = last;
2098       
2099       // General sanity checks
2100
2101       // numeral not in LUT
2102       if (!digit)
2103         return -1;
2104       
2105       while (test > 5)
2106         test /= 10;
2107       
2108       // N = 10^n may not precede (N+1) > 10^(N+1)
2109       if (test == 1 && digit > last * 10)
2110         return -1;
2111       
2112       // N = 5*10^n may not precede (N+1) >= N
2113       if (test == 5 && digit >= last) 
2114         return -1;
2115
2116       // End general sanity checks
2117
2118       if (last < digit)
2119       {
2120         // smaller numerals may not repeat before a larger one
2121         if (repeat) 
2122           return -1;
2123
2124         temp_sum += digit;
2125         
2126         repeat  = 0;
2127         trend   = 0;
2128       }
2129       else if (last == digit)
2130       {
2131         temp_sum += digit;
2132         repeat++;
2133         trend = 1;
2134       }
2135       else
2136       {
2137         if (!repeat)
2138           decimal += 2 * last - temp_sum;
2139         else
2140           decimal += temp_sum;
2141         
2142         temp_sum = digit;
2143
2144         trend   = 1;
2145         repeat  = 0;
2146       }
2147       // Post general sanity checks
2148
2149       // numerals may not repeat more than thrice
2150       if (repeat == 3)
2151         return -1;
2152
2153       last = digit;
2154       roman_numeral++;
2155     }
2156
2157     if (trend)
2158       decimal += temp_sum;
2159     else
2160       decimal += 2 * last - temp_sum;
2161   }
2162   return decimal;
2163 }
2164
2165 CStdString CUtil::ResolveExecutablePath()
2166 {
2167   CStdString strExecutablePath;
2168 #ifdef WIN32
2169   wchar_t szAppPathW[MAX_PATH] = L"";
2170   ::GetModuleFileNameW(0, szAppPathW, sizeof(szAppPathW) - 1);
2171   CStdStringW strPathW = szAppPathW;
2172   g_charsetConverter.wToUTF8(strPathW,strExecutablePath);
2173 #elif defined(__APPLE__)
2174   char     given_path[2*MAXPATHLEN];
2175   uint32_t path_size =2*MAXPATHLEN;
2176
2177   GetDarwinExecutablePath(given_path, &path_size);
2178   strExecutablePath = given_path;
2179 #else
2180   /* Get our PID and build the name of the link in /proc */
2181   pid_t pid = getpid();
2182   char linkname[64]; /* /proc/<pid>/exe */
2183   snprintf(linkname, sizeof(linkname), "/proc/%i/exe", pid);
2184
2185   /* Now read the symbolic link */
2186   char buf[PATH_MAX];
2187   int ret = readlink(linkname, buf, PATH_MAX);
2188   buf[ret] = 0;
2189
2190   strExecutablePath = buf;
2191 #endif
2192   return strExecutablePath;
2193 }
2194
2195 CStdString CUtil::GetFrameworksPath(bool forPython)
2196 {
2197   CStdString strFrameworksPath;
2198 #if defined(__APPLE__)
2199   char     given_path[2*MAXPATHLEN];
2200   uint32_t path_size =2*MAXPATHLEN;
2201
2202   GetDarwinFrameworkPath(forPython, given_path, &path_size);
2203   strFrameworksPath = given_path;
2204 #endif
2205   return strFrameworksPath;
2206 }
2207
2208 void CUtil::ScanForExternalSubtitles(const CStdString& strMovie, std::vector<CStdString>& vecSubtitles )
2209 {
2210   unsigned int startTimer = CTimeUtils::GetTimeMS();
2211   
2212   // new array for commons sub dirs
2213   const char * common_sub_dirs[] = {"subs",
2214     "Subs",
2215     "subtitles",
2216     "Subtitles",
2217     "vobsubs",
2218     "Vobsubs",
2219     "sub",
2220     "Sub",
2221     "vobsub",
2222     "Vobsub",
2223     "subtitle",
2224     "Subtitle",
2225     NULL};
2226   
2227   vector<CStdString> vecExtensionsCached;
2228   //strExtensionCached = "";
2229   
2230   CFileItem item(strMovie, false);
2231   if (item.IsInternetStream()) return ;
2232   if (item.IsHDHomeRun()) return ;
2233   if (item.IsPlayList()) return ;
2234   if (!item.IsVideo()) return ;
2235   
2236   vector<CStdString> strLookInPaths;
2237   
2238   CStdString strMovieFileName;
2239   CStdString strPath;
2240   
2241   URIUtils::Split(strMovie, strPath, strMovieFileName);
2242   CStdString strMovieFileNameNoExt(URIUtils::ReplaceExtension(strMovieFileName, ""));
2243   strLookInPaths.push_back(strPath);
2244   
2245   if (!g_settings.iAdditionalSubtitleDirectoryChecked && !g_guiSettings.GetString("subtitles.custompath").IsEmpty()) // to avoid checking non-existent directories (network) every time..
2246   {
2247     if (!g_application.getNetwork().IsAvailable() && !URIUtils::IsHD(g_guiSettings.GetString("subtitles.custompath")))
2248     {
2249       CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonaccessible");
2250       g_settings.iAdditionalSubtitleDirectoryChecked = -1; // disabled
2251     }
2252     else if (!CDirectory::Exists(g_guiSettings.GetString("subtitles.custompath")))
2253     {
2254       CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonexistant");
2255       g_settings.iAdditionalSubtitleDirectoryChecked = -1; // disabled
2256     }
2257     
2258     g_settings.iAdditionalSubtitleDirectoryChecked = 1;
2259   }
2260   
2261   if (strMovie.Left(6) == "rar://") // <--- if this is found in main path then ignore it!
2262   {
2263     CURL url(strMovie);
2264     CStdString strArchive = url.GetHostName();
2265     URIUtils::Split(strArchive, strPath, strMovieFileName);
2266     strLookInPaths.push_back(strPath);
2267   }
2268   
2269   // checking if any of the common subdirs exist ..
2270   CStdStringArray directories;
2271   int nTokens = StringUtils::SplitString( strPath, "/", directories );
2272   if (nTokens == 1)
2273     StringUtils::SplitString( strPath, "\\", directories );
2274   
2275   // if it's inside a cdX dir, add parent path
2276   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
2277   {
2278     CStdString strPath2;
2279     URIUtils::GetParentPath(strPath,strPath2);
2280     strLookInPaths.push_back(strPath2);
2281   }
2282   int iSize = strLookInPaths.size();
2283   for (int i=0;i<iSize;++i)
2284   {
2285     for (int j=0; common_sub_dirs[j]; j++)
2286     {
2287       CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],common_sub_dirs[j]);
2288       if (CDirectory::Exists(strPath2))
2289         strLookInPaths.push_back(strPath2);
2290     }
2291   }
2292   // .. done checking for common subdirs
2293   
2294   // check if there any cd-directories in the paths we have added so far
2295   iSize = strLookInPaths.size();
2296   for (int i=0;i<9;++i) // 9 cd's
2297   {
2298     CStdString cdDir;
2299     cdDir.Format("cd%i",i+1);
2300     for (int i=0;i<iSize;++i)
2301     {
2302       CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],cdDir);
2303       URIUtils::AddSlashAtEnd(strPath2);
2304       bool pathAlreadyAdded = false;
2305       for (unsigned int i=0; i<strLookInPaths.size(); i++)
2306       {
2307         // if movie file is inside cd-dir, this directory can exist in vector already
2308         if (strLookInPaths[i].Equals( strPath2 ) )
2309           pathAlreadyAdded = true;
2310       }
2311       if (CDirectory::Exists(strPath2) && !pathAlreadyAdded) 
2312         strLookInPaths.push_back(strPath2);
2313     }
2314   }
2315   // .. done checking for cd-dirs
2316   
2317   // this is last because we dont want to check any common subdirs or cd-dirs in the alternate <subtitles> dir.
2318   if (g_settings.iAdditionalSubtitleDirectoryChecked == 1)
2319   {
2320     strPath = g_guiSettings.GetString("subtitles.custompath");
2321     URIUtils::AddSlashAtEnd(strPath);
2322     strLookInPaths.push_back(strPath);
2323   }
2324   
2325   CStdString strLExt;
2326   CStdString strDest;
2327   CStdString strItem;
2328   
2329   // 2 steps for movie directory and alternate subtitles directory
2330   CLog::Log(LOGDEBUG,"%s: Searching for subtitles...", __FUNCTION__);
2331   for (unsigned int step = 0; step < strLookInPaths.size(); step++)
2332   {
2333     if (strLookInPaths[step].length() != 0)
2334     {
2335       CFileItemList items;
2336       
2337       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);
2338       int fnl = strMovieFileNameNoExt.size();
2339       
2340       for (int j = 0; j < items.Size(); j++)
2341       {
2342         URIUtils::Split(items[j]->m_strPath, strPath, strItem);
2343         
2344         // is this a rar or zip-file
2345         if (URIUtils::IsRAR(strItem) || URIUtils::IsZIP(strItem))
2346         {
2347           ScanArchiveForSubtitles( items[j]->m_strPath, strMovieFileNameNoExt, vecSubtitles );
2348         }
2349         else    // not a rar/zip file
2350         {
2351           for (int i = 0; sub_exts[i]; i++)
2352           {
2353             //Cache subtitle with same name as movie
2354             if (URIUtils::GetExtension(strItem).Equals(sub_exts[i]) && strItem.Left(fnl).Equals(strMovieFileNameNoExt))
2355             {
2356               vecSubtitles.push_back( items[j]->m_strPath ); 
2357               CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, items[j]->m_strPath.c_str() );
2358             }
2359           }
2360         }
2361       }
2362       g_directoryCache.ClearDirectory(strLookInPaths[step]);
2363     }
2364   }
2365
2366   iSize = vecSubtitles.size();
2367   for (int i = 0; i < iSize; i++)
2368   {
2369     if (URIUtils::GetExtension(vecSubtitles[i]).Equals(".smi"))
2370     {
2371       //Cache multi-language sami subtitle
2372       CDVDSubtitleStream* pStream = new CDVDSubtitleStream();
2373       if(pStream->Open(vecSubtitles[i]))
2374       {
2375         CDVDSubtitleTagSami TagConv;
2376         TagConv.LoadHead(pStream);
2377         if (TagConv.m_Langclass.size() >= 2)
2378         {
2379           for (unsigned int k = 0; k < TagConv.m_Langclass.size(); k++)
2380           {
2381             strDest.Format("special://temp/subtitle.%s.%d.smi", TagConv.m_Langclass[k].Name, i);
2382             if (CFile::Cache(vecSubtitles[i], strDest))
2383             {
2384               CLog::Log(LOGINFO, " cached subtitle %s->%s\n", vecSubtitles[i].c_str(), strDest.c_str());
2385               vecSubtitles.push_back(strDest);
2386             }
2387           }
2388         }
2389       }
2390       delete pStream;
2391     }
2392   }
2393   CLog::Log(LOGDEBUG,"%s: END (total time: %i ms)", __FUNCTION__, (int)(CTimeUtils::GetTimeMS() - startTimer));
2394 }
2395
2396 int CUtil::ScanArchiveForSubtitles( const CStdString& strArchivePath, const CStdString& strMovieFileNameNoExt, std::vector<CStdString>& vecSubtitles )
2397 {
2398   int nSubtitlesAdded = 0;
2399   CFileItemList ItemList;
2400  
2401   // zip only gets the root dir
2402   if (URIUtils::GetExtension(strArchivePath).Equals(".zip"))
2403   {
2404    CStdString strZipPath;
2405    URIUtils::CreateArchivePath(strZipPath,"zip",strArchivePath,"");
2406    if (!CDirectory::GetDirectory(strZipPath,ItemList,"",false))
2407     return false;
2408   }
2409   else
2410   {
2411  #ifdef HAS_FILESYSTEM_RAR
2412    // get _ALL_files in the rar, even those located in subdirectories because we set the bMask to false.
2413    // so now we dont have to find any subdirs anymore, all files in the rar is checked.
2414    if( !g_RarManager.GetFilesInRar(ItemList, strArchivePath, false, "") )
2415     return false;
2416  #else
2417    return false;
2418  #endif
2419   }
2420   for (int it= 0 ; it <ItemList.Size();++it)
2421   {
2422    CStdString strPathInRar = ItemList[it]->m_strPath;
2423    CStdString strExt = URIUtils::GetExtension(strPathInRar);
2424    
2425    CLog::Log(LOGDEBUG, "ScanArchiveForSubtitles:: Found file %s", strPathInRar.c_str());
2426    // always check any embedded rar archives
2427    // checking for embedded rars, I moved this outside the sub_ext[] loop. We only need to check this once for each file.
2428    if (URIUtils::IsRAR(strPathInRar) || URIUtils::IsZIP(strPathInRar))
2429    {
2430     CStdString strRarInRar;
2431     if (URIUtils::GetExtension(strPathInRar).Equals(".rar"))
2432       URIUtils::CreateArchivePath(strRarInRar, "rar", strArchivePath, strPathInRar);
2433     else
2434       URIUtils::CreateArchivePath(strRarInRar, "zip", strArchivePath, strPathInRar);
2435     ScanArchiveForSubtitles(strRarInRar,strMovieFileNameNoExt,vecSubtitles);
2436    }
2437    // done checking if this is a rar-in-rar
2438    
2439    int iPos=0;
2440     while (sub_exts[iPos])
2441     {
2442      if (strExt.CompareNoCase(sub_exts[iPos]) == 0)
2443      {
2444       CStdString strSourceUrl;
2445       if (URIUtils::GetExtension(strArchivePath).Equals(".rar"))
2446        URIUtils::CreateArchivePath(strSourceUrl, "rar", strArchivePath, strPathInRar);
2447       else
2448        strSourceUrl = strPathInRar;
2449       
2450        CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, strSourceUrl.c_str() );
2451        vecSubtitles.push_back( strSourceUrl );
2452        nSubtitlesAdded++;
2453      }
2454      
2455      iPos++;
2456     }
2457   }
2458
2459   return nSubtitlesAdded;
2460 }
2461
2462 /*! \brief in a vector of subtitles finds the corresponding .sub file for a given .idx file
2463  */
2464 bool CUtil::FindVobSubPair( const std::vector<CStdString>& vecSubtitles, const CStdString& strIdxPath, CStdString& strSubPath )
2465 {
2466   if (URIUtils::GetExtension(strIdxPath) == ".idx")
2467   {
2468     CStdString strIdxFile;
2469     CStdString strIdxDirectory;
2470     URIUtils::Split(strIdxPath, strIdxDirectory, strIdxFile);
2471     for (unsigned int j=0; j < vecSubtitles.size(); j++)
2472     {
2473       CStdString strSubFile;
2474       CStdString strSubDirectory;
2475       URIUtils::Split(vecSubtitles[j], strSubDirectory, strSubFile);
2476       if (URIUtils::GetExtension(strSubFile) == ".sub" &&
2477           URIUtils::ReplaceExtension(strIdxFile,"").Equals(URIUtils::ReplaceExtension(strSubFile,"")))
2478       {
2479         strSubPath = vecSubtitles[j];
2480         return true;
2481       }
2482     }
2483   }
2484   return false;
2485 }
2486
2487 /*! \brief checks if in the vector of subtitles the given .sub file has a corresponding idx and hence is a vobsub file
2488  */
2489 bool CUtil::IsVobSub( const std::vector<CStdString>& vecSubtitles, const CStdString& strSubPath )
2490 {
2491   if (URIUtils::GetExtension(strSubPath) == ".sub")
2492   {
2493     CStdString strSubFile;
2494     CStdString strSubDirectory;
2495     URIUtils::Split(strSubPath, strSubDirectory, strSubFile);
2496     for (unsigned int j=0; j < vecSubtitles.size(); j++)
2497     {
2498       CStdString strIdxFile;
2499       CStdString strIdxDirectory;
2500       URIUtils::Split(vecSubtitles[j], strIdxDirectory, strIdxFile);
2501       if (URIUtils::GetExtension(strIdxFile) == ".idx" &&
2502           URIUtils::ReplaceExtension(strIdxFile,"").Equals(URIUtils::ReplaceExtension(strSubFile,"")))
2503         return true;
2504     }
2505   }
2506   return false;
2507 }
2508