2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 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 AddDebugLogLineN( logPfConvert
, wxT("A new thread has been created.") );
131 case wxTHREAD_RUNNING
:
132 AddDebugLogLineC( logPfConvert
, wxT("Error, attempt to create an already running thread!") );
134 case wxTHREAD_NO_RESOURCE
:
135 AddDebugLogLineC( logPfConvert
, wxT("Error, attempt to create a thread without resources!") );
138 AddDebugLogLineC( 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 if ((*it
)->state
== CONV_QUEUE
) {
174 s_pfconverting
= *it
;
180 if (s_pfconverting
) {
182 wxMutexLocker
lock(s_mutex
);
183 s_pfconverting
->state
= CONV_INPROGRESS
;
186 Notify_ConvertUpdateJobInfo(s_pfconverting
);
188 ConvStatus convertResult
= performConvertToeMule(s_pfconverting
->folder
);
190 wxMutexLocker
lock(s_mutex
);
191 s_pfconverting
->state
= convertResult
;
194 if (s_pfconverting
->state
== CONV_OK
) {
198 Notify_ConvertUpdateJobInfo(s_pfconverting
);
200 AddLogLineC(CFormat(_("Importing %s: %s")) % s_pfconverting
->folder
% GetConversionState(s_pfconverting
->state
));
203 wxMutexLocker
lock(s_mutex
);
204 DeleteContents(s_jobs
);
208 break; // nothing more to do now
213 Notify_ConvertClearInfos();
216 theApp
->sharedfiles
->PublishNextTurn();
219 AddDebugLogLineN(logPfConvert
, wxT("No more jobs on queue, exiting from thread."));
221 s_convertPfThread
= NULL
;
226 ConvStatus
CPartFileConvert::performConvertToeMule(const CPath
& fileName
)
228 wxString filepartindex
, buffer
;
231 CPath folder
= fileName
.GetPath();
232 CPath partfile
= fileName
.GetFullName();
235 CDirIterator
finder(folder
);
237 Notify_ConvertUpdateProgressFull(0, _("Reading temp folder"), s_pfconverting
->folder
.GetPrintable());
239 filepartindex
= partfile
.RemoveAllExt().GetRaw();
241 Notify_ConvertUpdateProgress(4, _("Retrieving basic information from download info file"));
243 CPartFile
* file
= new CPartFile();
244 s_pfconverting
->partmettype
= file
->LoadPartFile(folder
, partfile
, false, true);
246 switch (s_pfconverting
->partmettype
) {
250 return CONV_BADFORMAT
;
253 CPath oldfile
= folder
.JoinPaths(partfile
.RemoveExt());
256 wxMutexLocker
lock(s_mutex
);
257 s_pfconverting
->size
= file
->GetFileSize();
258 s_pfconverting
->filename
= file
->GetFileName();
259 s_pfconverting
->filehash
= file
->GetFileHash().Encode();
262 Notify_ConvertUpdateJobInfo(s_pfconverting
);
264 if (theApp
->downloadqueue
->GetFileByID(file
->GetFileHash())) {
266 return CONV_ALREADYEXISTS
;
269 if (s_pfconverting
->partmettype
== PMT_SPLITTED
) {
270 char *ba
= new char [PARTSIZE
];
276 unsigned maxindex
= 0;
277 unsigned partfilecount
= 0;
278 CPath filePath
= finder
.GetFirstFile(CDirIterator::File
, filepartindex
+ wxT(".*.part"));
279 while (filePath
.IsOk()) {
282 filePath
.GetFullName().RemoveExt().GetExt().ToLong(&l
);
283 fileindex
= (unsigned)l
;
284 filePath
= finder
.GetNextFile();
285 if (fileindex
> maxindex
) maxindex
= fileindex
;
289 wxMutexLocker
lock(s_mutex
);
290 if (partfilecount
> 0) {
291 stepperpart
= (80.0f
/ partfilecount
);
292 if (maxindex
* PARTSIZE
<= s_pfconverting
->size
) {
293 s_pfconverting
->spaceneeded
= maxindex
* PARTSIZE
;
295 s_pfconverting
->spaceneeded
= s_pfconverting
->size
;
299 s_pfconverting
->spaceneeded
= 0;
303 Notify_ConvertUpdateJobInfo(s_pfconverting
);
305 sint64 freespace
= CPath::GetFreeSpaceAt(thePrefs::GetTempDir());
306 if (freespace
!= wxInvalidOffset
) {
307 if (static_cast<uint64
>(freespace
) < maxindex
* PARTSIZE
) {
310 return CONV_OUTOFDISKSPACE
;
314 // create new partmetfile, and remember the new name
315 file
->CreatePartFile();
316 newfilename
= file
->GetFullName();
318 Notify_ConvertUpdateProgress(8, _("Creating destination file"));
320 file
->m_hpartfile
.SetLength( s_pfconverting
->spaceneeded
);
322 unsigned curindex
= 0;
323 CPath filename
= finder
.GetFirstFile(CDirIterator::File
, filepartindex
+ wxT(".*.part"));
324 while (filename
.IsOk()) {
327 buffer
= CFormat(_("Loading data from old download file (%u of %u)")) % curindex
% partfilecount
;
329 Notify_ConvertUpdateProgress(10 + (curindex
* stepperpart
), buffer
);
332 filename
.GetFullName().RemoveExt().GetExt().ToLong(&l
);
333 fileindex
= (unsigned)l
;
334 if (fileindex
== 0) {
335 filename
= finder
.GetNextFile();
339 uint32 chunkstart
= (fileindex
- 1) * PARTSIZE
;
341 // open, read data of the part-part-file into buffer, close file
342 inputfile
.Open(filename
, CFile::read
);
343 uint64 toReadWrite
= std::min
<uint64
>(PARTSIZE
, inputfile
.GetLength());
344 inputfile
.Read(ba
, toReadWrite
);
347 buffer
= CFormat(_("Saving data block into new single download file (%u of %u)")) % curindex
% partfilecount
;
349 Notify_ConvertUpdateProgress(10 + (curindex
* stepperpart
), buffer
);
351 // write the buffered data
352 file
->m_hpartfile
.WriteAt(ba
, chunkstart
, toReadWrite
);
354 filename
= finder
.GetNextFile();
357 } catch (const CSafeIOException
& e
) {
358 AddDebugLogLineC(logPfConvert
, wxT("IO error while converting partfiles: ") + e
.what());
365 file
->m_hpartfile
.Close();
367 // import an external common format partdownload
368 else //if (pfconverting->partmettype==PMT_DEFAULTOLD || pfconverting->partmettype==PMT_NEWOLD || Shareaza )
370 if (!s_pfconverting
->removeSource
) {
371 wxMutexLocker
lock(s_mutex
);
372 s_pfconverting
->spaceneeded
= oldfile
.GetFileSize();
375 Notify_ConvertUpdateJobInfo(s_pfconverting
);
377 sint64 freespace
= CPath::GetFreeSpaceAt(thePrefs::GetTempDir());
378 if (freespace
== wxInvalidOffset
) {
381 } else if (freespace
< s_pfconverting
->spaceneeded
) {
383 return CONV_OUTOFDISKSPACE
;
386 file
->CreatePartFile();
387 newfilename
= file
->GetFullName();
389 file
->m_hpartfile
.Close();
393 Notify_ConvertUpdateProgress(92, _("Copy"));
395 CPath::RemoveFile(newfilename
.RemoveExt());
396 if (!oldfile
.FileExists()) {
397 // data file does not exist. well, then create a 0 byte big one
399 ret
= datafile
.Create(newfilename
.RemoveExt());
400 } else if (s_pfconverting
->removeSource
) {
401 ret
= CPath::RenameFile(oldfile
, newfilename
.RemoveExt());
403 ret
= CPath::CloneFile(oldfile
, newfilename
.RemoveExt(), false);
413 Notify_ConvertUpdateProgress(94, _("Retrieving source downloadfile information"));
415 CPath::RemoveFile(newfilename
);
416 if (s_pfconverting
->removeSource
) {
417 CPath::RenameFile(folder
.JoinPaths(partfile
), newfilename
);
419 CPath::CloneFile(folder
.JoinPaths(partfile
), newfilename
, false);
422 file
->m_hashlist
.clear();
424 if (!file
->LoadPartFile(thePrefs::GetTempDir(), file
->GetPartMetFileName(), false)) {
427 return CONV_BADFORMAT
;
430 if (s_pfconverting
->partmettype
== PMT_NEWOLD
|| s_pfconverting
->partmettype
== PMT_SPLITTED
) {
431 file
->SetCompletedSize(file
->transferred
);
432 file
->m_iGainDueToCompression
= 0;
433 file
->m_iLostDueToCorruption
= 0;
436 Notify_ConvertUpdateProgress(100, _("Adding download and saving new partfile"));
438 theApp
->downloadqueue
->AddDownload(file
, thePrefs::AddNewFilesPaused(), 0);
439 file
->SavePartFile();
441 if (file
->GetStatus(true) == PS_READY
) {
442 theApp
->sharedfiles
->SafeAddKFile(file
); // part files are always shared files
445 if (s_pfconverting
->removeSource
) {
446 CPath oldFile
= finder
.GetFirstFile(CDirIterator::File
, filepartindex
+ wxT(".*"));
447 while (oldFile
.IsOk()) {
448 CPath::RemoveFile(folder
.JoinPaths(oldFile
));
449 oldFile
= finder
.GetNextFile();
452 if (s_pfconverting
->partmettype
== PMT_SPLITTED
) {
453 CPath::RemoveDir(folder
);
460 // Notification handlers
462 void CPartFileConvert::RemoveJob(unsigned id
)
464 wxMutexLocker
lock(s_mutex
);
465 for (std::list
<ConvertJob
*>::iterator it
= s_jobs
.begin(); it
!= s_jobs
.end(); ++it
) {
466 if ((*it
)->id
== id
&& (*it
)->state
!= CONV_INPROGRESS
) {
467 ConvertJob
*job
= *it
;
469 Notify_ConvertRemoveJobInfo(id
);
476 void CPartFileConvert::RetryJob(unsigned id
)
478 wxMutexLocker
lock(s_mutex
);
479 for (std::list
<ConvertJob
*>::iterator it
= s_jobs
.begin(); it
!= s_jobs
.end(); ++it
) {
480 if ((*it
)->id
== id
&& (*it
)->state
!= CONV_INPROGRESS
&& (*it
)->state
!= CONV_OK
) {
481 (*it
)->state
= CONV_QUEUE
;
482 Notify_ConvertUpdateJobInfo(*it
);
489 void CPartFileConvert::ReaddAllJobs()
491 wxMutexLocker
lock(s_mutex
);
492 for (std::list
<ConvertJob
*>::iterator it
= s_jobs
.begin(); it
!= s_jobs
.end(); ++it
) {
493 Notify_ConvertUpdateJobInfo(*it
);