3 * Copyright (c) 2002 Frodo
4 * Portions Copyright (c) by the authors of ffmpeg and xvid
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "FileFactory.h"
23 #include "Application.h"
24 #include "DirectoryCache.h"
25 #include "Directory.h"
26 #include "FileCache.h"
27 #include "utils/log.h"
28 #include "utils/URIUtils.h"
29 #include "utils/BitstreamStats.h"
33 #include "utils/Win32Exception.h"
37 using namespace XFILE;
40 //////////////////////////////////////////////////////////////////////
41 // Construction/Destruction
42 //////////////////////////////////////////////////////////////////////
44 #pragma warning (disable:4244)
47 //*********************************************************************************************
53 m_bitStreamStats = NULL;
56 //*********************************************************************************************
62 SAFE_DELETE(m_pBuffer);
64 SAFE_DELETE(m_bitStreamStats);
67 //*********************************************************************************************
73 explicit CAutoBuffer(size_t s) { p = (char*)malloc(s); }
74 ~CAutoBuffer() { free(p); }
75 char* get() { return p; }
78 // This *looks* like a copy function, therefor the name "Cache" is misleading
79 bool CFile::Cache(const CStdString& strFileName, const CStdString& strDest, XFILE::IFileCallback* pCallback, void* pContext)
83 // special case for zips - ignore caching
84 CURL url(strFileName);
85 if (URIUtils::IsInZIP(strFileName))
86 url.SetOptions("?cache=no");
87 if (file.Open(url.Get(), READ_TRUNCATED))
91 if (URIUtils::IsHD(strDest)) // create possible missing dirs
93 vector<CStdString> tokens;
94 CStdString strDirectory;
95 URIUtils::GetDirectory(strDest,strDirectory);
96 URIUtils::RemoveSlashAtEnd(strDirectory); // for the test below
97 if (!(strDirectory.size() == 2 && strDirectory[1] == ':'))
99 CURL url(strDirectory);
106 CUtil::Tokenize(url.GetFileName(),tokens,pathsep.c_str());
107 CStdString strCurrPath;
109 if (!url.GetProtocol().IsEmpty()) {
111 strCurrPath += url.GetProtocol() + "://";
112 } // If the directory has a / at the beginning, don't forget it
113 else if (strDirectory[0] == pathsep[0])
114 strCurrPath += pathsep;
115 for (vector<CStdString>::iterator iter=tokens.begin();iter!=tokens.end();++iter)
117 strCurrPath += *iter+pathsep;
118 CDirectory::Create(strCurrPath);
122 if (CFile::Exists(strDest))
123 CFile::Delete(strDest);
124 if (!newFile.OpenForWrite(strDest, true)) // overwrite always
130 // 128k is optimal for xbox
131 int iBufferSize = 128 * 1024;
133 CAutoBuffer buffer(iBufferSize);
136 UINT64 llFileSize = file.GetLength();
144 g_application.ResetScreenSaver();
146 iRead = file.Read(buffer.get(), iBufferSize);
147 if (iRead == 0) break;
150 CLog::Log(LOGERROR, "%s - Failed read from file %s", __FUNCTION__, strFileName.c_str());
151 llFileSize = (uint64_t)-1;
155 /* write data and make sure we managed to write it all */
157 while(iWrite < iRead)
159 int iWrite2 = newFile.Write(buffer.get()+iWrite, iRead-iWrite);
167 CLog::Log(LOGERROR, "%s - Failed write to file %s", __FUNCTION__, strDest.c_str());
168 llFileSize = (uint64_t)-1;
174 // calculate the current and average speeds
175 float end = timer.GetElapsedSeconds();
177 if (pCallback && end - start > 0.5 && end)
181 float averageSpeed = llPos / end;
184 ipercent = 100 * llPos / llFileSize;
186 if(!pCallback->OnFileCallback(pContext, ipercent, averageSpeed))
188 CLog::Log(LOGERROR, "%s - User aborted copy", __FUNCTION__);
189 llFileSize = (uint64_t)-1;
195 /* close both files */
199 /* verify that we managed to completed the file */
200 if (llFileSize && llPos != llFileSize)
202 CFile::Delete(strDest);
210 //*********************************************************************************************
211 bool CFile::Open(const CStdString& strFileName, unsigned int flags)
217 CURL url2(strFileName);
218 if (url2.GetProtocol() == "zip")
220 if (!g_directoryCache.FileExists(url2.Get(), bPathInCache) )
226 CURL url(URIUtils::SubstitutePath(strFileName));
227 if ( (flags & READ_NO_CACHE) == 0 && URIUtils::IsInternetStream(url) && !CUtil::IsPicture(strFileName) )
228 m_flags |= READ_CACHED;
230 if (m_flags & READ_CACHED)
232 m_pFile = new CFileCache();
233 return m_pFile->Open(url);
236 m_pFile = CFileFactory::CreateLoader(url);
242 if (!m_pFile->Open(url))
244 SAFE_DELETE(m_pFile);
248 catch (CRedirectException *pRedirectEx)
250 // the file implementation decided this item should use a different implementation.
251 // the exception will contain the new implementation.
253 CLog::Log(LOGDEBUG,"File::Open - redirecting implementation for %s", strFileName.c_str());
254 SAFE_DELETE(m_pFile);
255 if (pRedirectEx && pRedirectEx->m_pNewFileImp)
257 m_pFile = pRedirectEx->m_pNewFileImp;
260 if (!m_pFile->Open(url))
262 SAFE_DELETE(m_pFile);
269 CLog::Log(LOGERROR, "File::Open - unknown exception when opening %s", strFileName.c_str());
270 SAFE_DELETE(m_pFile);
274 if (m_pFile->GetChunkSize() && !(m_flags & READ_CHUNKED))
276 m_pBuffer = new CFileStreamBuffer(0);
277 m_pBuffer->Attach(m_pFile);
280 if (m_flags & READ_BITRATE)
282 m_bitStreamStats = new BitstreamStats();
283 m_bitStreamStats->Start();
289 catch (const win32_exception &e)
291 e.writelog(__FUNCTION__);
296 CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
298 CLog::Log(LOGERROR, "%s - Error opening %s", __FUNCTION__, strFileName.c_str());
302 bool CFile::OpenForWrite(const CStdString& strFileName, bool bOverWrite)
306 CURL url(URIUtils::SubstitutePath(strFileName));
308 m_pFile = CFileFactory::CreateLoader(url);
309 if (m_pFile && m_pFile->OpenForWrite(url, bOverWrite))
311 // add this file to our directory cache (if it's stored)
312 g_directoryCache.AddFile(strFileName);
318 catch (const win32_exception &e)
320 e.writelog(__FUNCTION__);
325 CLog::Log(LOGERROR, "%s - Unhandled exception opening %s", __FUNCTION__, strFileName.c_str());
327 CLog::Log(LOGERROR, "%s - Error opening %s", __FUNCTION__, strFileName.c_str());
331 bool CFile::Exists(const CStdString& strFileName, bool bUseCache /* = true */)
335 if (strFileName.IsEmpty())
341 if (g_directoryCache.FileExists(strFileName, bPathInCache) )
347 CURL url(URIUtils::SubstitutePath(strFileName));
349 auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
353 return pFile->Exists(url);
356 catch (const win32_exception &e)
358 e.writelog(__FUNCTION__);
363 CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
365 CLog::Log(LOGERROR, "%s - Error checking for %s", __FUNCTION__, strFileName.c_str());
369 int CFile::Stat(struct __stat64 *buffer)
371 return m_pFile->Stat(buffer);
374 int CFile::Stat(const CStdString& strFileName, struct __stat64* buffer)
378 CURL url(URIUtils::SubstitutePath(strFileName));
380 auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
384 return pFile->Stat(url, buffer);
387 catch (const win32_exception &e)
389 e.writelog(__FUNCTION__);
394 CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
396 CLog::Log(LOGERROR, "%s - Error statting %s", __FUNCTION__, strFileName.c_str());
400 unsigned int CFile::Read(void *lpBuf, int64_t uiBufSize)
407 if(m_flags & READ_TRUNCATED)
409 unsigned int nBytes = m_pBuffer->sgetn(
410 (char *)lpBuf, min<streamsize>((streamsize)uiBufSize,
411 m_pBuffer->in_avail()));
412 if (m_bitStreamStats && nBytes>0)
413 m_bitStreamStats->AddSampleBytes(nBytes);
418 unsigned int nBytes = m_pBuffer->sgetn((char*)lpBuf, uiBufSize);
419 if (m_bitStreamStats && nBytes>0)
420 m_bitStreamStats->AddSampleBytes(nBytes);
427 if(m_flags & READ_TRUNCATED)
429 unsigned int nBytes = m_pFile->Read(lpBuf, uiBufSize);
430 if (m_bitStreamStats && nBytes>0)
431 m_bitStreamStats->AddSampleBytes(nBytes);
436 unsigned int done = 0;
437 while((uiBufSize-done) > 0)
439 int curr = m_pFile->Read((char*)lpBuf+done, uiBufSize-done);
445 if (m_bitStreamStats && done > 0)
446 m_bitStreamStats->AddSampleBytes(done);
451 catch (const win32_exception &e)
453 e.writelog(__FUNCTION__);
458 CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
463 //*********************************************************************************************
468 SAFE_DELETE(m_pBuffer);
469 SAFE_DELETE(m_pFile);
472 catch (const win32_exception &e)
474 e.writelog(__FUNCTION__);
479 CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
492 catch (const win32_exception &e)
494 e.writelog(__FUNCTION__);
499 CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
504 //*********************************************************************************************
505 int64_t CFile::Seek(int64_t iFilePosition, int iWhence)
512 if(iWhence == SEEK_CUR)
513 return m_pBuffer->pubseekoff(iFilePosition,ios_base::cur);
514 else if(iWhence == SEEK_END)
515 return m_pBuffer->pubseekoff(iFilePosition,ios_base::end);
516 else if(iWhence == SEEK_SET)
517 return m_pBuffer->pubseekoff(iFilePosition,ios_base::beg);
522 return m_pFile->Seek(iFilePosition, iWhence);
525 catch (const win32_exception &e)
527 e.writelog(__FUNCTION__);
532 CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
537 //*********************************************************************************************
538 int64_t CFile::GetLength()
543 return m_pFile->GetLength();
547 catch (const win32_exception &e)
549 e.writelog(__FUNCTION__);
554 CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
559 //*********************************************************************************************
560 int64_t CFile::GetPosition()
566 return m_pBuffer->pubseekoff(0, ios_base::cur);
570 return m_pFile->GetPosition();
573 catch (const win32_exception &e)
575 e.writelog(__FUNCTION__);
580 CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
586 //*********************************************************************************************
587 bool CFile::ReadString(char *szLine, int iLineLength)
594 typedef CFileStreamBuffer::traits_type traits;
595 CFileStreamBuffer::int_type aByte = m_pBuffer->sgetc();
597 if(aByte == traits::eof())
602 aByte = m_pBuffer->sbumpc();
604 if(aByte == traits::eof())
607 if(aByte == traits::to_int_type('\n'))
609 if(m_pBuffer->sgetc() == traits::to_int_type('\r'))
614 if(aByte == traits::to_int_type('\r'))
616 if(m_pBuffer->sgetc() == traits::to_int_type('\n'))
621 *szLine = traits::to_char_type(aByte);
626 // if we have no space for terminating character we failed
637 return m_pFile->ReadString(szLine, iLineLength);
640 catch (const win32_exception &e)
642 e.writelog(__FUNCTION__);
647 CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
652 int CFile::Write(const void* lpBuf, int64_t uiBufSize)
656 return m_pFile->Write(lpBuf, uiBufSize);
659 catch (const win32_exception &e)
661 e.writelog(__FUNCTION__);
666 CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
671 bool CFile::Delete(const CStdString& strFileName)
675 CURL url(URIUtils::SubstitutePath(strFileName));
677 auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
681 if(pFile->Delete(url))
683 g_directoryCache.ClearFile(strFileName);
688 catch (const access_violation &e)
690 e.writelog(__FUNCTION__);
692 catch (const win32_exception &e)
694 e.writelog(__FUNCTION__);
699 CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
701 if (Exists(strFileName))
702 CLog::Log(LOGERROR, "%s - Error deleting file %s", __FUNCTION__, strFileName.c_str());
706 bool CFile::Rename(const CStdString& strFileName, const CStdString& strNewFileName)
710 CURL url(URIUtils::SubstitutePath(strFileName));
711 CURL urlnew(URIUtils::SubstitutePath(strNewFileName));
713 auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
717 if(pFile->Rename(url, urlnew))
719 g_directoryCache.ClearFile(strFileName);
720 g_directoryCache.ClearFile(strNewFileName);
725 catch (const win32_exception &e)
727 e.writelog(__FUNCTION__);
732 CLog::Log(LOGERROR, "%s - Unhandled exception ", __FUNCTION__);
734 CLog::Log(LOGERROR, "%s - Error renaming file %s", __FUNCTION__, strFileName.c_str());
738 bool CFile::SetHidden(const CStdString& fileName, bool hidden)
742 CURL url(URIUtils::SubstitutePath(fileName));
744 auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
748 return pFile->SetHidden(url, hidden);
752 CLog::Log(LOGERROR, "%s(%s) - Unhandled exception", __FUNCTION__, fileName.c_str());
757 int CFile::IoControl(EIoControl request, void* param)
761 result = m_pFile->IoControl(request, param);
763 if(result == -1 && request == IOCTRL_SEEK_POSSIBLE)
765 if(m_pFile->GetLength() >= 0 && m_pFile->Seek(0, SEEK_CUR) >= 0)
773 //*********************************************************************************************
774 //*************** Stream IO for CFile objects *************************************************
775 //*********************************************************************************************
776 CFileStreamBuffer::~CFileStreamBuffer()
782 CFileStreamBuffer::CFileStreamBuffer(int backsize)
786 , m_backsize(backsize)
791 void CFileStreamBuffer::Attach(IFile *file)
795 m_frontsize = CFile::GetChunkSize(m_file->GetChunkSize(), 64*1024);
797 m_buffer = new char[m_frontsize+m_backsize];
802 void CFileStreamBuffer::Detach()
810 CFileStreamBuffer::int_type CFileStreamBuffer::underflow()
813 return traits_type::to_int_type(*gptr());
816 return traits_type::eof();
821 backsize = (size_t)min<ptrdiff_t>((ptrdiff_t)m_backsize, egptr()-eback());
822 memmove(m_buffer, egptr()-backsize, backsize);
825 unsigned int size = 0;
830 size = m_file->Read(m_buffer+backsize, m_frontsize);
833 catch (const win32_exception &e)
835 e.writelog(__FUNCTION__);
840 return traits_type::eof();
842 setg(m_buffer, m_buffer+backsize, m_buffer+backsize+size);
843 return traits_type::to_int_type(*gptr());
846 CFileStreamBuffer::pos_type CFileStreamBuffer::seekoff(
848 ios_base::seekdir way,
849 ios_base::openmode mode)
851 // calculate relative offset
852 off_type pos = m_file->GetPosition() - (egptr() - gptr());
854 if(way == ios_base::cur)
856 else if(way == ios_base::beg)
857 offset2 = offset - pos;
858 else if(way == ios_base::end)
859 offset2 = offset + m_file->GetLength() - pos;
861 return streampos(-1);
863 // a non seek shouldn't modify our buffer
867 // try to seek within buffer
868 if(gptr()+offset2 >= eback() && gptr()+offset2 < egptr())
871 return pos + offset2;
874 // reset our buffer pointer, will
875 // start buffering on next read
879 int64_t position = -1;
884 if(way == ios_base::cur)
885 position = m_file->Seek(offset, SEEK_CUR);
886 else if(way == ios_base::end)
887 position = m_file->Seek(offset, SEEK_END);
889 position = m_file->Seek(offset, SEEK_SET);
892 catch (const win32_exception &e)
894 e.writelog(__FUNCTION__);
895 return streampos(-1);
900 return streampos(-1);
905 CFileStreamBuffer::pos_type CFileStreamBuffer::seekpos(
907 ios_base::openmode mode)
909 return seekoff(pos, ios_base::beg, mode);
912 streamsize CFileStreamBuffer::showmanyc()
915 return egptr() - gptr();
918 CFileStream::CFileStream(int backsize /*= 0*/) :
925 CFileStream::~CFileStream()
931 bool CFileStream::Open(const CURL& filename)
935 // NOTE: This is currently not translated - reason is that all entry points into CFileStream::Open currently
936 // go from the CStdString version below. We may have to change this in future, but I prefer not decoding
937 // the URL and re-encoding, or applying the translation twice.
938 m_file = CFileFactory::CreateLoader(filename);
939 if(m_file && m_file->Open(filename))
941 m_buffer.Attach(m_file);
949 int64_t CFileStream::GetLength()
951 return m_file->GetLength();
954 void CFileStream::Close()
963 bool CFileStream::Open(const CStdString& filename)
965 return Open(CURL(URIUtils::SubstitutePath(filename)));
969 char* CFileStream::ReadFile()
974 int64_t length = m_file->GetLength();
975 char *str = new char[length+1];
976 m_file->Read(str, length);