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"
32 // This allows us to compile muuli_wdr
34 CConvertListCtrl::CConvertListCtrl(
35 wxWindow
* WXUNUSED(parent
),
36 wxWindowID
WXUNUSED(winid
),
37 const wxPoint
& WXUNUSED(pos
),
38 const wxSize
& WXUNUSED(size
),
40 const wxValidator
& WXUNUSED(validator
),
41 const wxString
& WXUNUSED(name
))
49 #include "DownloadQueue.h"
50 #include <common/Format.h>
53 #include "Preferences.h"
54 #include "SharedFileList.h"
55 #include <common/FileFunctions.h>
57 #include <wx/stdpaths.h>
58 #include "muuli_wdr.h"
83 ConvertJob() { size
=0; spaceneeded
=0; partmettype
=PMT_UNKNOWN
; removeSource
= true; }
87 wxThread
* CPartFileConvert::s_convertPfThread
= NULL
;
88 std::list
<ConvertJob
*> CPartFileConvert::s_jobs
;
89 ConvertJob
* CPartFileConvert::s_pfconverting
= NULL
;
90 wxMutex
CPartFileConvert::s_mutex
;
92 CPartFileConvertDlg
* CPartFileConvert::s_convertgui
= NULL
;
94 int CPartFileConvert::ScanFolderToAdd(const CPath
& folder
, bool deletesource
)
97 CDirIterator
finder(folder
);
99 CPath file
= finder
.GetFirstFile(CDirIterator::File
, wxT("*.part.met"));
100 while (file
.IsOk()) {
101 ConvertToeMule(folder
.JoinPaths(file
), deletesource
);
102 file
= finder
.GetNextFile();
106 file = finder.GetFirstFile(CDirIterator::File, wxT("*.sd"));
107 while (!file.IsEmpty()) {
108 ConvertToeMule(file, deletesource);
109 file = finder.GetNextFile();
114 file
= finder
.GetFirstFile(CDirIterator::Dir
, wxT("*.*"));
115 while (file
.IsOk()) {
116 ScanFolderToAdd(folder
.JoinPaths(file
), deletesource
);
118 file
= finder
.GetNextFile();
124 void CPartFileConvert::ConvertToeMule(const CPath
& file
, bool deletesource
)
126 if (!file
.FileExists()) {
130 ConvertJob
* newjob
= new ConvertJob();
131 newjob
->folder
= file
;
132 newjob
->removeSource
= deletesource
;
133 newjob
->state
= CONV_QUEUE
;
135 wxMutexLocker
lock(s_mutex
);
137 s_jobs
.push_back(newjob
);
140 s_convertgui
->AddJob(newjob
);
146 void CPartFileConvert::StartThread()
148 if (!s_convertPfThread
) {
149 s_convertPfThread
= new CPartFileConvert();
151 switch ( s_convertPfThread
->Create() ) {
152 case wxTHREAD_NO_ERROR
:
153 AddDebugLogLineM( false, logPfConvert
, wxT("A new thread has been created.") );
155 case wxTHREAD_RUNNING
:
156 AddDebugLogLineM( true, logPfConvert
, wxT("Error, attempt to create a already running thread!") );
158 case wxTHREAD_NO_RESOURCE
:
159 AddDebugLogLineM( true, logPfConvert
, wxT("Error, attempt to create a thread without resources!") );
162 AddDebugLogLineM( true, logPfConvert
, wxT("Error, unknown error attempting to create a thread!") );
165 // The thread shouldn't hog the CPU, as it will already be hogging the HD
166 s_convertPfThread
->SetPriority(WXTHREAD_MIN_PRIORITY
);
168 s_convertPfThread
->Run();
172 void CPartFileConvert::StopThread()
174 if (s_convertPfThread
) {
175 s_convertPfThread
->Delete();
180 AddLogLineNS(_("Waiting for partfile convert thread to die..."));
181 while (s_convertPfThread
) {
186 wxThread::ExitCode
CPartFileConvert::Entry()
192 // search next queued job and start it
194 wxMutexLocker
lock(s_mutex
);
195 s_pfconverting
= NULL
;
196 for (std::list
<ConvertJob
*>::iterator it
= s_jobs
.begin(); it
!= s_jobs
.end(); ++it
) {
197 s_pfconverting
= *it
;
198 if (s_pfconverting
->state
== CONV_QUEUE
) {
201 s_pfconverting
= NULL
;
206 if (s_pfconverting
) {
208 wxMutexLocker
lock(s_mutex
);
209 s_pfconverting
->state
= CONV_INPROGRESS
;
212 UpdateGUI(s_pfconverting
);
214 int convertResult
= performConvertToeMule(s_pfconverting
->folder
);
216 wxMutexLocker
lock(s_mutex
);
217 s_pfconverting
->state
= convertResult
;
220 if (s_pfconverting
->state
== CONV_OK
) {
225 wxMutexLocker
lock(s_mutex
);
226 for (std::list
<ConvertJob
*>::iterator it
= s_jobs
.begin(); it
!= s_jobs
.end(); ++it
) {
234 UpdateGUI(s_pfconverting
);
236 AddLogLineM(true, CFormat(_("Importing %s: %s")) % s_pfconverting
->folder
% GetReturncodeText(s_pfconverting
->state
));
238 break; // nothing more to do now
246 theApp
->sharedfiles
->PublishNextTurn();
249 AddDebugLogLineM(false, logPfConvert
, wxT("No more jobs on queue, exiting from thread."));
251 s_convertPfThread
= NULL
;
256 int CPartFileConvert::performConvertToeMule(const CPath
& fileName
)
258 wxString filepartindex
, buffer
;
261 CPath folder
= fileName
.GetPath();
262 CPath partfile
= fileName
.GetFullName();
265 CDirIterator
finder(folder
);
267 UpdateGUI(0, _("Reading temp folder"), true);
269 filepartindex
= partfile
.RemoveAllExt().GetRaw();
271 UpdateGUI(4, _("Retrieving basic information from download info file"));
273 CPartFile
* file
= new CPartFile();
274 s_pfconverting
->partmettype
= file
->LoadPartFile(folder
, partfile
, false, true);
276 switch (s_pfconverting
->partmettype
) {
280 return CONV_BADFORMAT
;
283 CPath oldfile
= folder
.JoinPaths(partfile
.RemoveExt());
286 wxMutexLocker
lock(s_mutex
);
287 s_pfconverting
->size
= file
->GetFileSize();
288 s_pfconverting
->filename
= file
->GetFileName();
289 s_pfconverting
->filehash
= file
->GetFileHash().Encode();
292 UpdateGUI(s_pfconverting
);
294 if (theApp
->downloadqueue
->GetFileByID(file
->GetFileHash())) {
296 return CONV_ALREADYEXISTS
;
299 if (s_pfconverting
->partmettype
== PMT_SPLITTED
) {
300 char *ba
= new char [PARTSIZE
];
306 unsigned maxindex
= 0;
307 unsigned partfilecount
= 0;
308 CPath filePath
= finder
.GetFirstFile(CDirIterator::File
, filepartindex
+ wxT(".*.part"));
309 while (filePath
.IsOk()) {
312 filePath
.GetFullName().RemoveExt().GetExt().ToLong(&l
);
313 fileindex
= (unsigned)l
;
314 filePath
= finder
.GetNextFile();
315 // GonoszTopi - why the hell does eMule need this??
316 //if (fileindex == 0) continue;
317 if (fileindex
> maxindex
) maxindex
= fileindex
;
321 wxMutexLocker
lock(s_mutex
);
322 if (partfilecount
> 0) {
323 stepperpart
= (80.0f
/ partfilecount
);
324 if (maxindex
* PARTSIZE
<= s_pfconverting
->size
) {
325 s_pfconverting
->spaceneeded
= maxindex
* PARTSIZE
;
327 s_pfconverting
->spaceneeded
= ((s_pfconverting
->size
/ PARTSIZE
) * PARTSIZE
) + (s_pfconverting
->size
% PARTSIZE
);
331 s_pfconverting
->spaceneeded
= 0;
335 UpdateGUI(s_pfconverting
);
337 sint64 freespace
= CPath::GetFreeSpaceAt(thePrefs::GetTempDir());
338 if (freespace
!= wxInvalidOffset
) {
339 if (static_cast<uint64
>(freespace
) < maxindex
* PARTSIZE
) {
342 return CONV_OUTOFDISKSPACE
;
346 // create new partmetfile, and remember the new name
347 file
->CreatePartFile();
348 newfilename
= file
->GetFullName();
350 UpdateGUI(8, _("Creating destination file"));
352 file
->m_hpartfile
.SetLength( s_pfconverting
->spaceneeded
);
354 unsigned curindex
= 0;
355 CPath filename
= finder
.GetFirstFile(CDirIterator::File
, filepartindex
+ wxT(".*.part"));
356 while (filename
.IsOk()) {
359 buffer
= wxString::Format(_("Loading data from old download file (%u of %u)"), curindex
, partfilecount
);
361 UpdateGUI(10 + (curindex
* stepperpart
), buffer
);
364 filename
.GetFullName().RemoveExt().GetExt().ToLong(&l
);
365 fileindex
= (unsigned)l
;
366 if (fileindex
== 0) {
367 filename
= finder
.GetNextFile();
371 uint32 chunkstart
= (fileindex
- 1) * PARTSIZE
;
373 // open, read data of the part-part-file into buffer, close file
374 inputfile
.Open(filename
, CFile::read
);
375 uint64 toReadWrite
= std::min
<uint64
>(PARTSIZE
, inputfile
.GetLength());
376 inputfile
.Read(ba
, toReadWrite
);
379 buffer
= wxString::Format(_("Saving data block into new single download file (%u of %u)"), curindex
, partfilecount
);
381 UpdateGUI(10 + (curindex
* stepperpart
), buffer
);
383 // write the buffered data
384 file
->m_hpartfile
.Seek(chunkstart
, wxFromStart
);
385 file
->m_hpartfile
.Write(ba
, toReadWrite
);
387 filename
= finder
.GetNextFile();
390 } catch (const CSafeIOException
& e
) {
391 AddDebugLogLineM(true, logPfConvert
, wxT("IO error while converting partfiles: ") + e
.what());
398 file
->m_hpartfile
.Close();
400 // import an external common format partdownload
401 else //if (pfconverting->partmettype==PMT_DEFAULTOLD || pfconverting->partmettype==PMT_NEWOLD || Shareaza )
403 if (!s_pfconverting
->removeSource
) {
404 wxMutexLocker
lock(s_mutex
);
405 s_pfconverting
->spaceneeded
= oldfile
.GetFileSize();
408 UpdateGUI(s_pfconverting
);
410 sint64 freespace
= CPath::GetFreeSpaceAt(thePrefs::GetTempDir());
411 if (freespace
== wxInvalidOffset
) {
414 } else if (!s_pfconverting
->removeSource
&& (freespace
< s_pfconverting
->spaceneeded
)) {
416 return CONV_OUTOFDISKSPACE
;
419 file
->CreatePartFile();
420 newfilename
= file
->GetFullName().RemoveExt();
422 file
->m_hpartfile
.Close();
426 UpdateGUI(92, _("Copy"));
428 CPath::RemoveFile(newfilename
);
429 if (!oldfile
.FileExists()) {
430 // data file does not exist. well, then create a 0 byte big one
432 ret
= datafile
.Create(newfilename
);
433 } else if (s_pfconverting
->removeSource
) {
434 ret
= CPath::RenameFile(oldfile
, newfilename
);
436 ret
= CPath::CloneFile(oldfile
, newfilename
, false);
446 UpdateGUI(94, _("Retrieving source downloadfile information"));
448 CPath::RemoveFile(newfilename
);
449 if (s_pfconverting
->removeSource
) {
450 CPath::RenameFile(folder
.JoinPaths(partfile
), newfilename
);
452 CPath::CloneFile(folder
.JoinPaths(partfile
), newfilename
, false);
455 file
->m_hashlist
.clear();
457 if (!file
->LoadPartFile(thePrefs::GetTempDir(), file
->GetPartMetFileName(), false)) {
460 return CONV_BADFORMAT
;
463 if (s_pfconverting
->partmettype
== PMT_NEWOLD
|| s_pfconverting
->partmettype
== PMT_SPLITTED
) {
464 file
->SetCompletedSize(file
->transferred
);
465 file
->m_iGainDueToCompression
= 0;
466 file
->m_iLostDueToCorruption
= 0;
469 UpdateGUI(100, _("Adding download and saving new partfile"));
471 theApp
->downloadqueue
->AddDownload(file
, thePrefs::AddNewFilesPaused(), 0);
472 file
->SavePartFile();
474 if (file
->GetStatus(true) == PS_READY
) {
475 theApp
->sharedfiles
->SafeAddKFile(file
); // part files are always shared files
478 if (s_pfconverting
->removeSource
) {
479 CPath oldFile
= finder
.GetFirstFile(CDirIterator::File
, filepartindex
+ wxT(".*"));
480 while (oldFile
.IsOk()) {
481 CPath::RemoveFile(folder
.JoinPaths(oldFile
));
482 oldFile
= finder
.GetNextFile();
485 if (s_pfconverting
->partmettype
== PMT_SPLITTED
) {
486 CPath::RemoveDir(folder
);
493 void CPartFileConvert::UpdateGUI(float percent
, wxString text
, bool fullinfo
)
500 s_convertgui
->m_pb_current
->SetValue((int)percent
);
501 wxString buffer
= wxString::Format(wxT("%.2f %%"), percent
);
502 wxStaticText
* percentlabel
= dynamic_cast<wxStaticText
*>(s_convertgui
->FindWindow(IDC_CONV_PROZENT
));
503 percentlabel
->SetLabel(buffer
);
505 if (!text
.IsEmpty()) {
506 dynamic_cast<wxStaticText
*>(s_convertgui
->FindWindow(IDC_CONV_PB_LABEL
))->SetLabel(text
);
509 percentlabel
->GetParent()->Layout();
512 dynamic_cast<wxStaticBoxSizer
*>(IDC_CURJOB
)->GetStaticBox()->SetLabel(s_pfconverting
->folder
.GetPrintable());
521 void CPartFileConvert::UpdateGUI(ConvertJob
* job
)
527 s_convertgui
->UpdateJobInfo(job
);
534 void CPartFileConvert::ShowGUI(wxWindow
* parent
)
537 s_convertgui
->Show(true);
538 s_convertgui
->Raise();
540 s_convertgui
= new CPartFileConvertDlg(parent
);
541 s_convertgui
->Show(true);
543 wxMutexLocker
lock(s_mutex
);
544 if (s_pfconverting
) {
545 UpdateGUI(s_pfconverting
);
546 UpdateGUI(50, _("Fetching status..."), true);
550 for (std::list
<ConvertJob
*>::iterator it
= s_jobs
.begin(); it
!= s_jobs
.end(); ++it
) {
551 s_convertgui
->AddJob(*it
);
557 void CPartFileConvert::CloseGUI()
560 s_convertgui
->Show(false);
561 s_convertgui
->Destroy();
566 void CPartFileConvert::RemoveAllJobs()
568 wxMutexLocker
lock(s_mutex
);
569 for (std::list
<ConvertJob
*>::iterator it
= s_jobs
.begin(); it
!= s_jobs
.end(); ++it
) {
571 s_convertgui
->RemoveJob(*it
);
578 void CPartFileConvert::RemoveJob(ConvertJob
* job
)
580 wxMutexLocker
lock(s_mutex
);
581 for (std::list
<ConvertJob
*>::iterator it
= s_jobs
.begin(); it
!= s_jobs
.end(); ++it
) {
584 s_convertgui
->RemoveJob(job
);
592 void CPartFileConvert::RemoveAllSuccJobs()
594 wxMutexLocker
lock(s_mutex
);
595 for (std::list
<ConvertJob
*>::iterator it
= s_jobs
.begin(); it
!= s_jobs
.end(); ++it
) {
596 if ((*it
)->state
== CONV_OK
) {
598 s_convertgui
->RemoveJob(*it
);
606 wxString
CPartFileConvert::GetReturncodeText(int ret
)
609 case CONV_OK
: return _("Completed");
610 case CONV_INPROGRESS
: return _("In progress");
611 case CONV_OUTOFDISKSPACE
: return _("ERROR: Out of diskspace");
612 case CONV_PARTMETNOTFOUND
: return _("ERROR: Partmet not found");
613 case CONV_IOERROR
: return _("ERROR: IO error!");
614 case CONV_FAILED
: return _("ERROR: Failed!");
615 case CONV_QUEUE
: return _("Queued");
616 case CONV_ALREADYEXISTS
: return _("Already downloading");
617 case CONV_BADFORMAT
: return _("Unknown or bad tempfile format.");
618 default: return wxT("?");
623 CConvertListCtrl::CConvertListCtrl(wxWindow
* parent
, wxWindowID winid
, const wxPoint
& pos
, const wxSize
& size
, long style
, const wxValidator
& validator
, const wxString
& name
)
624 : wxListCtrl(parent
, winid
, pos
, size
, style
, validator
, name
)
626 InsertColumn(0, _("File name"), wxLIST_FORMAT_LEFT
, 200);
627 InsertColumn(1, _("State"), wxLIST_FORMAT_LEFT
, 100);
628 InsertColumn(2, _("Size"), wxLIST_FORMAT_LEFT
, 100);
629 InsertColumn(3, _("Filehash"), wxLIST_FORMAT_LEFT
, 100);
634 static const char * convert_xpm
[] = {
661 #endif /* ! __WXMSW__ */
663 // Modeless Dialog Implementation
664 // CPartFileConvertDlg dialog
666 BEGIN_EVENT_TABLE(CPartFileConvertDlg
, wxDialog
)
667 EVT_BUTTON(IDC_ADDITEM
, CPartFileConvertDlg::OnAddFolder
)
668 EVT_BUTTON(IDC_RETRY
, CPartFileConvertDlg::RetrySel
)
669 EVT_BUTTON(IDC_CONVREMOVE
, CPartFileConvertDlg::RemoveSel
)
670 EVT_BUTTON(wxID_CANCEL
, CPartFileConvertDlg::OnCloseButton
)
671 EVT_CLOSE(CPartFileConvertDlg::OnClose
)
674 CPartFileConvertDlg::CPartFileConvertDlg(wxWindow
* parent
)
675 : wxDialog(parent
, -1, _("Import partfiles"), wxDefaultPosition
, wxDefaultSize
, wxDEFAULT_DIALOG_STYLE
|wxRESIZE_BORDER
)
677 convertDlg(this, true, true);
679 m_joblist
= CastChild(IDC_JOBLIST
, CConvertListCtrl
);
680 m_pb_current
= CastChild(IDC_CONV_PB_CURRENT
, wxGauge
);
682 SetIcon(wxICON(convert
));
684 // for some reason, if I try to get the mutex from the dialog
685 // it will end up in a deadlock(?) and I have to kill aMule
686 CastChild(IDC_RETRY
, wxButton
)->Enable(false);
687 CastChild(IDC_CONVREMOVE
, wxButton
)->Enable(false);
690 // CPartFileConvertDlg message handlers
692 void CPartFileConvertDlg::OnAddFolder(wxCommandEvent
& WXUNUSED(event
))
694 wxString folder
= ::wxDirSelector(
695 _("Please choose a folder to search for temporary downloads! (subfolders will be included)"),
696 wxStandardPaths::Get().GetDocumentsDir(), wxDD_DEFAULT_STYLE
,
697 wxDefaultPosition
, this);
698 if (!folder
.IsEmpty()) {
699 int reply
= wxMessageBox(_("Do you want the source files of succesfully imported downloads be deleted?"),
700 _("Remove sources?"),
701 wxYES_NO
| wxCANCEL
| wxICON_QUESTION
, this);
702 if (reply
!= wxCANCEL
) {
703 CPartFileConvert::ScanFolderToAdd(CPath(folder
), (reply
== wxYES
));
708 void CPartFileConvertDlg::OnClose(wxCloseEvent
& WXUNUSED(event
))
710 CPartFileConvert::CloseGUI();
713 void CPartFileConvertDlg::OnCloseButton(wxCommandEvent
& WXUNUSED(event
))
715 CPartFileConvert::CloseGUI();
718 void CPartFileConvertDlg::UpdateJobInfo(ConvertJob
* job
)
721 dynamic_cast<wxStaticBoxSizer
*>(IDC_CURJOB
)->GetStaticBox()->SetLabel(_("Waiting..."));
722 CastChild(IDC_CONV_PROZENT
, wxStaticText
)->SetLabel(wxEmptyString
);
723 m_pb_current
->SetValue(0);
724 CastChild(IDC_CONV_PB_LABEL
, wxStaticText
)->SetLabel(wxEmptyString
);
730 // search jobitem in listctrl
731 long itemnr
= m_joblist
->FindItem(-1, reinterpret_cast<wxUIntPtr
>(job
));
733 m_joblist
->SetItem(itemnr
, 0, job
->filename
.IsOk() ? job
->folder
.GetPrintable() : job
->filename
.GetPrintable() );
734 m_joblist
->SetItem(itemnr
, 1, CPartFileConvert::GetReturncodeText(job
->state
) );
736 buffer
= CFormat(_("%s (Disk: %s)")) % CastItoXBytes(job
->size
) % CastItoXBytes(job
->spaceneeded
);
737 m_joblist
->SetItem(itemnr
, 2, buffer
);
739 m_joblist
->SetItem(itemnr
, 2, wxEmptyString
);
741 m_joblist
->SetItem(itemnr
, 3, job
->filehash
);
744 // AddJob(job); why???
748 void CPartFileConvertDlg::RemoveJob(ConvertJob
* job
)
750 long itemnr
= m_joblist
->FindItem(-1, reinterpret_cast<wxUIntPtr
>(job
));
752 m_joblist
->DeleteItem(itemnr
);
756 void CPartFileConvertDlg::AddJob(ConvertJob
* job
)
758 long ix
= m_joblist
->InsertItem(m_joblist
->GetItemCount(), job
->folder
.GetPrintable());
760 m_joblist
->SetItemData(ix
, reinterpret_cast<wxUIntPtr
>(job
));
761 m_joblist
->SetItem(ix
, 1, CPartFileConvert::GetReturncodeText(job
->state
));
765 void CPartFileConvertDlg::RemoveSel(wxCommandEvent
& WXUNUSED(event
))
767 if (m_joblist
->GetSelectedItemCount() == 0) return;
769 long itemnr
= m_joblist
->GetNextItem(-1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
770 while (itemnr
!= -1) {
771 ConvertJob
* job
= (ConvertJob
*)m_joblist
->GetItemData(itemnr
);
772 if (job
->state
!= CONV_INPROGRESS
) {
773 // this will remove the job from both gui and list
774 CPartFileConvert::RemoveJob(job
);
776 itemnr
= m_joblist
->GetNextItem(itemnr
, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
780 void CPartFileConvertDlg::RetrySel(wxCommandEvent
& WXUNUSED(event
))
782 if (m_joblist
->GetSelectedItemCount() == 0) return;
784 long itemnr
= m_joblist
->GetNextItem(-1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
785 while (itemnr
!= -1) {
786 ConvertJob
* job
= (ConvertJob
*)m_joblist
->GetItemData(itemnr
);
787 wxMutexLocker
lock(CPartFileConvert::s_mutex
);
788 if (job
->state
!= CONV_OK
&& job
->state
!= CONV_INPROGRESS
) {
789 job
->state
= CONV_QUEUE
;
793 wxMutexLocker
lock(CPartFileConvert::s_mutex
);
794 CPartFileConvert::StartThread();
798 // File_checked_for_headers