Improve speed of category tab title updates
[amule.git] / src / PartFileConvert.cpp
blob402bc8df633c6af171438a7da772e0a5101fbe25
1 //
2 // This file is part of the aMule Project.
3 //
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 )
6 //
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
9 // respective authors.
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
26 #include <wx/app.h>
28 #include "PartFileConvert.h"
30 #include "amule.h"
31 #include "DownloadQueue.h"
32 #include <common/Format.h>
33 #include "Logger.h"
34 #include "PartFile.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;
45 struct ConvertJob {
46 unsigned id;
47 CPath folder;
48 CPath filename;
49 wxString filehash;
50 int format;
51 ConvStatus state;
52 uint32_t size;
53 uint32_t spaceneeded;
54 uint8 partmettype;
55 bool removeSource;
56 ConvertJob() { id=s_nextJobId++; size=0; spaceneeded=0; partmettype=PMT_UNKNOWN; removeSource=true; }
59 ConvertInfo::ConvertInfo(ConvertJob *job)
60 : id(job->id),
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)
74 int count = 0;
75 CDirIterator finder(folder);
77 CPath file = finder.GetFirstFile(CDirIterator::File, wxT("*.part.met"));
78 while (file.IsOk()) {
79 ConvertToeMule(folder.JoinPaths(file), deletesource);
80 file = finder.GetNextFile();
81 count++;
83 /* Shareaza
84 file = finder.GetFirstFile(CDirIterator::File, wxT("*.sd"));
85 while (!file.IsEmpty()) {
86 ConvertToeMule(file, deletesource);
87 file = finder.GetNextFile();
88 count++;
92 file = finder.GetFirstFile(CDirIterator::Dir, wxT("*.*"));
93 while (file.IsOk()) {
94 ScanFolderToAdd(folder.JoinPaths(file), deletesource);
96 file = finder.GetNextFile();
99 return count;
102 void CPartFileConvert::ConvertToeMule(const CPath& file, bool deletesource)
104 if (!file.FileExists()) {
105 return;
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);
119 StartThread();
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.") );
130 break;
131 case wxTHREAD_RUNNING:
132 AddDebugLogLineC( logPfConvert, wxT("Error, attempt to create an already running thread!") );
133 break;
134 case wxTHREAD_NO_RESOURCE:
135 AddDebugLogLineC( logPfConvert, wxT("Error, attempt to create a thread without resources!") );
136 break;
137 default:
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();
152 } else {
153 return;
156 AddLogLineNS(_("Waiting for partfile convert thread to die..."));
157 while (s_convertPfThread) {
158 wxSleep(1);
162 wxThread::ExitCode CPartFileConvert::Entry()
164 int imported = 0;
166 for (;;)
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;
175 break;
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) {
195 ++imported;
198 Notify_ConvertUpdateJobInfo(s_pfconverting);
200 AddLogLineC(CFormat(_("Importing %s: %s")) % s_pfconverting->folder % GetConversionState(s_pfconverting->state));
202 if (TestDestroy()) {
203 wxMutexLocker lock(s_mutex);
204 DeleteContents(s_jobs);
205 break;
207 } else {
208 break; // nothing more to do now
212 // clean up
213 Notify_ConvertClearInfos();
215 if (imported) {
216 theApp->sharedfiles->PublishNextTurn();
219 AddDebugLogLineN(logPfConvert, wxT("No more jobs on queue, exiting from thread."));
221 s_convertPfThread = NULL;
223 return NULL;
226 ConvStatus CPartFileConvert::performConvertToeMule(const CPath& fileName)
228 wxString filepartindex, buffer;
229 unsigned fileindex;
231 CPath folder = fileName.GetPath();
232 CPath partfile = fileName.GetFullName();
233 CPath newfilename;
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) {
247 case PMT_UNKNOWN:
248 case PMT_BADFORMAT:
249 delete file;
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())) {
265 delete file;
266 return CONV_ALREADYEXISTS;
269 if (s_pfconverting->partmettype == PMT_SPLITTED) {
270 char *ba = new char [PARTSIZE];
272 try {
273 CFile inputfile;
275 // just count
276 unsigned maxindex = 0;
277 unsigned partfilecount = 0;
278 CPath filePath = finder.GetFirstFile(CDirIterator::File, filepartindex + wxT(".*.part"));
279 while (filePath.IsOk()) {
280 long l;
281 ++partfilecount;
282 filePath.GetFullName().RemoveExt().GetExt().ToLong(&l);
283 fileindex = (unsigned)l;
284 filePath = finder.GetNextFile();
285 if (fileindex > maxindex) maxindex = fileindex;
287 float stepperpart;
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;
294 } else {
295 s_pfconverting->spaceneeded = s_pfconverting->size;
297 } else {
298 stepperpart = 80.0f;
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) {
308 delete file;
309 delete [] ba;
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()) {
325 // stats
326 ++curindex;
327 buffer = CFormat(_("Loading data from old download file (%u of %u)")) % curindex % partfilecount;
329 Notify_ConvertUpdateProgress(10 + (curindex * stepperpart), buffer);
331 long l;
332 filename.GetFullName().RemoveExt().GetExt().ToLong(&l);
333 fileindex = (unsigned)l;
334 if (fileindex == 0) {
335 filename = finder.GetNextFile();
336 continue;
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);
345 inputfile.Close();
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();
356 delete[] ba;
357 } catch (const CSafeIOException& e) {
358 AddDebugLogLineC(logPfConvert, wxT("IO error while converting partfiles: ") + e.what());
360 delete[] ba;
361 file->Delete();
362 return CONV_IOERROR;
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) {
379 delete file;
380 return CONV_IOERROR;
381 } else if (freespace < s_pfconverting->spaceneeded) {
382 delete file;
383 return CONV_OUTOFDISKSPACE;
386 file->CreatePartFile();
387 newfilename = file->GetFullName();
389 file->m_hpartfile.Close();
391 bool ret = false;
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
398 CFile datafile;
399 ret = datafile.Create(newfilename.RemoveExt());
400 } else if (s_pfconverting->removeSource) {
401 ret = CPath::RenameFile(oldfile, newfilename.RemoveExt());
402 } else {
403 ret = CPath::CloneFile(oldfile, newfilename.RemoveExt(), false);
405 if (!ret) {
406 file->Delete();
407 //delete file;
408 return CONV_FAILED;
413 Notify_ConvertUpdateProgress(94, _("Retrieving source downloadfile information"));
415 CPath::RemoveFile(newfilename);
416 if (s_pfconverting->removeSource) {
417 CPath::RenameFile(folder.JoinPaths(partfile), newfilename);
418 } else {
419 CPath::CloneFile(folder.JoinPaths(partfile), newfilename, false);
422 file->m_hashlist.clear();
424 if (!file->LoadPartFile(thePrefs::GetTempDir(), file->GetPartMetFileName(), false)) {
425 //delete file;
426 file->Delete();
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);
457 return CONV_OK;
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;
468 s_jobs.erase(it);
469 Notify_ConvertRemoveJobInfo(id);
470 delete job;
471 break;
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);
483 StartThread();
484 break;
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);