Files
Rocky5 921f9fe9f8 Clean Copy
If ever adding the XBMC4Xbox source to GitHub use "git add -f -A" or it wont add all the files.
2024-01-08 19:01:29 +00:00

428 lines
12 KiB
C++

/*
* Copyright (C) 2005-2013 Team XBMC
* http://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 "utils/log.h"
#include "RarManager.h"
#include "lib/UnrarXLib/rar.hpp"
#include "Util.h"
#include "utils/URIUtils.h"
#include "utils/SingleLock.h"
#include "GUIWindowManager.h"
#include "dialogs/GUIDialogYesNo.h"
#include "FileSystem/Directory.h"
#include "FileSystem/SpecialProtocol.h"
#include "settings/AdvancedSettings.h"
#include "FileItem.h"
#include "utils/CharsetConverter.h"
#include <set>
#define EXTRACTION_WARN_SIZE 50*1024*1024
using namespace std;
using namespace XFILE;
CRarManager g_RarManager;
CFileInfo::CFileInfo()
{
m_strCachedPath.Empty();
m_bAutoDel = true;
m_iUsed = 0;
m_iIsSeekable = -1;
}
CFileInfo::~CFileInfo()
{
}
/////////////////////////////////////////////////
CRarManager::CRarManager()
{
}
CRarManager::~CRarManager()
{
ClearCache(true);
}
bool CRarManager::CacheRarredFile(CStdString& strPathInCache, const CStdString& strRarPath, const CStdString& strPathInRar, BYTE bOptions, const CStdString& strDir, const int64_t iSize)
{
CSingleLock lock(m_CritSection);
//If file is listed in the cache, then use listed copy or cleanup before overwriting.
bool bOverwrite = (bOptions & EXFILE_OVERWRITE) != 0;
map<CStdString, pair<ArchiveList_struct*,vector<CFileInfo> > >::iterator j = m_ExFiles.find( strRarPath );
CFileInfo* pFile=NULL;
if( j != m_ExFiles.end() )
{
pFile = GetFileInRar(strRarPath,strPathInRar);
if (pFile)
{
if (pFile->m_bIsCanceled())
return false;
if( CFile::Exists( pFile->m_strCachedPath) )
{
if( !bOverwrite )
{
strPathInCache = pFile->m_strCachedPath;
pFile->m_iUsed++;
return true;
}
CFile::Delete(pFile->m_strCachedPath);
pFile->m_iUsed++;
}
}
}
int iRes = 0;
#if 0 // temporary workaround. disable dialogs as they cause deadlocks since we cannot render
// from spawned threads and dvdplayer stalls the app thread during startup
//Extract archived file, using existing local copy or overwriting if wanted...
if (iSize > EXTRACTION_WARN_SIZE)
{
CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
if (pDialog)
{
pDialog->SetHeading(120);
pDialog->SetLine(0, 645);
pDialog->SetLine(1, URIUtils::GetFileName(strPathInRar));
pDialog->SetLine(2, "");
pDialog->DoModal();
if (!pDialog->IsConfirmed())
iRes = 2; // pretend to be canceled
}
}
#endif
if (CheckFreeSpace(strDir) < iSize && iRes != 2)
{
ClearCache();
if (CheckFreeSpace(strDir) < iSize)
{
CFileItemList items;
CDirectory::GetDirectory(g_advancedSettings.m_cachePath,items);
items.Sort(SORT_METHOD_SIZE, SORT_ORDER_DESC);
while (items.Size() && CheckFreeSpace(strDir) < iSize)
{
CStdString strPath = items[0]->GetPath();
if (!items[0]->m_bIsFolder)
if (!CFile::Delete(items[0]->GetPath()))
break;
items.Remove(0);
}
if (!items.Size())
return false;
}
}
CStdString strPath = strPathInRar;
strPath.Replace('/', '\\');
//g_charsetConverter.unknownToUTF8(strPath);
int64_t iOffset = -1;
if (iRes != 2)
{
if (pFile)
{
if (pFile->m_iOffset != -1)
iOffset = pFile->m_iOffset;
}
if (iOffset == -1) // grab from list
{
for( ArchiveList_struct* pIterator = j->second.first; pIterator ; pIterator ? pIterator = pIterator->next : NULL)
{
CStdString strName;
/* convert to utf8 */
if( pIterator->item.NameW && wcslen(pIterator->item.NameW) > 0)
g_charsetConverter.wToUTF8(pIterator->item.NameW, strName);
else
g_charsetConverter.unknownToUTF8(pIterator->item.Name, strName);
if (strName.Equals(strPath))
{
iOffset = pIterator->item.iOffset;
break;
}
}
}
bool bShowProgress=false;
if (iSize > 1024*1024 || iSize == -2) // 1MB
bShowProgress=true;
CStdString strDir2(strDir);
URIUtils::RemoveSlashAtEnd(strDir2);
iRes = urarlib_get(const_cast<char*>(strRarPath.c_str()), const_cast<char*>(strDir2.c_str()),
const_cast<char*>(strPath.c_str()),NULL,&iOffset,bShowProgress);
}
if (iRes == 0)
{
CLog::Log(LOGERROR,"failed to extract file: %s",strPathInRar.c_str());
return false;
}
if(!pFile)
{
CFileInfo fileInfo;
fileInfo.m_strPathInRar = strPathInRar;
if (j == m_ExFiles.end())
{
ArchiveList_struct* pArchiveList;
if(ListArchive(strRarPath,pArchiveList))
{
m_ExFiles.insert(make_pair(strRarPath,make_pair(pArchiveList,vector<CFileInfo>())));
j = m_ExFiles.find(strRarPath);
}
else
return false;
}
j->second.second.push_back(fileInfo);
pFile = &(j->second.second[j->second.second.size()-1]);
pFile->m_iUsed = 1;
}
URIUtils::AddFileToFolder(strDir,URIUtils::GetFileName(strPathInRar),pFile->m_strCachedPath); // GetFileName
CUtil::GetFatXQualifiedPath(pFile->m_strCachedPath);
pFile->m_bAutoDel = (bOptions & EXFILE_AUTODELETE) != 0;
pFile->m_iOffset = iOffset;
strPathInCache = pFile->m_strCachedPath;
if (iRes == 2) //canceled
{
pFile->watch.StartZero();
CFile::Delete(pFile->m_strCachedPath);
return false;
}
return true;
}
// NB: The rar manager expects paths in rars to be terminated with a "\".
bool CRarManager::GetFilesInRar(CFileItemList& vecpItems, const CStdString& strRarPath,
bool bMask, const CStdString& strPathInRar)
{
CSingleLock lock(m_CritSection);
ArchiveList_struct* pFileList = NULL;
map<CStdString,pair<ArchiveList_struct*,vector<CFileInfo> > >::iterator it = m_ExFiles.find(strRarPath);
if (it == m_ExFiles.end())
{
if( urarlib_list((char*) strRarPath.c_str(), &pFileList, NULL) )
m_ExFiles.insert(make_pair(strRarPath,make_pair(pFileList,vector<CFileInfo>())));
else
{
if( pFileList ) urarlib_freelist(pFileList);
return false;
}
}
else
pFileList = it->second.first;
CFileItemPtr pFileItem;
vector<CStdString> vec;
set<CStdString> dirSet;
CUtil::Tokenize(strPathInRar,vec,"/");
unsigned int iDepth = vec.size();
ArchiveList_struct* pIterator;
CStdString strCompare = strPathInRar;
if (!URIUtils::HasSlashAtEnd(strCompare) && !strCompare.IsEmpty())
strCompare += '/';
for( pIterator = pFileList; pIterator ; pIterator ? pIterator = pIterator->next : NULL)
{
CStdString strDirDelimiter = (pIterator->item.HostOS==3 ? "/":"\\"); // win32 or unix paths?
CStdString strName;
/* convert to utf8 */
if( pIterator->item.NameW && wcslen(pIterator->item.NameW) > 0)
g_charsetConverter.wToUTF8(pIterator->item.NameW, strName);
else
g_charsetConverter.unknownToUTF8(pIterator->item.Name, strName);
/* replace back slashes into forward slashes */
/* this could get us into troubles, file could two different files, one with / and one with \ */
strName.Replace('\\', '/');
if (bMask)
{
if (!strstr(strName.c_str(),strCompare.c_str()))
continue;
vec.clear();
CUtil::Tokenize(strName,vec,"/");
if (vec.size() < iDepth)
continue;
}
unsigned int iMask = (pIterator->item.HostOS==3 ? 0x0040000:16); // win32 or unix attribs?
if (((pIterator->item.FileAttr & iMask) == iMask) || (vec.size() > iDepth+1 && bMask)) // we have a directory
{
if (!bMask) continue;
if (vec.size() == iDepth)
continue; // remove root of listing
if (dirSet.find(vec[iDepth]) == dirSet.end())
{
dirSet.insert(vec[iDepth]);
pFileItem.reset(new CFileItem(vec[iDepth]));
pFileItem->SetPath(vec[iDepth] + '/');
pFileItem->m_bIsFolder = true;
pFileItem->m_idepth = pIterator->item.Method;
pFileItem->m_iDriveType = pIterator->item.HostOS;
//pFileItem->m_lEndOffset = long(pIterator->item.iOffset);
}
}
else
{
if (vec.size() == iDepth+1 || !bMask)
{
if (vec.size() == 0)
pFileItem.reset(new CFileItem(strName));
else
pFileItem.reset(new CFileItem(vec[iDepth]));
pFileItem->SetPath(strName.c_str()+strPathInRar.size());
pFileItem->m_dwSize = pIterator->item.UnpSize;
pFileItem->m_idepth = pIterator->item.Method;
pFileItem->m_iDriveType = pIterator->item.HostOS;
//pFileItem->m_lEndOffset = long(pIterator->item.iOffset);
}
}
if (pFileItem)
vecpItems.Add(pFileItem);
pFileItem.reset();
}
return vecpItems.Size() > 0;
}
bool CRarManager::ListArchive(const CStdString& strRarPath, ArchiveList_struct* &pArchiveList)
{
return urarlib_list((char*) strRarPath.c_str(), &pArchiveList, NULL) == 1;
}
CFileInfo* CRarManager::GetFileInRar(const CStdString& strRarPath, const CStdString& strPathInRar)
{
map<CStdString,pair<ArchiveList_struct*,vector<CFileInfo> > >::iterator j = m_ExFiles.find(strRarPath);
if (j == m_ExFiles.end())
return NULL;
for (vector<CFileInfo>::iterator it2=j->second.second.begin(); it2 != j->second.second.end(); ++it2)
if (it2->m_strPathInRar == strPathInRar)
return &(*it2);
return NULL;
}
bool CRarManager::GetPathInCache(CStdString& strPathInCache, const CStdString& strRarPath, const CStdString& strPathInRar)
{
map<CStdString,pair<ArchiveList_struct*,vector<CFileInfo> > >::iterator j = m_ExFiles.find(strRarPath);
if (j == m_ExFiles.end())
return false;
for (vector<CFileInfo>::iterator it2=j->second.second.begin(); it2 != j->second.second.end(); ++it2)
if (it2->m_strPathInRar == strPathInRar)
return CFile::Exists(it2->m_strCachedPath);
return false;
}
bool CRarManager::IsFileInRar(bool& bResult, const CStdString& strRarPath, const CStdString& strPathInRar)
{
bResult = false;
CFileItemList ItemList;
if (!GetFilesInRar(ItemList,strRarPath,false))
return false;
int it;
for (it=0;it<ItemList.Size();++it)
{
if (strPathInRar.compare(ItemList[it]->GetPath()) == 0)
break;
}
if (it != ItemList.Size())
bResult = true;
return true;
}
void CRarManager::ClearCache(bool force)
{
CSingleLock lock(m_CritSection);
map<CStdString, pair<ArchiveList_struct*,vector<CFileInfo> > >::iterator j;
for (j = m_ExFiles.begin() ; j != m_ExFiles.end() ; j++)
{
for (vector<CFileInfo>::iterator it2 = j->second.second.begin(); it2 != j->second.second.end(); ++it2)
{
CFileInfo* pFile = &(*it2);
if (pFile->m_bAutoDel && (pFile->m_iUsed < 1 || force))
CFile::Delete( pFile->m_strCachedPath );
}
urarlib_freelist(j->second.first);
}
m_ExFiles.clear();
}
void CRarManager::ClearCachedFile(const CStdString& strRarPath, const CStdString& strPathInRar)
{
CSingleLock lock(m_CritSection);
map<CStdString,pair<ArchiveList_struct*,vector<CFileInfo> > >::iterator j = m_ExFiles.find(strRarPath);
if (j == m_ExFiles.end())
{
return; // no such subpath
}
for (vector<CFileInfo>::iterator it = j->second.second.begin(); it != j->second.second.end(); ++it)
{
if (it->m_strPathInRar == strPathInRar)
if (it->m_iUsed > 0)
{
it->m_iUsed--;
break;
}
}
}
void CRarManager::ExtractArchive(const CStdString& strArchive, const CStdString& strPath)
{
CStdString strPath2(strPath);
URIUtils::RemoveSlashAtEnd(strPath2);
if (!urarlib_get(const_cast<char*>(strArchive.c_str()), const_cast<char*>(strPath2.c_str()),NULL))
{
CLog::Log(LOGERROR,"rarmanager::extractarchive error while extracting %s", strArchive.c_str());
return;
}
}
int64_t CRarManager::CheckFreeSpace(const CStdString& strDrive)
{
ULARGE_INTEGER lTotalFreeBytes;
if (GetDiskFreeSpaceEx(_P(strDrive.c_str()), NULL, NULL, &lTotalFreeBytes))
return lTotalFreeBytes.QuadPart;
return 0;
}