Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

2610 lines (2311 sloc) 78.703 kB
/*
* Copyright (C) 2005-2012 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with XBMC; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
#include "threads/SystemClock.h"
#include "system.h"
#if defined(TARGET_DARWIN)
#include <sys/param.h>
#include <mach-o/dyld.h>
#endif
#if defined(__FreeBSD__)
#include <sys/param.h>
#include <sys/sysctl.h>
#endif
#ifdef _LINUX
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#endif
#include "Application.h"
#include "Util.h"
#include "addons/Addon.h"
#include "filesystem/PVRDirectory.h"
#include "filesystem/Directory.h"
#include "filesystem/StackDirectory.h"
#include "filesystem/MultiPathDirectory.h"
#include "filesystem/SpecialProtocol.h"
#include "filesystem/RSSDirectory.h"
#ifdef HAS_FILESYSTEM_RAR
#include "filesystem/RarManager.h"
#endif
#include "filesystem/MythDirectory.h"
#ifdef HAS_UPNP
#include "filesystem/UPnPDirectory.h"
#endif
#ifdef HAS_VIDEO_PLAYBACK
#include "cores/VideoRenderers/RenderManager.h"
#endif
#include "utils/RegExp.h"
#include "settings/GUISettings.h"
#include "guilib/TextureManager.h"
#include "utils/fstrcmp.h"
#include "storage/MediaManager.h"
#ifdef _WIN32
#include <shlobj.h>
#include "WIN32Util.h"
#endif
#if defined(TARGET_DARWIN)
#include "osx/DarwinUtils.h"
#endif
#include "GUIUserMessages.h"
#include "filesystem/File.h"
#include "settings/Settings.h"
#include "utils/StringUtils.h"
#include "settings/AdvancedSettings.h"
#ifdef HAS_IRSERVERSUITE
#include "input/windows/IRServerSuite.h"
#endif
#include "guilib/LocalizeStrings.h"
#include "utils/md5.h"
#include "utils/TimeUtils.h"
#include "utils/URIUtils.h"
#include "utils/log.h"
#include "pictures/Picture.h"
#include "utils/JobManager.h"
#include "cores/dvdplayer/DVDSubtitles/DVDSubtitleTagSami.h"
#include "cores/dvdplayer/DVDSubtitles/DVDSubtitleStream.h"
#include "windowing/WindowingFactory.h"
#include "URL.h"
#ifdef HAVE_LIBCAP
#include <sys/capability.h>
#endif
using namespace std;
using namespace XFILE;
#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]
static const int64_t SECS_BETWEEN_EPOCHS = 11644473600LL;
static const int64_t SECS_TO_100NS = 10000000;
using namespace XFILE;
using namespace PLAYLIST;
#ifdef HAS_DX
static D3DGAMMARAMP oldramp, flashramp;
#elif defined(HAS_SDL_2D)
static uint16_t oldrampRed[256];
static uint16_t oldrampGreen[256];
static uint16_t oldrampBlue[256];
static uint16_t flashrampRed[256];
static uint16_t flashrampGreen[256];
static uint16_t flashrampBlue[256];
#endif
CUtil::CUtil(void)
{
}
CUtil::~CUtil(void)
{}
CStdString CUtil::GetTitleFromPath(const CStdString& strFileNameAndPath, bool bIsFolder /* = false */)
{
// use above to get the filename
CStdString path(strFileNameAndPath);
URIUtils::RemoveSlashAtEnd(path);
CStdString strFilename = URIUtils::GetFileName(path);
CURL url(strFileNameAndPath);
CStdString strHostname = url.GetHostName();
#ifdef HAS_UPNP
// UPNP
if (url.GetProtocol() == "upnp")
strFilename = CUPnPDirectory::GetFriendlyName(strFileNameAndPath.c_str());
#endif
if (url.GetProtocol() == "rss")
{
CRSSDirectory dir;
CFileItemList items;
if(dir.GetDirectory(strFileNameAndPath, items) && !items.m_strTitle.IsEmpty())
return items.m_strTitle;
}
// LastFM
if (url.GetProtocol() == "lastfm")
{
if (strFilename.IsEmpty())
strFilename = g_localizeStrings.Get(15200);
else
strFilename = g_localizeStrings.Get(15200) + " - " + strFilename;
}
// Shoutcast
else if (url.GetProtocol() == "shout")
{
const int genre = strFileNameAndPath.find_first_of('=');
if(genre <0)
strFilename = g_localizeStrings.Get(260);
else
strFilename = g_localizeStrings.Get(260) + " - " + strFileNameAndPath.substr(genre+1).c_str();
}
// Windows SMB Network (SMB)
else if (url.GetProtocol() == "smb" && strFilename.IsEmpty())
{
if (url.GetHostName().IsEmpty())
{
strFilename = g_localizeStrings.Get(20171);
}
else
{
strFilename = url.GetHostName();
}
}
// iTunes music share (DAAP)
else if (url.GetProtocol() == "daap" && strFilename.IsEmpty())
strFilename = g_localizeStrings.Get(20174);
// HDHomerun Devices
else if (url.GetProtocol() == "hdhomerun" && strFilename.IsEmpty())
strFilename = "HDHomerun Devices";
// Slingbox Devices
else if (url.GetProtocol() == "sling")
strFilename = "Slingbox";
// ReplayTV Devices
else if (url.GetProtocol() == "rtv")
strFilename = "ReplayTV Devices";
// HTS Tvheadend client
else if (url.GetProtocol() == "htsp")
strFilename = g_localizeStrings.Get(20256);
// VDR Streamdev client
else if (url.GetProtocol() == "vtp")
strFilename = g_localizeStrings.Get(20257);
// MythTV client
else if (url.GetProtocol() == "myth")
strFilename = g_localizeStrings.Get(20258);
// SAP Streams
else if (url.GetProtocol() == "sap" && strFilename.IsEmpty())
strFilename = "SAP Streams";
// Root file views
else if (url.GetProtocol() == "sources")
strFilename = g_localizeStrings.Get(744);
// Music Playlists
else if (path.Left(24).Equals("special://musicplaylists"))
strFilename = g_localizeStrings.Get(136);
// Video Playlists
else if (path.Left(24).Equals("special://videoplaylists"))
strFilename = g_localizeStrings.Get(136);
else if (URIUtils::ProtocolHasParentInHostname(url.GetProtocol()) && strFilename.IsEmpty())
strFilename = URIUtils::GetFileName(url.GetHostName());
// now remove the extension if needed
if (!g_guiSettings.GetBool("filelists.showextensions") && !bIsFolder)
{
URIUtils::RemoveExtension(strFilename);
return strFilename;
}
// URLDecode since the original path may be an URL
CURL::Decode(strFilename);
return strFilename;
}
bool CUtil::GetVolumeFromFileName(const CStdString& strFileName, CStdString& strFileTitle, CStdString& strVolumeNumber)
{
const CStdStringArray &regexps = g_advancedSettings.m_videoStackRegExps;
CStdString strFileNameTemp = strFileName;
CRegExp reg(true);
for (unsigned int i = 0; i < regexps.size(); i++)
{
CStdString strRegExp = regexps[i];
if (!reg.RegComp(strRegExp.c_str()))
{ // invalid regexp - complain in logs
CLog::Log(LOGERROR, "Invalid RegExp:[%s]", regexps[i].c_str());
continue;
}
int iFoundToken = reg.RegFind(strFileName.c_str());
if (iFoundToken >= 0)
{
int iRegLength = reg.GetFindLen();
int iCount = reg.GetSubCount();
/*
reg.DumpOvector(LOGDEBUG);
CLog::Log(LOGDEBUG, "Subcount=%i", iCount);
for (int j = 0; j <= iCount; j++)
{
CStdString str = reg.GetMatch(j);
CLog::Log(LOGDEBUG, "Sub(%i):[%s]", j, str.c_str());
}
*/
// simple regexp, only the volume is captured
if (iCount == 1)
{
strVolumeNumber = reg.GetMatch(1);
if (strVolumeNumber.IsEmpty()) return false;
// Remove the extension (if any). We do this on the base filename, as the regexp
// match may include some of the extension (eg the "." in particular).
// The extension will then be added back on at the end - there is no reason
// to clean it off here. It will be cleaned off during the display routine, if
// the settings to hide extensions are turned on.
CStdString strFileNoExt = strFileNameTemp;
URIUtils::RemoveExtension(strFileNoExt);
CStdString strFileExt = strFileNameTemp.Right(strFileNameTemp.length() - strFileNoExt.length());
CStdString strFileRight = strFileNoExt.Mid(iFoundToken + iRegLength);
strFileTitle = strFileName.Left(iFoundToken) + strFileRight + strFileExt;
return true;
}
// advanced regexp with prefix (1), volume (2), and suffix (3)
else if (iCount == 3)
{
// second subpatten contains the stacking volume
strVolumeNumber = reg.GetMatch(2);
if (strVolumeNumber.IsEmpty()) return false;
// everything before the regexp match
strFileTitle = strFileName.Left(iFoundToken);
// first subpattern contains prefix
strFileTitle += reg.GetMatch(1);
// third subpattern contains suffix
strFileTitle += reg.GetMatch(3);
// everything after the regexp match
strFileTitle += strFileNameTemp.Mid(iFoundToken + iRegLength);
return true;
}
// unknown regexp format
else
{
CLog::Log(LOGERROR, "Incorrect movie stacking regexp format:[%s]", regexps[i].c_str());
}
}
}
return false;
}
void CUtil::CleanString(const CStdString& strFileName, CStdString& strTitle, CStdString& strTitleAndYear, CStdString& strYear, bool bRemoveExtension /* = false */, bool bCleanChars /* = true */)
{
strTitleAndYear = strFileName;
if (strFileName.Equals(".."))
return;
const CStdStringArray &regexps = g_advancedSettings.m_videoCleanStringRegExps;
CRegExp reTags(true);
CRegExp reYear;
CStdString strExtension;
URIUtils::GetExtension(strFileName, strExtension);
if (!reYear.RegComp(g_advancedSettings.m_videoCleanDateTimeRegExp))
{
CLog::Log(LOGERROR, "%s: Invalid datetime clean RegExp:'%s'", __FUNCTION__, g_advancedSettings.m_videoCleanDateTimeRegExp.c_str());
}
else
{
if (reYear.RegFind(strTitleAndYear.c_str()) >= 0)
{
char* ty = reYear.GetReplaceString("\\1");
char* y = reYear.GetReplaceString("\\2");
strTitleAndYear = ty;
strYear = y;
free(ty);
free(y);
}
}
URIUtils::RemoveExtension(strTitleAndYear);
for (unsigned int i = 0; i < regexps.size(); i++)
{
if (!reTags.RegComp(regexps[i].c_str()))
{ // invalid regexp - complain in logs
CLog::Log(LOGERROR, "%s: Invalid string clean RegExp:'%s'", __FUNCTION__, regexps[i].c_str());
continue;
}
int j=0;
if ((j=reTags.RegFind(strFileName.c_str())) > 0)
strTitleAndYear = strTitleAndYear.Mid(0, j);
}
// final cleanup - special characters used instead of spaces:
// all '_' tokens should be replaced by spaces
// if the file contains no spaces, all '.' tokens should be replaced by
// spaces - one possibility of a mistake here could be something like:
// "Dr..StrangeLove" - hopefully no one would have anything like this.
if (bCleanChars)
{
bool initialDots = true;
bool alreadyContainsSpace = (strTitleAndYear.Find(' ') >= 0);
for (int i = 0; i < (int)strTitleAndYear.size(); i++)
{
char c = strTitleAndYear.GetAt(i);
if (c != '.')
initialDots = false;
if ((c == '_') || ((!alreadyContainsSpace) && !initialDots && (c == '.')))
{
strTitleAndYear.SetAt(i, ' ');
}
}
}
strTitle = strTitleAndYear.Trim();
// append year
if (!strYear.IsEmpty())
strTitleAndYear = strTitle + " (" + strYear + ")";
// restore extension if needed
if (!bRemoveExtension)
strTitleAndYear += strExtension;
}
void CUtil::GetQualifiedFilename(const CStdString &strBasePath, CStdString &strFilename)
{
// Check if the filename is a fully qualified URL such as protocol://path/to/file
CURL plItemUrl(strFilename);
if (!plItemUrl.GetProtocol().IsEmpty())
return;
// If the filename starts "x:", "\\" or "/" it's already fully qualified so return
if (strFilename.size() > 1)
#ifdef _LINUX
if ( (strFilename[1] == ':') || (strFilename[0] == '/') )
#else
if ( strFilename[1] == ':' || (strFilename[0] == '\\' && strFilename[1] == '\\'))
#endif
return;
// add to base path and then clean
strFilename = URIUtils::AddFileToFolder(strBasePath, strFilename);
// get rid of any /./ or \.\ that happen to be there
strFilename.Replace("\\.\\", "\\");
strFilename.Replace("/./", "/");
// now find any "\\..\\" and remove them via GetParentPath
int pos;
while ((pos = strFilename.Find("/../")) > 0)
{
CStdString basePath = strFilename.Left(pos+1);
strFilename = strFilename.Mid(pos+4);
basePath = URIUtils::GetParentPath(basePath);
strFilename = URIUtils::AddFileToFolder(basePath, strFilename);
}
while ((pos = strFilename.Find("\\..\\")) > 0)
{
CStdString basePath = strFilename.Left(pos+1);
strFilename = strFilename.Mid(pos+4);
basePath = URIUtils::GetParentPath(basePath);
strFilename = URIUtils::AddFileToFolder(basePath, strFilename);
}
}
#ifdef UNIT_TESTING
bool CUtil::TestGetQualifiedFilename()
{
CStdString file = "../foo"; GetQualifiedFilename("smb://", file);
if (file != "foo") return false;
file = "C:\\foo\\bar"; GetQualifiedFilename("smb://", file);
if (file != "C:\\foo\\bar") return false;
file = "../foo/./bar"; GetQualifiedFilename("smb://my/path", file);
if (file != "smb://my/foo/bar") return false;
file = "smb://foo/bar/"; GetQualifiedFilename("upnp://", file);
if (file != "smb://foo/bar/") return false;
return true;
}
bool CUtil::TestMakeLegalPath()
{
CStdString path;
#ifdef _WIN32
path = "C:\\foo\\bar"; path = MakeLegalPath(path);
if (path != "C:\\foo\\bar") return false;
path = "C:\\foo:\\bar\\"; path = MakeLegalPath(path);
if (path != "C:\\foo_\\bar\\") return false;
#elif
path = "/foo/bar/"; path = MakeLegalPath(path);
if (path != "/foo/bar/") return false;
path = "/foo?/bar"; path = MakeLegalPath(path);
if (path != "/foo_/bar") return false;
#endif
path = "smb://foo/bar"; path = MakeLegalPath(path);
if (path != "smb://foo/bar") return false;
path = "smb://foo/bar?/"; path = MakeLegalPath(path);
if (path != "smb://foo/bar_/") return false;
return true;
}
#endif
void CUtil::RunShortcut(const char* szShortcutPath)
{
}
void CUtil::GetHomePath(CStdString& strPath, const CStdString& strTarget)
{
CStdString strHomePath;
strHomePath = ResolveExecutablePath();
#ifdef _WIN32
CStdStringW strPathW, strTargetW;
g_charsetConverter.utf8ToW(strTarget, strTargetW);
strPathW = _wgetenv(strTargetW);
g_charsetConverter.wToUTF8(strPathW,strPath);
#else
strPath = getenv(strTarget);
#endif
if (strPath != NULL && !strPath.IsEmpty())
{
#ifdef _WIN32
char tmp[1024];
//expand potential relative path to full path
if(GetFullPathName(strPath, 1024, tmp, 0) != 0)
{
strPath = tmp;
}
#endif
}
else
{
#if defined(TARGET_DARWIN)
int result = -1;
char given_path[2*MAXPATHLEN];
uint32_t path_size =2*MAXPATHLEN;
result = GetDarwinExecutablePath(given_path, &path_size);
if (result == 0)
{
// Move backwards to last /.
for (int n=strlen(given_path)-1; given_path[n] != '/'; n--)
given_path[n] = '\0';
#if defined(TARGET_DARWIN_IOS)
strcat(given_path, "/XBMCData/XBMCHome/");
#else
// Assume local path inside application bundle.
strcat(given_path, "../Resources/XBMC/");
#endif
// Convert to real path.
char real_path[2*MAXPATHLEN];
if (realpath(given_path, real_path) != NULL)
{
strPath = real_path;
return;
}
}
#endif
size_t last_sep = strHomePath.find_last_of(PATH_SEPARATOR_CHAR);
if (last_sep != string::npos)
strPath = strHomePath.Left(last_sep);
else
strPath = strHomePath;
}
#if defined(_LINUX) && !defined(TARGET_DARWIN)
/* Change strPath accordingly when target is XBMC_HOME and when INSTALL_PATH
* and BIN_INSTALL_PATH differ
*/
CStdString installPath = INSTALL_PATH;
CStdString binInstallPath = BIN_INSTALL_PATH;
if (!strTarget.compare("XBMC_HOME") && installPath.compare(binInstallPath))
{
int pos = strPath.length() - binInstallPath.length();
CStdString tmp = strPath;
tmp.erase(0, pos);
if (!tmp.compare(binInstallPath))
{
strPath.erase(pos, strPath.length());
strPath.append(installPath);
}
}
#endif
}
bool CUtil::IsPVR(const CStdString& strFile)
{
return strFile.Left(4).Equals("pvr:");
}
bool CUtil::IsHTSP(const CStdString& strFile)
{
return strFile.Left(5).Equals("htsp:");
}
bool CUtil::IsLiveTV(const CStdString& strFile)
{
if (strFile.Left(14).Equals("pvr://channels"))
return true;
if(URIUtils::IsTuxBox(strFile)
|| URIUtils::IsVTP(strFile)
|| URIUtils::IsHDHomeRun(strFile)
|| URIUtils::IsHTSP(strFile)
|| strFile.Left(4).Equals("sap:"))
return true;
if (URIUtils::IsMythTV(strFile) && CMythDirectory::IsLiveTV(strFile))
return true;
return false;
}
bool CUtil::IsTVRecording(const CStdString& strFile)
{
return strFile.Left(15).Equals("pvr://recording");
}
bool CUtil::IsPicture(const CStdString& strFile)
{
CStdString extension = URIUtils::GetExtension(strFile);
if (extension.IsEmpty())
return false;
extension.ToLower();
if (g_settings.m_pictureExtensions.Find(extension) != -1)
return true;
if (extension == ".tbn" || extension == ".dds")
return true;
return false;
}
bool CUtil::ExcludeFileOrFolder(const CStdString& strFileOrFolder, const CStdStringArray& regexps)
{
if (strFileOrFolder.IsEmpty())
return false;
CRegExp regExExcludes(true); // case insensitive regex
for (unsigned int i = 0; i < regexps.size(); i++)
{
if (!regExExcludes.RegComp(regexps[i].c_str()))
{ // invalid regexp - complain in logs
CLog::Log(LOGERROR, "%s: Invalid exclude RegExp:'%s'", __FUNCTION__, regexps[i].c_str());
continue;
}
if (regExExcludes.RegFind(strFileOrFolder) > -1)
{
CLog::Log(LOGDEBUG, "%s: File '%s' excluded. (Matches exclude rule RegExp:'%s')", __FUNCTION__, strFileOrFolder.c_str(), regexps[i].c_str());
return true;
}
}
return false;
}
void CUtil::GetFileAndProtocol(const CStdString& strURL, CStdString& strDir)
{
strDir = strURL;
if (!URIUtils::IsRemote(strURL)) return ;
if (URIUtils::IsDVD(strURL)) return ;
CURL url(strURL);
strDir.Format("%s://%s", url.GetProtocol().c_str(), url.GetFileName().c_str());
}
int CUtil::GetDVDIfoTitle(const CStdString& strFile)
{
CStdString strFilename = URIUtils::GetFileName(strFile);
if (strFilename.Equals("video_ts.ifo")) return 0;
//VTS_[TITLE]_0.IFO
return atoi(strFilename.Mid(4, 2).c_str());
}
CStdString CUtil::GetFileMD5(const CStdString& strPath)
{
CFile file;
CStdString result;
if (file.Open(strPath))
{
XBMC::XBMC_MD5 md5;
char temp[1024];
int pos=0;
int read=1;
while (read > 0)
{
read = file.Read(temp,1024);
pos += read;
md5.append(temp,read);
}
md5.getDigest(result);
file.Close();
}
return result;
}
bool CUtil::GetDirectoryName(const CStdString& strFileName, CStdString& strDescription)
{
CStdString strFName = URIUtils::GetFileName(strFileName);
strDescription = strFileName.Left(strFileName.size() - strFName.size());
URIUtils::RemoveSlashAtEnd(strDescription);
int iPos = strDescription.ReverseFind("\\");
if (iPos < 0)
iPos = strDescription.ReverseFind("/");
if (iPos >= 0)
{
CStdString strTmp = strDescription.Right(strDescription.size()-iPos-1);
strDescription = strTmp;//strDescription.Right(strDescription.size() - iPos - 1);
}
else if (strDescription.size() <= 0)
strDescription = strFName;
return true;
}
void CUtil::GetDVDDriveIcon( const CStdString& strPath, CStdString& strIcon )
{
if ( !g_mediaManager.IsDiscInDrive() )
{
strIcon = "DefaultDVDEmpty.png";
return ;
}
if ( URIUtils::IsDVD(strPath) )
{
#ifdef HAS_DVD_DRIVE
CCdInfo* pInfo = g_mediaManager.GetCdInfo();
// xbox DVD
if ( pInfo != NULL && pInfo->IsUDFX( 1 ) )
{
strIcon = "DefaultXboxDVD.png";
return ;
}
#endif
strIcon = "DefaultDVDRom.png";
return ;
}
if ( URIUtils::IsISO9660(strPath) )
{
#ifdef HAS_DVD_DRIVE
CCdInfo* pInfo = g_mediaManager.GetCdInfo();
if ( pInfo != NULL && pInfo->IsVideoCd( 1 ) )
{
strIcon = "DefaultVCD.png";
return ;
}
#endif
strIcon = "DefaultDVDRom.png";
return ;
}
if ( URIUtils::IsCDDA(strPath) )
{
strIcon = "DefaultCDDA.png";
return ;
}
}
void CUtil::RemoveTempFiles()
{
CStdString searchPath = g_settings.GetDatabaseFolder();
CFileItemList items;
if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".tmp", DIR_FLAG_NO_FILE_DIRS))
return;
for (int i = 0; i < items.Size(); ++i)
{
if (items[i]->m_bIsFolder)
continue;
XFILE::CFile::Delete(items[i]->GetPath());
}
}
void CUtil::ClearSubtitles()
{
//delete cached subs
CFileItemList items;
CDirectory::GetDirectory("special://temp/",items);
for( int i=0;i<items.Size();++i)
{
if (!items[i]->m_bIsFolder)
{
if ( items[i]->GetPath().Find("subtitle") >= 0 || items[i]->GetPath().Find("vobsub_queue") >= 0 )
{
CLog::Log(LOGDEBUG, "%s - Deleting temporary subtitle %s", __FUNCTION__, items[i]->GetPath().c_str());
CFile::Delete(items[i]->GetPath());
}
}
}
}
void CUtil::ClearTempFonts()
{
CStdString searchPath = "special://temp/fonts/";
if (!CFile::Exists(searchPath))
return;
CFileItemList items;
CDirectory::GetDirectory(searchPath, items, "", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_BYPASS_CACHE);
for (int i=0; i<items.Size(); ++i)
{
if (items[i]->m_bIsFolder)
continue;
CFile::Delete(items[i]->GetPath());
}
}
static const char * sub_exts[] = { ".utf", ".utf8", ".utf-8", ".sub", ".srt", ".smi", ".rt", ".txt", ".ssa", ".aqt", ".jss", ".ass", ".idx", NULL};
int64_t CUtil::ToInt64(uint32_t high, uint32_t low)
{
int64_t n;
n = high;
n <<= 32;
n += low;
return n;
}
CStdString CUtil::GetNextFilename(const CStdString &fn_template, int max)
{
if (!fn_template.Find("%03d"))
return "";
CStdString searchPath;
URIUtils::GetDirectory(fn_template, searchPath);
CStdString mask = URIUtils::GetExtension(fn_template);
CStdString name;
name.Format(fn_template.c_str(), 0);
CFileItemList items;
if (!CDirectory::GetDirectory(searchPath, items, mask, DIR_FLAG_NO_FILE_DIRS))
return name;
items.SetFastLookup(true);
for (int i = 0; i <= max; i++)
{
CStdString name;
name.Format(fn_template.c_str(), i);
if (!items.Get(name))
return name;
}
return "";
}
CStdString CUtil::GetNextPathname(const CStdString &path_template, int max)
{
if (!path_template.Find("%04d"))
return "";
for (int i = 0; i <= max; i++)
{
CStdString name;
name.Format(path_template.c_str(), i);
if (!CFile::Exists(name))
return name;
}
return "";
}
void CUtil::Tokenize(const CStdString& path, vector<CStdString>& tokens, const string& delimiters)
{
// Tokenize ripped from http://www.linuxselfhelp.com/HOWTO/C++Programming-HOWTO-7.html
// Skip delimiters at beginning.
string::size_type lastPos = path.find_first_not_of(delimiters, 0);
// Find first "non-delimiter".
string::size_type pos = path.find_first_of(delimiters, lastPos);
while (string::npos != pos || string::npos != lastPos)
{
// Found a token, add it to the vector.
tokens.push_back(path.substr(lastPos, pos - lastPos));
// Skip delimiters. Note the "not_of"
lastPos = path.find_first_not_of(delimiters, pos);
// Find next "non-delimiter"
pos = path.find_first_of(delimiters, lastPos);
}
}
void CUtil::TakeScreenshot(const CStdString &filename, bool sync)
{
int width;
int height;
int stride;
unsigned char* outpixels = NULL;
#ifdef HAS_DX
LPDIRECT3DSURFACE9 lpSurface = NULL, lpBackbuffer = NULL;
g_graphicsContext.Lock();
if (g_application.IsPlayingVideo())
{
#ifdef HAS_VIDEO_PLAYBACK
g_renderManager.SetupScreenshot();
#endif
}
g_application.RenderNoPresent();
if (FAILED(g_Windowing.Get3DDevice()->CreateOffscreenPlainSurface(g_Windowing.GetWidth(), g_Windowing.GetHeight(), D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, &lpSurface, NULL)))
return;
if (FAILED(g_Windowing.Get3DDevice()->GetRenderTarget(0, &lpBackbuffer)))
return;
// now take screenshot
if (SUCCEEDED(g_Windowing.Get3DDevice()->GetRenderTargetData(lpBackbuffer, lpSurface)))
{
D3DLOCKED_RECT lr;
D3DSURFACE_DESC desc;
lpSurface->GetDesc(&desc);
if (SUCCEEDED(lpSurface->LockRect(&lr, NULL, D3DLOCK_READONLY)))
{
width = desc.Width;
height = desc.Height;
stride = lr.Pitch;
outpixels = new unsigned char[height * stride];
memcpy(outpixels, lr.pBits, height * stride);
lpSurface->UnlockRect();
}
else
{
CLog::Log(LOGERROR, "%s LockRect failed", __FUNCTION__);
}
}
else
{
CLog::Log(LOGERROR, "%s GetBackBuffer failed", __FUNCTION__);
}
lpSurface->Release();
lpBackbuffer->Release();
g_graphicsContext.Unlock();
#elif defined(HAS_GL) || defined(HAS_GLES)
g_graphicsContext.BeginPaint();
if (g_application.IsPlayingVideo())
{
#ifdef HAS_VIDEO_PLAYBACK
g_renderManager.SetupScreenshot();
#endif
}
g_application.RenderNoPresent();
#ifndef HAS_GLES
glReadBuffer(GL_BACK);
#endif
//get current viewport
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
width = viewport[2] - viewport[0];
height = viewport[3] - viewport[1];
stride = width * 4;
unsigned char* pixels = new unsigned char[stride * height];
//read pixels from the backbuffer
#if HAS_GLES == 2
glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)pixels);
#else
glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid*)pixels);
#endif
g_graphicsContext.EndPaint();
//make a new buffer and copy the read image to it with the Y axis inverted
outpixels = new unsigned char[stride * height];
for (int y = 0; y < height; y++)
{
#ifdef HAS_GLES
// we need to save in BGRA order so XOR Swap RGBA -> BGRA
unsigned char* swap_pixels = pixels + (height - y - 1) * stride;
for (int x = 0; x < width; x++, swap_pixels+=4)
{
std::swap(swap_pixels[0], swap_pixels[2]);
}
#endif
memcpy(outpixels + y * stride, pixels + (height - y - 1) * stride, stride);
}
delete [] pixels;
#else
//nothing to take a screenshot from
return;
#endif
if (!outpixels)
{
CLog::Log(LOGERROR, "Screenshot %s failed", filename.c_str());
return;
}
CLog::Log(LOGDEBUG, "Saving screenshot %s", filename.c_str());
//set alpha byte to 0xFF
for (int y = 0; y < height; y++)
{
unsigned char* alphaptr = outpixels - 1 + y * stride;
for (int x = 0; x < width; x++)
*(alphaptr += 4) = 0xFF;
}
//if sync is true, the png file needs to be completely written when this function returns
if (sync)
{
if (!CPicture::CreateThumbnailFromSurface(outpixels, width, height, stride, filename))
CLog::Log(LOGERROR, "Unable to write screenshot %s", filename.c_str());
delete [] outpixels;
}
else
{
//make sure the file exists to avoid concurrency issues
FILE* fp = fopen(filename.c_str(), "w");
if (fp)
fclose(fp);
else
CLog::Log(LOGERROR, "Unable to create file %s", filename.c_str());
//write .png file asynchronous with CThumbnailWriter, prevents stalling of the render thread
//outpixels is deleted from CThumbnailWriter
CThumbnailWriter* thumbnailwriter = new CThumbnailWriter(outpixels, width, height, stride, filename);
CJobManager::GetInstance().AddJob(thumbnailwriter, NULL);
}
}
void CUtil::TakeScreenshot()
{
static bool savingScreenshots = false;
static vector<CStdString> screenShots;
bool promptUser = false;
// check to see if we have a screenshot folder yet
CStdString strDir = g_guiSettings.GetString("debug.screenshotpath", false);
if (strDir.IsEmpty())
{
strDir = "special://temp/";
if (!savingScreenshots)
{
promptUser = true;
savingScreenshots = true;
screenShots.clear();
}
}
URIUtils::RemoveSlashAtEnd(strDir);
if (!strDir.IsEmpty())
{
CStdString file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(strDir, "screenshot%03d.png"), 999);
if (!file.IsEmpty())
{
TakeScreenshot(file, false);
if (savingScreenshots)
screenShots.push_back(file);
if (promptUser)
{ // grab the real directory
CStdString newDir = g_guiSettings.GetString("debug.screenshotpath");
if (!newDir.IsEmpty())
{
for (unsigned int i = 0; i < screenShots.size(); i++)
{
CStdString file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(newDir, "screenshot%03d.png"), 999);
CFile::Cache(screenShots[i], file);
}
screenShots.clear();
}
savingScreenshots = false;
}
}
else
{
CLog::Log(LOGWARNING, "Too many screen shots or invalid folder");
}
}
}
void CUtil::StatToStatI64(struct _stati64 *result, struct stat *stat)
{
result->st_dev = stat->st_dev;
result->st_ino = stat->st_ino;
result->st_mode = stat->st_mode;
result->st_nlink = stat->st_nlink;
result->st_uid = stat->st_uid;
result->st_gid = stat->st_gid;
result->st_rdev = stat->st_rdev;
result->st_size = (int64_t)stat->st_size;
#ifndef _LINUX
result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
#else
result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
#endif
}
void CUtil::Stat64ToStatI64(struct _stati64 *result, struct __stat64 *stat)
{
result->st_dev = stat->st_dev;
result->st_ino = stat->st_ino;
result->st_mode = stat->st_mode;
result->st_nlink = stat->st_nlink;
result->st_uid = stat->st_uid;
result->st_gid = stat->st_gid;
result->st_rdev = stat->st_rdev;
result->st_size = stat->st_size;
#ifndef _LINUX
result->st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
result->st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
result->st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
#else
result->_st_atime = (long)(stat->st_atime & 0xFFFFFFFF);
result->_st_mtime = (long)(stat->st_mtime & 0xFFFFFFFF);
result->_st_ctime = (long)(stat->st_ctime & 0xFFFFFFFF);
#endif
}
void CUtil::StatI64ToStat64(struct __stat64 *result, struct _stati64 *stat)
{
result->st_dev = stat->st_dev;
result->st_ino = stat->st_ino;
result->st_mode = stat->st_mode;
result->st_nlink = stat->st_nlink;
result->st_uid = stat->st_uid;
result->st_gid = stat->st_gid;
result->st_rdev = stat->st_rdev;
result->st_size = stat->st_size;
#ifndef _LINUX
result->st_atime = stat->st_atime;
result->st_mtime = stat->st_mtime;
result->st_ctime = stat->st_ctime;
#else
result->st_atime = stat->_st_atime;
result->st_mtime = stat->_st_mtime;
result->st_ctime = stat->_st_ctime;
#endif
}
void CUtil::Stat64ToStat(struct stat *result, struct __stat64 *stat)
{
result->st_dev = stat->st_dev;
result->st_ino = stat->st_ino;
result->st_mode = stat->st_mode;
result->st_nlink = stat->st_nlink;
result->st_uid = stat->st_uid;
result->st_gid = stat->st_gid;
result->st_rdev = stat->st_rdev;
#ifndef _LINUX
if (stat->st_size <= LONG_MAX)
result->st_size = (_off_t)stat->st_size;
#else
if (sizeof(stat->st_size) <= sizeof(result->st_size) )
result->st_size = stat->st_size;
#endif
else
{
result->st_size = 0;
CLog::Log(LOGWARNING, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
}
result->st_atime = (time_t)(stat->st_atime & 0xFFFFFFFF);
result->st_mtime = (time_t)(stat->st_mtime & 0xFFFFFFFF);
result->st_ctime = (time_t)(stat->st_ctime & 0xFFFFFFFF);
}
#ifdef _WIN32
void CUtil::Stat64ToStat64i32(struct _stat64i32 *result, struct __stat64 *stat)
{
result->st_dev = stat->st_dev;
result->st_ino = stat->st_ino;
result->st_mode = stat->st_mode;
result->st_nlink = stat->st_nlink;
result->st_uid = stat->st_uid;
result->st_gid = stat->st_gid;
result->st_rdev = stat->st_rdev;
#ifndef _LINUX
if (stat->st_size <= LONG_MAX)
result->st_size = (_off_t)stat->st_size;
#else
if (sizeof(stat->st_size) <= sizeof(result->st_size) )
result->st_size = stat->st_size;
#endif
else
{
result->st_size = 0;
CLog::Log(LOGWARNING, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
}
#ifndef _LINUX
result->st_atime = stat->st_atime;
result->st_mtime = stat->st_mtime;
result->st_ctime = stat->st_ctime;
#else
result->st_atime = stat->_st_atime;
result->st_mtime = stat->_st_mtime;
result->st_ctime = stat->_st_ctime;
#endif
}
#endif
bool CUtil::CreateDirectoryEx(const CStdString& strPath)
{
// Function to create all directories at once instead
// of calling CreateDirectory for every subdir.
// Creates the directory and subdirectories if needed.
// return true if directory already exist
if (CDirectory::Exists(strPath)) return true;
// we currently only allow HD and smb, nfs and afp paths
if (!URIUtils::IsHD(strPath) && !URIUtils::IsSmb(strPath) && !URIUtils::IsNfs(strPath) && !URIUtils::IsAfp(strPath))
{
CLog::Log(LOGERROR,"%s called with an unsupported path: %s", __FUNCTION__, strPath.c_str());
return false;
}
CStdStringArray dirs = URIUtils::SplitPath(strPath);
CStdString dir(dirs.front());
URIUtils::AddSlashAtEnd(dir);
for (CStdStringArray::iterator it = dirs.begin() + 1; it != dirs.end(); it ++)
{
dir = URIUtils::AddFileToFolder(dir, *it);
CDirectory::Create(dir);
}
// was the final destination directory successfully created ?
if (!CDirectory::Exists(strPath)) return false;
return true;
}
CStdString CUtil::MakeLegalFileName(const CStdString &strFile, int LegalType)
{
CStdString result = strFile;
result.Replace('/', '_');
result.Replace('\\', '_');
result.Replace('?', '_');
if (LegalType == LEGAL_WIN32_COMPAT)
{
// just filter out some illegal characters on windows
result.Replace(':', '_');
result.Replace('*', '_');
result.Replace('?', '_');
result.Replace('\"', '_');
result.Replace('<', '_');
result.Replace('>', '_');
result.Replace('|', '_');
result.TrimRight(".");
result.TrimRight(" ");
}
return result;
}
// legalize entire path
CStdString CUtil::MakeLegalPath(const CStdString &strPathAndFile, int LegalType)
{
if (URIUtils::IsStack(strPathAndFile))
return MakeLegalPath(CStackDirectory::GetFirstStackedFile(strPathAndFile));
if (URIUtils::IsMultiPath(strPathAndFile))
return MakeLegalPath(CMultiPathDirectory::GetFirstPath(strPathAndFile));
if (!URIUtils::IsHD(strPathAndFile) && !URIUtils::IsSmb(strPathAndFile) && !URIUtils::IsNfs(strPathAndFile) && !URIUtils::IsAfp(strPathAndFile))
return strPathAndFile; // we don't support writing anywhere except HD, SMB, NFS and AFP - no need to legalize path
bool trailingSlash = URIUtils::HasSlashAtEnd(strPathAndFile);
CStdStringArray dirs = URIUtils::SplitPath(strPathAndFile);
// we just add first token to path and don't legalize it - possible values:
// "X:" (local win32), "" (local unix - empty string before '/') or
// "protocol://domain"
CStdString dir(dirs.front());
URIUtils::AddSlashAtEnd(dir);
for (CStdStringArray::iterator it = dirs.begin() + 1; it != dirs.end(); it ++)
dir = URIUtils::AddFileToFolder(dir, MakeLegalFileName(*it, LegalType));
if (trailingSlash) URIUtils::AddSlashAtEnd(dir);
return dir;
}
CStdString CUtil::ValidatePath(const CStdString &path, bool bFixDoubleSlashes /* = false */)
{
CStdString result = path;
// Don't do any stuff on URLs containing %-characters or protocols that embed
// filenames. NOTE: Don't use IsInZip or IsInRar here since it will infinitely
// recurse and crash XBMC
if (URIUtils::IsURL(path) &&
(path.Find('%') >= 0 ||
path.Left(4).Equals("apk:") ||
path.Left(4).Equals("zip:") ||
path.Left(4).Equals("rar:") ||
path.Left(6).Equals("stack:") ||
path.Left(7).Equals("bluray:") ||
path.Left(10).Equals("multipath:") ))
return result;
// check the path for incorrect slashes
#ifdef _WIN32
if (URIUtils::IsDOSPath(path))
{
result.Replace('/', '\\');
/* The double slash correction should only be used when *absolutely*
necessary! This applies to certain DLLs or use from Python DLLs/scripts
that incorrectly generate double (back) slashes.
*/
if (bFixDoubleSlashes)
{
// Fixup for double back slashes (but ignore the \\ of unc-paths)
for (int x = 1; x < result.GetLength() - 1; x++)
{
if (result[x] == '\\' && result[x+1] == '\\')
result.Delete(x);
}
}
}
else if (path.Find("://") >= 0 || path.Find(":\\\\") >= 0)
#endif
{
result.Replace('\\', '/');
/* The double slash correction should only be used when *absolutely*
necessary! This applies to certain DLLs or use from Python DLLs/scripts
that incorrectly generate double (back) slashes.
*/
if (bFixDoubleSlashes)
{
// Fixup for double forward slashes(/) but don't touch the :// of URLs
for (int x = 2; x < result.GetLength() - 1; x++)
{
if ( result[x] == '/' && result[x + 1] == '/' && !(result[x - 1] == ':' || (result[x - 1] == '/' && result[x - 2] == ':')) )
result.Delete(x);
}
}
}
return result;
}
bool CUtil::IsUsingTTFSubtitles()
{
return URIUtils::GetExtension(g_guiSettings.GetString("subtitles.font")).Equals(".ttf");
}
#ifdef UNIT_TESTING
bool CUtil::TestSplitExec()
{
CStdString function;
vector<CStdString> params;
CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\test\\foo\")", function, params);
if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo")
return false;
params.clear();
CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\test\\foo\\\")", function, params);
if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo\"")
return false;
CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\\\test\\\\foo\\\\\")", function, params);
if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\test\\foo\\")
return false;
CUtil::SplitExecFunction("ActivateWindow(Video, \"C:\\\\\\\\test\\\\\\foo\\\\\")", function, params);
if (function != "ActivateWindow" || params.size() != 2 || params[0] != "Video" || params[1] != "C:\\\\test\\\\foo\\")
return false;
CUtil::SplitExecFunction("SetProperty(Foo,\"\")", function, params);
if (function != "SetProperty" || params.size() != 2 || params[0] != "Foo" || params[1] != "")
return false;
CUtil::SplitExecFunction("SetProperty(foo,ba(\"ba black )\",sheep))", function, params);
if (function != "SetProperty" || params.size() != 2 || params[0] != "foo" || params[1] != "ba(\"ba black )\",sheep)")
return false;
return true;
}
#endif
void CUtil::SplitExecFunction(const CStdString &execString, CStdString &function, vector<CStdString> &parameters)
{
CStdString paramString;
int iPos = execString.Find("(");
int iPos2 = execString.ReverseFind(")");
if (iPos > 0 && iPos2 > 0)
{
paramString = execString.Mid(iPos + 1, iPos2 - iPos - 1);
function = execString.Left(iPos);
}
else
function = execString;
// remove any whitespace, and the standard prefix (if it exists)
function.Trim();
if( function.Left(5).Equals("xbmc.", false) )
function.Delete(0, 5);
SplitParams(paramString, parameters);
}
void CUtil::SplitParams(const CStdString &paramString, std::vector<CStdString> &parameters)
{
bool inQuotes = false;
bool lastEscaped = false; // only every second character can be escaped
int inFunction = 0;
size_t whiteSpacePos = 0;
CStdString parameter;
parameters.clear();
for (size_t pos = 0; pos < paramString.size(); pos++)
{
char ch = paramString[pos];
bool escaped = (pos > 0 && paramString[pos - 1] == '\\' && !lastEscaped);
lastEscaped = escaped;
if (inQuotes)
{ // if we're in a quote, we accept everything until the closing quote
if (ch == '\"' && !escaped)
{ // finished a quote - no need to add the end quote to our string
inQuotes = false;
}
}
else
{ // not in a quote, so check if we should be starting one
if (ch == '\"' && !escaped)
{ // start of quote - no need to add the quote to our string
inQuotes = true;
}
if (inFunction && ch == ')')
{ // end of a function
inFunction--;
}
if (ch == '(')
{ // start of function
inFunction++;
}
if (!inFunction && ch == ',')
{ // not in a function, so a comma signfies the end of this parameter
if (whiteSpacePos)
parameter = parameter.Left(whiteSpacePos);
// trim off start and end quotes
if (parameter.GetLength() > 1 && parameter[0] == '\"' && parameter[parameter.GetLength() - 1] == '\"')
parameter = parameter.Mid(1,parameter.GetLength() - 2);
parameters.push_back(parameter);
parameter.Empty();
whiteSpacePos = 0;
continue;
}
}
if ((ch == '\"' || ch == '\\') && escaped)
{ // escaped quote or backslash
parameter[parameter.size()-1] = ch;
continue;
}
// whitespace handling - we skip any whitespace at the left or right of an unquoted parameter
if (ch == ' ' && !inQuotes)
{
if (parameter.IsEmpty()) // skip whitespace on left
continue;
if (!whiteSpacePos) // make a note of where whitespace starts on the right
whiteSpacePos = parameter.size();
}
else
whiteSpacePos = 0;
parameter += ch;
}
if (inFunction || inQuotes)
CLog::Log(LOGWARNING, "%s(%s) - end of string while searching for ) or \"", __FUNCTION__, paramString.c_str());
if (whiteSpacePos)
parameter = parameter.Left(whiteSpacePos);
// trim off start and end quotes
if (parameter.GetLength() > 1 && parameter[0] == '\"' && parameter[parameter.GetLength() - 1] == '\"')
parameter = parameter.Mid(1,parameter.GetLength() - 2);
if (!parameter.IsEmpty() || parameters.size())
parameters.push_back(parameter);
}
int CUtil::GetMatchingSource(const CStdString& strPath1, VECSOURCES& VECSOURCES, bool& bIsSourceName)
{
if (strPath1.IsEmpty())
return -1;
// copy as we may change strPath
CStdString strPath = strPath1;
// Check for special protocols
CURL checkURL(strPath);
// stack://
if (checkURL.GetProtocol() == "stack")
strPath.Delete(0, 8); // remove the stack protocol
if (checkURL.GetProtocol() == "shout")
strPath = checkURL.GetHostName();
if (checkURL.GetProtocol() == "lastfm")
return 1;
if (checkURL.GetProtocol() == "tuxbox")
return 1;
if (checkURL.GetProtocol() == "plugin")
return 1;
if (checkURL.GetProtocol() == "multipath")
strPath = CMultiPathDirectory::GetFirstPath(strPath);
bIsSourceName = false;
int iIndex = -1;
int iLength = -1;
// we first test the NAME of a source
for (int i = 0; i < (int)VECSOURCES.size(); ++i)
{
CMediaSource share = VECSOURCES.at(i);
CStdString strName = share.strName;
// special cases for dvds
if (URIUtils::IsOnDVD(share.strPath))
{
if (URIUtils::IsOnDVD(strPath))
return i;
// not a path, so we need to modify the source name
// since we add the drive status and disc name to the source
// "Name (Drive Status/Disc Name)"
int iPos = strName.ReverseFind('(');
if (iPos > 1)
strName = strName.Mid(0, iPos - 1);
}
if (strPath.Equals(strName))
{
bIsSourceName = true;
return i;
}
}
// now test the paths
// remove user details, and ensure path only uses forward slashes
// and ends with a trailing slash so as not to match a substring
CURL urlDest(strPath);
urlDest.SetOptions("");
CStdString strDest = urlDest.GetWithoutUserDetails();
ForceForwardSlashes(strDest);
if (!URIUtils::HasSlashAtEnd(strDest))
strDest += "/";
int iLenPath = strDest.size();
for (int i = 0; i < (int)VECSOURCES.size(); ++i)
{
CMediaSource share = VECSOURCES.at(i);
// does it match a source name?
if (share.strPath.substr(0,8) == "shout://")
{
CURL url(share.strPath);
if (strPath.Equals(url.GetHostName()))
return i;
}
// doesnt match a name, so try the source path
vector<CStdString> vecPaths;
// add any concatenated paths if they exist
if (share.vecPaths.size() > 0)
vecPaths = share.vecPaths;
// add the actual share path at the front of the vector
vecPaths.insert(vecPaths.begin(), share.strPath);
// test each path
for (int j = 0; j < (int)vecPaths.size(); ++j)
{
// remove user details, and ensure path only uses forward slashes
// and ends with a trailing slash so as not to match a substring
CURL urlShare(vecPaths[j]);
urlShare.SetOptions("");
CStdString strShare = urlShare.GetWithoutUserDetails();
ForceForwardSlashes(strShare);
if (!URIUtils::HasSlashAtEnd(strShare))
strShare += "/";
int iLenShare = strShare.size();
if ((iLenPath >= iLenShare) && (strDest.Left(iLenShare).Equals(strShare)) && (iLenShare > iLength))
{
// if exact match, return it immediately
if (iLenPath == iLenShare)
{
// if the path EXACTLY matches an item in a concatentated path
// set source name to true to load the full virtualpath
bIsSourceName = false;
if (vecPaths.size() > 1)
bIsSourceName = true;
return i;
}
iIndex = i;
iLength = iLenShare;
}
}
}
// return the index of the share with the longest match
if (iIndex == -1)
{
// rar:// and zip://
// if archive wasn't mounted, look for a matching share for the archive instead
if( strPath.Left(6).Equals("rar://") || strPath.Left(6).Equals("zip://") )
{
// get the hostname portion of the url since it contains the archive file
strPath = checkURL.GetHostName();
bIsSourceName = false;
bool bDummy;
return GetMatchingSource(strPath, VECSOURCES, bDummy);
}
CLog::Log(LOGDEBUG,"CUtil::GetMatchingSource: no matching source found for [%s]", strPath1.c_str());
}
return iIndex;
}
CStdString CUtil::TranslateSpecialSource(const CStdString &strSpecial)
{
CStdString strReturn=strSpecial;
if (!strSpecial.IsEmpty() && strSpecial[0] == '$')
{
if (strSpecial.Left(5).Equals("$HOME"))
URIUtils::AddFileToFolder("special://home/", strSpecial.Mid(5), strReturn);
else if (strSpecial.Left(10).Equals("$SUBTITLES"))
URIUtils::AddFileToFolder("special://subtitles/", strSpecial.Mid(10), strReturn);
else if (strSpecial.Left(9).Equals("$USERDATA"))
URIUtils::AddFileToFolder("special://userdata/", strSpecial.Mid(9), strReturn);
else if (strSpecial.Left(9).Equals("$DATABASE"))
URIUtils::AddFileToFolder("special://database/", strSpecial.Mid(9), strReturn);
else if (strSpecial.Left(11).Equals("$THUMBNAILS"))
URIUtils::AddFileToFolder("special://thumbnails/", strSpecial.Mid(11), strReturn);
else if (strSpecial.Left(11).Equals("$RECORDINGS"))
URIUtils::AddFileToFolder("special://recordings/", strSpecial.Mid(11), strReturn);
else if (strSpecial.Left(12).Equals("$SCREENSHOTS"))
URIUtils::AddFileToFolder("special://screenshots/", strSpecial.Mid(12), strReturn);
else if (strSpecial.Left(15).Equals("$MUSICPLAYLISTS"))
URIUtils::AddFileToFolder("special://musicplaylists/", strSpecial.Mid(15), strReturn);
else if (strSpecial.Left(15).Equals("$VIDEOPLAYLISTS"))
URIUtils::AddFileToFolder("special://videoplaylists/", strSpecial.Mid(15), strReturn);
else if (strSpecial.Left(7).Equals("$CDRIPS"))
URIUtils::AddFileToFolder("special://cdrips/", strSpecial.Mid(7), strReturn);
// this one will be removed post 2.0
else if (strSpecial.Left(10).Equals("$PLAYLISTS"))
URIUtils::AddFileToFolder(g_guiSettings.GetString("system.playlistspath",false), strSpecial.Mid(10), strReturn);
}
return strReturn;
}
CStdString CUtil::MusicPlaylistsLocation()
{
vector<CStdString> vec;
CStdString strReturn;
URIUtils::AddFileToFolder(g_guiSettings.GetString("system.playlistspath"), "music", strReturn);
vec.push_back(strReturn);
URIUtils::AddFileToFolder(g_guiSettings.GetString("system.playlistspath"), "mixed", strReturn);
vec.push_back(strReturn);
return XFILE::CMultiPathDirectory::ConstructMultiPath(vec);;
}
CStdString CUtil::VideoPlaylistsLocation()
{
vector<CStdString> vec;
CStdString strReturn;
URIUtils::AddFileToFolder(g_guiSettings.GetString("system.playlistspath"), "video", strReturn);
vec.push_back(strReturn);
URIUtils::AddFileToFolder(g_guiSettings.GetString("system.playlistspath"), "mixed", strReturn);
vec.push_back(strReturn);
return XFILE::CMultiPathDirectory::ConstructMultiPath(vec);;
}
void CUtil::DeleteMusicDatabaseDirectoryCache()
{
CUtil::DeleteDirectoryCache("mdb-");
CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete video smartplaylists, but as we can't differentiate based on URL...
}
void CUtil::DeleteVideoDatabaseDirectoryCache()
{
CUtil::DeleteDirectoryCache("vdb-");
CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete music smartplaylists, but as we can't differentiate based on URL...
}
void CUtil::DeleteDirectoryCache(const CStdString &prefix)
{
CStdString searchPath = "special://temp/";
CFileItemList items;
if (!XFILE::CDirectory::GetDirectory(searchPath, items, ".fi", DIR_FLAG_NO_FILE_DIRS))
return;
for (int i = 0; i < items.Size(); ++i)
{
if (items[i]->m_bIsFolder)
continue;
CStdString fileName = URIUtils::GetFileName(items[i]->GetPath());
if (fileName.Left(prefix.GetLength()) == prefix)
XFILE::CFile::Delete(items[i]->GetPath());
}
}
bool CUtil::SetSysDateTimeYear(int iYear, int iMonth, int iDay, int iHour, int iMinute)
{
TIME_ZONE_INFORMATION tziNew;
SYSTEMTIME CurTime;
SYSTEMTIME NewTime;
GetLocalTime(&CurTime);
GetLocalTime(&NewTime);
int iRescBiases, iHourUTC;
int iMinuteNew;
DWORD dwRet = GetTimeZoneInformation(&tziNew); // Get TimeZone Informations
float iGMTZone = (float(tziNew.Bias)/(60)); // Calc's the GMT Time
CLog::Log(LOGDEBUG, "------------ TimeZone -------------");
CLog::Log(LOGDEBUG, "- GMT Zone: GMT %.1f",iGMTZone);
CLog::Log(LOGDEBUG, "- Bias: %lu minutes",tziNew.Bias);
CLog::Log(LOGDEBUG, "- DaylightBias: %lu",tziNew.DaylightBias);
CLog::Log(LOGDEBUG, "- StandardBias: %lu",tziNew.StandardBias);
switch (dwRet)
{
case TIME_ZONE_ID_STANDARD:
{
iRescBiases = tziNew.Bias + tziNew.StandardBias;
CLog::Log(LOGDEBUG, "- Timezone ID: 1, Standart");
}
break;
case TIME_ZONE_ID_DAYLIGHT:
{
iRescBiases = tziNew.Bias + tziNew.StandardBias + tziNew.DaylightBias;
CLog::Log(LOGDEBUG, "- Timezone ID: 2, Daylight");
}
break;
case TIME_ZONE_ID_UNKNOWN:
{
iRescBiases = tziNew.Bias + tziNew.StandardBias;
CLog::Log(LOGDEBUG, "- Timezone ID: 0, Unknown");
}
break;
case TIME_ZONE_ID_INVALID:
{
iRescBiases = tziNew.Bias + tziNew.StandardBias;
CLog::Log(LOGDEBUG, "- Timezone ID: Invalid");
}
break;
default:
iRescBiases = tziNew.Bias + tziNew.StandardBias;
}
CLog::Log(LOGDEBUG, "--------------- END ---------------");
// Calculation
iHourUTC = GMTZoneCalc(iRescBiases, iHour, iMinute, iMinuteNew);
iMinute = iMinuteNew;
if(iHourUTC <0)
{
iDay = iDay - 1;
iHourUTC =iHourUTC + 24;
}
if(iHourUTC >23)
{
iDay = iDay + 1;
iHourUTC =iHourUTC - 24;
}
// Set the New-,Detected Time Values to System Time!
NewTime.wYear = (WORD)iYear;
NewTime.wMonth = (WORD)iMonth;
NewTime.wDay = (WORD)iDay;
NewTime.wHour = (WORD)iHourUTC;
NewTime.wMinute = (WORD)iMinute;
FILETIME stNewTime, stCurTime;
SystemTimeToFileTime(&NewTime, &stNewTime);
SystemTimeToFileTime(&CurTime, &stCurTime);
return false;
}
int CUtil::GMTZoneCalc(int iRescBiases, int iHour, int iMinute, int &iMinuteNew)
{
int iHourUTC, iTemp;
iMinuteNew = iMinute;
iTemp = iRescBiases/60;
if (iRescBiases == 0 )return iHour; // GMT Zone 0, no need calculate
if (iRescBiases > 0)
iHourUTC = iHour + abs(iTemp);
else
iHourUTC = iHour - abs(iTemp);
if ((iTemp*60) != iRescBiases)
{
if (iRescBiases > 0)
iMinuteNew = iMinute + abs(iTemp*60 - iRescBiases);
else
iMinuteNew = iMinute - abs(iTemp*60 - iRescBiases);
if (iMinuteNew >= 60)
{
iMinuteNew = iMinuteNew -60;
iHourUTC = iHourUTC + 1;
}
else if (iMinuteNew < 0)
{
iMinuteNew = iMinuteNew +60;
iHourUTC = iHourUTC - 1;
}
}
return iHourUTC;
}
void CUtil::GetRecursiveListing(const CStdString& strPath, CFileItemList& items, const CStdString& strMask, bool bUseFileDirectories)
{
CFileItemList myItems;
int flags = DIR_FLAG_DEFAULTS;
if (!bUseFileDirectories)
flags |= DIR_FLAG_NO_FILE_DIRS;
CDirectory::GetDirectory(strPath,myItems,strMask,flags);
for (int i=0;i<myItems.Size();++i)
{
if (myItems[i]->m_bIsFolder)
CUtil::GetRecursiveListing(myItems[i]->GetPath(),items,strMask,bUseFileDirectories);
else
items.Add(myItems[i]);
}
}
void CUtil::GetRecursiveDirsListing(const CStdString& strPath, CFileItemList& item)
{
CFileItemList myItems;
CDirectory::GetDirectory(strPath,myItems,"",DIR_FLAG_NO_FILE_DIRS);
for (int i=0;i<myItems.Size();++i)
{
if (myItems[i]->m_bIsFolder && !myItems[i]->GetPath().Equals(".."))
{
item.Add(myItems[i]);
CUtil::GetRecursiveDirsListing(myItems[i]->GetPath(),item);
}
}
}
void CUtil::ForceForwardSlashes(CStdString& strPath)
{
int iPos = strPath.ReverseFind('\\');
while (iPos > 0)
{
strPath.at(iPos) = '/';
iPos = strPath.ReverseFind('\\');
}
}
double CUtil::AlbumRelevance(const CStdString& strAlbumTemp1, const CStdString& strAlbum1, const CStdString& strArtistTemp1, const CStdString& strArtist1)
{
// case-insensitive fuzzy string comparison on the album and artist for relevance
// weighting is identical, both album and artist are 50% of the total relevance
// a missing artist means the maximum relevance can only be 0.50
CStdString strAlbumTemp = strAlbumTemp1;
strAlbumTemp.MakeLower();
CStdString strAlbum = strAlbum1;
strAlbum.MakeLower();
double fAlbumPercentage = fstrcmp(strAlbumTemp, strAlbum, 0.0f);
double fArtistPercentage = 0.0f;
if (!strArtist1.IsEmpty())
{
CStdString strArtistTemp = strArtistTemp1;
strArtistTemp.MakeLower();
CStdString strArtist = strArtist1;
strArtist.MakeLower();
fArtistPercentage = fstrcmp(strArtistTemp, strArtist, 0.0f);
}
double fRelevance = fAlbumPercentage * 0.5f + fArtistPercentage * 0.5f;
return fRelevance;
}
bool CUtil::MakeShortenPath(CStdString StrInput, CStdString& StrOutput, int iTextMaxLength)
{
int iStrInputSize = StrInput.size();
if((iStrInputSize <= 0) || (iTextMaxLength >= iStrInputSize))
return false;
char cDelim = '\0';
size_t nGreaterDelim, nPos;
nPos = StrInput.find_last_of( '\\' );
if ( nPos != CStdString::npos )
cDelim = '\\';
else
{
nPos = StrInput.find_last_of( '/' );
if ( nPos != CStdString::npos )
cDelim = '/';
}
if ( cDelim == '\0' )
return false;
if (nPos == StrInput.size() - 1)
{
StrInput.erase(StrInput.size() - 1);
nPos = StrInput.find_last_of( cDelim );
}
while( iTextMaxLength < iStrInputSize )
{
nPos = StrInput.find_last_of( cDelim, nPos );
nGreaterDelim = nPos;
if ( nPos != CStdString::npos )
nPos = StrInput.find_last_of( cDelim, nPos - 1 );
if ( nPos == CStdString::npos ) break;
if ( nGreaterDelim > nPos ) StrInput.replace( nPos + 1, nGreaterDelim - nPos - 1, ".." );
iStrInputSize = StrInput.size();
}
// replace any additional /../../ with just /../ if necessary
CStdString replaceDots;
replaceDots.Format("..%c..", cDelim);
while (StrInput.size() > (unsigned int)iTextMaxLength)
if (!StrInput.Replace(replaceDots, ".."))
break;
// finally, truncate our string to force inside our max text length,
// replacing the last 2 characters with ".."
// eg end up with:
// "smb://../Playboy Swimsuit Cal.."
if (iTextMaxLength > 2 && StrInput.size() > (unsigned int)iTextMaxLength)
{
StrInput = StrInput.Left(iTextMaxLength - 2);
StrInput += "..";
}
StrOutput = StrInput;
return true;
}
bool CUtil::SupportsFileOperations(const CStdString& strPath)
{
// currently only hd, smb, nfs and afp support delete and rename
if (URIUtils::IsHD(strPath))
return true;
if (URIUtils::IsSmb(strPath))
return true;
if (CUtil::IsTVRecording(strPath))
return CPVRDirectory::SupportsFileOperations(strPath);
if (URIUtils::IsNfs(strPath))
return true;
if (URIUtils::IsAfp(strPath))
return true;
if (URIUtils::IsMythTV(strPath))
{
/*
* Can't use CFile::Exists() to check whether the myth:// path supports file operations because
* it hits the directory cache on the way through, which has the Live Channels and Guide
* items cached.
*/
return CMythDirectory::SupportsFileOperations(strPath);
}
if (URIUtils::IsStack(strPath))
return SupportsFileOperations(CStackDirectory::GetFirstStackedFile(strPath));
if (URIUtils::IsMultiPath(strPath))
return CMultiPathDirectory::SupportsFileOperations(strPath);
return false;
}
CStdString CUtil::GetDefaultFolderThumb(const CStdString &folderThumb)
{
if (g_TextureManager.HasTexture(folderThumb))
return folderThumb;
return "";
}
void CUtil::GetSkinThemes(vector<CStdString>& vecTheme)
{
CStdString strPath;
URIUtils::AddFileToFolder(g_graphicsContext.GetMediaDir(),"media",strPath);
CFileItemList items;
CDirectory::GetDirectory(strPath, items);
// Search for Themes in the Current skin!
for (int i = 0; i < items.Size(); ++i)
{
CFileItemPtr pItem = items[i];
if (!pItem->m_bIsFolder)
{
CStdString strExtension;
URIUtils::GetExtension(pItem->GetPath(), strExtension);
if ((strExtension == ".xpr" && pItem->GetLabel().CompareNoCase("Textures.xpr")) ||
(strExtension == ".xbt" && pItem->GetLabel().CompareNoCase("Textures.xbt")))
{
CStdString strLabel = pItem->GetLabel();
vecTheme.push_back(strLabel.Mid(0, strLabel.size() - 4));
}
}
}
sort(vecTheme.begin(), vecTheme.end(), sortstringbyname());
}
void CUtil::InitRandomSeed()
{
// Init random seed
int64_t now;
now = CurrentHostCounter();
unsigned int seed = (unsigned int)now;
// CLog::Log(LOGDEBUG, "%s - Initializing random seed with %u", __FUNCTION__, seed);
srand(seed);
}
#ifdef _LINUX
bool CUtil::RunCommandLine(const CStdString& cmdLine, bool waitExit)
{
CStdStringArray args;
StringUtils::SplitString(cmdLine, ",", args);
// Strip quotes and whitespace around the arguments, or exec will fail.
// This allows the python invocation to be written more naturally with any amount of whitespace around the args.
// But it's still limited, for example quotes inside the strings are not expanded, etc.
// TODO: Maybe some python library routine can parse this more properly ?
for (size_t i=0; i<args.size(); i++)
{
CStdString &s = args[i];
CStdString stripd = s.Trim();
if (stripd[0] == '"' || stripd[0] == '\'')
{
s = s.TrimLeft();
s = s.Right(s.size() - 1);
}
if (stripd[stripd.size() - 1] == '"' || stripd[stripd.size() - 1] == '\'')
{
s = s.TrimRight();
s = s.Left(s.size() - 1);
}
}
return Command(args, waitExit);
}
//
// FIXME, this should be merged with the function below.
//
bool CUtil::Command(const CStdStringArray& arrArgs, bool waitExit)
{
#ifdef _DEBUG
printf("Executing: ");
for (size_t i=0; i<arrArgs.size(); i++)
printf("%s ", arrArgs[i].c_str());
printf("\n");
#endif
pid_t child = fork();
int n = 0;
if (child == 0)
{
close(0);
close(1);
close(2);
if (arrArgs.size() > 0)
{
char **args = (char **)alloca(sizeof(char *) * (arrArgs.size() + 3));
memset(args, 0, (sizeof(char *) * (arrArgs.size() + 3)));
for (size_t i=0; i<arrArgs.size(); i++)
args[i] = (char *)arrArgs[i].c_str();
execvp(args[0], args);
}
}
else
{
if (waitExit) waitpid(child, &n, 0);
}
return (waitExit) ? (WEXITSTATUS(n) == 0) : true;
}
bool CUtil::SudoCommand(const CStdString &strCommand)
{
CLog::Log(LOGDEBUG, "Executing sudo command: <%s>", strCommand.c_str());
pid_t child = fork();
int n = 0;
if (child == 0)
{
close(0); // close stdin to avoid sudo request password
close(1);
close(2);
CStdStringArray arrArgs;
StringUtils::SplitString(strCommand, " ", arrArgs);
if (arrArgs.size() > 0)
{
char **args = (char **)alloca(sizeof(char *) * (arrArgs.size() + 3));
memset(args, 0, (sizeof(char *) * (arrArgs.size() + 3)));
args[0] = (char *)"/usr/bin/sudo";
args[1] = (char *)"-S";
for (size_t i=0; i<arrArgs.size(); i++)
{
args[i+2] = (char *)arrArgs[i].c_str();
}
execvp("/usr/bin/sudo", args);
}
}
else
waitpid(child, &n, 0);
return WEXITSTATUS(n) == 0;
}
#endif
int CUtil::LookupRomanDigit(char roman_digit)
{
switch (roman_digit)
{
case 'i':
case 'I':
return 1;
case 'v':
case 'V':
return 5;
case 'x':
case 'X':
return 10;
case 'l':
case 'L':
return 50;
case 'c':
case 'C':
return 100;
case 'd':
case 'D':
return 500;
case 'm':
case 'M':
return 1000;
default:
return 0;
}
}
int CUtil::TranslateRomanNumeral(const char* roman_numeral)
{
int decimal = -1;
if (roman_numeral && roman_numeral[0])
{
int temp_sum = 0,
last = 0,
repeat = 0,
trend = 1;
decimal = 0;
while (*roman_numeral)
{
int digit = CUtil::LookupRomanDigit(*roman_numeral);
int test = last;
// General sanity checks
// numeral not in LUT
if (!digit)
return -1;
while (test > 5)
test /= 10;
// N = 10^n may not precede (N+1) > 10^(N+1)
if (test == 1 && digit > last * 10)
return -1;
// N = 5*10^n may not precede (N+1) >= N
if (test == 5 && digit >= last)
return -1;
// End general sanity checks
if (last < digit)
{
// smaller numerals may not repeat before a larger one
if (repeat)
return -1;
temp_sum += digit;
repeat = 0;
trend = 0;
}
else if (last == digit)
{
temp_sum += digit;
repeat++;
trend = 1;
}
else
{
if (!repeat)
decimal += 2 * last - temp_sum;
else
decimal += temp_sum;
temp_sum = digit;
trend = 1;
repeat = 0;
}
// Post general sanity checks
// numerals may not repeat more than thrice
if (repeat == 3)
return -1;
last = digit;
roman_numeral++;
}
if (trend)
decimal += temp_sum;
else
decimal += 2 * last - temp_sum;
}
return decimal;
}
CStdString CUtil::ResolveExecutablePath()
{
CStdString strExecutablePath;
#ifdef WIN32
wchar_t szAppPathW[MAX_PATH] = L"";
::GetModuleFileNameW(0, szAppPathW, sizeof(szAppPathW)/sizeof(szAppPathW[0]) - 1);
CStdStringW strPathW = szAppPathW;
g_charsetConverter.wToUTF8(strPathW,strExecutablePath);
#elif defined(TARGET_DARWIN)
char given_path[2*MAXPATHLEN];
uint32_t path_size =2*MAXPATHLEN;
GetDarwinExecutablePath(given_path, &path_size);
strExecutablePath = given_path;
#elif defined(__FreeBSD__)
char buf[PATH_MAX];
size_t buflen;
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = getpid();
buflen = sizeof(buf) - 1;
if(sysctl(mib, 4, buf, &buflen, NULL, 0) < 0)
strExecutablePath = "";
else
strExecutablePath = buf;
#else
/* Get our PID and build the name of the link in /proc */
pid_t pid = getpid();
char linkname[64]; /* /proc/<pid>/exe */
snprintf(linkname, sizeof(linkname), "/proc/%i/exe", pid);
/* Now read the symbolic link */
char buf[PATH_MAX + 1];
buf[0] = 0;
int ret = readlink(linkname, buf, sizeof(buf) - 1);
if (ret != -1)
buf[ret] = 0;
strExecutablePath = buf;
#endif
return strExecutablePath;
}
CStdString CUtil::GetFrameworksPath(bool forPython)
{
CStdString strFrameworksPath;
#if defined(TARGET_DARWIN)
char given_path[2*MAXPATHLEN];
uint32_t path_size =2*MAXPATHLEN;
GetDarwinFrameworkPath(forPython, given_path, &path_size);
strFrameworksPath = given_path;
#endif
return strFrameworksPath;
}
void CUtil::ScanForExternalSubtitles(const CStdString& strMovie, std::vector<CStdString>& vecSubtitles )
{
unsigned int startTimer = XbmcThreads::SystemClockMillis();
// new array for commons sub dirs
const char * common_sub_dirs[] = {"subs",
"Subs",
"subtitles",
"Subtitles",
"vobsubs",
"Vobsubs",
"sub",
"Sub",
"vobsub",
"Vobsub",
"subtitle",
"Subtitle",
NULL};
vector<CStdString> vecExtensionsCached;
CFileItem item(strMovie, false);
if ( item.IsInternetStream()
|| item.IsHDHomeRun()
|| item.IsSlingbox()
|| item.IsPlayList()
|| item.IsLiveTV()
|| !item.IsVideo())
return;
vector<CStdString> strLookInPaths;
CStdString strMovieFileName;
CStdString strPath;
URIUtils::Split(strMovie, strPath, strMovieFileName);
CStdString strMovieFileNameNoExt(URIUtils::ReplaceExtension(strMovieFileName, ""));
strLookInPaths.push_back(strPath);
if (!g_settings.iAdditionalSubtitleDirectoryChecked && !g_guiSettings.GetString("subtitles.custompath").IsEmpty()) // to avoid checking non-existent directories (network) every time..
{
if (!g_application.getNetwork().IsAvailable() && !URIUtils::IsHD(g_guiSettings.GetString("subtitles.custompath")))
{
CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonaccessible");
g_settings.iAdditionalSubtitleDirectoryChecked = -1; // disabled
}
else if (!CDirectory::Exists(g_guiSettings.GetString("subtitles.custompath")))
{
CLog::Log(LOGINFO,"CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonexistant");
g_settings.iAdditionalSubtitleDirectoryChecked = -1; // disabled
}
g_settings.iAdditionalSubtitleDirectoryChecked = 1;
}
if (strMovie.Left(6) == "rar://") // <--- if this is found in main path then ignore it!
{
CURL url(strMovie);
CStdString strArchive = url.GetHostName();
URIUtils::Split(strArchive, strPath, strMovieFileName);
strLookInPaths.push_back(strPath);
}
int iSize = strLookInPaths.size();
for (int i=0; i<iSize; ++i)
{
CStdStringArray directories;
int nTokens = StringUtils::SplitString( strLookInPaths[i], "/", directories );
if (nTokens == 1)
StringUtils::SplitString( strLookInPaths[i], "\\", directories );
// if it's inside a cdX dir, add parent path
if (directories.size() >= 2 && directories[directories.size()-2].size() == 3 && directories[directories.size()-2].Left(2).Equals("cd")) // SplitString returns empty token as last item, hence size-2
{
CStdString strPath2;
URIUtils::GetParentPath(strLookInPaths[i], strPath2);
strLookInPaths.push_back(strPath2);
}
}
// checking if any of the common subdirs exist ..
iSize = strLookInPaths.size();
for (int i=0;i<iSize;++i)
{
for (int j=0; common_sub_dirs[j]; j++)
{
CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],common_sub_dirs[j]);
if (CDirectory::Exists(strPath2))
strLookInPaths.push_back(strPath2);
}
}
// .. done checking for common subdirs
// check if there any cd-directories in the paths we have added so far
iSize = strLookInPaths.size();
for (int i=0;i<9;++i) // 9 cd's
{
CStdString cdDir;
cdDir.Format("cd%i",i+1);
for (int i=0;i<iSize;++i)
{
CStdString strPath2 = URIUtils::AddFileToFolder(strLookInPaths[i],cdDir);
URIUtils::AddSlashAtEnd(strPath2);
bool pathAlreadyAdded = false;
for (unsigned int i=0; i<strLookInPaths.size(); i++)
{
// if movie file is inside cd-dir, this directory can exist in vector already
if (strLookInPaths[i].Equals( strPath2 ) )
pathAlreadyAdded = true;
}
if (CDirectory::Exists(strPath2) && !pathAlreadyAdded)
strLookInPaths.push_back(strPath2);
}
}
// .. done checking for cd-dirs
// this is last because we dont want to check any common subdirs or cd-dirs in the alternate <subtitles> dir.
if (g_settings.iAdditionalSubtitleDirectoryChecked == 1)
{
strPath = g_guiSettings.GetString("subtitles.custompath");
URIUtils::AddSlashAtEnd(strPath);
strLookInPaths.push_back(strPath);
}
CStdString strLExt;
CStdString strDest;
CStdString strItem;
// 2 steps for movie directory and alternate subtitles directory
CLog::Log(LOGDEBUG,"%s: Searching for subtitles...", __FUNCTION__);
for (unsigned int step = 0; step < strLookInPaths.size(); step++)
{
if (strLookInPaths[step].length() != 0)
{
CFileItemList items;
CDirectory::GetDirectory(strLookInPaths[step], items,".utf|.utf8|.utf-8|.sub|.srt|.smi|.rt|.txt|.ssa|.text|.ssa|.aqt|.jss|.ass|.idx|.ifo|.rar|.zip",DIR_FLAG_NO_FILE_DIRS);
int fnl = strMovieFileNameNoExt.size();
for (int j = 0; j < items.Size(); j++)
{
URIUtils::Split(items[j]->GetPath(), strPath, strItem);
if (strItem.Left(fnl).Equals(strMovieFileNameNoExt))
{
// is this a rar or zip-file
if (URIUtils::IsRAR(strItem) || URIUtils::IsZIP(strItem))
{
// zip-file name equals strMovieFileNameNoExt, don't check in zip-file
ScanArchiveForSubtitles( items[j]->GetPath(), "", vecSubtitles );
}
else // not a rar/zip file
{
for (int i = 0; sub_exts[i]; i++)
{
//Cache subtitle with same name as movie
if (URIUtils::GetExtension(strItem).Equals(sub_exts[i]))
{
vecSubtitles.push_back( items[j]->GetPath() );
CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, items[j]->GetPath().c_str() );
}
}
}
}
else
{
// is this a rar or zip-file
if (URIUtils::IsRAR(strItem) || URIUtils::IsZIP(strItem))
{
// check strMovieFileNameNoExt in zip-file
ScanArchiveForSubtitles( items[j]->GetPath(), strMovieFileNameNoExt, vecSubtitles );
}
}
}
}
}
iSize = vecSubtitles.size();
for (int i = 0; i < iSize; i++)
{
if (URIUtils::GetExtension(vecSubtitles[i]).Equals(".smi"))
{
//Cache multi-language sami subtitle
CDVDSubtitleStream* pStream = new CDVDSubtitleStream();
if(pStream->Open(vecSubtitles[i]))
{
CDVDSubtitleTagSami TagConv;
TagConv.LoadHead(pStream);
if (TagConv.m_Langclass.size() >= 2)
{
for (unsigned int k = 0; k < TagConv.m_Langclass.size(); k++)
{
strDest.Format("special://temp/subtitle.%s.%d.smi", TagConv.m_Langclass[k].Name, i);
if (CFile::Cache(vecSubtitles[i], strDest))
{
CLog::Log(LOGINFO, " cached subtitle %s->%s\n", vecSubtitles[i].c_str(), strDest.c_str());
vecSubtitles.push_back(strDest);
}
}
}
}
delete pStream;
}
}
CLog::Log(LOGDEBUG,"%s: END (total time: %i ms)", __FUNCTION__, (int)(XbmcThreads::SystemClockMillis() - startTimer));
}
int CUtil::ScanArchiveForSubtitles( const CStdString& strArchivePath, const CStdString& strMovieFileNameNoExt, std::vector<CStdString>& vecSubtitles )
{
int nSubtitlesAdded = 0;
CFileItemList ItemList;
// zip only gets the root dir
if (URIUtils::GetExtension(strArchivePath).Equals(".zip"))
{
CStdString strZipPath;
URIUtils::CreateArchivePath(strZipPath,"zip",strArchivePath,"");
if (!CDirectory::GetDirectory(strZipPath,ItemList,"",DIR_FLAG_NO_FILE_DIRS))
return false;
}
else
{
#ifdef HAS_FILESYSTEM_RAR
// get _ALL_files in the rar, even those located in subdirectories because we set the bMask to false.
// so now we dont have to find any subdirs anymore, all files in the rar is checked.
if( !g_RarManager.GetFilesInRar(ItemList, strArchivePath, false, "") )
return false;
#else
return false;
#endif
}
for (int it= 0 ; it <ItemList.Size();++it)
{
CStdString strPathInRar = ItemList[it]->GetPath();
CStdString strExt = URIUtils::GetExtension(strPathInRar);
CLog::Log(LOGDEBUG, "ScanArchiveForSubtitles:: Found file %s", strPathInRar.c_str());
// always check any embedded rar archives
// checking for embedded rars, I moved this outside the sub_ext[] loop. We only need to check this once for each file.
if (URIUtils::IsRAR(strPathInRar) || URIUtils::IsZIP(strPathInRar))
{
CStdString strRarInRar;
if (URIUtils::GetExtension(strPathInRar).Equals(".rar"))
URIUtils::CreateArchivePath(strRarInRar, "rar", strArchivePath, strPathInRar);
else
URIUtils::CreateArchivePath(strRarInRar, "zip", strArchivePath, strPathInRar);
ScanArchiveForSubtitles(strRarInRar,strMovieFileNameNoExt,vecSubtitles);
}
// done checking if this is a rar-in-rar
// check that the found filename matches the movie filename
int fnl = strMovieFileNameNoExt.size();
if (fnl && !URIUtils::GetFileName(strPathInRar).Left(fnl).Equals(strMovieFileNameNoExt))
continue;
int iPos=0;
while (sub_exts[iPos])
{
if (strExt.CompareNoCase(sub_exts[iPos]) == 0)
{
CStdString strSourceUrl;
if (URIUtils::GetExtension(strArchivePath).Equals(".rar"))
URIUtils::CreateArchivePath(strSourceUrl, "rar", strArchivePath, strPathInRar);
else
strSourceUrl = strPathInRar;
CLog::Log(LOGINFO, "%s: found subtitle file %s\n", __FUNCTION__, strSourceUrl.c_str() );
vecSubtitles.push_back( strSourceUrl );
nSubtitlesAdded++;
}
iPos++;
}
}
return nSubtitlesAdded;
}
/*! \brief in a vector of subtitles finds the corresponding .sub file for a given .idx file
*/
bool CUtil::FindVobSubPair( const std::vector<CStdString>& vecSubtitles, const CStdString& strIdxPath, CStdString& strSubPath )
{
if (URIUtils::GetExtension(strIdxPath) == ".idx")
{
CStdString strIdxFile;
CStdString strIdxDirectory;
URIUtils::Split(strIdxPath, strIdxDirectory, strIdxFile);
for (unsigned int j=0; j < vecSubtitles.size(); j++)
{
CStdString strSubFile;
CStdString strSubDirectory;
URIUtils::Split(vecSubtitles[j], strSubDirectory, strSubFile);
if (URIUtils::IsInArchive(vecSubtitles[j]))
CURL::Decode(strSubDirectory);
if (URIUtils::GetExtension(strSubFile) == ".sub" &&
(URIUtils::ReplaceExtension(strIdxFile,"").Equals(URIUtils::ReplaceExtension(strSubFile,"")) ||
strSubDirectory.Mid(6, strSubDirectory.length()-11).Equals(URIUtils::ReplaceExtension(strIdxPath,""))))
{
strSubPath = vecSubtitles[j];
return true;
}
}
}
return false;
}
/*! \brief checks if in the vector of subtitles the given .sub file has a corresponding idx and hence is a vobsub file
*/
bool CUtil::IsVobSub( const std::vector<CStdString>& vecSubtitles, const CStdString& strSubPath )
{
if (URIUtils::GetExtension(strSubPath) == ".sub")
{
CStdString strSubFile;
CStdString strSubDirectory;
URIUtils::Split(strSubPath, strSubDirectory, strSubFile);
if (URIUtils::IsInArchive(strSubPath))
CURL::Decode(strSubDirectory);
for (unsigned int j=0; j < vecSubtitles.size(); j++)
{
CStdString strIdxFile;
CStdString strIdxDirectory;
URIUtils::Split(vecSubtitles[j], strIdxDirectory, strIdxFile);
if (URIUtils::GetExtension(strIdxFile) == ".idx" &&
(URIUtils::ReplaceExtension(strIdxFile,"").Equals(URIUtils::ReplaceExtension(strSubFile,"")) ||
strSubDirectory.Mid(6, strSubDirectory.length()-11).Equals(URIUtils::ReplaceExtension(vecSubtitles[j],""))))
return true;
}
}
return false;
}
bool CUtil::CanBindPrivileged()
{
#ifdef _LINUX
if (geteuid() == 0)
return true; //root user can always bind to privileged ports
#ifdef HAVE_LIBCAP
//check if CAP_NET_BIND_SERVICE is enabled, this allows non-root users to bind to privileged ports
bool canbind = false;
cap_t capabilities = cap_get_proc();
if (capabilities)
{
cap_flag_value_t value;
if (cap_get_flag(capabilities, CAP_NET_BIND_SERVICE, CAP_EFFECTIVE, &value) == 0)
canbind = value;
cap_free(capabilities);
}
return canbind;
#else //HAVE_LIBCAP
return false;
#endif //HAVE_LIBCAP
#else //_LINUX
return true;
#endif //_LINUX
}
Jump to Line
Something went wrong with that request. Please try again.