2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 Merkur ( devs@emule-project.net / http://www.emule-project.net )
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #include "PartFileConvert.h"
31 #include "DownloadQueue.h"
32 #include <common/Format.h>
35 #include "Preferences.h"
36 #include "SharedFileList.h"
37 #include <common/FileFunctions.h>
38 #include "OtherFunctions.h"
39 #include "GuiEvents.h"
40 #include "DataToText.h"
42 static unsigned s_nextJobId
= 0;
56 ConvertJob() { id
=s_nextJobId
++; size
=0; spaceneeded
=0; partmettype
=PMT_UNKNOWN
; removeSource
=true; }
59 ConvertInfo::ConvertInfo(ConvertJob
*job
)
61 folder(job
->folder
), filename(job
->filename
), filehash(job
->filehash
),
62 state(job
->state
), size(job
->size
), spaceneeded(job
->spaceneeded
)
66 wxThread
* CPartFileConvert::s_convertPfThread
= NULL
;
67 std::list
<ConvertJob
*> CPartFileConvert::s_jobs
;
68 ConvertJob
* CPartFileConvert::s_pfconverting
= NULL
;
69 wxMutex
CPartFileConvert::s_mutex
;
72 int CPartFileConvert::ScanFolderToAdd(const CPath
& folder
, bool deletesource
)
75 CDirIterator
finder(folder
);
77 CPath file
= finder
.GetFirstFile(CDirIterator::File
, wxT("*.part.met"));
79 ConvertToeMule(folder
.JoinPaths(file
), deletesource
);
80 file
= finder
.GetNextFile();
84 file = finder.GetFirstFile(CDirIterator::File, wxT("*.sd"));
85 while (!file.IsEmpty()) {
86 ConvertToeMule(file, deletesource);
87 file = finder.GetNextFile();
92 file
= finder
.GetFirstFile(CDirIterator::Dir
, wxT("*.*"));
94 ScanFolderToAdd(folder
.JoinPaths(file
), deletesource
);
96 file
= finder
.GetNextFile();
102 void CPartFileConvert::ConvertToeMule(const CPath
& file
, bool deletesource
)
104 if (!file
.FileExists()) {
108 ConvertJob
* newjob
= new ConvertJob();
109 newjob
->folder
= file
;
110 newjob
->removeSource
= deletesource
;
111 newjob
->state
= CONV_QUEUE
;
113 wxMutexLocker
lock(s_mutex
);
115 s_jobs
.push_back(newjob
);
117 Notify_ConvertUpdateJobInfo(newjob
);
122 void CPartFileConvert::StartThread()
124 if (!s_convertPfThread
) {
125 s_convertPfThread
= new CPartFileConvert();
127 switch ( s_convertPfThread
->Create() ) {
128 case wxTHREAD_NO_ERROR
:
129 AddDebugLogLineM( false, logPfConvert
, wxT("A new thread has been created.") );
131 case wxTHREAD_RUNNING
:
132 AddDebugLogLineM( true, logPfConvert
, wxT("Error, attempt to create an already running thread!") );
134 case wxTHREAD_NO_RESOURCE
:
135 AddDebugLogLineM( true, logPfConvert
, wxT("Error, attempt to create a thread without resources!") );
138 AddDebugLogLineM( true, logPfConvert
, wxT("Error, unknown error attempting to create a thread!") );
141 // The thread shouldn't hog the CPU, as it will already be hogging the HD
142 s_convertPfThread
->SetPriority(WXTHREAD_MIN_PRIORITY
);
144 s_convertPfThread
->Run();
148 void CPartFileConvert::StopThread()
150 if (s_convertPfThread
) {
151 s_convertPfThread
->Delete();
156 AddLogLineNS(_("Waiting for partfile convert thread to die..."));
157 while (s_convertPfThread
) {
162 wxThread::ExitCode
CPartFileConvert::Entry()
168 // search next queued job and start it
170 wxMutexLocker
lock(s_mutex
);
171 s_pfconverting
= NULL
;
172 for (std::list
<ConvertJob
*>::iterator it
= s_jobs
.begin(); it
!= s_jobs
.end(); ++it
) {
173 s_pfconverting
= *it
;
174 if (s_pfconverting
->state
== CONV_QUEUE
) {
177 s_pfconverting
= NULL
;
182 if (s_pfconverting
) {
184 wxMutexLocker
lock(s_mutex
);
185 s_pfconverting
->state
= CONV_INPROGRESS
;
188 Notify_ConvertUpdateJobInfo(s_pfconverting
);
190 ConvStatus convertResult
= performConvertToeMule(s_pfconverting
->folder
);
192 wxMutexLocker
lock(s_mutex
);
193 s_pfconverting
->state
= convertResult
;
196 if (s_pfconverting
->state
== CONV_OK
) {
201 wxMutexLocker
lock(s_mutex
);
202 DeleteContents(s_jobs
);
206 Notify_ConvertUpdateJobInfo(s_pfconverting
);
208 AddLogLineM(true, CFormat(_("Importing %s: %s")) % s_pfconverting
->folder
% GetConversionState(s_pfconverting
->state
));
210 break; // nothing more to do now
215 Notify_ConvertClearInfos();
218 theApp
->sharedfiles
->PublishNextTurn();
221 AddDebugLogLineM(false, logPfConvert
, wxT("No more jobs on queue, exiting from thread."));
223 s_convertPfThread
= NULL
;
228 ConvStatus
CPartFileConvert::performConvertToeMule(const CPath
& fileName
)
230 wxString filepartindex
, buffer
;
233 CPath folder
= fileName
.GetPath();
234 CPath partfile
= fileName
.GetFullName();
237 CDirIterator
finder(folder
);
239 Notify_ConvertUpdateProgressFull(0, _("Reading temp folder"), s_pfconverting
->folder
.GetPrintable());
241 filepartindex
= partfile
.RemoveAllExt().GetRaw();
243 Notify_ConvertUpdateProgress(4, _("Retrieving basic information from download info file"));
245 CPartFile
* file
= new CPartFile();
246 s_pfconverting
->partmettype
= file
->LoadPartFile(folder
, partfile
, false, true);
248 switch (s_pfconverting
->partmettype
) {
252 return CONV_BADFORMAT
;
255 CPath oldfile
= folder
.JoinPaths(partfile
.RemoveExt());
258 wxMutexLocker
lock(s_mutex
);
259 s_pfconverting
->size
= file
->GetFileSize();
260 s_pfconverting
->filename
= file
->GetFileName();
261 s_pfconverting
->filehash
= file
->GetFileHash().Encode();
264 Notify_ConvertUpdateJobInfo(s_pfconverting
);
266 if (theApp
->downloadqueue
->GetFileByID(file
->GetFileHash())) {
268 return CONV_ALREADYEXISTS
;
271 if (s_pfconverting
->partmettype
== PMT_SPLITTED
) {
272 char *ba
= new char [PARTSIZE
];
278 unsigned maxindex
= 0;
279 unsigned partfilecount
= 0;
280 CPath filePath
= finder
.GetFirstFile(CDirIterator::File
, filepartindex
+ wxT(".*.part"));
281 while (filePath
.IsOk()) {
284 filePath
.GetFullName().RemoveExt().GetExt().ToLong(&l
);
285 fileindex
= (unsigned)l
;
286 filePath
= finder
.GetNextFile();
287 // GonoszTopi - why the hell does eMule need this??
288 //if (fileindex == 0) continue;
289 if (fileindex
> maxindex
) maxindex
= fileindex
;
293 wxMutexLocker
lock(s_mutex
);
294 if (partfilecount
> 0) {
295 stepperpart
= (80.0f
/ partfilecount
);
296 if (maxindex
* PARTSIZE
<= s_pfconverting
->size
) {
297 s_pfconverting
->spaceneeded
= maxindex
* PARTSIZE
;
299 s_pfconverting
->spaceneeded
= ((s_pfconverting
->size
/ PARTSIZE
) * PARTSIZE
) + (s_pfconverting
->size
% PARTSIZE
);
303 s_pfconverting
->spaceneeded
= 0;
307 Notify_ConvertUpdateJobInfo(s_pfconverting
);
309 sint64 freespace
= CPath::GetFreeSpaceAt(thePrefs::GetTempDir());
310 if (freespace
!= wxInvalidOffset
) {
311 if (static_cast<uint64
>(freespace
) < maxindex
* PARTSIZE
) {
314 return CONV_OUTOFDISKSPACE
;
318 // create new partmetfile, and remember the new name
319 file
->CreatePartFile();
320 newfilename
= file
->GetFullName();
322 Notify_ConvertUpdateProgress(8, _("Creating destination file"));
324 file
->m_hpartfile
.SetLength( s_pfconverting
->spaceneeded
);
326 unsigned curindex
= 0;
327 CPath filename
= finder
.GetFirstFile(CDirIterator::File
, filepartindex
+ wxT(".*.part"));
328 while (filename
.IsOk()) {
331 buffer
= wxString::Format(_("Loading data from old download file (%u of %u)"), curindex
, partfilecount
);
333 Notify_ConvertUpdateProgress(10 + (curindex
* stepperpart
), buffer
);
336 filename
.GetFullName().RemoveExt().GetExt().ToLong(&l
);
337 fileindex
= (unsigned)l
;
338 if (fileindex
== 0) {
339 filename
= finder
.GetNextFile();
343 uint32 chunkstart
= (fileindex
- 1) * PARTSIZE
;
345 // open, read data of the part-part-file into buffer, close file
346 inputfile
.Open(filename
, CFile::read
);
347 uint64 toReadWrite
= std::min
<uint64
>(PARTSIZE
, inputfile
.GetLength());
348 inputfile
.Read(ba
, toReadWrite
);
351 buffer
= wxString::Format(_("Saving data block into new single download file (%u of %u)"), curindex
, partfilecount
);
353 Notify_ConvertUpdateProgress(10 + (curindex
* stepperpart
), buffer
);
355 // write the buffered data
356 file
->m_hpartfile
.WriteAt(ba
, chunkstart
, toReadWrite
);
358 filename
= finder
.GetNextFile();
361 } catch (const CSafeIOException
& e
) {
362 AddDebugLogLineM(true, logPfConvert
, wxT("IO error while converting partfiles: ") + e
.what());
369 file
->m_hpartfile
.Close();
371 // import an external common format partdownload
372 else //if (pfconverting->partmettype==PMT_DEFAULTOLD || pfconverting->partmettype==PMT_NEWOLD || Shareaza )
374 if (!s_pfconverting
->removeSource
) {
375 wxMutexLocker
lock(s_mutex
);
376 s_pfconverting
->spaceneeded
= oldfile
.GetFileSize();
379 Notify_ConvertUpdateJobInfo(s_pfconverting
);
381 sint64 freespace
= CPath::GetFreeSpaceAt(thePrefs::GetTempDir());
382 if (freespace
== wxInvalidOffset
) {
385 } else if (!s_pfconverting
->removeSource
&& (freespace
< s_pfconverting
->spaceneeded
)) {
387 return CONV_OUTOFDISKSPACE
;
390 file
->CreatePartFile();
391 newfilename
= file
->GetFullName();
393 file
->m_hpartfile
.Close();
397 Notify_ConvertUpdateProgress(92, _("Copy"));
399 CPath::RemoveFile(newfilename
.RemoveExt());
400 if (!oldfile
.FileExists()) {
401 // data file does not exist. well, then create a 0 byte big one
403 ret
= datafile
.Create(newfilename
.RemoveExt());
404 } else if (s_pfconverting
->removeSource
) {
405 ret
= CPath::RenameFile(oldfile
, newfilename
.RemoveExt());
407 ret
= CPath::CloneFile(oldfile
, newfilename
.RemoveExt(), false);
417 Notify_ConvertUpdateProgress(94, _("Retrieving source downloadfile information"));
419 CPath::RemoveFile(newfilename
);
420 if (s_pfconverting
->removeSource
) {
421 CPath::RenameFile(folder
.JoinPaths(partfile
), newfilename
);
423 CPath::CloneFile(folder
.JoinPaths(partfile
), newfilename
, false);
426 file
->m_hashlist
.clear();
428 if (!file
->LoadPartFile(thePrefs::GetTempDir(), file
->GetPartMetFileName(), false)) {
431 return CONV_BADFORMAT
;
434 if (s_pfconverting
->partmettype
== PMT_NEWOLD
|| s_pfconverting
->partmettype
== PMT_SPLITTED
) {
435 file
->SetCompletedSize(file
->transferred
);
436 file
->m_iGainDueToCompression
= 0;
437 file
->m_iLostDueToCorruption
= 0;
440 Notify_ConvertUpdateProgress(100, _("Adding download and saving new partfile"));
442 theApp
->downloadqueue
->AddDownload(file
, thePrefs::AddNewFilesPaused(), 0);
443 file
->SavePartFile();
445 if (file
->GetStatus(true) == PS_READY
) {
446 theApp
->sharedfiles
->SafeAddKFile(file
); // part files are always shared files
449 if (s_pfconverting
->removeSource
) {
450 CPath oldFile
= finder
.GetFirstFile(CDirIterator::File
, filepartindex
+ wxT(".*"));
451 while (oldFile
.IsOk()) {
452 CPath::RemoveFile(folder
.JoinPaths(oldFile
));
453 oldFile
= finder
.GetNextFile();
456 if (s_pfconverting
->partmettype
== PMT_SPLITTED
) {
457 CPath::RemoveDir(folder
);
464 // Notification handlers
466 void CPartFileConvert::RemoveJob(unsigned id
)
468 wxMutexLocker
lock(s_mutex
);
469 for (std::list
<ConvertJob
*>::iterator it
= s_jobs
.begin(); it
!= s_jobs
.end(); ++it
) {
470 if ((*it
)->id
== id
&& (*it
)->state
!= CONV_INPROGRESS
) {
471 ConvertJob
*job
= *it
;
473 Notify_ConvertRemoveJobInfo(id
);
480 void CPartFileConvert::RetryJob(unsigned id
)
482 wxMutexLocker
lock(s_mutex
);
483 for (std::list
<ConvertJob
*>::iterator it
= s_jobs
.begin(); it
!= s_jobs
.end(); ++it
) {
484 if ((*it
)->id
== id
&& (*it
)->state
!= CONV_INPROGRESS
&& (*it
)->state
!= CONV_OK
) {
485 (*it
)->state
= CONV_QUEUE
;
486 Notify_ConvertUpdateJobInfo(*it
);
493 void CPartFileConvert::ReaddAllJobs()
495 wxMutexLocker
lock(s_mutex
);
496 for (std::list
<ConvertJob
*>::iterator it
= s_jobs
.begin(); it
!= s_jobs
.end(); ++it
) {
497 Notify_ConvertUpdateJobInfo(*it
);