Merge pull request #4314 from MartijnKaijser/beta1
[vuplus_xbmc] / xbmc / filesystem / File.cpp
1 /*
2  *      Copyright (c) 2002 Frodo
3  *      Portions Copyright (c) by the authors of ffmpeg and xvid
4  *      Copyright (C) 2002-2013 Team XBMC
5  *      http://xbmc.org
6  *
7  *  This Program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2, or (at your option)
10  *  any later version.
11  *
12  *  This Program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with XBMC; see the file COPYING.  If not, see
19  *  <http://www.gnu.org/licenses/>.
20  *
21  */
22
23 #include "File.h"
24 #include "IFile.h"
25 #include "FileFactory.h"
26 #include "Application.h"
27 #include "DirectoryCache.h"
28 #include "Directory.h"
29 #include "FileCache.h"
30 #include "utils/log.h"
31 #include "utils/URIUtils.h"
32 #include "utils/BitstreamStats.h"
33 #include "Util.h"
34 #include "URL.h"
35 #include "utils/StringUtils.h"
36
37 #include "commons/Exception.h"
38
39 using namespace XFILE;
40 using namespace std;
41
42 //////////////////////////////////////////////////////////////////////
43 // Construction/Destruction
44 //////////////////////////////////////////////////////////////////////
45 #ifndef __GNUC__
46 #pragma warning (disable:4244)
47 #endif
48
49 //*********************************************************************************************
50 CFile::CFile()
51 {
52   m_pFile = NULL;
53   m_pBuffer = NULL;
54   m_flags = 0;
55   m_bitStreamStats = NULL;
56 }
57
58 //*********************************************************************************************
59 CFile::~CFile()
60 {
61   Close();
62   if (m_pFile)
63     SAFE_DELETE(m_pFile);
64   if (m_pBuffer)
65     SAFE_DELETE(m_pBuffer);
66   if (m_bitStreamStats)
67     SAFE_DELETE(m_bitStreamStats);
68 }
69
70 //*********************************************************************************************
71 #ifdef TARGET_WINDOWS
72 #define MALLOC_EXCP_MSG "auto_buffer: malloc failed!"
73 #else
74 #define MALLOC_EXCP_MSG
75 #endif
76
77
78 auto_buffer::auto_buffer(size_t size) : p(NULL), s(0)
79 {
80   if (!size)
81     return;
82
83   p = malloc(size);
84   if (!p)
85     throw std::bad_alloc(MALLOC_EXCP_MSG);
86   s = size;
87 }
88
89 auto_buffer::~auto_buffer()
90 {
91   clear();
92 }
93
94 auto_buffer& auto_buffer::allocate(size_t size)
95 {
96   clear();
97   return resize(size);
98 }
99
100 auto_buffer& auto_buffer::resize(size_t newSize)
101 {
102   void* newPtr = realloc(p, newSize);
103   if (!newPtr && newSize)
104     throw std::bad_alloc(MALLOC_EXCP_MSG);
105   p = newPtr;
106   s = newSize;
107   return *this;
108 }
109
110 auto_buffer& auto_buffer::clear(void)
111 {
112   free(p);
113   p = NULL;
114   s = 0;
115   return *this;
116 }
117
118 auto_buffer& auto_buffer::attach(void* pointer, size_t size)
119 {
120   clear();
121   if ((pointer && size) || (!pointer && !size))
122   {
123     p = pointer;
124     s = size;
125   }
126   return *this;
127 }
128
129 void* auto_buffer::detach(void)
130 {
131   void* returnPtr = p;
132   p = NULL;
133   s = 0;
134   return returnPtr;
135 }
136
137
138 // This *looks* like a copy function, therefor the name "Cache" is misleading
139 bool CFile::Cache(const CStdString& strFileName, const CStdString& strDest, XFILE::IFileCallback* pCallback, void* pContext)
140 {
141   CFile file;
142
143   if (strFileName.empty() || strDest.empty())
144     return false;
145
146   // special case for zips - ignore caching
147   CURL url(strFileName);
148   if (URIUtils::IsInZIP(strFileName) || URIUtils::IsInAPK(strFileName))
149     url.SetOptions("?cache=no");
150   if (file.Open(url.Get(), READ_TRUNCATED))
151   {
152
153     CFile newFile;
154     if (URIUtils::IsHD(strDest)) // create possible missing dirs
155     {
156       vector<std::string> tokens;
157       CStdString strDirectory = URIUtils::GetDirectory(strDest);
158       URIUtils::RemoveSlashAtEnd(strDirectory);  // for the test below
159       if (!(strDirectory.size() == 2 && strDirectory[1] == ':'))
160       {
161         CURL url(strDirectory);
162         std::string pathsep;
163 #ifndef TARGET_POSIX
164         pathsep = "\\";
165 #else
166         pathsep = "/";
167 #endif
168         StringUtils::Tokenize(url.GetFileName(),tokens,pathsep.c_str());
169         CStdString strCurrPath;
170         // Handle special
171         if (!url.GetProtocol().empty()) {
172           pathsep = "/";
173           strCurrPath += url.GetProtocol() + "://";
174         } // If the directory has a / at the beginning, don't forget it
175         else if (strDirectory[0] == pathsep[0])
176           strCurrPath += pathsep;
177         for (vector<std::string>::iterator iter=tokens.begin();iter!=tokens.end();++iter)
178         {
179           strCurrPath += *iter+pathsep;
180           CDirectory::Create(strCurrPath);
181         }
182       }
183     }
184     if (CFile::Exists(strDest))
185       CFile::Delete(strDest);
186     if (!newFile.OpenForWrite(strDest, true))  // overwrite always
187     {
188       file.Close();
189       return false;
190     }
191
192     int iBufferSize = 128 * 1024;
193
194     auto_buffer buffer(iBufferSize);
195     int iRead, iWrite;
196
197     UINT64 llFileSize = file.GetLength();
198     UINT64 llPos = 0;
199
200     CStopWatch timer;
201     timer.StartZero();
202     float start = 0.0f;
203     while (true)
204     {
205       g_application.ResetScreenSaver();
206
207       iRead = file.Read(buffer.get(), iBufferSize);
208       if (iRead == 0) break;
209       else if (iRead < 0)
210       {
211         CLog::Log(LOGERROR, "%s - Failed read from file %s", __FUNCTION__, strFileName.c_str());
212         llFileSize = (uint64_t)-1;
213         break;
214       }
215
216       /* write data and make sure we managed to write it all */
217       iWrite = 0;
218       while(iWrite < iRead)
219       {
220         int iWrite2 = newFile.Write(buffer.get()+iWrite, iRead-iWrite);
221         if(iWrite2 <=0)
222           break;
223         iWrite+=iWrite2;
224       }
225
226       if (iWrite != iRead)
227       {
228         CLog::Log(LOGERROR, "%s - Failed write to file %s", __FUNCTION__, strDest.c_str());
229         llFileSize = (uint64_t)-1;
230         break;
231       }
232
233       llPos += iRead;
234
235       // calculate the current and average speeds
236       float end = timer.GetElapsedSeconds();
237
238       if (pCallback && end - start > 0.5 && end)
239       {
240         start = end;
241
242         float averageSpeed = llPos / end;
243         int ipercent = 0;
244         if(llFileSize)
245           ipercent = 100 * llPos / llFileSize;
246
247         if(!pCallback->OnFileCallback(pContext, ipercent, averageSpeed))
248         {
249           CLog::Log(LOGERROR, "%s - User aborted copy", __FUNCTION__);
250           llFileSize = (uint64_t)-1;
251           break;
252         }
253       }
254     }
255
256     /* close both files */
257     newFile.Close();
258     file.Close();
259
260     /* verify that we managed to completed the file */
261     if (llFileSize && llPos != llFileSize)
262     {
263       CFile::Delete(strDest);
264       return false;
265     }
266     return true;
267   }
268   return false;
269 }
270
271 //*********************************************************************************************
272 bool CFile::Open(const CStdString& strFileName, const unsigned int flags)
273 {
274   m_flags = flags;
275   try
276   {
277     bool bPathInCache;
278     CURL url2(URIUtils::SubstitutePath(strFileName));
279     if (url2.GetProtocol() == "apk")
280       url2.SetOptions("");
281     if (url2.GetProtocol() == "zip")
282       url2.SetOptions("");
283     if (!g_directoryCache.FileExists(url2.Get(), bPathInCache) )
284     {
285       if (bPathInCache)
286         return false;
287     }
288
289     CURL url(URIUtils::SubstitutePath(strFileName));
290
291     if (!(m_flags & READ_NO_CACHE))
292     {
293       if (URIUtils::IsInternetStream(url, true) && !CUtil::IsPicture(strFileName) )
294         m_flags |= READ_CACHED;
295
296       if (m_flags & READ_CACHED)
297       {
298         // for internet stream, if it contains multiple stream, file cache need handle it specially.
299         m_pFile = new CFileCache(m_flags & READ_MULTI_STREAM);
300         return m_pFile->Open(url);
301       }
302     }
303
304     m_pFile = CFileFactory::CreateLoader(url);
305     if (!m_pFile)
306       return false;
307
308     try
309     {
310       if (!m_pFile->Open(url))
311       {
312         SAFE_DELETE(m_pFile);
313         return false;
314       }
315     }
316     catch (CRedirectException *pRedirectEx)
317     {
318       // the file implementation decided this item should use a different implementation.
319       // the exception will contain the new implementation.
320       CLog::Log(LOGDEBUG,"File::Open - redirecting implementation for %s", strFileName.c_str());
321       SAFE_DELETE(m_pFile);
322       if (pRedirectEx && pRedirectEx->m_pNewFileImp)
323       {
324         auto_ptr<CURL> pNewUrl(pRedirectEx->m_pNewUrl);
325         m_pFile = pRedirectEx->m_pNewFileImp;
326         delete pRedirectEx;
327         
328         if (pNewUrl.get())
329         {
330           if (!m_pFile->Open(*pNewUrl))
331           {
332             SAFE_DELETE(m_pFile);
333             return false;
334           }
335         }
336         else
337         {        
338           if (!m_pFile->Open(url))
339           {
340             SAFE_DELETE(m_pFile);
341             return false;
342           }
343         }
344       }
345     }
346     catch (...)
347     {
348       CLog::Log(LOGERROR, "File::Open - unknown exception when opening %s", strFileName.c_str());
349       SAFE_DELETE(m_pFile);
350       return false;
351     }
352
353     if (m_pFile->GetChunkSize() && !(m_flags & READ_CHUNKED))
354     {
355       m_pBuffer = new CFileStreamBuffer(0);
356       m_pBuffer->Attach(m_pFile);
357     }
358
359     if (m_flags & READ_BITRATE)
360     {
361       m_bitStreamStats = new BitstreamStats();
362       m_bitStreamStats->Start();
363     }
364
365     return true;
366   }
367   XBMCCOMMONS_HANDLE_UNCHECKED
368   catch(...)
369   {
370     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
371   }
372   CLog::Log(LOGERROR, "%s - Error opening %s", __FUNCTION__, strFileName.c_str());
373   return false;
374 }
375
376 bool CFile::OpenForWrite(const CStdString& strFileName, bool bOverWrite)
377 {
378   try
379   {
380     CStdString storedFileName = URIUtils::SubstitutePath(strFileName);
381     CURL url(storedFileName);
382
383     m_pFile = CFileFactory::CreateLoader(url);
384     if (m_pFile && m_pFile->OpenForWrite(url, bOverWrite))
385     {
386       // add this file to our directory cache (if it's stored)
387       g_directoryCache.AddFile(storedFileName);
388       return true;
389     }
390     return false;
391   }
392   XBMCCOMMONS_HANDLE_UNCHECKED
393   catch(...)
394   {
395     CLog::Log(LOGERROR, "%s - Unhandled exception opening %s", __FUNCTION__, strFileName.c_str());
396   }
397   CLog::Log(LOGERROR, "%s - Error opening %s", __FUNCTION__, strFileName.c_str());
398   return false;
399 }
400
401 bool CFile::Exists(const CStdString& strFileName, bool bUseCache /* = true */)
402 {
403   CURL url = URIUtils::SubstitutePath(strFileName);
404   
405   try
406   {
407     if (strFileName.empty())
408       return false;
409
410     if (bUseCache)
411     {
412       bool bPathInCache;
413       if (g_directoryCache.FileExists(url.Get(), bPathInCache))
414         return true;
415       if (bPathInCache)
416         return false;
417     }
418
419     auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
420     if (!pFile.get())
421       return false;
422
423     return pFile->Exists(url);
424   }
425   XBMCCOMMONS_HANDLE_UNCHECKED
426   catch (CRedirectException *pRedirectEx)
427   {
428     // the file implementation decided this item should use a different implementation.
429     // the exception will contain the new implementation and optional a redirected URL.
430     CLog::Log(LOGDEBUG,"File::Exists - redirecting implementation for %s", strFileName.c_str());
431     if (pRedirectEx && pRedirectEx->m_pNewFileImp)
432     {
433       auto_ptr<IFile> pImp(pRedirectEx->m_pNewFileImp);
434       auto_ptr<CURL> pNewUrl(pRedirectEx->m_pNewUrl);
435       delete pRedirectEx;
436
437       if (pImp.get())
438       {
439         if (pNewUrl.get())
440         {
441           if (bUseCache)
442           {
443             bool bPathInCache;
444             if (g_directoryCache.FileExists(pNewUrl->Get(), bPathInCache))
445               return true;
446             if (bPathInCache)
447               return false;
448           }
449           return pImp->Exists(*pNewUrl);
450         }
451         else
452           return pImp->Exists(url);
453       }
454     }
455   }
456   catch(...)
457   {
458     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
459   }
460   CLog::Log(LOGERROR, "%s - Error checking for %s", __FUNCTION__, strFileName.c_str());
461   return false;
462 }
463
464 int CFile::Stat(struct __stat64 *buffer)
465 {
466   if (!buffer)
467     return -1;
468
469   if (!m_pFile)
470   {
471     memset(buffer, 0, sizeof(struct __stat64));
472     errno = ENOENT;
473     return -1;
474   }
475
476   return m_pFile->Stat(buffer);
477 }
478
479 bool CFile::SkipNext()
480 {
481   if (m_pFile)
482     return m_pFile->SkipNext();
483   return false;
484 }
485
486 int CFile::Stat(const CStdString& strFileName, struct __stat64* buffer)
487 {
488   if (!buffer)
489     return -1;
490
491   CURL url;
492   
493   try
494   {
495     url = URIUtils::SubstitutePath(strFileName);
496     
497     auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
498     if (!pFile.get())
499       return -1;
500     return pFile->Stat(url, buffer);
501   }
502   XBMCCOMMONS_HANDLE_UNCHECKED
503   catch (CRedirectException *pRedirectEx)
504   {
505     // the file implementation decided this item should use a different implementation.
506     // the exception will contain the new implementation and optional a redirected URL.
507     CLog::Log(LOGDEBUG,"File::Stat - redirecting implementation for %s", strFileName.c_str());
508     if (pRedirectEx && pRedirectEx->m_pNewFileImp)
509     {
510       auto_ptr<IFile> pImp(pRedirectEx->m_pNewFileImp);
511       auto_ptr<CURL> pNewUrl(pRedirectEx->m_pNewUrl);
512       delete pRedirectEx;
513         
514       if (pNewUrl.get())
515       {
516         if (pImp.get() && !pImp->Stat(*pNewUrl, buffer))
517         {
518           return 0;
519         }
520       }
521       else     
522       {
523         if (pImp.get() && !pImp->Stat(url, buffer))
524         {
525           return 0;
526         }
527       }
528     }
529   }
530   catch(...)
531   {
532     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
533   }
534   CLog::Log(LOGERROR, "%s - Error statting %s", __FUNCTION__, strFileName.c_str());
535   return -1;
536 }
537
538 unsigned int CFile::Read(void *lpBuf, int64_t uiBufSize)
539 {
540   if (!m_pFile || !lpBuf)
541     return 0;
542
543   if(m_pBuffer)
544   {
545     if(m_flags & READ_TRUNCATED)
546     {
547       unsigned int nBytes = m_pBuffer->sgetn(
548         (char *)lpBuf, min<streamsize>((streamsize)uiBufSize,
549                                                   m_pBuffer->in_avail()));
550       if (m_bitStreamStats && nBytes>0)
551         m_bitStreamStats->AddSampleBytes(nBytes);
552       return nBytes;
553     }
554     else
555     {
556       unsigned int nBytes = m_pBuffer->sgetn((char*)lpBuf, uiBufSize);
557       if (m_bitStreamStats && nBytes>0)
558         m_bitStreamStats->AddSampleBytes(nBytes);
559       return nBytes;
560     }
561   }
562
563   try
564   {
565     if(m_flags & READ_TRUNCATED)
566     {
567       unsigned int nBytes = m_pFile->Read(lpBuf, uiBufSize);
568       if (m_bitStreamStats && nBytes>0)
569         m_bitStreamStats->AddSampleBytes(nBytes);
570       return nBytes;
571     }
572     else
573     {
574       unsigned int done = 0;
575       while((uiBufSize-done) > 0)
576       {
577         int curr = m_pFile->Read((char*)lpBuf+done, uiBufSize-done);
578         if(curr<=0)
579           break;
580
581         done+=curr;
582       }
583       if (m_bitStreamStats && done > 0)
584         m_bitStreamStats->AddSampleBytes(done);
585       return done;
586     }
587   }
588   XBMCCOMMONS_HANDLE_UNCHECKED
589   catch(...)
590   {
591     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
592   }
593   return 0;
594 }
595
596 //*********************************************************************************************
597 void CFile::Close()
598 {
599   try
600   {
601     if (m_pFile)
602       m_pFile->Close();
603
604     SAFE_DELETE(m_pBuffer);
605     SAFE_DELETE(m_pFile);
606   }
607   XBMCCOMMONS_HANDLE_UNCHECKED
608   catch(...)
609   {
610     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
611   }
612   return;
613 }
614
615 void CFile::Flush()
616 {
617   try
618   {
619     if (m_pFile)
620       m_pFile->Flush();
621   }
622   XBMCCOMMONS_HANDLE_UNCHECKED
623   catch(...)
624   {
625     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
626   }
627   return;
628 }
629
630 //*********************************************************************************************
631 int64_t CFile::Seek(int64_t iFilePosition, int iWhence)
632 {
633   if (!m_pFile)
634     return -1;
635
636   if (m_pBuffer)
637   {
638     if(iWhence == SEEK_CUR)
639       return m_pBuffer->pubseekoff(iFilePosition,ios_base::cur);
640     else if(iWhence == SEEK_END)
641       return m_pBuffer->pubseekoff(iFilePosition,ios_base::end);
642     else if(iWhence == SEEK_SET)
643       return m_pBuffer->pubseekoff(iFilePosition,ios_base::beg);
644   }
645
646   try
647   {
648     return m_pFile->Seek(iFilePosition, iWhence);
649   }
650   XBMCCOMMONS_HANDLE_UNCHECKED
651   catch(...)
652   {
653     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
654   }
655   return -1;
656 }
657
658 //*********************************************************************************************
659 int CFile::Truncate(int64_t iSize)
660 {
661   if (!m_pFile)
662     return -1;
663   
664   try
665   {
666     return m_pFile->Truncate(iSize);
667   }
668   XBMCCOMMONS_HANDLE_UNCHECKED
669   catch(...)
670   {
671     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
672   }
673   return -1;
674 }
675
676 //*********************************************************************************************
677 int64_t CFile::GetLength()
678 {
679   try
680   {
681     if (m_pFile)
682       return m_pFile->GetLength();
683     return 0;
684   }
685   XBMCCOMMONS_HANDLE_UNCHECKED
686   catch(...)
687   {
688     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
689   }
690   return 0;
691 }
692
693 //*********************************************************************************************
694 int64_t CFile::GetPosition() const
695 {
696   if (!m_pFile)
697     return -1;
698
699   if (m_pBuffer)
700     return m_pBuffer->pubseekoff(0, ios_base::cur);
701
702   try
703   {
704     return m_pFile->GetPosition();
705   }
706   XBMCCOMMONS_HANDLE_UNCHECKED
707   catch(...)
708   {
709     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
710   }
711   return -1;
712 }
713
714
715 //*********************************************************************************************
716 bool CFile::ReadString(char *szLine, int iLineLength)
717 {
718   if (!m_pFile || !szLine)
719     return false;
720
721   if (m_pBuffer)
722   {
723     typedef CFileStreamBuffer::traits_type traits;
724     CFileStreamBuffer::int_type aByte = m_pBuffer->sgetc();
725
726     if(aByte == traits::eof())
727       return false;
728
729     while(iLineLength>0)
730     {
731       aByte = m_pBuffer->sbumpc();
732
733       if(aByte == traits::eof())
734         break;
735
736       if(aByte == traits::to_int_type('\n'))
737       {
738         if(m_pBuffer->sgetc() == traits::to_int_type('\r'))
739           m_pBuffer->sbumpc();
740         break;
741       }
742
743       if(aByte == traits::to_int_type('\r'))
744       {
745         if(m_pBuffer->sgetc() == traits::to_int_type('\n'))
746           m_pBuffer->sbumpc();
747         break;
748       }
749
750       *szLine = traits::to_char_type(aByte);
751       szLine++;
752       iLineLength--;
753     }
754
755     // if we have no space for terminating character we failed
756     if(iLineLength==0)
757       return false;
758
759     *szLine = 0;
760
761     return true;
762   }
763
764   try
765   {
766     return m_pFile->ReadString(szLine, iLineLength);
767   }
768   XBMCCOMMONS_HANDLE_UNCHECKED
769   catch(...)
770   {
771     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
772   }
773   return false;
774 }
775
776 int CFile::Write(const void* lpBuf, int64_t uiBufSize)
777 {
778   if (!m_pFile || !lpBuf)
779     return -1;
780
781   try
782   {
783     return m_pFile->Write(lpBuf, uiBufSize);
784   }
785   XBMCCOMMONS_HANDLE_UNCHECKED
786   catch(...)
787   {
788     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
789   }
790   return -1;
791 }
792
793 bool CFile::Delete(const CStdString& strFileName)
794 {
795   try
796   {
797     CURL url(URIUtils::SubstitutePath(strFileName));
798
799     auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
800     if (!pFile.get())
801       return false;
802
803     if(pFile->Delete(url))
804     {
805       g_directoryCache.ClearFile(url.Get());
806       return true;
807     }
808   }
809   XBMCCOMMONS_HANDLE_UNCHECKED
810   catch(...)
811   {
812     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
813   }
814   if (Exists(strFileName))
815     CLog::Log(LOGERROR, "%s - Error deleting file %s", __FUNCTION__, strFileName.c_str());
816   return false;
817 }
818
819 bool CFile::Rename(const CStdString& strFileName, const CStdString& strNewFileName)
820 {
821   try
822   {
823     CURL url(URIUtils::SubstitutePath(strFileName));
824     CURL urlnew(URIUtils::SubstitutePath(strNewFileName));
825
826     auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
827     if (!pFile.get())
828       return false;
829
830     if(pFile->Rename(url, urlnew))
831     {
832       g_directoryCache.ClearFile(url.Get());
833       g_directoryCache.AddFile(urlnew.Get());
834       return true;
835     }
836   }
837   XBMCCOMMONS_HANDLE_UNCHECKED
838   catch(...)
839   {
840     CLog::Log(LOGERROR, "%s - Unhandled exception ", __FUNCTION__);
841   }
842   CLog::Log(LOGERROR, "%s - Error renaming file %s", __FUNCTION__, strFileName.c_str());
843   return false;
844 }
845
846 bool CFile::SetHidden(const CStdString& fileName, bool hidden)
847 {
848   try
849   {
850     CURL url(URIUtils::SubstitutePath(fileName));
851
852     auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
853     if (!pFile.get())
854       return false;
855
856     return pFile->SetHidden(url, hidden);
857   }
858   catch(...)
859   {
860     CLog::Log(LOGERROR, "%s(%s) - Unhandled exception", __FUNCTION__, fileName.c_str());
861   }
862   return false;
863 }
864
865 int CFile::IoControl(EIoControl request, void* param)
866 {
867   int result = -1;
868   if (m_pFile == NULL)
869     return -1;
870   result = m_pFile->IoControl(request, param);
871
872   if(result == -1 && request == IOCTRL_SEEK_POSSIBLE)
873   {
874     if(m_pFile->GetLength() >= 0 && m_pFile->Seek(0, SEEK_CUR) >= 0)
875       return 1;
876     else
877       return 0;
878   }
879
880   return result;
881 }
882
883 int CFile::GetChunkSize()
884 {
885   if (m_pFile)
886     return m_pFile->GetChunkSize();
887   return 0;
888 }
889
890 std::string CFile::GetContentMimeType(void)
891 {
892   if (!m_pFile)
893     return "";
894   return m_pFile->GetContent();
895 }
896
897 std::string CFile::GetContentCharset(void)
898 {
899   if (!m_pFile)
900     return "";
901   return m_pFile->GetContentCharset();
902 }
903
904
905 unsigned int CFile::LoadFile(const std::string &filename, auto_buffer& outputBuffer)
906 {
907   static const unsigned int max_file_size = 0x7FFFFFFF;
908   static const unsigned int min_chunk_size = 64 * 1024U;
909   static const unsigned int max_chunk_size = 2048 * 1024U;
910
911   outputBuffer.clear();
912   if (filename.empty())
913     return 0;
914
915   if (!Open(filename, READ_TRUNCATED))
916     return 0;
917
918   /*
919   GetLength() will typically return values that fall into three cases:
920   1. The real filesize. This is the typical case.
921   2. Zero. This is the case for some http:// streams for example.
922   3. Some value smaller than the real filesize. This is the case for an expanding file.
923
924   In order to handle all three cases, we read the file in chunks, relying on Read()
925   returning 0 at EOF.  To minimize (re)allocation of the buffer, the chunksize in
926   cases 1 and 3 is set to one byte larger than the value returned by GetLength().
927   The chunksize in case 2 is set to the lowest value larger than min_chunk_size aligned
928   to GetChunkSize().
929
930   We fill the buffer entirely before reallocation.  Thus, reallocation never occurs in case 1
931   as the buffer is larger than the file, so we hit EOF before we hit the end of buffer.
932
933   To minimize reallocation, we double the chunksize each read while chunksize is lower
934   than max_chunk_size.
935   */
936   int64_t filesize = GetLength();
937   if (filesize > max_file_size)
938     return 0; /* file is too large for this function */
939
940   unsigned int chunksize = (filesize > 0) ? (unsigned int)(filesize + 1) : GetChunkSize(GetChunkSize(), min_chunk_size);
941   unsigned int total_read = 0;
942   while (true)
943   {
944     if (total_read == outputBuffer.size())
945     { // (re)alloc
946       if (outputBuffer.size() >= max_file_size)
947       {
948         outputBuffer.clear();
949         return 0;
950       }
951       outputBuffer.resize(outputBuffer.size() + chunksize);
952       if (chunksize < max_chunk_size)
953         chunksize *= 2;
954     }
955     unsigned int read = Read(outputBuffer.get() + total_read, outputBuffer.size() - total_read);
956     total_read += read;
957     if (!read)
958       break;
959   }
960
961   outputBuffer.resize(total_read);
962
963   return total_read;
964 }
965
966 //*********************************************************************************************
967 //*************** Stream IO for CFile objects *************************************************
968 //*********************************************************************************************
969 CFileStreamBuffer::~CFileStreamBuffer()
970 {
971   sync();
972   Detach();
973 }
974
975 CFileStreamBuffer::CFileStreamBuffer(int backsize)
976   : streambuf()
977   , m_file(NULL)
978   , m_buffer(NULL)
979   , m_backsize(backsize)
980   , m_frontsize(0)
981 {
982 }
983
984 void CFileStreamBuffer::Attach(IFile *file)
985 {
986   m_file = file;
987
988   m_frontsize = CFile::GetChunkSize(m_file->GetChunkSize(), 64*1024);
989
990   m_buffer = new char[m_frontsize+m_backsize];
991   setg(0,0,0);
992   setp(0,0);
993 }
994
995 void CFileStreamBuffer::Detach()
996 {
997   setg(0,0,0);
998   setp(0,0);
999   delete[] m_buffer;
1000   m_buffer = NULL;
1001 }
1002
1003 CFileStreamBuffer::int_type CFileStreamBuffer::underflow()
1004 {
1005   if(gptr() < egptr())
1006     return traits_type::to_int_type(*gptr());
1007
1008   if(!m_file)
1009     return traits_type::eof();
1010
1011   size_t backsize = 0;
1012   if(m_backsize)
1013   {
1014     backsize = (size_t)min<ptrdiff_t>((ptrdiff_t)m_backsize, egptr()-eback());
1015     memmove(m_buffer, egptr()-backsize, backsize);
1016   }
1017
1018   unsigned int size = m_file->Read(m_buffer+backsize, m_frontsize);
1019
1020   if(size == 0)
1021     return traits_type::eof();
1022
1023   setg(m_buffer, m_buffer+backsize, m_buffer+backsize+size);
1024   return traits_type::to_int_type(*gptr());
1025 }
1026
1027 CFileStreamBuffer::pos_type CFileStreamBuffer::seekoff(
1028   off_type offset,
1029   ios_base::seekdir way,
1030   ios_base::openmode mode)
1031 {
1032   // calculate relative offset
1033   off_type pos  = m_file->GetPosition() - (egptr() - gptr());
1034   off_type offset2;
1035   if(way == ios_base::cur)
1036     offset2 = offset;
1037   else if(way == ios_base::beg)
1038     offset2 = offset - pos;
1039   else if(way == ios_base::end)
1040     offset2 = offset + m_file->GetLength() - pos;
1041   else
1042     return streampos(-1);
1043
1044   // a non seek shouldn't modify our buffer
1045   if(offset2 == 0)
1046     return pos;
1047
1048   // try to seek within buffer
1049   if(gptr()+offset2 >= eback() && gptr()+offset2 < egptr())
1050   {
1051     gbump(offset2);
1052     return pos + offset2;
1053   }
1054
1055   // reset our buffer pointer, will
1056   // start buffering on next read
1057   setg(0,0,0);
1058   setp(0,0);
1059
1060   int64_t position = -1;
1061   if(way == ios_base::cur)
1062     position = m_file->Seek(offset, SEEK_CUR);
1063   else if(way == ios_base::end)
1064     position = m_file->Seek(offset, SEEK_END);
1065   else
1066     position = m_file->Seek(offset, SEEK_SET);
1067
1068   if(position<0)
1069     return streampos(-1);
1070
1071   return position;
1072 }
1073
1074 CFileStreamBuffer::pos_type CFileStreamBuffer::seekpos(
1075   pos_type pos,
1076   ios_base::openmode mode)
1077 {
1078   return seekoff(pos, ios_base::beg, mode);
1079 }
1080
1081 streamsize CFileStreamBuffer::showmanyc()
1082 {
1083   underflow();
1084   return egptr() - gptr();
1085 }
1086
1087 CFileStream::CFileStream(int backsize /*= 0*/) :
1088     istream(&m_buffer),
1089     m_buffer(backsize),
1090     m_file(NULL)
1091 {
1092 }
1093
1094 CFileStream::~CFileStream()
1095 {
1096   Close();
1097 }
1098
1099
1100 bool CFileStream::Open(const CURL& filename)
1101 {
1102   Close();
1103
1104   // NOTE: This is currently not translated - reason is that all entry points into CFileStream::Open currently
1105   //       go from the CStdString version below.  We may have to change this in future, but I prefer not decoding
1106   //       the URL and re-encoding, or applying the translation twice.
1107   m_file = CFileFactory::CreateLoader(filename);
1108   if(m_file && m_file->Open(filename))
1109   {
1110     m_buffer.Attach(m_file);
1111     return true;
1112   }
1113
1114   setstate(failbit);
1115   return false;
1116 }
1117
1118 int64_t CFileStream::GetLength()
1119 {
1120   return m_file->GetLength();
1121 }
1122
1123 void CFileStream::Close()
1124 {
1125   if(!m_file)
1126     return;
1127
1128   m_buffer.Detach();
1129   SAFE_DELETE(m_file);
1130 }
1131
1132 bool CFileStream::Open(const CStdString& filename)
1133 {
1134   return Open(CURL(URIUtils::SubstitutePath(filename)));
1135 }