only call IPlayerCallback::OnPlayBackSpeedChanged if the speed has actually changed
[vuplus_xbmc] / xbmc / filesystem / File.cpp
1 /*
2 * XBMC Media Center
3 * Copyright (c) 2002 Frodo
4 * Portions Copyright (c) by the authors of ffmpeg and xvid
5 *
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.
10 *
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.
15 *
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.
19 */
20
21 #include "File.h"
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"
30 #include "Util.h"
31
32 #ifndef _LINUX
33 #include "utils/Win32Exception.h"
34 #endif
35 #include "URL.h"
36
37 using namespace XFILE;
38 using namespace std;
39
40 //////////////////////////////////////////////////////////////////////
41 // Construction/Destruction
42 //////////////////////////////////////////////////////////////////////
43 #ifndef __GNUC__
44 #pragma warning (disable:4244)
45 #endif
46
47 //*********************************************************************************************
48 CFile::CFile()
49 {
50   m_pFile = NULL;
51   m_pBuffer = NULL;
52   m_flags = 0;
53   m_bitStreamStats = NULL;
54 }
55
56 //*********************************************************************************************
57 CFile::~CFile()
58 {
59   if (m_pFile)
60     SAFE_DELETE(m_pFile);
61   if (m_pBuffer)
62     SAFE_DELETE(m_pBuffer);
63   if (m_bitStreamStats)
64     SAFE_DELETE(m_bitStreamStats);
65 }
66
67 //*********************************************************************************************
68
69 class CAutoBuffer
70 {
71   char* p;
72 public:
73   explicit CAutoBuffer(size_t s) { p = (char*)malloc(s); }
74   ~CAutoBuffer() { free(p); }
75   char* get() { return p; }
76 };
77
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)
80 {
81   CFile file;
82
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))
88   {
89
90     CFile newFile;
91     if (URIUtils::IsHD(strDest)) // create possible missing dirs
92     {
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] == ':'))
98       {
99         CURL url(strDirectory);
100         CStdString pathsep;
101 #ifndef _LINUX
102         pathsep = "\\";
103 #else
104         pathsep = "/";
105 #endif
106         CUtil::Tokenize(url.GetFileName(),tokens,pathsep.c_str());
107         CStdString strCurrPath;
108         // Handle special
109         if (!url.GetProtocol().IsEmpty()) {
110           pathsep = "/";
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)
116         {
117           strCurrPath += *iter+pathsep;
118           CDirectory::Create(strCurrPath);
119         }
120       }
121     }
122     if (CFile::Exists(strDest))
123       CFile::Delete(strDest);
124     if (!newFile.OpenForWrite(strDest, true))  // overwrite always
125     {
126       file.Close();
127       return false;
128     }
129
130     // 128k is optimal for xbox
131     int iBufferSize = 128 * 1024;
132
133     CAutoBuffer buffer(iBufferSize);
134     int iRead, iWrite;
135
136     UINT64 llFileSize = file.GetLength();
137     UINT64 llPos = 0;
138
139     CStopWatch timer;
140     timer.StartZero();
141     float start = 0.0f;
142     while (true)
143     {
144       g_application.ResetScreenSaver();
145
146       iRead = file.Read(buffer.get(), iBufferSize);
147       if (iRead == 0) break;
148       else if (iRead < 0)
149       {
150         CLog::Log(LOGERROR, "%s - Failed read from file %s", __FUNCTION__, strFileName.c_str());
151         llFileSize = (uint64_t)-1;
152         break;
153       }
154
155       /* write data and make sure we managed to write it all */
156       iWrite = 0;
157       while(iWrite < iRead)
158       {
159         int iWrite2 = newFile.Write(buffer.get()+iWrite, iRead-iWrite);
160         if(iWrite2 <=0)
161           break;
162         iWrite+=iWrite2;
163       }
164
165       if (iWrite != iRead)
166       {
167         CLog::Log(LOGERROR, "%s - Failed write to file %s", __FUNCTION__, strDest.c_str());
168         llFileSize = (uint64_t)-1;
169         break;
170       }
171
172       llPos += iRead;
173
174       // calculate the current and average speeds
175       float end = timer.GetElapsedSeconds();
176
177       if (pCallback && end - start > 0.5 && end)
178       {
179         start = end;
180
181         float averageSpeed = llPos / end;
182         int ipercent = 0;
183         if(llFileSize)
184           ipercent = 100 * llPos / llFileSize;
185
186         if(!pCallback->OnFileCallback(pContext, ipercent, averageSpeed))
187         {
188           CLog::Log(LOGERROR, "%s - User aborted copy", __FUNCTION__);
189           llFileSize = (uint64_t)-1;
190           break;
191         }
192       }
193     }
194
195     /* close both files */
196     newFile.Close();
197     file.Close();
198
199     /* verify that we managed to completed the file */
200     if (llFileSize && llPos != llFileSize)
201     {
202       CFile::Delete(strDest);
203       return false;
204     }
205     return true;
206   }
207   return false;
208 }
209
210 //*********************************************************************************************
211 bool CFile::Open(const CStdString& strFileName, unsigned int flags)
212 {
213   m_flags = flags;
214   try
215   {
216     bool bPathInCache;
217     CURL url2(strFileName);
218     if (url2.GetProtocol() == "zip")
219       url2.SetOptions("");
220     if (!g_directoryCache.FileExists(url2.Get(), bPathInCache) )
221     {
222       if (bPathInCache)
223         return false;
224     }
225
226     CURL url(URIUtils::SubstitutePath(strFileName));
227     if ( (flags & READ_NO_CACHE) == 0 && URIUtils::IsInternetStream(url) && !CUtil::IsPicture(strFileName) )
228       m_flags |= READ_CACHED;
229
230     if (m_flags & READ_CACHED)
231     {
232       m_pFile = new CFileCache();
233       return m_pFile->Open(url);
234     }
235
236     m_pFile = CFileFactory::CreateLoader(url);
237     if (!m_pFile)
238       return false;
239
240     try
241     {
242       if (!m_pFile->Open(url))
243       {
244         SAFE_DELETE(m_pFile);
245         return false;
246       }
247     }
248     catch (CRedirectException *pRedirectEx)
249     {
250       // the file implementation decided this item should use a different implementation.
251       // the exception will contain the new implementation.
252
253       CLog::Log(LOGDEBUG,"File::Open - redirecting implementation for %s", strFileName.c_str());
254       SAFE_DELETE(m_pFile);
255       if (pRedirectEx && pRedirectEx->m_pNewFileImp)
256       {
257         m_pFile = pRedirectEx->m_pNewFileImp;
258         delete pRedirectEx;
259
260         if (!m_pFile->Open(url))
261         {
262           SAFE_DELETE(m_pFile);
263           return false;
264         }
265       }
266     }
267     catch (...)
268     {
269       CLog::Log(LOGERROR, "File::Open - unknown exception when opening %s", strFileName.c_str());
270       SAFE_DELETE(m_pFile);
271       return false;
272     }
273
274     if (m_pFile->GetChunkSize() && !(m_flags & READ_CHUNKED))
275     {
276       m_pBuffer = new CFileStreamBuffer(0);
277       m_pBuffer->Attach(m_pFile);
278     }
279
280     if (m_flags & READ_BITRATE)
281     {
282       m_bitStreamStats = new BitstreamStats();
283       m_bitStreamStats->Start();
284     }
285
286     return true;
287   }
288 #ifndef _LINUX
289   catch (const win32_exception &e)
290   {
291     e.writelog(__FUNCTION__);
292   }
293 #endif
294   catch(...)
295   {
296     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
297   }
298   CLog::Log(LOGERROR, "%s - Error opening %s", __FUNCTION__, strFileName.c_str());
299   return false;
300 }
301
302 bool CFile::OpenForWrite(const CStdString& strFileName, bool bOverWrite)
303 {
304   try
305   {
306     CURL url(URIUtils::SubstitutePath(strFileName));
307
308     m_pFile = CFileFactory::CreateLoader(url);
309     if (m_pFile && m_pFile->OpenForWrite(url, bOverWrite))
310     {
311       // add this file to our directory cache (if it's stored)
312       g_directoryCache.AddFile(strFileName);
313       return true;
314     }
315     return false;
316   }
317 #ifndef _LINUX
318   catch (const win32_exception &e)
319   {
320     e.writelog(__FUNCTION__);
321   }
322 #endif
323   catch(...)
324   {
325     CLog::Log(LOGERROR, "%s - Unhandled exception opening %s", __FUNCTION__, strFileName.c_str());
326   }
327   CLog::Log(LOGERROR, "%s - Error opening %s", __FUNCTION__, strFileName.c_str());
328   return false;
329 }
330
331 bool CFile::Exists(const CStdString& strFileName, bool bUseCache /* = true */)
332 {
333   try
334   {
335     if (strFileName.IsEmpty())
336       return false;
337
338     if (bUseCache)
339     {
340       bool bPathInCache;
341       if (g_directoryCache.FileExists(strFileName, bPathInCache) )
342         return true;
343       if (bPathInCache)
344         return false;
345     }
346
347     CURL url(URIUtils::SubstitutePath(strFileName));
348
349     auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
350     if (!pFile.get())
351       return false;
352
353     return pFile->Exists(url);
354   }
355 #ifndef _LINUX
356   catch (const win32_exception &e)
357   {
358     e.writelog(__FUNCTION__);
359   }
360 #endif
361   catch(...)
362   {
363     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
364   }
365   CLog::Log(LOGERROR, "%s - Error checking for %s", __FUNCTION__, strFileName.c_str());
366   return false;
367 }
368
369 int CFile::Stat(struct __stat64 *buffer)
370 {
371   return m_pFile->Stat(buffer);
372 }
373
374 int CFile::Stat(const CStdString& strFileName, struct __stat64* buffer)
375 {
376   try
377   {
378     CURL url(URIUtils::SubstitutePath(strFileName));
379
380     auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
381     if (!pFile.get())
382       return false;
383
384     return pFile->Stat(url, buffer);
385   }
386 #ifndef _LINUX
387   catch (const win32_exception &e)
388   {
389     e.writelog(__FUNCTION__);
390   }
391 #endif
392   catch(...)
393   {
394     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
395   }
396   CLog::Log(LOGERROR, "%s - Error statting %s", __FUNCTION__, strFileName.c_str());
397   return -1;
398 }
399
400 unsigned int CFile::Read(void *lpBuf, int64_t uiBufSize)
401 {
402   if (!m_pFile)
403     return 0;
404
405   if(m_pBuffer)
406   {
407     if(m_flags & READ_TRUNCATED)
408     {
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);
414       return nBytes;
415     }
416     else
417     {
418       unsigned int nBytes = m_pBuffer->sgetn((char*)lpBuf, uiBufSize);
419       if (m_bitStreamStats && nBytes>0)
420         m_bitStreamStats->AddSampleBytes(nBytes);
421       return nBytes;
422     }
423   }
424
425   try
426   {
427     if(m_flags & READ_TRUNCATED)
428     {
429       unsigned int nBytes = m_pFile->Read(lpBuf, uiBufSize);
430       if (m_bitStreamStats && nBytes>0)
431         m_bitStreamStats->AddSampleBytes(nBytes);
432       return nBytes;
433     }
434     else
435     {
436       unsigned int done = 0;
437       while((uiBufSize-done) > 0)
438       {
439         int curr = m_pFile->Read((char*)lpBuf+done, uiBufSize-done);
440         if(curr<=0)
441           break;
442
443         done+=curr;
444       }
445       if (m_bitStreamStats && done > 0)
446         m_bitStreamStats->AddSampleBytes(done);
447       return done;
448     }
449   }
450 #ifndef _LINUX
451   catch (const win32_exception &e)
452   {
453     e.writelog(__FUNCTION__);
454   }
455 #endif
456   catch(...)
457   {
458     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
459   }
460   return 0;
461 }
462
463 //*********************************************************************************************
464 void CFile::Close()
465 {
466   try
467   {
468     SAFE_DELETE(m_pBuffer);
469     SAFE_DELETE(m_pFile);
470   }
471 #ifndef _LINUX
472   catch (const win32_exception &e)
473   {
474     e.writelog(__FUNCTION__);
475   }
476 #endif
477   catch(...)
478   {
479     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
480   }
481   return;
482 }
483
484 void CFile::Flush()
485 {
486   try
487   {
488     if (m_pFile)
489       m_pFile->Flush();
490   }
491 #ifndef _LINUX
492   catch (const win32_exception &e)
493   {
494     e.writelog(__FUNCTION__);
495   }
496 #endif
497   catch(...)
498   {
499     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
500   }
501   return;
502 }
503
504 //*********************************************************************************************
505 int64_t CFile::Seek(int64_t iFilePosition, int iWhence)
506 {
507   if (!m_pFile)
508     return -1;
509
510   if (m_pBuffer)
511   {
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);
518   }
519
520   try
521   {
522     return m_pFile->Seek(iFilePosition, iWhence);
523   }
524 #ifndef _LINUX
525   catch (const win32_exception &e)
526   {
527     e.writelog(__FUNCTION__);
528   }
529 #endif
530   catch(...)
531   {
532     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
533   }
534   return -1;
535 }
536
537 //*********************************************************************************************
538 int64_t CFile::GetLength()
539 {
540   try
541   {
542     if (m_pFile)
543       return m_pFile->GetLength();
544     return 0;
545   }
546 #ifndef _LINUX
547   catch (const win32_exception &e)
548   {
549     e.writelog(__FUNCTION__);
550   }
551 #endif
552   catch(...)
553   {
554     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
555   }
556   return 0;
557 }
558
559 //*********************************************************************************************
560 int64_t CFile::GetPosition()
561 {
562   if (!m_pFile)
563     return -1;
564
565   if (m_pBuffer)
566     return m_pBuffer->pubseekoff(0, ios_base::cur);
567
568   try
569   {
570     return m_pFile->GetPosition();
571   }
572 #ifndef _LINUX
573   catch (const win32_exception &e)
574   {
575     e.writelog(__FUNCTION__);
576   }
577 #endif
578   catch(...)
579   {
580     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
581   }
582   return -1;
583 }
584
585
586 //*********************************************************************************************
587 bool CFile::ReadString(char *szLine, int iLineLength)
588 {
589   if (!m_pFile)
590     return false;
591
592   if (m_pBuffer)
593   {
594     typedef CFileStreamBuffer::traits_type traits;
595     CFileStreamBuffer::int_type aByte = m_pBuffer->sgetc();
596
597     if(aByte == traits::eof())
598       return false;
599
600     while(iLineLength>0)
601     {
602       aByte = m_pBuffer->sbumpc();
603
604       if(aByte == traits::eof())
605         break;
606
607       if(aByte == traits::to_int_type('\n'))
608       {
609         if(m_pBuffer->sgetc() == traits::to_int_type('\r'))
610           m_pBuffer->sbumpc();
611         break;
612       }
613
614       if(aByte == traits::to_int_type('\r'))
615       {
616         if(m_pBuffer->sgetc() == traits::to_int_type('\n'))
617           m_pBuffer->sbumpc();
618         break;
619       }
620
621       *szLine = traits::to_char_type(aByte);
622       szLine++;
623       iLineLength--;
624     }
625
626     // if we have no space for terminating character we failed
627     if(iLineLength==0)
628       return false;
629
630     *szLine = 0;
631
632     return true;
633   }
634
635   try
636   {
637     return m_pFile->ReadString(szLine, iLineLength);
638   }
639 #ifndef _LINUX
640   catch (const win32_exception &e)
641   {
642     e.writelog(__FUNCTION__);
643   }
644 #endif
645   catch(...)
646   {
647     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
648   }
649   return false;
650 }
651
652 int CFile::Write(const void* lpBuf, int64_t uiBufSize)
653 {
654   try
655   {
656     return m_pFile->Write(lpBuf, uiBufSize);
657   }
658 #ifndef _LINUX
659   catch (const win32_exception &e)
660   {
661     e.writelog(__FUNCTION__);
662   }
663 #endif
664   catch(...)
665   {
666     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
667   }
668   return -1;
669 }
670
671 bool CFile::Delete(const CStdString& strFileName)
672 {
673   try
674   {
675     CURL url(URIUtils::SubstitutePath(strFileName));
676
677     auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
678     if (!pFile.get())
679       return false;
680
681     if(pFile->Delete(url))
682     {
683       g_directoryCache.ClearFile(strFileName);
684       return true;
685     }
686   }
687 #ifndef _LINUX
688   catch (const access_violation &e)
689   {
690     e.writelog(__FUNCTION__);
691   }
692   catch (const win32_exception &e)
693   {
694     e.writelog(__FUNCTION__);
695   }
696 #endif
697   catch(...)
698   {
699     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
700   }
701   if (Exists(strFileName))
702     CLog::Log(LOGERROR, "%s - Error deleting file %s", __FUNCTION__, strFileName.c_str());
703   return false;
704 }
705
706 bool CFile::Rename(const CStdString& strFileName, const CStdString& strNewFileName)
707 {
708   try
709   {
710     CURL url(URIUtils::SubstitutePath(strFileName));
711     CURL urlnew(URIUtils::SubstitutePath(strNewFileName));
712
713     auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
714     if (!pFile.get())
715       return false;
716
717     if(pFile->Rename(url, urlnew))
718     {
719       g_directoryCache.ClearFile(strFileName);
720       g_directoryCache.ClearFile(strNewFileName);
721       return true;
722     }
723   }
724 #ifndef _LINUX
725   catch (const win32_exception &e)
726   {
727     e.writelog(__FUNCTION__);
728   }
729 #endif
730   catch(...)
731   {
732     CLog::Log(LOGERROR, "%s - Unhandled exception ", __FUNCTION__);
733   }
734   CLog::Log(LOGERROR, "%s - Error renaming file %s", __FUNCTION__, strFileName.c_str());
735   return false;
736 }
737
738 bool CFile::SetHidden(const CStdString& fileName, bool hidden)
739 {
740   try
741   {
742     CURL url(URIUtils::SubstitutePath(fileName));
743
744     auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
745     if (!pFile.get())
746       return false;
747
748     return pFile->SetHidden(url, hidden);
749   }
750   catch(...)
751   {
752     CLog::Log(LOGERROR, "%s(%s) - Unhandled exception", __FUNCTION__, fileName.c_str());
753   }
754   return false;
755 }
756
757 int CFile::IoControl(EIoControl request, void* param)
758 {
759   int result = -1;
760   if (m_pFile)
761     result = m_pFile->IoControl(request, param);
762
763   if(result == -1 && request == IOCTRL_SEEK_POSSIBLE)
764   {
765     if(m_pFile->GetLength() >= 0 && m_pFile->Seek(0, SEEK_CUR) >= 0)
766       return 1;
767     else
768       return 0;
769   }
770
771   return result;
772 }
773 //*********************************************************************************************
774 //*************** Stream IO for CFile objects *************************************************
775 //*********************************************************************************************
776 CFileStreamBuffer::~CFileStreamBuffer()
777 {
778   sync();
779   Detach();
780 }
781
782 CFileStreamBuffer::CFileStreamBuffer(int backsize)
783   : streambuf()
784   , m_file(NULL)
785   , m_buffer(NULL)
786   , m_backsize(backsize)
787   , m_frontsize(0)
788 {
789 }
790
791 void CFileStreamBuffer::Attach(IFile *file)
792 {
793   m_file = file;
794
795   m_frontsize = CFile::GetChunkSize(m_file->GetChunkSize(), 64*1024);
796
797   m_buffer = new char[m_frontsize+m_backsize];
798   setg(0,0,0);
799   setp(0,0);
800 }
801
802 void CFileStreamBuffer::Detach()
803 {
804   setg(0,0,0);
805   setp(0,0);
806   delete[] m_buffer;
807   m_buffer = NULL;
808 }
809
810 CFileStreamBuffer::int_type CFileStreamBuffer::underflow()
811 {
812   if(gptr() < egptr())
813     return traits_type::to_int_type(*gptr());
814
815   if(!m_file)
816     return traits_type::eof();
817
818   size_t backsize = 0;
819   if(m_backsize)
820   {
821     backsize = (size_t)min<ptrdiff_t>((ptrdiff_t)m_backsize, egptr()-eback());
822     memmove(m_buffer, egptr()-backsize, backsize);
823   }
824
825   unsigned int size = 0;
826 #ifndef _LINUX
827   try
828   {
829 #endif
830     size = m_file->Read(m_buffer+backsize, m_frontsize);
831 #ifndef _LINUX
832   }
833   catch (const win32_exception &e)
834   {
835     e.writelog(__FUNCTION__);
836   }
837 #endif
838
839   if(size == 0)
840     return traits_type::eof();
841
842   setg(m_buffer, m_buffer+backsize, m_buffer+backsize+size);
843   return traits_type::to_int_type(*gptr());
844 }
845
846 CFileStreamBuffer::pos_type CFileStreamBuffer::seekoff(
847   off_type offset,
848   ios_base::seekdir way,
849   ios_base::openmode mode)
850 {
851   // calculate relative offset
852   off_type pos  = m_file->GetPosition() - (egptr() - gptr());
853   off_type offset2;
854   if(way == ios_base::cur)
855     offset2 = offset;
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;
860   else
861     return streampos(-1);
862
863   // a non seek shouldn't modify our buffer
864   if(offset2 == 0)
865     return pos;
866
867   // try to seek within buffer
868   if(gptr()+offset2 >= eback() && gptr()+offset2 < egptr())
869   {
870     gbump(offset2);
871     return pos + offset2;
872   }
873
874   // reset our buffer pointer, will
875   // start buffering on next read
876   setg(0,0,0);
877   setp(0,0);
878
879   int64_t position = -1;
880 #ifndef _LINUX
881   try
882   {
883 #endif
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);
888     else
889       position = m_file->Seek(offset, SEEK_SET);
890 #ifndef _LINUX
891   }
892   catch (const win32_exception &e)
893   {
894     e.writelog(__FUNCTION__);
895     return streampos(-1);
896   }
897 #endif
898
899   if(position<0)
900     return streampos(-1);
901
902   return position;
903 }
904
905 CFileStreamBuffer::pos_type CFileStreamBuffer::seekpos(
906   pos_type pos,
907   ios_base::openmode mode)
908 {
909   return seekoff(pos, ios_base::beg, mode);
910 }
911
912 streamsize CFileStreamBuffer::showmanyc()
913 {
914   underflow();
915   return egptr() - gptr();
916 }
917
918 CFileStream::CFileStream(int backsize /*= 0*/) :
919     istream(&m_buffer),
920     m_buffer(backsize),
921     m_file(NULL)
922 {
923 }
924
925 CFileStream::~CFileStream()
926 {
927   Close();
928 }
929
930
931 bool CFileStream::Open(const CURL& filename)
932 {
933   Close();
934
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))
940   {
941     m_buffer.Attach(m_file);
942     return true;
943   }
944
945   setstate(failbit);
946   return false;
947 }
948
949 int64_t CFileStream::GetLength()
950 {
951   return m_file->GetLength();
952 }
953
954 void CFileStream::Close()
955 {
956   if(!m_file)
957     return;
958
959   m_buffer.Detach();
960   SAFE_DELETE(m_file);
961 }
962
963 bool CFileStream::Open(const CStdString& filename)
964 {
965   return Open(CURL(URIUtils::SubstitutePath(filename)));
966 }
967
968 #ifdef _ARMEL
969 char* CFileStream::ReadFile()
970 {
971   if(!m_file)
972     return NULL;
973
974   int64_t length = m_file->GetLength();
975   char *str = new char[length+1];
976   m_file->Read(str, length);
977   str[length] = '\0';
978   return str;
979 }
980 #endif