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

1014 lines
22 KiB
C++

/*
* XBMC Media Center
* Copyright (c) 2002 Frodo
* Portions Copyright (c) by the authors of ffmpeg and xvid
*
* 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 of the License, 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "utils/log.h"
#include "File.h"
#include "FileFactory.h"
#include "Application.h"
#include "Util.h"
#include "utils/URIUtils.h"
#include "DirectoryCache.h"
#include "FileCache.h"
#include "utils/Win32Exception.h"
#include "URL.h"
using namespace XFILE;
using namespace std;
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#ifndef __GNUC__
#pragma warning (disable:4244)
#endif
//*********************************************************************************************
CFile::CFile()
{
m_pFile = NULL;
m_pBuffer = NULL;
m_flags = 0;
m_bitStreamStats = NULL;
}
//*********************************************************************************************
CFile::~CFile()
{
if (m_pFile)
SAFE_DELETE(m_pFile);
if (m_pBuffer)
SAFE_DELETE(m_pBuffer);
if (m_bitStreamStats)
SAFE_DELETE(m_bitStreamStats);
}
//*********************************************************************************************
class CAutoBuffer
{
char* p;
public:
explicit CAutoBuffer(size_t s) { p = (char*)malloc(s); }
~CAutoBuffer() { free(p); }
char* get() { return p; }
};
// This *looks* like a copy function, therefor the name "Cache" is misleading
bool CFile::Cache(const CStdString& strFileName, const CStdString& strDest, XFILE::IFileCallback* pCallback, void* pContext)
{
CFile file;
// special case for zips - ignore caching
CURL url(strFileName);
if (URIUtils::IsInZIP(strFileName))
url.SetOptions("?cache=no");
if (file.Open(url.Get(), READ_TRUNCATED))
{
CFile newFile;
if (URIUtils::IsHD(strDest)) // create possible missing dirs
{
vector<CStdString> tokens;
CStdString strDirectory;
URIUtils::GetDirectory(strDest,strDirectory);
URIUtils::RemoveSlashAtEnd(strDirectory); // for the test below
if (!(strDirectory.size() == 2 && strDirectory[1] == ':'))
{
CURL url(strDirectory);
CStdString pathsep;
#ifndef _LINUX
pathsep = "\\";
#else
pathsep = "/";
#endif
CUtil::Tokenize(url.GetFileName(),tokens,pathsep.c_str());
CStdString strCurrPath;
// Handle special
if (!url.GetProtocol().IsEmpty()) {
pathsep = "/";
strCurrPath += url.GetProtocol() + "://";
} // If the directory has a / at the beginning, don't forget it
else if (strDirectory[0] == pathsep[0])
strCurrPath += pathsep;
for (vector<CStdString>::iterator iter=tokens.begin();iter!=tokens.end();++iter)
{
strCurrPath += *iter+pathsep;
CDirectory::Create(strCurrPath);
}
}
}
if (CFile::Exists(strDest))
CFile::Delete(strDest);
if (!newFile.OpenForWrite(strDest, true)) // overwrite always
{
file.Close();
return false;
}
// 128k is optimal for xbox
int iBufferSize = 128 * 1024;
CAutoBuffer buffer(iBufferSize);
int iRead, iWrite;
UINT64 llFileSize = file.GetLength();
UINT64 llPos = 0;
CStopWatch timer;
timer.StartZero();
float start = 0.0f;
while (true)
{
g_application.ResetScreenSaver();
iRead = file.Read(buffer.get(), iBufferSize);
if (iRead == 0) break;
else if (iRead < 0)
{
CLog::Log(LOGERROR, "%s - Failed read from file %s", __FUNCTION__, strFileName.c_str());
llFileSize = (uint64_t)-1;
break;
}
/* write data and make sure we managed to write it all */
iWrite = 0;
while(iWrite < iRead)
{
int iWrite2 = newFile.Write(buffer.get()+iWrite, iRead-iWrite);
if(iWrite2 <=0)
break;
iWrite+=iWrite2;
}
if (iWrite != iRead)
{
CLog::Log(LOGERROR, "%s - Failed write to file %s", __FUNCTION__, strDest.c_str());
llFileSize = (uint64_t)-1;
break;
}
llPos += iRead;
// calculate the current and average speeds
float end = timer.GetElapsedSeconds();
if (pCallback && end - start > 0.5 && end)
{
start = end;
float averageSpeed = llPos / end;
int ipercent = 0;
if(llFileSize)
ipercent = 100 * llPos / llFileSize;
if(!pCallback->OnFileCallback(pContext, ipercent, averageSpeed))
{
CLog::Log(LOGERROR, "%s - User aborted copy", __FUNCTION__);
llFileSize = (uint64_t)-1;
break;
}
}
}
/* close both files */
newFile.Close();
file.Close();
/* verify that we managed to completed the file */
if (llFileSize && llPos != llFileSize)
{
CFile::Delete(strDest);
return false;
}
return true;
}
return false;
}
//*********************************************************************************************
bool CFile::Open(const CStdString& strFileName, unsigned int flags)
{
m_flags = flags;
try
{
bool bPathInCache;
CURL url2(strFileName);
if (url2.GetProtocol() == "zip")
url2.SetOptions("");
if (!g_directoryCache.FileExists(url2.Get(), bPathInCache) )
{
if (bPathInCache)
return false;
}
CURL url(strFileName);
if ( (flags & READ_NO_CACHE) == 0 && URIUtils::IsInternetStream(url, true) && !CUtil::IsPicture(strFileName) )
m_flags |= READ_CACHED;
if (m_flags & READ_CACHED)
{
m_pFile = new CFileCache();
return m_pFile->Open(url);
}
m_pFile = CFileFactory::CreateLoader(url);
if (!m_pFile)
return false;
try
{
if (!m_pFile->Open(url))
{
SAFE_DELETE(m_pFile);
return false;
}
}
catch (CRedirectException *pRedirectEx)
{
// the file implementation decided this item should use a different implementation.
// the exception will contain the new implementation.
CLog::Log(LOGDEBUG,"File::Open - redirecting implementation for %s", strFileName.c_str());
SAFE_DELETE(m_pFile);
if (pRedirectEx && pRedirectEx->m_pNewFileImp)
{
auto_ptr<CURL> pNewUrl(pRedirectEx->m_pNewUrl);
m_pFile = pRedirectEx->m_pNewFileImp;
delete pRedirectEx;
if (pNewUrl.get())
{
if (!m_pFile->Open(*pNewUrl))
{
SAFE_DELETE(m_pFile);
return false;
}
}
else
{
if (!m_pFile->Open(url))
{
SAFE_DELETE(m_pFile);
return false;
}
}
}
}
catch (...)
{
CLog::Log(LOGERROR, "File::Open - unknown exception when opening %s", strFileName.c_str());
SAFE_DELETE(m_pFile);
return false;
}
if (m_pFile->GetChunkSize() && !(m_flags & READ_CHUNKED))
{
m_pBuffer = new CFileStreamBuffer(0);
m_pBuffer->Attach(m_pFile);
}
if (m_flags & READ_BITRATE)
{
m_bitStreamStats = new BitstreamStats();
m_bitStreamStats->Start();
}
return true;
}
#ifndef _LINUX
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
}
#endif
catch(...)
{
CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
}
CLog::Log(LOGERROR, "%s - Error opening %s", __FUNCTION__, strFileName.c_str());
return false;
}
bool CFile::OpenForWrite(const CStdString& strFileName, bool bOverWrite)
{
try
{
CURL url(strFileName);
m_pFile = CFileFactory::CreateLoader(url);
if (m_pFile && m_pFile->OpenForWrite(url, bOverWrite))
{
// add this file to our directory cache (if it's stored)
g_directoryCache.AddFile(strFileName);
return true;
}
return false;
}
#ifndef _LINUX
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
}
#endif
catch(...)
{
CLog::Log(LOGERROR, "%s - Unhandled exception opening %s", __FUNCTION__, strFileName.c_str());
}
CLog::Log(LOGERROR, "%s - Error opening %s", __FUNCTION__, strFileName.c_str());
return false;
}
bool CFile::Exists(const CStdString& strFileName, bool bUseCache /* = true */)
{
CURL url;
try
{
if (strFileName.IsEmpty())
return false;
if (bUseCache)
{
bool bPathInCache;
if (g_directoryCache.FileExists(strFileName, bPathInCache) )
return true;
if (bPathInCache)
return false;
}
url = URIUtils::SubstitutePath(strFileName);
auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
if (!pFile.get())
return false;
return pFile->Exists(url);
}
#ifndef _LINUX
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
}
#endif
catch (CRedirectException *pRedirectEx)
{
// the file implementation decided this item should use a different implementation.
// the exception will contain the new implementation and optional a redirected URL.
CLog::Log(LOGDEBUG,"File::Exists - redirecting implementation for %s", strFileName.c_str());
if (pRedirectEx && pRedirectEx->m_pNewFileImp)
{
auto_ptr<IFile> pImp(pRedirectEx->m_pNewFileImp);
auto_ptr<CURL> pNewUrl(pRedirectEx->m_pNewUrl);
delete pRedirectEx;
if (pNewUrl.get())
{
if (pImp.get() && !pImp->Exists(*pNewUrl))
{
return false;
}
}
else
{
if (pImp.get() && !pImp->Exists(url))
{
return false;
}
}
}
}
catch(...)
{
CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
}
CLog::Log(LOGERROR, "%s - Error checking for %s", __FUNCTION__, strFileName.c_str());
return false;
}
int CFile::Stat(struct __stat64 *buffer)
{
return m_pFile->Stat(buffer);
}
int CFile::Stat(const CStdString& strFileName, struct __stat64* buffer)
{
CURL url;
try
{
url = URIUtils::SubstitutePath(strFileName);
auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
if (!pFile.get())
return false;
return pFile->Stat(url, buffer);
}
#ifndef _LINUX
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
}
#endif
catch (CRedirectException *pRedirectEx)
{
// the file implementation decided this item should use a different implementation.
// the exception will contain the new implementation and optional a redirected URL.
CLog::Log(LOGDEBUG,"File::Stat - redirecting implementation for %s", strFileName.c_str());
if (pRedirectEx && pRedirectEx->m_pNewFileImp)
{
auto_ptr<IFile> pImp(pRedirectEx->m_pNewFileImp);
auto_ptr<CURL> pNewUrl(pRedirectEx->m_pNewUrl);
delete pRedirectEx;
if (pNewUrl.get())
{
if (pImp.get() && !pImp->Stat(*pNewUrl, buffer))
{
return false;
}
}
else
{
if (pImp.get() && !pImp->Stat(url, buffer))
{
return false;
}
}
}
}
catch(...)
{
CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
}
CLog::Log(LOGERROR, "%s - Error statting %s", __FUNCTION__, strFileName.c_str());
return -1;
}
unsigned int CFile::Read(void *lpBuf, int64_t uiBufSize)
{
if (!m_pFile)
return 0;
if(m_pBuffer)
{
if(m_flags & READ_TRUNCATED)
{
unsigned int nBytes = m_pBuffer->sgetn(
(char *)lpBuf, min<streamsize>((streamsize)uiBufSize,
m_pBuffer->in_avail()));
if (m_bitStreamStats && nBytes>0)
m_bitStreamStats->AddSampleBytes(nBytes);
return nBytes;
}
else
{
unsigned int nBytes = m_pBuffer->sgetn((char*)lpBuf, uiBufSize);
if (m_bitStreamStats && nBytes>0)
m_bitStreamStats->AddSampleBytes(nBytes);
return nBytes;
}
}
try
{
if(m_flags & READ_TRUNCATED)
{
unsigned int nBytes = m_pFile->Read(lpBuf, uiBufSize);
if (m_bitStreamStats && nBytes>0)
m_bitStreamStats->AddSampleBytes(nBytes);
return nBytes;
}
else
{
unsigned int done = 0;
while((uiBufSize-done) > 0)
{
int curr = m_pFile->Read((char*)lpBuf+done, uiBufSize-done);
if(curr<=0)
break;
done+=curr;
}
if (m_bitStreamStats && done > 0)
m_bitStreamStats->AddSampleBytes(done);
return done;
}
}
#ifndef _LINUX
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
}
#endif
catch(...)
{
CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
}
return 0;
}
//*********************************************************************************************
void CFile::Close()
{
try
{
if (m_pFile)
m_pFile->Close();
SAFE_DELETE(m_pBuffer);
SAFE_DELETE(m_pFile);
}
#ifndef _LINUX
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
}
#endif
catch(...)
{
CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
}
return;
}
void CFile::Flush()
{
try
{
if (m_pFile)
m_pFile->Flush();
}
#ifndef _LINUX
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
}
#endif
catch(...)
{
CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
}
return;
}
//*********************************************************************************************
int64_t CFile::Seek(int64_t iFilePosition, int iWhence)
{
if (!m_pFile)
return -1;
if (m_pBuffer)
{
if(iWhence == SEEK_CUR)
return m_pBuffer->pubseekoff(iFilePosition,ios_base::cur);
else if(iWhence == SEEK_END)
return m_pBuffer->pubseekoff(iFilePosition,ios_base::end);
else if(iWhence == SEEK_SET)
return m_pBuffer->pubseekoff(iFilePosition,ios_base::beg);
}
try
{
return m_pFile->Seek(iFilePosition, iWhence);
}
#ifndef _LINUX
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
}
#endif
catch(...)
{
CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
}
return -1;
}
//*********************************************************************************************
int64_t CFile::GetLength()
{
try
{
if (m_pFile)
return m_pFile->GetLength();
return 0;
}
#ifndef _LINUX
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
}
#endif
catch(...)
{
CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
}
return 0;
}
//*********************************************************************************************
int64_t CFile::GetPosition()
{
if (!m_pFile)
return -1;
if (m_pBuffer)
return m_pBuffer->pubseekoff(0, ios_base::cur);
try
{
return m_pFile->GetPosition();
}
#ifndef _LINUX
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
}
#endif
catch(...)
{
CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
}
return -1;
}
//*********************************************************************************************
bool CFile::ReadString(char *szLine, int iLineLength)
{
if (!m_pFile)
return false;
if (m_pBuffer)
{
typedef CFileStreamBuffer::traits_type traits;
CFileStreamBuffer::int_type aByte = m_pBuffer->sgetc();
if(aByte == traits::eof())
return false;
while(iLineLength>0)
{
aByte = m_pBuffer->sbumpc();
if(aByte == traits::eof())
break;
if(aByte == traits::to_int_type('\n'))
{
if(m_pBuffer->sgetc() == traits::to_int_type('\r'))
m_pBuffer->sbumpc();
break;
}
if(aByte == traits::to_int_type('\r'))
{
if(m_pBuffer->sgetc() == traits::to_int_type('\n'))
m_pBuffer->sbumpc();
break;
}
*szLine = traits::to_char_type(aByte);
szLine++;
iLineLength--;
}
// if we have no space for terminating character we failed
if(iLineLength==0)
return false;
*szLine = 0;
return true;
}
try
{
return m_pFile->ReadString(szLine, iLineLength);
}
#ifndef _LINUX
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
}
#endif
catch(...)
{
CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
}
return false;
}
int CFile::Write(const void* lpBuf, int64_t uiBufSize)
{
try
{
return m_pFile->Write(lpBuf, uiBufSize);
}
#ifndef _LINUX
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
}
#endif
catch(...)
{
CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
}
return -1;
}
bool CFile::Delete(const CStdString& strFileName)
{
try
{
CURL url(strFileName);
auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
if (!pFile.get())
return false;
if(pFile->Delete(url))
{
g_directoryCache.ClearFile(strFileName);
return true;
}
}
#ifndef _LINUX
catch (const access_violation &e)
{
e.writelog(__FUNCTION__);
}
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
}
#endif
catch(...)
{
CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
}
if (Exists(strFileName))
CLog::Log(LOGERROR, "%s - Error deleting file %s", __FUNCTION__, strFileName.c_str());
return false;
}
bool CFile::Rename(const CStdString& strFileName, const CStdString& strNewFileName)
{
try
{
CURL url(strFileName);
CURL urlnew(strNewFileName);
auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
if (!pFile.get())
return false;
if(pFile->Rename(url, urlnew))
{
g_directoryCache.ClearFile(strFileName);
g_directoryCache.ClearFile(strNewFileName);
return true;
}
}
#ifndef _LINUX
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
}
#endif
catch(...)
{
CLog::Log(LOGERROR, "%s - Unhandled exception ", __FUNCTION__);
}
CLog::Log(LOGERROR, "%s - Error renaming file %s", __FUNCTION__, strFileName.c_str());
return false;
}
int CFile::IoControl(EIoControl request, void* param)
{
int result = -1;
if (m_pFile == NULL)
return -1;
result = m_pFile->IoControl(request, param);
if(result == -1 && request == IOCTRL_SEEK_POSSIBLE)
{
if(m_pFile->GetLength() >= 0 && m_pFile->Seek(0, SEEK_CUR) >= 0)
return 1;
else
return 0;
}
return result;
}
//*********************************************************************************************
//*************** Stream IO for CFile objects *************************************************
//*********************************************************************************************
CFileStreamBuffer::~CFileStreamBuffer()
{
sync();
Detach();
}
CFileStreamBuffer::CFileStreamBuffer(int backsize)
: streambuf()
, m_file(NULL)
, m_buffer(NULL)
, m_backsize(backsize)
, m_frontsize(0)
{
}
void CFileStreamBuffer::Attach(IFile *file)
{
m_file = file;
m_frontsize = CFile::GetChunkSize(m_file->GetChunkSize(), 64*1024);
m_buffer = new char[m_frontsize+m_backsize];
setg(0,0,0);
setp(0,0);
}
void CFileStreamBuffer::Detach()
{
setg(0,0,0);
setp(0,0);
delete[] m_buffer;
m_buffer = NULL;
}
CFileStreamBuffer::int_type CFileStreamBuffer::underflow()
{
if(gptr() < egptr())
return traits_type::to_int_type(*gptr());
if(!m_file)
return traits_type::eof();
size_t backsize = 0;
if(m_backsize)
{
backsize = (size_t)min<ptrdiff_t>((ptrdiff_t)m_backsize, egptr()-eback());
memmove(m_buffer, egptr()-backsize, backsize);
}
unsigned int size = 0;
#ifndef _LINUX
try
{
#endif
size = m_file->Read(m_buffer+backsize, m_frontsize);
#ifndef _LINUX
}
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
}
#endif
if(size == 0)
return traits_type::eof();
setg(m_buffer, m_buffer+backsize, m_buffer+backsize+size);
return traits_type::to_int_type(*gptr());
}
CFileStreamBuffer::pos_type CFileStreamBuffer::seekoff(
off_type offset,
ios_base::seekdir way,
ios_base::openmode mode)
{
// calculate relative offset
off_type pos = m_file->GetPosition() - (egptr() - gptr());
off_type offset2;
if(way == ios_base::cur)
offset2 = offset;
else if(way == ios_base::beg)
offset2 = offset - pos;
else if(way == ios_base::end)
offset2 = offset + m_file->GetLength() - pos;
else
return streampos(-1);
// a non seek shouldn't modify our buffer
if(offset2 == 0)
return pos;
// try to seek within buffer
if(gptr()+offset2 >= eback() && gptr()+offset2 < egptr())
{
gbump(offset2);
return pos + offset2;
}
// reset our buffer pointer, will
// start buffering on next read
setg(0,0,0);
setp(0,0);
int64_t position = -1;
#ifndef _LINUX
try
{
#endif
if(way == ios_base::cur)
position = m_file->Seek(offset, SEEK_CUR);
else if(way == ios_base::end)
position = m_file->Seek(offset, SEEK_END);
else
position = m_file->Seek(offset, SEEK_SET);
#ifndef _LINUX
}
catch (const win32_exception &e)
{
e.writelog(__FUNCTION__);
return streampos(-1);
}
#endif
if(position<0)
return streampos(-1);
return position;
}
CFileStreamBuffer::pos_type CFileStreamBuffer::seekpos(
pos_type pos,
ios_base::openmode mode)
{
return seekoff(pos, ios_base::beg, mode);
}
streamsize CFileStreamBuffer::showmanyc()
{
underflow();
return egptr() - gptr();
}
CFileStream::CFileStream(int backsize /*= 0*/) :
istream(&m_buffer),
m_buffer(backsize),
m_file(NULL)
{
}
CFileStream::~CFileStream()
{
Close();
}
bool CFileStream::Open(const CURL& filename)
{
Close();
m_file = CFileFactory::CreateLoader(filename);
if(m_file && m_file->Open(filename))
{
m_buffer.Attach(m_file);
return true;
}
setstate(failbit);
return false;
}
int64_t CFileStream::GetLength()
{
return m_file->GetLength();
}
void CFileStream::Close()
{
if(!m_file)
return;
m_buffer.Detach();
SAFE_DELETE(m_file);
}
bool CFileStream::Open(const CStdString& filename)
{
return Open(CURL(filename));
}