1. Summary
  2. Files
  3. Support
  4. Report Spam
  5. Create account
  6. Log in

root/trunk/shareaza/DownloadWithFile.cpp @ 7434

Revision 7434, 17.1 KB (checked in by raspopov, 5 years ago)

Now Shareaza uses HashLib.DLL
Added CAutocompleteEdit class (not complete yet _)

  • Property svn:mime-type set to text/plain
  • Property svn:eol-style set to native
  • Property cvs2svn:cvs-rev set to 1.12
Line 
1//
2// DownloadWithFile.cpp
3//
4// Copyright (c) Shareaza Development Team, 2002-2008.
5// This file is part of SHAREAZA (shareaza.sourceforge.net)
6//
7// Shareaza is free software; you can redistribute it
8// and/or modify it under the terms of the GNU General Public License
9// as published by the Free Software Foundation; either version 2 of
10// the License, or (at your option) any later version.
11//
12// Shareaza 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 Shareaza; if not, write to the Free Software
19// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20//
21
22#include "StdAfx.h"
23#include "Shareaza.h"
24#include "Settings.h"
25#include "Downloads.h"
26#include "DownloadWithFile.h"
27#include "DownloadWithTorrent.h"
28#include "DownloadSource.h"
29#include "DownloadTransfer.h"
30#include "DownloadGroups.h"
31#include "FragmentedFile.h"
32#include "Uploads.h"
33
34#include "ID3.h"
35#include "XML.h"
36#include "SchemaCache.h"
37#include "LibraryBuilder.h"
38
39#ifdef _DEBUG
40#undef THIS_FILE
41static char THIS_FILE[]=__FILE__;
42#define new DEBUG_NEW
43#endif
44
45
46//////////////////////////////////////////////////////////////////////
47// CDownloadWithFile construction
48
49CDownloadWithFile::CDownloadWithFile() :
50        m_pFile         ( new CFragmentedFile() )
51,       m_tReceived     ( GetTickCount() )
52,       m_bDiskFull     ( FALSE )
53{
54}
55
56CDownloadWithFile::~CDownloadWithFile()
57{
58        if ( m_pFile != NULL ) delete m_pFile;
59}
60
61//////////////////////////////////////////////////////////////////////
62// CDownloadWithFile open the file
63
64BOOL CDownloadWithFile::OpenFile()
65{
66        if ( m_pFile == NULL || m_sName.IsEmpty() ) return FALSE;
67        if ( m_pFile->IsOpen() ) return TRUE;
68
69        SetModified();
70
71        if ( m_pFile->IsValid() )
72        {
73                if ( m_pFile->Open( m_sPath ) ) return TRUE;
74                theApp.Message( MSG_ERROR, IDS_DOWNLOAD_FILE_OPEN_ERROR, (LPCTSTR)m_sPath );
75        }
76        else if ( m_nSize != SIZE_UNKNOWN && !Downloads.IsSpaceAvailable( m_nSize, Downloads.dlPathIncomplete ) )
77        {
78                theApp.Message( MSG_ERROR, IDS_DOWNLOAD_DISK_SPACE,
79                        m_sName,
80                        Settings.SmartVolume( m_nSize ) );
81        }
82        else
83        {
84                CString strLocalName = m_sPath;
85                m_sPath.Empty();
86
87                GenerateDiskName();
88
89                for ( int nTry = 0 ; nTry < 5 ; nTry++ )
90                {
91                        CString strName;
92
93                        if ( nTry == 0 )
94                                strName = m_sPath;
95                        else
96                                strName.Format( _T("%s.x%i"), (LPCTSTR)m_sPath, GetRandomNum( 0, 127 ) );
97
98                        theApp.Message( MSG_INFO, IDS_DOWNLOAD_FILE_CREATE, (LPCTSTR)strName );
99
100                        if ( m_pFile->Create( strName, m_nSize ) )
101                        {
102                                theApp.WriteProfileString( _T("Delete"), strName, NULL );
103                                MoveFile( strLocalName + _T(".sd"), strName + _T(".sd") );
104                                m_sPath = strName;
105                                return TRUE;
106                        }
107
108                        theApp.Message( MSG_ERROR, IDS_DOWNLOAD_FILE_CREATE_ERROR, (LPCTSTR)strName );
109                }
110
111                m_sPath = strLocalName;
112        }
113
114        m_bDiskFull = TRUE;
115
116        return FALSE;
117}
118
119//////////////////////////////////////////////////////////////////////
120// CDownloadWithFile close the file
121
122void CDownloadWithFile::CloseFile()
123{
124        if ( m_pFile != NULL ) m_pFile->Close();
125}
126
127//////////////////////////////////////////////////////////////////////
128// CDownloadWithFile prepare file
129
130BOOL CDownloadWithFile::PrepareFile()
131{
132        return OpenFile() && m_pFile->GetRemaining() > 0;
133}
134
135//////////////////////////////////////////////////////////////////////
136// CDownloadWithFile delete the file
137
138void CDownloadWithFile::DeleteFile(bool bForce)
139{
140        if ( m_pFile != NULL && m_pFile->IsValid() == FALSE ) return;
141
142        // Close the file handle
143        while( !Uploads.OnRename( m_sPath, NULL, bForce ) );
144
145        if ( m_pFile != NULL )
146        {
147                if ( GetVolumeComplete() == 0 || ( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) == 0 )
148                {
149                        if ( !::DeleteFile( m_sPath ) )
150                                theApp.WriteProfileString( _T("Delete"), m_sPath, _T("") );
151                }
152                else
153                {
154                        MoveFile( m_sPath, m_sPath + _T(".aborted") );
155                }
156        }
157        else if ( bForce ) // be careful, do not delete completed BT seeding file
158        {
159                if ( !::DeleteFile( m_sPath ) )
160                        theApp.WriteProfileString( _T("Delete"), m_sPath, _T("") );
161        }
162
163        SetModified();
164}
165
166//////////////////////////////////////////////////////////////////////
167// CDownloadWithFile statistics
168
169float CDownloadWithFile::GetProgress() const
170{
171        if ( m_nSize == 0 || m_nSize == SIZE_UNKNOWN ) return 0;
172        if ( m_nSize == GetVolumeComplete() ) return 100.0f;
173        return float( GetVolumeComplete() * 10000 / m_nSize ) / 100.0f;
174}
175
176QWORD CDownloadWithFile::GetVolumeComplete() const
177{
178        if ( m_pFile != NULL )
179        {
180                if ( m_pFile->IsValid() )
181                        return m_pFile->GetCompleted();
182                else
183                        return 0;
184        }
185        return m_nSize;
186}
187
188QWORD CDownloadWithFile::GetVolumeRemaining() const
189{
190        if ( m_pFile != NULL )
191        {
192                if ( m_pFile->IsValid() )
193                        return m_pFile->GetRemaining();
194                else
195                        return m_nSize;
196        }
197        return 0;
198}
199
200DWORD CDownloadWithFile::GetTimeRemaining() const
201{
202        QWORD nRemaining        = GetVolumeRemaining();
203        DWORD nSpeed            = GetAverageSpeed();
204        if ( nSpeed == 0 || nRemaining == SIZE_UNKNOWN ) return 0xFFFFFFFF;
205        QWORD nTimeRemaining = nRemaining / nSpeed;
206        return ( ( nTimeRemaining > 0xFFFFFFFF ) ? 0xFFFFFFFF : (DWORD)nTimeRemaining );
207}
208
209CString CDownloadWithFile::GetDisplayName() const
210{
211        if ( m_sName.GetLength() ) return m_sName;
212
213        CString strName;
214
215        if ( m_oSHA1 )
216                strName = m_oSHA1.toShortUrn();
217        else if ( m_oTiger )
218                strName = m_oTiger.toShortUrn();
219        else if ( m_oED2K )
220                strName = m_oED2K.toShortUrn();
221        else if ( m_oBTH )
222                strName = m_oBTH.toShortUrn();
223        else if ( m_oMD5 )
224                strName = m_oMD5.toShortUrn();
225        else
226                strName = _T("Unknown File");
227
228        return strName;
229}
230
231//////////////////////////////////////////////////////////////////////
232// CDownloadWithFile get the first empty fragment
233
234const Fragments::List& CDownloadWithFile::GetEmptyFragmentList() const
235{
236        static const Fragments::List dummy( 0 );
237        return m_pFile ? m_pFile->GetEmptyFragmentList() : dummy;
238}
239
240//////////////////////////////////////////////////////////////////////
241// CDownloadWithFile get a list of possible download fragments
242
243Fragments::List CDownloadWithFile::GetPossibleFragments(
244        const Fragments::List& oAvailable, Fragments::Fragment& oLargest)
245{
246        if ( !PrepareFile() ) return Fragments::List( oAvailable.limit() );
247        Fragments::List oPossible( oAvailable );
248
249        if ( oAvailable.empty() )
250        {
251                oPossible = m_pFile->GetEmptyFragmentList();
252        }
253        else
254        {
255                Fragments::List tmp( inverse( m_pFile->GetEmptyFragmentList() ) );
256                oPossible.erase( tmp.begin(), tmp.end() );
257        }
258
259        if ( oPossible.empty() ) return oPossible;
260
261        oLargest = *oPossible.largest_range();
262
263        for ( CDownloadTransfer* pTransfer = GetFirstTransfer();
264                !oPossible.empty() && pTransfer;
265                pTransfer = pTransfer->m_pDlNext )
266        {
267                pTransfer->SubtractRequested( oPossible );
268        }
269
270        return oPossible;
271}
272
273//////////////////////////////////////////////////////////////////////
274// CDownloadWithFile select a fragment for a transfer
275
276inline DWORD CalcChunkSize(QWORD nSize)
277{
278        if ( nSize <= 268435456 ) return 1024 * 1024; // try to keep chunk size reasonably large
279        DWORD nChunk = DWORD( ( nSize - 1 ) / 256 ), nTemp; // default treeheight of 9
280        while ( nTemp = nChunk & ( nChunk - 1 ) ) nChunk = nTemp;
281        return nChunk * 2;
282}
283
284BOOL CDownloadWithFile::GetFragment(CDownloadTransfer* pTransfer)
285{
286        if ( !PrepareFile() )
287                return NULL;
288
289        Fragments::Fragment oLargest( SIZE_UNKNOWN, SIZE_UNKNOWN );
290
291        Fragments::List oPossible = GetPossibleFragments(
292                pTransfer->m_pSource->m_oAvailable, oLargest );
293
294        if ( oLargest.begin() == SIZE_UNKNOWN )
295        {
296                ASSERT( oPossible.empty() );
297                return FALSE;
298        }
299
300        if ( !oPossible.empty() )
301        {
302                Fragments::List::const_iterator pRandom = oPossible.begin()->begin() == 0
303                        ? oPossible.begin()
304                        : oPossible.random_range();
305
306                pTransfer->m_nOffset = pRandom->begin();
307                pTransfer->m_nLength = pRandom->size();
308
309                return TRUE;
310        }
311        else
312        {
313                CDownloadTransfer* pExisting = NULL;
314
315                for ( CDownloadTransfer* pOther = GetFirstTransfer() ; pOther ; pOther = pOther->m_pDlNext )
316                {
317                        if ( pOther->m_bRecvBackwards )
318                        {
319                                if ( pOther->m_nOffset + pOther->m_nLength - pOther->m_nPosition
320                                         != oLargest.end() ) continue;
321                        }
322                        else
323                        {
324                                if ( pOther->m_nOffset + pOther->m_nPosition != oLargest.begin() ) continue;
325                        }
326
327                        pExisting = pOther;
328                        break;
329                }
330
331                if ( pExisting == NULL )
332                {
333                        pTransfer->m_nOffset = oLargest.begin();
334                        pTransfer->m_nLength = oLargest.size();
335                        return TRUE;
336                }
337
338                if ( oLargest.size() < 32 ) return FALSE;
339
340                DWORD nOldSpeed = pExisting->GetAverageSpeed();
341                DWORD nNewSpeed = pTransfer->GetAverageSpeed();
342                QWORD nLength   = oLargest.size() / 2;
343
344                if ( nOldSpeed > 5 && nNewSpeed > 5 )
345                {
346                        nLength = oLargest.size() * nNewSpeed / ( nNewSpeed + nOldSpeed );
347
348                        if ( oLargest.size() > 102400 )
349                        {
350                                nLength = max( nLength, 51200ull );
351                                nLength = min( nLength, oLargest.size() - 51200ull );
352                        }
353                }
354
355                if ( pExisting->m_bRecvBackwards )
356                {
357                        pTransfer->m_nOffset            = oLargest.begin();
358                        pTransfer->m_nLength            = nLength;
359                        pTransfer->m_bWantBackwards     = FALSE;
360                }
361                else
362                {
363                        pTransfer->m_nOffset            = oLargest.end() - nLength;
364                        pTransfer->m_nLength            = nLength;
365                        pTransfer->m_bWantBackwards     = TRUE;
366                }
367
368                return TRUE;
369        }
370}
371
372//////////////////////////////////////////////////////////////////////
373// CDownloadWithFile check if a byte position is empty
374
375BOOL CDownloadWithFile::IsPositionEmpty(QWORD nOffset)
376{
377        if ( m_pFile == NULL || !m_pFile->IsValid() )
378                return FALSE;
379
380        return m_pFile->IsPositionRemaining( nOffset );
381}
382
383//////////////////////////////////////////////////////////////////////
384// CDownloadWithFile check if a range would "help"
385
386BOOL CDownloadWithFile::AreRangesUseful(const Fragments::List& oAvailable)
387{
388        if ( m_pFile == NULL || !m_pFile->IsValid() )
389                return FALSE;
390
391        return m_pFile->GetEmptyFragmentList().overlaps( oAvailable );
392}
393
394BOOL CDownloadWithFile::IsRangeUseful(QWORD nOffset, QWORD nLength)
395{
396        if ( m_pFile == NULL || !m_pFile->IsValid() )
397                return FALSE;
398
399        return m_pFile->GetEmptyFragmentList().overlaps( Fragments::Fragment( nOffset, nOffset + nLength ) );
400}
401
402// like IsRangeUseful( ) but take the amount of useful ranges relative to the amount of garbage
403// and source speed into account
404BOOL CDownloadWithFile::IsRangeUsefulEnough(CDownloadTransfer* pTransfer, QWORD nOffset, QWORD nLength)
405{
406        if ( m_pFile == NULL || !m_pFile->IsValid() )
407                return FALSE;
408
409        // range is useful if at least byte within the next amount of data transferable within the next 5 seconds
410        // is useful
411        DWORD nLength2 = 5 * pTransfer->GetAverageSpeed();
412        if ( nLength2 < nLength )
413        {
414                if ( !pTransfer->m_bRecvBackwards ) nOffset += nLength - nLength2;
415                nLength = nLength2;
416        }
417        return m_pFile->GetEmptyFragmentList().overlaps(
418                Fragments::Fragment( nOffset, nOffset + nLength ) );
419}
420
421//////////////////////////////////////////////////////////////////////
422// CDownloadWithFile get a string of available ranges
423
424CString CDownloadWithFile::GetAvailableRanges() const
425{
426        CString strRange, strRanges;
427
428        if ( m_pFile == NULL || !m_pFile->IsValid() )
429                return strRanges;
430
431        const Fragments::List oAvailable = inverse( m_pFile->GetEmptyFragmentList() );
432
433        for ( Fragments::List::const_iterator pFragment = oAvailable.begin();
434                pFragment != oAvailable.end(); ++pFragment )
435        {
436                if ( strRanges.IsEmpty() )
437                {
438                        strRanges = _T("bytes ");
439                }
440                else
441                {
442                        strRanges += ',';
443                }
444
445                strRange.Format( _T("%I64i-%I64i"), pFragment->begin(), pFragment->end() - 1 );
446                strRanges += strRange;
447        }
448
449        return strRanges;
450}
451
452//////////////////////////////////////////////////////////////////////
453// CDownloadWithFile clip a range to valid portions
454
455BOOL CDownloadWithFile::ClipUploadRange(QWORD nOffset, QWORD& nLength) const
456{
457        if ( m_pFile == NULL || !m_pFile->IsValid() )
458                return FALSE;
459
460        if ( nOffset >= m_nSize ) return FALSE;
461
462        if ( m_pFile->IsPositionRemaining( nOffset ) ) return FALSE;
463
464        if ( nOffset + nLength > m_nSize ) nLength = m_nSize - nOffset;
465
466        Fragments::List::const_iterator_pair match( m_pFile->GetEmptyFragmentList().equal_range(
467                Fragments::Fragment( nOffset, nOffset + nLength ) ) );
468
469        if ( match.first != match.second )
470        {
471                if ( match.first->begin() <= nOffset ) return ( nLength = 0 ) > 0;
472                nLength = match.first->end() - nOffset;
473                return TRUE;
474        }
475
476        return nLength > 0;
477}
478
479//////////////////////////////////////////////////////////////////////
480// CDownloadWithFile select a random range of available data
481
482BOOL CDownloadWithFile::GetRandomRange(QWORD& nOffset, QWORD& nLength) const
483{
484        if ( m_pFile == NULL || !m_pFile->IsValid() )
485                return FALSE;
486
487        if ( m_pFile->GetEmptyFragmentList().missing() == 0 ) return FALSE;
488
489        Fragments::List oFilled = inverse( m_pFile->GetEmptyFragmentList() );
490        Fragments::List::const_iterator pRandom = oFilled.random_range();
491
492        nOffset = pRandom->begin();
493        nLength = pRandom->size();
494
495        return TRUE;
496}
497
498//////////////////////////////////////////////////////////////////////
499// CDownloadWithFile submit data
500
501BOOL CDownloadWithFile::SubmitData(QWORD nOffset, LPBYTE pData, QWORD nLength)
502{
503        SetModified();
504        m_tReceived = GetTickCount();
505
506        if ( static_cast< CDownloadWithTorrent* >( this )->IsTorrent() )        // Hack: Only do this for BitTorrent
507        {
508                for ( CDownloadTransfer* pTransfer = GetFirstTransfer() ; pTransfer ; pTransfer = pTransfer->m_pDlNext )
509                {
510                        if ( pTransfer->m_nProtocol == PROTOCOL_BT ) pTransfer->UnrequestRange( nOffset, nLength );
511                }
512        }
513
514        return m_pFile != NULL && m_pFile->WriteRange( nOffset, pData, nLength );
515}
516
517//////////////////////////////////////////////////////////////////////
518// CDownloadWithFile erase a range
519
520QWORD CDownloadWithFile::EraseRange(QWORD nOffset, QWORD nLength)
521{
522        if ( m_pFile == NULL ) return 0;
523        QWORD nCount = m_pFile->InvalidateRange( nOffset, nLength );
524        if ( nCount > 0 ) SetModified();
525        return nCount;
526}
527
528//////////////////////////////////////////////////////////////////////
529// CDownloadWithFile make the file appear complete
530
531BOOL CDownloadWithFile::MakeComplete()
532{
533        if ( m_sPath.IsEmpty() ) return FALSE;
534
535        if ( !PrepareFile() )
536                return FALSE;
537
538        return m_pFile->MakeComplete();
539}
540
541//////////////////////////////////////////////////////////////////////
542// CDownloadWithFile run the file
543
544BOOL CDownloadWithFile::RunFile(DWORD /*tNow*/)
545{
546        if ( m_pFile->IsOpen() )
547        {
548                if ( m_pFile->GetRemaining() == 0 ) return TRUE;
549        }
550
551        return FALSE;
552}
553
554//////////////////////////////////////////////////////////////////////
555// CDownloadWithFile append intrinsic metadata
556
557BOOL CDownloadWithFile::AppendMetadata()
558{
559        if ( !Settings.Library.VirtualFiles )
560                return FALSE;
561
562        if ( GetMetadata() == NULL ) return FALSE;
563        CXMLElement* pXML = GetMetadata()->GetFirstElement();
564        if ( pXML == NULL ) return FALSE;
565
566        HANDLE hFile = CreateFile( m_sPath, GENERIC_READ | GENERIC_WRITE,
567                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
568                NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
569        VERIFY_FILE_ACCESS( hFile, m_sPath )
570        if ( hFile == INVALID_HANDLE_VALUE ) return FALSE;
571
572        CString strURI = GetMetadata()->GetAttributeValue( CXMLAttribute::schemaName );
573        BOOL bSuccess = FALSE;
574
575        if ( CheckURI( strURI, CSchema::uriAudio ) )
576        {
577                if ( _tcsistr( m_sPath, _T(".mp3") ) != NULL )
578                {
579                        bSuccess |= AppendMetadataID3v1( hFile, pXML );
580                }
581        }
582
583        CloseHandle( hFile );
584
585        return bSuccess;
586}
587
588BOOL CDownloadWithFile::AppendMetadataID3v1(HANDLE hFile, CXMLElement* pXML)
589{
590        DWORD nBytes;
591        CString str;
592
593        ID3V1 pID3 = {};
594        SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
595
596        if ( !ReadFile( hFile, &pID3, 3, &nBytes, NULL ) )
597                return FALSE;
598
599        if ( memcmp( pID3.szTag, ID3V2_TAG, 3 ) == 0 )
600                return FALSE;
601
602        ZeroMemory( &pID3, sizeof(pID3) );
603        SetFilePointer( hFile, -(int)sizeof(pID3), NULL, FILE_END );
604
605        if ( !ReadFile( hFile, &pID3, sizeof(pID3), &nBytes, NULL ) )
606                return FALSE;
607
608        if ( memcmp( pID3.szTag, ID3V1_TAG, 3 ) == 0 )
609                return FALSE;
610
611        ZeroMemory( &pID3, sizeof(pID3) );
612        std::memcpy( pID3.szTag, ID3V1_TAG, 3 );
613
614        str = pXML->GetAttributeValue( _T("title") );
615        if ( str.GetLength() )
616                strncpy( pID3.szSongname, CT2CA( str ), 30 );
617
618        str = pXML->GetAttributeValue( _T("artist") );
619        if ( str.GetLength() )
620                strncpy( pID3.szArtist, CT2CA( str ), 30 );
621
622        str = pXML->GetAttributeValue( _T("album") );
623        if ( str.GetLength() )
624                strncpy( pID3.szAlbum, CT2CA( str ), 30 );
625
626        str = pXML->GetAttributeValue( _T("year") );
627        if ( str.GetLength() )
628                strncpy( pID3.szYear, CT2CA( str ), 4 );
629
630        str = pXML->GetAttributeValue( _T("genre") );
631        if ( str.GetLength() )
632        {
633                int nGenre = LibraryBuilder.LookupID3v1Genre( str );
634                if ( nGenre != -1 )
635                        pID3.nGenre = static_cast< BYTE >( nGenre );
636        }
637
638        SetFilePointer( hFile, 0, NULL, FILE_END );
639        WriteFile( hFile, &pID3, sizeof(pID3), &nBytes, NULL );
640
641        return TRUE;
642}
643
644//////////////////////////////////////////////////////////////////////
645// CDownloadWithFile serialize
646
647void CDownloadWithFile::Serialize(CArchive& ar, int nVersion)
648{
649        CDownloadWithTransfers::Serialize( ar, nVersion );
650
651        if ( ar.IsStoring() )
652        {
653                ar.WriteCount( m_pFile != NULL );
654                if ( m_pFile != NULL ) m_pFile->Serialize( ar, nVersion );
655        }
656        else
657        {
658                if ( nVersion < 28 )
659                {
660                        CString strLocalName;
661                        ar >> strLocalName;
662
663                        if ( strLocalName.GetLength() )
664                        {
665                                if ( m_sPath.GetLength() )
666                                        MoveFile( m_sPath + _T(".sd"), strLocalName + _T(".sd") );
667                                m_sPath = strLocalName;
668                        }
669                        else
670                        {
671                                GenerateDiskName();
672                        }
673                }
674
675                if ( nVersion < 25 || ar.ReadCount() )
676                {
677                        m_pFile->Serialize( ar, nVersion );
678                }
679                else
680                {
681                        delete m_pFile;
682                        m_pFile = NULL;
683                }
684        }
685}
Note: See TracBrowser for help on using the browser.