2 * Copyright (C) 2005-2008 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
23 #include <sys/param.h>
24 #include <mach-o/dyld.h>
28 #include <sys/types.h>
35 #include "Application.h"
36 #include "utils/AutoPtrHandle.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"
50 #include "filesystem/MythDirectory.h"
52 #include "filesystem/UPnPDirectory.h"
57 #ifdef HAS_VIDEO_PLAYBACK
58 #include "cores/VideoRenderers/RenderManager.h"
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"
70 #include "WIN32Util.h"
72 #if defined(__APPLE__)
73 #include "osx/DarwinUtils.h"
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"
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"
96 using namespace XFILE;
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;
102 using namespace AUTOPTR;
103 using namespace XFILE;
104 using namespace PLAYLIST;
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];
124 CStdString CUtil::GetTitleFromPath(const CStdString& strFileNameAndPath, bool bIsFolder /* = false */)
126 // use above to get the filename
127 CStdString path(strFileNameAndPath);
128 URIUtils::RemoveSlashAtEnd(path);
129 CStdString strFilename = URIUtils::GetFileName(path);
131 CURL url(strFileNameAndPath);
132 CStdString strHostname = url.GetHostName();
136 if (url.GetProtocol() == "upnp")
137 strFilename = CUPnPDirectory::GetFriendlyName(strFileNameAndPath.c_str());
140 if (url.GetProtocol() == "rss")
144 if(dir.GetDirectory(strFileNameAndPath, items) && !items.m_strTitle.IsEmpty())
145 return items.m_strTitle;
149 if (url.GetProtocol() == "lastfm")
151 if (strFilename.IsEmpty())
152 strFilename = g_localizeStrings.Get(15200);
154 strFilename = g_localizeStrings.Get(15200) + " - " + strFilename;
158 else if (url.GetProtocol() == "shout")
160 const int genre = strFileNameAndPath.find_first_of('=');
162 strFilename = g_localizeStrings.Get(260);
164 strFilename = g_localizeStrings.Get(260) + " - " + strFileNameAndPath.substr(genre+1).c_str();
167 // Windows SMB Network (SMB)
168 else if (url.GetProtocol() == "smb" && strFilename.IsEmpty())
170 if (url.GetHostName().IsEmpty())
172 strFilename = g_localizeStrings.Get(20171);
176 strFilename = url.GetHostName();
180 else if (url.GetProtocol() == "xbms" && strFilename.IsEmpty())
181 strFilename = "XBMSP Network";
183 // iTunes music share (DAAP)
184 else if (url.GetProtocol() == "daap" && strFilename.IsEmpty())
185 strFilename = g_localizeStrings.Get(20174);
188 else if (url.GetProtocol() == "hdhomerun" && strFilename.IsEmpty())
189 strFilename = "HDHomerun Devices";
192 else if (url.GetProtocol() == "rtv")
193 strFilename = "ReplayTV Devices";
195 // HTS Tvheadend client
196 else if (url.GetProtocol() == "htsp")
197 strFilename = g_localizeStrings.Get(20256);
199 // VDR Streamdev client
200 else if (url.GetProtocol() == "vtp")
201 strFilename = g_localizeStrings.Get(20257);
204 else if (url.GetProtocol() == "myth")
205 strFilename = g_localizeStrings.Get(20258);
208 else if (url.GetProtocol() == "sap" && strFilename.IsEmpty())
209 strFilename = "SAP Streams";
212 else if (url.GetProtocol() == "sources")
213 strFilename = g_localizeStrings.Get(744);
216 else if (path.Left(24).Equals("special://musicplaylists"))
217 strFilename = g_localizeStrings.Get(136);
220 else if (path.Left(24).Equals("special://videoplaylists"))
221 strFilename = g_localizeStrings.Get(136);
223 else if ((url.GetProtocol() == "rar" || url.GetProtocol() == "zip") && strFilename.IsEmpty())
224 strFilename = URIUtils::GetFileName(url.GetHostName());
226 // now remove the extension if needed
227 if (!g_guiSettings.GetBool("filelists.showextensions") && !bIsFolder)
229 URIUtils::RemoveExtension(strFilename);
233 // URLDecode since the original path may be an URL
234 CURL::Decode(strFilename);
238 bool CUtil::GetVolumeFromFileName(const CStdString& strFileName, CStdString& strFileTitle, CStdString& strVolumeNumber)
240 const CStdStringArray ®exps = g_advancedSettings.m_videoStackRegExps;
242 CStdString strFileNameTemp = strFileName;
246 for (unsigned int i = 0; i < regexps.size(); i++)
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());
254 // CLog::Log(LOGDEBUG, "Regexp:[%s]", regexps[i].c_str());
256 int iFoundToken = reg.RegFind(strFileName.c_str());
257 if (iFoundToken >= 0)
259 int iRegLength = reg.GetFindLen();
260 int iCount = reg.GetSubCount();
263 reg.DumpOvector(LOGDEBUG);
264 CLog::Log(LOGDEBUG, "Subcount=%i", iCount);
265 for (int j = 0; j <= iCount; j++)
267 CStdString str = reg.GetMatch(j);
268 CLog::Log(LOGDEBUG, "Sub(%i):[%s]", j, str.c_str());
272 // simple regexp, only the volume is captured
275 strVolumeNumber = reg.GetMatch(1);
276 if (strVolumeNumber.IsEmpty()) return false;
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;
292 // advanced regexp with prefix (1), volume (2), and suffix (3)
293 else if (iCount == 3)
295 // second subpatten contains the stacking volume
296 strVolumeNumber = reg.GetMatch(2);
297 if (strVolumeNumber.IsEmpty()) return false;
299 // everything before the regexp match
300 strFileTitle = strFileName.Left(iFoundToken);
302 // first subpattern contains prefix
303 strFileTitle += reg.GetMatch(1);
305 // third subpattern contains suffix
306 strFileTitle += reg.GetMatch(3);
308 // everything after the regexp match
309 strFileTitle += strFileNameTemp.Mid(iFoundToken + iRegLength);
314 // unknown regexp format
317 CLog::Log(LOGERROR, "Incorrect movie stacking regexp format:[%s]", regexps[i].c_str());
324 void CUtil::CleanString(const CStdString& strFileName, CStdString& strTitle, CStdString& strTitleAndYear, CStdString& strYear, bool bRemoveExtension /* = false */, bool bCleanChars /* = true */)
326 strTitleAndYear = strFileName;
328 if (strFileName.Equals(".."))
331 const CStdStringArray ®exps = g_advancedSettings.m_videoCleanStringRegExps;
333 CRegExp reTags(true);
335 CStdString strExtension;
336 URIUtils::GetExtension(strFileName, strExtension);
338 if (!reYear.RegComp(g_advancedSettings.m_videoCleanDateTimeRegExp))
340 CLog::Log(LOGERROR, "%s: Invalid datetime clean RegExp:'%s'", __FUNCTION__, g_advancedSettings.m_videoCleanDateTimeRegExp.c_str());
344 if (reYear.RegFind(strTitleAndYear.c_str()) >= 0)
346 strTitleAndYear = reYear.GetReplaceString("\\1");
347 strYear = reYear.GetReplaceString("\\2");
351 URIUtils::RemoveExtension(strTitleAndYear);
353 for (unsigned int i = 0; i < regexps.size(); i++)
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());
361 if ((j=reTags.RegFind(strFileName.c_str())) > 0)
362 strTitleAndYear = strTitleAndYear.Mid(0, j);
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.
372 bool initialDots = true;
373 bool alreadyContainsSpace = (strTitleAndYear.Find(' ') >= 0);
375 for (int i = 0; i < (int)strTitleAndYear.size(); i++)
377 char c = strTitleAndYear.GetAt(i);
382 if ((c == '_') || ((!alreadyContainsSpace) && !initialDots && (c == '.')))
384 strTitleAndYear.SetAt(i, ' ');
389 strTitle = strTitleAndYear.Trim();
392 if (!strYear.IsEmpty())
393 strTitleAndYear = strTitle + " (" + strYear + ")";
395 // restore extension if needed
396 if (!bRemoveExtension)
397 strTitleAndYear += strExtension;
400 void CUtil::GetQualifiedFilename(const CStdString &strBasePath, CStdString &strFilename)
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;
407 if (plBaseUrl.IsLocal()) //Base in local directory
409 if (plItemUrl.IsLocal() ) //Filename is local or not qualified
412 if (!( (strFilename.c_str()[1] == ':') || (strFilename.c_str()[0] == '/') ) ) //Filename not fully qualified
414 if (!( strFilename.c_str()[1] == ':')) //Filename not fully qualified
417 if (strFilename.c_str()[0] == '/' || strFilename.c_str()[0] == '\\' || URIUtils::HasSlashAtEnd(strBasePath))
419 strFilename = strBasePath + strFilename;
420 strFilename.Replace('/', '\\');
424 strFilename = strBasePath + '\\' + strFilename;
425 strFilename.Replace('/', '\\');
429 strFilename.Replace("\\.\\", "\\");
430 while ((iDotDotLoc = strFilename.Find("\\..\\")) > 0)
432 iEndCut = iDotDotLoc + 4;
433 iBeginCut = strFilename.Left(iDotDotLoc).ReverseFind('\\') + 1;
434 strFilename.Delete(iBeginCut, iEndCut - iBeginCut);
437 else //Base is remote
439 if (plItemUrl.IsLocal()) //Filename is local
442 if ( (strFilename.c_str()[1] == ':') || (strFilename.c_str()[0] == '/') ) //Filename not fully qualified
444 if (strFilename[1] == ':') // already fully qualified
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..
450 strFilename = strBasePath + strFilename;
451 strFilename.Replace('\\', '/');
455 strFilename = strBasePath + '/' + strFilename;
456 strFilename.Replace('\\', '/');
459 strFilename.Replace("/./", "/");
460 while ((iDotDotLoc = strFilename.Find("/../")) > 0)
462 iEndCut = iDotDotLoc + 4;
463 iBeginCut = strFilename.Left(iDotDotLoc).ReverseFind('/') + 1;
464 strFilename.Delete(iBeginCut, iEndCut - iBeginCut);
469 void CUtil::RunShortcut(const char* szShortcutPath)
473 void CUtil::GetHomePath(CStdString& strPath, const CStdString& strTarget)
475 CStdString strHomePath;
476 strHomePath = ResolveExecutablePath();
478 CStdStringW strPathW, strTargetW;
479 g_charsetConverter.utf8ToW(strTarget, strTargetW);
480 strPathW = _wgetenv(strTargetW);
481 g_charsetConverter.wToUTF8(strPathW,strPath);
483 strPath = getenv(strTarget);
486 if (strPath != NULL && !strPath.IsEmpty())
490 //expand potential relative path to full path
491 if(GetFullPathName(strPath, 1024, tmp, 0) != 0)
501 char given_path[2*MAXPATHLEN];
502 uint32_t path_size =2*MAXPATHLEN;
504 result = GetDarwinExecutablePath(given_path, &path_size);
507 // Move backwards to last /.
508 for (int n=strlen(given_path)-1; given_path[n] != '/'; n--)
509 given_path[n] = '\0';
512 strcat(given_path, "/XBMCData/XBMCHome/");
514 // Assume local path inside application bundle.
515 strcat(given_path, "../Resources/XBMC/");
518 // Convert to real path.
519 char real_path[2*MAXPATHLEN];
520 if (realpath(given_path, real_path) != NULL)
527 size_t last_sep = strHomePath.find_last_of(PATH_SEPARATOR_CHAR);
528 if (last_sep != string::npos)
529 strPath = strHomePath.Left(last_sep);
531 strPath = strHomePath;
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
538 CStdString installPath = INSTALL_PATH;
539 CStdString binInstallPath = BIN_INSTALL_PATH;
540 if (!strTarget.compare("XBMC_HOME") && installPath.compare(binInstallPath))
542 int pos = strPath.length() - binInstallPath.length();
543 CStdString tmp = strPath;
545 if (!tmp.compare(binInstallPath))
547 strPath.erase(pos, strPath.length());
548 strPath.append(installPath);
554 bool CUtil::IsPicture(const CStdString& strFile)
556 CStdString extension = URIUtils::GetExtension(strFile);
558 if (extension.IsEmpty())
562 if (g_settings.m_pictureExtensions.Find(extension) != -1)
565 if (extension == ".tbn" || extension == ".dds")
571 bool CUtil::ExcludeFileOrFolder(const CStdString& strFileOrFolder, const CStdStringArray& regexps)
573 if (strFileOrFolder.IsEmpty())
576 CRegExp regExExcludes(true); // case insensitive regex
578 for (unsigned int i = 0; i < regexps.size(); i++)
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());
585 if (regExExcludes.RegFind(strFileOrFolder) > -1)
587 CLog::Log(LOGDEBUG, "%s: File '%s' excluded. (Matches exclude rule RegExp:'%s')", __FUNCTION__, strFileOrFolder.c_str(), regexps[i].c_str());
594 void CUtil::GetFileAndProtocol(const CStdString& strURL, CStdString& strDir)
597 if (!URIUtils::IsRemote(strURL)) return ;
598 if (URIUtils::IsDVD(strURL)) return ;
601 strDir.Format("%s://%s", url.GetProtocol().c_str(), url.GetFileName().c_str());
604 int CUtil::GetDVDIfoTitle(const CStdString& strFile)
606 CStdString strFilename = URIUtils::GetFileName(strFile);
607 if (strFilename.Equals("video_ts.ifo")) return 0;
609 return atoi(strFilename.Mid(4, 2).c_str());
612 CStdString CUtil::GetFileMD5(const CStdString& strPath)
616 if (file.Open(strPath))
624 read = file.Read(temp,1024);
626 md5.append(temp,read);
628 md5.getDigest(result);
635 bool CUtil::GetDirectoryName(const CStdString& strFileName, CStdString& strDescription)
637 CStdString strFName = URIUtils::GetFileName(strFileName);
638 strDescription = strFileName.Left(strFileName.size() - strFName.size());
639 URIUtils::RemoveSlashAtEnd(strDescription);
641 int iPos = strDescription.ReverseFind("\\");
643 iPos = strDescription.ReverseFind("/");
646 CStdString strTmp = strDescription.Right(strDescription.size()-iPos-1);
647 strDescription = strTmp;//strDescription.Right(strDescription.size() - iPos - 1);
649 else if (strDescription.size() <= 0)
650 strDescription = strFName;
654 void CUtil::GetDVDDriveIcon( const CStdString& strPath, CStdString& strIcon )
656 if ( !g_mediaManager.IsDiscInDrive() )
658 strIcon = "DefaultDVDEmpty.png";
662 if ( URIUtils::IsDVD(strPath) )
665 CCdInfo* pInfo = g_mediaManager.GetCdInfo();
667 if ( pInfo != NULL && pInfo->IsUDFX( 1 ) )
669 strIcon = "DefaultXboxDVD.png";
673 strIcon = "DefaultDVDRom.png";
677 if ( URIUtils::IsISO9660(strPath) )
680 CCdInfo* pInfo = g_mediaManager.GetCdInfo();
681 if ( pInfo != NULL && pInfo->IsVideoCd( 1 ) )
683 strIcon = "DefaultVCD.png";
687 strIcon = "DefaultDVDRom.png";
691 if ( URIUtils::IsCDDA(strPath) )
693 strIcon = "DefaultCDDA.png";
698 void CUtil::RemoveTempFiles()
700 CStdString searchPath = g_settings.GetDatabaseFolder();
702 if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".tmp", false))
705 for (int i = 0; i < items.Size(); ++i)
707 if (items[i]->m_bIsFolder)
709 XFILE::CFile::Delete(items[i]->m_strPath);
713 void CUtil::ClearSubtitles()
717 CDirectory::GetDirectory("special://temp/",items);
718 for( int i=0;i<items.Size();++i)
720 if (!items[i]->m_bIsFolder)
722 if ( items[i]->m_strPath.Find("subtitle") >= 0 || items[i]->m_strPath.Find("vobsub_queue") >= 0 )
724 CLog::Log(LOGDEBUG, "%s - Deleting temporary subtitle %s", __FUNCTION__, items[i]->m_strPath.c_str());
725 CFile::Delete(items[i]->m_strPath);
731 static const char * sub_exts[] = { ".utf", ".utf8", ".utf-8", ".sub", ".srt", ".smi", ".rt", ".txt", ".ssa", ".aqt", ".jss", ".ass", ".idx", NULL};
733 int64_t CUtil::ToInt64(uint32_t high, uint32_t low)
742 bool CUtil::ThumbExists(const CStdString& strFileName, bool bAddCache)
744 return CThumbnailCache::GetThumbnailCache()->ThumbExists(strFileName, bAddCache);
747 void CUtil::ThumbCacheAdd(const CStdString& strFileName, bool bFileExists)
749 CThumbnailCache::GetThumbnailCache()->Add(strFileName, bFileExists);
752 void CUtil::ThumbCacheClear()
754 CThumbnailCache::GetThumbnailCache()->Clear();
757 bool CUtil::ThumbCached(const CStdString& strFileName)
759 return CThumbnailCache::GetThumbnailCache()->IsCached(strFileName);
762 void CUtil::PlayDVD(const CStdString& strProtocol)
764 #if defined(HAS_DVDPLAYER) && defined(HAS_DVD_DRIVE)
765 CIoSupport::Dismount("Cdrom0");
766 CIoSupport::RemapDriveLetter('D', "Cdrom0");
768 strPath.Format("%s://1", strProtocol.c_str());
769 CFileItem item(strPath, false);
770 item.SetLabel(g_mediaManager.GetDiskLabel());
771 g_application.PlayFile(item);
775 CStdString CUtil::GetNextFilename(const CStdString &fn_template, int max)
777 if (!fn_template.Find("%03d"))
780 CStdString searchPath;
781 URIUtils::GetDirectory(fn_template, searchPath);
782 CStdString mask = URIUtils::GetExtension(fn_template);
785 name.Format(fn_template.c_str(), 0);
788 if (!CDirectory::GetDirectory(searchPath, items, mask, false))
791 items.SetFastLookup(true);
792 for (int i = 0; i <= max; i++)
795 name.Format(fn_template.c_str(), i);
796 if (!items.Get(name))
802 CStdString CUtil::GetNextPathname(const CStdString &path_template, int max)
804 if (!path_template.Find("%04d"))
807 for (int i = 0; i <= max; i++)
810 name.Format(path_template.c_str(), i);
811 if (!CFile::Exists(name))
817 void CUtil::Tokenize(const CStdString& path, vector<CStdString>& tokens, const string& delimiters)
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);
825 while (string::npos != pos || string::npos != lastPos)
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);
836 void CUtil::TakeScreenshot(const CStdString &filename, bool sync)
841 unsigned char* outpixels = NULL;
844 LPDIRECT3DSURFACE9 lpSurface = NULL, lpBackbuffer = NULL;
845 g_graphicsContext.Lock();
846 if (g_application.IsPlayingVideo())
848 #ifdef HAS_VIDEO_PLAYBACK
849 g_renderManager.SetupScreenshot();
852 g_application.RenderNoPresent();
854 if (FAILED(g_Windowing.Get3DDevice()->CreateOffscreenPlainSurface(g_Windowing.GetWidth(), g_Windowing.GetHeight(), D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, &lpSurface, NULL)))
857 if (FAILED(g_Windowing.Get3DDevice()->GetRenderTarget(0, &lpBackbuffer)))
860 // now take screenshot
861 if (SUCCEEDED(g_Windowing.Get3DDevice()->GetRenderTargetData(lpBackbuffer, lpSurface)))
864 D3DSURFACE_DESC desc;
865 lpSurface->GetDesc(&desc);
866 if (SUCCEEDED(lpSurface->LockRect(&lr, NULL, D3DLOCK_READONLY)))
869 height = desc.Height;
871 outpixels = new unsigned char[height * stride];
872 memcpy(outpixels, lr.pBits, height * stride);
873 lpSurface->UnlockRect();
877 CLog::Log(LOGERROR, "%s LockRect failed", __FUNCTION__);
882 CLog::Log(LOGERROR, "%s GetBackBuffer failed", __FUNCTION__);
884 lpSurface->Release();
885 lpBackbuffer->Release();
887 g_graphicsContext.Unlock();
889 #elif defined(HAS_GL) || defined(HAS_GLES)
891 g_graphicsContext.BeginPaint();
892 if (g_application.IsPlayingVideo())
894 #ifdef HAS_VIDEO_PLAYBACK
895 g_renderManager.SetupScreenshot();
898 g_application.RenderNoPresent();
900 glReadBuffer(GL_BACK);
902 //get current viewport
904 glGetIntegerv(GL_VIEWPORT, viewport);
906 width = viewport[2] - viewport[0];
907 height = viewport[3] - viewport[1];
909 unsigned char* pixels = new unsigned char[stride * height];
911 //read pixels from the backbuffer
913 glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)pixels);
915 glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid*)pixels);
917 g_graphicsContext.EndPaint();
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);
927 //nothing to take a screenshot from
933 CLog::Log(LOGERROR, "Screenshot %s failed", filename.c_str());
937 CLog::Log(LOGDEBUG, "Saving screenshot %s", filename.c_str());
939 //set alpha byte to 0xFF
940 for (int y = 0; y < height; y++)
942 unsigned char* alphaptr = outpixels - 1 + y * stride;
943 for (int x = 0; x < width; x++)
944 *(alphaptr += 4) = 0xFF;
947 //if sync is true, the png file needs to be completely written when this function returns
950 if (!CPicture::CreateThumbnailFromSurface(outpixels, width, height, stride, filename))
951 CLog::Log(LOGERROR, "Unable to write screenshot %s", filename.c_str());
957 //make sure the file exists to avoid concurrency issues
958 FILE* fp = fopen(filename.c_str(), "w");
962 CLog::Log(LOGERROR, "Unable to create file %s", filename.c_str());
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);
971 void CUtil::TakeScreenshot()
973 static bool savingScreenshots = false;
974 static vector<CStdString> screenShots;
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())
981 strDir = "special://temp/";
982 if (!savingScreenshots)
985 savingScreenshots = true;
989 URIUtils::RemoveSlashAtEnd(strDir);
991 if (!strDir.IsEmpty())
993 CStdString file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(strDir, "screenshot%03d.png"), 999);
997 TakeScreenshot(file, false);
998 if (savingScreenshots)
999 screenShots.push_back(file);
1001 { // grab the real directory
1002 CStdString newDir = g_guiSettings.GetString("debug.screenshotpath");
1003 if (!newDir.IsEmpty())
1005 for (unsigned int i = 0; i < screenShots.size(); i++)
1007 CStdString file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(newDir, "screenshot%03d.png"), 999);
1008 CFile::Cache(screenShots[i], file);
1010 screenShots.clear();
1012 savingScreenshots = false;
1017 CLog::Log(LOGWARNING, "Too many screen shots or invalid folder");
1022 void CUtil::StatToStatI64(struct _stati64 *result, struct stat *stat)
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;
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);
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);
1044 void CUtil::Stat64ToStatI64(struct _stati64 *result, struct __stat64 *stat)
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;
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);
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);
1065 void CUtil::StatI64ToStat64(struct __stat64 *result, struct _stati64 *stat)
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;
1076 result->st_atime = stat->st_atime;
1077 result->st_mtime = stat->st_mtime;
1078 result->st_ctime = stat->st_ctime;
1080 result->st_atime = stat->_st_atime;
1081 result->st_mtime = stat->_st_mtime;
1082 result->st_ctime = stat->_st_ctime;
1086 void CUtil::Stat64ToStat(struct stat *result, struct __stat64 *stat)
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;
1096 if (stat->st_size <= LONG_MAX)
1097 result->st_size = (_off_t)stat->st_size;
1099 if (sizeof(stat->st_size) <= sizeof(result->st_size) )
1100 result->st_size = (off_t)stat->st_size;
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");
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);
1113 void CUtil::Stat64ToStat64i32(struct _stat64i32 *result, struct __stat64 *stat)
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;
1123 if (stat->st_size <= LONG_MAX)
1124 result->st_size = (_off_t)stat->st_size;
1126 if (sizeof(stat->st_size) <= sizeof(result->st_size) )
1127 result->st_size = (off_t)stat->st_size;
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");
1135 result->st_atime = stat->st_atime;
1136 result->st_mtime = stat->st_mtime;
1137 result->st_ctime = stat->st_ctime;
1139 result->st_atime = stat->_st_atime;
1140 result->st_mtime = stat->_st_mtime;
1141 result->st_ctime = stat->_st_ctime;
1146 bool CUtil::CreateDirectoryEx(const CStdString& strPath)
1148 // Function to create all directories at once instead
1149 // of calling CreateDirectory for every subdir.
1150 // Creates the directory and subdirectories if needed.
1152 // return true if directory already exist
1153 if (CDirectory::Exists(strPath)) return true;
1155 // we currently only allow HD and smb paths
1156 if (!URIUtils::IsHD(strPath) && !URIUtils::IsSmb(strPath))
1158 CLog::Log(LOGERROR,"%s called with an unsupported path: %s", __FUNCTION__, strPath.c_str());
1163 // silly CStdString can't take a char in the constructor
1164 CStdString sep(1, url.GetDirectorySeparator());
1166 // split the filename portion of the URL up into separate dirs
1167 CStdStringArray dirs;
1168 StringUtils::SplitString(url.GetFileName(), sep, dirs);
1170 // we start with the root path
1171 CStdString dir = url.GetWithoutFilename();
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;
1178 // and append the rest of the directories successively, creating each dir
1180 for (; i < dirs.size(); i++)
1182 dir = URIUtils::AddFileToFolder(dir, dirs[i]);
1183 CDirectory::Create(dir);
1186 // was the final destination directory successfully created ?
1187 if (!CDirectory::Exists(strPath)) return false;
1191 CStdString CUtil::MakeLegalFileName(const CStdString &strFile, int LegalType)
1193 CStdString result = strFile;
1195 result.Replace('/', '_');
1196 result.Replace('\\', '_');
1197 result.Replace('?', '_');
1199 if (LegalType == LEGAL_WIN32_COMPAT)
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(" ");
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)
1220 URIUtils::GetDirectory(strPathAndFile,strPath);
1221 CStdString strFileName = URIUtils::GetFileName(strPathAndFile);
1222 return strPath + MakeLegalFileName(strFileName, LegalType);
1225 CStdString CUtil::ValidatePath(const CStdString &path, bool bFixDoubleSlashes /* = false */)
1227 CStdString result = path;
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:") ))
1240 // check the path for incorrect slashes
1242 if (URIUtils::IsDOSPath(path))
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.
1249 if (bFixDoubleSlashes)
1251 // Fixup for double back slashes (but ignore the \\ of unc-paths)
1252 for (int x = 1; x < result.GetLength() - 1; x++)
1254 if (result[x] == '\\' && result[x+1] == '\\')
1259 else if (path.Find("://") >= 0 || path.Find(":\\\\") >= 0)
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.
1267 if (bFixDoubleSlashes)
1269 // Fixup for double forward slashes(/) but don't touch the :// of URLs
1270 for (int x = 2; x < result.GetLength() - 1; x++)
1272 if ( result[x] == '/' && result[x + 1] == '/' && !(result[x - 1] == ':' || (result[x - 1] == '/' && result[x - 2] == ':')) )
1280 bool CUtil::IsUsingTTFSubtitles()
1282 return URIUtils::GetExtension(g_guiSettings.GetString("subtitles.font")).Equals(".ttf");
1286 bool CUtil::TestSplitExec()
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")
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\"")
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\\")
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\\")
1307 void CUtil::SplitExecFunction(const CStdString &execString, CStdString &function, vector<CStdString> ¶meters)
1309 CStdString paramString;
1311 int iPos = execString.Find("(");
1312 int iPos2 = execString.ReverseFind(")");
1313 if (iPos > 0 && iPos2 > 0)
1315 paramString = execString.Mid(iPos + 1, iPos2 - iPos - 1);
1316 function = execString.Left(iPos);
1319 function = execString;
1321 // remove any whitespace, and the standard prefix (if it exists)
1323 if( function.Left(5).Equals("xbmc.", false) )
1324 function.Delete(0, 5);
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
1330 size_t whiteSpacePos = 0;
1331 CStdString parameter;
1333 for (size_t pos = 0; pos < paramString.size(); pos++)
1335 char ch = paramString[pos];
1336 bool escaped = (pos > 0 && paramString[pos - 1] == '\\' && !lastEscaped);
1337 lastEscaped = escaped;
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
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
1353 if (inFunction && ch == ')')
1354 { // end of a function
1358 { // start of function
1361 if (!inFunction && !URIUtils::IsStack(paramString) && ch == ',')
1362 { // not in a function, so a comma signfies the end of this parameter
1364 parameter = parameter.Left(whiteSpacePos);
1365 parameters.push_back(parameter);
1371 if ((ch == '\"' || ch == '\\') && escaped)
1372 { // escaped quote or backslash
1373 parameter[parameter.size()-1] = ch;
1376 // whitespace handling - we skip any whitespace at the left or right of an unquoted parameter
1377 if (ch == ' ' && !inQuotes)
1379 if (parameter.IsEmpty()) // skip whitespace on left
1381 if (!whiteSpacePos) // make a note of where whitespace starts on the right
1382 whiteSpacePos = parameter.size();
1388 if (inFunction || inQuotes)
1389 CLog::Log(LOGWARNING, "%s(%s) - end of string while searching for ) or \"", __FUNCTION__, execString.c_str());
1391 parameter = parameter.Left(whiteSpacePos);
1392 if (!parameter.IsEmpty())
1393 parameters.push_back(parameter);
1396 int CUtil::GetMatchingSource(const CStdString& strPath1, VECSOURCES& VECSOURCES, bool& bIsSourceName)
1398 if (strPath1.IsEmpty())
1401 //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, testing original path/name [%s]", strPath1.c_str());
1403 // copy as we may change strPath
1404 CStdString strPath = strPath1;
1406 // Check for special protocols
1407 CURL checkURL(strPath);
1410 if (checkURL.GetProtocol() == "stack")
1411 strPath.Delete(0, 8); // remove the stack protocol
1413 if (checkURL.GetProtocol() == "shout")
1414 strPath = checkURL.GetHostName();
1415 if (checkURL.GetProtocol() == "lastfm")
1417 if (checkURL.GetProtocol() == "tuxbox")
1419 if (checkURL.GetProtocol() == "plugin")
1421 if (checkURL.GetProtocol() == "multipath")
1422 strPath = CMultiPathDirectory::GetFirstPath(strPath);
1424 //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, testing for matching name [%s]", strPath.c_str());
1425 bIsSourceName = false;
1428 // we first test the NAME of a source
1429 for (int i = 0; i < (int)VECSOURCES.size(); ++i)
1431 CMediaSource share = VECSOURCES.at(i);
1432 CStdString strName = share.strName;
1434 // special cases for dvds
1435 if (URIUtils::IsOnDVD(share.strPath))
1437 if (URIUtils::IsOnDVD(strPath))
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('(');
1445 strName = strName.Mid(0, iPos - 1);
1447 //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, comparing name [%s]", strName.c_str());
1448 if (strPath.Equals(strName))
1450 bIsSourceName = true;
1455 // now test the paths
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))
1465 int iLenPath = strDest.size();
1467 //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, testing url [%s]", strDest.c_str());
1469 for (int i = 0; i < (int)VECSOURCES.size(); ++i)
1471 CMediaSource share = VECSOURCES.at(i);
1473 // does it match a source name?
1474 if (share.strPath.substr(0,8) == "shout://")
1476 CURL url(share.strPath);
1477 if (strPath.Equals(url.GetHostName()))
1481 // doesnt match a name, so try the source path
1482 vector<CStdString> vecPaths;
1484 // add any concatenated paths if they exist
1485 if (share.vecPaths.size() > 0)
1486 vecPaths = share.vecPaths;
1488 // add the actual share path at the front of the vector
1489 vecPaths.insert(vecPaths.begin(), share.strPath);
1492 for (int j = 0; j < (int)vecPaths.size(); ++j)
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))
1502 int iLenShare = strShare.size();
1503 //CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource, comparing url [%s]", strShare.c_str());
1505 if ((iLenPath >= iLenShare) && (strDest.Left(iLenShare).Equals(strShare)) && (iLenShare > iLength))
1507 //CLog::Log(LOGDEBUG,"Found matching source at index %i: [%s], Len = [%i]", i, strShare.c_str(), iLenShare);
1509 // if exact match, return it immediately
1510 if (iLenPath == iLenShare)
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;
1520 iLength = iLenShare;
1525 // return the index of the share with the longest match
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://") )
1533 // get the hostname portion of the url since it contains the archive file
1534 strPath = checkURL.GetHostName();
1536 bIsSourceName = false;
1538 return GetMatchingSource(strPath, VECSOURCES, bDummy);
1541 CLog::Log(LOGWARNING,"CUtil::GetMatchingSource... no matching source found for [%s]", strPath1.c_str());
1546 CStdString CUtil::TranslateSpecialSource(const CStdString &strSpecial)
1548 CStdString strReturn=strSpecial;
1549 if (!strSpecial.IsEmpty() && strSpecial[0] == '$')
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);
1578 CStdString CUtil::MusicPlaylistsLocation()
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);;
1589 CStdString CUtil::VideoPlaylistsLocation()
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);;
1600 void CUtil::DeleteMusicDatabaseDirectoryCache()
1602 CUtil::DeleteDirectoryCache("mdb-");
1605 void CUtil::DeleteVideoDatabaseDirectoryCache()
1607 CUtil::DeleteDirectoryCache("vdb-");
1610 void CUtil::DeleteDirectoryCache(const CStdString &prefix)
1612 CStdString searchPath = "special://temp/";
1613 CFileItemList items;
1614 if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".fi", false))
1617 for (int i = 0; i < items.Size(); ++i)
1619 if (items[i]->m_bIsFolder)
1621 CStdString fileName = URIUtils::GetFileName(items[i]->m_strPath);
1622 if (fileName.Left(prefix.GetLength()) == prefix)
1623 XFILE::CFile::Delete(items[i]->m_strPath);
1627 bool CUtil::SetSysDateTimeYear(int iYear, int iMonth, int iDay, int iHour, int iMinute)
1629 TIME_ZONE_INFORMATION tziNew;
1632 GetLocalTime(&CurTime);
1633 GetLocalTime(&NewTime);
1634 int iRescBiases, iHourUTC;
1637 DWORD dwRet = GetTimeZoneInformation(&tziNew); // Get TimeZone Informations
1638 float iGMTZone = (float(tziNew.Bias)/(60)); // Calc's the GMT Time
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);
1648 case TIME_ZONE_ID_STANDARD:
1650 iRescBiases = tziNew.Bias + tziNew.StandardBias;
1651 CLog::Log(LOGDEBUG, "- Timezone ID: 1, Standart");
1654 case TIME_ZONE_ID_DAYLIGHT:
1656 iRescBiases = tziNew.Bias + tziNew.StandardBias + tziNew.DaylightBias;
1657 CLog::Log(LOGDEBUG, "- Timezone ID: 2, Daylight");
1660 case TIME_ZONE_ID_UNKNOWN:
1662 iRescBiases = tziNew.Bias + tziNew.StandardBias;
1663 CLog::Log(LOGDEBUG, "- Timezone ID: 0, Unknown");
1666 case TIME_ZONE_ID_INVALID:
1668 iRescBiases = tziNew.Bias + tziNew.StandardBias;
1669 CLog::Log(LOGDEBUG, "- Timezone ID: Invalid");
1673 iRescBiases = tziNew.Bias + tziNew.StandardBias;
1675 CLog::Log(LOGDEBUG, "--------------- END ---------------");
1678 iHourUTC = GMTZoneCalc(iRescBiases, iHour, iMinute, iMinuteNew);
1679 iMinute = iMinuteNew;
1683 iHourUTC =iHourUTC + 24;
1688 iHourUTC =iHourUTC - 24;
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;
1698 FILETIME stNewTime, stCurTime;
1699 SystemTimeToFileTime(&NewTime, &stNewTime);
1700 SystemTimeToFileTime(&CurTime, &stCurTime);
1703 int CUtil::GMTZoneCalc(int iRescBiases, int iHour, int iMinute, int &iMinuteNew)
1705 int iHourUTC, iTemp;
1706 iMinuteNew = iMinute;
1707 iTemp = iRescBiases/60;
1709 if (iRescBiases == 0 )return iHour; // GMT Zone 0, no need calculate
1710 if (iRescBiases > 0)
1711 iHourUTC = iHour + abs(iTemp);
1713 iHourUTC = iHour - abs(iTemp);
1715 if ((iTemp*60) != iRescBiases)
1717 if (iRescBiases > 0)
1718 iMinuteNew = iMinute + abs(iTemp*60 - iRescBiases);
1720 iMinuteNew = iMinute - abs(iTemp*60 - iRescBiases);
1722 if (iMinuteNew >= 60)
1724 iMinuteNew = iMinuteNew -60;
1725 iHourUTC = iHourUTC + 1;
1727 else if (iMinuteNew < 0)
1729 iMinuteNew = iMinuteNew +60;
1730 iHourUTC = iHourUTC - 1;
1736 void CUtil::GetRecursiveListing(const CStdString& strPath, CFileItemList& items, const CStdString& strMask, bool bUseFileDirectories)
1738 CFileItemList myItems;
1739 CDirectory::GetDirectory(strPath,myItems,strMask,bUseFileDirectories);
1740 for (int i=0;i<myItems.Size();++i)
1742 if (myItems[i]->m_bIsFolder)
1743 CUtil::GetRecursiveListing(myItems[i]->m_strPath,items,strMask,bUseFileDirectories);
1745 items.Add(myItems[i]);
1749 void CUtil::GetRecursiveDirsListing(const CStdString& strPath, CFileItemList& item)
1751 CFileItemList myItems;
1752 CDirectory::GetDirectory(strPath,myItems,"",false);
1753 for (int i=0;i<myItems.Size();++i)
1755 if (myItems[i]->m_bIsFolder && !myItems[i]->m_strPath.Equals(".."))
1757 item.Add(myItems[i]);
1758 CUtil::GetRecursiveDirsListing(myItems[i]->m_strPath,item);
1763 void CUtil::ForceForwardSlashes(CStdString& strPath)
1765 int iPos = strPath.ReverseFind('\\');
1768 strPath.at(iPos) = '/';
1769 iPos = strPath.ReverseFind('\\');
1773 double CUtil::AlbumRelevance(const CStdString& strAlbumTemp1, const CStdString& strAlbum1, const CStdString& strArtistTemp1, const CStdString& strArtist1)
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())
1786 CStdString strArtistTemp = strArtistTemp1;
1787 strArtistTemp.MakeLower();
1788 CStdString strArtist = strArtist1;
1789 strArtist.MakeLower();
1790 fArtistPercentage = fstrcmp(strArtistTemp, strArtist, 0.0f);
1792 double fRelevance = fAlbumPercentage * 0.5f + fArtistPercentage * 0.5f;
1796 CStdString CUtil::SubstitutePath(const CStdString& strFileName)
1798 for (CAdvancedSettings::StringMapping::iterator i = g_advancedSettings.m_pathSubstitutions.begin();
1799 i != g_advancedSettings.m_pathSubstitutions.end(); i++)
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()));
1807 bool CUtil::MakeShortenPath(CStdString StrInput, CStdString& StrOutput, int iTextMaxLength)
1809 int iStrInputSize = StrInput.size();
1810 if((iStrInputSize <= 0) || (iTextMaxLength >= iStrInputSize))
1814 size_t nGreaterDelim, nPos;
1816 nPos = StrInput.find_last_of( '\\' );
1817 if ( nPos != CStdString::npos )
1821 nPos = StrInput.find_last_of( '/' );
1822 if ( nPos != CStdString::npos )
1825 if ( cDelim == '\0' )
1828 if (nPos == StrInput.size() - 1)
1830 StrInput.erase(StrInput.size() - 1);
1831 nPos = StrInput.find_last_of( cDelim );
1833 while( iTextMaxLength < iStrInputSize )
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();
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, ".."))
1849 // finally, truncate our string to force inside our max text length,
1850 // replacing the last 2 characters with ".."
1853 // "smb://../Playboy Swimsuit Cal.."
1854 if (iTextMaxLength > 2 && StrInput.size() > (unsigned int)iTextMaxLength)
1856 StrInput = StrInput.Left(iTextMaxLength - 2);
1859 StrOutput = StrInput;
1863 bool CUtil::SupportsFileOperations(const CStdString& strPath)
1865 // currently only hd and smb support delete and rename
1866 if (URIUtils::IsHD(strPath))
1868 if (URIUtils::IsSmb(strPath))
1870 if (URIUtils::IsMythTV(strPath))
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
1877 return CMythDirectory::SupportsFileOperations(strPath);
1879 if (URIUtils::IsStack(strPath))
1881 CStackDirectory dir;
1882 return SupportsFileOperations(dir.GetFirstStackedFile(strPath));
1884 if (URIUtils::IsMultiPath(strPath))
1885 return CMultiPathDirectory::SupportsFileOperations(strPath);
1890 CStdString CUtil::GetCachedAlbumThumb(const CStdString& album, const CStdString& artist)
1892 if (album.IsEmpty())
1893 return GetCachedMusicThumb("unknown"+artist);
1894 if (artist.IsEmpty())
1895 return GetCachedMusicThumb(album+"unknown");
1896 return GetCachedMusicThumb(album+artist);
1899 CStdString CUtil::GetCachedMusicThumb(const CStdString& path)
1902 CStdString noSlashPath(path);
1903 URIUtils::RemoveSlashAtEnd(noSlashPath);
1904 crc.ComputeFromLowerCase(noSlashPath);
1906 hex.Format("%08x", (unsigned __int32) crc);
1908 thumb.Format("%c/%s.tbn", hex[0], hex.c_str());
1909 return URIUtils::AddFileToFolder(g_settings.GetMusicThumbFolder(), thumb);
1912 CStdString CUtil::GetDefaultFolderThumb(const CStdString &folderThumb)
1914 if (g_TextureManager.HasTexture(folderThumb))
1919 void CUtil::GetSkinThemes(vector<CStdString>& vecTheme)
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)
1928 CFileItemPtr pItem = items[i];
1929 if (!pItem->m_bIsFolder)
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")))
1936 CStdString strLabel = pItem->GetLabel();
1937 vecTheme.push_back(strLabel.Mid(0, strLabel.size() - 4));
1941 sort(vecTheme.begin(), vecTheme.end(), sortstringbyname());
1944 void CUtil::InitRandomSeed()
1948 now = CurrentHostCounter();
1949 unsigned int seed = (unsigned int)now;
1950 // CLog::Log(LOGDEBUG, "%s - Initializing random seed with %u", __FUNCTION__, seed);
1955 bool CUtil::RunCommandLine(const CStdString& cmdLine, bool waitExit)
1957 CStdStringArray args;
1959 StringUtils::SplitString(cmdLine, ",", args);
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++)
1967 CStdString &s = args[i];
1968 CStdString stripd = s.Trim();
1969 if (stripd[0] == '"' || stripd[0] == '\'')
1972 s = s.Right(s.size() - 1);
1974 if (stripd[stripd.size() - 1] == '"' || stripd[stripd.size() - 1] == '\'')
1977 s = s.Left(s.size() - 1);
1981 return Command(args, waitExit);
1985 // FIXME, this should be merged with the function below.
1987 bool CUtil::Command(const CStdStringArray& arrArgs, bool waitExit)
1990 printf("Executing: ");
1991 for (size_t i=0; i<arrArgs.size(); i++)
1992 printf("%s ", arrArgs[i].c_str());
1996 pid_t child = fork();
2003 if (arrArgs.size() > 0)
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);
2014 if (waitExit) waitpid(child, &n, 0);
2017 return (waitExit) ? (WEXITSTATUS(n) == 0) : true;
2020 bool CUtil::SudoCommand(const CStdString &strCommand)
2022 CLog::Log(LOGDEBUG, "Executing sudo command: <%s>", strCommand.c_str());
2023 pid_t child = fork();
2027 close(0); // close stdin to avoid sudo request password
2030 CStdStringArray arrArgs;
2031 StringUtils::SplitString(strCommand, " ", arrArgs);
2032 if (arrArgs.size() > 0)
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++)
2040 args[i+2] = (char *)arrArgs[i].c_str();
2042 execvp("/usr/bin/sudo", args);
2046 waitpid(child, &n, 0);
2048 return WEXITSTATUS(n) == 0;
2052 int CUtil::LookupRomanDigit(char roman_digit)
2054 switch (roman_digit)
2082 int CUtil::TranslateRomanNumeral(const char* roman_numeral)
2087 if (roman_numeral && roman_numeral[0])
2094 while (*roman_numeral)
2096 int digit = CUtil::LookupRomanDigit(*roman_numeral);
2099 // General sanity checks
2101 // numeral not in LUT
2108 // N = 10^n may not precede (N+1) > 10^(N+1)
2109 if (test == 1 && digit > last * 10)
2112 // N = 5*10^n may not precede (N+1) >= N
2113 if (test == 5 && digit >= last)
2116 // End general sanity checks
2120 // smaller numerals may not repeat before a larger one
2129 else if (last == digit)
2138 decimal += 2 * last - temp_sum;
2140 decimal += temp_sum;
2147 // Post general sanity checks
2149 // numerals may not repeat more than thrice
2158 decimal += temp_sum;
2160 decimal += 2 * last - temp_sum;
2165 CStdString CUtil::ResolveExecutablePath()
2167 CStdString strExecutablePath;
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;
2177 GetDarwinExecutablePath(given_path, &path_size);
2178 strExecutablePath = given_path;
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);
2185 /* Now read the symbolic link */
2187 int ret = readlink(linkname, buf, PATH_MAX);
2190 strExecutablePath = buf;
2192 return strExecutablePath;
2195 CStdString CUtil::GetFrameworksPath(bool forPython)
2197 CStdString strFrameworksPath;
2198 #if defined(__APPLE__)
2199 char given_path[2*MAXPATHLEN];
2200 uint32_t path_size =2*MAXPATHLEN;
2202 GetDarwinFrameworkPath(forPython, given_path, &path_size);
2203 strFrameworksPath = given_path;
2205 return strFrameworksPath;
2208 void CUtil::ScanForExternalSubtitles(const CStdString& strMovie, std::vector<CStdString>& vecSubtitles )
2210 unsigned int startTimer = CTimeUtils::GetTimeMS();
2212 // new array for commons sub dirs
2213 const char * common_sub_dirs[] = {"subs",
2227 vector<CStdString> vecExtensionsCached;
2228 //strExtensionCached = "";
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 ;
2236 vector<CStdString> strLookInPaths;
2238 CStdString strMovieFileName;
2241 URIUtils::Split(strMovie, strPath, strMovieFileName);
2242 CStdString strMovieFileNameNoExt(URIUtils::ReplaceExtension(strMovieFileName, ""));
2243 strLookInPaths.push_back(strPath);
2245 if (!g_settings.iAdditionalSubtitleDirectoryChecked && !g_guiSettings.GetString("subtitles.custompath").IsEmpty()) // to avoid checking non-existent directories (network) every time..
2247 if (!g_application.getNetwork().IsAvailable() && !URIUtils::IsHD(g_guiSettings.GetString("subtitles.custompath")))
2249 CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonaccessible");
2250 g_settings.iAdditionalSubtitleDirectoryChecked = -1; // disabled
2252 else if (!CDirectory::Exists(g_guiSettings.GetString("subtitles.custompath")))
2254 CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonexistant");
2255 g_settings.iAdditionalSubtitleDirectoryChecked = -1; // disabled
2258 g_settings.iAdditionalSubtitleDirectoryChecked = 1;
2261 if (strMovie.Left(6) == "rar://") // <--- if this is found in main path then ignore it!
2264 CStdString strArchive = url.GetHostName();
2265 URIUtils::Split(strArchive, strPath, strMovieFileName);
2266 strLookInPaths.push_back(strPath);
2269 // checking if any of the common subdirs exist ..
2270 CStdStringArray directories;
2271 int nTokens = StringUtils::SplitString( strPath, "/", directories );
2273 StringUtils::SplitString( strPath, "\\", directories );
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
2278 CStdString strPath2;
2279 URIUtils::GetParentPath(strPath,strPath2);
2280 strLookInPaths.push_back(strPath2);
2282 int iSize = strLookInPaths.size();
2283 for (int i=0;i<iSize;++i)
2285 for (int j=0; common_sub_dirs[j]; j++)
2287 CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],common_sub_dirs[j]);
2288 if (CDirectory::Exists(strPath2))
2289 strLookInPaths.push_back(strPath2);
2292 // .. done checking for common subdirs
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
2299 cdDir.Format("cd%i",i+1);
2300 for (int i=0;i<iSize;++i)
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++)
2307 // if movie file is inside cd-dir, this directory can exist in vector already
2308 if (strLookInPaths[i].Equals( strPath2 ) )
2309 pathAlreadyAdded = true;
2311 if (CDirectory::Exists(strPath2) && !pathAlreadyAdded)
2312 strLookInPaths.push_back(strPath2);
2315 // .. done checking for cd-dirs
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)
2320 strPath = g_guiSettings.GetString("subtitles.custompath");
2321 URIUtils::AddSlashAtEnd(strPath);
2322 strLookInPaths.push_back(strPath);
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++)
2333 if (strLookInPaths[step].length() != 0)
2335 CFileItemList items;
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();
2340 for (int j = 0; j < items.Size(); j++)
2342 URIUtils::Split(items[j]->m_strPath, strPath, strItem);
2344 // is this a rar or zip-file
2345 if (URIUtils::IsRAR(strItem) || URIUtils::IsZIP(strItem))
2347 ScanArchiveForSubtitles( items[j]->m_strPath, strMovieFileNameNoExt, vecSubtitles );
2349 else // not a rar/zip file
2351 for (int i = 0; sub_exts[i]; i++)
2353 //Cache subtitle with same name as movie
2354 if (URIUtils::GetExtension(strItem).Equals(sub_exts[i]) && strItem.Left(fnl).Equals(strMovieFileNameNoExt))
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() );
2362 g_directoryCache.ClearDirectory(strLookInPaths[step]);
2366 iSize = vecSubtitles.size();
2367 for (int i = 0; i < iSize; i++)
2369 if (URIUtils::GetExtension(vecSubtitles[i]).Equals(".smi"))
2371 //Cache multi-language sami subtitle
2372 CDVDSubtitleStream* pStream = new CDVDSubtitleStream();
2373 if(pStream->Open(vecSubtitles[i]))
2375 CDVDSubtitleTagSami TagConv;
2376 TagConv.LoadHead(pStream);
2377 if (TagConv.m_Langclass.size() >= 2)
2379 for (unsigned int k = 0; k < TagConv.m_Langclass.size(); k++)
2381 strDest.Format("special://temp/subtitle.%s.%d.smi", TagConv.m_Langclass[k].Name, i);
2382 if (CFile::Cache(vecSubtitles[i], strDest))
2384 CLog::Log(LOGINFO, " cached subtitle %s->%s\n", vecSubtitles[i].c_str(), strDest.c_str());
2385 vecSubtitles.push_back(strDest);
2393 CLog::Log(LOGDEBUG,"%s: END (total time: %i ms)", __FUNCTION__, (int)(CTimeUtils::GetTimeMS() - startTimer));
2396 int CUtil::ScanArchiveForSubtitles( const CStdString& strArchivePath, const CStdString& strMovieFileNameNoExt, std::vector<CStdString>& vecSubtitles )
2398 int nSubtitlesAdded = 0;
2399 CFileItemList ItemList;
2401 // zip only gets the root dir
2402 if (URIUtils::GetExtension(strArchivePath).Equals(".zip"))
2404 CStdString strZipPath;
2405 URIUtils::CreateArchivePath(strZipPath,"zip",strArchivePath,"");
2406 if (!CDirectory::GetDirectory(strZipPath,ItemList,"",false))
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, "") )
2420 for (int it= 0 ; it <ItemList.Size();++it)
2422 CStdString strPathInRar = ItemList[it]->m_strPath;
2423 CStdString strExt = URIUtils::GetExtension(strPathInRar);
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))
2430 CStdString strRarInRar;
2431 if (URIUtils::GetExtension(strPathInRar).Equals(".rar"))
2432 URIUtils::CreateArchivePath(strRarInRar, "rar", strArchivePath, strPathInRar);
2434 URIUtils::CreateArchivePath(strRarInRar, "zip", strArchivePath, strPathInRar);
2435 ScanArchiveForSubtitles(strRarInRar,strMovieFileNameNoExt,vecSubtitles);
2437 // done checking if this is a rar-in-rar
2440 while (sub_exts[iPos])
2442 if (strExt.CompareNoCase(sub_exts[iPos]) == 0)
2444 CStdString strSourceUrl;
2445 if (URIUtils::GetExtension(strArchivePath).Equals(".rar"))
2446 URIUtils::CreateArchivePath(strSourceUrl, "rar", strArchivePath, strPathInRar);
2448 strSourceUrl = strPathInRar;
2450 CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, strSourceUrl.c_str() );
2451 vecSubtitles.push_back( strSourceUrl );
2459 return nSubtitlesAdded;
2462 /*! \brief in a vector of subtitles finds the corresponding .sub file for a given .idx file
2464 bool CUtil::FindVobSubPair( const std::vector<CStdString>& vecSubtitles, const CStdString& strIdxPath, CStdString& strSubPath )
2466 if (URIUtils::GetExtension(strIdxPath) == ".idx")
2468 CStdString strIdxFile;
2469 CStdString strIdxDirectory;
2470 URIUtils::Split(strIdxPath, strIdxDirectory, strIdxFile);
2471 for (unsigned int j=0; j < vecSubtitles.size(); j++)
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,"")))
2479 strSubPath = vecSubtitles[j];
2487 /*! \brief checks if in the vector of subtitles the given .sub file has a corresponding idx and hence is a vobsub file
2489 bool CUtil::IsVobSub( const std::vector<CStdString>& vecSubtitles, const CStdString& strSubPath )
2491 if (URIUtils::GetExtension(strSubPath) == ".sub")
2493 CStdString strSubFile;
2494 CStdString strSubDirectory;
2495 URIUtils::Split(strSubPath, strSubDirectory, strSubFile);
2496 for (unsigned int j=0; j < vecSubtitles.size(); j++)
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,"")))