summaryrefslogtreecommitdiffstats
path: root/libk3b
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-03 02:15:56 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-03 02:15:56 +0000
commit50b48aec6ddd451a6d1709c0942477b503457663 (patch)
treea9ece53ec06fd0a2819de7a2a6de997193566626 /libk3b
downloadk3b-50b48aec6ddd451a6d1709c0942477b503457663.tar.gz
k3b-50b48aec6ddd451a6d1709c0942477b503457663.zip
Added abandoned KDE3 version of K3B
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/k3b@1084400 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'libk3b')
-rw-r--r--libk3b/Makefile.am28
-rw-r--r--libk3b/README29
-rw-r--r--libk3b/cddb/Makefile.am9
-rw-r--r--libk3b/cddb/k3bcddb.cpp280
-rw-r--r--libk3b/cddb/k3bcddb.h103
-rw-r--r--libk3b/cddb/k3bcddbhttpquery.cpp233
-rw-r--r--libk3b/cddb/k3bcddbhttpquery.h64
-rw-r--r--libk3b/cddb/k3bcddblocalquery.cpp129
-rw-r--r--libk3b/cddb/k3bcddblocalquery.h48
-rw-r--r--libk3b/cddb/k3bcddblocalsubmit.cpp102
-rw-r--r--libk3b/cddb/k3bcddblocalsubmit.h43
-rw-r--r--libk3b/cddb/k3bcddbmultientriesdialog.cpp74
-rw-r--r--libk3b/cddb/k3bcddbmultientriesdialog.h48
-rw-r--r--libk3b/cddb/k3bcddbpquery.cpp278
-rw-r--r--libk3b/cddb/k3bcddbpquery.h62
-rw-r--r--libk3b/cddb/k3bcddbquery.cpp275
-rw-r--r--libk3b/cddb/k3bcddbquery.h115
-rw-r--r--libk3b/cddb/k3bcddbresult.cpp49
-rw-r--r--libk3b/cddb/k3bcddbresult.h79
-rw-r--r--libk3b/cddb/k3bcddbsubmit.cpp84
-rw-r--r--libk3b/cddb/k3bcddbsubmit.h60
-rw-r--r--libk3b/configure.in.in3
-rw-r--r--libk3b/core/Makefile.am19
-rw-r--r--libk3b/core/k3b_export.h33
-rw-r--r--libk3b/core/k3bcore.cpp375
-rw-r--r--libk3b/core/k3bcore.h181
-rw-r--r--libk3b/core/k3bdataevent.h48
-rw-r--r--libk3b/core/k3bdefaultexternalprograms.cpp1030
-rw-r--r--libk3b/core/k3bdefaultexternalprograms.h143
-rw-r--r--libk3b/core/k3bexternalbinmanager.cpp389
-rw-r--r--libk3b/core/k3bexternalbinmanager.h162
-rw-r--r--libk3b/core/k3bglobals.cpp634
-rw-r--r--libk3b/core/k3bglobals.h257
-rw-r--r--libk3b/core/k3bglobalsettings.cpp61
-rw-r--r--libk3b/core/k3bglobalsettings.h70
-rw-r--r--libk3b/core/k3bjob.cpp253
-rw-r--r--libk3b/core/k3bjob.h311
-rw-r--r--libk3b/core/k3bjobhandler.h64
-rw-r--r--libk3b/core/k3bprocess.cpp452
-rw-r--r--libk3b/core/k3bprocess.h204
-rw-r--r--libk3b/core/k3bprogressinfoevent.h85
-rw-r--r--libk3b/core/k3bsimplejobhandler.cpp62
-rw-r--r--libk3b/core/k3bsimplejobhandler.h61
-rw-r--r--libk3b/core/k3bthread.cpp221
-rw-r--r--libk3b/core/k3bthread.h95
-rw-r--r--libk3b/core/k3bthreadjob.cpp161
-rw-r--r--libk3b/core/k3bthreadjob.h89
-rw-r--r--libk3b/core/k3bversion.cpp318
-rw-r--r--libk3b/core/k3bversion.h141
-rw-r--r--libk3b/dummy.cpp1
-rw-r--r--libk3b/jobs/Makefile.am43
-rw-r--r--libk3b/jobs/k3baudiocuefilewritingjob.cpp272
-rw-r--r--libk3b/jobs/k3baudiocuefilewritingjob.h79
-rw-r--r--libk3b/jobs/k3baudiosessionreadingjob.cpp278
-rw-r--r--libk3b/jobs/k3baudiosessionreadingjob.h75
-rw-r--r--libk3b/jobs/k3bbinimagewritingjob.cpp234
-rw-r--r--libk3b/jobs/k3bbinimagewritingjob.h79
-rw-r--r--libk3b/jobs/k3bblankingjob.cpp176
-rw-r--r--libk3b/jobs/k3bblankingjob.h71
-rw-r--r--libk3b/jobs/k3bcdcopyjob.cpp1213
-rw-r--r--libk3b/jobs/k3bcdcopyjob.h117
-rw-r--r--libk3b/jobs/k3bcdda2wavreader.cpp254
-rw-r--r--libk3b/jobs/k3bcdda2wavreader.h70
-rw-r--r--libk3b/jobs/k3bclonejob.cpp375
-rw-r--r--libk3b/jobs/k3bclonejob.h99
-rw-r--r--libk3b/jobs/k3bclonetocreader.cpp235
-rw-r--r--libk3b/jobs/k3bclonetocreader.h45
-rw-r--r--libk3b/jobs/k3bdatatrackreader.cpp515
-rw-r--r--libk3b/jobs/k3bdatatrackreader.h87
-rw-r--r--libk3b/jobs/k3bdvdcopyjob.cpp894
-rw-r--r--libk3b/jobs/k3bdvdcopyjob.h99
-rw-r--r--libk3b/jobs/k3bdvdformattingjob.cpp536
-rw-r--r--libk3b/jobs/k3bdvdformattingjob.h91
-rw-r--r--libk3b/jobs/k3biso9660imagewritingjob.cpp458
-rw-r--r--libk3b/jobs/k3biso9660imagewritingjob.h98
-rw-r--r--libk3b/jobs/k3breadcdreader.cpp335
-rw-r--r--libk3b/jobs/k3breadcdreader.h91
-rw-r--r--libk3b/jobs/k3bverificationjob.cpp384
-rw-r--r--libk3b/jobs/k3bverificationjob.h92
-rw-r--r--libk3b/jobs/k3bvideodvdtitledetectclippingjob.cpp291
-rw-r--r--libk3b/jobs/k3bvideodvdtitledetectclippingjob.h106
-rw-r--r--libk3b/jobs/k3bvideodvdtitletranscodingjob.cpp583
-rw-r--r--libk3b/jobs/k3bvideodvdtitletranscodingjob.h275
-rw-r--r--libk3b/k3bimage.xsd175
-rw-r--r--libk3b/plugin/Makefile.am28
-rw-r--r--libk3b/plugin/k3baudioclient.cpp46
-rw-r--r--libk3b/plugin/k3baudioclient.h51
-rw-r--r--libk3b/plugin/k3baudiodecoder.cpp599
-rw-r--r--libk3b/plugin/k3baudiodecoder.h254
-rw-r--r--libk3b/plugin/k3baudioencoder.cpp175
-rw-r--r--libk3b/plugin/k3baudioencoder.h203
-rw-r--r--libk3b/plugin/k3baudiooutputplugin.h69
-rw-r--r--libk3b/plugin/k3baudioserver.cpp214
-rw-r--r--libk3b/plugin/k3baudioserver.h85
-rw-r--r--libk3b/plugin/k3bplugin.cpp36
-rw-r--r--libk3b/plugin/k3bplugin.h113
-rw-r--r--libk3b/plugin/k3bpluginconfigwidget.cpp48
-rw-r--r--libk3b/plugin/k3bpluginconfigwidget.h40
-rw-r--r--libk3b/plugin/k3bpluginfactory.cpp33
-rw-r--r--libk3b/plugin/k3bpluginfactory.h98
-rw-r--r--libk3b/plugin/k3bpluginmanager.cpp189
-rw-r--r--libk3b/plugin/k3bpluginmanager.h70
-rw-r--r--libk3b/plugin/k3bprojectplugin.h161
-rw-r--r--libk3b/plugin/libsamplerate/Makefile.am20
-rw-r--r--libk3b/plugin/libsamplerate/common.h105
-rw-r--r--libk3b/plugin/libsamplerate/configure.in.in13
-rw-r--r--libk3b/plugin/libsamplerate/fastest_coeffs.h2493
-rw-r--r--libk3b/plugin/libsamplerate/float_cast.h203
-rw-r--r--libk3b/plugin/libsamplerate/high_qual_coeffs.h17116
-rw-r--r--libk3b/plugin/libsamplerate/mid_qual_coeffs.h5315
-rw-r--r--libk3b/plugin/libsamplerate/samplerate.c301
-rw-r--r--libk3b/plugin/libsamplerate/samplerate.h138
-rw-r--r--libk3b/plugin/libsamplerate/src_linear.c194
-rw-r--r--libk3b/plugin/libsamplerate/src_sinc.c471
-rw-r--r--libk3b/plugin/libsamplerate/src_zoh.c186
-rw-r--r--libk3b/projects/Makefile.am32
-rw-r--r--libk3b/projects/audiocd/Makefile.am35
-rw-r--r--libk3b/projects/audiocd/k3baudiocdtrackdrag.cpp109
-rw-r--r--libk3b/projects/audiocd/k3baudiocdtrackdrag.h50
-rw-r--r--libk3b/projects/audiocd/k3baudiocdtracksource.cpp262
-rw-r--r--libk3b/projects/audiocd/k3baudiocdtracksource.h99
-rw-r--r--libk3b/projects/audiocd/k3baudiodatasource.cpp210
-rw-r--r--libk3b/projects/audiocd/k3baudiodatasource.h168
-rw-r--r--libk3b/projects/audiocd/k3baudiodatasourceiterator.cpp71
-rw-r--r--libk3b/projects/audiocd/k3baudiodatasourceiterator.h61
-rw-r--r--libk3b/projects/audiocd/k3baudiodoc.cpp1127
-rw-r--r--libk3b/projects/audiocd/k3baudiodoc.h263
-rw-r--r--libk3b/projects/audiocd/k3baudiofile.cpp112
-rw-r--r--libk3b/projects/audiocd/k3baudiofile.h85
-rw-r--r--libk3b/projects/audiocd/k3baudioimager.cpp203
-rw-r--r--libk3b/projects/audiocd/k3baudioimager.h59
-rw-r--r--libk3b/projects/audiocd/k3baudiojob.cpp864
-rw-r--r--libk3b/projects/audiocd/k3baudiojob.h107
-rw-r--r--libk3b/projects/audiocd/k3baudiojobtempdata.cpp132
-rw-r--r--libk3b/projects/audiocd/k3baudiojobtempdata.h64
-rw-r--r--libk3b/projects/audiocd/k3baudiomaxspeedjob.cpp224
-rw-r--r--libk3b/projects/audiocd/k3baudiomaxspeedjob.h43
-rw-r--r--libk3b/projects/audiocd/k3baudionormalizejob.cpp205
-rw-r--r--libk3b/projects/audiocd/k3baudionormalizejob.h63
-rw-r--r--libk3b/projects/audiocd/k3baudiotrack.cpp628
-rw-r--r--libk3b/projects/audiocd/k3baudiotrack.h214
-rw-r--r--libk3b/projects/audiocd/k3baudiozerodata.cpp115
-rw-r--r--libk3b/projects/audiocd/k3baudiozerodata.h55
-rw-r--r--libk3b/projects/datacd/Makefile.am35
-rw-r--r--libk3b/projects/datacd/k3bbootitem.cpp58
-rw-r--r--libk3b/projects/datacd/k3bbootitem.h66
-rw-r--r--libk3b/projects/datacd/k3bdatadoc.cpp1376
-rw-r--r--libk3b/projects/datacd/k3bdatadoc.h297
-rw-r--r--libk3b/projects/datacd/k3bdataitem.cpp264
-rw-r--r--libk3b/projects/datacd/k3bdataitem.h225
-rw-r--r--libk3b/projects/datacd/k3bdatajob.cpp972
-rw-r--r--libk3b/projects/datacd/k3bdatajob.h111
-rw-r--r--libk3b/projects/datacd/k3bdatapreparationjob.cpp283
-rw-r--r--libk3b/projects/datacd/k3bdatapreparationjob.h51
-rw-r--r--libk3b/projects/datacd/k3bdiritem.cpp406
-rw-r--r--libk3b/projects/datacd/k3bdiritem.h155
-rw-r--r--libk3b/projects/datacd/k3bfilecompilationsizehandler.cpp228
-rw-r--r--libk3b/projects/datacd/k3bfilecompilationsizehandler.h73
-rw-r--r--libk3b/projects/datacd/k3bfileitem.cpp300
-rw-r--r--libk3b/projects/datacd/k3bfileitem.h124
-rw-r--r--libk3b/projects/datacd/k3bisoimager.cpp1187
-rw-r--r--libk3b/projects/datacd/k3bisoimager.h188
-rw-r--r--libk3b/projects/datacd/k3bisooptions.cpp216
-rw-r--r--libk3b/projects/datacd/k3bisooptions.h183
-rw-r--r--libk3b/projects/datacd/k3bmkisofshandler.cpp150
-rw-r--r--libk3b/projects/datacd/k3bmkisofshandler.h74
-rw-r--r--libk3b/projects/datacd/k3bmsinfofetcher.cpp243
-rw-r--r--libk3b/projects/datacd/k3bmsinfofetcher.h64
-rw-r--r--libk3b/projects/datacd/k3bsessionimportitem.cpp59
-rw-r--r--libk3b/projects/datacd/k3bsessionimportitem.h63
-rw-r--r--libk3b/projects/datacd/k3bspecialdataitem.h76
-rw-r--r--libk3b/projects/datadvd/Makefile.am21
-rw-r--r--libk3b/projects/datadvd/k3bdvdbooktypejob.cpp350
-rw-r--r--libk3b/projects/datadvd/k3bdvdbooktypejob.h99
-rw-r--r--libk3b/projects/datadvd/k3bdvddoc.cpp39
-rw-r--r--libk3b/projects/datadvd/k3bdvddoc.h37
-rw-r--r--libk3b/projects/datadvd/k3bdvdjob.cpp344
-rw-r--r--libk3b/projects/datadvd/k3bdvdjob.h57
-rw-r--r--libk3b/projects/datadvd/k3bdvdview.cpp48
-rw-r--r--libk3b/projects/datadvd/k3bdvdview.h40
-rw-r--r--libk3b/projects/k3babstractwriter.cpp96
-rw-r--r--libk3b/projects/k3babstractwriter.h92
-rw-r--r--libk3b/projects/k3bcdrdaowriter.cpp1101
-rw-r--r--libk3b/projects/k3bcdrdaowriter.h157
-rw-r--r--libk3b/projects/k3bcdrecordwriter.cpp810
-rw-r--r--libk3b/projects/k3bcdrecordwriter.h123
-rw-r--r--libk3b/projects/k3bcuefileparser.cpp461
-rw-r--r--libk3b/projects/k3bcuefileparser.h57
-rw-r--r--libk3b/projects/k3bdoc.cpp221
-rw-r--r--libk3b/projects/k3bdoc.h229
-rw-r--r--libk3b/projects/k3bdvdrecordwriter.cpp119
-rw-r--r--libk3b/projects/k3bdvdrecordwriter.h40
-rw-r--r--libk3b/projects/k3bgrowisofshandler.cpp318
-rw-r--r--libk3b/projects/k3bgrowisofshandler.h87
-rw-r--r--libk3b/projects/k3bgrowisofswriter.cpp630
-rw-r--r--libk3b/projects/k3bgrowisofswriter.h106
-rw-r--r--libk3b/projects/k3bimagefilereader.cpp88
-rw-r--r--libk3b/projects/k3bimagefilereader.h55
-rw-r--r--libk3b/projects/k3binffilewriter.cpp186
-rw-r--r--libk3b/projects/k3binffilewriter.h119
-rw-r--r--libk3b/projects/k3bpipebuffer.cpp281
-rw-r--r--libk3b/projects/k3bpipebuffer.h59
-rw-r--r--libk3b/projects/k3btocfilewriter.cpp356
-rw-r--r--libk3b/projects/k3btocfilewriter.h62
-rw-r--r--libk3b/projects/mixedcd/Makefile.am23
-rw-r--r--libk3b/projects/mixedcd/k3bmixeddoc.cpp249
-rw-r--r--libk3b/projects/mixedcd/k3bmixeddoc.h95
-rw-r--r--libk3b/projects/mixedcd/k3bmixedjob.cpp1339
-rw-r--r--libk3b/projects/mixedcd/k3bmixedjob.h144
-rw-r--r--libk3b/projects/movixcd/Makefile.am23
-rw-r--r--libk3b/projects/movixcd/k3bmovixdoc.cpp445
-rw-r--r--libk3b/projects/movixcd/k3bmovixdoc.h125
-rw-r--r--libk3b/projects/movixcd/k3bmovixdocpreparer.cpp490
-rw-r--r--libk3b/projects/movixcd/k3bmovixdocpreparer.h67
-rw-r--r--libk3b/projects/movixcd/k3bmovixfileitem.cpp68
-rw-r--r--libk3b/projects/movixcd/k3bmovixfileitem.h52
-rw-r--r--libk3b/projects/movixcd/k3bmovixjob.cpp132
-rw-r--r--libk3b/projects/movixcd/k3bmovixjob.h60
-rw-r--r--libk3b/projects/movixcd/k3bmovixprogram.cpp339
-rw-r--r--libk3b/projects/movixcd/k3bmovixprogram.h103
-rw-r--r--libk3b/projects/movixdvd/Makefile.am21
-rw-r--r--libk3b/projects/movixdvd/k3bmovixdvddoc.cpp36
-rw-r--r--libk3b/projects/movixdvd/k3bmovixdvddoc.h40
-rw-r--r--libk3b/projects/movixdvd/k3bmovixdvdjob.cpp131
-rw-r--r--libk3b/projects/movixdvd/k3bmovixdvdjob.h60
-rw-r--r--libk3b/projects/videocd/Makefile.am20
-rw-r--r--libk3b/projects/videocd/cdi/Makefile.am5
-rw-r--r--libk3b/projects/videocd/cdi/cdi_imag.rtfbin0 -> 1314877 bytes
-rw-r--r--libk3b/projects/videocd/cdi/cdi_text.fntbin0 -> 13616 bytes
-rw-r--r--libk3b/projects/videocd/cdi/cdi_vcd.appbin0 -> 102400 bytes
-rw-r--r--libk3b/projects/videocd/cdi/cdi_vcd.cfg12
-rw-r--r--libk3b/projects/videocd/cdi/icdia.htm12
-rw-r--r--libk3b/projects/videocd/cdi/vcd_on_cdi_41.pdfbin0 -> 126675 bytes
-rw-r--r--libk3b/projects/videocd/extra/Makefile.am5
-rw-r--r--libk3b/projects/videocd/extra/k3bphotosvcd.mpgbin0 -> 824970 bytes
-rw-r--r--libk3b/projects/videocd/extra/k3bphotovcd.mpgbin0 -> 1731138 bytes
-rw-r--r--libk3b/projects/videocd/k3bvcddoc.cpp894
-rw-r--r--libk3b/projects/videocd/k3bvcddoc.h192
-rw-r--r--libk3b/projects/videocd/k3bvcdjob.cpp567
-rw-r--r--libk3b/projects/videocd/k3bvcdjob.h115
-rw-r--r--libk3b/projects/videocd/k3bvcdoptions.cpp146
-rw-r--r--libk3b/projects/videocd/k3bvcdoptions.h377
-rw-r--r--libk3b/projects/videocd/k3bvcdtrack.cpp456
-rw-r--r--libk3b/projects/videocd/k3bvcdtrack.h198
-rw-r--r--libk3b/projects/videocd/k3bvcdxmlview.cpp440
-rw-r--r--libk3b/projects/videocd/k3bvcdxmlview.h59
-rw-r--r--libk3b/projects/videocd/mpeginfo/Makefile.am5
-rw-r--r--libk3b/projects/videocd/mpeginfo/k3bmpeginfo.cpp844
-rw-r--r--libk3b/projects/videocd/mpeginfo/k3bmpeginfo.h178
-rw-r--r--libk3b/projects/videodvd/Makefile.am20
-rw-r--r--libk3b/projects/videodvd/k3bvideodvddoc.cpp71
-rw-r--r--libk3b/projects/videodvd/k3bvideodvddoc.h46
-rw-r--r--libk3b/projects/videodvd/k3bvideodvdimager.cpp221
-rw-r--r--libk3b/projects/videodvd/k3bvideodvdimager.h61
-rw-r--r--libk3b/projects/videodvd/k3bvideodvdjob.cpp101
-rw-r--r--libk3b/projects/videodvd/k3bvideodvdjob.h46
-rw-r--r--libk3b/scripts/Makefile.am11
-rwxr-xr-xlibk3b/scripts/k3b_automount66
-rw-r--r--libk3b/tools/Makefile.am44
-rw-r--r--libk3b/tools/k3bactivepipe.cpp255
-rw-r--r--libk3b/tools/k3bactivepipe.h134
-rw-r--r--libk3b/tools/k3bbusywidget.cpp103
-rw-r--r--libk3b/tools/k3bbusywidget.h54
-rw-r--r--libk3b/tools/k3bcdparanoialib.cpp783
-rw-r--r--libk3b/tools/k3bcdparanoialib.h161
-rw-r--r--libk3b/tools/k3bcdtextvalidator.cpp42
-rw-r--r--libk3b/tools/k3bcdtextvalidator.h33
-rw-r--r--libk3b/tools/k3bchecksumpipe.cpp99
-rw-r--r--libk3b/tools/k3bchecksumpipe.h66
-rw-r--r--libk3b/tools/k3bcutcombobox.cpp230
-rw-r--r--libk3b/tools/k3bcutcombobox.h92
-rw-r--r--libk3b/tools/k3bdevicecombobox.cpp174
-rw-r--r--libk3b/tools/k3bdevicecombobox.h67
-rw-r--r--libk3b/tools/k3bdevicehandler.cpp332
-rw-r--r--libk3b/tools/k3bdevicehandler.h237
-rw-r--r--libk3b/tools/k3bdeviceselectiondialog.cpp130
-rw-r--r--libk3b/tools/k3bdeviceselectiondialog.h62
-rw-r--r--libk3b/tools/k3bdirsizejob.cpp184
-rw-r--r--libk3b/tools/k3bdirsizejob.h67
-rw-r--r--libk3b/tools/k3bexceptions.cpp43
-rw-r--r--libk3b/tools/k3bexceptions.h35
-rw-r--r--libk3b/tools/k3bfilesplitter.cpp307
-rw-r--r--libk3b/tools/k3bfilesplitter.h108
-rw-r--r--libk3b/tools/k3bfilesysteminfo.cpp141
-rw-r--r--libk3b/tools/k3bfilesysteminfo.h56
-rw-r--r--libk3b/tools/k3bintmapcombobox.cpp127
-rw-r--r--libk3b/tools/k3bintmapcombobox.h83
-rw-r--r--libk3b/tools/k3bintvalidator.cpp137
-rw-r--r--libk3b/tools/k3bintvalidator.h84
-rw-r--r--libk3b/tools/k3biso9660.cpp899
-rw-r--r--libk3b/tools/k3biso9660.h453
-rw-r--r--libk3b/tools/k3biso9660backend.cpp239
-rw-r--r--libk3b/tools/k3biso9660backend.h95
-rw-r--r--libk3b/tools/k3blibdvdcss.cpp307
-rw-r--r--libk3b/tools/k3blibdvdcss.h84
-rw-r--r--libk3b/tools/k3blistview.cpp1290
-rw-r--r--libk3b/tools/k3blistview.h296
-rw-r--r--libk3b/tools/k3blistviewitemanimator.cpp137
-rw-r--r--libk3b/tools/k3blistviewitemanimator.h78
-rw-r--r--libk3b/tools/k3bmd5job.cpp322
-rw-r--r--libk3b/tools/k3bmd5job.h92
-rw-r--r--libk3b/tools/k3bmsfedit.cpp153
-rw-r--r--libk3b/tools/k3bmsfedit.h70
-rw-r--r--libk3b/tools/k3bmultichoicedialog.cpp191
-rw-r--r--libk3b/tools/k3bmultichoicedialog.h73
-rw-r--r--libk3b/tools/k3bpipe.cpp79
-rw-r--r--libk3b/tools/k3bpipe.h60
-rw-r--r--libk3b/tools/k3bprogressdialog.cpp107
-rw-r--r--libk3b/tools/k3bprogressdialog.h63
-rw-r--r--libk3b/tools/k3bpushbutton.cpp136
-rw-r--r--libk3b/tools/k3bpushbutton.h75
-rw-r--r--libk3b/tools/k3bradioaction.cpp94
-rw-r--r--libk3b/tools/k3bradioaction.h122
-rw-r--r--libk3b/tools/k3brichtextlabel.cpp109
-rw-r--r--libk3b/tools/k3brichtextlabel.h62
-rw-r--r--libk3b/tools/k3bsignalwaiter.cpp62
-rw-r--r--libk3b/tools/k3bsignalwaiter.h51
-rw-r--r--libk3b/tools/k3bstdguiitems.cpp215
-rw-r--r--libk3b/tools/k3bstdguiitems.h45
-rw-r--r--libk3b/tools/k3bstringutils.cpp111
-rw-r--r--libk3b/tools/k3bstringutils.h39
-rw-r--r--libk3b/tools/k3btempfile.cpp51
-rw-r--r--libk3b/tools/k3btempfile.h43
-rw-r--r--libk3b/tools/k3bthreadwidget.cpp142
-rw-r--r--libk3b/tools/k3bthreadwidget.h78
-rw-r--r--libk3b/tools/k3bthroughputestimator.cpp98
-rw-r--r--libk3b/tools/k3bthroughputestimator.h57
-rw-r--r--libk3b/tools/k3btitlelabel.cpp266
-rw-r--r--libk3b/tools/k3btitlelabel.h68
-rw-r--r--libk3b/tools/k3btoolbox.cpp293
-rw-r--r--libk3b/tools/k3btoolbox.h93
-rw-r--r--libk3b/tools/k3btoolbutton.cpp109
-rw-r--r--libk3b/tools/k3btoolbutton.h50
-rw-r--r--libk3b/tools/k3bvalidators.cpp154
-rw-r--r--libk3b/tools/k3bvalidators.h131
-rw-r--r--libk3b/tools/k3bwavefilewriter.cpp186
-rw-r--r--libk3b/tools/k3bwavefilewriter.h78
-rw-r--r--libk3b/tools/kcutlabel.cpp115
-rw-r--r--libk3b/tools/kcutlabel.h68
-rw-r--r--libk3b/tools/libisofs/COPYING280
-rw-r--r--libk3b/tools/libisofs/ChangeLog9
-rw-r--r--libk3b/tools/libisofs/Makefile.am5
-rw-r--r--libk3b/tools/libisofs/README24
-rw-r--r--libk3b/tools/libisofs/bswap.h94
-rw-r--r--libk3b/tools/libisofs/el_torito.h63
-rw-r--r--libk3b/tools/libisofs/iso_fs.h219
-rw-r--r--libk3b/tools/libisofs/isofs.cpp878
-rw-r--r--libk3b/tools/libisofs/isofs.h151
-rw-r--r--libk3b/tools/libisofs/rock.h127
-rw-r--r--libk3b/videodvd/Makefile.am19
-rw-r--r--libk3b/videodvd/configure.in.bot11
-rw-r--r--libk3b/videodvd/configure.in.in28
-rw-r--r--libk3b/videodvd/k3bvideodvd.cpp327
-rw-r--r--libk3b/videodvd/k3bvideodvd.h91
-rw-r--r--libk3b/videodvd/k3bvideodvdaudiostream.h112
-rw-r--r--libk3b/videodvd/k3bvideodvdptt.h50
-rw-r--r--libk3b/videodvd/k3bvideodvdsubpicturestream.h68
-rw-r--r--libk3b/videodvd/k3bvideodvdtime.cpp106
-rw-r--r--libk3b/videodvd/k3bvideodvdtime.h59
-rw-r--r--libk3b/videodvd/k3bvideodvdtitle.h112
-rw-r--r--libk3b/videodvd/k3bvideodvdvideostream.cpp60
-rw-r--r--libk3b/videodvd/k3bvideodvdvideostream.h107
362 files changed, 91051 insertions, 0 deletions
diff --git a/libk3b/Makefile.am b/libk3b/Makefile.am
new file mode 100644
index 0000000..4c74f7d
--- /dev/null
+++ b/libk3b/Makefile.am
@@ -0,0 +1,28 @@
+if include_videodvdrip
+VIDEODVDDIR = videodvd
+VIDEODVDLIB = videodvd/libvideodvd.la
+endif
+
+
+lib_LTLIBRARIES = libk3b.la
+
+libk3b_la_SOURCES = dummy.cpp
+
+libk3b_la_LIBADD = core/libk3bcore.la \
+ cddb/libcddb.la \
+ projects/libk3bproject.la \
+ plugin/libk3bplugin.la \
+ tools/libk3btools.la \
+ jobs/libjobs.la \
+ $(VIDEODVDLIB) \
+ ../libk3bdevice/libk3bdevice.la
+
+libk3b_la_LDFLAGS = $(all_libraries) -version-info 3:0:0 -no-undefined
+
+SUBDIRS = core plugin tools projects cddb jobs $(VIDEODVDDIR)
+
+#pkgconfigdir = $(libdir)/pkgconfig
+#pkgconfig_DATA = libk3b.pc
+
+messages:
+ $(XGETTEXT) `find -name "*.cpp" -o -name "*.h"` -o $(podir)/libk3b.pot
diff --git a/libk3b/README b/libk3b/README
new file mode 100644
index 0000000..a1ba273
--- /dev/null
+++ b/libk3b/README
@@ -0,0 +1,29 @@
+libk3b
+=========================
+
+This is the k3b library which provides a lot of CD/DVD writing classes.
+
+If you want to use it please be aware that the API is far from stable and
+there will be no binary compatibility (or even source compatibility) before
+K3b 1.0.
+
+But you are welcome to help fix the API and improve it whereever it is needed.
+
+
+Usage
+==========================
+
+Just a very basic scetch how to create an audio cd:
+
+1. create a k3bcore instance (this provides all the stuff that is needed by the lib)
+1.1 K3bCore::init() to initialize the core.
+
+2. create a K3bAudioDoc object and add urls to it
+
+3. create a K3bJobHandler derived class (for example a widget which displays the progress)
+
+4. call K3bAudioDoc::newBurnJob or create a K3bAudioJob manually.
+
+5. modify the doc's settings.
+
+6. call K3bAudioJob::start() to start the burning process.
diff --git a/libk3b/cddb/Makefile.am b/libk3b/cddb/Makefile.am
new file mode 100644
index 0000000..e73c5ce
--- /dev/null
+++ b/libk3b/cddb/Makefile.am
@@ -0,0 +1,9 @@
+AM_CPPFLAGS = -I$(srcdir)/../core -I$(srcdir)/../../libk3bdevice -I$(srcdir)/../../src $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libcddb.la
+
+libcddb_la_SOURCES = k3bcddbquery.cpp k3bcddb.cpp k3bcddbresult.cpp k3bcddbhttpquery.cpp k3bcddbpquery.cpp k3bcddblocalquery.cpp k3bcddbsubmit.cpp k3bcddblocalsubmit.cpp k3bcddbmultientriesdialog.cpp
+
+include_HEADERS = k3bcddb.h k3bcddbresult.h
diff --git a/libk3b/cddb/k3bcddb.cpp b/libk3b/cddb/k3bcddb.cpp
new file mode 100644
index 0000000..a0e4fe1
--- /dev/null
+++ b/libk3b/cddb/k3bcddb.cpp
@@ -0,0 +1,280 @@
+/*
+ *
+ * $Id: k3bcddb.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+
+#include <qstring.h>
+#include <qvaluelist.h>
+#include <qstringlist.h>
+#include <qtimer.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kdeversion.h>
+
+#include "k3bcddb.h"
+#include "k3bcddbhttpquery.h"
+#include "k3bcddbpquery.h"
+#include "k3bcddblocalquery.h"
+#include "k3bcddblocalsubmit.h"
+
+#include <k3btoc.h>
+#include <k3btrack.h>
+#include "k3bcddbmultientriesdialog.h"
+
+
+K3bCddb::K3bCddb( QObject* parent, const char* name )
+ : QObject( parent, name )
+{
+ m_httpQuery = 0;
+ m_cddbpQuery = 0;
+ m_localQuery = 0;
+ m_localSubmit = 0;
+
+ m_lastUsedQuery = 0;
+}
+
+
+K3bCddb::~K3bCddb()
+{
+}
+
+
+void K3bCddb::readConfig( KConfig* c )
+{
+ c->setGroup( "Cddb" );
+
+ m_bRemoteCddbQuery = c->readBoolEntry( "use remote cddb", true );
+ m_bLocalCddbQuery = c->readBoolEntry( "use local cddb query", false );
+
+ // old config <= 0.7.3
+ QStringList cddbpServer = c->readListEntry( "cddbp server" );
+ QStringList httpServer = c->readListEntry( "http server" );
+
+ // new config
+ m_cddbServer = c->readListEntry( "cddb server" );
+
+ m_localCddbDirs = c->readPathListEntry( "local cddb dirs" );
+
+ m_bUseManualCgiPath = c->readBoolEntry( "use manual cgi path", false );
+ m_cgiPath = c->readEntry( "cgi path", "/~cddb/cddb.cgi" );
+
+ if( m_localCddbDirs.isEmpty() )
+ m_localCddbDirs.append( "~/.cddb/" );
+
+ // old config <= 0.7.3
+ if( !httpServer.isEmpty() ) {
+ for( QStringList::iterator it = httpServer.begin(); it != httpServer.end(); ++it ) {
+ m_cddbServer.append( "Http " + *it );
+ }
+ }
+ if( !cddbpServer.isEmpty() ) {
+ for( QStringList::iterator it = cddbpServer.begin(); it != cddbpServer.end(); ++it ) {
+ m_cddbServer.append( "Cddbp " + *it );
+ }
+ }
+
+ if( m_cddbServer.isEmpty() )
+ m_cddbServer.append( "Http freedb2.org:80" );
+}
+
+
+void K3bCddb::query( const K3bDevice::Toc& toc )
+{
+ m_toc = toc;
+
+ if( m_bLocalCddbQuery ) {
+ m_iCurrentQueriedLocalDir = 0;
+ QTimer::singleShot( 0, this, SLOT(localQuery()) );
+ }
+ else if( m_bRemoteCddbQuery ) {
+ m_iCurrentQueriedServer = 0;
+ QTimer::singleShot( 0, this, SLOT(remoteQuery()) );
+ }
+ else {
+ QTimer::singleShot( 0, this, SLOT(slotNoEntry()) );
+ }
+}
+
+
+void K3bCddb::slotNoEntry()
+{
+ emit queryFinished( K3bCddbQuery::NO_ENTRY_FOUND );
+}
+
+
+void K3bCddb::remoteQuery()
+{
+ K3bCddbQuery* q = getQuery( m_cddbServer[m_iCurrentQueriedServer] );
+ q->query(m_toc);
+}
+
+
+void K3bCddb::slotMultibleMatches( K3bCddbQuery* query )
+{
+ K3bCddbResultHeader hdr = K3bCddbMultiEntriesDialog::selectCddbEntry( query, 0 );
+ if( !hdr.discid.isEmpty() )
+ query->queryMatch( hdr );
+ else
+ emit queryFinished( K3bCddbQuery::CANCELED );
+}
+
+
+void K3bCddb::slotQueryFinished( K3bCddbQuery* query )
+{
+ m_lastUsedQuery = query;
+
+ if( query->error() == K3bCddbQuery::SUCCESS ) {
+ m_lastResult = m_lastUsedQuery->result();
+
+ // make sure the result has the requested discid since otherwise local saving does not make much sense
+ m_lastResult.discid = QString::number( m_toc.discId(), 16 ).rightJustify( 8, '0' );
+
+ emit queryFinished( K3bCddbQuery::SUCCESS );
+ }
+ else if( query == m_localQuery ) {
+ m_iCurrentQueriedLocalDir++;
+ if( m_iCurrentQueriedLocalDir < m_localCddbDirs.size() )
+ localQuery();
+ else if( m_bRemoteCddbQuery ) {
+ m_iCurrentQueriedServer = 0;
+ remoteQuery();
+ }
+ else {
+ emit queryFinished( query->error() );
+ }
+ }
+ else {
+ m_iCurrentQueriedServer++;
+ if( m_iCurrentQueriedServer < m_cddbServer.size() ) {
+ remoteQuery();
+ }
+ else {
+ emit queryFinished( query->error() );
+ }
+ }
+}
+
+
+K3bCddbQuery* K3bCddb::getQuery( const QString& s )
+{
+ QStringList buf = QStringList::split( ":", s.mid( s.find(" ")+1 ) );
+ QString server = buf[0];
+ int port = buf[1].toInt();
+
+ if( s.startsWith("Http") ) {
+ if( !m_httpQuery ) {
+ m_httpQuery = new K3bCddbHttpQuery( this );
+ connect( m_httpQuery, SIGNAL(infoMessage(const QString&)),
+ this, SIGNAL(infoMessage(const QString&)) );
+ connect( m_httpQuery, SIGNAL(queryFinished(K3bCddbQuery*)),
+ this, SLOT(slotQueryFinished(K3bCddbQuery*)) );
+ connect( m_httpQuery, SIGNAL(inexactMatches(K3bCddbQuery*)),
+ this, SLOT(slotMultibleMatches(K3bCddbQuery*)) );
+ }
+
+ m_httpQuery->setServer( server, port );
+ m_httpQuery->setCgiPath( m_bUseManualCgiPath ? m_cgiPath : QString::fromLatin1("/~cddb/cddb.cgi") );
+
+ return m_httpQuery;
+ }
+ else {
+ if( !m_cddbpQuery ) {
+ m_cddbpQuery = new K3bCddbpQuery( this );
+ connect( m_cddbpQuery, SIGNAL(infoMessage(const QString&)),
+ this, SIGNAL(infoMessage(const QString&)) );
+ connect( m_cddbpQuery, SIGNAL(queryFinished(K3bCddbQuery*)),
+ this, SLOT(slotQueryFinished(K3bCddbQuery*)) );
+ connect( m_cddbpQuery, SIGNAL(inexactMatches(K3bCddbQuery*)),
+ this, SLOT(slotMultibleMatches(K3bCddbQuery*)) );
+ }
+
+ m_cddbpQuery->setServer( server, port );
+
+ return m_cddbpQuery;
+ }
+}
+
+
+void K3bCddb::localQuery()
+{
+ if( !m_localQuery ) {
+ m_localQuery = new K3bCddbLocalQuery( this );
+ connect( m_localQuery, SIGNAL(infoMessage(const QString&)),
+ this, SIGNAL(infoMessage(const QString&)) );
+ connect( m_localQuery, SIGNAL(queryFinished(K3bCddbQuery*)),
+ this, SLOT(slotQueryFinished(K3bCddbQuery*)) );
+ connect( m_localQuery, SIGNAL(inexactMatches(K3bCddbQuery*)),
+ this, SLOT(slotMultibleMatches(K3bCddbQuery*)) );
+ }
+
+ m_localQuery->setCddbDir( m_localCddbDirs[m_iCurrentQueriedLocalDir] );
+
+ m_localQuery->query( m_toc );
+}
+
+
+QString K3bCddb::errorString() const
+{
+ if( !m_lastUsedQuery )
+ return "no query";
+
+ switch( m_lastUsedQuery->error() ) {
+ case K3bCddbQuery::SUCCESS:
+ return i18n("Found freedb entry.");
+ case K3bCddbQuery::NO_ENTRY_FOUND:
+ return i18n("No entry found");
+ case K3bCddbQuery::CONNECTION_ERROR:
+ return i18n("Error while connecting to host.");
+ case K3bCddbQuery::WORKING:
+ return i18n("Working...");
+ case K3bCddbQuery::QUERY_ERROR:
+ case K3bCddbQuery::READ_ERROR:
+ case K3bCddbQuery::FAILURE:
+ default:
+ return i18n("Communication error.");
+ }
+}
+
+
+const K3bCddbResultEntry& K3bCddb::result() const
+{
+ // return m_lastUsedQuery->result();
+ return m_lastResult;
+}
+
+
+void K3bCddb::saveEntry( const K3bCddbResultEntry& entry )
+{
+ if( !m_localSubmit ) {
+ m_localSubmit = new K3bCddbLocalSubmit( this );
+ connect( m_localSubmit, SIGNAL(submitFinished(K3bCddbSubmit*)),
+ this, SLOT(slotSubmitFinished(K3bCddbSubmit*)) );
+ }
+
+ m_localSubmit->setCddbDir( m_localCddbDirs[0] );
+
+ m_localSubmit->submit( entry );
+}
+
+
+void K3bCddb::slotSubmitFinished( K3bCddbSubmit* s )
+{
+ emit submitFinished( s->error() == K3bCddbSubmit::SUCCESS );
+}
+
+#include "k3bcddb.moc"
+
diff --git a/libk3b/cddb/k3bcddb.h b/libk3b/cddb/k3bcddb.h
new file mode 100644
index 0000000..86b67c5
--- /dev/null
+++ b/libk3b/cddb/k3bcddb.h
@@ -0,0 +1,103 @@
+/*
+ *
+ * $Id: k3bcddb.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BCDDB_H
+#define K3BCDDB_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qobject.h>
+
+#include <k3btoc.h>
+
+#include "k3bcddbresult.h"
+#include "k3b_export.h"
+
+class KConfig;
+class K3bCddbQuery;
+class K3bCddbHttpQuery;
+class K3bCddbpQuery;
+class K3bCddbLocalQuery;
+class K3bCddbSubmit;
+class K3bCddbLocalSubmit;
+
+
+class LIBK3B_EXPORT K3bCddb : public QObject
+{
+ Q_OBJECT
+
+ public:
+ K3bCddb( QObject* parent = 0, const char* name = 0 );
+ ~K3bCddb();
+
+ QString errorString() const;
+
+ /**
+ * Do NOT call this before queryResult has
+ * been emitted
+ */
+ const K3bCddbResultEntry& result() const;
+
+ public slots:
+ /** query a cd and connect to the queryFinished signal */
+ void query( const K3bDevice::Toc& );
+ void readConfig( KConfig* c );
+ void saveEntry( const K3bCddbResultEntry& );
+
+ signals:
+ void queryFinished( int error );
+ void submitFinished( bool success );
+ void infoMessage( const QString& );
+
+ private slots:
+ void localQuery();
+ void remoteQuery();
+ void slotQueryFinished( K3bCddbQuery* );
+ void slotSubmitFinished( K3bCddbSubmit* );
+ void slotMultibleMatches( K3bCddbQuery* );
+ void slotNoEntry();
+
+ private:
+ K3bCddbQuery* getQuery( const QString& );
+
+ K3bCddbHttpQuery* m_httpQuery;
+ K3bCddbpQuery* m_cddbpQuery;
+ K3bCddbLocalQuery* m_localQuery;
+ K3bCddbLocalSubmit* m_localSubmit;
+
+ K3bDevice::Toc m_toc;
+ unsigned int m_iCurrentQueriedServer;
+ unsigned int m_iCurrentQueriedLocalDir;
+
+ const K3bCddbQuery* m_lastUsedQuery;
+ K3bCddbResultEntry m_lastResult;
+
+ // config
+ QStringList m_cddbServer;
+ QString m_proxyServer;
+ int m_proxyPort;
+ QString m_cgiPath;
+ bool m_bUseProxyServer;
+ bool m_bUseKdeSettings;
+ QStringList m_localCddbDirs;
+ bool m_bSaveCddbEntriesLocally;
+ bool m_bUseManualCgiPath;
+ bool m_bRemoteCddbQuery;
+ bool m_bLocalCddbQuery;
+};
+
+
+#endif
diff --git a/libk3b/cddb/k3bcddbhttpquery.cpp b/libk3b/cddb/k3bcddbhttpquery.cpp
new file mode 100644
index 0000000..a453c3e
--- /dev/null
+++ b/libk3b/cddb/k3bcddbhttpquery.cpp
@@ -0,0 +1,233 @@
+/*
+ *
+ * $Id: k3bcddbhttpquery.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+
+#include "k3bcddbhttpquery.h"
+
+#include "k3bcddbresult.h"
+
+#include <qstringlist.h>
+#include <qregexp.h>
+#include <qtextstream.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kio/global.h>
+#include <kio/job.h>
+
+
+K3bCddbHttpQuery::K3bCddbHttpQuery( QObject* parent, const char* name )
+ : K3bCddbQuery( parent, name )
+{
+ m_server = "freedb.org";
+ m_port = 80;
+ m_cgiPath = "/~cddb/cddb.cgi";
+}
+
+
+K3bCddbHttpQuery::~K3bCddbHttpQuery()
+{
+}
+
+
+void K3bCddbHttpQuery::doQuery()
+{
+ setError( WORKING );
+ m_state = QUERY;
+
+ performCommand( queryString() );
+}
+
+
+void K3bCddbHttpQuery::doMatchQuery()
+{
+ setError( WORKING );
+ m_state = READ;
+ m_parsingBuffer.truncate(0);
+
+ performCommand( QString( "cddb read %1 %2").arg( header().category ).arg( header().discid ) );
+}
+
+
+void K3bCddbHttpQuery::performCommand( const QString& cmd )
+{
+ KURL url;
+ url.setProtocol( "http" );
+ url.setHost( m_server );
+ url.setPort( m_port );
+ url.setPath( m_cgiPath );
+
+ url.addQueryItem( "cmd", cmd );
+ url.addQueryItem( "hello", handshakeString() );
+ url.addQueryItem( "proto", "6" );
+
+ m_data.truncate(0);
+
+ kdDebug() << "(K3bCddbHttpQuery) getting url: " << url.prettyURL() << endl;
+
+ KIO::TransferJob* job = KIO::get( url, false, false );
+
+ if( !job ) {
+ setError( CONNECTION_ERROR );
+ emit infoMessage( i18n("Could not connect to host %1").arg(m_server) );
+ emitQueryFinished();
+ return;
+ }
+
+ connect( job, SIGNAL(data(KIO::Job*, const QByteArray&)),
+ SLOT(slotData(KIO::Job*, const QByteArray&)) );
+ connect( job, SIGNAL(result(KIO::Job*)),
+ SLOT(slotResult(KIO::Job*)) );
+}
+
+
+
+void K3bCddbHttpQuery::slotData( KIO::Job*, const QByteArray& data )
+{
+ if( data.size() ) {
+ QDataStream stream( m_data, IO_WriteOnly | IO_Append );
+ stream.writeRawBytes( data.data(), data.size() );
+ }
+}
+
+
+void K3bCddbHttpQuery::slotResult( KIO::Job* job )
+{
+ if( job->error() ) {
+ emit infoMessage( job->errorString() );
+ setError( CONNECTION_ERROR );
+ emitQueryFinished();
+ return;
+ }
+
+ QStringList lines = QStringList::split( "\n", QString::fromUtf8( m_data.data(), m_data.size() ) );
+
+ for( QStringList::const_iterator it = lines.begin(); it != lines.end(); ++it ) {
+ QString line = *it;
+
+ // kdDebug() << "(K3bCddbHttpQuery) line: " << line << endl;
+
+ switch( m_state ) {
+
+ case QUERY:
+ if( getCode( line ) == 200 ) {
+ // parse exact match and send a read command
+ K3bCddbResultHeader header;
+ parseMatchHeader( line.mid(4), header );
+
+ queryMatch( header );
+ }
+
+ else if( getCode( line ) == 210 ) {
+ // TODO: perhaps add an "exact" field to K3bCddbEntry
+ kdDebug() << "(K3bCddbHttpQuery) Found multiple exact matches" << endl;
+
+ emit infoMessage( i18n("Found multiple exact matches") );
+
+ m_state = QUERY_DATA;
+ }
+
+ else if( getCode( line ) == 211 ) {
+ kdDebug() << "(K3bCddbHttpQuery) Found inexact matches" << endl;
+
+ emit infoMessage( i18n("Found inexact matches") );
+
+ m_state = QUERY_DATA;
+ }
+
+ else if( getCode( line ) == 202 ) {
+ kdDebug() << "(K3bCddbHttpQuery) no match found" << endl;
+ emit infoMessage( i18n("No match found") );
+ setError(NO_ENTRY_FOUND);
+ m_state = FINISHED;
+ emitQueryFinished();
+ return;
+ }
+
+ else {
+ kdDebug() << "(K3bCddbHttpQuery) Error while querying: " << line << endl;
+ emit infoMessage( i18n("Error while querying") );
+ setError(QUERY_ERROR);
+ m_state = FINISHED;
+ emitQueryFinished();
+ return;
+ }
+ break;
+
+ case QUERY_DATA:
+ if( line.startsWith( "." ) ) {
+ // finished query
+ // go on reading
+
+
+ // here we have the inexact matches headers and should emit the
+ // inexactMatches signal
+ emit inexactMatches( this );
+ }
+ else {
+ kdDebug() << "(K3bCddbHttpQuery) inexact match: " << line << endl;
+
+ // create a new resultHeader
+ K3bCddbResultHeader header;
+ parseMatchHeader( line, header );
+ m_inexactMatches.append(header);
+ }
+ break;
+
+ case READ:
+ if( getCode( line ) == 210 ) {
+
+ // we just start parsing the read data
+ m_state = READ_DATA;
+ }
+
+ else {
+ emit infoMessage( i18n("Could not read match") );
+ setError(READ_ERROR);
+ m_state = FINISHED;
+ emitQueryFinished();
+ return;
+ }
+ break;
+
+
+ case READ_DATA:
+
+ // kdDebug() << "parsing line: " << line << endl;
+
+ if( line.startsWith( "." ) ) {
+
+ kdDebug() << "(K3bCddbHttpQuery query finished." << endl;
+
+ QTextStream strStream( m_parsingBuffer, IO_ReadOnly );
+ parseEntry( strStream, result() );
+
+ setError(SUCCESS);
+ m_state = FINISHED;
+ emitQueryFinished();
+ return;
+ }
+
+ else {
+ m_parsingBuffer.append(line + "\n");
+ }
+ break;
+ }
+ }
+}
+
+
+#include "k3bcddbhttpquery.moc"
diff --git a/libk3b/cddb/k3bcddbhttpquery.h b/libk3b/cddb/k3bcddbhttpquery.h
new file mode 100644
index 0000000..b1e544e
--- /dev/null
+++ b/libk3b/cddb/k3bcddbhttpquery.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * $Id: k3bcddbhttpquery.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BCDDB_HTTP_QUERY_H
+#define K3BCDDB_HTTP_QUERY_H
+
+#include "k3bcddbquery.h"
+#include "k3bcddbresult.h"
+
+#include <qvaluelist.h>
+
+namespace KIO {
+ class Job;
+}
+
+class K3bCddbHttpQuery : public K3bCddbQuery
+{
+ Q_OBJECT
+
+ public:
+ K3bCddbHttpQuery( QObject* parent = 0, const char* name = 0 );
+ ~K3bCddbHttpQuery();
+
+ public slots:
+ void setServer( const QString& s, int port = 80 ) { m_server = s; m_port = port; }
+ void setCgiPath( const QString& p ) { m_cgiPath = p; }
+
+ protected slots:
+ void doQuery();
+ void doMatchQuery();
+ void slotResult( KIO::Job* );
+ void slotData( KIO::Job*, const QByteArray& data );
+
+ private:
+ void performCommand( const QString& );
+
+ enum State { QUERY, QUERY_DATA, READ, READ_DATA, FINISHED };
+
+ int m_state;
+ QString m_server;
+ int m_port;
+ QString m_cgiPath;
+
+ QString m_currentlyConnectingServer;
+
+ QByteArray m_data;
+ QString m_parsingBuffer;
+};
+
+#endif
+
diff --git a/libk3b/cddb/k3bcddblocalquery.cpp b/libk3b/cddb/k3bcddblocalquery.cpp
new file mode 100644
index 0000000..b3a1264
--- /dev/null
+++ b/libk3b/cddb/k3bcddblocalquery.cpp
@@ -0,0 +1,129 @@
+/*
+ *
+ * $Id: k3bcddblocalquery.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bcddblocalquery.h"
+
+#include <qdir.h>
+#include <qfile.h>
+#include <qtextstream.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+
+K3bCddbLocalQuery::K3bCddbLocalQuery( QObject* parent , const char* name )
+ : K3bCddbQuery( parent, name )
+{
+}
+
+
+K3bCddbLocalQuery::~K3bCddbLocalQuery()
+{
+}
+
+
+void K3bCddbLocalQuery::doQuery()
+{
+ emit infoMessage( i18n("Searching entry in %1").arg( m_cddbDir ) );
+ kapp->processEvents(); //BAD!
+
+ QString path = preparePath( m_cddbDir );
+
+ kdDebug() << "(K3bCddbLocalQuery) searching in dir " << path << " for "
+ << QString::number( toc().discId(), 16 ).rightJustify( 8, '0' ) << endl;
+
+ for( QStringList::const_iterator it = categories().begin();
+ it != categories().end(); ++it ) {
+
+ QString file = path + *it + "/" + QString::number( toc().discId(), 16 ).rightJustify( 8, '0' );
+
+ if( QFile::exists( file ) ) {
+ // found file
+
+ QFile f( file );
+ if( !f.open( IO_ReadOnly ) ) {
+ kdDebug() << "(K3bCddbLocalQuery) Could not open file" << endl;
+ }
+ else {
+ QTextStream t( &f );
+
+ K3bCddbResultEntry entry;
+ parseEntry( t, entry );
+ K3bCddbResultHeader header;
+ header.discid = QString::number( toc().discId(), 16 ).rightJustify( 8, '0' );
+ header.category = *it;
+ header.title = entry.cdTitle;
+ header.artist = entry.cdArtist;
+ m_inexactMatches.append(header);
+ }
+ }
+ else {
+ kdDebug() << "(K3bCddbLocalQuery) Could not find local entry in category " << *it << endl;
+ }
+ }
+
+ if( m_inexactMatches.count() > 0 ) {
+ setError( SUCCESS );
+ if( m_inexactMatches.count() == 1 ) {
+ queryMatch( m_inexactMatches.first() );
+ }
+ else {
+ emit inexactMatches( this );
+ }
+ }
+ else {
+ setError( NO_ENTRY_FOUND );
+ emit queryFinished( this );
+ }
+}
+
+
+void K3bCddbLocalQuery::doMatchQuery()
+{
+ QString path = preparePath( m_cddbDir ) + header().category + "/" + header().discid;
+
+ QFile f( path );
+ if( !f.open( IO_ReadOnly ) ) {
+ kdDebug() << "(K3bCddbLocalQuery) Could not open file" << endl;
+ setError( READ_ERROR );
+ }
+ else {
+ QTextStream t( &f );
+
+ parseEntry( t, result() );
+ result().discid = header().discid;
+ result().category = header().category;
+ setError( SUCCESS );
+ }
+ emit queryFinished( this );
+}
+
+
+QString K3bCddbLocalQuery::preparePath( const QString& p )
+{
+ QString path = p;
+ if( path.startsWith( "~" ) )
+ path.replace( 0, 1, QDir::homeDirPath() );
+ else if( !path.startsWith( "/" ) )
+ path.prepend( QDir::homeDirPath() );
+ if( path[path.length()-1] != '/' )
+ path.append( "/" );
+
+ return path;
+}
+
+#include "k3bcddblocalquery.moc"
diff --git a/libk3b/cddb/k3bcddblocalquery.h b/libk3b/cddb/k3bcddblocalquery.h
new file mode 100644
index 0000000..d68d379
--- /dev/null
+++ b/libk3b/cddb/k3bcddblocalquery.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * $Id: k3bcddblocalquery.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+
+#ifndef K3BCDDB_LOCAL_QUERY_H
+#define K3BCDDB_LOCAL_QUERY_H
+
+#include "k3bcddbquery.h"
+#include "k3bcddbresult.h"
+
+#include <qstring.h>
+
+
+class K3bCddbLocalQuery : public K3bCddbQuery
+{
+ Q_OBJECT
+
+ public:
+ K3bCddbLocalQuery( QObject* parent = 0, const char* name = 0 );
+ ~K3bCddbLocalQuery();
+
+ public slots:
+ void setCddbDir( const QString& dir ) { m_cddbDir = dir; }
+
+ protected:
+ void doQuery();
+ void doMatchQuery();
+
+ private:
+ QString preparePath( const QString& p );
+
+ QString m_cddbDir;
+};
+
+#endif
diff --git a/libk3b/cddb/k3bcddblocalsubmit.cpp b/libk3b/cddb/k3bcddblocalsubmit.cpp
new file mode 100644
index 0000000..f2d1e69
--- /dev/null
+++ b/libk3b/cddb/k3bcddblocalsubmit.cpp
@@ -0,0 +1,102 @@
+/*
+ *
+ * $Id: k3bcddblocalsubmit.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+
+#include "k3bcddblocalsubmit.h"
+
+#include <qdir.h>
+#include <qfile.h>
+#include <qtextstream.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+
+K3bCddbLocalSubmit::K3bCddbLocalSubmit( QObject* parent, const char* name )
+ : K3bCddbSubmit( parent, name )
+{
+}
+
+
+K3bCddbLocalSubmit::~K3bCddbLocalSubmit()
+{
+}
+
+
+void K3bCddbLocalSubmit::doSubmit()
+{
+ QString path = m_cddbDir;
+ if( path.startsWith( "~" ) )
+ path.replace( 0, 1, QDir::homeDirPath() + "/" );
+ else if( !path.startsWith( "/" ) )
+ path.prepend( QDir::homeDirPath() + "/" );
+ if( path[path.length()-1] != '/' )
+ path.append( "/" );
+
+ if( !QFile::exists( path ) && !QDir().mkdir( path ) ) {
+ kdDebug() << "(K3bCddbLocalSubmit) could not create directory: " << path << endl;
+ setError( IO_ERROR );
+ emit submitFinished( this );
+ return;
+ }
+
+ if( QFile::exists( path ) ) {
+ // if the category dir does not exists
+ // create it
+
+ path += resultEntry().category;
+
+ if( !QFile::exists( path ) ) {
+ if( !QDir().mkdir( path ) ) {
+ kdDebug() << "(K3bCddbLocalSubmit) could not create directory: " << path << endl;
+ setError( IO_ERROR );
+ emit submitFinished( this );
+ return;
+ }
+ }
+
+ // we always overwrite existing entries
+ path += "/" + resultEntry().discid;
+ QFile entryFile( path );
+ if( entryFile.exists() ) {
+ kdDebug() << "(K3bCddbLocalSubmit) file already exists: " << path << endl;
+ }
+
+ if( !entryFile.open( IO_WriteOnly ) ) {
+ kdDebug() << "(K3bCddbLocalSubmit) could not create file: " << path << endl;
+ setError( IO_ERROR );
+ emit submitFinished( this );
+ }
+ else {
+ kdDebug() << "(K3bCddbLocalSubmit) creating file: " << path << endl;
+ QTextStream entryStream( &entryFile );
+ entryStream.setEncoding( QTextStream::UnicodeUTF8 );
+ entryStream << resultEntry().rawData;
+ entryFile.close();
+
+ setError( SUCCESS );
+ emit submitFinished( this );
+ }
+ }
+ else {
+ kdDebug() << "(K3bCddbLocalSubmit) could not find directory: " << path << endl;
+ setError( IO_ERROR );
+ emit infoMessage( i18n("Could not find directory: %1").arg(path) );
+ emit submitFinished( this );
+ }
+}
+
+#include "k3bcddblocalsubmit.moc"
diff --git a/libk3b/cddb/k3bcddblocalsubmit.h b/libk3b/cddb/k3bcddblocalsubmit.h
new file mode 100644
index 0000000..8b7ea91
--- /dev/null
+++ b/libk3b/cddb/k3bcddblocalsubmit.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * $Id: k3bcddblocalsubmit.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BCDDB_LOCAL_SUBMIT_H
+#define K3BCDDB_LOCAL_SUBMIT_H
+
+#include "k3bcddbsubmit.h"
+
+#include <qstring.h>
+
+
+class K3bCddbLocalSubmit : public K3bCddbSubmit
+{
+ Q_OBJECT
+
+ public:
+ K3bCddbLocalSubmit( QObject* parent = 0, const char* name = 0 );
+ ~K3bCddbLocalSubmit();
+
+ public slots:
+ void setCddbDir( const QString& dir ) { m_cddbDir = dir; }
+
+ protected slots:
+ void doSubmit();
+
+ private:
+ QString m_cddbDir;
+};
+
+#endif
diff --git a/libk3b/cddb/k3bcddbmultientriesdialog.cpp b/libk3b/cddb/k3bcddbmultientriesdialog.cpp
new file mode 100644
index 0000000..094176a
--- /dev/null
+++ b/libk3b/cddb/k3bcddbmultientriesdialog.cpp
@@ -0,0 +1,74 @@
+/*
+ *
+ * $Id: k3bcddbmultientriesdialog.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bcddbmultientriesdialog.h"
+
+#include <qlayout.h>
+#include <qframe.h>
+#include <qlabel.h>
+
+#include <klistbox.h>
+#include <klocale.h>
+
+
+
+K3bCddbMultiEntriesDialog::K3bCddbMultiEntriesDialog( QWidget* parent, const char* name )
+ : KDialogBase( Plain, i18n("CDDB Database Entry"), Ok|Cancel, Ok, parent, name )
+{
+ QFrame* frame = plainPage();
+ QVBoxLayout* layout = new QVBoxLayout( frame );
+ layout->setAutoAdd( true );
+ layout->setSpacing( spacingHint() );
+ layout->setMargin( 0 );
+
+ QLabel* infoLabel = new QLabel( i18n("K3b found multiple inexact CDDB entries. Please select one."), frame );
+ infoLabel->setAlignment( WordBreak );
+
+ m_listBox = new KListBox( frame, "list_box");
+
+ setMinimumSize( 280, 200 );
+}
+
+K3bCddbResultHeader K3bCddbMultiEntriesDialog::selectCddbEntry( K3bCddbQuery* query, QWidget* parent )
+{
+ K3bCddbMultiEntriesDialog d( parent );
+
+ const QValueList<K3bCddbResultHeader> headers = query->getInexactMatches();
+
+ int i = 1;
+ for( QValueListConstIterator<K3bCddbResultHeader> it = headers.begin();
+ it != headers.end(); ++it ) {
+ d.m_listBox->insertItem( QString::number(i) + " " +
+ (*it).artist + " - " +
+ (*it).title + " (" +
+ (*it).category + ")" );
+ ++i;
+ }
+
+ d.m_listBox->setSelected( 0, true );
+
+ if( d.exec() == QDialog::Accepted )
+ return headers[ d.m_listBox->currentItem() >= 0 ? d.m_listBox->currentItem() : 0 ];
+ else
+ return K3bCddbResultHeader();
+}
+
+
+K3bCddbMultiEntriesDialog::~K3bCddbMultiEntriesDialog(){
+}
+
+
+#include "k3bcddbmultientriesdialog.moc"
diff --git a/libk3b/cddb/k3bcddbmultientriesdialog.h b/libk3b/cddb/k3bcddbmultientriesdialog.h
new file mode 100644
index 0000000..15cc6f8
--- /dev/null
+++ b/libk3b/cddb/k3bcddbmultientriesdialog.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * $Id: k3bcddbmultientriesdialog.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BCDDBMULTIENTRIESDIALOG_H
+#define K3BCDDBMULTIENTRIESDIALOG_H
+
+#include <kdialogbase.h>
+
+#include "k3bcddbquery.h"
+#include "k3bcddbresult.h"
+
+
+class QStringList;
+class KListBox;
+
+/**
+ *@author Sebastian Trueg
+ */
+class K3bCddbMultiEntriesDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ ~K3bCddbMultiEntriesDialog();
+
+ static K3bCddbResultHeader selectCddbEntry( K3bCddbQuery* query, QWidget* parent = 0 );
+
+ protected:
+ K3bCddbMultiEntriesDialog( QWidget* parent = 0, const char* name = 0);
+
+ private:
+ KListBox *m_listBox;
+};
+
+#endif
diff --git a/libk3b/cddb/k3bcddbpquery.cpp b/libk3b/cddb/k3bcddbpquery.cpp
new file mode 100644
index 0000000..fefc8e4
--- /dev/null
+++ b/libk3b/cddb/k3bcddbpquery.cpp
@@ -0,0 +1,278 @@
+/*
+ *
+ * $Id: k3bcddbpquery.cpp 619556 2007-01-03 17:38:12Z trueg $
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bcddbpquery.h"
+
+#include <qstringlist.h>
+#include <qsocket.h>
+#include <qtextstream.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+
+
+
+K3bCddbpQuery::K3bCddbpQuery( QObject* parent, const char* name )
+ :K3bCddbQuery( parent, name )
+{
+ m_socket = new QSocket( this );
+ m_stream.setDevice( m_socket );
+ m_stream.setEncoding( QTextStream::UnicodeUTF8 );
+
+ connect( m_socket, SIGNAL(connected()), this, SLOT(slotConnected()) );
+ connect( m_socket, SIGNAL(hostFound()), this, SLOT(slotHostFound()) );
+ connect( m_socket, SIGNAL(connectionClosed()), this, SLOT(slotConnectionClosed()) );
+ connect( m_socket, SIGNAL(error(int)), this, SLOT(slotError(int)) );
+ connect( m_socket, SIGNAL(readyRead()), this, SLOT(slotReadyRead()) );
+}
+
+
+K3bCddbpQuery::~K3bCddbpQuery()
+{
+ delete m_socket;
+}
+
+void K3bCddbpQuery::doQuery()
+{
+ setError( WORKING );
+
+ m_state = GREETING;
+
+ // connect to the server
+
+ m_socket->connectToHost( m_server, m_port );
+ emit infoMessage( i18n("Searching %1 on port %2").arg(m_server).arg(m_port) );
+}
+
+
+void K3bCddbpQuery::doMatchQuery()
+{
+ // we should still be connected
+ // TODO: check this
+
+ QString read = QString( "cddb read %1 %2").arg( header().category ).arg( header().discid );
+
+ m_state = READ;
+ m_parsingBuffer = "";
+
+ kdDebug() << "(K3bCddbpQuery) Read: " << read << endl;
+
+ m_stream << read << endl << flush;
+}
+
+
+void K3bCddbpQuery::slotHostFound()
+{
+ emit infoMessage( i18n("Host found") );
+}
+
+
+void K3bCddbpQuery::slotConnected()
+{
+ emit infoMessage( i18n("Connected") );
+}
+
+
+void K3bCddbpQuery::slotConnectionClosed()
+{
+ emit infoMessage( i18n("Connection closed") );
+ emitQueryFinished();
+}
+
+
+void K3bCddbpQuery::cddbpQuit()
+{
+ m_state = QUIT;
+ m_stream << "quit" << endl << flush;
+}
+
+
+void K3bCddbpQuery::slotReadyRead()
+{
+ while( m_socket->canReadLine() ) {
+ QString line = m_stream.readLine();
+
+ // kdDebug() << "(K3bCddbpQuery) line: " << line << endl;
+
+ switch( m_state ) {
+ case GREETING:
+ if( getCode( line ) == 200 || getCode( line ) == 201) {
+ emit infoMessage( i18n("OK, read access") );
+ m_state = HANDSHAKE;
+
+ m_stream << "cddb hello " << handshakeString() << endl << flush;
+ }
+
+ else {
+ emit infoMessage( i18n("Connection refused") );
+ setError( CONNECTION_ERROR );
+ m_socket->close();
+ }
+ break;
+
+ case HANDSHAKE:
+ if( getCode( line ) == 200 ) {
+ emit infoMessage( i18n("Handshake successful") );
+
+ m_state = PROTO;
+
+ m_stream << "proto 6" << endl << flush;
+ }
+
+ else {
+ emit infoMessage( i18n("Handshake failed") ); // server closes connection
+ setError( CONNECTION_ERROR );
+ m_socket->close(); // just to be sure
+ }
+ break;
+
+ case PROTO:
+ {
+ if( getCode( line ) == 501 ) {
+ kdDebug() << "(K3bCddbpQuery) illigal protocol level!" << endl;
+ }
+
+ // just ignore the reply since it's not important for the functionality
+ m_state = QUERY;
+
+ m_stream << queryString() << endl << flush;
+ break;
+ }
+
+ case QUERY:
+ if( getCode( line ) == 200 ) {
+ // parse exact match and send a read command
+ K3bCddbResultHeader header;
+ parseMatchHeader( line.mid( 4 ), header );
+
+ emit infoMessage( i18n("Found exact match") );
+
+ queryMatch( header );
+ }
+
+ else if( getCode( line ) == 210 ) {
+ // TODO: perhaps add an "exact" field to K3bCddbEntry
+ kdDebug() << "(K3bCddbpQuery) Found multiple exact matches" << endl;
+
+ emit infoMessage( i18n("Found multiple exact matches") );
+
+ m_state = QUERY_DATA;
+ }
+
+ else if( getCode( line ) == 211 ) {
+ kdDebug() << "(K3bCddbpQuery) Found inexact matches" << endl;
+
+ emit infoMessage( i18n("Found inexact matches") );
+
+ m_state = QUERY_DATA;
+ }
+
+ else if( getCode( line ) == 202 ) {
+ kdDebug() << "(K3bCddbpQuery) no match found" << endl;
+ emit infoMessage( i18n("No match found") );
+ setError( NO_ENTRY_FOUND );
+ cddbpQuit();
+ }
+
+ else {
+ kdDebug() << "(K3bCddbpQuery) Error while querying: " << line << endl;
+ emit infoMessage( i18n("Error while querying") );
+ setError( QUERY_ERROR );
+ cddbpQuit();
+ }
+ break;
+
+ case QUERY_DATA:
+ if( line.startsWith( "." ) ) {
+ // finished query
+ // go on reading
+
+ emit inexactMatches( this );
+ return;
+ }
+ else {
+ kdDebug() << "(K3bCddbpQuery) inexact match: " << line << endl;
+ K3bCddbResultHeader header;
+ parseMatchHeader( line, header );
+ m_inexactMatches.append( header );
+ }
+ break;
+
+ case READ:
+ if( getCode( line ) == 210 ) {
+
+ // we just start parsing the read data
+ m_state = READ_DATA;
+ }
+
+ else {
+ emit infoMessage( i18n("Could not read match") );
+ setError( READ_ERROR );
+ cddbpQuit();
+ }
+ break;
+
+
+ case READ_DATA:
+
+ // kdDebug() << "(K3bCddbpQuery) parsing line: " << line << endl;
+
+ if( line.startsWith( "." ) ) {
+
+ kdDebug() << "(K3bCddbpQuery) query finished." << endl;
+
+ QTextStream strStream( m_parsingBuffer, IO_ReadOnly );
+ parseEntry( strStream, result() );
+
+ setError( SUCCESS );
+ cddbpQuit();
+ }
+
+ else {
+ m_parsingBuffer.append(line + "\n");
+ }
+ break;
+
+ case QUIT:
+ // no parsing needed
+ break;
+ }
+ }
+}
+
+
+void K3bCddbpQuery::slotError( int e )
+{
+ switch(e) {
+ case QSocket::ErrConnectionRefused:
+ kdDebug() << i18n("Connection to %1 refused").arg( m_server ) << endl;
+ emit infoMessage( i18n("Connection to %1 refused").arg( m_server ) );
+ break;
+ case QSocket::ErrHostNotFound:
+ kdDebug() << i18n("Could not find host %1").arg( m_server ) << endl;
+ emit infoMessage( i18n("Could not find host %1").arg( m_server ) );
+ break;
+ case QSocket::ErrSocketRead:
+ kdDebug() << i18n("Error while reading from %1").arg( m_server ) << endl;
+ emit infoMessage( i18n("Error while reading from %1").arg( m_server ) );
+ break;
+ }
+
+ m_socket->close();
+ emitQueryFinished();
+}
+
+#include "k3bcddbpquery.moc"
diff --git a/libk3b/cddb/k3bcddbpquery.h b/libk3b/cddb/k3bcddbpquery.h
new file mode 100644
index 0000000..78fe5df
--- /dev/null
+++ b/libk3b/cddb/k3bcddbpquery.h
@@ -0,0 +1,62 @@
+/*
+ *
+ * $Id: k3bcddbpquery.h 619556 2007-01-03 17:38:12Z trueg $
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BCDDBP_QUERY_H
+#define K3BCDDBP_QUERY_H
+
+#include "k3bcddbquery.h"
+#include "k3bcddbresult.h"
+
+#include <qstring.h>
+#include <qvaluelist.h>
+#include <qtextstream.h>
+
+class QSocket;
+
+class K3bCddbpQuery : public K3bCddbQuery
+{
+ Q_OBJECT
+
+ public:
+ K3bCddbpQuery( QObject* parent = 0, const char* name = 0 );
+ ~K3bCddbpQuery();
+
+ public slots:
+ void setServer( const QString& s, int port = 8080 ) { m_server = s; m_port = port; }
+
+ protected slots:
+ void slotHostFound();
+ void slotConnected();
+ void slotConnectionClosed();
+ void slotReadyRead();
+ void slotError( int e );
+ void doQuery();
+ void doMatchQuery();
+
+ private:
+ void cddbpQuit();
+ enum State { GREETING, HANDSHAKE, PROTO, QUERY, QUERY_DATA, READ, READ_DATA, QUIT };
+
+ int m_state;
+ QString m_server;
+ int m_port;
+
+ QSocket* m_socket;
+ QTextStream m_stream;
+
+ QString m_parsingBuffer;
+};
+
+#endif
diff --git a/libk3b/cddb/k3bcddbquery.cpp b/libk3b/cddb/k3bcddbquery.cpp
new file mode 100644
index 0000000..783f9a4
--- /dev/null
+++ b/libk3b/cddb/k3bcddbquery.cpp
@@ -0,0 +1,275 @@
+/*
+ *
+ * $Id: k3bcddbquery.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+
+#include "k3bcddbquery.h"
+
+#include "k3bcddbresult.h"
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+
+
+#include <qtextstream.h>
+#include <qstringlist.h>
+#include <qregexp.h>
+#include <qtimer.h>
+
+#include <stdlib.h>
+
+
+
+
+K3bCddbQuery::K3bCddbQuery( QObject* parent, const char* name )
+ : QObject(parent, name)
+{
+ m_bQueryFinishedEmited = false;
+}
+
+
+K3bCddbQuery::~K3bCddbQuery()
+{
+}
+
+
+void K3bCddbQuery::query( const K3bDevice::Toc& toc )
+{
+ m_bQueryFinishedEmited = false;
+ m_toc = toc;
+ m_inexactMatches.clear();
+
+ QTimer::singleShot( 0, this, SLOT(doQuery()) );
+}
+
+
+void K3bCddbQuery::queryMatch( const K3bCddbResultHeader& header )
+{
+ m_header = header;
+ m_result = K3bCddbResultEntry();
+ m_result.category = header.category;
+ m_result.discid = header.discid;
+
+ QTimer::singleShot( 0, this, SLOT(doMatchQuery()) );
+}
+
+
+const QStringList& K3bCddbQuery::categories()
+{
+ static QStringList s_cat = QStringList::split( ",", "rock,blues,misc,classical,"
+ "country,data,folk,jazz,newage,reggae,soundtrack" );
+ return s_cat;
+}
+
+
+bool K3bCddbQuery::parseEntry( QTextStream& stream, K3bCddbResultEntry& entry )
+{
+ entry.rawData = "";
+
+ stream.setEncoding( QTextStream::UnicodeUTF8 );
+
+ // parse data
+ QString line;
+ while( !(line = stream.readLine()).isNull() ) {
+ entry.rawData.append(line + "\n");
+
+ // !all fields may be splitted into several lines!
+
+ if( line.startsWith( "DISCID" ) ) {
+ // TODO: this could be several discids separated by comma!
+ }
+
+ else if( line.startsWith( "DYEAR" ) ) {
+ QString year = line.mid( 6 );
+ if( year.length() == 4 )
+ entry.year = year.toInt();
+ }
+
+ else if( line.startsWith( "DGENRE" ) ) {
+ entry.genre = line.mid( 7 );
+ }
+
+ else if( line.startsWith( "DTITLE" ) ) {
+ entry.cdTitle += line.mid( 7 );
+ }
+
+ else if( line.startsWith( "TTITLE" ) ) {
+ int eqSgnPos = line.find( "=" );
+ bool ok;
+ uint trackNum = (uint)line.mid( 6, eqSgnPos - 6 ).toInt( &ok );
+ if( !ok )
+ kdDebug() << "(K3bCddbQuery) !!! PARSE ERROR: " << line << endl;
+ else {
+ // kdDebug() << "(K3bCddbQuery) Track title for track " << trackNum << endl;
+
+ // make sure the list is big enough
+ while( entry.titles.count() <= trackNum )
+ entry.titles.append( "" );
+
+ entry.titles[trackNum] += line.mid( eqSgnPos+1 );
+ }
+ }
+
+ else if( line.startsWith( "EXTD" ) ) {
+ entry.cdExtInfo += line.mid( 5 );
+ }
+
+ else if( line.startsWith( "EXTT" ) ) {
+ int eqSgnPos = line.find( "=" );
+ bool ok;
+ uint trackNum = (uint)line.mid( 4, eqSgnPos - 4 ).toInt( &ok );
+ if( !ok )
+ kdDebug() << "(K3bCddbQuery) !!! PARSE ERROR: " << line << endl;
+ else {
+ // kdDebug() << "(K3bCddbQuery) Track extr track " << trackNum << endl;
+
+ // make sure the list is big enough
+ while( entry.extInfos.count() <= trackNum )
+ entry.extInfos.append( "" );
+
+ entry.extInfos[trackNum] += line.mid( eqSgnPos+1 );
+ }
+ }
+
+ else if( line.startsWith( "#" ) ) {
+ // kdDebug() << "(K3bCddbQuery) comment: " << line << endl;
+ }
+
+ else {
+ kdDebug() << "(K3bCddbQuery) Unknown field: " << line << endl;
+ }
+ }
+
+ // now split the titles in the last added match
+ // if no " / " delimiter is present title and artist are the same
+ // -------------------------------------------------------------------
+ QString fullTitle = entry.cdTitle;
+ int splitPos = fullTitle.find( " / " );
+ if( splitPos < 0 )
+ entry.cdArtist = fullTitle;
+ else {
+ // split
+ entry.cdTitle = fullTitle.mid( splitPos + 3 );
+ entry.cdArtist = fullTitle.left( splitPos );
+ }
+
+
+ for( QStringList::iterator it = entry.titles.begin();
+ it != entry.titles.end(); ++it ) {
+ QString fullTitle = *it;
+ int splitPos = fullTitle.find( " / " );
+ if( splitPos < 0 )
+ entry.artists.append( entry.cdArtist );
+ else {
+ // split
+ *it = fullTitle.mid( splitPos + 3 );
+ entry.artists.append( fullTitle.left( splitPos ) );
+ }
+ }
+
+
+ // replace all "\\n" with "\n"
+ for( QStringList::iterator it = entry.titles.begin();
+ it != entry.titles.end(); ++it ) {
+ (*it).replace( "\\\\\\\\n", "\\n" );
+ }
+
+ for( QStringList::iterator it = entry.artists.begin();
+ it != entry.artists.end(); ++it ) {
+ (*it).replace( "\\\\\\\\n", "\\n" );
+ }
+
+ for( QStringList::iterator it = entry.extInfos.begin();
+ it != entry.extInfos.end(); ++it ) {
+ (*it).replace( "\\\\\\\\n", "\\n" );
+ }
+
+ entry.cdTitle.replace( "\\\\\\\\n", "\\n" );
+ entry.cdArtist.replace( "\\\\\\\\n", "\\n" );
+ entry.cdExtInfo.replace( "\\\\\\\\n", "\\n" );
+ entry.genre.replace( "\\\\\\\\n", "\\n" );
+
+ return true;
+}
+
+
+int K3bCddbQuery::getCode( const QString& line )
+{
+ bool ok;
+ int code = line.left( 3 ).toInt( &ok );
+ if( !ok )
+ code = -1;
+ return code;
+}
+
+
+QString K3bCddbQuery::handshakeString() const
+{
+ QString user( getenv("USER") );
+ QString host( getenv("HOST") );
+ if( user.isEmpty() )
+ user = "kde-user";
+ if( host.isEmpty() )
+ host = "kde-host";
+
+ return QString("%1 %2 K3b %3").arg(user).arg(host).arg(kapp->aboutData()->version());
+}
+
+
+QString K3bCddbQuery::queryString() const
+{
+ QString query = "cddb query "
+ + QString::number( m_toc.discId(), 16 ).rightJustify( 8, '0' )
+ + " "
+ + QString::number( (unsigned int)m_toc.count() );
+
+ for( K3bDevice::Toc::const_iterator it = m_toc.begin(); it != m_toc.end(); ++it ) {
+ query.append( QString( " %1" ).arg( (*it).firstSector().lba() ) );
+ }
+
+ query.append( QString( " %1" ).arg( m_toc.length().lba() / 75 ) );
+
+ return query;
+}
+
+
+bool K3bCddbQuery::parseMatchHeader( const QString& line, K3bCddbResultHeader& header )
+{
+ // format: category id title
+ // where title could be artist and title splitted with a /
+ header.category = line.section( ' ', 0, 0 );
+ header.discid = line.section( ' ', 1, 1 );
+ header.title = line.mid( header.category.length() + header.discid.length() + 2 );
+ int slashPos = header.title.find( "/" );
+ if( slashPos > 0 ) {
+ header.artist = header.title.left(slashPos).stripWhiteSpace();
+ header.title = header.title.mid( slashPos+1 ).stripWhiteSpace();
+ }
+ return true;
+}
+
+
+void K3bCddbQuery::emitQueryFinished()
+{
+ if( !m_bQueryFinishedEmited ) {
+ m_bQueryFinishedEmited = true;
+ emit queryFinished( this );
+ }
+}
+
+
+#include "k3bcddbquery.moc"
diff --git a/libk3b/cddb/k3bcddbquery.h b/libk3b/cddb/k3bcddbquery.h
new file mode 100644
index 0000000..569e882
--- /dev/null
+++ b/libk3b/cddb/k3bcddbquery.h
@@ -0,0 +1,115 @@
+/*
+ *
+ * $Id: k3bcddbquery.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BCDDB_QUERY_H
+#define K3BCDDB_QUERY_H
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qtextstream.h>
+
+#include "k3bcddbresult.h"
+
+#include <k3btoc.h>
+#include "k3b_export.h"
+
+
+class LIBK3B_EXPORT K3bCddbQuery : public QObject
+{
+ Q_OBJECT
+
+ public:
+ K3bCddbQuery( QObject* parent = 0, const char* name = 0 );
+ virtual ~K3bCddbQuery();
+
+ void query( const K3bDevice::Toc& );
+
+ /**
+ * Use this if the query returned multiple matches
+ */
+ void queryMatch( const K3bCddbResultHeader& );
+
+ const K3bCddbResultEntry& result() const { return m_result; }
+
+ /**
+ * After emitting the signal inexactMatches one has to choose one
+ * of these entries and query it with queryInexactMatch
+ */
+ const QValueList<K3bCddbResultHeader>& getInexactMatches() const { return m_inexactMatches; }
+
+ static const QStringList& categories();
+
+ enum Error { SUCCESS = 0,
+ CANCELED,
+ NO_ENTRY_FOUND,
+ CONNECTION_ERROR,
+ QUERY_ERROR,
+ READ_ERROR,
+ FAILURE,
+ WORKING };
+
+ int error() const { return m_error; }
+
+ signals:
+ /**
+ * This gets emitted if a single entry has been found or
+ * no entry has been found.
+ */
+ void queryFinished( K3bCddbQuery* );
+
+ /**
+ * This gets emitted if multiple entries have been found.
+ * Call queryInexactMatch() after receiving it.
+ */
+ void inexactMatches( K3bCddbQuery* );
+
+ void infoMessage( const QString& );
+
+ protected slots:
+ virtual void doQuery() = 0;
+ virtual void doMatchQuery() = 0;
+
+ protected:
+ const K3bDevice::Toc& toc() const { return m_toc; }
+ K3bCddbResultHeader& header() { return m_header; }
+ K3bCddbResultEntry& result() { return m_result; }
+ void setError( int e ) { m_error = e; }
+
+ bool parseEntry( QTextStream&, K3bCddbResultEntry& );
+ int getCode( const QString& );
+ QString handshakeString() const;
+ QString queryString() const;
+ bool parseMatchHeader( const QString& line, K3bCddbResultHeader& header );
+
+ /**
+ * since I'm not quite sure when the socket will emit connectionClosed
+ * this method makes sure the queryFinished signal
+ * gets emited only once.
+ */
+ void emitQueryFinished();
+
+ QValueList<K3bCddbResultHeader> m_inexactMatches;
+
+ private:
+ K3bDevice::Toc m_toc;
+ K3bCddbResultEntry m_result;
+ K3bCddbResultHeader m_header;
+ int m_error;
+
+ bool m_bQueryFinishedEmited;
+};
+
+#endif
diff --git a/libk3b/cddb/k3bcddbresult.cpp b/libk3b/cddb/k3bcddbresult.cpp
new file mode 100644
index 0000000..b33b16e
--- /dev/null
+++ b/libk3b/cddb/k3bcddbresult.cpp
@@ -0,0 +1,49 @@
+/*
+ *
+ * $Id: k3bcddbresult.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+
+#include "k3bcddbresult.h"
+
+
+K3bCddbResult::K3bCddbResult()
+{
+}
+
+
+void K3bCddbResult::clear()
+{
+ m_entries.clear();
+}
+
+
+int K3bCddbResult::foundEntries() const
+{
+ return m_entries.count();
+}
+
+const K3bCddbResultEntry& K3bCddbResult::entry( unsigned int number ) const
+{
+ if( number >= m_entries.count() )
+ return m_emptyEntry;
+
+ return m_entries[number];
+}
+
+
+void K3bCddbResult::addEntry( const K3bCddbResultEntry& entry )
+{
+ m_entries.append( entry );
+}
diff --git a/libk3b/cddb/k3bcddbresult.h b/libk3b/cddb/k3bcddbresult.h
new file mode 100644
index 0000000..46dcb9a
--- /dev/null
+++ b/libk3b/cddb/k3bcddbresult.h
@@ -0,0 +1,79 @@
+/*
+ *
+ * $Id: k3bcddbresult.h 768492 2008-01-30 08:39:42Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+
+#ifndef K3B_CDDB_RESULT_H
+#define K3B_CDDB_RESULT_H
+
+
+#include <qstringlist.h>
+#include "k3b_export.h"
+
+
+class LIBK3B_EXPORT K3bCddbResultHeader
+{
+ public:
+ QString category;
+ QString title;
+ QString artist;
+ QString discid;
+};
+
+
+class LIBK3B_EXPORT K3bCddbResultEntry
+{
+ public:
+ // just to set a default
+ K3bCddbResultEntry()
+ : category("misc"),
+ year(0) {
+ }
+
+ QStringList titles;
+ QStringList artists;
+ QStringList extInfos;
+
+ QString cdTitle;
+ QString cdArtist;
+ QString cdExtInfo;
+
+ QString genre;
+ QString category;
+ int year;
+ QString discid;
+
+ QString rawData;
+};
+
+
+class LIBK3B_EXPORT K3bCddbResult
+{
+ public:
+ K3bCddbResult();
+ // K3bCddbQuery( const K3bCddbQuery& );
+
+ void clear();
+ void addEntry( const K3bCddbResultEntry& = K3bCddbResultEntry() );
+ const K3bCddbResultEntry& entry( unsigned int number = 0 ) const;
+ int foundEntries() const;
+
+ private:
+ QValueList<K3bCddbResultEntry> m_entries;
+
+ K3bCddbResultEntry m_emptyEntry;
+};
+
+#endif
diff --git a/libk3b/cddb/k3bcddbsubmit.cpp b/libk3b/cddb/k3bcddbsubmit.cpp
new file mode 100644
index 0000000..a04dbcb
--- /dev/null
+++ b/libk3b/cddb/k3bcddbsubmit.cpp
@@ -0,0 +1,84 @@
+/*
+ *
+ * $Id: k3bcddbsubmit.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bcddbsubmit.h"
+
+#include <qtimer.h>
+
+
+K3bCddbSubmit::K3bCddbSubmit( QObject* parent, const char* name )
+ : QObject( parent, name )
+{
+}
+
+
+K3bCddbSubmit::~K3bCddbSubmit()
+{
+}
+
+
+void K3bCddbSubmit::submit( const K3bCddbResultEntry& entry )
+{
+ m_resultEntry = entry;
+
+ if( m_resultEntry.rawData.isEmpty() )
+ createDataStream( m_resultEntry );
+
+ QTimer::singleShot( 0, this, SLOT(doSubmit()) );
+}
+
+
+void K3bCddbSubmit::createDataStream( K3bCddbResultEntry& entry )
+{
+ entry.rawData.truncate(0);
+
+ QTextStream ts( &entry.rawData, IO_WriteOnly );
+
+ ts << "#" << endl
+ << "# Submitted via: K3b" << endl
+ << "#" << endl;
+
+ ts << "DISCID=" << entry.discid << endl
+ << "DTITLE=" << entry.cdArtist << " / " << entry.cdTitle << endl
+ << "DYEAR=";
+ if( entry.year > 0 )
+ ts << entry.year;
+ ts << endl;
+ ts << "DGENRE=" << entry.genre << endl;
+
+ bool allEqualArtist = true;
+ for( unsigned int i = 0; i < entry.artists.count(); ++i )
+ if( entry.artists[i] != entry.cdArtist &&
+ !entry.artists[i].isEmpty() ) {
+ allEqualArtist = false;
+ break;
+ }
+
+ for( unsigned int i = 0; i < entry.titles.count(); ++i ) {
+ ts << "TTITLE" << i << "=";
+ if( !allEqualArtist )
+ ts << entry.artists[i] << " / ";
+ ts << entry.titles[i] << endl;
+ }
+
+ ts << "EXTD=" << entry.cdExtInfo << endl;
+
+ for( unsigned int i = 0; i < entry.titles.count(); ++i ) {
+ ts << "EXTT" << i << "=" << entry.extInfos[i] << endl;
+ }
+}
+
+#include "k3bcddbsubmit.moc"
diff --git a/libk3b/cddb/k3bcddbsubmit.h b/libk3b/cddb/k3bcddbsubmit.h
new file mode 100644
index 0000000..ff57101
--- /dev/null
+++ b/libk3b/cddb/k3bcddbsubmit.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * $Id: k3bcddbsubmit.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BCDDB_SUBMIT_H
+#define K3BCDDB_SUBMIT_H
+
+#include <qobject.h>
+#include <qstring.h>
+
+#include "k3bcddbresult.h"
+
+
+
+class K3bCddbSubmit : public QObject
+{
+ Q_OBJECT
+
+ public:
+ K3bCddbSubmit( QObject* parent = 0, const char* name = 0 );
+ virtual ~K3bCddbSubmit();
+
+ int error() const { return m_error; }
+
+ enum State { SUCCESS, WORKING, IO_ERROR, CONNECTION_ERROR };
+
+ public slots:
+ void submit( const K3bCddbResultEntry& );
+
+ signals:
+ void infoMessage( const QString& );
+ void submitFinished( K3bCddbSubmit* );
+
+ protected slots:
+ virtual void doSubmit() = 0;
+ void setError( int e ) { m_error = e; }
+
+ protected:
+ K3bCddbResultEntry& resultEntry() { return m_resultEntry; }
+
+ private:
+ void createDataStream( K3bCddbResultEntry& entry );
+
+ int m_error;
+ K3bCddbResultEntry m_resultEntry;
+};
+
+#endif
diff --git a/libk3b/configure.in.in b/libk3b/configure.in.in
new file mode 100644
index 0000000..af0c8f5
--- /dev/null
+++ b/libk3b/configure.in.in
@@ -0,0 +1,3 @@
+AC_CHECK_FUNCS(stat64)
+AC_CHECK_HEADERS(sys/vfs.h)
+AC_CHECK_HEADERS(sys/statvfs.h)
diff --git a/libk3b/core/Makefile.am b/libk3b/core/Makefile.am
new file mode 100644
index 0000000..3764d86
--- /dev/null
+++ b/libk3b/core/Makefile.am
@@ -0,0 +1,19 @@
+AM_CPPFLAGS = -I$(srcdir)/../../libk3bdevice -I$(srcdir)/../plugin -I$(srcdir)/../tools $(all_includes)
+
+noinst_LTLIBRARIES = libk3bcore.la
+
+libk3bcore_la_LIBADD = $(LIB_KIO) $(ARTSC_LIB)
+
+libk3bcore_la_LDFLAGS = $(all_libraries)
+
+libk3bcore_la_SOURCES = k3bcore.cpp k3bglobals.cpp k3bdefaultexternalprograms.cpp \
+ k3bexternalbinmanager.cpp k3bversion.cpp k3bprocess.cpp k3bjob.cpp \
+ k3bthread.cpp k3bthreadjob.cpp k3bglobalsettings.cpp k3bsimplejobhandler.cpp
+
+include_HEADERS = k3bcore.h k3bdefaultexternalprograms.h k3bexternalbinmanager.h \
+ k3bprocess.h k3bversion.h k3bglobals.h k3bjob.h k3bthread.h \
+ k3bthreadjob.h k3bglobalsettings.h k3bjobhandler.h \
+ k3b_export.h k3bjobhandler.h k3bsimplejobhandler.h
+
+METASOURCES = AUTO
+
diff --git a/libk3b/core/k3b_export.h b/libk3b/core/k3b_export.h
new file mode 100644
index 0000000..b6272f1
--- /dev/null
+++ b/libk3b/core/k3b_export.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
+ * Copyright (c) 2005 Laurent Montel <montel@kde.org>
+ * Copyright (C) 2005-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_EXPORT_H_
+#define _K3B_EXPORT_H_
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef __KDE_HAVE_GCC_VISIBILITY
+#define LIBK3B_NO_EXPORT __attribute__ ((visibility("hidden")))
+#define LIBK3B_EXPORT __attribute__ ((visibility("default")))
+#else
+#define LIBK3B_NO_EXPORT
+#define LIBK3B_EXPORT
+#endif
+
+#endif
+
diff --git a/libk3b/core/k3bcore.cpp b/libk3b/core/k3bcore.cpp
new file mode 100644
index 0000000..c10fec0
--- /dev/null
+++ b/libk3b/core/k3bcore.cpp
@@ -0,0 +1,375 @@
+/*
+ *
+ * $Id: k3bcore.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include <config.h>
+
+#include "k3bcore.h"
+#include "k3bjob.h"
+
+#include <k3bdevicemanager.h>
+#ifdef HAVE_HAL
+#include <k3bhalconnection.h>
+#endif
+#include <k3bexternalbinmanager.h>
+#include <k3bdefaultexternalprograms.h>
+#include <k3bglobals.h>
+#include <k3bversion.h>
+#include <k3bjob.h>
+#include <k3bthreadwidget.h>
+#include <k3bglobalsettings.h>
+#include <k3bpluginmanager.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kaboutdata.h>
+#include <kstandarddirs.h>
+#include <kapplication.h>
+
+#include <qptrlist.h>
+#include <qthread.h>
+#include <qmutex.h>
+
+
+static Qt::HANDLE s_guiThreadHandle = QThread::currentThread();
+
+// We cannot use QWaitCondition here since the event might be handled faster
+// than the thread starts the waiting
+class DeviceBlockingEventDoneCondition {
+public:
+ DeviceBlockingEventDoneCondition()
+ : m_done(false) {
+ }
+
+ void done() {
+ m_doneMutex.lock();
+ m_done = true;
+ m_doneMutex.unlock();
+ }
+
+ void wait() {
+ while( true ) {
+ m_doneMutex.lock();
+ bool done = m_done;
+ m_doneMutex.unlock();
+ if( done )
+ return;
+ }
+ }
+
+private:
+ QMutex m_doneMutex;
+ bool m_done;
+};
+
+class DeviceBlockingEvent : public QCustomEvent
+{
+public:
+ DeviceBlockingEvent( bool block_, K3bDevice::Device* dev, DeviceBlockingEventDoneCondition* cond_, bool* success_ )
+ : QCustomEvent( QEvent::User + 33 ),
+ block(block_),
+ device(dev),
+ cond(cond_),
+ success(success_) {
+ }
+
+ bool block;
+ K3bDevice::Device* device;
+ DeviceBlockingEventDoneCondition* cond;
+ bool* success;
+};
+
+
+class K3bCore::Private {
+public:
+ Private()
+ : version( LIBK3B_VERSION ),
+ config(0),
+ deleteConfig(false),
+ deviceManager(0),
+ externalBinManager(0),
+ pluginManager(0),
+ globalSettings(0) {
+ }
+
+ K3bVersion version;
+ KConfig* config;
+ bool deleteConfig;
+ K3bDevice::DeviceManager* deviceManager;
+ K3bExternalBinManager* externalBinManager;
+ K3bPluginManager* pluginManager;
+ K3bGlobalSettings* globalSettings;
+
+ QValueList<K3bJob*> runningJobs;
+ QValueList<K3bDevice::Device*> blockedDevices;
+};
+
+
+
+K3bCore* K3bCore::s_k3bCore = 0;
+
+
+
+K3bCore::K3bCore( QObject* parent, const char* name )
+ : QObject( parent, name )
+{
+ d = new Private();
+
+ if( s_k3bCore )
+ qFatal("ONLY ONE INSTANCE OF K3BCORE ALLOWED!");
+ s_k3bCore = this;
+
+ // create the thread widget instance in the GUI thread
+ K3bThreadWidget::instance();
+}
+
+
+K3bCore::~K3bCore()
+{
+ s_k3bCore = 0;
+
+ delete d->globalSettings;
+ delete d;
+}
+
+
+K3bDevice::DeviceManager* K3bCore::deviceManager() const
+{
+ const_cast<K3bCore*>(this)->initDeviceManager();
+ return d->deviceManager;
+}
+
+
+K3bExternalBinManager* K3bCore::externalBinManager() const
+{
+ const_cast<K3bCore*>(this)->initExternalBinManager();
+ return d->externalBinManager;
+}
+
+
+K3bPluginManager* K3bCore::pluginManager() const
+{
+ const_cast<K3bCore*>(this)->initPluginManager();
+ return d->pluginManager;
+}
+
+
+K3bGlobalSettings* K3bCore::globalSettings() const
+{
+ const_cast<K3bCore*>(this)->initGlobalSettings();
+ return d->globalSettings;
+}
+
+
+const K3bVersion& K3bCore::version() const
+{
+ return d->version;
+}
+
+
+KConfig* K3bCore::config() const
+{
+ if( !d->config ) {
+ kdDebug() << "(K3bCore) opening k3b config file." << endl;
+ kdDebug() << "(K3bCore) while I am a " << className() << endl;
+ d->deleteConfig = true;
+ d->config = new KConfig( "k3brc" );
+ }
+
+ return d->config;
+}
+
+
+void K3bCore::init()
+{
+ initGlobalSettings();
+ initExternalBinManager();
+ initDeviceManager();
+ initPluginManager();
+
+ // load the plugins before doing anything else
+ // they might add external bins
+ pluginManager()->loadAll();
+
+ externalBinManager()->search();
+
+#ifdef HAVE_HAL
+ connect( K3bDevice::HalConnection::instance(), SIGNAL(deviceAdded(const QString&)),
+ deviceManager(), SLOT(addDevice(const QString&)) );
+ connect( K3bDevice::HalConnection::instance(), SIGNAL(deviceRemoved(const QString&)),
+ deviceManager(), SLOT(removeDevice(const QString&)) );
+ QStringList devList = K3bDevice::HalConnection::instance()->devices();
+ if( devList.isEmpty() )
+ deviceManager()->scanBus();
+ else
+ for( unsigned int i = 0; i < devList.count(); ++i )
+ deviceManager()->addDevice( devList[i] );
+#else
+ deviceManager()->scanBus();
+#endif
+}
+
+
+void K3bCore::initGlobalSettings()
+{
+ if( !d->globalSettings )
+ d->globalSettings = new K3bGlobalSettings();
+}
+
+
+void K3bCore::initExternalBinManager()
+{
+ if( !d->externalBinManager ) {
+ d->externalBinManager = new K3bExternalBinManager( this );
+ K3b::addDefaultPrograms( d->externalBinManager );
+ }
+}
+
+
+void K3bCore::initDeviceManager()
+{
+ if( !d->deviceManager )
+ d->deviceManager = new K3bDevice::DeviceManager( this );
+}
+
+
+void K3bCore::initPluginManager()
+{
+ if( !d->pluginManager )
+ d->pluginManager = new K3bPluginManager( this );
+}
+
+
+void K3bCore::readSettings( KConfig* cnf )
+{
+ KConfig* c = cnf;
+ if( !c )
+ c = config();
+
+ QString oldGrp = c->group();
+
+ globalSettings()->readSettings( c );
+ deviceManager()->readConfig( c );
+ externalBinManager()->readConfig( c );
+
+ c->setGroup( oldGrp );
+}
+
+
+void K3bCore::saveSettings( KConfig* cnf )
+{
+ KConfig* c = cnf;
+ if( !c )
+ c = config();
+
+ QString oldGrp = c->group();
+
+ c->setGroup( "General Options" );
+ c->writeEntry( "config version", version() );
+
+ deviceManager()->saveConfig( c );
+ externalBinManager()->saveConfig( c );
+ d->globalSettings->saveSettings( c );
+
+ c->setGroup( oldGrp );
+}
+
+
+void K3bCore::registerJob( K3bJob* job )
+{
+ d->runningJobs.append( job );
+ emit jobStarted( job );
+ if( K3bBurnJob* bj = dynamic_cast<K3bBurnJob*>( job ) )
+ emit burnJobStarted( bj );
+}
+
+
+void K3bCore::unregisterJob( K3bJob* job )
+{
+ d->runningJobs.remove( job );
+ emit jobFinished( job );
+ if( K3bBurnJob* bj = dynamic_cast<K3bBurnJob*>( job ) )
+ emit burnJobFinished( bj );
+}
+
+
+bool K3bCore::jobsRunning() const
+{
+ return !d->runningJobs.isEmpty();
+}
+
+
+const QValueList<K3bJob*>& K3bCore::runningJobs() const
+{
+ return d->runningJobs;
+}
+
+
+bool K3bCore::blockDevice( K3bDevice::Device* dev )
+{
+ if( QThread::currentThread() == s_guiThreadHandle ) {
+ return internalBlockDevice( dev );
+ }
+ else {
+ bool success = false;
+ DeviceBlockingEventDoneCondition w;
+ QApplication::postEvent( this, new DeviceBlockingEvent( true, dev, &w, &success ) );
+ w.wait();
+ return success;
+ }
+}
+
+
+void K3bCore::unblockDevice( K3bDevice::Device* dev )
+{
+ if( QThread::currentThread() == s_guiThreadHandle ) {
+ internalUnblockDevice( dev );
+ }
+ else {
+ DeviceBlockingEventDoneCondition w;
+ QApplication::postEvent( this, new DeviceBlockingEvent( false, dev, &w, 0 ) );
+ w.wait();
+ }
+}
+
+
+bool K3bCore::internalBlockDevice( K3bDevice::Device* dev )
+{
+ if( !d->blockedDevices.contains( dev ) ) {
+ d->blockedDevices.append( dev );
+ return true;
+ }
+ else
+ return false;
+}
+
+
+void K3bCore::internalUnblockDevice( K3bDevice::Device* dev )
+{
+ d->blockedDevices.remove( dev );
+}
+
+
+void K3bCore::customEvent( QCustomEvent* e )
+{
+ if( DeviceBlockingEvent* de = dynamic_cast<DeviceBlockingEvent*>(e) ) {
+ if( de->block )
+ *de->success = internalBlockDevice( de->device );
+ else
+ internalUnblockDevice( de->device );
+ de->cond->done();
+ }
+}
+
+#include "k3bcore.moc"
diff --git a/libk3b/core/k3bcore.h b/libk3b/core/k3bcore.h
new file mode 100644
index 0000000..ce73e32
--- /dev/null
+++ b/libk3b/core/k3bcore.h
@@ -0,0 +1,181 @@
+/*
+ *
+ * $Id: k3bcore.h 733470 2007-11-06 12:10:29Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_CORE_H_
+#define _K3B_CORE_H_
+
+#include <qobject.h>
+#include <qvaluelist.h>
+
+#include "k3b_export.h"
+
+
+#define LIBK3B_VERSION "1.0.5"
+
+#define k3bcore K3bCore::k3bCore()
+
+
+class K3bExternalBinManager;
+class K3bVersion;
+class KConfig;
+class KAboutData;
+class K3bJob;
+class K3bBurnJob;
+class K3bGlobalSettings;
+class K3bPluginManager;
+class QCustomEvent;
+
+
+namespace K3bDevice {
+ class DeviceManager;
+ class Device;
+}
+
+
+/**
+ * The K3b core takes care of the managers.
+ * This has been separated from K3bApplication to
+ * make creating a K3bPart easy.
+ * This is the heart of the K3b system. Every plugin may use this
+ * to get the information it needs.
+ */
+class LIBK3B_EXPORT K3bCore : public QObject
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Although K3bCore is a singlelton it's constructor is not private to make inheritance
+ * possible. Just make sure to only create one instance.
+ */
+ K3bCore( QObject* parent = 0, const char* name = 0 );
+ virtual ~K3bCore();
+
+ const QValueList<K3bJob*>& runningJobs() const;
+
+ /**
+ * Equals to !runningJobs().isEmpty()
+ */
+ bool jobsRunning() const;
+
+ /**
+ * The default implementation calls add four initXXX() methods,
+ * scans for devices, applications, and reads the global settings.
+ */
+ virtual void init();
+
+ /**
+ * @param c if 0 K3bCore uses the K3b configuration
+ */
+ virtual void readSettings( KConfig* c = 0 );
+
+ /**
+ * @param c if 0 K3bCore uses the K3b configuration
+ */
+ virtual void saveSettings( KConfig* c = 0 );
+
+ /**
+ * If this is reimplemented it is recommended to also reimplement
+ * init().
+ */
+ virtual K3bDevice::DeviceManager* deviceManager() const;
+
+ /**
+ * Returns the external bin manager from K3bCore.
+ *
+ * By default K3bCore only adds the default programs:
+ * cdrecord, cdrdao, growisofs, mkisofs, dvd+rw-format, readcd
+ *
+ * If you need other programs you have to add them manually like this:
+ * <pre>externalBinManager()->addProgram( new K3bNormalizeProgram() );</pre>
+ */
+ K3bExternalBinManager* externalBinManager() const;
+ K3bPluginManager* pluginManager() const;
+
+ /**
+ * Global settings used throughout libk3b. Change the settings directly in the
+ * K3bGlobalSettings object. They will be saved by K3bCore::saveSettings
+ */
+ K3bGlobalSettings* globalSettings() const;
+
+ /**
+ * returns the version of the library as defined by LIBK3B_VERSION
+ */
+ const K3bVersion& version() const;
+
+ /**
+ * Default implementation returns the K3b configuration from k3brc.
+ * Normally this should not be used.
+ */
+ virtual KConfig* config() const;
+
+ /**
+ * Used by the writing jobs to block a device.
+ * This makes sure no device is used twice within libk3b
+ *
+ * When using this method in a job be aware that reimplementations might
+ * open dialogs and resulting in a blocking call.
+ *
+ * This method calls internalBlockDevice() to do the actual work.
+ */
+ bool blockDevice( K3bDevice::Device* );
+ void unblockDevice( K3bDevice::Device* );
+
+ static K3bCore* k3bCore() { return s_k3bCore; }
+
+ signals:
+ /**
+ * Emitted once a new job has been started. This includes burn jobs.
+ */
+ void jobStarted( K3bJob* );
+ void burnJobStarted( K3bBurnJob* );
+ void jobFinished( K3bJob* );
+ void burnJobFinished( K3bBurnJob* );
+
+ public slots:
+ /**
+ * Every running job registers itself with the core.
+ * For now this is only used to determine if some job
+ * is running.
+ */
+ void registerJob( K3bJob* job );
+ void unregisterJob( K3bJob* job );
+
+ protected:
+ /**
+ * Reimplement this to add additonal checks.
+ *
+ * This method is thread safe. blockDevice makes sure
+ * it is only executed in the GUI thread.
+ */
+ virtual bool internalBlockDevice( K3bDevice::Device* );
+ virtual void internalUnblockDevice( K3bDevice::Device* );
+
+ virtual void initGlobalSettings();
+ virtual void initExternalBinManager();
+ virtual void initDeviceManager();
+ virtual void initPluginManager();
+
+ virtual void customEvent( QCustomEvent* e );
+
+ private:
+ class Private;
+ Private* d;
+
+ static K3bCore* s_k3bCore;
+};
+
+#endif
diff --git a/libk3b/core/k3bdataevent.h b/libk3b/core/k3bdataevent.h
new file mode 100644
index 0000000..b6a4334
--- /dev/null
+++ b/libk3b/core/k3bdataevent.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * $Id: k3bdataevent.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef K3B_DATA_EVENT_H
+#define K3B_DATA_EVENT_H
+
+#include <qevent.h>
+
+
+/**
+ * Custom event class for posting events corresponding to the
+ * K3bJob signals. This is useful for a threaded job since
+ * in that case it's not possible to emit signals that directly
+ * change the GUI (see QThread docu).
+ */
+class K3bDataEvent : public QCustomEvent
+{
+ public:
+ // make sure we get not in the way of K3bProgressInfoEvent
+ static const int EVENT_TYPE = QEvent::User + 100;
+
+ K3bDataEvent( const char* data, int len )
+ : QCustomEvent( EVENT_TYPE ),
+ m_data(data),
+ m_length(len)
+ {}
+
+ const char* data() const { return m_data; }
+ int length() const { return m_length; }
+
+ private:
+ const char* m_data;
+ int m_length;
+};
+
+#endif
diff --git a/libk3b/core/k3bdefaultexternalprograms.cpp b/libk3b/core/k3bdefaultexternalprograms.cpp
new file mode 100644
index 0000000..b654d22
--- /dev/null
+++ b/libk3b/core/k3bdefaultexternalprograms.cpp
@@ -0,0 +1,1030 @@
+/*
+ *
+ * $Id: k3bdefaultexternalprograms.cpp 731898 2007-11-02 08:22:18Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bdefaultexternalprograms.h"
+#include "k3bexternalbinmanager.h"
+#include <k3bglobals.h>
+
+#include <qfile.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qobject.h>
+#include <qregexp.h>
+#include <qtextstream.h>
+
+#include <k3bprocess.h>
+#include <kdebug.h>
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+
+
+void K3b::addDefaultPrograms( K3bExternalBinManager* m )
+{
+ m->addProgram( new K3bCdrecordProgram(false) );
+ m->addProgram( new K3bMkisofsProgram() );
+ m->addProgram( new K3bReadcdProgram() );
+ m->addProgram( new K3bCdrdaoProgram() );
+ m->addProgram( new K3bGrowisofsProgram() );
+ m->addProgram( new K3bDvdformatProgram() );
+ // m->addProgram( new K3bDvdBooktypeProgram() );
+}
+
+
+void K3b::addTranscodePrograms( K3bExternalBinManager* m )
+{
+ static const char* transcodeTools[] = { "transcode",
+ 0, // K3b 1.0 only uses the transcode binary
+ "tcprobe",
+ "tccat",
+ "tcscan",
+ "tcextract",
+ "tcdecode",
+ 0 };
+
+ for( int i = 0; transcodeTools[i]; ++i )
+ m->addProgram( new K3bTranscodeProgram( transcodeTools[i] ) );
+}
+
+
+void K3b::addVcdimagerPrograms( K3bExternalBinManager* m )
+{
+ // don't know if we need more vcdTools in the future (vcdxrip)
+ static const char* vcdTools[] = { "vcdxbuild",
+ "vcdxminfo",
+ "vcdxrip",
+ 0 };
+
+ for( int i = 0; vcdTools[i]; ++i )
+ m->addProgram( new K3bVcdbuilderProgram( vcdTools[i] ) );
+}
+
+
+K3bCdrecordProgram::K3bCdrecordProgram( bool dvdPro )
+ : K3bExternalProgram( dvdPro ? "cdrecord-prodvd" : "cdrecord" ),
+ m_dvdPro(dvdPro)
+{
+}
+
+
+//
+// This is a hack for Debian based systems which use
+// a wrapper cdrecord script to call cdrecord.mmap or cdrecord.shm
+// depending on the kernel version.
+// For 2.0.x and 2.2.x kernels the shm version is used. In all
+// other cases it's the mmap version.
+//
+// But since it may be that someone manually installed cdrecord
+// replacing the wrapper we check if cdrecord is a script.
+//
+static QString& debianWeirdnessHack( QString& path )
+{
+ if( QFile::exists( path + ".mmap" ) ) {
+ kdDebug() << "(K3bCdrecordProgram) checking for Debian cdrecord wrapper script." << endl;
+ if( QFileInfo( path ).size() < 1024 ) {
+ kdDebug() << "(K3bCdrecordProgram) Debian Wrapper script size fits. Checking file." << endl;
+ QFile f( path );
+ f.open( IO_ReadOnly );
+ QString s = QTextStream( &f ).read();
+ if( s.contains( "cdrecord.mmap" ) && s.contains( "cdrecord.shm" ) ) {
+ kdDebug() << "(K3bCdrecordProgram) Found Debian Wrapper script." << endl;
+ QString ext;
+ if( K3b::kernelVersion().versionString().left(3) > "2.2" )
+ ext = ".mmap";
+ else
+ ext = ".shm";
+
+ kdDebug() << "(K3bCdrecordProgram) Using cdrecord" << ext << endl;
+
+ path += ext;
+ }
+ }
+ }
+
+ return path;
+}
+
+
+bool K3bCdrecordProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ bool wodim = false;
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+
+ if( QFile::exists( path + "wodim" ) ) {
+ wodim = true;
+ path += "wodim";
+ }
+ else if( QFile::exists( path + "cdrecord" ) ) {
+ path += "cdrecord";
+ }
+ else
+ return false;
+ }
+
+ debianWeirdnessHack( path );
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ K3bProcessOutputCollector out( &vp );
+
+ vp << path << "-version";
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = -1;
+ if( wodim ) {
+ pos = out.output().find( "Wodim" );
+ }
+ else if( m_dvdPro ) {
+ pos = out.output().find( "Cdrecord-ProDVD" );
+ }
+ else {
+ pos = out.output().find( "Cdrecord" );
+ }
+
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( QRegExp("[0-9]"), pos );
+ if( pos < 0 )
+ return false;
+
+ int endPos = out.output().find( QRegExp("\\s"), pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+
+ if( wodim )
+ bin->addFeature( "wodim" );
+
+ pos = out.output().find( "Copyright") + 14;
+ endPos = out.output().find( "\n", pos );
+
+ // cdrecord does not use local encoding for the copyright statement but plain latin1
+ bin->copyright = QString::fromLatin1( out.output().mid( pos, endPos-pos ).local8Bit() ).stripWhiteSpace();
+ }
+ else {
+ kdDebug() << "(K3bCdrecordProgram) could not start " << path << endl;
+ return false;
+ }
+
+ if( !m_dvdPro && bin->version.suffix().endsWith( "-dvd" ) ) {
+ bin->addFeature( "dvd-patch" );
+ bin->version = QString(bin->version.versionString()).remove("-dvd");
+ }
+
+ // probe features
+ KProcess fp;
+ out.setProcess( &fp );
+ fp << path << "-help";
+ if( fp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ if( out.output().contains( "gracetime" ) )
+ bin->addFeature( "gracetime" );
+ if( out.output().contains( "-overburn" ) )
+ bin->addFeature( "overburn" );
+ if( out.output().contains( "-text" ) )
+ bin->addFeature( "cdtext" );
+ if( out.output().contains( "-clone" ) )
+ bin->addFeature( "clone" );
+ if( out.output().contains( "-tao" ) )
+ bin->addFeature( "tao" );
+ if( out.output().contains( "cuefile=" ) &&
+ ( wodim || bin->version > K3bVersion( 2, 1, -1, "a14") ) ) // cuefile handling was still buggy in a14
+ bin->addFeature( "cuefile" );
+
+ // new mode 2 options since cdrecord 2.01a12
+ // we use both checks here since the help was not updated in 2.01a12 yet (well, I
+ // just double-checked and the help page is proper but there is no harm in having
+ // two checks)
+ // and the version check does not handle versions like 2.01-dvd properly
+ if( out.output().contains( "-xamix" ) ||
+ bin->version >= K3bVersion( 2, 1, -1, "a12" ) ||
+ wodim )
+ bin->addFeature( "xamix" );
+
+ // check if we run cdrecord as root
+ struct stat s;
+ if( !::stat( QFile::encodeName(path), &s ) ) {
+ if( (s.st_mode & S_ISUID) && s.st_uid == 0 )
+ bin->addFeature( "suidroot" );
+ }
+ }
+ else {
+ kdDebug() << "(K3bCdrecordProgram) could not start " << bin->path << endl;
+ delete bin;
+ return false;
+ }
+
+ if( bin->version < K3bVersion( 2, 0 ) && !wodim )
+ bin->addFeature( "outdated" );
+
+ // FIXME: are these version correct?
+ if( bin->version >= K3bVersion("1.11a38") || wodim )
+ bin->addFeature( "plain-atapi" );
+ if( bin->version > K3bVersion("1.11a17") || wodim )
+ bin->addFeature( "hacked-atapi" );
+
+ if( bin->version >= K3bVersion( 2, 1, 1, "a02" ) || wodim )
+ bin->addFeature( "short-track-raw" );
+
+ if( bin->version >= K3bVersion( 2, 1, -1, "a13" ) || wodim )
+ bin->addFeature( "audio-stdin" );
+
+ if( bin->version >= K3bVersion( "1.11a02" ) || wodim )
+ bin->addFeature( "burnfree" );
+ else
+ bin->addFeature( "burnproof" );
+
+ addBin( bin );
+ return true;
+}
+
+
+
+K3bMkisofsProgram::K3bMkisofsProgram()
+ : K3bExternalProgram( "mkisofs" )
+{
+}
+
+bool K3bMkisofsProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ bool genisoimage = false;
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+
+ if( QFile::exists( path + "genisoimage" ) ) {
+ genisoimage = true;
+ path += "genisoimage";
+ }
+ else if( QFile::exists( path + "mkisofs" ) ) {
+ path += "mkisofs";
+ }
+ else
+ return false;
+ }
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ vp << path << "-version";
+ K3bProcessOutputCollector out( &vp );
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = -1;
+ if( genisoimage )
+ pos = out.output().find( "genisoimage" );
+ else
+ pos = out.output().find( "mkisofs" );
+
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( QRegExp("[0-9]"), pos );
+ if( pos < 0 )
+ return false;
+
+ int endPos = out.output().find( ' ', pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+
+ if( genisoimage )
+ bin->addFeature( "genisoimage" );
+ }
+ else {
+ kdDebug() << "(K3bMkisofsProgram) could not start " << path << endl;
+ return false;
+ }
+
+
+
+ // probe features
+ KProcess fp;
+ fp << path << "-help";
+ out.setProcess( &fp );
+ if( fp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ if( out.output().contains( "-udf" ) )
+ bin->addFeature( "udf" );
+ if( out.output().contains( "-dvd-video" ) )
+ bin->addFeature( "dvd-video" );
+ if( out.output().contains( "-joliet-long" ) )
+ bin->addFeature( "joliet-long" );
+ if( out.output().contains( "-xa" ) )
+ bin->addFeature( "xa" );
+ if( out.output().contains( "-sectype" ) )
+ bin->addFeature( "sectype" );
+
+ // check if we run mkisofs as root
+ struct stat s;
+ if( !::stat( QFile::encodeName(path), &s ) ) {
+ if( (s.st_mode & S_ISUID) && s.st_uid == 0 )
+ bin->addFeature( "suidroot" );
+ }
+ }
+ else {
+ kdDebug() << "(K3bMkisofsProgram) could not start " << bin->path << endl;
+ delete bin;
+ return false;
+ }
+
+ if( bin->version < K3bVersion( 1, 14) && !genisoimage )
+ bin->addFeature( "outdated" );
+
+ if( bin->version >= K3bVersion( 1, 15, -1, "a40" ) || genisoimage )
+ bin->addFeature( "backslashed_filenames" );
+
+ if ( genisoimage && bin->version >= K3bVersion( 1, 1, 4 ) )
+ bin->addFeature( "no-4gb-limit" );
+
+ if ( !genisoimage && bin->version >= K3bVersion( 2, 1, 1, "a32" ) )
+ bin->addFeature( "no-4gb-limit" );
+
+ addBin(bin);
+ return true;
+}
+
+
+K3bReadcdProgram::K3bReadcdProgram()
+ : K3bExternalProgram( "readcd" )
+{
+}
+
+bool K3bReadcdProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ bool readom = false;
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+
+ if( QFile::exists( path + "readom" ) ) {
+ readom = true;
+ path += "readom";
+ }
+ else if( QFile::exists( path + "readcd" ) ) {
+ path += "readcd";
+ }
+ else
+ return false;
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ vp << path << "-version";
+ K3bProcessOutputCollector out( &vp );
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = -1;
+ if( readom )
+ pos = out.output().find( "readom" );
+ else
+ pos = out.output().find( "readcd" );
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( QRegExp("[0-9]"), pos );
+ if( pos < 0 )
+ return false;
+
+ int endPos = out.output().find( ' ', pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+
+ if( readom )
+ bin->addFeature( "readom" );
+ }
+ else {
+ kdDebug() << "(K3bMkisofsProgram) could not start " << path << endl;
+ return false;
+ }
+
+
+
+ // probe features
+ KProcess fp;
+ fp << path << "-help";
+ out.setProcess( &fp );
+ if( fp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ if( out.output().contains( "-clone" ) )
+ bin->addFeature( "clone" );
+
+ // check if we run mkisofs as root
+ struct stat s;
+ if( !::stat( QFile::encodeName(path), &s ) ) {
+ if( (s.st_mode & S_ISUID) && s.st_uid == 0 )
+ bin->addFeature( "suidroot" );
+ }
+ }
+ else {
+ kdDebug() << "(K3bReadcdProgram) could not start " << bin->path << endl;
+ delete bin;
+ return false;
+ }
+
+
+ // FIXME: are these version correct?
+ if( bin->version >= K3bVersion("1.11a38") || readom )
+ bin->addFeature( "plain-atapi" );
+ if( bin->version > K3bVersion("1.11a17") || readom )
+ bin->addFeature( "hacked-atapi" );
+
+ addBin(bin);
+ return true;
+}
+
+
+K3bCdrdaoProgram::K3bCdrdaoProgram()
+ : K3bExternalProgram( "cdrdao" )
+{
+}
+
+bool K3bCdrdaoProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+ path.append("cdrdao");
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ vp << path ;
+ K3bProcessOutputCollector out( &vp );
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = out.output().find( "Cdrdao version" );
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( QRegExp("[0-9]"), pos );
+ if( pos < 0 )
+ return false;
+
+ int endPos = out.output().find( ' ', pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+
+ pos = out.output().find( "(C)", endPos+1 ) + 4;
+ endPos = out.output().find( '\n', pos );
+ bin->copyright = out.output().mid( pos, endPos-pos );
+ }
+ else {
+ kdDebug() << "(K3bCdrdaoProgram) could not start " << path << endl;
+ return false;
+ }
+
+
+
+ // probe features
+ KProcess fp;
+ fp << path << "write" << "-h";
+ out.setProcess( &fp );
+ if( fp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ if( out.output().contains( "--overburn" ) )
+ bin->addFeature( "overburn" );
+ if( out.output().contains( "--multi" ) )
+ bin->addFeature( "multisession" );
+
+ if( out.output().contains( "--buffer-under-run-protection" ) )
+ bin->addFeature( "disable-burnproof" );
+
+ // check if we run cdrdao as root
+ struct stat s;
+ if( !::stat( QFile::encodeName(path), &s ) ) {
+ if( (s.st_mode & S_ISUID) && s.st_uid == 0 )
+ bin->addFeature( "suidroot" );
+ }
+ }
+ else {
+ kdDebug() << "(K3bCdrdaoProgram) could not start " << bin->path << endl;
+ delete bin;
+ return false;
+ }
+
+
+ // SuSE 9.0 ships with a patched cdrdao 1.1.7 which contains an updated libschily
+ // Gentoo ships with a patched cdrdao 1.1.7 which contains scglib support
+ if( bin->version > K3bVersion( 1, 1, 7 ) ||
+ bin->version == K3bVersion( 1, 1, 7, "-gentoo" ) ||
+ bin->version == K3bVersion( 1, 1, 7, "-suse" ) ) {
+ // bin->addFeature( "plain-atapi" );
+ bin->addFeature( "hacked-atapi" );
+ }
+
+ if( bin->version >= K3bVersion( 1, 1, 8 ) )
+ bin->addFeature( "plain-atapi" );
+
+ addBin(bin);
+ return true;
+}
+
+
+K3bTranscodeProgram::K3bTranscodeProgram( const QString& transcodeProgram )
+ : K3bExternalProgram( transcodeProgram ),
+ m_transcodeProgram( transcodeProgram )
+{
+}
+
+bool K3bTranscodeProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+
+ QString appPath = path + m_transcodeProgram;
+
+ if( !QFile::exists( appPath ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ vp << appPath << "-v";
+ K3bProcessOutputCollector out( &vp );
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = out.output().find( "transcode v" );
+ if( pos < 0 )
+ return false;
+
+ pos += 11;
+
+ int endPos = out.output().find( QRegExp("[\\s\\)]"), pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = appPath;
+ bin->version = out.output().mid( pos, endPos-pos );
+ }
+ else {
+ kdDebug() << "(K3bTranscodeProgram) could not start " << appPath << endl;
+ return false;
+ }
+
+ //
+ // Check features
+ //
+ QString modInfoBin = path + "tcmodinfo";
+ KProcess modp;
+ modp << modInfoBin << "-p";
+ out.setProcess( &modp );
+ if( modp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ QString modPath = out.output().stripWhiteSpace();
+ QDir modDir( modPath );
+ if( !modDir.entryList( "*export_xvid*", QDir::Files ).isEmpty() )
+ bin->addFeature( "xvid" );
+ if( !modDir.entryList( "*export_lame*", QDir::Files ).isEmpty() )
+ bin->addFeature( "lame" );
+ if( !modDir.entryList( "*export_ffmpeg*", QDir::Files ).isEmpty() )
+ bin->addFeature( "ffmpeg" );
+ if( !modDir.entryList( "*export_ac3*", QDir::Files ).isEmpty() )
+ bin->addFeature( "ac3" );
+ }
+
+ addBin(bin);
+ return true;
+}
+
+
+
+K3bVcdbuilderProgram::K3bVcdbuilderProgram( const QString& p )
+ : K3bExternalProgram( p ),
+ m_vcdbuilderProgram( p )
+{
+}
+
+bool K3bVcdbuilderProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+ path.append(m_vcdbuilderProgram);
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ vp << path << "-V";
+ K3bProcessOutputCollector out( &vp );
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = out.output().find( "GNU VCDImager" );
+ if( pos < 0 )
+ return false;
+
+ pos += 14;
+
+ int endPos = out.output().find( QRegExp("[\\n\\)]"), pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos ).stripWhiteSpace();
+
+ pos = out.output().find( "Copyright" ) + 14;
+ endPos = out.output().find( "\n", pos );
+ bin->copyright = out.output().mid( pos, endPos-pos ).stripWhiteSpace();
+ }
+ else {
+ kdDebug() << "(K3bVcdbuilderProgram) could not start " << path << endl;
+ return false;
+ }
+
+ addBin(bin);
+ return true;
+}
+
+
+K3bNormalizeProgram::K3bNormalizeProgram()
+ : K3bExternalProgram( "normalize-audio" )
+{
+}
+
+
+bool K3bNormalizeProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+ path.append("normalize-audio");
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ K3bProcessOutputCollector out( &vp );
+
+ vp << path << "--version";
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = out.output().find( "normalize" );
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( QRegExp("\\d"), pos );
+ if( pos < 0 )
+ return false;
+
+ int endPos = out.output().find( QRegExp("\\s"), pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+
+ pos = out.output().find( "Copyright" )+14;
+ endPos = out.output().find( "\n", pos );
+ bin->copyright = out.output().mid( pos, endPos-pos ).stripWhiteSpace();
+ }
+ else {
+ kdDebug() << "(K3bCdrecordProgram) could not start " << path << endl;
+ return false;
+ }
+
+ addBin( bin );
+ return true;
+}
+
+
+K3bGrowisofsProgram::K3bGrowisofsProgram()
+ : K3bExternalProgram( "growisofs" )
+{
+}
+
+bool K3bGrowisofsProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+ path.append("growisofs");
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ K3bProcessOutputCollector out( &vp );
+
+ vp << path << "-version";
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = out.output().find( "growisofs" );
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( QRegExp("\\d"), pos );
+ if( pos < 0 )
+ return false;
+
+ int endPos = out.output().find( ",", pos+1 );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+ }
+ else {
+ kdDebug() << "(K3bGrowisofsProgram) could not start " << path << endl;
+ return false;
+ }
+
+ // fixed Copyright:
+ bin->copyright = "Andy Polyakov <appro@fy.chalmers.se>";
+
+ // check if we run growisofs as root
+ struct stat s;
+ if( !::stat( QFile::encodeName(path), &s ) ) {
+ if( (s.st_mode & S_ISUID) && s.st_uid == 0 )
+ bin->addFeature( "suidroot" );
+ }
+
+ addBin( bin );
+ return true;
+}
+
+
+K3bDvdformatProgram::K3bDvdformatProgram()
+ : K3bExternalProgram( "dvd+rw-format" )
+{
+}
+
+bool K3bDvdformatProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+ path.append("dvd+rw-format");
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ K3bProcessOutputCollector out( &vp );
+
+ vp << path;
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ // different locales make searching for the +- char difficult
+ // so we simply ignore it.
+ int pos = out.output().find( QRegExp("DVD.*RAM format utility") );
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( "version", pos );
+ if( pos < 0 )
+ return false;
+
+ pos += 8;
+
+ // the version ends in a dot.
+ int endPos = out.output().find( QRegExp("\\.\\D"), pos );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+ }
+ else {
+ kdDebug() << "(K3bDvdformatProgram) could not start " << path << endl;
+ return false;
+ }
+
+ // fixed Copyright:
+ bin->copyright = "Andy Polyakov <appro@fy.chalmers.se>";
+
+ // check if we run dvd+rw-format as root
+ struct stat s;
+ if( !::stat( QFile::encodeName(path), &s ) ) {
+ if( (s.st_mode & S_ISUID) && s.st_uid == 0 )
+ bin->addFeature( "suidroot" );
+ }
+
+ addBin( bin );
+ return true;
+}
+
+
+K3bDvdBooktypeProgram::K3bDvdBooktypeProgram()
+ : K3bExternalProgram( "dvd+rw-booktype" )
+{
+}
+
+bool K3bDvdBooktypeProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+ path.append("dvd+rw-booktype");
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ K3bProcessOutputCollector out( &vp );
+
+ vp << path;
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = out.output().find( "dvd+rw-booktype" );
+ if( pos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ // No version information. Create dummy version
+ bin->version = K3bVersion( 1, 0, 0 );
+ }
+ else {
+ kdDebug() << "(K3bDvdBooktypeProgram) could not start " << path << endl;
+ return false;
+ }
+
+ addBin( bin );
+ return true;
+}
+
+
+
+K3bCdda2wavProgram::K3bCdda2wavProgram()
+ : K3bExternalProgram( "cdda2wav" )
+{
+}
+
+bool K3bCdda2wavProgram::scan( const QString& p )
+{
+ if( p.isEmpty() )
+ return false;
+
+ QString path = p;
+ QFileInfo fi( path );
+ if( fi.isDir() ) {
+ if( path[path.length()-1] != '/' )
+ path.append("/");
+ path.append("cdda2wav");
+ }
+
+ if( !QFile::exists( path ) )
+ return false;
+
+ K3bExternalBin* bin = 0;
+
+ // probe version
+ KProcess vp;
+ K3bProcessOutputCollector out( &vp );
+
+ vp << path << "-h";
+ if( vp.start( KProcess::Block, KProcess::AllOutput ) ) {
+ int pos = out.output().find( "cdda2wav" );
+ if( pos < 0 )
+ return false;
+
+ pos = out.output().find( "Version", pos );
+ if( pos < 0 )
+ return false;
+
+ pos += 8;
+
+ // the version does not end in a space but the kernel info
+ int endPos = out.output().find( QRegExp("[^\\d\\.]"), pos );
+ if( endPos < 0 )
+ return false;
+
+ bin = new K3bExternalBin( this );
+ bin->path = path;
+ bin->version = out.output().mid( pos, endPos-pos );
+
+ // features (we do this since the cdda2wav help says that the short
+ // options will disappear soon)
+ if( out.output().find( "-info-only" ) )
+ bin->addFeature( "info-only" ); // otherwise use the -J option
+ if( out.output().find( "-no-infofile" ) )
+ bin->addFeature( "no-infofile" ); // otherwise use the -H option
+ if( out.output().find( "-gui" ) )
+ bin->addFeature( "gui" ); // otherwise use the -g option
+ if( out.output().find( "-bulk" ) )
+ bin->addFeature( "bulk" ); // otherwise use the -B option
+ if( out.output().find( "dev=" ) )
+ bin->addFeature( "dev" ); // otherwise use the -B option
+ }
+ else {
+ kdDebug() << "(K3bCdda2wavProgram) could not start " << path << endl;
+ return false;
+ }
+
+ // check if we run as root
+ struct stat s;
+ if( !::stat( QFile::encodeName(path), &s ) ) {
+ if( (s.st_mode & S_ISUID) && s.st_uid == 0 )
+ bin->addFeature( "suidroot" );
+ }
+
+ addBin( bin );
+ return true;
+}
+
diff --git a/libk3b/core/k3bdefaultexternalprograms.h b/libk3b/core/k3bdefaultexternalprograms.h
new file mode 100644
index 0000000..3212727
--- /dev/null
+++ b/libk3b/core/k3bdefaultexternalprograms.h
@@ -0,0 +1,143 @@
+/*
+ *
+ * $Id: k3bdefaultexternalprograms.h 623768 2007-01-15 13:33:55Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+
+#ifndef _K3B_DEFAULT_EXTERNAL_BIN_PROGRAMS_H_
+#define _K3B_DEFAULT_EXTERNAL_BIN_PROGRAMS_H_
+
+#include "k3bexternalbinmanager.h"
+#include "k3b_export.h"
+class K3bExternalBinManager;
+
+namespace K3b
+{
+ LIBK3B_EXPORT void addDefaultPrograms( K3bExternalBinManager* );
+ LIBK3B_EXPORT void addTranscodePrograms( K3bExternalBinManager* );
+ LIBK3B_EXPORT void addVcdimagerPrograms( K3bExternalBinManager* );
+}
+
+
+class LIBK3B_EXPORT K3bCdrecordProgram : public K3bExternalProgram
+{
+ public:
+ K3bCdrecordProgram( bool dvdPro );
+
+ bool scan( const QString& );
+
+ private:
+ bool m_dvdPro;
+};
+
+
+class LIBK3B_EXPORT K3bMkisofsProgram : public K3bExternalProgram
+{
+ public:
+ K3bMkisofsProgram();
+
+ bool scan( const QString& );
+};
+
+
+class LIBK3B_EXPORT K3bReadcdProgram : public K3bExternalProgram
+{
+ public:
+ K3bReadcdProgram();
+
+ bool scan( const QString& );
+};
+
+
+class LIBK3B_EXPORT K3bCdrdaoProgram : public K3bExternalProgram
+{
+ public:
+ K3bCdrdaoProgram();
+
+ bool scan( const QString& );
+};
+
+
+class LIBK3B_EXPORT K3bTranscodeProgram : public K3bExternalProgram
+{
+ public:
+ K3bTranscodeProgram( const QString& transcodeProgram );
+
+ bool scan( const QString& );
+
+ // no user parameters (yet)
+ bool supportsUserParameters() const { return false; }
+
+ private:
+ QString m_transcodeProgram;
+};
+
+
+class LIBK3B_EXPORT K3bVcdbuilderProgram : public K3bExternalProgram
+{
+ public:
+ K3bVcdbuilderProgram( const QString& );
+
+ bool scan( const QString& );
+
+ private:
+ QString m_vcdbuilderProgram;
+};
+
+
+class LIBK3B_EXPORT K3bNormalizeProgram : public K3bExternalProgram
+{
+ public:
+ K3bNormalizeProgram();
+
+ bool scan( const QString& );
+};
+
+
+class LIBK3B_EXPORT K3bGrowisofsProgram : public K3bExternalProgram
+{
+ public:
+ K3bGrowisofsProgram();
+
+ bool scan( const QString& );
+};
+
+
+class LIBK3B_EXPORT K3bDvdformatProgram : public K3bExternalProgram
+{
+ public:
+ K3bDvdformatProgram();
+
+ bool scan( const QString& );
+};
+
+
+class LIBK3B_EXPORT K3bDvdBooktypeProgram : public K3bExternalProgram
+{
+ public:
+ K3bDvdBooktypeProgram();
+
+ bool scan( const QString& );
+};
+
+
+class LIBK3B_EXPORT K3bCdda2wavProgram : public K3bExternalProgram
+{
+ public:
+ K3bCdda2wavProgram();
+
+ bool scan( const QString& );
+};
+
+#endif
diff --git a/libk3b/core/k3bexternalbinmanager.cpp b/libk3b/core/k3bexternalbinmanager.cpp
new file mode 100644
index 0000000..2b21a85
--- /dev/null
+++ b/libk3b/core/k3bexternalbinmanager.cpp
@@ -0,0 +1,389 @@
+/*
+ *
+ * $Id: k3bexternalbinmanager.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bexternalbinmanager.h"
+
+#include <kdebug.h>
+#include <kprocess.h>
+#include <kconfig.h>
+#include <kdeversion.h>
+
+#include <qstring.h>
+#include <qregexp.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qptrlist.h>
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+
+
+QString K3bExternalBinManager::m_noPath = "";
+
+
+// ///////////////////////////////////////////////////////////
+//
+// K3BEXTERNALBIN
+//
+// ///////////////////////////////////////////////////////////
+
+K3bExternalBin::K3bExternalBin( K3bExternalProgram* p )
+ : m_program(p)
+{
+}
+
+
+bool K3bExternalBin::isEmpty() const
+{
+ return !version.isValid();
+}
+
+
+const QString& K3bExternalBin::name() const
+{
+ return m_program->name();
+}
+
+
+bool K3bExternalBin::hasFeature( const QString& f ) const
+{
+ return m_features.contains( f );
+}
+
+
+void K3bExternalBin::addFeature( const QString& f )
+{
+ m_features.append( f );
+}
+
+
+const QStringList& K3bExternalBin::userParameters() const
+{
+ return m_program->userParameters();
+}
+
+
+
+// ///////////////////////////////////////////////////////////
+//
+// K3BEXTERNALPROGRAM
+//
+// ///////////////////////////////////////////////////////////
+
+
+K3bExternalProgram::K3bExternalProgram( const QString& name )
+ : m_name( name )
+{
+ m_bins.setAutoDelete( true );
+}
+
+
+K3bExternalProgram::~K3bExternalProgram()
+{
+}
+
+
+const K3bExternalBin* K3bExternalProgram::mostRecentBin() const
+{
+ QPtrListIterator<K3bExternalBin> it( m_bins );
+ K3bExternalBin* bin = *it;
+ ++it;
+ while( *it ) {
+ if( it.current()->version > bin->version )
+ bin = *it;
+ ++it;
+ }
+ return bin;
+}
+
+
+void K3bExternalProgram::addBin( K3bExternalBin* bin )
+{
+ if( !m_bins.contains( bin ) ) {
+ // insertion sort
+ // the first bin in the list is always the one used
+ // so we default to using the newest one
+ K3bExternalBin* oldBin = m_bins.first();
+ while( oldBin && oldBin->version > bin->version )
+ oldBin = m_bins.next();
+
+ m_bins.insert( oldBin ? m_bins.at() : m_bins.count(), bin );
+ }
+}
+
+void K3bExternalProgram::setDefault( const K3bExternalBin* bin )
+{
+ if( m_bins.contains( bin ) )
+ m_bins.take( m_bins.find( bin ) );
+
+ // the first bin in the list is always the one used
+ m_bins.insert( 0, bin );
+}
+
+
+void K3bExternalProgram::setDefault( const QString& path )
+{
+ for( QPtrListIterator<K3bExternalBin> it( m_bins ); it.current(); ++it ) {
+ if( it.current()->path == path ) {
+ setDefault( it.current() );
+ return;
+ }
+ }
+}
+
+
+void K3bExternalProgram::addUserParameter( const QString& p )
+{
+ if( !m_userParameters.contains( p ) )
+ m_userParameters.append(p);
+}
+
+
+
+// ///////////////////////////////////////////////////////////
+//
+// K3BEXTERNALBINMANAGER
+//
+// ///////////////////////////////////////////////////////////
+
+
+K3bExternalBinManager::K3bExternalBinManager( QObject* parent, const char* name )
+ : QObject( parent, name )
+{
+}
+
+
+K3bExternalBinManager::~K3bExternalBinManager()
+{
+ clear();
+}
+
+
+bool K3bExternalBinManager::readConfig( KConfig* c )
+{
+ loadDefaultSearchPath();
+
+ c->setGroup( "External Programs" );
+
+ if( c->hasKey( "search path" ) )
+ setSearchPath( c->readPathListEntry( "search path" ) );
+
+ search();
+
+ for ( QMap<QString, K3bExternalProgram*>::iterator it = m_programs.begin(); it != m_programs.end(); ++it ) {
+ K3bExternalProgram* p = it.data();
+ if( c->hasKey( p->name() + " default" ) ) {
+ p->setDefault( c->readEntry( p->name() + " default" ) );
+ }
+ if( c->hasKey( p->name() + " user parameters" ) ) {
+ QStringList list = c->readListEntry( p->name() + " user parameters" );
+ for( QStringList::iterator strIt = list.begin(); strIt != list.end(); ++strIt )
+ p->addUserParameter( *strIt );
+ }
+ if( c->hasKey( p->name() + " last seen newest version" ) ) {
+ K3bVersion lastMax( c->readEntry( p->name() + " last seen newest version" ) );
+ // now search for a newer version and use it (because it was installed after the last
+ // K3b run and most users would probably expect K3b to use a newly installed version)
+ const K3bExternalBin* newestBin = p->mostRecentBin();
+ if( newestBin && newestBin->version > lastMax )
+ p->setDefault( newestBin );
+ }
+ }
+
+ return true;
+}
+
+bool K3bExternalBinManager::saveConfig( KConfig* c )
+{
+ c->setGroup( "External Programs" );
+ c->writePathEntry( "search path", m_searchPath );
+
+ for ( QMap<QString, K3bExternalProgram*>::iterator it = m_programs.begin(); it != m_programs.end(); ++it ) {
+ K3bExternalProgram* p = it.data();
+ if( p->defaultBin() )
+ c->writeEntry( p->name() + " default", p->defaultBin()->path );
+
+ c->writeEntry( p->name() + " user parameters", p->userParameters() );
+
+ const K3bExternalBin* newestBin = p->mostRecentBin();
+ if( newestBin )
+ c->writeEntry( p->name() + " last seen newest version", newestBin->version );
+ }
+
+ return true;
+}
+
+
+bool K3bExternalBinManager::foundBin( const QString& name )
+{
+ if( m_programs.find( name ) == m_programs.end() )
+ return false;
+ else
+ return (m_programs[name]->defaultBin() != 0);
+}
+
+
+const QString& K3bExternalBinManager::binPath( const QString& name )
+{
+ if( m_programs.find( name ) == m_programs.end() )
+ return m_noPath;
+
+ if( m_programs[name]->defaultBin() != 0 )
+ return m_programs[name]->defaultBin()->path;
+ else
+ return m_noPath;
+}
+
+
+const K3bExternalBin* K3bExternalBinManager::binObject( const QString& name )
+{
+ if( m_programs.find( name ) == m_programs.end() )
+ return 0;
+
+ return m_programs[name]->defaultBin();
+}
+
+
+void K3bExternalBinManager::addProgram( K3bExternalProgram* p )
+{
+ m_programs.insert( p->name(), p );
+}
+
+
+void K3bExternalBinManager::clear()
+{
+ for( QMap<QString, K3bExternalProgram*>::Iterator it = m_programs.begin(); it != m_programs.end(); ++it )
+ delete it.data();
+ m_programs.clear();
+}
+
+
+void K3bExternalBinManager::search()
+{
+ if( m_searchPath.isEmpty() )
+ loadDefaultSearchPath();
+
+ for( QMap<QString, K3bExternalProgram*>::iterator it = m_programs.begin(); it != m_programs.end(); ++it ) {
+ it.data()->clear();
+ }
+
+ // do not search one path twice
+ QStringList paths;
+ for( QStringList::const_iterator it = m_searchPath.begin(); it != m_searchPath.end(); ++it ) {
+ QString p = *it;
+ if( p[p.length()-1] == '/' )
+ p.truncate( p.length()-1 );
+ if( !paths.contains( p ) && !paths.contains( p + "/" ) )
+ paths.append(p);
+ }
+
+ // get the environment path variable
+ char* env_path = ::getenv("PATH");
+ if( env_path ) {
+ QStringList env_pathList = QStringList::split(":", QString::fromLocal8Bit(env_path));
+ for( QStringList::const_iterator it = env_pathList.begin(); it != env_pathList.end(); ++it ) {
+ QString p = *it;
+ if( p[p.length()-1] == '/' )
+ p.truncate( p.length()-1 );
+ if( !paths.contains( p ) && !paths.contains( p + "/" ) )
+ paths.append(p);
+ }
+ }
+
+
+ for( QStringList::const_iterator it = paths.begin(); it != paths.end(); ++it )
+ for( QMap<QString, K3bExternalProgram*>::iterator pit = m_programs.begin(); pit != m_programs.end(); ++pit )
+ pit.data()->scan(*it);
+
+ // TESTING
+ // /////////////////////////
+ const K3bExternalBin* bin = program("cdrecord")->defaultBin();
+
+ if( !bin ) {
+ kdDebug() << "(K3bExternalBinManager) Probing cdrecord failed" << endl;
+ }
+ else {
+ kdDebug() << "(K3bExternalBinManager) Cdrecord " << bin->version << " features: "
+ << bin->features().join( ", " ) << endl;
+
+ if( bin->version >= K3bVersion("1.11a02") )
+ kdDebug() << "(K3bExternalBinManager) "
+ << bin->version.majorVersion() << " " << bin->version.minorVersion() << " " << bin->version.patchLevel()
+ << " " << bin->version.suffix()
+ << " seems to be cdrecord version >= 1.11a02, using burnfree instead of burnproof" << endl;
+ if( bin->version >= K3bVersion("1.11a31") )
+ kdDebug() << "(K3bExternalBinManager) seems to be cdrecord version >= 1.11a31, support for Just Link via burnfree "
+ << "driveroption" << endl;
+ }
+}
+
+
+K3bExternalProgram* K3bExternalBinManager::program( const QString& name ) const
+{
+ if( m_programs.find( name ) == m_programs.end() )
+ return 0;
+ else
+ return m_programs[name];
+}
+
+
+void K3bExternalBinManager::loadDefaultSearchPath()
+{
+ static const char* defaultSearchPaths[] = { "/usr/bin/",
+ "/usr/local/bin/",
+ "/usr/sbin/",
+ "/usr/local/sbin/",
+ "/opt/schily/bin/",
+ "/sbin",
+ 0 };
+
+ m_searchPath.clear();
+ for( int i = 0; defaultSearchPaths[i]; ++i ) {
+ m_searchPath.append( defaultSearchPaths[i] );
+ }
+}
+
+
+void K3bExternalBinManager::setSearchPath( const QStringList& list )
+{
+ loadDefaultSearchPath();
+
+ for( QStringList::const_iterator it = list.begin(); it != list.end(); ++it ) {
+ if( !m_searchPath.contains( *it ) )
+ m_searchPath.append( *it );
+ }
+}
+
+
+void K3bExternalBinManager::addSearchPath( const QString& path )
+{
+ if( !m_searchPath.contains( path ) )
+ m_searchPath.append( path );
+}
+
+
+
+const K3bExternalBin* K3bExternalBinManager::mostRecentBinObject( const QString& name )
+{
+ if( K3bExternalProgram* p = program( name ) )
+ return p->mostRecentBin();
+ else
+ return 0;
+}
+
+#include "k3bexternalbinmanager.moc"
+
diff --git a/libk3b/core/k3bexternalbinmanager.h b/libk3b/core/k3bexternalbinmanager.h
new file mode 100644
index 0000000..e7fe601
--- /dev/null
+++ b/libk3b/core/k3bexternalbinmanager.h
@@ -0,0 +1,162 @@
+/*
+ *
+ * $Id: k3bexternalbinmanager.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef K3B_EXTERNAL_BIN_MANAGER_H
+#define K3B_EXTERNAL_BIN_MANAGER_H
+
+#include <qmap.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qptrlist.h>
+#include "k3b_export.h"
+#include "k3bversion.h"
+
+class KConfig;
+class KProcess;
+
+
+class K3bExternalProgram;
+
+
+/**
+ * A K3bExternalBin represents an installed version of a program.
+ * All K3bExternalBin objects are managed by K3bExternalPrograms.
+ *
+ * A bin may have certain features that are represented by a string.
+ */
+class LIBK3B_EXPORT K3bExternalBin
+{
+ public:
+ K3bExternalBin( K3bExternalProgram* );
+ virtual ~K3bExternalBin() {}
+
+ K3bVersion version;
+ QString path;
+ QString copyright;
+
+ const QString& name() const;
+ bool isEmpty() const;
+ const QStringList& userParameters() const;
+ const QStringList& features() const { return m_features; }
+
+ bool hasFeature( const QString& ) const;
+ void addFeature( const QString& );
+
+ K3bExternalProgram* program() const { return m_program; }
+
+ private:
+ QStringList m_features;
+ K3bExternalProgram* m_program;
+};
+
+
+/**
+ * This is the main class that represents a program
+ * It's scan method has to be reimplemented for every program
+ * It manages a list of K3bExternalBin-objects that each represent
+ * one installed version of the program.
+ */
+class LIBK3B_EXPORT K3bExternalProgram
+{
+ public:
+ K3bExternalProgram( const QString& name );
+ virtual ~K3bExternalProgram();
+
+ const K3bExternalBin* defaultBin() const { return m_bins.getFirst(); }
+ const K3bExternalBin* mostRecentBin() const;
+
+ void addUserParameter( const QString& );
+ void setUserParameters( const QStringList& list ) { m_userParameters = list; }
+
+ const QStringList& userParameters() const { return m_userParameters; }
+ const QString& name() const { return m_name; }
+
+ void addBin( K3bExternalBin* );
+ void clear() { m_bins.clear(); }
+ void setDefault( const K3bExternalBin* );
+ void setDefault( const QString& path );
+
+ const QPtrList<K3bExternalBin>& bins() const { return m_bins; }
+
+ /**
+ * this scans for the program in the given path,
+ * adds the found bin object to the list and returnes true.
+ * if nothing could be found false is returned.
+ */
+ virtual bool scan( const QString& ) {return false;}//= 0;
+
+ /**
+ * reimplement this if it does not make sense to have the user be able
+ * to specify additional parameters
+ */
+ virtual bool supportsUserParameters() const { return true; }
+
+ private:
+ QString m_name;
+ QStringList m_userParameters;
+ QPtrList<K3bExternalBin> m_bins;
+};
+
+
+class LIBK3B_EXPORT K3bExternalBinManager : public QObject
+{
+ Q_OBJECT
+
+ public:
+ K3bExternalBinManager( QObject* parent = 0, const char* name = 0 );
+ ~K3bExternalBinManager();
+
+ void search();
+
+ /**
+ * read config and add changes to current map.
+ * Takes care of setting the config group
+ */
+ bool readConfig( KConfig* );
+
+ /**
+ * Takes care of setting the config group
+ */
+ bool saveConfig( KConfig* );
+
+ bool foundBin( const QString& name );
+ const QString& binPath( const QString& name );
+ const K3bExternalBin* binObject( const QString& name );
+ const K3bExternalBin* mostRecentBinObject( const QString& name );
+
+ K3bExternalProgram* program( const QString& ) const;
+ const QMap<QString, K3bExternalProgram*>& programs() const { return m_programs; }
+
+ /** always extends the default searchpath */
+ void setSearchPath( const QStringList& );
+ void addSearchPath( const QString& );
+ void loadDefaultSearchPath();
+
+ const QStringList& searchPath() const { return m_searchPath; }
+
+ void addProgram( K3bExternalProgram* );
+ void clear();
+
+ private:
+ QMap<QString, K3bExternalProgram*> m_programs;
+ QStringList m_searchPath;
+
+ static QString m_noPath; // used for binPath() to return const string
+
+ QString m_gatheredOutput;
+};
+
+#endif
diff --git a/libk3b/core/k3bglobals.cpp b/libk3b/core/k3bglobals.cpp
new file mode 100644
index 0000000..fc5a4e1
--- /dev/null
+++ b/libk3b/core/k3bglobals.cpp
@@ -0,0 +1,634 @@
+/*
+ *
+ * $Id: k3bglobals.cpp 659634 2007-04-30 14:51:32Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include <config.h>
+
+
+#include "k3bglobals.h"
+#include <k3bversion.h>
+#include <k3bdevice.h>
+#include <k3bdevicemanager.h>
+#include <k3bdeviceglobals.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bcore.h>
+#include <k3bhalconnection.h>
+
+#include <kdeversion.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kio/job.h>
+#include <kio/netaccess.h>
+#include <kurl.h>
+#include <dcopref.h>
+#include <kprocess.h>
+
+#include <qdatastream.h>
+#include <qdir.h>
+#include <qfile.h>
+
+#include <cmath>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+# include <sys/param.h>
+# include <sys/mount.h>
+# include <sys/endian.h>
+# define bswap_16(x) bswap16(x)
+# define bswap_32(x) bswap32(x)
+# define bswap_64(x) bswap64(x)
+#else
+# include <byteswap.h>
+#endif
+#ifdef HAVE_SYS_STATVFS_H
+# include <sys/statvfs.h>
+#endif
+#ifdef HAVE_SYS_VFS_H
+# include <sys/vfs.h>
+#endif
+
+
+/*
+struct Sample {
+ unsigned char msbLeft;
+ unsigned char lsbLeft;
+ unsigned char msbRight;
+ unsigned char lsbRight;
+
+ short left() const {
+ return ( msbLeft << 8 ) | lsbLeft;
+ }
+ short right() const {
+ return ( msbRight << 8 ) | lsbRight;
+ }
+ void left( short d ) {
+ msbLeft = d >> 8;
+ lsbLeft = d;
+ }
+ void right( short d ) {
+ msbRight = d >> 8;
+ lsbRight = d;
+ }
+};
+*/
+
+QString K3b::framesToString( int h, bool showFrames )
+{
+ int m = h / 4500;
+ int s = (h % 4500) / 75;
+ int f = h % 75;
+
+ QString str;
+
+ if( showFrames ) {
+ // cdrdao needs the MSF format where 1 second has 75 frames!
+ str.sprintf( "%.2i:%.2i:%.2i", m, s, f );
+ }
+ else
+ str.sprintf( "%.2i:%.2i", m, s );
+
+ return str;
+}
+
+/*QString K3b::sizeToTime(long size)
+{
+ int h = size / sizeof(Sample) / 588;
+ return framesToString(h, false);
+}*/
+
+
+Q_INT16 K3b::swapByteOrder( const Q_INT16& i )
+{
+ return bswap_16( i );
+ //((i << 8) & 0xff00) | ((i >> 8 ) & 0xff);
+}
+
+
+Q_INT32 K3b::swapByteOrder( const Q_INT32& i )
+{
+ //return ((i << 24) & 0xff000000) | ((i << 8) & 0xff0000) | ((i >> 8) & 0xff00) | ((i >> 24) & 0xff );
+ return bswap_32( i );
+}
+
+
+Q_INT64 K3b::swapByteOrder( const Q_INT64& i )
+{
+ return bswap_64( i );
+}
+
+
+int K3b::round( double d )
+{
+ return (int)( floor(d) + 0.5 <= d ? ceil(d) : floor(d) );
+}
+
+
+QString K3b::findUniqueFilePrefix( const QString& _prefix, const QString& path )
+{
+ QString url;
+ if( path.isEmpty() || !QFile::exists(path) )
+ url = defaultTempPath();
+ else
+ url = prepareDir( path );
+
+ QString prefix = _prefix;
+ if( prefix.isEmpty() )
+ prefix = "k3b_";
+
+ // now create the unique prefix
+ QDir dir( url );
+ QStringList entries = dir.entryList( QDir::DefaultFilter, QDir::Name );
+ int i = 0;
+ for( QStringList::iterator it = entries.begin();
+ it != entries.end(); ++it ) {
+ if( (*it).startsWith( prefix + QString::number(i) ) ) {
+ i++;
+ it = entries.begin();
+ }
+ }
+
+ return url + prefix + QString::number(i);
+}
+
+
+QString K3b::findTempFile( const QString& ending, const QString& d )
+{
+ return findUniqueFilePrefix( "k3b_", d ) + ( ending.isEmpty() ? QString::null : (QString::fromLatin1(".") + ending) );
+}
+
+
+QString K3b::defaultTempPath()
+{
+ QString oldGroup = kapp->config()->group();
+ kapp->config()->setGroup( "General Options" );
+ QString url = kapp->config()->readPathEntry( "Temp Dir", KGlobal::dirs()->resourceDirs( "tmp" ).first() );
+ kapp->config()->setGroup( oldGroup );
+ return prepareDir(url);
+}
+
+
+QString K3b::prepareDir( const QString& dir )
+{
+ return (dir + (dir[dir.length()-1] != '/' ? "/" : ""));
+}
+
+
+QString K3b::parentDir( const QString& path )
+{
+ QString parent = path;
+ if( path[path.length()-1] == '/' )
+ parent.truncate( parent.length()-1 );
+
+ int pos = parent.findRev( '/' );
+ if( pos >= 0 )
+ parent.truncate( pos+1 );
+ else // relative path, do anything...
+ parent = "/";
+
+ return parent;
+}
+
+
+QString K3b::fixupPath( const QString& path )
+{
+ QString s;
+ bool lastWasSlash = false;
+ for( unsigned int i = 0; i < path.length(); ++i ) {
+ if( path[i] == '/' ) {
+ if( !lastWasSlash ) {
+ lastWasSlash = true;
+ s.append( "/" );
+ }
+ }
+ else {
+ lastWasSlash = false;
+ s.append( path[i] );
+ }
+ }
+
+ return s;
+}
+
+
+K3bVersion K3b::kernelVersion()
+{
+ // initialize kernel version
+ K3bVersion v;
+ utsname unameinfo;
+ if( ::uname(&unameinfo) == 0 ) {
+ v = QString::fromLocal8Bit( unameinfo.release );
+ kdDebug() << "kernel version: " << v << endl;
+ }
+ else
+ kdError() << "could not determine kernel version." << endl;
+ return v;
+}
+
+
+K3bVersion K3b::simpleKernelVersion()
+{
+ return kernelVersion().simplify();
+}
+
+
+QString K3b::systemName()
+{
+ QString v;
+ utsname unameinfo;
+ if( ::uname(&unameinfo) == 0 ) {
+ v = QString::fromLocal8Bit( unameinfo.sysname );
+ }
+ else
+ kdError() << "could not determine system name." << endl;
+ return v;
+}
+
+
+bool K3b::kbFreeOnFs( const QString& path, unsigned long& size, unsigned long& avail )
+{
+ struct statvfs fs;
+ if( ::statvfs( QFile::encodeName(path), &fs ) == 0 ) {
+ unsigned long kBfak = fs.f_frsize/1024;
+
+ size = fs.f_blocks*kBfak;
+ avail = fs.f_bavail*kBfak;
+
+ return true;
+ }
+ else
+ return false;
+}
+
+
+KIO::filesize_t K3b::filesize( const KURL& url )
+{
+ if( url.isLocalFile() ) {
+ k3b_struct_stat buf;
+ if ( !k3b_stat( QFile::encodeName( url.path() ), &buf ) ) {
+ return (KIO::filesize_t)buf.st_size;
+ }
+ }
+
+ KIO::UDSEntry uds;
+ KIO::NetAccess::stat( url, uds, 0 );
+ for( KIO::UDSEntry::const_iterator it = uds.begin(); it != uds.end(); ++it ) {
+ if( (*it).m_uds == KIO::UDS_SIZE ) {
+ return (*it).m_long;
+ }
+ }
+
+ return ( KIO::filesize_t )0;
+}
+
+
+KIO::filesize_t K3b::imageFilesize( const KURL& url )
+{
+ KIO::filesize_t size = K3b::filesize( url );
+ int cnt = 0;
+ while( KIO::NetAccess::exists( KURL::fromPathOrURL( url.url() + '.' + QString::number(cnt).rightJustify( 3, '0' ) ), true ) )
+ size += K3b::filesize( KURL::fromPathOrURL( url.url() + '.' + QString::number(cnt++).rightJustify( 3, '0' ) ) );
+ return size;
+}
+
+
+QString K3b::cutFilename( const QString& name, unsigned int len )
+{
+ if( name.length() > len ) {
+ QString ret = name;
+
+ // determine extension (we think of an extension to be at most 5 chars in length)
+ int pos = name.find( '.', -6 );
+ if( pos > 0 )
+ len -= (name.length() - pos);
+
+ ret.truncate( len );
+
+ if( pos > 0 )
+ ret.append( name.mid( pos ) );
+
+ return ret;
+ }
+ else
+ return name;
+}
+
+
+QString K3b::removeFilenameExtension( const QString& name )
+{
+ QString v = name;
+ int dotpos = v.findRev( '.' );
+ if( dotpos > 0 )
+ v.truncate( dotpos );
+ return v;
+}
+
+
+QString K3b::appendNumberToFilename( const QString& name, int num, unsigned int maxlen )
+{
+ // determine extension (we think of an extension to be at most 5 chars in length)
+ QString result = name;
+ QString ext;
+ int pos = name.find( '.', -6 );
+ if( pos > 0 ) {
+ ext = name.mid(pos);
+ result.truncate( pos );
+ }
+
+ ext.prepend( QString::number(num) );
+ result.truncate( maxlen - ext.length() );
+
+ return result + ext;
+}
+
+
+bool K3b::plainAtapiSupport()
+{
+ // FIXME: what about BSD?
+ return ( K3b::simpleKernelVersion() >= K3bVersion( 2, 5, 40 ) );
+}
+
+
+bool K3b::hackedAtapiSupport()
+{
+ // IMPROVEME!!!
+ // FIXME: since when does the kernel support this?
+ return ( K3b::simpleKernelVersion() >= K3bVersion( 2, 4, 0 ) );
+}
+
+
+QString K3b::externalBinDeviceParameter( K3bDevice::Device* dev, const K3bExternalBin* bin )
+{
+#ifdef Q_OS_LINUX
+ //
+ // experimental: always use block devices on 2.6 kernels
+ //
+ if( simpleKernelVersion() >= K3bVersion( 2, 6, 0 ) )
+ return dev->blockDeviceName();
+ else
+#endif
+ if( dev->interfaceType() == K3bDevice::SCSI )
+ return dev->busTargetLun();
+ else if( (plainAtapiSupport() && bin->hasFeature("plain-atapi") ) )
+ return dev->blockDeviceName();
+ else
+ return QString("ATAPI:%1").arg(dev->blockDeviceName());
+}
+
+
+int K3b::writingAppFromString( const QString& s )
+{
+ if( s.lower() == "cdrdao" )
+ return K3b::CDRDAO;
+ else if( s.lower() == "cdrecord" )
+ return K3b::CDRECORD;
+ else if( s.lower() == "dvdrecord" )
+ return K3b::DVDRECORD;
+ else if( s.lower() == "growisofs" )
+ return K3b::GROWISOFS;
+ else if( s.lower() == "dvd+rw-format" )
+ return K3b::DVD_RW_FORMAT;
+ else
+ return K3b::DEFAULT;
+}
+
+
+QString K3b::writingModeString( int mode )
+{
+ if( mode == WRITING_MODE_AUTO )
+ return i18n("Auto");
+ else
+ return K3bDevice::writingModeString( mode );
+}
+
+
+QString K3b::resolveLink( const QString& file )
+{
+ QFileInfo f( file );
+ QStringList steps( f.absFilePath() );
+ while( f.isSymLink() ) {
+ QString p = f.readLink();
+ if( !p.startsWith( "/" ) )
+ p.prepend( f.dirPath(true) + "/" );
+ f.setFile( p );
+ if( steps.contains( f.absFilePath() ) ) {
+ kdDebug() << "(K3b) symlink loop detected." << endl;
+ break;
+ }
+ else
+ steps.append( f.absFilePath() );
+ }
+ return f.absFilePath();
+}
+
+
+K3bDevice::Device* K3b::urlToDevice( const KURL& deviceUrl )
+{
+ if( deviceUrl.protocol() == "media" || deviceUrl.protocol() == "system" ) {
+ kdDebug() << "(K3b) Asking mediamanager for " << deviceUrl.fileName() << endl;
+ DCOPRef mediamanager("kded", "mediamanager");
+ DCOPReply reply = mediamanager.call("properties(QString)", deviceUrl.fileName());
+ QStringList properties = reply;
+ if( !reply.isValid() || properties.count() < 6 ) {
+ kdError() << "(K3b) Invalid reply from mediamanager" << endl;
+ return 0;
+ }
+ else {
+ kdDebug() << "(K3b) Reply from mediamanager " << properties[5] << endl;
+ return k3bcore->deviceManager()->findDevice( properties[5] );
+ }
+ }
+
+ return k3bcore->deviceManager()->findDevice( deviceUrl.path() );
+}
+
+
+KURL K3b::convertToLocalUrl( const KURL& url )
+{
+ if( !url.isLocalFile() ) {
+#if KDE_IS_VERSION(3,4,91)
+ return KIO::NetAccess::mostLocalURL( url, 0 );
+#else
+#ifndef UDS_LOCAL_PATH
+#define UDS_LOCAL_PATH (72 | KIO::UDS_STRING)
+#else
+ using namespace KIO;
+#endif
+ KIO::UDSEntry e;
+ if( KIO::NetAccess::stat( url, e, 0 ) ) {
+ const KIO::UDSEntry::ConstIterator end = e.end();
+ for( KIO::UDSEntry::ConstIterator it = e.begin(); it != end; ++it ) {
+ if( (*it).m_uds == UDS_LOCAL_PATH && !(*it).m_str.isEmpty() )
+ return KURL::fromPathOrURL( (*it).m_str );
+ }
+ }
+#endif
+ }
+
+ return url;
+}
+
+
+KURL::List K3b::convertToLocalUrls( const KURL::List& urls )
+{
+ KURL::List r;
+ for( KURL::List::const_iterator it = urls.constBegin(); it != urls.constEnd(); ++it )
+ r.append( convertToLocalUrl( *it ) );
+ return r;
+}
+
+
+Q_INT16 K3b::fromLe16( char* data )
+{
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ return swapByteOrder( *((Q_INT16*)data) );
+#else
+ return *((Q_INT16*)data);
+#endif
+}
+
+
+Q_INT32 K3b::fromLe32( char* data )
+{
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ return swapByteOrder( *((Q_INT32*)data) );
+#else
+ return *((Q_INT32*)data);
+#endif
+}
+
+
+Q_INT64 K3b::fromLe64( char* data )
+{
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ return swapByteOrder( *((Q_INT64*)data) );
+#else
+ return *((Q_INT64*)data);
+#endif
+}
+
+
+QString K3b::findExe( const QString& name )
+{
+ // first we search the path
+ QString bin = KStandardDirs::findExe( name );
+
+ // then go on with our own little list
+ if( bin.isEmpty() )
+ bin = KStandardDirs::findExe( name, k3bcore->externalBinManager()->searchPath().join(":") );
+
+ return bin;
+}
+
+
+bool K3b::isMounted( K3bDevice::Device* dev )
+{
+ if( !dev )
+ return false;
+
+ return !KIO::findDeviceMountPoint( dev->blockDeviceName() ).isEmpty();
+}
+
+
+bool K3b::unmount( K3bDevice::Device* dev )
+{
+ if( !dev )
+ return false;
+
+ QString mntDev = dev->blockDeviceName();
+
+#if KDE_IS_VERSION(3,4,0)
+ // first try to unmount it the standard way
+ if( KIO::NetAccess::synchronousRun( KIO::unmount( mntDev, false ), 0 ) )
+ return true;
+#endif
+
+ QString umountBin = K3b::findExe( "umount" );
+ if( !umountBin.isEmpty() ) {
+ KProcess p;
+ p << umountBin;
+ p << "-l"; // lazy unmount
+ p << dev->blockDeviceName();
+ p.start( KProcess::Block );
+ if( !p.exitStatus() )
+ return true;
+ }
+
+ // now try pmount
+ QString pumountBin = K3b::findExe( "pumount" );
+ if( !pumountBin.isEmpty() ) {
+ KProcess p;
+ p << pumountBin;
+ p << "-l"; // lazy unmount
+ p << dev->blockDeviceName();
+ p.start( KProcess::Block );
+ return !p.exitStatus();
+ }
+ else {
+#ifdef HAVE_HAL
+ return !K3bDevice::HalConnection::instance()->unmount( dev );
+#else
+ return false;
+#endif
+ }
+}
+
+
+bool K3b::mount( K3bDevice::Device* dev )
+{
+ if( !dev )
+ return false;
+
+ QString mntDev = dev->blockDeviceName();
+
+#if KDE_IS_VERSION(3,4,0)
+ // first try to mount it the standard way
+ if( KIO::NetAccess::synchronousRun( KIO::mount( true, 0, mntDev, false ), 0 ) )
+ return true;
+#endif
+
+#ifdef HAVE_HAL
+ if( !K3bDevice::HalConnection::instance()->mount( dev ) )
+ return true;
+#endif
+
+ // now try pmount
+ QString pmountBin = K3b::findExe( "pmount" );
+ if( !pmountBin.isEmpty() ) {
+ KProcess p;
+ p << pmountBin;
+ p << mntDev;
+ p.start( KProcess::Block );
+ return !p.exitStatus();
+ }
+ return false;
+}
+
+
+bool K3b::eject( K3bDevice::Device* dev )
+{
+#ifdef HAVE_HAL
+ if( !K3bDevice::HalConnection::instance()->eject( dev ) )
+ return true;
+#endif
+
+ if( K3b::isMounted( dev ) )
+ K3b::unmount( dev );
+
+ return dev->eject();
+}
diff --git a/libk3b/core/k3bglobals.h b/libk3b/core/k3bglobals.h
new file mode 100644
index 0000000..2795630
--- /dev/null
+++ b/libk3b/core/k3bglobals.h
@@ -0,0 +1,257 @@
+/*
+ *
+ * $Id: k3bglobals.h 630384 2007-02-05 09:33:17Z mlaurent $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BGLOBALS_H
+#define K3BGLOBALS_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <qstring.h>
+#include <qfile.h>
+#include <kio/global.h>
+#include <kurl.h>
+#include <k3bdevicetypes.h>
+#include "k3b_export.h"
+
+class KConfig;
+class K3bVersion;
+class K3bExternalBin;
+
+
+#include <sys/stat.h>
+
+
+#ifdef HAVE_STAT64
+#define k3b_struct_stat struct stat64
+#define k3b_stat ::stat64
+#define k3b_lstat ::lstat64
+#else
+#define k3b_struct_stat struct stat
+#define k3b_stat ::stat
+#define k3b_lstat ::lstat
+#endif
+
+
+namespace K3bDevice {
+ class Device;
+}
+
+namespace K3b
+{
+ enum WritingApp {
+ DEFAULT = 1,
+ CDRECORD = 2,
+ CDRDAO = 4,
+ DVDRECORD = 8,
+ GROWISOFS = 16,
+ DVD_RW_FORMAT = 32
+ };
+
+ LIBK3B_EXPORT int writingAppFromString( const QString& );
+
+ /**
+ * DATA_MODE_AUTO - let K3b determine the best mode
+ * MODE1 - refers to the default Yellow book mode1
+ * MODE2 - refers to CDROM XA mode2 form1
+ */
+ enum DataMode {
+ DATA_MODE_AUTO,
+ MODE1,
+ MODE2
+ };
+
+ /**
+ * The sector size denotes the number of bytes K3b provides per sector.
+ * This is based on the sizes cdrecord's -data, -xa, and -xamix parameters
+ * demand.
+ */
+ enum SectorSize {
+ SECTORSIZE_AUDIO = 2352,
+ SECTORSIZE_DATA_2048 = 2048,
+ SECTORSIZE_DATA_2048_SUBHEADER = 2056,
+ SECTORSIZE_DATA_2324 = 2324,
+ SECTORSIZE_DATA_2324_SUBHEADER = 2332,
+ SECTORSIZE_RAW = 2448
+ };
+
+ /**
+ * AUTO - let K3b determine the best mode
+ * TAO - Track at once
+ * DAO - Disk at once (or session at once)
+ * RAW - Raw mode
+ *
+ * may be or'ed together (except for WRITING_MODE_AUTO of course)
+ */
+ enum WritingMode {
+ WRITING_MODE_AUTO = 0,
+ TAO = K3bDevice::WRITINGMODE_TAO,
+ DAO = K3bDevice::WRITINGMODE_SAO,
+ RAW = K3bDevice::WRITINGMODE_RAW,
+ WRITING_MODE_INCR_SEQ = K3bDevice::WRITINGMODE_INCR_SEQ, // Incremental Sequential
+ WRITING_MODE_RES_OVWR = K3bDevice::WRITINGMODE_RES_OVWR // Restricted Overwrite
+ };
+
+ LIBK3B_EXPORT QString writingModeString( int );
+
+ LIBK3B_EXPORT QString framesToString( int h, bool showFrames = true );
+ /*LIBK3B_EXPORT QString sizeToTime( long size );*/
+
+ LIBK3B_EXPORT Q_INT16 swapByteOrder( const Q_INT16& i );
+ LIBK3B_EXPORT Q_INT32 swapByteOrder( const Q_INT32& i );
+ LIBK3B_EXPORT Q_INT64 swapByteOrder( const Q_INT64& i );
+
+ int round( double );
+
+ /**
+ * This checks the free space on the filesystem path is in.
+ * We use this since we encountered problems with the KDE version.
+ * @returns true on success.
+ */
+ LIBK3B_EXPORT bool kbFreeOnFs( const QString& path, unsigned long& size, unsigned long& avail );
+
+ /**
+ * Cut a filename preserving the extension
+ */
+ LIBK3B_EXPORT QString cutFilename( const QString& name, unsigned int len );
+
+ LIBK3B_EXPORT QString removeFilenameExtension( const QString& name );
+
+ /**
+ * Append a number to a filename preserving the extension.
+ * The resulting name's length will not exceed @p maxlen
+ */
+ LIBK3B_EXPORT QString appendNumberToFilename( const QString& name, int num, unsigned int maxlen );
+
+ LIBK3B_EXPORT QString findUniqueFilePrefix( const QString& _prefix = QString::null, const QString& path = QString::null );
+
+ /**
+ * Find a unique filename in directory d (if d is empty the method uses the defaultTempPath)
+ */
+ LIBK3B_EXPORT QString findTempFile( const QString& ending = QString::null, const QString& d = QString::null );
+
+ /**
+ * Wrapper around KStandardDirs::findExe which searches the PATH and some additional
+ * directories to find system tools which are normally only in root's PATH.
+ */
+ LIBK3B_EXPORT QString findExe( const QString& name );
+
+ /**
+ * get the default K3b temp path to store image files
+ */
+ LIBK3B_EXPORT QString defaultTempPath();
+
+ /**
+ * makes sure a path ends with a "/"
+ */
+ LIBK3B_EXPORT QString prepareDir( const QString& dir );
+
+ /**
+ * returns the parent dir of a path.
+ * CAUTION: this does only work well with absolut paths.
+ *
+ * Example: /usr/share/doc -> /usr/share/
+ */
+ QString parentDir( const QString& path );
+
+ /**
+ * For now this just replaces multiple occurrences of / with a single /
+ */
+ LIBK3B_EXPORT QString fixupPath( const QString& );
+
+ /**
+ * resolves a symlinks completely. Meaning it also handles links to links to links...
+ */
+ LIBK3B_EXPORT QString resolveLink( const QString& );
+
+ LIBK3B_EXPORT K3bVersion kernelVersion();
+
+ /**
+ * Kernel version stripped of all suffixes
+ */
+ LIBK3B_EXPORT K3bVersion simpleKernelVersion();
+
+ QString systemName();
+
+ LIBK3B_EXPORT KIO::filesize_t filesize( const KURL& );
+
+ /**
+ * Calculate the total size of an image file. This also includes
+ * images splitted by a K3bFileSplitter.
+ *
+ * \returns the total size of the image file at url
+ */
+ LIBK3B_EXPORT KIO::filesize_t imageFilesize( const KURL& url );
+
+ /**
+ * true if the kernel supports ATAPI devices without SCSI emulation.
+ * use in combination with the K3bExternalProgram feature "plain-atapi"
+ */
+ LIBK3B_EXPORT bool plainAtapiSupport();
+
+ /**
+ * true if the kernel supports ATAPI devices without SCSI emulation
+ * via the ATAPI: pseudo stuff
+ * use in combination with the K3bExternalProgram feature "hacked-atapi"
+ */
+ LIBK3B_EXPORT bool hackedAtapiSupport();
+
+ /**
+ * Used to create a parameter for cdrecord, cdrdao or readcd.
+ * Takes care of SCSI and ATAPI.
+ */
+ QString externalBinDeviceParameter( K3bDevice::Device* dev, const K3bExternalBin* );
+
+ /**
+ * Convert an url pointing to a local device to a K3bDevice.
+ * Supports media:// and system::// urls.
+ */
+ LIBK3B_EXPORT K3bDevice::Device* urlToDevice( const KURL& deviceUrl );
+
+ /**
+ * Tries to convert urls from local protocols != "file" to file (for now supports media:/)
+ */
+ LIBK3B_EXPORT KURL convertToLocalUrl( const KURL& url );
+ LIBK3B_EXPORT KURL::List convertToLocalUrls( const KURL::List& l );
+
+ LIBK3B_EXPORT Q_INT16 fromLe16( char* );
+ LIBK3B_EXPORT Q_INT32 fromLe32( char* );
+ LIBK3B_EXPORT Q_INT64 fromLe64( char* );
+
+ LIBK3B_EXPORT bool isMounted( K3bDevice::Device* );
+
+ /**
+ * Tries to unmount the device ignoring its actual mounting state.
+ * This method uses both KIO::unmount and pumount if available.
+ */
+ LIBK3B_EXPORT bool unmount( K3bDevice::Device* );
+
+ /**
+ * Tries to mount the medium. Since K3b does not gather any information
+ * about mount points the only methods used are pmount and HAL::mount
+ */
+ LIBK3B_EXPORT bool mount( K3bDevice::Device* );
+
+ /**
+ * Ejects the medium in the device or simply opens the tray.
+ * This method improves over K3bDevice::Device::eject in that it
+ * unmounts before ejecting and introduces HAL support.
+ */
+ LIBK3B_EXPORT bool eject( K3bDevice::Device* );
+}
+
+#endif
diff --git a/libk3b/core/k3bglobalsettings.cpp b/libk3b/core/k3bglobalsettings.cpp
new file mode 100644
index 0000000..6f58592
--- /dev/null
+++ b/libk3b/core/k3bglobalsettings.cpp
@@ -0,0 +1,61 @@
+/*
+ *
+ * $Id: k3bglobalsettings.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bglobalsettings.h"
+
+#include <kconfig.h>
+
+
+K3bGlobalSettings::K3bGlobalSettings()
+ : m_eject(true),
+ m_burnfree(true),
+ m_overburn(false),
+ m_useManualBufferSize(false),
+ m_bufferSize(4),
+ m_force(false)
+{
+}
+
+
+void K3bGlobalSettings::readSettings( KConfig* c )
+{
+ QString lastG = c->group();
+ c->setGroup( "General Options" );
+
+ m_eject = !c->readBoolEntry( "No cd eject", false );
+ m_burnfree = c->readBoolEntry( "burnfree", true );
+ m_overburn = c->readBoolEntry( "Allow overburning", false );
+ m_useManualBufferSize = c->readBoolEntry( "Manual buffer size", false );
+ m_bufferSize = c->readNumEntry( "Fifo buffer", 4 );
+ m_force = c->readBoolEntry( "Force unsafe operations", false );
+
+ c->setGroup( lastG );
+}
+
+
+void K3bGlobalSettings::saveSettings( KConfig* c )
+{
+ QString lastG = c->group();
+ c->setGroup( "General Options" );
+
+ c->writeEntry( "No cd eject", !m_eject );
+ c->writeEntry( "burnfree", m_burnfree );
+ c->writeEntry( "Allow overburning", m_overburn );
+ c->writeEntry( "Manual buffer size", m_useManualBufferSize );
+ c->writeEntry( "Fifo buffer", m_bufferSize );
+ c->writeEntry( "Force unsafe operations", m_force );
+
+ c->setGroup( lastG );
+}
diff --git a/libk3b/core/k3bglobalsettings.h b/libk3b/core/k3bglobalsettings.h
new file mode 100644
index 0000000..1194789
--- /dev/null
+++ b/libk3b/core/k3bglobalsettings.h
@@ -0,0 +1,70 @@
+/*
+ *
+ * $Id: k3bglobalsettings.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_GLOBAL_SETTINGS_H_
+#define _K3B_GLOBAL_SETTINGS_H_
+#include "k3b_export.h"
+class KConfig;
+
+/**
+ * Some global settings used throughout K3b.
+ */
+class LIBK3B_EXPORT K3bGlobalSettings
+{
+ public:
+ K3bGlobalSettings();
+
+ /**
+ * This method takes care of settings the config group
+ */
+ void readSettings( KConfig* );
+
+ /**
+ * This method takes care of settings the config group
+ */
+ void saveSettings( KConfig* );
+
+ bool ejectMedia() const { return m_eject; }
+ bool burnfree() const { return m_burnfree; }
+ bool overburn() const { return m_overburn; }
+ bool useManualBufferSize() const { return m_useManualBufferSize; }
+ int bufferSize() const { return m_bufferSize; }
+
+ /**
+ * If force is set to true K3b will continue in certain "unsafe" situations.
+ * The most common being a medium not suitable for the writer in terms of
+ * writing speed.
+ * Compare cdrecord's parameter -force
+ */
+ bool force() const { return m_force; }
+
+ void setEjectMedia( bool b ) { m_eject = b; }
+ void setBurnfree( bool b ) { m_burnfree = b; }
+ void setOverburn( bool b ) { m_overburn = b; }
+ void setUseManualBufferSize( bool b ) { m_useManualBufferSize = b; }
+ void setBufferSize( int size ) { m_bufferSize = size; }
+ void setForce( bool b ) { m_force = b; }
+
+ private:
+ bool m_eject;
+ bool m_burnfree;
+ bool m_overburn;
+ bool m_useManualBufferSize;
+ int m_bufferSize;
+ bool m_force;
+};
+
+
+#endif
diff --git a/libk3b/core/k3bjob.cpp b/libk3b/core/k3bjob.cpp
new file mode 100644
index 0000000..b545107
--- /dev/null
+++ b/libk3b/core/k3bjob.cpp
@@ -0,0 +1,253 @@
+/*
+ *
+ * $Id: k3bjob.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bjob.h"
+#include <k3bglobals.h>
+#include <k3bcore.h>
+
+#include <klocale.h>
+#include <kprocess.h>
+
+#include <qstringlist.h>
+#include <kdebug.h>
+
+
+class K3bJob::Private
+{
+public:
+};
+
+
+const char* K3bJob::DEFAULT_SIGNAL_CONNECTION = "K3bJobDefault";
+
+
+K3bJob::K3bJob( K3bJobHandler* handler, QObject* parent, const char* name )
+ : QObject( parent, name ),
+ m_jobHandler( handler ),
+ m_canceled(false),
+ m_active(false)
+{
+ connect( this, SIGNAL(canceled()),
+ this, SLOT(slotCanceled()) );
+}
+
+K3bJob::~K3bJob()
+{
+ //
+ // Normally a job (or the user of a job should take care of this
+ // but we do this here for security reasons.
+ //
+ if( m_active )
+ jobFinished( false );
+}
+
+
+void K3bJob::setJobHandler( K3bJobHandler* jh )
+{
+ m_jobHandler = jh;
+}
+
+
+void K3bJob::jobStarted()
+{
+ m_canceled = false;
+ m_active = true;
+
+ if( jobHandler() && jobHandler()->isJob() )
+ static_cast<K3bJob*>(jobHandler())->registerSubJob( this );
+ else
+ k3bcore->registerJob( this );
+
+ emit started();
+}
+
+
+void K3bJob::jobFinished( bool success )
+{
+ m_active = false;
+
+ if( jobHandler() && jobHandler()->isJob() )
+ static_cast<K3bJob*>(jobHandler())->unregisterSubJob( this );
+ else
+ k3bcore->unregisterJob( this );
+
+ emit finished( success );
+}
+
+
+void K3bJob::slotCanceled()
+{
+ m_canceled = true;
+}
+
+
+int K3bJob::waitForMedia( K3bDevice::Device* device,
+ int mediaState,
+ int mediaType,
+ const QString& message )
+{
+ // TODO: What about: emit newSubTask( i18n("Waiting for media") );
+ return m_jobHandler->waitForMedia( device, mediaState, mediaType, message );
+}
+
+
+bool K3bJob::questionYesNo( const QString& text,
+ const QString& caption,
+ const QString& yesText,
+ const QString& noText )
+{
+ return m_jobHandler->questionYesNo( text, caption, yesText, noText );
+}
+
+
+void K3bJob::blockingInformation( const QString& text,
+ const QString& caption )
+{
+ return m_jobHandler->blockingInformation( text, caption );
+}
+
+
+void K3bJob::connectSubJob( K3bJob* subJob,
+ const char* finishedSlot,
+ bool connectProgress,
+ const char* progressSlot,
+ const char* subProgressSlot,
+ const char* processedSizeSlot,
+ const char* processedSubSizeSlot )
+{
+ connect( subJob, SIGNAL(newTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( subJob, SIGNAL(newSubTask(const QString&)), this, SLOT(slotNewSubTask(const QString&)) );
+ connect( subJob, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+ connect( subJob, SIGNAL(infoMessage(const QString&, int)),
+ this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( subJob, SIGNAL(finished(bool)), this, finishedSlot );
+
+ if( connectProgress ) {
+ connect( subJob, SIGNAL(percent(int)),
+ this, progressSlot != 0 ? progressSlot : SIGNAL(subPercent(int)) );
+ if( subProgressSlot )
+ connect( subJob, SIGNAL(subPercent(int)), this, subProgressSlot );
+ connect( subJob, SIGNAL(processedSize(int, int)),
+ this, processedSizeSlot != 0 ? processedSizeSlot : SIGNAL(processedSubSize(int, int)) );
+ if( processedSubSizeSlot )
+ connect( subJob, SIGNAL(processedSubSize(int, int)), this, processedSubSizeSlot );
+ }
+}
+
+
+void K3bJob::connectSubJob( K3bJob* subJob,
+ const char* finishedSlot,
+ const char* newTaskSlot,
+ const char* newSubTaskSlot,
+ const char* progressSlot,
+ const char* subProgressSlot,
+ const char* processedSizeSlot,
+ const char* processedSubSizeSlot )
+{
+ // standard connections
+ connect( subJob, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+ connect( subJob, SIGNAL(infoMessage(const QString&, int)),
+ this, SIGNAL(infoMessage(const QString&, int)) );
+
+ // task connections
+ if( newTaskSlot == DEFAULT_SIGNAL_CONNECTION )
+ connect( subJob, SIGNAL(newTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ else if( newTaskSlot )
+ connect( subJob, SIGNAL(newTask(const QString&)), this, newTaskSlot );
+
+ if( newSubTaskSlot == DEFAULT_SIGNAL_CONNECTION )
+ connect( subJob, SIGNAL(newSubTask(const QString&)), this, SLOT(slotNewSubTask(const QString&)) );
+ else if( newSubTaskSlot )
+ connect( subJob, SIGNAL(newSubTask(const QString&)), this, newSubTaskSlot );
+
+ if( finishedSlot && finishedSlot != DEFAULT_SIGNAL_CONNECTION )
+ connect( subJob, SIGNAL(finished(bool)), this, finishedSlot );
+
+ // progress
+ if( progressSlot == DEFAULT_SIGNAL_CONNECTION )
+ connect( subJob, SIGNAL(percent(int)), this, SIGNAL(subPercent(int)) );
+ else if( progressSlot )
+ connect( subJob, SIGNAL(percent(int)), this, progressSlot );
+
+ if( subProgressSlot && subProgressSlot != DEFAULT_SIGNAL_CONNECTION )
+ connect( subJob, SIGNAL(subPercent(int)), this, subProgressSlot );
+
+ // processed size
+ if( processedSizeSlot == DEFAULT_SIGNAL_CONNECTION )
+ connect( subJob, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSubSize(int, int)) );
+ else if( processedSizeSlot )
+ connect( subJob, SIGNAL(processedSize(int, int)), this, processedSizeSlot );
+
+ if( processedSubSizeSlot && processedSubSizeSlot != DEFAULT_SIGNAL_CONNECTION )
+ connect( subJob, SIGNAL(processedSubSize(int, int)), this, processedSubSizeSlot );
+}
+
+
+unsigned int K3bJob::numRunningSubJobs() const
+{
+ return m_runningSubJobs.count();
+}
+
+
+void K3bJob::slotNewSubTask( const QString& str )
+{
+ emit infoMessage( str, INFO );
+}
+
+
+void K3bJob::registerSubJob( K3bJob* job )
+{
+ m_runningSubJobs.append( job );
+}
+
+
+void K3bJob::unregisterSubJob( K3bJob* job )
+{
+ m_runningSubJobs.removeRef( job );
+}
+
+
+
+
+class K3bBurnJob::Private
+{
+public:
+};
+
+
+
+K3bBurnJob::K3bBurnJob( K3bJobHandler* handler, QObject* parent, const char* name )
+ : K3bJob( handler, parent, name ),
+ m_writeMethod( K3b::DEFAULT )
+{
+ d = new Private;
+}
+
+
+K3bBurnJob::~K3bBurnJob()
+{
+ delete d;
+}
+
+
+int K3bBurnJob::supportedWritingApps() const
+{
+ return K3b::DEFAULT | K3b::CDRDAO | K3b::CDRECORD;
+}
+
+#include "k3bjob.moc"
diff --git a/libk3b/core/k3bjob.h b/libk3b/core/k3bjob.h
new file mode 100644
index 0000000..59e1f9b
--- /dev/null
+++ b/libk3b/core/k3bjob.h
@@ -0,0 +1,311 @@
+/*
+ *
+ * $Id: k3bjob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BJOB_H
+#define K3BJOB_H
+
+#include <qobject.h>
+#include <qptrlist.h>
+#include "k3bjobhandler.h"
+#include "k3b_export.h"
+
+class K3bDoc;
+namespace K3bDevice {
+ class Device;
+}
+
+
+/**
+ * This is the baseclass for all the jobs in K3b which actually do the work like burning a cd!
+ * The K3bJob object takes care of registering with the k3bcore or with a parent K3bJob.
+ *
+ * Every job has a jobhandler which can be another job (in which case the job is handled as
+ * a subjob) or an arbitrary class implementing the K3bJobHandler interface.
+ *
+ * A Job should never create any widgets. User interaction should be done through the methods
+ * questionYesNo, waitForMedia.
+ *
+ * @author Sebastian Trueg
+ */
+class LIBK3B_EXPORT K3bJob : public QObject, public K3bJobHandler
+{
+ Q_OBJECT
+
+ public:
+ virtual ~K3bJob();
+
+ /**
+ * \reimplemented from K3bJobHandler
+ */
+ bool isJob() const { return true; }
+
+ K3bJobHandler* jobHandler() const { return m_jobHandler; }
+
+ /**
+ * Is the job active?
+ * The default implementation is based on the jobStarted() and jobFinished()
+ * methods and there is normally no need to reimplement this.
+ */
+ virtual bool active() const { return m_active; }
+
+ /**
+ * The default implementation is based on the canceled() signal.
+ *
+ * This means that one cannot count on this value being valid
+ * in a slot connected to the canceled() signal. It is, however, save
+ * to call this method from a slot connected to the finished() signal
+ * in case the job makes proper usage of the jobStarted/jobFinished
+ * methods.
+ */
+ virtual bool hasBeenCanceled() const { return m_canceled; }
+
+ virtual QString jobDescription() const { return "K3bJob"; }
+ virtual QString jobDetails() const { return QString::null; }
+
+ /**
+ * @returns the number of running subjobs.
+ * this is useful for proper cancellation of jobs.
+ */
+ unsigned int numRunningSubJobs() const;
+
+ const QPtrList<K3bJob>& runningSubJobs() const { return m_runningSubJobs; }
+
+ /**
+ * \deprecated
+ */
+ virtual void connectSubJob( K3bJob* subJob,
+ const char* finishedSlot = 0,
+ bool progress = false,
+ const char* progressSlot = 0,
+ const char* subProgressSot = 0,
+ const char* processedSizeSlot = 0,
+ const char* processedSubSizeSlot = 0 );
+
+ static const char* DEFAULT_SIGNAL_CONNECTION;
+
+ /**
+ * \param newTaskSlot If DEFAULT_SIGNAL_CONNECTION the newTask signal from the subjob will
+ * be connected to the newSubTask signal
+ * \param newSubTaskSlot If DEFAULT_SIGNAL_CONNECTION the newSubTask signal from the subjob
+ * will create an infoMessage signal
+ * \param progressSlot If DEFAULT_SIGNAL_CONNECTION the percent signal of the subjob will be
+ * connected to the subPercent signal.
+ * debuggingOutput and infoMessage will always be direcctly connected.
+ *
+ * If a parameter is set to 0 it will not be connected at all
+ */
+ virtual void connectSubJob( K3bJob* subJob,
+ const char* finishedSlot = DEFAULT_SIGNAL_CONNECTION,
+ const char* newTaskSlot = DEFAULT_SIGNAL_CONNECTION,
+ const char* newSubTaskSlot = DEFAULT_SIGNAL_CONNECTION,
+ const char* progressSlot = DEFAULT_SIGNAL_CONNECTION,
+ const char* subProgressSlot = DEFAULT_SIGNAL_CONNECTION,
+ const char* processedSizeSlot = DEFAULT_SIGNAL_CONNECTION,
+ const char* processedSubSizeSlot = DEFAULT_SIGNAL_CONNECTION );
+
+ /**
+ * Message types to be used in combination with the infoMessage signal.
+ *
+ * \see infoMessage()
+ */
+ enum MessageType {
+ INFO, /**< Informational message. For example a message that informs the user about what is
+ currently going on */
+ WARNING, /**< A warning message. Something did not go perfectly but the job may continue. */
+ ERROR, /**< An error. Only use this message type if the job will actually fail afterwards
+ with a call to jobFinished( false ) */
+ SUCCESS /**< This message type may be used to inform the user that a sub job has
+ been successfully finished. */
+ };
+
+ /**
+ * reimplemented from K3bJobHandler
+ */
+ int waitForMedia( K3bDevice::Device*,
+ int mediaState = K3bDevice::STATE_EMPTY,
+ int mediaType = K3bDevice::MEDIA_WRITABLE_CD,
+ const QString& message = QString::null );
+
+ /**
+ * reimplemented from K3bJobHandler
+ */
+ bool questionYesNo( const QString& text,
+ const QString& caption = QString::null,
+ const QString& yesText = QString::null,
+ const QString& noText = QString::null );
+
+ /**
+ * reimplemented from K3bJobHandler
+ */
+ void blockingInformation( const QString& text,
+ const QString& caption = QString::null );
+
+ public slots:
+ /**
+ * This is the slot that starts the job. The first call should always
+ * be jobStarted().
+ *
+ * Once the job has finished it has to call jobFinished() with the result as
+ * a parameter.
+ *
+ * \see jobStarted()
+ * \see jobFinished()
+ */
+ virtual void start() = 0;
+
+ /**
+ * This slot should cancel the job. The job has to emit the canceled() signal and make a call
+ * to jobFinished().
+ * It is not important to do any of those two directly in this slot though.
+ */
+ virtual void cancel() = 0;
+
+ void setJobHandler( K3bJobHandler* );
+
+ signals:
+ void infoMessage( const QString& msg, int type );
+ void percent( int p );
+ void subPercent( int p );
+ void processedSize( int processed, int size );
+ void processedSubSize( int processed, int size );
+ void newTask( const QString& job );
+ void newSubTask( const QString& job );
+ void debuggingOutput(const QString&, const QString&);
+ void data( const char* data, int len );
+ void nextTrack( int track, int numTracks );
+
+ void canceled();
+
+ /**
+ * Emitted once the job has been started. Never emit this signal directly.
+ * Use jobStarted() instead, otherwise the job will not be properly registered
+ */
+ void started();
+
+ /**
+ * Emitted once the job has been finshed. Never emit this signal directly.
+ * Use jobFinished() instead, otherwise the job will not be properly deregistered
+ */
+ void finished( bool success );
+
+ protected:
+ /**
+ * \param hdl the handler of the job. This allows for some user interaction without
+ * specifying any details (like the GUI).
+ * The job handler can also be another job. In that case this job is a sub job
+ * and will be part of the parents running sub jobs.
+ *
+ * \see runningSubJobs()
+ * \see numRunningSubJobs()
+ */
+ K3bJob( K3bJobHandler* hdl, QObject* parent = 0, const char* name = 0 );
+
+ /**
+ * Call this in start() to properly register the job and emit the started() signal.
+ * Do never emit the started() signal manually.
+ *
+ * Always call K3bJob::jobStarted in reimplementations.
+ */
+ virtual void jobStarted();
+
+ /**
+ * Call this at the end of the job to properly deregister the job and emit the finished() signal.
+ * Do never emit the started() signal manually.
+ *
+ * Always call K3bJob::jobFinished in reimplementations.
+ */
+ virtual void jobFinished( bool success );
+
+ private slots:
+ void slotCanceled();
+ void slotNewSubTask( const QString& str );
+
+ private:
+ void registerSubJob( K3bJob* );
+ void unregisterSubJob( K3bJob* );
+
+ K3bJobHandler* m_jobHandler;
+ QPtrList<K3bJob> m_runningSubJobs;
+
+ bool m_canceled;
+ bool m_active;
+
+ class Private;
+ Private* d;
+};
+
+
+/**
+ * Every job used to actually burn a medium is derived from K3bBurnJob.
+ * This class implements additional signals like buffer status or writing speed
+ * as well as a handling of the used writing application.
+ */
+class LIBK3B_EXPORT K3bBurnJob : public K3bJob
+{
+ Q_OBJECT
+
+ public:
+ K3bBurnJob( K3bJobHandler* hdl, QObject* parent = 0, const char* name = 0 );
+ virtual ~K3bBurnJob();
+
+ /**
+ * The writing device used by this job.
+ */
+ virtual K3bDevice::Device* writer() const { return 0; }
+
+ /**
+ * use K3b::WritingApp
+ */
+ int writingApp() const { return m_writeMethod; }
+
+ /**
+ * K3b::WritingApp "ored" together
+ */
+ virtual int supportedWritingApps() const;
+
+ public slots:
+ /**
+ * use K3b::WritingApp
+ */
+ void setWritingApp( int w ) { m_writeMethod = w; }
+
+ signals:
+ void bufferStatus( int );
+
+ void deviceBuffer( int );
+
+ /**
+ * @param speed current writing speed in Kb
+ * @param multiplicator use 150 for CDs and 1380 for DVDs
+ * FIXME: maybe one should be able to ask the burnjob if it burns a CD or a DVD and remove the
+ * multiplicator parameter)
+ */
+ void writeSpeed( int speed, int multiplicator );
+
+ /**
+ * This signal may be used to inform when the burning starts or ends
+ * The BurningProgressDialog for example uses it to enable and disable
+ * the buffer and writing speed displays.
+ */
+ void burning(bool);
+
+ private:
+ int m_writeMethod;
+
+ class Private;
+ Private* d;
+};
+#endif
diff --git a/libk3b/core/k3bjobhandler.h b/libk3b/core/k3bjobhandler.h
new file mode 100644
index 0000000..1262e0e
--- /dev/null
+++ b/libk3b/core/k3bjobhandler.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * $Id: k3bjobhandler.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_JOB_HANDLER_H_
+#define _K3B_JOB_HANDLER_H_
+
+
+#include <k3bdiskinfo.h>
+#include <k3bdevice.h>
+
+
+/**
+ * See @p K3bJobProgressDialog as an example for the usage of
+ * the K3bJobHandler interface.
+ */
+class K3bJobHandler
+{
+ public:
+ K3bJobHandler() {}
+ virtual ~K3bJobHandler() {}
+
+ /**
+ * \return true if the handler itself is also a job
+ */
+ virtual bool isJob() const { return false; }
+
+ /**
+ * @return K3bDevice::MediaType on success,
+ * 0 if forced (no media info available),
+ * and -1 on error (canceled)
+ */
+ virtual int waitForMedia( K3bDevice::Device*,
+ int mediaState = K3bDevice::STATE_EMPTY,
+ int mediaType = K3bDevice::MEDIA_WRITABLE_CD,
+ const QString& message = QString::null ) = 0;
+
+ // FIXME: use KGuiItem
+ virtual bool questionYesNo( const QString& text,
+ const QString& caption = QString::null,
+ const QString& yesText = QString::null,
+ const QString& noText = QString::null ) = 0;
+
+ /**
+ * Use this if you need the user to do something before the job is able to continue.
+ * In all other cases an infoMessage should be used.
+ */
+ virtual void blockingInformation( const QString& text,
+ const QString& caption = QString::null ) = 0;
+
+};
+
+#endif
diff --git a/libk3b/core/k3bprocess.cpp b/libk3b/core/k3bprocess.cpp
new file mode 100644
index 0000000..35ddff4
--- /dev/null
+++ b/libk3b/core/k3bprocess.cpp
@@ -0,0 +1,452 @@
+/*
+ *
+ * $Id: k3bprocess.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+
+#include "k3bprocess.h"
+#include "k3bexternalbinmanager.h"
+
+#include <qstringlist.h>
+#include <qsocketnotifier.h>
+#include <qptrqueue.h>
+#include <qapplication.h>
+
+#include <kdebug.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+
+class K3bProcess::Data
+{
+public:
+ QString unfinishedStdoutLine;
+ QString unfinishedStderrLine;
+
+ int dupStdoutFd;
+ int dupStdinFd;
+
+ bool rawStdin;
+ bool rawStdout;
+
+ int in[2];
+ int out[2];
+
+ bool suppressEmptyLines;
+};
+
+
+K3bProcess::K3bProcess()
+ : KProcess(),
+ m_bSplitStdout(false)
+{
+ d = new Data();
+ d->dupStdinFd = d->dupStdoutFd = -1;
+ d->rawStdout = d->rawStdin = false;
+ d->in[0] = d->in[1] = -1;
+ d->out[0] = d->out[1] = -1;
+ d->suppressEmptyLines = true;
+}
+
+K3bProcess::~K3bProcess()
+{
+ delete d;
+}
+
+
+K3bProcess& K3bProcess::operator<<( const K3bExternalBin* bin )
+{
+ return this->operator<<( bin->path );
+}
+
+K3bProcess& K3bProcess::operator<<( const QString& arg )
+{
+ static_cast<KProcess*>(this)->operator<<( arg );
+ return *this;
+}
+
+K3bProcess& K3bProcess::operator<<( const char* arg )
+{
+ static_cast<KProcess*>(this)->operator<<( arg );
+ return *this;
+}
+
+K3bProcess& K3bProcess::operator<<( const QCString& arg )
+{
+ static_cast<KProcess*>(this)->operator<<( arg );
+ return *this;
+}
+
+K3bProcess& K3bProcess::operator<<( const QStringList& args )
+{
+ static_cast<KProcess*>(this)->operator<<( args );
+ return *this;
+}
+
+
+bool K3bProcess::start( RunMode run, Communication com )
+{
+ if( com & Stderr ) {
+ connect( this, SIGNAL(receivedStderr(KProcess*, char*, int)),
+ this, SLOT(slotSplitStderr(KProcess*, char*, int)) );
+ }
+ if( com & Stdout ) {
+ connect( this, SIGNAL(receivedStdout(KProcess*, char*, int)),
+ this, SLOT(slotSplitStdout(KProcess*, char*, int)) );
+ }
+
+ return KProcess::start( run, com );
+}
+
+
+void K3bProcess::slotSplitStdout( KProcess*, char* data, int len )
+{
+ if( m_bSplitStdout ) {
+ QStringList lines = splitOutput( data, len, d->unfinishedStdoutLine, d->suppressEmptyLines );
+
+ for( QStringList::iterator it = lines.begin(); it != lines.end(); ++it ) {
+ QString& str = *it;
+
+ // just to be sure since something in splitOutput does not do this right
+ if( d->suppressEmptyLines && str.isEmpty() )
+ continue;
+
+ emit stdoutLine( str );
+ }
+ }
+}
+
+
+void K3bProcess::slotSplitStderr( KProcess*, char* data, int len )
+{
+ QStringList lines = splitOutput( data, len, d->unfinishedStderrLine, d->suppressEmptyLines );
+
+ for( QStringList::iterator it = lines.begin(); it != lines.end(); ++it ) {
+ QString& str = *it;
+
+ // just to be sure since something in splitOutput does not do this right
+ if( d->suppressEmptyLines && str.isEmpty() )
+ continue;
+
+ emit stderrLine( str );
+ }
+}
+
+
+QStringList K3bProcess::splitOutput( char* data, int len,
+ QString& unfinishedLine, bool suppressEmptyLines )
+{
+ //
+ // The stderr splitting is mainly used for parsing of messages
+ // That's why we simplify the data before proceeding
+ //
+
+ QString buffer;
+ for( int i = 0; i < len; i++ ) {
+ if( data[i] == '\b' ) {
+ while( data[i] == '\b' ) // we replace multiple backspaces with a single line feed
+ i++;
+ buffer += '\n';
+ }
+ if( data[i] == '\r' )
+ buffer += '\n';
+ else if( data[i] == '\t' ) // replace tabs with a single space
+ buffer += " ";
+ else
+ buffer += data[i];
+ }
+
+ QStringList lines = QStringList::split( '\n', buffer, !suppressEmptyLines );
+
+ // in case we suppress empty lines we need to handle the following separately
+ // to make sure we join unfinished lines correctly
+ if( suppressEmptyLines && buffer[0] == '\n' )
+ lines.prepend( QString::null );
+
+ if( !unfinishedLine.isEmpty() ) {
+ lines.first().prepend( unfinishedLine );
+ unfinishedLine.truncate(0);
+
+ kdDebug() << "(K3bProcess) joined line: '" << (lines.first()) << "'" << endl;
+ }
+
+ QStringList::iterator it;
+
+ // check if line ends with a newline
+ // if not save the last line because it is not finished
+ QChar c = buffer.right(1).at(0);
+ bool hasUnfinishedLine = ( c != '\n' && c != '\r' && c != QChar(46) ); // What is unicode 46?? It is printed as a point
+ if( hasUnfinishedLine ) {
+ kdDebug() << "(K3bProcess) found unfinished line: '" << lines.last() << "'" << endl;
+ kdDebug() << "(K3bProcess) last char: '" << buffer.right(1) << "'" << endl;
+ unfinishedLine = lines.last();
+ it = lines.end();
+ --it;
+ lines.remove(it);
+ }
+
+ return lines;
+}
+
+
+int K3bProcess::setupCommunication( Communication comm )
+{
+ if( KProcess::setupCommunication( comm ) ) {
+
+ //
+ // Setup our own socketpair
+ //
+
+ if( d->rawStdin ) {
+ if( socketpair(AF_UNIX, SOCK_STREAM, 0, d->in) == 0 ) {
+ fcntl(d->in[0], F_SETFD, FD_CLOEXEC);
+ fcntl(d->in[1], F_SETFD, FD_CLOEXEC);
+ }
+ else
+ return 0;
+ }
+
+ if( d->rawStdout ) {
+ if( socketpair(AF_UNIX, SOCK_STREAM, 0, d->out) == 0 ) {
+ fcntl(d->out[0], F_SETFD, FD_CLOEXEC);
+ fcntl(d->out[1], F_SETFD, FD_CLOEXEC);
+ }
+ else {
+ if( d->rawStdin || d->dupStdinFd ) {
+ close(d->in[0]);
+ close(d->in[1]);
+ }
+ return 0;
+ }
+ }
+
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+void K3bProcess::commClose()
+{
+ if( d->rawStdin ) {
+ close(d->in[1]);
+ d->in[1] = -1;
+ }
+ if( d->rawStdout ) {
+ close(d->out[0]);
+ d->out[0] = -1;
+ }
+
+ KProcess::commClose();
+}
+
+
+int K3bProcess::commSetupDoneP()
+{
+ int ok = KProcess::commSetupDoneP();
+
+ if( d->rawStdin )
+ close(d->in[0]);
+ if( d->rawStdout )
+ close(d->out[1]);
+
+ d->in[0] = d->out[1] = -1;
+
+ return ok;
+}
+
+
+int K3bProcess::commSetupDoneC()
+{
+ int ok = KProcess::commSetupDoneC();
+
+ if( d->dupStdoutFd != -1 ) {
+ //
+ // make STDOUT_FILENO a duplicate of d->dupStdoutFd such that writes to STDOUT_FILENO are "redirected"
+ // to d->dupStdoutFd
+ //
+ if( ::dup2( d->dupStdoutFd, STDOUT_FILENO ) < 0 ) {
+ kdDebug() << "(K3bProcess) Error while dup( " << d->dupStdoutFd << ", " << STDOUT_FILENO << endl;
+ ok = 0;
+ }
+ }
+ else if( d->rawStdout ) {
+ if( ::dup2( d->out[1], STDOUT_FILENO ) < 0 ) {
+ kdDebug() << "(K3bProcess) Error while dup( " << d->out[1] << ", " << STDOUT_FILENO << endl;
+ ok = 0;
+ }
+ }
+
+ if( d->dupStdinFd != -1 ) {
+ if( ::dup2( d->dupStdinFd, STDIN_FILENO ) < 0 ) {
+ kdDebug() << "(K3bProcess) Error while dup( " << d->dupStdinFd << ", " << STDIN_FILENO << endl;
+ ok = 0;
+ }
+ }
+ else if( d->rawStdin ) {
+ if( ::dup2( d->in[0], STDIN_FILENO ) < 0 ) {
+ kdDebug() << "(K3bProcess) Error while dup( " << d->in[0] << ", " << STDIN_FILENO << endl;
+ ok = 0;
+ }
+ }
+
+ return ok;
+}
+
+
+
+int K3bProcess::stdinFd() const
+{
+ if( d->rawStdin )
+ return d->in[1];
+ else if( d->dupStdinFd != -1 )
+ return d->dupStdinFd;
+ else
+ return -1;
+}
+
+int K3bProcess::stdoutFd() const
+{
+ if( d->rawStdout )
+ return d->out[0];
+ else if( d->dupStdoutFd != -1 )
+ return d->dupStdoutFd;
+ else
+ return -1;
+}
+
+
+void K3bProcess::dupStdout( int fd )
+{
+ writeToFd( fd );
+}
+
+void K3bProcess::dupStdin( int fd )
+{
+ readFromFd( fd );
+}
+
+
+void K3bProcess::writeToFd( int fd )
+{
+ d->dupStdoutFd = fd;
+ if( fd != -1 )
+ d->rawStdout = false;
+}
+
+void K3bProcess::readFromFd( int fd )
+{
+ d->dupStdinFd = fd;
+ if( fd != -1 )
+ d->rawStdin = false;
+}
+
+
+void K3bProcess::setRawStdin(bool b)
+{
+ if( b ) {
+ d->rawStdin = true;
+ d->dupStdinFd = -1;
+ }
+ else
+ d->rawStdin = false;
+}
+
+
+void K3bProcess::setRawStdout(bool b)
+{
+ if( b ) {
+ d->rawStdout = true;
+ d->dupStdoutFd = -1;
+ }
+ else
+ d->rawStdout = false;
+}
+
+
+void K3bProcess::setSuppressEmptyLines( bool b )
+{
+ d->suppressEmptyLines = b;
+}
+
+
+bool K3bProcess::closeStdin()
+{
+ if( d->rawStdin ) {
+ close(d->in[1]);
+ d->in[1] = -1;
+ return true;
+ }
+ else
+ return KProcess::closeStdin();
+}
+
+
+bool K3bProcess::closeStdout()
+{
+ if( d->rawStdout ) {
+ close(d->out[0]);
+ d->out[0] = -1;
+ return true;
+ }
+ else
+ return KProcess::closeStdout();
+}
+
+
+K3bProcessOutputCollector::K3bProcessOutputCollector( KProcess* p )
+ : m_process(0)
+{
+ setProcess( p );
+}
+
+void K3bProcessOutputCollector::setProcess( KProcess* p )
+{
+ if( m_process )
+ m_process->disconnect( this );
+
+ m_process = p;
+ if( p ) {
+ connect( p, SIGNAL(receivedStdout(KProcess*, char*, int)),
+ this, SLOT(slotGatherStdout(KProcess*, char*, int)) );
+ connect( p, SIGNAL(receivedStderr(KProcess*, char*, int)),
+ this, SLOT(slotGatherStderr(KProcess*, char*, int)) );
+ }
+
+ m_gatheredOutput.truncate( 0 );
+ m_stderrOutput.truncate( 0 );
+ m_stdoutOutput.truncate( 0 );
+}
+
+void K3bProcessOutputCollector::slotGatherStderr( KProcess*, char* data, int len )
+{
+ m_gatheredOutput.append( QString::fromLocal8Bit( data, len ) );
+ m_stderrOutput.append( QString::fromLocal8Bit( data, len ) );
+}
+
+void K3bProcessOutputCollector::slotGatherStdout( KProcess*, char* data, int len )
+{
+ m_gatheredOutput.append( QString::fromLocal8Bit( data, len ) );
+ m_stdoutOutput.append( QString::fromLocal8Bit( data, len ) );
+}
+
+
+#include "k3bprocess.moc"
diff --git a/libk3b/core/k3bprocess.h b/libk3b/core/k3bprocess.h
new file mode 100644
index 0000000..959bda1
--- /dev/null
+++ b/libk3b/core/k3bprocess.h
@@ -0,0 +1,204 @@
+/*
+ *
+ * $Id: k3bprocess.h 621644 2007-01-09 12:53:09Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3B_PROCESS_H
+#define K3B_PROCESS_H
+
+
+#include <kprocess.h>
+#include <qstring.h>
+#include "k3b_export.h"
+
+class K3bExternalBin;
+
+
+/**
+ * This is an enhanced KProcess.
+ * It splits the stderr output to lines making sure the client gets every line as it
+ * was written by the process.
+ * Aditionally one may set raw stdout and stdin handling using the stdin() and stdout() methods
+ * to get the process' file descriptors.
+ * Last but not least K3bProcess is able to duplicate stdout making it possible to connect two
+ * K3bProcesses like used in K3bDataJob to duplicate mkisofs' stdout to the stdin of the writer
+ * (cdrecord or cdrdao)
+ */
+class LIBK3B_EXPORT K3bProcess : public KProcess
+{
+ Q_OBJECT
+
+ public:
+ class OutputCollector;
+
+ public:
+ K3bProcess();
+ ~K3bProcess();
+
+ /**
+ * In the future this might also set the nice value
+ */
+ K3bProcess& operator<<( const K3bExternalBin* );
+
+ K3bProcess& operator<<( const QString& arg );
+ K3bProcess& operator<<( const char* arg );
+ K3bProcess& operator<<( const QCString& arg );
+ K3bProcess& operator<<( const QStringList& args );
+
+ bool start( RunMode run = NotifyOnExit, Communication com = NoCommunication );
+
+ /**
+ * get stdin file descriptor
+ * Only makes sense while process is running.
+ *
+ * Only use with setRawStdin
+ */
+ int stdinFd() const;
+
+ /**
+ * get stdout file descriptor
+ * Only makes sense while process is running.
+ *
+ * Only use with setRawStdout
+ */
+ int stdoutFd() const;
+
+ /**
+ * @deprecated use writeToFd
+ */
+ void dupStdout( int fd );
+
+ /**
+ * @deprecated use readFromFd
+ */
+ void dupStdin( int fd );
+
+ /**
+ * Make the process write to @fd instead of Stdout.
+ * This means you won't get any stdoutReady() or receivedStdout()
+ * signals anymore.
+ *
+ * Only use this before starting the process.
+ */
+ void writeToFd( int fd );
+
+ /**
+ * Make the process read from @fd instead of Stdin.
+ * This means you won't get any wroteStdin()
+ * signals anymore.
+ *
+ * Only use this before starting the process.
+ */
+ void readFromFd( int fd );
+
+ /**
+ * If set true the process' stdin fd will be available
+ * through @stdinFd.
+ * Be aware that you will not get any wroteStdin signals
+ * anymore.
+ *
+ * Only use this before starting the process.
+ */
+ void setRawStdin(bool b);
+
+ /**
+ * If set true the process' stdout fd will be available
+ * through @stdoutFd.
+ * Be aware that you will not get any stdoutReady or receivedStdout
+ * signals anymore.
+ *
+ * Only use this before starting the process.
+ */
+ void setRawStdout(bool b);
+
+ public slots:
+ void setSplitStdout( bool b ) { m_bSplitStdout = b; }
+
+ /**
+ * default is true
+ */
+ void setSuppressEmptyLines( bool b );
+
+ bool closeStdin();
+ bool closeStdout();
+
+ private slots:
+ void slotSplitStderr( KProcess*, char*, int );
+ void slotSplitStdout( KProcess*, char*, int );
+
+ signals:
+ void stderrLine( const QString& line );
+ void stdoutLine( const QString& line );
+
+ /**
+ * Gets emitted if raw stdout mode has been requested
+ * The data has to be read from @p fd.
+ */
+ void stdoutReady( int fd );
+
+ protected:
+ /**
+ * reimplemeted from KProcess
+ */
+ int commSetupDoneP();
+
+ /**
+ * reimplemeted from KProcess
+ */
+ int commSetupDoneC();
+
+ /**
+ * reimplemeted from KProcess
+ */
+ int setupCommunication( Communication comm );
+
+ /**
+ * reimplemeted from KProcess
+ */
+ void commClose();
+
+ private:
+ static QStringList splitOutput( char*, int, QString&, bool );
+
+ class Data;
+ Data* d;
+
+ bool m_bSplitStdout;
+};
+
+class LIBK3B_EXPORT K3bProcessOutputCollector: public QObject
+{
+ Q_OBJECT
+
+ public:
+ K3bProcessOutputCollector( KProcess* );
+ void setProcess( KProcess* );
+
+ const QString& output() const { return m_gatheredOutput; }
+ const QString& stderrOutput() const { return m_stderrOutput; }
+ const QString& stdoutOutput() const { return m_stdoutOutput; }
+
+ private slots:
+ void slotGatherStderr( KProcess*, char*, int );
+ void slotGatherStdout( KProcess*, char*, int );
+
+ private:
+ QString m_gatheredOutput;
+ QString m_stderrOutput;
+ QString m_stdoutOutput;
+ KProcess* m_process;
+};
+
+
+#endif
diff --git a/libk3b/core/k3bprogressinfoevent.h b/libk3b/core/k3bprogressinfoevent.h
new file mode 100644
index 0000000..0e77718
--- /dev/null
+++ b/libk3b/core/k3bprogressinfoevent.h
@@ -0,0 +1,85 @@
+/*
+ *
+ * $Id: k3bprogressinfoevent.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef K3B_PROGRESS_INFO_EVENT_H
+#define K3B_PROGRESS_INFO_EVENT_H
+
+#include <qevent.h>
+#include <qstring.h>
+
+
+/**
+ * Custom event class for posting events corresponding to the
+ * K3bJob signals. This is useful for a threaded job since
+ * in that case it's not possible to emit signals that directly
+ * change the GUI (see QThread docu).
+ */
+class K3bProgressInfoEvent : public QCustomEvent
+{
+ public:
+ K3bProgressInfoEvent( int type )
+ : QCustomEvent( type ),
+ m_type(type)
+ {}
+
+ K3bProgressInfoEvent( int type, const QString& v1, const QString& v2 = QString::null,
+ int value1 = 0, int value2 = 0 )
+ : QCustomEvent( type ),
+ m_type( type),
+ m_firstValue(value1),
+ m_secondValue(value2),
+ m_firstString(v1),
+ m_secondString(v2)
+ {}
+
+ K3bProgressInfoEvent( int type, int value1, int value2 = 0 )
+ : QCustomEvent( type ),
+ m_type( type),
+ m_firstValue(value1),
+ m_secondValue(value2)
+ {}
+
+ int type() const { return m_type; }
+ const QString& firstString() const { return m_firstString; }
+ const QString& secondString() const { return m_secondString; }
+ int firstValue() const { return m_firstValue; }
+ int secondValue() const { return m_secondValue; }
+
+ enum K3bProgressInfoEventType {
+ Progress = QEvent::User + 1,
+ SubProgress,
+ ProcessedSize,
+ ProcessedSubSize,
+ InfoMessage,
+ Started,
+ Canceled,
+ Finished,
+ NewTask,
+ NewSubTask,
+ DebuggingOutput,
+ BufferStatus,
+ WriteSpeed,
+ NextTrack
+ };
+
+ private:
+ int m_type;
+ int m_firstValue;
+ int m_secondValue;
+ QString m_firstString;
+ QString m_secondString;
+};
+
+#endif
diff --git a/libk3b/core/k3bsimplejobhandler.cpp b/libk3b/core/k3bsimplejobhandler.cpp
new file mode 100644
index 0000000..eaf7cd6
--- /dev/null
+++ b/libk3b/core/k3bsimplejobhandler.cpp
@@ -0,0 +1,62 @@
+/*
+ *
+ * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
+ * Copyright (C) 2006 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bsimplejobhandler.h"
+
+
+K3bSimpleJobHandler::K3bSimpleJobHandler( QObject* parent )
+ : QObject( parent ),
+ K3bJobHandler()
+{
+}
+
+K3bSimpleJobHandler::~K3bSimpleJobHandler()
+{
+}
+
+int K3bSimpleJobHandler::waitForMedia( K3bDevice::Device* dev,
+ int mediaState,
+ int mediaType,
+ const QString& message )
+{
+ Q_UNUSED( dev );
+ Q_UNUSED( mediaState );
+ Q_UNUSED( mediaType );
+ Q_UNUSED( message );
+
+ return 0;
+}
+
+bool K3bSimpleJobHandler::questionYesNo( const QString& text,
+ const QString& caption,
+ const QString& yesText,
+ const QString& noText )
+{
+ Q_UNUSED( text );
+ Q_UNUSED( caption );
+ Q_UNUSED( yesText );
+ Q_UNUSED( noText );
+
+ return true;
+}
+
+void K3bSimpleJobHandler::blockingInformation( const QString& text,
+ const QString& caption )
+{
+ Q_UNUSED( text );
+ Q_UNUSED( caption );
+}
+
+#include "k3bsimplejobhandler.moc"
diff --git a/libk3b/core/k3bsimplejobhandler.h b/libk3b/core/k3bsimplejobhandler.h
new file mode 100644
index 0000000..f84064e
--- /dev/null
+++ b/libk3b/core/k3bsimplejobhandler.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
+ * Copyright (C) 2006 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_SIMPLE_JOB_HANDLER_H_
+#define _K3B_SIMPLE_JOB_HANDLER_H_
+
+#include <k3b_export.h>
+
+#include <qobject.h>
+#include <k3bjobhandler.h>
+
+
+/**
+ * This is a simplified job handler which just consumes the
+ * job handler calls without doing anything.
+ * Use it for very simple jobs that don't need the job handler
+ * methods.
+ */
+class LIBK3B_EXPORT K3bSimpleJobHandler : public QObject, public K3bJobHandler
+{
+ Q_OBJECT
+
+ public:
+ K3bSimpleJobHandler( QObject* parent = 0 );
+ ~K3bSimpleJobHandler();
+
+ /*
+ * \return 0
+ */
+ int waitForMedia( K3bDevice::Device*,
+ int mediaState = K3bDevice::STATE_EMPTY,
+ int mediaType = K3bDevice::MEDIA_WRITABLE_CD,
+ const QString& message = QString::null );
+ /**
+ * \return true
+ */
+ bool questionYesNo( const QString& text,
+ const QString& caption = QString::null,
+ const QString& yesText = QString::null,
+ const QString& noText = QString::null );
+
+ /**
+ * Does nothing
+ */
+ void blockingInformation( const QString& text,
+ const QString& caption = QString::null );
+};
+
+#endif
diff --git a/libk3b/core/k3bthread.cpp b/libk3b/core/k3bthread.cpp
new file mode 100644
index 0000000..07414ad
--- /dev/null
+++ b/libk3b/core/k3bthread.cpp
@@ -0,0 +1,221 @@
+/*
+ *
+ * $Id: k3bthread.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bthread.h"
+#include "k3bprogressinfoevent.h"
+#include "k3bdataevent.h"
+
+#include <kdebug.h>
+
+#include <qapplication.h>
+
+
+static QPtrList<K3bThread> s_threads;
+
+
+void K3bThread::waitUntilFinished()
+{
+ QPtrListIterator<K3bThread> it( s_threads );
+ while( it.current() ) {
+ kdDebug() << "Waiting for thread " << it.current() << endl;
+ it.current()->wait();
+ ++it;
+ }
+
+ kdDebug() << "Thread waiting done." << endl;
+}
+
+
+class K3bThread::Private
+{
+public:
+ Private()
+ : eventHandler( 0 ) {
+ }
+
+ QObject* eventHandler;
+};
+
+
+K3bThread::K3bThread( QObject* eventHandler )
+ : QThread()
+{
+ d = new Private;
+ d->eventHandler = eventHandler;
+
+ s_threads.append(this);
+}
+
+
+K3bThread::K3bThread( unsigned int stackSize, QObject* eventHandler )
+ : QThread( stackSize )
+{
+ d = new Private;
+ d->eventHandler = eventHandler;
+
+ s_threads.append(this);
+}
+
+
+K3bThread::~K3bThread()
+{
+ s_threads.removeRef(this);
+ delete d;
+}
+
+
+void K3bThread::setProgressInfoEventHandler( QObject* eventHandler )
+{
+ d->eventHandler = eventHandler;
+}
+
+QString K3bThread::jobDescription() const
+{
+ return QString::null;
+}
+
+
+QString K3bThread::jobDetails() const
+{
+ return QString::null;
+}
+
+
+void K3bThread::init()
+{
+ // do nothing...
+}
+
+
+void K3bThread::cancel()
+{
+ if( running() ) {
+ terminate();
+ if( d->eventHandler ) {
+ emitCanceled();
+ emitFinished(false);
+ }
+ }
+}
+
+
+void K3bThread::emitInfoMessage( const QString& msg, int type )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler,
+ new K3bProgressInfoEvent( K3bProgressInfoEvent::InfoMessage, msg, QString::null, type ) );
+ else
+ kdWarning() << "(K3bThread) call to emitInfoMessage() without eventHandler." << endl;
+}
+
+void K3bThread::emitPercent( int p )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler,
+ new K3bProgressInfoEvent( K3bProgressInfoEvent::Progress, p ) );
+ else
+ kdWarning() << "(K3bThread) call to emitPercent() without eventHandler." << endl;
+}
+
+void K3bThread::emitSubPercent( int p )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler,
+ new K3bProgressInfoEvent( K3bProgressInfoEvent::SubProgress, p ) );
+ else
+ kdWarning() << "(K3bThread) call to emitSubPercent() without eventHandler." << endl;
+}
+
+void K3bThread::emitStarted()
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::Started ) );
+ else
+ kdWarning() << "(K3bThread) call to emitStarted() without eventHandler." << endl;
+}
+
+void K3bThread::emitCanceled()
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::Canceled ) );
+ else
+ kdWarning() << "(K3bThread) call to emitCanceled() without eventHandler." << endl;
+}
+
+void K3bThread::emitFinished( bool success )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::Finished, success ) );
+ else
+ kdWarning() << "(K3bThread) call to emitFinished() without eventHandler." << endl;
+}
+
+void K3bThread::emitProcessedSize( int p, int size )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::ProcessedSize, p, size ) );
+ else
+ kdWarning() << "(K3bThread) call to emitProcessedSize() without eventHandler." << endl;
+}
+
+void K3bThread::emitProcessedSubSize( int p, int size )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::ProcessedSubSize, p, size ) );
+ else
+ kdWarning() << "(K3bThread) call to emitProcessedSubSize() without eventHandler." << endl;
+}
+
+void K3bThread::emitNewTask( const QString& job )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::NewTask, job ) );
+ else
+ kdWarning() << "(K3bThread) call to emitNewTask() without eventHandler." << endl;
+}
+
+void K3bThread::emitNewSubTask( const QString& job )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::NewSubTask, job ) );
+ else
+ kdWarning() << "(K3bThread) call to emitNewSubTask() without eventHandler." << endl;
+}
+
+void K3bThread::emitDebuggingOutput(const QString& group, const QString& text)
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::DebuggingOutput, group, text ) );
+ else
+ kdWarning() << "(K3bThread) call to emitDebuggingOutput() without eventHandler." << endl;
+}
+
+void K3bThread::emitData( const char* data, int len )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bDataEvent( data, len ) );
+ else
+ kdWarning() << "(K3bThread) call to emitData() without eventHandler." << endl;
+}
+
+void K3bThread::emitNextTrack( int t, int n )
+{
+ if( d->eventHandler )
+ QApplication::postEvent( d->eventHandler, new K3bProgressInfoEvent( K3bProgressInfoEvent::NextTrack, t, n ) );
+ else
+ kdWarning() << "(K3bThread) call to emitNextTrack() without eventHandler." << endl;
+}
+
diff --git a/libk3b/core/k3bthread.h b/libk3b/core/k3bthread.h
new file mode 100644
index 0000000..f7e68fc
--- /dev/null
+++ b/libk3b/core/k3bthread.h
@@ -0,0 +1,95 @@
+/*
+ *
+ * $Id: k3bthread.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_THREAD_H_
+#define _K3B_THREAD_H_
+
+#include <qthread.h>
+#include "k3b_export.h"
+
+class QObject;
+
+/**
+ * The threaded couterpart to K3bJob
+ * instead of emitting the information signals
+ * one has to use the emitXXX methods which will post
+ * K3bProgressInfoEvents to the eventhandler.
+ *
+ * K3bThreadJob can be used to automatically wrap the thread in a K3bJob.
+ *
+ * As in K3bJob it is important to call emitStarted and emitFinished.
+ *
+ * See K3bThreadJob for more information.
+ */
+class LIBK3B_EXPORT K3bThread : public QThread
+{
+ public:
+ K3bThread( QObject* eventHandler = 0 );
+ K3bThread( unsigned int stackSize, QObject* eventHandler = 0 );
+ virtual ~K3bThread();
+
+ void setProgressInfoEventHandler( QObject* eventHandler );
+
+ /**
+ * Initialize the thread before starting it in the GUi thread.
+ * K3bThreadJob automatically calls this.
+ *
+ * The default implementation does nothing.
+ */
+ virtual void init();
+
+ /**
+ * to provide the same api like K3bJob
+ * the default implementation calls terminate and
+ * emitCancled() and emitFinished(false)
+ */
+ virtual void cancel();
+
+ virtual QString jobDescription() const;
+ virtual QString jobDetails() const;
+
+ /**
+ * waits until all running K3bThread have finished.
+ * This is used by K3bApplication.
+ */
+ static void waitUntilFinished();
+
+ protected:
+ virtual void run() = 0;
+
+ /**
+ * uses the K3bJob::MessageType enum
+ */
+ void emitInfoMessage( const QString& msg, int type );
+ void emitPercent( int p );
+ void emitSubPercent( int p );
+ void emitStarted();
+ void emitCanceled();
+ void emitFinished( bool success );
+ void emitProcessedSize( int processed, int size );
+ void emitProcessedSubSize( int processed, int size );
+ void emitNewTask( const QString& job );
+ void emitNewSubTask( const QString& job );
+ void emitDebuggingOutput(const QString&, const QString&);
+ void emitData( const char* data, int len );
+ void emitNextTrack( int track, int trackNum );
+
+ private:
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/core/k3bthreadjob.cpp b/libk3b/core/k3bthreadjob.cpp
new file mode 100644
index 0000000..a13f10a
--- /dev/null
+++ b/libk3b/core/k3bthreadjob.cpp
@@ -0,0 +1,161 @@
+/*
+ *
+ * $Id: k3bthreadjob.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bthreadjob.h"
+#include "k3bthread.h"
+#include "k3bprogressinfoevent.h"
+#include "k3bdataevent.h"
+
+#include <kdebug.h>
+#include <kapplication.h>
+
+
+
+K3bThreadJob::K3bThreadJob( K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bJob( jh, parent, name ),
+ m_running(false)
+{
+}
+
+
+K3bThreadJob::K3bThreadJob( K3bThread* thread, K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bJob( jh, parent, name ),
+ m_running(false)
+{
+ setThread(thread);
+}
+
+
+K3bThreadJob::~K3bThreadJob()
+{
+}
+
+
+QString K3bThreadJob::jobDescription() const
+{
+ if( m_thread )
+ return m_thread->jobDescription();
+ else
+ return QString::null;
+}
+
+
+QString K3bThreadJob::jobDetails() const
+{
+ if( m_thread )
+ return m_thread->jobDetails();
+ else
+ return QString::null;
+}
+
+
+void K3bThreadJob::setThread( K3bThread* t )
+{
+ m_thread = t;
+ m_thread->setProgressInfoEventHandler(this);
+}
+
+
+void K3bThreadJob::start()
+{
+ if( m_thread ) {
+ if( !m_running ) {
+ m_thread->setProgressInfoEventHandler(this);
+ m_running = true;
+ m_thread->init();
+ m_thread->start();
+ }
+ else
+ kdDebug() << "(K3bThreadJob) thread not finished yet." << endl;
+ }
+ else {
+ kdError() << "(K3bThreadJob) no job set." << endl;
+ jobFinished(false);
+ }
+}
+
+
+void K3bThreadJob::cancel()
+{
+ m_thread->cancel();
+ // wait for the thread to finish
+ // m_thread->wait();
+}
+
+
+void K3bThreadJob::cleanupJob( bool success )
+{
+ Q_UNUSED( success );
+}
+
+
+void K3bThreadJob::customEvent( QCustomEvent* e )
+{
+ if( K3bDataEvent* de = dynamic_cast<K3bDataEvent*>(e) ) {
+ emit data( de->data(), de->length() );
+ }
+ else {
+ K3bProgressInfoEvent* be = static_cast<K3bProgressInfoEvent*>(e);
+ switch( be->type() ) {
+ case K3bProgressInfoEvent::Progress:
+ emit percent( be->firstValue() );
+ break;
+ case K3bProgressInfoEvent::SubProgress:
+ emit subPercent( be->firstValue() );
+ break;
+ case K3bProgressInfoEvent::ProcessedSize:
+ emit processedSize( be->firstValue(), be->secondValue() );
+ break;
+ case K3bProgressInfoEvent::ProcessedSubSize:
+ emit processedSubSize( be->firstValue(), be->secondValue() );
+ break;
+ case K3bProgressInfoEvent::InfoMessage:
+ emit infoMessage( be->firstString(), be->firstValue() );
+ break;
+ case K3bProgressInfoEvent::Started:
+ jobStarted();
+ break;
+ case K3bProgressInfoEvent::Canceled:
+ emit canceled();
+ break;
+ case K3bProgressInfoEvent::Finished:
+ // we wait until the thred really finished
+ // although this may be dangerous if some thread
+ // emits the finished signal although it has not finished yet
+ // but makes a lot stuff easier.
+ kdDebug() << "(K3bThreadJob) waiting for the thread to finish." << endl;
+ m_thread->wait();
+ kdDebug() << "(K3bThreadJob) thread finished." << endl;
+ cleanupJob( be->firstValue() );
+ m_running = false;
+ jobFinished( be->firstValue() );
+ break;
+ case K3bProgressInfoEvent::NewTask:
+ emit newTask( be->firstString() );
+ break;
+ case K3bProgressInfoEvent::NewSubTask:
+ emit newSubTask( be->firstString() );
+ break;
+ case K3bProgressInfoEvent::DebuggingOutput:
+ emit debuggingOutput( be->firstString(), be->secondString() );
+ break;
+ case K3bProgressInfoEvent::NextTrack:
+ emit nextTrack( be->firstValue(), be->secondValue() );
+ break;
+ }
+ }
+}
+
+#include "k3bthreadjob.moc"
diff --git a/libk3b/core/k3bthreadjob.h b/libk3b/core/k3bthreadjob.h
new file mode 100644
index 0000000..25919f1
--- /dev/null
+++ b/libk3b/core/k3bthreadjob.h
@@ -0,0 +1,89 @@
+/*
+ *
+ * $Id: k3bthreadjob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_THREAD_JOB_H_
+#define _K3B_THREAD_JOB_H_
+
+#include "k3bjob.h"
+#include "k3b_export.h"
+class QCustomEvent;
+class K3bThread;
+
+
+/**
+ * A Wrapper to use a K3bThread just like a K3bJob.
+ * Usage:
+ * <pre>
+ * K3bThread* thread = new MySuperThread(...);
+ * K3bThreadJob* job = new K3bThreadJob( thread, ... );
+ * K3bBurnProgressDialog d;
+ * d.setJob(job);
+ * job->start();
+ * d.exec();
+ * delete job;
+ * </pre>
+ * Be aware that K3bThreadJob'd destructor does NOT delete the thread.
+ */
+class LIBK3B_EXPORT K3bThreadJob : public K3bJob
+{
+ Q_OBJECT
+
+ public:
+ K3bThreadJob( K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ K3bThreadJob( K3bThread*, K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ virtual ~K3bThreadJob();
+
+ void setThread( K3bThread* t );
+ K3bThread* thread() const { return m_thread; }
+
+ /**
+ * \reimplemented from K3bJob
+ *
+ * \return true if the job has been started and has not yet
+ * emitted the finished signal
+ */
+ virtual bool active() const { return m_running; }
+
+ virtual QString jobDescription() const;
+ virtual QString jobDetails() const;
+
+ public slots:
+ virtual void start();
+ virtual void cancel();
+
+ protected:
+ /**
+ * converts K3bThread events to K3bJob signals
+ */
+ virtual void customEvent( QCustomEvent* );
+
+ /**
+ * Reimplement this method to do some housekeeping once
+ * the thread has finished.
+ *
+ * The default implementation does nothing.
+ *
+ * \param success True if the thread finished successfully
+ */
+ virtual void cleanupJob( bool success );
+
+ private:
+ K3bThread* m_thread;
+ bool m_running;
+};
+
+#endif
+
diff --git a/libk3b/core/k3bversion.cpp b/libk3b/core/k3bversion.cpp
new file mode 100644
index 0000000..f7af248
--- /dev/null
+++ b/libk3b/core/k3bversion.cpp
@@ -0,0 +1,318 @@
+/*
+ *
+ * $Id: k3bversion.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bversion.h"
+
+#include <qregexp.h>
+#include <kdebug.h>
+
+
+K3bVersion::K3bVersion()
+ : m_majorVersion( -1 ),
+ m_minorVersion( -1 ),
+ m_patchLevel( -1 )
+{
+}
+
+K3bVersion::K3bVersion( const K3bVersion& v )
+ : m_versionString( v.versionString() ),
+ m_majorVersion( v.majorVersion() ),
+ m_minorVersion( v.minorVersion() ),
+ m_patchLevel( v.patchLevel() ),
+ m_suffix( v.suffix() )
+{
+}
+
+K3bVersion::K3bVersion( const QString& version )
+{
+ setVersion( version );
+}
+
+K3bVersion::K3bVersion( int majorVersion,
+ int minorVersion,
+ int patchlevel,
+ const QString& suffix )
+{
+ setVersion( majorVersion, minorVersion, patchlevel, suffix );
+}
+
+void K3bVersion::setVersion( const QString& v )
+{
+ QString suffix;
+ splitVersionString( v.stripWhiteSpace(), m_majorVersion, suffix );
+ if( m_majorVersion >= 0 ) {
+ if( suffix.startsWith(".") ) {
+ suffix = suffix.mid( 1 );
+ splitVersionString( suffix, m_minorVersion, suffix );
+ if( m_minorVersion < 0 ) {
+ kdDebug() << "(K3bVersion) suffix must not start with a dot!" << endl;
+ m_majorVersion = -1;
+ m_minorVersion = -1;
+ m_patchLevel = -1;
+ m_suffix = "";
+ }
+ else {
+ if( suffix.startsWith(".") ) {
+ suffix = suffix.mid( 1 );
+ splitVersionString( suffix, m_patchLevel, suffix );
+ if( m_patchLevel < 0 ) {
+ kdDebug() << "(K3bVersion) suffix must not start with a dot!" << endl;
+ m_majorVersion = -1;
+ m_minorVersion = -1;
+ m_patchLevel = -1;
+ m_suffix = "";
+ }
+ else {
+ m_suffix = suffix;
+ }
+ }
+ else {
+ m_patchLevel = -1;
+ m_suffix = suffix;
+ }
+ }
+ }
+ else {
+ m_minorVersion = -1;
+ m_patchLevel = -1;
+ m_suffix = suffix;
+ }
+ }
+
+ m_versionString = createVersionString( m_majorVersion, m_minorVersion, m_patchLevel, m_suffix );
+}
+
+
+// splits the leading number from s and puts it in num
+// the dot is removed and the rest put in suffix
+// if s does not start with a digit or the first non-digit char is not a dot
+// suffix = s and num = -1 is returned
+void K3bVersion::splitVersionString( const QString& s, int& num, QString& suffix )
+{
+ int pos = s.find( QRegExp("\\D") );
+ if( pos < 0 ) {
+ num = s.toInt();
+ suffix = "";
+ }
+ else if( pos == 0 ) {
+ num = -1;
+ suffix = s;
+ }
+ else {
+ num = s.left( pos ).toInt();
+ suffix = s.mid( pos );
+ }
+}
+
+
+bool K3bVersion::isValid() const
+{
+ return (m_majorVersion >= 0);
+}
+
+
+void K3bVersion::setVersion( int majorVersion,
+ int minorVersion,
+ int patchlevel,
+ const QString& suffix )
+{
+ m_majorVersion = majorVersion;
+ m_minorVersion = minorVersion;
+ m_patchLevel = patchlevel;
+ m_suffix = suffix;
+ m_versionString = createVersionString( majorVersion, minorVersion, patchlevel, suffix );
+}
+
+K3bVersion& K3bVersion::operator=( const QString& v )
+{
+ setVersion( v );
+ return *this;
+}
+
+K3bVersion K3bVersion::simplify() const
+{
+ K3bVersion v( *this );
+ v.m_suffix.truncate(0);
+ return v;
+}
+
+QString K3bVersion::createVersionString( int majorVersion,
+ int minorVersion,
+ int patchlevel,
+ const QString& suffix )
+{
+ if( majorVersion >= 0 ) {
+ QString s = QString::number(majorVersion);
+
+ if( minorVersion > -1 ) {
+ s.append( QString(".%1").arg(minorVersion) );
+ if( patchlevel > -1 )
+ s.append( QString(".%1").arg(patchlevel) );
+ }
+
+ if( !suffix.isNull() )
+ s.append( suffix );
+
+ return s;
+ }
+ else
+ return "";
+}
+
+
+int K3bVersion::compareSuffix( const QString& suffix1, const QString& suffix2 )
+{
+ static QRegExp rcRx( "rc(\\d+)" );
+ static QRegExp preRx( "pre(\\d+)" );
+ static QRegExp betaRx( "beta(\\d+)" );
+ static QRegExp alphaRx( "a(?:lpha)?(\\d+)" );
+
+ // first we check if one of the suffixes (or both are empty) becasue that case if simple
+ if( suffix1.isEmpty() ) {
+ if( suffix2.isEmpty() )
+ return 0;
+ else
+ return 1; // empty greater than the non-empty (should we treat something like 1.0a as greater than 1.0?)
+ }
+ else if( suffix2.isEmpty() )
+ return -1;
+
+ // now search for our special suffixes
+ if( rcRx.exactMatch( suffix1 ) ) {
+ int v1 = rcRx.cap(1).toInt();
+
+ if( rcRx.exactMatch( suffix2 ) ) {
+ int v2 = rcRx.cap(1).toInt();
+ return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
+ }
+ else if( preRx.exactMatch( suffix2 ) ||
+ betaRx.exactMatch( suffix2 ) ||
+ alphaRx.exactMatch( suffix2 ) )
+ return 1; // rc > than all the others
+ else
+ return QString::compare( suffix1, suffix2 );
+ }
+
+ else if( preRx.exactMatch( suffix1 ) ) {
+ int v1 = preRx.cap(1).toInt();
+
+ if( rcRx.exactMatch( suffix2 ) ) {
+ return -1; // pre is less than rc
+ }
+ else if( preRx.exactMatch( suffix2 ) ) {
+ int v2 = preRx.cap(1).toInt();
+ return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
+ }
+ else if( betaRx.exactMatch( suffix2 ) ||
+ alphaRx.exactMatch( suffix2 ) )
+ return 1; // pre is greater than beta or alpha
+ else
+ return QString::compare( suffix1, suffix2 );
+ }
+
+ else if( betaRx.exactMatch( suffix1 ) ) {
+ int v1 = betaRx.cap(1).toInt();
+
+ if( rcRx.exactMatch( suffix2 ) ||
+ preRx.exactMatch( suffix2 ) )
+ return -1; // beta is less than rc or pre
+ else if( betaRx.exactMatch( suffix2 ) ) {
+ int v2 = betaRx.cap(1).toInt();
+ return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
+ }
+ else if( alphaRx.exactMatch( suffix2 ) )
+ return 1; // beta is greater then alpha
+ else
+ return QString::compare( suffix1, suffix2 );
+ }
+
+ else if( alphaRx.exactMatch( suffix1 ) ) {
+ int v1 = alphaRx.cap(1).toInt();
+
+ if( rcRx.exactMatch( suffix2 ) ||
+ preRx.exactMatch( suffix2 ) ||
+ betaRx.exactMatch( suffix2 ) )
+ return -1; // alpha is less than all the others
+ else if( alphaRx.exactMatch( suffix2 ) ) {
+ int v2 = alphaRx.cap(1).toInt();
+ return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
+ }
+ else
+ return QString::compare( suffix1, suffix2 );
+ }
+
+ else
+ return QString::compare( suffix1, suffix2 );
+}
+
+
+bool operator<( const K3bVersion& v1, const K3bVersion& v2 )
+{
+ // both version objects need to be valid
+
+ if( v1.majorVersion() == v2.majorVersion() ) {
+
+ // 1 == 1.0
+ if( ( v1.minorVersion() == v2.minorVersion() )
+ ||
+ ( v1.minorVersion() == -1 && v2.minorVersion() == 0 )
+ ||
+ ( v2.minorVersion() == -1 && v1.minorVersion() == 0 )
+ )
+ {
+ // 1.0 == 1.0.0
+ if( ( v1.patchLevel() == v2.patchLevel() )
+ ||
+ ( v1.patchLevel() == -1 && v2.patchLevel() == 0 )
+ ||
+ ( v2.patchLevel() == -1 && v1.patchLevel() == 0 )
+ )
+ {
+ return K3bVersion::compareSuffix( v1.suffix(), v2.suffix() ) < 0;
+ }
+ else
+ return ( v1.patchLevel() < v2.patchLevel() );
+ }
+ else
+ return ( v1.minorVersion() < v2.minorVersion() );
+ }
+ else
+ return ( v1.majorVersion() < v2.majorVersion() );
+}
+
+bool operator>( const K3bVersion& v1, const K3bVersion& v2 )
+{
+ return operator<( v2, v1 );
+}
+
+
+bool operator==( const K3bVersion& v1, const K3bVersion& v2 )
+{
+ return ( v1.majorVersion() == v2.majorVersion() &&
+ v1.minorVersion() == v2.minorVersion() &&
+ v1.patchLevel() == v2.patchLevel() &&
+ K3bVersion::compareSuffix( v1.suffix(), v2.suffix() ) == 0 );
+}
+
+
+bool operator<=( const K3bVersion& v1, const K3bVersion& v2 )
+{
+ return ( operator<( v1, v2 ) || operator==( v1, v2 ) );
+}
+
+bool operator>=( const K3bVersion& v1, const K3bVersion& v2 )
+{
+ return ( operator>( v1, v2 ) || operator==( v1, v2 ) );
+}
diff --git a/libk3b/core/k3bversion.h b/libk3b/core/k3bversion.h
new file mode 100644
index 0000000..a6e3aee
--- /dev/null
+++ b/libk3b/core/k3bversion.h
@@ -0,0 +1,141 @@
+/*
+ *
+ * $Id: k3bversion.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_VERSION_H_
+#define _K3B_VERSION_H_
+
+#include <qstring.h>
+#include "k3b_export.h"
+/**
+ * \brief Representation of a version.
+ *
+ * K3bVersion represents a version consisting of a major version (accessible via majorVersion()),
+ * a minor version (accessible via minorVersion()), a patchLevel (accessible via patchLevel()),
+ * and a suffix (accessible via suffix()).
+ *
+ * The major version is mandatory while all other fields are optional (in case of the minor version
+ * and the patchlevel -1 means that the field is undefined).
+ *
+ * K3bVersion tries to treat version suffixes in an "intelligent" way to properly compare versions
+ * (see compareSuffix() for more details).
+ *
+ * K3bVersion may also be used everywhere a QString is needed as it automatically converts to a
+ * string representation using createVersionString().
+ */
+class LIBK3B_EXPORT K3bVersion
+{
+ public:
+ /**
+ * construct an empty version object
+ * which is invalid
+ * @ see isValid()
+ */
+ K3bVersion();
+
+ /**
+ * copy constructor
+ */
+ K3bVersion( const K3bVersion& );
+
+ /**
+ * this constructor tries to parse the given version string
+ */
+ K3bVersion( const QString& version );
+
+ /**
+ * sets the version and generates a version string from it
+ */
+ K3bVersion( int majorVersion, int minorVersion, int pachlevel = -1, const QString& suffix = QString::null );
+
+ /**
+ * tries to parse the version string
+ * used by the constructor
+ */
+ void setVersion( const QString& );
+
+ bool isValid() const;
+
+ /**
+ * sets the version and generates a version string from it
+ * used by the constructor
+ *
+ * If minorVersion or pachlevel are -1 they will not be used when generating the version string.
+ */
+ void setVersion( int majorVersion, int minorVersion = -1, int patchlevel = -1, const QString& suffix = QString::null );
+
+ const QString& versionString() const { return m_versionString; }
+ int majorVersion() const { return m_majorVersion; }
+ int minorVersion() const { return m_minorVersion; }
+ int patchLevel() const { return m_patchLevel; }
+ const QString& suffix() const { return m_suffix; }
+
+ /**
+ * just to make it possible to use as a QString
+ */
+ operator const QString& () const { return m_versionString; }
+ K3bVersion& operator=( const QString& v );
+
+ /**
+ * \return A new K3bVersion object which equals this one except that the suffix is empty.
+ */
+ K3bVersion simplify() const;
+
+ /**
+ * If minorVersion or pachlevel are -1 they will not be used when generating the version string.
+ * If minorVersion is -1 patchlevel will be ignored.
+ */
+ static QString createVersionString( int majorVersion,
+ int minorVersion = -1,
+ int patchlevel = -1,
+ const QString& suffix = QString::null );
+
+ /**
+ * "Intelligent" comparison of two version suffixes.
+ *
+ * This method checks for the following types of suffixes and treats them in the
+ * following order:
+ *
+ * [empty prefix] > rcX > preX > betaX > alphaX = aX (where X is a number)
+ *
+ * Every other suffixes are compared alphanumerical.
+ * An empty prefix is always considered newer than an unknown non-emtpy suffix (e.g. not one of the above.)
+ *
+ * @return \li -1 if suffix1 is less than suffix2
+ * \li 0 if suffix1 equals suffix2 (be aware that this is not the same as comparing to strings as
+ * alphaX equals aX in this case.)
+ * \li 1 if suffix1 is greater than suffix2
+ */
+ static int compareSuffix( const QString& suffix1, const QString& suffix2 );
+
+ private:
+ static void splitVersionString( const QString& s, int& num, QString& suffix );
+
+ QString m_versionString;
+ int m_majorVersion;
+ int m_minorVersion;
+ int m_patchLevel;
+ QString m_suffix;
+};
+
+
+LIBK3B_EXPORT bool operator<( const K3bVersion& v1, const K3bVersion& v2 );
+LIBK3B_EXPORT bool operator>( const K3bVersion& v1, const K3bVersion& v2 );
+LIBK3B_EXPORT bool operator==( const K3bVersion& v1, const K3bVersion& v2 );
+LIBK3B_EXPORT bool operator<=( const K3bVersion& v1, const K3bVersion& v2 );
+LIBK3B_EXPORT bool operator>=( const K3bVersion& v1, const K3bVersion& v2 );
+
+
+#endif
diff --git a/libk3b/dummy.cpp b/libk3b/dummy.cpp
new file mode 100644
index 0000000..9f6c7b2
--- /dev/null
+++ b/libk3b/dummy.cpp
@@ -0,0 +1 @@
+/* dummy file to have anything around.*/
diff --git a/libk3b/jobs/Makefile.am b/libk3b/jobs/Makefile.am
new file mode 100644
index 0000000..72a9eac
--- /dev/null
+++ b/libk3b/jobs/Makefile.am
@@ -0,0 +1,43 @@
+AM_CPPFLAGS = -I$(srcdir)/../core \
+ -I$(srcdir)/../../libk3bdevice \
+ -I$(srcdir)/../../src \
+ -I$(srcdir)/../tools \
+ -I$(srcdir)/../cddb \
+ -I$(srcdir)/../plugin \
+ -I$(srcdir)/../projects \
+ -I$(srcdir)/../videodvd \
+ -I$(srcdir)/../projects/audiocd \
+ $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libjobs.la
+
+if include_videodvdrip
+libjobs_la_SOURCES = k3bdatatrackreader.cpp k3breadcdreader.cpp \
+ k3bcdcopyjob.cpp k3bclonejob.cpp k3baudiosessionreadingjob.cpp \
+ k3bdvdcopyjob.cpp k3bvideodvdtitletranscodingjob.cpp k3bvideodvdtitledetectclippingjob.cpp \
+ k3baudiocuefilewritingjob.cpp k3bbinimagewritingjob.cpp \
+ k3biso9660imagewritingjob.cpp \
+ k3bdvdformattingjob.cpp k3bblankingjob.cpp k3bclonetocreader.cpp \
+ k3bverificationjob.cpp
+
+include_HEADERS = k3bcdcopyjob.h k3bdvdcopyjob.h k3bclonejob.h \
+ k3baudiocuefilewritingjob.h k3bbinimagewritingjob.h \
+ k3biso9660imagewritingjob.h k3bdvdformattingjob.h \
+ k3bblankingjob.h k3bvideodvdtitletranscodingjob.h k3bvideodvdtitledetectclippingjob.h \
+ k3bverificationjob.h
+else
+libjobs_la_SOURCES = k3bdatatrackreader.cpp k3breadcdreader.cpp \
+ k3bcdcopyjob.cpp k3bclonejob.cpp k3baudiosessionreadingjob.cpp \
+ k3bdvdcopyjob.cpp \
+ k3baudiocuefilewritingjob.cpp k3bbinimagewritingjob.cpp \
+ k3biso9660imagewritingjob.cpp \
+ k3bdvdformattingjob.cpp k3bblankingjob.cpp k3bclonetocreader.cpp \
+ k3bverificationjob.cpp
+
+include_HEADERS = k3bcdcopyjob.h k3bdvdcopyjob.h k3bclonejob.h \
+ k3baudiocuefilewritingjob.h k3bbinimagewritingjob.h \
+ k3biso9660imagewritingjob.h k3bdvdformattingjob.h \
+ k3bblankingjob.h k3bverificationjob.h
+endif
diff --git a/libk3b/jobs/k3baudiocuefilewritingjob.cpp b/libk3b/jobs/k3baudiocuefilewritingjob.cpp
new file mode 100644
index 0000000..0c5cd9a
--- /dev/null
+++ b/libk3b/jobs/k3baudiocuefilewritingjob.cpp
@@ -0,0 +1,272 @@
+/*
+ *
+ * $Id: k3baudiocuefilewritingjob.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3baudiocuefilewritingjob.h"
+
+#include <k3baudiodoc.h>
+#include <k3baudiojob.h>
+#include <k3bdevice.h>
+#include <k3baudiodecoder.h>
+#include <k3baudiotrack.h>
+#include <k3baudiofile.h>
+#include <k3bcuefileparser.h>
+#include <k3bthread.h>
+#include <k3bthreadjob.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+
+class K3bAudioCueFileWritingJob::AnalyserThread : public K3bThread
+{
+public:
+ AnalyserThread()
+ : K3bThread() {
+ }
+
+ void setDecoder( K3bAudioDecoder* dec ) { m_decoder = dec; }
+
+protected:
+ void run() {
+ emitStarted();
+ m_decoder->analyseFile();
+ emitFinished(true);
+ }
+
+private:
+ K3bAudioDecoder* m_decoder;
+};
+
+
+K3bAudioCueFileWritingJob::K3bAudioCueFileWritingJob( K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bBurnJob( jh, parent, name ),
+ m_decoder(0)
+{
+ m_analyserThread = new AnalyserThread();
+ m_analyserJob = new K3bThreadJob( m_analyserThread, this, this );
+ connect( m_analyserJob, SIGNAL(finished(bool)), this, SLOT(slotAnalyserThreadFinished(bool)) );
+
+ m_audioDoc = new K3bAudioDoc( this );
+ m_audioDoc->newDocument();
+ m_audioJob = new K3bAudioJob( m_audioDoc, this, this );
+
+ // just loop all through
+ connect( m_audioJob, SIGNAL(newTask(const QString&)), this, SIGNAL(newTask(const QString&)) );
+ connect( m_audioJob, SIGNAL(newSubTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( m_audioJob, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+ connect( m_audioJob, SIGNAL(infoMessage(const QString&, int)),
+ this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_audioJob, SIGNAL(finished(bool)), this, SIGNAL(finished(bool)) );
+ connect( m_audioJob, SIGNAL(canceled()), this, SIGNAL(canceled()) );
+ connect( m_audioJob, SIGNAL(percent(int)), this, SIGNAL(percent(int)) );
+ connect( m_audioJob, SIGNAL(subPercent(int)), this, SIGNAL(subPercent(int)) );
+ connect( m_audioJob, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSubSize(int, int)) );
+ connect( m_audioJob, SIGNAL(processedSubSize(int, int)), this, SIGNAL(processedSubSize(int, int)) );
+ connect( m_audioJob, SIGNAL(burning(bool)), this, SIGNAL(burning(bool)) );
+ connect( m_audioJob, SIGNAL(bufferStatus(int)), this, SIGNAL(bufferStatus(int)) );
+ connect( m_audioJob, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
+ connect( m_audioJob, SIGNAL(writeSpeed(int, int)), this, SIGNAL(writeSpeed(int, int)) );
+
+ m_canceled = false;
+ m_audioJobRunning = false;
+}
+
+
+K3bAudioCueFileWritingJob::~K3bAudioCueFileWritingJob()
+{
+ // the threadjob does not delete the thread
+ delete m_analyserThread;
+}
+
+
+K3bDevice::Device* K3bAudioCueFileWritingJob::writer() const
+{
+ return m_audioDoc->burner();
+}
+
+
+QString K3bAudioCueFileWritingJob::jobDescription() const
+{
+ return i18n("Writing Audio Cue File");
+}
+
+
+QString K3bAudioCueFileWritingJob::jobDetails() const
+{
+ return m_cueFile.section( '/', -1 );
+}
+
+
+void K3bAudioCueFileWritingJob::start()
+{
+ // FIXME: here we trust that a job won't be started twice :(
+ jobStarted();
+ m_canceled = false;
+ m_audioJobRunning = false;
+ importCueInProject();
+}
+
+
+void K3bAudioCueFileWritingJob::cancel()
+{
+ m_canceled = true;
+
+ // the AudioJob cancel method is very stupid. It emits the canceled signal even if it was never running :(
+ if( m_audioJobRunning )
+ m_audioJob->cancel();
+ m_analyserJob->cancel();
+}
+
+
+void K3bAudioCueFileWritingJob::setCueFile( const QString& s )
+{
+ m_cueFile = s;
+}
+
+
+void K3bAudioCueFileWritingJob::setOnTheFly( bool b )
+{
+ m_audioDoc->setOnTheFly( b );
+}
+
+
+void K3bAudioCueFileWritingJob::setSpeed( int s )
+{
+ m_audioDoc->setSpeed( s );
+}
+
+
+void K3bAudioCueFileWritingJob::setBurnDevice( K3bDevice::Device* dev )
+{
+ m_audioDoc->setBurner( dev );
+}
+
+
+void K3bAudioCueFileWritingJob::setWritingMode( int mode )
+{
+ m_audioDoc->setWritingMode( mode );
+}
+
+
+void K3bAudioCueFileWritingJob::setSimulate( bool b )
+{
+ m_audioDoc->setDummy( b );
+}
+
+
+void K3bAudioCueFileWritingJob::setCopies( int c )
+{
+ m_audioDoc->setCopies( c );
+}
+
+
+void K3bAudioCueFileWritingJob::setTempDir( const QString& s )
+{
+ m_audioDoc->setTempDir( s );
+}
+
+
+void K3bAudioCueFileWritingJob::slotAnalyserThreadFinished( bool )
+{
+ if( !m_canceled ) {
+ if( m_audioDoc->lastTrack()->length() == 0 ) {
+ emit infoMessage( i18n("Analysing the audio file failed. Corrupt file?"), ERROR );
+ jobFinished(false);
+ }
+ else {
+ // FIXME: m_audioJobRunning is never reset
+ m_audioJobRunning = true;
+ m_audioJob->start(); // from here on the audio job takes over completely
+ }
+ }
+ else {
+ emit canceled();
+ jobFinished(false);
+ }
+}
+
+
+void K3bAudioCueFileWritingJob::importCueInProject()
+{
+ // cleanup the project (this wil also delete the decoder)
+ // we do not use newDocument as that would overwrite the settings already made
+ while( m_audioDoc->firstTrack() )
+ delete m_audioDoc->firstTrack()->take();
+
+ m_decoder = 0;
+
+ K3bCueFileParser parser( m_cueFile );
+ if( parser.isValid() && parser.toc().contentType() == K3bDevice::AUDIO ) {
+
+ kdDebug() << "(K3bAudioCueFileWritingJob::importCueFile) parsed with image: " << parser.imageFilename() << endl;
+
+ // global cd-text
+ m_audioDoc->setTitle( parser.cdText().title() );
+ m_audioDoc->setPerformer( parser.cdText().performer() );
+ m_audioDoc->writeCdText( !parser.cdText().title().isEmpty() );
+
+ m_decoder = K3bAudioDecoderFactory::createDecoder( parser.imageFilename() );
+ if( m_decoder ) {
+ m_decoder->setFilename( parser.imageFilename() );
+
+ K3bAudioTrack* after = 0;
+ K3bAudioFile* newFile = 0;
+ unsigned int i = 0;
+ for( K3bDevice::Toc::const_iterator it = parser.toc().begin();
+ it != parser.toc().end(); ++it ) {
+ const K3bDevice::Track& track = *it;
+
+ newFile = new K3bAudioFile( m_decoder, m_audioDoc );
+ newFile->setStartOffset( track.firstSector() );
+ newFile->setEndOffset( track.lastSector()+1 );
+
+ K3bAudioTrack* newTrack = new K3bAudioTrack( m_audioDoc );
+ newTrack->addSource( newFile );
+ newTrack->moveAfter( after );
+
+ // cd-text
+ newTrack->setTitle( parser.cdText()[i].title() );
+ newTrack->setPerformer( parser.cdText()[i].performer() );
+
+ // add the next track after this one
+ after = newTrack;
+ ++i;
+ }
+
+ // let the last source use the data up to the end of the file
+ if( newFile )
+ newFile->setEndOffset(0);
+
+ // now analyze the source
+ emit newTask( i18n("Analysing the audio file") );
+ emit newSubTask( i18n("Analysing %1").arg( parser.imageFilename() ) );
+
+ // start the analyser thread
+ m_analyserThread->setDecoder( m_decoder );
+ m_analyserJob->start();
+ }
+ else {
+ emit infoMessage( i18n("Unable to handle '%1' due to an unsupported format.").arg( m_cueFile ), ERROR );
+ jobFinished(false);
+ }
+ }
+ else {
+ emit infoMessage( i18n("No valid audio cue file: '%1'").arg( m_cueFile ), ERROR );
+ jobFinished(false);
+ }
+}
+
+#include "k3baudiocuefilewritingjob.moc"
diff --git a/libk3b/jobs/k3baudiocuefilewritingjob.h b/libk3b/jobs/k3baudiocuefilewritingjob.h
new file mode 100644
index 0000000..6e0a3c2
--- /dev/null
+++ b/libk3b/jobs/k3baudiocuefilewritingjob.h
@@ -0,0 +1,79 @@
+/*
+ *
+ * $Id: k3baudiocuefilewritingjob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_AUDIO_CUE_FILEWRITING_JOB_H_
+#define _K3B_AUDIO_CUE_FILEWRITING_JOB_H_
+
+#include <k3bjob.h>
+#include "k3b_export.h"
+class K3bAudioDoc;
+class K3bAudioJob;
+class K3bAudioDecoder;
+class K3bThreadJob;
+namespace K3bDevice {
+ class Device;
+}
+
+
+class LIBK3B_EXPORT K3bAudioCueFileWritingJob : public K3bBurnJob
+{
+ Q_OBJECT
+
+ public:
+ K3bAudioCueFileWritingJob( K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ ~K3bAudioCueFileWritingJob();
+
+ K3bDevice::Device* writer() const;
+
+ QString jobDescription() const;
+ QString jobDetails() const;
+
+ const QString& cueFile() const { return m_cueFile; }
+
+ public slots:
+ void start();
+ void cancel();
+
+ void setCueFile( const QString& );
+ void setSpeed( int s );
+ void setBurnDevice( K3bDevice::Device* dev );
+ void setWritingMode( int mode );
+ void setSimulate( bool b );
+ void setCopies( int c );
+ void setOnTheFly( bool b );
+ void setTempDir( const QString& );
+
+ private slots:
+ void slotAnalyserThreadFinished(bool);
+
+ private:
+ void importCueInProject();
+
+ K3bDevice::Device* m_device;
+
+ QString m_cueFile;
+ K3bAudioDoc* m_audioDoc;
+ K3bAudioJob* m_audioJob;
+ K3bAudioDecoder* m_decoder;
+
+ bool m_canceled;
+ bool m_audioJobRunning;
+
+ class AnalyserThread;
+ AnalyserThread* m_analyserThread;
+ K3bThreadJob* m_analyserJob;
+};
+
+#endif
diff --git a/libk3b/jobs/k3baudiosessionreadingjob.cpp b/libk3b/jobs/k3baudiosessionreadingjob.cpp
new file mode 100644
index 0000000..f4ac550
--- /dev/null
+++ b/libk3b/jobs/k3baudiosessionreadingjob.cpp
@@ -0,0 +1,278 @@
+/*
+ *
+ * $Id: k3baudiosessionreadingjob.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3baudiosessionreadingjob.h"
+
+#include <k3bthread.h>
+#include <k3btoc.h>
+#include <k3bcdparanoialib.h>
+#include <k3bwavefilewriter.h>
+#include <k3bglobals.h>
+#include <k3bdevice.h>
+#include <k3bcore.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <unistd.h>
+
+
+class K3bAudioSessionReadingJob::WorkThread : public K3bThread
+{
+public:
+ WorkThread();
+ ~WorkThread();
+
+ void init();
+ void run();
+ void cancel();
+
+ bool canceled;
+
+ int fd;
+ K3bCdparanoiaLib* paranoia;
+ K3bDevice::Device* device;
+ K3bDevice::Toc toc;
+ K3bWaveFileWriter* waveFileWriter;
+ QStringList filenames;
+ int paranoiaMode;
+ int retries;
+ bool neverSkip;
+};
+
+
+K3bAudioSessionReadingJob::WorkThread::WorkThread()
+ : K3bThread(),
+ fd(-1),
+ paranoia(0),
+ waveFileWriter(0),
+ paranoiaMode(0),
+ retries(50),
+ neverSkip(false)
+{
+}
+
+
+K3bAudioSessionReadingJob::WorkThread::~WorkThread()
+{
+ delete waveFileWriter;
+ delete paranoia;
+}
+
+
+void K3bAudioSessionReadingJob::WorkThread::init()
+{
+ canceled = false;
+}
+
+
+void K3bAudioSessionReadingJob::WorkThread::run()
+{
+ if( !paranoia )
+ paranoia = K3bCdparanoiaLib::create();
+
+ if( !paranoia ) {
+ emitInfoMessage( i18n("Could not load libcdparanoia."), K3bJob::ERROR );
+ emitFinished(false);
+ return;
+ }
+
+ if( toc.isEmpty() )
+ toc = device->readToc();
+
+ if( !paranoia->initParanoia( device, toc ) ) {
+ emitInfoMessage( i18n("Could not open device %1").arg(device->blockDeviceName()),
+ K3bJob::ERROR );
+ emitFinished(false);
+ return;
+ }
+
+ if( !paranoia->initReading() ) {
+ emitInfoMessage( i18n("Error while initializing audio ripping."), K3bJob::ERROR );
+ emitFinished(false);
+ return;
+ }
+
+ device->block( true );
+
+ // init settings
+ paranoia->setMaxRetries( retries );
+ paranoia->setParanoiaMode( paranoiaMode );
+ paranoia->setNeverSkip( neverSkip );
+
+ bool writeError = false;
+ unsigned int trackNum = 1;
+ unsigned int currentTrack = 0;
+ unsigned long trackRead = 0;
+ unsigned long totalRead = 0;
+ unsigned int lastTrackPercent = 0;
+ unsigned int lastTotalPercent = 0;
+ bool newTrack = true;
+ int status = 0;
+ char* buffer = 0;
+ while( !canceled && (buffer = paranoia->read( &status, &trackNum, fd == -1 /*when writing to a wav be want little endian */ )) ) {
+
+ if( currentTrack != trackNum ) {
+ emitNextTrack( trackNum, paranoia->toc().count() );
+ trackRead = 0;
+ lastTrackPercent = 0;
+
+ currentTrack = trackNum;
+ newTrack = true;
+ }
+
+ if( fd > 0 ) {
+ if( ::write( fd, buffer, CD_FRAMESIZE_RAW ) != CD_FRAMESIZE_RAW ) {
+ kdDebug() << "(K3bAudioSessionCopyJob::WorkThread) error while writing to fd " << fd << endl;
+ writeError = true;
+ break;
+ }
+ }
+ else {
+ if( newTrack ) {
+ newTrack = false;
+
+ if( !waveFileWriter )
+ waveFileWriter = new K3bWaveFileWriter();
+
+ if( filenames.count() < currentTrack ) {
+ kdDebug() << "(K3bAudioSessionCopyJob) not enough image filenames given: " << currentTrack << endl;
+ writeError = true;
+ break;
+ }
+
+ if( !waveFileWriter->open( filenames[currentTrack-1] ) ) {
+ emitInfoMessage( i18n("Unable to open '%1' for writing.").arg(filenames[currentTrack-1]), K3bJob::ERROR );
+ writeError = true;
+ break;
+ }
+ }
+
+ waveFileWriter->write( buffer,
+ CD_FRAMESIZE_RAW,
+ K3bWaveFileWriter::LittleEndian );
+ }
+
+ trackRead++;
+ totalRead++;
+
+ unsigned int trackPercent = 100 * trackRead / toc[currentTrack-1].length().lba();
+ if( trackPercent > lastTrackPercent ) {
+ lastTrackPercent = trackPercent;
+ emitSubPercent( lastTrackPercent );
+ }
+ unsigned int totalPercent = 100 * totalRead / paranoia->rippedDataLength();
+ if( totalPercent > lastTotalPercent ) {
+ lastTotalPercent = totalPercent;
+ emitPercent( lastTotalPercent );
+ }
+ }
+
+ if( waveFileWriter )
+ waveFileWriter->close();
+
+ paranoia->close();
+
+ device->block( false );
+
+ if( status != K3bCdparanoiaLib::S_OK ) {
+ emitInfoMessage( i18n("Unrecoverable error while ripping track %1.").arg(trackNum), K3bJob::ERROR );
+ emitFinished(false);
+ return;
+ }
+
+ emitFinished( !writeError & !canceled );
+}
+
+
+void K3bAudioSessionReadingJob::WorkThread::cancel()
+{
+ canceled = true;
+ // FIXME: add backup killing like in the audio ripping and make sure to close paranoia
+}
+
+
+
+
+K3bAudioSessionReadingJob::K3bAudioSessionReadingJob( K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bThreadJob( jh, parent, name )
+{
+ m_thread = new WorkThread();
+ setThread( m_thread );
+}
+
+
+K3bAudioSessionReadingJob::~K3bAudioSessionReadingJob()
+{
+ delete m_thread;
+}
+
+
+void K3bAudioSessionReadingJob::setDevice( K3bDevice::Device* dev )
+{
+ m_thread->device = dev;
+ m_thread->toc = K3bDevice::Toc();
+}
+
+
+void K3bAudioSessionReadingJob::setToc( const K3bDevice::Toc& toc )
+{
+ m_thread->toc = toc;
+}
+
+
+void K3bAudioSessionReadingJob::writeToFd( int fd )
+{
+ m_thread->fd = fd;
+}
+
+void K3bAudioSessionReadingJob::setImageNames( const QStringList& l )
+{
+ m_thread->filenames = l;
+ m_thread->fd = -1;
+}
+
+
+void K3bAudioSessionReadingJob::setParanoiaMode( int m )
+{
+ m_thread->paranoiaMode = m;
+}
+
+
+void K3bAudioSessionReadingJob::setReadRetries( int r )
+{
+ m_thread->retries = r;
+}
+
+void K3bAudioSessionReadingJob::setNeverSkip( bool b )
+{
+ m_thread->neverSkip = b;
+}
+
+
+void K3bAudioSessionReadingJob::start()
+{
+ k3bcore->blockDevice( m_thread->device );
+ K3bThreadJob::start();
+}
+
+
+void K3bAudioSessionReadingJob::cleanupJob( bool success )
+{
+ Q_UNUSED( success );
+ k3bcore->unblockDevice( m_thread->device );
+}
+
+#include "k3baudiosessionreadingjob.moc"
diff --git a/libk3b/jobs/k3baudiosessionreadingjob.h b/libk3b/jobs/k3baudiosessionreadingjob.h
new file mode 100644
index 0000000..21f3d50
--- /dev/null
+++ b/libk3b/jobs/k3baudiosessionreadingjob.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * $Id: k3baudiosessionreadingjob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_AUDIOSESSION_READING_JOB_H_
+#define _K3B_AUDIOSESSION_READING_JOB_H_
+
+#include <k3bthreadjob.h>
+
+#include <qstringlist.h>
+
+
+namespace K3bDevice {
+ class Device;
+ class Toc;
+}
+
+
+class K3bAudioSessionReadingJob : public K3bThreadJob
+{
+ Q_OBJECT
+
+ public:
+ K3bAudioSessionReadingJob( K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ ~K3bAudioSessionReadingJob();
+
+ /**
+ * For now this simply reads all the audio tracks at the beginning
+ * since we only support CD-Extra mixed mode cds.
+ */
+ void setDevice( K3bDevice::Device* );
+
+ /**
+ * Use for faster initialization
+ */
+ void setToc( const K3bDevice::Toc& toc );
+
+ /**
+ * the data gets written directly into fd instead of imagefiles.
+ * To disable just set fd to -1 (the default)
+ */
+ void writeToFd( int fd );
+
+ /**
+ * Used if fd == -1
+ */
+ void setImageNames( const QStringList& l );
+
+ void setParanoiaMode( int m );
+ void setReadRetries( int );
+ void setNeverSkip( bool b );
+
+ public slots:
+ void start();
+
+ protected:
+ void cleanupJob( bool success );
+
+ private:
+ class WorkThread;
+ WorkThread* m_thread;
+};
+
+#endif
diff --git a/libk3b/jobs/k3bbinimagewritingjob.cpp b/libk3b/jobs/k3bbinimagewritingjob.cpp
new file mode 100644
index 0000000..de76e3f
--- /dev/null
+++ b/libk3b/jobs/k3bbinimagewritingjob.cpp
@@ -0,0 +1,234 @@
+/*
+ *
+ * $Id: k3bbinimagewritingjob.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Klaus-Dieter Krannich <kd@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bbinimagewritingjob.h"
+#include <k3bcdrecordwriter.h>
+#include <k3bcdrdaowriter.h>
+#include <k3bcore.h>
+#include <k3bdevice.h>
+#include <k3bglobals.h>
+#include <k3bexternalbinmanager.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qfile.h>
+#include <qtextstream.h>
+
+
+
+K3bBinImageWritingJob::K3bBinImageWritingJob( K3bJobHandler* hdl, QObject* parent )
+ : K3bBurnJob( hdl, parent ),
+ m_device(0),
+ m_simulate(false),
+ m_force(false),
+ m_noFix(false),
+ m_tocFile(0),
+ m_speed(2),
+ m_copies(1),
+ m_writer(0)
+{
+}
+
+K3bBinImageWritingJob::~K3bBinImageWritingJob()
+{
+}
+
+void K3bBinImageWritingJob::start()
+{
+ m_canceled = false;
+
+ if( m_copies < 1 )
+ m_copies = 1;
+ m_finishedCopies = 0;
+
+ jobStarted();
+ emit newTask( i18n("Write Binary Image") );
+
+ if( prepareWriter() )
+ writerStart();
+ else
+ cancel();
+
+}
+
+void K3bBinImageWritingJob::cancel()
+{
+ m_canceled = true;
+ m_writer->cancel();
+ emit canceled();
+ jobFinished( false );
+}
+
+bool K3bBinImageWritingJob::prepareWriter()
+{
+ if( m_writer )
+ delete m_writer;
+
+ int usedWritingApp = writingApp();
+ const K3bExternalBin* cdrecordBin = k3bcore->externalBinManager()->binObject("cdrecord");
+ if( usedWritingApp == K3b::CDRECORD ||
+ ( usedWritingApp == K3b::DEFAULT && cdrecordBin && cdrecordBin->hasFeature("cuefile") && m_device->dao() ) ) {
+ usedWritingApp = K3b::CDRECORD;
+
+ // IMPROVEME: check if it's a cdrdao toc-file
+ if( m_tocFile.right(4) == ".toc" ) {
+ kdDebug() << "(K3bBinImageWritingJob) imagefile has ending toc." << endl;
+ usedWritingApp = K3b::CDRDAO;
+ }
+ else {
+ // TODO: put this into K3bCueFileParser
+ // TODO: check K3bCueFileParser::imageFilenameInCue()
+ // let's see if cdrecord can handle the cue file
+ QFile f( m_tocFile );
+ if( f.open( IO_ReadOnly ) ) {
+ QTextStream fStr( &f );
+ if( fStr.read().contains( "MODE1/2352" ) ) {
+ kdDebug() << "(K3bBinImageWritingJob) cuefile contains MODE1/2352 track. using cdrdao." << endl;
+ usedWritingApp = K3b::CDRDAO;
+ }
+ f.close();
+ }
+ else
+ kdDebug() << "(K3bBinImageWritingJob) could not open file " << m_tocFile << endl;
+ }
+ }
+ else
+ usedWritingApp = K3b::CDRDAO;
+
+ if( usedWritingApp == K3b::CDRECORD ) {
+ // create cdrecord job
+ K3bCdrecordWriter* writer = new K3bCdrecordWriter( m_device, this );
+
+ writer->setDao( true );
+ writer->setSimulate( m_simulate );
+ writer->setBurnSpeed( m_speed );
+ writer->setCueFile ( m_tocFile );
+
+ if( m_noFix ) {
+ writer->addArgument("-multi");
+ }
+
+ if( m_force ) {
+ writer->addArgument("-force");
+ }
+
+ m_writer = writer;
+ }
+ else {
+ // create cdrdao job
+ K3bCdrdaoWriter* writer = new K3bCdrdaoWriter( m_device, this );
+ writer->setCommand( K3bCdrdaoWriter::WRITE );
+ writer->setSimulate( m_simulate );
+ writer->setBurnSpeed( m_speed );
+ writer->setForce( m_force );
+
+ // multisession
+ writer->setMulti( m_noFix );
+
+ writer->setTocFile( m_tocFile );
+
+ m_writer = writer;
+ }
+
+ connect( m_writer, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_writer, SIGNAL(percent(int)), this, SLOT(copyPercent(int)) );
+ connect( m_writer, SIGNAL(subPercent(int)), this, SLOT(copySubPercent(int)) );
+ connect( m_writer, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSize(int, int)) );
+ connect( m_writer, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) );
+ connect( m_writer, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
+ connect( m_writer, SIGNAL(writeSpeed(int, int)), this, SIGNAL(writeSpeed(int, int)) );
+ connect( m_writer, SIGNAL(finished(bool)), this, SLOT(writerFinished(bool)) );
+ connect( m_writer, SIGNAL(newTask(const QString&)), this, SIGNAL(newTask(const QString&)) );
+ connect( m_writer, SIGNAL(newSubTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( m_writer, SIGNAL(nextTrack(int, int)), this, SLOT(slotNextTrack(int, int)) );
+ connect( m_writer, SIGNAL(debuggingOutput(const QString&, const QString&)), this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+
+ return true;
+}
+
+
+void K3bBinImageWritingJob::writerStart()
+{
+
+ if( waitForMedia( m_device ) < 0 ) {
+ cancel();
+ }
+ // just to be sure we did not get canceled during the async discWaiting
+ else if( !m_canceled ) {
+ emit burning(true);
+ m_writer->start();
+ }
+}
+
+void K3bBinImageWritingJob::copyPercent(int p)
+{
+ emit percent( (100*m_finishedCopies + p)/m_copies );
+}
+
+void K3bBinImageWritingJob::copySubPercent(int p)
+{
+ emit subPercent(p);
+}
+
+void K3bBinImageWritingJob::writerFinished(bool ok)
+{
+ if( m_canceled )
+ return;
+
+ if (ok) {
+ m_finishedCopies++;
+ if ( m_finishedCopies == m_copies ) {
+ emit infoMessage( i18n("%n copy successfully created", "%n copies successfully created", m_copies),K3bJob::INFO );
+ jobFinished( true );
+ }
+ else {
+ writerStart();
+ }
+ }
+ else {
+ jobFinished(false);
+ }
+}
+
+
+void K3bBinImageWritingJob::slotNextTrack( int t, int tt )
+{
+ emit newSubTask( i18n("Writing track %1 of %2").arg(t).arg(tt) );
+}
+
+
+QString K3bBinImageWritingJob::jobDescription() const
+{
+ return ( i18n("Writing cue/bin Image")
+ + ( m_copies > 1
+ ? i18n(" - %n Copy", " - %n Copies", m_copies)
+ : QString::null ) );
+}
+
+
+QString K3bBinImageWritingJob::jobDetails() const
+{
+ return m_tocFile.section("/", -1);
+}
+
+
+void K3bBinImageWritingJob::setTocFile(const QString& s)
+{
+ m_tocFile = s;
+}
+
+#include "k3bbinimagewritingjob.moc"
diff --git a/libk3b/jobs/k3bbinimagewritingjob.h b/libk3b/jobs/k3bbinimagewritingjob.h
new file mode 100644
index 0000000..3666793
--- /dev/null
+++ b/libk3b/jobs/k3bbinimagewritingjob.h
@@ -0,0 +1,79 @@
+/*
+ *
+ * $Id$
+ * Copyright (C) 2003 Klaus-Dieter Krannich <kd@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BBINIMAGEWRITINGJOB_H
+#define K3BBINIMAGEWRITINGJOB_H
+
+#include <k3bjob.h>
+#include "k3b_export.h"
+class K3bAbstractWriter;
+namespace K3bDevice {
+ class Device;
+}
+
+/**
+ *@author Klaus-Dieter Krannich
+ */
+class LIBK3B_EXPORT K3bBinImageWritingJob : public K3bBurnJob
+{
+ Q_OBJECT
+
+ public:
+ K3bBinImageWritingJob( K3bJobHandler*, QObject* parent = 0 );
+ ~K3bBinImageWritingJob();
+
+ K3bDevice::Device* writer() const { return m_device; };
+
+ QString jobDescription() const;
+ QString jobDetails() const;
+
+ public slots:
+ void start();
+ void cancel();
+
+ void setWriter( K3bDevice::Device* dev ) { m_device = dev; }
+ void setSimulate( bool b ) { m_simulate = b; }
+ void setForce(bool b) { m_force = b; }
+ void setMulti( bool b ) { m_noFix = b; }
+ void setTocFile( const QString& s);
+ void setCopies(int c) { m_copies = c; }
+ void setSpeed( int s ) { m_speed = s; }
+
+ private slots:
+ void writerFinished(bool);
+ void copyPercent(int p);
+ void copySubPercent(int p);
+ void slotNextTrack( int, int );
+
+ private:
+ void writerStart();
+ bool prepareWriter();
+
+ K3bDevice::Device* m_device;
+ bool m_simulate;
+ bool m_force;
+ bool m_noFix;
+ QString m_tocFile;
+ int m_speed;
+ int m_copies;
+ int m_finishedCopies;
+
+ bool m_canceled;
+
+ K3bAbstractWriter* m_writer;
+};
+
+#endif
diff --git a/libk3b/jobs/k3bblankingjob.cpp b/libk3b/jobs/k3bblankingjob.cpp
new file mode 100644
index 0000000..c11f4b4
--- /dev/null
+++ b/libk3b/jobs/k3bblankingjob.cpp
@@ -0,0 +1,176 @@
+/*
+ *
+ * $Id: k3bblankingjob.cpp 630823 2007-02-06 14:07:10Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bblankingjob.h"
+#include "k3bcdrecordwriter.h"
+#include "k3bcdrdaowriter.h"
+
+#include <k3bglobals.h>
+#include <k3bdevice.h>
+#include <k3bdevicehandler.h>
+
+#include <kconfig.h>
+#include <klocale.h>
+#include <kio/global.h>
+#include <kio/job.h>
+#include <kdebug.h>
+
+#include <qstring.h>
+
+
+
+K3bBlankingJob::K3bBlankingJob( K3bJobHandler* hdl, QObject* parent )
+ : K3bBurnJob( hdl, parent ),
+ m_writerJob(0),
+ m_force(true),
+ m_device(0),
+ m_speed(0),
+ m_mode(Fast),
+ m_writingApp(K3b::DEFAULT),
+ m_canceled(false),
+ m_forceNoEject(false)
+{
+}
+
+
+K3bBlankingJob::~K3bBlankingJob()
+{
+ delete m_writerJob;
+}
+
+
+K3bDevice::Device* K3bBlankingJob::writer() const
+{
+ return m_device;
+}
+
+
+void K3bBlankingJob::setDevice( K3bDevice::Device* dev )
+{
+ m_device = dev;
+}
+
+
+void K3bBlankingJob::start()
+{
+ if( m_device == 0 )
+ return;
+
+ jobStarted();
+
+ slotStartErasing();
+}
+
+void K3bBlankingJob::slotStartErasing()
+{
+ m_canceled = false;
+
+ if( m_writerJob )
+ delete m_writerJob;
+
+ if( m_writingApp == K3b::CDRDAO ) {
+ K3bCdrdaoWriter* writer = new K3bCdrdaoWriter( m_device, this );
+ m_writerJob = writer;
+
+ writer->setCommand(K3bCdrdaoWriter::BLANK);
+ writer->setBlankMode( m_mode == Fast ? K3bCdrdaoWriter::MINIMAL : K3bCdrdaoWriter::FULL );
+ writer->setForce(m_force);
+ writer->setBurnSpeed(m_speed);
+ writer->setForceNoEject( m_forceNoEject );
+ }
+ else {
+ K3bCdrecordWriter* writer = new K3bCdrecordWriter( m_device, this );
+ m_writerJob = writer;
+
+ QString mode;
+ switch( m_mode ) {
+ case Fast:
+ mode = "fast";
+ break;
+ case Complete:
+ mode = "all";
+ break;
+ case Track:
+ mode = "track";
+ break;
+ case Unclose:
+ mode = "unclose";
+ break;
+ case Session:
+ mode = "session";
+ break;
+ }
+
+ writer->addArgument("blank="+ mode);
+
+ if (m_force)
+ writer->addArgument("-force");
+ writer->setBurnSpeed(m_speed);
+ writer->setForceNoEject( m_forceNoEject );
+ }
+
+ connect(m_writerJob, SIGNAL(finished(bool)), this, SLOT(slotFinished(bool)));
+ connect(m_writerJob, SIGNAL(infoMessage( const QString&, int)),
+ this,SIGNAL(infoMessage( const QString&, int)));
+ connect( m_writerJob, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+
+ if( waitForMedia( m_device,
+ K3bDevice::STATE_COMPLETE|K3bDevice::STATE_INCOMPLETE,
+ K3bDevice::MEDIA_CD_RW,
+ i18n("Please insert a rewritable CD medium into drive<p><b>%1 %2 (%3)</b>.")
+ .arg(m_device->vendor())
+ .arg(m_device->description())
+ .arg(m_device->devicename()) ) < 0 ) {
+ emit canceled();
+ jobFinished(false);
+ return;
+ }
+
+ m_writerJob->start();
+}
+
+
+void K3bBlankingJob::cancel()
+{
+ m_canceled = true;
+
+ if( m_writerJob )
+ m_writerJob->cancel();
+}
+
+
+void K3bBlankingJob::slotFinished(bool success)
+{
+ if( success ) {
+ emit infoMessage( i18n("Process completed successfully"), K3bJob::SUCCESS );
+ jobFinished( true );
+ }
+ else {
+ if( m_canceled ) {
+ emit infoMessage( i18n("Canceled."), ERROR );
+ emit canceled();
+ }
+ else {
+ emit infoMessage( i18n("Blanking error "), K3bJob::ERROR );
+ emit infoMessage( i18n("Sorry, no error handling yet."), K3bJob::ERROR );
+ }
+ jobFinished( false );
+ }
+}
+
+
+
+#include "k3bblankingjob.moc"
diff --git a/libk3b/jobs/k3bblankingjob.h b/libk3b/jobs/k3bblankingjob.h
new file mode 100644
index 0000000..8cfe0a1
--- /dev/null
+++ b/libk3b/jobs/k3bblankingjob.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * $Id: k3bblankingjob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef K3B_BLANKING_JOB_H
+#define K3B_BLANKING_JOB_H
+
+#include <k3bjob.h>
+#include "k3b_export.h"
+class KProcess;
+class QString;
+class K3bDevice::Device;
+class K3bAbstractWriter;
+
+
+class LIBK3B_EXPORT K3bBlankingJob : public K3bBurnJob
+{
+ Q_OBJECT
+
+ public:
+ K3bBlankingJob( K3bJobHandler*, QObject* parent = 0 );
+ ~K3bBlankingJob();
+
+ K3bDevice::Device* writer() const;
+
+ bool hasBeenCanceled() const { return m_canceled; }
+
+ enum blank_mode { Fast, Complete, Track, Unclose, Session };
+
+ public slots:
+ void start();
+ void cancel();
+ void setForce( bool f ) { m_force = f; }
+ void setDevice( K3bDevice::Device* d );
+ void setSpeed( int s ) { m_speed = s; }
+ void setMode( int m ) { m_mode = m; }
+ void setWritingApp (int app) { m_writingApp = app; }
+
+ /**
+ * If set true the job ignores the global K3b setting
+ * and does not eject the CD-RW after finishing
+ */
+ void setForceNoEject( bool b ) { m_forceNoEject = b; }
+
+ private slots:
+ void slotFinished(bool);
+ void slotStartErasing();
+
+ private:
+ K3bAbstractWriter* m_writerJob;
+ bool m_force;
+ K3bDevice::Device* m_device;
+ int m_speed;
+ int m_mode;
+ int m_writingApp;
+ bool m_canceled;
+ bool m_forceNoEject;
+};
+
+#endif
diff --git a/libk3b/jobs/k3bcdcopyjob.cpp b/libk3b/jobs/k3bcdcopyjob.cpp
new file mode 100644
index 0000000..ff8f35d
--- /dev/null
+++ b/libk3b/jobs/k3bcdcopyjob.cpp
@@ -0,0 +1,1213 @@
+/*
+ *
+ * $Id.cpp,v 1.82 2005/02/04 09:27:19 trueg Exp $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3bcdcopyjob.h"
+#include "k3baudiosessionreadingjob.h"
+
+#include <k3bexternalbinmanager.h>
+#include <k3bdevice.h>
+#include <k3bdiskinfo.h>
+#include <k3btoc.h>
+#include <k3bglobals.h>
+#include <k3bdevicehandler.h>
+#include <k3breadcdreader.h>
+#include <k3bdatatrackreader.h>
+#include <k3bcdrecordwriter.h>
+#include <k3bcdtext.h>
+#include <k3bcddb.h>
+#include <k3bcddbresult.h>
+#include <k3bcddbquery.h>
+#include <k3bcore.h>
+#include <k3binffilewriter.h>
+
+#include <kconfig.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+#include <kio/job.h>
+#include <kio/global.h>
+
+#include <qtimer.h>
+#include <qstringlist.h>
+#include <qfile.h>
+#include <qregexp.h>
+#include <qtextstream.h>
+#include <qcstring.h>
+#include <qfileinfo.h>
+#include <qdir.h>
+#include <qapplication.h>
+
+
+class K3bCdCopyJob::Private
+{
+public:
+ Private()
+ : canceled(false),
+ running(false),
+ readcdReader(0),
+ dataTrackReader(0),
+ audioSessionReader(0),
+ cdrecordWriter(0),
+ infFileWriter(0),
+ cddb(0) {
+ }
+
+ bool canceled;
+ bool error;
+ bool readingSuccessful;
+ bool running;
+
+ unsigned int numSessions;
+ bool doNotCloseLastSession;
+
+ unsigned int doneCopies;
+ unsigned int currentReadSession;
+ unsigned int currentWrittenSession;
+
+ K3bDevice::Toc toc;
+ QByteArray cdTextRaw;
+
+ K3bReadcdReader* readcdReader;
+ K3bDataTrackReader* dataTrackReader;
+ K3bAudioSessionReadingJob* audioSessionReader;
+ K3bCdrecordWriter* cdrecordWriter;
+ K3bInfFileWriter* infFileWriter;
+
+ bool audioReaderRunning;
+ bool dataReaderRunning;
+ bool writerRunning;
+
+ // image filenames, one for every track
+ QStringList imageNames;
+
+ // inf-filenames for writing audio tracks
+ QStringList infNames;
+
+ // indicates if we created a dir or not
+ bool deleteTempDir;
+
+ K3bCddb* cddb;
+ K3bCddbResultEntry cddbInfo;
+
+ bool haveCddb;
+ bool haveCdText;
+
+ QValueVector<bool> dataSessionProbablyTAORecorded;
+
+ // used to determine progress
+ QValueVector<long> sessionSizes;
+ long overallSize;
+};
+
+
+K3bCdCopyJob::K3bCdCopyJob( K3bJobHandler* hdl, QObject* parent )
+ : K3bBurnJob( hdl, parent ),
+ m_simulate(false),
+ m_copies(1),
+ m_onlyCreateImages(false),
+ m_onTheFly(true),
+ m_ignoreDataReadErrors(false),
+ m_ignoreAudioReadErrors(true),
+ m_noCorrection(false),
+ m_dataReadRetries(128),
+ m_audioReadRetries(5),
+ m_preferCdText(false),
+ m_copyCdText(true),
+ m_writingMode( K3b::WRITING_MODE_AUTO )
+{
+ d = new Private();
+}
+
+
+K3bCdCopyJob::~K3bCdCopyJob()
+{
+ delete d->infFileWriter;
+ delete d;
+}
+
+
+void K3bCdCopyJob::start()
+{
+ d->running = true;
+ d->canceled = false;
+ d->error = false;
+ d->readingSuccessful = false;
+ d->audioReaderRunning = d->dataReaderRunning = d->writerRunning = false;
+ d->sessionSizes.clear();
+ d->dataSessionProbablyTAORecorded.clear();
+ d->deleteTempDir = false;
+ d->haveCdText = false;
+ d->haveCddb = false;
+
+ jobStarted();
+
+ emit newTask( i18n("Checking Source Medium") );
+
+ emit burning(false);
+ emit newSubTask( i18n("Waiting for source medium") );
+
+ // wait for a source disk
+ if( waitForMedia( m_readerDevice,
+ K3bDevice::STATE_COMPLETE|K3bDevice::STATE_INCOMPLETE,
+ K3bDevice::MEDIA_WRITABLE_CD|K3bDevice::MEDIA_CD_ROM ) < 0 ) {
+ finishJob( true, false );
+ return;
+ }
+
+ emit newSubTask( i18n("Checking source medium") );
+
+ // FIXME: read ISRCs and MCN
+
+ connect( K3bDevice::diskInfo( m_readerDevice ), SIGNAL(finished(K3bDevice::DeviceHandler*)),
+ this, SLOT(slotDiskInfoReady(K3bDevice::DeviceHandler*)) );
+}
+
+
+void K3bCdCopyJob::slotDiskInfoReady( K3bDevice::DeviceHandler* dh )
+{
+ if( dh->success() ) {
+ d->toc = dh->toc();
+
+ //
+ // for now we copy audio, pure data (aka 1 data track), cd-extra (2 session, audio and data),
+ // and data multisession which one track per session.
+ // Everything else will be rejected
+ //
+ bool canCopy = true;
+ bool audio = false;
+ d->numSessions = dh->diskInfo().numSessions();
+ d->doNotCloseLastSession = (dh->diskInfo().diskState() == K3bDevice::STATE_INCOMPLETE);
+ switch( dh->toc().contentType() ) {
+ case K3bDevice::DATA:
+ // check if every track is in it's own session
+ // only then we copy the cd
+ if( (int)dh->toc().count() != dh->diskInfo().numSessions() ) {
+ emit infoMessage( i18n("K3b does not copy CDs containing multiple data tracks."), ERROR );
+ canCopy = false;
+ }
+ else if( dh->diskInfo().numSessions() > 1 )
+ emit infoMessage( i18n("Copying Multisession Data CD."), INFO );
+ else
+ emit infoMessage( i18n("Copying Data CD."), INFO );
+ break;
+
+ case K3bDevice::MIXED:
+ audio = true;
+ if( dh->diskInfo().numSessions() != 2 || d->toc[0].type() != K3bDevice::Track::AUDIO ) {
+ emit infoMessage( i18n("K3b can only copy CD-Extra mixed mode CDs."), ERROR );
+ canCopy = false;
+ }
+ else
+ emit infoMessage( i18n("Copying Enhanced Audio CD (CD-Extra)."), INFO );
+ break;
+
+ case K3bDevice::AUDIO:
+ audio = true;
+ emit infoMessage( i18n("Copying Audio CD."), INFO );
+ break;
+
+ case K3bDevice::NONE:
+ default:
+ emit infoMessage( i18n("The source disk is empty."), ERROR );
+ canCopy = false;
+ break;
+ }
+
+ //
+ // A data track recorded in TAO mode has two run-out blocks which cannot be read and contain
+ // zero data anyway. The problem is that I do not know of a valid method to determine if a track
+ // was written in TAO (the control nibble does definitely not work, I never saw one which did not
+ // equal 4).
+ // So the solution for now is to simply try to read the last sector of a data track. If this is not
+ // possible we assume it was written in TAO mode and reduce the length by 2 sectors
+ //
+ unsigned char buffer[2048];
+ int i = 1;
+ for( K3bDevice::Toc::iterator it = d->toc.begin(); it != d->toc.end(); ++it ) {
+ if( (*it).type() == K3bDevice::Track::DATA ) {
+ // we try twice just to be sure
+ if( m_readerDevice->read10( buffer, 2048, (*it).lastSector().lba(), 1 ) ||
+ m_readerDevice->read10( buffer, 2048, (*it).lastSector().lba(), 1 ) ) {
+ d->dataSessionProbablyTAORecorded.append(false);
+ kdDebug() << "(K3bCdCopyJob) track " << i << " probably DAO recorded." << endl;
+ }
+ else {
+ d->dataSessionProbablyTAORecorded.append(true);
+ kdDebug() << "(K3bCdCopyJob) track " << i << " probably TAO recorded." << endl;
+ }
+ }
+
+ ++i;
+ }
+
+
+ //
+ // To copy mode2 data tracks we need cdrecord >= 2.01a12 which introduced the -xa1 and -xamix options
+ //
+ if( k3bcore->externalBinManager()->binObject("cdrecord") &&
+ !k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "xamix" ) ) {
+ for( K3bDevice::Toc::const_iterator it = d->toc.begin(); it != d->toc.end(); ++it ) {
+ if( (*it).type() == K3bDevice::Track::DATA &&
+ ( (*it).mode() == K3bDevice::Track::XA_FORM1 ||
+ (*it).mode() == K3bDevice::Track::XA_FORM2 ) ) {
+ emit infoMessage( i18n("K3b needs cdrecord 2.01a12 or newer to copy Mode2 data tracks."), ERROR );
+ finishJob( true, false );
+ return;
+ }
+ }
+ }
+
+
+ //
+ // It is not possible to create multisession cds in raw writing mode
+ //
+ if( d->numSessions > 1 && m_writingMode == K3b::RAW ) {
+ if( !questionYesNo( i18n("You will only be able to copy the first session in raw writing mode. "
+ "Continue anyway?"),
+ i18n("Multisession CD") ) ) {
+ finishJob( true, false );
+ return;
+ }
+ else {
+ emit infoMessage( i18n("Only copying first session."), WARNING );
+ // TODO: remove the second session from the progress stuff
+ }
+ }
+
+
+ //
+ // We already create the temp filenames here since we need them to check the free space
+ //
+ if( !m_onTheFly || m_onlyCreateImages ) {
+ if( !prepareImageFiles() ) {
+ finishJob( false, true );
+ return;
+ }
+
+ //
+ // check free temp space
+ //
+ KIO::filesize_t imageSpaceNeeded = 0;
+ for( K3bDevice::Toc::const_iterator it = d->toc.begin(); it != d->toc.end(); ++it ) {
+ if( (*it).type() == K3bDevice::Track::AUDIO )
+ imageSpaceNeeded += (*it).length().audioBytes() + 44;
+ else
+ imageSpaceNeeded += (*it).length().mode1Bytes();
+ }
+
+ unsigned long avail, size;
+ QString pathToTest = m_tempPath.left( m_tempPath.findRev( '/' ) );
+ if( !K3b::kbFreeOnFs( pathToTest, size, avail ) ) {
+ emit infoMessage( i18n("Unable to determine free space in temporary directory '%1'.").arg(pathToTest), ERROR );
+ d->error = true;
+ canCopy = false;
+ }
+ else {
+ if( avail < imageSpaceNeeded/1024 ) {
+ emit infoMessage( i18n("Not enough space left in temporary directory."), ERROR );
+ d->error = true;
+ canCopy = false;
+ }
+ }
+ }
+
+ if( canCopy ) {
+ if( K3b::isMounted( m_readerDevice ) ) {
+ emit infoMessage( i18n("Unmounting source medium"), INFO );
+ K3b::unmount( m_readerDevice );
+ }
+
+ d->overallSize = 0;
+
+ // now create some progress helper values
+ for( K3bDevice::Toc::const_iterator it = d->toc.begin(); it != d->toc.end(); ++it ) {
+ d->overallSize += (*it).length().lba();
+ if( d->sessionSizes.isEmpty() || (*it).type() == K3bDevice::Track::DATA )
+ d->sessionSizes.append( (*it).length().lba() );
+ else
+ d->sessionSizes[0] += (*it).length().lba();
+ }
+
+ if( audio && !m_onlyCreateImages ) {
+ if( m_copyCdText )
+ searchCdText();
+ else
+ queryCddb();
+ }
+ else
+ startCopy();
+ }
+ else {
+ finishJob( false, true );
+ }
+ }
+ else {
+ emit infoMessage( i18n("Unable to read TOC"), ERROR );
+ finishJob( false, true );
+ }
+}
+
+
+void K3bCdCopyJob::searchCdText()
+{
+ emit newSubTask( i18n("Searching CD-TEXT") );
+
+ connect( K3bDevice::sendCommand( K3bDevice::DeviceHandler::CD_TEXT_RAW, m_readerDevice ),
+ SIGNAL(finished(K3bDevice::DeviceHandler*)),
+ this,
+ SLOT(slotCdTextReady(K3bDevice::DeviceHandler*)) );
+}
+
+
+void K3bCdCopyJob::slotCdTextReady( K3bDevice::DeviceHandler* dh )
+{
+ if( dh->success() ) {
+ if( K3bDevice::CdText::checkCrc( dh->cdTextRaw() ) ) {
+ K3bDevice::CdText cdt( dh->cdTextRaw() );
+ emit infoMessage( i18n("Found CD-TEXT (%1 - %2).").arg(cdt.performer()).arg(cdt.title()), SUCCESS );
+ d->haveCdText = true;
+ d->cdTextRaw = dh->cdTextRaw();
+ }
+ else {
+ emit infoMessage( i18n("Found corrupted CD-TEXT. Ignoring it."), WARNING );
+ d->haveCdText = false;
+ }
+
+ if( d->haveCdText && m_preferCdText )
+ startCopy();
+ else
+ queryCddb();
+ }
+ else {
+ emit infoMessage( i18n("No CD-TEXT found."), INFO );
+
+ d->haveCdText = false;
+
+ queryCddb();
+ }
+}
+
+
+void K3bCdCopyJob::queryCddb()
+{
+ emit newSubTask( i18n("Querying Cddb") );
+
+ d->haveCddb = false;
+
+ if( !d->cddb ) {
+ d->cddb = new K3bCddb( this );
+ connect( d->cddb, SIGNAL(queryFinished(int)),
+ this, SLOT(slotCddbQueryFinished(int)) );
+ }
+
+ KConfig* c = k3bcore->config();
+ c->setGroup("Cddb");
+
+ d->cddb->readConfig( c );
+ d->cddb->query( d->toc );
+}
+
+
+void K3bCdCopyJob::slotCddbQueryFinished( int error )
+{
+ if( error == K3bCddbQuery::SUCCESS ) {
+ d->cddbInfo = d->cddb->result();
+ d->haveCddb = true;
+
+ emit infoMessage( i18n("Found Cddb entry (%1 - %2).").arg(d->cddbInfo.cdArtist).arg(d->cddbInfo.cdTitle), SUCCESS );
+
+ // save the entry locally
+ KConfig* c = k3bcore->config();
+ c->setGroup( "Cddb" );
+ if( c->readBoolEntry( "save cddb entries locally", true ) )
+ d->cddb->saveEntry( d->cddbInfo );
+ }
+ else if( error == K3bCddbQuery::NO_ENTRY_FOUND ) {
+ emit infoMessage( i18n("No Cddb entry found."), WARNING );
+ }
+ else {
+ emit infoMessage( i18n("Cddb error (%1).").arg(d->cddb->errorString()), ERROR );
+ }
+
+ startCopy();
+}
+
+
+void K3bCdCopyJob::startCopy()
+{
+ d->currentWrittenSession = d->currentReadSession = 1;
+ d->doneCopies = 0;
+
+ if( m_onTheFly ) {
+ emit newSubTask( i18n("Preparing write process...") );
+
+ if( writeNextSession() )
+ readNextSession();
+ else {
+ finishJob( d->canceled, d->error );
+ }
+ }
+ else
+ readNextSession();
+}
+
+
+void K3bCdCopyJob::cancel()
+{
+ d->canceled = true;
+
+ if( d->writerRunning ) {
+ //
+ // we will handle cleanup in slotWriterFinished()
+ // if we are writing onthefly the reader won't be able to write
+ // anymore and will finish unsuccessfully, too
+ //
+ d->cdrecordWriter->cancel();
+ }
+ else if( d->audioReaderRunning )
+ d->audioSessionReader->cancel();
+ else if( d->dataReaderRunning )
+ // d->readcdReader->cancel();
+ d->dataTrackReader->cancel();
+}
+
+
+bool K3bCdCopyJob::prepareImageFiles()
+{
+ kdDebug() << "(K3bCdCopyJob) prepareImageFiles()" << endl;
+
+ d->imageNames.clear();
+ d->infNames.clear();
+ d->deleteTempDir = false;
+
+ QFileInfo fi( m_tempPath );
+
+ if( d->toc.count() > 1 || d->toc.contentType() == K3bDevice::AUDIO ) {
+ // create a directory which contains all the images and inf and stuff
+ // and save it in some cool structure
+
+ bool tempDirReady = false;
+ if( !fi.isDir() ) {
+ if( QFileInfo( m_tempPath.section( '/', 0, -2 ) ).isDir() ) {
+ if( !QFile::exists( m_tempPath ) ) {
+ QDir dir( m_tempPath.section( '/', 0, -2 ) );
+ dir.mkdir( m_tempPath.section( '/', -1 ) );
+ tempDirReady = true;
+ }
+ else
+ m_tempPath = m_tempPath.section( '/', 0, -2 );
+ }
+ else {
+ emit infoMessage( i18n("Specified an unusable temporary path. Using default."), WARNING );
+ m_tempPath = K3b::defaultTempPath();
+ }
+ }
+
+ // create temp dir
+ if( !tempDirReady ) {
+ QDir dir( m_tempPath );
+ m_tempPath = K3b::findUniqueFilePrefix( "k3bCdCopy", m_tempPath );
+ kdDebug() << "(K3bCdCopyJob) creating temp dir: " << m_tempPath << endl;
+ if( !dir.mkdir( m_tempPath, true ) ) {
+ emit infoMessage( i18n("Unable to create temporary directory '%1'.").arg(m_tempPath), ERROR );
+ return false;
+ }
+ d->deleteTempDir = true;
+ }
+
+ m_tempPath = K3b::prepareDir( m_tempPath );
+ emit infoMessage( i18n("Using temporary directory %1.").arg(m_tempPath), INFO );
+
+ // create temp filenames
+ int i = 1;
+ for( K3bDevice::Toc::const_iterator it = d->toc.begin(); it != d->toc.end(); ++it ) {
+ if( (*it).type() == K3bDevice::Track::AUDIO ) {
+ d->imageNames.append( m_tempPath + QString("Track%1.wav").arg(QString::number(i).rightJustify(2, '0')) );
+ d->infNames.append( m_tempPath + QString("Track%1.inf").arg(QString::number(i).rightJustify(2, '0')) );
+ }
+ else
+ d->imageNames.append( m_tempPath + QString("Track%1.iso").arg(QString::number(i).rightJustify(2, '0')) );
+ ++i;
+ }
+
+ kdDebug() << "(K3bCdCopyJob) created image filenames:" << endl;
+ for( unsigned int i = 0; i < d->imageNames.count(); ++i )
+ kdDebug() << "(K3bCdCopyJob) " << d->imageNames[i] << endl;
+
+ return true;
+ }
+ else {
+ // we only need a single image file
+ if( !fi.isFile() ||
+ questionYesNo( i18n("Do you want to overwrite %1?").arg(m_tempPath),
+ i18n("File Exists") ) ) {
+ if( fi.isDir() )
+ m_tempPath = K3b::findTempFile( "iso", m_tempPath );
+ else if( !QFileInfo( m_tempPath.section( '/', 0, -2 ) ).isDir() ) {
+ emit infoMessage( i18n("Specified an unusable temporary path. Using default."), WARNING );
+ m_tempPath = K3b::findTempFile( "iso" );
+ }
+ // else the user specified a file in an existing dir
+
+ emit infoMessage( i18n("Writing image file to %1.").arg(m_tempPath), INFO );
+ }
+ else
+ return false;
+
+ d->imageNames.append( m_tempPath );
+
+ return true;
+ }
+}
+
+
+void K3bCdCopyJob::readNextSession()
+{
+ if( !m_onTheFly || m_onlyCreateImages ) {
+ if( d->numSessions > 1 )
+ emit newTask( i18n("Reading Session %1").arg(d->currentReadSession) );
+ else
+ emit newTask( i18n("Reading Source Medium") );
+
+ if( d->currentReadSession == 1 )
+ emit newSubTask( i18n("Reading track %1 of %2").arg(1).arg(d->toc.count()) );
+ }
+
+ // there is only one situation where we need the audiosessionreader:
+ // if the first session is an audio session. That means the first track
+ // is an audio track
+ if( d->currentReadSession == 1 && d->toc[0].type() == K3bDevice::Track::AUDIO ) {
+ if( !d->audioSessionReader ) {
+ d->audioSessionReader = new K3bAudioSessionReadingJob( this, this );
+ connect( d->audioSessionReader, SIGNAL(nextTrack(int, int)),
+ this, SLOT(slotReadingNextTrack(int, int)) );
+ connectSubJob( d->audioSessionReader,
+ SLOT(slotSessionReaderFinished(bool)),
+ true,
+ SLOT(slotReaderProgress(int)),
+ SLOT(slotReaderSubProgress(int)) );
+ }
+
+ d->audioSessionReader->setDevice( m_readerDevice );
+ d->audioSessionReader->setToc( d->toc );
+ d->audioSessionReader->setParanoiaMode( m_paranoiaMode );
+ d->audioSessionReader->setReadRetries( m_audioReadRetries );
+ d->audioSessionReader->setNeverSkip( !m_ignoreAudioReadErrors );
+ if( m_onTheFly )
+ d->audioSessionReader->writeToFd( d->cdrecordWriter->fd() );
+ else
+ d->audioSessionReader->setImageNames( d->imageNames ); // the audio tracks are always the first tracks
+
+ d->audioReaderRunning = true;
+ d->audioSessionReader->start();
+ }
+ else {
+ if( !d->dataTrackReader ) {
+ d->dataTrackReader = new K3bDataTrackReader( this, this );
+ connect( d->dataTrackReader, SIGNAL(percent(int)), this, SLOT(slotReaderProgress(int)) );
+ connect( d->dataTrackReader, SIGNAL(processedSize(int, int)), this, SLOT(slotReaderProcessedSize(int, int)) );
+ connect( d->dataTrackReader, SIGNAL(finished(bool)), this, SLOT(slotSessionReaderFinished(bool)) );
+ connect( d->dataTrackReader, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( d->dataTrackReader, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+ }
+
+ d->dataTrackReader->setDevice( m_readerDevice );
+ d->dataTrackReader->setIgnoreErrors( m_ignoreDataReadErrors );
+ d->dataTrackReader->setNoCorrection( m_noCorrection );
+ d->dataTrackReader->setRetries( m_dataReadRetries );
+ if( m_onlyCreateImages )
+ d->dataTrackReader->setSectorSize( K3bDataTrackReader::MODE1 );
+ else
+ d->dataTrackReader->setSectorSize( K3bDataTrackReader::AUTO );
+
+ K3bTrack* track = 0;
+ unsigned int dataTrackIndex = 0;
+ if( d->toc.contentType() == K3bDevice::MIXED ) {
+ track = &d->toc[d->toc.count()-1];
+ dataTrackIndex = 0;
+ }
+ else {
+ track = &d->toc[d->currentReadSession-1]; // only one track per session
+ dataTrackIndex = d->currentReadSession-1;
+ }
+
+ // HACK: if the track is TAO recorded cut the two run-out sectors
+ if( d->dataSessionProbablyTAORecorded.count() > dataTrackIndex &&
+ d->dataSessionProbablyTAORecorded[dataTrackIndex] )
+ d->dataTrackReader->setSectorRange( track->firstSector(), track->lastSector() - 2 );
+ else
+ d->dataTrackReader->setSectorRange( track->firstSector(), track->lastSector() );
+
+ int trackNum = d->currentReadSession;
+ if( d->toc.contentType() == K3bDevice::MIXED )
+ trackNum = d->toc.count();
+
+ if( m_onTheFly )
+ d->dataTrackReader->writeToFd( d->cdrecordWriter->fd() );
+ else
+ d->dataTrackReader->setImagePath( d->imageNames[trackNum-1] );
+
+ d->dataReaderRunning = true;
+ if( !m_onTheFly || m_onlyCreateImages )
+ slotReadingNextTrack( 1, 1 );
+
+ d->dataTrackReader->start();
+ }
+}
+
+
+bool K3bCdCopyJob::writeNextSession()
+{
+ // we emit our own task since the cdrecord task is way too simple
+ if( d->numSessions > 1 ) {
+ if( m_simulate )
+ emit newTask( i18n("Simulating Session %1").arg(d->currentWrittenSession) );
+ else if( m_copies > 1 )
+ emit newTask( i18n("Writing Copy %1 (Session %2)").arg(d->doneCopies+1).arg(d->currentWrittenSession) );
+ else
+ emit newTask( i18n("Writing Copy (Session %2)").arg(d->currentWrittenSession) );
+ }
+ else {
+ if( m_simulate )
+ emit newTask( i18n("Simulating") );
+ else if( m_copies > 1 )
+ emit newTask( i18n("Writing Copy %1").arg(d->doneCopies+1) );
+ else
+ emit newTask( i18n("Writing Copy") );
+ }
+
+ emit newSubTask( i18n("Waiting for media") );
+
+ // if session > 1 we wait for an appendable CD
+ if( waitForMedia( m_writerDevice,
+ d->currentWrittenSession > 1 && !m_simulate
+ ? K3bDevice::STATE_INCOMPLETE
+ : K3bDevice::STATE_EMPTY,
+ K3bDevice::MEDIA_WRITABLE_CD ) < 0 ) {
+
+ finishJob( true, false );
+ return false;
+ }
+
+ if( !d->cdrecordWriter ) {
+ d->cdrecordWriter = new K3bCdrecordWriter( m_writerDevice, this, this );
+ connect( d->cdrecordWriter, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( d->cdrecordWriter, SIGNAL(percent(int)), this, SLOT(slotWriterProgress(int)) );
+ connect( d->cdrecordWriter, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSize(int, int)) );
+ connect( d->cdrecordWriter, SIGNAL(subPercent(int)), this, SIGNAL(subPercent(int)) );
+ connect( d->cdrecordWriter, SIGNAL(processedSubSize(int, int)), this, SIGNAL(processedSubSize(int, int)) );
+ connect( d->cdrecordWriter, SIGNAL(nextTrack(int, int)), this, SLOT(slotWritingNextTrack(int, int)) );
+ connect( d->cdrecordWriter, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) );
+ connect( d->cdrecordWriter, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
+ connect( d->cdrecordWriter, SIGNAL(writeSpeed(int, int)), this, SIGNAL(writeSpeed(int, int)) );
+ connect( d->cdrecordWriter, SIGNAL(finished(bool)), this, SLOT(slotWriterFinished(bool)) );
+ // connect( d->cdrecordWriter, SIGNAL(newTask(const QString&)), this, SIGNAL(newTask(const QString&)) );
+ connect( d->cdrecordWriter, SIGNAL(newSubTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( d->cdrecordWriter, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+ }
+
+ d->cdrecordWriter->setBurnDevice( m_writerDevice );
+ d->cdrecordWriter->clearArguments();
+ d->cdrecordWriter->setSimulate( m_simulate );
+ d->cdrecordWriter->setBurnSpeed( m_speed );
+
+
+ // create the cdrecord arguments
+ if( d->currentWrittenSession == 1 && d->toc[0].type() == K3bDevice::Track::AUDIO ) {
+ //
+ // Audio session
+ //
+
+
+ if( !d->infFileWriter )
+ d->infFileWriter = new K3bInfFileWriter();
+
+ //
+ // create the inf files if not already done
+ //
+ if( d->infNames.isEmpty() || !QFile::exists( d->infNames[0] ) ) {
+
+ unsigned int trackNumber = 1;
+
+ for( K3bDevice::Toc::const_iterator it = d->toc.begin(); it != d->toc.end(); ++it ) {
+ const K3bDevice::Track& track = *it;
+
+ if( track.type() == K3bDevice::Track::DATA )
+ break;
+
+ d->infFileWriter->setTrack( track );
+ d->infFileWriter->setTrackNumber( trackNumber );
+
+ if( d->haveCddb ) {
+ d->infFileWriter->setTrackTitle( d->cddbInfo.titles[trackNumber-1] );
+ d->infFileWriter->setTrackPerformer( d->cddbInfo.artists[trackNumber-1] );
+ d->infFileWriter->setTrackMessage( d->cddbInfo.extInfos[trackNumber-1] );
+
+ d->infFileWriter->setAlbumTitle( d->cddbInfo.cdTitle );
+ d->infFileWriter->setAlbumPerformer( d->cddbInfo.cdArtist );
+ }
+
+ if( m_onTheFly ) {
+
+ d->infFileWriter->setBigEndian( true );
+
+ // we let KTempFile choose a temp file but delete it on our own
+ // the same way we delete them when writing with images
+ // It is important that the files have the ending inf because
+ // cdrecord only checks this
+
+ KTempFile tmp( QString::null, ".inf" );
+ d->infNames.append( tmp.name() );
+ bool success = d->infFileWriter->save( *tmp.textStream() );
+ tmp.close();
+ if( !success )
+ return false;
+ }
+ else {
+ d->infFileWriter->setBigEndian( false );
+
+ if( !d->infFileWriter->save( d->infNames[trackNumber-1] ) )
+ return false;
+ }
+
+ ++trackNumber;
+ }
+ }
+
+ //
+ // the inf files are ready and named correctly when writing with images
+ //
+ int usedWritingMode = m_writingMode;
+ if( usedWritingMode == K3b::WRITING_MODE_AUTO ) {
+ //
+ // there are a lot of writers out there which produce coasters
+ // in dao mode if the CD contains pregaps of length 0 (or maybe already != 2 secs?)
+ //
+ bool zeroPregap = false;
+ if( d->numSessions == 1 ) {
+ for( K3bDevice::Toc::const_iterator it = d->toc.begin(); it != d->toc.end(); ++it ) {
+ const K3bDevice::Track& track = *it;
+ if( track.index0() == 0 ) {
+ ++it;
+ if( it != d->toc.end() )
+ zeroPregap = true;
+ --it;
+ }
+ }
+ }
+
+ if( zeroPregap && m_writerDevice->supportsRawWriting() ) {
+ if( d->numSessions == 1 )
+ usedWritingMode = K3b::RAW;
+ else
+ usedWritingMode = K3b::TAO;
+ }
+ else if( m_writerDevice->dao() )
+ usedWritingMode = K3b::DAO;
+ else if( m_writerDevice->supportsRawWriting() )
+ usedWritingMode = K3b::RAW;
+ else
+ usedWritingMode = K3b::TAO;
+ }
+ d->cdrecordWriter->setWritingMode( usedWritingMode );
+
+ if( d->numSessions > 1 )
+ d->cdrecordWriter->addArgument( "-multi" );
+
+ if( d->haveCddb || d->haveCdText ) {
+ if( usedWritingMode == K3b::TAO ) {
+ emit infoMessage( i18n("It is not possible to write CD-Text in TAO mode."), WARNING );
+ }
+ else if( d->haveCdText && ( !d->haveCddb || m_preferCdText ) ) {
+ // use the raw CDTEXT data
+ d->cdrecordWriter->setRawCdText( d->cdTextRaw );
+ }
+ else {
+ // make sure the writer job does not create raw cdtext
+ d->cdrecordWriter->setRawCdText( QByteArray() );
+ // cdrecord will use the cdtext data in the inf files
+ d->cdrecordWriter->addArgument( "-text" );
+ }
+ }
+
+ d->cdrecordWriter->addArgument( "-useinfo" );
+
+ //
+ // add all the audio tracks
+ //
+ d->cdrecordWriter->addArgument( "-audio" )->addArgument( "-shorttrack" );
+
+ for( unsigned int i = 0; i < d->infNames.count(); ++i ) {
+ if( m_onTheFly )
+ d->cdrecordWriter->addArgument( d->infNames[i] );
+ else
+ d->cdrecordWriter->addArgument( d->imageNames[i] );
+ }
+ }
+ else {
+ //
+ // Data Session
+ //
+ K3bTrack* track = 0;
+ unsigned int dataTrackIndex = 0;
+ if( d->toc.contentType() == K3bDevice::MIXED ) {
+ track = &d->toc[d->toc.count()-1];
+ dataTrackIndex = 0;
+ }
+ else {
+ track = &d->toc[d->currentWrittenSession-1];
+ dataTrackIndex = d->currentWrittenSession-1;
+ }
+
+ bool multi = d->doNotCloseLastSession || (d->numSessions > 1 && d->currentWrittenSession < d->toc.count());
+ int usedWritingMode = m_writingMode;
+ if( usedWritingMode == K3b::WRITING_MODE_AUTO ) {
+ // at least the NEC3540a does write 2056 byte sectors only in tao mode. Same for LG4040b
+ // since writing data tracks in TAO mode is no loss let's default to TAO in the case of 2056 byte
+ // sectors (which is when writing xa form1 sectors here)
+ if( m_writerDevice->dao() &&
+ d->toc.count() == 1 &&
+ !multi &&
+ track->mode() == K3bDevice::Track::MODE1 )
+ usedWritingMode = K3b::DAO;
+ else
+ usedWritingMode = K3b::TAO;
+ }
+ d->cdrecordWriter->setWritingMode( usedWritingMode );
+
+ //
+ // all but the last session of a multisession disk are written in multi mode
+ // and every data track has it's own session which we forced above
+ //
+ if( multi )
+ d->cdrecordWriter->addArgument( "-multi" );
+
+ // just to let the reader init
+ if( m_onTheFly )
+ d->cdrecordWriter->addArgument( "-waiti" );
+
+ if( track->mode() == K3bDevice::Track::MODE1 )
+ d->cdrecordWriter->addArgument( "-data" );
+ else if( track->mode() == K3bDevice::Track::XA_FORM1 )
+ d->cdrecordWriter->addArgument( "-xa1" );
+ else
+ d->cdrecordWriter->addArgument( "-xamix" );
+
+ if( m_onTheFly ) {
+ // HACK: if the track is TAO recorded cut the two run-out sectors
+ unsigned long trackLen = track->length().lba();
+ if( d->dataSessionProbablyTAORecorded.count() > dataTrackIndex &&
+ d->dataSessionProbablyTAORecorded[dataTrackIndex] )
+ trackLen -= 2;
+
+ if( track->mode() == K3bDevice::Track::MODE1 )
+ trackLen = trackLen * 2048;
+ else if( track->mode() == K3bDevice::Track::XA_FORM1 )
+ trackLen = trackLen * 2056; // see k3bdatatrackreader.h
+ else
+ trackLen = trackLen * 2332; // see k3bdatatrackreader.h
+ d->cdrecordWriter->addArgument( QString("-tsize=%1").arg(trackLen) )->addArgument("-");
+ }
+ else if( d->toc.contentType() == K3bDevice::MIXED )
+ d->cdrecordWriter->addArgument( d->imageNames[d->toc.count()-1] );
+ else
+ d->cdrecordWriter->addArgument( d->imageNames[d->currentWrittenSession-1] );
+
+ // clear cd text from previous sessions
+ d->cdrecordWriter->setRawCdText( QByteArray() );
+ }
+
+
+ //
+ // Finally start the writer
+ //
+ emit burning(true);
+ d->writerRunning = true;
+ d->cdrecordWriter->start();
+
+ return true;
+}
+
+
+// both the readcdreader and the audiosessionreader are connected to this slot
+void K3bCdCopyJob::slotSessionReaderFinished( bool success )
+{
+ d->audioReaderRunning = d->dataReaderRunning = false;
+
+ if( success ) {
+ if( d->numSessions > 1 )
+ emit infoMessage( i18n("Successfully read session %1.").arg(d->currentReadSession), SUCCESS );
+ else
+ emit infoMessage( i18n("Successfully read source disk."), SUCCESS );
+
+ if( !m_onTheFly ) {
+ if( d->numSessions > d->currentReadSession ) {
+ d->currentReadSession++;
+ readNextSession();
+ }
+ else {
+ d->readingSuccessful = true;
+ if( !m_onlyCreateImages ) {
+ if( m_readerDevice == m_writerDevice ) {
+ // eject the media (we do this blocking to know if it worked
+ // becasue if it did not it might happen that k3b overwrites a CD-RW
+ // source)
+ if( !m_readerDevice->eject() ) {
+ blockingInformation( i18n("K3b was unable to eject the source disk. Please do so manually.") );
+ }
+ }
+
+ if( !writeNextSession() ) {
+ // nothing is running here...
+ finishJob( d->canceled, d->error );
+ }
+ }
+ else {
+ finishJob( false, false );
+ }
+ }
+ }
+ }
+ else {
+ if( !d->canceled ) {
+ emit infoMessage( i18n("Error while reading session %1.").arg(d->currentReadSession), ERROR );
+ if( m_onTheFly )
+ d->cdrecordWriter->setSourceUnreadable(true);
+ }
+
+ finishJob( d->canceled, !d->canceled );
+ }
+}
+
+
+void K3bCdCopyJob::slotWriterFinished( bool success )
+{
+ emit burning(false);
+
+ d->writerRunning = false;
+
+ if( success ) {
+ //
+ // if this was the last written session we need to reset d->currentWrittenSession
+ // and start a new writing if more copies are wanted
+ //
+
+ if( d->currentWrittenSession < d->numSessions ) {
+ d->currentWrittenSession++;
+ d->currentReadSession++;
+
+ // reload the media
+ emit newSubTask( i18n("Reloading the medium") );
+ connect( K3bDevice::reload( m_writerDevice ), SIGNAL(finished(K3bDevice::DeviceHandler*)),
+ this, SLOT(slotMediaReloadedForNextSession(K3bDevice::DeviceHandler*)) );
+ }
+ else {
+ d->doneCopies++;
+
+ if( !m_simulate && d->doneCopies < m_copies ) {
+ // start next copy
+ K3bDevice::eject( m_writerDevice );
+
+ d->currentWrittenSession = 1;
+ d->currentReadSession = 1;
+ if( writeNextSession() ) {
+ if( m_onTheFly )
+ readNextSession();
+ }
+ else {
+ // nothing running here...
+ finishJob( d->canceled, d->error );
+ }
+ }
+ else {
+ finishJob( false, false );
+ }
+ }
+ }
+ else {
+ //
+ // If we are writing on the fly the reader will also stop when it is not able to write anymore
+ // The error handling will be done only here in that case
+ //
+
+ // the K3bCdrecordWriter emitted an error message
+
+ finishJob( d->canceled, !d->canceled );
+ }
+}
+
+
+void K3bCdCopyJob::slotMediaReloadedForNextSession( K3bDevice::DeviceHandler* dh )
+{
+ if( !dh->success() )
+ blockingInformation( i18n("Please reload the medium and press 'ok'"),
+ i18n("Unable to close the tray") );
+
+ if( !writeNextSession() ) {
+ // nothing is running here...
+ finishJob( d->canceled, d->error );
+ }
+ else if( m_onTheFly )
+ readNextSession();
+}
+
+
+void K3bCdCopyJob::cleanup()
+{
+ if( m_onTheFly || !m_keepImage || ((d->canceled || d->error) && !d->readingSuccessful) ) {
+ emit infoMessage( i18n("Removing temporary files."), INFO );
+ for( QStringList::iterator it = d->infNames.begin(); it != d->infNames.end(); ++it )
+ QFile::remove( *it );
+ }
+
+ if( !m_onTheFly && (!m_keepImage || ((d->canceled || d->error) && !d->readingSuccessful)) ) {
+ emit infoMessage( i18n("Removing image files."), INFO );
+ for( QStringList::iterator it = d->imageNames.begin(); it != d->imageNames.end(); ++it )
+ QFile::remove( *it );
+
+ // remove the tempdir created in prepareImageFiles()
+ if( d->deleteTempDir ) {
+ KIO::NetAccess::del( KURL::fromPathOrURL(m_tempPath), 0 );
+ d->deleteTempDir = false;
+ }
+ }
+}
+
+
+void K3bCdCopyJob::slotReaderProgress( int p )
+{
+ if( !m_onTheFly || m_onlyCreateImages ) {
+ int bigParts = ( m_onlyCreateImages ? 1 : (m_simulate ? 2 : m_copies + 1 ) );
+ double done = (double)p * (double)d->sessionSizes[d->currentReadSession-1] / 100.0;
+ for( unsigned int i = 0; i < d->currentReadSession-1; ++i )
+ done += (double)d->sessionSizes[i];
+ emit percent( (int)(100.0*done/(double)d->overallSize/(double)bigParts) );
+
+ if( d->dataReaderRunning )
+ emit subPercent(p);
+ }
+}
+
+
+void K3bCdCopyJob::slotReaderSubProgress( int p )
+{
+ // only if reading an audiosession
+ if( !m_onTheFly || m_onlyCreateImages ) {
+ emit subPercent( p );
+ }
+}
+
+
+void K3bCdCopyJob::slotReaderProcessedSize( int p, int pp )
+{
+ if( !m_onTheFly )
+ emit processedSubSize( p, pp );
+}
+
+
+void K3bCdCopyJob::slotWriterProgress( int p )
+{
+ int bigParts = ( m_simulate ? 1 : m_copies ) + ( m_onTheFly ? 0 : 1 );
+ long done = ( m_onTheFly ? d->doneCopies : d->doneCopies+1 ) * d->overallSize
+ + (p * d->sessionSizes[d->currentWrittenSession-1] / 100);
+ for( unsigned int i = 0; i < d->currentWrittenSession-1; ++i )
+ done += d->sessionSizes[i];
+ emit percent( 100*done/d->overallSize/bigParts );
+}
+
+
+void K3bCdCopyJob::slotWritingNextTrack( int t, int tt )
+{
+ if( d->toc.contentType() == K3bDevice::MIXED ) {
+ if( d->currentWrittenSession == 1 )
+ emit newSubTask( i18n("Writing track %1 of %2").arg(t).arg(d->toc.count()) );
+ else
+ emit newSubTask( i18n("Writing track %1 of %2").arg(d->toc.count()).arg(d->toc.count()) );
+ }
+ else if( d->numSessions > 1 )
+ emit newSubTask( i18n("Writing track %1 of %2").arg(d->currentWrittenSession).arg(d->toc.count()) );
+ else
+ emit newSubTask( i18n("Writing track %1 of %2").arg(t).arg(tt) );
+}
+
+
+void K3bCdCopyJob::slotReadingNextTrack( int t, int )
+{
+ if( !m_onTheFly || m_onlyCreateImages ) {
+ int track = t;
+ if( d->audioReaderRunning )
+ track = t;
+ else if( d->toc.contentType() == K3bDevice::MIXED )
+ track = d->toc.count();
+ else
+ track = d->currentReadSession;
+
+ emit newSubTask( i18n("Reading track %1 of %2").arg(track).arg(d->toc.count()) );
+ }
+}
+
+
+QString K3bCdCopyJob::jobDescription() const
+{
+ if( m_onlyCreateImages ) {
+ return i18n("Creating CD Image");
+ }
+ else if( m_simulate ) {
+ if( m_onTheFly )
+ return i18n("Simulating CD Copy On-The-Fly");
+ else
+ return i18n("Simulating CD Copy");
+ }
+ else {
+ if( m_onTheFly )
+ return i18n("Copying CD On-The-Fly");
+ else
+ return i18n("Copying CD");
+ }
+}
+
+
+QString K3bCdCopyJob::jobDetails() const
+{
+ return i18n("Creating 1 copy",
+ "Creating %n copies",
+ (m_simulate||m_onlyCreateImages) ? 1 : m_copies );
+}
+
+
+void K3bCdCopyJob::finishJob( bool c, bool e )
+{
+ if( d->running ) {
+ if( c ) {
+ d->canceled = true;
+ emit canceled();
+ }
+ if( e )
+ d->error = true;
+
+ cleanup();
+
+ d->running = false;
+
+ jobFinished( !(c||e) );
+ }
+}
+
+#include "k3bcdcopyjob.moc"
diff --git a/libk3b/jobs/k3bcdcopyjob.h b/libk3b/jobs/k3bcdcopyjob.h
new file mode 100644
index 0000000..3ab77e8
--- /dev/null
+++ b/libk3b/jobs/k3bcdcopyjob.h
@@ -0,0 +1,117 @@
+/*
+ *
+ * $Id: k3bcdcopyjob.h 690187 2007-07-20 09:18:03Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3BCDCOPYJOB_H_
+#define _K3BCDCOPYJOB_H_
+
+#include <k3bjob.h>
+#include "k3b_export.h"
+
+namespace K3bDevice {
+ class Device;
+ class DeviceHandler;
+}
+
+
+/**
+ *@author Sebastian Trueg
+ */
+class LIBK3B_EXPORT K3bCdCopyJob : public K3bBurnJob
+{
+ Q_OBJECT
+
+ public:
+ K3bCdCopyJob( K3bJobHandler* hdl, QObject* parent = 0 );
+ ~K3bCdCopyJob();
+
+ K3bDevice::Device* writer() const { return m_onlyCreateImages ? 0 : m_writerDevice; }
+ K3bDevice::Device* reader() const { return m_readerDevice; }
+
+ QString jobDescription() const;
+ QString jobDetails() const;
+
+ public slots:
+ void start();
+ void cancel();
+
+ public:
+ void setWriterDevice( K3bDevice::Device* dev ) { m_writerDevice = dev; }
+ void setReaderDevice( K3bDevice::Device* dev ) { m_readerDevice = dev; }
+ void setWritingMode( int m ) { m_writingMode = m; }
+ void setSpeed( int s ) { m_speed = s; }
+ void setOnTheFly( bool b ) { m_onTheFly = b; }
+ void setKeepImage( bool b ) { m_keepImage = b; }
+ void setOnlyCreateImage( bool b ) { m_onlyCreateImages = b; }
+ void setSimulate( bool b ) { m_simulate = b; }
+ void setTempPath( const QString& path ) { m_tempPath= path; }
+ void setCopies( unsigned int c ) { m_copies = c; }
+ void setParanoiaMode( int i ) { m_paranoiaMode = i; }
+ void setIgnoreDataReadErrors( bool b ) { m_ignoreDataReadErrors = b; }
+ void setDataReadRetries( int i ) { m_dataReadRetries = i; }
+ void setIgnoreAudioReadErrors( bool b ) { m_ignoreAudioReadErrors = b; }
+ void setAudioReadRetries( int i ) { m_audioReadRetries = i; }
+ void setPreferCdText( bool b ) { m_preferCdText = b; }
+ void setCopyCdText( bool b ) { m_copyCdText = b; }
+ void setNoCorrection( bool b ) { m_noCorrection = b; }
+
+ private slots:
+ void slotDiskInfoReady( K3bDevice::DeviceHandler* );
+ void slotCdTextReady( K3bDevice::DeviceHandler* );
+ void slotMediaReloadedForNextSession( K3bDevice::DeviceHandler* dh );
+ void slotCddbQueryFinished(int);
+ void slotWritingNextTrack( int t, int tt );
+ void slotReadingNextTrack( int t, int tt );
+ void slotSessionReaderFinished( bool success );
+ void slotWriterFinished( bool success );
+ void slotReaderProgress( int p );
+ void slotReaderSubProgress( int p );
+ void slotWriterProgress( int p );
+ void slotReaderProcessedSize( int p, int pp );
+
+ private:
+ void startCopy();
+ void searchCdText();
+ void queryCddb();
+ bool writeNextSession();
+ void readNextSession();
+ bool prepareImageFiles();
+ void cleanup();
+ void finishJob( bool canceled, bool error );
+
+ K3bDevice::Device* m_writerDevice;
+ K3bDevice::Device* m_readerDevice;
+ bool m_simulate;
+ int m_speed;
+ int m_paranoiaMode;
+ unsigned int m_copies;
+ bool m_keepImage;
+ bool m_onlyCreateImages;
+ bool m_onTheFly;
+ bool m_ignoreDataReadErrors;
+ bool m_ignoreAudioReadErrors;
+ bool m_noCorrection;
+ int m_dataReadRetries;
+ int m_audioReadRetries;
+ bool m_preferCdText;
+ bool m_copyCdText;
+ QString m_tempPath;
+ int m_writingMode;
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/jobs/k3bcdda2wavreader.cpp b/libk3b/jobs/k3bcdda2wavreader.cpp
new file mode 100644
index 0000000..3df87d3
--- /dev/null
+++ b/libk3b/jobs/k3bcdda2wavreader.cpp
@@ -0,0 +1,254 @@
+/*
+ *
+ * $Id: k3bcdda2wavreader.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bcdda2wavreader.h"
+
+#include <k3bexternalbinmanager.h>
+#include <k3bdevice.h>
+#include <k3bdevicemanager.h>
+#include <k3bcore.h>
+#include <k3bprocess.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <qvaluevector.h>
+#include <qregexp.h>
+
+
+class K3bCdda2wavReader::Private
+{
+public:
+ Private()
+ : cdda2wavBin(0),
+ process(0),
+ cancaled(false),
+ running(false),
+ fdToWriteTo(-1) {
+ }
+
+ const K3bExternalBin* cdda2wavBin;
+ K3bProcess* process;
+
+ bool cancaled;
+ bool running;
+
+ int fdToWriteTo;
+
+ int currentTrack;
+ QValueVector<int> trackOffsets;
+};
+
+
+K3bCdda2wavReader::K3bCdda2wavReader( QObject* parent, const char* name )
+ : K3bJob( parent, name )
+{
+ d = new Private();
+}
+
+
+K3bCdda2wavReader::~K3bCdda2wavReader()
+{
+ delete d->process;
+ delete d;
+}
+
+
+bool K3bCdda2wavReader::active() const
+{
+ return d->running;
+}
+
+
+void K3bCdda2wavReader::writeToFd( int fd )
+{
+ d->fdToWriteTo = fd;
+}
+
+
+void K3bCdda2wavReader::start()
+{
+ start( false );
+}
+
+
+void K3bCdda2wavReader::start( bool onlyInfo )
+{
+ d->running = true;
+ d->cancaled = false;
+ d->currentTrack = 1;
+ d->trackOffsets.clear();
+
+ jobStarted();
+
+ d->cdda2wavBin = k3bcore->externalBinManager()->binObject( "cdda2wav" );
+ if( !d->cdda2wavBin ) {
+ emit infoMessage( i18n("Could not find %1 executable.").arg("cdda2wav"), ERROR );
+ jobFinished(false);
+ d->running = false;
+ return;
+ }
+
+ // prepare the process
+ delete d->process;
+ d->process = new K3bProcess();
+ d->process->setSplitStdout(true);
+ d->process->setSuppressEmptyLines(true);
+ d->process->setWorkingDirectory( m_imagePath );
+ connect( d->process, SIGNAL(stdoutLine(const QString&)), this, SLOT(slotProcessLine(const QString&)) );
+ connect( d->process, SIGNAL(stderrLine(const QString&)), this, SLOT(slotProcessLine(const QString&)) );
+ connect( d->process, SIGNAL(processExited(KProcess*)), this, SLOT(slotProcessExited(KProcess*)) );
+
+ // create the command line
+ *d->process << d->cdda2wavBin->path;
+ *d->process << "-vall" << ( d->cdda2wavBin->hasFeature( "gui" ) ? "-gui" : "-g" );
+ if( d->cdda2wavBin->hasFeature( "dev" ) )
+ *d->process << QString("dev=%1").arg(K3bDevice::externalBinDeviceParameter(m_device, d->cdda2wavBin));
+ else
+ *d->process << "-D" << K3bDevice::externalBinDeviceParameter(m_device, d->cdda2wavBin);
+ *d->process << ( d->cdda2wavBin->hasFeature( "bulk" ) ? "-bulk" : "-B" );
+ if( onlyInfo )
+ *d->process << ( d->cdda2wavBin->hasFeature( "info-only" ) ? "-info-only" : "-J" );
+ else if( d->fdToWriteTo != -1 )
+ *d->process << ( d->cdda2wavBin->hasFeature( "no-infofile" ) ? "-no-infofile" : "-H" );
+
+ // additional user parameters from config
+ const QStringList& params = d->cdda2wavBin->userParameters();
+ for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
+ *d->process << *it;
+
+ // start the thing
+ if( !d->process->start( KProcess::NotifyOnExit, KProcess::All ) ) {
+ // something went wrong when starting the program
+ // it "should" be the executable
+ kdDebug() << "(K3bCdda2wavReader) could not start cdda2wav" << endl;
+ emit infoMessage( i18n("Could not start %1.").arg("cdda2wav"), K3bJob::ERROR );
+ d->running = false;
+ jobFinished(false);
+ }
+}
+
+
+void K3bCdda2wavReader::cancel()
+{
+ if( d->running ) {
+ d->cancaled = true;
+ if( d->process )
+ if( d->process->isRunning() )
+ d->process->kill();
+ }
+}
+
+
+void K3bCdda2wavReader::slotProcessLine( const QString& line )
+{
+ // Tracks:11 44:37.30
+ // CDINDEX discid: ZvzBXv614ACgzn1bWWy107cs0nA-
+ // CDDB discid: 0x8a0a730b
+ // CD-Text: not detected
+ // CD-Extra: not detected
+ // Album title: '' from ''
+ // T01: 0 3:39.70 audio linear copydenied stereo title '' from ''
+ // T02: 16495 3:10.47 audio linear copydenied stereo title '' from ''
+ // T03: 30792 3:30.00 audio linear copydenied stereo title '' from ''
+ // T04: 46542 4:05.05 audio linear copydenied stereo title '' from ''
+ // T05: 64922 3:44.35 audio linear copydenied stereo title '' from ''
+ // T06: 81757 4:36.45 audio linear copydenied stereo title '' from ''
+ // T07: 102502 3:59.30 audio linear copydenied stereo title '' from ''
+ // T08: 120457 5:24.30 audio linear copydenied stereo title '' from ''
+ // T09: 144787 3:26.28 audio linear copydenied stereo title '' from ''
+ // T10: 160265 4:07.20 audio linear copydenied stereo title '' from ''
+ // T11: 178810 4:51.20 audio linear copydenied stereo title '' from ''
+
+ // percent_done:
+ // 100% track 1 successfully recorded
+ // 100% track 2 successfully recorded
+ // 100% track 3 successfully recorded
+
+
+
+ static QRegExp rx( "T\\d\\d:" );
+ if( rx.exactMatch( line.left(4) ) || line.startsWith( "Leadout" ) ) {
+ int pos = line.find( " " );
+ int endpos = line.find( QRegExp( "\\d" ), pos );
+ endpos = line.find( " ", endpos );
+ bool ok;
+ int offset = line.mid( pos, endpos-pos ).toInt(&ok);
+ if( ok )
+ d->trackOffsets.append( offset );
+ else
+ kdDebug() << "(K3bCdda2wavReader) track offset parsing error: '" << line.mid( pos, endpos-pos ) << "'" << endl;
+ }
+
+ else if( line.startsWith( "percent_done" ) ) {
+ // the reading starts
+ d->currentTrack = 1;
+ emit nextTrack( d->currentTrack, d->trackOffsets.count() );
+ }
+
+ else if( line.contains("successfully recorded") ) {
+ d->currentTrack++;
+ emit nextTrack( d->currentTrack, d->trackOffsets.count() );
+ }
+
+ else if( line.contains("%") ) {
+ // parse progress
+ bool ok;
+ int p = line.left(3).toInt(&ok);
+ if( ok ) {
+ emit subPercent( p );
+
+ int overall = d->trackOffsets[d->currentTrack-1];
+ int tSize = d->trackOffsets[d->currentTrack] - d->trackOffsets[d->currentTrack-1];
+ overall += (tSize*p/100);
+
+ emit percent( overall*100/d->trackOffsets[d->trackOffsets.count()-1] );
+ }
+ else
+ kdDebug() << "(K3bCdda2wavReader) track progress parsing error: '" << line.left(3) << "'" << endl;
+ }
+}
+
+
+void K3bCdda2wavReader::slotProcessExited( KProcess* p )
+{
+ d->running = false;
+
+ if( d->cancaled ) {
+ emit canceled();
+ jobFinished(false);
+ return;
+ }
+
+ if( p->normalExit() ) {
+ // TODO: improve this
+
+ if( p->exitStatus() == 0 ) {
+ jobFinished( true );
+ }
+ else {
+ emit infoMessage( i18n("%1 returned an unknown error (code %2).")
+ .arg("Cdda2wav").arg(p->exitStatus()), ERROR );
+ jobFinished( false );
+ }
+ }
+ else {
+ emit infoMessage( i18n("%1 did not exit cleanly.").arg("Cdda2wav"),
+ ERROR );
+ jobFinished( false );
+ }
+}
+
+#include "k3bcdda2wavreader.moc"
diff --git a/libk3b/jobs/k3bcdda2wavreader.h b/libk3b/jobs/k3bcdda2wavreader.h
new file mode 100644
index 0000000..edde65c
--- /dev/null
+++ b/libk3b/jobs/k3bcdda2wavreader.h
@@ -0,0 +1,70 @@
+/*
+ *
+ * $Id: k3bcdda2wavreader.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_CDDA2WAV_READER_H_
+#define _K3B_CDDA2WAV_READER_H_
+
+#include <k3bjob.h>
+
+class KProcess;
+namespace K3bDevice {
+ class Device;
+};
+
+
+/**
+ * An Audio CD reader completely based on cdda2wav.
+ * It does not use K3bDevice::Device but parses the track offsets
+ * from the cdda2wav output.
+ */
+class K3bCdda2wavReader : public K3bJob
+{
+ Q_OBJECT
+
+ public:
+ K3bCdda2wavReader( QObject* parent = 0, const char* name = 0 );
+ ~K3bCdda2wavReader();
+
+ bool active() const;
+
+ public slots:
+ void start();
+ void start( bool onlyReadInfo );
+ void cancel();
+
+ void setReadDevice( K3bDevice::Device* dev ) { m_device = dev; }
+ void setImagePath( const QString& p ) { m_imagePath = p; }
+
+ /**
+ * the data gets written directly into fd instead of the imagefile.
+ * Be aware that this only makes sense before starting the job.
+ * To disable just set fd to -1
+ */
+ void writeToFd( int fd );
+
+ private slots:
+ void slotProcessLine( const QString& );
+ void slotProcessExited( KProcess* );
+
+ private:
+ K3bDevice::Device* m_device;
+
+ QString m_imagePath;
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/jobs/k3bclonejob.cpp b/libk3b/jobs/k3bclonejob.cpp
new file mode 100644
index 0000000..9fb61ab
--- /dev/null
+++ b/libk3b/jobs/k3bclonejob.cpp
@@ -0,0 +1,375 @@
+/*
+ *
+ * $Id: k3bclonejob.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bclonejob.h"
+
+#include <k3breadcdreader.h>
+#include <k3bcdrecordwriter.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bdevice.h>
+#include <k3bdevicehandler.h>
+#include <k3bglobals.h>
+#include <k3bcore.h>
+#include <k3bclonetocreader.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <qfile.h>
+#include <qfileinfo.h>
+
+
+
+class K3bCloneJob::Private
+{
+public:
+ Private()
+ : doneCopies(0) {
+ }
+
+ int doneCopies;
+};
+
+
+K3bCloneJob::K3bCloneJob( K3bJobHandler* hdl, QObject* parent, const char* name )
+ : K3bBurnJob( hdl, parent, name ),
+ m_writerDevice(0),
+ m_readerDevice(0),
+ m_writerJob(0),
+ m_readcdReader(0),
+ m_removeImageFiles(false),
+ m_canceled(false),
+ m_running(false),
+ m_simulate(false),
+ m_speed(1),
+ m_copies(1),
+ m_onlyCreateImage(false),
+ m_onlyBurnExistingImage(false),
+ m_readRetries(128)
+{
+ d = new Private;
+}
+
+
+K3bCloneJob::~K3bCloneJob()
+{
+ delete d;
+}
+
+
+void K3bCloneJob::start()
+{
+ jobStarted();
+
+ m_canceled = false;
+ m_running = true;
+
+
+ // TODO: check the cd size and warn the user if not enough space
+
+ //
+ // We first check if cdrecord has clone support
+ // The readcdReader will check the same for readcd
+ //
+ const K3bExternalBin* cdrecordBin = k3bcore->externalBinManager()->binObject( "cdrecord" );
+ if( !cdrecordBin ) {
+ emit infoMessage( i18n("Could not find %1 executable.").arg("cdrecord"), ERROR );
+ jobFinished(false);
+ m_running = false;
+ return;
+ }
+ else if( !cdrecordBin->hasFeature( "clone" ) ) {
+ emit infoMessage( i18n("Cdrecord version %1 does not have cloning support.").arg(cdrecordBin->version), ERROR );
+ jobFinished(false);
+ m_running = false;
+ return;
+ }
+
+ if( (!m_onlyCreateImage && !writer()) ||
+ (!m_onlyBurnExistingImage && !readingDevice()) ) {
+ emit infoMessage( i18n("No device set."), ERROR );
+ jobFinished(false);
+ m_running = false;
+ return;
+ }
+
+ if( !m_onlyCreateImage ) {
+ if( !writer()->supportsWritingMode( K3bDevice::RAW_R96R ) &&
+ !writer()->supportsWritingMode( K3bDevice::RAW_R16 ) ) {
+ emit infoMessage( i18n("CD writer %1 does not support cloning.")
+ .arg(writer()->vendor())
+ .arg(writer()->description()), ERROR );
+ m_running = false;
+ jobFinished(false);
+ return;
+ }
+ }
+
+ if( m_imagePath.isEmpty() ) {
+ m_imagePath = K3b::findTempFile( "img" );
+ }
+ else if( QFileInfo(m_imagePath).isDir() ) {
+ m_imagePath = K3b::findTempFile( "img", m_imagePath );
+ }
+
+ if( m_onlyBurnExistingImage ) {
+ startWriting();
+ }
+ else {
+ emit burning( false );
+
+ prepareReader();
+
+ if( waitForMedia( readingDevice(),
+ K3bDevice::STATE_COMPLETE,
+ K3bDevice::MEDIA_WRITABLE_CD|K3bDevice::MEDIA_CD_ROM ) < 0 ) {
+ m_running = false;
+ emit canceled();
+ jobFinished(false);
+ return;
+ }
+
+ emit newTask( i18n("Reading clone image") );
+
+ m_readcdReader->start();
+ }
+}
+
+
+void K3bCloneJob::prepareReader()
+{
+ if( !m_readcdReader ) {
+ m_readcdReader = new K3bReadcdReader( this, this );
+ connect( m_readcdReader, SIGNAL(percent(int)), this, SLOT(slotReadingPercent(int)) );
+ connect( m_readcdReader, SIGNAL(percent(int)), this, SIGNAL(subPercent(int)) );
+ connect( m_readcdReader, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSubSize(int, int)) );
+ connect( m_readcdReader, SIGNAL(finished(bool)), this, SLOT(slotReadingFinished(bool)) );
+ connect( m_readcdReader, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_readcdReader, SIGNAL(newTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( m_readcdReader, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+ }
+
+ m_readcdReader->setReadDevice( readingDevice() );
+ m_readcdReader->setReadSpeed( 0 ); // MAX
+ m_readcdReader->setDisableCorrection( m_noCorrection );
+ m_readcdReader->setImagePath( m_imagePath );
+ m_readcdReader->setClone( true );
+ m_readcdReader->setRetries( m_readRetries );
+}
+
+
+void K3bCloneJob::prepareWriter()
+{
+ if( !m_writerJob ) {
+ m_writerJob = new K3bCdrecordWriter( writer(), this, this );
+ connect( m_writerJob, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_writerJob, SIGNAL(percent(int)), this, SLOT(slotWriterPercent(int)) );
+ connect( m_writerJob, SIGNAL(percent(int)), this, SIGNAL(subPercent(int)) );
+ connect( m_writerJob, SIGNAL(nextTrack(int, int)), this, SLOT(slotWriterNextTrack(int, int)) );
+ connect( m_writerJob, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSubSize(int, int)) );
+ connect( m_writerJob, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) );
+ connect( m_writerJob, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
+ connect( m_writerJob, SIGNAL(writeSpeed(int, int)), this, SIGNAL(writeSpeed(int, int)) );
+ connect( m_writerJob, SIGNAL(finished(bool)), this, SLOT(slotWriterFinished(bool)) );
+ // connect( m_writerJob, SIGNAL(newTask(const QString&)), this, SIGNAL(newTask(const QString&)) );
+ connect( m_writerJob, SIGNAL(newSubTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( m_writerJob, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+ }
+
+ m_writerJob->clearArguments();
+ m_writerJob->setWritingMode( K3b::RAW );
+ m_writerJob->setClone( true );
+ m_writerJob->setSimulate( m_simulate );
+ m_writerJob->setBurnSpeed( m_speed );
+ m_writerJob->addArgument( m_imagePath );
+}
+
+
+void K3bCloneJob::cancel()
+{
+ if( m_running ) {
+ m_canceled = true;
+ if( m_readcdReader )
+ m_readcdReader->cancel();
+ if( m_writerJob )
+ m_writerJob->cancel();
+ }
+}
+
+
+void K3bCloneJob::slotWriterPercent( int p )
+{
+ if( m_onlyBurnExistingImage )
+ emit percent( (int)((double)(d->doneCopies)*100.0/(double)(m_copies) + (double)p/(double)(m_copies)) );
+ else
+ emit percent( (int)((double)(1+d->doneCopies)*100.0/(double)(1+m_copies) + (double)p/(double)(1+m_copies)) );
+}
+
+
+void K3bCloneJob::slotWriterNextTrack( int t, int tt )
+{
+ emit newSubTask( i18n("Writing Track %1 of %2").arg(t).arg(tt) );
+}
+
+
+void K3bCloneJob::slotWriterFinished( bool success )
+{
+ if( m_canceled ) {
+ removeImageFiles();
+ m_running = false;
+ emit canceled();
+ jobFinished(false);
+ return;
+ }
+
+ if( success ) {
+ d->doneCopies++;
+
+ emit infoMessage( i18n("Successfully written clone copy %1.").arg(d->doneCopies), INFO );
+
+ if( d->doneCopies < m_copies ) {
+ K3bDevice::eject( writer() );
+ startWriting();
+ }
+ else {
+ if( m_removeImageFiles )
+ removeImageFiles();
+ m_running = false;
+ jobFinished(true);
+ }
+ }
+ else {
+ removeImageFiles();
+ m_running = false;
+ jobFinished(false);
+ }
+}
+
+
+void K3bCloneJob::slotReadingPercent( int p )
+{
+ emit percent( m_onlyCreateImage ? p : (int)((double)p/(double)(1+m_copies)) );
+}
+
+
+void K3bCloneJob::slotReadingFinished( bool success )
+{
+ if( m_canceled ) {
+ removeImageFiles();
+ m_running = false;
+ emit canceled();
+ jobFinished(false);
+ return;
+ }
+
+ if( success ) {
+ //
+ // Make a quick test if the image is really valid.
+ // Readcd does not seem to have proper exit codes
+ //
+ K3bCloneTocReader ctr( m_imagePath );
+ if( ctr.isValid() ) {
+ emit infoMessage( i18n("Successfully read disk."), INFO );
+ if( m_onlyCreateImage ) {
+ m_running = false;
+ jobFinished(true);
+ }
+ else {
+ if( writer() == readingDevice() )
+ K3bDevice::eject( writer() );
+ startWriting();
+ }
+ }
+ else {
+ emit infoMessage( i18n("Failed to read disk completely in clone mode."), ERROR );
+ removeImageFiles();
+ m_running = false;
+ jobFinished(false);
+ }
+ }
+ else {
+ emit infoMessage( i18n("Error while reading disk."), ERROR );
+ removeImageFiles();
+ m_running = false;
+ jobFinished(false);
+ }
+}
+
+
+void K3bCloneJob::startWriting()
+{
+ emit burning( true );
+
+ // start writing
+ prepareWriter();
+
+ if( waitForMedia( writer(),
+ K3bDevice::STATE_EMPTY,
+ K3bDevice::MEDIA_WRITABLE_CD ) < 0 ) {
+ removeImageFiles();
+ m_running = false;
+ emit canceled();
+ jobFinished(false);
+ return;
+ }
+
+ if( m_simulate )
+ emit newTask( i18n("Simulating clone copy") );
+ else
+ emit newTask( i18n("Writing clone copy %1").arg(d->doneCopies+1) );
+
+ m_writerJob->start();
+}
+
+
+void K3bCloneJob::removeImageFiles()
+{
+ if( !m_onlyBurnExistingImage ) {
+ emit infoMessage( i18n("Removing image files."), INFO );
+ if( QFile::exists( m_imagePath ) )
+ QFile::remove( m_imagePath );
+ if( QFile::exists( m_imagePath + ".toc" ) )
+ QFile::remove( m_imagePath + ".toc" );
+ }
+}
+
+
+QString K3bCloneJob::jobDescription() const
+{
+ if( m_onlyCreateImage )
+ return i18n("Creating Clone Image");
+ else if( m_onlyBurnExistingImage ) {
+ if( m_simulate )
+ return i18n("Simulating Clone Image");
+ else
+ return i18n("Burning Clone Image");
+ }
+ else if( m_simulate )
+ return i18n("Simulating CD Cloning");
+ else
+ return i18n("Cloning CD");
+}
+
+
+QString K3bCloneJob::jobDetails() const
+{
+ return i18n("Creating 1 clone copy",
+ "Creating %n clone copies",
+ (m_simulate||m_onlyCreateImage) ? 1 : m_copies );
+}
+
+#include "k3bclonejob.moc"
diff --git a/libk3b/jobs/k3bclonejob.h b/libk3b/jobs/k3bclonejob.h
new file mode 100644
index 0000000..80c8ea9
--- /dev/null
+++ b/libk3b/jobs/k3bclonejob.h
@@ -0,0 +1,99 @@
+/*
+ *
+ * $Id: k3bclonejob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_CLONE_JOB_H_
+#define _K3B_CLONE_JOB_H_
+
+#include <k3bjob.h>
+#include "k3b_export.h"
+#include <qstring.h>
+
+
+namespace K3bDevice {
+ class Device;
+}
+class K3bCdrecordWriter;
+class K3bReadcdReader;
+
+
+class LIBK3B_EXPORT K3bCloneJob : public K3bBurnJob
+{
+ Q_OBJECT
+
+ public:
+ K3bCloneJob( K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ ~K3bCloneJob();
+
+ K3bDevice::Device* writer() const { return m_writerDevice; }
+ K3bDevice::Device* readingDevice() const { return m_readerDevice; }
+
+ QString jobDescription() const;
+ QString jobDetails() const;
+
+ public slots:
+ void start();
+ void cancel();
+
+ void setWriterDevice( K3bDevice::Device* w ) { m_writerDevice = w; }
+ void setReaderDevice( K3bDevice::Device* w ) { m_readerDevice = w; }
+ void setImagePath( const QString& p ) { m_imagePath = p; }
+ void setNoCorrection( bool b ) { m_noCorrection = b; }
+ void setRemoveImageFiles( bool b ) { m_removeImageFiles = b; }
+ void setOnlyCreateImage( bool b ) { m_onlyCreateImage = b; }
+ void setOnlyBurnExistingImage( bool b ) { m_onlyBurnExistingImage = b; }
+ void setSimulate( bool b ) { m_simulate = b; }
+ void setWriteSpeed( int s ) { m_speed = s; }
+ void setCopies( int c ) { m_copies = c; }
+ void setReadRetries( int i ) { m_readRetries = i; }
+
+ private slots:
+ void slotWriterPercent( int );
+ void slotWriterFinished( bool );
+ void slotWriterNextTrack( int, int );
+ void slotReadingPercent( int );
+ void slotReadingFinished( bool );
+
+ private:
+ void removeImageFiles();
+ void prepareReader();
+ void prepareWriter();
+ void startWriting();
+
+ K3bDevice::Device* m_writerDevice;
+ K3bDevice::Device* m_readerDevice;
+ QString m_imagePath;
+
+ K3bCdrecordWriter* m_writerJob;
+ K3bReadcdReader* m_readcdReader;
+
+ bool m_noCorrection;
+ bool m_removeImageFiles;
+
+ bool m_canceled;
+ bool m_running;
+
+ bool m_simulate;
+ int m_speed;
+ int m_copies;
+ bool m_onlyCreateImage;
+ bool m_onlyBurnExistingImage;
+ int m_readRetries;
+
+ class Private;
+ Private* d;
+};
+
+
+#endif
diff --git a/libk3b/jobs/k3bclonetocreader.cpp b/libk3b/jobs/k3bclonetocreader.cpp
new file mode 100644
index 0000000..5dd8b8b
--- /dev/null
+++ b/libk3b/jobs/k3bclonetocreader.cpp
@@ -0,0 +1,235 @@
+/*
+ *
+ * $Id: k3bclonetocreader.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include <config.h>
+
+
+#include "k3bclonetocreader.h"
+
+#include <k3bdeviceglobals.h>
+#include <k3bglobals.h>
+
+#include <qfile.h>
+#include <qtextstream.h>
+
+#include <kdebug.h>
+
+
+class K3bCloneTocReader::Private
+{
+public:
+ Private()
+ : size(0) {
+ }
+
+ K3b::Msf size;
+ QString tocFile;
+};
+
+
+
+K3bCloneTocReader::K3bCloneTocReader( const QString& filename )
+ : K3bImageFileReader()
+{
+ d = new Private;
+ openFile( filename );
+}
+
+
+K3bCloneTocReader::~K3bCloneTocReader()
+{
+ delete d;
+}
+
+
+const K3b::Msf& K3bCloneTocReader::imageSize() const
+{
+ return d->size;
+}
+
+
+void K3bCloneTocReader::readFile()
+{
+ // first of all we check if we find the image file which contains the data for this toc
+ // cdrecord always uses this strange file naming:
+ // somedata
+ // somedata.toc
+
+ // filename should always be the toc file
+ if( filename().right( 4 ) == ".toc" )
+ d->tocFile = filename();
+ else
+ d->tocFile = filename() + ".toc";
+
+ // now get rid of the ".toc" extension
+ QString imageFileName = d->tocFile.left( d->tocFile.length()-4 );
+ if( !QFile::exists( imageFileName ) ) {
+ kdDebug() << "(K3bCloneTocReader) could not find image file " << imageFileName << endl;
+ return;
+ }
+
+ setImageFilename( imageFileName );
+
+ d->size = 0;
+
+ QFile f( d->tocFile );
+ if( f.open( IO_ReadOnly ) ) {
+ //
+ // Inspired by clone.c from the cdrecord sources
+ //
+ char buffer[2048];
+ int read = f.readBlock( buffer, 2048 );
+ f.close();
+
+ if( read == 2048 ) {
+ kdDebug() << "(K3bCloneTocReader) TOC too large." << endl;
+ return;
+ }
+
+ // the toc starts with a tocheader
+ struct tocheader {
+ unsigned char len[2];
+ unsigned char first; // first session
+ unsigned char last; // last session
+ };
+
+ struct tocheader* th = (struct tocheader*)buffer;
+ int dataLen = K3bDevice::from2Byte( th->len ) + 2; // the len field does not include it's own length
+
+ if( th->first != 1 ) {
+ kdDebug() << "(K3bCloneTocReader) first session != 1" << endl;
+ return;
+ }
+
+ // the following bytes are multiple instances of
+ struct ftrackdesc {
+ unsigned char sess_number;
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ unsigned char adr : 4;
+ unsigned char control : 4;
+#else
+ unsigned char control : 4;
+ unsigned char adr : 4;
+#endif
+ unsigned char track;
+ unsigned char point;
+ unsigned char amin;
+ unsigned char asec;
+ unsigned char aframe;
+ unsigned char res7;
+ unsigned char pmin;
+ unsigned char psec;
+ unsigned char pframe;
+ };
+
+ for( int i = 4; i < dataLen; i += 11) {
+ struct ftrackdesc* ft = (struct ftrackdesc*)&buffer[i];
+
+ if( ft->sess_number != 1 ) {
+ kdDebug() << "(K3bCloneTocReader} session number != 1" << endl;
+ return;
+ }
+
+ // now we check some of the values
+ if( ft->point >= 0x1 && ft->point <= 0x63 ) {
+ if( ft->adr == 1 ) {
+ // check track starttime
+ if( ft->psec > 60 || ft->pframe > 75 ) {
+ kdDebug() << "(K3bCloneTocReader) invalid track start: "
+ << (int)ft->pmin << "."
+ << (int)ft->psec << "."
+ << (int)ft->pframe << endl;
+ return;
+ }
+ }
+ }
+ else {
+ switch( ft->point ) {
+ case 0xa0:
+ if( ft->adr != 1 ) {
+ kdDebug() << "(K3bCloneTocReader) adr != 1" << endl;
+ return;
+ }
+
+ // disk type in psec
+ if( ft->psec != 0x00 && ft->psec != 0x10 && ft->psec != 0x20 ) {
+ kdDebug() << "(K3bCloneTocReader) invalid disktype: " << ft->psec << endl;
+ return;
+ }
+
+ if( ft->pmin != 1 ) {
+ kdDebug() << "(K3bCloneTocReader) first track number != 1 " << endl;
+ return;
+ }
+
+ if( ft->pframe != 0x0 ) {
+ kdDebug() << "(K3bCloneTocReader) found data when there should be 0x0" << endl;
+ return;
+ }
+ break;
+
+ case 0xa1:
+ if( ft->adr != 1 ) {
+ kdDebug() << "(K3bCloneTocReader) adr != 1" << endl;
+ return;
+ }
+
+ if( !(ft->pmin >= 1) ) {
+ kdDebug() << "(K3bCloneTocReader) last track number needs to be >= 1." << endl;
+ return;
+ }
+ if( ft->psec != 0x0 || ft->pframe != 0x0 ) {
+ kdDebug() << "(K3bCloneTocReader) found data when there should be 0x0" << endl;
+ return;
+ }
+ break;
+
+ case 0xa2:
+ if( ft->adr != 1 ) {
+ kdDebug() << "(K3bCloneTocReader) adr != 1" << endl;
+ return;
+ }
+
+ // start of the leadout = size of the image
+ // substract 2 seconds since in cdrecord other than in K3b lba 0 = msf 2:00
+ // (the cdrecord way is actually more accurate but we use k3b::Msf for many
+ // things and it is simpler this way.)
+ d->size = K3b::Msf( ft->pmin, ft->psec, ft->pframe ) - K3b::Msf( 0, 2, 0 );
+
+ // leadout... no check so far...
+ break;
+
+ default:
+ if( ft->adr != 5 ) {
+ kdDebug() << "(K3bCloneTocReader) adr != 5" << endl;
+ return;
+ }
+ break;
+ }
+ }
+ }
+
+ if( d->size.rawBytes() != K3b::filesize( imageFileName ) ) {
+ kdDebug() << "(K3bCloneTocReader) image file size invalid." << endl;
+ return;
+ }
+
+ // ok, could be a cdrecord toc file
+ setValid(true);
+ }
+ else {
+ kdDebug() << "(K3bCloneTocReader) could not open file " << d->tocFile << endl;
+ }
+}
diff --git a/libk3b/jobs/k3bclonetocreader.h b/libk3b/jobs/k3bclonetocreader.h
new file mode 100644
index 0000000..17e80d7
--- /dev/null
+++ b/libk3b/jobs/k3bclonetocreader.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * $Id: k3bclonetocreader.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_CLONETOC_FILE_PARSER_H_
+#define _K3B_CLONETOC_FILE_PARSER_H_
+
+#include "k3bimagefilereader.h"
+
+#include <k3bmsf.h>
+
+#include "k3b_export.h"
+
+
+/**
+ * Reads a cdrecord clone toc file and searches for the
+ * corresponding image file.
+ */
+class LIBK3B_EXPORT K3bCloneTocReader : public K3bImageFileReader
+{
+ public:
+ K3bCloneTocReader( const QString& filename = QString::null );
+ ~K3bCloneTocReader();
+
+ const K3b::Msf& imageSize() const;
+
+ protected:
+ void readFile();
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/jobs/k3bdatatrackreader.cpp b/libk3b/jobs/k3bdatatrackreader.cpp
new file mode 100644
index 0000000..8300ada
--- /dev/null
+++ b/libk3b/jobs/k3bdatatrackreader.cpp
@@ -0,0 +1,515 @@
+/*
+ *
+ * $Id: k3bdatatrackreader.cpp 690529 2007-07-21 10:51:47Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bdatatrackreader.h"
+
+#include <k3blibdvdcss.h>
+#include <k3bdevice.h>
+#include <k3bdeviceglobals.h>
+#include <k3btrack.h>
+#include <k3bthread.h>
+#include <k3bcore.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qfile.h>
+
+#include <unistd.h>
+
+
+
+// FIXME: determine max DMA buffer size
+static int s_bufferSizeSectors = 10;
+
+
+class K3bDataTrackReader::WorkThread : public K3bThread
+{
+public:
+ WorkThread();
+ ~WorkThread();
+
+ void init();
+ void run();
+ int read( unsigned char* buffer, unsigned long sector, unsigned int len );
+ bool retryRead( unsigned char* buffer, unsigned long startSector, unsigned int len );
+ bool setErrorRecovery( K3bDevice::Device* dev, int code );
+ void cancel();
+
+ bool m_canceled;
+ bool m_ignoreReadErrors;
+ bool m_noCorrection;
+ int m_retries;
+ K3bDevice::Device* m_device;
+ K3b::Msf m_firstSector;
+ K3b::Msf m_lastSector;
+ K3b::Msf m_nextReadSector;
+ int m_fd;
+ QString m_imagePath;
+ int m_sectorSize;
+ bool m_useLibdvdcss;
+ K3bLibDvdCss* m_libcss;
+
+ int m_oldErrorRecoveryMode;
+
+ int m_errorSectorCount;
+
+private:
+ int m_usedSectorSize;
+};
+
+
+K3bDataTrackReader::K3bDataTrackReader( K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bThreadJob( jh, parent, name )
+{
+ m_thread = new WorkThread();
+ setThread( m_thread );
+}
+
+
+K3bDataTrackReader::WorkThread::WorkThread()
+ : K3bThread(),
+ m_canceled(false),
+ m_ignoreReadErrors(false),
+ m_noCorrection(false),
+ m_retries(10),
+ m_device(0),
+ m_fd(-1),
+ m_libcss(0)
+{
+}
+
+
+K3bDataTrackReader::WorkThread::~WorkThread()
+{
+ delete m_libcss;
+}
+
+
+void K3bDataTrackReader::WorkThread::init()
+{
+ m_canceled = false;
+}
+
+
+void K3bDataTrackReader::WorkThread::run()
+{
+ emitStarted();
+
+ if( !m_device->open() ) {
+ emitInfoMessage( i18n("Could not open device %1").arg(m_device->blockDeviceName()), K3bJob::ERROR );
+ emitFinished(false);
+ return;
+ }
+
+ // 1. determine sector size by checking the first sectors mode
+ // if impossible or MODE2 (mode2 formless) finish(false)
+
+ m_useLibdvdcss = false;
+ m_usedSectorSize = m_sectorSize;
+ if( m_device->isDVD() ) {
+ m_usedSectorSize = MODE1;
+
+ //
+ // In case of an encrypted VideoDVD we read with libdvdcss which takes care of decrypting the vobs
+ //
+ if( m_device->copyrightProtectionSystemType() == 1 ) {
+
+ // close the device for libdvdcss
+ m_device->close();
+
+ kdDebug() << "(K3bDataTrackReader::WorkThread) found encrypted dvd. using libdvdcss." << endl;
+
+ // open the libdvdcss stuff
+ if( !m_libcss )
+ m_libcss = K3bLibDvdCss::create();
+ if( !m_libcss ) {
+ emitInfoMessage( i18n("Unable to open libdvdcss."), K3bJob::ERROR );
+ emitFinished(false);
+ return;
+ }
+
+ if( !m_libcss->open(m_device) ) {
+ emitInfoMessage( i18n("Could not open device %1").arg(m_device->blockDeviceName()), K3bJob::ERROR );
+ emitFinished(false);
+ return;
+ }
+
+ emitInfoMessage( i18n("Retrieving all CSS keys. This might take a while."), K3bJob::INFO );
+ if( !m_libcss->crackAllKeys() ) {
+ m_libcss->close();
+ emitInfoMessage( i18n("Failed to retrieve all CSS keys."), K3bJob::ERROR );
+ emitInfoMessage( i18n("Video DVD decryption failed."), K3bJob::ERROR );
+ emitFinished(false);
+ return;
+ }
+
+ m_useLibdvdcss = true;
+ }
+ }
+ else {
+ if( m_usedSectorSize == AUTO ) {
+ switch( m_device->getDataMode( m_firstSector ) ) {
+ case K3bDevice::Track::MODE1:
+ case K3bDevice::Track::DVD:
+ m_usedSectorSize = MODE1;
+ break;
+ case K3bDevice::Track::XA_FORM1:
+ m_usedSectorSize = MODE2FORM1;
+ break;
+ case K3bDevice::Track::XA_FORM2:
+ m_usedSectorSize = MODE2FORM2;
+ break;
+ case K3bDevice::Track::MODE2:
+ emitInfoMessage( i18n("No support for reading formless Mode2 sectors."), K3bJob::ERROR );
+ default:
+ emitInfoMessage( i18n("Unsupported sector type."), K3bJob::ERROR );
+ m_device->close();
+ emitFinished(false);
+ return;
+ }
+ }
+ }
+
+ emitInfoMessage( i18n("Reading with sector size %1.").arg(m_usedSectorSize), K3bJob::INFO );
+ emitDebuggingOutput( "K3bDataTrackReader",
+ QString("reading sectors %1 to %2 with sector size %3. Length: %4 sectors, %5 bytes.")
+ .arg( m_firstSector.lba() )
+ .arg( m_lastSector.lba() )
+ .arg( m_usedSectorSize )
+ .arg( m_lastSector.lba() - m_firstSector.lba() + 1 )
+ .arg( Q_UINT64(m_usedSectorSize) * (Q_UINT64)(m_lastSector.lba() - m_firstSector.lba() + 1) ) );
+
+ QFile file;
+ if( m_fd == -1 ) {
+ file.setName( m_imagePath );
+ if( !file.open( IO_WriteOnly ) ) {
+ m_device->close();
+ if( m_useLibdvdcss )
+ m_libcss->close();
+ emitInfoMessage( i18n("Unable to open '%1' for writing.").arg(m_imagePath), K3bJob::ERROR );
+ emitFinished( false );
+ return;
+ }
+ }
+
+ k3bcore->blockDevice( m_device );
+ m_device->block( true );
+
+ //
+ // set the error recovery mode to 0x21 or 0x20 depending on m_ignoreReadErrors
+ // TODO: should we also set RC=1 in m_ignoreReadErrors mode (0x11 because TB is ignored)
+ //
+ setErrorRecovery( m_device, m_noCorrection ? 0x21 : 0x20 );
+
+ //
+ // Let the drive determine the optimal reading speed
+ //
+ m_device->setSpeed( 0xffff, 0xffff );
+
+ s_bufferSizeSectors = 128;
+ unsigned char* buffer = new unsigned char[m_usedSectorSize*s_bufferSizeSectors];
+ while( s_bufferSizeSectors > 0 && read( buffer, m_firstSector.lba(), s_bufferSizeSectors ) < 0 ) {
+ kdDebug() << "(K3bDataTrackReader) determine max read sectors: "
+ << s_bufferSizeSectors << " too high." << endl;
+ s_bufferSizeSectors--;
+ }
+ kdDebug() << "(K3bDataTrackReader) determine max read sectors: "
+ << s_bufferSizeSectors << " is max." << endl;
+
+ // s_bufferSizeSectors = K3bDevice::determineMaxReadingBufferSize( m_device, m_firstSector );
+ if( s_bufferSizeSectors <= 0 ) {
+ emitInfoMessage( i18n("Error while reading sector %1.").arg(m_firstSector.lba()), K3bJob::ERROR );
+ emitFinished(false);
+ m_device->block( false );
+ k3bcore->unblockDevice( m_device );
+ return;
+ }
+
+ kdDebug() << "(K3bDataTrackReader) using buffer size of " << s_bufferSizeSectors << " blocks." << endl;
+ emitDebuggingOutput( "K3bDataTrackReader", QString("using buffer size of %1 blocks.").arg( s_bufferSizeSectors ) );
+
+ // 2. get it on
+ K3b::Msf currentSector = m_firstSector;
+ K3b::Msf totalReadSectors;
+ m_nextReadSector = 0;
+ m_errorSectorCount = 0;
+ bool writeError = false;
+ bool readError = false;
+ int lastPercent = 0;
+ unsigned long lastReadMb = 0;
+ int bufferLen = s_bufferSizeSectors*m_usedSectorSize;
+ while( !m_canceled && currentSector <= m_lastSector ) {
+
+ int maxReadSectors = QMIN( bufferLen/m_usedSectorSize, m_lastSector.lba()-currentSector.lba()+1 );
+
+ int readSectors = read( buffer,
+ currentSector.lba(),
+ maxReadSectors );
+ if( readSectors < 0 ) {
+ if( !retryRead( buffer,
+ currentSector.lba(),
+ maxReadSectors ) ) {
+ readError = true;
+ break;
+ }
+ else
+ readSectors = maxReadSectors;
+ }
+
+ totalReadSectors += readSectors;
+
+ int readBytes = readSectors * m_usedSectorSize;
+
+ if( m_fd != -1 ) {
+ if( ::write( m_fd, reinterpret_cast<void*>(buffer), readBytes ) != readBytes ) {
+ kdDebug() << "(K3bDataTrackReader::WorkThread) error while writing to fd " << m_fd
+ << " current sector: " << (currentSector.lba()-m_firstSector.lba()) << endl;
+ emitDebuggingOutput( "K3bDataTrackReader",
+ QString("Error while writing to fd %1. Current sector is %2.")
+ .arg(m_fd).arg(currentSector.lba()-m_firstSector.lba()) );
+ writeError = true;
+ break;
+ }
+ }
+ else {
+ if( file.writeBlock( reinterpret_cast<char*>(buffer), readBytes ) != readBytes ) {
+ kdDebug() << "(K3bDataTrackReader::WorkThread) error while writing to file " << m_imagePath
+ << " current sector: " << (currentSector.lba()-m_firstSector.lba()) << endl;
+ emitDebuggingOutput( "K3bDataTrackReader",
+ QString("Error while writing to file %1. Current sector is %2.")
+ .arg(m_imagePath).arg(currentSector.lba()-m_firstSector.lba()) );
+ writeError = true;
+ break;
+ }
+ }
+
+ currentSector += readSectors;
+
+ int percent = 100 * (currentSector.lba() - m_firstSector.lba() + 1 ) /
+ (m_lastSector.lba() - m_firstSector.lba() + 1 );
+
+ if( percent > lastPercent ) {
+ lastPercent = percent;
+ emitPercent( percent );
+ }
+
+ unsigned long readMb = (currentSector.lba() - m_firstSector.lba() + 1) / 512;
+ if( readMb > lastReadMb ) {
+ lastReadMb = readMb;
+ emitProcessedSize( readMb, ( m_lastSector.lba() - m_firstSector.lba() + 1 ) / 512 );
+ }
+ }
+
+ if( m_errorSectorCount > 0 )
+ emitInfoMessage( i18n("Ignored %n erroneous sector.", "Ignored a total of %n erroneous sectors.", m_errorSectorCount ),
+ K3bJob::ERROR );
+
+ // reset the error recovery mode
+ setErrorRecovery( m_device, m_oldErrorRecoveryMode );
+
+ m_device->block( false );
+ k3bcore->unblockDevice( m_device );
+
+ // cleanup
+ if( m_useLibdvdcss )
+ m_libcss->close();
+ m_device->close();
+ delete [] buffer;
+
+ emitDebuggingOutput( "K3bDataTrackReader",
+ QString("Read a total of %1 sectors (%2 bytes)")
+ .arg(totalReadSectors.lba())
+ .arg((Q_UINT64)totalReadSectors.lba()*(Q_UINT64)m_usedSectorSize) );
+
+ if( m_canceled )
+ emitCanceled();
+
+ emitFinished( !m_canceled && !writeError && !readError );
+}
+
+
+int K3bDataTrackReader::WorkThread::read( unsigned char* buffer, unsigned long sector, unsigned int len )
+{
+
+ //
+ // Encrypted DVD reading with libdvdcss
+ //
+ if( m_useLibdvdcss ) {
+ return m_libcss->readWrapped( reinterpret_cast<void*>(buffer), sector, len );
+ }
+
+ //
+ // Standard reading
+ //
+ else {
+ bool success = false;
+ // setErrorRecovery( m_device, m_ignoreReadErrors ? 0x21 : 0x20 );
+ if( m_usedSectorSize == 2048 )
+ success = m_device->read10( buffer, len*2048, sector, len );
+ else
+ success = m_device->readCd( buffer,
+ len*m_usedSectorSize,
+ 0, // all sector types
+ false, // no dap
+ sector,
+ len,
+ false, // no sync
+ false, // no header
+ m_usedSectorSize != MODE1, // subheader
+ true, // user data
+ false, // no edc/ecc
+ 0, // no c2 error info... FIXME: should we check this??
+ 0 // no subchannel data
+ );
+
+ if( success )
+ return len;
+ else
+ return -1;
+ }
+}
+
+
+// here we read every single sector for itself to find the troubleing ones
+bool K3bDataTrackReader::WorkThread::retryRead( unsigned char* buffer, unsigned long startSector, unsigned int len )
+{
+ emitDebuggingOutput( "K3bDataTrackReader", QString( "Problem while reading. Retrying from sector %1.").arg(startSector) );
+ emitInfoMessage( i18n("Problem while reading. Retrying from sector %1.").arg(startSector), K3bJob::WARNING );
+
+ int sectorsRead = -1;
+ bool success = true;
+ for( unsigned long sector = startSector; sector < startSector+len; ++sector ) {
+ int retry = m_retries;
+ while( !m_canceled && retry && (sectorsRead = read( &buffer[( sector - startSector ) * m_usedSectorSize], sector, 1 )) < 0 )
+ --retry;
+
+ success = ( sectorsRead > 0 );
+
+ if( m_canceled )
+ return false;
+
+ if( !success ) {
+ if( m_ignoreReadErrors ) {
+ emitInfoMessage( i18n("Ignoring read error in sector %1.").arg(sector), K3bJob::ERROR );
+ emitDebuggingOutput( "K3bDataTrackReader", QString( "Ignoring read error in sector %1.").arg(sector) );
+
+ ++m_errorSectorCount;
+ // ::memset( &buffer[i], 0, 1 );
+ success = true;
+ }
+ else {
+ emitInfoMessage( i18n("Error while reading sector %1.").arg(sector), K3bJob::ERROR );
+ emitDebuggingOutput( "K3bDataTrackReader", QString( "Read error in sector %1.").arg(sector) );
+ break;
+ }
+ }
+ }
+
+ return success;
+}
+
+
+bool K3bDataTrackReader::WorkThread::setErrorRecovery( K3bDevice::Device* dev, int code )
+{
+ unsigned char* data = 0;
+ unsigned int dataLen = 0;
+ if( !dev->modeSense( &data, dataLen, 0x01 ) )
+ return false;
+
+ // in MMC1 the page has 8 bytes (12 in MMC4 but we only need the first 3 anyway)
+ if( dataLen < 8+8 ) {
+ kdDebug() << "(K3bDataTrackReader) modepage 0x01 data too small: " << dataLen << endl;
+ delete [] data;
+ return false;
+ }
+
+ m_oldErrorRecoveryMode = data[8+2];
+ data[8+2] = code;
+
+ if( m_oldErrorRecoveryMode != code )
+ kdDebug() << "(K3bDataTrackReader) changing data recovery mode from " << m_oldErrorRecoveryMode << " to " << code << endl;
+
+ bool success = dev->modeSelect( data, dataLen, true, false );
+
+ delete [] data;
+
+ return success;
+}
+
+
+void K3bDataTrackReader::WorkThread::cancel()
+{
+ m_canceled = true;
+}
+
+
+
+
+
+K3bDataTrackReader::~K3bDataTrackReader()
+{
+ delete m_thread;
+}
+
+
+void K3bDataTrackReader::setDevice( K3bDevice::Device* dev )
+{
+ m_thread->m_device = dev;
+}
+
+
+void K3bDataTrackReader::setSectorRange( const K3b::Msf& start, const K3b::Msf& end )
+{
+ m_thread->m_firstSector = start;
+ m_thread->m_lastSector = end;
+}
+
+
+void K3bDataTrackReader::setRetries( int r )
+{
+ m_thread->m_retries = r;
+}
+
+
+void K3bDataTrackReader::setIgnoreErrors( bool b )
+{
+ m_thread->m_ignoreReadErrors = b;
+}
+
+
+void K3bDataTrackReader::setNoCorrection( bool b )
+{
+ m_thread->m_noCorrection = b;
+}
+
+
+void K3bDataTrackReader::writeToFd( int fd )
+{
+ m_thread->m_fd = fd;
+}
+
+
+void K3bDataTrackReader::setImagePath( const QString& p )
+{
+ m_thread->m_imagePath = p;
+ m_thread->m_fd = -1;
+}
+
+
+void K3bDataTrackReader::setSectorSize( SectorSize size )
+{
+ m_thread->m_sectorSize = size;
+}
diff --git a/libk3b/jobs/k3bdatatrackreader.h b/libk3b/jobs/k3bdatatrackreader.h
new file mode 100644
index 0000000..814c01c
--- /dev/null
+++ b/libk3b/jobs/k3bdatatrackreader.h
@@ -0,0 +1,87 @@
+/*
+ *
+ * $Id: k3bdatatrackreader.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_DATATRACK_READER_H_
+#define _K3B_DATATRACK_READER_H_
+
+
+#include <k3bthreadjob.h>
+#include <k3bmsf.h>
+#include <k3bglobals.h>
+
+namespace K3bDevice {
+ class Device;
+}
+
+
+/**
+ * This is a replacement for readcd. We need this since
+ * it is not possible to influence the sector size used
+ * by readcd and readcd is not very good to handle anyway.
+ *
+ * The sector size read is the following:
+ * @li Mode1: 2048 bytes (only user data)
+ * @li Mode2 Form1: 2056 bytes containing the subheader and the user data
+ * @li Mode2 Form2: 2332 bytes containing the subheader and the user data
+ *
+ * Formless Mode2 sectors will not be read.
+ */
+class K3bDataTrackReader : public K3bThreadJob
+{
+ public:
+ K3bDataTrackReader( K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ ~K3bDataTrackReader();
+
+ enum SectorSize {
+ AUTO = 0,
+ MODE1 = K3b::SECTORSIZE_DATA_2048,
+ MODE2FORM1 = K3b::SECTORSIZE_DATA_2048_SUBHEADER,
+ MODE2FORM2 = K3b::SECTORSIZE_DATA_2324_SUBHEADER
+ };
+
+ void setSectorSize( SectorSize size );
+
+ void setDevice( K3bDevice::Device* );
+
+ /**
+ * @param start the first sector to be read
+ * @end the last sector to be read
+ */
+ void setSectorRange( const K3b::Msf& start, const K3b::Msf& end );
+ void setRetries( int );
+
+ /**
+ * If true unreadable sectors will be replaced by zero data to always
+ * maintain the track length.
+ */
+ void setIgnoreErrors( bool b );
+
+ void setNoCorrection( bool b );
+
+ /**
+ * the data gets written directly into fd instead of the imagefile.
+ * Be aware that this only makes sense before starting the job.
+ * To disable just set fd to -1
+ */
+ void writeToFd( int fd );
+
+ void setImagePath( const QString& p );
+
+ private:
+ class WorkThread;
+ WorkThread* m_thread;
+};
+
+#endif
diff --git a/libk3b/jobs/k3bdvdcopyjob.cpp b/libk3b/jobs/k3bdvdcopyjob.cpp
new file mode 100644
index 0000000..96d727c
--- /dev/null
+++ b/libk3b/jobs/k3bdvdcopyjob.cpp
@@ -0,0 +1,894 @@
+/*
+ *
+ * $Id: k3bdvdcopyjob.cpp 690529 2007-07-21 10:51:47Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bdvdcopyjob.h"
+#include "k3blibdvdcss.h"
+
+#include <k3breadcdreader.h>
+#include <k3bdatatrackreader.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bdevice.h>
+#include <k3bdeviceglobals.h>
+#include <k3bdevicehandler.h>
+#include <k3bdiskinfo.h>
+#include <k3bglobals.h>
+#include <k3bcore.h>
+#include <k3bgrowisofswriter.h>
+#include <k3bversion.h>
+#include <k3biso9660.h>
+#include <k3bfilesplitter.h>
+#include <k3bchecksumpipe.h>
+#include <k3bverificationjob.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kio/global.h>
+
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qapplication.h>
+
+
+class K3bDvdCopyJob::Private
+{
+public:
+ Private()
+ : doneCopies(0),
+ running(false),
+ canceled(false),
+ writerJob(0),
+ readcdReader(0),
+ dataTrackReader(0),
+ verificationJob(0),
+ usedWritingMode(0),
+ verifyData(false) {
+ outPipe.readFromIODevice( &imageFile );
+ }
+
+ int doneCopies;
+
+ bool running;
+ bool readerRunning;
+ bool writerRunning;
+ bool canceled;
+
+ K3bGrowisofsWriter* writerJob;
+ K3bReadcdReader* readcdReader;
+ K3bDataTrackReader* dataTrackReader;
+ K3bVerificationJob* verificationJob;
+
+ K3bDevice::DiskInfo sourceDiskInfo;
+
+ K3b::Msf lastSector;
+
+ int usedWritingMode;
+
+ K3bFileSplitter imageFile;
+ K3bChecksumPipe inPipe;
+ K3bActivePipe outPipe;
+
+ bool verifyData;
+};
+
+
+K3bDvdCopyJob::K3bDvdCopyJob( K3bJobHandler* hdl, QObject* parent, const char* name )
+ : K3bBurnJob( hdl, parent, name ),
+ m_writerDevice(0),
+ m_readerDevice(0),
+ m_onTheFly(false),
+ m_removeImageFiles(false),
+ m_simulate(false),
+ m_speed(1),
+ m_copies(1),
+ m_onlyCreateImage(false),
+ m_ignoreReadErrors(false),
+ m_readRetries(128),
+ m_writingMode( K3b::WRITING_MODE_AUTO )
+{
+ d = new Private();
+}
+
+
+K3bDvdCopyJob::~K3bDvdCopyJob()
+{
+ delete d;
+}
+
+
+void K3bDvdCopyJob::start()
+{
+ jobStarted();
+ emit burning(false);
+
+ d->canceled = false;
+ d->running = true;
+ d->readerRunning = d->writerRunning = false;
+
+ emit newTask( i18n("Checking Source Medium") );
+
+ if( m_onTheFly &&
+ k3bcore->externalBinManager()->binObject( "growisofs" )->version < K3bVersion( 5, 12 ) ) {
+ m_onTheFly = false;
+ emit infoMessage( i18n("K3b does not support writing on-the-fly with growisofs %1.")
+ .arg(k3bcore->externalBinManager()->binObject( "growisofs" )->version), ERROR );
+ emit infoMessage( i18n("Disabling on-the-fly writing."), INFO );
+ }
+
+ emit newSubTask( i18n("Waiting for source medium") );
+
+ // wait for a source disk
+ if( waitForMedia( m_readerDevice,
+ K3bDevice::STATE_COMPLETE|K3bDevice::STATE_INCOMPLETE,
+ K3bDevice::MEDIA_WRITABLE_DVD|K3bDevice::MEDIA_DVD_ROM ) < 0 ) {
+ emit canceled();
+ d->running = false;
+ jobFinished( false );
+ return;
+ }
+
+ emit newSubTask( i18n("Checking source medium") );
+
+ connect( K3bDevice::sendCommand( K3bDevice::DeviceHandler::DISKINFO, m_readerDevice ),
+ SIGNAL(finished(K3bDevice::DeviceHandler*)),
+ this,
+ SLOT(slotDiskInfoReady(K3bDevice::DeviceHandler*)) );
+}
+
+
+void K3bDvdCopyJob::slotDiskInfoReady( K3bDevice::DeviceHandler* dh )
+{
+ if( d->canceled ) {
+ emit canceled();
+ jobFinished(false);
+ d->running = false;
+ }
+
+ d->sourceDiskInfo = dh->diskInfo();
+
+ if( dh->diskInfo().empty() || dh->diskInfo().diskState() == K3bDevice::STATE_NO_MEDIA ) {
+ emit infoMessage( i18n("No source medium found."), ERROR );
+ jobFinished(false);
+ d->running = false;
+ }
+ else {
+ if( m_readerDevice->copyrightProtectionSystemType() == 1 ) {
+ emit infoMessage( i18n("Found encrypted DVD."), WARNING );
+ // check for libdvdcss
+ bool haveLibdvdcss = false;
+ kdDebug() << "(K3bDvdCopyJob) trying to open libdvdcss." << endl;
+ if( K3bLibDvdCss* libcss = K3bLibDvdCss::create() ) {
+ kdDebug() << "(K3bDvdCopyJob) succeeded." << endl;
+ kdDebug() << "(K3bDvdCopyJob) dvdcss_open(" << m_readerDevice->blockDeviceName() << ") = "
+ << libcss->open(m_readerDevice) << endl;
+ haveLibdvdcss = true;
+
+ delete libcss;
+ }
+ else
+ kdDebug() << "(K3bDvdCopyJob) failed." << endl;
+
+ if( !haveLibdvdcss ) {
+ emit infoMessage( i18n("Cannot copy encrypted DVDs."), ERROR );
+ d->running = false;
+ jobFinished( false );
+ return;
+ }
+ }
+
+
+ //
+ // We cannot rely on the kernel to determine the size of the DVD for some reason
+ // On the other hand it is not always a good idea to rely on the size from the ISO9660
+ // header since that may be wrong due to some buggy encoder or some boot code appended
+ // after creating the image.
+ // That is why we try our best to determine the size of the DVD. For DVD-ROM this is very
+ // easy since it has only one track. The same goes for single session DVD-R(W) and DVD+R.
+ // Multisession DVDs we will simply not copy. ;)
+ // For DVD+RW and DVD-RW in restricted overwrite mode we are left with no other choice but
+ // to use the ISO9660 header.
+ //
+ // On the other hand: in on-the-fly mode growisofs determines the size of the data to be written
+ // by looking at the ISO9660 header when writing in DAO mode. So in this case
+ // it would be best for us to do the same....
+ //
+ // With growisofs 5.15 we have the option to specify the size of the image to be written in DAO mode.
+ //
+
+ switch( dh->diskInfo().mediaType() ) {
+ case K3bDevice::MEDIA_DVD_ROM:
+ case K3bDevice::MEDIA_DVD_PLUS_R_DL:
+ case K3bDevice::MEDIA_DVD_R_DL:
+ case K3bDevice::MEDIA_DVD_R_DL_SEQ:
+ case K3bDevice::MEDIA_DVD_R_DL_JUMP:
+ if( !m_onlyCreateImage ) {
+ if( dh->diskInfo().numLayers() > 1 &&
+ dh->diskInfo().size().mode1Bytes() > 4700372992LL ) {
+ if( !(m_writerDevice->type() & (K3bDevice::DEVICE_DVD_R_DL|K3bDevice::DEVICE_DVD_PLUS_R_DL)) ) {
+ emit infoMessage( i18n("The writer does not support writing Double Layer DVD."), ERROR );
+ d->running = false;
+ jobFinished(false);
+ return;
+ }
+ // FIXME: check for growisofs 5.22 (or whatever version is needed) for DVD-R DL
+ else if( k3bcore->externalBinManager()->binObject( "growisofs" ) &&
+ k3bcore->externalBinManager()->binObject( "growisofs" )->version < K3bVersion( 5, 20 ) ) {
+ emit infoMessage( i18n("Growisofs >= 5.20 is needed to write Double Layer DVD+R."), ERROR );
+ d->running = false;
+ jobFinished(false);
+ return;
+ }
+ }
+ }
+ case K3bDevice::MEDIA_DVD_R:
+ case K3bDevice::MEDIA_DVD_R_SEQ:
+ case K3bDevice::MEDIA_DVD_RW:
+ case K3bDevice::MEDIA_DVD_RW_SEQ:
+ case K3bDevice::MEDIA_DVD_PLUS_R:
+
+ if( dh->diskInfo().numSessions() > 1 ) {
+ emit infoMessage( i18n("K3b does not support copying multi-session DVDs."), ERROR );
+ d->running = false;
+ jobFinished(false);
+ return;
+ }
+
+ // growisofs only uses the size from the PVD for reserving
+ // writable space in DAO mode
+ // with version >= 5.15 growisofs supports specifying the size of the track
+ if( m_writingMode != K3b::DAO || !m_onTheFly || m_onlyCreateImage ||
+ ( k3bcore->externalBinManager()->binObject( "growisofs" ) &&
+ k3bcore->externalBinManager()->binObject( "growisofs" )->version >= K3bVersion( 5, 15, -1 ) ) ) {
+ d->lastSector = dh->toc().lastSector();
+ break;
+ }
+
+ // fallthrough
+
+ case K3bDevice::MEDIA_DVD_PLUS_RW:
+ case K3bDevice::MEDIA_DVD_RW_OVWR:
+ {
+ emit infoMessage( i18n("K3b relies on the size saved in the ISO9660 header."), WARNING );
+ emit infoMessage( i18n("This might result in a corrupt copy if the source was mastered with buggy software."), WARNING );
+
+ K3bIso9660 isoF( m_readerDevice, 0 );
+ if( isoF.open() ) {
+ d->lastSector = ((long long)isoF.primaryDescriptor().logicalBlockSize*isoF.primaryDescriptor().volumeSpaceSize)/2048LL - 1;
+ }
+ else {
+ emit infoMessage( i18n("Unable to determine the ISO9660 filesystem size."), ERROR );
+ jobFinished(false);
+ d->running = false;
+ return;
+ }
+ }
+ break;
+
+ case K3bDevice::MEDIA_DVD_RAM:
+ emit infoMessage( i18n("K3b does not support copying DVD-RAM."), ERROR );
+ jobFinished(false);
+ d->running = false;
+ return;
+
+ default:
+ emit infoMessage( i18n("Unable to determine DVD media type."), ERROR );
+ jobFinished(false);
+ d->running = false;
+ return;
+ }
+
+
+ if( !m_onTheFly ) {
+ //
+ // Check the image path
+ //
+ QFileInfo fi( m_imagePath );
+ if( !fi.isFile() ||
+ questionYesNo( i18n("Do you want to overwrite %1?").arg(m_imagePath),
+ i18n("File Exists") ) ) {
+ if( fi.isDir() )
+ m_imagePath = K3b::findTempFile( "iso", m_imagePath );
+ else if( !QFileInfo( m_imagePath.section( '/', 0, -2 ) ).isDir() ) {
+ emit infoMessage( i18n("Specified an unusable temporary path. Using default."), WARNING );
+ m_imagePath = K3b::findTempFile( "iso" );
+ }
+ // else the user specified a file in an existing dir
+
+ emit infoMessage( i18n("Writing image file to %1.").arg(m_imagePath), INFO );
+ emit newSubTask( i18n("Reading source medium.") );
+ }
+
+ //
+ // check free temp space
+ //
+ KIO::filesize_t imageSpaceNeeded = (KIO::filesize_t)(d->lastSector.lba()+1)*2048;
+ unsigned long avail, size;
+ QString pathToTest = m_imagePath.left( m_imagePath.findRev( '/' ) );
+ if( !K3b::kbFreeOnFs( pathToTest, size, avail ) ) {
+ emit infoMessage( i18n("Unable to determine free space in temporary directory '%1'.").arg(pathToTest), ERROR );
+ jobFinished(false);
+ d->running = false;
+ return;
+ }
+ else {
+ if( avail < imageSpaceNeeded/1024 ) {
+ emit infoMessage( i18n("Not enough space left in temporary directory."), ERROR );
+ jobFinished(false);
+ d->running = false;
+ return;
+ }
+ }
+
+ d->imageFile.setName( m_imagePath );
+ if( !d->imageFile.open( IO_WriteOnly ) ) {
+ emit infoMessage( i18n("Unable to open '%1' for writing.").arg(m_imagePath), ERROR );
+ jobFinished( false );
+ d->running = false;
+ return;
+ }
+ }
+
+ if( K3b::isMounted( m_readerDevice ) ) {
+ emit infoMessage( i18n("Unmounting source medium"), INFO );
+ K3b::unmount( m_readerDevice );
+ }
+
+ if( m_onlyCreateImage || !m_onTheFly ) {
+ emit newTask( i18n("Creating DVD image") );
+ }
+ else if( m_onTheFly && !m_onlyCreateImage ) {
+ if( waitForDvd() ) {
+ prepareWriter();
+ if( m_simulate )
+ emit newTask( i18n("Simulating DVD copy") );
+ else if( m_copies > 1 )
+ emit newTask( i18n("Writing DVD copy %1").arg(d->doneCopies+1) );
+ else
+ emit newTask( i18n("Writing DVD copy") );
+
+ emit burning(true);
+ d->writerRunning = true;
+ d->writerJob->start();
+ }
+ else {
+ if( d->canceled )
+ emit canceled();
+ jobFinished(false);
+ d->running = false;
+ return;
+ }
+ }
+
+ prepareReader();
+ d->readerRunning = true;
+ d->dataTrackReader->start();
+ }
+}
+
+
+void K3bDvdCopyJob::cancel()
+{
+ if( d->running ) {
+ d->canceled = true;
+ if( d->readerRunning )
+ d->dataTrackReader->cancel();
+ if( d->writerRunning )
+ d->writerJob->cancel();
+ d->inPipe.close();
+ d->outPipe.close();
+ d->imageFile.close();
+ }
+ else {
+ kdDebug() << "(K3bDvdCopyJob) not running." << endl;
+ }
+}
+
+
+void K3bDvdCopyJob::prepareReader()
+{
+ if( !d->dataTrackReader ) {
+ d->dataTrackReader = new K3bDataTrackReader( this );
+ connect( d->dataTrackReader, SIGNAL(percent(int)), this, SLOT(slotReaderProgress(int)) );
+ connect( d->dataTrackReader, SIGNAL(processedSize(int, int)), this, SLOT(slotReaderProcessedSize(int, int)) );
+ connect( d->dataTrackReader, SIGNAL(finished(bool)), this, SLOT(slotReaderFinished(bool)) );
+ connect( d->dataTrackReader, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( d->dataTrackReader, SIGNAL(newTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( d->dataTrackReader, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+ }
+
+ d->dataTrackReader->setDevice( m_readerDevice );
+ d->dataTrackReader->setIgnoreErrors( m_ignoreReadErrors );
+ d->dataTrackReader->setRetries( m_readRetries );
+ d->dataTrackReader->setSectorRange( 0, d->lastSector );
+
+ if( m_onTheFly && !m_onlyCreateImage )
+ d->inPipe.writeToFd( d->writerJob->fd(), true );
+ else
+ d->inPipe.writeToIODevice( &d->imageFile );
+
+ d->inPipe.open( true );
+ d->dataTrackReader->writeToFd( d->inPipe.in() );
+}
+
+
+// ALWAYS CALL WAITFORDVD BEFORE PREPAREWRITER!
+void K3bDvdCopyJob::prepareWriter()
+{
+ delete d->writerJob;
+
+ d->writerJob = new K3bGrowisofsWriter( m_writerDevice, this );
+
+ connect( d->writerJob, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( d->writerJob, SIGNAL(percent(int)), this, SLOT(slotWriterProgress(int)) );
+ connect( d->writerJob, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSize(int, int)) );
+ connect( d->writerJob, SIGNAL(processedSubSize(int, int)), this, SIGNAL(processedSubSize(int, int)) );
+ connect( d->writerJob, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) );
+ connect( d->writerJob, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
+ connect( d->writerJob, SIGNAL(writeSpeed(int, int)), this, SIGNAL(writeSpeed(int, int)) );
+ connect( d->writerJob, SIGNAL(finished(bool)), this, SLOT(slotWriterFinished(bool)) );
+ // connect( d->writerJob, SIGNAL(newTask(const QString&)), this, SIGNAL(newTask(const QString&)) );
+ connect( d->writerJob, SIGNAL(newSubTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( d->writerJob, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+
+ // these do only make sense with DVD-R(W)
+ d->writerJob->setSimulate( m_simulate );
+ d->writerJob->setBurnSpeed( m_speed );
+ d->writerJob->setWritingMode( d->usedWritingMode );
+ d->writerJob->setCloseDvd( true );
+
+ //
+ // In case the first layer size is not known let the
+ // split be determined by growisofs
+ //
+ if( d->sourceDiskInfo.numLayers() > 1 &&
+ d->sourceDiskInfo.firstLayerSize() > 0 ) {
+ d->writerJob->setLayerBreak( d->sourceDiskInfo.firstLayerSize().lba() );
+ }
+ else {
+ // this is only used in DAO mode with growisofs >= 5.15
+ d->writerJob->setTrackSize( d->lastSector.lba()+1 );
+ }
+
+ d->writerJob->setImageToWrite( QString::null ); // write to stdin
+}
+
+
+void K3bDvdCopyJob::slotReaderProgress( int p )
+{
+ if( !m_onTheFly || m_onlyCreateImage ) {
+ emit subPercent( p );
+
+ int bigParts = ( m_onlyCreateImage ? 1 : (m_simulate ? 2 : ( d->verifyData ? m_copies*2 : m_copies ) + 1 ) );
+ emit percent( p/bigParts );
+ }
+}
+
+
+void K3bDvdCopyJob::slotReaderProcessedSize( int p, int c )
+{
+ if( !m_onTheFly || m_onlyCreateImage )
+ emit processedSubSize( p, c );
+
+ if( m_onlyCreateImage )
+ emit processedSize( p, c );
+}
+
+
+void K3bDvdCopyJob::slotWriterProgress( int p )
+{
+ int bigParts = ( m_simulate ? 1 : ( d->verifyData ? m_copies*2 : m_copies ) ) + ( m_onTheFly ? 0 : 1 );
+ int doneParts = ( m_simulate ? 0 : ( d->verifyData ? d->doneCopies*2 : d->doneCopies ) ) + ( m_onTheFly ? 0 : 1 );
+ emit percent( 100*doneParts/bigParts + p/bigParts );
+
+ emit subPercent( p );
+}
+
+
+void K3bDvdCopyJob::slotVerificationProgress( int p )
+{
+ int bigParts = ( m_simulate ? 1 : ( d->verifyData ? m_copies*2 : m_copies ) ) + ( m_onTheFly ? 0 : 1 );
+ int doneParts = ( m_simulate ? 0 : ( d->verifyData ? d->doneCopies*2 : d->doneCopies ) ) + ( m_onTheFly ? 0 : 1 ) + 1;
+ emit percent( 100*doneParts/bigParts + p/bigParts );
+}
+
+
+void K3bDvdCopyJob::slotReaderFinished( bool success )
+{
+ d->readerRunning = false;
+
+ d->inPipe.close();
+
+ // close the socket
+ // otherwise growisofs will never quit.
+ // FIXME: is it posiible to do this in a generic manner?
+ if( d->writerJob )
+ d->writerJob->closeFd();
+
+ // already finished?
+ if( !d->running )
+ return;
+
+ if( d->canceled ) {
+ removeImageFiles();
+ emit canceled();
+ jobFinished(false);
+ d->running = false;
+ }
+
+ if( success ) {
+ emit infoMessage( i18n("Successfully read source DVD."), SUCCESS );
+ if( m_onlyCreateImage ) {
+ jobFinished(true);
+ d->running = false;
+ }
+ else {
+ if( m_writerDevice == m_readerDevice ) {
+ // eject the media (we do this blocking to know if it worked
+ // because if it did not it might happen that k3b overwrites a CD-RW
+ // source)
+ if( !m_readerDevice->eject() ) {
+ blockingInformation( i18n("K3b was unable to eject the source disk. Please do so manually.") );
+ }
+ }
+
+ if( !m_onTheFly ) {
+ if( waitForDvd() ) {
+ prepareWriter();
+ if( m_copies > 1 )
+ emit newTask( i18n("Writing DVD copy %1").arg(d->doneCopies+1) );
+ else
+ emit newTask( i18n("Writing DVD copy") );
+
+ emit burning(true);
+
+ d->writerRunning = true;
+ d->writerJob->start();
+ d->outPipe.writeToFd( d->writerJob->fd(), true );
+ d->outPipe.open( true );
+ }
+ else {
+ if( m_removeImageFiles )
+ removeImageFiles();
+ if( d->canceled )
+ emit canceled();
+ jobFinished(false);
+ d->running = false;
+ }
+ }
+ }
+ }
+ else {
+ removeImageFiles();
+ jobFinished(false);
+ d->running = false;
+ }
+}
+
+
+void K3bDvdCopyJob::slotWriterFinished( bool success )
+{
+ d->writerRunning = false;
+
+ d->outPipe.close();
+
+ // already finished?
+ if( !d->running )
+ return;
+
+ if( d->canceled ) {
+ if( m_removeImageFiles )
+ removeImageFiles();
+ emit canceled();
+ jobFinished(false);
+ d->running = false;
+ }
+
+ if( success ) {
+ emit infoMessage( i18n("Successfully written DVD copy %1.").arg(d->doneCopies+1), INFO );
+
+ if( d->verifyData && !m_simulate ) {
+ if( !d->verificationJob ) {
+ d->verificationJob = new K3bVerificationJob( this, this );
+ connect( d->verificationJob, SIGNAL(infoMessage(const QString&, int)),
+ this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( d->verificationJob, SIGNAL(newTask(const QString&)),
+ this, SIGNAL(newSubTask(const QString&)) );
+ connect( d->verificationJob, SIGNAL(percent(int)),
+ this, SLOT(slotVerificationProgress(int)) );
+ connect( d->verificationJob, SIGNAL(percent(int)),
+ this, SIGNAL(subPercent(int)) );
+ connect( d->verificationJob, SIGNAL(finished(bool)),
+ this, SLOT(slotVerificationFinished(bool)) );
+ connect( d->verificationJob, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+
+ }
+ d->verificationJob->setDevice( m_writerDevice );
+ d->verificationJob->addTrack( 1, d->inPipe.checksum(), d->lastSector+1 );
+
+ if( m_copies > 1 )
+ emit newTask( i18n("Verifying DVD copy %1").arg(d->doneCopies+1) );
+ else
+ emit newTask( i18n("Verifying DVD copy") );
+
+ emit burning( false );
+
+ d->verificationJob->start();
+ }
+
+ else if( ++d->doneCopies < m_copies ) {
+
+ if ( !m_writerDevice->eject() ) {
+ blockingInformation( i18n("K3b was unable to eject the written disk. Please do so manually.") );
+ }
+
+ if( waitForDvd() ) {
+ prepareWriter();
+ emit newTask( i18n("Writing DVD copy %1").arg(d->doneCopies+1) );
+
+ emit burning(true);
+
+ d->writerRunning = true;
+ d->writerJob->start();
+ }
+ else {
+ if( d->canceled )
+ emit canceled();
+ jobFinished(false);
+ d->running = false;
+ return;
+ }
+
+ if( m_onTheFly ) {
+ prepareReader();
+ d->readerRunning = true;
+ d->dataTrackReader->start();
+ }
+ else {
+ d->outPipe.writeToFd( d->writerJob->fd(), true );
+ d->outPipe.open( true );
+ }
+ }
+ else {
+ if( m_removeImageFiles )
+ removeImageFiles();
+ d->running = false;
+ jobFinished(true);
+ }
+ }
+ else {
+ if( m_removeImageFiles )
+ removeImageFiles();
+ d->running = false;
+ jobFinished(false);
+ }
+}
+
+
+void K3bDvdCopyJob::slotVerificationFinished( bool success )
+{
+ // we simply ignore the results from the verification, the verification
+ // job already emits a message
+ if( ++d->doneCopies < m_copies ) {
+
+ if( waitForDvd() ) {
+ prepareWriter();
+ emit newTask( i18n("Writing DVD copy %1").arg(d->doneCopies+1) );
+
+ emit burning(true);
+
+ d->writerRunning = true;
+ d->writerJob->start();
+ }
+ else {
+ if( d->canceled )
+ emit canceled();
+ jobFinished(false);
+ d->running = false;
+ return;
+ }
+
+ if( m_onTheFly ) {
+ prepareReader();
+ d->readerRunning = true;
+ d->dataTrackReader->start();
+ }
+ else {
+ d->outPipe.writeToFd( d->writerJob->fd(), true );
+ d->outPipe.open( true );
+ }
+ }
+ else {
+ if( m_removeImageFiles )
+ removeImageFiles();
+ d->running = false;
+ jobFinished( success );
+ }
+}
+
+
+// this is basically the same code as in K3bDvdJob... :(
+// perhaps this should be moved to some K3bGrowisofsHandler which also parses the growisofs output?
+bool K3bDvdCopyJob::waitForDvd()
+{
+ int mt = 0;
+ if( m_writingMode == K3b::WRITING_MODE_RES_OVWR ) // we treat DVD+R(W) as restricted overwrite media
+ mt = K3bDevice::MEDIA_DVD_RW_OVWR|K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_PLUS_R;
+ else
+ mt = K3bDevice::MEDIA_WRITABLE_DVD_SL;
+
+ //
+ // in case the source is a double layer DVD we made sure above that the writer
+ // is capable of writing DVD+R-DL or DVD-R DL and here we wait for a DL DVD
+ //
+ if( d->sourceDiskInfo.numLayers() > 1 &&
+ d->sourceDiskInfo.size().mode1Bytes() > 4700372992LL ) {
+ mt = K3bDevice::MEDIA_WRITABLE_DVD_DL;
+ }
+
+ int m = waitForMedia( m_writerDevice, K3bDevice::STATE_EMPTY, mt );
+
+ if( m < 0 ) {
+ cancel();
+ return false;
+ }
+
+ if( m == 0 ) {
+ emit infoMessage( i18n("Forced by user. Growisofs will be called without further tests."), INFO );
+ }
+
+ else {
+ // -------------------------------
+ // DVD Plus
+ // -------------------------------
+ if( m & K3bDevice::MEDIA_DVD_PLUS_ALL ) {
+
+ d->usedWritingMode = K3b::WRITING_MODE_RES_OVWR;
+
+ if( m_simulate ) {
+ if( !questionYesNo( i18n("K3b does not support simulation with DVD+R(W) media. "
+ "Do you really want to continue? The media will actually be "
+ "written to."),
+ i18n("No Simulation with DVD+R(W)") ) ) {
+ cancel();
+ return false;
+ }
+
+// m_simulate = false;
+ emit newTask( i18n("Writing DVD copy") );
+ }
+
+ if( m_writingMode != K3b::WRITING_MODE_AUTO && m_writingMode != K3b::WRITING_MODE_RES_OVWR )
+ emit infoMessage( i18n("Writing mode ignored when writing DVD+R(W) media."), INFO );
+
+ if( m & K3bDevice::MEDIA_DVD_PLUS_RW )
+ emit infoMessage( i18n("Writing DVD+RW."), INFO );
+ else if( m & K3bDevice::MEDIA_DVD_PLUS_R_DL )
+ emit infoMessage( i18n("Writing Double Layer DVD+R."), INFO );
+ else
+ emit infoMessage( i18n("Writing DVD+R."), INFO );
+ }
+
+ // -------------------------------
+ // DVD Minus
+ // -------------------------------
+ else {
+ if( m_simulate && !m_writerDevice->dvdMinusTestwrite() ) {
+ if( !questionYesNo( i18n("Your writer (%1 %2) does not support simulation with DVD-R(W) media. "
+ "Do you really want to continue? The media will be written "
+ "for real.")
+ .arg(m_writerDevice->vendor())
+ .arg(m_writerDevice->description()),
+ i18n("No Simulation with DVD-R(W)") ) ) {
+ cancel();
+ return false;
+ }
+
+// m_simulate = false;
+ }
+
+ //
+ // We do not default to DAO in onthefly mode since otherwise growisofs would
+ // use the size from the PVD to reserve space on the DVD and that can be bad
+ // if this size is wrong
+ // With growisofs 5.15 we have the option to specify the size of the image to be written in DAO mode.
+ //
+// bool sizeWithDao = ( k3bcore->externalBinManager()->binObject( "growisofs" ) &&
+// k3bcore->externalBinManager()->binObject( "growisofs" )->version >= K3bVersion( 5, 15, -1 ) );
+
+
+ // TODO: check for feature 0x21
+
+ if( m & K3bDevice::MEDIA_DVD_RW_OVWR ) {
+ emit infoMessage( i18n("Writing DVD-RW in restricted overwrite mode."), INFO );
+ d->usedWritingMode = K3b::WRITING_MODE_RES_OVWR;
+ }
+ else if( m & (K3bDevice::MEDIA_DVD_RW_SEQ|
+ K3bDevice::MEDIA_DVD_RW) ) {
+ if( m_writingMode == K3b::DAO ) {
+// ( m_writingMode == K3b::WRITING_MODE_AUTO &&
+// ( sizeWithDao || !m_onTheFly ) ) ) {
+ emit infoMessage( i18n("Writing DVD-RW in DAO mode."), INFO );
+ d->usedWritingMode = K3b::DAO;
+ }
+ else {
+ emit infoMessage( i18n("Writing DVD-RW in incremental mode."), INFO );
+ d->usedWritingMode = K3b::WRITING_MODE_INCR_SEQ;
+ }
+ }
+ else {
+
+ // FIXME: DVD-R DL jump and stuff
+
+ if( m_writingMode == K3b::WRITING_MODE_RES_OVWR )
+ emit infoMessage( i18n("Restricted Overwrite is not possible with DVD-R media."), INFO );
+
+ if( m_writingMode == K3b::DAO ) {
+// ( m_writingMode == K3b::WRITING_MODE_AUTO &&
+// ( sizeWithDao || !m_onTheFly ) ) ) {
+ emit infoMessage( i18n("Writing %1 in DAO mode.").arg( K3bDevice::mediaTypeString(m, true) ), INFO );
+ d->usedWritingMode = K3b::DAO;
+ }
+ else {
+ emit infoMessage( i18n("Writing %1 in incremental mode.").arg( K3bDevice::mediaTypeString(m, true) ), INFO );
+ d->usedWritingMode = K3b::WRITING_MODE_INCR_SEQ;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+
+
+void K3bDvdCopyJob::removeImageFiles()
+{
+ if( QFile::exists( m_imagePath ) ) {
+ d->imageFile.remove();
+ emit infoMessage( i18n("Removed image file %1").arg(m_imagePath), K3bJob::SUCCESS );
+ }
+}
+
+
+QString K3bDvdCopyJob::jobDescription() const
+{
+ if( m_onlyCreateImage ) {
+ return i18n("Creating DVD Image");
+ }
+ else {
+ if( m_onTheFly )
+ return i18n("Copying DVD On-The-Fly");
+ else
+ return i18n("Copying DVD");
+ }
+}
+
+
+QString K3bDvdCopyJob::jobDetails() const
+{
+ return i18n("Creating 1 copy",
+ "Creating %n copies",
+ (m_simulate||m_onlyCreateImage) ? 1 : m_copies );
+}
+
+
+void K3bDvdCopyJob::setVerifyData( bool b )
+{
+ d->verifyData = b;
+}
+
+#include "k3bdvdcopyjob.moc"
diff --git a/libk3b/jobs/k3bdvdcopyjob.h b/libk3b/jobs/k3bdvdcopyjob.h
new file mode 100644
index 0000000..91da4e9
--- /dev/null
+++ b/libk3b/jobs/k3bdvdcopyjob.h
@@ -0,0 +1,99 @@
+/*
+ *
+ * $Id: k3bdvdcopyjob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_DVD_COPY_JOB_H_
+#define _K3B_DVD_COPY_JOB_H_
+
+#include <k3bjob.h>
+#include "k3b_export.h"
+#include <qstring.h>
+
+
+namespace K3bDevice {
+ class Device;
+ class DeviceHandler;
+}
+
+
+class LIBK3B_EXPORT K3bDvdCopyJob : public K3bBurnJob
+{
+ Q_OBJECT
+
+ public:
+ K3bDvdCopyJob( K3bJobHandler* hdl, QObject* parent = 0, const char* name = 0 );
+ ~K3bDvdCopyJob();
+
+ K3bDevice::Device* writer() const { return m_onlyCreateImage ? 0 : m_writerDevice; }
+ K3bDevice::Device* readingDevice() const { return m_readerDevice; }
+
+ QString jobDescription() const;
+ QString jobDetails() const;
+
+ public slots:
+ void start();
+ void cancel();
+
+ void setWriterDevice( K3bDevice::Device* w ) { m_writerDevice = w; }
+ void setReaderDevice( K3bDevice::Device* w ) { m_readerDevice = w; }
+ void setImagePath( const QString& p ) { m_imagePath = p; }
+ void setRemoveImageFiles( bool b ) { m_removeImageFiles = b; }
+ void setOnlyCreateImage( bool b ) { m_onlyCreateImage = b; }
+ void setSimulate( bool b ) { m_simulate = b; }
+ void setOnTheFly( bool b ) { m_onTheFly = b; }
+ void setWriteSpeed( int s ) { m_speed = s; }
+ void setCopies( int c ) { m_copies = c; }
+ void setWritingMode( int w ) { m_writingMode = w; }
+ void setIgnoreReadErrors( bool b ) { m_ignoreReadErrors = b; }
+ void setReadRetries( int i ) { m_readRetries = i; }
+ void setVerifyData( bool b );
+
+ private slots:
+ void slotDiskInfoReady( K3bDevice::DeviceHandler* );
+ void slotReaderProgress( int );
+ void slotReaderProcessedSize( int, int );
+ void slotWriterProgress( int );
+ void slotReaderFinished( bool );
+ void slotWriterFinished( bool );
+ void slotVerificationFinished( bool );
+ void slotVerificationProgress( int p );
+
+ private:
+ bool waitForDvd();
+ void prepareReader();
+ void prepareWriter();
+ void removeImageFiles();
+
+ K3bDevice::Device* m_writerDevice;
+ K3bDevice::Device* m_readerDevice;
+ QString m_imagePath;
+
+ bool m_onTheFly;
+ bool m_removeImageFiles;
+
+ bool m_simulate;
+ int m_speed;
+ int m_copies;
+ bool m_onlyCreateImage;
+ bool m_ignoreReadErrors;
+ int m_readRetries;
+
+ int m_writingMode;
+
+ class Private;
+ Private* d;
+};
+
+
+#endif
diff --git a/libk3b/jobs/k3bdvdformattingjob.cpp b/libk3b/jobs/k3bdvdformattingjob.cpp
new file mode 100644
index 0000000..732e404
--- /dev/null
+++ b/libk3b/jobs/k3bdvdformattingjob.cpp
@@ -0,0 +1,536 @@
+/*
+ *
+ * $Id: k3bdvdformattingjob.cpp 696897 2007-08-06 07:14:14Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bdvdformattingjob.h"
+
+#include <k3bglobals.h>
+#include <k3bprocess.h>
+#include <k3bdevice.h>
+#include <k3bdeviceglobals.h>
+#include <k3bdevicehandler.h>
+#include <k3bdiskinfo.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bcore.h>
+#include <k3bversion.h>
+#include <k3bglobalsettings.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qvaluelist.h>
+#include <qregexp.h>
+
+#include <errno.h>
+#include <string.h>
+
+
+class K3bDvdFormattingJob::Private
+{
+public:
+ Private()
+ : quick(false),
+ force(false),
+ mode(K3b::WRITING_MODE_AUTO),
+ device(0),
+ process(0),
+ dvdFormatBin(0),
+ lastProgressValue(0),
+ running(false),
+ forceNoEject(false) {
+ }
+
+ bool quick;
+ bool force;
+ int mode;
+
+ K3bDevice::Device* device;
+ K3bProcess* process;
+ const K3bExternalBin* dvdFormatBin;
+
+ int lastProgressValue;
+
+ bool success;
+ bool canceled;
+ bool running;
+
+ bool forceNoEject;
+
+ bool error;
+};
+
+
+K3bDvdFormattingJob::K3bDvdFormattingJob( K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bBurnJob( jh, parent, name )
+{
+ d = new Private;
+}
+
+
+K3bDvdFormattingJob::~K3bDvdFormattingJob()
+{
+ delete d->process;
+ delete d;
+}
+
+
+K3bDevice::Device* K3bDvdFormattingJob::writer() const
+{
+ return d->device;
+}
+
+
+void K3bDvdFormattingJob::setForceNoEject( bool b )
+{
+ d->forceNoEject = b;
+}
+
+
+QString K3bDvdFormattingJob::jobDescription() const
+{
+ return i18n("Formatting DVD"); // Formatting DVD±RW
+}
+
+
+QString K3bDvdFormattingJob::jobDetails() const
+{
+ if( d->quick )
+ return i18n("Quick Format");
+ else
+ return QString::null;
+}
+
+
+void K3bDvdFormattingJob::start()
+{
+ d->canceled = false;
+ d->running = true;
+ d->error = false;
+
+ jobStarted();
+
+ if( !d->device ) {
+ emit infoMessage( i18n("No device set"), ERROR );
+ d->running = false;
+ jobFinished(false);
+ return;
+ }
+
+ // FIXME: check the return value
+ if( K3b::isMounted( d->device ) ) {
+ emit infoMessage( i18n("Unmounting medium"), INFO );
+ K3b::unmount( d->device );
+ }
+
+ //
+ // first wait for a dvd+rw or dvd-rw
+ // Be aware that an empty DVD-RW might be reformatted to another writing mode
+ // so we also wait for empty dvds
+ //
+ if( waitForMedia( d->device,
+ K3bDevice::STATE_COMPLETE|K3bDevice::STATE_INCOMPLETE|K3bDevice::STATE_EMPTY,
+ K3bDevice::MEDIA_WRITABLE_DVD,
+ i18n("Please insert a rewritable DVD medium into drive<p><b>%1 %2 (%3)</b>.")
+ .arg(d->device->vendor()).arg(d->device->description()).arg(d->device->devicename()) ) == -1 ) {
+ emit canceled();
+ d->running = false;
+ jobFinished(false);
+ return;
+ }
+
+ emit infoMessage( i18n("Checking media..."), INFO );
+ emit newTask( i18n("Checking media") );
+
+ connect( K3bDevice::sendCommand( K3bDevice::DeviceHandler::NG_DISKINFO, d->device ),
+ SIGNAL(finished(K3bDevice::DeviceHandler*)),
+ this,
+ SLOT(slotDeviceHandlerFinished(K3bDevice::DeviceHandler*)) );
+}
+
+
+void K3bDvdFormattingJob::start( const K3bDevice::DiskInfo& di )
+{
+ d->canceled = false;
+ d->running = true;
+
+ jobStarted();
+
+ startFormatting( di );
+}
+
+
+void K3bDvdFormattingJob::cancel()
+{
+ if( d->running ) {
+ d->canceled = true;
+ if( d->process )
+ d->process->kill();
+ }
+ else {
+ kdDebug() << "(K3bDvdFormattingJob) not running." << endl;
+ }
+}
+
+
+void K3bDvdFormattingJob::setDevice( K3bDevice::Device* dev )
+{
+ d->device = dev;
+}
+
+
+void K3bDvdFormattingJob::setMode( int m )
+{
+ d->mode = m;
+}
+
+
+void K3bDvdFormattingJob::setQuickFormat( bool b )
+{
+ d->quick = b;
+}
+
+
+void K3bDvdFormattingJob::setForce( bool b )
+{
+ d->force = b;
+}
+
+
+void K3bDvdFormattingJob::slotStderrLine( const QString& line )
+{
+// * DVDRW format utility by <appro@fy.chalmers.se>, version 4.4.
+// * 4.7GB DVD-RW media in Sequential mode detected.
+// * blanking 100.0|
+
+// * formatting 100.0|
+
+ emit debuggingOutput( "dvd+rw-format", line );
+
+ // parsing for the -gui mode (since dvd+rw-format 4.6)
+ int pos = line.find( "blanking" );
+ if( pos < 0 )
+ pos = line.find( "formatting" );
+ if( pos >= 0 ) {
+ pos = line.find( QRegExp( "\\d" ), pos );
+ }
+ // parsing for \b\b... stuff
+ else if( !line.startsWith("*") ) {
+ pos = line.find( QRegExp( "\\d" ) );
+ }
+ else if( line.startsWith( ":-(" ) ) {
+ if( line.startsWith( ":-( unable to proceed with format" ) ) {
+ d->error = true;
+ }
+ }
+
+ if( pos >= 0 ) {
+ int endPos = line.find( QRegExp("[^\\d\\.]"), pos ) - 1;
+ bool ok;
+ int progress = (int)(line.mid( pos, endPos - pos ).toDouble(&ok));
+ if( ok ) {
+ d->lastProgressValue = progress;
+ emit percent( progress );
+ }
+ else {
+ kdDebug() << "(K3bDvdFormattingJob) parsing error: '" << line.mid( pos, endPos - pos ) << "'" << endl;
+ }
+ }
+}
+
+
+void K3bDvdFormattingJob::slotProcessFinished( KProcess* p )
+{
+ if( d->canceled ) {
+ emit canceled();
+ d->success = false;
+ }
+ else if( p->normalExit() ) {
+ if( !d->error && p->exitStatus() == 0 ) {
+ emit infoMessage( i18n("Formatting successfully completed"), K3bJob::SUCCESS );
+
+ if( d->lastProgressValue < 100 ) {
+ emit infoMessage( i18n("Do not be concerned with the progress stopping before 100%."), INFO );
+ emit infoMessage( i18n("The formatting will continue in the background while writing."), INFO );
+ }
+
+ d->success = true;
+ }
+ else {
+ emit infoMessage( i18n("%1 returned an unknown error (code %2).").arg(d->dvdFormatBin->name()).arg(p->exitStatus()),
+ K3bJob::ERROR );
+ emit infoMessage( i18n("Please send me an email with the last output."), K3bJob::ERROR );
+
+ d->success = false;
+ }
+ }
+ else {
+ emit infoMessage( i18n("%1 did not exit cleanly.").arg(d->dvdFormatBin->name()),
+ ERROR );
+ d->success = false;
+ }
+
+ if( d->forceNoEject ||
+ !k3bcore->globalSettings()->ejectMedia() ) {
+ d->running = false;
+ jobFinished(d->success);
+ }
+ else {
+ emit infoMessage( i18n("Ejecting DVD..."), INFO );
+ connect( K3bDevice::eject( d->device ),
+ SIGNAL(finished(K3bDevice::DeviceHandler*)),
+ this,
+ SLOT(slotEjectingFinished(K3bDevice::DeviceHandler*)) );
+ }
+}
+
+
+void K3bDvdFormattingJob::slotEjectingFinished( K3bDevice::DeviceHandler* dh )
+{
+ if( !dh->success() )
+ emit infoMessage( i18n("Unable to eject media."), ERROR );
+
+ d->running = false;
+ jobFinished(d->success);
+}
+
+
+void K3bDvdFormattingJob::slotDeviceHandlerFinished( K3bDevice::DeviceHandler* dh )
+{
+ if( d->canceled ) {
+ emit canceled();
+ jobFinished(false);
+ d->running = false;
+ }
+
+ if( dh->success() ) {
+ startFormatting( dh->diskInfo() );
+ }
+ else {
+ emit infoMessage( i18n("Unable to determine media state."), ERROR );
+ d->running = false;
+ jobFinished(false);
+ }
+}
+
+
+void K3bDvdFormattingJob::startFormatting( const K3bDevice::DiskInfo& diskInfo )
+{
+ //
+ // Now check the media type:
+ // if DVD-RW: use d->mode
+ // emit warning if formatting is full and stuff
+ //
+ // in overwrite mode: emit info that progress might stop before 100% since formatting will continue
+ // in the background once the media gets rewritten (only DVD+RW?)
+ //
+
+ // emit info about what kind of media has been found
+
+ if( !(diskInfo.mediaType() & (K3bDevice::MEDIA_DVD_RW|
+ K3bDevice::MEDIA_DVD_RW_SEQ|
+ K3bDevice::MEDIA_DVD_RW_OVWR|
+ K3bDevice::MEDIA_DVD_PLUS_RW)) ) {
+ emit infoMessage( i18n("No rewritable DVD media found. Unable to format."), ERROR );
+ d->running = false;
+ jobFinished(false);
+ return;
+ }
+
+
+ bool format = true; // do we need to format
+ bool blank = false; // blank is for DVD-RW sequential incremental
+ // DVD-RW restricted overwrite and DVD+RW uses the force option (or no option at all)
+
+
+
+ //
+ // DVD+RW is quite easy to handle. There is only one possible mode and it is always recommended to not
+ // format it more than once but to overwrite it once it is formatted
+ // Once the initial formatting has been done it's always "appendable" (or "complete"???)
+ //
+
+
+ if( diskInfo.mediaType() == K3bDevice::MEDIA_DVD_PLUS_RW ) {
+ emit infoMessage( i18n("Found %1 media.").arg(K3bDevice::mediaTypeString(K3bDevice::MEDIA_DVD_PLUS_RW)),
+ INFO );
+
+ // mode is ignored
+
+ if( diskInfo.empty() ) {
+ //
+ // The DVD+RW is blank and needs to be initially formatted
+ //
+ blank = false;
+ }
+ else {
+ emit infoMessage( i18n("No need to format %1 media more than once.")
+ .arg(K3bDevice::mediaTypeString(K3bDevice::MEDIA_DVD_PLUS_RW)), INFO );
+ emit infoMessage( i18n("It may simply be overwritten."), INFO );
+
+ if( d->force ) {
+ emit infoMessage( i18n("Forcing formatting anyway."), INFO );
+ emit infoMessage( i18n("It is not recommended to force formatting of DVD+RW media."), INFO );
+ emit infoMessage( i18n("Already after 10-20 reformats the media might be unusable."), INFO );
+ blank = false;
+ }
+ else {
+ format = false;
+ }
+ }
+
+ if( format )
+ emit newSubTask( i18n("Formatting DVD+RW") );
+ }
+
+
+
+ //
+ // DVD-RW has two modes: incremental sequential (the default which is also needed for DAO writing)
+ // and restricted overwrite which compares to the DVD+RW mode.
+ //
+
+ else { // MEDIA_DVD_RW
+ emit infoMessage( i18n("Found %1 media.").arg(K3bDevice::mediaTypeString(K3bDevice::MEDIA_DVD_RW)),
+ INFO );
+
+ if( diskInfo.currentProfile() != K3bDevice::MEDIA_UNKNOWN ) {
+ emit infoMessage( i18n("Formatted in %1 mode.").arg(K3bDevice::mediaTypeString(diskInfo.currentProfile())), INFO );
+
+
+ //
+ // Is it possible to have an empty DVD-RW in restricted overwrite mode???? I don't think so.
+ //
+
+ if( diskInfo.empty() &&
+ (d->mode == K3b::WRITING_MODE_AUTO ||
+ (d->mode == K3b::WRITING_MODE_INCR_SEQ &&
+ diskInfo.currentProfile() == K3bDevice::MEDIA_DVD_RW_SEQ) ||
+ (d->mode == K3b::WRITING_MODE_RES_OVWR &&
+ diskInfo.currentProfile() == K3bDevice::MEDIA_DVD_RW_OVWR) )
+ ) {
+ emit infoMessage( i18n("Media is already empty."), INFO );
+ if( d->force )
+ emit infoMessage( i18n("Forcing formatting anyway."), INFO );
+ else
+ format = false;
+ }
+ else if( diskInfo.currentProfile() == K3bDevice::MEDIA_DVD_RW_OVWR &&
+ d->mode != K3b::WRITING_MODE_INCR_SEQ ) {
+ emit infoMessage( i18n("No need to format %1 media more than once.")
+ .arg(K3bDevice::mediaTypeString(diskInfo.currentProfile())), INFO );
+ emit infoMessage( i18n("It may simply be overwritten."), INFO );
+
+ if( d->force )
+ emit infoMessage( i18n("Forcing formatting anyway."), INFO );
+ else
+ format = false;
+ }
+
+
+ if( format ) {
+ if( d->mode == K3b::WRITING_MODE_AUTO ) {
+ // just format in the same mode as the media is currently formatted
+ blank = (diskInfo.currentProfile() == K3bDevice::MEDIA_DVD_RW_SEQ);
+ }
+ else {
+ blank = (d->mode == K3b::WRITING_MODE_INCR_SEQ);
+ }
+
+ emit newSubTask( i18n("Formatting"
+ " DVD-RW in %1 mode.").arg(K3bDevice::mediaTypeString( blank ?
+ K3bDevice::MEDIA_DVD_RW_SEQ :
+ K3bDevice::MEDIA_DVD_RW_OVWR )) );
+ }
+ }
+ else {
+ emit infoMessage( i18n("Unable to determine the current formatting state of the DVD-RW media."), ERROR );
+ d->running = false;
+ jobFinished(false);
+ return;
+ }
+ }
+
+
+ if( format ) {
+ delete d->process;
+ d->process = new K3bProcess();
+ d->process->setRunPrivileged(true);
+ // d->process->setSuppressEmptyLines(false);
+ connect( d->process, SIGNAL(stderrLine(const QString&)), this, SLOT(slotStderrLine(const QString&)) );
+ connect( d->process, SIGNAL(processExited(KProcess*)), this, SLOT(slotProcessFinished(KProcess*)) );
+
+ d->dvdFormatBin = k3bcore->externalBinManager()->binObject( "dvd+rw-format" );
+ if( !d->dvdFormatBin ) {
+ emit infoMessage( i18n("Could not find %1 executable.").arg("dvd+rw-format"), ERROR );
+ d->running = false;
+ jobFinished(false);
+ return;
+ }
+
+ if( !d->dvdFormatBin->copyright.isEmpty() )
+ emit infoMessage( i18n("Using %1 %2 - Copyright (C) %3").arg(d->dvdFormatBin->name()).arg(d->dvdFormatBin->version).arg(d->dvdFormatBin->copyright), INFO );
+
+
+ *d->process << d->dvdFormatBin;
+
+ if( d->dvdFormatBin->version >= K3bVersion( 4, 6 ) )
+ *d->process << "-gui";
+
+ QString p;
+ if( blank )
+ p = "-blank";
+ else
+ p = "-force";
+ if( !d->quick )
+ p += "=full";
+
+ *d->process << p;
+
+ *d->process << d->device->blockDeviceName();
+
+ // additional user parameters from config
+ const QStringList& params = d->dvdFormatBin->userParameters();
+ for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
+ *d->process << *it;
+
+ kdDebug() << "***** dvd+rw-format parameters:\n";
+ const QValueList<QCString>& args = d->process->args();
+ QString s;
+ for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) {
+ s += *it + " ";
+ }
+ kdDebug() << s << endl << flush;
+ emit debuggingOutput( "dvd+rw-format command:", s );
+
+ if( !d->process->start( KProcess::NotifyOnExit, KProcess::All ) ) {
+ // something went wrong when starting the program
+ // it "should" be the executable
+ kdDebug() << "(K3bDvdFormattingJob) could not start " << d->dvdFormatBin->path << endl;
+ emit infoMessage( i18n("Could not start %1.").arg(d->dvdFormatBin->name()), K3bJob::ERROR );
+ d->running = false;
+ jobFinished(false);
+ }
+ else {
+ emit newTask( i18n("Formatting") );
+ }
+ }
+ else {
+ // already formatted :)
+ d->running = false;
+ jobFinished(true);
+ }
+}
+
+
+#include "k3bdvdformattingjob.moc"
diff --git a/libk3b/jobs/k3bdvdformattingjob.h b/libk3b/jobs/k3bdvdformattingjob.h
new file mode 100644
index 0000000..10672cb
--- /dev/null
+++ b/libk3b/jobs/k3bdvdformattingjob.h
@@ -0,0 +1,91 @@
+/*
+ *
+ * $Id: k3bdvdformattingjob.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_DVD_FORMATTING_JOB_H_
+#define _K3B_DVD_FORMATTING_JOB_H_
+
+
+#include <k3bjob.h>
+#include "k3b_export.h"
+
+class KProcess;
+namespace K3bDevice {
+ class Device;
+ class DeviceHandler;
+}
+
+
+class LIBK3B_EXPORT K3bDvdFormattingJob : public K3bBurnJob
+{
+ Q_OBJECT
+
+ public:
+ K3bDvdFormattingJob( K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ ~K3bDvdFormattingJob();
+
+ QString jobDescription() const;
+ QString jobDetails() const;
+
+ K3bDevice::Device* writer() const;
+
+ public slots:
+ void start();
+
+ /**
+ * Use this to force the start of the formatting without checking for a usable medium.
+ */
+ void start( const K3bDevice::DiskInfo& );
+
+ void cancel();
+
+ void setDevice( K3bDevice::Device* );
+
+ /**
+ * One of: WRITING_MODE_INCR_SEQ, WRITING_MODE_RES_OVWR
+ * Ignored for DVD+RW
+ */
+ void setMode( int );
+
+ /**
+ * Not all writers support this
+ */
+ void setQuickFormat( bool );
+
+ /**
+ * @param b If true empty DVDs will also be formatted
+ */
+ void setForce( bool b );
+
+ /**
+ * If set true the job ignores the global K3b setting
+ * and does not eject the CD-RW after finishing
+ */
+ void setForceNoEject( bool );
+
+ private slots:
+ void slotStderrLine( const QString& );
+ void slotProcessFinished( KProcess* );
+ void slotDeviceHandlerFinished( K3bDevice::DeviceHandler* );
+ void slotEjectingFinished( K3bDevice::DeviceHandler* );
+
+ private:
+ void startFormatting( const K3bDevice::DiskInfo& );
+
+ class Private;
+ Private* d;
+};
+
+
+#endif
diff --git a/libk3b/jobs/k3biso9660imagewritingjob.cpp b/libk3b/jobs/k3biso9660imagewritingjob.cpp
new file mode 100644
index 0000000..1fb3871
--- /dev/null
+++ b/libk3b/jobs/k3biso9660imagewritingjob.cpp
@@ -0,0 +1,458 @@
+/*
+ *
+ * $Id: k3biso9660imagewritingjob.cpp 690187 2007-07-20 09:18:03Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3biso9660imagewritingjob.h"
+#include "k3bverificationjob.h"
+
+#include <k3bdevice.h>
+#include <k3bdiskinfo.h>
+#include <k3bdevicehandler.h>
+#include <k3bcdrecordwriter.h>
+#include <k3bcdrdaowriter.h>
+#include <k3bgrowisofswriter.h>
+#include <k3bglobals.h>
+#include <k3bcore.h>
+#include <k3bversion.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bchecksumpipe.h>
+#include <k3bfilesplitter.h>
+
+#include <kdebug.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <ktempfile.h>
+#include <kio/global.h>
+
+#include <qstring.h>
+#include <qtextstream.h>
+#include <qfile.h>
+#include <qapplication.h>
+
+
+class K3bIso9660ImageWritingJob::Private
+{
+public:
+ K3bChecksumPipe checksumPipe;
+ K3bFileSplitter imageFile;
+};
+
+
+K3bIso9660ImageWritingJob::K3bIso9660ImageWritingJob( K3bJobHandler* hdl )
+ : K3bBurnJob( hdl ),
+ m_writingMode(K3b::WRITING_MODE_AUTO),
+ m_simulate(false),
+ m_device(0),
+ m_noFix(false),
+ m_speed(2),
+ m_dataMode(K3b::DATA_MODE_AUTO),
+ m_writer(0),
+ m_tocFile(0),
+ m_copies(1),
+ m_verifyJob(0)
+{
+ d = new Private;
+}
+
+K3bIso9660ImageWritingJob::~K3bIso9660ImageWritingJob()
+{
+ delete m_tocFile;
+ delete d;
+}
+
+
+void K3bIso9660ImageWritingJob::start()
+{
+ m_canceled = m_finished = false;
+ m_currentCopy = 1;
+
+ jobStarted();
+
+ if( m_simulate )
+ m_verifyData = false;
+
+ emit newTask( i18n("Preparing data") );
+
+ if( !QFile::exists( m_imagePath ) ) {
+ emit infoMessage( i18n("Could not find image %1").arg(m_imagePath), K3bJob::ERROR );
+ jobFinished( false );
+ return;
+ }
+
+ unsigned long gb = K3b::imageFilesize( m_imagePath )/1024/1024;
+
+ // very rough test but since most dvd images are 4,x or 8,x GB it should be enough
+ m_dvd = ( gb > 900 );
+
+ startWriting();
+}
+
+
+void K3bIso9660ImageWritingJob::slotWriterJobFinished( bool success )
+{
+ if( m_canceled ) {
+ m_finished = true;
+ emit canceled();
+ jobFinished(false);
+ return;
+ }
+
+ d->checksumPipe.close();
+
+ if( success ) {
+ if( !m_simulate && m_verifyData ) {
+ emit burning(false);
+
+ // allright
+ // the writerJob should have emited the "simulation/writing successful" signal
+
+ if( !m_verifyJob ) {
+ m_verifyJob = new K3bVerificationJob( this );
+ connectSubJob( m_verifyJob,
+ SLOT(slotVerificationFinished(bool)),
+ true,
+ SLOT(slotVerificationProgress(int)),
+ SIGNAL(subPercent(int)) );
+ }
+ m_verifyJob->setDevice( m_device );
+ m_verifyJob->clear();
+ m_verifyJob->addTrack( 1, d->checksumPipe.checksum(), K3b::imageFilesize( m_imagePath )/2048 );
+
+ if( m_copies == 1 )
+ emit newTask( i18n("Verifying written data") );
+ else
+ emit newTask( i18n("Verifying written copy %1 of %2").arg(m_currentCopy).arg(m_copies) );
+
+ m_verifyJob->start();
+ }
+ else if( m_currentCopy >= m_copies ) {
+ m_finished = true;
+ jobFinished(true);
+ }
+ else {
+ m_currentCopy++;
+ startWriting();
+ }
+ }
+ else {
+ m_finished = true;
+ jobFinished(false);
+ }
+}
+
+
+void K3bIso9660ImageWritingJob::slotVerificationFinished( bool success )
+{
+ if( m_canceled ) {
+ m_finished = true;
+ emit canceled();
+ jobFinished(false);
+ return;
+ }
+
+ if( success && m_currentCopy < m_copies ) {
+ m_currentCopy++;
+ connect( K3bDevice::eject( m_device ), SIGNAL(finished(bool)),
+ this, SLOT(startWriting()) );
+ return;
+ }
+
+ k3bcore->config()->setGroup("General Options");
+ if( !k3bcore->config()->readBoolEntry( "No cd eject", false ) )
+ K3bDevice::eject( m_device );
+
+ m_finished = true;
+ jobFinished( success );
+}
+
+
+void K3bIso9660ImageWritingJob::slotVerificationProgress( int p )
+{
+ emit percent( (int)(100.0 / (double)m_copies * ( (double)(m_currentCopy-1) + 0.5 + (double)p/200.0 )) );
+}
+
+
+void K3bIso9660ImageWritingJob::slotWriterPercent( int p )
+{
+ emit subPercent( p );
+
+ if( m_verifyData )
+ emit percent( (int)(100.0 / (double)m_copies * ( (double)(m_currentCopy-1) + ((double)p/200.0) )) );
+ else
+ emit percent( (int)(100.0 / (double)m_copies * ( (double)(m_currentCopy-1) + ((double)p/100.0) )) );
+}
+
+
+void K3bIso9660ImageWritingJob::slotNextTrack( int, int )
+{
+ if( m_copies == 1 )
+ emit newSubTask( i18n("Writing image") );
+ else
+ emit newSubTask( i18n("Writing copy %1 of %2").arg(m_currentCopy).arg(m_copies) );
+}
+
+
+void K3bIso9660ImageWritingJob::cancel()
+{
+ if( !m_finished ) {
+ m_canceled = true;
+
+ if( m_writer )
+ m_writer->cancel();
+ if( m_verifyData && m_verifyJob )
+ m_verifyJob->cancel();
+ }
+}
+
+
+void K3bIso9660ImageWritingJob::startWriting()
+{
+ emit newSubTask( i18n("Waiting for medium") );
+
+ // we wait for the following:
+ // 1. if writing mode auto and writing app auto: all writable media types
+ // 2. if writing mode auto and writing app not growisofs: all writable cd types
+ // 3. if writing mode auto and writing app growisofs: all writable dvd types
+ // 4. if writing mode TAO or RAW: all writable cd types
+ // 5. if writing mode DAO and writing app auto: all writable cd types and DVD-R(W)
+ // 6. if writing mode DAO and writing app GROWISOFS: DVD-R(W)
+ // 7. if writing mode DAO and writing app CDRDAO or CDRECORD: all writable cd types
+ // 8. if writing mode WRITING_MODE_INCR_SEQ: DVD-R(W)
+ // 9. if writing mode WRITING_MODE_RES_OVWR: DVD-RW or DVD+RW
+
+ int mt = 0;
+ if( m_writingMode == K3b::WRITING_MODE_AUTO ) {
+ if( writingApp() == K3b::DEFAULT ) {
+ if( m_dvd )
+ mt = K3bDevice::MEDIA_WRITABLE_DVD;
+ else
+ mt = K3bDevice::MEDIA_WRITABLE_CD;
+ }
+ else if( writingApp() != K3b::GROWISOFS )
+ mt = K3bDevice::MEDIA_WRITABLE_CD;
+ else
+ mt = K3bDevice::MEDIA_WRITABLE_DVD;
+ }
+ else if( m_writingMode == K3b::TAO || m_writingMode == K3b::RAW )
+ mt = K3bDevice::MEDIA_WRITABLE_CD;
+ else if( m_writingMode == K3b::DAO ) {
+ if( writingApp() == K3b::DEFAULT ) {
+ if( m_dvd )
+ mt = K3bDevice::MEDIA_WRITABLE_DVD;
+ else
+ mt = K3bDevice::MEDIA_WRITABLE_CD;
+ }
+ else if( writingApp() == K3b::GROWISOFS )
+ mt = K3bDevice::MEDIA_WRITABLE_DVD;
+ else
+ mt = K3bDevice::MEDIA_WRITABLE_CD;
+ }
+ else if( m_writingMode == K3b::WRITING_MODE_RES_OVWR )
+ mt = K3bDevice::MEDIA_DVD_PLUS_R|K3bDevice::MEDIA_DVD_PLUS_R_DL|K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_RW_OVWR;
+ else
+ mt = K3bDevice::MEDIA_WRITABLE_DVD;
+
+
+ // wait for the media
+ int media = waitForMedia( m_device, K3bDevice::STATE_EMPTY, mt );
+ if( media < 0 ) {
+ m_finished = true;
+ emit canceled();
+ jobFinished(false);
+ return;
+ }
+
+ // we simply always calculate the checksum, thus making the code simpler
+ d->imageFile.close();
+ d->imageFile.setName( m_imagePath );
+ d->imageFile.open( IO_ReadOnly );
+ d->checksumPipe.close();
+ d->checksumPipe.readFromIODevice( &d->imageFile );
+
+ if( prepareWriter( media ) ) {
+ emit burning(true);
+ m_writer->start();
+ d->checksumPipe.writeToFd( m_writer->fd(), true );
+ d->checksumPipe.open( K3bChecksumPipe::MD5, true );
+ }
+ else {
+ m_finished = true;
+ jobFinished(false);
+ }
+}
+
+
+bool K3bIso9660ImageWritingJob::prepareWriter( int mediaType )
+{
+ if( mediaType == 0 ) { // media forced
+ // just to get it going...
+ if( writingApp() != K3b::GROWISOFS && !m_dvd )
+ mediaType = K3bDevice::MEDIA_CD_R;
+ else
+ mediaType = K3bDevice::MEDIA_DVD_R;
+ }
+
+ delete m_writer;
+
+ if( mediaType == K3bDevice::MEDIA_CD_R || mediaType == K3bDevice::MEDIA_CD_RW ) {
+ int usedWritingMode = m_writingMode;
+ if( usedWritingMode == K3b::WRITING_MODE_AUTO ) {
+ // cdrecord seems to have problems when writing in mode2 in dao mode
+ // so with cdrecord we use TAO
+ if( m_noFix || m_dataMode == K3b::MODE2 || !m_device->dao() )
+ usedWritingMode = K3b::TAO;
+ else
+ usedWritingMode = K3b::DAO;
+ }
+
+ int usedApp = writingApp();
+ if( usedApp == K3b::DEFAULT ) {
+ if( usedWritingMode == K3b::DAO &&
+ ( m_dataMode == K3b::MODE2 || m_noFix ) )
+ usedApp = K3b::CDRDAO;
+ else
+ usedApp = K3b::CDRECORD;
+ }
+
+
+ if( usedApp == K3b::CDRECORD ) {
+ K3bCdrecordWriter* writer = new K3bCdrecordWriter( m_device, this );
+
+ writer->setWritingMode( usedWritingMode );
+ writer->setSimulate( m_simulate );
+ writer->setBurnSpeed( m_speed );
+
+ if( m_noFix ) {
+ writer->addArgument("-multi");
+ }
+
+ if( (m_dataMode == K3b::DATA_MODE_AUTO && m_noFix) ||
+ m_dataMode == K3b::MODE2 ) {
+ if( k3bcore->externalBinManager()->binObject("cdrecord") &&
+ k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "xamix" ) )
+ writer->addArgument( "-xa" );
+ else
+ writer->addArgument( "-xa1" );
+ }
+ else
+ writer->addArgument("-data");
+
+ // read from stdin
+ writer->addArgument( QString("-tsize=%1s").arg( K3b::imageFilesize( m_imagePath )/2048 ) )->addArgument( "-" );
+
+ m_writer = writer;
+ }
+ else {
+ // create cdrdao job
+ K3bCdrdaoWriter* writer = new K3bCdrdaoWriter( m_device, this );
+ writer->setCommand( K3bCdrdaoWriter::WRITE );
+ writer->setSimulate( m_simulate );
+ writer->setBurnSpeed( m_speed );
+ // multisession
+ writer->setMulti( m_noFix );
+
+ // now write the tocfile
+ delete m_tocFile;
+ m_tocFile = new KTempFile( QString::null, "toc" );
+ m_tocFile->setAutoDelete(true);
+
+ if( QTextStream* s = m_tocFile->textStream() ) {
+ if( (m_dataMode == K3b::DATA_MODE_AUTO && m_noFix) ||
+ m_dataMode == K3b::MODE2 ) {
+ *s << "CD_ROM_XA" << "\n";
+ *s << "\n";
+ *s << "TRACK MODE2_FORM1" << "\n";
+ }
+ else {
+ *s << "CD_ROM" << "\n";
+ *s << "\n";
+ *s << "TRACK MODE1" << "\n";
+ }
+ *s << "DATAFILE \"-\" " << QString::number( K3b::imageFilesize( m_imagePath ) ) << "\n";
+
+ m_tocFile->close();
+ }
+ else {
+ kdDebug() << "(K3bDataJob) could not write tocfile." << endl;
+ emit infoMessage( i18n("IO Error"), ERROR );
+ return false;
+ }
+
+ writer->setTocFile( m_tocFile->name() );
+
+ m_writer = writer;
+ }
+ }
+ else { // DVD
+ if( mediaType & K3bDevice::MEDIA_DVD_PLUS_ALL ) {
+ if( m_simulate ) {
+ if( questionYesNo( i18n("K3b does not support simulation with DVD+R(W) media. "
+ "Do you really want to continue? The media will be written "
+ "for real."),
+ i18n("No Simulation with DVD+R(W)") ) ) {
+ return false;
+ }
+ }
+
+ m_simulate = false;
+ }
+
+ K3bGrowisofsWriter* writer = new K3bGrowisofsWriter( m_device, this );
+ writer->setSimulate( m_simulate );
+ writer->setBurnSpeed( m_speed );
+ writer->setWritingMode( m_writingMode == K3b::DAO ? K3b::DAO : 0 );
+ writer->setImageToWrite( QString::null ); // read from stdin
+ writer->setCloseDvd( !m_noFix );
+ writer->setTrackSize( K3b::imageFilesize( m_imagePath )/2048 );
+
+ m_writer = writer;
+ }
+
+ connect( m_writer, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( m_writer, SIGNAL(nextTrack(int, int)), this, SLOT(slotNextTrack(int, int)) );
+ connect( m_writer, SIGNAL(percent(int)), this, SLOT(slotWriterPercent(int)) );
+ connect( m_writer, SIGNAL(processedSize(int, int)), this, SIGNAL(processedSize(int, int)) );
+ connect( m_writer, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) );
+ connect( m_writer, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
+ connect( m_writer, SIGNAL(writeSpeed(int, int)), this, SIGNAL(writeSpeed(int, int)) );
+ connect( m_writer, SIGNAL(finished(bool)), this, SLOT(slotWriterJobFinished(bool)) );
+ connect( m_writer, SIGNAL(newTask(const QString&)), this, SIGNAL(newTask(const QString&)) );
+ connect( m_writer, SIGNAL(newSubTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( m_writer, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+
+ return true;
+}
+
+
+QString K3bIso9660ImageWritingJob::jobDescription() const
+{
+ if( m_simulate )
+ return i18n("Simulating ISO9660 Image");
+ else
+ return ( i18n("Burning ISO9660 Image")
+ + ( m_copies > 1
+ ? i18n(" - %n Copy", " - %n Copies", m_copies)
+ : QString::null ) );
+}
+
+
+QString K3bIso9660ImageWritingJob::jobDetails() const
+{
+ return m_imagePath.section("/", -1) + QString( " (%1)" ).arg(KIO::convertSize(K3b::filesize(KURL::fromPathOrURL(m_imagePath))));
+}
+
+
+#include "k3biso9660imagewritingjob.moc"
diff --git a/libk3b/jobs/k3biso9660imagewritingjob.h b/libk3b/jobs/k3biso9660imagewritingjob.h
new file mode 100644
index 0000000..eceb6dc
--- /dev/null
+++ b/libk3b/jobs/k3biso9660imagewritingjob.h
@@ -0,0 +1,98 @@
+/*
+ *
+ * $Id$
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef K3BISO9660_IMAGE_WRITING_JOB_H
+#define K3BISO9660_IMAGE_WRITING_JOB_H
+
+#include <k3bjob.h>
+#include "k3b_export.h"
+class QString;
+class K3bAbstractWriter;
+class KTempFile;
+namespace K3bDevice {
+ class Device;
+}
+class K3bVerificationJob;
+
+
+/**
+ *@author Sebastian Trueg
+ */
+class LIBK3B_EXPORT K3bIso9660ImageWritingJob : public K3bBurnJob
+{
+ Q_OBJECT
+
+ public:
+ K3bIso9660ImageWritingJob( K3bJobHandler* );
+ ~K3bIso9660ImageWritingJob();
+
+ K3bDevice::Device* writer() const { return m_device; };
+
+ QString jobDescription() const;
+ QString jobDetails() const;
+
+ public slots:
+ void cancel();
+ void start();
+
+ void setImagePath( const QString& path ) { m_imagePath = path; }
+ void setSpeed( int s ) { m_speed = s; }
+ void setBurnDevice( K3bDevice::Device* dev ) { m_device = dev; }
+ void setWritingMode( int mode ) { m_writingMode = mode; }
+ void setSimulate( bool b ) { m_simulate = b; }
+ void setNoFix( bool b ) { m_noFix = b; }
+ void setDataMode( int m ) { m_dataMode = m; }
+ void setVerifyData( bool b ) { m_verifyData = b; }
+ void setCopies( int c ) { m_copies = c; }
+
+ protected slots:
+ void slotWriterJobFinished( bool );
+ void slotVerificationFinished( bool );
+ void slotVerificationProgress( int );
+ void slotWriterPercent( int );
+ void slotNextTrack( int, int );
+ void startWriting();
+
+ private:
+ bool prepareWriter( int mediaType );
+
+ int m_writingMode;
+ bool m_simulate;
+ K3bDevice::Device* m_device;
+ bool m_noFix;
+ int m_speed;
+ int m_dataMode;
+ bool m_verifyData;
+ bool m_dvd;
+
+ QString m_imagePath;
+
+ K3bAbstractWriter* m_writer;
+ KTempFile* m_tocFile;
+
+ bool m_canceled;
+ bool m_finished;
+
+ int m_copies;
+ int m_currentCopy;
+
+ K3bVerificationJob* m_verifyJob;
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/jobs/k3breadcdreader.cpp b/libk3b/jobs/k3breadcdreader.cpp
new file mode 100644
index 0000000..d75eb63
--- /dev/null
+++ b/libk3b/jobs/k3breadcdreader.cpp
@@ -0,0 +1,335 @@
+/*
+ *
+ * $Id: k3breadcdreader.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#include "k3breadcdreader.h"
+
+#include <k3bcore.h>
+#include <k3bexternalbinmanager.h>
+#include <k3bdevice.h>
+#include <k3bdevicemanager.h>
+#include <k3bprocess.h>
+#include <k3bmsf.h>
+#include <k3bglobals.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kconfig.h>
+
+#include <qregexp.h>
+#include <qvaluelist.h>
+#include <qstringlist.h>
+
+
+
+class K3bReadcdReader::Private
+{
+public:
+ Private()
+ : process(0),
+ fdToWriteTo(-1),
+ canceled(false) {
+ }
+
+ K3b::Msf firstSector, lastSector;
+
+ K3bProcess* process;
+ const K3bExternalBin* readcdBinObject;
+
+ int fdToWriteTo;
+ bool canceled;
+
+ long blocksToRead;
+ int unreadableBlocks;
+
+ int lastProgress;
+ int lastProcessedSize;
+};
+
+
+
+K3bReadcdReader::K3bReadcdReader( K3bJobHandler* jh, QObject* parent, const char* name )
+ : K3bJob( jh, parent, name ),
+ m_noCorr(false),
+ m_clone(false),
+ m_noError(false),
+ m_c2Scan(false),
+ m_speed(0),
+ m_retries(128)
+{
+ d = new Private();
+}
+
+
+K3bReadcdReader::~K3bReadcdReader()
+{
+ delete d->process;
+ delete d;
+}
+
+
+bool K3bReadcdReader::active() const
+{
+ return (d->process ? d->process->isRunning() : false);
+}
+
+
+void K3bReadcdReader::writeToFd( int fd )
+{
+ d->fdToWriteTo = fd;
+}
+
+
+void K3bReadcdReader::start()
+{
+ jobStarted();
+
+ d->blocksToRead = 1;
+ d->unreadableBlocks = 0;
+ d->lastProgress = 0;
+ d->lastProcessedSize = 0;
+
+ // the first thing to do is to check for readcd
+ d->readcdBinObject = k3bcore->externalBinManager()->binObject( "readcd" );
+ if( !d->readcdBinObject ) {
+ emit infoMessage( i18n("Could not find %1 executable.").arg("readcd"), ERROR );
+ jobFinished(false);
+ return;
+ }
+
+ // check if we have clone support if we need it
+ if( m_clone ) {
+ bool foundCloneSupport = false;
+
+ if( !d->readcdBinObject->hasFeature( "clone" ) ) {
+ // search all readcd installations
+ K3bExternalProgram* readcdProgram = k3bcore->externalBinManager()->program( "readcd" );
+ const QPtrList<K3bExternalBin>& readcdBins = readcdProgram->bins();
+ for( QPtrListIterator<K3bExternalBin> it( readcdBins ); it.current(); ++it ) {
+ if( it.current()->hasFeature( "clone" ) ) {
+ d->readcdBinObject = it.current();
+ emit infoMessage( i18n("Using readcd %1 instead of default version for clone support.").arg(d->readcdBinObject->version), INFO );
+ foundCloneSupport = true;
+ break;
+ }
+ }
+
+ if( !foundCloneSupport ) {
+ emit infoMessage( i18n("Could not find readcd executable with cloning support."), ERROR );
+ jobFinished(false);
+ return;
+ }
+ }
+ }
+
+
+ // create the commandline
+ delete d->process;
+ d->process = new K3bProcess();
+ connect( d->process, SIGNAL(stderrLine(const QString&)), this, SLOT(slotStdLine(const QString&)) );
+ connect( d->process, SIGNAL(processExited(KProcess*)), this, SLOT(slotProcessExited(KProcess*)) );
+
+
+ *d->process << d->readcdBinObject;
+
+ // display progress
+ *d->process << "-v";
+
+ // Again we assume the device to be set!
+ *d->process << QString("dev=%1").arg(K3b::externalBinDeviceParameter(m_readDevice,
+ d->readcdBinObject));
+ if( m_speed > 0 )
+ *d->process << QString("speed=%1").arg(m_speed);
+
+
+ // output
+ if( d->fdToWriteTo != -1 ) {
+ *d->process << "f=-";
+ d->process->dupStdout( d->fdToWriteTo );
+ }
+ else {
+ emit newTask( i18n("Writing image to %1.").arg(m_imagePath) );
+ emit infoMessage( i18n("Writing image to %1.").arg(m_imagePath), INFO );
+ *d->process << "f=" + m_imagePath;
+ }
+
+
+ if( m_noError )
+ *d->process << "-noerror";
+ if( m_clone ) {
+ *d->process << "-clone";
+ // noCorr can only be used with cloning
+ if( m_noCorr )
+ *d->process << "-nocorr";
+ }
+ if( m_c2Scan )
+ *d->process << "-c2scan";
+
+ *d->process << QString("retries=%1").arg(m_retries);
+
+ // readcd does not read the last sector specified
+ if( d->firstSector < d->lastSector )
+ *d->process << QString("sectors=%1-%2").arg(d->firstSector.lba()).arg(d->lastSector.lba()+1);
+
+ // Joerg sais it is a Linux kernel bug, anyway, with the default value it does not work
+ *d->process << "ts=128k";
+
+ // additional user parameters from config
+ const QStringList& params = d->readcdBinObject->userParameters();
+ for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
+ *d->process << *it;
+
+
+ kdDebug() << "***** readcd parameters:\n";
+ const QValueList<QCString>& args = d->process->args();
+ QString s;
+ for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) {
+ s += *it + " ";
+ }
+ kdDebug() << s << endl << flush;
+
+ emit debuggingOutput("readcd command:", s);
+
+ d->canceled = false;
+
+ if( !d->process->start( KProcess::NotifyOnExit, KProcess::AllOutput) ) {
+ // something went wrong when starting the program
+ // it "should" be the executable
+ kdError() << "(K3bReadcdReader) could not start readcd" << endl;
+ emit infoMessage( i18n("Could not start readcd."), K3bJob::ERROR );
+ jobFinished( false );
+ }
+}
+
+
+void K3bReadcdReader::cancel()
+{
+ if( d->process ) {
+ if( d->process->isRunning() ) {
+ d->canceled = true;
+ d->process->kill();
+ }
+ }
+}
+
+
+void K3bReadcdReader::slotStdLine( const QString& line )
+{
+ emit debuggingOutput( "readcd", line );
+
+ int pos = -1;
+
+ if( line.startsWith( "end:" ) ) {
+ bool ok;
+ d->blocksToRead = line.mid(4).toInt(&ok);
+ if( d->firstSector < d->lastSector )
+ d->blocksToRead -= d->firstSector.lba();
+ if( !ok )
+ kdError() << "(K3bReadcdReader) blocksToRead parsing error in line: "
+ << line.mid(4) << endl;
+ }
+
+ else if( line.startsWith( "addr:" ) ) {
+ bool ok;
+ long currentReadBlock = line.mid( 6, line.find("cnt")-7 ).toInt(&ok);
+ if( d->firstSector < d->lastSector )
+ currentReadBlock -= d->firstSector.lba();
+ if( ok ) {
+ int p = (int)(100.0 * (double)currentReadBlock / (double)d->blocksToRead);
+ if( p > d->lastProgress ) {
+ emit percent( p );
+ d->lastProgress = p;
+ }
+ int ps = currentReadBlock*2/1024;
+ if( ps > d->lastProcessedSize ) {
+ emit processedSize( ps, d->blocksToRead*2/1024 );
+ d->lastProcessedSize = ps;
+ }
+ }
+ else
+ kdError() << "(K3bReadcdReader) currentReadBlock parsing error in line: "
+ << line.mid( 6, line.find("cnt")-7 ) << endl;
+ }
+
+ else if( line.contains("Cannot read source disk") ) {
+ emit infoMessage( i18n("Cannot read source disk."), ERROR );
+ }
+
+ else if( (pos = line.find("Retrying from sector")) >= 0 ) {
+ // parse the sector
+ pos += 21;
+ bool ok;
+ int problemSector = line.mid( pos, line.find( QRegExp("\\D"), pos )-pos ).toInt(&ok);
+ if( !ok ) {
+ kdError() << "(K3bReadcdReader) problemSector parsing error in line: "
+ << line.mid( pos, line.find( QRegExp("\\D"), pos )-pos ) << endl;
+ }
+ emit infoMessage( i18n("Retrying from sector %1.").arg(problemSector), INFO );
+ }
+
+ else if( (pos = line.find("Error on sector")) >= 0 ) {
+ d->unreadableBlocks++;
+
+ pos += 16;
+ bool ok;
+ int problemSector = line.mid( pos, line.find( QRegExp("\\D"), pos )-pos ).toInt(&ok);
+ if( !ok ) {
+ kdError() << "(K3bReadcdReader) problemSector parsing error in line: "
+ << line.mid( pos, line.find( QRegExp("\\D"), pos )-pos ) << endl;
+ }
+
+ if( line.contains( "not corrected") ) {
+ emit infoMessage( i18n("Uncorrected error in sector %1").arg(problemSector), ERROR );
+ }
+ else {
+ emit infoMessage( i18n("Corrected error in sector %1").arg(problemSector), ERROR );
+ }
+ }
+
+ else {
+ kdDebug() << "(readcd) " << line << endl;
+ }
+}
+
+void K3bReadcdReader::slotProcessExited( KProcess* p )
+{
+ if( d->canceled ) {
+ emit canceled();
+ jobFinished(false);
+ }
+ else if( p->normalExit() ) {
+ if( p->exitStatus() == 0 ) {
+ jobFinished( true );
+ }
+ else {
+ emit infoMessage( i18n("%1 returned error: %2").arg("Readcd").arg(p->exitStatus()), ERROR );
+ jobFinished( false );
+ }
+ }
+ else {
+ emit infoMessage( i18n("Readcd exited abnormally."), ERROR );
+ jobFinished( false );
+ }
+}
+
+
+void K3bReadcdReader::setSectorRange( const K3b::Msf& first, const K3b::Msf& last )
+{
+ d->firstSector = first;
+ d->lastSector = last;
+}
+
+#include "k3breadcdreader.moc"
+
diff --git a/libk3b/jobs/k3breadcdreader.h b/libk3b/jobs/k3breadcdreader.h
new file mode 100644
index 0000000..93ebce0
--- /dev/null
+++ b/libk3b/jobs/k3breadcdreader.h
@@ -0,0 +1,91 @@
+/*
+ *
+ * $Id: k3breadcdreader.h 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+
+#ifndef _K3B_READCD_READER_H_
+#define _K3B_READCD_READER_H_
+
+#include <k3bjob.h>
+
+
+class K3bProcess;
+class KProcess;
+class K3bExternalBin;
+namespace K3bDevice {
+ class Device;
+}
+namespace K3b {
+ class Msf;
+}
+
+
+class K3bReadcdReader : public K3bJob
+{
+ Q_OBJECT
+
+ public:
+ K3bReadcdReader( K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ ~K3bReadcdReader();
+
+ bool active() const;
+
+ public slots:
+ void start();
+ void cancel();
+
+ void setReadDevice( K3bDevice::Device* dev ) { m_readDevice = dev; }
+
+ /** 0 means MAX */
+ void setReadSpeed( int s ) { m_speed = s; }
+ void setDisableCorrection( bool b ) { m_noCorr = b; }
+
+ /** default: true */
+ void setAbortOnError( bool b ) { m_noError = !b; }
+ void setC2Scan( bool b ) { m_c2Scan = b; }
+ void setClone( bool b ) { m_clone = b; }
+ void setRetries( int i ) { m_retries = i; }
+
+ void setSectorRange( const K3b::Msf&, const K3b::Msf& );
+
+ void setImagePath( const QString& p ) { m_imagePath = p; }
+
+ /**
+ * the data gets written directly into fd instead of the imagefile.
+ * Be aware that this only makes sense before starting the job.
+ * To disable just set fd to -1
+ */
+ void writeToFd( int fd );
+
+ private slots:
+ void slotStdLine( const QString& line );
+ void slotProcessExited(KProcess*);
+
+ private:
+ bool m_noCorr;
+ bool m_clone;
+ bool m_noError;
+ bool m_c2Scan;
+ int m_speed;
+ int m_retries;
+
+ K3bDevice::Device* m_readDevice;
+
+ QString m_imagePath;
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/jobs/k3bverificationjob.cpp b/libk3b/jobs/k3bverificationjob.cpp
new file mode 100644
index 0000000..e73530e
--- /dev/null
+++ b/libk3b/jobs/k3bverificationjob.cpp
@@ -0,0 +1,384 @@
+/*
+ *
+ * $Id: k3bisoimageverificationjob.cpp 597651 2006-10-21 08:04:01Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bverificationjob.h"
+
+#include <k3bdevice.h>
+#include <k3bdevicehandler.h>
+#include <k3bmd5job.h>
+#include <k3bglobals.h>
+#include <k3bdatatrackreader.h>
+#include <k3bpipe.h>
+#include <k3biso9660.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kio/global.h>
+#include <kio/job.h>
+#include <kio/netaccess.h>
+
+#include <qcstring.h>
+#include <qapplication.h>
+#include <qvaluelist.h>
+#include <qpair.h>
+
+
+class K3bVerificationJobTrackEntry
+{
+public:
+ K3bVerificationJobTrackEntry()
+ : trackNumber(0) {
+ }
+
+ K3bVerificationJobTrackEntry( int tn, const QCString& cs, const K3b::Msf& msf )
+ : trackNumber(tn),
+ checksum(cs),
+ length(msf) {
+ }
+
+ int trackNumber;
+ QCString checksum;
+ K3b::Msf length;
+};
+
+
+class K3bVerificationJob::Private
+{
+public:
+ Private()
+ : md5Job(0),
+ device(0),
+ dataTrackReader(0) {
+ }
+
+ bool canceled;
+ K3bMd5Job* md5Job;
+ K3bDevice::Device* device;
+
+ K3b::Msf grownSessionSize;
+
+ QValueList<K3bVerificationJobTrackEntry> tracks;
+ int currentTrackIndex;
+
+ K3bDevice::DiskInfo diskInfo;
+ K3bDevice::Toc toc;
+
+ K3bDataTrackReader* dataTrackReader;
+
+ K3b::Msf currentTrackSize;
+ K3b::Msf totalSectors;
+ K3b::Msf alreadyReadSectors;
+
+ K3bPipe pipe;
+
+ bool readSuccessful;
+
+ bool mediumHasBeenReloaded;
+};
+
+
+K3bVerificationJob::K3bVerificationJob( K3bJobHandler* hdl, QObject* parent, const char* name )
+ : K3bJob( hdl, parent, name )
+{
+ d = new Private();
+
+ d->md5Job = new K3bMd5Job( this );
+ connect( d->md5Job, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( d->md5Job, SIGNAL(finished(bool)), this, SLOT(slotMd5JobFinished(bool)) );
+ connect( d->md5Job, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+}
+
+
+K3bVerificationJob::~K3bVerificationJob()
+{
+ delete d;
+}
+
+
+void K3bVerificationJob::cancel()
+{
+ d->canceled = true;
+ if( d->md5Job && d->md5Job->active() )
+ d->md5Job->cancel();
+ if( d->dataTrackReader && d->dataTrackReader->active() )
+ d->dataTrackReader->cancel();
+}
+
+
+void K3bVerificationJob::addTrack( int trackNum, const QCString& checksum, const K3b::Msf& length )
+{
+ d->tracks.append( K3bVerificationJobTrackEntry( trackNum, checksum, length ) );
+}
+
+
+void K3bVerificationJob::clear()
+{
+ d->tracks.clear();
+ d->grownSessionSize = 0;
+}
+
+
+void K3bVerificationJob::setDevice( K3bDevice::Device* dev )
+{
+ d->device = dev;
+}
+
+
+void K3bVerificationJob::setGrownSessionSize( const K3b::Msf& s )
+{
+ d->grownSessionSize = s;
+}
+
+
+void K3bVerificationJob::start()
+{
+ jobStarted();
+
+ d->canceled = false;
+ d->currentTrackIndex = 0;
+ d->alreadyReadSectors = 0;
+
+ emit newTask( i18n("Checking medium") );
+
+ d->mediumHasBeenReloaded = false;
+ connect( K3bDevice::sendCommand( K3bDevice::DeviceHandler::DISKINFO, d->device ),
+ SIGNAL(finished(K3bDevice::DeviceHandler*)),
+ this,
+ SLOT(slotDiskInfoReady(K3bDevice::DeviceHandler*)) );
+}
+
+
+void K3bVerificationJob::slotMediaReloaded( bool /*success*/ )
+{
+ // we always need to wait for the medium. Otherwise the diskinfo below
+ // may run before the drive is ready!
+ waitForMedia( d->device,
+ K3bDevice::STATE_COMPLETE|K3bDevice::STATE_INCOMPLETE,
+ K3bDevice::MEDIA_WRITABLE );
+
+ d->mediumHasBeenReloaded = true;
+
+ emit newTask( i18n("Checking medium") );
+
+ connect( K3bDevice::sendCommand( K3bDevice::DeviceHandler::DISKINFO, d->device ),
+ SIGNAL(finished(K3bDevice::DeviceHandler*)),
+ this,
+ SLOT(slotDiskInfoReady(K3bDevice::DeviceHandler*)) );
+}
+
+
+void K3bVerificationJob::slotDiskInfoReady( K3bDevice::DeviceHandler* dh )
+{
+ if( d->canceled ) {
+ emit canceled();
+ jobFinished(false);
+ }
+
+ d->diskInfo = dh->diskInfo();
+ d->toc = dh->toc();
+ d->totalSectors = 0;
+
+ // just to be sure check if we actually have all the tracks
+ int i = 0;
+ for( QValueList<K3bVerificationJobTrackEntry>::iterator it = d->tracks.begin();
+ it != d->tracks.end(); ++i, ++it ) {
+
+ // 0 means "last track"
+ if( (*it).trackNumber == 0 )
+ (*it).trackNumber = d->toc.count();
+
+ if( (int)d->toc.count() < (*it).trackNumber ) {
+ if ( d->mediumHasBeenReloaded ) {
+ emit infoMessage( i18n("Internal Error: Verification job improperly initialized (%1)")
+ .arg( "Specified track number not found on medium" ), ERROR );
+ jobFinished( false );
+ return;
+ }
+ else {
+ // many drives need to reload the medium to return to a proper state
+ emit newTask( i18n("Reloading the medium") );
+ connect( K3bDevice::reload( d->device ),
+ SIGNAL(finished(bool)),
+ this,
+ SLOT(slotMediaReloaded(bool)) );
+ return;
+ }
+ }
+
+ d->totalSectors += trackLength( i );
+ }
+
+ readTrack( 0 );
+}
+
+
+void K3bVerificationJob::readTrack( int trackIndex )
+{
+ d->currentTrackIndex = trackIndex;
+ d->readSuccessful = true;
+
+ d->currentTrackSize = trackLength( trackIndex );
+ if( d->currentTrackSize == 0 ) {
+ jobFinished(false);
+ return;
+ }
+
+ emit newTask( i18n("Verifying track %1").arg( d->tracks[trackIndex].trackNumber ) );
+
+ d->pipe.open();
+
+ if( d->toc[d->tracks[trackIndex].trackNumber-1].type() == K3bDevice::Track::DATA ) {
+ if( !d->dataTrackReader ) {
+ d->dataTrackReader = new K3bDataTrackReader( this );
+ connect( d->dataTrackReader, SIGNAL(percent(int)), this, SLOT(slotReaderProgress(int)) );
+ // connect( d->dataTrackReader, SIGNAL(processedSize(int, int)), this, SLOT(slotReaderProcessedSize(int, int)) );
+ connect( d->dataTrackReader, SIGNAL(finished(bool)), this, SLOT(slotReaderFinished(bool)) );
+ connect( d->dataTrackReader, SIGNAL(infoMessage(const QString&, int)), this, SIGNAL(infoMessage(const QString&, int)) );
+ connect( d->dataTrackReader, SIGNAL(newTask(const QString&)), this, SIGNAL(newSubTask(const QString&)) );
+ connect( d->dataTrackReader, SIGNAL(debuggingOutput(const QString&, const QString&)),
+ this, SIGNAL(debuggingOutput(const QString&, const QString&)) );
+ }
+
+ d->dataTrackReader->setDevice( d->device );
+ d->dataTrackReader->setIgnoreErrors( false );
+ d->dataTrackReader->setSectorSize( K3bDataTrackReader::MODE1 );
+
+ // in case a session was grown the track size does not say anything about the verification data size
+ if( d->diskInfo.mediaType() & (K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_RW_OVWR) &&
+ d->grownSessionSize > 0 ) {
+ K3bIso9660 isoF( d->device );
+ if( isoF.open() ) {
+ int firstSector = isoF.primaryDescriptor().volumeSpaceSize - d->grownSessionSize.lba();
+ d->dataTrackReader->setSectorRange( firstSector,
+ isoF.primaryDescriptor().volumeSpaceSize -1 );
+ }
+ else {
+ emit infoMessage( i18n("Unable to determine the ISO9660 filesystem size."), ERROR );
+ jobFinished( false );
+ return;
+ }
+ }
+ else
+ d->dataTrackReader->setSectorRange( d->toc[d->tracks[trackIndex].trackNumber-1].firstSector(),
+ d->toc[d->tracks[trackIndex].trackNumber-1].firstSector() + d->currentTrackSize -1 );
+
+ d->md5Job->setMaxReadSize( d->currentTrackSize.mode1Bytes() );
+
+ d->dataTrackReader->writeToFd( d->pipe.in() );
+ d->dataTrackReader->start();
+ }
+ else {
+ // FIXME: handle audio tracks
+ }
+
+ d->md5Job->setFd( d->pipe.out() );
+ d->md5Job->start();
+}
+
+
+void K3bVerificationJob::slotReaderProgress( int p )
+{
+ emit subPercent( p );
+
+ emit percent( 100 * ( d->alreadyReadSectors.lba() + ( p*d->currentTrackSize.lba()/100 ) ) / d->totalSectors.lba() );
+}
+
+
+void K3bVerificationJob::slotMd5JobFinished( bool success )
+{
+ d->pipe.close();
+
+ if( success && !d->canceled && d->readSuccessful ) {
+ // compare the two sums
+ if( d->tracks[d->currentTrackIndex].checksum != d->md5Job->hexDigest() ) {
+ emit infoMessage( i18n("Written data in track %1 differs from original.").arg(d->tracks[d->currentTrackIndex].trackNumber), ERROR );
+ jobFinished(false);
+ }
+ else {
+ emit infoMessage( i18n("Written data verified."), SUCCESS );
+ ++d->currentTrackIndex;
+ if( d->currentTrackIndex < (int)d->tracks.count() )
+ readTrack( d->currentTrackIndex );
+ else
+ jobFinished(true);
+ }
+ }
+ else {
+ // The md5job emitted an error message. So there is no need to do this again
+ jobFinished(false);
+ }
+}
+
+
+void K3bVerificationJob::slotReaderFinished( bool success )
+{
+ d->readSuccessful = success;
+ if( !d->readSuccessful )
+ d->md5Job->cancel();
+ else {
+ d->alreadyReadSectors += trackLength( d->currentTrackIndex );
+
+ // close the pipe and let the md5 job finish gracefully
+ d->pipe.closeIn();
+ // d->md5Job->stop();
+ }
+}
+
+
+K3b::Msf K3bVerificationJob::trackLength( int trackIndex )
+{
+ K3b::Msf& trackSize = d->tracks[trackIndex].length;
+ const int& trackNum = d->tracks[trackIndex].trackNumber;
+
+ if( trackSize == 0 ) {
+ trackSize = d->toc[trackNum-1].length();
+
+ if( d->diskInfo.mediaType() & (K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_RW_OVWR) ) {
+ K3bIso9660 isoF( d->device, d->toc[trackNum-1].firstSector().lba() );
+ if( isoF.open() ) {
+ trackSize = isoF.primaryDescriptor().volumeSpaceSize;
+ }
+ else {
+ emit infoMessage( i18n("Unable to determine the ISO9660 filesystem size."), ERROR );
+ return 0;
+ }
+ }
+
+ //
+ // A data track recorded in TAO mode has two run-out blocks which cannot be read and contain
+ // zero data anyway. The problem is that I do not know of a valid method to determine if a track
+ // was written in TAO (the control nibble does definitely not work, I never saw one which did not
+ // equal 4).
+ // So the solution for now is to simply try to read the last sector of a data track. If this is not
+ // possible we assume it was written in TAO mode and reduce the length by 2 sectors
+ //
+ if( d->toc[trackNum-1].type() == K3bDevice::Track::DATA &&
+ d->diskInfo.mediaType() & K3bDevice::MEDIA_CD_ALL ) {
+ // we try twice just to be sure
+ unsigned char buffer[2048];
+ if( !d->device->read10( buffer, 2048, d->toc[trackNum-1].lastSector().lba(), 1 ) &&
+ !d->device->read10( buffer, 2048, d->toc[trackNum-1].lastSector().lba(), 1 ) ) {
+ trackSize -= 2;
+ kdDebug() << "(K3bCdCopyJob) track " << trackNum << " probably TAO recorded." << endl;
+ }
+ }
+ }
+
+ return trackSize;
+}
+
+
+#include "k3bverificationjob.moc"
diff --git a/libk3b/jobs/k3bverificationjob.h b/libk3b/jobs/k3bverificationjob.h
new file mode 100644
index 0000000..ad750ee
--- /dev/null
+++ b/libk3b/jobs/k3bverificationjob.h
@@ -0,0 +1,92 @@
+/*
+ *
+ * $Id: k3bisoimageverificationjob.h 597651 2006-10-21 08:04:01Z trueg $
+ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_VERIFICATION_JOB_H_
+#define _K3B_VERIFICATION_JOB_H_
+
+#include <k3bjob.h>
+
+namespace K3bDevice {
+ class Device;
+ class DeviceHandler;
+}
+
+
+/**
+ * Generic verification job. Add tracks to be verified via addTrack.
+ * The job will then verifiy the tracks set against the set checksums.
+ *
+ * The different track types are handled as follows:
+ * \li Data/DVD tracks: Read the track with a 2048 bytes sector size.
+ * Tracks length on DVD+RW media will be read from the iso9660
+ * descriptor.
+ * \li Audio tracks: Rip the track with a 2352 bytes sector size.
+ * In the case of audio tracks the job will not fail if the checksums
+ * differ becasue audio CD tracks do not contain error correction data.
+ * In this case only a warning will be emitted.
+ *
+ * Other sector sizes than 2048 bytes for data tracks are not supported yet,
+ * i.e. Video CDs cannot be verified.
+ *
+ * TAO written tracks have two run-out sectors that are not read.
+ *
+ * The VerificationJob will also reload the medium before starting.
+ */
+class K3bVerificationJob : public K3bJob
+{
+ Q_OBJECT
+
+ public:
+ K3bVerificationJob( K3bJobHandler*, QObject* parent = 0, const char* name = 0 );
+ ~K3bVerificationJob();
+
+ public slots:
+ void start();
+ void cancel();
+ void setDevice( K3bDevice::Device* dev );
+
+ void clear();
+
+ /**
+ * Add a track to be verified.
+ * \param tracknum The number of the track. If \a tracknum is 0
+ * the last track will be verified.
+ * \param length Set to override the track length from the TOC. This may be
+ * useful when writing to DVD+RW media and the iso descriptor does not
+ * contain the exact image size (as true for many commercial Video DVDs)
+ */
+ void addTrack( int tracknum, const QCString& checksum, const K3b::Msf& length = K3b::Msf() );
+
+ /**
+ * Handle the special case of iso session growing
+ */
+ void setGrownSessionSize( const K3b::Msf& );
+
+ private slots:
+ void slotMediaReloaded( bool success );
+ void slotDiskInfoReady( K3bDevice::DeviceHandler* dh );
+ void readTrack( int trackIndex );
+ void slotMd5JobFinished( bool success );
+ void slotReaderProgress( int p );
+ void slotReaderFinished( bool success );
+
+ private:
+ K3b::Msf trackLength( int trackNum );
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/jobs/k3bvideodvdtitledetectclippingjob.cpp b/libk3b/jobs/k3bvideodvdtitledetectclippingjob.cpp
new file mode 100644
index 0000000..fdcc3a4
--- /dev/null
+++ b/libk3b/jobs/k3bvideodvdtitledetectclippingjob.cpp
@@ -0,0 +1,291 @@
+/*
+ *
+ * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
+ * Copyright (C) 2006 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bvideodvdtitledetectclippingjob.h"
+
+#include <k3bexternalbinmanager.h>
+#include <k3bprocess.h>
+#include <k3bcore.h>
+#include <k3bglobals.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+
+static const int s_unrealisticHighClippingValue = 100000;
+
+
+class K3bVideoDVDTitleDetectClippingJob::Private
+{
+public:
+ const K3bExternalBin* usedTranscodeBin;
+
+ K3bProcess* process;
+
+ bool canceled;
+
+ unsigned int currentChapter;
+ unsigned int currentFrames;
+ unsigned int totalChapters;
+
+ int lastProgress;
+ int lastSubProgress;
+};
+
+
+
+K3bVideoDVDTitleDetectClippingJob::K3bVideoDVDTitleDetectClippingJob( K3bJobHandler* hdl, QObject* parent )
+ : K3bJob( hdl, parent ),
+ m_clippingTop( 0 ),
+ m_clippingBottom( 0 ),
+ m_clippingLeft( 0 ),
+ m_clippingRight( 0 ),
+ m_lowPriority( true )
+{
+ d = new Private;
+ d->process = 0;
+}
+
+
+K3bVideoDVDTitleDetectClippingJob::~K3bVideoDVDTitleDetectClippingJob()
+{
+ delete d->process;
+ delete d;
+}
+
+
+void K3bVideoDVDTitleDetectClippingJob::start()
+{
+ jobStarted();
+
+ d->canceled = false;
+ d->lastProgress = 0;
+
+ //
+ // It seems as if the last chapter is often way too short
+ //
+ d->totalChapters = m_dvd[m_titleNumber-1].numPTTs();
+ if( d->totalChapters > 1 && m_dvd[m_titleNumber-1][d->totalChapters-1].playbackTime().totalFrames() < 200 )
+ d->totalChapters--;
+
+ // initial values (some way to big value)
+ m_clippingTop = s_unrealisticHighClippingValue;
+ m_clippingBottom = s_unrealisticHighClippingValue;
+ m_clippingLeft = s_unrealisticHighClippingValue;
+ m_clippingRight = s_unrealisticHighClippingValue;
+
+ d->usedTranscodeBin = k3bcore->externalBinManager()->binObject("transcode");
+ if( !d->usedTranscodeBin ) {
+ emit infoMessage( i18n("%1 executable could not be found.").arg("transcode"), ERROR );
+ jobFinished( false );
+ return;
+ }
+
+ if( d->usedTranscodeBin->version < K3bVersion( 1, 0, 0 ) ){
+ emit infoMessage( i18n("%1 version %2 is too old.")
+ .arg("transcode")
+ .arg(d->usedTranscodeBin->version), ERROR );
+ jobFinished( false );
+ return;
+ }
+
+ emit debuggingOutput( "Used versions", "transcode: " + d->usedTranscodeBin->version );
+
+ if( !d->usedTranscodeBin->copyright.isEmpty() )
+ emit infoMessage( i18n("Using %1 %2 - Copyright (C) %3")
+ .arg(d->usedTranscodeBin->name())
+ .arg(d->usedTranscodeBin->version)
+ .arg(d->usedTranscodeBin->copyright), INFO );
+
+ emit newTask( i18n("Analysing Title %1 of Video DVD %2").arg(m_titleNumber).arg(m_dvd.volumeIdentifier()) );
+
+ startTranscode( 1 );
+}
+
+
+void K3bVideoDVDTitleDetectClippingJob::startTranscode( int chapter )
+{
+ d->currentChapter = chapter;
+ d->lastSubProgress = 0;
+
+ //
+ // If we have only one chapter and it is not longer than 2 minutes (value guessed based on some test DVD)
+ // use the whole chapter
+ //
+ if( d->totalChapters == 1 )
+ d->currentFrames = QMIN( 3000, QMAX( 1, m_dvd[m_titleNumber-1][d->currentChapter-1].playbackTime().totalFrames() ) );
+ else
+ d->currentFrames = QMIN( 200, QMAX( 1, m_dvd[m_titleNumber-1][d->currentChapter-1].playbackTime().totalFrames() ) );
+
+ //
+ // prepare the process
+ //
+ delete d->process;
+ d->process = new K3bProcess();
+ d->process->setSuppressEmptyLines(true);
+ d->process->setSplitStdout(true);
+ // connect( d->process, SIGNAL(stderrLine(const QString&)), this, SLOT(slotTranscodeStderr(const QString&)) );
+ connect( d->process, SIGNAL(stdoutLine(const QString&)), this, SLOT(slotTranscodeStderr(const QString&)) );
+ connect( d->process, SIGNAL(processExited(KProcess*)), this, SLOT(slotTranscodeExited(KProcess*)) );
+
+ // the executable
+ *d->process << d->usedTranscodeBin;
+
+ // low priority
+ if( m_lowPriority )
+ *d->process << "--nice" << "19";
+
+ // the input
+ *d->process << "-i" << m_dvd.device()->blockDeviceName();
+
+ // select the title number and chapter
+ *d->process << "-T" << QString("%1,%2").arg(m_titleNumber).arg(chapter);
+
+ // null output
+ *d->process << "-y" << "null,null" << "-o" << "/dev/null";
+
+ // analyze the first 200 frames
+ *d->process << "-J" << QString("detectclipping=range=0-%1/5").arg(d->currentFrames);
+
+ // also only decode the first 200 frames
+ *d->process << "-c" << QString("0-%1").arg(d->currentFrames+1);
+
+ // additional user parameters from config
+ const QStringList& params = d->usedTranscodeBin->userParameters();
+ for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
+ *d->process << *it;
+
+ // produce some debugging output
+ kdDebug() << "***** transcode parameters:\n";
+ const QValueList<QCString>& args = d->process->args();
+ QString s;
+ for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) {
+ s += *it + " ";
+ }
+ kdDebug() << s << flush << endl;
+ emit debuggingOutput( d->usedTranscodeBin->name() + " command:", s);
+
+ // start the process
+ if( !d->process->start( KProcess::NotifyOnExit, KProcess::All ) ) {
+ // something went wrong when starting the program
+ // it "should" be the executable
+ emit infoMessage( i18n("Could not start %1.").arg(d->usedTranscodeBin->name()), K3bJob::ERROR );
+ jobFinished(false);
+ }
+ else {
+ emit newSubTask( i18n("Analysing Chapter %1 of %2").arg(chapter).arg(m_dvd[m_titleNumber-1].numPTTs()) );
+ emit subPercent( 0 );
+ }
+}
+
+
+void K3bVideoDVDTitleDetectClippingJob::cancel()
+{
+ d->canceled = true;
+ if( d->process && d->process->isRunning() )
+ d->process->kill();
+}
+
+
+void K3bVideoDVDTitleDetectClippingJob::slotTranscodeStderr( const QString& line )
+{
+ emit debuggingOutput( "transcode", line );
+
+ // parse progress
+ // encoding frame [185], 24.02 fps, 93.0%, ETA: 0:00:00, ( 0| 0| 0)
+ if( line.startsWith( "encoding frame" ) ) {
+ int pos1 = line.find( '[', 15 );
+ int pos2 = line.find( ']', pos1+1 );
+ if( pos1 > 0 && pos2 > 0 ) {
+ bool ok;
+ int encodedFrames = line.mid( pos1+1, pos2-pos1-1 ).toInt( &ok );
+ if( ok ) {
+ int progress = 100 * encodedFrames / d->currentFrames;
+
+ if( progress > d->lastSubProgress ) {
+ d->lastSubProgress = progress;
+ emit subPercent( progress );
+ }
+
+ double part = 100.0 / (double)d->totalChapters;
+
+ progress = (int)( ( (double)(d->currentChapter-1) * part )
+ + ( (double)progress / (double)d->totalChapters )
+ + 0.5 );
+
+ if( progress > d->lastProgress ) {
+ d->lastProgress = progress;
+ emit percent( progress );
+ }
+ }
+ }
+ }
+
+ // [detectclipping#0] valid area: X: 5..719 Y: 72..507 -> -j 72,6,68,0
+ else if( line.startsWith( "[detectclipping" ) ) {
+ int pos = line.find( "-j" );
+ if( pos > 0 ) {
+ QStringList values = QStringList::split( ',', line.mid( pos+3 ) );
+ m_clippingTop = QMIN( m_clippingTop, values[0].toInt() );
+ m_clippingLeft = QMIN( m_clippingLeft, values[1].toInt() );
+ m_clippingBottom = QMIN( m_clippingBottom, values[2].toInt() );
+ m_clippingRight = QMIN( m_clippingRight, values[3].toInt() );
+ }
+ else
+ kdDebug() << "(K3bVideoDVDTitleDetectClippingJob) failed to parse line: " << line << endl;
+ }
+}
+
+
+void K3bVideoDVDTitleDetectClippingJob::slotTranscodeExited( KProcess* p )
+{
+ switch( p->exitStatus() ) {
+ case 0:
+ d->currentChapter++;
+ if( d->currentChapter > d->totalChapters ) {
+ //
+ // check if we did set any values at all
+ //
+ if( m_clippingTop == s_unrealisticHighClippingValue )
+ m_clippingTop = m_clippingLeft = m_clippingBottom = m_clippingRight = 0;
+
+ if( d->totalChapters < m_dvd[m_titleNumber-1].numPTTs() )
+ emit infoMessage( i18n("Ignoring last chapter due to its short playback time."), INFO );
+
+ jobFinished( true );
+ }
+ else {
+ startTranscode( d->currentChapter );
+ }
+ break;
+
+ default:
+ // FIXME: error handling
+
+ if( d->canceled ) {
+ emit canceled();
+ }
+ else {
+ emit infoMessage( i18n("%1 returned an unknown error (code %2).")
+ .arg(d->usedTranscodeBin->name()).arg(p->exitStatus()),
+ K3bJob::ERROR );
+ emit infoMessage( i18n("Please send me an email with the last output."), K3bJob::ERROR );
+ }
+
+ jobFinished( false );
+ }
+}
+
+#include "k3bvideodvdtitledetectclippingjob.moc"
diff --git a/libk3b/jobs/k3bvideodvdtitledetectclippingjob.h b/libk3b/jobs/k3bvideodvdtitledetectclippingjob.h
new file mode 100644
index 0000000..b13bbf8
--- /dev/null
+++ b/libk3b/jobs/k3bvideodvdtitledetectclippingjob.h
@@ -0,0 +1,106 @@
+/*
+ *
+ * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
+ * Copyright (C) 2006 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_VIDEODVD_TITLE_DETECTCLIPPING_JOB_H_
+#define _K3B_VIDEODVD_TITLE_DETECTCLIPPING_JOB_H_
+
+#include <k3b_export.h>
+#include <k3bjob.h>
+#include <k3bvideodvd.h>
+
+class KProcess;
+
+/**
+ * Job to detect the clipping values for a Video DVD title.
+ */
+class LIBK3B_EXPORT K3bVideoDVDTitleDetectClippingJob : public K3bJob
+{
+ Q_OBJECT
+
+ public:
+ K3bVideoDVDTitleDetectClippingJob( K3bJobHandler* hdl, QObject* parent );
+ ~K3bVideoDVDTitleDetectClippingJob();
+
+ const K3bVideoDVD::VideoDVD& videoDVD() const { return m_dvd; }
+ int title() const { return m_titleNumber; }
+ bool lowPriority() const { return m_lowPriority; }
+
+ /**
+ * Only valid after a successful completion of the job.
+ */
+ int clippingTop() const { return m_clippingTop; }
+
+ /**
+ * Only valid after a successful completion of the job.
+ */
+ int clippingLeft() const { return m_clippingLeft; }
+
+ /**
+ * Only valid after a successful completion of the job.
+ */
+ int clippingBottom() const { return m_clippingBottom; }
+
+ /**
+ * Only valid after a successful completion of the job.
+ */
+ int clippingRight() const { return m_clippingRight; }
+
+ public slots:
+ void start();
+ void cancel();
+
+ /**
+ * The device containing the Video DVD
+ */
+ void setVideoDVD( const K3bVideoDVD::VideoDVD& dvd ) { m_dvd = dvd; }
+
+ /**
+ * Set the title number to be analysed
+ *
+ * The default value is 1, denoting the first title.
+ */
+ void setTitle( int t ) { m_titleNumber = t; }
+
+ /**
+ * If true the transcode processes will be run with a very low scheduling
+ * priority.
+ *
+ * The default is true.
+ */
+ void setLowPriority( bool b ) { m_lowPriority = b; }
+
+ private slots:
+ void slotTranscodeStderr( const QString& );
+ void slotTranscodeExited( KProcess* );
+
+ private:
+ void startTranscode( int chapter );
+
+ K3bVideoDVD::VideoDVD m_dvd;
+
+ int m_clippingTop;
+ int m_clippingBottom;
+ int m_clippingLeft;
+ int m_clippingRight;
+
+ int m_titleNumber;
+
+ bool m_lowPriority;
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/jobs/k3bvideodvdtitletranscodingjob.cpp b/libk3b/jobs/k3bvideodvdtitletranscodingjob.cpp
new file mode 100644
index 0000000..9fec637
--- /dev/null
+++ b/libk3b/jobs/k3bvideodvdtitletranscodingjob.cpp
@@ -0,0 +1,583 @@
+/*
+ *
+ * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
+ * Copyright (C) 2006 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#include "k3bvideodvdtitletranscodingjob.h"
+
+#include <k3bexternalbinmanager.h>
+#include <k3bprocess.h>
+#include <k3bcore.h>
+#include <k3bglobals.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+
+#include <qfile.h>
+#include <qfileinfo.h>
+
+
+class K3bVideoDVDTitleTranscodingJob::Private
+{
+public:
+ const K3bExternalBin* usedTranscodeBin;
+
+ K3bProcess* process;
+
+ QString twoPassEncodingLogFile;
+
+ int currentEncodingPass;
+
+ bool canceled;
+
+ int lastProgress;
+ int lastSubProgress;
+};
+
+
+
+K3bVideoDVDTitleTranscodingJob::K3bVideoDVDTitleTranscodingJob( K3bJobHandler* hdl, QObject* parent )
+ : K3bJob( hdl, parent ),
+ m_clippingTop( 0 ),
+ m_clippingBottom( 0 ),
+ m_clippingLeft( 0 ),
+ m_clippingRight( 0 ),
+ m_width( 0 ),
+ m_height( 0 ),
+ m_titleNumber( 1 ),
+ m_audioStreamIndex( 0 ),
+ m_videoCodec( VIDEO_CODEC_FFMPEG_MPEG4 ),
+ m_audioCodec( AUDIO_CODEC_MP3 ),
+ m_videoBitrate( 1800 ),
+ m_audioBitrate( 128 ),
+ m_audioVBR( false ),
+ m_resampleAudio( false ),
+ m_twoPassEncoding( false ),
+ m_lowPriority( true )
+{
+ d = new Private;
+ d->process = 0;
+}
+
+
+K3bVideoDVDTitleTranscodingJob::~K3bVideoDVDTitleTranscodingJob()
+{
+ delete d->process;
+ delete d;
+}
+
+
+void K3bVideoDVDTitleTranscodingJob::start()
+{
+ jobStarted();
+
+ d->canceled = false;
+ d->lastProgress = 0;
+
+ d->usedTranscodeBin = k3bcore->externalBinManager()->binObject("transcode");
+ if( !d->usedTranscodeBin ) {
+ emit infoMessage( i18n("%1 executable could not be found.").arg("transcode"), ERROR );
+ jobFinished( false );
+ return;
+ }
+
+ if( d->usedTranscodeBin->version < K3bVersion( 1, 0, 0 ) ){
+ emit infoMessage( i18n("%1 version %2 is too old.")
+ .arg("transcode")
+ .arg(d->usedTranscodeBin->version), ERROR );
+ jobFinished( false );
+ return;
+ }
+
+ emit debuggingOutput( "Used versions", "transcode: " + d->usedTranscodeBin->version );
+
+ if( !d->usedTranscodeBin->copyright.isEmpty() )
+ emit infoMessage( i18n("Using %1 %2 - Copyright (C) %3")
+ .arg(d->usedTranscodeBin->name())
+ .arg(d->usedTranscodeBin->version)
+ .arg(d->usedTranscodeBin->copyright), INFO );
+
+ //
+ // Let's take a look at the filename
+ //
+ if( m_filename.isEmpty() ) {
+ m_filename = K3b::findTempFile( "avi" );
+ }
+ else {
+ // let's see if the directory exists and we can write to it
+ QFileInfo fileInfo( m_filename );
+ QFileInfo dirInfo( fileInfo.dirPath() );
+ if( !dirInfo.exists() && !KStandardDirs::makeDir( dirInfo.absFilePath() ) ) {
+ emit infoMessage( i18n("Unable to create folder '%1'").arg(dirInfo.filePath()), ERROR );
+ return;
+ }
+ else if( !dirInfo.isDir() || !dirInfo.isWritable() ) {
+ emit infoMessage( i18n("Invalid filename: '%1'").arg(m_filename), ERROR );
+ jobFinished( false );
+ return;
+ }
+ }
+
+ //
+ // Determine a log file for two-pass encoding
+ //
+ d->twoPassEncodingLogFile = K3b::findTempFile( "log" );
+
+ emit newTask( i18n("Transcoding title %1 from Video DVD %2").arg(m_titleNumber).arg(m_dvd.volumeIdentifier()) );
+
+ //
+ // Ok then, let's begin
+ //
+ startTranscode( m_twoPassEncoding ? 1 : 0 );
+}
+
+
+void K3bVideoDVDTitleTranscodingJob::startTranscode( int pass )
+{
+ d->currentEncodingPass = pass;
+ d->lastSubProgress = 0;
+
+ QString videoCodecString;
+ switch( m_videoCodec ) {
+ case VIDEO_CODEC_XVID:
+ videoCodecString = "xvid";
+ break;
+
+ case VIDEO_CODEC_FFMPEG_MPEG4:
+ videoCodecString = "ffmpeg";
+ break;
+
+ default:
+ emit infoMessage( i18n("Invalid Video codec set: %1").arg(m_videoCodec), ERROR );
+ jobFinished( false );
+ return;
+ }
+
+ QString audioCodecString;
+ switch( m_audioCodec ) {
+ case AUDIO_CODEC_MP3:
+ audioCodecString = "0x55";
+ break;
+
+ // ogg only works (as in: transcode does something) with .y <codec>,ogg
+ // but then the video is garbage (at least to xine and mplayer on my system)
+ // case AUDIO_CODEC_OGG_VORBIS:
+ // audioCodecString = "0xfffe";
+ // break;
+
+ case AUDIO_CODEC_AC3_STEREO:
+ case AUDIO_CODEC_AC3_PASSTHROUGH:
+ audioCodecString = "0x2000";
+ break;
+
+ default:
+ emit infoMessage( i18n("Invalid Audio codec set: %1").arg(m_audioCodec), ERROR );
+ jobFinished( false );
+ return;
+ }
+
+ //
+ // prepare the process
+ //
+ delete d->process;
+ d->process = new K3bProcess();
+ d->process->setSuppressEmptyLines(true);
+ d->process->setSplitStdout(true);
+ connect( d->process, SIGNAL(stderrLine(const QString&)), this, SLOT(slotTranscodeStderr(const QString&)) );
+ connect( d->process, SIGNAL(stdoutLine(const QString&)), this, SLOT(slotTranscodeStderr(const QString&)) );
+ connect( d->process, SIGNAL(processExited(KProcess*)), this, SLOT(slotTranscodeExited(KProcess*)) );
+
+ // the executable
+ *d->process << d->usedTranscodeBin;
+
+ // low priority
+ if( m_lowPriority )
+ *d->process << "--nice" << "19";
+
+ // we only need 100 steps, but to make sure we use 150
+ if ( d->usedTranscodeBin->version.simplify() >= K3bVersion( 1, 1, 0 ) )
+ *d->process << "--progress_meter" << "2" << "--progress_rate" << QString::number(m_dvd[m_titleNumber-1].playbackTime().totalFrames()/150);
+ else
+ *d->process << "--print_status" << QString::number(m_dvd[m_titleNumber-1].playbackTime().totalFrames()/150);
+
+ // the input
+ *d->process << "-i" << m_dvd.device()->blockDeviceName();
+
+ // just to make sure
+ *d->process << "-x" << "dvd";
+
+ // select the title number
+ *d->process << "-T" << QString("%1,-1,1").arg( m_titleNumber );
+
+ // select the audio stream to extract
+ if ( m_dvd[m_titleNumber-1].numAudioStreams() > 0 )
+ *d->process << "-a" << QString::number( m_audioStreamIndex );
+
+ // clipping
+ *d->process << "-j" << QString("%1,%2,%3,%4")
+ .arg(m_clippingTop)
+ .arg(m_clippingLeft)
+ .arg(m_clippingBottom)
+ .arg(m_clippingRight);
+
+ // select the encoding type (single pass or two-pass) and the log file for two-pass encoding
+ // the latter is unused for pass = 0
+ *d->process << "-R" << QString("%1,%2").arg( pass ).arg( d->twoPassEncodingLogFile );
+
+ // depending on the pass we use different options
+ if( pass != 1 ) {
+ // select video codec
+ *d->process << "-y" << videoCodecString;
+
+ // select the audio codec to use
+ *d->process << "-N" << audioCodecString;
+
+ if( m_audioCodec == AUDIO_CODEC_AC3_PASSTHROUGH ) {
+ // keep 5.1 sound
+ *d->process << "-A";
+ }
+ else {
+ // audio quality settings
+ *d->process << "-b" << QString("%1,%2").arg(m_audioBitrate).arg(m_audioVBR ? 1 : 0);
+
+ // resample audio stream to 44.1 khz
+ if( m_resampleAudio )
+ *d->process << "-E" << "44100";
+ }
+
+ // the output filename
+ *d->process << "-o" << m_filename;
+ }
+ else {
+ // gather information about the video stream, ignore audio
+ *d->process << "-y" << QString("%1,null").arg( videoCodecString );
+
+ // we ignore the output from the first pass
+ *d->process << "-o" << "/dev/null";
+ }
+
+ // choose the ffmpeg codec
+ if( m_videoCodec == VIDEO_CODEC_FFMPEG_MPEG4 ) {
+ *d->process << "-F" << "mpeg4";
+ }
+
+ // video bitrate
+ *d->process << "-w" << QString::number( m_videoBitrate );
+
+ // video resizing
+ int usedWidth = m_width;
+ int usedHeight = m_height;
+ if( m_width == 0 || m_height == 0 ) {
+ //
+ // The "real" size of the video, considering anamorph encoding
+ //
+ int realHeight = m_dvd[m_titleNumber-1].videoStream().realPictureHeight();
+ int readWidth = m_dvd[m_titleNumber-1].videoStream().realPictureWidth();
+
+ //
+ // The clipped size with the correct aspect ratio
+ //
+ int clippedHeight = realHeight - m_clippingTop - m_clippingBottom;
+ int clippedWidth = readWidth - m_clippingLeft - m_clippingRight;
+
+ //
+ // Now simply resize the clipped video to the wanted size
+ //
+ if( usedWidth > 0 ) {
+ usedHeight = clippedHeight * usedWidth / clippedWidth;
+ }
+ else {
+ if( usedHeight == 0 ) {
+ //
+ // This is the default case in which both m_width and m_height are 0.
+ // The result will be a size of clippedWidth x clippedHeight
+ //
+ usedHeight = clippedHeight;
+ }
+ usedWidth = clippedWidth * usedHeight / clippedHeight;
+ }
+ }
+
+ //
+ // Now make sure both width and height are multiple of 16 the simple way
+ //
+ usedWidth -= usedWidth%16;
+ usedHeight -= usedHeight%16;
+
+ // we only give information about the resizing of the video once
+ if( pass < 2 )
+ emit infoMessage( i18n("Resizing picture of title %1 to %2x%3").arg(m_titleNumber).arg(usedWidth).arg(usedHeight), INFO );
+ *d->process << "-Z" << QString("%1x%2").arg(usedWidth).arg(usedHeight);
+
+ // additional user parameters from config
+ const QStringList& params = d->usedTranscodeBin->userParameters();
+ for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
+ *d->process << *it;
+
+ // produce some debugging output
+ kdDebug() << "***** transcode parameters:\n";
+ const QValueList<QCString>& args = d->process->args();
+ QString s;
+ for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) {
+ s += *it + " ";
+ }
+ kdDebug() << s << flush << endl;
+ emit debuggingOutput( d->usedTranscodeBin->name() + " command:", s);
+
+ // start the process
+ if( !d->process->start( KProcess::NotifyOnExit, KProcess::All ) ) {
+ // something went wrong when starting the program
+ // it "should" be the executable
+ emit infoMessage( i18n("Could not start %1.").arg(d->usedTranscodeBin->name()), K3bJob::ERROR );
+ jobFinished(false);
+ }
+ else {
+ if( pass == 0 )
+ emit newSubTask( i18n("Single-pass Encoding") );
+ else if( pass == 1 )
+ emit newSubTask( i18n("Two-pass Encoding: First Pass") );
+ else
+ emit newSubTask( i18n("Two-pass Encoding: Second Pass") );
+
+ emit subPercent( 0 );
+ }
+}
+
+
+void K3bVideoDVDTitleTranscodingJob::cancel()
+{
+ // FIXME: do not cancel before one frame has been encoded. transcode seems to hang then
+ // find a way to determine all subprocess ids to kill all of them
+ d->canceled = true;
+ if( d->process && d->process->isRunning() )
+ d->process->kill();
+}
+
+
+void K3bVideoDVDTitleTranscodingJob::cleanup( bool success )
+{
+ if( QFile::exists( d->twoPassEncodingLogFile ) ) {
+ QFile::remove( d->twoPassEncodingLogFile );
+ }
+
+ if( !success && QFile::exists( m_filename ) ) {
+ emit infoMessage( i18n("Removing incomplete video file '%1'").arg(m_filename), INFO );
+ QFile::remove( m_filename );
+ }
+}
+
+
+void K3bVideoDVDTitleTranscodingJob::slotTranscodeStderr( const QString& line )
+{
+ emit debuggingOutput( "transcode", line );
+
+ // parse progress
+ // encoding frames [000000-000144], 27.58 fps, EMT: 0:00:05, ( 0| 0| 0)
+ if( line.startsWith( "encoding frame" ) ) {
+ int pos1 = line.find( '-', 15 );
+ int pos2 = line.find( ']', pos1+1 );
+ if( pos1 > 0 && pos2 > 0 ) {
+ bool ok;
+ int encodedFrames = line.mid( pos1+1, pos2-pos1-1 ).toInt( &ok );
+ if( ok ) {
+ int progress = 100 * encodedFrames / m_dvd[m_titleNumber-1].playbackTime().totalFrames();
+
+ if( progress > d->lastSubProgress ) {
+ d->lastSubProgress = progress;
+ emit subPercent( progress );
+ }
+
+ if( m_twoPassEncoding ) {
+ progress /= 2;
+ if( d->currentEncodingPass == 2 )
+ progress += 50;
+ }
+
+ if( progress > d->lastProgress ) {
+ d->lastProgress = progress;
+ emit percent( progress );
+ }
+ }
+ }
+ }
+}
+
+
+void K3bVideoDVDTitleTranscodingJob::slotTranscodeExited( KProcess* p )
+{
+ if( d->canceled ) {
+ emit canceled();
+ cleanup( false );
+ jobFinished( false );
+ }
+ else if( p->normalExit() ) {
+ switch( p->exitStatus() ) {
+ case 0:
+ if( d->currentEncodingPass == 1 ) {
+ emit percent( 50 );
+ // start second encoding pass
+ startTranscode( 2 );
+ }
+ else {
+ emit percent( 100 );
+ cleanup( true );
+ jobFinished( true );
+ }
+ break;
+
+ default:
+ // FIXME: error handling
+
+ emit infoMessage( i18n("%1 returned an unknown error (code %2).")
+ .arg(d->usedTranscodeBin->name()).arg(p->exitStatus()),
+ K3bJob::ERROR );
+ emit infoMessage( i18n("Please send me an email with the last output."), K3bJob::ERROR );
+
+ cleanup( false );
+ jobFinished( false );
+ }
+ }
+ else {
+ cleanup( false );
+ emit infoMessage( i18n("Execution of %1 failed.").arg("transcode"), ERROR );
+ emit infoMessage( i18n("Please consult the debugging output for details."), ERROR );
+ jobFinished( false );
+ }
+}
+
+
+void K3bVideoDVDTitleTranscodingJob::setClipping( int top, int left, int bottom, int right )
+{
+ m_clippingTop = top;
+ m_clippingLeft = left;
+ m_clippingBottom = bottom;
+ m_clippingRight = right;
+
+ //
+ // transcode seems unable to handle different clipping values for left and right
+ //
+ m_clippingLeft = m_clippingRight = QMIN( m_clippingRight, m_clippingLeft );
+}
+
+
+void K3bVideoDVDTitleTranscodingJob::setSize( int width, int height )
+{
+ m_width = width;
+ m_height = height;
+}
+
+
+QString K3bVideoDVDTitleTranscodingJob::audioCodecString( K3bVideoDVDTitleTranscodingJob::AudioCodec codec )
+{
+ switch( codec ) {
+ case AUDIO_CODEC_AC3_STEREO:
+ return i18n("AC3 (Stereo)");
+ case AUDIO_CODEC_AC3_PASSTHROUGH:
+ return i18n("AC3 (Pass-through)");
+ case AUDIO_CODEC_MP3:
+ return i18n("MPEG1 Layer III");
+ default:
+ return "unknown audio codec";
+ }
+}
+
+
+QString K3bVideoDVDTitleTranscodingJob::videoCodecString( K3bVideoDVDTitleTranscodingJob::VideoCodec codec )
+{
+ switch( codec ) {
+ case VIDEO_CODEC_FFMPEG_MPEG4:
+ return i18n("MPEG4 (FFMPEG)");
+ case VIDEO_CODEC_XVID:
+ return i18n("XviD");
+ default:
+ return "unknown video codec";
+ }
+}
+
+
+QString K3bVideoDVDTitleTranscodingJob::videoCodecDescription( K3bVideoDVDTitleTranscodingJob::VideoCodec codec )
+{
+ switch( codec ) {
+ case VIDEO_CODEC_FFMPEG_MPEG4:
+ return i18n("FFmpeg is an open-source project trying to support most video and audio codecs used "
+ "these days. Its subproject libavcodec forms the basis for multimedia players such as "
+ "xine or mplayer.")
+ + "<br>"
+ + i18n("FFmpeg contains an implementation of the MPEG-4 video encoding standard which produces "
+ "high quality results.");
+ case VIDEO_CODEC_XVID:
+ return i18n("XviD is a free and open source MPEG-4 video codec. XviD was created by a group of "
+ "volunteer programmers after the OpenDivX source was closed in July 2001.")
+ + "<br>"
+ + i18n("XviD features MPEG-4 Advanced Profile settings such as b-frames, global "
+ "and quarter pixel motion compensation, lumi masking, trellis quantization, and "
+ "H.263, MPEG and custom quantization matrices.")
+ + "<br>"
+ + i18n("XviD is a primary competitor of DivX (XviD being DivX spelled backwards). "
+ "While DivX is closed source and may only run on Windows, Mac OS and Linux, "
+ "XviD is open source and can potentially run on any platform.")
+ + "<br><em>"
+ + i18n("(Description taken from the Wikipedia article)")
+ + "</em>";
+ default:
+ return "unknown video codec";
+ }
+}
+
+
+QString K3bVideoDVDTitleTranscodingJob::audioCodecDescription( K3bVideoDVDTitleTranscodingJob::AudioCodec codec )
+{
+ static QString s_ac3General = i18n("AC3, better known as Dolby Digital is standardized as ATSC A/52. "
+ "It contains up to 6 total channels of sound.");
+ switch( codec ) {
+ case AUDIO_CODEC_AC3_STEREO:
+ return s_ac3General
+ + "<br>" + i18n("With this setting K3b will create a two-channel stereo "
+ "Dolby Digital audio stream.");
+ case AUDIO_CODEC_AC3_PASSTHROUGH:
+ return s_ac3General
+ + "<br>" + i18n("With this setting K3b will use the Dolby Digital audio stream "
+ "from the source DVD without changing it.")
+ + "<br>" + i18n("Use this setting to preserve 5.1 channel sound from the DVD.");
+ case AUDIO_CODEC_MP3:
+ return i18n("MPEG1 Layer III is better known as MP3 and is the most used lossy audio format.")
+ + "<br>" + i18n("With this setting K3b will create a two-channel stereo MPEG1 Layer III audio stream.");
+ default:
+ return "unknown audio codec";
+ }
+}
+
+
+bool K3bVideoDVDTitleTranscodingJob::transcodeBinaryHasSupportFor( K3bVideoDVDTitleTranscodingJob::VideoCodec codec, const K3bExternalBin* bin )
+{
+ static char* s_codecFeatures[] = { "xvid", "ffmpeg" };
+ if( !bin )
+ bin = k3bcore->externalBinManager()->binObject("transcode");
+ if( !bin )
+ return false;
+ return bin->hasFeature( QString::fromLatin1( s_codecFeatures[(int)codec] ) );
+}
+
+
+bool K3bVideoDVDTitleTranscodingJob::transcodeBinaryHasSupportFor( K3bVideoDVDTitleTranscodingJob::AudioCodec codec, const K3bExternalBin* bin )
+{
+ static char* s_codecFeatures[] = { "lame", "ac3", "ac3" };
+ if( !bin )
+ bin = k3bcore->externalBinManager()->binObject("transcode");
+ if( !bin )
+ return false;
+ return bin->hasFeature( QString::fromLatin1( s_codecFeatures[(int)codec] ) );
+}
+
+#include "k3bvideodvdtitletranscodingjob.moc"
diff --git a/libk3b/jobs/k3bvideodvdtitletranscodingjob.h b/libk3b/jobs/k3bvideodvdtitletranscodingjob.h
new file mode 100644
index 0000000..77a48b5
--- /dev/null
+++ b/libk3b/jobs/k3bvideodvdtitletranscodingjob.h
@@ -0,0 +1,275 @@
+/*
+ *
+ * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
+ * Copyright (C) 2006 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * See the file "COPYING" for the exact licensing terms.
+ */
+
+#ifndef _K3B_VIDEODVD_TITLE_TRANSCODING_JOB_H_
+#define _K3B_VIDEODVD_TITLE_TRANSCODING_JOB_H_
+
+#include <k3b_export.h>
+#include <k3bjob.h>
+#include <k3bvideodvd.h>
+
+class KProcess;
+class K3bExternalBin;
+
+
+/**
+ * The K3bVideoDVDTitleTranscodingJob rips a Video DVD title directly
+ * from the medium and transcodes it on-the-fly to, for example, an XviD video
+ *
+ * For now only one audio stream is supported.
+ */
+class LIBK3B_EXPORT K3bVideoDVDTitleTranscodingJob : public K3bJob
+{
+ Q_OBJECT
+
+ public:
+ K3bVideoDVDTitleTranscodingJob( K3bJobHandler* hdl, QObject* parent );
+ ~K3bVideoDVDTitleTranscodingJob();
+
+ /**
+ * The video codecs supported by this job.
+ */
+ enum VideoCodec {
+ VIDEO_CODEC_XVID,
+ VIDEO_CODEC_FFMPEG_MPEG4,
+ VIDEO_CODEC_NUM_ENTRIES /**< Do not use this as a codec. */
+ };
+
+ /**
+ * The audio codecs supported by this job.
+ */
+ enum AudioCodec {
+ AUDIO_CODEC_MP3,
+ /* AUDIO_CODEC_OGG_VORBIS,*/
+ AUDIO_CODEC_AC3_STEREO,
+ AUDIO_CODEC_AC3_PASSTHROUGH,
+ AUDIO_CODEC_NUM_ENTRIES /**< Do not use this as a codec. */
+ };
+
+ const K3bVideoDVD::VideoDVD& videoDVD() const { return m_dvd; }
+ int title() const { return m_titleNumber; }
+ int audioStream() const { return m_audioStreamIndex; }
+ int clippingTop() const { return m_clippingTop; }
+ int clippingLeft() const { return m_clippingLeft; }
+ int clippingBottom() const { return m_clippingBottom; }
+ int clippingRight() const { return m_clippingRight; }
+ int height() const { return m_height; }
+ int width() const { return m_width; }
+ const QString& filename() { return m_filename; }
+ VideoCodec videoCodec() const { return m_videoCodec; }
+ int videoBitrate() const { return m_videoBitrate; }
+ bool twoPassEncoding() const { return m_twoPassEncoding; }
+ AudioCodec audioCodec() const { return m_audioCodec; }
+ int audioBitrate() const { return m_audioBitrate; }
+ bool audioVBR() const { return m_audioVBR; }
+ bool resampleAudioTo44100() const { return m_resampleAudio; }
+ bool lowPriority() const { return m_lowPriority; }
+
+ /**
+ * \param bin If 0 the default binary from K3bCore will be used
+ */
+ static bool transcodeBinaryHasSupportFor( VideoCodec codec, const K3bExternalBin* bin = 0 );
+
+ /**
+ * \param bin If 0 the default binary from K3bCore will be used
+ */
+ static bool transcodeBinaryHasSupportFor( AudioCodec codec, const K3bExternalBin* bin = 0 );
+
+ static QString videoCodecString( VideoCodec );
+ static QString audioCodecString( AudioCodec );
+
+ static QString videoCodecDescription( VideoCodec );
+ static QString audioCodecDescription( AudioCodec );
+
+ public slots:
+ void start();
+ void cancel();
+
+ /**
+ * The device containing the Video DVD
+ */
+ void setVideoDVD( const K3bVideoDVD::VideoDVD& dvd ) { m_dvd = dvd; }
+
+ /**
+ * Set the title number to be transcoded
+ *
+ * The default value is 1, denoting the first title.
+ */
+ void setTitle( int t ) { m_titleNumber = t; }
+
+ /**
+ * Set the audio stream to use.
+ *
+ * For now K3b does not support encoding multiple audio streams
+ * in one video file.
+ *
+ * The default value is 0, meaning that the first audio stream will
+ * be encoded.
+ */
+ void setAudioStream( int i ) { m_audioStreamIndex = i; }
+
+ /**
+ * Set the clipping values for the Video title.
+ * The clipping will be applied before the transcoding.
+ *
+ * For now it is not possible to use different clipping values for left
+ * and right as transcode cannot handle this. Thus, the job uses the
+ * smaller value for both the left and right clipping.
+ *
+ * The default is to not clip the video.
+ */
+ void setClipping( int top, int left, int bottom, int right );
+
+ /**
+ * The size of the resulting transcoded video.
+ *
+ * The default is to automatically adjust the size (width=height=0), which
+ * essentially means that anamorph encoded source material will be resized
+ * according to its aspect ratio.
+ *
+ * It is also possible to set just the width or just the height and leave
+ * the other value to 0 which will then be determined automatically.
+ *
+ * The clipping values will be taken into account if at least one value
+ * is determined automatically.
+ *
+ * The width and height values have to be a multiple of 16. If it is not,
+ * they will be changed accordingly.
+ *
+ * FIXME: GET INFORMATION: why a multiple of 16 and not 8 or 32?
+ */
+ void setSize( int width, int height );
+
+ /**
+ * The filename to write the resulting video to.
+ *
+ * The default is some automatically generated filename
+ * in the default K3b temp directory.
+ */
+ void setFilename( const QString& name ) { m_filename = name; }
+
+ /**
+ * Set the video codec used to encode the video title.
+ *
+ * The default is VIDEO_CODEC_FFMPEG_MPEG4
+ */
+ void setVideoCodec( VideoCodec codec ) { m_videoCodec = codec; }
+
+ /**
+ * Set the bitrate used to encode the video.
+ *
+ * The default is 1800
+ */
+ void setVideoBitrate( int bitrate ) { m_videoBitrate = bitrate; }
+
+ /**
+ * Set if the job should use two-pass encoding to improve
+ * the quality of the resulting video.
+ *
+ * The default is false.
+ */
+ void setTwoPassEncoding( bool b ) { m_twoPassEncoding = b; }
+
+ /**
+ * Set the audio codec used to encode the audio stream
+ * in the video title.
+ *
+ * The default is AUDIO_CODEC_MP3
+ */
+ void setAudioCodec( AudioCodec codec ) { m_audioCodec = codec; }
+
+ /**
+ * Set the bitrate used to encode the audio stream.
+ *
+ * The default is 128
+ *
+ * In case of the AC3 codec the bitrate can be some value between 32 and 640.
+ *
+ * For the AC3 passthrough mode the bitrate is ignored.
+ */
+ void setAudioBitrate( int bitrate ) { m_audioBitrate = bitrate; }
+
+ /**
+ * Set if the audio stream should be encoded with a variable bitrate.
+ *
+ * The default is false.
+ *
+ * For the AC3 passthrough mode the bitrate is ignored.
+ */
+ void setAudioVBR( bool vbr ) { m_audioVBR = vbr; }
+
+ /**
+ * Set if the audio data should be resampled to 44100 Hz/s
+ *
+ * The default is false.
+ *
+ * For the AC3 passthrough mode this is ignored.
+ */
+ void setResampleAudioTo44100( bool b ) { m_resampleAudio = b; }
+
+ /**
+ * If true the transcode processes will be run with a very low scheduling
+ * priority.
+ *
+ * The default is true.
+ */
+ void setLowPriority( bool b ) { m_lowPriority = b; }
+
+ private slots:
+ void slotTranscodeStderr( const QString& );
+ void slotTranscodeExited( KProcess* );
+
+ private:
+ /**
+ * \param 0 - single pass encoding
+ * 1 - two pass encoding/first pass
+ * 2 - two pass encoding/second pass
+ */
+ void startTranscode( int pass );
+
+ void cleanup( bool success );
+
+ K3bVideoDVD::VideoDVD m_dvd;
+
+ QString m_filename;
+
+ int m_clippingTop;
+ int m_clippingBottom;
+ int m_clippingLeft;
+ int m_clippingRight;
+
+ int m_width;
+ int m_height;
+
+ int m_titleNumber;
+ int m_audioStreamIndex;
+
+ VideoCodec m_videoCodec;
+ AudioCodec m_audioCodec;
+
+ int m_videoBitrate;
+ int m_audioBitrate;
+ bool m_audioVBR;
+
+ bool m_resampleAudio;
+ bool m_twoPassEncoding;
+
+ bool m_lowPriority;
+
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/libk3b/k3bimage.xsd b/libk3b/k3bimage.xsd
new file mode 100644
index 0000000..ab7f36c
--- /dev/null
+++ b/libk3b/k3bimage.xsd
@@ -0,0 +1,175 @@
+<!--
+ This document describes the layout of the toc.xml file in a K3b image archive.
+ A K3b image archive is a TAR archive with a simple layout as follows.
+
+ Every K3b image archive contains the toc.xml file as described in this document
+ which describes the layout of the CD or DVD saved in the K3b image archive.
+ Additionally the K3b image archive contains an arbitrary number of files which
+ contain the CD/DVD sector data. These files are referenced in the elements of
+ type "file".
+
+ The most simple K3b image archive may look as follows:
+ Archive
+ |- toc.xml
+ |- image.iso
+ and the toc.xml may look as follows:
+ <image>
+ <numsessions>1</numsessions>
+ <numtracks>1</numtracks>
+ <session number="1">
+ <numtracks>1</numtracks>
+ <track number="1" type="Data Mode1">
+ <data sectorsize="2048">
+ <file>image.iso</file>
+ </data>
+ </track>
+ </session>
+ </image>
+
+
+ We used tar as a backend because K3b image archives tend to get really big when it
+ comes to DVD images and ZIP does not support archives bigger than 2 GB.
+ Although tar supports files bigger than 2 GB (inside the archive) K3b splits every
+ track bigger than 1 GB into chunks of 1 GB to avoid any problems with big filesizes.
+-->
+
+
+<!-- FIXME: introduce a version field! -->
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://www.k3b.org"
+ xmlns="http://www.k3b.org"
+ elementFormDefault="qualified">
+
+<!-- THE TYPES -->
+ <xs:simpleType name="tracktype">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="Audio" />
+ <xs:enumeration value="Data Mode1" />
+ <xs:enumeration value="Data Mode2" />
+ <xs:enumeration value="Data XA Form1" />
+ <xs:enumeration value="Data XA Form2" />
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="sector_size">
+ <xs:restriction base="xs:integer">
+ <xs:enumeration value="2048" /> <!-- Mode1/Mode2 Form1/DVD: plain user data -->
+ <xs:enumeration value="2056" /> <!-- Mode2 Form1: 2048 bytes user data + 8 bytes sub header FIXME: or does this include 4 bytes crc? -->
+ <xs:enumeration value="2324" /> <!-- Mode2 Form2: plain user data -->
+ <xs:enumeration value="2332" /> <!-- Mode2 Form2: 2324 bytes user data + 8 bytes sub header FIXME: or does this include 4 bytes crc? -->
+ <xs:enumeration value="2352" /> <!-- Audio: 16bit, stereo, 44100Hz, big endian -->
+ <xs:enumeration value="2448" /> <!-- Raw data -->
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:complexType name="filecontents">
+ <xs:simpleContent>
+ <!-- Filename in the image archive. -->
+ <xs:extension base="xs:string">
+ <!-- The start_sector allows for multiple tracks including the CD-TEXT to be defined in one file. Default: 0 -->
+ <xs:attribute name="start_offset" type="xs:nonNegativeInteger" use="optional" />
+
+ <!-- The number of bytes to be used. Default: up to end of the file -->
+ <xs:attribute name="length" type="xs:positiveInteger" use="optional" />
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+ <xs:simpleType name="catalogname">
+ <xs:restriction base="xs:string">
+ <xs:pattern value="[0-9]{13}" />
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="isrcname">
+ <xs:restriction base="xs:string">
+ <xs:pattern value="[A-Z0-9]{5}[0-9]{7}" />
+ </xs:restriction>
+ </xs:simpleType>
+
+
+
+<!-- THE IMAGE DEFINITION -->
+
+ <xs:element name="image">
+ <xs:complexType>
+ <xs:sequence>
+ <!-- Number of session for convinience -->
+ <xs:element name="numsessions" type="xs:positiveInteger" />
+
+ <!-- Number of tracks for convinience -->
+ <xs:element name="numtracks" type="xs:positiveInteger" />
+
+ <!-- CD-TEXT is always stored in binary form -->
+ <xs:element name="cdtext" type="filecontents" minOccurs="0" />
+
+ <!-- The optional catalog name. -->
+ <xs:element name="catalog" type="catalogname" minOccurs="0" />
+
+ <!-- The tracks are splitted into sessions. In most cases there will only be one session. -->
+ <xs:element name="session" maxOccurs="99"> <!-- FIXME: what is the real max here? -->
+ <xs:complexType>
+ <xs:sequence>
+ <!-- Number of tracks in this session for convinience -->
+ <xs:element name="numtracks" type="xs:positiveInteger" />
+
+ <!-- And here come the tracks. -->
+ <xs:element name="track" maxOccurs="99">
+ <xs:complexType>
+ <xs:sequence>
+ <!-- The data tag contains file elements specifying where to find the data for the track.
+ The data may span over multiple files but all need to have the same sector size. -->
+ <xs:element name="data" minOccurs="1" maxOccurs="1">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="file" type="filecontents" />
+ </xs:sequence>
+ <xs:attribute name="sectorsize" type="sector_size" />
+ </xs:complexType>
+ </xs:element>
+
+ <!-- The index tag contains the start sector of the index relative to the start of the track. -->
+ <xs:element name="index" minOccurs="0" maxOccurs="100">
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:nonNegativeInteger">
+ <xs:attribute name="number" type="xs:nonNegativeInteger" />
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- Pre emphasis. -->
+ <xs:element name="preemp" type="xs:boolean" minOccurs="0" default="false" />
+
+ <!-- Copy permitted. -->
+ <xs:element name="copy" type="xs:boolean" minOccurs="0" default="true" />
+
+ <!-- SCMS enabled (alternating copy control bit). Only useful in combination with
+ copy=true. -->
+ <xs:element name="scms" type="xs:boolean" minOccurs="0" default="false" />
+
+ <xs:element name="isrc" type="isrcname" minOccurs="0" />
+ </xs:sequence>
+
+ <!-- The track number for convinience. -->
+ <xs:attribute name="number" type="xs:positiveInteger" />
+
+ <!-- Type of the track as defined above. -->
+ <xs:attribute name="type" type="tracktype" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+
+ <!-- The session number for convinience. There is one special case in which the first session's number may be
+ bigger than 1. It is used internally by K3b to indicate that the image contains a session to be appended
+ to a multisession CD or DVD. It cannot be used in another situation since the data does only fit for the
+ multisession CD or DVD it was created for. -->
+ <xs:attribute name="number" type="xs:positiveInteger" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+</xs:schema> \ No newline at end of file
diff --git a/libk3b/plugin/Makefile.am b/libk3b/plugin/Makefile.am
new file mode 100644
index 0000000..49ac69b
--- /dev/null
+++ b/libk3b/plugin/Makefile.am
@@ -0,0 +1,28 @@
+if compile_libsamplerate
+USED_LIBSAMPLERATE=./libsamplerate/libsamplerate.la
+SUBDIRS = libsamplerate
+else
+USED_LIBSAMPLERATE=$(LIBSAMPLERATE)
+endif
+
+
+AM_CPPFLAGS = -I$(srcdir)/.. -I$(srcdir)/../core/ -I$(srcdir)/../../src -I$(srcdir)/../../libk3bdevice $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libk3bplugin.la
+
+libk3bplugin_la_LIBADD = $(USED_LIBSAMPLERATE)
+
+libk3bplugin_la_LDFLAGS = $(all_libraries)
+
+libk3bplugin_la_SOURCES = k3bplugin.cpp \
+ k3bpluginconfigwidget.cpp \
+ k3bpluginmanager.cpp \
+ k3baudiodecoder.cpp \
+ k3baudioencoder.cpp \
+ k3baudioclient.cpp \
+ k3baudioserver.cpp
+
+
+include_HEADERS = k3bplugin.h k3bpluginfactory.h k3bpluginmanager.h k3baudiodecoder.h k3baudioencoder.h k3bpluginconfigwidget.h k3baudiooutputplugin.h k3bprojectplugin.h
diff --git a/libk3b/plugin/k3baudioclient.cpp b/libk3b/plugin/k3baudioclient.cpp
new file mode 100644
index 0000000..5133d28
--- /dev/null
+++ b/libk3b/plugin/k3baudioclient.cpp
@@ -0,0 +1,46 @@
+/*
+ *
+ * $Id: k3baudioclient.cpp 619556 2007-01-03 17:38:12Z trueg $
+ * Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Seb