From 2616c50e1e76539f68923ee5b28d0a39f3f49a9b Mon Sep 17 00:00:00 2001 From: artkar Date: Mon, 26 Jul 2021 05:31:15 +0200 Subject: [PATCH] ORIG --- AUTHORS | 1 + COPYING | 339 ++ ChangeLog | 554 ++++ INSTALL | 16 + README | 58 + SupportedDevices | 161 + TODO | 22 + confclean | 7 + configure | 981 ++++++ console/Makefile | 30 + console/cdvdcontrol/Makefile | 25 + console/cdvdcontrol/cdvdcontrol.cpp | 1290 ++++++++ console/cdvdcontrol/version.h | 12 + console/f1tattoo/Makefile | 25 + console/f1tattoo/f1tattoo.cpp | 501 +++ console/f1tattoo/version.h | 12 + console/pxfw/Makefile | 25 + console/pxfw/pxfw.cpp | 654 ++++ console/pxfw/pxfw.h | 54 + console/pxfw/version.h | 13 + console/qscan/Makefile | 24 + console/qscan/qscan.cpp | 847 +++++ console/qscan/version.h | 13 + console/qscand/Makefile | 24 + console/qscand/child.cpp | 476 +++ console/qscand/child.h | 40 + console/qscand/qscand.cpp | 372 +++ console/qscand/qscand.h | 19 + console/qscand/qscand_defs.h | 50 + console/readdvd/Makefile | 25 + console/readdvd/deadreader.cpp | 322 ++ console/readdvd/dvd_udf.cpp | 1262 ++++++++ console/readdvd/dvd_udf.h | 80 + console/readdvd/imgwriter.cpp | 82 + console/readdvd/imgwriter.h | 37 + console/readdvd/kbhit.cpp | 74 + console/readdvd/kbhit.h | 29 + console/readdvd/reader.cpp | 81 + console/readdvd/reader.h | 66 + console/readdvd/reader_disc.cpp | 574 ++++ console/readdvd/reader_disc.h | 14 + console/readdvd/sectmap.cpp | 185 ++ console/readdvd/sectmap.h | 58 + console/readdvd/version.h | 12 + debian/changelog | 371 +++ debian/control | 70 + debian/copyright | 36 + debian/libqpx-dev.install | 2 + debian/libqpx0.install | 1 + debian/libqpx0.lintian-overrides | 3 + debian/pxfw.install | 2 + debian/qpxtool.doc-base | 13 + debian/qpxtool.docs | 4 + debian/qpxtool.install | 6 + debian/rules | 11 + debian/source/format | 1 + debian/upstream/metadata | 8 + debian/watch | 3 + gui/Makefile.qproject | 10 + gui/about.html | 20 + gui/images/add.png | Bin 0 -> 1279 bytes gui/images/autostrategy.png | Bin 0 -> 3124 bytes gui/images/cdwriter.png | Bin 0 -> 5778 bytes gui/images/colors.png | Bin 0 -> 5072 bytes gui/images/device.png | Bin 0 -> 5475 bytes gui/images/directory.png | Bin 0 -> 1294 bytes gui/images/disc-eraser.png | Bin 0 -> 2824 bytes gui/images/disc.png | Bin 0 -> 7031 bytes gui/images/document.png | Bin 0 -> 5198 bytes gui/images/edit-clear.png | Bin 0 -> 1729 bytes gui/images/edit-redo.png | Bin 0 -> 1608 bytes gui/images/edit-undo.png | Bin 0 -> 1555 bytes gui/images/edit.png | Bin 0 -> 2692 bytes gui/images/eraser.png | Bin 0 -> 2047 bytes gui/images/exit.png | Bin 0 -> 4823 bytes gui/images/fileopen.png | Bin 0 -> 1969 bytes gui/images/gigarec.png | Bin 0 -> 1832 bytes gui/images/html.png | Bin 0 -> 5520 bytes gui/images/info.png | Bin 0 -> 2078 bytes gui/images/lock.png | Bin 0 -> 1765 bytes gui/images/loej.png | Bin 0 -> 3090 bytes gui/images/logo.png | Bin 0 -> 8503 bytes gui/images/media/bd.png | Bin 0 -> 8579 bytes gui/images/media/cd.png | Bin 0 -> 14105 bytes gui/images/media/cd_r.png | Bin 0 -> 21277 bytes gui/images/media/cd_rw.png | Bin 0 -> 22742 bytes gui/images/media/dvd+r.png | Bin 0 -> 1182 bytes gui/images/media/dvd+r_dl.png | Bin 0 -> 1520 bytes gui/images/media/dvd+rw.png | Bin 0 -> 1736 bytes gui/images/media/dvd-r.png | Bin 0 -> 1643 bytes gui/images/media/dvd-r_dl.png | Bin 0 -> 1731 bytes gui/images/media/dvd-ram.png | Bin 0 -> 1805 bytes gui/images/media/dvd-rom.png | Bin 0 -> 1847 bytes gui/images/media/dvd-rw.png | Bin 0 -> 1785 bytes gui/images/media/hddvd-r.png | Bin 0 -> 1141 bytes gui/images/media/hddvd-ram.png | Bin 0 -> 1207 bytes gui/images/media/hddvd-rom.png | Bin 0 -> 1513 bytes gui/images/media/hddvd-rw.png | Bin 0 -> 1172 bytes gui/images/ok.png | Bin 0 -> 900 bytes gui/images/page-setup.png | Bin 0 -> 4217 bytes gui/images/password.png | Bin 0 -> 5442 bytes gui/images/pdf.png | Bin 0 -> 4148 bytes gui/images/printer.png | Bin 0 -> 1983 bytes gui/images/q.png | Bin 0 -> 3790 bytes gui/images/refresh-info.png | Bin 0 -> 8506 bytes gui/images/refresh-media.png | Bin 0 -> 8917 bytes gui/images/refresh.png | Bin 0 -> 9147 bytes gui/images/save.png | Bin 0 -> 1874 bytes gui/images/scan.png | Bin 0 -> 2980 bytes gui/images/search.png | Bin 0 -> 1962 bytes gui/images/settings.png | Bin 0 -> 6397 bytes gui/images/settings2.png | Bin 0 -> 3010 bytes gui/images/sound.png | Bin 0 -> 6382 bytes gui/images/splash.png | Bin 0 -> 16649 bytes gui/images/stop.png | Bin 0 -> 218 bytes gui/images/tattoo.png | Bin 0 -> 7802 bytes gui/images/test_errc.png | Bin 0 -> 1401 bytes gui/images/test_ft.png | Bin 0 -> 1536 bytes gui/images/test_jb.png | Bin 0 -> 1536 bytes gui/images/test_rt.png | Bin 0 -> 25210 bytes gui/images/test_ta.png | Bin 0 -> 574 bytes gui/images/test_wt.png | Bin 0 -> 8395 bytes gui/images/unlock.png | Bin 0 -> 1925 bytes gui/images/varirec.png | Bin 0 -> 1586 bytes gui/images/x.png | Bin 0 -> 1073 bytes gui/images/zoom-in.png | Bin 0 -> 2169 bytes gui/images/zoom-orig.png | Bin 0 -> 2048 bytes gui/images/zoom-out.png | Bin 0 -> 2148 bytes gui/include/about.h | 40 + gui/include/abstractpreview.h | 209 ++ gui/include/colorlabel.h | 38 + gui/include/db_connection.h | 48 + gui/include/db_report_selection.h | 96 + gui/include/defs.h | 41 + gui/include/device.h | 580 ++++ gui/include/devsettings.h | 58 + gui/include/devsettings_widgets.h | 397 +++ gui/include/dpi_metrics.h | 25 + gui/include/errc_detailed.h | 65 + gui/include/graphtab.h | 72 + gui/include/hostedit_dialog.h | 48 + gui/include/image_label.h | 63 + gui/include/images_list.h | 72 + gui/include/mainwidget.h | 83 + gui/include/mainwindow.h | 200 ++ gui/include/mcapwidget.h | 40 + gui/include/mwatcher.h | 48 + gui/include/pref_colors.h | 78 + gui/include/pref_common.h | 57 + gui/include/pref_devices.h | 55 + gui/include/pref_reports.h | 78 + gui/include/preferences.h | 65 + gui/include/printpreview.h | 70 + gui/include/progresswidget.h | 66 + gui/include/qpxgraph.h | 110 + gui/include/qpxiodevice.h | 44 + gui/include/qpxsettings.h | 211 ++ gui/include/resultsio.h | 61 + gui/include/splitbutton.h | 42 + gui/include/tab_devinfo.h | 109 + gui/include/tab_errc.h | 77 + gui/include/tab_fete.h | 55 + gui/include/tab_jb.h | 57 + gui/include/tab_mediainfo.h | 128 + gui/include/tab_ta.h | 88 + gui/include/tab_transfer.h | 62 + gui/include/tattoowidget.h | 46 + gui/include/testdialog.h | 105 + gui/include/textslider.h | 81 + gui/include/version.h | 19 + gui/locale/qpxtool.de_DE.ts | 1863 +++++++++++ gui/locale/qpxtool.ru_RU.ts | 1867 +++++++++++ gui/qpxtool.desktop | 13 + gui/qpxtool.ico | Bin 0 -> 16958 bytes gui/qpxtool.pri | 19 + gui/qpxtool.pro | 104 + gui/qpxtool.qrc | 82 + gui/qpxtool.rc | 1 + gui/sql/db_create.sql | 65 + gui/sql/db_drop.sql | 15 + gui/sql/db_media_types.sql | 55 + gui/src/about.cpp | 64 + gui/src/abstractpreview.cpp | 1609 ++++++++++ gui/src/colorlabel.cpp | 65 + gui/src/db_connection.cpp | 109 + gui/src/db_report_selection.cpp | 423 +++ gui/src/device.cpp | 2162 +++++++++++++ gui/src/devsettings.cpp | 135 + gui/src/devsettings_widgets.cpp | 1227 +++++++ gui/src/errc_detailed.cpp | 225 ++ gui/src/graphtab.cpp | 138 + gui/src/hostedit_dialog.cpp | 80 + gui/src/image_label.cpp | 142 + gui/src/images_list.cpp | 197 ++ gui/src/main.cpp | 74 + gui/src/mainwidget.cpp | 180 ++ gui/src/mainwindow.cpp | 2049 ++++++++++++ gui/src/mcapwidget.cpp | 106 + gui/src/mwatcher.cpp | 102 + gui/src/pref_colors.cpp | 403 +++ gui/src/pref_common.cpp | 115 + gui/src/pref_devices.cpp | 211 ++ gui/src/pref_reports.cpp | 229 ++ gui/src/preferences.cpp | 183 ++ gui/src/printpreview.cpp | 318 ++ gui/src/progresswidget.cpp | 136 + gui/src/qpxgraph.cpp | 1355 ++++++++ gui/src/qpxiodevice.cpp | 114 + gui/src/qpxsettings.cpp | 352 +++ gui/src/resultsio.cpp | 521 +++ gui/src/splitbutton.cpp | 67 + gui/src/tab_devinfo.cpp | 344 ++ gui/src/tab_errc.cpp | 276 ++ gui/src/tab_fete.cpp | 138 + gui/src/tab_jb.cpp | 143 + gui/src/tab_mediainfo.cpp | 346 ++ gui/src/tab_ta.cpp | 201 ++ gui/src/tab_transfer.cpp | 182 ++ gui/src/tattoowidget.cpp | 127 + gui/src/testdialog.cpp | 443 +++ gui/src/textslider.cpp | 389 +++ lib/Makefile | 84 + lib/include/colors.h | 1 + lib/include/common_functions.h | 1 + lib/include/pioneer_spdctl.h | 1 + lib/include/plextor_features.h | 1 + lib/include/qpx_mmc.h | 1 + lib/include/qpx_mmc_defs.h | 1 + lib/include/qpx_opcodes.h | 1 + lib/include/qpx_scan.h | 1 + lib/include/qpx_scan_plugin_api.h | 1 + lib/include/qpx_transport.h | 1 + lib/include/threads.h | 1 + lib/include/yamaha_features.h | 1 + lib/qpxpioneer/Makefile | 13 + lib/qpxpioneer/include/pioneer_spdctl.h | 45 + lib/qpxpioneer/pioneer_spdctl.cpp | 70 + lib/qpxplextor/Makefile | 14 + lib/qpxplextor/include/plextor_features.h | 298 ++ lib/qpxplextor/plextor_features.cpp | 1091 +++++++ lib/qpxscan/Makefile | 19 + lib/qpxscan/include/qpx_scan.h | 117 + lib/qpxscan/include/qpx_scan_plugin_api.h | 304 ++ lib/qpxscan/include/qpx_writer.h | 109 + lib/qpxscan/qpx_scan.cpp | 438 +++ lib/qpxscan/qpx_scan_algo.cpp | 901 ++++++ lib/qpxscan/qpx_writer.cpp | 417 +++ lib/qpxtransport/Makefile | 28 + lib/qpxtransport/common_functions.cpp | 254 ++ lib/qpxtransport/include/colors.h | 29 + lib/qpxtransport/include/common_functions.h | 253 ++ lib/qpxtransport/include/csstables.h | 392 +++ lib/qpxtransport/include/qpx_mmc.h | 509 +++ lib/qpxtransport/include/qpx_mmc_defs.h | 1032 ++++++ lib/qpxtransport/include/qpx_opcodes.h | 141 + lib/qpxtransport/include/qpx_transport.h | 402 +++ lib/qpxtransport/include/sense.h | 21 + lib/qpxtransport/include/threads.h | 67 + lib/qpxtransport/qpx_mmc.cpp | 3156 +++++++++++++++++++ lib/qpxtransport/qpx_mmc_css.cpp | 2029 ++++++++++++ lib/qpxtransport/qpx_transport.cpp | 1126 +++++++ lib/qpxtransport/sense.cpp | 428 +++ lib/qpxtransport/threads.cpp | 308 ++ lib/qpxyamaha/Makefile | 14 + lib/qpxyamaha/include/yamaha_features.h | 31 + lib/qpxyamaha/yamaha_features.cpp | 288 ++ man/Makefile | 56 + man/cdvdcontrol.1 | 423 +++ man/f1tattoo.1 | 107 + man/pxfw.8 | 96 + man/qpxtool.1 | 14 + man/qscan.1 | 177 ++ man/qscand.1 | 40 + man/readdvd.1 | 80 + patches/01-ArtKarMOD.patch | 533 ++++ patches/series | 1 + plugins/Makefile | 34 + plugins/asus/Makefile | 3 + plugins/asus/qscan_cmd.cpp | 130 + plugins/asus/qscan_plugin.cpp | 148 + plugins/asus/qscan_plugin.h | 65 + plugins/benq/Makefile | 3 + plugins/benq/qscan_cmd.cpp | 574 ++++ plugins/benq/qscan_plugin.cpp | 201 ++ plugins/benq/qscan_plugin.h | 82 + plugins/benq_dvdrom/Makefile | 3 + plugins/benq_dvdrom/qscan_cmd.cpp | 187 ++ plugins/benq_dvdrom/qscan_plugin.cpp | 154 + plugins/benq_dvdrom/qscan_plugin.h | 78 + plugins/generic/Makefile | 3 + plugins/generic/qscan_cmd.cpp | 84 + plugins/generic/qscan_plugin.cpp | 108 + plugins/generic/qscan_plugin.h | 53 + plugins/liteon/Makefile | 3 + plugins/liteon/qscan_cmd.cpp | 437 +++ plugins/liteon/qscan_plugin.cpp | 178 ++ plugins/liteon/qscan_plugin.h | 193 ++ plugins/nec/Makefile | 3 + plugins/nec/qscan_cmd.cpp | 149 + plugins/nec/qscan_plugin.cpp | 177 ++ plugins/nec/qscan_plugin.h | 77 + plugins/pioneer/Makefile | 3 + plugins/pioneer/qscan_cmd.cpp | 247 ++ plugins/pioneer/qscan_plugin.cpp | 163 + plugins/pioneer/qscan_plugin.h | 92 + plugins/plextor/Makefile | 3 + plugins/plextor/qscan_cmd.cpp | 736 +++++ plugins/plextor/qscan_plugin.cpp | 277 ++ plugins/plextor/qscan_plugin.h | 114 + plugins/tsst/Makefile | 3 + plugins/tsst/qscan_cmd.cpp | 110 + plugins/tsst/qscan_plugin.cpp | 141 + plugins/tsst/qscan_plugin.h | 59 + qpxtool.spec | 138 + slack-desc | 13 + status.html | 340 ++ 316 files changed, 55391 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 README create mode 100644 SupportedDevices create mode 100644 TODO create mode 100755 confclean create mode 100755 configure create mode 100644 console/Makefile create mode 100644 console/cdvdcontrol/Makefile create mode 100644 console/cdvdcontrol/cdvdcontrol.cpp create mode 100644 console/cdvdcontrol/version.h create mode 100644 console/f1tattoo/Makefile create mode 100644 console/f1tattoo/f1tattoo.cpp create mode 100644 console/f1tattoo/version.h create mode 100644 console/pxfw/Makefile create mode 100644 console/pxfw/pxfw.cpp create mode 100644 console/pxfw/pxfw.h create mode 100644 console/pxfw/version.h create mode 100644 console/qscan/Makefile create mode 100644 console/qscan/qscan.cpp create mode 100644 console/qscan/version.h create mode 100644 console/qscand/Makefile create mode 100644 console/qscand/child.cpp create mode 100644 console/qscand/child.h create mode 100644 console/qscand/qscand.cpp create mode 100644 console/qscand/qscand.h create mode 100644 console/qscand/qscand_defs.h create mode 100644 console/readdvd/Makefile create mode 100644 console/readdvd/deadreader.cpp create mode 100644 console/readdvd/dvd_udf.cpp create mode 100644 console/readdvd/dvd_udf.h create mode 100644 console/readdvd/imgwriter.cpp create mode 100644 console/readdvd/imgwriter.h create mode 100644 console/readdvd/kbhit.cpp create mode 100644 console/readdvd/kbhit.h create mode 100644 console/readdvd/reader.cpp create mode 100644 console/readdvd/reader.h create mode 100644 console/readdvd/reader_disc.cpp create mode 100644 console/readdvd/reader_disc.h create mode 100644 console/readdvd/sectmap.cpp create mode 100644 console/readdvd/sectmap.h create mode 100644 console/readdvd/version.h create mode 100644 debian/changelog create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/libqpx-dev.install create mode 100644 debian/libqpx0.install create mode 100644 debian/libqpx0.lintian-overrides create mode 100644 debian/pxfw.install create mode 100644 debian/qpxtool.doc-base create mode 100644 debian/qpxtool.docs create mode 100644 debian/qpxtool.install create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 debian/upstream/metadata create mode 100644 debian/watch create mode 100644 gui/Makefile.qproject create mode 100644 gui/about.html create mode 100644 gui/images/add.png create mode 100644 gui/images/autostrategy.png create mode 100644 gui/images/cdwriter.png create mode 100644 gui/images/colors.png create mode 100644 gui/images/device.png create mode 100644 gui/images/directory.png create mode 100644 gui/images/disc-eraser.png create mode 100644 gui/images/disc.png create mode 100644 gui/images/document.png create mode 100644 gui/images/edit-clear.png create mode 100644 gui/images/edit-redo.png create mode 100644 gui/images/edit-undo.png create mode 100644 gui/images/edit.png create mode 100644 gui/images/eraser.png create mode 100644 gui/images/exit.png create mode 100644 gui/images/fileopen.png create mode 100644 gui/images/gigarec.png create mode 100644 gui/images/html.png create mode 100644 gui/images/info.png create mode 100644 gui/images/lock.png create mode 100644 gui/images/loej.png create mode 100644 gui/images/logo.png create mode 100644 gui/images/media/bd.png create mode 100644 gui/images/media/cd.png create mode 100644 gui/images/media/cd_r.png create mode 100644 gui/images/media/cd_rw.png create mode 100644 gui/images/media/dvd+r.png create mode 100644 gui/images/media/dvd+r_dl.png create mode 100644 gui/images/media/dvd+rw.png create mode 100644 gui/images/media/dvd-r.png create mode 100644 gui/images/media/dvd-r_dl.png create mode 100644 gui/images/media/dvd-ram.png create mode 100644 gui/images/media/dvd-rom.png create mode 100644 gui/images/media/dvd-rw.png create mode 100644 gui/images/media/hddvd-r.png create mode 100644 gui/images/media/hddvd-ram.png create mode 100644 gui/images/media/hddvd-rom.png create mode 100644 gui/images/media/hddvd-rw.png create mode 100644 gui/images/ok.png create mode 100644 gui/images/page-setup.png create mode 100644 gui/images/password.png create mode 100644 gui/images/pdf.png create mode 100644 gui/images/printer.png create mode 100644 gui/images/q.png create mode 100644 gui/images/refresh-info.png create mode 100644 gui/images/refresh-media.png create mode 100644 gui/images/refresh.png create mode 100644 gui/images/save.png create mode 100644 gui/images/scan.png create mode 100644 gui/images/search.png create mode 100644 gui/images/settings.png create mode 100644 gui/images/settings2.png create mode 100644 gui/images/sound.png create mode 100644 gui/images/splash.png create mode 100644 gui/images/stop.png create mode 100644 gui/images/tattoo.png create mode 100644 gui/images/test_errc.png create mode 100644 gui/images/test_ft.png create mode 100644 gui/images/test_jb.png create mode 100644 gui/images/test_rt.png create mode 100644 gui/images/test_ta.png create mode 100644 gui/images/test_wt.png create mode 100644 gui/images/unlock.png create mode 100644 gui/images/varirec.png create mode 100644 gui/images/x.png create mode 100644 gui/images/zoom-in.png create mode 100644 gui/images/zoom-orig.png create mode 100644 gui/images/zoom-out.png create mode 100644 gui/include/about.h create mode 100644 gui/include/abstractpreview.h create mode 100644 gui/include/colorlabel.h create mode 100644 gui/include/db_connection.h create mode 100644 gui/include/db_report_selection.h create mode 100644 gui/include/defs.h create mode 100644 gui/include/device.h create mode 100644 gui/include/devsettings.h create mode 100644 gui/include/devsettings_widgets.h create mode 100644 gui/include/dpi_metrics.h create mode 100644 gui/include/errc_detailed.h create mode 100644 gui/include/graphtab.h create mode 100644 gui/include/hostedit_dialog.h create mode 100644 gui/include/image_label.h create mode 100644 gui/include/images_list.h create mode 100644 gui/include/mainwidget.h create mode 100644 gui/include/mainwindow.h create mode 100644 gui/include/mcapwidget.h create mode 100644 gui/include/mwatcher.h create mode 100644 gui/include/pref_colors.h create mode 100644 gui/include/pref_common.h create mode 100644 gui/include/pref_devices.h create mode 100644 gui/include/pref_reports.h create mode 100644 gui/include/preferences.h create mode 100644 gui/include/printpreview.h create mode 100644 gui/include/progresswidget.h create mode 100644 gui/include/qpxgraph.h create mode 100644 gui/include/qpxiodevice.h create mode 100644 gui/include/qpxsettings.h create mode 100644 gui/include/resultsio.h create mode 100644 gui/include/splitbutton.h create mode 100644 gui/include/tab_devinfo.h create mode 100644 gui/include/tab_errc.h create mode 100644 gui/include/tab_fete.h create mode 100644 gui/include/tab_jb.h create mode 100644 gui/include/tab_mediainfo.h create mode 100644 gui/include/tab_ta.h create mode 100644 gui/include/tab_transfer.h create mode 100644 gui/include/tattoowidget.h create mode 100644 gui/include/testdialog.h create mode 100644 gui/include/textslider.h create mode 100644 gui/include/version.h create mode 100644 gui/locale/qpxtool.de_DE.ts create mode 100644 gui/locale/qpxtool.ru_RU.ts create mode 100644 gui/qpxtool.desktop create mode 100644 gui/qpxtool.ico create mode 100644 gui/qpxtool.pri create mode 100644 gui/qpxtool.pro create mode 100644 gui/qpxtool.qrc create mode 100644 gui/qpxtool.rc create mode 100644 gui/sql/db_create.sql create mode 100644 gui/sql/db_drop.sql create mode 100644 gui/sql/db_media_types.sql create mode 100644 gui/src/about.cpp create mode 100644 gui/src/abstractpreview.cpp create mode 100644 gui/src/colorlabel.cpp create mode 100644 gui/src/db_connection.cpp create mode 100644 gui/src/db_report_selection.cpp create mode 100644 gui/src/device.cpp create mode 100644 gui/src/devsettings.cpp create mode 100644 gui/src/devsettings_widgets.cpp create mode 100644 gui/src/errc_detailed.cpp create mode 100644 gui/src/graphtab.cpp create mode 100644 gui/src/hostedit_dialog.cpp create mode 100644 gui/src/image_label.cpp create mode 100644 gui/src/images_list.cpp create mode 100644 gui/src/main.cpp create mode 100644 gui/src/mainwidget.cpp create mode 100644 gui/src/mainwindow.cpp create mode 100644 gui/src/mcapwidget.cpp create mode 100644 gui/src/mwatcher.cpp create mode 100644 gui/src/pref_colors.cpp create mode 100644 gui/src/pref_common.cpp create mode 100644 gui/src/pref_devices.cpp create mode 100644 gui/src/pref_reports.cpp create mode 100644 gui/src/preferences.cpp create mode 100644 gui/src/printpreview.cpp create mode 100644 gui/src/progresswidget.cpp create mode 100644 gui/src/qpxgraph.cpp create mode 100644 gui/src/qpxiodevice.cpp create mode 100644 gui/src/qpxsettings.cpp create mode 100644 gui/src/resultsio.cpp create mode 100644 gui/src/splitbutton.cpp create mode 100644 gui/src/tab_devinfo.cpp create mode 100644 gui/src/tab_errc.cpp create mode 100644 gui/src/tab_fete.cpp create mode 100644 gui/src/tab_jb.cpp create mode 100644 gui/src/tab_mediainfo.cpp create mode 100644 gui/src/tab_ta.cpp create mode 100644 gui/src/tab_transfer.cpp create mode 100644 gui/src/tattoowidget.cpp create mode 100644 gui/src/testdialog.cpp create mode 100644 gui/src/textslider.cpp create mode 100644 lib/Makefile create mode 100644 lib/include/colors.h create mode 100644 lib/include/common_functions.h create mode 100644 lib/include/pioneer_spdctl.h create mode 100644 lib/include/plextor_features.h create mode 100644 lib/include/qpx_mmc.h create mode 100644 lib/include/qpx_mmc_defs.h create mode 100644 lib/include/qpx_opcodes.h create mode 100644 lib/include/qpx_scan.h create mode 100644 lib/include/qpx_scan_plugin_api.h create mode 100644 lib/include/qpx_transport.h create mode 100644 lib/include/threads.h create mode 100644 lib/include/yamaha_features.h create mode 100644 lib/qpxpioneer/Makefile create mode 100644 lib/qpxpioneer/include/pioneer_spdctl.h create mode 100644 lib/qpxpioneer/pioneer_spdctl.cpp create mode 100644 lib/qpxplextor/Makefile create mode 100644 lib/qpxplextor/include/plextor_features.h create mode 100644 lib/qpxplextor/plextor_features.cpp create mode 100644 lib/qpxscan/Makefile create mode 100644 lib/qpxscan/include/qpx_scan.h create mode 100644 lib/qpxscan/include/qpx_scan_plugin_api.h create mode 100644 lib/qpxscan/include/qpx_writer.h create mode 100644 lib/qpxscan/qpx_scan.cpp create mode 100644 lib/qpxscan/qpx_scan_algo.cpp create mode 100644 lib/qpxscan/qpx_writer.cpp create mode 100644 lib/qpxtransport/Makefile create mode 100644 lib/qpxtransport/common_functions.cpp create mode 100644 lib/qpxtransport/include/colors.h create mode 100644 lib/qpxtransport/include/common_functions.h create mode 100644 lib/qpxtransport/include/csstables.h create mode 100644 lib/qpxtransport/include/qpx_mmc.h create mode 100644 lib/qpxtransport/include/qpx_mmc_defs.h create mode 100644 lib/qpxtransport/include/qpx_opcodes.h create mode 100644 lib/qpxtransport/include/qpx_transport.h create mode 100644 lib/qpxtransport/include/sense.h create mode 100644 lib/qpxtransport/include/threads.h create mode 100644 lib/qpxtransport/qpx_mmc.cpp create mode 100644 lib/qpxtransport/qpx_mmc_css.cpp create mode 100644 lib/qpxtransport/qpx_transport.cpp create mode 100644 lib/qpxtransport/sense.cpp create mode 100644 lib/qpxtransport/threads.cpp create mode 100644 lib/qpxyamaha/Makefile create mode 100644 lib/qpxyamaha/include/yamaha_features.h create mode 100644 lib/qpxyamaha/yamaha_features.cpp create mode 100644 man/Makefile create mode 100644 man/cdvdcontrol.1 create mode 100644 man/f1tattoo.1 create mode 100644 man/pxfw.8 create mode 100644 man/qpxtool.1 create mode 100644 man/qscan.1 create mode 100644 man/qscand.1 create mode 100644 man/readdvd.1 create mode 100644 patches/01-ArtKarMOD.patch create mode 100644 patches/series create mode 100644 plugins/Makefile create mode 100644 plugins/asus/Makefile create mode 100644 plugins/asus/qscan_cmd.cpp create mode 100644 plugins/asus/qscan_plugin.cpp create mode 100644 plugins/asus/qscan_plugin.h create mode 100644 plugins/benq/Makefile create mode 100644 plugins/benq/qscan_cmd.cpp create mode 100644 plugins/benq/qscan_plugin.cpp create mode 100644 plugins/benq/qscan_plugin.h create mode 100644 plugins/benq_dvdrom/Makefile create mode 100644 plugins/benq_dvdrom/qscan_cmd.cpp create mode 100644 plugins/benq_dvdrom/qscan_plugin.cpp create mode 100644 plugins/benq_dvdrom/qscan_plugin.h create mode 100644 plugins/generic/Makefile create mode 100644 plugins/generic/qscan_cmd.cpp create mode 100644 plugins/generic/qscan_plugin.cpp create mode 100644 plugins/generic/qscan_plugin.h create mode 100644 plugins/liteon/Makefile create mode 100644 plugins/liteon/qscan_cmd.cpp create mode 100644 plugins/liteon/qscan_plugin.cpp create mode 100644 plugins/liteon/qscan_plugin.h create mode 100644 plugins/nec/Makefile create mode 100644 plugins/nec/qscan_cmd.cpp create mode 100644 plugins/nec/qscan_plugin.cpp create mode 100644 plugins/nec/qscan_plugin.h create mode 100644 plugins/pioneer/Makefile create mode 100644 plugins/pioneer/qscan_cmd.cpp create mode 100644 plugins/pioneer/qscan_plugin.cpp create mode 100644 plugins/pioneer/qscan_plugin.h create mode 100644 plugins/plextor/Makefile create mode 100644 plugins/plextor/qscan_cmd.cpp create mode 100644 plugins/plextor/qscan_plugin.cpp create mode 100644 plugins/plextor/qscan_plugin.h create mode 100644 plugins/tsst/Makefile create mode 100644 plugins/tsst/qscan_cmd.cpp create mode 100644 plugins/tsst/qscan_plugin.cpp create mode 100644 plugins/tsst/qscan_plugin.h create mode 100644 qpxtool.spec create mode 100644 slack-desc create mode 100644 status.html diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..531c629 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Gennady "ShultZ" Kozlov diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..65bc355 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,554 @@ +QPxTool ChangeLog + +*********************************************** +** 0.8.1 (2020-11-10) + + common: + - fix some cppcheck notes + gui: + - fix saving results to *.qpx files + cdvdcontrol: + - fix Pioneer QuietMode operation: the argument given to --pio-quiet is + stored in piosilent, but pioneer_set_silent() was called with silent + instead + +*********************************************** +** 0.8.0 (2020-01-28) + + common: + - add support of libpng >= 1.4 + - compress man pages at build time instead of storing compressed ones + - fix installation path for plugins when configuration option --libdir + is used + - use compiler options from build environment + - fix spelling errors + gui: + - port to Qt 5.x + - fix desktop file for GNU/Linux and *FSD systems + - fix build on Debian GNU/kFreeBSD + - add support of Debian GNU/Hurd + libqpxtransport: + - fix build on architectures where char is unsigned + +*********************************************** +** 0.7.2 (2012-12-13) + + - initial Blu-Ray media support + - fixed binary stripping in MacOSX + - Lite-On plugin: added new CD ERRC commands (fixes low test speed on some devices) + - BD ERRC test on Lite-On drives + - CD and BD FE/TE test in Lite-On plugin implemented, DVD FE/TE test fixed + - DVD FE/TE test in BENQ plugin works now + gui: + - some AutoStrategy-related fixes + - fixed translation loading in win32 + - fixed tests summary updating on results loading + - Drag&Drop support for results loading + - results can be stored in database now + +*********************************************** +** 0.7.1 (2010-03-10) + + - fixed some typos + - added qscand and qpxtool man pages, just description now (Thomas Maguin) + - some fixes in Makefiles created by 'configure' + - extended BENQ CD ERCC test + - added POE info to BENQ DVD ERRC test + - fixed ERRC speeds in BENQ plugin + - DVD FE/TE test in Lite-On plugin implemented (speed graph is not valid) + - DVD FE/TE test in BENQ plugin introduced (but unusable now) + - fixed starting address in generic 'C2P' plugin + - added blacklist in Lite-On plugin: HL-DT-ST devices and TEAC CD-W552E + readdvd: + - some additional media checks + - disabling read cache if possible + cdvdcontrol: + - fixed displaying saved SilentMode tray speeds + libqscan: + - fixed DVD ERRC algo for drives based on MediaTek chipsets (Lite-On, TSST) + - fixed write transfer rate test on DVD+RW + - fixed info about last block in read transfer rate test + - internal write transfer rate implementation: + - CD-R(W) (SAO & TAO write modes supported, TAO is used by default) + - DVD-R(W), sequential write only (only single layer media tested) + - DVD+R(W) (only single layer media tested) + - DVD-RAM + gui: + - added German translation (Thomas Maguin) + - some interface changes + - some fixes in print preview + - Plextor MQCK results shows correctly now + - independent Jitter/Asymmetry scaling + - does not loose media info, tests results and selected tests speeds then selecting plugin + - fixed displaying saved SilentMode speed limits + - fixed bugs with lrelease (thanks to Carsten Lohrke) + - media info autoupdating (gui depends on libqpxtransport now) + gui settings: + - reports autosaving + - autostart tests on inserted media + - default tests for blank and written media setting + - ejecting media after tests finished + +*********************************************** +** 0.7.0 (2009-12-24) + + gui: + - printpreview mostly rewritten to workaround QTextDocument margins bug + - added russian translation + +*********************************************** +** 0.7.0 RC4 (2009-12-08) + + libqpxyamaha: + - additional checks for raw files + gui: + - implemented results saving/loading functions (via virtual "results viewer" device) + - changed printpreview rendering mode for QT 4.4+ (much faster) + - some fixes in printpreview + +*********************************************** +** 0.7.0 RC3 (2009-11-23) + + - NEC scan plugin: fixed returning drive to normal operation after test finished + - implemented probing in all plugins + - spec file: main package splitted to main, libs; fixed paths + qscan: + - added spare area info for DVD-RAM media + libqpxscan: + - fixed write transfer test could not start on DVD+R on non-PLEXTOR drives + libqpxtransport: + - fixed reading DVD+R recorded capacity on ASUS DRW-2014 + +*********************************************** +** 0.7.0 RC2 (2009-11-09) + + - fixed some bugs with big-endian CPU's (tested on SPARC) + +*********************************************** +** 0.7.0 RC1 (2009-11-02) + + - spec file: qpxtool separated to 3 packages: main, devel, gui + - man pages: device names section is for all supported platorms now + - added test speeds reporting by plugins + libqpxtransport: + - bus scaning code reworked + gui: + - fixed test capabilities updating when selecting plugin + +*********************************************** +** 0.7.0pre6 (2009-10-17) + + - mans path changed to man1 (except pxfw one) + - added some path options to configure script + - some MacOSX fixes in configure script + - some code cleanup + - fixes in transport code + - some minor bugfixes + gui: + - added color presets + - added qscan plugin selection + - optimized print preview + +*********************************************** +** 0.7.0pre5 (2009-09-30) + + - added options to configuration script + - successfully builds on OpenBSD (needs GNU Make) + (all features works with libpng and qt4 installed) + - some fixes for 64-bit machines + +*********************************************** +** 0.7.0pre4 (2009-09-22) + + - added configuration script + - successfully builds on NetBSD (needs GNU Make), cli only + gui: + - some fixes in printing/saving results + readdvd: + - output file option '-i' changed to more obvisious '-o' + +*********************************************** +** 0.7.0pre3 (2009-09-11) + + gui: + - another fixes in ERRC results + - TA graph added to printed/saved results + +*********************************************** +** 0.7.0pre2 (2009-08-31) + + gui: + - fixed average values in ERRC results + - fixed build on x86-64 systems + +*********************************************** +** 0.7.0pre1 (2009-08-24) + + - win32(mingw32) and experimental MacOSX support + - qpxtool gui completely rewritten using QT4 + (gui is just front-end now) + - deadreader renamed to readdvd + - pxcontrol and pioquiet merged (and named cdvdcontrol) + - added qscan: console quality scan tool + - added qscand: network wrapper for qscan + - added f1tattoo: DiscT@2 tool for Yamaha CRW-F1 + + + qpxtool (gui): + - simultaneous scan on miltiple devices + - logarythmic/linear scale for error correction graph + - user-changable graph colors + - results printing + readdvd: + - parallel reading from multiple drives + - reading CSS protected DVD's + cdvdcontrol (pxcontrol): + - fixed DVD+R(W) testwrite control + - added MQCK support + pxfw: + - now supports Plextor Premium2 + - fixed eeprom reading on Premium and PX-712 + - fixed Premuim checksum caclulation error + + and more... + +*********************************************** +** 0.6.1 (2007-08-19) + + - Makefile-based build, thanks to Mike Frysinger + - added 'uninstall' target + - pxfw & pxcontrol man pages: Thomas Maguin + - some code cleanup + +new: + - pxfw now in QPxTool package + - new tool called deadreader - multi-pass damaged discs reading + - pxcontrol: full AutoStrategy support + - pxcontrol: experimental AS database save/load, PX755/PX760 only + (PX755 output is buggy, PX760 is more or less stable) + - media change detection + +fixed: + - it was unable to set Silent Mode speed limits for CD in 0.6.0 + - fixed Plextor FE/TE scan if it's not standalone scan + - fixed problem with NEC PIE & PIF scan on DVD+RW + - fixed bug with DVD-ROM results saving + - fixed Cx scan stability issue + - fixed SegFault at the and of PIE/PIF scan on LiteOn and Pioneer drives + - fixed compilation problems on some Linux distributions + +*********************************************** +** 0.6.0 (2006-08-25) + + - scons is used to compile stuff + (get it at http://scons.org) + - transport and vendor-specific code moved to separate libraries, + you can use it for you purposes, but API is far to be stable + - more informative SCSI error reporting + - stability improved:) + - Pi/Cx graph plot rewritten + (readability improved) + +new: + - POF on Plextor drives + - C1/C2 on Pioneer drives + - generic C2 scan on drives supported C2-pointers + - changes in Pioneer PIE/PIF scan + (especialy DVR-111 support improved) + - one-pass PIE/PIF sum8 scan on many drives + (separate PIF sum1 on Plextor & Nec only) + - scale for all tests (except TA) in saved html + - SecuRec support + - Pioneer QuietMode support (for XL modifications) + - pioquiet - console Pioneer QuietMode control tool + +fixed: + - fixed CD-R detection problems on some drives + - changed CD track list reading algorythm, + now it should also work on old MMC-1 drives + - transfer rate can be performed on Audio-CD now if drive can do DAE + - spinup is a time controled now, not fixed sectors count + - problem with resolving png's path if it's relative + (then saving results as html) + - various minor bugfixes + - some bugfixes in pxcontrol + +*********************************************** +** 0.5.4 (2006-07-13) + +new: + - pxcontrol 0.9 - console Plextor control tool + - more drives support + +fixed: + - some minor bugfixes + +*********************************************** +** 0.5.3 (2006-06-05) + +new: + - PIE/PIF on Lite-On drives + - BenQ support (Cx and PIE/PIF) + +*********************************************** +** 0.5.2 (2006-05-29) + +new: + - PIE/PIF scan on NEC drives + +fixed: + - manual vendor-specific command set selection didn't work in 0.5.0 + NB: some drives may hang, if you selected Lite-On commands + +*********************************************** +** 0.5.1 (2006-05-27) + + - To get full fuctionality of Plextor PX-755/760 you need Plexluthor patched FW. + can be found at this page: + http://forum.rpc1.org/dl_firmware.php?category=4&manufactor=38 + +new: + - Extended CD scan for Plextor drives. + it shows BLER, E11, E21, E31, E12, E22, E32! + (this graphs automatically included in saved html if Cx checked) + - autoclearing tests results before new scan + - manual selection of vendor-specific command set for you drive, + if automatic recognition fails. + Please report INQUIRY of your drive and command set, if it works! + +fixed: + - more realistic PIE/PIF scan results on Pioneer drives. + now it runs with 8ECC interval, also results changed interpreting + +*********************************************** +** 0.5.0 (2006-03-19) + + - some optimizations for lower CPU usage + +new: + - Plextor PX-755 now realy works! (with patched FW) + - Plextor PX-130 support + - PIE/PIF scan on Pioneer drives + - C1/C2 scan on LiteOn drives + - disc lock and eject ability + - full VariRec support: + separate Laserpower & Strategy change for CD & DVD + - PoweRec support + - shows GigaRec rate of inserted CD + - Silent Mode support + - Bitsetting for DVD+R & DVD+R DL + - Simulation on DVD+R(W) + - AutoStrategy support + - PlexEraser support + - FE/TE test + +fixed: + - drive selection is disabled during scan to prevent some problems + - fixed speed detection problems on some DVD drives + supported Realtime Streaming but refuses GET_PERFORMANCE command + +*********************************************** +** 0.4.3 (2006-01-26) + + +problem: + - PX-7[55|60] are not really supported due to + 'authorised commands' + +new: + - shows CD-R(W) manufacturer + - Jitter in percentage + - spindown setting + (works correctly on all drives I have) + - reduced CPU usage during Transfer Rate test + - supported speeds detection instead fixed speeds list + +fixed: + - maximum speed selection now sets meximum speed on ALL drives + +*********************************************** +** 0.4.2 (2005-12-09) + +new: + - Plextor PX-755 support + (same as PX760, but can't write DVD+/-R at 18x) +fixed: + - fixed average CD read speed (it was multiplyed by 100) + - fixed Segmentation Fault then selecting CD-ROM drive + +*********************************************** +** 0.4.1 (2005-11-29) + +fixed: + - 'C2/PIF' in Pi/Cx tab was called 'C1/PIF' in 0.4.0 + - some minor bugfixes + +*********************************************** +** 0.4.0 (2005-11-05) + + - C1/C2 on NEC drives tested. It works. + +new: + - tracks information displaying (type, duration...) + - TA results clearing + - results saving as .html or individual .png's + +*********************************************** +** 0.3.5.8 (2005-10-30) + +fixed: + - transfer rate can't run on CD's in 0.3.5 + +*********************************************** +** 0.3.5 (2005-10-30) + + - improvements in media check code + - changes in Capabilities page: + "V" = drive supported this feature + "X" = unsupported + "?" = detection incomplete + +new: + - C1/C2 scan on NEC drives. UNTESTED! + +fixed: + - now correctly detects DVD+/-R(W)[DL] + even if drive is DVD-ROM. + only one problem - can't differ DVD-RW + Restricted Overwrite from Sequential + - some minor bugfixes + +*********************************************** +** 0.3.4 (2005-10-25) + + - some GUI changes + +new: + - Plextor PX-760 support + - result clearing ability + +fixed: + - speed setting method now different for CD and DVD + - swaped DVD-RW Sequential and Restricted Overwrite + +*********************************************** +** 0.3.3 (2005-10-21) + + - some GUI changes, added status and menu bar + - test order changed: now Transfer Rate runs first + +new: + - simple build script added to prevent come compilation problems + +fixed: + - C1/C2/CU total & max error rate were zeroed at the end of scan + - now zeroing writable size if drive is not writer, + because readers can't perfectly detect free size + +*********************************************** +** 0.3.2 (2005-10-07) + +new: + - more drive capabilities detection + +fixed: + - segmentation fault then trying to turn SpeedRead _on_ in 0.3.1 + +*********************************************** +** 0.3.1 (2005-10-06) + +new: + - shows current sector during test + - test duration (for all tests, except TA) + - realtime average error rate calculating, not only at the end of test + - average speed + - reading writer info from RMA (only for DVD-R) + +fixed: + - Plextor Hide CD-R and SingleSession goes off + while detecting it (bug only in 0.3.0) + - DVD DL media testing + +*********************************************** +** 0.3.0 (2005-10-02) + +new: + - read/write capabilities detection + CD-ROM/R/RW + DDCD-ROM/R/RW + DVD-ROM/RAM/-R/-RW/-RDL/+R/+RW/+RDL + BD-ROM/R/RE + - general capabilities detection + +fixed: + - for stability improvement all GUI operations moved to main thread + (to prevent X assync reply error) + - media type detection problems on MMC-1 drives + - CD transfer rate test on drives not supported CD-DA commands + +*********************************************** +** 0.2.1 (2005-09-21) + + - added hdu..hdz, sr16..sr21 to scan list +fixed: + - spinup disc before transfer rate test + - broken error calculation in 0.2.0 + +*********************************************** +** 0.2.0 (2005-09-20) + + - HISTORY renamed to ChangeLog +new: + - PX-708A2 support + - PX-714 support (same as PX-716) + - vertical grid + - grid for read transfer rate test + - fixed horizontal scale: + - 750 sectors/pix for CD + - 4368 sectors/pix for DVD +fixed: + - missing Plextor Premium ID string + - main thread goes down during tests in older versions + +*********************************************** +** 0.1.2 (2005-09-09) + + - transport code reorganisation +fixed: + - fatal error, then exit by window controls, not 'exit' button + +*********************************************** +** 0.1.1 (2005-09-08) + + - some code cleanups +fixed: + - real DVD transfer rate was limited to 3x + +*********************************************** +** 0.1 (2005-09-04) initial release + +drives supported for disc quality testing: + - Plextor: + - PlexWriter Premium + - PX-712 + - PX-716 + +features: + - bus scanning (hda..hdt, sr0..sr15) + - detecting media type (CD-ROM/R/RW, DVD-ROM/RAM/R/RDL/RW/+R/+RW/+RDL) + and MID code for DVD's + - Plextor features: + - GigaRec + - Hide-CDR + - Single-Session + - Speed Read + - Read transfer rate test + - CD quality tests: + - C1/C2/CU (logarythmic scale) + - Beta/Jitter + - DVD quality tests: + - PIE (sum8) (logarythmic scale) + - PIF (sum1) (logarythmic scale) + - Beta/Jitter + - TA (only PX-716 supported this) + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..9c0a604 --- /dev/null +++ b/INSTALL @@ -0,0 +1,16 @@ +To install from source: + ./configure + make + make install + + +Alailable targets for `make`: +all - build all +cli - build libs and console tools +gui - build gui only +install - install all +cli_install - install libs and console tools +gui_install - install gui only + +clean - clean source tree from object files +uninstall - uninstall all diff --git a/README b/README new file mode 100644 index 0000000..cd714a7 --- /dev/null +++ b/README @@ -0,0 +1,58 @@ +************************************************************ + +QPxTool -- CD/DVD quality checker + +Website: https://qpxtool.sourceforge.io +Sources: https://sourceforge.net/p/qpxtool/code/ci/master/tree/ + +Authors: + Gennady "ShultZ" Kozlov + +SCSI transport code (transport.cpp, transport.h) based on transport.hxx from +dvd+rw-tools, some plextor-specific code got from pxScan/CDVDlib (C) Alex Noe`, +CD-R(W) manufacturer identification code got from cdrecord (C) Joerg Schilling + +************************************************************ + +Features: + - detecting drive R/W and general features + - Read/Write transfer rate tests + - CD quality tests: + - C1/C2/CU (logarythmic scale) + - Extended Error Correction test on Plextor & BENQ drives + - FE/TE (Plextor) + - Jitter/Asymmetry + - DVD quality tests: + - PIE/PIF + - Extended Error Correction test on Plextor & BENQ drives + - Jitter/Asymmetry + - FE/TE (Plextor, Lite-On, BENQ) + - TA (only Plextor PX-7[14|16|55|60] can do this) + - BD quality tests: + - LDC/BIS (Lite-On only) + - FE/TE (Lite-On only) + - Saving test results as HTML or PDF or in internal XML-based + format to see them later + - Results printing + + - Plextor features control (via cdvdcontrol): + - Hide-CDR + - Single-Session + - Speed Read + - GigaRec + - PoweRec + - Silent mode + - VariRec (CD & DVD) + - Bitsetting on DVD+R & DVD+R DL + - Simulation on DVD+R(W) + - AutoStrategy + - Media Quality Check + - PlexEraser + - SecuRec + - AutoStrategy + - Other vendors features: + - Pioneer DVR-A**XL QuietMode (tested on A09) + - DiscT@2 burning on Yamaha CRW-F1 (via f1tattoo and from gui), + accepts PNG images + +************************************************************ diff --git a/SupportedDevices b/SupportedDevices new file mode 100644 index 0000000..c5ec8e2 --- /dev/null +++ b/SupportedDevices @@ -0,0 +1,161 @@ +Per-plugin supported divices list (hardcoded in plugins, does not need probing) + +******************************************************************** +** C2P +** Generic scan plugin for devices supported C2 pointers +** + works on drives that can report C2 pointers with READ_CD command + +******************************************************************** +** ASUS +** Scan plugin for real ASUS devices (based on MediaTek chipset) +** + ASUS DRW-1612 + ASUS DRW-1814 + ASUS DRW-2014S1 + ASUS DRW-2014L1 + +******************************************************************** +** PLEXTOR +** Scan plugin for PLEXTOR devices (based on SANYO chipset) +** + PLEXTOR CD-R PREMIUM + PLEXTOR CD-R PREMIUM2 + PLEXTOR DVDR PX-708A2 + PLEXTOR DVDR PX-712A + PLEXTOR DVDR PX-714A + PLEXTOR DVDR PX-716A + PLEXTOR DVDR PX-716AL + PLEXTOR DVDR PX-755A + PLEXTOR DVDR PX-760A + +******************************************************************** +** LITEON +** Scan plugin for LITE-ON and LITE-ON-based devices, MediaTek chipset +** +** iHAS-xxx and iHAP-xxx devices also works if Lite-On plugin probe is not disabled +** + + LITE-ON LTR-52327S + LITE-ON DVDRW LDW-811S + LITE-ON DVDRW LDW-451S + LITE-ON DVDRW LDW-851S + LITE-ON DVDRW SOHW-812S + LITE-ON DVD+RW SOHW-802S + LITE-ON DVDRW SOHW-832S + LITE-ON DVD+RW SOHW-822S + LITE-ON DVDRW SOHW-1213S + LITE-ON DVDRW SOHW-1613S + LITE-ON DVDRW SOHW-1633S + LITE-ON DVDRW SOHW-1653S + LITE-ON DVDRW SOHW-1673S + LITE-ON DVDRW SOHW-1693S + LITE-ON DVDRW SHW-1635S + LITE-ON DVDRW SHW-16H5S + LITE-ON DVDRW SHW-160P6S + LITE-ON DVDRW SHW-160H6S + LITE-ON DVDRW SHM-165P6S + LITE-ON DVDRW SHM-165H6S + LITE-ON DVDRW SH-16A7 + LITE-ON DVDRW LH-16W1 + LITE-ON DVDRW LH-16A1 + LITE-ON DVDRW LH-18A1 + LITE-ON DVDRW LH-20A1 + LITE-ON DVDRW DH-20A3 + LITE-ON DVDRW DH-20A4 + Slimtype DVD+RW SDW-421S + Slimtype DVDRW SDW-431S + Slimtype DVDRW SOSW-852S + Slimtype DVD+RW SOSW-862S + Slimtype DVDRW SOSW-813S + Slimtype DVDRW SOSW-833S + Slimtype DVD A DS8A2S + SONY DVD RW DW-U18A + SONY DVD RW DRU-700A + SONY DVD RW DW-D18A + SONY DVD RW DW-U20A + SONY DVD RW DW-U21A + SONY DVD RW DRU-710A + SONY DVD RW DW-D22A + SONY DVD RW DW-D23A + SONY DVD RW DRU-720A + SONY DVD RW DW-D26A + SONY DVD RW DRU-800A + SONY DVD RW DW-Q28A + SONY DVD RW DW-Q30A + SONY DVD RW DW-Q31A + SONY DVD RW DW-Q120A + SONY DVD RW DW-G120A + TEAC DV-W58G + TEAC DV-W58G-A + TEAC DV-W512G + TEAC DV-W516G + TEAC DV-W516GA + TEAC DV-W516GB + PLEXTOR DVR PX-806 + PLEXTOR DVR PX-850 + SONY DVD RW DW-D56A + SONY DVD+RW DW-R56A + SONY DVD RW DW-Q58A + SONY DVD RW DW-Q60A + +******************************************************************** +** NEC +** Scan plugin for NEC devices +** + _NEC DVD_RW ND-3520 + _NEC DVD_RW ND-353 + _NEC DVD_RW ND-354 + _NEC DVD_RW ND-355 + _NEC DVD_RW ND-357 + _NEC DVD_RW ND-365 + _NEC DVD_RW ND-455 + _NEC DVD_RW ND-457 + _NEC DVD_RW ND-465 + Optiarc DVD RW AD-717 + +******************************************************************** +** TSST +** Scan plugin for Toshiba-Samsung devices +** + TSSTcorp CDDVDRW SH-S202N + +******************************************************************** +** PIONEER +** Scan plugin for PIONEER and PIONEER-based devices +** + PIONEER DVD-RW DVR-106 + PIONEER DVD-RW DVR-107 + PIONEER DVD-RW DVR-108 + PIONEER DVD-RW DVR-109 + PIONEER DVD-RW DVR-110 + PIONEER DVD-RW DVR-111 + PIONEER DVD-RW DVR-112 + ASUS DRW-0402P + ASUS DRW-0804P + ASUS DRW-1604P + ASUS DRW-1608P + ASUS DRW-1608P2 + ASUS DRW-1608P3 + TEAC DV-W50D + TEAC DV-W58D + TEAC DV-W516D + PLEXTOR DVR PX-810 + +******************************************************************** +** BENQ_DVDROM +** Scan plugin for BENQ DVD-ROM devices +** + PLEXTOR DVD-ROM PX-130 + +******************************************************************** +** BENQ +** Scan plugin for BENQ and BENQ-based devices +** + BENQ DVD DD DW1620 + BENQ DVD DD DW1625 + BENQ DVD DD DW1640 + BENQ DVD DD DW1650 + BENQ DVD DD DW1655 + +******************************************************************** diff --git a/TODO b/TODO new file mode 100644 index 0000000..621f05d --- /dev/null +++ b/TODO @@ -0,0 +1,22 @@ +scan plugins: + - fix BENQ FE/TE + +cdvdcontrol: + - check available PX716 MQCK speeds + - implement Lite-On SmartErase + +qscand: + - daemon mode on win32 + +qpxtool: + - check VariRec CD disabling (win32) + - save drive/scan settings + +pxfw: + - crc calc for PX-708A2 / PX-712 + +gui: + - save selected test speeds + - table+images groups in print preview + - (?) OpenGL graph render engine + diff --git a/confclean b/confclean new file mode 100755 index 0000000..d79580c --- /dev/null +++ b/confclean @@ -0,0 +1,7 @@ +#!/bin/sh + +rm -f Makefile +rm -f gui/Makefile +rm -f lib/Makefile.lib +rm -f plugins/Makefile.plugin +rm -f config.h diff --git a/configure b/configure new file mode 100755 index 0000000..ea940f0 --- /dev/null +++ b/configure @@ -0,0 +1,981 @@ +#!/bin/sh + +TMPC=tmp.c +TMPB=tmpb + +GMAKE="no" +MAKE="make" +QMAKE5_NAMES="qmake qmake5 qmake-qt5 qmake-5" +OSDEFS="" +OSLIBS="" +OSLIBS_HW="" +OSLIBS_INET="" +OSLIBS_DL="" +OSLIBS_THREAD="" + +__cc=cc +__cxx=c++ +__defprefix="" +__prefix="" +__enable_debug=no +__enable_png=yes +__enable_gui=yes +__wt_internal=yes +__qmake="" +__plugins_liteon_noprobe=no +__cross_compilation=no + +__binsuff="" +__libpref="lib" +__libsuff=".so" + +lpng_iopts="" +lpng_ldflags="" +LPNG_DEFS="" +PLUGIN_DEFS="" +DEFS="" + +qt_ver="" +qt_dir="" + +cleanup() +{ + rm -f $TMPC $TMPB$__binsuff +} + +copy_sources() +{ + __src_dir=`dirname $0` + __src_path=`realpath $__src_dir` + cp -a "$__src_path"/* `pwd`/ +} + +check_make() +{ + echo -n "Checking for make... " + x=`gmake -v 2>/dev/null` + if test "$?" -gt 0; then + x=`make -v 2>/dev/null` + if test "$?" -gt 0; then + echo "no" + echo "Make not found! Can't continue" + exit + fi + MAKE="make" + else + MAKE="gmake" + fi + echo "yes" + + + echo -n "Checking if we have GNU Make... " + if test "$OSL" = "mingw32" ; then + make_t0=`$MAKE -v | tr '[A-Z]' '[a-z]' | grep make | grep ver | cut -d ' ' -f 1` + else + make_t0=`$MAKE -v | tr '[A-Z]' '[a-z]' | grep make | cut -d ' ' -f 1` + fi + + if test "$make_t0" = "gnu" ; then + GMAKE="yes" + else + GMAKE="no" + fi + + echo $GMAKE +} + +check_compiler() +{ + echo -n "Checking C compiler... " + cc_name=`$__cc -v 2>&1 | tail -n 1 | cut -d ' ' -f 1` + cc_ver=`$__cc -dumpversion 2>&1` + if test "$?" -gt 0; then + cc_name="not found" + cc_ver="" + fi + echo -n $cc_name $cc_ver + + case $cc_name in + "not found") + echo "" + echo "** No C compiler found, can not continue!" + exit 1 + ;; + gcc) + echo "" + ;; + *) + echo " only gcc tested!" + ;; + esac + + + echo -n "Checking C++ compiler... " + cxx_name=`$__cxx -v 2>&1 | tail -n 1 | cut -d ' ' -f 1` + cxx_ver=`$__cxx -dumpversion 2>&1` + if test "$?" -gt 0; then + cxx_name="not found" + cxx_ver="" + fi + echo $cxx_name $cxx_ver + + if test "$cxx_name" = "not found" ; then + echo "" + echo "** No C++ compiler found, can not continue!" + exit 1 + fi +} + +check_libpng() +{ + __enable_png=no + echo -n "Checking for libpng... " + lpng_ver=`libpng-config --version 2>/dev/null` + if test "$?" -gt 0; then + lpng_ver="not found" + fi + echo $lpng_ver + + if test "$lpng_ver" != "not found" ; then + + echo -n " libpng compiler flags: " + lpng_iopts=`libpng-config --I_opts` + echo $lpng_iopts + echo -n " libpng linker flags: " + lpng_ldflags=`libpng-config --ldflags` + echo $lpng_ldflags + + echo -n "Checking png.h presense... " + + echo " + #include + int main(int ac, char** av) { return 0; } + " > $TMPC + + $__cc $lpng_iopts $TMPC -o $TMPB 2>&1 1>/dev/null + if test "$?" -gt 0; then + echo "no" + lpng_iopts="" + lpng_ldflags="" + else + echo "yes" + + echo -n "Checking png.h usability... " + echo " + #include + int main(int ac, char** av) { + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + return 0; } + " > $TMPC + + + $__cc $lpng_iopts $TMPC $lpng_ldflags -o $TMPB 2>&1 1>/dev/null + if test "$?" -gt 0; then + echo "no" + lpng_iopts="" + lpng_ldflags="" + else + echo "yes" + __enable_png=yes + LPNG_DEFS="-DUSE_LIBPNG" + fi + fi + fi +} + +check_offset64() +{ + echo -n "Checking off_t size with _FILE_OFFSET_BITS = 64... " + echo " + #define _FILE_OFFSET_BITS 64 + #include + #include + #include + int main(int ac, char** av) { + printf(\"%d\", sizeof(off_t)); + return 0; } + " > $TMPC + + if test "$__cross_compilation" = "no"; then + #$__cc $TMPC -o $TMPB 2>/dev/null 1>/dev/null + $__cc $TMPC -o $TMPB + if test "$?" -eq 0; then + OFFT_SIZE=`./$TMPB` + echo $OFFT_SIZE + if test "$OFFT_SIZE" -eq 8; then + OSDEFS=$OSDEFS" -DOFFT_64BIT" + fi + else + echo "no off_t type defined!" + exit 1 + fi + fi + + echo -n "Checking for fopen64()... " + echo " + #define _FILE_OFFSET_BITS 64 + #include + int main(int ac, char** av) { + FILE* f; + f = fopen64(\"f\",\"r+\"); + return 0; } + " > $TMPC + + $__cc $TMPC -o $TMPB 2>/dev/null 1>/dev/null + if test "$?" -gt 0; then + echo "no" + else + echo "yes" + OSDEFS=$OSDEFS" -DHAVE_FOPEN64" + fi + + + echo -n "Checking for fseek64()... " + echo " + #define _FILE_OFFSET_BITS 64 + #include + int main(int ac, char** av) { + FILE* f; + fseek64(f,0, SEEK_SET); + return 0; } + " > $TMPC + + $__cc $TMPC -o $TMPB 2>/dev/null 1>/dev/null + if test "$?" -gt 0; then + echo "no" + else + echo "yes" + OSDEFS=$OSDEFS" -DHAVE_FSEEK64" + fi + + + echo -n "Checking for fseeko()... " + echo " + #define _FILE_OFFSET_BITS 64 + #include + int main(int ac, char** av) { + FILE* f; + fseeko(f,0, SEEK_SET); + return 0; } + " > $TMPC + + $__cc $TMPC -o $TMPB 2>/dev/null 1>/dev/null + if test "$?" -gt 0; then + echo "no" + else + echo "yes" + OSDEFS=$OSDEFS" -DHAVE_FSEEKO" + fi +} + +qt_check_qmake() +{ + qt_ver=`$1 -v 2>/dev/null` + if test "$?" -gt 0; then + qt_ver="not found" + qt_dir="" + else + qt_ver=`$1 -v 2>/dev/null | tail -n 1 | cut -d' ' -f 4 ` + qt_dir=`$1 -v 2>/dev/null | tail -n 1 | cut -d' ' -f 6 ` + fi + + + case $qt_ver in + "not found") + __qmake="" + ;; + 5.*) + __qmake=$1 + ;; + *) + echo "Unknown/unsupported Qt version" + __qmake="" + ;; + esac +} + +qt_check_default() +{ + for qmake5 in $QMAKE5_NAMES ; do + qt_check_qmake $qmake5 + if test "$qt_dir" != "" ; then + return + fi + done +} + + +ckeck_qt5() +{ + __enable_gui=no + echo -n "Checking for Qt5... " + if test "$__qmake" = "" ; then + qt_check_default + else + qt_check_qmake $__qmake + fi + + if test "$qt_dir" = "" ; then + echo "no, GUI will not be compiled. + If you really have Qt5 and want to build GUI try --qmake=/path/to/qmake" + else + __enable_gui=yes + echo "$qt_dir ($qt_ver), qmake command: $__qmake" + fi +} + +create_makefile_top() +{ + echo "* Makefile" + rm -f Makefile + + echo "#ifndef __CONFIG_H__" > config.h + echo "#define __CONFIG_H__" >> config.h + echo "#define INSTALL_PREFIX \"$__prefix\"" >> config.h + echo "#endif //__CONFIG_H__" >> config.h + + echo "PREFIX = $__prefix +BINDIR = $__bindir +SBINDIR = $__sbindir +LIBDIR = $__libdir +PLUGINDIR = $__plugindir +INCDIR = $__incdir +MANDIR = $__mandir + +BINSUFF = $__binsuff +LIBS_HW = $OSLIBS_HW +LIBS_INET = $OSLIBS_INET +LIBS_DL = $OSLIBS_DL +LIBS_THREAD = $OSLIBS_THREAD + +LPNG_INC = $lpng_iopts +LPNG_LIB = $lpng_ldflags + +" >> Makefile + + echo "CXXFLAGS += -Wall -O2 $OSDEFS $LPNG_DEFS $PLUGIN_DEFS $DEFS" >> Makefile + echo "CFLAGS += -Wall -O2 $OSDEFS $LPNG_DEFS $PLUGIN_DEFS $DEFS" >> Makefile + + if test "$__cross_compilation" = "yes" ; then + echo "# additional cross-compilatio options" >> Makefile + echo "CC=$__cxx" >> Makefile + echo "CXX=$__cc" >> Makefile + echo "CXXFLAGS += -fPIC" >> Makefile + echo "CFLAGS += -fPIC" >> Makefile + echo "export CXX CC" >> Makefile + echo "export CXXFLAGS CFLAGS" >> Makefile + echo "export LPNG_INC LPNG_LIB" >> Makefile + echo "export PREFIX BINDIR SBINDIR LIBDIR PLUGINDIR INCDIR MANDIR BINSUFF LIBS_HW LIBS_INET LIBS_DL LIBS_THREAD" >> Makefile + fi + + if test "$__enable_debug" = "yes" ; then + echo "# additional debug options" >> Makefile + echo "CXXFLAGS += -g" >> Makefile + echo "CFLAGS += -g" >> Makefile + fi + + if test "$OSL" = "darwin" ; then + echo "QMAKESPEC=macx-g++" >> Makefile + echo "export QMAKESPEC" >> Makefile + fi + + if test "$GMAKE" = "yes" ; then + echo "export PREFIX BINDIR SBINDIR LIBDIR PLUGINDIR INCDIR MANDIR BINSUFF LIBS_HW LIBS_INET LIBS_DL LIBS_THREAD" >> Makefile + echo "export LPNG_INC LPNG_LIB" >> Makefile + echo "export CXXFLAGS CFLAGS" >> Makefile + fi + + + echo " +all: cli gui +cli: lib console plugins man" >> Makefile + +echo " +lib: + \$(MAKE) -C lib + +console: lib + \$(MAKE) -C console + +plugins: lib + \$(MAKE) -C plugins + +man: + \$(MAKE) -C man +" >> Makefile + + + echo " +gui: lib" >> Makefile + if test "$__enable_gui" = "yes" ; then + echo " \$(MAKE) -C gui" >> Makefile + fi + + echo " +clean:" >> Makefile + echo " \$(MAKE) -C lib clean + \$(MAKE) -C console clean + \$(MAKE) -C plugins clean + \$(MAKE) -C man clean" >> Makefile + if test "$__enable_gui" = "yes" ; then + echo " \$(MAKE) -C gui clean" >> Makefile + fi + + echo " +install: all cli_install gui_install" >> Makefile + + echo " +cli_install:" >> Makefile + echo " \$(MAKE) -C lib install + \$(MAKE) -C console install + \$(MAKE) -C plugins install + \$(MAKE) -C man install" >> Makefile + + echo " +gui_install:" >> Makefile + if test "$__enable_gui" = "yes" ; then + echo " \$(MAKE) -C gui install" >> Makefile + fi + + echo " +uninstall: cli_uninstall gui_uninstall" >> Makefile + + echo " +cli_uninstall:" >> Makefile + echo " \$(MAKE) -C lib uninstall + \$(MAKE) -C console uninstall + \$(MAKE) -C plugins uninstall + \$(MAKE) -C man uninstall" >> Makefile + + echo " +gui_uninstall:" >> Makefile + if test "$__enable_gui" = "yes" ; then + echo " \$(MAKE) -C gui uninstall" >> Makefile + fi + + echo " +help: + @echo \"\" + @echo \"Alailable targets:\" + @echo \"all - build all\" + @echo \"cli - build libs and console tools\" + @echo \"gui - build gui only\" + @echo \"plugins - build plugins only\" + @echo \"install - install all\" + @echo \"cli_install - install libs and console tools\" + @echo \"gui_install - install gui only\" + @echo \"\" + @echo \"clean - clean source tree from object files\" + @echo \"uninstall - uninstall all\" + @echo \"cli_uninstall - uninstall libs and console tools\" + @echo \"gui_uninstall - uninstall gui only\"" >> Makefile + + echo " +.PHONY: all clean cli lib console plugins man nogui gui install cli_install gui_install uninstall cli_uninstall gui_uninstall help" >> Makefile +} + +create_makefile_gui() +{ + mf="gui/Makefile" + echo "* $mf" + + rm -f $mf + mkdir -p `dirname $mf` + + if test "$OSL" = "netbsd" ; then + qt_dir="/usr/pkg/qt5" + echo "QTDIR=$qt_dir" >> $mf + echo "LDFLAGS+=-Wl,-R/usr/X11R7/lib" >> $mf + fi + echo "QTDIR=$qt_dir" >> $mf + echo "QMAKE5= $__qmake" >> $mf + + if test "$GMAKE" = "yes" ; then + echo "export QTDIR QMAKE5" >> $mf + fi + + echo " +all: Makefile.qmake" >> $mf +if test "$OSL" = "mingw32" ; then + if test "$__enable_debug" = "yes" ; then + echo " \$(MAKE) -f Makefile.qmake debug $@" >> $mf + else + echo " \$(MAKE) -f Makefile.qmake release $@" >> $mf + fi +else + echo " \$(MAKE) -f Makefile.qmake $@" >> $mf + if test "$__enable_debug" = "no" ; then + if test "$OSL" = "darwin" ; then +# echo " strip qpxtool.app/Contents/MacOS/qpxtool" >> $mf + echo " " >> $mf + else + echo " strip --strip-unneeded qpxtool" >> $mf + fi + fi +fi + echo " lrelease -verbose qpxtool.pro" >> $mf + + echo " +clean: Makefile.qmake + rm -f Makefile.qmake + rm -f qpxtool$__binsuff + rm -f obj/* + rm -f moc/* + rm -f *~ + rm -f src/*~ + rm -f include/*~ + rm -f locale/*.qm + rm -f qrc_qpxtool.cpp" >> $mf + + case "$OSL" in + "mingw32") + echo " +install: Makefile.qmake" >> $mf + if test "$__enable_debug" = "yes" ; then + echo " install -m 755 debug/qpxtool.exe \$(DESTDIR)\$(BINDIR)" >> $mf + else + echo " install -m 755 release/qpxtool.exe \$(DESTDIR)\$(BINDIR)" >> $mf + fi + echo " mkdir -p \$(DESTDIR)\$(BINDIR)/locale" >> $mf + echo " install -m 644 locale/*.qm \$(DESTDIR)\$(BINDIR)/locale" >> $mf + echo " +uninstall: + rm -f \$(DESTDIR)\$(BINDIR)/qpxtool.exe + rm -rf \$(DESTDIR)\$(BINDIR)/locale" >> $mf + ;; + *) + echo " +install: Makefile.qmake" >> $mf + + if test "$OSL" = "darwin" ; then + echo " install -m 755 qpxtool.app/Contents/MacOS/qpxtool \$(DESTDIR)\$(BINDIR)/qpxtool" >> $mf + else + echo " install -m 755 qpxtool \$(DESTDIR)\$(BINDIR)/qpxtool" >> $mf + fi + + echo " mkdir -p \$(DESTDIR)\$(PREFIX)/share/qpxtool/locale" >> $mf + echo " install -m 644 locale/*.qm \$(DESTDIR)\$(PREFIX)/share/qpxtool/locale" >> $mf + echo " mkdir -p \$(DESTDIR)\$(PREFIX)/share/pixmaps" >> $mf + echo " install -m 644 images/q.png \$(DESTDIR)\$(PREFIX)/share/pixmaps/qpxtool.png" >> $mf + echo " mkdir -p \$(DESTDIR)\$(PREFIX)/share/applications" >> $mf + echo " install -m 644 qpxtool.desktop \$(DESTDIR)\$(PREFIX)/share/applications/qpxtool.desktop" >> $mf + echo " +uninstall: + rm -f \$(DESTDIR)\$(BINDIR)/qpxtool + rm -rf \$(DESTDIR)\$(PREFIX)/share/qpxtool + rm -f \$(DESTDIR)\$(PREFIX)/share/pixmaps/qpxtool.png + rm -f \$(DESTDIR)\$(PREFIX)/share/applications/qpxtool.desktop" >> $mf + ;; + esac + + echo " +Makefile.qmake: + rm -f Makefile.qmake + \$(QMAKE5) LIBS+=-L../lib/lib LIBS+=-lqpxtransport $QDEBUG -o Makefile.qmake" >> $mf + echo " +.PHONY: all clean install uninstall" >> $mf +} + +create_makefile_lib() +{ + mf="lib/Makefile.lib" + echo "* $mf" + + rm -f $mf + mkdir -p `dirname $mf` + + case "$OSL" in + darwin) + echo " +LIB_SHORT = $__libpref\$(LIBN)$__libsuff +LIB = $__libpref\$(LIBN).\$(VER_MAJOR).\$(VER_MINOR).\$(VER_MICRO)$__libsuff + +CXXFLAGS += -I. -I./include -I../include +LDFLAGS += -dynamiclib -Wl -install_name \$(LIBDIR)/\$(LIB_SHORT) +" >> $mf + ;; + + + mingw32) + echo " +LIB_SHORT = $__libpref\$(LIBN)$__libsuff +LIB = \$(LIB_SHORT) + +CXXFLAGS += -I. -I./include -I../include +LDFLAGS += -shared -Wl,-soname,\$(LIB_SHORT) +" >> $mf + ;; + *) + echo " +LIB_SHORT = $__libpref\$(LIBN)$__libsuff +LIB_SONAME = \$(LIB_SHORT).\$(VER_MAJOR) +LIB_SONAMEV= \$(LIB_SONAME).\$(VER_MINOR) +LIB = \$(LIB_SONAMEV).\$(VER_MICRO) + +CXXFLAGS += -I. -I./include -I../include +LDFLAGS += -shared -Wl,-soname,\$(LIB_SONAME) +" >> $mf + ;; + esac + + +echo " +all: \$(LIB) + +\$(LIB): \$(OBJS) + \$(CXX) \$(CXXFLAGS) \$(LDFLAGS) $^ -o \$@ \$(LDLIBS) + mkdir -p ../lib + ln -fs ../\$(DIR)/\$(LIB) ../lib/\$(LIB_SHORT) +" >> $mf + + + case "$OSL" in + darwin) + echo " +clean: + rm -f \$(LIB)* \$(OBJS) ../lib/\$(LIB) *~ include/*~ + +install: " >> $mf + if test "$__enable_debug" = "no" ; then + echo " strip --strip-unneeded \$(LIB)" >> $mf + fi + echo " + mkdir -p \$(DESTDIR)\$(LIBDIR) + install -m 755 \$(LIB) \$(DESTDIR)\$(LIBDIR) + ln -sf \$(LIB) \$(DESTDIR)\$(LIBDIR)/\$(LIB_SHORT) + install -m 644 \$(HDRS) \$(DESTDIR)\$(INCDIR)/qpxtool + +uninstall: + rm -f \$(DESTDIR)\$(LIBDIR)/\$(LIB_SHORT) + rm -f \$(DESTDIR)\$(LIBDIR)/\$(LIB) +" >> $mf + + ;; + mingw32) + echo " +clean: + rm -f \$(LIB_SHORT)* \$(OBJS) ../lib/\$(LIB_SHORT) *~ include/*~ + +install: + mkdir -p \$(DESTDIR)\$(LIBDIR) + install -m 755 \$(LIB) \$(DESTDIR)\$(LIBDIR) + +uninstall: + rm -f \$(DESTDIR)\$(LIBDIR)/\$(LIB) +" >> $mf + ;; + *) + echo " +clean: + rm -f \$(LIB_SHORT)* \$(OBJS) ../lib/\$(LIB_SHORT) *~ include/*~ + +install: " >> $mf + if test "$__enable_debug" = "no" ; then + echo " strip --strip-unneeded \$(LIB)" >> $mf + fi + echo " + mkdir -p \$(DESTDIR)\$(LIBDIR) + install -m 755 \$(LIB) \$(DESTDIR)\$(LIBDIR) + ln -sf \$(LIB) \$(DESTDIR)\$(LIBDIR)/\$(LIB_SONAME) + ln -sf \$(LIB_SONAME) \$(DESTDIR)\$(LIBDIR)/\$(LIB_SHORT) + install -m 644 \$(HDRS) \$(DESTDIR)\$(INCDIR)/qpxtool + +uninstall: + rm -f \$(DESTDIR)\$(LIBDIR)/\$(LIB_SHORT) + rm -f \$(DESTDIR)\$(LIBDIR)/\$(LIB_SONAME) + rm -f \$(DESTDIR)\$(LIBDIR)/\$(LIB) +" >> $mf + ;; + esac + + echo " +.PHONY: all clean install uninstall" >> $mf +} + + +create_makefile_plugin() +{ + mf="plugins/Makefile.plugin" + echo "* $mf" + + rm -f $mf + mkdir -p `dirname $mf` + + case "$OSL" in + darwin) + PLUGIN_LDFLAGS=" -dynamiclib -Wl" + ;; + *) + PLUGIN_LDFLAGS=" -shared -Wl,-soname,\$(LIB_SONAME)" + ;; + esac + +echo " +SRC = qscan_plugin qscan_cmd +HDRS = qscan_plugin.h + +SRCS = \$(patsubst %,%.cpp, \$(SRC)) +OBJS = \$(patsubst %.cpp,%.o,\$(SRCS)) + +LIB_SHORT = lib\$(LIBN)$__libsuff +LIB_SONAME = \$(LIB_SHORT) +LIB = \$(LIB_SHORT) + +CPPFLAGS += -I. -I../../lib/include +LDFLAGS += $PLUGIN_LDFLAGS +LDLIBS += -L../../lib/lib -lqpxtransport -lqpxscan + +all: \$(LIB) + +\$(LIB): \$(OBJS) + \$(CXX) \$(CXXFLAGS) \$(LDFLAGS) $^ -o \$@ \$(LDLIBS) + + + mkdir -p ../lib + ln -fs ../\$(DIR)/\$(LIB) ../lib/\$(LIB_SHORT) + +clean: + rm -f \$(LIB_SHORT) \$(OBJS) ../lib/\$(LIB_SHORT) *~ include/*~ + +install: " > $mf + if test "$__enable_debug" = "no" ; then + echo " strip --strip-unneeded \$(LIB_SHORT)" >> $mf + fi +echo " + mkdir -p \$(DESTDIR)\$(PLUGINDIR) + install -m 755 \$(LIB_SHORT) \$(DESTDIR)\$(PLUGINDIR) + +uninstall: + rm -f \$(DESTDIR)\$(PLUGINDIR)/\$(LIB_SHORT) + +.PHONY: all clean install uninstall +" >> $mf + +} + +show_help() +{ + echo "--prefix=PREFIX set installation prefix" + echo "--bindir=BINDIR set path to install binaries" + echo "--sbindir=SBINDIR set path to install system binaries" + echo "--libdir=LIBDIR set path to install libraries" + echo "--mandir=MANDIR set path to install mans" + echo "--qmake=QMAKE_COMMAND set QT5 qmake command" + echo " default is to check following: $QMAKE5_NAMES" + echo "--disable-png disable PNG support in f1tattoo (auto)" + echo "--disable-gui don't compile GUI" + echo "--enable-debug build debug version" + echo "--disable-liteon-probe disable device probing in LiteOn plugin" + echo " enabled by default, but this code may cause some devices hang" + echo "--disable-internal-wt disable internal CD/DVD writing implementation and use cdrecord instead" + exit +} + + +#echo "Parsing parameters..." +for arg in $* ; do + case $arg in + --help|-h) show_help ;; + --prefix=*) __prefix=`echo $arg | cut -d '=' -f 2-` ;; + --bindir=*) bindir=`echo $arg | cut -d '=' -f 2-` ;; + --sbindir=*) sbindir=`echo $arg | cut -d '=' -f 2-` ;; + --libdir=*) libdir=`echo $arg | cut -d '=' -f 2-` ;; + --mandir=*) mandir=`echo $arg | cut -d '=' -f 2-` ;; + --target=*) target=`echo $arg | cut -d '=' -f 2-` + echo target: $target + __cc=${target}-gcc + __cxx=${target}-g++ + __cross_compilation=yes + ;; + --host=*) echo host: `echo $arg | cut -d '=' -f 2-` ;; + + --qmake=*) __qmake=`echo $arg | cut -d '=' -f 2-` ;; + --disable-png) __enable_png="no" ;; + --disable-gui) __enable_gui="no" ;; + --enable-debug) __enable_debug="yes" ;QDEBUG="CONFIG+=debug" ;; + --disable-liteon-probe) __plugins_liteon_noprobe="yes" ; PLUGIN_DEFS+="-DPLUGINS_LITEON_NOPROBE" ;; + --disable-internal-wt) __wt_internal="no" ; DEFS+="-DDISABLE_INTERNAL_WT" ;; + *) + echo "invalid argument: $arg" + exit + ;; + esac +done + + +echo -n "Checking OS... " +OS=`uname -s` +OSL=`uname -s | tr '[A-Z]' '[a-z]'` +echo $OS + +echo -n "Cross-compilation... " +echo $__cross_compilation + +if test "$__cross_compilation" = "yes" ; then + OS=$target + OSL=$target +fi + +case $OSL in + *linux*) + OSDEFS="-fPIC" + OSLIBS_DL="-ldl" + OSLIBS_THREAD="-lpthread" + __defprefix="/usr/local" + ;; + *gnu*) + OSDEFS="-fPIC" + OSLIBS_DL="-ldl" + OSLIBS_THREAD="-lpthread" + __defprefix="/usr" + ;; + *darwin*) + OSDEFS="-D__unix -fPIC" + OSLIBS_THREAD="-lpthread" + OSLIBS_HW="-framework CoreFoundation -framework IOKit" + __defprefix="/usr" + __libsuff=".dylib" + ;; + *freebsd*) + OSDEFS="-fPIC" + OSLIBS_THREAD="-lpthread" + OSLIBS_HW="-lcam" + __defprefix="/usr" + ;; + *gnu/kfreebsd*) + OSDEFS="-fPIC" + OSLIBS_DL="-ldl" + OSLIBS_THREAD="-lpthread" + OSLIBS_HW="-lcam" + __defprefix="/usr" + ;; + netbsd|openbsd) + OSDEFS="-fPIC" + OSLIBS_THREAD="-lpthread" + __defprefix="/usr" + ;; + *mingw32*) + OSL="mingw32" + OSDEFS="-fPIC" + OSLIBS_THREAD="-lpthread" + OSLIBS_INET="-lws2_32 -lstdc++" + #__defprefix="/c/Progra~1/QPxTool" + __binsuff=".exe" + __libpref="" + __libsuff=".dll" + + ;; + *) + echo "OS not supported: $OS" + ;; +esac + +if test "$__prefix" = "" ; then + __prefix=$__defprefix +fi + +echo -n "Checking machine arch... " +ARCH=`uname -m | tr '[A-Z]' '[a-z]' | tr ', /\\()"' ',//////' | tr ',/' ',-'` +BHOST=`uname -srv` + +case "$OSL" in + linux|gnu/kfreebsd|gnu) + __bindir=$__prefix/bin + __sbindir=$__prefix/sbin + if test "$ARCH" = "x86_64" ; then + __libdir=$__prefix/lib64 + else + __libdir=$__prefix/lib + fi + __plugindir=$__libdir/qpxtool + __incdir=$__prefix/include + __mandir=$__prefix/share/man + ;; + netbsd|openbsd|freebsd|darwin) + __bindir=$__prefix/bin + __sbindir=$__prefix/sbin + __libdir=$__prefix/lib + __plugindir=$__libdir/qpxtool + __incdir=$__prefix/include + __mandir=$__prefix/share/man + ;; + *mingw32*) + __bindir=$__prefix + __sbindir=$__prefix + __libdir=$__prefix + __plugindir=$__prefix/plugins + __incdir=$__prefix/include + __mandir=$__prefix/man + ;; + *) + __bindir=$__prefix/bin + __sbindir=$__prefix/sbin + __libdir=$__prefix/lib + __plugindir=$__libdir/qpxtool + __incdir=$__prefix/include + __mandir=$__prefix/man + ;; +esac +echo $ARCH + + +if test "$bindir" != "" ; then + __bindir=$bindir +fi +if test "$sbindir" != "" ; then + __sbindir=$sbindir +fi +if test "$libdir" != "" ; then + __libdir=$libdir + __plugindir=$__libdir/qpxtool +fi +if test "$mandir" != "" ; then + __mandir=$mandir +fi + + +check_make +check_compiler +check_offset64 + +if test "$__enable_png" = "yes" ; then + check_libpng +fi + +if test "$__enable_gui" = "yes" ; then + ckeck_qt5 +fi + +echo "" +echo "** Creating Makefiles..." + +create_makefile_top +create_makefile_lib +create_makefile_plugin + +if test "$__enable_gui" = "yes" ; then + create_makefile_gui +fi + +if test "$__cross_compilation" = "yes" ; then + copy_sources +fi + +echo "" +echo "** install prefix: "$__prefix +echo "** debug version: "$__enable_debug +echo "** PNG support: "$__enable_png +echo "** build gui: "$__enable_gui +echo "** use internal WT: "$__wt_internal +echo "" +echo "Installation paths:" +echo " bin : "$__bindir +echo " sbin : "$__sbindir +echo " libs : "$__libdir +echo " plugins : "$__plugindir +echo " includes : "$__incdir"/qpxtool" +echo " man : "$__mandir +echo "** Configuration finished! run \`$MAKE\` now" + +cleanup + diff --git a/console/Makefile b/console/Makefile new file mode 100644 index 0000000..051798f --- /dev/null +++ b/console/Makefile @@ -0,0 +1,30 @@ +all: readdvd cdvdcontrol pxfw f1tattoo qscan qscand + +clean install uninstall: + $(MAKE) -C readdvd DIR=readdvd $@ + $(MAKE) -C cdvdcontrol DIR=cdvdcontrol $@ + $(MAKE) -C pxfw DIR=pxfw $@ + $(MAKE) -C f1tattoo DIR=f1tattoo $@ + $(MAKE) -C qscan DIR=qscan $@ + $(MAKE) -C qscand DIR=qscand $@ + + +readdvd: + $(MAKE) -C readdvd DIR=readdvd + +cdvdcontrol: + $(MAKE) -C cdvdcontrol DIR=cdvdcontrol + +pxfw: + $(MAKE) -C pxfw DIR=pxfw + +f1tattoo: + $(MAKE) -C f1tattoo DIR=f1tattoo + +qscan: + $(MAKE) -C qscan DIR=qscan + +qscand: + $(MAKE) -C qscand DIR=qscand + +.PHONY: all clean install uninstall readdvd cdvdcontrol pxfw f1tattoo qscan qscand diff --git a/console/cdvdcontrol/Makefile b/console/cdvdcontrol/Makefile new file mode 100644 index 0000000..9c67fbf --- /dev/null +++ b/console/cdvdcontrol/Makefile @@ -0,0 +1,25 @@ +SRCS = $(patsubst %,%.cpp, cdvdcontrol) +OBJS = $(patsubst %.cpp,%.o,$(SRCS)) + +BIN = cdvdcontrol$(BINSUFF) + +CXXFLAGS += -I. -I../../lib/include +CFLAGS += -I. -I../../lib/include +LDLIBS += -L../../lib/lib -lqpxtransport -lqpxplextor -lqpxyamaha -lqpxpioneer + +$(BIN): $(OBJS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) + +all:$(BIN) + +clean: + rm -f $(BIN) $(OBJS) *~ + +install: + mkdir -p $(DESTDIR)$(BINDIR) + install -m 4755 $(BIN) $(DESTDIR)$(BINDIR) + +uninstall: + rm -f $(DESTDIR)$(BINDIR)/$(BIN) + +.PHONY: all clean install uninstall diff --git a/console/cdvdcontrol/cdvdcontrol.cpp b/console/cdvdcontrol/cdvdcontrol.cpp new file mode 100644 index 0000000..6c243b1 --- /dev/null +++ b/console/cdvdcontrol/cdvdcontrol.cpp @@ -0,0 +1,1290 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2009 Gennady "ShultZ" Kozlov + * + * + * Some Plextor commands got from PxScan and CDVDlib (C) Alexander Noe` + * + * + * 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 +#include + +#include +#include +#include +#include + +#include + + +const uint32_t FL_HELP = 0x00000001; +const uint32_t FL_SCAN = 0x00000002; +const uint32_t FL_DEVICE = 0x00000004; +const uint32_t FL_CURRENT = 0x00000008; + +const uint32_t FL_SUPPORTED = 0x00000010; +const uint32_t FL_LOCK = 0x00000020; +const uint32_t FL_POWEREC = 0x00000040; +const uint32_t FL_GIGAREC = 0x00000080; + +const uint32_t FL_VARIREC_CD = 0x00000100; +const uint32_t FL_VARIREC_CD_OFF = 0x00000200; +const uint32_t FL_VARIREC_DVD = 0x00000400; +const uint32_t FL_VARIREC_DVD_OFF = 0x00000800; + +const uint32_t FL_HCDR = 0x00001000; +const uint32_t FL_SSS = 0x00002000; +const uint32_t FL_SPDREAD = 0x00004000; +const uint32_t FL_BOOK_R = 0x00008000; + +const uint32_t FL_BOOK_RDL = 0x00010000; +const uint32_t FL_TESTWRITE = 0x00020000; +const uint32_t FL_SILENT = 0x00040000; +const uint32_t FL_AS = 0x00080000; + +const uint32_t FL_PXERASER = 0x00100000; +const uint32_t FL_SECUREC = 0x00200000; +const uint32_t FL_NOSECUREC = 0x00400000; +const uint32_t FL_VERBOSE = 0x00800000; + +const uint32_t FL_YMH_AMQR = 0x01000000; +const uint32_t FL_YMH_FORCESPEED = 0x02000000; +const uint32_t FL_MQCK = 0x04000000; + +const uint32_t FL_PIOLIMIT = 0x08000000; +const uint32_t FL_PIOQUIET = 0x10000000; +const uint32_t FL_LOEJ = 0x20000000; + +const uint32_t FLAS_RETR = 0x00000001; +const uint32_t FLAS_STOR = 0x00000002; +const uint32_t FLAS_CREATE = 0x00000004; +const uint32_t FLAS_DEL = 0x00000008; + +const uint32_t FLAS_VIEW = 0x00000010; +const uint32_t FLAS_ACT = 0x00000020; +const uint32_t FLAS_DEACT = 0x00000040; +const uint32_t FLAS_CLEAR = 0x00000080; + + uint32_t flags = 0; + uint32_t flags_as = 0; + +int get_device_info(drive_info* dev) +{ + dev->ven_features=0; + dev->chk_features=0; + detect_capabilities(dev); +// detect_check_capabilities(dev); + determine_disc_type(dev); + if ( !isPlextor(dev) && !isYamaha(dev) && !isPioneer(dev)) { + printf ("%s: drive not supported, only common controls will work!\n", dev->device); +// return 1; + } + if ( isPlextor(dev) ) { + plextor_get_life(dev); + if (dev->life.ok) { + printf("Discs loaded: %6d\n", dev->life.dn); + printf("Drive operating time:\n"); + printf(" CD Rd : %4d:%02d:%02d\n", dev->life.cr.h, dev->life.cr.m, dev->life.cr.s); + printf(" CD Wr : %4d:%02d:%02d\n", dev->life.cw.h, dev->life.cw.m, dev->life.cw.s); + if (dev->rd_capabilities & DEVICE_DVD) + printf(" DVD Rd : %4d:%02d:%02d\n", dev->life.dr.h, dev->life.dr.m, dev->life.dr.s); + if (dev->wr_capabilities & DEVICE_DVD) + printf(" DVD Wr : %4d:%02d:%02d\n", dev->life.dw.h, dev->life.dw.m, dev->life.dw.s); + } + +// if ( isPlextorLockPresent(dev) ) + plextor_px755_do_auth(dev); + if (!plextor_get_hidecdr_singlesession(dev)) dev->ven_features|=PX_HCDRSS; + if (!plextor_get_speedread(dev)) dev->ven_features|=PX_SPDREAD; + if (dev->wr_capabilities) { +// if (!yamaha_check_amqr(dev)) dev->ven_features|=YMH_AMQR; + if (!plextor_get_powerec(dev)) { + dev->ven_features|=PX_POWEREC; +// plextor_get_speeds(dev); + } + if (!plextor_get_gigarec(dev)) dev->ven_features|=PX_GIGAREC; + if (!plextor_get_varirec(dev, VARIREC_CD)) dev->ven_features|=PX_VARIREC_CD; +// if (!plextor_get_securec(dev)) dev->ven_features|=PX_SECUREC; + if (!plextor_get_silentmode(dev)) dev->ven_features|=PX_SILENT; + if (!plextor_get_securec_state(dev)) dev->ven_features|=PX_SECUREC; + } + if (dev->wr_capabilities & DEVICE_DVD) { + if (!plextor_get_varirec(dev, VARIREC_DVD)) dev->ven_features|=PX_VARIREC_DVD; + if (!plextor_get_bitset(dev, PLEX_BITSET_R)) dev->ven_features|=PX_BITSET_R; + if (!plextor_get_bitset(dev, PLEX_BITSET_RDL)) dev->ven_features|=PX_BITSET_RDL; + if (!plextor_get_autostrategy(dev)) dev->ven_features|=PX_ASTRATEGY; + if (!plextor_get_testwrite_dvdplus(dev)) dev->ven_features|=PX_SIMUL_PLUS; + } +#warning "PlexEraser DETECTION. Just assume PX755/760 and Premium-II" + // if ((dev->dev_ID == PLEXTOR_755) || (dev->dev_ID == PLEXTOR_760) || (dev->dev_ID == PLEXTOR_PREMIUM2)) + if ( isPlextorLockPresent(dev) ) + dev->ven_features|=PX_ERASER; + if (!yamaha_check_amqr(dev)) dev->ven_features|=YMH_AMQR; + } else if ( isYamaha(dev) ) { + if (!yamaha_check_amqr(dev)) dev->ven_features|=YMH_AMQR; + if (!yamaha_check_forcespeed(dev)) dev->ven_features|=YMH_FORCESPEED; + if (!yamaha_f1_get_tattoo(dev)) dev->ven_features|=YMH_TATTOO; + } else if ( isPioneer(dev) ) { + if (!pioneer_get_quiet(dev)) dev->ven_features|=PIO_QUIET; + } + +// printf("Trying opcode E9 modes...\n"); +// for (int i=0; i<256; i++) {if (!plextor_get_mode(dev,i)) printf(" MODE 0x%02X\n",i);} + + +// printf("Trying opcode ED modes...\n"); +// for (int i=0; i<256; i++) {if (!plextor_get_mode2(dev,i)) printf(" MODE 0x%02X\n",i);} + + + if (flags & FL_SUPPORTED) { +// printf("____________________________\n"); + printf("\n** Supported features:\n"); + printf("AudioMaster Q.R. : %s\n", dev->ven_features & YMH_AMQR ? "YES" : "-"); + printf("Yamaha ForceSpeed : %s\n", dev->ven_features & YMH_FORCESPEED ? "YES" : "-"); + printf("Yamaha DiscT@2 : %s\n", dev->ven_features & YMH_TATTOO ? "YES" : "-"); + printf("Hide CD-R : %s\n", dev->ven_features & PX_HCDRSS ? "YES" : "-"); + printf("SingleSession : %s\n", dev->ven_features & PX_HCDRSS ? "YES" : "-"); + printf("SpeedRead : %s\n", dev->ven_features & PX_SPDREAD ? "YES" : "-"); + printf("PoweRec : %s\n", dev->ven_features & PX_POWEREC ? "YES" : "-"); + printf("GigaRec : %s\n", dev->ven_features & PX_GIGAREC ? "YES" : "-"); + printf("VariRec CD : %s\n", dev->ven_features & PX_VARIREC_CD ? "YES" : "-"); + printf("VariRec DVD : %s\n", dev->ven_features & PX_VARIREC_DVD ? "YES" : "-"); + printf("SecuRec : %s\n", dev->ven_features & PX_SECUREC ? "YES" : "-"); + printf("Silent mode : %s\n", dev->ven_features & PX_SILENT ? "YES" : "-"); + printf("DVD+R bitsetting : %s\n", dev->ven_features & PX_BITSET_R ? "YES" : "-"); + printf("DVD+R DL bitsetting : %s\n", dev->ven_features & PX_BITSET_RDL ? "YES" : "-"); + printf("DVD+R(W) testwrite : %s\n", dev->ven_features & PX_SIMUL_PLUS ? "YES" : "-"); + printf("AutoStrategy : %s%s\n", + dev->ven_features & PX_ASTRATEGY ? "YES" : "-", + (dev->ven_features & PX_ASTRATEGY) && (dev->dev_ID & (PLEXTOR_755 | PLEXTOR_760)) ? " (EXTENDED)" : ""); + printf("PlexEraser : %s\n", dev->ven_features & PX_ERASER ? "YES" : "-"); + printf("Pioneer QuietMode : %s\n", dev->ven_features & PIO_QUIET ? "YES" : "-"); + } + + +// get_media_info(dev); +// if (dev->rd_capabilities & DEVICE_DVD) { + + if (flags & FL_CURRENT) { +// printf("____________________________\n"); + printf("\n** Current drive settings:\n"); + } + if ((dev->capabilities & CAP_LOCK) && (flags & FL_CURRENT)) + { get_lock(dev); + printf("Lock state : %s\n", (dev->parms.status & STATUS_LOCK) ? "ON":"OFF"); } +// if ((dev->ven_features & YMH_AMQR) && ((flags & FL_CURRENT) || (flags & FL_YMH_AMQR))) +// printf("AudioMaster Q.R. : %s\n", dev->yamaha.amqr ? "ON":"OFF"); +// if ((dev->ven_features & YMH_FORCESPEED) && ((flags & FL_CURRENT) || (flags & FL_YMH_FORCESPEED))) +// printf("Yamaha ForceSpeed : %s\n", dev->yamaha.forcespeed ? "ON":"OFF"); + if ((dev->ven_features & YMH_TATTOO) && (flags & FL_CURRENT)) { + if (dev->yamaha.tattoo_rows) { + printf("Yamaha DiscT@2 : inner %d outer %d image 3744 x %d\n", + dev->yamaha.tattoo_i, + dev->yamaha.tattoo_o, + dev->yamaha.tattoo_rows); + } else { + if (dev->media.type & DISC_CD) + printf("DiscT@2: Can't write T@2 on inserted disc! No space left?\n"); + else + printf("DiscT@2: No disc found! Can't get T@2 info!\n"); + } + } + if ((dev->ven_features & PX_HCDRSS) && ((flags & FL_CURRENT) || (flags & FL_HCDR))) + printf("Hide-CDR state : %s\n", dev->plextor.hcdr ? "ON":"OFF"); + if ((dev->ven_features & PX_HCDRSS) && ((flags & FL_CURRENT) || (flags & FL_SSS))) + printf("SingleSession state : %s\n", dev->plextor.sss ? "ON":"OFF"); + if ((dev->ven_features & PX_SPDREAD) && ((flags & FL_CURRENT) || (flags & FL_SPDREAD))) + printf("SpeedRead state : %s\n", dev->plextor.spdread ? "ON":"OFF"); + if ((dev->ven_features & PX_POWEREC) && ((flags & FL_CURRENT) || (flags & FL_POWEREC))) { + printf("PoweRec state : %s\n", dev->plextor.powerec_state ? "ON":"OFF"); + if (dev->media.type & DISC_CD) + printf(" PoweRec Speed : %dX (CD)\n",dev->plextor.powerec_spd / 176); + if (dev->media.type & DISC_DVD) + printf(" PoweRec Speed : %dX (DVD)\n",dev->plextor.powerec_spd / 1385); +// show_powerec_speed(); + } + if ((dev->ven_features & PX_GIGAREC) && ((flags & FL_CURRENT) || (flags & FL_GIGAREC))) + print_gigarec_value(dev); + if ((dev->ven_features & PX_VARIREC_CD) && ((flags & FL_CURRENT) || (flags & FL_VARIREC_CD))) + { printf("VariRec CD state : %s\n", dev->plextor.varirec_state_cd ? "ON":"OFF"); + if (dev->plextor.varirec_state_cd) print_varirec(dev, VARIREC_CD); } + if ((dev->ven_features & PX_VARIREC_DVD) && ((flags & FL_CURRENT) || (flags & FL_VARIREC_DVD))) + { printf("VariRec DVD state : %s\n", dev->plextor.varirec_state_dvd ? "ON":"OFF"); + if (dev->plextor.varirec_state_dvd) print_varirec(dev, VARIREC_DVD); } + if ((dev->ven_features & PX_SECUREC) && ((flags & FL_CURRENT) || (flags & FL_SECUREC))) + printf("SecuRec state : %s\n", dev->plextor.securec ? "ON":"OFF"); + if ((dev->ven_features & PX_SILENT) && ((flags & FL_CURRENT) || (flags & FL_SILENT))) + { printf("Silent mode : %s\n",dev->plextor_silent.state ? "ON":"OFF"); + if (dev->plextor_silent.state) plextor_print_silentmode_state(dev); } + if ((dev->ven_features & PX_BITSET_R) && ((flags & FL_CURRENT) || (flags & FL_BOOK_R))) + printf("DVD+R bitsetting : %s\n", dev->book_plus_r ? "ON":"OFF"); + if ((dev->ven_features & PX_BITSET_RDL) && ((flags & FL_CURRENT) || (flags & FL_BOOK_RDL))) + printf("DVD+R DL bitsetting : %s\n", dev->book_plus_rdl ? "ON":"OFF"); + if ((dev->ven_features & PX_SIMUL_PLUS) && ((flags & FL_CURRENT) || (flags & FL_TESTWRITE))) + printf("DVD+R(W) testwrite : %s\n", dev->plextor.testwrite_dvdplus ? "ON":"OFF"); + if ((dev->ven_features & PX_ASTRATEGY) && ((flags & FL_CURRENT) || (flags & FL_AS))) + plextor_print_autostrategy_state(dev); + if (dev->ven_features & PX_ERASER) + printf("PlexEraser : supported\n"); + + if ((dev->ven_features & PIO_QUIET) && ((flags & FL_CURRENT) || (flags & FL_PIOQUIET))) + printf("QuietMode setting : %s\n", pioneer_silent_tbl[(int)dev->pioneer.silent]); + if ((dev->ven_features & PIO_QUIET) && ((flags & FL_CURRENT) || (flags & FL_PIOLIMIT))) + printf("Speed Limit : %s\n", dev->pioneer.limit ? "ON" : "OFF" ); + return 0; +} + +void usage(char* bin) { + fprintf (stderr,"\nusage: %s [-d device] [options]\n",bin); + printf("Common options:\n"); + printf("\t-l, --scanbus list drives (scan IDE/SCSI bus)\n"); + printf("\t-h, --help show help\n"); + printf("\t-v, --verbose be verbose\n"); + printf("\t-c, --current show current drive settings\n"); + printf("\t-s, --supported show supported features\n"); + printf("\t--[un]lock lock/unlock disc in drive\n"); + printf("\t--lockt toggle media lock state\n"); + printf("\t--eject eject media\n"); + printf("\t--load load media (only for tray devices)\n"); + printf("\t--loej load/eject media (toggle tray state)\n"); + printf("\t--loej-immed don't wait for media is loaded or ejected\n"); + + printf("\noptions for Plextor devices:\n"); + printf("\t--spdread [on|off] turn SpeedRead on/off (default: off)\n"); + printf("\t--sss [on|off] turn SingleSession on/off (default: off)\n"); + printf("\t--hcdr [on|off] turn Hide-CDR on/off (default: off)\n"); + printf("\t--powerec [on|off] turn PoweREC on/off (default: on)\n"); +// printf("\t--amqr [on|off] turn Yamaha AMQR on/off (default: off)\n"); +// printf("\t--forcespeed [on|off] turn Yamaha ForceSpeed on/off (default: off)\n"); + printf("\t--gigarec set GigaREC rate or turn it off\n"); + printf("\t values: 0.6, 0.7, 0.8, 0.9, 1.1, 1.2, 1.3, 1.4, off\n"); + printf("\t--varirec-cd set VariREC power for CD or turn VariREC off\n"); + printf("\t values: +4, +3, +2, +1, 0, -1, -2, -3, -4, off\n"); + printf("\t--varirec-cd-strategy set VariREC strategy for CD\n"); + printf("\t values: default, azo, cya, pha, phb, phc, phd\n"); + printf("\t--varirec-dvd set VariREC power for DVD or turn VariREC off\n"); + printf("\t values: +4, +3, +2, +1, 0, -1, -2, -3, -4, off\n"); + printf("\t--varirec-dvd-strategy set VariREC strategy for DVD\n"); + printf("\t values: default, 0, 1, 2, 3, 4, 5, 6, 7\n"); + printf("\t--securec set SecuREC password and turn it on\n"); + printf("\t passwd must be 4..10 characters length\n"); + printf("\t--nosecurec turn SecuRec off\n"); + printf("\t--bitset+r [on|off] turn on/off setting DVD-ROM book on DVD+R media\n"); + printf("\t--bitset+rdl [on|off] turn on/off setting DVD-ROM book on DVD+R DL media\n"); + printf("\t--mqck run MediaQuality Check\n"); + printf("\t--mqck-speed # set MQCK speed (default: maximum)\n"); + printf("\t--as-mode AS: select AutoStrategy mode: forced,auto,on,off,\n"); + printf("\t--as-list AS: view strategies list\n"); + printf("\t--as-on # AS: activate strategy #\n"); + printf("\t--as-off # AS: deactivate strategy #\n"); + printf("\t--as-del # AS: delete strategy #\n"); + printf("\t--as-create AS: create new strategy for inserted DVD media (PX-755/PX-760 only)\n"); + printf("\t mode: q = quick, f = full\n"); + printf("\t action: a = add, r = replace\n"); + printf("\t--as-save AS: save Strategies / EXPERIMENTAL /\n"); + printf("\t--as-load AS: load Strategy from file / EXPERIMENTAL /\n"); + printf("\t--as-clear AS: remove ALL strategies from database\n"); + printf("\t--dvd+testwrite [on|off] turn on/off testwrite on DVD+R(W) media\n"); + printf("\t--silent [on|off] just turn Silent Mode on/off (default: on)\n"); + printf("\t--sm-nosave don't save Silent Mode settings\n"); + printf("\t--sm-cd-rd # set max CD READ speed (default: 32X)\n"); + printf("\t values: 4, 8, 24, 32, 40, 48\n"); + printf("\t--sm-cd-wr # set max CD WRITE speed (default: 32X)\n"); + printf("\t values: 4, 8, 16, 24, 32, 48\n"); + printf("\t--sm-dvd-rd # set max DVD READ speed (default: 12X)\n"); + printf("\t values: 2, 5, 8, 12, 16\n"); +// printf("\t--sm-dvd-wr # set max DVD WRITE speed (default: 8X)\n"); +// printf("\t values: 4, 6, 8, 12, 16\n"); + printf("\t--sm-load # set tray load speed. spd can be 0 to 80 (default: 63)\n"); + printf("\t--sm-eject # set tray eject speed. spd can be 0 to 80 (default: 0)\n"); + printf("\t--sm-access set access time, has effect only with CD/DVD speed setting\n"); + printf("\t--destruct perform PlexEraser function\n"); + printf("\t WARNING! data on inserted CD-R/DVD-R will be lost!\n"); + printf("\t works on PX-755/PX-760/Premium2 only\n"); + + printf("\noptions for Pioneer devices:\n"); + printf("\t--pio-limit [on|off] limit (or not) read speed by 24x for CD and 8x for DVD\n"); + printf("\t--pio-quiet [quiet|perf|std] select QuietMode setting: Quiet, Performance or Standard (default: quiet)\n"); + printf("\t--pio-nosave don't save settings to drive (changes will be lost after reboot)\n"); +} + +int main (int argc, char* argv[]) +{ + int i; + int drvcnt=0; + char* device=NULL; + drive_info* dev=NULL; + char aslfn[2048]; + char assfn[2048]; +// char asfn2[1032]; + FILE* asf; +// FILE* asf2; + + int powerec = 1; + int gigarec = GIGAREC_10; + int varirec_cd_pwr = VARIREC_NULL; + int varirec_cd_str = 0; + int varirec_dvd_pwr = VARIREC_NULL; + int varirec_dvd_str = 0; + int hcdr = 1; + int sss = 1; + int spdread = 1; + int bookr = 1; + int bookrdl = 1; + int testwrite = 1; + int as_mqck = -1; + int as_mqck_spd = -1; + int as = AS_AUTO; + int ascre = ASDB_CRE_QUICK; + int as_idx_act = 0; + int as_idx_deact = 0; + int as_idx_del = 0; + int silent = 1; + int silent_load = 63; + int silent_eject = 0; + int silent_access = SILENT_ACCESS_FAST; + int silent_cd_rd = SILENT_CD_RD_32X; + int silent_cd_wr = SILENT_CD_WR_32X; + int silent_dvd_rd = SILENT_DVD_RD_12X; +// int silent_dvd_wr = SILENT_DVD_WR_8X; + int silent_cd = 0; + int silent_dvd = 0; + int silent_tray = 0; + int silent_save = 1; + int pxeraser = 0; + int amqr = 0; + int forcespeed = 0; + char passwd[256]; + + char piosilent = PIO_SILENT_QUIET; + bool piolimit = 1; + int eject = 2; + int lock = 2; + bool loej_immed = 0; + + printf("** CDVD Control v%s (c) 2005-2009 Gennady \"ShultZ\" Kozlov\n", VERSION); + +// printf("Parsing commandline options (%d args)...\n",argc-1); + for (i=1; i(i+1)) { + i++; + flags |= FL_DEVICE; + device = argv[i]; + } else { + printf("Option %s needs parameter\n",argv[i]); + exit (1); + } + } + else if(!strcmp(argv[i],"-h")) flags |= FL_HELP; + else if(!strcmp(argv[i],"--help")) flags |= FL_HELP; + else if(!strcmp(argv[i],"-c")) flags |= FL_CURRENT; + else if(!strcmp(argv[i],"--current")) flags |= FL_CURRENT; + else if(!strcmp(argv[i],"-s")) flags |= FL_SUPPORTED; + else if(!strcmp(argv[i],"--supported")) flags |= FL_SUPPORTED; + else if(!strcmp(argv[i],"-l")) flags |= FL_SCAN; + else if(!strcmp(argv[i],"--scanbus")) flags |= FL_SCAN; +// ************ Lock + else if(!strcmp(argv[i],"--unlock")) { + if ( !(flags & FL_LOCK)) { + flags |= FL_LOCK; lock=0; + } else { + printf("Conflicting/duplicated lock/unlock option!\n"); + } + } + else if(!strcmp(argv[i],"--lock")) { + if ( !(flags & FL_LOCK)) { + flags |= FL_LOCK; lock=1; + } else { + printf("Conflicting/duplicated lock/unlock option!\n"); + } + } + else if(!strcmp(argv[i],"--lockt")) { + if ( !(flags & FL_LOCK)) { + flags |= FL_LOCK; lock=2; + } else { + printf("Conflicting/duplicated lock/unlock option!\n"); + } + } + else if(!strcmp(argv[i],"--load")) { + if ( !(flags & FL_LOEJ)) { + flags |= FL_LOEJ; eject=0; + } else { + printf("Conflicting/duplicated load/eject option!\n"); + } + } + else if(!strcmp(argv[i],"--eject")) { + if ( !(flags & FL_LOEJ)) { + flags |= FL_LOEJ; eject=1; + } else { + printf("Conflicting/duplicated load/eject option!\n"); + } + } + else if(!strcmp(argv[i],"--loej")) { + if ( !(flags & FL_LOEJ)) { + flags |= FL_LOEJ; eject=2; + } else { + printf("Conflicting/duplicated load/eject option!\n"); + } + } + else if(!strcmp(argv[i],"--loej-immed")) { + loej_immed = true; + } + else if(!strcmp(argv[i],"--spdread")) { + flags |= FL_SPDREAD; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"off")) spdread = 0; + else if (!strcmp(argv[i],"on")) spdread = 1; + else { + printf("Illegal argument for SpeedRead: %s\n", argv[i]); + } + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--sss")) { + flags |= FL_SSS; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"off")) sss = 0; + else if (!strcmp(argv[i],"on")) sss = 1; + else { + printf("Illegal argument for SingleSession: %s\n", argv[i]); + } + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--hcdr")) { + flags |= FL_HCDR; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"off")) hcdr = 0; + else if (!strcmp(argv[i],"on")) hcdr = 1; + else { + printf("Illegal argument for Hide-CDR: %s\n", argv[i]); + } + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } +// ************ PoweREC + else if(!strcmp(argv[i],"--powerec")) { + flags |= FL_POWEREC; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"off")) powerec = 0; + else if (!strcmp(argv[i],"on")) powerec = 1; + else { + printf("Illegal argument for PoweRec: %s\n", argv[i]); + } + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } +// ************ GigaREC + else if(!strcmp(argv[i],"--gigarec")) { + if (argc>(i+1)) { + i++; + flags |= FL_GIGAREC; + if (!strcmp(argv[i],"0.6")) gigarec = GIGAREC_06; + else if (!strcmp(argv[i],"0.7")) gigarec = GIGAREC_07; + else if (!strcmp(argv[i],"0.8")) gigarec = GIGAREC_08; + else if (!strcmp(argv[i],"0.9")) gigarec = GIGAREC_09; + else if (!strcmp(argv[i],"1.0")) gigarec = GIGAREC_10; + else if (!strcmp(argv[i],"off")) gigarec = GIGAREC_10; + else if (!strcmp(argv[i],"1.1")) gigarec = GIGAREC_11; + else if (!strcmp(argv[i],"1.2")) gigarec = GIGAREC_12; + else if (!strcmp(argv[i],"1.3")) gigarec = GIGAREC_13; + else if (!strcmp(argv[i],"1.4")) gigarec = GIGAREC_14; + else { + printf("Illegal GigaREC value: %s\n", argv[i]); + } + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } +// ************ VariREC CD + else if(!strcmp(argv[i],"--varirec-cd")) { + if (argc>(i+1)) { + i++; + flags |= FL_VARIREC_CD; + int val = 0; + if (!strcmp(argv[i],"off")) flags |= FL_VARIREC_CD_OFF; + else { + val = strtol(argv[i], NULL, 0); + if (errno || val<-4 || val>4) { + printf("Illegal VariREC CD power value: %s\n", argv[i]); + return 5; + } else switch(val) { + case -4: varirec_cd_pwr = VARIREC_MINUS_4; break; + case -3: varirec_cd_pwr = VARIREC_MINUS_3; break; + case -2: varirec_cd_pwr = VARIREC_MINUS_2; break; + case -1: varirec_cd_pwr = VARIREC_MINUS_1; break; + case 0: varirec_cd_pwr = VARIREC_NULL; break; + case 1: varirec_cd_pwr = VARIREC_PLUS_1; break; + case 2: varirec_cd_pwr = VARIREC_PLUS_2; break; + case 3: varirec_cd_pwr = VARIREC_PLUS_3; break; + case 4: varirec_cd_pwr = VARIREC_PLUS_4; break; + } + } + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--varirec-cd-strategy")) { + if (argc>(i+1)) { + i++; + int val = atol(argv[i]); + if (!errno && val>=-1 && val<6) { + varirec_cd_str = val+1; + } else + if (!strcmp(argv[i],"default")) varirec_cd_str = 0; + else if (!strcmp(argv[i],"azo")) varirec_cd_str = 1; + else if (!strcmp(argv[i],"cya")) varirec_cd_str = 2; + else if (!strcmp(argv[i],"pha")) varirec_cd_str = 3; + else if (!strcmp(argv[i],"phb")) varirec_cd_str = 4; + else if (!strcmp(argv[i],"phc")) varirec_cd_str = 5; + else if (!strcmp(argv[i],"phd")) varirec_cd_str = 6; + else { + printf("Illegal VariREC CD strategy: %s\n", argv[i]); + } + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } +// ************ VariREC DVD + else if(!strcmp(argv[i],"--varirec-dvd")) { + if (argc>(i+1)) { + i++; + flags |= FL_VARIREC_DVD; + int val = 0; + if (!strcmp(argv[i],"off")) flags |= FL_VARIREC_DVD_OFF; + else { + val = strtol(argv[i], NULL, 0); + if (errno || val<-4 || val>4) { + printf("Illegal VariREC DVD power value: %s\n", argv[i]); + return 5; + } else switch(val) { + case -4: varirec_dvd_pwr = VARIREC_MINUS_4; break; + case -3: varirec_dvd_pwr = VARIREC_MINUS_3; break; + case -2: varirec_dvd_pwr = VARIREC_MINUS_2; break; + case -1: varirec_dvd_pwr = VARIREC_MINUS_1; break; + case 0: varirec_dvd_pwr = VARIREC_NULL; break; + case 1: varirec_dvd_pwr = VARIREC_PLUS_1; break; + case 2: varirec_dvd_pwr = VARIREC_PLUS_2; break; + case 3: varirec_dvd_pwr = VARIREC_PLUS_3; break; + case 4: varirec_dvd_pwr = VARIREC_PLUS_4; break; + } + } + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--varirec-dvd-strategy")) { + if (argc>(i+1)) { + i++; + int val = atol(argv[i]); + if (!errno && val>=-1 && val<8) { + varirec_dvd_str = val+1; + } + else if (!strcmp(argv[i],"default")) varirec_dvd_str = 0; + else { + printf("Illegal VariREC DVD strategy: %s\n", argv[i]); + } + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--securec")) { + if (argc>(i+1)) { + i++; + strcpy(passwd,argv[i]); + flags |= FL_SECUREC; + // printf("SecuRec pass: %s\n", passwd); + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--nosecurec")) flags |= FL_NOSECUREC; + else if(!strcmp(argv[i],"--bitset+r")) { + flags |= FL_BOOK_R; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"off")) bookr = 0; + else if (!strcmp(argv[i],"on")) bookr = 1; + } + } + else if(!strcmp(argv[i],"--bitset+rdl")) { + flags |= FL_BOOK_RDL; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"off")) bookrdl = 0; + else if (!strcmp(argv[i],"on")) bookrdl = 1; + } + } + else if(!strcmp(argv[i],"--dvd+testwrite")) { + flags |= FL_TESTWRITE; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"off")) testwrite = 0; + else if (!strcmp(argv[i],"on")) testwrite = 1; + } + } + else if(!strcmp(argv[i],"--mqck")) { + flags |= FL_MQCK; + if (argc>(i+1)) { + i++; + as_idx_act = (int)strtol(argv[i], NULL, 0); + if (!strcmp(argv[i],"quick")) as_mqck = AS_MEDIACK_QUICK; + else if (!strcmp(argv[i],"adv")) as = AS_MEDIACK_ADV; + else if (!strcmp(argv[i],"advanced")) as = AS_MEDIACK_ADV; + else + printf("invalid MQCK mode requested: %s\n", argv[i]); + } + } + else if(!strcmp(argv[i],"--mqck-speed")) { + if (argc>(i+1)) { + i++; + as_mqck_spd = (int)strtol(argv[i], NULL, 0); + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--as-mode")) { + flags |= FL_AS; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"forced")) as = AS_FORCED; + else if (!strcmp(argv[i],"on")) as = AS_ON; + else if (!strcmp(argv[i],"auto")) as = AS_AUTO; + else if (!strcmp(argv[i],"off")) as = AS_OFF; + else + printf("invalid AutoStrategy mode requested: %s\n", argv[i]); + } + } + else if(!strcmp(argv[i],"--as-create")) { + flags_as |= FLAS_CREATE; + ascre = 0; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"q")) ascre |= ASDB_CRE_QUICK; + else if (!strcmp(argv[i],"f")) ascre |= ASDB_CRE_FULL; + else + printf("invalid --as-create parameter 1: '%s'. Only 'q|f' are valid\n", argv[i]); + } + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"a")) ascre |= ASDB_ADD; + else if (!strcmp(argv[i],"r")) ascre |= ASDB_REPLACE; + else + printf("invalid --as-create parameter 2: '%s'. Only 'a|r' are valid\n", argv[i]); + } + } + else if(!strcmp(argv[i],"--as-list")) { + flags_as |= FLAS_VIEW; + } + else if(!strcmp(argv[i],"--as-clear")) { + flags_as |= FLAS_CLEAR; + } + else if(!strcmp(argv[i],"--as-on")) { + flags_as |= FLAS_ACT; + if (argc>(i+1)) { + i++; + as_idx_act = (int)strtol(argv[i], NULL, 0); + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--as-off")) { + flags_as |= FLAS_DEACT; + if (argc>(i+1)) { + i++; + as_idx_deact = (int)strtol(argv[i], NULL, 0); + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--as-del")) { + flags_as |= FLAS_DEL; + if (argc>(i+1)) { + i++; + as_idx_del = (int)strtol(argv[i], NULL, 0); + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--as-save")) { + if (argc>(i+1)) { + i++; + flags_as |= FLAS_RETR; + strcpy(assfn, argv[i]); + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--as-load")) { + if (argc>(i+1)) { + i++; + flags_as |= FLAS_STOR; + strcpy(aslfn, argv[i]); + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--silent")) { + flags |= FL_SILENT; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"off")) silent = 0; + else if (!strcmp(argv[i],"on")) silent = 1; + } + } + else if(!strcmp(argv[i],"--sm-load")) { + flags |= FL_SILENT; silent = 1; silent_tray = 1; + if (argc>(i+1)) { + i++; + silent_load = (int)strtol(argv[i], NULL, 0); +// printf("tray load speed: %d\n", silent_load); + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--sm-eject")) { + flags |= FL_SILENT; silent = 1; silent_tray = 1; + if (argc>(i+1)) { + i++; + silent_eject = (int)strtol(argv[i], NULL, 0); +// printf("tray load speed: %d\n", silent_load); + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--sm-access")) { + flags |= FL_SILENT; silent = 1; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"fast")) silent_access = SILENT_ACCESS_FAST; + else if (!strcmp(argv[i],"slow")) silent_access = SILENT_ACCESS_SLOW; + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--sm-cd-rd")) { + flags |= FL_SILENT; silent = 1; silent_cd = 1; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"48")) silent_cd_rd = SILENT_CD_RD_48X; + else if (!strcmp(argv[i],"40")) silent_cd_rd = SILENT_CD_RD_40X; + else if (!strcmp(argv[i],"32")) silent_cd_rd = SILENT_CD_RD_32X; + else if (!strcmp(argv[i],"24")) silent_cd_rd = SILENT_CD_RD_24X; + else if (!strcmp(argv[i], "8")) silent_cd_rd = SILENT_CD_RD_8X; + else if (!strcmp(argv[i], "4")) silent_cd_rd = SILENT_CD_RD_4X; + else + printf("invalid --sm-cd-rd parameter: %s\n", argv[i]); + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--sm-cd-wr")) { + flags |= FL_SILENT; silent = 1; silent_cd = 1; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"48")) silent_cd_wr = SILENT_CD_WR_48X; + else if (!strcmp(argv[i],"32")) silent_cd_wr = SILENT_CD_WR_32X; + else if (!strcmp(argv[i],"24")) silent_cd_wr = SILENT_CD_WR_24X; + else if (!strcmp(argv[i],"16")) silent_cd_wr = SILENT_CD_WR_16X; + else if (!strcmp(argv[i], "8")) silent_cd_wr = SILENT_CD_WR_8X; + else if (!strcmp(argv[i], "4")) silent_cd_wr = SILENT_CD_WR_4X; + else + printf("invalid --sm-cd-wr parameter: %s\n", argv[i]); + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } + else if(!strcmp(argv[i],"--sm-dvd-rd")) { + flags |= FL_SILENT; silent = 1; silent_dvd = 1; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"16")) silent_dvd_rd = SILENT_DVD_RD_16X; + else if (!strcmp(argv[i],"12")) silent_dvd_rd = SILENT_DVD_RD_12X; + else if (!strcmp(argv[i], "8")) silent_dvd_rd = SILENT_DVD_RD_8X; + else if (!strcmp(argv[i], "5")) silent_dvd_rd = SILENT_DVD_RD_5X; + else if (!strcmp(argv[i], "2")) silent_dvd_rd = SILENT_DVD_RD_2X; + else + printf("invalid --sm-dvd-rd parameter: %s\n", argv[i]); + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } +/* + else if(!strcmp(argv[i],"--sm-dvd-wr")) { + flags |= FL_SILENT; silent = 1; silent_dvd = 1; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"18")) silent_dvd_wr = SILENT_DVD_WR_18X; + else if (!strcmp(argv[i],"16")) silent_dvd_wr = SILENT_DVD_WR_12X; + else if (!strcmp(argv[i],"12")) silent_dvd_wr = SILENT_DVD_WR_12X; + else if (!strcmp(argv[i], "8")) silent_dvd_wr = SILENT_DVD_WR_8X; + else if (!strcmp(argv[i], "6")) silent_dvd_wr = SILENT_DVD_WR_6X; + else if (!strcmp(argv[i], "4")) silent_dvd_wr = SILENT_DVD_WR_4X; + else + printf("invalid --sm-dvd-wr parameter: %s\n", argv[i]); + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } +*/ + else if(!strcmp(argv[i],"--sm-nosave")) silent_save = 0; + else if(!strcmp(argv[i],"--destruct")) { + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"quick")) { flags |= FL_PXERASER; pxeraser = PLEXERASER_QUICK; } + if (!strcmp(argv[i],"full")) { flags |= FL_PXERASER; pxeraser = PLEXERASER_FULL; } + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } else if(!strcmp(argv[i],"--amqr")) { + flags |= FL_YMH_AMQR; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"off")) amqr = 0; + else if (!strcmp(argv[i],"on")) amqr = 1; + } + } else if(!strcmp(argv[i],"--forcespeed")) { + flags |= FL_YMH_FORCESPEED; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"off")) forcespeed = 0; + else if (!strcmp(argv[i],"on")) forcespeed = 1; + } + + +// printf("\t--pio-limit [on|off] limit (or not) read speed by 24x for CD and 8x for DVD\n"); +// printf("\t--pio-quiet [quiet|perf|std] select QuietMode setting: Quiet, Performance or Standard (default: quiet)\n"); +// printf("\t--pio-nosave don't save settings to drive (changes will be lost after reboot)\n"); + + } else if(!strcmp(argv[i],"--pio-nosave")) { + silent_save = 0; + } else if(!strcmp(argv[i],"--pio-limit")) { + flags |= FL_PIOLIMIT; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"off")) piolimit = 0; + else if (!strcmp(argv[i],"on")) piolimit = 1; + } + } else if(!strcmp(argv[i],"--pio-quiet")) { + flags |= FL_PIOQUIET; + if (argc>(i+1)) { + i++; + if (!strcmp(argv[i],"quiet")) piosilent = PIO_SILENT_QUIET; + else if (!strcmp(argv[i],"perf")) piosilent = PIO_SILENT_PERF; + else if (!strcmp(argv[i],"std")) piosilent = PIO_SILENT_STD; + else + printf("invalid --pio-quiet parameter: %s\n", argv[i]); + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + + } else if(!strcmp(argv[i],"-v")) flags |= FL_VERBOSE; + else if(!strcmp(argv[i],"--verbose")) flags |= FL_VERBOSE; + else { + printf("unknown option: %s\n", argv[i]); + return 6; + } + } + if (flags & FL_HELP) { + usage(argv[0]); + return 0; + } + if (!flags) { + usage(argv[0]); + return 1; + } + + if (flags & FL_SCAN) { + drvcnt = scanbus( DEV_PLEXTOR | DEV_YAMAHA ); + if (!drvcnt) printf("ERR: no drives found!\n"); + return 2; + } + if (!(flags & FL_DEVICE)) { + printf("** ERR: no device selected\n"); + return 3; + } +// printf("____________________________\n"); + printf("Device : %s\n", device); + dev = new drive_info(device); + if (dev->err) { + printf("%s: can't open device: %s\n", argv[0], device); + delete dev; + return 4; + } + + inquiry(dev); +// convert_to_ID(dev); + printf("Vendor : '%s'\n",dev->ven); + printf("Model : '%s'",dev->dev); + if ( isPlextor(dev)) { + plextor_get_TLA(dev); + printf(" (TLA#%s)",dev->TLA); + } + printf("\nF/W : '%s'\n",dev->fw); + + if (!(flags & FL_VERBOSE)) dev->silent++; + if (get_drive_serial_number(dev)) printf("Serial#: %s\n",dev->serial); + + if (flags) { +// if (flags & FL_VERBOSE) { + printf("\nCDVD Control flags : "); + if (flags & FL_VERBOSE) printf(" VERBOSE"); + if (flags & FL_DEVICE) printf(" DEVICE"); + if (flags & FL_HELP) printf(" HELP"); + if (flags & FL_CURRENT) printf(" CURRENT"); + if (flags & FL_SUPPORTED) printf(" SUPPORTED"); + if (flags & FL_SCAN) printf(" SCAN"); + if (flags & FL_LOCK) printf(" LOCK"); + if (flags & FL_HCDR) printf(" HCDR"); + if (flags & FL_SSS) printf(" SSS"); + if (flags & FL_SPDREAD) printf(" SPDREAD"); + if (flags & FL_POWEREC) printf(" POWEREC"); + if (flags & FL_GIGAREC) printf(" GIGAREC"); + if (flags & FL_VARIREC_CD) printf(" VARIREC_CD"); + if (flags & FL_VARIREC_DVD) printf(" VARIREC_DVD"); + if (flags & FL_SECUREC) printf(" SECUREC"); + if (flags & FL_BOOK_R) printf(" BITSET_R"); + if (flags & FL_BOOK_RDL) printf(" BITSET_RDL"); + if (flags & FL_TESTWRITE) printf(" DVD+TESTWRITE"); + if (flags & FL_AS) printf(" AS"); + if (flags & FL_SILENT) printf(" SILENT"); + if (flags & FL_YMH_AMQR) printf(" AMQR"); + if (flags & FL_YMH_FORCESPEED) printf(" FORCESPEED"); + if (flags & FL_PIOLIMIT) printf(" PIOLIMIT"); + if (flags & FL_PIOQUIET) printf(" PIOQUIET"); + if (flags & FL_LOEJ) printf(" LOEJ"); + if (flags_as & FLAS_RETR) printf(" AS_RETR"); + if (flags_as & FLAS_STOR) printf(" AS_STOR"); + if (flags_as & FLAS_CREATE) printf(" AS_CREATE"); + if (flags_as & FLAS_DEL) printf(" AS_DEL"); + if (flags_as & FLAS_VIEW) printf(" AS_VIEW"); + if (flags_as & FLAS_ACT) printf(" AS_ACT"); + if (flags_as & FLAS_DEACT) printf(" AS_DEACT"); + if (flags_as & FLAS_CLEAR) printf(" AS_CLEAR"); + printf("\n\n"); + } +// printf("____________________________\n"); + if (flags & FL_LOCK) { +// dev->silent++; + switch (lock) { + case 0: + printf("Unlocking media...\n"); + dev->parms.status &= (~STATUS_LOCK); + break; + case 1: + printf("Locking media...\n"); + dev->parms.status |= STATUS_LOCK; + break; + case 2: + printf("Toggle media lock state...\n"); + get_lock(dev); + dev->parms.status ^= STATUS_LOCK; + break; + } + set_lock(dev); + printf("Media is%s locked\n", (dev->parms.status & STATUS_LOCK) ? "" : " NOT"); +// dev->silent--; + } + + if (flags & FL_LOEJ) { +// printf("loej_immed: %d\n",loej_immed); + if (eject == 2) { + load_eject(dev, loej_immed); + } else { + load_eject(dev,!eject, loej_immed); + } + } + +// PLEXTOR features + if (flags & FL_POWEREC) { +// printf("Set PoweREC...\n"); + dev->plextor.powerec_state = powerec; + plextor_set_powerec(dev); + } +/* + if (flags & FL_YMH_AMQR) { + dev->yamaha.amqr = amqr; + yamaha_set_amqr(dev); + } + if (flags & FL_YMH_FORCESPEED) { + dev->yamaha.forcespeed = forcespeed; + yamaha_set_forcespeed(dev); + } +*/ + if (flags & FL_GIGAREC) { +// printf("Set GigaREC...\n"); + dev->plextor.gigarec = gigarec; + plextor_set_gigarec(dev); + } + if (flags & FL_VARIREC_CD) { +// printf("Set VariREC CD...\n"); +// printf("PWR = %02X STR = %02X\n",varirec_cd_pwr, varirec_cd_str); + dev->plextor.varirec_state_cd = !(flags & FL_VARIREC_CD_OFF); + dev->plextor.varirec_pwr_cd = varirec_cd_pwr; + dev->plextor.varirec_str_cd = varirec_cd_str; + plextor_set_varirec(dev, VARIREC_CD); + } + if (flags & FL_VARIREC_DVD) { +// printf("Set VariREC DVD...\n"); +// printf("PWR = %02X STR = %02X\n",varirec_dvd_pwr, varirec_dvd_str); + dev->plextor.varirec_state_dvd = !(flags & FL_VARIREC_DVD_OFF); + dev->plextor.varirec_pwr_dvd = varirec_dvd_pwr; + dev->plextor.varirec_str_dvd = varirec_dvd_str; + plextor_set_varirec(dev, VARIREC_DVD); + } + if (flags & FL_NOSECUREC) { + plextor_set_securec(dev, 0, NULL); + } + else if (flags & FL_SECUREC) { + int pwdlen = (char)strlen(passwd); + if ((pwdlen>=4) && (pwdlen<=10)) + plextor_set_securec(dev, pwdlen, passwd); + else + printf("Invalid SecuRec password length! must be 4..10\n"); + } + if (flags & FL_HCDR) { + plextor_set_hidecdr(dev, hcdr); + } + if (flags & FL_SSS) { + plextor_set_singlesession(dev, sss); + } + if (flags & FL_SPDREAD) { + plextor_set_speedread(dev, spdread); + } + if (flags & FL_BOOK_R) { + dev->book_plus_r = bookr; + plextor_set_bitset(dev, PLEX_BITSET_R); + } + if (flags & FL_BOOK_RDL) { + dev->book_plus_rdl = bookrdl; + plextor_set_bitset(dev, PLEX_BITSET_RDL); + } + if (flags & FL_AS) { + dev->astrategy.state = as; + plextor_set_autostrategy(dev); + } + + if (flags & FL_MQCK) { + if (dev->media.type & ( DISC_CDROM | DISC_DDCD_ROM| DISC_DVDROM | DISC_BD_ROM | DISC_HDDVD_ROM)) { + printf("Can't run MQCK on stamped media!\n"); + return 5; + } + detect_speeds(dev); + + printf("Available write speeds:\n"); + printf("WR speed max: %4.1fX (%d kB/s)\n", + ((float)dev->parms.max_write_speed_kb) / dev->parms.speed_mult, + dev->parms.max_write_speed_kb); + for (int i=0; iparms.wr_speed_tbl_kb[i] > 0; i++) { + printf(" speed #%02d: %4.1fX (%d kB/s)\n", i, + ((float)dev->parms.wr_speed_tbl_kb[i]) / ((float)dev->parms.speed_mult), + dev->parms.wr_speed_tbl_kb[i]); + } + if (as_mqck >= 0) { + dev->parms.max_write_speed_kb = (int) (as_mqck_spd * dev->parms.speed_mult), + set_rw_speeds(dev); + get_rw_speeds(dev); + if ( !(dev->media.type & (DISC_DVD)) ) { + printf("MQCK: Media Quality Check supported on DVD media only!\n"); + return 5; + } + printf("Starting media check at speed %.1f X...\n", + ((float)dev->parms.max_write_speed_kb) / dev->parms.speed_mult); + if (plextor_media_check(dev, as_mqck)) { + if ((dev->err & 0x0FFF00) == 0x023A00) + printf("MQCK: No media found\n"); + else + printf("MQCK: Error starting Media Check\n"); + return 5; + } + if (!dev->rd_buf[0x11]) { + if ((dev->rd_buf[0x10] & 0xFF) == 0xFF) + printf("MQCK: Cant' run Media Check: AUTOSTRATEGY is OFF\n"); + else + printf("MQCK: Media is GOOD for writing at selected speed\n"); + } else { + switch (dev->rd_buf[0x10]) { + case 0x01: + printf("MQCK: Write error may occur\n"); + break; + case 0x03: + printf("MQCK: Drive may not write correctly at selected speed\n"); + break; + default: + printf("MQCK: Unknown MQCK error: 0x%02X\n", dev->rd_buf[0x10]); + } + } + } + } + + dev->silent--; + if (flags_as & FLAS_VIEW) { + plextor_get_autostrategy_db_entry_count(dev); + plextor_get_autostrategy_db(dev); + } + if (flags_as & FLAS_CLEAR) { + printf("deleting all AutoStrategy entries...\n"); + plextor_clear_autostrategy_db(dev); + } + if (flags_as & FLAS_ACT) { + printf("activating strategy #%d\n", as_idx_act); + plextor_modify_autostrategy_db(dev, as_idx_act, ASDB_ENABLE); + } + if (flags_as & FLAS_DEACT) { + printf("DEactivating strategy #%d\n", as_idx_deact); + plextor_modify_autostrategy_db(dev, as_idx_deact, ASDB_DISABLE); + } + if (flags_as & FLAS_DEL) { + printf("DELETING strategy #%d\n", as_idx_del); + plextor_modify_autostrategy_db(dev, as_idx_del, ASDB_DELETE); + } + if (flags_as & FLAS_CREATE) { + printf("AS: Creating new strategy, mode: %s, action: %s...\n", + (ascre & ASDB_CRE_FULL) == ASDB_CRE_FULL? "FULL" : "QUICK", + (ascre & ASDB_ADD) == ASDB_ADD? "ADD" : "REPLACE"); + plextor_create_strategy(dev, ascre); + } + if (flags_as & FLAS_RETR) { + unsigned char hdr[9]; memset(hdr, 0, 9); + printf("AS RETR...\n"); + plextor_get_strategy(dev); + printf("Saving AS DB...\n"); + asf = fopen(assfn,"wb"); + if (!asf) { printf("can't create asdb file!\n"); return 6; } + memcpy(hdr,"ASDB ",5); + hdr[5] = dev->astrategy.dbcnt; + fwrite((void*)hdr,1,8,asf); + for (i=0; iastrategy.dbcnt; i++) { + fwrite((void*)&dev->astrategy.entry[i],1,0x20,asf); + for (int j=0; j<7; j++) + fwrite((void*)&dev->astrategy.entry_data[i][j],1,0x20,asf); + } + fclose(asf); + } + if (flags_as & FLAS_STOR) { + unsigned char hdr[9]; memset(hdr, 0, 9); + printf("AS STOR...\n"); + + printf("Loading AS DB...\n"); + asf = fopen(aslfn,"rb"); + if (!asf) + { printf("can't open asdb file!\n"); return 6; } + if (fread((void*)hdr,1,8,asf)<8) + { printf("error reading asdb file!\n"); return 6; } + + if (!strncmp((char*)hdr,"ASDB ",5)) + dev->astrategy.dbcnt = hdr[5]; + i=0; + while (!feof(asf) && iastrategy.dbcnt) { + if (fread((void*)&dev->astrategy.entry[i],1,0x20,asf)<0x20) + { printf("error reading asdb file!\n"); return 6; } + for (int j=0; j<7; j++) + if (fread((void*)&dev->astrategy.entry_data[i][j],1,0x20,asf)<0x20) + { printf("error reading asdb file!\n"); return 6; } + + i++; + } + fclose(asf); + if (dev->astrategy.dbcnt != i) { + printf("Can't read all strategies! File corrupted!\n"); + return 5; + } + printf("%d strategies loaded, sending to drive...\n",dev->astrategy.dbcnt); + plextor_add_strategy(dev); + } + if (flags_as & (FLAS_ACT | FLAS_DEACT | FLAS_DEL | FLAS_CREATE | FLAS_STOR | FLAS_CLEAR)) { + printf("AutoStrategy DB modified...\n"); + plextor_get_autostrategy_db_entry_count(dev); + plextor_get_autostrategy_db(dev); + } +dev->silent++; + if (flags & FL_TESTWRITE) { + dev->plextor.testwrite_dvdplus = testwrite; + plextor_set_testwrite_dvdplus(dev); + } + if (flags & FL_SILENT) { + plextor_get_silentmode(dev); + if (!silent) + plextor_set_silentmode_disable(dev, silent_save); + else { + if (!dev->plextor_silent.state) + { silent_cd = 1; silent_dvd = 1; silent_tray = 1; } + if (silent_cd) { + dev->plextor_silent.access = silent_access; + dev->plextor_silent.rd = silent_cd_rd; + dev->plextor_silent.wr = silent_cd_wr; + plextor_set_silentmode_disc(dev, SILENT_CD, silent_save); + } + if (silent_dvd) { + dev->plextor_silent.access = silent_access; + dev->plextor_silent.rd = silent_dvd_rd; +// dev->plextor_silent.wr = silent_dvd_wr; + plextor_set_silentmode_disc(dev, SILENT_DVD, silent_save); + } + if (silent_tray) { + dev->plextor_silent.load = silent_load; + dev->plextor_silent.eject = silent_eject; + plextor_set_silentmode_tray(dev, SILENT_CD, silent_save); + } + } + } + if (flags & FL_PXERASER) { + dev->plextor.plexeraser = pxeraser; + plextor_plexeraser(dev); + } + +// PIONEER features + if (flags & FL_PIOQUIET) { + pioneer_set_silent(dev, piosilent, silent_save); + } + if (flags & FL_PIOLIMIT) { + pioneer_set_spdlim(dev, piolimit, silent_save); + } + + get_device_info(dev); + if (!(flags & FL_VERBOSE)) dev->silent--; + delete dev; + return 0; +} diff --git a/console/cdvdcontrol/version.h b/console/cdvdcontrol/version.h new file mode 100644 index 0000000..69ef112 --- /dev/null +++ b/console/cdvdcontrol/version.h @@ -0,0 +1,12 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2009 Gennady "ShultZ" Kozlov + * + * 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. + */ + +#define VERSION "1.4" diff --git a/console/f1tattoo/Makefile b/console/f1tattoo/Makefile new file mode 100644 index 0000000..f600e41 --- /dev/null +++ b/console/f1tattoo/Makefile @@ -0,0 +1,25 @@ +SRCS = $(patsubst %,%.cpp, f1tattoo) +OBJS = $(patsubst %.cpp,%.o,$(SRCS)) + +BIN = f1tattoo$(BINSUFF) + +CXXFLAGS += -I. -I../../lib/include $(LPNG_INC) +CFLAGS += -I. -I../../lib/include $(LPNG_INC) +LDLIBS += -L../../lib/lib -lqpxtransport -lqpxyamaha $(LPNG_LIB) + +$(BIN): $(OBJS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) + +all:$(BIN) + +clean: + rm -f $(BIN) $(OBJS) *~ + +install: + mkdir -p $(DESTDIR)$(BINDIR) + install -m 4755 $(BIN) $(DESTDIR)$(BINDIR) + +uninstall: + rm -f $(DESTDIR)$(BINDIR)/$(BIN) + +.PHONY: all clean install uninstall diff --git a/console/f1tattoo/f1tattoo.cpp b/console/f1tattoo/f1tattoo.cpp new file mode 100644 index 0000000..92bced2 --- /dev/null +++ b/console/f1tattoo/f1tattoo.cpp @@ -0,0 +1,501 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2006,2009 Gennady "ShultZ" Kozlov + * + * 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 +#include + +#include +#include + +#ifdef USE_LIBPNG +#include +#endif + +#include "version.h" + +const uint32_t FL_HELP = 0x00000001; +const uint32_t FL_SCAN = 0x00000002; +const uint32_t FL_DEVICE = 0x00000004; +const uint32_t FL_VERBOSE = 0x00000008; +const uint32_t FL_CURRENT = 0x00000010; +const uint32_t FL_SUPPORTED = 0x00000020; +const uint32_t FL_TATTOO_TEST = 0x00000040; +const uint32_t FL_TATTOO_RAW = 0x00000080; +#ifdef USE_LIBPNG +const uint32_t FL_TATTOO_PNG = 0x00000100; +const uint32_t FL_TATTOO = FL_TATTOO_RAW | FL_TATTOO_PNG | FL_TATTOO_TEST; +#else +const uint32_t FL_TATTOO = FL_TATTOO_RAW | FL_TATTOO_TEST; +#endif + +uint32_t flags = 0; + +int get_device_info(drive_info* drive) +{ + drive->ven_features=0; + drive->chk_features=0; + detect_capabilities(drive); +// detect_check_capabilities(drive); + determine_disc_type(drive); + if (!isYamaha(drive)) { + printf ("%s: drive not supported\n", drive->device); + return 1; + } +// if (!yamaha_check_amqr(drive)) drive->ven_features|=YMH_AMQR; +// if (!yamaha_check_forcespeed(drive)) drive->ven_features|=YMH_FORCESPEED; + if (!yamaha_f1_get_tattoo(drive)) drive->ven_features|=YMH_TATTOO; + + if (flags & FL_SUPPORTED) { + printf("\n** Supported features:\n"); +// printf("AudioMaster Q.R. : %s\n", drive->ven_features & YMH_AMQR ? "YES" : "---"); +// printf("ForceSpeed : %s\n", drive->ven_features & YMH_FORCESPEED ? "YES" : "---"); + printf("DiscT@2 : %s\n", drive->ven_features & YMH_TATTOO ? "YES" : "---"); + } + + if (flags & FL_CURRENT) { + printf("\n** Current drive settings:\n"); + } + if ((flags & (FL_CURRENT | FL_TATTOO | FL_TATTOO_TEST)) && (drive->ven_features & YMH_TATTOO)) { + if (drive->yamaha.tattoo_rows) { + printf("DiscT@2 info:\ninner: %d\nouter: %d\nimage: 3744x%d\n", + drive->yamaha.tattoo_i, + drive->yamaha.tattoo_o, + drive->yamaha.tattoo_rows); + } else { + if (drive->media.type & DISC_CD) + printf("Can't write DiscT@2 on inserted disc!\n"); + else + printf("No disc found! Can't get DiscT@2 info!\n"); + } + } + return 0; +} + +#ifdef USE_LIBPNG +static int my_png_get_image_width(png_structp png_ptr, png_infop info_ptr) { +#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4 + return png_get_image_width(png_ptr, info_ptr); +#else + return info_ptr->width; +#endif +} + +static int my_png_get_image_height(png_structp png_ptr, png_infop info_ptr) { +#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4 + return png_get_image_height(png_ptr, info_ptr); +#else + return info_ptr->height; +#endif +} + +static png_byte my_png_get_color_type(png_structp png_ptr, png_infop info_ptr) +{ +#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4 + return png_get_color_type(png_ptr, info_ptr); +#else + return info_ptr->color_type; +#endif +} + +static png_uint_32 my_png_get_valid(png_structp png_ptr, png_infop info_ptr, png_uint_32 flags) +{ +#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4 + return png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE); +#else + return (info_ptr->valid & flags); +#endif +} + +static int my_png_get_bit_depth(png_structp png_ptr, png_infop info_ptr) +{ +#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4 + return png_get_bit_depth(png_ptr, info_ptr); +#else + return info_ptr->bit_depth; +#endif +} + +static int my_png_get_rowbytes(png_structp png_ptr, png_infop info_ptr) +{ +#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4 + return png_get_rowbytes(png_ptr, info_ptr); +#else + return info_ptr->rowbytes; +#endif +} + +bool tattoo_read_png(unsigned char *buf, uint32_t rows, FILE *fp) +{ + png_byte header[8]; // 8 is the maximum size that can be checked + png_structp png_ptr; + png_infop info_ptr; + uint32_t number_of_passes; + png_bytep png_row_pointer = NULL; + unsigned char *raw_row_pointer; +// unsigned char *tp = NULL; + +// int width; + uint32_t row, col; + int c; + int32_t r,g,b; + int num_palette; +#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4 + png_colorp palette; +#endif + + if (fread(header, 1, 8, fp) < 8) { + printf("Error reading PNG header\n"); + fclose(fp); + return 1; + } + if (png_sig_cmp(header, 0, 8)) { + printf("File not recognized as a PNG\n"); + fclose(fp); + return 1; + } + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + printf("png_create_read_struct failed!\n"); + fclose(fp); + return 1; + } + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + printf("png_create_info_struct failed!\n"); + fclose(fp); + return 1; + } + if (setjmp(png_jmpbuf(png_ptr))) { + printf("png_jmpbuf failed!\n"); + fclose(fp); + return 1; + } + + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, 8); + + png_read_info(png_ptr, info_ptr); + + printf("Image size: %ld x %ld\n", + long(my_png_get_image_width(png_ptr, info_ptr)), + long(my_png_get_image_height(png_ptr, info_ptr))); + + if (my_png_get_image_width(png_ptr, info_ptr) != 3744U || my_png_get_image_height(png_ptr, info_ptr) != rows ) { + printf("Image should be 3744 x %ld", long(rows)); + return 1; + } + +// width = info_ptr->width; +// height = info_ptr->height; +// bit_depth = info_ptr->bit_depth; + +#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4 + number_of_passes = png_set_interlace_handling(png_ptr); +#else + number_of_passes = png_set_interlace_handling(png_ptr); +#endif + png_read_update_info(png_ptr, info_ptr); + + printf("Color type: [%d] ", my_png_get_color_type(png_ptr, info_ptr)); + switch (my_png_get_color_type(png_ptr, info_ptr)) { + case PNG_COLOR_TYPE_GRAY: + printf("PNG_COLOR_TYPE_GRAY\n"); + break; + case PNG_COLOR_TYPE_PALETTE: + printf("PNG_COLOR_TYPE_PALETTE\n"); + if (!(my_png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE))) { + printf("PNG color type is indexed, but no palette found!"); + goto err_read_png; + } + break; + case PNG_COLOR_TYPE_RGB: + printf("PNG_COLOR_TYPE_RGB\n"); + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + printf("PNG_COLOR_TYPE_RGB_ALPHA\n"); + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + printf("PNG_COLOR_TYPE_GRAY_ALPHA\n"); + break; + default: + printf("unlnown PNG color type!\n"); + goto err_read_png; + } + printf("Bit depth : %d\n", my_png_get_bit_depth(png_ptr, info_ptr)); + if (my_png_get_bit_depth(png_ptr, info_ptr) != 8) { + printf("Unsupported bit depth!\n"); + goto err_read_png; + } + +#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4 + png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); +#else + num_palette = info_ptr->num_palette; +#endif + if (my_png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)) { + printf("Palette : %d colors\n", num_palette); + } else { + printf("Palette : NO\n"); + } + printf("ROW bytes : %ld\n", long(my_png_get_rowbytes(png_ptr, info_ptr))); + + + raw_row_pointer = buf; + png_row_pointer = (png_byte*) malloc(my_png_get_rowbytes(png_ptr, info_ptr)); + for (row=0; row= 4 + r = palette[c].red; + g = palette[c].green; + b = palette[c].blue; +#else + r = info_ptr->palette[c].red; + g = info_ptr->palette[c].green; + b = info_ptr->palette[c].blue; +#endif + c = (r*11 + g*16 + b*5) / 32; + raw_row_pointer[col] = c ^ 0xFF; + } +#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4 + png_set_PLTE(png_ptr, info_ptr, palette, num_palette); +#endif + break; + case PNG_COLOR_TYPE_RGB: + for (col=0; col< my_png_get_image_width(png_ptr, info_ptr); col++) { + r = png_row_pointer[col*3]; + g = png_row_pointer[col*3+1]; + b = png_row_pointer[col*3+2]; + c = (r*11 + g*16 + b*5) / 32; + raw_row_pointer[col] = c ^ 0xFF; + } + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + for (col=0; col burn selected RAW image as DiscT@2\n"); + printf("\t--tattoo-png burn selected PNG image as DiscT@2\n"); +#ifdef USE_LIBPNG + printf("\t WARNING: f1tattoo compiled without libpng\n"); +#endif + printf("\t--tattoo-test burn tattoo test image\n"); + printf("\t-v, --verbose be verbose\n"); +} + +int main(int argc, char* argv[]) +{ + int i; + int drvcnt=0; + char *device = NULL; + char *tattoofn = NULL; + unsigned char *tattoobuf = NULL; + FILE *tattoof; + drive_info* drive; + bool fr=0; + + printf("** DiscT@2 writer for Yamaha CRW-F1 v%s (c) 2005-2006,2009 Gennady \"ShultZ\" Kozlov **\n", VERSION); + + for (i=1; i(i+1)) { + i++; + flags |= FL_DEVICE; + device = argv[i]; + } else { + printf("Option %s needs parameter\n",argv[i]); + exit (1); + } + } + else if (!strcmp(argv[i],"-h")) flags |= FL_HELP; + else if(!strcmp(argv[i],"--help")) flags |= FL_HELP; + else if(!strcmp(argv[i],"-c")) flags |= FL_CURRENT; + else if(!strcmp(argv[i],"--current")) flags |= FL_CURRENT; + else if(!strcmp(argv[i],"-l")) flags |= FL_SCAN; + else if(!strcmp(argv[i],"--scanbus")) flags |= FL_SCAN; + else if(!strcmp(argv[i],"-s")) flags |= FL_SUPPORTED; + else if(!strcmp(argv[i],"--supported")) flags |= FL_SUPPORTED; + else if(!strcmp(argv[i],"-v")) flags |= FL_VERBOSE; + else if(!strcmp(argv[i],"--verbose")) flags |= FL_VERBOSE; + else if(!strcmp(argv[i],"--tattoo-test")) flags |= FL_TATTOO_TEST; + else if(!strcmp(argv[i],"--tattoo-raw")) { + flags |= FL_TATTOO_RAW; + if (argc>(i+1)) { + i++; + tattoofn = argv[i]; + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } + } else if(!strcmp(argv[i],"--tattoo-png")) { +#ifdef USE_LIBPNG + flags |= FL_TATTOO_PNG; + if (argc>(i+1)) { + i++; + tattoofn = argv[i]; + } else { + printf("option %s needs parameter!\n", argv[i]); + return 5; + } +#else + printf("Can't use PNG as input file: compiled without libpng"); +#endif + } else { + printf("Illegal option: %s.\nUse -h for details\n",argv[i]); + return 6; + } + } + + if (flags & FL_HELP) { + usage(argv[0]); + return 0; + } + if (!flags) { + usage(argv[0]); + return 1; + } + if (flags & FL_SCAN) { + drvcnt = scanbus(DEV_YAMAHA); + if (!drvcnt) printf("ERR: no drives found!\n"); + return 2; + } + if (!(flags & FL_DEVICE)) { + printf("** ERR: no device selected\n"); + return 3; + } + +// printf("____________________________\n"); + printf("Device : %s\n", device); + drive = new drive_info(device); + if (drive->err) { + printf("%s: device open error!\n", argv[0]); + delete drive; + return 4; + } + inquiry(drive); +// convert_to_ID(drive); + printf("Vendor : '%s'\n",drive->ven); + printf("Model : '%s'\n",drive->dev); + printf("F/W : '%s'\n",drive->fw); + if (!(flags & FL_VERBOSE)) drive->silent++; + if (get_drive_serial_number(drive)) printf("Serial#: %s\n",drive->serial); + + if (flags) { +// if (flags & FL_VERBOSE) { + printf("\nf1tattoo flags : "); + if (flags & FL_DEVICE) printf(" DEVICE"); + if (flags & FL_HELP) printf(" HELP"); + if (flags & FL_CURRENT) printf(" CURRENT"); + if (flags & FL_SCAN) printf(" SCAN"); + if (flags & FL_VERBOSE) printf(" VERBOSE"); + if (flags & FL_SUPPORTED) printf(" SUPPORTED"); + if (flags & FL_TATTOO) printf(" TATTOO"); + if (flags & FL_TATTOO_TEST) printf(" TATTOO_TEST"); + printf("\n\n"); + } + get_device_info(drive); +// printf("____________________________\n"); + + if (flags & FL_TATTOO) { + if (!(drive->ven_features & YMH_TATTOO)) { + printf("Selected device doesn't have DiscT@2 feature!\n"); + delete drive; + return 1; + } + if (flags & FL_TATTOO_TEST) { + printf("%s: writing T@2 test image...\n", device); + yamaha_f1_do_tattoo(drive, NULL, 0); + } else { + tattoof = fopen(tattoofn, "r"); + if (!tattoof) { + printf("Can't open tattoo file: %s", tattoofn); + } else { + printf("Reading tattoo file...\n"); + tattoobuf = (unsigned char*) malloc (drive->yamaha.tattoo_rows * 3744); +#ifdef USE_LIBPNG + if (flags & FL_TATTOO_PNG) { + fr = tattoo_read_png(tattoobuf, drive->yamaha.tattoo_rows, tattoof); + } else { +#endif + memset(tattoobuf, 0, drive->yamaha.tattoo_rows * 3744); + fr = (fread((void*)tattoobuf, 3744, drive->yamaha.tattoo_rows, tattoof) > 0); +#ifdef USE_LIBPNG + } +#endif + fclose(tattoof); + if (fr) { + yamaha_f1_do_tattoo(drive, tattoobuf, fr * 3744); + } else { + printf("Error reading T@2 image!\n"); + } + free(tattoobuf); + } + } + } + if (!(flags & FL_VERBOSE)) drive->silent--; + delete drive; + return 0; +} + diff --git a/console/f1tattoo/version.h b/console/f1tattoo/version.h new file mode 100644 index 0000000..1b50b5f --- /dev/null +++ b/console/f1tattoo/version.h @@ -0,0 +1,12 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2009 Gennady "ShultZ" Kozlov + * + * 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. + */ + +#define VERSION "0.7.1" diff --git a/console/pxfw/Makefile b/console/pxfw/Makefile new file mode 100644 index 0000000..4fe932f --- /dev/null +++ b/console/pxfw/Makefile @@ -0,0 +1,25 @@ +SRCS = $(patsubst %,%.cpp, pxfw) +OBJS = $(patsubst %.cpp,%.o,$(SRCS)) + +BIN = pxfw$(BINSUFF) + +CXXFLAGS += -I. -I../../lib/include +CFLAGS += -I. -I../../lib/include +LDLIBS += -L../../lib/lib -lqpxtransport -lqpxplextor + +$(BIN): $(OBJS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) + +all:$(BIN) + +clean: + rm -f $(BIN) $(OBJS) *~ + +install: + mkdir -p $(DESTDIR)$(SBINDIR) + install -m 755 $(BIN) $(DESTDIR)$(SBINDIR) + +uninstall: + rm -f $(DESTDIR)$(SBINDIR)/$(BIN) + +.PHONY: all clean install uninstall diff --git a/console/pxfw/pxfw.cpp b/console/pxfw/pxfw.cpp new file mode 100644 index 0000000..7a8bfb8 --- /dev/null +++ b/console/pxfw/pxfw.cpp @@ -0,0 +1,654 @@ +/* + * This file is a part of QPxTool project + * Copyright (C) 2006-2009, Gennady "ShultZ" Kozlov + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +// const unsigned char op_blacklist[]={ 0xAC, 0xDE, 0xDF , 0xF8 }; + +const unsigned char op_blacklist[]={ 0xDE, 0xDF, 0xF8 }; // used in TEST mode +const int op_blacklist_sz=sizeof(op_blacklist); + +int op_blacklisted(unsigned char opcode){ + if (op_blacklist_sz) for (int i=0; i +#include +#include +#include + +#include + +#include +#include + +#include + +#include "pxfw.h" + +#include "version.h" + +long fsize(FILE* f){ + struct stat st; + fstat(fileno(f),&st); + return st.st_size; +} + + +int custom_command (drive_info* dev, unsigned char opcode) { + dev->cmd[0]=opcode; + if ((dev->err=dev->cmd.transport(NONE,NULL,0))) return dev->err; + return 0; +} + +void FW_convert_to_ID (int* dev_ID, char* buf, int* FWSZ_CRC) { + *FWSZ_CRC=0; + if (!strncmp(buf,"PLEXTOR CD-R PX-W4824A",24)) { + *dev_ID=PLEXTOR_4824; + *FWSZ_CRC=0x79FFE; + } else + if (!strncmp(buf,"PLEXTOR CD-R PX-W5224A",24)) { + *dev_ID=PLEXTOR_5224; + *FWSZ_CRC=0x79FFE; + } else + if (!strncmp(buf,"PLEXTOR CD-R PREMIUM2",23)) { + *dev_ID=PLEXTOR_PREMIUM2; + *FWSZ_CRC=0xEFFFE; + } else + if (!strncmp(buf,"PLEXTOR CD-R PREMIUM",22)) { + *dev_ID=PLEXTOR_PREMIUM; + *FWSZ_CRC=0x7C7FE; + } else + if (!strncmp(buf,"PLEXTOR DVDR PX-708A",22)) { + *dev_ID=PLEXTOR_708; + *FWSZ_CRC=0xEFFFE; + } else + if (!strncmp(buf,"PLEXTOR DVDR PX-712A",22)) { + *dev_ID=PLEXTOR_712 | PLEXTOR_708A2; + *FWSZ_CRC=0xEFFFE; +// *FWSZ_CRC=0xFAD9E; +// *FWSZ_CRC=0x0F0000; + } else + if (!strncmp(buf,"PLEXTOR DVDR PX-716A ",23)) { + *dev_ID=PLEXTOR_716 | PLEXTOR_714; + *FWSZ_CRC=0xEFFFE; + } else + if (!strncmp(buf,"PLEXTOR DVDR PX-716AL",23)) { + *dev_ID=PLEXTOR_716AL; + *FWSZ_CRC=0xEFFFE; + } else + if (!strncmp(buf,"PLEXTOR DVDR PX-760A",22)) { + *dev_ID=PLEXTOR_760 | PLEXTOR_755; + *FWSZ_CRC=0x1EFFFE; + } else + *dev_ID = 0; +} + +void PLEXTOR_convert_to_ID (drive_info* dev, int* FWSZ) { + *FWSZ=0; + if (!strncmp(dev->ven,"PLEXTOR ",8)) { + dev->ven_ID=DEV_PLEXTOR; + if(!strncmp(dev->dev,"CD-R PX-W4824A",16)) { + dev->dev_ID=PLEXTOR_4824; + *FWSZ=524288; + } else + if(!strncmp(dev->dev,"CD-R PX-W5224A",16)) { + dev->dev_ID=PLEXTOR_5224; + *FWSZ=524288; + } else + if(!strncmp(dev->dev,"CD-R PREMIUM ",15)) { + dev->dev_ID=PLEXTOR_PREMIUM; + *FWSZ=524288; + } else + if(!strncmp(dev->dev,"CD-R PREMIUM2",15)) { + dev->dev_ID=PLEXTOR_PREMIUM2; + *FWSZ=983040; + } else + if(!strncmp(dev->dev,"DVDR PX-708A ",15)) { + dev->dev_ID=PLEXTOR_708; + *FWSZ=983040; + } else + if(!strncmp(dev->dev,"DVDR PX-708A2",15)) { + dev->dev_ID=PLEXTOR_708A2; + *FWSZ=1028096; + } else + if(!strncmp(dev->dev,"DVDR PX-712A",14)) { + dev->dev_ID=PLEXTOR_712; + *FWSZ=1028096; + } else + if(!strncmp(dev->dev,"DVDR PX-714A",14)) { + dev->dev_ID=PLEXTOR_714; + *FWSZ=983040; + } else + if(!strncmp(dev->dev,"DVDR PX-716A ",15)) { + dev->dev_ID=PLEXTOR_716; + *FWSZ=983040; + } else + if(!strncmp(dev->dev,"DVDR PX-716AL",15)) { + dev->dev_ID=PLEXTOR_716AL; + *FWSZ=983040; + } else + if(!strncmp(dev->dev,"DVDR PX-755A",14)) { + dev->dev_ID=PLEXTOR_755; + *FWSZ=2031616; + } else + if(!strncmp(dev->dev,"DVDR PX-760A",14)) { + dev->dev_ID=PLEXTOR_760; + *FWSZ=2031616; + } else + dev->dev_ID=PLEXTOR_OLD; + } +} + +/* +int plextor_read_eeprom(drive_info* dev, unsigned char idx, unsigned int sz) { +// char* data; + unsigned int i,j; + int offs=idx*sz; + unsigned char* buf=dev->rd_buf+offs; + + dev->cmd[0] = 0xF1; + dev->cmd[1] = 0x01; + dev->cmd[7] = idx; + dev->cmd[8] = (sz >> 8) & 0xFF; + dev->cmd[9] = sz & 0xFF; + if ((dev->err=dev->cmd.transport(READ,buf,sz) )) + { sperror ("read EEPROM",dev->err); return (0); } + + printf("EEPROM block #%d:\n",idx); + for(i=0;i<(sz/0x10);i++) { + printf("| %X0 | ", i); + for(j=0;j<0x10;j++) printf("%02X ",buf[i*0x10+j]); + printf("|"); + for(j=0;j<0x10;j++) { + if (buf[i*0x10+j] > 0x20) printf("%c",buf[i*0x10+j]); + else printf(" "); + } + printf("|\n"); + }; + return 1; +} +*/ + +int fwblk_send(drive_info* dev, int offs, int blksz, bool last) { + dev->cmd[0] = 0x3B; + dev->cmd[1] = last ? 0x05:0x04; + dev->cmd[2] = 0x00; + dev->cmd[3] = (offs >> 16) & 0xFF; + dev->cmd[4] = (offs >> 8) & 0xFF; + dev->cmd[5] = offs & 0xFF; + dev->cmd[6] = (blksz >> 16) & 0xFF; + dev->cmd[7] = (blksz >> 8) & 0xFF; + dev->cmd[8] = blksz & 0xFF; + dev->cmd[9] = 0x00; + dev->cmd[10]= 0x00; + dev->cmd[11]= 0x00; + + if ((dev->err=dev->cmd.transport(WRITE,dev->rd_buf,blksz) )){ + sperror ("SEND_FWBLK",dev->err); + if (!last) return 1; + } + return 0; +} + + +int fwblk_read(drive_info* dev, int offs, int blksz, unsigned char flag) { + printf("data read...\n"); + return 1; +} + +/* + dev->cmd[0] = 0x3B; +// cmd[1] = last ? 0x05:0x04; + dev->cmd[1] = flag; + + dev->cmd[2] = 0x00; + dev->cmd[3] = (offs >> 16) & 0xFF; + dev->cmd[4] = (offs >> 8) & 0xFF; + dev->cmd[5] = offs & 0xFF; + dev->cmd[6] = (blksz >> 16) & 0xFF; + dev->cmd[7] = (blksz >> 8) & 0xFF; + dev->cmd[8] = blksz & 0xFF; + dev->cmd[9] = 0x00; + dev->cmd[10]= 0x00; + dev->cmd[11]= 0x00; + + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,blksz) )){ + sperror ("READ_FWBLK",dev->err); + return 1; + } + return 0; +} +*/ + +void csum_init(unsigned short int *csum, int FW_dev_ID){ + *csum = 0; + if (FW_dev_ID <= PLEXTOR_PREMIUM) *csum = 0x8000; +} + +void csum_update(unsigned short int *csum, unsigned char* buf, int len) { + int i; + unsigned int ad; + for (i=0; i1) { +// printf("Parsing additional options...\n"); + for (i=1; i(i+1)) { + i++; + flags |= FL_DEV; + devname = argv[i]; + } else { + printf("Option %s needs parameter\n",argv[i]); + exit (1); + } + } + else if(!strcmp(argv[i],"-if")) { + if(argc>(i+1)) { + i++; + flags |= FL_FWIF; + fwfname = argv[i]; + } else { + printf("Option %s needs parameter\n",argv[i]); + exit (1); + } + } + else if(!strcmp(argv[i],"-oe")) { + if(argc>(i+1)) { + i++; + flags |= FL_EEOF; + eefname = argv[i]; + } else { + printf("Option %s needs parameter\n",argv[i]); + exit (1); + } + } + else if(!strcmp(argv[i],"-h")) flags |= FL_HELP; + else if(!strcmp(argv[i],"-l")) flags |= FL_SCAN; + else if(!strcmp(argv[i],"-u")) flags |= FL_UPDATE; +// else if(!strcmp(argv[i],"-b")) flags |= FL_BACKUP; + else if(!strcmp(argv[i],"-v")) flags |= FL_DEBUG; + else if(!strcmp(argv[i],"-t")) flags |= FL_TEST; + else if(!strcmp(argv[i],"-e")) flags |= FL_EEPROM; + else if(!strcmp(argv[i],"-r")) flags |= FL_RESET; + else if(!strcmp(argv[i],"-f")) flags |= FL_FORCE; + else { + printf("Illegal option: %s.\nUse -h for details\n",argv[i]); +// usage(argv[0]); + exit (1); + } + } + } + if (flags & FL_DEBUG) { + printf("Flags: "); + if (flags & FL_DEV) printf(" DEV"); + if (flags & FL_FWIF) printf(" FWIF"); + if (flags & FL_HELP) printf(" HELP"); + if (flags & FL_UPDATE) printf(" UPDATE"); + if (flags & FL_BACKUP) printf(" BACKUP"); + if (flags & FL_DEBUG) printf(" DEBUG"); + if (flags & FL_TEST) printf(" TEST"); + if (flags & FL_EEPROM) printf(" EEPROM"); + if (flags & FL_RESET) printf(" RESET"); + } + if (flags & FL_HELP) { + usage(argv[0]); + exit (0); + } + if (flags & FL_SCAN) { + scanbus(); + exit (0); + } + if (!(flags & FL_DEV)) { + printf("\n*** No device selected! ***\n\n"); + usage(argv[0]); + scanbus(); + exit (3); + } + + dev = new drive_info(devname); + if (dev->err) { + printf("%s: can't open device: %s\n", argv[0], devname); + delete dev; + return 4; + } + + + printf("\nDevice : %s\n",devname); + inquiry(dev); + PLEXTOR_convert_to_ID(dev,&fwsz); +// dev->silent++; +// detect_mm_capabilities(dev); +// dev->silent--; + + printf("Vendor : '%s'\n",dev->ven); + printf("Model : '%s'", dev->dev); + if ( isPlextor(dev) ) { +// if ( dev->ven_ID == DEV_PLEXTOR ) { + plextor_get_TLA(dev); + printf(" (TLA#%s)",dev->TLA); + } + printf("\nF/W : '%s'\n",dev->fw); + if (get_drive_serial_number(dev)) + printf("Serial#: %s\n",dev->serial); + if ((dev->ven_ID != DEV_PLEXTOR) && (!(flags & FL_FORCE))){ + printf("%s: Only PLEXTOR drives supported!\n",argv[0]); + return 1; + } else { + if ((dev->dev_ID == PLEXTOR_OLD)){ + printf("%s: Not supported PLEXTOR drive!\n",argv[0]); + return 1; + } + } +// plextor_get_life(dev); + if (flags & FL_RESET) { + plextor_reboot (dev); + exit (0); + } + + if (flags & FL_EEPROM) { + int eelen; +// unsigned char buf[1024]; + plextor_read_eeprom(dev, &eelen); + if (flags & FL_EEOF) { + eefile = fopen(eefname,"w"); + if (!eefile) { + printf("%s: Can't write file: %s\n",argv[0],eefname); + exit (2); + } + fwrite(dev->rd_buf, eelen, 1, eefile); + fclose(eefile); + } + exit (0); + } + + if (flags & FL_TEST) { + for (unsigned int opcode=0x0000; opcode<0x0100; opcode++) { + printf("Trying OpCode: "); + print_opcode((unsigned char)opcode); + if (!op_blacklisted((unsigned char)opcode)) { + int err = custom_command(dev, (opcode & 0x00FF)); + if (!err) printf ("OK"); + else if (err==-1) printf ("command didn't passed to dev"); + else if (err==0x52000) printf ("*** command not supported ***"); + else print_sense(err); + } else { + printf("!!! BLACK LIST !!!"); + } + printf("\n"); + } + exit(0); + } + + if (!(flags & FL_FWIF)) exit(3); + + fwfile = fopen(fwfname, "r"); + if (!fwfile) { + printf("%s: Can't open file: %s\n",argv[0],fwfname); + return 1; + } + +// fwblk=4096; + const int FB=64; + const int HH=16; + fwfsz=fsize(fwfile); + if (!fread(dev->rd_buf, FB, 1, fwfile)) { + printf("%s: error reading file: %s\n", argv[0], fwfname); + return 1; + } + + printf("this dev FW size : %7d (%06X)\n",fwsz,fwsz); + printf("FW file size : %7d (%06X)\n",fwfsz,fwfsz); + printf("First %d bytes of FW:\n", FB); + + for (i=0; i<(FB/HH); i++){ + printf("| "); + for (j=0; jrd_buf[i*HH+j] & 0xFF); + printf(" | "); + for (j=0; jrd_buf[i*HH+j] & 0xFF) > 0x20) + printf("%c",dev->rd_buf[i*HH+j] & 0xFF); + else + printf(" "); + printf(" |\n"); + } + FW_convert_to_ID(&FW_dev_ID,(char*) dev->rd_buf,&crc_offs); +// if (FW_dev_ID & (PLEXTOR_PREMIUM | PLEXTOR_PREMIUM2)) + if (FW_dev_ID & (PLEXTOR_5224 | PLEXTOR_4824)) + fwblk = 16384; + else + fwblk = 4096; + if (flags & FL_BACKUP) { + printf("BackUp feature not implemented yet...\n"); + return 1; + + + for (i=0; ird_buf[i]=0; + i=0; + if ((err = fwblk_read(dev, i*fwblk, fwblk, 2))) { + printf("** error reading data\n"); + }; + + printf("First %d bytes of FW:\n", FB); + for (i=0; i<(FB/HH); i++){ + printf("| "); + for (j=0; jrd_buf[i*HH+j] & 0xFF); + printf(" | "); + for (j=0; jrd_buf[i*HH+j] & 0xFF) > 0x20) + printf("%c", dev->rd_buf[i*HH+j] & 0xFF); + else + printf(" "); + printf(" |\n"); + } + } + + fseek(fwfile,0,SEEK_SET); + last=0; +// printf("fwblocks = %d\n",fwblocks); + fwblocks=fwfsz/fwblk; + fwblocks_crc=(crc_offs+2)/fwblk_crc; + +#if 0 + csum_init(&CSUM, FW_dev_ID); + for (i=0;ird_buf, fwblk_crc, 1, fwfile); + if (i == (fwblocks_crc-1)) last=1; + if (last) { + blen = fwblk_crc - 2; + crca = i*fwblk_crc+blen; + fCSUM = (dev->rd_buf[fwblk_crc-2] << 8) | dev->rd_buf[fwblk_crc-1]; + printf("blk offs: %06X, last: %06X\n", fwblk_crc*i, fwblk_crc*i+blen); + } else blen = fwblk_crc; +// printf("Offset %04X, block # %X\n",i*fwblk,i); +// printf ("%02X: %4dB CRC=%04X\n",i,blen,CRC16blk); + csum_update(&CSUM, dev->rd_buf, blen); + } +#else + unsigned char *fw = (unsigned char*) malloc(fwfsz); + printf("FW buffer @%p\n", fw); + if (!fread(fw, fwfsz, 1, fwfile)) { + printf("%s: error reading file: %s\n", argv[0], fwfname); + return 1; + } + +/* + for (int of=2; ofdev_ID & FW_dev_ID)) { + printf("FW is not for selected dev!\n"); + fclose(fwfile); + exit(4); + } + + if (flags & FL_UPDATE) { + if (fwfsz!=fwsz) { + printf("*** File size does not match FW size! ***\n"); + delete dev; + fclose(fwfile); + exit(3); + } + if (CSUM_diff) { + if (!(flags & FL_FORCE)) { + printf("*** CheckSum incorrect, you can try -f option AT YOUR RISC to Force flashing ...\n"); + delete dev; + fclose(fwfile); + exit(3); + } else { + printf("*** CheckSum incorrect, but -f option found. Force flashing...\n"); + } + } + determine_disc_type(dev); +// printf("Disc type: %02X\n",dev->media.type); + if (dev->media.type > 1) { + printf("Disc found, doing eject...\n"); + if (load_eject(dev, false, false)) { + printf("Can't eject disc:( remove disc manually and try again\n"); + delete dev; + fclose(fwfile); + exit (1); + } + } + printf("Waiting for dev to become ready... "); + if (!wait_unit_ready(dev,2,0)) { + printf(" OK!\n"); + } else { + printf("\nDrive not ready! Aborting...\n"); + delete dev; + fclose(fwfile); + exit(1); + } + last=0; + printf("Sending FirmWare to dev, %d bytes per block\n", fwblk); + fseek(fwfile,0,SEEK_SET); +// for (i=0;i<(fwblocks);i++) { + for (i=0;!last;i++) { + if (!fread(dev->rd_buf, fwblk, 1, fwfile)) { + printf("%s: error reading file: %s\n", argv[0], fwfname); + return 1; + } +// printf("Block #%d:\n",i); + if (feof(fwfile) || (i == (fwblocks-1) )) { + last=1; + printf("\nData transfer complete: %d bytes (%X blocks). Updating...\n", (i+1)*fwblk, i+1); + } + err = fwblk_send(dev, i*fwblk, fwblk, last); + if (err) { + printf("FW UPDATE ERROR!\n"); + last=1; + } + } + inquiry(dev); + printf("FW update complete! New INQUIRY data:\n"); + printf("Vendor : '%s'\n",dev->ven); + printf("Model : '%s'\n",dev->dev); + printf("F/W : '%s'\n",dev->fw); + } + printf("\n"); + delete dev; + fclose(fwfile); + return 0; +} diff --git a/console/pxfw/pxfw.h b/console/pxfw/pxfw.h new file mode 100644 index 0000000..0e8664c --- /dev/null +++ b/console/pxfw/pxfw.h @@ -0,0 +1,54 @@ +/* + * This file is a part of QPxTool project + * Copyright (C) 2006-2009, Gennady "ShultZ" Kozlov + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PXFW_H +#define __PXFW_H + +// const int WR_PLEXTOR = 0x0001; + +/* +const uint32_t PX_OLD = 0x0001; +const uint32_t PX_PREMIUM = 0x0002; +const uint32_t PX_708 = 0x0004; +const uint32_t PX_708A2 = 0x0008; +const uint32_t PX_712 = 0x0010; +const uint32_t PX_714 = 0x0020; +const uint32_t PX_716 = 0x0040; +const uint32_t PX_716AL = 0x0080; +const uint32_t PX_755 = 0x0100; +const uint32_t PX_760 = 0x0200; +const uint32_t PX_PREMIUM2 = 0x0400; +*/ + +const uint32_t FL_HELP = 0x00000001; +const uint32_t FL_DEV = 0x00000002; +const uint32_t FL_FWIF = 0x00000004; +const uint32_t FL_FWOF = 0x00000008; +const uint32_t FL_UPDATE = 0x00000010; +const uint32_t FL_BACKUP = 0x00000020; +const uint32_t FL_DEBUG = 0x00000040; +const uint32_t FL_TEST = 0x00000080; +const uint32_t FL_RESET = 0x00000100; +const uint32_t FL_FORCE = 0x00000200; +const uint32_t FL_SCAN = 0x00000400; +const uint32_t FL_EEPROM = 0x00000800; +const uint32_t FL_EEIF = 0x00001000; +const uint32_t FL_EEOF = 0x00002000; + +#endif // __PXFW_H diff --git a/console/pxfw/version.h b/console/pxfw/version.h new file mode 100644 index 0000000..82861a5 --- /dev/null +++ b/console/pxfw/version.h @@ -0,0 +1,13 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2007 Gennady "ShultZ" Kozlov + * + * 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. + */ + +#define VERSION "0.5" + diff --git a/console/qscan/Makefile b/console/qscan/Makefile new file mode 100644 index 0000000..137503c --- /dev/null +++ b/console/qscan/Makefile @@ -0,0 +1,24 @@ +SRCS = $(patsubst %,%.cpp, qscan) +OBJS = $(patsubst %.cpp,%.o,$(SRCS)) + +BIN = qscan$(BINSUFF) + +CXXFLAGS += -I. -I../../lib/include +CFLAGS += -I. -I../../lib/include +LDLIBS += -L../../lib/lib -lqpxtransport -lqpxscan -lqpxplextor + +$(BIN): $(OBJS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) + +all:$(BIN) + +clean: + rm -f $(BIN) $(OBJS) *~ + +install: + mkdir -p $(DESTDIR)$(BINDIR) + install -m 4755 $(BIN) $(DESTDIR)$(BINDIR) + +uninstall: + rm -f $(DESTDIR)$(BINDIR)/$(BIN) +.PHONY: all clean install diff --git a/console/qscan/qscan.cpp b/console/qscan/qscan.cpp new file mode 100644 index 0000000..cdcafc1 --- /dev/null +++ b/console/qscan/qscan.cpp @@ -0,0 +1,847 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2012, Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + +#include "version.h" + +#define MSGPREF "qscan: " +#define CAPDEV "CD: " +#define CAPVND "CV: " +#define CAPMEDIA "CM: " +#define IDEV "ID: " +#define IMEDIA "IM: " +#define SMEDIA "SM: " + +//#define USE_SIGUSR2 + +#define FL_INFO 0x00000001 +#define FL_SCANBUS 0x00000002 +#define FL_DEV 0x00000004 +#define FL_SPEED 0x00000008 +#define FL_SPEEDS 0x00000010 +#define FL_MINFO 0x00000020 +#define FL_DEBUG 0x00000040 +#define FL_LPLUGIN 0x00000080 +#define FL_DINFO 0x00000100 +#define FL_MID_RAW 0x00000200 + +qscanner *scanner; + +#if defined (__unix) || defined (__unix__) +void sigint_handler (int) { + printf("\nSIGINT\n"); + if (scanner) { + printf("Terminating scan...\n"); + scanner->stop(); + } +// printf("\nwaiting scanner to stop\n"); +} + +void sigusr_handler (int signum) { + switch (signum) { + case SIGUSR1: + printf("\nSIGUSR1\n"); + if (scanner) + scanner->stat(); + break; +#ifdef USE_SIGUSR2 + case SIGUSR2: + printf("\nSIGUSR2\n"); + if (scanner) + scanner->stat(); + break; +#endif + default: + break; + } +} + +#elif defined (_WIN32) +BOOL WINAPI sigint_handler (DWORD) { + printf("\nSIGINT\n"); + if (scanner) { + printf("Terminating scan...\n"); + scanner->stop(); + } + return true; +// printf("\nwaiting scanner to stop\n"); +} +#endif + +void detect_vendor_features(drive_info *dev) { + if (isPlextor(dev)) { + plextor_get_life(dev); +// if ((dev->dev_ID == PLEXTOR_755) || (dev->dev_ID == PLEXTOR_760) || (dev->dev_ID == PLEXTOR_PREMIUM2)) +// if (isPlextorLockPresent(dev)) + plextor_px755_do_auth(dev); + if (!plextor_get_hidecdr_singlesession(dev)) dev->ven_features|=PX_HCDRSS; + if (!plextor_get_speedread(dev)) dev->ven_features|=PX_SPDREAD; + if (dev->wr_capabilities) { +// if (!yamaha_check_amqr(dev)) dev->ven_features|=YMH_AMQR; + if (!plextor_get_powerec(dev)) { + dev->ven_features|=PX_POWEREC; +// plextor_get_speeds(dev); + } + if (!plextor_get_gigarec(dev)) dev->ven_features|=PX_GIGAREC; + if (!plextor_get_varirec(dev, VARIREC_CD)) dev->ven_features|=PX_VARIREC_CD; + if (!plextor_get_silentmode(dev)) dev->ven_features|=PX_SILENT; + if (!plextor_get_securec_state(dev)) dev->ven_features|=PX_SECUREC; + } + if (dev->wr_capabilities & DEVICE_DVD) { + if (!plextor_get_varirec(dev, VARIREC_DVD)) dev->ven_features|=PX_VARIREC_DVD; + if (!plextor_get_bitset(dev, PLEX_BITSET_R)) dev->ven_features|=PX_BITSET_R; + if (!plextor_get_bitset(dev, PLEX_BITSET_RDL)) dev->ven_features|=PX_BITSET_RDL; + if (!plextor_get_autostrategy(dev)) dev->ven_features|=PX_ASTRATEGY; + if (!plextor_get_testwrite_dvdplus(dev)) dev->ven_features|=PX_SIMUL_PLUS; + } + //if ((dev->dev_ID == PLEXTOR_755) || (dev->dev_ID == PLEXTOR_760) || (dev->dev_ID == PLEXTOR_PREMIUM2)) + if (isPlextorLockPresent(dev)) + dev->ven_features|=PX_ERASER; +#if 0 + } else if (!strncmp(dev->ven,"YAMAHA",7)) { + if (!yamaha_check_amqr(dev)) dev->ven_features|=YMH_AMQR; + if (!yamaha_check_forcespeed(dev)) dev->ven_features|=YMH_FORCESPEED; +#endif + } +} + +static struct option long_options[] = { + {"help", 0, NULL, 'h'}, + {"scan", 0, NULL, 'l'}, + {"dev", 1, NULL, 'd'}, + {"plugins", 0, NULL, 'p'}, + {"force-plugin", 1, NULL, 'f'}, + {"test", 1, NULL, 't'}, + {"write", 0, NULL, 'W'}, + {"speed", 1, NULL, 's'}, + {"speeds", 0, NULL, 'S'}, + {"rspeed", 1, NULL, 'r'}, + {"wspeed", 1, NULL, 'w'}, + {"info", 0, NULL, 'i'}, + {"infoshort", 0, NULL, 'I'}, + {"media", 0, NULL, 'm'}, + {"mediashort",0, NULL, 'M'}, + {"verbose", 0, NULL, 'v'}, + {0,0,0,0} +}; + +void show_available_errc_data(qscanner *scanner) { + int errc_data = 0; + drive_info *dev; + if (!scanner || !(dev=scanner->device())) return; + errc_data = scanner->errc_data(); + printf(IMEDIA "ERRC data :"); + if (dev->media.type & DISC_CD) { + for (int i=0; i<8; i++) if (errc_data & (1<media.type & DISC_DVD) { + for (int i=0; i<8; i++) if (errc_data & (1<media.type & DISC_BD) { + for (int i=0; i<8; i++) if (errc_data & (1<\n"); + printf("\n"); + printf("qscan is a CD/DVD quality scanning utility\n"); + printf("\n"); + printf("-h --help you are reading this:)\n"); + printf("-d --dev DEVICE use this device\n"); + printf("-s --speed # test media at given speed (default: maximum). Don't use with -r or -w\n"); + printf("-S --speeds detect available read/write speeds\n"); + printf("-r --rspeed # set read speed\n"); + printf("-w --wspeed # set write speed\n"); + printf("-l --scanbus scan IDE/SATA/SCSI buses for MMC-compliant devices\n"); + printf("-t --test TEST run specified test:\n"); + printf(" rt : Read Transfer rate\n"); + printf(" wt : Write Transfer rate\n"); + printf(" errc : Error Correction \n"); + printf(" jb : Jitter/Asymmetry\n"); + printf(" ft : Focus/Tracking errors\n"); + printf(" ta : Time Analyser\n"); + printf("-W --write do real write instead simulation (for use with --test wt)\n"); + printf("-p --plugins list all available plugins\n"); + printf("-f --force PLUGIN force using specified plugin (default: autodetect)\n"); + printf("-I --shortinfo print device info\n"); + printf("-i --info print device info (with supported features list)\n"); + printf("-m --media print media info\n"); + printf("-M --mediashort print media info without raw media identification data\n"); + printf("-v --verbose print a lot of debug info\n"); + printf("\n"); + return 0; + case 'd': + if (flags & FL_DEV) { + printf("Duplicated device option ignored: %s\n", optarg); + } else { + device = optarg; + flags |= FL_DEV; + } + break; + case 'i': + if (!device) { + printf( MSGPREF "no device selected!\n"); + } + flags |= (FL_DINFO | FL_INFO); + break; + case 'I': + if (!device) { + printf( MSGPREF "no device selected!\n"); + } + flags |= FL_INFO; + break; + case 'M': + if (!device) { + printf( MSGPREF "no device selected!\n"); + } + flags |= FL_MINFO; + break; + case 'm': + if (!device) { + printf( MSGPREF "no device selected!\n"); + } + flags |= FL_MINFO; + flags |= FL_MID_RAW; + break; + case 'S': + flags |= FL_SPEEDS; + break; + case 's': + flags |= FL_SPEED; + speed = atol(optarg); + break; + case 'r': +// printf(MSGPREF " -r option conflicts with -s!\n"); + flags |= FL_SPEED; + rspeed = atol(optarg); + break; + case 'w': + flags |= FL_SPEED; + wspeed = atol(optarg); + break; + case 'l': + flags |= FL_SCANBUS; + scanbus(); + return 0; + case 't': + if (!(strcmp(optarg, "rt") + && strcmp(optarg, "wt") + && strcmp(optarg, "errc") + && strcmp(optarg, "jb") + && strcmp(optarg, "ft") + && strcmp(optarg, "ta"))) + { + test = optarg; + } else { + printf( MSGPREF "invalid test name: %s\n", optarg); + return 4; + } + break; + case 'W': + simul=0; + break; + case 'f': + pname = optarg; + break; + case 'p': + if (test) { + printf( MSGPREF "option -p conflicts with -t !\n"); + } else { + flags |= FL_LPLUGIN; + } + break; + case 'v': + flags |= FL_DEBUG; + break; + default: + break; + } + } + if (!device) { + printf( MSGPREF "no device specified! Try using -l to see list\n"); + return 1; + } + + dev = new drive_info(device); + if (dev->mmc < 0) { + printf( MSGPREF " can't open device %s!\n",device); + delete dev; + return 2; + } + switch (inquiry(dev)) { + case 0: + break; + case ERR_NO_DEV: + printf( MSGPREF "%s: no such device!\n",device); + delete dev; + return 3; + case ERR_NO_SCSI: + printf( MSGPREF "%s: device is not SCSI-compliant!\n",device); + delete dev; + return 3; + case ERR_NO_MMC: + printf( MSGPREF "%s: device is not MMC-compliant!\n",device); + delete dev; + return 3; + default: + printf( MSGPREF "%s: unknown error\n",device); + delete dev; + return 3; + } + printf( MSGPREF "using device '%s': '%s' '%s' '%s'\n", device, dev->ven, dev->dev, dev->fw); + //dev->silent = 0; +// get_features_list(dev); + if (!(flags & FL_DEBUG)) dev->silent++; + detect_capabilities(dev); + get_buffer_capacity(dev); + determine_disc_type(dev); + read_capacities(dev); + + detect_vendor_features(dev); + get_rw_speeds(dev); + +// if (!(flags & FL_DEBUG)) dev->silent--; + +// if (flags & (FL_INFO | FL_MINFO | FL_SPEED | FL_SPEEDS)) + detect_speeds(dev); +// test_dma_speed(dev); + + if (flags & FL_INFO) { + printf(IDEV "Device: '%s'\n", device); + printf(IDEV "Vendor: '%s'\n",dev->ven); + printf(IDEV "Model : '%s'\n",dev->dev); + printf(IDEV "F/W : '%s'\n",dev->fw); + if (isPlextor(dev)) { + plextor_get_TLA(dev); + printf(IDEV "TLA# : %s\n",dev->TLA); + } else { + printf(IDEV "TLA# : N/A \n"); + } + printf(IDEV "Buffer: %d kB\n", dev->buffer_size); + printf(IDEV "S/N : %s\n", dev->serial); + printf(IDEV "IFace : %s\n", dev->iface); + printf(IDEV "Loader: %s\n", loader_list[dev->loader_id]); + + if (dev->life.ok) { + printf(IDEV "Discs loaded: %6d\n", dev->life.dn); + printf(IDEV "Drive operating time:\n"); + printf(IDEV " CD Rd : %4d:%02d:%02d\n", dev->life.cr.h, dev->life.cr.m, dev->life.cr.s); + printf(IDEV " CD Wr : %4d:%02d:%02d\n", dev->life.cw.h, dev->life.cw.m, dev->life.cw.s); + if (dev->rd_capabilities & DEVICE_DVD) + printf(IDEV " DVD Rd : %4d:%02d:%02d\n", dev->life.dr.h, dev->life.dr.m, dev->life.dr.s); + if (dev->wr_capabilities & DEVICE_DVD) + printf(IDEV " DVD Wr : %4d:%02d:%02d\n", dev->life.dw.h, dev->life.dw.m, dev->life.dw.s); + } + } + + if (flags & (FL_INFO | FL_MINFO)) { + get_rw_speeds(dev); + + printf(IDEV "Current read speed : %4.1fX ( %5d kB/s)\n", + (float)dev->parms.read_speed_kb / dev->parms.speed_mult, + dev->parms.read_speed_kb); + if (dev->wr_capabilities) { + printf(IDEV "Current write speed : %4.1fX ( %5d kB/s) \n", + (float)dev->parms.write_speed_kb / dev->parms.speed_mult, + dev->parms.write_speed_kb); + } + } + + if (dev->wr_capabilities) { + check_write_modes(dev); + } + if (flags & FL_INFO) { + printf(IDEV "Device Generic capabilities : 0x%016LX\n",dev->capabilities); + printf(IDEV "Device Read capabilities : 0x%016LX\n",dev->rd_capabilities); + printf(IDEV "Device Write capabilities : 0x%016LX\n",dev->wr_capabilities); + } + + if (dev->wr_capabilities) { + printf(IDEV "Supported write modes:"); + for (int i=0; wr_modes[i].id; i++) { + if ((dev->wr_modes & wr_modes[i].id) == wr_modes[i].id) printf(" %s", wr_modes[i].name); + } + printf("\n"); + } + + + if (flags & FL_DINFO) { + printf("\nDevice capabilities:\n"); + for (int idx=0; capabilities[idx].id || strlen(capabilities[idx].name); idx++) { + if (capabilities[idx].id) + printf(CAPDEV "%18s: %s\n", capabilities[idx].name, + (dev->capabilities & capabilities[idx].id) ? "YES" : "-"); + } + + if (dev->capabilities & CAP_DVD_CSS) { + get_rpc_state(dev); + printf(IDEV " RPC phase : %d\n", dev->rpc.phase); + if (dev->rpc.phase == 2) { + if (dev->rpc.region) + printf(IDEV " Cur Region : %d\n", dev->rpc.region); + else + printf(IDEV " Cur Region : not set\n"); + printf(IDEV " Changes left : %d\n", dev->rpc.ch_u); + printf(IDEV " Resets left : %d\n", dev->rpc.ch_v); + } + } + printf(CAPDEV "Media is locked : %s\n", (dev->parms.status & STATUS_LOCK) ? "LOCK": "OFF"); +/* + if (get_drive_serial_number(dev)) { + text_serial->setText(drive->serial); + } +*/ + printf("\nVendor-specific features:\n"); +#if 0 + printf("AudioMaster Q.R.: %s\n", dev->ven_features & YMH_AMQR ? "YES" : "-"); +// if ((dev->ven_features & YMH_AMQR) && ((flags & FL_CURRENT) || (flags & FL_YMH_AMQR))) +// printf("AudioMaster Q.R. : %s\n", drive->yamaha.amqr ? "ON":"OFF"); + printf("Yamaha ForceSpd : %s\n", dev->ven_features & YMH_FORCESPEED ? "YES" : "-"); +// if ((dev->ven_features & YMH_FORCESPEED) && ((flags & FL_CURRENT) || (flags & FL_YMH_FORCESPEED))) +// printf("Yamaha ForceSpeed : %s\n", drive->yamaha.forcespeed ? "ON":"OFF"); +#endif + printf(CAPVND "Hide CD-R : %s\n", + dev->ven_features & PX_HCDRSS ? (dev->plextor.hcdr ? "ON":"OFF") : "-"); + printf(CAPVND "SingleSession : %s\n", + dev->ven_features & PX_HCDRSS ? (dev->plextor.sss ? "ON":"OFF") : "-"); + printf(CAPVND "SpeedRead : %s\n", + dev->ven_features & PX_SPDREAD ? (dev->plextor.spdread ? "ON":"OFF") : "-"); + printf(CAPVND "Silent mode : %s\n", + dev->ven_features & PX_SILENT ? (dev->plextor_silent.state ? "ON":"OFF") : "-"); + if ((dev->ven_features & PX_SILENT) && (dev->plextor_silent.state)) plextor_print_silentmode_state(dev); + printf(CAPVND "SecuRec : %s\n", + dev->ven_features & PX_SECUREC ? (dev->plextor.securec ? "ON":"OFF") : "-"); + printf(CAPVND "PoweRec : %s\n", + dev->ven_features & PX_POWEREC ? (dev->plextor.powerec_state ? "ON":"OFF") : "-"); + if (dev->ven_features & PX_POWEREC) { + if (dev->media.type & DISC_CD) + printf(CAPVND " PoweREC Speed : %dX (CD)\n",dev->plextor.powerec_spd / 176); + if (dev->media.type & DISC_DVD) + printf(CAPVND " PoweREC Speed : %dX (DVD)\n",dev->plextor.powerec_spd / 1385); + } + printf(CAPVND "GigaRec : %s\n", dev->ven_features & PX_GIGAREC ? "YES" : "-"); + if (dev->ven_features & PX_GIGAREC) print_gigarec_value(dev); + printf(CAPVND "VariRec CD : %s\n", dev->ven_features & PX_VARIREC_CD ? (dev->plextor.varirec_state_cd ? "ON":"OFF") : "-"); + if ((dev->ven_features & PX_VARIREC_CD) && (dev->plextor.varirec_state_cd)) print_varirec(dev, VARIREC_CD); + if (dev->wr_capabilities & DEVICE_DVD) { + printf(CAPVND "VariRec DVD : %s\n", dev->ven_features & PX_VARIREC_DVD ? (dev->plextor.varirec_state_dvd ? "ON":"OFF") : "-"); + if ((dev->ven_features & PX_VARIREC_DVD) && (dev->plextor.varirec_state_dvd)) print_varirec(dev, VARIREC_DVD); + printf(CAPVND "DVD+R bitset : %s\n", + dev->ven_features & PX_BITSET_R ? (dev->book_plus_r ? "DVD-ROM book" : "DVD+R book") : "-"); + printf(CAPVND "DVD+R DL bitset : %s\n", + dev->ven_features & PX_BITSET_RDL ? (dev->book_plus_rdl ? "DVD-ROM book" : "DVD+R DL book") : "-"); + printf(CAPVND "DVD+ testwrite : %s\n", + dev->ven_features & PX_SIMUL_PLUS ? (dev->plextor.testwrite_dvdplus ? "ON":"OFF") : "-"); + printf(CAPVND "AutoStrategy : %s%s\n", + dev->ven_features & PX_ASTRATEGY ? "YES" : "-", + ((dev->dev_ID == PLEXTOR_755) || (dev->dev_ID == PLEXTOR_760)) ? " (EXTENDED)" : ""); + if (dev->ven_features & PX_ASTRATEGY) { printf(CAPVND); plextor_print_autostrategy_state(dev); } + } + printf(CAPVND "PlexEraser : %s\n", dev->ven_features & PX_ERASER ? "YES" : "-"); + + printf("\nDevice Read/Write capabilities:\n"); + for (int idx=0; rw_capabilities[idx].id || strlen(rw_capabilities[idx].name); idx++) { + if (rw_capabilities[idx].id) + printf(CAPMEDIA "%11s: %c%c\n", rw_capabilities[idx].name, + (dev->rd_capabilities & rw_capabilities[idx].id) ? 'R' : '-', + (dev->wr_capabilities & rw_capabilities[idx].id) ? 'W' : '-'); + } + if (!(flags & FL_MINFO)) + goto end; + } + + if ((flags & (FL_SPEEDS | FL_MINFO)) && dev->media.type) { + printf(SMEDIA "Read speeds:\n"); + printf(SMEDIA " RD speed max: %4.1fX (%d kB/s)\n", + ((float)dev->parms.max_read_speed_kb) / dev->parms.speed_mult, + dev->parms.max_read_speed_kb); + for (int i=0; iparms.speed_tbl_kb[i] > 0; i++) { + printf(SMEDIA " RD speed #%02d: %4.1fX (%d kB/s)\n", i, + ((float)dev->parms.speed_tbl_kb[i]) / dev->parms.speed_mult, + dev->parms.speed_tbl_kb[i]); + } + if (!(dev->media.type & ( DISC_CDROM | DISC_DDCD_ROM| DISC_DVDROM | DISC_BD_ROM | DISC_HDDVD_ROM))) { + printf(SMEDIA "Write speeds:\n"); + printf(SMEDIA"D WR speed max: %4.1fX (%d kB/s)\n", + ((float)dev->parms.max_write_speed_kb) / dev->parms.speed_mult, + dev->parms.max_write_speed_kb); + for (int i=0; iparms.wr_speed_tbl_kb[i] > 0; i++) { + printf(SMEDIA "D WR speed #%02d: %4.1fX (%d kB/s)\n", i, + ((float)dev->parms.wr_speed_tbl_kb[i]) / ((float)dev->parms.speed_mult), + dev->parms.wr_speed_tbl_kb[i]); + } + } + } + + scanner = new qscanner(dev); + if (pname) { + // scanner = new qscanner(dev); + scanner->plugin_attach(pname); + } + + if (!pname && ((test && (strcmp(test,"rt") && strcmp(test,"wt"))) || (flags & (FL_MINFO | FL_LPLUGIN))) ) { + if (!dev->silent) printf( MSGPREF "creating scanner...\n"); + // scanner = new qscanner(dev); + if (scanner->plugins_probe(!test && (flags & FL_LPLUGIN), false) && !(flags & FL_LPLUGIN) ) { + printf( MSGPREF "Device not found in any plugin support list, trying to probe...\n"); + if (scanner->plugins_probe(!test && (flags & FL_LPLUGIN), !(flags & FL_LPLUGIN))) { + printf( MSGPREF "Probe failed! Trying fallback plugin...\n"); + if (!(flags & FL_LPLUGIN)) { + if (scanner->plugin_attach_fallback()) { + printf( MSGPREF "Error loading fallback plugin!\n"); + } + } + } + } + printf( MSGPREF "using plugin: %s\n", scanner->plugin_name() ? scanner->plugin_name() : "no plugin" ); + } + + if (flags & FL_MINFO) { + char *mt; + int i=0; + if (!dev->media.type) { + printf(IMEDIA "Media type : No media\n"); + return 0; + } + + while ( MEDIA[i].id != 0xFFFFFFFF && (dev->media.type & (~DISC_CDRWSUBT)) != MEDIA [i].id) i++; + mt = (char*) MEDIA[i].name ; + + if (dev->media.type & DISC_CD) { + printf(IMEDIA "Media class : CD\n"); + printf(IMEDIA "Media type : %s\n", mt); + if (dev->media.type & DISC_CDR) { + printf(IMEDIA "Disc Category : %s\n", + cdr_subtype_tbl[ (dev->media.type >> 3) & 0x7]); + } + if (dev->media.type & DISC_CDRW) { + printf(IMEDIA "Disc Category : %s\n", + cdrw_subtype_tbl[ (dev->media.type >> 3) & 0x7]); + } + } else if (dev->media.type & DISC_DVD) { + printf(IMEDIA "Media class : DVD\n"); + printf(IMEDIA "Media type : %s\n", mt); + printf(IMEDIA "Disc Category : %s [rev %d]\n", + book_type_tbl[ (dev->media.book_type >> 4) & 0xF], + dev->media.book_type & 0xF); + printf(IMEDIA "Media size : %s\n", + dev->media.disc_size ? "80mm" : "120mm"); + printf(IMEDIA "Maximum rate : %s\n", + max_rate_tbl[ dev->media.max_rate ]); + + if (!(dev->media.type & DISC_DVDRAM)) { + get_rpc_state(dev); + read_disc_regions(dev); + } + } else if (dev->media.type & DISC_BD) { + printf(IMEDIA "Media class : BD\n"); + printf(IMEDIA "Media type : %s\n", mt); + printf(IMEDIA "Media size : %s\n", + dev->media.disc_size ? "80mm" : "120mm"); + printf(IMEDIA "Polarity flags: %02x\n", dev->media.polarity); + printf(IMEDIA "Disc Category : %s\n", dev->media.polarity ? "LTH" : "HTL"); + } else { + printf(IMEDIA "Media type : unknown\n"); + goto end; + } + + if (scanner && scanner->is_plugin_attached()) { + printf(IMEDIA "Available quality tests:%s%s%s%s%s%s\n", + dev->media.capacity ? " rt" : "", + dev->media.dstatus ? "" : " wt", + scanner->check_test(CHK_ERRC) ? "" : " errc", + scanner->check_test(CHK_JB) ? "" : " jb", + scanner->check_test(CHK_FETE) ? "" : " ft", + scanner->check_test(CHK_TA) ? "" : " ta" + ); + if (!scanner->check_test(CHK_ERRC)) { + show_available_errc_data(scanner); + + int* sp = scanner->get_test_speeds(CHK_ERRC); + if (!sp) sp = dev->parms.speed_tbl_kb; + printf(IMEDIA "ERRC speeds :"); + + for (int i=0; i 0; i++) + printf(" %.0fX",((float)sp[i]) / dev->parms.speed_mult); + + printf("\n"); + } + if (!scanner->check_test(CHK_JB)) { + int* sp = scanner->get_test_speeds(CHK_JB); + if (!sp) sp = dev->parms.speed_tbl_kb; + printf(IMEDIA "JB speeds :"); + + for (int i=0; i 0; i++) + printf(" %.0fX",((float)sp[i]) / dev->parms.speed_mult); + + printf("\n"); + } + } + printf(IMEDIA "Layers : %d\n", dev->media.layers); + + if ( dev->media.type & (DISC_DVD) ) { +// read_disc_regions(drive); + if (!dev->media.dvdcss.protection) { + printf(IMEDIA "Protection : none\n"); + } else { + switch (dev->media.dvdcss.protection) { + case 0x01: + printf(IMEDIA "Protection : CSS/CPPM\n"); + break; + case 0x02: + printf(IMEDIA "Protection : CPRM\n"); + break; + default: + printf(IMEDIA "Protection : Unknown\n"); + break; + } + printf(IMEDIA "Disc regions : "); + if (dev->media.dvdcss.regmask != 0xFF) { + for (i=0; i<8; i++) + if (!((dev->media.dvdcss.regmask >> i) & 1)) + { + printf("%d",i+1); + //dev->rpc.region = i+1; + } + printf("\n"); + } else { + printf("Invalid region mask!\n"); + } + } +/* + } else if ( dev->media.type & (DISC_BD) ) { + printf(IMEDIA "Protection : ??? (BD)\n"); + } else if ( dev->media.type & (DISC_HDDVD) ) { + printf(IMEDIA "Protection : ??? (HDDVD)\n"); +*/ + } + + printf(IMEDIA "Erasable : %s\n",dev->media.erasable ? "yes" : "no" ); + printf(IMEDIA "Disc state : %s\n",disc_status_list[dev->media.dstatus]); + printf(IMEDIA "Session state : %s\n",session_status_list[dev->media.sstatus]); + printf(IMEDIA "Read capacity : %d sectors/%dMB/%02d:%02d.%02dMSF\n", + dev->media.capacity, + dev->media.capacity/512, + dev->media.capacity/4500, ((dev->media.capacity)/75)%60, dev->media.capacity%75); + printf(IMEDIA "Free capacity : %d sectors/%dMB/%02d:%02d.%02dMSF\n", + dev->media.capacity_free, + dev->media.capacity_free/512, + dev->media.capacity_free/4500, ((dev->media.capacity_free)/75)%60, dev->media.capacity_free%75); + printf(IMEDIA "Total capacity: %d sectors/%dMB/%02d:%02d.%02dMSF\n", + dev->media.capacity_total, + dev->media.capacity_total/512, + dev->media.capacity_total/4500, ((dev->media.capacity_total)/75)%60, dev->media.capacity_total%75); + + if (dev->media.type & (DISC_DVDRAM | DISC_HDDVD_RAM) ) { + printf(IMEDIA "DVD-RAM Spare Area information (free/total):\n"); + printf(IMEDIA "Primary SA : %d (%dMB) / %d (%dMB)\n", + dev->media.spare_psa_free, + dev->media.spare_psa_free/512, + dev->media.spare_psa_total, + dev->media.spare_psa_total/512); + printf(IMEDIA "Supplement. SA: %d (%dMB) / %d (%dMB)\n", + dev->media.spare_ssa_free, + dev->media.spare_ssa_free/512, + dev->media.spare_ssa_total, + dev->media.spare_ssa_total/512); + } + + if ( !(dev->media.type & (DISC_CDROM | DISC_DVDROM)) ) + printf(IMEDIA "Media ID : %s\n", dev->media.MID); + if ( dev->media.type & (DISC_DVDminus) ) + printf(IMEDIA "Written on : %s\n", dev->media.writer); + if ((flags & FL_MID_RAW) && (dev->media.type & DISC_CD) && dev->media.ATIP_size) { + for (int i=0; i<((dev->media.ATIP_size-4) >> 2); i++) { + printf(IMEDIA "ATIP data %X : ", i); + printf(" %02X %02X %02X %02X", + dev->media.ATIP[(i<<2)+4], dev->media.ATIP[(i<<2)+5], dev->media.ATIP[(i<<2)+6], dev->media.ATIP[(i<<2)+7]); + printf(" | %3d %3d %3d %3d\n", + dev->media.ATIP[(i<<2)+4], dev->media.ATIP[(i<<2)+5], dev->media.ATIP[(i<<2)+6], dev->media.ATIP[(i<<2)+7]); + } + } + if (dev->media.MID_size) { + if (dev->parms.wr_speed_tbl_media[0] > 0) { + printf(SMEDIA "Manufacturer defined write speeds:\n"); + for (int i=0; iparms.wr_speed_tbl_media[i] > 0; i++) + //for (int i=0; iparms.wr_speed_tbl_media[i]); + } + if (flags & FL_MID_RAW) { + for (int i=0; i<(dev->media.MID_size >> 4); i++) { + unsigned char c; + printf(IMEDIA "MID raw data %X0: ", i); + for (int ii=0; ii<16 && ( (i<<4) +ii) < dev->media.MID_size ; ii++) { + printf("%02X ", (unsigned char) dev->media.MID_raw[(i<<4) + ii + 4]); + } + printf(" | "); + for (int ii=0; ii<16 && ( (i<<4) +ii) < dev->media.MID_size ; ii++) { + c = dev->media.MID_raw[(i<<4) + ii + 4]; + printf("%c", (c > 32) ? c : '.' ); + } + printf("\n"); + } + } + } + get_track_list(dev); + printf(IMEDIA "Total tracks : %d\n", dev->media.tracks); + printf(IMEDIA "Track: Session TMode DMode Start End Size Free Next Writable\n"); + printf("--------------------------------------------------------------------------------------------------\n"); + if (dev->media.tracks) for (i=0; imedia.tracks; i++) { + printf(IMEDIA "Track# %2d %2d %2d %2d %8d %8d %8d %8d %8d\n", + dev->media.track[i].n, + dev->media.track[i].session, + dev->media.track[i].track_mode, + dev->media.track[i].data_mode, + dev->media.track[i].start, + dev->media.track[i].last, + dev->media.track[i].size, + dev->media.track[i].free, + dev->media.track[i].next_writable); + } + printf("--------------------------------------------------------------------------------------------------\n\n"); + goto end; + } + if (test && !strcmp(test,"wt")) { + if (!dev->media.type) { + printf("No media!\n"); + result=2; + goto end; + } + if (dev->media.dstatus & !(dev->media.type & (DISC_DVDRAM | DISC_DVDpRW | DISC_DVDpRWDL))) { + printf("Neither blank media nor DVD-RAM/DVD+RW found!\n"); + result=3; + goto end; + } + } + +// if (test && (strcmp(test,"rt") && strcmp(test,"wt")) && !scanner->is_plugin_attached()) { +// } + + if (speed>0) + scanner->setTestSpeed(speed); + if (test) { +// printf("setting signal handlers...\n"); +#if defined (__unix) || defined (__unix__) + signal(SIGINT, &sigint_handler); + signal(SIGUSR1, &sigusr_handler); +#ifdef USE_SIGUSR2 + signal(SIGUSR2, &sigusr_handler); +#endif +#elif defined (_WIN32) + SetConsoleCtrlHandler(&sigint_handler, 1); +#endif + +// starting test... + //fcntl(1, F_SETFL, fcntl(1, F_GETFL) | O_DIRECT); + //fcntl(2, F_SETFL, fcntl(2, F_GETFL) | O_DIRECT); + if (!strcmp(test, "wt")) { + if ((dev->media.dstatus || !dev->media.capacity_free) && !(dev->media.type & (DISC_DVDRAM| DISC_DVDpRW | DISC_DVDpRWDL))) { +// sprintf(linef, "tsize=%dk", 2295104 * 2); + printf("Media should be blank to run write transfer rate. Aborting...\n"); + result=4; + goto end; + } + if (scanner->setTestWrite( simul )) { + printf("Can't turn TestWrite %s\n", simul ? "ON" : "OFF"); + result=5; + goto end; +// } else { +// printf("TestWrite set\n"); + } + } else if (!strcmp(test, "errc")) { + if (!scanner->check_test(CHK_ERRC)) { + show_available_errc_data(scanner); + } + } + result = scanner->run(test); + } else { + if (!(flags & FL_SPEED)) { + printf( MSGPREF "no test selected!\n"); + } else if (rspeed > 0 || wspeed > 0) { + //printf("1X speed mult: %.1f\n", dev->parms.speed_mult); + get_rw_speeds(dev); + if (rspeed>0) { + dev->parms.read_speed_kb = (int) (rspeed * dev->parms.speed_mult); + printf(IDEV "Setting read speed : %2d X, %5d kB/s\n", + rspeed, + dev->parms.read_speed_kb); + } + if (dev->wr_capabilities && wspeed>0) { + dev->parms.write_speed_kb = (int) (wspeed * dev->parms.speed_mult); + printf(IDEV "Setting write speed : %2d X, %5d kB/s\n", + wspeed, + dev->parms.write_speed_kb); + } + set_rw_speeds(dev); + get_rw_speeds(dev); + if (rspeed>0) { + printf(IDEV "Current read speed : %4.1f X, %5d kB/s\n", + dev->parms.read_speed_kb / dev->parms.speed_mult, + dev->parms.read_speed_kb); + } + if (dev->wr_capabilities && wspeed>0) { + printf(IDEV "Current write speed : %4.1f X, %5d kB/s\n", + dev->parms.write_speed_kb / dev->parms.speed_mult, + dev->parms.write_speed_kb); + } + } + } +end: + if (scanner) { + if (!dev->silent) printf( MSGPREF "destroying scanner...\n"); + delete scanner; + } + if (!dev->silent) printf( MSGPREF "destroying dev...\n"); + delete dev; + return result; +} diff --git a/console/qscan/version.h b/console/qscan/version.h new file mode 100644 index 0000000..d024eb2 --- /dev/null +++ b/console/qscan/version.h @@ -0,0 +1,13 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2012, Gennady "ShultZ" Kozlov + * + * 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. + */ + +#define VERSION "0.7.2" + diff --git a/console/qscand/Makefile b/console/qscand/Makefile new file mode 100644 index 0000000..71ad26a --- /dev/null +++ b/console/qscand/Makefile @@ -0,0 +1,24 @@ +SRCS = $(patsubst %,%.cpp, qscand child) +OBJS = $(patsubst %.cpp,%.o,$(SRCS)) + +BIN = qscand$(BINSUFF) + +CXXFLAGS += -I. -I../../lib/include +CFLAGS += -I. -I../../lib/include +LDLIBS += -L../../lib/lib -lqpxtransport + +$(BIN): $(OBJS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) $(LIBS_THREAD) $(LIBS_INET) + +all:$(BIN) + +clean: + rm -f $(BIN) $(OBJS) *~ + +install: + mkdir -p $(DESTDIR)$(BINDIR) + install -m 755 $(BIN) $(DESTDIR)$(BINDIR) + +uninstall: + rm -f $(DESTDIR)$(BINDIR)/$(BIN) +.PHONY: all clean install diff --git a/console/qscand/child.cpp b/console/qscand/child.cpp new file mode 100644 index 0000000..fc116d7 --- /dev/null +++ b/console/qscand/child.cpp @@ -0,0 +1,476 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include +#include +#include +#include "child.h" + +int clients; +Mutex* cmutex; + +child_t children[CLIENTS_MAX+1]; + +int child_find_unused() +{ +// printf("Child find unused..."); + int idx = -1; + int i=0; + cmutex->lock(); + while (idx<0 && i<(CLIENTS_MAX+1)) { + if (!children[i].arg.used) { + children[i].arg.used=1; + idx=i; + } + i++; + } + cmutex->unlock(); + return idx; +}; + +void child_list_clear() +{ + for (int i=0; i<(CLIENTS_MAX+1); i++) + children[i].arg.used=0; +} + +const char helpstr[] = "QSCAND: valid commands:\n\ +close close connection\n\ +list|scanbus list available devices\n\ +dinfo show device info (have to set device first)\n\ +minfo show media info (have to set device first)\n\ +run run test (have to set device and test type first)\n\ +\n\ +get get current parameters\n\ +set = set parameter. PAR can be:\n\ + dev - set device, VAL should be like '/dev/hdX' or '/dev/sdX'\n\ + test - set test type, VAL should be one of following:\n\ + rt - read transfer rate\n\ + wt - write transfer rate\n\ + errc - error correction test\n\ + jb - jitter/asymmetry test\n\ + ft - focus/tracking test\n\ + ta - time analyser\n\ + speed - set test speed, VAL should be integer\n\ + simul - set test speed, VAL should be 0 or 1 (default: 1)\n\ +"; + +const int helpstr_sz = sizeof(helpstr); + +enum qscan_mode { + none = 0, + scanbus = 1, + scan, + dinfo, + minfo, + help +}; + +char tchar = -1; + +enum fdtype_t { + FD_PIPE = 1, + FD_SOCKET = 2 +}; + +//int readline(int fd, char *buf, int maxlen, fdtype_t fdtype = FD_PIPE) +int readline(int fd, char *buf, int maxlen, fdtype_t fdtype) +{ + int cnt=0; + char *cbuf=buf; + int r; + int sret; + fd_set rd_set; + timeval tv; + + FD_ZERO(&rd_set); + +// printf("tchar=%02X\n", tchar); + if (tchar>=0) { + cbuf[0] = tchar; + cnt++; + cbuf++; + tchar = -1; + } + while ( !term && (cnt<(maxlen-1))) { + FD_SET(fd, &rd_set); + tv.tv_sec = 1; + tv.tv_usec = 0; +#ifdef _WIN32 + if (fdtype == FD_SOCKET) { +#endif + sret = select(fd+1, &rd_set, NULL, NULL, &tv); +#ifdef _WIN32 + errno = GetLastError(); + } else { + sret = 1; + errno = 0; + } +#endif +// printf("select(%d): %s\n", fd, strerror(errno)); + if (sret < 0) { +#ifndef _WIN32 + if (debug) { + if (debug && !daemonized) + printf("readline() %d select: %s\n", fd, strerror(errno)); + } +#endif + if (errno == EINTR) continue; + return -1; + } + else if (sret > 0 && FD_ISSET(fd, &rd_set)) { +#ifdef _WIN32 + if (fdtype == FD_SOCKET) + r = recv(fd, cbuf, 1, 0); + else +#endif + r = read(fd, cbuf, 1); + if (r<0) { + if (debug && !daemonized) + printf("read = %d, %d, %s\n", r, errno, strerror(errno)); + switch (errno) { + case EAGAIN: + // printf("EAGAIN\n"); + continue; + case EINTR: + // printf("EINTR\n"); + continue; + default: + return -1; + } + } + if (!r) return -1; + // look for CR/LF/CR+LF + if (fdtype == FD_SOCKET) { + if (buf[cnt] == 0x0A || buf[cnt] == 0x0D) goto readline_end; + } else if (cnt && (buf[cnt-1] == 0x0A || buf[cnt-1] == 0x0D)) { + if (buf[cnt] != 0x0A && buf[cnt] != 0x0D) tchar = buf[cnt]; + buf[cnt-1]='\n'; + buf[cnt]=0; + return cnt; + } + cnt++; + cbuf++; + } + } + if (term) return -1; +readline_end: + buf[cnt]='\n'; + buf[cnt+1]=0; + return cnt+1; +} + +void child_proc(child_arg_t *arg) +{ + ssize_t n; + char speeds[16]; + char linei [MAXLINE+1]; + char lineo [MAXLINE+IDENT_LEN+1]; + pipe_t pipefd; + pid_t cpid; + qscan_mode mode = none; + char device[128] = "\0"; + char test[8] = "\0"; + int speed = -1; + bool WT_simul = 1; +#ifdef _WIN32 + send(arg->connfd, IDENTV, IDENTV_LEN, 0); +#else + write(arg->connfd, IDENTV, IDENTV_LEN); +#endif + for (;;) { + mode = none; + if (term) return; +#ifdef _WIN32 + send(arg->connfd, PROMPT, PROMPT_LEN, 0); +#else + write(arg->connfd, PROMPT, PROMPT_LEN); +#endif + if ((n = readline(arg->connfd, linei, MAXLINE, FD_SOCKET)) < 0) + { + //if ((n = fgets(line, MAXLINE, arg->connfd)) == EOF) { + //printf("Client disconnected\n"); + return; + } + if (n<=1) continue; + linei[n]=0; +#ifdef _WIN32 + send(arg->connfd, "\n", 1, 0); +#else + write(arg->connfd, "\n", 1); +#endif + if (debug && !daemonized) + printf("%s:%d : command: %s", + inet_ntoa(arg->cliaddr.sin_addr), + ntohs(arg->cliaddr.sin_port), + linei); + if (!strcmp(linei, "help\n")) { +#ifdef _WIN32 + send(arg->connfd, helpstr, helpstr_sz, 0); +#else + write(arg->connfd, helpstr, helpstr_sz); +#endif + continue; + } else if (!strcmp(linei, "close\n")) { +// shutdown(arg->connfd, SHUT_RDWR); + return; +// } else if (!strcmp(linei, "qhelp\n")) { +// mode = help; + } else if (!strcmp(linei, "list\n") || !strcmp(linei, "scanbus\n")) { + mode = scanbus; + } else if (!strcmp(linei, "dinfo\n")) { + mode = dinfo; + } else if (!strcmp(linei, "minfo\n")) { + mode = minfo; + } else if (!strcmp(linei, "run\n")) { + mode = scan; + } else if (!strcmp(linei, "get\n")) { + sprintf(lineo, "current parameters:\ndevice: '%s'\ntest : '%s'\nspeed : %d\nsimul : %s\n", + device, + test, + speed, + WT_simul ? "on" : "off"); +#ifdef _WIN32 + send(arg->connfd, lineo, strlen(lineo), 0); +#else + write(arg->connfd, lineo, strlen(lineo)); +#endif + continue; + } else if (!strncmp(linei, "set", 3)) { + char *linet = linei+4; + size_t len; + if ((strlen(linei) <=4) || linei[3]!=' ') { + sprintf(lineo, "QSCAND: set: needs parameter!\n"); +#ifdef _WIN32 + send(arg->connfd, lineo, strlen(lineo), 0); +#else + write(arg->connfd, lineo, strlen(lineo)); +#endif + continue; + } + if (!strncmp(linet, "dev=", 4)) { + linet+=4; + len = strlen(linet); + if (lenconnfd, lineo, strlen(lineo), 0); +#else + write(arg->connfd, lineo, strlen(lineo)); +#endif + } + } else if (!strncmp(linet, "test=", 5)) { + linet+=5; + len = strlen(linet); + if (lenconnfd, lineo, strlen(lineo), 0); +#else + write(arg->connfd, lineo, strlen(lineo)); +#endif + } + } else if (!strncmp(linet, "speed=", 6)) { + linet+=6; + speed = atol(linet); + } else if (!strncmp(linet, "simul=", 6)) { + linet+=6; + WT_simul = atol(linet) ? 1 : 0; + } else { + sprintf(lineo, "QSCAND: set: invalid parameter!\n"); +#ifdef _WIN32 + send(arg->connfd, lineo, strlen(lineo), 0); +#else + write(arg->connfd, lineo, strlen(lineo)); +#endif + } + continue; + } else { + // sprintf(lineo, "str len: %d, cmd len: %d. '%d'\n", n, strlen(linei), linei); + //sprintf(lineo, "str len: %d, cmd len: %d, last %02x %02x\n", n, strlen(linei), linei[n-2], linei[n-1]); + // write(arg->connfd, lineo, strlen(lineo)); + sprintf(lineo, "QSCAND: invalid command. try \"help\"\n"); +#ifdef _WIN32 + send(arg->connfd, lineo, strlen(lineo), 0); +#else + write(arg->connfd, lineo, strlen(lineo)); +#endif + } + + if (mode != none) { + int argc = 0; + char **argv = (char**) malloc(sizeof(char*)); + argv[0] = NULL; + if ( ((mode == scan) || (mode == dinfo) || (mode == minfo)) && !strlen(device)) { +#ifdef DAEMON_EN + if (daemonized) + syslog(LOG_WARNING, "QSCAND: No device specified!\n"); + else +#endif + printf("QSCAND: No device specified!\n"); + _exit(0); + } + if ( (mode == scan) && !strlen(test)) { +#ifdef DAEMON_EN + if (daemonized) + syslog(LOG_WARNING, "QSCAND: No test specified!\n"); + else +#endif + printf("QSCAND: No test specified!\n"); + _exit(0); + } + argv = add_arg(argv, &argc, "qscan"); + switch (mode) { + case scanbus: + argv = add_arg(argv, &argc, "-l"); + break; + case scan: + argv = add_arg(argv, &argc, "-d"); + argv = add_arg(argv, &argc, device); + + argv = add_arg(argv, &argc, "-t"); + argv = add_arg(argv, &argc, test); + + sprintf(speeds,"%d", speed); + argv = add_arg(argv, &argc, "-s"); + argv = add_arg(argv, &argc, speeds); + + if (!strcmp(test, "wt") && !WT_simul) + argv = add_arg(argv, &argc, "-W"); + break; + case dinfo: + argv = add_arg(argv, &argc, "-d"); + argv = add_arg(argv, &argc, device); + argv = add_arg(argv, &argc, "-Ip"); + break; + case minfo: + argv = add_arg(argv, &argc, "-d"); + argv = add_arg(argv, &argc, device); + argv = add_arg(argv, &argc, "-m"); + break; + case help: + argv = add_arg(argv, &argc, "-h"); + break; + default: + if (debug && !daemonized) + printf("unknown mode: %d\n", mode); + return; + } + + if ((cpid = createChildProcess(argv, &pipefd, NULL)) == -1) { +#ifdef DAEMON_EN + if (daemonized) + syslog(LOG_ERR, "Can't start child!\n"); + else +#endif + printf("QSCAND: Can't start child!\n"); + return; + } else { + // parent. copy messages from pipe to socket + close(pipefd[1]); // unused write end + + sprintf(lineo, "QSCAND: child created, reading from pipe...\n"); +#ifdef _WIN32 + send(arg->connfd, lineo, strlen(lineo), 0); +#else + write(arg->connfd, lineo, strlen(lineo)); +#endif + int wn, woffs; + while((n = readline((int)pipefd[0], lineo, MAXLINE, FD_PIPE))>=0) { + // while((n = read(pipefd[0], lineo, MAXLINE)) > 0) { + // sprintf(linei,"\nread #%d: %d bytes\n\0",idx, n); + // write(arg->connfd, linei, strlen(linei)); + // idx++; +// printf(lineo); + woffs=0; + while (woffsconnfd, lineo+woffs, n-woffs, 0); +#else + wn = write(arg->connfd, lineo+woffs, n-woffs); +#endif + if (wn<0) { + switch (errno) { + case EAGAIN: + break; + default: + woffs=n; + } + } else { + woffs+=wn; + } + } + } +#ifdef DAEMON_EN + if (daemonized) + syslog(LOG_WARNING, "Pipe end!\n"); + else +#endif + printf("QSCAND: Pipe end!\n"); + close(pipefd[0]); + } + } // if (mode != none) + } +} + +void *child_thread(void *argp) +{ + child_arg_t *arg = (child_arg_t*)argp; + +// childl++; +// pid=getpid(); +// close(listenfd); +#ifdef DAEMON_EN + if (daemonized) + syslog(LOG_INFO, "Client connected: %s:%d\n", + inet_ntoa(arg->cliaddr.sin_addr), + ntohs(arg->cliaddr.sin_port)); + else +#endif + printf("%d: Client connected: %s:%d\n", pid, + inet_ntoa(arg->cliaddr.sin_addr), + ntohs(arg->cliaddr.sin_port)); + + /* serve connection */ + child_proc(arg); +#ifdef DAEMON_EN + if (daemonized) + syslog(LOG_INFO, "Client disconnected: %s:%d\n", + inet_ntoa(arg->cliaddr.sin_addr), + ntohs(arg->cliaddr.sin_port)); + else +#endif + printf("%d: Client disconnected: %s:%d\n", pid, + inet_ntoa(arg->cliaddr.sin_addr), + ntohs(arg->cliaddr.sin_port)); + if (debug && !daemonized) + printf("%d: Closing client socket...\n", pid); + shutdown(arg->connfd, SHUT_RDWR); + close(arg->connfd); + + cmutex->lock(); + clients--; + arg->used = 0; + cmutex->unlock(); + + return 0; +// thread_exit(0); +} + diff --git a/console/qscand/child.h b/console/qscand/child.h new file mode 100644 index 0000000..de303ea --- /dev/null +++ b/console/qscand/child.h @@ -0,0 +1,40 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 CHILDPROC_H +#define CHILDPROC_H + +#include +#include +#include + +typedef struct { + bool used; + struct sockaddr_in cliaddr; + int connfd; +} child_arg_t; + +typedef struct { + thread_t tid; + child_arg_t arg; +} child_t; + +extern child_t children[CLIENTS_MAX+1]; +extern void *child_thread(void *argp); +extern int child_find_unused(); +extern void child_list_clear(); + +extern int clients; +extern Mutex *cmutex; + +#endif // CHILDPROC_H + diff --git a/console/qscand/qscand.cpp b/console/qscand/qscand.cpp new file mode 100644 index 0000000..ea018fe --- /dev/null +++ b/console/qscand/qscand.cpp @@ -0,0 +1,372 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include "qscand.h" +#ifndef _WIN32 +#include +#endif + +#include + +bool term; +int childl; +pid_t pid; +// pid_t cpids[CLIENTS_MAX]; +bool daemonized=0; +bool debug=0; + +#if defined (__unix) || defined (__unix__) +void sigint_handler (int) +#elif defined (_WIN32) +BOOL WINAPI sigint_handler (DWORD) +#endif +{ + if (!childl) { + if (debug && !daemonized && clients>0) + printf("%d: SIGINT. %d client(s) left. Terminating connections...\n", pid, clients); +// while (clients) msleep((1 << 8)); +// if (debug && !daemonized) +// printf("%d: SIGINT. Terminating listener...\n", pid); + } else if (childl == 1) { + if (debug && !daemonized) + printf("%d: SIGINT. Terminating client connection...\n", pid); + } + term=1; +#if defined (_WIN32) + return 1; +#endif +}; + +#ifdef DAEMON_EN +int daemonize() +{ + pid_t pid; + switch (pid = fork()) { + case -1: + return -1; + case 0: + openlog("qscand", LOG_PID, LOG_DAEMON); + close(0); + close(1); + close(2); + daemonized = 1; + break; + default: + exit(0); + } + if (setsid() == -1) + return -1; + return 0; +} +#endif + +static struct option long_options[] = { + {"help", 0, NULL, 'h'}, +#ifdef DAEMON_EN + {"nodaemon", 0, NULL, 'n'}, +#endif + {"debug", 0, NULL, 'd'}, + {"iface", 1, NULL, 'i'}, + {"port", 1, NULL, 'p'}, +#ifdef AUTOPORT_ENABLE + {"portauto", 0, NULL, 'a'}, +#endif + {0,0,0,0} +}; + +int child_create(int idx) +{ + int r; + if ((r = thread_create(&children[idx].tid, NULL, &child_thread, (void*)&children[idx].arg))) { +#ifdef DAEMON_EN + syslog(LOG_ERR, "Error creating thread!\n"); +#else + printf("Error creating thread!\n"); +#endif + return r; + close(children[idx].arg.connfd); + } + return 0; +} + +int main (int argc, char **argv) +{ +// pid_t cpid; + char *iface=NULL; + uint32_t ip; + uint16_t port; + int listenfd; + struct sockaddr_in srvaddr; + socklen_t srvaddr_len = sizeof(srvaddr); + int cli_idx; +#ifdef _WIN32 + WSADATA WSAdata; +#endif + + bool portauto=0; + bool nodaemon=0; + daemonized = 0; + + struct timeval tv; + fd_set rd_set; + FD_ZERO(&rd_set); + + term=0; + clients=0; + cli_idx=-1; + pid=getpid(); +// for (int i=0; i\n"); + printf("\n"); + printf("-h --help you are reading this:)\n"); +#ifdef DAEMON_EN + printf("-n --nodaemon don't daemonize\n"); +#endif + printf("-i --iface listen on interface with selected address\n"); + printf("-p --port listen selected port\n"); +#ifdef AUTOPORT_ENABLE + printf("-a --portauto autoselect port listen to\n"); +#endif + printf("-d --debug debug\n"); + printf("\n"); + exit(0); + case 'n': + nodaemon=1; +#ifndef DAEMON_EN + printf("daemon mode not available, option '-n' ignored!\n"); +#endif + break; + case 'd': + debug=1; + break; + case 'i': + iface = optarg; + break; + case 'p': + { + unsigned long lp = strtol(optarg, NULL, 10); + if (errno == ERANGE || lp > 65535) { + printf("port number out of range!\n"); + } else { + port = lp; + } + } + break; +#ifdef AUTOPORT_ENABLE + case 'a': + portauto=1; + break; +#endif + default: + printf("Invalid option: -%c\n", c); + break; + } + } + if (!port && !portauto) + port = DEFPORT; +#ifdef _WIN32 + if ((errno = WSAStartup(MAKEWORD(1,1), &WSAdata))) { + printf("Can't initialize winsock: [%d] %s\n", errno, strerror(errno)); + return -1; + } +#endif + if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { + printf("%d: Can't create socket. Terminating\n", pid); + return -1; + } +// fcntl(listenfd, F_SETFL, fcntl(listenfd, F_GETFL) | O_NONBLOCK); + + //listenfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + memset (&srvaddr, 0, sizeof(srvaddr)); + +#if defined (__unix) || defined (__unix__) + signal(SIGINT, &sigint_handler); +// signal(SIGCHLD, &sigchld_handler); +#elif defined (_WIN32) + SetConsoleCtrlHandler(&sigint_handler, 1); +#endif + + srvaddr.sin_family = AF_INET; + if (!iface || !inet_aton(iface, &srvaddr.sin_addr)) { + srvaddr.sin_addr.s_addr = ip; + } + srvaddr.sin_port = htons(port); + + int on=1; + setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); + + if (bind( listenfd, (struct sockaddr*) &srvaddr, srvaddr_len)) { +#ifdef _WIN32 + errno = GetLastError(); +#endif +// if (errno) { + if (errno != EADDRINUSE || !portauto) { + printf("%d: Can'n bind() %s:%d: [%d] %s\n", pid, inet_ntoa(srvaddr.sin_addr), ntohs(srvaddr.sin_port), errno, strerror(errno)); + return -1; + } else { + printf("%d: Can'n bind() %s:%d, trying to autoassig port...\n", pid, inet_ntoa(srvaddr.sin_addr), ntohs(srvaddr.sin_port)); + srvaddr.sin_port = 0; + if (bind( listenfd, (struct sockaddr*) &srvaddr, srvaddr_len)) { + printf("%d: Can'n bind(): %s\n", pid, strerror(errno)); + return -1; + } + } + } + + if (listen(listenfd, LISTENQ)) { +#ifdef _WIN32 + errno = GetLastError(); +#endif + printf("%d: Can'n listen() %s:%d: [%d] %s\n", pid, inet_ntoa(srvaddr.sin_addr), ntohs(srvaddr.sin_port), errno, strerror(errno)); + return -1; + } +// printf("listenfd: %d\n", listenfd); + getsockname(listenfd, (struct sockaddr*) &srvaddr, &srvaddr_len); + +#ifdef DAEMON_EN + if (!nodaemon) daemonize(); + pid = getpid(); + + if (daemonized) { + syslog(LOG_INFO, "Listening %s:%d\n", inet_ntoa(srvaddr.sin_addr), port); + syslog(LOG_DEBUG, "daemonized\n"); + } else +#endif + printf("%d: Listening %s:%d\n", pid, inet_ntoa(srvaddr.sin_addr), port); + + cmutex = new Mutex(); + child_list_clear(); + + for (; !term ;) { + // printf("listen loop...\n"); + socklen_t cliaddr_len = sizeof(children[cli_idx].arg.cliaddr); + FD_SET(listenfd, &rd_set); + tv.tv_sec =1; + tv.tv_usec=0; + + cli_idx = child_find_unused(); + if (cli_idx<0) continue; + + children[cli_idx].arg.connfd=0; + // printf("select...\n"); + int sret = select(listenfd+1, &rd_set, NULL, NULL, &tv); + // printf("Sret = %d, err: %s\n", sret, strerror(errno)); + if ( sret > 0 ) { + if ( FD_ISSET(listenfd, &rd_set) ) { +// #ifndef _WIN32 + children[cli_idx].arg.connfd = accept(listenfd,(struct sockaddr*) &children[cli_idx].arg.cliaddr, &cliaddr_len); +/* +#else + int tconnfd = accept(listenfd,(struct sockaddr*) &children[cli_idx].arg.cliaddr, &cliaddr_len); +#endif +*/ + if (errno == ECONNABORTED) { + children[cli_idx].arg.used=0; + continue; + } +#if 0 +//#ifdef _WIN32 + // children[cli_idx].arg.connfd = _get_osfhandle( tconnfd ); + children[cli_idx].arg.connfd = _open_osfhandle( tconnfd, _O_RDWR | _O_BINARY | _O_SEQUENTIAL); + // write(children[cli_idx].arg.connfd, "012345678\n", 10); + // write(tconnfd, "012345678\n", 10); + printf("connfd: %d, errno: [%d] %s\n", children[cli_idx].arg.connfd, errno, strerror(errno)); +#endif + } + } + //if (errno == EINTR) continue; + if (children[cli_idx].arg.connfd > 0) { + // write(connfd, IDENT, IDENT_LEN); + // fcntl(connfd, F_SETFL, fcntl(connfd, F_GETFL) | O_NONBLOCK); + cmutex->lock(); + if (clients>=CLIENTS_MAX) { + write(children[cli_idx].arg.connfd, "QSCAND: clients limit reached!\n", 31); + close(children[cli_idx].arg.connfd); + children[cli_idx].arg.used=0; + cmutex->unlock(); + continue; + } + cmutex->unlock(); + if (!child_create(cli_idx)) { + cmutex->lock(); + clients++; + cmutex->unlock(); + if (debug && !daemonized) + printf("%d: Clients: %d\n", pid, clients); + /* for (int i=0; i + * + * 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 QSCAND_H +#define QSCAND_H + +#include + +#endif // QSCAND_H + diff --git a/console/qscand/qscand_defs.h b/console/qscand/qscand_defs.h new file mode 100644 index 0000000..d4ef935 --- /dev/null +++ b/console/qscand/qscand_defs.h @@ -0,0 +1,50 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Gennady "ShultZ" Kozlov + * + * 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 QSCAND_DEFS_H +#define QSCAND_DEFS_H + +#if defined (__unix) || defined (__unix__) +#include +#include +#include +#include +#include +#include +#define DAEMON_EN +#elif defined (_WIN32) +#include +#endif + + +#define CLIENTS_MAX 16 + +#define MAXLINE 1024 +#define DEFPORT 46660 +#define LISTENQ 16 +//#define AUTOPORT_ENABLE +//#define ECHO_CHILD + +#define IDENT "QSCAND\n" +#define IDENT_LEN sizeof(IDENT) +#define IDENTV "qScand 0.1\n" +#define IDENTV_LEN sizeof(IDENTV) +#define PROMPT "qscand $ " +#define PROMPT_LEN sizeof(PROMPT) + +extern pid_t pid; +extern bool daemonized; +extern bool debug; +extern bool term; + +#endif // QSCAND_DEFS_H + diff --git a/console/readdvd/Makefile b/console/readdvd/Makefile new file mode 100644 index 0000000..f93cb2e --- /dev/null +++ b/console/readdvd/Makefile @@ -0,0 +1,25 @@ +SRCS = $(patsubst %,%.cpp, deadreader kbhit sectmap reader reader_disc imgwriter dvd_udf) +OBJS = $(patsubst %.cpp,%.o,$(SRCS)) + +BIN = readdvd$(BINSUFF) + +CXXFLAGS += -I. -I../../lib/include +CFLAGS += -I. -I../../lib/include +LDLIBS += -L../../lib/lib -lqpxtransport $(LIBS_THREAD) + +$(BIN): $(OBJS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) + +all:$(BIN) + +clean: + rm -f $(BIN) $(OBJS) *~ + +install: + mkdir -p $(DESTDIR)$(BINDIR) + install -m 755 $(BIN) $(DESTDIR)$(BINDIR) + +uninstall: + rm -f $(DESTDIR)$(BINDIR)/$(BIN) + +.PHONY: all clean install uninstall diff --git a/console/readdvd/deadreader.cpp b/console/readdvd/deadreader.cpp new file mode 100644 index 0000000..05785a5 --- /dev/null +++ b/console/readdvd/deadreader.cpp @@ -0,0 +1,322 @@ +/* + * + * DeadDiscReader + * Copyright (C) 2006-2009, Gennady "ShultZ" Kozlov + * it uses QPxTool SCSI transport library + * + */ + +#define MAX_THREADS 8 +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +//#include +#include + +#include +#include +#include +#include "version.h" + +int init_drives(drive_info** dev, char** dev_n) { + printf("Initialising drives...\n"); + int i=0; + int n=0; + while ((isilent++; + detect_capabilities(dev[n]); + printf("%2d: [%s] %s %s %s\n", n, dev[n]->device, dev[n]->ven, dev[n]->dev, dev[n]->fw); + n++; + } else { + printf("Can't open device '%s'!\n", dev_n[i]); + } + i++; + } + printf("%d devices found\n", n); + return n; +} + +int sort_drives(drive_info** dev) { + bool swap=1; + int i; + int devcnt=0; + while (swap) { + swap=0; + for (i=0; i<(MAX_THREADS-1); i++) if((!dev[i]) && (dev[i+1])) + { + dev[i]=dev[i+1]; + dev[i+1]=NULL; + swap=1; + } + } + for (i=0; iset_pass(pass); + reader[i]->set_retry(retry); +// printf("Starting thread %d\n", i); + reader[i]->start(); + } + + while (reader[0]->running()) { + msleep(1); + for (;kb.kb_hit();) { + c=kb.kb_getch(); + switch (c) { + case 'w': + case 'W': + map->lock(); + map->save(); + map->unlock(); + break; + case 'q': + case 'Q': + for (i=0; istop(); + break; + default: + break; + } + } + } + printf("\nWaiting for reader threads...\n"); + for (i=0; iwait(); + return 0; +} + +void usage(char* av0) { + printf("usage: %s [device_list] [options]\n", av0); + printf("device_list - you may specify multiple reader devices:\n"); + printf("\t-d /dev/dvd0 [-d /dev/dvd1 [-d /dev/dvd2[..]]]\n"); + printf("options:\n"); + printf("\t-o \tsave image to this file\n"); + printf("\t-s \tset read speed (-1 = maximum)\n"); + printf("\t-v, -vv\t\tbe verbose\n"); + printf("\t-l,-s\t\tscan IDE/SCSI bus and exit\n"); + printf("\t-h\t\tshow this help and exit\n"); + printf("\ncontrol keys:\n"); + printf("\t q - stop reading and exit\n"); + printf("\t w - save current sector map and continue\n\n"); +} + +#if defined (_WIN32) +BOOL WINAPI sigint_handler (DWORD) { + printf("\nSIGINT\n"); + return true; +} +#endif + +int main(int argc, char** argv) +{ + int spd=-1; + int devcnt=0; + drive_info* dev[MAX_THREADS]; + char* dev_n[MAX_THREADS]; + cdvdreader* reader[MAX_THREADS]; + smap* map=NULL; + imgwriter* iso=NULL; +// FILE *f_img; + int32_t capacity; + int done; + char* n_img=0; + char n_map[1024]; +// unsigned int fc, dc, ic; +// bool rfail=0; +// bool rcont=0; + int i; + int verbose=0; + + + printf("** DVD reader v%s (c) 2006-2009 Gennady \"ShultZ\" Kozlov **\n", VERSION); + printf("** multiple-device DVD reader with CSS support, optimized for corrupted media\n"); + + if (argc<2) { + // usage(argv[0]); + printf("No option specified!\nUse -h for details\n"); + return 1; + } + + for (i=1; i(i+1)) { + i++; + spd = atol(argv[i]); + } else { + printf("Option %s needs a parameter!\n", argv[i]); + } + } else if(!strcmp(argv[i],"-o")) { + if(argc>(i+1)) { + i++; + n_img = argv[i]; + } else { + printf("Option %s needs a parameter!\n", argv[i]); + } + } else if(!strcmp(argv[i],"-d")) { + if(argc>(i+1)) { + i++; + if (devcnt < MAX_THREADS) { + dev_n[devcnt]=argv[i]; + devcnt++; + } else { + printf("Maximum devices limit reached: %d\n", MAX_THREADS); + } + } else { + printf("Option %s needs a parameter!\n", argv[i]); + } + } else { + printf("Option not recognized: %s\n", argv[i]); + } + } + + + + if (!devcnt) { + printf(" No devices selected!\n"); + exit (1); + } + if (!n_img) { + printf(" No image file selected!\n"); + exit (2); + } + + strcpy(n_map, n_img); + strcat(n_map, ".smap"); + printf(" image file : '%s'\n", n_img); + printf(" bitmap file : '%s'\n", n_map); + printf("\n"); + + devcnt = init_drives(dev, dev_n); + printf(" Checking media...\n"); + for (i=0; iparms.read_speed_kb = spd * 1350; + set_rw_speeds(dev[i]); + + if(!dev[i]->media.type) { + printf("%s: NO media!\n", dev[i]->device); + } else { + int mi=0; + while ( MEDIA[mi].id != 0xFFFFFFFF && (dev[i]->media.type & (~DISC_CDRWSUBT)) != MEDIA [mi].id) mi++; + printf("%s: media type: %s\n", dev[i]->device, MEDIA[mi].name); + } + if (!(dev[i]->media.type & DISC_DVD)) { + printf("%s: no DVD found! removing device from list...\n", dev[i]->device); + dev[i]=NULL; + //exit (3); + } else { + read_capacities(dev[i]); + } + } + devcnt = sort_drives(dev); + if (!devcnt) { + printf(" No discs detected!\n"); + exit (3); + } + capacity = dev[0]->media.capacity; + if (devcnt>1) { + printf("Using %s as primary device\nChecking capacities (expecting %d sectors)...", dev[0]->device, capacity); + for (i=1; idevice, dev[i]->media.capacity); + if (dev[i]->media.capacity == dev[0]->media.capacity) { + printf("OK\n"); + } else { + printf("failed! removing device from list...\n"); + dev[i]=NULL; + } + } + } + devcnt = sort_drives(dev); + printf("using %d devices\n", devcnt); + + map = new smap(n_map, capacity); + iso = new imgwriter(n_img, map); + +#if defined (_WIN32) + SetConsoleCtrlHandler(&sigint_handler, 1); +#endif + + if (verbose) printf("Initializing threads...\n"); + for (i=0; ilock(); + done = map->get_done(); + map->unlock(); + if (!done) { + printf("Starting first reading pass...\n"); + read_multi(reader, devcnt, map, PASS_FIRST); + } else { + if (done < capacity) { + printf("Continue reading incomplete image...\n"); + read_multi(reader, devcnt, map, PASS_CONT); + } + } + + map->lock(); + done = map->get_fail(); + map->unlock(); + if (!done || reader[0]->stoped()) goto dreader_done; + + printf("There are some corrupted sectors!\nStarting first RECOVERY pass...\n"); + read_multi(reader, devcnt, map, PASS_RECOVER0, 1); + + map->lock(); + done = map->get_fail(); + map->unlock(); + if (!done || reader[0]->stoped()) goto dreader_done; + + printf("There are hard corrupted sectors!\nStarting long RECOVERY pass...\n"); + read_multi(reader, devcnt, map, PASS_RECOVER1, 4); + +dreader_done: + if (verbose) printf("\nDestroying readers...\n"); + for (i=0; iprint_stat(); + delete reader[i]; + } + printf("Image summary:\n"); + printf(" Sectors tot : %d\n", map->get_tot()); + printf(" Sectors wait : %d\n", map->get_wait()); + printf(" Sectors read : %d\n", map->get_read()); + printf(" Sectors done : %d\n", map->get_done()); + printf(" Sectors corrupted: %d\n", map->get_fail()); +// printf("Closing image file...\n"); +// fclose(f_img); + +// map->save(); + +// printf("Destroying drive_info*...\n"); + for (i=0; i. + * + * Modifications by: + * Billy Biggs . + * Björn Englund . + * + * dvdudf: parse and read the UDF volume information of a DVD Video + * Copyright (C) 1999 Christian Wolff for convergence integrated media + * GmbH The author can be reached at scarabaeus@convergence.de, the + * project's page is at http://linuxtv.org/dvd/ + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. Or, point your browser to + * http://www.gnu.org/copyleft/gpl.html + */ + +//#include "config.h" + +#include +#include +#include + +#include +#include +#include +#include + +#if defined(HAVE_INTTYPES_H) +#include +#elif defined(HAVE_STDINT_H) +#include +#endif + +//#include "dvd_reader.h" +#include "dvd_udf.h" +//#include "dvdread_internal.h" + +#ifndef EMEDIUMTYPE +#define EMEDIUMTYPE ENOENT +#endif + +#ifndef HAVE_UINTPTR_T +#warning "Assuming that (unsigned long) can hold (void *)" +//typedef unsigned long uintptr_t; +#endif + +#define DVD_ALIGN(ptr) (void *)((((uintptr_t)(ptr)) + (DVD_VIDEO_LB_LEN-1)) \ + / DVD_VIDEO_LB_LEN * DVD_VIDEO_LB_LEN) + +typedef struct { + void *start; + void *aligned; +} dvdalign_ptrs_t; + +typedef struct { + dvdalign_ptrs_t *ptrs; + uint32_t ptrs_in_use; + uint32_t ptrs_max; +} dvdalign_t; + +#if 0 +extern void *GetAlignHandle(dvd_reader_t *device); +extern void SetAlignHandle(dvd_reader_t *device, void *align); +#endif + +/** + * Allocates aligned memory (for use with reads from raw/O_DIRECT devices). + * This memory must be freed with dvdalign_free() + * The size of the memory that is allocate is num_lbs*2048 bytes. + * The memory will be suitably aligned for use with + * block reads from raw/O_DIRECT device. + * @param num_lbs Number of logical blocks (2048 bytes) to allocate. + * @return Returns pointer to allocated memory, or NULL on failure + * This isn't supposed to be fast/efficient, if that is needed + * this function should be rewritten to use posix_memalign or similar. + * It's just needed for aligning memory for small block reads from + * raw/O_DIRECT devices. + * We assume that 2048 is enough alignment for all systems at the moment. + * Not thread safe. Only use this from one thread. + * Depends on sizeof(unsigned long) being at least as large as sizeof(void *) + */ + +#if 0 +static void *dvdalign_lbmalloc(dvd_reader_t *device, uint32_t num_lbs) +{ + void *m; + int n; + dvdalign_t *a; + + m = malloc((num_lbs+1)*DVD_VIDEO_LB_LEN); + if(m == NULL) { + return m; + } + a = (dvdalign_t *)GetAlignHandle(device); + if(a == NULL) { + a = malloc(sizeof(dvdalign_t)); + if(a == NULL) { + return a; + } + a->ptrs = NULL; + a->ptrs_in_use = 0; + a->ptrs_max = 0; + SetAlignHandle(device, (void *)a); + } + + if(a->ptrs_in_use >= a->ptrs_max) { + a->ptrs = realloc(a->ptrs, (a->ptrs_max+10)*sizeof(dvdalign_ptrs_t)); + if(a->ptrs == NULL) { + free(m); + return NULL; + } + a->ptrs_max+=10; + for(n = a->ptrs_in_use; n < a->ptrs_max; n++) { + a->ptrs[n].start = NULL; + a->ptrs[n].aligned = NULL; + } + n = a->ptrs_in_use; + } else { + for(n = 0; n < a->ptrs_max; n++) { + if(a->ptrs[n].start == NULL) { + break; + } + } + } + + a->ptrs[n].start = m; + a->ptrs[n].aligned = DVD_ALIGN(m); + + a->ptrs_in_use++; + + /* If this function starts to be used too much print a warning. + Either there is a memory leak somewhere or we need to rewrite this to + a more efficient version. + */ + if(a->ptrs_in_use > 50) { + if(dvdread_verbose(device) >= 0) { + fprintf(stderr, "libdvdread: dvdalign_lbmalloc(), more allocs than supposed: %u\n", a->ptrs_in_use); + } + } + + return a->ptrs[n].aligned; +} + +/** + * Frees memory allocated with dvdalign_lbmemory() + * @param ptr Pointer to memory space to free + * Not thread safe. + */ +static void dvdalign_lbfree(dvd_reader_t *device, void *ptr) +{ + int n; + dvdalign_t *a; + + a = (dvdalign_t *)GetAlignHandle(device); + if(a && a->ptrs) { + for(n = 0; n < a->ptrs_max; n++) { + if(a->ptrs[n].aligned == ptr) { + free(a->ptrs[n].start); + a->ptrs[n].start = NULL; + a->ptrs[n].aligned = NULL; + a->ptrs_in_use--; + if(a->ptrs_in_use == 0) { + free(a->ptrs); + a->ptrs = NULL; + a->ptrs_max = 0; + free(a); + a = NULL; + SetAlignHandle(device, (void *)a); + } + return; + } + } + } + if(dvdread_verbose(device) >= 0) { + fprintf(stderr, "libdvdread: dvdalign_lbfree(), error trying to free mem: %08lx (%u)\n", (unsigned long)ptr, a ? a->ptrs_in_use : 0); + } +} + + +/* Private but located in/shared with dvd_reader.c */ +extern int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number, + size_t block_count, uint8_t *data, + int encrypted ); + +/** @internal + * Its required to either fail or deliver all the blocks asked for. + * + * @param data Pointer to a buffer where data is returned. This must be large + * enough to hold lb_number*2048 bytes. + * It must be aligned to system specific (2048) logical blocks size when + * reading from raw/O_DIRECT device. + */ +static int DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number, + size_t block_count, uint8_t *data, + int encrypted ) +{ + int ret; + size_t count = block_count; + + while(count > 0) { + + ret = UDFReadBlocksRaw(device, lb_number, count, data, encrypted); + + if(ret <= 0) { + /* One of the reads failed or nothing more to read, too bad. + * We won't even bother returning the reads that went ok. */ + return ret; + } + + count -= (size_t)ret; + lb_number += (uint32_t)ret; + } + + return block_count; +} + +#endif + +#ifndef NULL +#define NULL ((void *)0) +#endif + +struct Partition { + int valid; + char VolumeDesc[128]; + uint16_t Flags; + uint16_t Number; + char Contents[32]; + uint32_t AccessType; + uint32_t Start; + uint32_t Length; +}; + +struct AD { + uint32_t Location; + uint32_t Length; + uint8_t Flags; + uint16_t Partition; +}; + +struct extent_ad { + uint32_t location; + uint32_t length; +}; + +struct avdp_t { + struct extent_ad mvds; + struct extent_ad rvds; +}; + +struct pvd_t { + uint8_t VolumeIdentifier[32]; + uint8_t VolumeSetIdentifier[128]; +}; + +struct lbudf { + uint32_t lb; + uint8_t *data; +}; + +struct icbmap { + uint32_t lbn; + struct AD file; + uint8_t filetype; +}; + +struct udf_cache { + int avdp_valid; + struct avdp_t avdp; + int pvd_valid; + struct pvd_t pvd; + int partition_valid; + struct Partition partition; + int rooticb_valid; + struct AD rooticb; + int lb_num; + struct lbudf *lbs; + int map_num; + struct icbmap *maps; +}; + +typedef enum { + PartitionCache, RootICBCache, LBUDFCache, MapCache, AVDPCache, PVDCache +} UDFCacheType; + +#if 0 + +extern void *GetUDFCacheHandle(dvd_reader_t *device); +extern void SetUDFCacheHandle(dvd_reader_t *device, void *cache); + +#endif + +void FreeUDFCache(udf_t *udf, void *cache) +{ + int n; + + struct udf_cache *c = (struct udf_cache *)cache; + if(c == NULL) { + return; + } + + for(n = 0; n < c->lb_num; n++) { + if(c->lbs[n].data) { + /* free data */ + //dvdalign_lbfree(device, c->lbs[n].data); + free(c->lbs[n].data); + } + } + c->lb_num = 0; + + if(c->lbs) { + free(c->lbs); + } + if(c->maps) { + free(c->maps); + } + free(c); +} + +static int GetUDFCache(udf_t *udf, UDFCacheType type, + uint32_t nr, void *data) +{ + int n; + struct udf_cache *c; +/* + if(DVDUDFCacheLevel(device, -1) <= 0) { + return 0; + } +*/ + //c = (struct udf_cache *)GetUDFCacheHandle(device); + c = (struct udf_cache *) udf->cache; + + if(c == NULL) { + return 0; + } + + switch(type) { + case AVDPCache: + if(c->avdp_valid) { + *(struct avdp_t *)data = c->avdp; + return 1; + } + break; + case PVDCache: + if(c->pvd_valid) { + *(struct pvd_t *)data = c->pvd; + return 1; + } + break; + case PartitionCache: + if(c->partition_valid) { + *(struct Partition *)data = c->partition; + return 1; + } + break; + case RootICBCache: + if(c->rooticb_valid) { + *(struct AD *)data = c->rooticb; + return 1; + } + break; + case LBUDFCache: + for(n = 0; n < c->lb_num; n++) { + if(c->lbs[n].lb == nr) { + *(uint8_t **)data = c->lbs[n].data; + return 1; + } + } + break; + case MapCache: + for(n = 0; n < c->map_num; n++) { + if(c->maps[n].lbn == nr) { + *(struct icbmap *)data = c->maps[n]; + return 1; + } + } + break; + default: + break; + } + + return 0; +} + +static int SetUDFCache(udf_t *udf, UDFCacheType type, + uint32_t nr, void *data) +{ + int n; + struct udf_cache *c; +/* + if(DVDUDFCacheLevel(dev, -1) <= 0) { + return 0; + } +*/ + //c = (struct udf_cache *)GetUDFCacheHandle(dev); + c = (struct udf_cache *) udf->cache; + + if(c == NULL) { + c = (struct udf_cache*) calloc(1, sizeof(struct udf_cache)); + // fprintf(stderr, "calloc: %d\n", sizeof(struct udf_cache)); + if(c == NULL) { + return 0; + } + //SetUDFCacheHandle(udf, c); + udf->cache = (void*) c; + } + + + switch(type) { + case AVDPCache: + c->avdp = *(struct avdp_t *)data; + c->avdp_valid = 1; + break; + case PVDCache: + c->pvd = *(struct pvd_t *)data; + c->pvd_valid = 1; + break; + case PartitionCache: + c->partition = *(struct Partition *)data; + c->partition_valid = 1; + break; + case RootICBCache: + c->rooticb = *(struct AD *)data; + c->rooticb_valid = 1; + break; + case LBUDFCache: + for(n = 0; n < c->lb_num; n++) { + if(c->lbs[n].lb == nr) { + /* replace with new data */ + c->lbs[n].data = *(uint8_t **)data; + c->lbs[n].lb = nr; + return 1; + } + } + c->lb_num++; + c->lbs = (lbudf*) realloc(c->lbs, c->lb_num * sizeof(struct lbudf)); + /* + fprintf(stderr, "realloc lb: %d * %d = %d\n", + c->lb_num, sizeof(struct lbudf), + c->lb_num * sizeof(struct lbudf)); + */ + if(c->lbs == NULL) { + c->lb_num = 0; + return 0; + } + c->lbs[n].data = *(uint8_t **)data; + c->lbs[n].lb = nr; + break; + case MapCache: + for(n = 0; n < c->map_num; n++) { + if(c->maps[n].lbn == nr) { + /* replace with new data */ + c->maps[n] = *(struct icbmap *)data; + c->maps[n].lbn = nr; + return 1; + } + } + c->map_num++; + c->maps = (icbmap*) realloc(c->maps, c->map_num * sizeof(struct icbmap)); + /* + fprintf(stderr, "realloc maps: %d * %d = %d\n", + c->map_num, sizeof(struct icbmap), + c->map_num * sizeof(struct icbmap)); + */ + if(c->maps == NULL) { + c->map_num = 0; + return 0; + } + c->maps[n] = *(struct icbmap *)data; + c->maps[n].lbn = nr; + break; + default: + return 0; + } + + return 1; +} + +/* For direct data access, LSB first */ +#define GETN1(p) ((uint8_t)data[p]) +#define GETN2(p) ((uint16_t)data[p] | ((uint16_t)data[(p) + 1] << 8)) +#define GETN3(p) ((uint32_t)data[p] | ((uint32_t)data[(p) + 1] << 8) \ + | ((uint32_t)data[(p) + 2] << 16)) +#define GETN4(p) ((uint32_t)data[p] \ + | ((uint32_t)data[(p) + 1] << 8) \ + | ((uint32_t)data[(p) + 2] << 16) \ + | ((uint32_t)data[(p) + 3] << 24)) +/* This is wrong with regard to endianess */ +#define GETN(p, n, target) memcpy(target, &data[p], n) + +static int Unicodedecode( uint8_t *data, int len, char *target ) +{ + int p = 1, i = 0; + + if( ( data[ 0 ] == 8 ) || ( data[ 0 ] == 16 ) ) do { + if( data[ 0 ] == 16 ) p++; /* Ignore MSB of unicode16 */ + if( p < len ) { + target[ i++ ] = data[ p++ ]; + } + } while( p < len ); + + target[ i ] = '\0'; + return 0; +} + +static int UDFDescriptor( uint8_t *data, uint16_t *TagID ) +{ + *TagID = GETN2(0); + // TODO: check CRC 'n stuff + return 0; +} + +static int UDFExtentAD( uint8_t *data, uint32_t *Length, uint32_t *Location ) +{ + *Length = GETN4(0); + *Location = GETN4(4); + return 0; +} + +static int UDFShortAD( uint8_t *data, struct AD *ad, + struct Partition *partition ) +{ + ad->Length = GETN4(0); + ad->Flags = ad->Length >> 30; + ad->Length &= 0x3FFFFFFF; + ad->Location = GETN4(4); + ad->Partition = partition->Number; // use number of current partition + return 0; +} + +static int UDFLongAD( uint8_t *data, struct AD *ad ) +{ + ad->Length = GETN4(0); + ad->Flags = ad->Length >> 30; + ad->Length &= 0x3FFFFFFF; + ad->Location = GETN4(4); + ad->Partition = GETN2(8); + //GETN(10, 6, Use); + return 0; +} + +static int UDFExtAD( uint8_t *data, struct AD *ad ) +{ + ad->Length = GETN4(0); + ad->Flags = ad->Length >> 30; + ad->Length &= 0x3FFFFFFF; + ad->Location = GETN4(12); + ad->Partition = GETN2(16); + //GETN(10, 6, Use); + return 0; +} + +static int UDFICB( uint8_t *data, uint8_t *FileType, uint16_t *Flags ) +{ + *FileType = GETN1(11); + *Flags = GETN2(18); + return 0; +} + +static int UDFPartition( uint8_t *data, uint16_t *Flags, uint16_t *Number, + char *Contents, uint32_t *Start, uint32_t *Length ) +{ + *Flags = GETN2(20); + *Number = GETN2(22); + GETN(24, 32, Contents); + *Start = GETN4(188); + *Length = GETN4(192); + return 0; +} + +/** + * Reads the volume descriptor and checks the parameters. Returns 0 on OK, 1 + * on error. + */ +static int UDFLogVolume( uint8_t *data, char *VolumeDescriptor ) +{ + uint32_t lbsize, MT_L, N_PM; + Unicodedecode(&data[84], 128, VolumeDescriptor); + lbsize = GETN4(212); // should be 2048 + MT_L = GETN4(264); // should be 6 + N_PM = GETN4(268); // should be 1 + if (lbsize != DVD_VIDEO_LB_LEN) return 1; + return 0; +} + +static int UDFFileEntry( uint8_t *data, uint8_t *FileType, + struct Partition *partition, struct AD *ad ) +{ + uint16_t flags; + uint32_t L_EA, L_AD; + uint32_t p; + + UDFICB( &data[ 16 ], FileType, &flags ); + + /* Init ad for an empty file (i.e. there isn't a AD, L_AD == 0 ) */ + ad->Length = GETN4( 60 ); // Really 8 bytes a 56 + ad->Flags = 0; + ad->Location = 0; // what should we put here? + ad->Partition = partition->Number; // use number of current partition + + L_EA = GETN4( 168 ); + L_AD = GETN4( 172 ); + p = 176 + L_EA; + while( p < 176 + L_EA + L_AD ) { + switch( flags & 0x0007 ) { + case 0: UDFShortAD( &data[ p ], ad, partition ); p += 8; break; + case 1: UDFLongAD( &data[ p ], ad ); p += 16; break; + case 2: UDFExtAD( &data[ p ], ad ); p += 20; break; + case 3: + switch( L_AD ) { + case 8: UDFShortAD( &data[ p ], ad, partition ); break; + case 16: UDFLongAD( &data[ p ], ad ); break; + case 20: UDFExtAD( &data[ p ], ad ); break; + } + p += L_AD; + break; + default: + p += L_AD; break; + } + } + return 0; +} + +static int UDFFileIdentifier( uint8_t *data, uint8_t *FileCharacteristics, + char *FileName, struct AD *FileICB ) +{ + uint8_t L_FI; + uint16_t L_IU; + + *FileCharacteristics = GETN1(18); + L_FI = GETN1(19); + UDFLongAD(&data[20], FileICB); + L_IU = GETN2(36); + if (L_FI) Unicodedecode(&data[38 + L_IU], L_FI, FileName); + else FileName[0] = '\0'; + return 4 * ((38 + L_FI + L_IU + 3) / 4); +} + +/** + * Maps ICB to FileAD + * ICB: Location of ICB of directory to scan + * FileType: Type of the file + * File: Location of file the ICB is pointing to + * return 1 on success, 0 on error; + */ +static int UDFMapICB( udf_t *udf, struct AD ICB, uint8_t *FileType, + struct Partition *partition, struct AD *File ) +{ + uint8_t *LogBlock; + uint32_t lbnum; + uint16_t TagID; + struct icbmap tmpmap; + + lbnum = partition->Start + ICB.Location; + tmpmap.lbn = lbnum; + if(GetUDFCache(udf, MapCache, lbnum, &tmpmap)) { + *FileType = tmpmap.filetype; + *File = tmpmap.file; + return 1; + } + + //LogBlock = dvdalign_lbmalloc(device, 1); + LogBlock = (uint8_t*) malloc(2048); + if(!LogBlock) { + return 0; + } + + do { + //if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) { + if( read ( udf->dev, LogBlock, lbnum++, 1, 0 ) ) { + TagID = 0; + } else { + UDFDescriptor( LogBlock, &TagID ); + } + + if( TagID == 261 ) { + UDFFileEntry( LogBlock, FileType, partition, File ); + tmpmap.file = *File; + tmpmap.filetype = *FileType; + SetUDFCache(udf, MapCache, tmpmap.lbn, &tmpmap); + //dvdalign_lbfree(device, LogBlock); + free(LogBlock); + return 1; + }; + } while( ( lbnum <= partition->Start + ICB.Location + ( ICB.Length - 1 ) + / DVD_VIDEO_LB_LEN ) && ( TagID != 261 ) ); + + //dvdalign_lbfree(device, LogBlock); + free(LogBlock); + return 0; +} + +/** + * Dir: Location of directory to scan + * FileName: Name of file to look for + * FileICB: Location of ICB of the found file + * return 1 on success, 0 on error; + */ +static int UDFScanDir( udf_t *udf, struct AD Dir, char *FileName, + struct Partition *partition, struct AD *FileICB, + int cache_file_info) +{ + char filename[ MAX_UDF_FILE_NAME_LEN ]; + uint8_t *directory; + uint32_t lbnum; + uint16_t TagID; + uint8_t filechar; + uint32_t p; + uint8_t *cached_dir = NULL; + uint32_t dir_lba; + struct AD tmpICB; + int found = 0; + int in_cache = 0; + + /* Scan dir for ICB of file */ + lbnum = partition->Start + Dir.Location; + +// if(DVDUDFCacheLevel(device, -1) > 0) { + /* caching */ + + //if(!GetUDFCache(device, LBUDFCache, lbnum, &cached_dir)) { + if(!GetUDFCache(udf, LBUDFCache, lbnum, &cached_dir)) { + dir_lba = (Dir.Length + DVD_VIDEO_LB_LEN) / DVD_VIDEO_LB_LEN; + //if((cached_dir = dvdalign_lbmalloc(device, dir_lba)) == NULL) { + if((cached_dir = (uint8_t*) malloc(2048 * dir_lba)) == NULL) { + return 0; + } + //if( DVDReadLBUDF( device, lbnum, dir_lba, cached_dir, 0) <= 0 ) { + if( read( udf->dev, cached_dir, lbnum, dir_lba, 0) ) { + //dvdalign_lbfree(device, cached_dir); + free(cached_dir); + cached_dir = NULL; + } + SetUDFCache(udf, LBUDFCache, lbnum, &cached_dir); + } else { + in_cache = 1; + } + + if(cached_dir == NULL) { + return 0; + } + + p = 0; + + while( p < Dir.Length ) { + UDFDescriptor( &cached_dir[ p ], &TagID ); + if( TagID == 257 ) { + p += UDFFileIdentifier( &cached_dir[ p ], &filechar, + filename, &tmpICB ); + if(cache_file_info && !in_cache) { + uint8_t tmpFiletype; + struct AD tmpFile; + + if( !strcasecmp( FileName, filename ) ) { + *FileICB = tmpICB; + found = 1; + + } + UDFMapICB(udf, tmpICB, &tmpFiletype, + partition, &tmpFile); + } else { + if( !strcasecmp( FileName, filename ) ) { + *FileICB = tmpICB; + return 1; + } + } + } else { + if(cache_file_info && (!in_cache) && found) { + return 1; + } + return 0; + } + } + if(cache_file_info && (!in_cache) && found) { + return 1; + } + return 0; +// } + + //directory = dvdalign_lbmalloc(device, 2); + directory = (uint8_t*) malloc(2 * 2048); + if(!directory) { + return 0; + } +/* + if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) { + dvdalign_lbfree(device, directory); + return 0; + } +*/ + + if( read( udf->dev, directory, lbnum, 2, 0 ) <= 0 ) { + free(directory); + return 0; + } + + p = 0; + while( p < Dir.Length ) { + if( p > DVD_VIDEO_LB_LEN ) { + ++lbnum; + p -= DVD_VIDEO_LB_LEN; + Dir.Length -= DVD_VIDEO_LB_LEN; +/* + if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) { + dvdalign_lbfree(device, directory); + return 0; + } +*/ + if( read( udf->dev, directory, lbnum, 2, 0 ) <= 0 ) { + free(directory); + return 0; + } + } + UDFDescriptor( &directory[ p ], &TagID ); + if( TagID == 257 ) { + p += UDFFileIdentifier( &directory[ p ], &filechar, + filename, FileICB ); + if( !strcasecmp( FileName, filename ) ) { + //dvdalign_lbfree(device, directory); + free(directory); + return 1; + } + } else { + //dvdalign_lbfree(device, directory); + free(directory); + return 0; + } + } + + //dvdalign_lbfree(device, directory); + free(directory); + return 0; +} + +static int UDFGetAVDP( udf_t *udf, + struct avdp_t *avdp) +{ + uint8_t *Anchor; + uint32_t lbnum, MVDS_location, MVDS_length; + uint16_t TagID; + uint32_t lastsector; + int terminate; + struct avdp_t; + + if(GetUDFCache(udf, AVDPCache, 0, avdp)) { + return 1; + } + + /* Find Anchor */ + lastsector = 0; + lbnum = 256; /* Try #1, prime anchor */ + terminate = 0; + + //Anchor = dvdalign_lbmalloc(device, 1); + Anchor = (uint8_t*) malloc(2048); + if(!Anchor) { + return 0; + } + for(;;) { + //if( DVDReadLBUDF( device, lbnum, 1, Anchor, 0 ) > 0 ) { + if( ! read( udf->dev, Anchor, lbnum, 1, 0 ) ) { + UDFDescriptor( Anchor, &TagID ); + } else { + TagID = 0; + } + if (TagID != 2) { + /* Not an anchor */ + if( terminate ) { + //dvdalign_lbfree(device, Anchor); + free(Anchor); + errno = EMEDIUMTYPE; + return 0; /* Final try failed */ + } + + if( lastsector ) { + /* We already found the last sector. Try #3, alternative + * backup anchor. If that fails, don't try again. + */ + lbnum = lastsector; + terminate = 1; + } else { + /* TODO: Find last sector of the disc (this is optional). */ + if( lastsector ) { + /* Try #2, backup anchor */ + lbnum = lastsector - 256; + } else { + /* Unable to find last sector */ + //dvdalign_lbfree(device, Anchor); + free(Anchor); + errno = EMEDIUMTYPE; + return 0; + } + } + } else { + /* It's an anchor! We can leave */ + break; + } + } + /* Main volume descriptor */ + UDFExtentAD( &Anchor[ 16 ], &MVDS_length, &MVDS_location ); + avdp->mvds.location = MVDS_location; + avdp->mvds.length = MVDS_length; + + /* Backup volume descriptor */ + UDFExtentAD( &Anchor[ 24 ], &MVDS_length, &MVDS_location ); + avdp->rvds.location = MVDS_location; + avdp->rvds.length = MVDS_length; + + SetUDFCache(udf, AVDPCache, 0, avdp); + + //dvdalign_lbfree(device, Anchor); + free(Anchor); + return 1; +} + +/** + * Looks for partition on the disc. Returns 1 if partition found, 0 on error. + * partnum: Number of the partition, starting at 0. + * part: structure to fill with the partition information + */ +static int UDFFindPartition( udf_t *udf, int partnum, + struct Partition *part ) +{ + uint8_t *LogBlock; + uint32_t lbnum, MVDS_location, MVDS_length; + uint16_t TagID; + int i, volvalid; + struct avdp_t avdp; + + if(!UDFGetAVDP(udf, &avdp)) { + return 0; + } + + //LogBlock = dvdalign_lbmalloc(device, 1); + LogBlock = (uint8_t*) malloc(2048); + if(!LogBlock) { + return 0; + } + /* Main volume descriptor */ + MVDS_location = avdp.mvds.location; + MVDS_length = avdp.mvds.length; + + part->valid = 0; + volvalid = 0; + part->VolumeDesc[ 0 ] = '\0'; + i = 1; + do { + /* Find Volume Descriptor */ + lbnum = MVDS_location; + do { + + //if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) { + if( read( udf->dev, LogBlock, lbnum++, 1, 0 ) ) { + TagID = 0; + } else { + UDFDescriptor( LogBlock, &TagID ); + } + + if( ( TagID == 5 ) && ( !part->valid ) ) { + /* Partition Descriptor */ + UDFPartition( LogBlock, &part->Flags, &part->Number, + part->Contents, &part->Start, &part->Length ); + part->valid = ( partnum == part->Number ); + } else if( ( TagID == 6 ) && ( !volvalid ) ) { + /* Logical Volume Descriptor */ + if( UDFLogVolume( LogBlock, part->VolumeDesc ) ) { + /* TODO: sector size wrong! */ + } else { + volvalid = 1; + } + } + + } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 ) + / DVD_VIDEO_LB_LEN ) && ( TagID != 8 ) + && ( ( !part->valid ) || ( !volvalid ) ) ); + + if( ( !part->valid) || ( !volvalid ) ) { + /* Backup volume descriptor */ + MVDS_location = avdp.mvds.location; + MVDS_length = avdp.mvds.length; + } + } while( i-- && ( ( !part->valid ) || ( !volvalid ) ) ); + + //dvdalign_lbfree(device, LogBlock); + free(LogBlock); + /* We only care for the partition, not the volume */ + return part->valid; +} + +uint32_t UDFFindFile( udf_t *udf, char *filename, + uint32_t *filesize ) +{ + uint8_t *LogBlock; + uint32_t lbnum; + uint16_t TagID; + struct Partition partition; + struct AD RootICB, File, ICB; + char tokenline[ MAX_UDF_FILE_NAME_LEN ]; + char *token; + uint8_t filetype; + + if(filesize) { + *filesize = 0; + } + tokenline[0] = '\0'; + strcat( tokenline, filename ); + + + if(!(GetUDFCache(udf, PartitionCache, 0, &partition) && + GetUDFCache(udf, RootICBCache, 0, &RootICB))) { + /* Find partition, 0 is the standard location for DVD Video.*/ + if( !UDFFindPartition( udf, 0, &partition ) ) { + return 0; + } + SetUDFCache(udf, PartitionCache, 0, &partition); + + //LogBlock = dvdalign_lbmalloc(device, 1); + LogBlock = (uint8_t*) malloc(2048); + if(!LogBlock) { + return 0; + } + /* Find root dir ICB */ + lbnum = partition.Start; + do { + //if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) { + if( read( udf->dev, LogBlock, lbnum++, 1, 0 ) ) { + TagID = 0; + } else { + UDFDescriptor( LogBlock, &TagID ); + } + + /* File Set Descriptor */ + if( TagID == 256 ) { // File Set Descriptor + UDFLongAD( &LogBlock[ 400 ], &RootICB ); + } + } while( ( lbnum < partition.Start + partition.Length ) + && ( TagID != 8 ) && ( TagID != 256 ) ); + + //dvdalign_lbfree(device, LogBlock); + free(LogBlock); + + /* Sanity checks. */ + if( TagID != 256 ) { + return 0; + } + if( RootICB.Partition != 0 ) { + return 0; + } + SetUDFCache(udf, RootICBCache, 0, &RootICB); + } + + /* Find root dir */ + if( !UDFMapICB( udf, RootICB, &filetype, &partition, &File ) ) { + return 0; + } + if( filetype != 4 ) { + return 0; /* Root dir should be dir */ + } + { + int cache_file_info = 0; + /* Tokenize filepath */ + token = strtok(tokenline, "/"); + + while( token != NULL ) { + + if( !UDFScanDir( udf, File, token, &partition, &ICB, + cache_file_info)) { + return 0; + } + if( !UDFMapICB( udf, ICB, &filetype, &partition, &File ) ) { + return 0; + } + if(!strcmp(token, "VIDEO_TS")) { + cache_file_info = 1; + } + token = strtok( NULL, "/" ); + } + } + + /* Sanity check. */ + if( File.Partition != 0 ) { + return 0; + } + + if(filesize) { + *filesize = File.Length; + } + /* Hack to not return partition.Start for empty files. */ + if( !File.Location ) { + return 0; + } else { + return partition.Start + File.Location; + } +} + +/** + * Gets a Descriptor . + * Returns 1 if descriptor found, 0 on error. + * id, tagid of descriptor + * bufsize, size of BlockBuf (must be >= DVD_VIDEO_LB_LEN) + * and aligned for raw/O_DIRECT read. + */ +static int UDFGetDescriptor( udf_t *udf, int id, + uint8_t *descriptor, int bufsize) +{ + uint32_t lbnum, MVDS_location, MVDS_length; + struct avdp_t avdp; + uint16_t TagID; + uint32_t lastsector; + int i, terminate; + int desc_found = 0; + /* Find Anchor */ + lastsector = 0; + lbnum = 256; /* Try #1, prime anchor */ + terminate = 0; + if(bufsize < DVD_VIDEO_LB_LEN) { + return 0; + } + + if(!UDFGetAVDP(udf, &avdp)) { + return 0; + } + + /* Main volume descriptor */ + MVDS_location = avdp.mvds.location; + MVDS_length = avdp.mvds.length; + + i = 1; + do { + /* Find Descriptor */ + lbnum = MVDS_location; + do { + + //if( DVDReadLBUDF( device, lbnum++, 1, descriptor, 0 ) <= 0 ) { + if( read( udf->dev, descriptor, lbnum++, 1, 0 ) ) { + TagID = 0; + } else { + UDFDescriptor( descriptor, &TagID ); + } + + if( (TagID == id) && ( !desc_found ) ) { + /* Descriptor */ + desc_found = 1; + } + } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 ) + / DVD_VIDEO_LB_LEN ) && ( TagID != 8 ) + && ( !desc_found) ); + + if( !desc_found ) { + /* Backup volume descriptor */ + MVDS_location = avdp.rvds.location; + MVDS_length = avdp.rvds.length; + } + } while( i-- && ( !desc_found ) ); + + + return desc_found; +} + +static int UDFGetPVD(udf_t *udf, struct pvd_t *pvd) +{ + uint8_t *pvd_buf; + + if(GetUDFCache(udf, PVDCache, 0, pvd)) { + return 1; + } + + //pvd_buf = dvdalign_lbmalloc(device, 1); + pvd_buf = (uint8_t*) malloc(2048); + if(!pvd_buf) { + return 0; + } + if(!UDFGetDescriptor( udf, 1, pvd_buf, 1*DVD_VIDEO_LB_LEN)) { + //dvdalign_lbfree(device, pvd_buf); + free(pvd_buf); + return 0; + } + + memcpy(pvd->VolumeIdentifier, &pvd_buf[24], 32); + memcpy(pvd->VolumeSetIdentifier, &pvd_buf[72], 128); + SetUDFCache(udf, PVDCache, 0, pvd); + + //dvdalign_lbfree(device, pvd_buf); + free(pvd_buf); + + return 1; +} + +/** + * Gets the Volume Identifier string, in 8bit unicode (latin-1) + * volid, place to put the string + * volid_size, size of the buffer volid points to + * returns the size of buffer needed for all data + */ +int UDFGetVolumeIdentifier(udf_t *udf, char *volid, + uint32_t volid_size) +{ + struct pvd_t pvd; + uint32_t volid_len; + + /* get primary volume descriptor */ + if(!UDFGetPVD(udf, &pvd)) { + return 0; + } + + volid_len = pvd.VolumeIdentifier[31]; + if(volid_len > 31) { + /* this field is only 32 bytes something is wrong */ + volid_len = 31; + } + if(volid_size > volid_len) { + volid_size = volid_len; + } + Unicodedecode(pvd.VolumeIdentifier, volid_size, volid); + + return volid_len; +} + +/** + * Gets the Volume Set Identifier, as a 128-byte dstring (not decoded) + * WARNING This is not a null terminated string + * volsetid, place to put the data + * volsetid_size, size of the buffer volsetid points to + * the buffer should be >=128 bytes to store the whole volumesetidentifier + * returns the size of the available volsetid information (128) + * or 0 on error + */ +int UDFGetVolumeSetIdentifier(udf_t *udf, uint8_t *volsetid, + uint32_t volsetid_size) +{ + struct pvd_t pvd; + + /* get primary volume descriptor */ + if(!UDFGetPVD(udf, &pvd)) { + return 0; + } + + + if(volsetid_size > 128) { + volsetid_size = 128; + } + + memcpy(volsetid, pvd.VolumeSetIdentifier, volsetid_size); + + return 128; +} + diff --git a/console/readdvd/dvd_udf.h b/console/readdvd/dvd_udf.h new file mode 100644 index 0000000..8262ae6 --- /dev/null +++ b/console/readdvd/dvd_udf.h @@ -0,0 +1,80 @@ +/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */ +#ifndef DVD_UDF_H_INCLUDED +#define DVD_UDF_H_INCLUDED + +/* + * This code is based on dvdudf by: + * Christian Wolff . + * + * Modifications by: + * Billy Biggs . + * Björn Englund . + * + * dvdudf: parse and read the UDF volume information of a DVD Video + * Copyright (C) 1999 Christian Wolff for convergence integrated media + * GmbH The author can be reached at scarabaeus@convergence.de, the + * project's page is at http://linuxtv.org/dvd/ + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. Or, point your browser to + * http://www.gnu.org/copyleft/gpl.html + */ + +#if defined(HAVE_INTTYPES_H) +#include +#elif defined(HAVE_STDINT_H) +#include +#endif + +//#include "dvd_reader.h" + +//#ifdef __cplusplus +//extern "C" { +//#endif + +#define DVD_VIDEO_LB_LEN 2048 +#define MAX_UDF_FILE_NAME_LEN 2048 + +/** + * Looks for a file on the UDF disc/imagefile and returns the block number + * where it begins, or 0 if it is not found. The filename should be an + * absolute pathname on the UDF filesystem, starting with '/'. For example, + * '/VIDEO_TS/VTS_01_1.IFO'. On success, filesize will be set to the size of + * the file in bytes. + * This implementation relies on that the file size is less than 2^32 + * A DVD file can at most be 2^30 (-2048 ?). + */ + +#include + +struct udf_t { + drive_info *dev; + void* cache; +}; + +unsigned int UDFFindFile( udf_t *udf, char *filename, unsigned int *size ); + +void FreeUDFCache( udf_t *udf, void *cache); + +int UDFGetVolumeIdentifier( udf_t *udf, + char *volid, unsigned int volid_size); +int UDFGetVolumeSetIdentifier( udf_t *udf, + unsigned char *volsetid, unsigned int volsetid_size); + +//#ifdef __cplusplus +//}; +//#endif +#endif /* DVD_UDF_H_INCLUDED */ + diff --git a/console/readdvd/imgwriter.cpp b/console/readdvd/imgwriter.cpp new file mode 100644 index 0000000..9766b5f --- /dev/null +++ b/console/readdvd/imgwriter.cpp @@ -0,0 +1,82 @@ +/* + * + * image writer class for DeadDiscReader + * Copyright (C) 2007, 2010, Gennady "ShultZ" Kozlov + * + */ + +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include + +#include +#include "imgwriter.h" + +#ifndef HAVE_FOPEN64 +#define fopen64 fopen +#endif + +imgwriter::imgwriter(char* fn, smap* map) { + mutex = new Mutex(); + fname=fn; + +#if !( defined(HAVE_FSEEKO) && defined(OFFT_64BIT) ) && !defined(HAVE_FSEEK64) + printf("Warning! No 64-bit file offset. Image size limits to 2GiB\n"); +#endif + if (!(iso = fopen64(fname,"r+"))){ + printf("can't open image file, creating new one!\n"); + if (!(iso = fopen64(fname,"w+"))){ + printf("can't create image file!\n"); + } + } else { + printf("image opened: '%s'\n", fname); + map->load(); + } + if (iso) fclose(iso); +} + +imgwriter::~imgwriter() { + delete mutex; +} + +int imgwriter::write(int lba, int scnt, int ssz, void* buff) { + int res=0; +#if defined(HAVE_FSEEKO) && defined(OFFT_64BIT) + off_t offs = ssz*(off_t)lba; +#else + int64_t offs = ssz*(int64_t)lba; +#endif + + mutex->lock(); + iso = fopen64(fname, "r+"); + + if (iso) { +#if defined(HAVE_FSEEKO) && defined(OFFT_64BIT) + if (fseeko(iso, offs, SEEK_SET)) +#elif defined(HAVE_FSEEK64) + if (fseek64(iso, offs, SEEK_SET)) +#else + if (fseek(iso, offs, SEEK_SET)) +#endif + { + printf("\nseek() failed! Offs: %lld (%08LX)\n", + static_cast(offs), + static_cast(offs)); + mutex->unlock(); + return 0; + } + res = fwrite(buff, ssz, scnt, iso); +// printf("\nwrote: %ld of %ld\n", res, scnt); + fclose(iso); + } + mutex->unlock(); + return res; +} + +//void imgwriter::set_file(char* fn) { fname=fn; } + +//int imgwriter::open(){} + +//int imgwriter::close(){} diff --git a/console/readdvd/imgwriter.h b/console/readdvd/imgwriter.h new file mode 100644 index 0000000..d748f79 --- /dev/null +++ b/console/readdvd/imgwriter.h @@ -0,0 +1,37 @@ +/* + * + * image writer class header + * Copyright (C) 2007, 2010, Gennady "ShultZ" Kozlov + * + */ + +#ifndef __IMGWRITER_H +#define __IMGWRITER_H + +#define _FILE_OFFSET_BITS 64 + +#include +#include + +class smap; + +class imgwriter { +public: + imgwriter(char* fn, smap* map); + ~imgwriter(); + int write(int lba, int scnt, int ssz, void* buff); +// void set_file(char* fn); +// int open(); +// int close(); + imgwriter(const imgwriter &in) = delete; + imgwriter(imgwriter &&in) = delete; + imgwriter& operator=(const imgwriter &in) = delete; + +private: + char* fname; + FILE* iso; + Mutex* mutex; +}; + +#endif + diff --git a/console/readdvd/kbhit.cpp b/console/readdvd/kbhit.cpp new file mode 100644 index 0000000..c2e5b08 --- /dev/null +++ b/console/readdvd/kbhit.cpp @@ -0,0 +1,74 @@ +// KBHIT.CPP + +#include "kbhit.h" + + +#if defined (__unix) || defined (__unix__) +#include // read() +#elif defined (_WIN32) +#include +#endif + +keyboard::keyboard() +{ +#if defined (__unix) || defined (__unix__) + tcgetattr(0,&initial_settings); + new_settings = initial_settings; + new_settings.c_lflag &= ~ICANON; + new_settings.c_lflag &= ~ECHO; + new_settings.c_lflag &= ~ISIG; + new_settings.c_cc[VMIN] = 1; + new_settings.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &new_settings); +#endif + peek_character=-1; +} + +keyboard::~keyboard() +{ +#if defined (__unix) || defined (__unix__) + tcsetattr(0, TCSANOW, &initial_settings); +#endif +} + +int keyboard::kb_hit() +{ +#if defined (__unix) || defined (__unix__) + unsigned char ch; + int nread; + if (peek_character != -1) return 1; + new_settings.c_cc[VMIN]=0; + tcsetattr(0, TCSANOW, &new_settings); + nread = read(0,&ch,1); + new_settings.c_cc[VMIN]=1; + tcsetattr(0, TCSANOW, &new_settings); + if (nread == 1) + { + peek_character = ch; + return 1; + } + return 0; +#elif defined (_WIN32) + return kbhit(); +#endif +} + +int keyboard::kb_getch() +{ +#if defined (__unix) || defined (__unix__) + char ch; + if (peek_character != -1) { + ch = peek_character; + peek_character = -1; + } else { + if (!read(0,&ch,1)) return 0; + } + return ch; +#elif defined (_WIN32) + if (kbhit()) + return getch(); + else + return 0; +#endif +} + diff --git a/console/readdvd/kbhit.h b/console/readdvd/kbhit.h new file mode 100644 index 0000000..f2a5bf0 --- /dev/null +++ b/console/readdvd/kbhit.h @@ -0,0 +1,29 @@ +// KBHIT.H + +#ifndef KBHIT_H +#define KBHIT_H + +#if defined (__unix) || defined (__unix__) +#include +#endif + +class keyboard +{ +public: + + keyboard(); + ~keyboard(); + int kb_hit(); + int kb_getch(); + +private: + +#if defined (__unix) || defined (__unix__) + struct termios initial_settings, new_settings; +#endif + int peek_character; + +}; + +#endif + diff --git a/console/readdvd/reader.cpp b/console/readdvd/reader.cpp new file mode 100644 index 0000000..77347ba --- /dev/null +++ b/console/readdvd/reader.cpp @@ -0,0 +1,81 @@ +/* + * + * reader class for DeadDiscReader + * Copyright (C) 2007,2009, Gennady "ShultZ" Kozlov + * + */ + +#define _FILE_OFFSET_BITS 64 + + +#include +#include +//#include + +#include +#include +//#include + +#include +#include + +#include "reader.h" + +#define sector_sz 2048 + +cdvdreader::cdvdreader( + int itidx, int iparent, drive_info* idev, smap* imap, imgwriter* iiso, int ipass, int itries) +{ + parm.running=0; + parm.stop=0; + parm.tidx=itidx; + parm.parent=iparent; + parm.dev=idev; + parm.map=imap; + parm.iso=iiso; + parm.pass=ipass; + parm.tries=itries; + +// printf("%02d: device: %s\n", parm.tidx, parm.dev->device); +// printf("%02d: %s %s %s\n", parm.tidx, parm.dev->ven, parm.dev->dev, parm.dev->fw); +} + +cdvdreader::~cdvdreader() { +} + +void cdvdreader::set_dev(drive_info* idev) { parm.dev=idev; } + +void cdvdreader::set_map(smap* imap) { parm.map=imap; } + +void cdvdreader::set_iso(imgwriter* iiso) { parm.iso=iiso; } + +void cdvdreader::set_pass(int ipass) { parm.pass=ipass; } + +void cdvdreader::set_retry(int itries) { parm.tries=itries; } + +int cdvdreader::start() { +// printf("Creating thread for %s\n", parm.dev->device); + parm.running=1; + return thread_create(&tid, NULL,read_disc,(void*)(&parm)); +// printf("pthread for %s created successfully:)\n", parm.dev->device); +} + +void cdvdreader::stop() { parm.stop=1; } + +bool cdvdreader::stoped() { return parm.stop; } + +void cdvdreader::wait() { +// int x; + while (parm.running) msleep(1); +// x= + thread_join(tid, NULL); +// printf("thread %d exit state: %d\n", parm.tidx, x); +} + +int cdvdreader::running() { return parm.running; } + +int cdvdreader::print_stat() { + printf("%s: %7d (%06x) sectors read\n", parm.dev->device, parm.cnt_ok, parm.cnt_ok); + return parm.cnt_ok; +} + diff --git a/console/readdvd/reader.h b/console/readdvd/reader.h new file mode 100644 index 0000000..51dd524 --- /dev/null +++ b/console/readdvd/reader.h @@ -0,0 +1,66 @@ +/* + * + * reader class header + * Copyright (C) 2007,2009, Gennady "ShultZ" Kozlov + * + */ + +#ifndef __READER_H +#define __READER_H + +#define _FILE_OFFSET_BITS 64 + +//#include +//#include + +//#include +//#include + +#include + +const char PASS_FIRST = 0; +const char PASS_CONT = 1; +const char PASS_RECOVER = 2; +const char PASS_RECOVER0= PASS_RECOVER; +const char PASS_RECOVER1= 3; + +class drive_info; +class map; +class imgwriter; + +typedef struct { + int tidx, parent; + drive_info* dev; + smap* map; + imgwriter* iso; + int running; + int stop; + int result; + int cnt_ok; + int tries; + char pass; +} rdparm_t; + +class cdvdreader { +public: + cdvdreader(int itidx, int iparent, drive_info* idev, smap* imap, imgwriter* iiso, int ipass=0, int itries=4); + ~cdvdreader(); + void set_dev(drive_info* idev); + void set_map(smap* imap); + void set_iso(imgwriter* iiso); + void set_pass(int ipass); + void set_retry(int itries); + int start(); + void stop(); + bool stoped(); + void wait(); + int running(); + int print_stat(); + +private: + thread_t tid; + rdparm_t parm; +}; + +#endif + diff --git a/console/readdvd/reader_disc.cpp b/console/readdvd/reader_disc.cpp new file mode 100644 index 0000000..8b0af99 --- /dev/null +++ b/console/readdvd/reader_disc.cpp @@ -0,0 +1,574 @@ +/* + * + * disc reader algo for DeadDiscReader + * Copyright (C) 2007-2009, Gennady "ShultZ" Kozlov + * + * + * initAllCSSKeys function from libdvdread + * + */ + +#define _FILE_OFFSET_BITS 64 + +#include +#include + +#if (DVDCSS_KEY_CACHE > 0) +#include +#include +#endif + +#include + +#include +#include +#include + +#include + +#include +#include +#include "reader_disc.h" + +#define sector_sz 2048 + +#if (DVDCSS_KEY_CACHE > 0) +static int initCSSCache(drive_info *dev) +{ + char psz_buffer[PATH_MAX]; + char *psz_home; + char *psz_cache; + + psz_home = getenv( "HOME" ); + if( psz_home == NULL ) + psz_home = getenv( "USERPROFILE" ); + + /* Cache our keys in ${HOME}/.dvdcss/ */ + if( psz_home ) + { + snprintf( psz_buffer, PATH_MAX, "%s/.dvdcss", psz_home ); + psz_buffer[PATH_MAX-1] = '\0'; + psz_cache = psz_buffer; + } + + + if( psz_cache != NULL ) + { + /* Check that we can add the ID directory and the block filename */ + if( strlen( psz_cache ) + 1 + 32 + 1 + (DVD_KEY_SIZE * 2) + 10 + 1 > PATH_MAX ) + { + printf( "CSS: cache directory name is too long\n" ); + psz_cache = NULL; + } + } + + if( !psz_cache ) goto nocache; + + /* If the cache is enabled, write the cache directory tag */ + if( psz_cache ) + { + char *psz_tag = "Signature: 8a477f597d28d172789f06886806bc55\r\n" + "# This file is a cache directory tag created by libdvdcss.\r\n" + "# For information about cache directory tags, see:\r\n" + "# http://www.brynosaurus.com/cachedir/\r\n"; + char psz_tagfile[PATH_MAX + 1 + 12 + 1]; + int i_fd; + + sprintf( psz_tagfile, "%s/CACHEDIR.TAG", psz_cache ); + i_fd = open( psz_tagfile, O_RDWR|O_CREAT, 0644 ); + if( i_fd >= 0 ) + { + write( i_fd, psz_tag, strlen(psz_tag) ); + close( i_fd ); + } + } + + /* If the cache is enabled, extract a unique disc ID */ + if( psz_cache ) + { + unsigned char p_sector[DVDCSS_BLOCK_SIZE]; + // char psz_debug[PATH_MAX + 30]; + char psz_key[1 + DVD_KEY_SIZE * 2 + 1]; + char *psz_title; + unsigned char *psz_serial; + int i; + + /* We read sector 0. If it starts with 0x000001ba (BE), we are + * reading a VOB file, and we should not cache anything. */ + + if (!read( dev, p_sector, 0, 1 )) + { + goto nocache; + } + + if( p_sector[0] == 0x00 && p_sector[1] == 0x00 + && p_sector[2] == 0x01 && p_sector[3] == 0xba ) + { + goto nocache; + } + + /* The data we are looking for is at sector 16 (32768 bytes): + * - offset 40: disc title (32 uppercase chars) + * - offset 813: manufacturing date + serial no (16 digits) */ + + if (!read( dev, p_sector, 16, 1 )) + { + goto nocache; + } + + /* Get the disc title */ + psz_title = (char *)p_sector + 40; + psz_title[32] = '\0'; + + for( i = 0 ; i < 32 ; i++ ) + { + if( psz_title[i] <= ' ' ) + { + psz_title[i] = '\0'; + break; + } + else if( psz_title[i] == '/' || psz_title[i] == '\\' ) + { + psz_title[i] = '-'; + } + } + + /* Get the date + serial */ + psz_serial = p_sector + 813; + psz_serial[16] = '\0'; + + /* Check that all characters are digits, otherwise convert. */ + for( i = 0 ; i < 16 ; i++ ) + { + if( psz_serial[i] < '0' || psz_serial[i] > '9' ) + { + char psz_tmp[16 + 1]; + sprintf( psz_tmp, + "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", + psz_serial[0], psz_serial[1], psz_serial[2], + psz_serial[3], psz_serial[4], psz_serial[5], + psz_serial[6], psz_serial[7] ); + memcpy( psz_serial, psz_tmp, 16 ); + break; + } + } + + /* Get disk key, since some discs have got same title, manufacturing + * date and serial number, but different keys */ + if( dev->media.dvdcss.protection == 0x01 ) + { + psz_key[0] = '-'; + for( i = 0; i < DVD_KEY_SIZE; i++ ) + { + sprintf( &psz_key[1+i*2], "%.2x", dev->media.dvdcss.DK[i] ); + } + psz_key[1 + DVD_KEY_SIZE * 2] = '\0'; + } + else + { + psz_key[0] = 0; + } + + /* We have a disc name or ID, we can create the cache dir */ + i = sprintf( dev->media.dvdcss.psz_cachefile, "%s", psz_cache ); + if (mkdir( dev->media.dvdcss.psz_cachefile, 0755 ) < 0 && errno != EEXIST ) + { + printf( "CSS: failed creating cache directory\n" ); + dev->media.dvdcss.psz_cachefile[0] = '\0'; + goto nocache; + } + + i += sprintf( dev->media.dvdcss.psz_cachefile + i, "/%s-%s%s", psz_title, + psz_serial, psz_key ); + if ( mkdir( dev->media.dvdcss.psz_cachefile, 0755 ) < 0 && errno != EEXIST ) + { + printf( "CSS: failed creating cache subdirectory\n" ); + dev->media.dvdcss.psz_cachefile[0] = '\0'; + goto nocache; + } + i += sprintf( dev->media.dvdcss.psz_cachefile + i, "/"); + + /* Pointer to the filename we will use. */ + dev->media.dvdcss.psz_block = dev->media.dvdcss.psz_cachefile + i; + + printf( "CSS: using CSS key cache dir: %s\n", dev->media.dvdcss.psz_cachefile ); + } +nocache: + return 0; +} +#endif + + +/* Loop over all titles and call dvdcss_title to crack the keys. */ +static int initAllCSSKeys( drive_info *dev ) +{ + struct timeval all_s, all_e; + struct timeval t_s, t_e; + char filename[ MAX_UDF_FILE_NAME_LEN ]; + unsigned int start, len; + int title; +// int stitle; +// bool st0; + + udf_t udf; +/* + char *nokeys_str = getenv("DVDREAD_NOKEYS"); + if(nokeys_str != NULL) + return 0; +*/ + udf.dev = dev; + udf.cache = NULL; + + if (!dev->silent) { + printf( "libdvdread: Attempting to retrieve all CSS keys\n" ); + } + + gettimeofday(&all_s, NULL); + + for( title = 0; title < 100; title++ ) { +// stitle=0; st0=0; + gettimeofday( &t_s, NULL ); + if( title == 0 ) { + sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" ); + } else { + sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 0 ); + } + start = UDFFindFile( &udf, filename, &len ); + if( start != 0 && len != 0 ) { + /* Perform CSS key cracking for this title. */ +// st0=1; + if (!dev->silent) { + printf( "libdvdread: Get key for %s at 0x%08x, len %10u\n", filename, start, len ); + } + if( css_title( dev, (int)start ) < 0 ) { + if (!dev->silent) { + printf( "libdvdread: Error cracking CSS key for %s (0x%08x)\n", filename, start); + } + } + gettimeofday( &t_e, NULL ); + if (!dev->silent) { + printf( "libdvdread: Elapsed time %ld\n", + (long int) t_e.tv_sec - t_s.tv_sec ); + } + } + + if( title == 0 ) continue; + +// for (stitle = 1; stitle<9; stitle++) { + // printf("stitle=%d\n", stitle); + gettimeofday( &t_s, NULL ); + //sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, stitle ); + sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 1 ); + start = UDFFindFile( &udf, filename, &len ); + if( start == 0 || len == 0 ) break; + + /* Perform CSS key cracking for this title. */ + if (!dev->silent) { + printf( "libdvdread: Get key for %s at 0x%08x, len %10u\n", filename, start, len ); + } + if( css_title( dev, (int)start ) < 0 ) { + if (!dev->silent) { + printf( "libdvdread: Error cracking CSS key for %s (0x%08x)!!\n", filename, start); + } + } + gettimeofday( &t_e, NULL ); + if (!dev->silent) { + printf( "libdvdread: Elapsed time %ld\n", + (long int) t_e.tv_sec - t_s.tv_sec ); + } +// } +// if( (!st0) && (stitle !=0) && (start == 0 || len == 0) ) break; + } + title--; + + if (!dev->silent) { + printf( "libdvdread: Found %d VTS's\n", title ); + } + gettimeofday(&all_e, NULL); + if (!dev->silent) { + printf( "libdvdread: Elapsed time %ld\n", + (long int) all_e.tv_sec - all_s.tv_sec ); + } + FreeUDFCache( &udf, udf.cache); + if (!dev->silent) { + printf("Title keys:\n"); + + dvd_title_t *p_title = dev->media.dvdcss.p_titles; + while( p_title != NULL ) +// && p_title->p_next != NULL + { + printf("STA: %8X, KEY: %02X:%02X:%02X:%02X:%02X\n",p_title->i_startlb, + p_title->p_key[0], + p_title->p_key[1], + p_title->p_key[2], + p_title->p_key[3], + p_title->p_key[4]); + + p_title = p_title->p_next; + } + } + return 0; +} + +void *read_disc(void* arg) { + rdparm_t* parm = (rdparm_t*) arg; + drive_info* dev = parm->dev; + smap* map = parm->map; + imgwriter* iso = parm->iso; + parm->stop=0; + parm->running=1; + parm->cnt_ok = 0; + printf("%s: Starting reader thread #%d...\n", dev->device, parm->tidx); + dvd_title_t *p_title = NULL; + uint32_t title_next = 0xFFFFFFFF; + + struct timeval t_s, t_e; + int32_t sects1X=75; + uint32_t lba = 0; + int32_t scnt=1; + int32_t ctry=0; + bool descramble=0; + + dev->silent=0; + + if (dev->media.type & DISC_CD) { + printf("%s: media is CD\n",dev->device); + scnt=15; + sects1X=75; + } else if (dev->media.type & DISC_DVD) { + printf("%s: media is DVD\n",dev->device); + scnt=16; + sects1X=693; + get_rpc_state(dev); + read_disc_regions(dev); + + if (!dev->media.dvdcss.protection) { + printf("DVD is NOT copy-protected\n"); + } else { + switch (dev->media.dvdcss.protection) { + case 0x01: + printf("DVD is CSS-protected\n"); + break; + case 0x02: + printf("DVD is CPRM-protected\n"); + break; + default: + printf("Unknown DVD protection shceme!\n"); + break; + } + printf("Disc regions : "); + if (dev->media.dvdcss.regmask != 0xFF) { + for (int i=0; i<8; i++) + if (!((dev->media.dvdcss.regmask >> i) & 1)) + {printf("%d",i+1); /*dev->rpc.region = i+1;*/} + printf("\n"); + } else { + printf("Invalid region mask!\n"); + } + } + + + switch (dev->media.dvdcss.protection) { + case 0: // unprotected DVD + dev->media.dvdcss.method = DVDCSS_METHOD_NONE; + break; + case 1: // CSS/CPPM protected DVD + // just to auth to be able read data +// scnt=1; + dev->media.dvdcss.method = DVDCSS_METHOD_KEY; + //dev->media.dvdcss.method = DVDCSS_METHOD_DISC; + if (css_disckey(dev)) { + printf("DVD auth failure!\n"); + goto exit_reader; + } + initAllCSSKeys( dev ); + break; + case 2: // CPRM - protected DVD + // just to auth to be able read data + dev->media.dvdcss.method = DVDCSS_METHOD_KEY; + if (css_disckey(dev)) { + printf("DVD auth failure!\n"); + goto exit_reader; + } + break; + default: + // printf("Unknown DVD protection scheme: %02X\n",dev->media.dvdcss.protection); + dev->media.dvdcss.method = DVDCSS_METHOD_NONE; + break; + } + } else { + printf("Unsupported media!\n"); + goto exit_reader; + } + + if (parm->pass >= PASS_RECOVER) scnt=1; + + map->lock(); + if (parm->pass < PASS_RECOVER) { + map->get_next(&lba, BM_WAIT, &scnt); + } else { + bool rce,wce; + printf("Recover pass - trying to disable read cache...\n"); + get_cache(dev,&rce,&wce); + if (!rce) { + printf("Read cache already disabled:)\n"); + } else { + if (set_cache(dev,0,wce)) { + printf("It seems drive does not support disabling read cache!\n"); + } else { + printf("Read cache disabled successfully\n"); + } + } + map->get_next(&lba, BM_FAIL, &scnt); + } + map->set(lba, BM_READ, scnt); + map->unlock(); + + if (dev->media.dvdcss.protection == 0x01) { + for (int i=0; imedia.dvdcss.TK[i]=0; + p_title = dev->media.dvdcss.p_titles; + descramble = 0; + if (!p_title) { + title_next = 0xFFFFFFFF; + } else { + title_next = p_title->i_startlb; + } + printf("\nSetting title key: %02X:%02X:%02X:%02X:%02X for %x-%x, CSS=%d\n", + dev->media.dvdcss.TK[0], + dev->media.dvdcss.TK[1], + dev->media.dvdcss.TK[2], + dev->media.dvdcss.TK[3], + dev->media.dvdcss.TK[4], + 0, title_next, + descramble); + } + + gettimeofday(&t_e,NULL); + seek(dev,0); + while (lba < dev->media.capacity) { + if (lba == 0xFFFFFFFF) goto exit_reader; + t_s.tv_sec = t_e.tv_sec; + t_s.tv_usec = t_e.tv_usec; + + if (dev->media.type & DISC_CD) { + read(dev, dev->rd_buf, lba, scnt); + } else if (dev->media.type & DISC_DVD) { + if (dev->media.dvdcss.protection == 0x01) { + + if (lba >= title_next && p_title) { + // scnt = 16; + while (lba >= title_next) { + if (p_title->p_next) { + title_next = p_title->p_next->i_startlb; + } else { + title_next = 0xFFFFFFFF; + } + if (lba >= title_next) p_title = p_title->p_next; + } + + memcpy( dev->media.dvdcss.TK, p_title->p_key, sizeof(dvd_key_t) ); + if( ! memcmp( dev->media.dvdcss.TK, "\0\0\0\0\0", 5 ) ) descramble = 0; else descramble=1; + printf("\nSetting title key: %02X:%02X:%02X:%02X:%02X for %x-%x, CSS=%d\n", + dev->media.dvdcss.TK[0], + dev->media.dvdcss.TK[1], + dev->media.dvdcss.TK[2], + dev->media.dvdcss.TK[3], + dev->media.dvdcss.TK[4], + lba, title_next, + descramble); + p_title = p_title->p_next; + } + + read_dvd(dev, dev->rd_buf, lba, scnt, descramble ? DVDCSS_READ_DECRYPT : 0); + } else { + read(dev, dev->rd_buf, lba, scnt); + } + } + gettimeofday(&t_e,NULL); + + printf("%s: %5.2f X, lba %7u / %7d ( %06x / %06x ) scnt=%d\r", + dev->device, + ((float)scnt/(float)sects1X)/((t_e.tv_sec - t_s.tv_sec) + (t_e.tv_usec - t_s.tv_usec) / 1000000.0), + lba, dev->media.capacity, + lba, dev->media.capacity, +// map->get_done() * 100 / dev->media.capacity, + scnt); + if ((dev->err & 0x0FFF00) == 0x23A00) { + printf("%s: media removed!\n", dev->device); + map->lock(); + map->set(lba, BM_WAIT, scnt); + map->unlock(); + goto exit_reader; + } + + switch (parm->pass) { + case PASS_FIRST: + case PASS_CONT: + if (!dev->err) { + if (iso->write(lba, scnt, sector_sz, (void*)dev->rd_buf) < scnt) { + map->set(lba, BM_WAIT, scnt); + goto exit_reader; + } + ctry=0; + } else { + ctry++; + } + break; + case PASS_RECOVER0: + case PASS_RECOVER1: + default: + if (!dev->err) { + if (iso->write(lba, scnt, sector_sz, (void*)dev->rd_buf) < scnt) { + map->set(lba, BM_WAIT, scnt); + goto exit_reader; + } + ctry=0; + } else { + ctry++; + } + break; + } + + map->lock(); +// set current block state + if (!ctry) { + map->set(lba, BM_DONE, scnt); + parm->cnt_ok+=scnt; + lba+=scnt; + } else { + if (parm->pass < PASS_RECOVER) + map->set(lba, BM_FAIL, scnt); + if (ctry == parm->tries) ctry=0; + } + map->unlock(); + + if (parm->stop) goto exit_reader; + + map->lock(); +// get next block address + if (parm->pass < PASS_RECOVER1) { + if ((dev->media.type) & DISC_DVD && (dev->media.dvdcss.protection == 0x01)) { + if ((lba!=title_next) && (lba+scnt > title_next) && p_title) { + scnt = title_next - lba; + } else { + scnt = 16; + } + } + + map->get_next(&lba, BM_WAIT, &scnt); + map->set(lba, BM_READ, scnt); + } else { + lba+=scnt; + map->get_next(&lba, BM_FAIL, &scnt); + map->set(lba, BM_READ, scnt); + } + map->unlock(); + } + +exit_reader: + printf("%s: exit\n", dev->device); + parm->running=0; + parm->result=1; + thread_exit(parm->result); +} + diff --git a/console/readdvd/reader_disc.h b/console/readdvd/reader_disc.h new file mode 100644 index 0000000..7e4c3eb --- /dev/null +++ b/console/readdvd/reader_disc.h @@ -0,0 +1,14 @@ +/* + * + * disc reader functions for DeadDiscReader + * Copyright (C) 2007, Gennady "ShultZ" Kozlov + * + */ + +#ifndef __READERDISC_H +#define __READERDISC_H + +void *read_disc(void* arg); + +#endif + diff --git a/console/readdvd/sectmap.cpp b/console/readdvd/sectmap.cpp new file mode 100644 index 0000000..eb88d7e --- /dev/null +++ b/console/readdvd/sectmap.cpp @@ -0,0 +1,185 @@ +/* + * + * sector map class for DeadDiscReader + * Copyright (C) 2006-2007,2009, Gennady "ShultZ" Kozlov + * it uses QPxTool SCSI transport library + * + */ + +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include + +#include "sectmap.h" + +long fsize(FILE* f){ + struct stat st; + fstat(fileno(f), &st); + return st.st_size; +} + +smap::smap(char* fn, unsigned int sects) { + sectors=sects; + fname = fn; + blocks=(sectors/map_block_sz) + !!(sectors%map_block_sz); + arr=(map_block*)malloc(sizeof(map_block)*blocks); + fill(BM_WAIT); + mutex = new Mutex(); + printf("* map: created for %u sectors\n", sectors); +} + +smap::~smap() { + save(); + delete mutex; + delete arr; +} + +mape smap::get(unsigned int sector) { + if (sector > sectors) return BM_INV; + unsigned int row=sector/map_block_sz; + unsigned int col=sector%map_block_sz; + return arr[row][col]; +} + +int smap::is_done() { + return (sectors-get_done()); +} + +int smap::get_wait() { + unsigned int cnt=0; + for (unsigned int i=0; i sectors) scnt = sectors - lba; + +// printf("smap::get_next(%x,%d,%d)\n",lba ? *lba : -1,state, count ? *count : -1); + + if (lba) offs=*lba; + if (icount<2) { + while ((get(offs) != state) && (offs= sectors) { + if (icount > 2) { +// printf("Can't find %d sectors block", icount); + (*count)--; + return get_next(lba, state, count); + } else { + offs = 0xFFFFFFFF; + } + } + if (lba) *lba=offs; + if (count) *count = icount; + return offs; +} + + +void smap::set(unsigned int sector, mape state, int count) { +// if (sector>sectors) return 1; +// if (sector+count>sectors) return 2; + if(count) { + for (int i=0; i sectors) return; + arr[row][col]=state; +} + + +void smap::fill(mape state){ + for (unsigned int i=0; ilock(); } + +void smap::unlock() { mutex->unlock(); } + diff --git a/console/readdvd/sectmap.h b/console/readdvd/sectmap.h new file mode 100644 index 0000000..cbffa66 --- /dev/null +++ b/console/readdvd/sectmap.h @@ -0,0 +1,58 @@ +/* + * + * sector map class header + * Copyright (C) 2006-2007,2009, Gennady "ShultZ" Kozlov + * + */ + +#ifndef __SECTMAP_H +#define __SECTMAP_H + +#define _FILE_OFFSET_BITS 64 + +#include +#include + +enum mape { + BM_WAIT = 0, + BM_READ = 1, + BM_DONE = 2, + BM_FAIL = 3, + BM_INV = 15 +}; + +#define map_block_sz 16384 + +typedef mape map_block[map_block_sz]; + +extern long fsize(FILE* f); + +class smap { +public: + smap(char* fn, uint32_t sects); + ~smap(); + mape get(uint32_t sector); + int32_t is_done(); + int32_t get_wait(); + int32_t get_read(); + int32_t get_done(); + int32_t get_fail(); + int32_t get_tot(); + uint32_t get_next(uint32_t *lba, mape state, int32_t *count); + void set(uint32_t sector, mape state, int32_t count=0); + void set_one(uint32_t sector, mape state); + void fill(mape state); + void set_file(char* fn); + int32_t load(); + int32_t save(); + void lock(); + void unlock(); +private: + uint32_t sectors; + uint32_t blocks; + map_block* arr; + Mutex* mutex; + char* fname; +}; + +#endif diff --git a/console/readdvd/version.h b/console/readdvd/version.h new file mode 100644 index 0000000..c3dcf45 --- /dev/null +++ b/console/readdvd/version.h @@ -0,0 +1,12 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2006-2009 Gennady "ShultZ" Kozlov + * + * 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. + */ + +#define VERSION "1.0" diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..d43f5d8 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,371 @@ +qpxtool (0.8.1-1~bpo10+1) buster-backports; urgency=medium + + * Rebuild for buster-backports. + * Decrease debhelper-compat to 11. + * Add QT_SELECT variable in debian/rules. + + -- Boris Pek Tue, 17 Nov 2020 01:07:32 +0300 + +qpxtool (0.8.1-1) unstable; urgency=medium + + * New upstream release: + fix build with latest versions of Qt 5.x (Closes: #964693) + * Update debian/control: + bump debhelper-compat to 13 (was 12): no changes required + * Simplify debian/rules: + - remove QT_SELECT variable + - remove DEB_LDFLAGS_MAINT_APPEND with -Wl,--as-needed flag + * Update debian/watch. + + -- Boris Pek Tue, 10 Nov 2020 22:36:06 +0300 + +qpxtool (0.8.0-1) unstable; urgency=medium + + * New upstream release. + * Remove all patches: accepted in upstream. + * Remove all man pages: accepted in upstream. + * Remove debian/pxfw.manpages and debian/qpxtool.manpages. + * Update debian/pxfw.install and debian/qpxtool.install + * Update years in debian/copyright. + * Update debian/control: + - bump Standards-Version to 4.5.0 (was 4.4.0): no changes required + - replace build dependency from debhelper (>= 12~) to + debhelper-compat (= 12) + - add "Rules-Requires-Root: no" + - update Homepage URL + * Remove debian/compat. + * Add debian/upstream/metadata. + + -- Boris Pek Wed, 29 Jan 2020 00:25:38 +0300 + +qpxtool (0.7.2-6) unstable; urgency=medium + + * Add patch 10-fix-checking-of-libpng: + fixes enabling of PNG support in qpxtool. + + -- Boris Pek Fri, 13 Sep 2019 01:56:24 +0300 + +qpxtool (0.7.2-5) unstable; urgency=medium + + [ Boris Pek ] + * Port to Qt5: + - remove build dependencies from libqt4-dev and qt4-qmake + - add build dependencies from qtchooser, qtbase5-dev, qttools5-dev and + qttools5-dev-tools + - add patch 08-port-to-qt5.patch + (Closes: #875141) + * Bump Standards-Version to 4.4.0 (was 3.9.5). + * Bump debhelper version to 12~ (was 9); update debian/compat. + * Simplify debian/libqpx0.lintian-overrides. + * Update years in debian/copyright. + * Update maintainer's email address. + * Update debian/control: move package libqpx0 to Section libs. + * Update debian/rules: + - delete --list-missing option from dh + - delete get-orig-source section and all related variables + - add --no-parallel option for dh + - enable all hardening flags + * Delete extra files: + - debian/gbp.conf + - debian/pixmap/qpxtool.xpm + - debian/qpxtool.lintian-overrides + - debian/qpxtool.menu + * Update debian/qpxtool.install. + * Sort strings in debian/qpxtool.docs. + * Add debian/patches/09-fix-spelling-errors.patch. + * Fix spelling errors in debian/man/*.1 files. + + [ OndÅ™ej Nový ] + * d/copyright: Use https protocol in Format field + * d/control: Set Vcs-* to salsa.debian.org + * d/watch: Use https protocol + + -- Boris Pek Wed, 28 Aug 2019 04:03:45 +0300 + +qpxtool (0.7.2-4.1) unstable; urgency=medium + + * Non-maintainer upload. + * Add patch 07-fix-wr-mode-declarations.patch: + - Fix declaration of char attributes in wr_mode. (Closes: #856231) + + -- John Paul Adrian Glaubitz Mon, 27 Feb 2017 14:04:55 +0100 + +qpxtool (0.7.2-4) unstable; urgency=medium + + * Update debian/rules: delete option --parallel from dh command because it + causes FTBFS on buildd hosts where parallel build is realy enabled. + + -- Boris Pek Sun, 13 Apr 2014 00:18:54 +0400 + +qpxtool (0.7.2-3) unstable; urgency=medium + + * Simplify patch 05-add-hardening-flags-in-compiler-options.patch. + * Update debian/rules: + - simplify rules after changes in related patch + - add options --parallel and --list-missing to dh + * Update debian/qpxtool.install. + + -- Boris Pek Sat, 12 Apr 2014 20:25:56 +0400 + +qpxtool (0.7.2-2) unstable; urgency=medium + + * Update debian/control: + - udate using wrap-and-sort tool + - delete now useless sections "Breaks: qpxtool (<< 0.7.1.002-1)" + - add build dependency from hurd-dev on hurd-i386 + * Add patch 06-make-port-for-hurd-i386.patch: + Make port for Debian GNU/Hurd. + * Update debian/copyright. + + -- Boris Pek Sat, 12 Apr 2014 16:19:23 +0400 + +qpxtool (0.7.2-1) unstable; urgency=medium + + * New maintainer. (Closes: #629599) + * Update to stable release 0.7.2. + * Bump Standards-Version to 3.9.5 (was 3.9.3). + * Update patches + - add 01-do_not_use_lib64_dir.patch + - rewrite 03-ftbfs_kfreebsd.patch + - rewrite 02-desktop_file.patch + - update 04-fix-build-with-libpng15.patch + - delete 01-makefile.patch (it is unnecessary now) + * Update debian/rules: + - add necessary options to configure script for installing files + in according with Debian Policy + - add get-orig-source section + - simplify rules + * Fix lintian notes vcs-field-not-canonical. + * Update years in debian/copyright. + * Update debian/qpxtool.docs. + * Add debian/qpxtool.doc-base. + * Fix a lot of lintian notes hyphen-used-as-minus-sign: + - add fixed debian/man/* files + - add debian/qpxtool.manpages and debian/pxfw.manpages + - update debian/qpxtool.install and debian/pxfw.install + * Update debian/libqpx0.lintian-overrides. + + -- Boris Pek Wed, 26 Feb 2014 16:26:22 +0400 + +qpxtool (0.7.1.002-6) unstable; urgency=low + + * QA upload. + * Bumped Standards-Version to 3.9.3 (was 3.9.2). + * Bumped debhelper version to 9 (was 7.0.50~); updated debian/compat. + * Added file debian/patches/05-add-hardening-flags-in-compiler-options.patch. + * Updated debian/rules: added hardening flags in compiler options. + * Changed build dependency from libpng12-dev to libpng-dev. + Added file debian/patches/04-fix-build-with-libpng15.patch. + Added small hack in debian/rules (see LIBPNG_VER variable). + Build with libpng version 1.5.10 was tested successfully. + (Closes: #662481, #648127) + * Used [kfreebsd-any] instead of hardcoded list of kFreeBSD architectures + [kfreebsd-i386 kfreebsd-amd64] in build dependency. (Closes: #634714) + * File debian/copyright was updated in according to Copyright format 1.0. + * Fixed such lintian warnings and notes: + - out-of-date-copyright-format-uri + - hardening-no-relro and hardening-no-fortify-functions + + -- Boris Pek Tue, 26 Jun 2012 03:11:10 +0300 + +qpxtool (0.7.1.002-5) unstable; urgency=low + + * QA upload. + * Bump Standards. + * Orphaning this. + + -- Alessio Treglia Tue, 21 Jun 2011 13:04:12 +0200 + +qpxtool (0.7.1.002-4) unstable; urgency=low + + * We need both Breaks and Replaces fields. + + -- Alessio Treglia Wed, 06 Oct 2010 12:48:26 +0200 + +qpxtool (0.7.1.002-3) unstable; urgency=low + + * Link against libcam on kfreebsd to fix FTBFS. + * debian/control: + - Properly replace Conflicts/Replaces with Breaks fields. + - Bump Standards. + * Update debian/copyright. + + -- Alessio Treglia Wed, 06 Oct 2010 09:09:15 +0200 + +qpxtool (0.7.1.002-2) unstable; urgency=low + + * Correct little typo in the descriptions, thanks to Davide Prina for + reporting this (Closes: #582445). + * Change my email address. + + -- Alessio Treglia Sun, 06 Jun 2010 19:48:46 +0200 + +qpxtool (0.7.1.002-1) unstable; urgency=low + + * New maintainer (Closes: #543879). + * New upstream release. + * Move packaging to collab-maint's git area. + * Split libraries: + - libqpx0 (shared libs) + - libqpx-dev (development files) + * debian/control: + - Drop libqt3-mt-dev, it's no longer needed. + - Build-depend on qmake-qt4. + - Bump Standards. + - Set DM-Upload-Allowed to yes. + - Build-depend on libqt4-dev. + - Build-depend on libpng12-dev. + - Set Conflicts/Replaces proper fields. + * Switch to 3.0 (quilt) format. + * Switch to debhelper 7. + * Drop dpatch support: + - Drop 02-typos.dpatch patch, no longer needed. + - Drop 03-0.6.1.1.dpatch, no longer needed. + - Drop 04-libata.dpatch patch, no longer needed. + - 01-qtdir.dpatch -> 01-makefile.patch: + + Drop QTDIR which seems to be not respected. + + Set QMAKE4 properly. + + Add DEP-3 compliant tags. + * debian/patches/02-desktop_file.patch: + - Fix desktop file as per spec. + * debian/{libqpx0,qpxtool}.lintian-overrides: + - "MultiMediaMasters & Machinery" is a manufacturer's name and not a + spelling error. + * debian/rules: + - Cleanup. + - Fix clean rule by passing the QMAKE4 envar. + * Add watch file. + + -- Alessio Treglia Mon, 26 Apr 2010 09:36:21 +0200 + +qpxtool (0.6.1-5) unstable; urgency=low + + * Updating package to standards version 3.8.3. + * Removing vcs fields. + * Orphaning package. + + -- Daniel Baumann Thu, 27 Aug 2009 10:17:45 +0200 + +qpxtool (0.6.1-4) unstable; urgency=medium + + * Adding patch from Mirko Parthey + to make qpxtool work with libata (Closes: #463886). + * Rediffing 0.6.1.1.dpatch. + * Completing clean target in rules, thanks to Barry deFreese + (Closes: #442718). + * Reordering rules file. + * Using lintian debhelper to install lintian overrides. + * Rewriting copyright file in machine-interpretable format. + * Adding suggests to pxfw in qpxtool. + * Adding vcs fields to control file. + * Upgrading package to standards 3.8.0. + * Upgrading package to debhelper 7. + * Removing watch file. + * Reverting Makefile.qmake to upstream. + + -- Daniel Baumann Wed, 02 Jul 2008 12:42:00 +0200 + +qpxtool (0.6.1-3) unstable; urgency=low + + * Bumped to new policy. + * Using new homepage field in control. + * Don't hide make errors in clean target of rules. + * Added manpage for deadreader and pioquiet. + + -- Daniel Baumann Thu, 27 Dec 2007 14:13:00 +0100 + +qpxtool (0.6.1-2) unstable; urgency=low + + * Applied patch from upstream to fix FTBFS with linux 2.6.23, thanks to + Thomas Maguin . + + -- Daniel Baumann Tue, 23 Oct 2007 14:47:00 +0200 + +qpxtool (0.6.1-1) unstable; urgency=low + + * New upstream release: + - Including pxfw. + * Updated 02-typos.dpatch. + + -- Daniel Baumann Sun, 02 Sep 2007 17:50:00 +0200 + +qpxtool (0.6.0.9-5) unstable; urgency=low + + * Extendet 04-patch.dpatch to also cover install, not just build. + All build system caused breakages are now fixed (Closes: #413211). + + -- Daniel Baumann Thu, 08 Mar 2007 14:40:00 +0100 + +qpxtool (0.6.0.9-4) unstable; urgency=low + + * Reordered dpatches, thanks Eduard for taking care about it before. + * Added patch to disable another ldconfig call. + * Added lintian overrides. + + -- Daniel Baumann Thu, 08 Mar 2007 13:06:00 +0100 + +qpxtool (0.6.0.9-3) unstable; urgency=low + + * 02-libflags.dpatch, 03-typos.dpatch: dpatch'ified upstream relevant + changes from 0.6.0.9-2.1 + * 04-CWDA.dpatch: fixes typo in ./build, causing failure in a build + environment with invalid $HOME (closes: #413211) + + -- Eduard Bloch Tue, 06 Mar 2007 18:30:31 +0100 + +qpxtool (0.6.0.9-2.1) unstable; urgency=low + + * NMU, blessed by maintainer + * Added missing -fPIC flags on libraries, work around hidden build failures + on amd64 + * setting provisional SONAMEs on the supplementary libraries + * debian/rules: explicit removal of final build files + * qpxtool/qcheck/test_threads.cpp: minor typo fix + + -- Eduard Bloch Tue, 06 Mar 2007 17:03:01 +0100 + +qpxtool (0.6.0.9-2) unstable; urgency=low + + * Replaced 01-libdeps.dpatch with a patch from upstream. + + -- Daniel Baumann Sun, 04 Mar 2007 17:42:00 +0100 + +qpxtool (0.6.0.9-1) unstable; urgency=low + + * New upstream release. + * Removed 01-kfreebsd.dpatch, went upstream. + + -- Daniel Baumann Sat, 03 Mar 2007 10:49:00 +0100 + +qpxtool (0.5.4-1) unstable; urgency=low + + * New upstream release. + + -- Daniel Baumann Tue, 01 Aug 2006 13:13:00 +0200 + +qpxtool (0.5.3-3) unstable; urgency=low + + * Making sure, the patch gets really applied in the right order this time + (Closes: #376196). + + -- Daniel Baumann Sat, 08 Jul 2006 13:26:00 +0200 + +qpxtool (0.5.3-2) unstable; urgency=low + + * New email address. + * Added patch to fix FTBFS on kFreeBSD (Closes: #376196). + + -- Daniel Baumann Fri, 07 Jul 2006 08:33:00 +0200 + +qpxtool (0.5.3-1) unstable; urgency=low + + * New upstream release. + + -- Daniel Baumann Mon, 26 Jun 2006 14:03:00 +0200 + +qpxtool (0.5.0-1) unstable; urgency=low + + * Initial release (Closes: #357847). + * Added manpage. + + -- Daniel Baumann Tue, 21 Mar 2006 11:01:00 +0100 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..27adac3 --- /dev/null +++ b/debian/control @@ -0,0 +1,70 @@ +Source: qpxtool +Section: otherosfs +Priority: optional +Maintainer: Boris Pek +Build-Depends: debhelper-compat (= 11), + hurd-dev [hurd-i386], + libcam-dev [kfreebsd-any], + libpng-dev, + qtbase5-dev, + qtchooser, + qttools5-dev, + qttools5-dev-tools +Standards-Version: 4.5.0 +Rules-Requires-Root: no +Homepage: https://qpxtool.sourceforge.io/ +Vcs-Git: https://salsa.debian.org/debian/qpxtool.git +Vcs-Browser: https://salsa.debian.org/debian/qpxtool + +Package: qpxtool +Architecture: any +Depends: libqpx0 (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} +Suggests: pxfw +Description: CD/DVD quality checker + QPxTool gives you access to all available Quality Checks (Q-Checks) on written + and blank media, that are available for your drive. This will help you to find + the right media and the optimized writing speed for your hardware, which will + increase the chance for a long data lifetime. + . + QPxTool depends on the used drive, not all drives are able to read the Q-Checks + and not all of them are supported by QPxTool. Currently, most newer drives of + LiteOn, NEC, Pioneer and Plextor are supported. + +Package: pxfw +Architecture: any +Depends: libqpx0 (= ${binary:Version}), + qpxtool, + ${misc:Depends}, + ${shlibs:Depends} +Description: Plextor firmware updater + QPxTool gives you access to all available Quality Checks (Q-Checks) on written + and blank media, that are available for your drive. This will help you to find + the right media and the optimized writing speed for your hardware, which will + increase the chance for a long data lifetime. + . + This package contains pxfw, a firmware flash program for Plextor optical + drives. + +Package: libqpx0 +Architecture: any +Section: libs +Depends: ${misc:Depends}, ${shlibs:Depends} +Description: CD/DVD quality checker (shared libraries) + QPxTool gives you access to all available Quality Checks (Q-Checks) on written + and blank media, that are available for your drive. This will help you to find + the right media and the optimized writing speed for your hardware, which will + increase the chance for a long data lifetime. + . + This package contains the shared libraries for QPxTool. + +Package: libqpx-dev +Architecture: any +Section: libdevel +Depends: libqpx0 (= ${binary:Version}), ${misc:Depends} +Description: CD/DVD quality checker (development files) + QPxTool gives you access to all available Quality Checks (Q-Checks) on written + and blank media, that are available for your drive. This will help you to find + the right media and the optimized writing speed for your hardware, which will + increase the chance for a long data lifetime. + . + This package contains the headers and development files. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..463bc26 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,36 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: QPxTool +Upstream-Contact: Gennady "ShultZ" Kozlov +Source: https://sourceforge.net/projects/qpxtool/files/ + +Files: * +Copyright: 2005-2012 Gennady "ShultZ" Kozlov + 1999 Christian Wolff + 1998 Gerald Combs + 2009 Maxim Aldanov + 1999-2003 VideoLAN +License: GPL-2+ + +Files: debian/* +Copyright: 2010 Alessio Treglia + 2006-2008 Daniel Baumann + 2012 Nobuhiro Iwamatsu + 2012-2020 Boris Pek +License: GPL-2+ + +License: GPL-2+ + 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. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + . + On Debian systems, the complete text of the GNU General + Public License can be found in `/usr/share/common-licenses/GPL-2'. diff --git a/debian/libqpx-dev.install b/debian/libqpx-dev.install new file mode 100644 index 0000000..e183cf9 --- /dev/null +++ b/debian/libqpx-dev.install @@ -0,0 +1,2 @@ +usr/include +usr/lib/*.so diff --git a/debian/libqpx0.install b/debian/libqpx0.install new file mode 100644 index 0000000..093956b --- /dev/null +++ b/debian/libqpx0.install @@ -0,0 +1 @@ +usr/lib/*.so.* diff --git a/debian/libqpx0.lintian-overrides b/debian/libqpx0.lintian-overrides new file mode 100644 index 0000000..fc6601a --- /dev/null +++ b/debian/libqpx0.lintian-overrides @@ -0,0 +1,3 @@ +# These libraries are for internal usage. +libqpx0: package-name-doesnt-match-sonames +libqpx0: no-symbols-control-file diff --git a/debian/pxfw.install b/debian/pxfw.install new file mode 100644 index 0000000..b174b44 --- /dev/null +++ b/debian/pxfw.install @@ -0,0 +1,2 @@ +usr/sbin +usr/share/man/man8 diff --git a/debian/qpxtool.doc-base b/debian/qpxtool.doc-base new file mode 100644 index 0000000..599988d --- /dev/null +++ b/debian/qpxtool.doc-base @@ -0,0 +1,13 @@ +Document: qpxtool +Title: QPxTool release status documentation +Author: Gennady "ShultZ" Kozlov +Abstract: QPxTool is CD/DVD quality checker tool which gives you access to all + available Quality Checks (Q-Checks) on written and blank media, that are + available for your drive. This will help you to find the right media and the + optimized writing speed for your hardware, which will increase the chance for + a long data lifetime. +Section: System/Administration + +Format: HTML +Index: /usr/share/doc/qpxtool/ +Files: /usr/share/doc/qpxtool/*.html diff --git a/debian/qpxtool.docs b/debian/qpxtool.docs new file mode 100644 index 0000000..789d813 --- /dev/null +++ b/debian/qpxtool.docs @@ -0,0 +1,4 @@ +README +SupportedDevices +TODO +status.html diff --git a/debian/qpxtool.install b/debian/qpxtool.install new file mode 100644 index 0000000..ba031fd --- /dev/null +++ b/debian/qpxtool.install @@ -0,0 +1,6 @@ +usr/bin +usr/lib/qpxtool +usr/share/applications +usr/share/man/man1 +usr/share/pixmaps +usr/share/qpxtool diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..1402d7d --- /dev/null +++ b/debian/rules @@ -0,0 +1,11 @@ +#!/usr/bin/make -f + +export DEB_CXXFLAGS_MAINT_APPEND = $(shell dpkg-buildflags --get CPPFLAGS) +export DEB_BUILD_MAINT_OPTIONS = hardening=+all +export QT_SELECT=qt5 + +%: + dh $@ --no-parallel + +override_dh_auto_configure: + ./configure --prefix=/usr --libdir=/usr/lib --mandir=/usr/share/man diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debian/upstream/metadata b/debian/upstream/metadata new file mode 100644 index 0000000..3509a32 --- /dev/null +++ b/debian/upstream/metadata @@ -0,0 +1,8 @@ +Name: QPxTool +Contact: Gennady "ShultZ" Kozlov +Security-Contact: Gennady "ShultZ" Kozlov +Repository: https://git.code.sf.net/p/qpxtool/code +Repository-Browse: https://sourceforge.net/p/qpxtool/code/ci/master/tree/ +Bug-Submit: https://sourceforge.net/p/qpxtool/bugs/ +Changelog: https://sourceforge.net/p/qpxtool/code/ci/master/tree/ChangeLog +Screenshots: https://qpxtool.sourceforge.io/snapshots.html diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..78584f7 --- /dev/null +++ b/debian/watch @@ -0,0 +1,3 @@ +version=4 +opts=uversionmangle=s/_([\d]+)/.$1/ \ +https://sf.net/qpxtool/qpxtool-(.*)\.tar\.bz2 diff --git a/gui/Makefile.qproject b/gui/Makefile.qproject new file mode 100644 index 0000000..6e6226b --- /dev/null +++ b/gui/Makefile.qproject @@ -0,0 +1,10 @@ +all: qproject + +qproject: + qmake -project MOC_DIR="moc" OBJECTS_DIR="obj" -o qpxtool.pro \ + QT+=network \ + CONFIG+="thread" \ + INCLUDEPATH+="../lib/include" \ + INCLUDEPATH+="include" + +.PHONY: qproject all diff --git a/gui/about.html b/gui/about.html new file mode 100644 index 0000000..1064268 --- /dev/null +++ b/gui/about.html @@ -0,0 +1,20 @@ + + +

QPxTool CD/DVD quality scanning utility
+Copyright (C) 2005-2010 Gennady "ShultZ" Kozlov
+QPxTool comes under GPL V2 with ABSOLUTELY NO WARRANTY
+This is free software, and you are welcome to redistribute it under certain conditions
+See http://qpxtool.sourceforge.net/ for new versions
+
+QPxTool team:
+Gennady "ShultZ" Kozlov, qpxtool@mail.ru, developer
+Thomas Maguin webmaster
+
+Greetings
+Mike Frysinger, Makefile-based build
+Artur Kalimullin (Kaliy), kalimullin@gmail.com, assistance on LiteOn Cx test implementation
+Michael Masliansky, assistance on BenQ tests implementation
+Pavel Volkovitskiy, assistance on Nec Cx and LiteOn Pi tests implementation
+

+ + diff --git a/gui/images/add.png b/gui/images/add.png new file mode 100644 index 0000000000000000000000000000000000000000..f98e2a8ca7386c19292423b94c97818052a38674 GIT binary patch literal 1279 zcmV-i5SR2M2sP1Q8DgD*PY4i$8=S_9=hi%Zn8a+)G%E|(Y(Ls{k>Jy-J+^gZRuz6 zDMVfr6%hp!3OZK*1J_>+Nw9kpW4RBBPTyNef0EVViE#Mz~k^Z z98Me_&xM=cKGS})@#W@g{hzADHB|u+7LOe`R65Lr`O%3-o?TQ_l|Y~v zDN4|w0p5`4rb{=aV!G*jO_P7$nhjOcFQQIlO(YQPX#BsUz1D7@~A*RS$Q3S?U8O{k< z1w-4S{3q*?=*W>Fz*NOYeLvjc{Q5Vn^jFYGArb_gf&f9K5FbGRR-Nj=4+`3%bG?6kRMTPBD))hq$kAJt;&u(6^0DryJy|T~E z95~ZDyw`I+vl*GqsN0Oh#%K`f(!IQXcJu4~V#17%<4FKPXeDi0NsC6@Ad557D8&WG zjjBOs0`QbiwxV^?4xMzKR@$Z+H_76RdQ@k}Z_gk<%PZMLYbGt)B|sNlZc-)u>1H!i}U!C!HsYvzZc_ z1z??=U4tQ#2y5)~lB>U8{bKFyjistF{{@~KDQfpwvA_TT002ovPDHLkV1nEmR^I>s literal 0 HcmV?d00001 diff --git a/gui/images/autostrategy.png b/gui/images/autostrategy.png new file mode 100644 index 0000000000000000000000000000000000000000..d32e0ad15ab5263f670bcf55f249d8824beef619 GIT binary patch literal 3124 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 O0000I{k79UAO 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�bjn*ESsfYU9rHOH(xq>*{8m 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 VW5!0R?vVfh002ovPDHLkV1g}FkmCRV literal 0 HcmV?d00001 diff --git a/gui/images/document.png b/gui/images/document.png new file mode 100644 index 0000000000000000000000000000000000000000..2308a6d6d5958fbda77b7a907ec6b7d5c0d4122e GIT binary patch literal 5198 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 literal 0 HcmV?d00001 diff --git a/gui/images/exit.png b/gui/images/exit.png new file mode 100644 index 0000000000000000000000000000000000000000..abf3f790dcb762a23ddd39ab58f8b2a2bb69a420 GIT binary patch literal 4823 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 D8L6tO literal 0 HcmV?d00001 diff --git a/gui/images/gigarec.png b/gui/images/gigarec.png new file mode 100644 index 0000000000000000000000000000000000000000..4132a12d64b9d89fdc53e7bd434e4587eca2e174 GIT binary patch literal 1832 zcmV+@2iN$CP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXK2 z3M(O*n5HBE00yW@L_t(|+U=Wpj1*N6hrit&77#f+P!L_rLWC${L=Bgy35v!Q6EA{^ zfhdRw;z5iY5+aFFBOXXXl*5A<6_G>DY6ytp0wSld0XdcBa#ikS0oeh@Ke{@;sjZ&r zk=b=v=a+Oc-BtDay?WJE_1>$2EV9U=2GIhT1yss43ut*Zbi5Jp5HKD12{-^mfm1*Q zunm|8j00`}ast}<7Ko{9VI~i}9xxI(>~wf9FbNnU*AzvM$ukR8h619QI{0QliR$df zKx2cqIk3RtD^9@zzRA>?FEKirmq6wmpfrgBS^{%`qrfro0T*YY0!FF9p9b26kn5U6 z0U3`RG4WkMQ|I{tpjXH?zLOcg%b?`}mjZ2^s{(lG4EtRm@7$jbCG<1C(%J~fqfEfPDmT?0XG93fw919Y3FW7z9}#mm@5(Lh*)tYuo0Ll@+Lkq zI@vW1ihUILG=u_rtEdtMUIB&!<%WDr6x2-Q`vQj?+H&A=;7cR=+-`OBXX6Fwa#ZIg zQ9wK3FGU~e(7PM*6M>tA-a}^Er~M*13_!kElPkN@>f{mQCH2ziV-hJ~qCqcE^1BrM zD~JBNq772-U#93MbO$3cOB&Gxscka+?);7=;l0{;wnxeD7DwviR_AvyFchc{90z8q zwoa-0d`kf*qye25SV|}lIqP9@*m%}hzG)v#42yLmPX#nQ0->?mQxWt>n;3OJs?^!@9Qq{%~6 zj#8oc4$CUl#}=cl20|0by-*NCnh0_?9W2l_3=1Uf(-|m}Agq1J?o12FTx; z$fSD6VnTb~alk;yv?dviE;ZU308BP`DmeYVX%q#v0}08 zD6k#)LM*#=ChSMFl}%UIe6JD{kI0TvUtot)KviAYHvdr#ENHFK*)ZcefoMn%9Z`I1 z($#_7|5yQK#vY@ELUsRPU9o_{YG&60=Np;gOqJ}^uPX}30Yiivo%53xVGp-*8Z7{545#vdAKfEV9TVi!5pdsccXj2X^As4Lwc#QG3RxqCpra zi;xk^c z{Q2C-0L^zGzNqOm@q2(dR6ihjLz(?CMb||c#fm<>CKM2K%}(;|zNPrL89ci9VxvPF zNA>doUC`A_o}U6fCk=aumICgOZ+>OpQN^=9#lC0y2mI)qNqURi&00@{KY< z0n3eNt?>#LT`V^8hn@-A9V$M?M~RVC6(x`#LxMtxbgl*)V%PdSVpxUdSwr>mH$*X2 zz_29pn*-ADBFQuEt%>~rtG@UXgKwMRxF!cEp^qd=-SLW`qw;C#iZQE1XFXmaMF*f8 z@T$nqKNCke0CcJsiS0cEhVv`c>X_8n4*uYYD-5WRXP{$?!MN Ws)?h`T*UhT0000 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 Hu0mjf!vQ5n literal 0 HcmV?d00001 diff --git a/gui/images/loej.png b/gui/images/loej.png new file mode 100644 index 0000000000000000000000000000000000000000..554e6e2e2120a1137a1f947310b9ae6758723ed3 GIT binary patch literal 3090 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

r;h}S;&KFjb$Fs zI^bY18Y!WNFr_^*sbAg(f%6r1M^guWQM_N>F({O zL))1vg{-9ifEM2NihL=fb|{nvA1u`wXhM?Rfa&5+jP)dSFR~=%p?9gzy*?_f9#E9s z7|~X=zW1@tqTupJGF^;}ZBG&%cm)<;Y4dY;h6co>)KM|>bU||+tS4VmhIdhqq3}%6 zl~b|6crD@~k!=!M8$8O)P6w5Xp-=>t$%`}hLF+gD(cDl)tSA7*fl53*gFToUCvJWE zEr$Icwu?O!lkAZ5K%Es3uk|;l@5PBHbMS|s*SEKeGj@dDywPH0%J%s~mL2$X%j)zD zxJW3gtqABY5m8@^LiDN_0Mee)2NY%v3i&M|x1?zdRWLKF9lImI00@bRVLHig zB8?;@;0_lWJ~#*VJ?M3IcESX?0UhV3yvB6ODW2gg5a}+Z4Tp4e8x-0eydswzT3hhO@PCQGVEb(2= z1q*Vk`J*(iXbv@l7G!cmRX=}PTV2rc(3&sPS@UrsvJBLkDJGezPwlW%YG*+qa1YmH zhg`wRgDOW#a^Pt<-W*EiX{o=fFf7{Y>gsYQ>s2l@zl9+tX1~*rM-V2`Gw@h16E-5! zzU)bY!Xp^sghGoc%hrPoqP8AK>f`gL;IYX0-;oe(GBFU!+G^@vd=Ye6M*>=MRd#(T zG~IIjyo{q>BB0rvNZO9^C@w2gN~vh2VFgM}y#J;JI{@(dW`YV`h-x{Xb^Lsb!F~5^ zfjo|ioUc3uDuVmbVp?1r?rmtILZuAIs@(0yC4f-+r!Nz{v_@1L6N(z)iW8GL&ilqo zv*`WcqcNr|Dp~G2xl_B?ao?bj%dO0-#gzATeK-#yO834RqVu_=qUsMHYU_LW)!X(=FfcDC3a89>jH|VnJA0%F777yY{|P%Dws@i zblVf;;UeD4aNp(LU`y;i*L!Xcp3WTiV)!CqU24+nSV_gOJjuukZ~9j`Y6&cbI2l0U z0M^_SLn*?0`>jpPYHcF+{f&vUe3Z147P6*JJ%NkP`HW$nYM})r^iC)-^wXyW?RJN0 zLkai^Z_h_x>fMPkQ)3-Kaz9V#fci9hkQZLa?>m?qKWV_nE@7m-8 zQ+`5w3Gc{LUwL%<{1dWrbC<>gT&B}_5Y;+1$($1UeC01{Zz>;(vo zRi+jFueUJzV4l0*^_;Lx}u=G94oR% z3TQuZ#2>f-*p1b8D|5EW*x1;FT>c&&o^Il?c>jL3-jQ5UQISGCV6hEozk99Zyu)QQ z@9B29l5`)(+*JH?ndH8DkVsB4JX>e>c@GtcqFwiu+UQO5?o|EpT(DTw&+G884qI;>Cdy5_G!J zM}UWC?xj2r;ICZ26L6XdtdoU>ML|(<%po!?%$Zd@F(rirkQG*^^vvo$4EFb%um21O zm;h=>r-YJHg!P4~W3x&+uc9x`%jn;!+#x`Do0I>ltD}QQMATDlHF$D*O2fyO6pU~< zB=qsZ1`wp0T^XUH)0bcv303@+ z7a^mNZzV>+BVXs-z^`Ks!|Nsv4?UE?AcaQia=vK@=5t38Dc6M)S>Ef=#*giadh;4rTxg_zwYr57?tJR4$-h;ism-8z3t$0uy`>~7M-9RdVGMV# z75k6(jeB~9gA-vbS=jAJyflriio|RsgD;n>%*2<#LOoNWtw#t2rZ)npZS%5or^ZY? z60$_vB*E{sYZ_T2V6zVD8VGaX5~bPUvDpzHzfQUQwqLXr3LieU-2Tb%IUf?p`eP~_ z_#_H+aW7dTMGDR``JTQ~2mAu$a^4@-@$vC30A;;`a{<~0pdly_;s*pSU~JO6B}5^Q z%Mvk8&n@VC<*?)~N<>6NA>Jh{(=?XFaH|f>$B`^_dGUPj)n5w+x0V=&^iS3 zH*_rHcxf5Xp64Q~ktN9tRCS7QsZ9sDRLrSK#A#O<0~T8Fn|iHC=;MbA9Z2(f;G2P5 zzT8RW8n-7z2j<8OTQYa(-KK-*E<&^E;ZF4fZUvrHrfJl##ZYKssq!DWpSA+?{S6Rl zp+HIPtuiaia_L5;%U2F}{QP&HDl}DorweZEt#GLv=oK#6*epSJ?8|(iz&5CxC_mqh z1U7EOh`9SC!o_UQk~Y!D_ajb_E>vM}&<3O1iAPO4igM8(A%x&*m?-vaNu9l^5lS%V zM->_Vv8&(xG;?y|+;+DIL6NTjWMl0OYYZ$bcj3cm*7^@C!$9nR|Iu=Bw3GqpV)~3M z3@Yg|$IGpU(@c>)U1d5DQt<%40!8_RZ{K*gfEmCIRZex@ycpMS8-0j z008>ACO2>aheR5=GOcY__nV8Eu!BHbc3cB9y6U#JJH}&Y@$Xd-@>ae0FA9EVuqhLC z3S_)v8vLBDBPo$*FNX*`=_U6 zT(!rb;z{38MT7`L!DBi*Q7>nj{$x7&n7Evr*y3WwwY4>14w!y_+@1qyxedP;rO;HfMf(9= zH7qicPDF&fU`#<@pA@)Y^XdMWQB<_0pTR9-NbciDVIY57T3HEY8G;8AnPH7Q0AWrw z&y9E3iQ&|^qS8 zaOdn^aB$N!=V%OOS#T#zOc#)utNg~CDLBR7s|Ve9vF1W;pf8czQp6Y0)}5% zvxbYA9}T^tb!o1!CVEPl1=v3%BSQ=jFehh=0n`wXlP|1`)5;aZ19SkGGVYGQ{wqnf z3+Cx~qPQ^2`Ac((7srfiiB8@=%*V*=dW%Oq#}N~u)NxvF9?Mxeg${n5BsDT3BK7ZV zMy%b7#&^aUHuf@c!HP6Fw^5{@;Ycr^Kf?Ox^!4N z(;wBfVP;5nN1U*55Ub%2t@hwA`OHUq6|%WKiO=v-7>vTk(?Mp3N%5 zVBu%t*Mese$caWPokIari5bU;awy`;e-)iOw0ic&;30#GdfD)uu-=InYke`ZMZK8@doTc`NQ60?CVEiIvL z+p2)>ub`@0AA82i$=PRJvvznG-Sd7-Wh$QuV{B}U&8WUy@l@-d?5(gUZwRHB-)L_T z92p|!CGK7waiR#?dhr#98UXCK7EyoPW!9>eF8iuh2HE6)4+cgB)InQhp|1PZggL51 z9M>>XR!)-fGUtLpEm!wJfR}}-wr6u^ElW1@IT}&oyaHz#HtJ?0A1RAn%gNaym zxd6A$N&jd4_YON20P>a-MJmExc~f>8s2DI>c$dl* z1ZZPR;7Afq>*W@oAys&qe-_G|MH1@&cRzY@60BcXvp4mxO?HH{bLB9)7&Q zwwFC|@44rkyO4K^Z*j56upkf!u8g#V3Iu`#euel!k-?Q7?yC-dAla+D6@!!vQ|y30 z&<*9@Nee`_9m$2~ZBq)jt2n)mvL^Tx zrwXUk9i$8jb2!c4Ue|F-idUN!bgMrd!1E;|p2PV7r8f3$T7f&?c$FSD2Z$`wVm2MZ zJ;dymn=hB#hnxSVxF6Kxz=EjBxOx~Zw^=hpu_3seC_x4l@rG_Pa3W0y5=|%!M@mjk zPDv?BrC@T$!PC&EIMm#p2dMv8rOWsQyY*AZV zTM3DPolRaoKD&Ayq4dMu-QBG%dIIQ`$3va%dYhiF$m-_i=FW~;;vteoINDqWTAPX* z6}rE&o?fX+!OiV$we~NSf^P$xSb;w)D=V|IvYMOkHwO|_3aaYs>kA5G=;M-f3*zG9 zN=vOpM10=8!T|#cg@P*HiN+&Dn1ZsvR92V0!uAFtj1Xj`gFL777XqqW_Kdi998^u$KS=A)?-lgoFfE z$N72iYh+|(SQwZbIogQ`cH1iwoE(+w)pq~e%R}$WC1<5{p%)Y09fmLH=mt~yd8nxk zF81cs8FADZAhhd^Ff=)VmaHzrV+}pe7epZkE3HajKCbsfd}wgk=?KIoVb=7iplC?0HpPygnaeWd&&cn#a*!L@JV6#N8)vG)7Sx87o zyijJFI$EkCMyHL22FVg9y%X)W3@QnmZi=wC(6c2NIeBtg+Vb3-S%wp52P0bB!a}n7 z0ZCZ5v||=|qH~kuzv+Lat%{Nf=%}bE0E8mk3u zZS6rObnqg~o=f@EUEz2%pLay#6BEbo2HE;O;iQ3qfm=i<7DPT^4$4FK!Vg!pL-*)i ztHY`MVwusQY4~_}QheTY-H?iSq`rZHtRJ_JXL-V7i`L=muXuQ71_r`HsiC;Io)34| z(yVCF)$+n>_N}e0!bA7sME-+=^2A}WHaE4)ZWdi!H2(;EB*Tc6Ss);$s~|YyI1hJ! zg;lv|W=cMiBa!+rr{7A_5Kb+ofB!CN|5!$1QlLggKp@gxH<-j3Y@QzQY!;$s|Cnsu zB4gjWV|cU{3|HY2sjjY`WI>A#ykwsGjh`@(Q=iTpAW$CABB9+|MgZt%y zvfw=r7uP7V$bkw03?|(zoyzAJ8X8k!`s}Oufs%p(xDSgok;j_y*}CK2Ec17WMulQ%qFC$geWObT}AWxU828z`<9xj z;C9{J-MzN9W{jH>h~-mRX|JS&@3{GQYYS{>!j@OzC;gL+el^V=I*wlvXikd{_qPn2 z4`A;z>(gz}UKX)F$D*}dOC;>8QhEQr`*WoX8$4x+ItQQS3tDu)u>fUM!^!^M9tcjw zyvp+H??(fhCm#rX z>({Sbrtlqd%F0h(Dgw9>$wt1_?(~EM@@b+%qY_t`{R!ZR&bxZH+>PkEy3S`cU0+1li z*>G&+eYQPjGLlB^$%{bfuXTt>3Y&8MP_7`9Dp)XaprxVtvsiB*5fSl~PIHT<%ywOB zeC2*`u2P*1lHm*vQ?9ZiWcAqPrBWIysSEQvez&V5>YdWNym-J`SZsRn-B#ts)ic+ zV-^uVSI@%9Sy@nE7VrP31j9$KoZ zs@mE&dvly25+{GU!vN4x{f6YeS6jU~|K}mpnw8Z_)HJBZeE$6Tb9H72-209_hr-7u zF+E++!{e^v(*V(vCz$?((dXZuvLbEH&dpu_e;E8(|HLu3)aYDUQQ>v97Cb+%4fRWu zrw`%L(jT3h+gV?qn4LAf=vQ3Xl*?^XdmvqUs|zLy;s+sH=DI|iIPb{!(ar4!swqlJ zO8U7LHD8+AcbT+`gZ3&bO6aFUA3mjkDYd+HIAU+1HfnrXv&uAPZpC>bS0TFH@hN!U zX=nti6;7GA(~tF)VrHn=U^AY4va;H5_s4kKCK;EMWOnhaZF)L>G2zd#i+U5jNHtb~ zZRb!j&*9n51b~XR7k)wNMQDEP2n58>9EZYqtalEIj*bq>F;Z5R>+=jyhO@Ib00f@? z9f7mg%IoWA%$>Il4=awZSgdsfPP@^=u4KSLNju|^Tf>9F0FWLSh=zs+@(c{WEEf9V zUaG2s+FA|zI7eq^Il5RdDk>^K#8uWAifjuQAE1nynVI$X_iw~s_rFZvo39?oP8thG zhR|;X1Ox=*!y|p(dPtW6tgWuzw5vzsw*;=>WuDfXwzYq@_n~8~ub}1iyw?A4Ht=6_O`v3>=r`E)Ov-`eCn807Jp!M!# zV0Kn>0}GuvOcERA=Ngs#h+%n0XQxt{K*76L(8e*M+(!*v*T6HyL{PXH#J*$}5RS2_ zzkmNdHkRYl@26EHu6unbiR)w2gb4$n245|#JgBRx8oi;WyorvB6PK(_gdrJt@oYy^ zK>J!%3e@O`KAD)1JbU)~_3QKV^Gr$PHI#pwjs(uQxsVNaXJ==S_;2jDnDtr&=;?z7 zHh2HQu_^c&e_$9k$+$Ypkw8#7AfnM?nfWCpUI4DL-bTsF$t^4_G&VFKc8Yu~sUBF* zf@A(#2T;Rs(0L2}GS`k>zI^%e@bCcWQ~GNQb$R)45vsOg$FkSiBan#juU}uWvgY;$ zD-LJ0sV8SC!QuH+yVKLt4GnzdnmW3=tTEDLWMsx1N!q{AOWbJ1{o6dkJ-BReo0P~Zg*Y&O)_2L=S7VqjpPp#=s9ySusZy?L{C&Nquy+3mx9I9t9B!Zn-3Yk$6) zXW0dXa;e`|A%z^rIYKsv3&h(Q*-)ofB$2Jv5c-R z<+Biqzk^A%L?J+1NbyFoH6xEAx^6`9eGOayL8ica$;O613!$6l@VdJK`DWdEPrUd_ zprcp76e+{NbUN6;<=^x#dHPd8qqG0ff!ah9lV4x|hKuWIkio_ooCU%)I0Be}A&3a@ zs>%@|luC|}`HHYGId%1Tu}sZ6n@q(#P9rkZdGw59rj}qTK;f*Ho8AZu!+)-#`}_F# z0Q4J46D0Ses?=K$EroDBq@R=G^jx3*ZEk9!S4z9v9;2ngp|g7&&yfS%UVo7->pXpOW94t9k^@N zSk8fj6a7dQ(bI8QnisLkZZIQ8FgZSc66Jlj4t8Z#Q4v7SpJwWsnwlc@U$QbXAz#1x zc!k7OhIT1bLBc=h1{?6Qv9SS|SnCX4MJKbnUheHRS*WqHm?^=9Ld#fN^3e1rIRuZ4 z(gfYPnV7IK)oIyXd^^{QHOmbL;#mMc0t(TO>X*7jvzCq-5)7B3bYe$VR(3dDI6W>- z_2Wm}lyGKZY@H}Gw+KM*WMpK>xh=njhT1th2YZDCL0tEW8#v97%48#hsq&kf1^M}J zdm_k%@6Vpt9pl9Hqe|uN2&ELL=7Bgg2iy#hG1SXjzu31(6BEe#XcSQf9IzmAa&k$U zu^Bz@k2*R!fQ03r7_;AJ%f!VqYgL$z{RDTrxw`tY-QM2Dgy($weVUQ#^FVS+%JIVK zhZCW;BBdpOb_In5){pZIjutvPDZ#-QZhg`rDQkB1V2S*WyOZm^Q8_9MDzByegX^_Z zh%f}OuE_Iauh{ zZuxj&4x+B$bf)0q;ibK{mqcBnMj^jUHweW*uvs=>@EDcu$K;N zg8zbGhTfM)No4l%@$nk#mB_BooUrgze&^V69Bs&}HA|d?%gali*RQ80C%<+Z0z6e! zJq6@wczF2Y!s(ZKSZ)`*l1zmw5{Lp*Q&R~E31efrQDanOq&gQY76Gb|XQoYBe$muW z_FWqE-nT>fWPlTi{~v^QJ@%g zGcwM~fbH$YK3mHba<&D!Xe(AA^5C@gFZBX7z&S0r$ZULT)QjF1O_^6~XN~#b(}%*T zvnuB-LCwR*$A`n=0Ly{=$b7dAJPClCfGqFr4Xz&_^&GdgK@x}Aw^mIX?Cb%o09rDT zz-EGg0qBmR6fFGpYv}jypJ@75eIp|yLqk(L4)^!>+sU7UFg|?vKpY0@t%-$&g|+qJ z>T27j9bf<;7)8D~0PAEONS?H!BBrR`b-Q{{%m7Z|;o*U601pb)lQ*%?QJ;5&cpwt+ zI1NWlxj=b(1o|{xz?F!cywU#ea9J55^PTkX>E+q?zIbpUkEM7-@(58^@!fkoeh0 zsibAhTmz@cnVFf%$-#7C_^Bu0@yg0$K(w@*F9J2FqZ1wtSVu_rpL+Yhi)lfaxi3YA}gMkOEopz1bIL`2Xsk} z!7ieaxWTjTH)m$x5W$i(G&F#Q70ZOdU=U?$s9;>b1Bf7yw(Leyrl#8&_7mZxoRq>| z;nxKvCBrK#D`F(fPfo2e#L0p&!0jEerMk_sZpmgoElWUm9HBD_3AJQqBE?yuU0;Gw zzIqD+@8~RxnvK9E2NY>LNy4Y@j}M*#a}AFFO!2rq`F7w!m3_MZ+9d=7jQo1Xv+Qx3=p&Ioa7%s`z2uz`Gicd#MBsiHW<~uE9h__GG2< z#PCo!BvzHbTO)7J@ayW5*l?itnzDCtl1i{%?GX;Zd77M@97X~GInvW3bym*{^#e#S zzPPx!vVz}fXMC@q5FtThW@Yug-&jykkg6%EbM44QfQ_wmbaWK+3H_>tsQ1C)A%+r?oJ_hj zFbuaH_#&iG4ra$+J_F$9`8U=1=+M-}~;JnP@W#8dbJdZI$DH2+*(^z^({VrdS=Gi(a~3& zFhfqXtg75Tnt^GjGZ_E;88Fmz&k8YwvU78Zh*p~2NmPZ_9xg_NAH(|{c6N3`G8q^c z+G{yqzQhnGrl1g*wEqXh6ecF-%`y)+cTH9niop2N62N5&A|k^kAB7)W1$A|>iM`!j z^D{FksGqLq`TLIWbZinNn36Y;+zy;TinWRAasiNLS72dc`cU~9_vT#% z(qe-n_G1MQ(BYw>WM^779^s7SkSeKkq4n+UuihIA)~zq-mYdyqq;6lH*?n1VA{7tH zel3NPIt+=Xu6#c#qaGZbOZ;63fd`dA=Fd}aNWl zA0IATx3AHSypU$+=l?7urKE)C$;-)cro@@NbIxpSeFWtrc;A&u?Ff`JKeYz0d$&&1 zRck=OeCtUcRob}QK=JptxzTHIV4%Ja!X+Pl4(Ke4=SlcC8JC7_uwvO9l^m7xwG5R4 zM5pL%>|CXZu=_B;IcQA-kJgaTXLLBYVI5M7%sHzMcxGWoXY z(*W@qf3Z&exQzx`{_{G~u{+zH@fQMffM^mTi(PWt7@_z9rnZjh14kcW1?ku+l*I1+ z;L-WLM7Nntl|g7G+F(IrB^0;m;c5mjSn6ziOiWRGZv_S1w4SM{X9_4KgKhr(#(or} zP=IE@xB@K*L`3@rKXPAxKlLYjT(aFWrTjFSF~tCUq7okh)Q|OcXbd&^`B0^{^<#u( zv-uRGfR?#=VNA_oX$>SKmS0U>-B0galE-(^Gm&BUbVQiv(+dd!4M6n!sAB5scu{20 zg04Xnj1>=%)!{Tcf7j-%nvuAYOag}U<((CY= z`TI&MeIfuL3g_#pOiq{eX;OnAO5B7z8kO&D!=u+$j* z5{iw@$sr+|`Hp})YE%hJbQDYz@dV?_<7``5%4Ino*&50RRo?Tv!baH4zG?`7%8*z)Y znV|G?LfzX$bd27=e|D4eA5sepfijKTh%riawnoDp)DkiXoQB0ZJ|hiCCr{Bnz}*qH zSx_$nD^d)u| z{JQaf)z;Rw>??6WaSxnVJV_W#K%2t_nSeU12LtY*AI@m)GK6aB04zK}p@DS`#=73) z%?$9!al5{F9JOO?Q@ev4&A2ExI6FJj|0w_w;SE9Rya#HsM}E4;=t!TpY@iw7LC0&i zd>dQan116R|K(hTWb~(W#=WRYoxMnMrpwgrBGwl<$BiqPZTDR3YE-M-IJ zAyj#8QJs1`(`H(SVxqUlpIOL&CMU_h(0zt;ac~2KHgtS``%n!0J@Vr zgpLZFtbl@ylhadQBp=Ldez&uHp_RwzGRwJ&;$mipYfEl&VQK)Gfai-$kB*jEuLyzG zUtKXMFkbiPGgpV!D=iVIBZ96c0i&arXJ;uu&(13EDHrBd=$L@;WUIMmFje}I$(C~f}zi(mTwD>|>G2IhZMr=y19 zyR1sk-h`5HH`jTaFN#aW{q1G)pX>G_rGSGLQQtjyTru)a7?g)HCME{-HLqTbCtA^i zELs{C_bV#009}pGrDp)*gU}#{c3yy)K;i5JvDx@QCIKwm-P`N>01aq}IA&GzI6YXb zH~Q@Y%rHPFXjmi?qV&!_52u8fbw=Z9)6vt@U@*ssK|f-ngANFgX_A>(4uD(6NKc+( zS820+{e^)_^^*5$V9f^DEMns@3jPxBa`?qgzZ}S3w%)r1vTwENm&6}^ypzE8Epz-eAJ2kaQKrRTKIdubX zPX^cyml^>Ij4duQCk%)V_zuJjY(~O`bzeOV3JmP->7l2mN0zjO1*e1)LHai2%X{F7 zKhMtM*X+*TTdkf4Pp;j**#I98xcu+400@+>paECl^9bExdJ23!qb46$W`;Qn-bWWw z?%HxsU>KSLA*CY%8yFh;RaAt{_KfOJf?|v@*0$WqVE>%Iydn-dswm)OUG|TMUYGno zZe@^~krUrSd`inoN)%UKQvkh57V-J<;ojvCtjFHjnP(~D3`@Vd63@T<%Mza_yLnLi zrk$uIr{H}YG9Q1HtB-J=HI9mk%7)#WSXj`3lr~z0`nQCH-ripP!u~SNVz5!Jr&}y~ zEfo6-x%KJ9kfK_oHqbf6Izekkgj;^Gsn_up@lm=Fs}y2^WW7W0`Yq6(vf<^!@$^YC?MU literal 0 HcmV?d00001 diff --git a/gui/images/media/cd.png b/gui/images/media/cd.png new file mode 100644 index 0000000000000000000000000000000000000000..647372dcf9eba68670c2736930e1795993cc3345 GIT binary patch literal 14105 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 HW%&O9#8+tz literal 0 HcmV?d00001 diff --git a/gui/images/media/cd_r.png b/gui/images/media/cd_r.png new file mode 100644 index 0000000000000000000000000000000000000000..9b60a666eb875879420945b4ad09882005d7406e GIT binary patch literal 21277 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 literal 0 HcmV?d00001 diff --git a/gui/images/media/cd_rw.png b/gui/images/media/cd_rw.png new file mode 100644 index 0000000000000000000000000000000000000000..154634d54bd0dabab8ea3a75c89b4a299f77a968 GIT binary patch literal 22742 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 literal 0 HcmV?d00001 diff --git a/gui/images/media/dvd+r_dl.png b/gui/images/media/dvd+r_dl.png new file mode 100644 index 0000000000000000000000000000000000000000..e674dbba35f559459a3788cc2c5f08feba879ed6 GIT binary patch literal 1520 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 literal 0 HcmV?d00001 diff --git a/gui/images/media/dvd-r_dl.png b/gui/images/media/dvd-r_dl.png new file mode 100644 index 0000000000000000000000000000000000000000..03bac7ef8511f9e2a78c1a237bf1dd5db6040f7c GIT binary patch literal 1731 zcmV;!20ZzRP)zl00001b5ch_0Itp) z=>Px#Gf+%aMK}NefPjENKtRC2zz`4+@bK_pU|`VD(4e58FfcIS;NXyukRTu+P*6~C zaB#4&umAu65CYPY00001bW%=J06^y0W&i*H32;bRa{vGf6951U69E94oEQKA00(qQ zO+^RV2@VPxFND>?{{R362}wjjRCwC$TFuzsS^_thycX|eA+Lb zM6-!z?GAYZ7GNgU0U%_kS`$JTFZaNMxykeZ2zX_o(IHHEdyZf~0Kgs^3IXH-9m41> z1djq}y8tY+jEKk(hV6$3syHQ7f)B6wY@KlyVIg>lL>Yl%fsCfizzv{6m~vRQ8mgq_ zAdwIJgmEA(l=L>%EbxLhDnm`biweqo zJA~1&U(VDNGNSQWYH%#d+jOlECRFlZN9FrsC?&8CNpHXx8fq|C;3;xIlF%W2q#hlB z&(|5SNS(U!NJ)3YXgc)dV$@t*QrBaS@2HIEq5%Dr*xlBTE(6MBYY*bppp)pLfQkyo zvSB?Hi@=GfiVf*{Ono7`sIm?*>_rE%`Dwr=GR)D^*Ig97cxtd0Ey$0afkSmLeVDrE zsJf`4dyo5rYv>OWjD$Q8?mwgqnp%I;X!Q(K=Hit{>!PX`sZ;E6Q3hYoO2nfp1|d>` zR-ejC*rv95YG+2HgX@`EcV?A~P`PDdN&Bo_t1ON3ejaC9mm z>hQkZcTs!lKL9*HJ1KdIaj5HXWO$U)8Pw%5p+qkyG1{xcZODl0M2%JUad)k+xq;?Xs3wSw3$<68N}V zUpgAvsE`>k=A(y=*Z@5(u+3{nj7|k|k#z6DSsKah8l93eISa-<*^)>L=I}kEQ{0c5 zI!m6Y^}7G)lxz)QZXVN!BNr1O?4h=Kdmu3rYGIpP zlna<~1oF{E&QQ~qhA*g8-Uve_Hh`wWfJt!e8W2ss^?kkS-%$N{%CfG`s!dXQnt6+#Q&(N>1_~+6l~bu z%RgnAzB@dsKkBWdk42E~ADReMhufV{%Z1l&7C`JSblji@HB2^ z`sW2PASq_U1Ia@Vw0E`s(Z7` z-b!Xq-IdRqX*%VUQ%*VMlv7SQWw3}}9o15F{F4>j>!a5{hy&ug6lWnH zs<7&y_?8ss*3+Ad_~O?b&H9eV5%ICFmI*83TYtU1IG>J-Pt@_1+Vs&aMv!ZP=D z!`DCPTNzCp2Ok9Rq0(ud2#FmC9{}(d{oGy$J{Z311pUqJnbJhO_!9N|4|nm?DX0AJ Zzl00001b5ch_0Itp) z=>Px#Gf+%aMK}NefPjENKtRC2zz`4+@bK_pU|`VD(4e58FfcIS;NT!2AdrxdP*6~C zaB#4&umAu6?tv;t00001bW%=J06^y0W&i*H32;bRa{vGf6951U69E94oEQKA00(qQ zO+^RV2@VPxBaLH@)BpemQ%OWYRCwC$T-kcFIt+~W-Q@kB_5?!PKRLG1fFa9=u6mJT zkF9kixLhjYSyzb{nhdL+ZiT;qVy}ILwt->w^QE>BKHV7~=>m=1vV_#YExOM55@Agh zwyr`(sII+59do8k?Q94&84fauASn1)cYOLVkCqAecz1kqVMWnzDZZ?u*ZyL5be&J) z%#x$t0bf&XR9}BB7XZc`@VVB4skb$>0j3@BwNjYeeX*b}8^v4j6e8$4DJg-7wVZ%o zwu^_{Y{IPF5pTd&JQM2xux6-Q11XG`Tj14wlj#Al5|xD}r!eL1Ilz7@0DEX?4Imff z6h^Oc@Fak?3&66M5rLS(up0~w!1lVefdrdvv3VxtarsJ}0!Qi2+g^a^}&K@ElmJOw8tiJa00 z^yn)1eA@#SU8in5QsQBl><&FlMh(R!bvx(y&C0+KRnbpL-EI5maz&YJ>p{Gkbb=wO z!l-bnH*A+;p>iUsVMDr|Q(wUlRn{R!z32#=pRU+MuKQ@|+aU@kPYv~=1^nota;UEE zK1@4uG(%J|yoc?{b?qNyH52lcbpN4b&?x_=$?6$V&&3;$)x=yhrL>W4vmB6Db zCLvN)tv;2Pu#4K}(aww}C)WeYJF~_`sJwVN1DITA`t^ZzNtqdts;r(*oMQ!1KD)1`WiPR;=k*>qR@DS>(*60!d)lfdVC~5nRFg$vQs%cO?7ogT9 zf>ZlaZ3Qjq5taW@Ov1f>5_P652l|Q}Z9~%dACo0M%1}Hy{v-k0uSL78C9f=>Hz5Ik z+^sJi3^poc_89ZgyN%dXdRo;suiayGD#9e`)`PQ{liM^pB?q_)rasw1POI+2w~S74 zzt_}R@}So1{-aZJX$W)mm_~rPE!}@~N-wswR~}thY6MFF-r6}d%IN1RrO?X*i5cL+ zR)r`RDdPz6(FM*()0PGpj(|s}xVG?gd2b11c%LbmQk#9S_zklkA0h6$;r)5V?}YcK zp!V{e;=UW+Tts{=yn9ubFW)PP@QTN0E9h6lyQp+I<&@XypBUx${aMWyxY{fUf)>)&v6w>WAl3>dqA0(0N4(}j&mIyoe+sY^D)W2eBk5-=a{(DBDvXE zQPJ&SAuJaC-RO_?4lig|iDsj_W6Q&$S(w=UE!bd7ZF=4PAgNJG+1_ds{->saC=YBuMuUO++-{c6X>{;Pp;e}@AU2pmG7HrI^~p8PC4b2Q%*UhhlqdLrbr4rF`bV5^Qmnx58B_C z6i4H`A_I>zO)emO^Yi)P_-=K~y>T~S`=vLR=HHKn-~9UEk@(n8UxfS(*dLO^WBB-A zgXCy@0>L-lfee5~|FrGw?_USw8ys>SPx#Gf+%aMK}Nez`(#jKtORN3u z+t#=xr^ApE(pm zP>_nc=IAC~UzN7l#rpa2_TZ*?mcp=p#+?^aTU54MNmq_9bU-I6;7G%zVs`_05mj8$ zik551(o#LZO`&M%`ZPNrUC`+gy74?S_g&DH(v=Q|UQ*6Y3K!W%3|*xb5Ctk-NxRFD z)y3pr1ebe0c>!Mp7%=II!fW#8!?`di_;5sg!Oal?Pgi*IIU4+OO7BnpO6Qk%bi{zu zm9)Fmn4LH6b+ec}kqGu5Nx zOlfCumZl^ZRnoSbusn^*GgJOuc};>5XhjyL1|dTsys>7h#FR}%ItFuvLub5 zG^~(j*c`e5c#h6z83LJr1W>KVfX=79X(SEX1I}F*G@c`CQVQ`t2JMYW`e4R-K5GiU z(^x*?1syI!%(--uc|l&Y0aJ4E0b90Fh#9F}=xo`{lkU=#Ho2U9K+q-C7hlMe%x_FN zQtD!$DU%lvbOEBhb^eTJVvs0q%1?61&}lJ&s4~TJXfmJS@q&_@a^_6YY)ODH;AnMs zg*_Xyzmb*98Q+U=0Ea=aG_o)8UovM|s**XA2^)1mpYX^ilP2nD!SmV7r+$?M0UZx0 zBvl!aPo?Fu(IibF=`K|@LyKtk+yb;0o7`=DY)m@7v9?Is50@ObfaKg3XUyrcL)c>G z!&!9)BQ_q4preZ@W0v(sWa4m5T|GeFz4{^nqG=-%oWZxK4Wux#NTYKc;v_r{+!LAQ zc7U_Ec#!Ew6Hu;3P;~q=Qy1{y()*UJ!iWGiWeC_+@eCOzEU0uZhf{-z2N@Nju=Ai8 z`Zv+`thKHZw5)?COu1c7k-Z}@h0by8sb_jLI6FZ%d58|ELb*+Q)d-S1j*){|r4r{X zq49v^dc;Yx*FrQxX4}r0LaC&h$=4V`^nl7Yp|pb2O1}l)ikC=mY0`m0-y_>#Inn{8 ze8=#M>M^5cc>hQ{l`deqJjoVT2L@M4 zDzr}}oZQztH0{Sp0&hGs0>GB`#Rf(GLe8x+orxb?q!4ULWiySZxU{ddqAm8=0&tWy zp_;ns6c_bsm;Pkc0P?_m2rCRUD2fy2k4`om7q@>};}1wUFsDdz4O} zC7K3Cj+GiV-VST}WO0YL>Lv*pA#Bl)$3S4_A!&2m`D8U=9x58 zC1|?!a%0BBso7QaK^^a*ZSDDbt29~W0DH(jodx$t8BZ3N2PNe zOg)rH{)2+0PTGGRNNvdQh@g|a4&J37IwZf)`bz?y(Y5}TpAnPik{*%am*vOSj%$># zdZm4gP0c*n_Lfaqr6RL2bb*$dz)&4L*R3E6Og`70b}s1qRUes7KJTL?TSB3GdS2}0 zTRHy0`T#Q;p!{D%NEyMD<$tHhqhU{h&IvQ0jd8B9jA$-GtkpmK(~+%EiEOV{*GkR(?z`O{>EeI=C1ndlI#`u2*m zhzNA|73itx++(^3w%~e2%`t{~%lmDo`H$svD!Q4=_6btE)Wlj&K|VJzs_5v?m;IfI zj)2ke4WtEVM4+4Ge?OgwE-tERQ5%BVlNpm1^X-iJhj(}tIrK$GXHA|F#`Do>GW14w zzx>6>nds=!uZ7;|v76u_!E*yEy46JwThQ?zVpXC3Nz>dzl00001b5ch_0Itp) z=>Px#Gf+%aMK}NefPjENKtRC2!0_ZulA+=!n50!tH_T2*F_*Cddx5n2`9c~LJ~ru&Wdc@Qnat5m+_)@q|O)7 zsz=m2;A^T4AIsOG1JLh)Pq!8fqphJ0FzkS@6~e^fi#a}R6l;*X;E~h`2??05#RPQP zE*3?z0cY(Rcmk%vOk@DSnxRUG2w|+)00bl*6)Y zr~;RRI7G-qaco=86_o=hO!@4y-B@G}=@AT5T9jce&=WuiqpP$~(p%TEzzf)@6gK@X zDk!o25JuMha>S;P4vf!WgJV{HP2CG&d?OFmSiUcYQVi;lL1OD~60Dvc%UrbaXjxSCB6YGYE=s`*S^>Fr z#UMoRs?~?`5~izd7TcMT;NUu8>&~Qc5IlAdCn1CD2tPh#UQ(_M2;Nv(_W3#DTvS5T z%aM6W=u|pkg?_ooMU_=ZaPUz}WXn$X(w5%yQNeYkGh^VE>T&QEOixz8L%FC5ynv`R zRJM)>Kpo!p`!0M-{ig!2qMew$L}}1*Xf!-b>8#f1Vu(^hdF#A@y{|{ZgS)7j2GwH$ zXkNl7v@g|G(2^cO>pz?bbgv&&I^%{S`id0WhJ@9BR22A8hT_)I7jd}vHP|j|iIv6c zO9%uX59>=ig&P%8dyM(W-9~IGJ*{e+*X%Jm<B)4gFN+fg^41Kaekyf3< zw~S6vyVulNut=?!<431NZwPbsn1(~ww&eKHDcNmluiQGjxZ-ha1^sGx6A#lVr~ID&u`KibSxLL{yghr9 z4+}`HZ zXNkMwOj~u=rmGrWK3N=%eice(;%6gUpbUf|svV5Hk2(gD5U7A=eGq$b;Ee?zK5?f- zVzaTrz23n>h%fS8=3}|T1?_5~S;^tpV(OZO^22YzI#X)X%i$Xd^-|o7RvY(^nyS$T z5m3Q~8NK{VmT8B>!}_DqO1dn94FAxC$2#2Mgjz1V4zmDccVYMoEXT?C@a+?rBA^}r zC4rZ5D>XhZa2`l8X}Du5)FAozk0V?mN~1bS7uh4sEe--$xmRSCb5r#iyqIhBveSa5 z>S%ZQqQ+upcV8f?b}S)(IYZx+-4g}M{f>wcIOOm&{BRhMG#a(8q~zmgr@A_jw7J;C z(v|8y>@aCgE63Ig?Um9a{oV7_G-PM9dkx;dv%q^V?b;dnUb-vaH`8>=DW{xr$|`p}j=2T>`|?H3CU4&hFmo|K z|CK&2zRP0h`c`bs>FKRtmtomeU5n4a_tTHo%XX2UZ!z*m1Hv8?-}Lj*{H211`O>4g zAEcfRi|_fvQ$fXi%_!V2b@{{Mv*e|zdQ(uA)$^vg_sfBHIyAp5E>vX&C0RXXZoa2@ zO!fR5-?Af@0nx0UDKpEhVD68KkLZ6}{%Y>C+8-sgN5%K}e$nzjEO7In!(%$+i0b*n zFL!_o1AO#T)9fVtf$Px#W>8F2MF0Q*_xJaurKM(OW-~K0*4EaHjEoc&6}`Q^N=iz5dwUQN5a#CQAt50` zLPD9Dnas@0W@cu!wY3Nc2vSl~|NsB*@9!xoDTIWCt*x!z-rhPoI>p7snVFedT3T~+ za~T;K?dj=5T_qd<000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ige^3Kt$=rM!p$ z00WgtL_t(Y$L&_zuHz^WG#3LlV33{o8X5fmKe+|i@i}K-W~7OZ3 zTPRRiPRn+xR4#zRH~eE&Ie880C56e8ecF z_}^eb%uvr}h(jo}C1`g+yCIQR41Sh)LH0BcHqz=$NTT#X&e>9W#Au$#(c-yI;L%;= z%9{{|^*f(TP1ps5P)xQ-L}M#~kdm%>v?w(30h|i!KadfRIa{Ssc+vq8Dc)V%m{f6A zqmX_Cp8-j9^{QNh9HhHSlg@a`;2HL@l#ZMkDwz{me)aBw`BJ>amsLS86~@gx4XB2^ zrihIjrv54|xva)++L`2eGSP=cNGc$n@pidehN2a8RyApgFlcU$ssczS)>0a;(mV`E z9!d|9MxVM=SH~c}^X*)IQ+xmFsiQqi`FeaFr^hWSyV8D{EYXEhC+2x>a|b)RVAT8i_&2ms zRIqK*dg|D`cr%WWUCTOKJk;YfCu7~a@Zd?ESqRl-_>)u(g)~eoe!QWHVm#*2hOWi0 z*aqEe8FyEok;DNNx{ERv+gO;9dAsv?8v^Xc`~`h<)lk~}B5s5Shl-6;{jSgJ>O2iH zhP3@PBH}lbRTbWZtR-s*iLOOzb@C*cZf?EAw{%mwBJv%OAUG2gCLY&fNG)bLx-WXx(IfBuO>+C4b( zk=GOHn27!}&^zBgm36Z>;X}}Y`lkQPOO+6aPx#W>8F2MF0Q*_xJaurKM(OW-~K0*4EaHjEoc&6}`Q^N=iz5dwUQN5a#CQAt50` zLPD9Dnas@0W@cu!wY3Nc2vSl~|NsB*@9!xoDTIWCt*x!z-rhPoI>p7snVFedT3T~+ za~T;K?dj=5T_qd<000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ige^3Kb33EuYi? z00Y-aL_t(Y$L&^YkE|#Vm4|@xGHQ#Dj^h9SllK7HJu~}ZH|f2}W^*f<(JqQZ)!`Ia zJ${>2f1v+Y(33G{%5TJ+Uu`1qXmb9r={^1BBR`s(8Y(V%nbyzjKiv>tQ?wqq`T{Mql{N!RPDkk& zJKDBA7~3~2I)nn@;be?8R>v4^kZ~x5GHo?Se4FC2`90dco6Ezks*X zM?knUCFfx%mMA9y-NdLvjh`)$t)Sl19B<)AnisU;NOcs+80xf`uvfZjNXaS6wAiCZ zv4sMK<>ZWk0VEznj6t=P9=06cqNYLI&`&9&m4!#FxlIqU>ATz+(6Udgfl;0}u8SC@ z6u%oRh#Bh93~@LLZ3)`n(0)kd6@#B8UXVS_gN?L06Ot$$$u(Qb4l$Z1alk>dTmjY$<( zH45oR@OdB!SFg%9$U*wMH0e!889c*2mC}(jLnU(}t6MuiV!jkF@nu!eTZM5m4+E+p zuMn|uL+J0)lFMrBrkzQiClh^GgroxE6>pb!%TTm}&Z;I&5eCi8QB?qOVlAcdD$T=y zh2iCx80XU@6ie(p9f|>+5?OBtj#9q^F9;K`M6yD-S}Wc^EylHc*Rfu z&dFC_E&MwU)misJxq1=^f)h%UsQIb{W4ji3#DGn^W5eR_H@B$*VX4= zp^c(~Z9(hc*t~RRnjpKDIa@rm!)Z>Yy7$M(lRC2ys@w1*sTvAtm{_{$4NVl|F^@KM zEq=u|=w8dX`}&F`9;nb=l(E>xjv1M^JCAQefZbSkL!Yi1N}J!rP4M7Qu`$%I`Z`@* zr$NS$w!cS2{ARMMju#TBtJMvYR1nQ?KWDg-r0N z#e3|yr=>T~pPqEVt~FExfj}W>63Apu5`m=5bwxj^=zi13f?1Oq{uY`{uMYz9Hgn?{skjP V3Ybjn_k;id002ovPDHLkV1m1qM8E(5 literal 0 HcmV?d00001 diff --git a/gui/images/media/hddvd-rom.png b/gui/images/media/hddvd-rom.png new file mode 100644 index 0000000000000000000000000000000000000000..b819485d04d0e533eaba2817d20e0384eaeca8a3 GIT binary patch literal 1513 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 P00000NkvXXu0mjf^$gx= literal 0 HcmV?d00001 diff --git a/gui/images/password.png b/gui/images/password.png new file mode 100644 index 0000000000000000000000000000000000000000..074c89d9c01a036fd11979c0e8a0bcdb6b215b76 GIT binary patch literal 5442 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 G=Kc+n>3f<0 literal 0 HcmV?d00001 diff --git a/gui/images/q.png b/gui/images/q.png new file mode 100644 index 0000000000000000000000000000000000000000..1a9ffd8fa3ffd736ed592329667b47f2c7a8183d GIT binary patch literal 3790 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 z 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 literal 0 HcmV?d00001 diff --git a/gui/images/refresh-media.png b/gui/images/refresh-media.png new file mode 100644 index 0000000000000000000000000000000000000000..06600f6a89d19801136c0bf234c020d0c43c88b9 GIT binary patch literal 8917 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 literal 0 HcmV?d00001 diff --git a/gui/images/refresh.png b/gui/images/refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..592c728c0857196541ebf90a28e2808103c945b6 GIT binary patch literal 9147 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 FV1lD0TBra3 literal 0 HcmV?d00001 diff --git a/gui/images/save.png b/gui/images/save.png new file mode 100644 index 0000000000000000000000000000000000000000..7970d4f4b55d7598e67b49603af7a1e616104715 GIT binary patch literal 1874 zcmV-Y2d(&tP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2igM) z3nCGgJBDrm00z=YL_t(|+U;9SYaBNiel(h0lf(^6iEFkjbqbAb5|hN_l9M5{rZ-hLLrR%VB&CFu5^)?Oj$=qE5k*l}sqoBPX_`W7-Nme7SSf`xO|!PW zy}cjy_xHaCuuN%UG=2`?&!wfMZ<@{KhF1Vu>*Jg;&U9YCVZdn6)HF>qhN#x5HKb{Z zD2g%*xPJY*)yi()zMTm{DfM-pG8oNM0RC87TKcxxY_9nP(8H)9jQ8<2!!Xx+rBcbv zpId`@hFXBJ0-NxSYfy$A+NVPK6u@r)KJ&Rik0gD4WY9Ug}t}!;o zO&@g~>JA|mD}{n@87>i zk|b!i+lb>h*9D3Mz(P2{h0e_V^XJcVwMLnyS;A=2?DUc(!NI`+4i68zw!?JJD2lMR zw})1%RWxXeQaIq{%a>SKSis@oVVCJ~bA0&lA$E6naqQSJt1H+9!grBev!;*ErIf_RC;K`FGR*1_9c=P6swU0+~=P6AN266Qj0dPTCATJga+=5#B zTtKUzy+~0Mn41NP?5;wiZjpHhy{@YS1;Kt9?PrT!mQkO8y#nY*qk|^4AAz#_ARr`$ zsnLGSdJyZDhQg;#ox{lL)${fM^S{=uV3Tl&70P9g;G#>Wn~4`YBkrCLJ4&-#lcSz0q{CAGlQ9# z8N7P+YB0OOTCEnO`C&5_Ixhg6IB~+J{c=#)l_5e24rQ*4F!I_%HbX&BI4>d(ir8U{ z^#yg81qk2iiq;xiTU%IPU+*Cz@7I;~__&ZZ+E=gFap}?}t595u3=V=e&Ck!zV{UHl z$o-ukj`-dg^mQaQXkY&jZsCh(K|zr2$w$%O3j{$?*ah-@cASJzC%cqL6eH)1dF&MO;+-qiE``bn62{L7Sm*6vDu9z%Ym%RvZ}^4sf}_vuDq+v9VDI z$>n7eU!M;hPESwc;>C+rf%Js~_}M&Nbar+YXU?1%S+^K!5F7_A(D&P6Ul1G>f49wB zWNRPveL#q`M`@qn`h0F^o1sy1i@Y<8LQvSX6&^Ps5Nj5NUEnB=vj(+K7}$A$Ee#%( zzZadm@AClHr)lGm2ATGQ&I3dU6drXUl->*hz_n}FaQgIV0KntNkF5)AZ*OCBb2A9m zmW8>Y2c?Zh182{k?GeD0D_3yt+&KWi&dv_*-Mbg&0#~nI#njXk0H9v4TO~a=H;38T z*`fJ+KLOH|<>lp!#`*dAFaeZ#12oE8VqdWeU~O$Jlid3HIxbweFgE9abo|q&PqVk# zFJ8Q`3Z*A1G#U-f1IC>@cX0mv`LPi~tyaU`yLYqgI^}0`b5oRp!bQ{Hhw}G9zZZz` z@Bi$keNZ@*v7^EbHbn{{S{)vBr)Dl7O|94KD^kjz0o2VTlarG@Uv0P!8Ek!_#C2*!Yop~!MC92HCeR1oGqWo=X_+9E8S^Gt5}2k)@tU7fbB3IG5A M07*qoM6N<$g5R5mZ~y=R literal 0 HcmV?d00001 diff --git a/gui/images/scan.png b/gui/images/scan.png new file mode 100644 index 0000000000000000000000000000000000000000..0fc1f393d6c5a4bba50ff971b209a298b2729910 GIT binary patch literal 2980 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 Eg7?kjb^rhX literal 0 HcmV?d00001 diff --git a/gui/images/sound.png b/gui/images/sound.png new file mode 100644 index 0000000000000000000000000000000000000000..d340c3a721cb495f2d8f82de83a2564b9186f365 GIT binary patch literal 6382 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 aVeb65A8TkHM!x)u&F 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 Hu0mjfEEIk% literal 0 HcmV?d00001 diff --git a/gui/images/test_ft.png b/gui/images/test_ft.png new file mode 100644 index 0000000000000000000000000000000000000000..d6ee317559c9f6c4c6f66272c7f62f4550152d61 GIT binary patch literal 1536 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 IlQIkbA6La#pa1{> literal 0 HcmV?d00001 diff --git a/gui/images/test_ta.png b/gui/images/test_ta.png new file mode 100644 index 0000000000000000000000000000000000000000..045deca9388de2a3a63492474b073c2dd6ebc4fc GIT binary patch literal 574 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 literal 0 HcmV?d00001 diff --git a/gui/images/unlock.png b/gui/images/unlock.png new file mode 100644 index 0000000000000000000000000000000000000000..2f0123488273596e1006dbcf6c1d6450bd684711 GIT binary patch literal 1925 zcmV;02YUF4P)43q703VQ-21lKJl+#dc4k?_wGG>cr&is zjGb}wq@&S$^K{Se{LgalBh+va5r~MbwYIsb$@%K;-8=5yx$~a7y1FJuJEQNM{!jnG zrw{(=t$%k9lu9LHW>{;brnPHvE+Ul8WaQyT9{t9H58nT+mTY?S`Ez|VF+PfHz7ZWO zJL#?NQAANLSaBcin#5HxECt-yS&jM@ti< zsxgNzHrPtJb58SUe-?U-W&jjbfQkcHnKd|x8!Gn79#&zL^u?hdtJ$v8W_rra6 zo;-CbDl52r0A^-ut^MMzdv81N)KhPCzy3G(jqcODZu8b@&zc`R@#|k67`S+;VB#3N zcRzIJUANz{f1s~-s~`@0E{}Pi*s$t>$G*Gg4@xOzj4`z+eNFTL@ceTx{NnV#{waUy zIu!Qxyd#f%=i!GEK;rUy1ORtz-(G+8NZ0@VeDtXN>gPX`qt6}d@&g|Lcqu=;h;xXD z$>;Ov?CiW{zeG8y;6;Kf%D{Qh^(0MJBS(N(n8?up~a-#>G%?@+$61vjs3#gmoC(&sx8OTqh6uIoU{>f8s|FsNgs=e>H&UiYNhBYyY zv$IZ1YfEswRO_Ywv__^N@*^V#q%;=PY}K;XS{Y_W9EIYLLVbO`e?tI@84PH!K#>Bo zGK)D+h?9WN#3fannU?-@{qH7(vMc~H)=Hi4-jWTczPA7I$F{1OiDLK5FG+*zWWK#~ zn^s!+5K)z-jZic^Ha?XOr}MD@ekx6IY^b3$`SpzE?@h;MZZQB=C705XOSj(lnNPkN zM&UhY&-AR;CcYUa+Q3n`+Vk8_hq>L;+~z9P=4sXDD(;vmhHEG1itQb1*CU*p!O-A< zz0z}Xds{9vK=K+4IvU*AGw56bMKvWQi0A02qOtygG`>!lcu_ zb`>?RXdw=Ku(pt<6|->)r5kjX3t3AG4JAl|hNR^;d0?{dmA*gb| z%u8;B$vf2_3zz|Bf>McTFB!$*Oa$e)VC{gF25SvwHE+b6L{Q^Z9e@DEn0L;a!HS_A zg;FuX=(|3L_N8rm8AO03bK2001UH zl~ceAOeqB~aA9JDQn3W$h0e;lQ0)pVvKJ&ZfcZLL7%)fx)(q`vxW1cgO11l11^`t$ zfxt|#ioim!r~oK9t_CZ{jS^HHL`(!CKqvtye;I+0w3gBg?P@Snm3@X#W$G?|`mXk{ z7#k_?ycH&?j3}byJiyFJ%U#1SsC)>B2&{+|z%DBVqjRMum3R^nG6I1Wkcg0#`Gx0y zD`Pn^2q3T`;(9JyE8hE)(`^7IM6S)(he9ZmHe8)8l}-*%^vy*^YliZ*O3cw5IGO|3 z;gsic;JKXkUCyL@&ZYy-W&+NpeU%A3c9_-B;6?o7&9{eR!Iy~Wa{zL+=0*W%$$9#- zZQ0bPn{(-#GC{Dy(JG%y`>FbT*3D)zj^lfzJ=Z#cYjoNxiKb$qFgtzm^7!cBmB~y0 z?LB*TY<#>Iz?HI%00?U;jw%-cNCC(I2mp9xPy%28#O1~3%2ouDgVR!d@_*cI*asC? zF8Tlf03~!qSaf4@Wnpw>Eo5PIWdJfTFgYzSGA%GOR53R?FflqcF)J`LIxsMTx_C1H z001R)MObugZ)9m^c`amNbY%cCFfcSNFgYzTIaDw=Ix#sqGBztPG&(Ra>%c_l00000 LNkvXXu0mjfgT#g4 literal 0 HcmV?d00001 diff --git a/gui/images/varirec.png b/gui/images/varirec.png new file mode 100644 index 0000000000000000000000000000000000000000..846a437ac2c7642872e85ab46450a07f8b6d53c6 GIT binary patch literal 1586 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 literal 0 HcmV?d00001 diff --git a/gui/images/zoom-orig.png b/gui/images/zoom-orig.png new file mode 100644 index 0000000000000000000000000000000000000000..870c35cc44ebfc6298d78a7558aee5d4a3bbb8df GIT binary patch literal 2048 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 aFe@-LIxsLqx^YYZ0000 + * + * 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 _ABOUT_H +#define _ABOUT_H + +#include + +class QBoxLayout; +class QTabWidget; +class QTextEdit; +class QLabel; + +class AboutDialog : public QDialog +{ + Q_OBJECT +public: + AboutDialog(QWidget *p=NULL, Qt::WindowFlags fl=0); + ~AboutDialog(); + +private: + QBoxLayout *layout; + QLabel *lversion; + QTabWidget *tw; + QTextEdit *te_licence; + QTextEdit *te_about; +}; + +#endif // _ABOUT_H + diff --git a/gui/include/abstractpreview.h b/gui/include/abstractpreview.h new file mode 100644 index 0000000..45ec0a9 --- /dev/null +++ b/gui/include/abstractpreview.h @@ -0,0 +1,209 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Maxim Aldanov , Gennady "ShultZ" Kozlov + * + * 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 QPREVIEW_H +#define QPREVIEW_H + +#include +#include +#include +#include +#include +//#define PRINTER_CHANGE_DEVICE + +class QResizeEvent; +class QPaintEvent; +class QAbstractScrollArea; +class QMouseEvent; +class QMousePressData; +class AbstractPreviewPrivate; +class QVariant; +class QWheelEvent; + +class QPageInfo { +public: + QPageInfo() { + fpage = -1; + frect = QRect(); + } + QPageInfo(int page, const QRect &r) { + fpage = page; + frect = r; + } + ~QPageInfo() {} + + QPageInfo &operator=(const QPageInfo &data) { + fpage = data.fpage; + frect = data.frect; + return *this; + } + + bool isValid() { return (bool)(fpage != -1 && !frect.isEmpty()); } + + int page()const; + QRect rect()const; +private: + int fpage; + QRect frect; +}; + +class AbstractPreview : public QAbstractScrollArea +{ + Q_OBJECT + +friend class AbstractPreviewPrivate; + +signals: + void pageSelected(int); + void scaleChanged(double); + void pageFormatChanged(); + void currentPage(int); +public: + enum PreviewMode {Mode_Normal, Mode_Thumbs}; + AbstractPreview(QWidget *parent, QPrinter *printer); + AbstractPreview(QWidget *parent, QPrinter *printer, int _countPage); + virtual ~AbstractPreview(); + + QSize paperSize(); + + void setInterPageSpacing(int spacing); + int interPageSpacing(); + void setPageMargin(int margin); + int pageMargin(); + + int pageCount(); + + int addPages(int count = 1); + int insertPages(int index, int count = 1); + int deletePages(int index, int count = 1); + void clear(); + void setPageData(int index, QVariant data); + QVariant pageData(int index); + + + void setMargins(qreal ml, qreal mt, qreal mr, qreal mb); + void setMarginLeft(qreal ml); + void setMarginTop(qreal mt); + void setMarginRight(qreal mr); + void setMarginBottom(qreal mb); + qreal marginLeft(); + qreal marginTop(); + qreal marginRight(); + qreal marginBottom(); + int marginLeftPx(); + int marginTopPx(); + int marginRightPx(); + int marginBottomPx(); + + + void setViewMode(AbstractPreview::PreviewMode mode); + AbstractPreview::PreviewMode viewMode(); + + inline void updatePreview() { viewport()->update(); }; + void repaintPreview(); + void repaintPage(int page); + void updatePage(int page, QRect rect); + + void setScaleStep(double step); + double scaleStep(); + void setScaleRange(double zmin, double zmax); + void setScaleMaximum(double zmax); + void setScaleMinimum(double zmin); + double scaleMaximum(); + double scaleMinimum(); + + int currentPage(); + qreal scale(); + + int page(QPoint point); + QPoint toPage(QPoint point); + QPoint toCurrentPage(QPoint point); + QRect pageRect(QPoint point); + + QPixmap *grabPage(int page, const QRectF &rect); + QPixmap *grabPage(int page, qreal left, qreal top, qreal width, qreal height); + + //Движение мышки над Ñтраницами + void setMouseTrackingPage(bool mtp); + bool isMouseTrackingPage(); + + //Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ Ñтранице над которой ÑÐµÐ¹Ñ‡Ð°Ñ Ð½Ð°Ñ…Ð¾Ð´Ð¸Ñ‚ÑÑ Ð¼Ñ‹ÑˆÐºÐ° + QPageInfo enterPageInfo(); + QPageInfo pageInfo(int page); + QPageInfo pageInfo(const QPoint &point); + + QVector visiblePages(); + + //Вход и выход мышки в\из облаÑти Ñтраницы + void setMouseEnterLeavePage(bool melp); + bool mouseEnterLeavePage(); + + void setMovedContents(bool move); + bool movedContents(); + + void startMoveContext(const QPoint &point); + void moveContext(const QPoint &point); + void endMoveContext(); +public slots: + void setScale(double scale); + void pageSetup(); + void setupPageFormat(); + void print(); + void print(QPrinter *printer); + + void scaleIn(); + void scaleOut(); + void scaleOrig(); + void gotoPage(int index); + +private: + AbstractPreviewPrivate *d; + AbstractPreviewPrivate *dv, *dp; + void paintBorder(QPainter *p); + QPixmap* getTile(int page, int tx, int ty); +#ifdef PRINTER_CHANGE_DEVICE + void changeDevice(QPaintDevice*); +#endif + void autoscaleThumbs(); + +protected slots: + +protected: + int sizeToCountPage(); + + virtual void printPages(QPrinter *printer); + virtual void resizeEvent(QResizeEvent *event); + virtual void paintEvent(QPaintEvent *event); + virtual void additionalPaintEvent(QPainter *p, int page, const QRect &rect); + virtual void scrollContentsBy(int dx, int dy); + + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + virtual void wheelEvent(QWheelEvent * event); + + virtual void scaleEvent(const double &scaleOld, const double &scaleNew); + virtual void addedPages(int count, int begin); + virtual void paintPage(QPainter *p, int numberPage, const QRect&) = 0; + virtual void updatePageFormat() = 0; +#ifdef PRINTER_CHANGE_DEVICE + virtual void deviceChanged(QPaintDevice*) = 0; +#endif + virtual void clearEvent() = 0; + + QSize getPaperSize(); + QSize getPageSize(); + + virtual void mousePressPageEvent(QMouseEvent *event, int page, QRect rect, QPoint point); + virtual void mouseMovePageEvent(QMouseEvent *event, const QPageInfo &info); +}; +#endif // QPREVIEW_H + diff --git a/gui/include/colorlabel.h b/gui/include/colorlabel.h new file mode 100644 index 0000000..1561c89 --- /dev/null +++ b/gui/include/colorlabel.h @@ -0,0 +1,38 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 COLOR_LABEL_H +#define COLOR_LABEL_H + +#include +#include + +class ColorLabel : public QWidget +{ +public: + ColorLabel(QColor icol, QString itext, bool ieditable, QWidget *p=0, Qt::WindowFlags f=0); + ~ColorLabel(); + inline QColor color() { return col; }; + inline void setColor(QColor c) { col = c; update(); }; + inline void setText(QString t) { text = t; update(); }; + +protected: + void mousePressEvent(QMouseEvent*); + void paintEvent(QPaintEvent*); + +private: + QColor col; + QString text; + bool editable; +}; + +#endif + diff --git a/gui/include/db_connection.h b/gui/include/db_connection.h new file mode 100644 index 0000000..da4d0eb --- /dev/null +++ b/gui/include/db_connection.h @@ -0,0 +1,48 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2010 Gennady "ShultZ" Kozlov + * + * 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 _DB_CONNECTION_H +#define _DB_CONNECTION_H + +class QSqlDatabase; +class QString; + +class DBParams { +public: + DBParams(); + DBParams(QString driver, + QString host, + QString name, + QString user, + QString pass, + int port = 5432 + ); +// DBParams(const DBParams& p); + ~DBParams() {}; + + bool params_set; + QString driver; + QString host; + int port; + QString name; + QString user; + QString pass; + + QString conn_name; +}; + +bool SqlProbeConnection(const DBParams& par, const QString& CONNECTION="test connection"); +bool SqlOpenConnection (const DBParams& par, const QString& CONNECTION="test connection"); +void SqlCloseConnection(const QString& CONNECTION="test connection"); + +#endif // _DB_CONNECTION_H + diff --git a/gui/include/db_report_selection.h b/gui/include/db_report_selection.h new file mode 100644 index 0000000..09afdc2 --- /dev/null +++ b/gui/include/db_report_selection.h @@ -0,0 +1,96 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2010 Gennady "ShultZ" Kozlov + * + * 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 DB_REPORT_SELECTION +#define DB_REPORT_SELECTION + +#include +#include +#include +#include +#include + +class QLabel; +class QLineEdit; +class QListView; +class QSplitter; +class QPushButton; +class QToolButton; +class QBoxLayout; +class QEvent; +class QTextBrowser; + +class ListModel : public QAbstractListModel +{ + Q_OBJECT +public: + ListModel(QObject *p) : QAbstractListModel(p) {}; + ~ListModel() {}; + + void clear(); + virtual int rowCount(const QModelIndex&) const; + virtual int columnCount(const QModelIndex&) const; + virtual QVariant data(const QModelIndex&, int role) const; + void addRow(int, QString, QIcon icon = QIcon()); + bool setData(int, int role, QVariant); + void removeRow(int); + int getKey(int); + +private: + QList tdata; + QList idata; + QList keys; +}; + + + +class DbReportSelection : public QDialog +{ + Q_OBJECT + +public: + DbReportSelection(QString conn, QWidget *parent = NULL, Qt::WindowFlags fl = 0); + ~DbReportSelection(); + int getReportID() const { return report_id; }; + +private slots: + void load(); + void search(); + void itemActivated(const QModelIndex); + +protected: + bool eventFilter(QObject *o, QEvent *e); + static QImage getMediaLogo(quint64); + +private: + void winit(); + + QBoxLayout *layout, + *layout_l, + *layout_pb; + QTextBrowser *info; + + QLabel *llabel; + QLineEdit *elabel; + QToolButton *elabel_clear, + *elabel_search; + QListView *list; + QPushButton *pb_load, + *pb_cancel; + + ListModel *model; + QSqlDatabase db; + int report_id; +}; + +#endif // DB_REPORT_SELECTION + diff --git a/gui/include/defs.h b/gui/include/defs.h new file mode 100644 index 0000000..7fe129b --- /dev/null +++ b/gui/include/defs.h @@ -0,0 +1,41 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Gennady "ShultZ" Kozlov + * + * 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 _defs_h +#define _defs_h + + +#if defined (_WIN32) + +#include + +#if defined(__MINGW32__) +#define msleep(t) _sleep(t) +#define sleep(t) _sleep((t) << 10) +#else +#define msleep(t) Sleep(t) +#define sleep(t) Sleep((t) << 10) +#endif + +#else + +#ifndef msleep +#define msleep(t) usleep((t) << 10) +#endif +extern int min(int a, int b); +extern int max(int a, int b); + +#endif + + +#endif + diff --git a/gui/include/device.h b/gui/include/device.h new file mode 100644 index 0000000..1ca5ece --- /dev/null +++ b/gui/include/device.h @@ -0,0 +1,580 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 _DEVICE_H +#define _DEVICE_H + +#include +#include +#include +#include +#include + +#include +#include + +#define MINFO_TREE +//#define FT_AVG +//#define SHOW_SPEEDS + +#define TEST_MAX 6 +#define TEST_RT 0x0001 +#define TEST_WT 0x0002 +#define TEST_ERRC 0x0004 +#define TEST_JB 0x0008 +#define TEST_FT 0x0010 +#define TEST_TA 0x0020 + +#ifdef MINFO_TREE +class QTreeWidgetItem; +#endif +class QPxIODevice; +class QProcess; +class QTcpSocket; + +#define GRAPH_BLER 1 +#define GRAPH_E11 (1 << 1) +#define GRAPH_E21 (1 << 2) +#define GRAPH_E31 (1 << 3) +#define GRAPH_E12 (1 << 4) +#define GRAPH_E22 (1 << 5) +#define GRAPH_E32 (1 << 6) + +#define GRAPH_PIE (1 << 1) +#define GRAPH_PI8 (1 << 2) +#define GRAPH_PIF (1 << 3) +#define GRAPH_POE (1 << 4) +#define GRAPH_PO8 (1 << 5) +#define GRAPH_POF (1 << 6) + +#define GRAPH_LDC (1 << 1) +#define GRAPH_BIS (1 << 4) + +#define GRAPH_UNCR (1 << 7) + + +#define FEATURE_POWEREC 0x00000001 +#define FEATURE_HIDECDR 0x00000002 +#define FEATURE_SINGLESESSION 0x00000004 +#define FEATURE_SPEEDREAD 0x00000008 +#define FEATURE_BITSETR 0x00000010 +#define FEATURE_BITSETRDL 0x00000020 +#define FEATURE_SIMULPLUS 0x00000040 + +#define FEATURE_GIGAREC 0x00000080 +#define FEATURE_VARIREC_CDBASE 0x00000100 +#define FEATURE_VARIREC_CDEXT 0x00000200 +#define FEATURE_VARIREC_CD ( FEATURE_VARIREC_CDBASE | FEATURE_VARIREC_CDEXT) +#define FEATURE_VARIREC_DVD 0x00000400 +#define FEATURE_VARIREC ( FEATURE_VARIREC_CD | FEATURE_VARIREC_DVD ) +#define FEATURE_SECUREC 0x00000800 +#define FEATURE_SILENT 0x00001000 + +#define FEATURE_AS_BASE 0x00002000 +#define FEATURE_AS_EXT 0x00004000 +#define FEATURE_AS ( FEATURE_AS_BASE | FEATURE_AS_EXT ) +#define FEATURE_DESTRUCT 0x00008000 +#define FEATURE_F1TATTOO 0x00010000 +#define FEATURE_PIOLIMIT 0x00020000 +#define FEATURE_PIOQUIET 0x00040000 + +#define FEATURE_LOEJ 0x00080000 +#define FEATURE_LOEJ_TOGGLE 0x00100000 +#define FEATURE_LOCK 0x00200000 +#define FEATURE_LOCK_TOGGLE 0x00400000 + +#define AS_ACTION_MODE 1 +#define AS_ACTION_ACT 2 +#define AS_ACTION_DEACT 3 +#define AS_ACTION_DEL 4 +#define AS_ACTION_CLEAR 5 +#define AS_ACTION_CRE 6 +#define AS_ACTION_MQCK 7 + +#define AS_MODE_OFF 0 +#define AS_MODE_AUTO 1 +#define AS_MODE_ON 2 +#define AS_MODE_FORCED 3 + +#define ASCRE_FULL 8 +#define ASCRE_REPLACE 16 + +#define ASMQCK_ADV 32 + +#define PIOQ_QUIET 0 +#define PIOQ_STD 1 +#define PIOQ_PERF 2 + +#define QPX_FILE_SIGN "QPXD" + +#if 0 +// chunk. cnout = 1 +#define META_TEST_ERRC_SUMMARY 57 +#define META_TEST_JB_SUMMARY 58 +#define META_TEST_FT_SUMMARY 59 +#endif + +struct ASDB_item { + bool present, active; + QString type; + QString mid,speed,writes; +}; + +typedef QList ASDB; + +struct DevFeatures { + int supported; + int enabled; + + int prec_spd; + float grec; + char vrec_cd_pwr; + int vrec_cd_str; + char vrec_dvd_pwr; + int vrec_dvd_str; + + QString sr_pass; + + int sm_cd_rd, + sm_cd_wr, + sm_dvd_rd; + bool sm_access; // 0 = slow, 1 = fast + int sm_trayl, sm_traye; + bool sm_nosave; + + int psm_cd_rd, + psm_cd_wr, + psm_dvd_rd; +// psm_dvd_wr; + bool psm_access; // 0 = slow, 1 = fast + int psm_trayl, psm_traye; + + int tattoo_inner, + tattoo_outer; + QString tattoo_file; + + int as_action; + int as_act_mode; + int as_mode; + int as_idx; + int as_mqckspd; + QString as_mqckres; + int pioq_quiet; + bool pioq_nosave; +}; + +struct DI_Transfer { + quint64 lba; + float spdx; + uint32_t spdk; +}; + +template +struct Errc_ARRAY { + quint64 lba; + float spdx; + T err[8]; +}; + +template +struct Errc_CD { + quint64 lba; + float spdx; + T bler; + T e11, e21, e31; + T e12, e22, e32; + T uncr; +}; + +template +struct Errc_DVD { + quint64 lba; + float spdx; + T res; + T pie, pi8, pif; + T poe, po8, pof; + T uncr; +}; + +template +struct Errc_BD { + quint64 lba; + float spdx; + T res0; + T ldc, res1, res2; + T bis, res3, res4; + T uncr; +}; + +template +union Errc { + Errc_ARRAY raw; + Errc_CD cd; + Errc_DVD dvd; + Errc_BD bd; +}; + +typedef Errc DI_Errc; +typedef Errc TOT_Errc; +typedef Errc AVG_Errc; + +void ErrcADD(TOT_Errc *tot, const DI_Errc& o); +void ErrcMAX(DI_Errc *max, const DI_Errc& o); +void CDErrcAVG(AVG_Errc *avg, TOT_Errc *tot, uint64_t blocks); +void DVDErrcAVG(AVG_Errc *avg, TOT_Errc *tot, uint64_t blocks); +void BDErrcAVG(AVG_Errc *avg, TOT_Errc *tot, uint64_t blocks); + +struct DI_JB { + quint64 lba; + float spdx; + float jitter; + float asymm; +}; + +struct MM_JB { + float jmin; + float jmax; + float bmin; + float bmax; +}; + +struct DI_FT { + quint64 lba; + float spdx; + quint32 fe; + quint32 te; +}; + +struct DI_TA { + //float T; + int idx; + int pit; + int land; +}; + +class TestData { +public: + TestData() {}; + ~TestData() {}; + void clear() { + clearRT(); + clearWT(); + clearErrc(); + clearJB(); + clearFT(); + clearTA(); + }; + + inline void clearRT() { + rt.clear(); + rt_time = 0; + }; + inline void clearWT() { + wt.clear(); + wt_time = 0; + }; + inline void clearErrc() { + errc.clear(); + errc_time = 0; + + errcTOT.raw.spdx=0.0; for (int i=0; i<8; i++) errcTOT.raw.err[i]=0; + errcMAX.raw.spdx=0.0; for (int i=0; i<8; i++) errcMAX.raw.err[i]=0; + errcAVG.raw.spdx=0.0; for (int i=0; i<8; i++) errcAVG.raw.err[i]=0.0; + }; + inline void clearJB() { + jb.clear(); + jb_time = 0; + + jbMM.jmin=0.0; + jbMM.jmax=0.0; + jbMM.bmin=0.0; + jbMM.bmax=0.0; + }; + inline void clearFT() { + ft.clear(); + ft_time = 0; + + ftMAX.fe = 0; + ftMAX.te = 0; + }; + inline void clearTA() { + for (int i=0; i<6; i++) ta[i].clear(); + ta_time = 0; + }; + + QList rt; + QList wt; + QList errc; + QList jb; + QList ft; + QList ta[6]; + + TOT_Errc errcTOT; + DI_Errc errcMAX; + AVG_Errc errcAVG; + MM_JB jbMM; + DI_FT ftMAX; + + float rt_time; + float wt_time; + float errc_time; + float jb_time; + float ft_time; + float ta_time; +}; + +struct MediaInfo { + QString type; + QString label; + +// bool isCD; +// bool isDVD; + + int spd1X; +// int itype; + QString category; + QString mid; + QString layers; + int ilayers; + QString prot; + QString regions; + + int creads; + int creadm; + QString creadmsf; + int cfrees; + int cfreem; + QString cfreemsf; + int ctots; + int ctotm; + QString ctotmsf; + + QString erasable; + QString dstate; + QString sstate; + QStringList rspeeds; + QStringList wspeedsd; + QStringList wspeedsm; + QStringList tspeeds_errc; + int tdata_errc; + QStringList tspeeds_jb; + + float grec; + QString writer; +}; + +struct TestSpeeds { + int32_t rt, wt, errc, jb, ft; +}; + +class MediaWatcher; +class ResultsReader; +class ResultsWriter; + +class device : public QObject +{ + Q_OBJECT +public: + enum devtype { + DevtypeNone = 0, + DevtypeVirtual = 1, + DevtypeLocal = 2, + DevtypeTCP = 3 + }; + + enum ThreadType { + threadNone = 0, + threadDevice = 1, + threadMedia = 2, + threadTest = 3, + threadGetFeatures = 4, + threadGetASDB = 5, + threadMQCK = 6, + threadAScre = 7, + threadDestruct = 8, + threadTattoo = 9 + }; + + device(QObject* p); + ~device(); + + bool isRunning(); + void clearMinfo(); + + bool update_device_info(); + bool update_media_info(); + void clear_media_info(); + bool update_plugin_info(); + bool getFeatures(); + bool getASDB(); + bool setFeature(int, bool); + bool setComplexFeature(int, DevFeatures*); + + bool startMqck(); + bool startAScre(); + bool startDestruct(); + bool startTattoo(); + + bool start_tests(); + bool stop_tests(); + + void startWatcher(); + void stopWatcher(); + void pauseWatcher(); + void unpauseWatcher(); + + void save(QIODevice*); + bool isSaving(); + bool saveResult(); + void load(QIODevice*); + bool isLoading(); + bool loadResult(); + +devtype type; + QString host; + uint16_t port; + QString path; + QString id; + + QString ven; + QString dev; + QString fw; + QString tla; + QString sn; + QString buf; + QString iface; + QString loader; + + int32_t life_dn; + QString life_cr, + life_cw, + life_dr, + life_dw; + + int8_t rpc_phase; + int8_t rpc_reg; + int8_t rpc_ch; + int8_t rpc_rst; + + bool info_set; + + uint64_t cap; + uint64_t cap_rd; + uint64_t cap_wr; + + bool plextor_lock; + + int test_cap; + int test_req; + int tests, ctest; + int test_spd; + QString plugin; + QStringList plugin_names; + QStringList plugin_infos; + QString nprocess; + float pprocess; + + DevFeatures features; + ASDB asdb; + MediaInfo media; + TestData testData; + TestSpeeds tspeeds; + bool WT_simul; + timeval timeSta; + + QMutex *mutex; + QMutex *io_mutex; + + QProcess *proc; + QTcpSocket *sock; + QPxIODevice *io; + + ResultsReader *resReader; + ResultsWriter *resWriter; + + bool autoupdate; +#ifdef MINFO_TREE + QList info_media; +#endif +/* + uint64_t caps; + uint64_t caps_rd; + uint64_t caps_wr; +*/ + +private slots: + void qscan_process_info(); + void qscan_process_test(); + void qscan_callback_info(); + void qscan_callback_test(); + + void watcherStarted(); + void watcherStoped(); + void watcherEventLoading(); + void watcherEventRemoved(); + void watcherEventNew(); + void watcherEventNoMedia(); + + void resLoaderDone(); + +signals: + void mediaRemoved(); + void mediaNew(); + void doneDInfo(int); + void doneMInfo(int); + void doneGetFeatures(int); + void doneGetASDB(int); + + void process_started(); + void process_finished(); + void process_progress(); + + void testsDone(); + void testsError(); + + void block_RT(); + void block_WT(); + void block_ERRC(); + void block_JB(); + void block_FT(); + void block_TA(); + +private: + void qscan_process_line(QString&); + void cdvdcontrol_process_line(QString&); + void cdvdcontrol_process_asdb(QString&); + + bool start(); + bool start_update_info(); + bool next_test(); + ThreadType threadType; + bool stop; + bool running; + bool preserveMediaInfo; + + int taIdx; + MediaWatcher *mwatcher; +}; + +class devlist : public QList { +public: + devlist(); + ~devlist(); + int idx(); + void setIdx(int iidx); + void clear(); + device* current(); + int devidx; +}; + +#endif + diff --git a/gui/include/devsettings.h b/gui/include/devsettings.h new file mode 100644 index 0000000..aca3850 --- /dev/null +++ b/gui/include/devsettings.h @@ -0,0 +1,58 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Gennady "ShultZ" Kozlov + * + * 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 _DEVSETTINGS_H +#define _DEVSETTINGS_H + +#include + +class QBoxLayout; +class QGridLayout; + +class QTreeWidget; +class QTreeWidgetItem; +class ImagesList; + +class QLabel; +class QLineEdit; +class QCheckBox; +class QComboBox; +class QPushButton; + +class TextSlider; +class QPxSettings; +class device; + +class devSettings : public QDialog +{ + Q_OBJECT +public: + devSettings(QPxSettings *iset, device *idev, QWidget *p=0, Qt::WindowFlags fl = 0); + ~devSettings(); + +public slots: + +private slots: + void setPage(int); + +private: + QBoxLayout *layout; + QBoxLayout *layoutc; + ImagesList *ilist; + QWidget *cpage; + + QPxSettings *set; + device *dev; +}; + +#endif + diff --git a/gui/include/devsettings_widgets.h b/gui/include/devsettings_widgets.h new file mode 100644 index 0000000..fcdd6a3 --- /dev/null +++ b/gui/include/devsettings_widgets.h @@ -0,0 +1,397 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009-2010 Gennady "ShultZ" Kozlov + * + * 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 _DEVSETTINGS_WIDGETS_H +#define _DEVSETTINGS_WIDGETS_H + +#include +#include +#include + +class QBoxLayout; +class QGridLayout; + +class QLabel; +class QLineEdit; +class QCheckBox; +class QComboBox; +class QPushButton; +class QRadioButton; +class QTreeWidget; +class QTreeWidgetItem; +class QButtonGroup; +class QSlider; + +class TextSlider; +class device; + +class devctlAutostrategy; +class TattooWidget; + +class devctlCommon : public QGroupBox +{ + Q_OBJECT + +public: + devctlCommon(device* idev, QWidget* p=NULL); + ~devctlCommon() {}; + +private slots: + void set_prec(bool); + void set_hcdr(bool); + void set_ss(bool); + void set_spdrd(bool); + void set_bitset(bool); + void set_bitset_dl(bool); + void set_simulplus(bool); +private: + device* dev; + + QGridLayout *layout; + QCheckBox *ck_prec; + QLabel *pl_prec; + QLabel *l_prec; + + QCheckBox *ck_hcdr, + *ck_ss, + *ck_spdrd, + *ck_bitset, + *ck_bitset_dl, + *ck_simulplus; + + devctlAutostrategy *as; +}; + +// GigaRec control + +class devctlGigarec : public QGroupBox +{ + Q_OBJECT +public: + devctlGigarec(device* idev, QWidget* p=NULL); + ~devctlGigarec() {}; + +private slots: + void set(bool); + void set(); + +private: + device* dev; + QBoxLayout *layout; + TextSlider *grec_ratio; + QLabel *l_cap; + QLabel *l_ncap; + QLabel *l_hint; +}; + +// VariRec control + +class devctlVarirecBase : public QGroupBox +{ + Q_OBJECT + +public: + devctlVarirecBase(device* idev, QString title, QWidget* p=NULL); + ~devctlVarirecBase() {}; + +protected: + device* dev; + QBoxLayout *layout; + QLabel *pl_pwr; + TextSlider *pwr; + QBoxLayout *layouts; + QLabel *pl_str; + QComboBox *str; + QLabel *l_hint; +}; + +// VariRec control (CD) + +class devctlVarirecCD : public devctlVarirecBase +{ + Q_OBJECT +public: + devctlVarirecCD(device* idev, QWidget* p=NULL); + ~devctlVarirecCD() {}; + +private slots: + void set(bool); + void set(); +}; + +// VariRec control (DVD) + +class devctlVarirecDVD : public devctlVarirecBase +{ + Q_OBJECT +public: + devctlVarirecDVD(device* idev, QWidget* p=NULL); + ~devctlVarirecDVD() {}; + +private slots: + void set(bool); + void set(); +}; + +class devctlVarirec: public QWidget +{ + Q_OBJECT +public: + devctlVarirec(device* idev, QWidget* p=NULL); + ~devctlVarirec() {}; + +private: + QBoxLayout *layout; + devctlVarirecCD *vrec_cd; + devctlVarirecDVD *vrec_dvd; +}; + +// SecuRec control + +class devctlSecurec : public QGroupBox +{ + Q_OBJECT +public: + devctlSecurec(device* idev, QWidget* p=NULL); + ~devctlSecurec() {}; + +private slots: + void set(); + void reset(); + int validate(); + +private: + device* dev; + QGridLayout *layout; + + QLabel *l_pwd[2]; + QLineEdit *e_pwd[2]; + QFrame *hline0; + QPushButton *pb_set; + QLabel *l_active; + QPushButton *pb_reset; + QLabel *l_hint; +}; + +// Silent Mode control + +class devctlSilent : public QWidget +{ + Q_OBJECT +public: + devctlSilent(device* idev, QWidget* p=NULL); + ~devctlSilent() {}; + +private slots: + void set(); + +private: + device* dev; + QBoxLayout *layoutm; + QGroupBox *cw; + QGridLayout *layout; + + QLabel *h_speed, + *h_access, + *h_tray; +// R/W Speeds + QLabel *spdr, *spdw, *spdc, *spdd; + QComboBox *box_rcd, + *box_wcd, + *box_rdvd, + *box_wdvd; +// Access time + QButtonGroup *grp_access; + QRadioButton *a_slow, + *a_fast; +// Tray speed + QLabel *l_load, *l_eject; + QSlider *s_load, *s_eject; + + QBoxLayout *layoutb; + QCheckBox *ck_perm; + QPushButton *pb_save; +}; + +// AutoStrategy control + +class devctlAutostrategy : public QWidget +{ + Q_OBJECT +public: + devctlAutostrategy(device* idev, QWidget* p=NULL); + ~devctlAutostrategy() {}; +private slots: + void asdb(); + void set_mode(int); + void run_mqck(); + +private: + device* dev; + QGridLayout *layout; + +// AS mode + QGroupBox *box_as; + QGridLayout *layout_as; + QButtonGroup *grp_mode; + QRadioButton *rb_as_on, + *rb_as_off, + *rb_as_auto, + *rb_as_forced; + QPushButton *pb_asdb; +// MQCK + QGroupBox *box_mqck; + QGridLayout *layout_mqck; + QButtonGroup *grp_mqck; + + QRadioButton *rb_mqck_q, + *rb_mqck_a; + QLabel *lmqck_spd; + QComboBox *bmqck_spd; + QPushButton *pb_mqck; + QLabel *lmqck_res; +}; + +// AutoStrategy DB control + +class devctlAutostrategyDB : public QDialog +{ + Q_OBJECT +public: + devctlAutostrategyDB(device* idev, QWidget* p=NULL); + ~devctlAutostrategyDB() {}; + +public slots: + void setVisible(bool); + +private slots: + void db_toggle(QTreeWidgetItem*); + void db_act(); + void db_act(int); + void db_deact(); + void db_deact(int); + void db_del(); + void db_clear(); + + void run_ascre(); + void db_update(int idx=-1); + void db_update_done(bool); + +private: + + device* dev; + + QBoxLayout *layout; + QBoxLayout *layoutb; + + QTreeWidget *tree; + QPushButton *pb_act, + *pb_deact, + *pb_del, + *pb_clear; + +// Strategy creation + QGroupBox *box_ascre; + QGridLayout *layout_ascre; + QGroupBox *grp_ascre_mode, + *grp_ascre_act; + QBoxLayout *layout_crem, + *layout_crea; + QRadioButton *rb_ascre_quick, + *rb_ascre_full, + *rb_ascre_add, + *rb_ascre_replace; + QPushButton *ascre_start; + + +}; + +// PlexEraser control + +class devctlDestruct : public QGroupBox +{ + Q_OBJECT +public: + devctlDestruct(device* idev, QWidget* p=NULL); + ~devctlDestruct() {}; +private slots: + void start(); +private: + device* dev; + QGridLayout *layout; + + QRadioButton *rb_quick; + QLabel *pl_quick; + QRadioButton *rb_full; + QLabel *pl_full; + + QPushButton *pb_start; + QLabel *l_hint; +}; + + +// Pioneer QuietMode + +class devctlPioquiet : public QGroupBox +{ + Q_OBJECT +public: + devctlPioquiet(device* idev, QWidget* p=NULL); + ~devctlPioquiet() {}; + +private slots: + void set(); + +private: + device* dev; + QGridLayout *layout; + + QGroupBox *box_mode; + QBoxLayout *layoutm; + QRadioButton *rb_quiet; + QRadioButton *rb_std; + QRadioButton *rb_perf; + + QCheckBox *ck_limit; + + QCheckBox *ck_perm; + QPushButton *pb_set; +}; + +// Yamaha CRW-F1 Disc T@2 + +class devctlF1Tattoo : public QGroupBox +{ + Q_OBJECT +public: + devctlF1Tattoo(device* idev, QWidget* p=NULL); + ~devctlF1Tattoo() {}; + +private slots: + void loadImage(); + void burn(); + +private: + device* dev; + int r0,r1; + QImage srcimg, img; + + QGridLayout *layout; + TattooWidget *tw; + QBoxLayout *layoutb; + QLabel *l_file; + QPushButton *pb_load; + QPushButton *pb_burn; +}; + +#endif + diff --git a/gui/include/dpi_metrics.h b/gui/include/dpi_metrics.h new file mode 100644 index 0000000..91da957 --- /dev/null +++ b/gui/include/dpi_metrics.h @@ -0,0 +1,25 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Gennady "ShultZ" Kozlov + * + * 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 DPI_METRICS_H +#define DPI_METRICS_H +//-------------------------------------------------------------- +#include +//-------------------------------------------------------------- +static inline int inchesToPixelsX(float inches, QPaintDevice *device) { return qRound(inches * device->logicalDpiX()); } +static inline int inchesToPixelsY(float inches, QPaintDevice *device) { return qRound(inches * device->logicalDpiY()); } +static inline int inchesToPixelsX(float inches, int lDpiX) { return qRound(inches * lDpiX); } +static inline int inchesToPixelsY(float inches, int lDpiY) { return qRound(inches * lDpiY); } +//-------------------------------------------------------------- +static inline float mmToInches(double mm) { return mm*0.039370147; } +//-------------------------------------------------------------- +#endif // DPI_METRICS_H + diff --git a/gui/include/errc_detailed.h b/gui/include/errc_detailed.h new file mode 100644 index 0000000..75a66e6 --- /dev/null +++ b/gui/include/errc_detailed.h @@ -0,0 +1,65 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009-2010 Gennady "ShultZ" Kozlov + * + * 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 __ERRC_DETAILED_H +#define __ERRC_DETAILED_H + +#include + +class QGridLayout; +class QFrame; +class QLabel; +class QPxGraph; + +class QPxSettings; +class device; +class devlist; + +class ErrcDetailedDialog : public QDialog +{ + Q_OBJECT + +public: + ErrcDetailedDialog(QPxSettings *iset, devlist *idev, QWidget *p=0, Qt::WindowFlags fl = 0); + ~ErrcDetailedDialog(); + void updateAll(); + void updateGraphs(device *idev = NULL); + +signals: + void closed(); + +protected: + void hideEvent(QHideEvent*); + +protected slots: + void changeScale(); + +private: + devlist *devices; + QPxSettings *settings; + const char** labels; + + QGridLayout *layout; + QGridLayout *layout_top; + QPxGraph *graph[8]; + + QGridLayout *layout_summary; + QLabel *pl_tot, *pl_max, *pl_avg; + QFrame *hline0, *hline1; + QLabel *pl_name[8]; + QLabel *l_tot[8]; + QLabel *l_max[8]; + QLabel *l_avg[8]; +}; + +#endif + diff --git a/gui/include/graphtab.h b/gui/include/graphtab.h new file mode 100644 index 0000000..7662d7b --- /dev/null +++ b/gui/include/graphtab.h @@ -0,0 +1,72 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 GRAPHTAB_H +#define GRAPHTAB_H + +#include +#include + +class device; +class QLabel; +class QFrame; +class QGroupBox; +class QBoxLayout; +class SplitButton; +class QPxGraph; + +class devlist; +class QPxSettings; + +class GraphTab : public QWidget +{ + Q_OBJECT +public: + GraphTab(QPxSettings *iset, devlist *idev, QString iname, int test, QWidget *p=0, Qt::WindowFlags fl = 0); + ~GraphTab(); + void drawGraph(QImage& img, device *dev, int ttype, int eflags=0); + +public slots: + void clear(); + virtual void selectDevice()=0; + void updateLast(int, bool *Tvalid=NULL, bool force=0); +// void setDevice(device *idev); + void reconfig() { updateLegend(); updateGraph(); }; + +private slots: + void infoToggle(); + +protected: + virtual void updateGraph()=0; + virtual void updateLegend()=0; + + devlist *devices; + QPxSettings *settings; + QWidget *infow; + QPxGraph *graph; + QLabel *ltime; + timeval prevT; + +private: + QString name; + QBoxLayout *layout; + QBoxLayout *layoutl; + QWidget *lw; + QGroupBox *grp_time; + QBoxLayout *layoutt; + SplitButton *split; + QFrame *vline0; +// bool prevTvalid; +}; + +#endif + diff --git a/gui/include/hostedit_dialog.h b/gui/include/hostedit_dialog.h new file mode 100644 index 0000000..7a37cfe --- /dev/null +++ b/gui/include/hostedit_dialog.h @@ -0,0 +1,48 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 HOSTEDIT_DIALOG_H +#define HOSTEDIT_DIALOG_H + +#include +#include +#include +#include + +class QLabel; +class QDialogButtonBox; +class QGridLayout; + +class hostEditDialog : public QDialog +{ + Q_OBJECT +public: + hostEditDialog(QString host, int port, QWidget* p=NULL, Qt::WindowFlags f=0); + ~hostEditDialog(); + inline QString hostname() { return e_host->text(); }; + inline int port() { return e_port->value(); }; + +private slots: + void setPortDfl(); + void hostChanged(QString&); + +private: + QGridLayout *layout; + QLabel *l_host; + QLineEdit *e_host; + QLabel *l_port; + QSpinBox *e_port; + QPushButton *bdef; + QDialogButtonBox *bbox; +}; + +#endif + diff --git a/gui/include/image_label.h b/gui/include/image_label.h new file mode 100644 index 0000000..e7e67e3 --- /dev/null +++ b/gui/include/image_label.h @@ -0,0 +1,63 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 __image_label_h +#define __image_label_h + +#include + +#define HIGHLIGHT_FOCUSED + +class QLabel; +class QLayout; +class QBoxLayout; + +class ImageLabel : public QWidget +{ + Q_OBJECT + +public: + ImageLabel(int minW, QString label, QImage image, int idx, QWidget *parent); + ImageLabel(int minW, QSize is, QString label, QImage image, int idx, QWidget *parent); + ImageLabel(int minW, int iw, int ih, QString label, QImage image, int idx, QWidget *parent); + ~ImageLabel(); + inline void setLabel(QString label) { name = label; }; + inline void setImage(QImage image) { img = image; }; + +signals: + void selected(int); + +public slots: + void select(int); + +protected: + void mousePressEvent(QMouseEvent*); + +#ifdef HIGHLIGHT_FOCUSED + void enterEvent(QEvent*); + void leaveEvent(QEvent*); +#endif + void paintEvent(QPaintEvent*); + +private: + bool ck; +#ifdef HIGHLIGHT_FOCUSED + bool focus; +#endif + QString name; + QImage img; + int id; + int iconw, iconh; + int minW; +}; + +#endif + diff --git a/gui/include/images_list.h b/gui/include/images_list.h new file mode 100644 index 0000000..50dfe5b --- /dev/null +++ b/gui/include/images_list.h @@ -0,0 +1,72 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 __images_list_h +#define __images_list_h + +#include +#include + +#ifndef QT_NO_DEBUG +//#define __images_list_debug +#endif + +class QBoxLayout; +class QWidget; +class ImageLabel; +class ImageDialog; + + +class ImagesList : public QScrollArea +{ + Q_OBJECT + +public: + enum ListDir { + Vertical = 1, + Horizontal = 2 + }; + + ImagesList(int minW, QWidget *parent, ListDir orient = Vertical); + ImagesList(int minW, QSize isize, QWidget *parent, ListDir orient = Vertical); + ImagesList(int minW, int iw, int ih, QWidget *parent, ListDir orient = Vertical); + ~ImagesList(); + + int addLabel(QString label, QImage image, int imgid=-1); + +public slots: + void clear(); + +private slots: + void clicked(int); + +signals: + void selected(int); + +protected: + void keyPressEvent(QKeyEvent*); + +private: + void winit(ListDir orient); + + int current; +// QScrollArea *w; + void clear_img(); + QWidget *cwidget; + QBoxLayout *clayout; + + QList images_l; + int iconw, iconh; + int minW; +}; + +#endif + diff --git a/gui/include/mainwidget.h b/gui/include/mainwidget.h new file mode 100644 index 0000000..d823db8 --- /dev/null +++ b/gui/include/mainwidget.h @@ -0,0 +1,83 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2010 Gennady "ShultZ" Kozlov + * + * 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 _MAINWIDGET_H +#define _MAINWIDGET_H + +#include +class QBoxLayout; +class QStackedLayout; +class QFrame; + +class QAbstractButton; +class QPushButton; +class QButtonGroup; + +#include +#include + +class QIODevice; + +class tabDevInfo; +class tabMediaInfo; +class tabTransfer; +class tabERRC; +class tabJB; +class tabFETE; +class tabTA; +class QPxGraph; + +class QPxMainWidget : public QWidget +{ + Q_OBJECT +public: + QPxMainWidget(QPxSettings *iset, devlist *idev, QWidget *p=0); + ~QPxMainWidget(); + void drawGraph(QImage& img, device *dev, int ttype, int eflags=0); + +public slots: + void clearDev(); + void clearMedia(); + void selectDevice(); + void reconfig(); + void setSidebarVisible(bool); + void selectTab(int); + //inline void reconfig() { emit configured(); }; + +private slots: + +signals: + void deviceSelected(); + void configured(); + +private: + devlist *devices; + QPxSettings *settings; + + QBoxLayout *layout; + QFrame *bframe; + QBoxLayout *layout_buttons; + QButtonGroup *grp; + QStackedLayout *stack; + + tabDevInfo *tab_DevInfo; + tabMediaInfo *tab_MediaInfo; + tabTransfer *tab_RT; + tabTransfer *tab_WT; + tabERRC *tab_ERRC; + tabJB *tab_JB; + tabFETE *tab_FETE; + tabTA *tab_TA; +}; + +#endif + diff --git a/gui/include/mainwindow.h b/gui/include/mainwindow.h new file mode 100644 index 0000000..aab8c9c --- /dev/null +++ b/gui/include/mainwindow.h @@ -0,0 +1,200 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2010 Gennady "ShultZ" Kozlov + * + * 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 _MAINWINDOW_H +#define _MAINWINDOW_H + +#include +#include +#include +#include +#include + +class QAction; +class QActionGroup; + +class QWidget; +class QMenuBar; +class QToolBar; +class QHBoxLayout; +class QVBoxLayout; +//class QToolButton; +class QPushButton; + +class QLabel; +class QProgressBar; +class QComboBox; +class QFrame; +class QProcess; +class QTcpSocket; +class QPxIODevice; + +class QPxMainWidget; +class ProgressWidget; +class TestDialog; + +class QTextDocument; + +#ifndef QT_NO_DEBUG +class QMutex2 : public QMutex +{ +public: + inline void lock() + { qDebug(" * QMutex::lock()"); QMutex::lock(); } ; + inline bool tryLock() + { qDebug(" * QMutex::trylock()"); return QMutex::tryLock(); } ; + inline bool tryLock(int to) + { qDebug(" * QMutex::trylock(int)"); return QMutex::tryLock(to); } ; + inline void unlock() + { qDebug(" * QMutex::unlock()"); QMutex::unlock(); } ; +}; +#endif + +class QPxToolMW : public QMainWindow +{ + Q_OBJECT + +public: + QPxToolMW(int ac, char **av, QWidget *p=0, Qt::WindowFlags fl = 0); + ~QPxToolMW(); + +public slots: + void mediaUpdated(int); + +public slots: + void process_started(); // for statusBar + void process_finished(); // updating + void process_progress(); // + +private slots: + void setDevice(int idx); + void loejToggle(); + void lockToggle(); + void selectTab(); + + void select_tests(); + void select_tests(device *idev); + void run_tests(); + void cancel_run_tests(); + void terminate_tests(); + + + void update_device_info(); + void update_media_info(); + void device_settings(); + void update_features_done(int); + void exit(); + void scanbus(); + void qscan_process_scanbus(); + void preferences(); + + void save_report(); + void print_results(); + void export_results(); + void save_results(); + void save_results_db(device *idev = NULL); + void load_results(); + void load_results(QString); + void load_results_db(); + + void about(); + + void tests_done(); + void tests_error(); + +protected: + virtual void dragEnterEvent(QDragEnterEvent*); + virtual void dropEvent(QDropEvent*); + +private: + ProgressWidget *progress; + void winit(); + void create_actions(); + void winit_menubar(); + void winit_toolbar(); + void winit_statusbar(); + void settings_load(); + void settings_save(); + + void scanbus_local(); + void scanbus_remote(); + void update_media_info(device* dev); + void run_test(int); +// void prepare_images(QString path=QString::null); + void autosave_report(device* dev); + void save_report(device* dev, QString fname); + void prepare_report(device* dev, QTextDocument*); + QString generate_html(device *dev, QString idir = QString()); + +// infoTypeT infoType; + device::devtype dt; + QString dhost; + int dport; + QPxIODevice *scanbusio; +#ifndef QT_NO_DEBUG + QMutex2 mutex_dev; +#else + QMutex mutex_dev; +#endif +// bool scanbusDis; + + QMenuBar *menubar; + + QToolBar *toolbar; + QAction *act_exit, + *act_pref, + *act_save, + *act_save_db, + *act_load, + *act_load_db, + *act_export, + *act_report, + *act_print, + *act_scanbus, + *act_minfo, + *act_devctl, + *act_test, + *act_stop, + *act_about; + + QAction *act_sb; + QList act_sblist; + QActionGroup *act_sbgrp; + + QWidget *cwidget; + + QVBoxLayout *layout; + QHBoxLayout *layout_dev; + QLabel *l_dev; + QComboBox *c_dev; + QPushButton *pb_loej; + QPushButton *pb_lock; +// QFrame *hline; + QPxMainWidget *mwidget; + +// statusbar widgets + QProgressBar *status_progress; + QLabel *status_process, + *status_media, + *status_mid; + + TestDialog *testDialog; + + devlist devices; // devices +// QSqlDatabase db; // reports database connection +// settings + QPxSettings set; + bool splash; +}; + +#endif + diff --git a/gui/include/mcapwidget.h b/gui/include/mcapwidget.h new file mode 100644 index 0000000..26e9292 --- /dev/null +++ b/gui/include/mcapwidget.h @@ -0,0 +1,40 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 + +class MediaCapWidget : public QWidget +{ + Q_OBJECT +public: +// MediaCapWidget(QWidget *p, Qt::WindowFlags f) : QWidget(p,f) {}; + MediaCapWidget(QString itext, bool tri, quint64 df, QWidget *p, Qt::WindowFlags f=0); + ~MediaCapWidget(); + void setText(QString itext); + inline quint64 flag() { return cf; }; + void setR(quint64 r); + void setW(quint64 r); + void setCap(quint64 s); + void clear(); +// inline void setTristate(bool s) { tristate=s; rd=0; wr=0; }; + + virtual QSize sizeHint() const; +protected: + void paintEvent(QPaintEvent*); +private: + void setRW(); + quint64 cf; + QImage icon; + QString text; + bool rd, wr, tristate; +}; + diff --git a/gui/include/mwatcher.h b/gui/include/mwatcher.h new file mode 100644 index 0000000..750275c --- /dev/null +++ b/gui/include/mwatcher.h @@ -0,0 +1,48 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2010 Gennady "ShultZ" Kozlov + * + * 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 MWATCHER_H +#define MWATCHER_H + +class device; +class drive_info; + +#include + +class MediaWatcher: public QThread +{ + Q_OBJECT + +public: + MediaWatcher(device *qdev); + ~MediaWatcher(); + + void stop(); + void pause(); + void unPause(); + +signals: + void mediaLoading(); + void mediaRemoved(); + void mediaNew(); + void mediaNoMedia(); + +protected: + virtual void run(); + +private: + bool preq,sreq; + drive_info *dev; +}; + +#endif + diff --git a/gui/include/pref_colors.h b/gui/include/pref_colors.h new file mode 100644 index 0000000..b543102 --- /dev/null +++ b/gui/include/pref_colors.h @@ -0,0 +1,78 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 PREF_COLORS_H +#define PREF_COLORS_H + +#include +#include + +class QBoxLayout; +class QGridLayout; +class QPushButton; +class QMenu; +class QAction; +class QLineEdit; + +class ColorLabel; + +class prefColors : public QWidget +{ + Q_OBJECT +public: + prefColors(QPxSettings *iset, QWidget *p=0, Qt::WindowFlags fl=0); + ~prefColors(); + +private slots: + void loadPresets(); + void savePresets(); + void usePreset(); + void addPreset(); + void delPreset(); + void replacePreset(); + void presetNameValidate(QString); + +private: + QGridLayout *layout; + + ColorLabel *lc_bg; + ColorLabel *lc_grid; + + ColorLabel *lc_rspeed; + ColorLabel *lc_wspeed; + + ColorLabel *lc_errc[8]; + + ColorLabel *lc_jitter; + ColorLabel *lc_asymm; + + ColorLabel *lc_fe; + ColorLabel *lc_te; + + ColorLabel *lc_tapit; + ColorLabel *lc_taland; + + QBoxLayout *layout_presets; + QPushButton *pb_preset, + *pb_replace, + *pb_add, + *pb_del; + QLineEdit *e_preset; + QMenu *menu_preset; + QAction *act_default; + QList act_presets; + QList presets; + + QPxSettings *set; +}; + +#endif + diff --git a/gui/include/pref_common.h b/gui/include/pref_common.h new file mode 100644 index 0000000..483c81a --- /dev/null +++ b/gui/include/pref_common.h @@ -0,0 +1,57 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2010 Gennady "ShultZ" Kozlov + * + * 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 PREF_COMMON_H +#define PREF_COMMON_H + +#include +#include + +class QVBoxLayout; +class QGridLayout; + +class QGroupBox; +class QLabel; +class QCheckBox; + +class prefCommon : public QWidget +{ + Q_OBJECT + +public: + prefCommon(QPxSettings *iset, QWidget *p=0, Qt::WindowFlags fl=0); + ~prefCommon(); + +private: + QVBoxLayout *layout; + + QCheckBox *ck_autow, + *ck_autob; + + QLabel *pl_testsw, + *pl_testsb; + QGridLayout *layout_testsb; + + QCheckBox *ck_rt, + *ck_wt, + *ck_wt_simul, + *ck_errc, + *ck_jb, + *ck_ftb, + *ck_ftw, + *ck_ta; + + QPxSettings *set; +}; + +#endif + diff --git a/gui/include/pref_devices.h b/gui/include/pref_devices.h new file mode 100644 index 0000000..3acf3f0 --- /dev/null +++ b/gui/include/pref_devices.h @@ -0,0 +1,55 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 PREF_DEVICES_H +#define PREF_DEVICES_H + +#include + +class QLabel; +class QCheckBox; +class QGroupBox; +class QTreeWidget; + +class QBoxLayout; +class QPxSettings; + +class prefDevices : public QWidget +{ + Q_OBJECT +public: + prefDevices(QPxSettings *iset, QWidget *p=0, Qt::WindowFlags f=0); + virtual ~prefDevices(); + +protected: + bool eventFilter(QObject* obj, QEvent* e); + void hostsContextMenu(QContextMenuEvent* e); + +private slots: + void hostAdd(); + void hostEdit(); + void hostRemove(); + +private: + QBoxLayout *layout; + + QCheckBox *ck_local; + QCheckBox *ck_remote; + +// QGroupBox *g_hosts; +// QBoxLayout *l_hosts; + QTreeWidget *lst_hosts; + + QPxSettings *set; +}; + +#endif + diff --git a/gui/include/pref_reports.h b/gui/include/pref_reports.h new file mode 100644 index 0000000..06c9781 --- /dev/null +++ b/gui/include/pref_reports.h @@ -0,0 +1,78 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2010 Gennady "ShultZ" Kozlov + * + * 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 PREF_REPORTS_H +#define PREF_REPORTS_H + +#include +#include + +class QGridLayout; +class QHBoxLayout; +class QVBoxLayout; + +class QGroupBox; +class QLabel; +class QLineEdit; +class QPushButton; +class QCheckBox; +class QComboBox; +class QSpinBox; + +class prefReports : public QWidget +{ + Q_OBJECT + +public: + prefReports(QPxSettings *iset, QWidget *p=0, Qt::WindowFlags fl=0); + ~prefReports(); + +private slots: + void select_rep_path(); + void box_rep_toggled(bool); + void check_db_connection(); +private: + QVBoxLayout *layout; + + QGroupBox *box_rep; + QVBoxLayout *layout_rep; + QHBoxLayout *layout_rep_path; + + QLabel *l_rep_path; + QLineEdit *e_rep_path; + QPushButton *pb_rep_path; + +// database settings + QGroupBox *box_rep_db; + QGridLayout *layout_db; + QLabel *ldb_driver, + *ldb_host, + *ldb_port, + *ldb_name, + *ldb_user, + *ldb_pass; + QComboBox *db_driver; + QLineEdit *db_host, + *db_name, + *db_user, + *db_pass; + QSpinBox *db_port; + QPushButton *pb_db_check; + QCheckBox *ck_autosave_db; + + QCheckBox *ck_eject; + + QPxSettings *set; +}; + +#endif // PREF_REPORTS_H + diff --git a/gui/include/preferences.h b/gui/include/preferences.h new file mode 100644 index 0000000..b16128c --- /dev/null +++ b/gui/include/preferences.h @@ -0,0 +1,65 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 _PREFERENCES_H +#define _PREFERENCES_H + +#define PREF_PAGES 4 + +#include +#include + +class QFrame; +class ImagesList; +class QHBoxLayout; +class QVBoxLayout; +class QScrollArea; +class QPushButton; + +class QPxPreferences : public QDialog { + Q_OBJECT + +public: + QPxPreferences(QPxSettings *iset, QWidget *p=0, Qt::WindowFlags f=0); + virtual ~QPxPreferences(); + +public slots: + void setPage(int); + +private slots: + void save(); + void cancel(); + +protected: + void closeEvent(QCloseEvent*); + void keyPressEvent(QKeyEvent*); + +private: + void winit(); + int curPage; + + QPxSettings set; + QPxSettings *set_old; + + QHBoxLayout *layout; + ImagesList *ilist; + + QVBoxLayout *parea; + QFrame *hline0; + QHBoxLayout *layout_butt; + QPushButton *pb_save, + *pb_cancel; +// QWidget* pagew; + QWidget* pages[PREF_PAGES]; +}; + +#endif + diff --git a/gui/include/printpreview.h b/gui/include/printpreview.h new file mode 100644 index 0000000..c8075f0 --- /dev/null +++ b/gui/include/printpreview.h @@ -0,0 +1,70 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 PRINTPREVIEW_H +#define PRINTPREVIEW_H + +#include +#include +#include +#include +#include + +#include + +#include "dpi_metrics.h" + +class PreviewView; +class QTextDocument; +class QBoxLayout; +class QPushButton; +class QLabel; +class QComboBox; +class QSplitter; + +class PrintPreview : public QDialog +{ + Q_OBJECT + friend class PreviewView; + +public: + PrintPreview(QWidget *parent, QTextDocument *document = 0); + virtual ~PrintPreview(); + void setDocument(QTextDocument *document); + + static void printDoc(QWidget* parent, QPrinter* printer, QTextDocument* doc); + +private slots: + void print(); + void pageSetup(); + void scaleChanged(double); + void scaleChanged(QString); + +private: + QLabel *l_scale; + QComboBox *box_scale; + QPushButton *pb_print, + *pb_psetup, + *pb_zoomin, + *pb_zoom1, + *pb_zoomout; + + QTextDocument *doc; + QBoxLayout *layout_main, + *layout_butt; + QSplitter *split; + PreviewView *view, + *thumbs; + QPrinter printer; +}; + +#endif // PRINTPREVIEW_H + diff --git a/gui/include/progresswidget.h b/gui/include/progresswidget.h new file mode 100644 index 0000000..84657cb --- /dev/null +++ b/gui/include/progresswidget.h @@ -0,0 +1,66 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009-2010 Gennady "ShultZ" Kozlov + * + * 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 PROGRESS_WIDGET_H +#define PROGRESS_WIDGET_H + +#include +#include + +class ProgressWidget: public QWidget +{ + Q_OBJECT + +public: + enum Direction { + DirectionForward = 0, + DirectionBackward = 1, + DirectionBoth = 2 + }; + enum ProgressColor { + BgColor = 0, + FrameColor = 1, + TextColor = 2, + RectColor = 3 + }; + + ProgressWidget(int, int, QWidget *p); + ~ProgressWidget(); + void setDirection(ProgressWidget::Direction); + void setText(QString); + void setColor(ProgressColor, QColor); + +public slots: + virtual void setVisible(bool); + +private slots: + void step(); + +protected: + void paintEvent(QPaintEvent*); + +private: + int blocks, shown; + int idx; + Direction dir; + bool dir2; + + QString text; + + QTimer timer; + QColor col_frame, + col_bg, + col_text, + col_rect; +}; + +#endif diff --git a/gui/include/qpxgraph.h b/gui/include/qpxgraph.h new file mode 100644 index 0000000..b0b42e7 --- /dev/null +++ b/gui/include/qpxgraph.h @@ -0,0 +1,110 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 QPX_GRAPH_H +#define QPX_GRAPH_H + +#include +#include +#include + +#include + +//#define CACHE_GRAPH +//#define ENABLE_UNCR + +#define NOFORCE_REPAINT 0 +#define FORCE_REPAINT 1 + +class QPxGraph : public QWidget +{ + Q_OBJECT +public: + QPxGraph(QPxSettings *iset, devlist *idev, QString iname, int ttype, QWidget *p=0, Qt::WindowFlags fl = 0); + ~QPxGraph(); +// void setDataNames(QStringList dn); +// void setZeroPos(float); +// float zeroPos(); + void drawGraph(QPainter*, QSize, device*, int ttype, const QRect& rect, int eflags=0, bool forceRepaint=0); + void setErrcList(int, QString glabel); + void setErrcShow(int, bool); + void setShowSpeed(bool); + void setRightMarginHidden(bool); + int getLastX(); + +public slots: + void setZoneTA(int zone); + void setLayerTA(int layer); + void setModeTA(bool mode) { taMode = mode; }; + + void changeScale(int idx=0); + +protected: + void contextMenuEvent(QContextMenuEvent*); + void paintEvent(QPaintEvent*); + void resizeEvent(QResizeEvent*); + void wheelEvent(QWheelEvent*); + + void drawTransfer(QPainter*, const QSize&, device*, const QRect& rect, bool rw); + void drawErrc(QPainter*, const QSize&, device*, const QRect& rect); + void drawJB(QPainter*, const QSize&, device*, const QRect& rect); + void drawFT(QPainter*, const QSize&, device*, const QRect& rect); + void drawTA(QPainter*, const QSize&, device*, const QRect& rect); + void drawGrid(QPainter*, const QSize&, device*, int ttype); + void drawGridTA(QPainter*, const QSize&, device*, int ttype); + +private slots: + void setScalePolicyAuto(); + void setScalePolicyFixed(); + void setScaleTypeLog(); + void setScaleTypeLin(); + void setScaleValue( int val, int idx=-1); + void scaleIn(int idx=0); + void scaleOut(int idx=0); + +signals: + void scaleChanged(); + +private: + bool taMode; // 0 - Pit, 1 - Land + int taZone; + int taLayer; + int errc2h(int h, int val); + int jitter2h(int h, float val); + int asymm2h(int h, float val); + int ta2h(int h, int val); + double Hscale; + double HscaleLBA; + float Vscale; + float Vscale1X; + int test; + bool showspeed; + devlist *devices; + QPxSettings *settings; + QString label; + QString name[2]; + QStringList dataNames; + Scale *scale[2]; +// float zeropos; + uint8_t errcList; + bool forceAll; + uint64_t lastX; +#ifdef CACHE_GRAPH + QImage *img; +#endif + int margin_left, + margin_right, + margin_bottom; +}; + +#endif + diff --git a/gui/include/qpxiodevice.h b/gui/include/qpxiodevice.h new file mode 100644 index 0000000..feb55ad --- /dev/null +++ b/gui/include/qpxiodevice.h @@ -0,0 +1,44 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Gennady "ShultZ" Kozlov + * + * 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 QPX_IODEVICE_H +#define QPX_IODEVICE_H + +#include +#include +#include + +class QPxIODevice : public QObject +{ + Q_OBJECT +public: + QPxIODevice(QObject*); + ~QPxIODevice(); + void setIODevice(QIODevice*); + QIODevice* IODevice(); + QString readLine(); + int linesAvailable(); + +signals: + void readyReadLine(); + +protected slots: + void splitInput(); + +private: + QIODevice *io; + QString buf; + QStringList lines; +}; + +#endif + diff --git a/gui/include/qpxsettings.h b/gui/include/qpxsettings.h new file mode 100644 index 0000000..f71d653 --- /dev/null +++ b/gui/include/qpxsettings.h @@ -0,0 +1,211 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 _QPXSETTINGS_H +#define _QPXSETTINGS_H + +#include +#include +#include +#include + +#include + +#include + +struct colorSet { + QString name; + QColor bg,grid,rspeed,wspeed, + errc[8], + jitter,asymm, + fe,te, + tapit,taland; +}; + +static const colorSet defColors = { + "Default", + 0xFFFFFF, + 0x808080, + 0x009000, + 0xC00000, + { + 0x009000, + 0x00C000, + 0x00FF00, + 0xFFAA00, + 0x0055FF, + 0x550080, + 0xFF0000, + Qt::black + }, + 0x00A000, + 0xA00000, + 0xA00000, + 0x0000A0, + 0xA00000, + 0x0000A0 +}; + +class Scale { +public: + Scale(QString iname); + ~Scale(); + + enum ScalePolicy { + Auto = 1, + Fixed = 2 + }; + + enum ScaleType { + Linear = 4, + Log = 8 + }; + + QString name; +// ScaleType type; +// ScaleMode mode; + int policy; + int type; + int value; + + inline bool operator== (const QString &s) { return name == s; }; + inline bool operator== (const Scale &o) { return name == o.name; }; +}; + +class ScaleList : public QList { + public: + inline Scale& get(const QString s) { + int idx = indexOf(s); + if (idx>=0) + return (*this)[idx]; + append(Scale(s)); + return (*this)[indexOf(s)]; + }; +}; + +struct ErrcColorCD{ + QColor *bler; + QColor *e11; + QColor *e21; + QColor *e31; + QColor *e12; + QColor *e22; + QColor *e32; + QColor *uncr; +}; + +struct ErrcColorDVD{ + QColor *res0; + QColor *pie; + QColor *pi8; + QColor *pif; + QColor *poe; + QColor *po8; + QColor *pof; + QColor *uncr; +}; + +struct ErrcColorBD{ + QColor *res0; + QColor *ldc; + QColor *res1; + QColor *res2; + QColor *bis; + QColor *res3; + QColor *res4; + QColor *uncr; +}; + +union ErrcColor{ + QColor *raw[8]; + ErrcColorCD cd; + ErrcColorDVD dvd; + ErrcColorBD bd; +}; + +#define AFLAG_MWATCH 0x0001 +#define AFLAG_AUTOSTART_W 0x0002 +#define AFLAG_AUTOSTART_B 0x0004 +#define AFLAG_EJECT_AFTER 0x0008 // + +#define AFLAG_DTEST_RT 0x0010 +#define AFLAG_DTEST_WT 0x0020 +#define AFLAG_DTEST_WT_SIMUL 0x0040 +#define AFLAG_DTEST_ERRC 0x0080 +#define AFLAG_DTEST_JB 0x0100 +#define AFLAG_DTEST_FT_W 0x0200 +#define AFLAG_DTEST_FT_B 0x0400 +#define AFLAG_DTEST_TA 0x0800 + +class QPxSettings { +public: + QPxSettings(); + QPxSettings(const QPxSettings&); + ~QPxSettings(); + void load(); + void save(); + void loadScale(QString name); + void saveScale(); + + void setDefaultColors(); + QPxSettings& operator = (const QPxSettings& o); + +// geometry + QRect geometry_mw; + QRect geometry_pref; +// QRect geometry_testsel; + int tests; + +// general options + bool show_sidebar; + bool show_allctl; + bool report_autosave; + QString report_path; + uint32_t actions_flags; + + bool use_reports_db; + bool report_autosave_db; + DBParams db; + +// device settings + bool useLocal; + bool useRemote; + QStringList hosts; + QStringList ports; + +// graph settings + QColor col_bg; + QColor col_bginv; + QColor col_grid; + QColor col_rspeed; + QColor col_wspeed; + + ErrcColor col_errc; + + QColor col_jitter; + QColor col_asymm; + + QColor col_fe; + QColor col_te; + + QColor col_tapit; + QColor col_taland; + + ScaleList scales; + +// paths + QString last_res_path_native, + last_res_path_html, + last_res_path_pdf; +}; + +#endif + diff --git a/gui/include/resultsio.h b/gui/include/resultsio.h new file mode 100644 index 0000000..e4af66e --- /dev/null +++ b/gui/include/resultsio.h @@ -0,0 +1,61 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2010 Gennady "ShultZ" Kozlov + * + * 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 _RESULTSIO_H +#define _RESULTSIO_H + +#include + +#include + +#define RECALC_ON_LOAD 1 + +class ResultsIO : public QThread +{ +public: + ResultsIO(device *dev) :QThread((QObject*)dev) { this->dev = dev; this->res = false; io = NULL; } + ~ResultsIO() {}; + + inline void setIO(QIODevice *io) { this->io = io; }; + inline bool result() { return res; }; + +protected: + QIODevice *io; + device *dev; + bool res; +}; + + +class ResultsReader : public ResultsIO +{ + Q_OBJECT +public: + ResultsReader(device *dev) : ResultsIO(dev) {}; + ~ResultsReader() {}; + +protected: + virtual void run(); +}; + +class ResultsWriter : public ResultsIO +{ + Q_OBJECT +public: + ResultsWriter(device *dev) : ResultsIO(dev) {}; + ~ResultsWriter() {}; + +protected: + virtual void run(); +}; + +#endif // _RESULTSIO_H + diff --git a/gui/include/splitbutton.h b/gui/include/splitbutton.h new file mode 100644 index 0000000..cffa3c3 --- /dev/null +++ b/gui/include/splitbutton.h @@ -0,0 +1,42 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 SPLITBUTTON_H +#define SPLITBUTTON_H + +#include + +class SplitButton : public QWidget +{ + Q_OBJECT + +public: + SplitButton(Qt::Orientation o, QWidget *p=0, Qt::WindowFlags f=0); + ~SplitButton(); + +protected: + void paintEvent(QPaintEvent*); + void mousePressEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); + void enterEvent(QEvent*); + void leaveEvent(QEvent*); + +private: + Qt::Orientation orient; + bool mouseFocus; + +signals: + void clicked(); +}; + +#endif + diff --git a/gui/include/tab_devinfo.h b/gui/include/tab_devinfo.h new file mode 100644 index 0000000..f68be36 --- /dev/null +++ b/gui/include/tab_devinfo.h @@ -0,0 +1,109 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 _TAB_DEVINFO_H +#define _TAB_DEVINFO_H + +#include +#include +#include + + +class QBoxLayout; +class QGridLayout; + +class QFrame; +class QLabel; +class QComboBox; + +#ifdef DINFO_TREE +class QTreeWidget; +class QTreeWidgetItem; +#else + +#define CAP_COLS 4 + +class MediaCapWidget; +#endif + +class device; + +class tabDevInfo : public QWidget { + Q_OBJECT +public: + tabDevInfo(QPxSettings *iset, devlist *idev, QWidget *p=0, Qt::WindowFlags fl = 0); + ~tabDevInfo(); + +public slots: + void clear(); + void selectDevice(); + void updateData(int xcode=0); + +private: + devlist *devices; + QPxSettings *settings; + + QBoxLayout *layout; + QGridLayout *layout_left; + +// basic info + QLabel *label_left; + + QLabel *pl_vendor, + *l_vendor, + *pl_model, + *l_model, + *pl_fw, + *l_fw, + *pl_tla, + *l_tla, + *pl_buf, + *l_buf, + *pl_sn, + *l_sn; + + QLabel *pl_iface, + *l_iface, + *pl_loader, + *l_loader; + +// DVD RPC info + QLabel *pl_rpc_phase, + *l_rpc_phase, + *pl_rpc_reg, + *l_rpc_reg, + *pl_rpc_ch, + *l_rpc_ch, + *pl_rpc_rst, + *l_rpc_rst; +// Plextor Life + QFrame *hline0; + QLabel *pl_life_dn, + *l_life_dn, + *pl_life_cr, + *l_life_cr, + *pl_life_cw, + *l_life_cw, + *pl_life_dr, + *l_life_dr, + *pl_life_dw, + *l_life_dw; + + QGridLayout *cap_grid; + QLabel *lc_media, + *lc_generic; + QList cap_media, + cap_generic; +}; + +#endif + diff --git a/gui/include/tab_errc.h b/gui/include/tab_errc.h new file mode 100644 index 0000000..7d9fb4d --- /dev/null +++ b/gui/include/tab_errc.h @@ -0,0 +1,77 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 _TAB_ERRC_H +#define _TAB_ERRC_H + +class QLabel; +class ColorLabel; +class QPushButton; +class QBoxLayout; +class ErrcDetailedDialog; + +#include +#include + + +class tabERRC : public GraphTab +{ + Q_OBJECT +public: + tabERRC(QPxSettings *iset, devlist *idev, QString iname, QWidget *p=0, Qt::WindowFlags fl = 0); + ~tabERRC(); + +public slots: +// void clear(); + void selectDevice(); + void updateAll(); + void updateLast(); +// void setDevice(device *idev); +// void update() { graph->update(); }; + + void XerrcClosed(); + +private slots: + void updateSummary(device*); + void toggleXErrc(); + +protected: + virtual void updateLegend(); + virtual void updateGraph(); + +private: + QBoxLayout *layout_info; + +#ifdef __LEGEND_SHOW_SPEED + ColorLabel *pl_spd; +#endif + ColorLabel *pl_e0; + QLabel *l_e0t; + QLabel *l_e0m; + QLabel *l_e0a; + + ColorLabel *pl_e1; + QLabel *l_e1t; + QLabel *l_e1m; + QLabel *l_e1a; + + ColorLabel *pl_e2; + QLabel *l_e2t; + QLabel *l_e2m; + QLabel *l_e2a; + + QPushButton *pb_xerrc; + ErrcDetailedDialog *xerrc; +}; + +#endif + diff --git a/gui/include/tab_fete.h b/gui/include/tab_fete.h new file mode 100644 index 0000000..4937b82 --- /dev/null +++ b/gui/include/tab_fete.h @@ -0,0 +1,55 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 _TAB_FETE_H +#define _TAB_FETE_H + +class ColorLabel; +class QLabel; +class QBoxLayout; + +#include + +class tabFETE : public GraphTab +{ + Q_OBJECT +public: + tabFETE(QPxSettings *iset, devlist *idev, QString iname, QWidget *p=0, Qt::WindowFlags fl = 0); + ~tabFETE(); + +public slots: +// void clear(); + void selectDevice(); + void updateLast(); + void updateAll(); + void updateSummary(); +// void setDevice(device *idev); +// void update() { graph->update(); }; + +protected: + virtual void updateLegend(); + virtual void updateGraph(); + +private: + QBoxLayout *layout_info; + +#ifdef __LEGEND_SHOW_SPEED + ColorLabel *pl_spd; +#endif + ColorLabel *pl_fmax; + QLabel *l_fmax; + ColorLabel *pl_tmax; + QLabel *l_tmax; +}; + +#endif + diff --git a/gui/include/tab_jb.h b/gui/include/tab_jb.h new file mode 100644 index 0000000..47c8c79 --- /dev/null +++ b/gui/include/tab_jb.h @@ -0,0 +1,57 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 _TAB_JB_H +#define _TAB_JB_H + +class ColorLabel; +class QLabel; +class QBoxLayout; + +#include + +class tabJB : public GraphTab +{ + Q_OBJECT +public: + tabJB(QPxSettings *iset, devlist *idev, QString iname, QWidget *p=0, Qt::WindowFlags fl = 0); + ~tabJB(); + +public slots: +// void clear(); + void selectDevice(); + void updateLast(); + void updateAll(); + void updateSummary(); +// void setDevice(device *idev); +// void update() { graph->update(); }; + +protected: + virtual void updateLegend(); + virtual void updateGraph(); + +private: + QBoxLayout *layout_info; + +#ifdef __LEGEND_SHOW_SPEED + ColorLabel *pl_spd; +#endif + ColorLabel *pl_jitter; + QLabel *l_jmax; + QLabel *l_jmin; + ColorLabel *pl_asymm; + QLabel *l_amax; + QLabel *l_amin; +}; + +#endif + diff --git a/gui/include/tab_mediainfo.h b/gui/include/tab_mediainfo.h new file mode 100644 index 0000000..7627326 --- /dev/null +++ b/gui/include/tab_mediainfo.h @@ -0,0 +1,128 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 _TAB_MEDIAINFO_H +#define _TAB_MEDIAINFO_H + +#include +#include +#include + +class QBoxLayout; +class QGridLayout; + + +class QLabel; +class QFrame; +class QPushButton; +class QCheckBox; + +#ifdef MINFO_TREE +class QTreeWidget; +class QTreeWidgetItem; +#endif + +class tabMediaInfo : public QWidget +{ + Q_OBJECT +public: + tabMediaInfo(QPxSettings *iset, devlist *idev, QWidget *p=0, Qt::WindowFlags fl = 0); + ~tabMediaInfo(); + +public slots: + void clear(); + void selectDevice(); + void updateData(int xcode=0); + +private: + devlist *devices; + QPxSettings *settings; + + QBoxLayout *layout; + + QGridLayout *layout_info; + QLabel *pl_type; + QLabel *l_type; + QLabel *pl_book; + QLabel *l_book; + QLabel *pl_mid; + QLabel *l_mid; + QLabel *pl_layers; + QLabel *l_layers; + QLabel *pl_erasable; + QLabel *l_erasable; + QLabel *pl_prot; + QLabel *l_prot; + QLabel *pl_regions; + QLabel *l_regions; + QLabel *pl_writer; + QLabel *l_writer; + + QGridLayout *l_cap; + QLabel *pl_cread; + QLabel *l_creads; + QLabel *l_creadm; + QLabel *l_creadmsf; + QLabel *pl_cfree; + QLabel *l_cfrees; + QLabel *l_cfreem; + QLabel *l_cfreemsf; + QLabel *pl_ctot; + QLabel *l_ctots; + QLabel *l_ctotm; + QLabel *l_ctotmsf; + + QLabel *pl_dstate; + QLabel *l_dstate; + QLabel *pl_sstate; + QLabel *l_sstate; + QLabel *pl_rspeeds; + QLabel *l_rspeeds; + QLabel *pl_wspeedsm; + QLabel *l_wspeedsm; + QLabel *pl_wspeedsd; + QLabel *l_wspeedsd; + + + QFrame *hline0; + + +// media speeds info +#ifdef SHOW_SPEEDS + QLabel *pl_rd_max; + QLabel *l_rd_max; + QLabel *pl_rd_lst; + QComboBox *c_rd_lst; + QLabel *pl_wr_max; + QLabel *l_wr_max; + QLabel *pl_wr_lst; + QComboBox *c_wr_lst; +#endif + + +#if 0 + QCheckBox *ch_rt; + QCheckBox *ch_wt; + QCheckBox *ch_errc; + QCheckBox *ch_jb; + QCheckBox *ch_fete; + QCheckBox *ch_ta; +#endif + // device *dev; +#ifdef MINFO_TREE + QTreeWidget *minfo; + QTreeWidgetItem *minfo_detail; +#endif +}; + +#endif + diff --git a/gui/include/tab_ta.h b/gui/include/tab_ta.h new file mode 100644 index 0000000..0fa1cca --- /dev/null +++ b/gui/include/tab_ta.h @@ -0,0 +1,88 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 _TAB_TA_H +#define _TAB_TA_H + +class QLabel; +class QBoxLayout; +class QGroupBox; +class QButtonGroup; +class QRadioButton; + +class QPxGraph; + +class device; +class devlist; +class QPxSettings; +class ColorLabel; + +//#define __LEGEND_SHOW_TA + + +#include +#include + +class tabTA : public QWidget +{ + Q_OBJECT +public: + tabTA(QPxSettings *iset, devlist *idev, QString iname, QWidget *p=0, Qt::WindowFlags fl = 0); + ~tabTA(); + void drawGraph(QImage& img, device *dev, int ttype, int eflags=0); + +public slots: + void clear(); + void selectDevice(); +// void infoToggle(); + void updateAll(); + void updateLast(); + void reconfig(); + +private: + devlist *devices; + QPxSettings *settings; + QString name; + timeval prevT; + + QBoxLayout *layout; +// Selectors/time + QBoxLayout *layoutl; +#ifdef __LEGEND_SHOW_TA + ColorLabel *cl_pit, + *cl_land; +#endif + QGroupBox *box_layer; + QBoxLayout *lay_layer; + QButtonGroup *grp_layer; + QRadioButton *layer0; + QRadioButton *layer1; + + QGroupBox *box_zone; + QBoxLayout *lay_zone; + QButtonGroup *grp_zone; + QRadioButton *zone0; + QRadioButton *zone1; + QRadioButton *zone2; + + QGroupBox *grp_time; + QBoxLayout *layoutt; + QLabel *ltime; + +// Pit / Land TA graphs + QBoxLayout *layoutg; + QPxGraph *graphPit; + QPxGraph *graphLand; +}; + +#endif + diff --git a/gui/include/tab_transfer.h b/gui/include/tab_transfer.h new file mode 100644 index 0000000..5956705 --- /dev/null +++ b/gui/include/tab_transfer.h @@ -0,0 +1,62 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 _TAB_TRANSFER_H +#define _TAB_TRANSFER_H + +class QBoxLayout; +class QLabel; +class ColorLabel; + +#include +#include + +//#define __LEGEND_SHOW_SPEED + +class tabTransfer : public GraphTab +{ + Q_OBJECT +public: + tabTransfer(QPxSettings *iset, devlist *idev, QString iname, bool irw, + QWidget *p=0, Qt::WindowFlags fl = 0); + ~tabTransfer(); + +public slots: +// void clear(); + void selectDevice(); + void updateLast(); + void updateSummary(device*, float); + +protected: + virtual void updateLegend(); + virtual void updateGraph(); + +private: + bool rw; + QBoxLayout *layout_info; + +#ifdef __LEGEND_SHOW_SPEED + ColorLabel *pl_spd; +#endif + QLabel *pl_sta; + QLabel *l_sta_x; + QLabel *l_sta_kb; + QLabel *pl_end; + QLabel *l_end_x; + QLabel *l_end_kb; + QLabel *pl_avg; + QLabel *l_avg_x; + QLabel *l_avg_kb; +}; + +#endif + diff --git a/gui/include/tattoowidget.h b/gui/include/tattoowidget.h new file mode 100644 index 0000000..f015e40 --- /dev/null +++ b/gui/include/tattoowidget.h @@ -0,0 +1,46 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Gennady "ShultZ" Kozlov + * + * 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 TATTOOWIDGET_H +#define TATTOOWIDGET_H + +#define F1TATTOOW 3744 +#define F1TATTOOR 582 + +#include + +class TattooWidget : public QWidget +{ + Q_OBJECT +public: + TattooWidget(QWidget *p); + ~TattooWidget(); + void setRadius(int,int); + void setImage(QImage&); + +private: + void convertImage(); + int r0mm,r1mm; + int r0p, r1p; + QImage timg, dimg; + + int x0,y0; + +protected: + void paintEvent(QPaintEvent*); + void resizeEvent(QResizeEvent*); + float SRC_x2ang(int, int w=F1TATTOOW); + float SRC_y2rad(int, int); +}; + +#endif // TATTOOWIDGET_H + diff --git a/gui/include/testdialog.h b/gui/include/testdialog.h new file mode 100644 index 0000000..6f64392 --- /dev/null +++ b/gui/include/testdialog.h @@ -0,0 +1,105 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 TEST_DIALOG_H +#define TEST_DIALOG_H + +#include +#include +#include + +class QBoxLayout; +class QGridLayout; +class QGroupBox; + +class QFrame; +class QLabel; +class QLineEdit; +class QCheckBox; +class QComboBox; +class QTextBrowser; +class QPushButton; + +class TestDialog : public QDialog +{ + Q_OBJECT + +public: + TestDialog(QPxSettings *iset, device *idev, QWidget *p, Qt::WindowFlags f=0); + ~TestDialog(); + device* getDevice(); + +public slots: + void mediaChanged(); + +private slots: + void start(); + void updateData(bool save=1, bool setPlugin=1); + void WTchecked(bool); + void pluginChanged(int); + void checkSimul(); + +private: + void winit(); + void saveData(); + bool noSimul; + + QGridLayout *layout; + + QLabel *ldev, + *devid, + *lmedia, + *media, + *llabel; + QLineEdit *elabel; + + QGroupBox *grp_tests; + QGridLayout *layout_tests; + QLabel *l_tests; + QLabel *l_speeds; + + QCheckBox *ck_RT; + QComboBox *spd_RT; + QCheckBox *ck_WT; + QComboBox *spd_WT; + QCheckBox *ck_WT_simul; + QFrame *hline0; + QCheckBox *ck_ERRC; + QComboBox *spd_ERRC; + QCheckBox *ck_JB; + QComboBox *spd_JB; + QCheckBox *ck_FT; + QComboBox *spd_FT; + QCheckBox *ck_TA; +// QComboBox *spd_TA; + + QFrame *hline1; + QLabel *l_plugin; + QComboBox *cb_plugin; + QLabel *l_plugin_info; +/* + QGroupBox *grp_media; + QBoxLayout *layout_media; + QTextBrowser *media; +*/ + + QBoxLayout *layout_butt; + QPushButton *butt_run; + QPushButton *butt_cancel; + +// + device *dev; + QPxSettings *settings; +}; + +#endif // TEST_DIALOG_H + diff --git a/gui/include/textslider.h b/gui/include/textslider.h new file mode 100644 index 0000000..177cf99 --- /dev/null +++ b/gui/include/textslider.h @@ -0,0 +1,81 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Gennady "ShultZ" Kozlov + * + * 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 _TEXT_SLIDER_H +#define _TEXT_SLIDER_H + +#include +#include + +class SliderItem { +public: + SliderItem(QString s, bool en = true) { text=s; enabled=en; } + + inline bool operator==(SliderItem o) { return text==o.text; }; + inline bool operator==(SliderItem& o) { return text==o.text; }; + inline bool operator==(QString o) { return text==o; }; + inline bool operator==(QString& o) { return text==o; }; + + QString text; + bool enabled; +}; + + +class TextSlider : public QAbstractSlider +{ + Q_OBJECT +public: + TextSlider(QWidget *p = NULL); + TextSlider(Qt::Orientation, QWidget *p = NULL); + void addItem(QString, bool en = true); + void setItemEnabled(int, bool); + void setItemEnabled(QString, bool); + inline void removeItem(QString s) { removeItem(items.indexOf(s)); }; + void removeItem(int); + void setTickPosition(QSlider::TickPosition); + void setUpsideDown(bool); + void setOrientation(Qt::Orientation); + void first(); + void last(); + bool prev(); + bool next(); + int value(); + QString text(); + +public slots: + void setValue(int); + void setCurrentItem(const QString&); + void setEnabled(bool); + void setDisabled(bool); + +protected: + void mousePressEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent*); + void wheelEvent(QWheelEvent*); + void keyPressEvent(QKeyEvent*); + void focusInEvent(QFocusEvent*); + void focusOutEvent(QFocusEvent*); + void paintEvent(QPaintEvent*); + +private: + void initDefaults(); + void moveToEnabled(); + int oldpos; + bool en; + + QStyleOptionSlider op; + QList items; +}; + +#endif + diff --git a/gui/include/version.h b/gui/include/version.h new file mode 100644 index 0000000..b1ee4ba --- /dev/null +++ b/gui/include/version.h @@ -0,0 +1,19 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 _QPX_VERSION_H +#define _QPX_VERSION_H + +#define VERSION "0.7.2" + +#endif + diff --git a/gui/locale/qpxtool.de_DE.ts b/gui/locale/qpxtool.de_DE.ts new file mode 100644 index 0000000..42ebbfa --- /dev/null +++ b/gui/locale/qpxtool.de_DE.ts @@ -0,0 +1,1863 @@ + + + + + AboutDialog + + + + About QPxTool + QPxTool Info + + + + Licence + Lizenz + + + + DbReportSelection + + + Select report... + + + + + Label: + + + + + Load + Laden + + + + Cancel + Abbrechen + + + + + copy + + + + + Device + Gerät + + + + Date + + + + + ErrcDetailedDialog + + + Detailed Error Correction + Detailierte Fehlerkorrektur + + + + GraphTab + + + Time + Zeit + + + + PrintPreview + + + Print Preview + Druckvorschau + + + + Print + Drucken + + + + Page Setup + Seiteneinstellungen + + + + Scale + Skala + + + + 25% + 25% + + + + 50% + 50% + + + + 100% + 100% + + + + 200% + 200% + + + + Zoom In + Vergrößern + + + + Zoom Out + Verkleinern + + + + Original size + Original Größe + + + + QPxGraph + + + Logarithmic scale + Logarythmische Skalierung + + + + Linear Scale + Lineare Skalierung + + + + Scale in + Skala vergößern + + + + Scale out + Skala verkleinern + + + + QPxPreferences + + + + + + + Preferences + Einstellungen + + + + + Devices + Geräte + + + + + Colors + Farben + + + + + Common + Allgemein + + + + + Reports + + + + + Save + Sichern + + + + Cancel + Abbrechen + + + + QPxToolMW + + + Device: + Gerät: + + + + Exit + Beenden + + + + Rescan local bus and network + Angeschlossene Geräte aktualisieren + + + + Update media info + Medieninformationen aktualisieren + + + + Device controls + Gerätesteuerung + + + + Run tests... + Tests beginnen... + + + + Stop tests + Tests beenden + + + + Preferences + Einstellungen + + + + Print test results + Testergebnisse drucken + + + + + Export results to HTML + Ergebnisse exportieren - html + + + + Save results... + Ergebnisse speichern... + + + + Load results... + Ergebnisse laden... + + + + Save results (DB)... + + + + + Load results (DB)... + + + + + Show sidebar + + + + + File + Datei + + + + View + + + + + Device + Gerät + + + + Help + Hilfe + + + + About + Info + + + + + No devices + Keine Geräte + + + + No devices found! +Can't run tests + Keine Geräte gefunden! +Test können nicht ausgeführt werden + + + + + + + + + Device busy + Gerät in Benutzung + + + + + Bus scan in progress + Geräte werden eingelesen + + + + It seems device already performing test +or info updating in progress + Gerät ist bereits in Nutzung +oder eine Aktualisierung läuft + + + + + + + + + + + Error + Fehler + + + + Error performing test! + Fehler bei Testdurchführung! + + + + qScan finished with non-zero exit code + qScan terminierte mit Rückgabewert ungleich 0 + + + + + Can't scan bus: some device(s) busy! + Kein Einlesen der Geräte: Gerät(e) in Benutzung! + + + + Searching devices... + Gerätesuche... + + + + No devices found! + Keine Geräte gefunden! + + + + + + + + + + + Warning + Warnung + + + + Unable to start qscan! +Local devices will not work + Kann qscan nicht starten! +Lokale Geräte sind nicht verfügbar + + + + Unable to connect to qscand at %1:%2 + keine Verbindung zu qScand %1:%2 + + + + Found locked Plextor device! +Quality check and some other features will not work + Kommandoverschlüsseltes Plextor-Gerät! +Qualitätsmessungen und einige anderen Funktionen stehen nicht zur Verfügung. +Firmware und/oder qScan Berechtigungen prüfen + + + + No devices found! +Nothing to configure + + Keine Geräte gefunden! +Konfiguration nicht möglich + + + + Selected device busy + + + + + Retrieving device parameters... + Einlesen der Geräteparameter... + + + + Can't get device features! + Gerätefunktionen nicht erkannt! + + + + Error requesting device settings! + Fehler beim Einlesen der Geräteeinstellungen! + + + + cdvdcontrol finished with non-zero exit code + cdvdcontrol terminierte mit Rückgabewert ungleich 0 + + + + + Unable to create file: + + Konnte Datei nicht erstellen: + + + + + Save results to file... + Ergebnisse in Datei speichern... + + + + + Saving results... + + + + + + Error saving tests data! + Fehler beim Abspeichern der Werte! + + + + + + + Info + Infos + + + + + Tests data saved in %1 sec + Meßwerte in %1 sec gesichert + + + + + Unable to open buffer! + + + + + + Load results from file... + Lade Meßwerte aus Datei... + + + + Unable to open file: + + Konnte Datei nicht öffnen: + + + + + + Loading results... + + + + + + Do tests data found in this file! + Nutze Testwerte aus dieser Datei! + + + + + Tests data loaded in %1 sec + Meßwerte in %1 gelesen + + + + Done + Beendet + + + + All tests finished + Alle Tests beendet + + + + + Export results to PDF + Ergebnisse als PDF exportieren + + + + Can't save report + Konnte Bericht nicht speichern + + + + Direcrory not exists and I can't create it + Verzeichnis existiert nicht und kann es nicht erstellen + + + + + Preparing report... + Bereite Bericht vor... + + + + Can't update media info! Device busy + Mediadaten nicht aktualisiert! Gerät in Benutzung + + + + TestDialog + + + Select tests... + Testauswahl... + + + + Device: + Gerät: + + + + Media: + Medium: + + + + Label: + + + + + Tests: + Теsts: + + + + Speeds: + Tempi: + + + + Read Transfer Rate + Lese-Tranferrate + + + + Write Transfer Rate + Schreib-Transferrate + + + + Simulation + Simulation + + + + Error Correction + Fehlerkorrektur + + + + Jitter/Asymmetry + Jitter/Asymmetry + + + + Focus/Tracking + Focus/Track + + + + Time Analyser + Zeitanalyse + + + + qScan plugin: + qScan Plugin: + + + + Run + Start + + + + Cancel + Abbrechen + + + + Media label is empty! + + + + + You have define media label! + + + + + No tests selected! + Keine Testauswahl! + + + + You have selected no tests! + Es wurden keine Tests gewählt! + + + + < Autodetect > + < Autom. Erkennung > + + + + plugin info error + Plugin-Infofehler + + + + qScan will probe plugin for your drive + qScan prüft Plugins für Gerät + + + + no plugin info + Keine Plugin-Infos + + + + Retrieving test capabilities... + Lese Meßfähigkeiten... + + + + devSettings + + + Device Controls + + + + + Common + + + + + VariRec + + + + + GigaRec + + + + + SecuRec + + + + + Silent mode + + + + + PioQuiet + + + + + Destruction + + + + + Disc T@2 + + + + + devctlAutostrategy + + + AutoStrategy + AutoStrategie + + + + AutoStrategy DataBase + AutoStrategie Datenbank + + + + Media Quality Check + Medium-Qualitätsprüfung + + + + Quick + Schnell + + + + Advanced + Erweitert + + + + Speed: + Tempo: + + + + Check Media + Medium prüfen + + + + Checking media quality... + Prüfe Mediumqualität... + + + + Can't run cdvdcontrol! + Kann cdvdcontrol nicht ausführen! + + + + Unknown result + Unbekanntes Ergebnis + + + + devctlAutostrategyDB + + + AutoStrategy DataBase + AutoStrategie Datenbank + + + + Type + Typ + + + + Media ID + Medium ID + + + + Speed + Tempo + + + + Writes + Schreiben + + + + Activate + Aktivieren + + + + Deactivate + Deaktivieren + + + + Remove + Entfernen + + + + Clear + Bereinigen + + + + Strategy creation + Strategie erstellen + + + + Mode + Modus + + + + DB Action + DB Aktion + + + + Create + Erstellen + + + + Quick + Schnell + + + + Full + Komplett + + + + Add + Hinzufügen + + + + Replace + Ersetzen + + + + Retrieving ASDB... + Lese ASDB... + + + + Error + Fehler + + + + Error requesting ASDB! + Fehler beim Einlesen der ASDB! + + + + cdvdcontrol finished with non-zero exit code + cdvdcontrol terminierte mit Rückgabewert ungleich 0 + + + + + Are you sure? + Sicher? + + + + Are you sure to delete strategy #%1? + Soll die Strategie #%1 wirklich gelöscht werden? + + + + Creating strategy... + Erzeuge Strategie... + + + + Are you sure delete all strategies? + + + + + Can't create strategy! + + + + + Strategy creation does not supported on current media: + + + + + supported media types: DVD+R(DL) and DVD-R(DL) + + + + + devctlDestruct + + + Quick + Schnell + + + + Destruct Lead-in & TOC + Zerstöre Lead-in & TOC + + + + Full + Komplett + + + + Destruct entire disc + Zerstöre eingelegte Disk + + + + Destruct + Zerstören + + + + Are you sure? + Sicher? + + + + Media will be unreadable after this operation! + Medium wird nach diesem Vorgang unlesbar sein! + + + + Data destruction + Datenzerstörung + + + + Destructing data... + Zerstöre Daten... + + + + devctlF1Tattoo + + + Burn T@2 + Schreibe T@2 + + + + Select T@2 image... + Wähle T@2-Bild... + + + + + Error + Fehler + + + + Can't load image: + Bild kann nicht geladen werden: + + + + Can't create DiscT@2 temporary file! + Kann temporäre DiscT@2 Datei nicht anlegen! + + + + Burning DiscT@2 image... + Schreibe DiscT@2 Bild... + + + + devctlGigarec + + + GigaRec limits maximum write speed to 8X +only DAO write mode can be used with GigaRec + GigaRec reduziert die Maximalgeschwindigkeit auf 8x +nur der DAO Schreibmodus wird unterstützt + + + + Original capacity: + Ursprüngliche Kapazität: + + + + + GigaRec capacity: + Kapazität GigaRec: + + + + devctlPioquiet + + + Pioneer Quiet Mode + Pioneer Flüstermodus + + + + Quiet + Leise + + + + Standard + Standard + + + + Performance + Performant + + + + Limit read speed to 24X for CD and 8X for DVD + Limitiere Lesegeschwindigkeit auf 24x für CD und 8x für DVD + + + + Permanent + Permanent + + + + Set + Setzen + + + + Profile + Profil + + + + devctlSecurec + + + Password + Passwort + + + + Confirm + Bestätigen + + + + Set + Anwenden + + + + Reset + Reset + + + + Password length must me between 4 and 10 characters. + +To read SecuRec-protected CD you have to activate SecuRec +with same password as used for writing + Passwortlänge muß zwischen 4 und 10. + +Zeichen liegen um SecuRec-geschütze Disks zu lesen, muß +SecuRec mit selbigem Passwort wieder freigeschaltet werden + + + + devctlSilent + + + Speed Limits + Tempo-Limits + + + + CD + CD + + + + DVD + DVD + + + + Read + Lesen + + + + Write + Schreiben + + + + Access Time + Zugriffszeit + + + + Slow + Langsam + + + + Fast + Schnell + + + + Tray Speed + Tray-Tempo + + + + Load + Laden + + + + Eject + Auswerfen + + + + Permanent + Permanent + + + + Save + Sichern + + + + devctlVarirecCD + + + VariRec limits maximum CD write speed to 8X + VariRec limitiert max. CD Schreibtempo auf 8x + + + + VariRec limits maximum CD write speed to 4X + VariRec limitiert max. CD Schreibtempo auf 4x + + + + devctlVarirecDVD + + + VariRec limits maximum DVD write speed to 4X + VariRec limitiert max. DVD Schreibtempo auf 4x + + + + device + + + Updating media info... + + + + + Read Transfer + + + + + Write Transfer + + + + + Error Correction + Fehlerkorrektur + + + + Jitter/Asymmetry + + + + + Focus/Tracking + Focus/Track + + + + Time Analyser + Zeitanalyse + + + + Loading media... + + + + + hostEditDialog + + + Add host + Host hinzufügen + + + + Host: + Host: + + + + Port: + Port: + + + + prefColors + + + Background + Hintergrund + + + + Grid + Gitter + + + + Read Speed + Lesetempo + + + + Write Speed + Schreibtempo + + + + Jitter + Jitter + + + + Asymmetry + Ðymmetrie + + + + Focus Errors + Focusfehler + + + + Tracking errors + Trackfehler + + + + Presets + Voreinstellungen + + + + Default + Standard + + + + prefCommon + + Path: + Pfad: + + + Select directory... + Verzeichnis wählen... + + + Autosave reports after tests completed + Bericht nach beendetem Test autom. sichern + + + + Simulation + Simulation + + + + Error Correction + Fehlerkorrektur + + + + Jitter/Asymmetry + Jitter/Asymmetrie + + + + + Focus/Tracking + Focus/Track + + + + Time Analyser + Zeitanalyse + + + + Read Transfer Rate + Lese-Transferrate + + + + Write Transfer Rate + Schreib-Transferrate + + + + Autostart tests on written media inserted + Tests nach Einlegen beschriebener Medien autom. ausführen + + + + Autostart tests on blank media inserted + Tests nach Einlegen leerer Medien autom. ausführen + + + Eject media after tests finished + Medien nach beendeter Prüfung autom. auswerfen + + + + Default tests for written media + Standardtests für beschriebene Medien + + + + Default tests for blank media + Standardtests für unbeschriebene Medien + + + + prefDevices + + + Use local devices + Nutze lokale Geräte + + + + Use network devices + Nutze Netzwerkgeräte + + + + host + host + + + + qscand port + qscand Port + + + + + Edit host + Host bearbeiten + + + + Add host + Host hinzufügen + + + + Remove host + Host entfernen + + + + Remove host? + Host entfernen? + + + + You are about to remove host from list: +%1:%2 +Are you sure? + Host wird von Liste entfernt: %1:%2 +Sicher? + + + + prefReports + + + Autosave reports + + + + + Path: + Pfad: + + + + Select directory for reports saving... + + + + + Use reports database + + + + + Driver: + + + + + Host: + Host: + + + + Port: + Port: + + + + DB name: + + + + + User: + + + + + Password: + + + + + Check + + + + + Autosave reports into database + + + + + Eject media after tests finished + Medien nach beendeter Prüfung autom. auswerfen + + + + Select directory... + Verzeichnis wählen... + + + + Success! + + + + + Database connection successfully +established with given parameters + + + + + tabDevInfo + + + Vendor: + + + + + Model: + + + + + F/W: + + + + + TLA# + + + + + S/N: + + + + + Buffer: + + + + + IFace: + + + + + Loader: + + + + + RPC Phase: + + + + + Region: + + + + + Changes: + + + + + Resets: + + + + + Discs loaded: + + + + + CD read: + + + + + CD write: + + + + + DVD read: + + + + + DVD write: + + + + + Media R/W Capabilities + Medium Schreib-/Lesefähigkeiten + + + + Generic Capabilities + Allgemeine Fähigkeiten + + + + Error + Fehler + + + + Error updating device info! + Fehler bei Aktualisierung der Geräteinfos! + + + + qScan finished with non-zero exit code + qScan terminierte mit Rückgabewert ungleich 0 + + + + Basic info + Basis Infos + + + + tabFETE + + + FE max + + + + + TE max + + + + + tabJB + + + Jitter + Jitter + + + + Asymmetry + Ðsymmetrie + + + + tabMediaInfo + + + Type: + Тyp: + + + + Disc Category: + Medium Kategorie: + + + + Media ID: + Medium ID: + + + + Layers: + Layers: + + + + Erasable: + Löschbar: + + + + Protection: + Schutz: + + + + Regions: + Regionen: + + + + Written on: + Geschrieben mit: + + + + Readable: + Lesbar: + + + + Free: + Frei: + + + + Total: + Gesamt: + + + + Disc state: + Medienstatus: + + + + Session state: + Sessionstatus: + + + + Read speeds: + Lesetempi: + + + + Write speeds (Device): + Schreibtempi (Gerät): + + + + Write speeds (Media): + Schreibtempi (Medium): + + + + RD max: + + + + + RD lst: + + + + + WR max: + + + + + WR lst: + + + + + Detailed info + Detailinfos + + + + Error + Fehler + + + + Error updating media info! + Fehler bei Aktualisierung der Mediuminfos! + + + + qScan finished with non-zero exit code + qScan terminierte mit Rückgabewert ungleich 0 + + + + tabTA + + + + + Layer + Layer + + + + Zone + Zonen + + + + Inner + Innen + + + + Middle + Mitte + + + + Outer + Außen + + + + Pit + + + + + Land + + + + + Time + Zeit + + + + tabTransfer + + + Start + Start + + + + End + Ende + + + + Average + Durchschnitt + + + + Write + Schreiben + + + + Read + Lesen + + + diff --git a/gui/locale/qpxtool.ru_RU.ts b/gui/locale/qpxtool.ru_RU.ts new file mode 100644 index 0000000..6b28a0c --- /dev/null +++ b/gui/locale/qpxtool.ru_RU.ts @@ -0,0 +1,1867 @@ + + + + + AboutDialog + + + + About QPxTool + О программе + + + + Licence + Ð›Ð¸Ñ†ÐµÐ½Ð·Ð¸Ñ + + + + DbReportSelection + + + Select report... + Выберите отчёт... + + + + Label: + Метка: + + + + Load + Загрузить + + + + Cancel + Отмена + + + + + copy + ÐºÐ¾Ð¿Ð¸Ñ + + + + Device + УÑтройÑтво + + + + Date + Дата + + + + ErrcDetailedDialog + + + Detailed Error Correction + Ð”ÐµÑ‚Ð°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ†Ð¸Ð¸ ошибок + + + + GraphTab + + + Time + Ð’Ñ€ÐµÐ¼Ñ + + + + PrintPreview + + + Print Preview + ПредпроÑмотр + + + + Print + Печать + + + + Page Setup + Параметры Ñтраницы + + + + Scale + МаÑштаб + + + + 25% + 25% + + + + 50% + 50% + + + + 100% + 100% + + + + 200% + 200% + + + + Zoom In + Увеличить + + + + Zoom Out + Уменьшить + + + + Original size + Оригинальный размер + + + + QPxGraph + + + Logarithmic scale + ЛогарифмичеÑÐºÐ°Ñ ÑˆÐºÐ°Ð»Ð° + + + + Linear Scale + Ð›Ð¸Ð½ÐµÐ¹Ð½Ð°Ñ ÑˆÐºÐ°Ð»Ð° + + + + Scale in + Увеличить маÑштаб + + + + Scale out + Уменьшать маÑштаб + + + + QPxPreferences + + + + + + + Preferences + ÐаÑтройки + + + + + Devices + УÑтройÑтва + + + + + Colors + Цвета + + + + + Common + Общие + + + + + Reports + Отчеты + + + + Save + Сохранить + + + + Cancel + Отмена + + + + QPxToolMW + + + Device: + УÑтройÑтво: + + + + Exit + Выход + + + + Rescan local bus and network + ПереÑканировать шину и Ñетевые уÑтройÑтва + + + + Update media info + Обновить информацию о ноÑителе + + + + Device controls + Управление уÑтройÑтвом + + + + Run tests... + Ðачать теÑтирование... + + + + Stop tests + ОÑтановить теÑтирование + + + + Preferences + ÐаÑтройки + + + + Print test results + РаÑпечатать результаты теÑтов + + + + + Export results to HTML + ЭкÑпорт результатов в HTML + + + + Save results... + Сохранить результаты... + + + + Load results... + Загрузить результаты... + + + + Save results (DB)... + Сохранить результаты (БД)... + + + + Load results (DB)... + Загрузить результаты (БД)... + + + + Show sidebar + Показать боковую панель + + + + File + Файл + + + + View + Вид + + + + Device + УÑтройÑтво + + + + Help + Справка + + + + About + О программе + + + + + No devices + Ðет уÑтройÑтв + + + + No devices found! +Can't run tests + Ðе могу запуÑтить теÑтирование: уÑтройÑтва не найдены + + + + + + + + + Device busy + УÑтройÑтво занÑто + + + + + Bus scan in progress + Сканирование шины + + + + It seems device already performing test +or info updating in progress + УÑтройÑтво занÑто теÑтированием +или обновлением информации + + + + + + + + + + + Error + Ошибка + + + + Error performing test! + Ошибка теÑтированиÑ! + + + + qScan finished with non-zero exit code + qScan завершилÑÑ Ñ Ð½ÐµÐ½ÑƒÐ»ÐµÐ²Ñ‹Ð¼ кодом возврата + + + + + Can't scan bus: some device(s) busy! + Ðе могу Ñканировать шину: некоторые уÑтройÑтва занÑÑ‚Ñ‹! + + + + Searching devices... + ПоиÑк уÑтройÑтв... + + + + No devices found! + УÑтройÑтва не найдены! + + + + + + + + + + + Warning + Предупреждение + + + + Unable to start qscan! +Local devices will not work + Ðе могу запуÑтить qscan! Локальные уÑтройÑтва работать не будут + + + + Unable to connect to qscand at %1:%2 + Ðе могу подключитьÑÑ Ðº qscand на %1:%2 + + + + Found locked Plextor device! +Quality check and some other features will not work + Обнаружен заблокированный Plextor! +ТеÑтирование качеÑтва и некоторые другие возможноÑти работать не будут + + + + No devices found! +Nothing to configure + + УÑтройÑтва не найдены! Ðечего наÑтраивать + + + + Selected device busy + Выбранное уÑтройÑтво занÑто + + + + Retrieving device parameters... + Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð¾Ð² уÑтройÑтва... + + + + Can't get device features! + Ошибка запроÑа возможноÑтей уÑтройÑтва! + + + + Error requesting device settings! + Ошибка запроÑа параметров уÑтройÑтва! + + + + cdvdcontrol finished with non-zero exit code + cdvdcontrol завершилÑÑ Ñ Ð½ÐµÐ½ÑƒÐ»ÐµÐ²Ñ‹Ð¼ кодом возврата + + + + + Unable to create file: + + Ðе могу Ñоздать файл: + + + + + Save results to file... + Сохранить результаты в файл... + + + + + Saving results... + СохранÑÑŽ результаты... + + + + + Error saving tests data! + Ошибка ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ…! + + + + + + + Info + Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ + + + + + Tests data saved in %1 sec + Данные Ñохранены за %1 Ñекунд + + + + + Unable to open buffer! + + + + + + Load results from file... + Загрузить результаты из файла... + + + + Unable to open file: + + Ðе могу открыть файл: + + + + + + Loading results... + Загружаю результаты... + + + + + Do tests data found in this file! + Ð’ Ñтом файле нет данных теÑтированиÑ! + + + + + Tests data loaded in %1 sec + Данные загружены за %1 Ñекунд + + + + Done + Завершено + + + + All tests finished + Ð’Ñе теÑÑ‚Ñ‹ завершены + + + + + Export results to PDF + ЭкÑпорт результатов в PDF + + + + Can't save report + Ðе могу Ñохранить отчет + + + + Direcrory not exists and I can't create it + Каталог не ÑущеÑтвует и Ñ Ð½Ðµ могу его Ñоздать + + + + + Preparing report... + Подготовка отчета... + + + + Can't update media info! Device busy + Ðе могу обновить информацию о ноÑителе: уÑтройÑтво занÑто + + + + TestDialog + + + Select tests... + Выберите теÑÑ‚Ñ‹... + + + + Device: + УÑтройÑтво: + + + + Media: + ÐоÑитель: + + + + Label: + Метка: + + + + Tests: + ТеÑÑ‚Ñ‹: + + + + Speeds: + СкороÑÑ‚ÑŒ: + + + + Read Transfer Rate + СкороÑÑ‚ÑŒ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ + + + + Write Transfer Rate + СкороÑÑ‚ÑŒ запиÑи + + + + Simulation + СимулÑÑ†Ð¸Ñ + + + + Error Correction + ÐšÐ¾Ñ€Ñ€ÐµÐºÑ†Ð¸Ñ Ð¾ÑˆÐ¸Ð±Ð¾Ðº + + + + Jitter/Asymmetry + Джиттер/ÐÑÐ¸Ð¼Ð¼ÐµÑ‚Ñ€Ð¸Ñ + + + + Focus/Tracking + ФокуÑ/Слежение за дорожкой + + + + Time Analyser + Временной анализ + + + + qScan plugin: + плагин qScan: + + + + Run + ЗапуÑк + + + + Cancel + Отмена + + + + Media label is empty! + Метка диÑка не задана! + + + + You have define media label! + Ð’Ñ‹ должны задать метку диÑка! + + + + No tests selected! + ТеÑÑ‚Ñ‹ не выбраны! + + + + You have selected no tests! + Ð’Ñ‹ не выбрали ни один теÑÑ‚! + + + + < Autodetect > + < Ðвтоопределение > + + + + plugin info error + ошибка информации о плагине + + + + qScan will probe plugin for your drive + qScan Ñам подберет плагин Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ уÑтройÑтва + + + + no plugin info + нет информации о плагине + + + + Retrieving test capabilities... + ОпределÑÑŽ возможноÑти теÑтированиÑ... + + + + devSettings + + + Device Controls + + + + + Common + Общие + + + + VariRec + + + + + GigaRec + + + + + SecuRec + + + + + Silent mode + + + + + PioQuiet + + + + + Destruction + + + + + Disc T@2 + + + + + devctlAutostrategy + + + AutoStrategy + AutoStrategy + + + + AutoStrategy DataBase + Таблица AutoStrategy + + + + Media Quality Check + Проверка качеÑтва ноÑÐ¸Ñ‚ÐµÐ»Ñ + + + + Quick + БыÑтрый + + + + Advanced + РаÑширенный + + + + Speed: + СкороÑÑ‚ÑŒ: + + + + Check Media + Проверить + + + + Checking media quality... + ПроверÑÑŽ качеÑтво ноÑителÑ... + + + + Can't run cdvdcontrol! + Ðе мону запуÑтить cdvdcontrol! + + + + Unknown result + Резутьтат неизвеÑтен + + + + devctlAutostrategyDB + + + AutoStrategy DataBase + Таблица AutoStrategy + + + + Type + Тип + + + + Media ID + Идентификатор + + + + Speed + СкороÑÑ‚ÑŒ + + + + Writes + ЗапиÑей + + + + Activate + Ðктивировать + + + + Deactivate + Деактивировать + + + + Remove + Удалить + + + + Clear + ОчиÑтить + + + + Strategy creation + Создание Ñтратегии + + + + Mode + Режим + + + + DB Action + ДейÑтвие + + + + Create + Создать + + + + Quick + БыÑтрый + + + + Full + РаÑширенный + + + + Add + Добавить + + + + Replace + Заменить + + + + Retrieving ASDB... + Ð—Ð°Ð¿Ñ€Ð¾Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ Ñтратегий... + + + + Error + Ошибка + + + + Error requesting ASDB! + Ошибка Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ Ñтратегий! + + + + cdvdcontrol finished with non-zero exit code + cdvdcontrol завершилÑÑ Ñ Ð½ÐµÐ½ÑƒÐ»ÐµÐ²Ñ‹Ð¼ кодом возврата + + + + + Are you sure? + Ð’Ñ‹ уверены? + + + + Are you sure to delete strategy #%1? + Ð’Ñ‹ уверены, что хотите удалить Ñтратегию #%1? + + + + Creating strategy... + Создание Ñтратегии... + + + + Are you sure delete all strategies? + Ð’Ñ‹ уверены, что хотите удалить вÑе Ñтратегии? + + + + Can't create strategy! + Ðе могу Ñоздать Ñтратегию! + + + + Strategy creation does not supported on current media: + Создание Ñтратегии Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾ ноÑÐ¸Ñ‚ÐµÐ»Ñ Ð½Ðµ поддерживаетÑÑ: + + + + supported media types: DVD+R(DL) and DVD-R(DL) + поддерживаемые типы ноÑителей: DVD+R(DL) и DVD-R(DL) + + + + devctlDestruct + + + Quick + БыÑтрое + + + + Destruct Lead-in & TOC + Разрушить Lead-in и TOC + + + + Full + Полное + + + + Destruct entire disc + Разрушить данные по вÑему диÑку + + + + Destruct + Уничтожить + + + + Are you sure? + Ð’Ñ‹ уверены? + + + + Media will be unreadable after this operation! + ПоÑле Ñтой операции ноÑитель Ñтанет нечитаемым! + + + + Data destruction + Уничтожение данных + + + + Destructing data... + Уничтожение данных... + + + + devctlF1Tattoo + + + Burn T@2 + ЗапиÑать T@2 + + + + Select T@2 image... + Выберите изображение Ð´Ð»Ñ T@2... + + + + + Error + Ошибка + + + + Can't load image: + Ðе могу загрузить изображение: + + + + Can't create DiscT@2 temporary file! + Ðе могу Ñоздать временный файл DiscT@2! + + + + Burning DiscT@2 image... + Пишу изображение DiscT@2... + + + + devctlGigarec + + + GigaRec limits maximum write speed to 8X +only DAO write mode can be used with GigaRec + При иÑпользовании GigaRec ÑкороÑÑ‚ÑŒ запиÑи +будет ограничена 8X.Режим запиÑи - только DAO + + + + Original capacity: + ÐžÑ€Ð¸Ð³Ð¸Ð½Ð°Ð»ÑŒÐ½Ð°Ñ ÐµÐ¼ÐºÐ¾ÑÑ‚ÑŒ: + + + + + GigaRec capacity: + ЕмкоÑÑ‚ÑŒ Ñ GigaRec: + + + + devctlPioquiet + + + Pioneer Quiet Mode + Pioneer Quiet Mode + + + + Quiet + Тихий + + + + Standard + Стандартный + + + + Performance + ПроизводитальноÑÑ‚ÑŒ + + + + Limit read speed to 24X for CD and 8X for DVD + Ограничить ÑкороÑÑ‚ÑŒ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð½Ð° 24X Ð´Ð»Ñ CD и 8X Ð´Ð»Ñ DVD + + + + Permanent + Сохранить в знергонезавиÑимой памÑти + + + + Set + УÑтановить + + + + Profile + Профиль + + + + devctlSecurec + + + Password + Пароль + + + + Confirm + Подтверждение + + + + Set + УÑтановить + + + + Reset + СброÑить + + + + Password length must me between 4 and 10 characters. + +To read SecuRec-protected CD you have to activate SecuRec +with same password as used for writing + Пароль должен Ñодержать от 4 до 10 Ñимволов + +Ð”Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ CD, запиÑанного Ñ Ð¸Ñпользованием SecuRec, +надо включить SecuRec, задав тот же пароль + + + + devctlSilent + + + Speed Limits + Ограничение ÑкороÑти + + + + CD + CD + + + + DVD + DVD + + + + Read + Чтение + + + + Write + ЗапиÑÑŒ + + + + Access Time + СкороÑÑ‚ÑŒ доÑтупа + + + + Slow + Медленно + + + + Fast + БыÑтро + + + + Tray Speed + СкороÑÑ‚ÑŒ лотка + + + + Load + Загрузка + + + + Eject + Открытие + + + + Permanent + Сохранить в знергонезавиÑимой памÑти + + + + Save + Сохранить + + + + devctlVarirecCD + + + VariRec limits maximum CD write speed to 8X + При иÑпользовании VariRec ÑкороÑÑ‚ÑŒ запиÑи CD +будет ограничена 8X + + + + VariRec limits maximum CD write speed to 4X + При иÑпользовании VariRec ÑкороÑÑ‚ÑŒ запиÑи CD +будет ограничена 4X + + + + devctlVarirecDVD + + + VariRec limits maximum DVD write speed to 4X + При иÑпользовании VariRec ÑкороÑÑ‚ÑŒ запиÑи DVD +будет ограничена 4X + + + + device + + + Updating media info... + Обновление информации о ноÑителе... + + + + Read Transfer + СкороÑÑ‚ÑŒ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ + + + + Write Transfer + СкороÑÑ‚ÑŒ запиÑи + + + + Error Correction + ÐšÐ¾Ñ€Ñ€ÐµÐºÑ†Ð¸Ñ Ð¾ÑˆÐ¸Ð±Ð¾Ðº + + + + Jitter/Asymmetry + Джиттер/ÐÑÐ¸Ð¼Ð¼ÐµÑ‚Ñ€Ð¸Ñ + + + + Focus/Tracking + ФокуÑ/Слежение за дорожкой + + + + Time Analyser + Временной анализ + + + + Loading media... + Загрузка ноÑителÑ... + + + + hostEditDialog + + + Add host + Добавить хоÑÑ‚ + + + + Host: + ХоÑÑ‚: + + + + Port: + Порт: + + + + prefColors + + + Background + Фон + + + + Grid + Сетка + + + + Read Speed + СкороÑÑ‚ÑŒ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ + + + + Write Speed + СкороÑÑ‚ÑŒ запиÑи + + + + Jitter + Джиттер + + + + Asymmetry + ÐÑÐ¸Ð¼Ð¼ÐµÑ‚Ñ€Ð¸Ñ + + + + Focus Errors + ФокуÑировка + + + + Tracking errors + Слежение за дорожкой + + + + Presets + ПредуÑтановки + + + + Default + По умолчанию + + + + prefCommon + + Path: + Путь: + + + Select directory... + Выберите каталог... + + + Autosave reports after tests completed + СохранÑÑ‚ÑŒ отчет поÑле Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ñ‚ÐµÑтов + + + + Simulation + СимулÑÑ†Ð¸Ñ + + + + Error Correction + ÐšÐ¾Ñ€Ñ€ÐµÐºÑ†Ð¸Ñ Ð¾ÑˆÐ¸Ð±Ð¾Ðº + + + + Jitter/Asymmetry + Джиттер/ÐÑÐ¸Ð¼Ð¼ÐµÑ‚Ñ€Ð¸Ñ + + + + + Focus/Tracking + ФокуÑ/Слежение за дорожкой + + + + Time Analyser + Временной анализ + + + + Read Transfer Rate + СкороÑÑ‚ÑŒ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ + + + + Write Transfer Rate + СкороÑÑ‚ÑŒ запиÑи + + + + Autostart tests on written media inserted + ÐвтозапуÑк теÑтов на запиÑанном диÑке + + + + Autostart tests on blank media inserted + ÐвтозапуÑк теÑтов на чиÑтом диÑке + + + Eject media after tests finished + Извлечь диÑк поÑле Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ñ‚ÐµÑтов + + + + Default tests for written media + ТеÑÑ‚Ñ‹ по умолчанию Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñанного диÑка + + + + Default tests for blank media + ТеÑÑ‚Ñ‹ по умолчанию Ð´Ð»Ñ Ñ‡Ð¸Ñтого диÑка + + + Select directory for reports saving... + Выберите каталог Ð´Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¾Ñ‚Ñ‡ÐµÑ‚Ð¾Ð²... + + + + prefDevices + + + Use local devices + ИÑпользовать локальные уÑтройÑтва + + + + Use network devices + ИÑпользовать Ñетевые уÑтройÑтва + + + + host + хоÑÑ‚ + + + + qscand port + порт qscand + + + + + Edit host + Изменить хоÑÑ‚ + + + + Add host + Добавить хоÑÑ‚ + + + + Remove host + Удалить хоÑÑ‚ + + + + Remove host? + Удалить хоÑÑ‚? + + + + You are about to remove host from list: +%1:%2 +Are you sure? + Ð’Ñ‹ ÑобралиÑÑŒ удалить хоÑÑ‚ из ÑпиÑка: +%1:%2 +Ð’Ñ‹ уверены? + + + + prefReports + + + Autosave reports + ÐвтоÑохранение отчетов + + + + Path: + Путь: + + + + Select directory for reports saving... + Выберите каталог Ð´Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¾Ñ‚Ñ‡ÐµÑ‚Ð¾Ð²... + + + + Use reports database + ИÑпользовать БД отчетов + + + + Driver: + Драйвер: + + + + Host: + ХоÑÑ‚: + + + + Port: + Порт: + + + + DB name: + Ð˜Ð¼Ñ Ð‘Ð”: + + + + User: + Пользователь: + + + + Password: + Пароль: + + + + Check + Проверить + + + + Autosave reports into database + ÐвтоÑохранение отчетов в БД + + + + Eject media after tests finished + Извлечь диÑк поÑле Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ñ‚ÐµÑтов + + + + Select directory... + Выберите каталог... + + + + Success! + УÑпешно! + + + + Database connection successfully +established with given parameters + Подключение к БД уÑтановлено Ñ Ð·Ð°Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸ параметрами + + + + tabDevInfo + + + Vendor: + + + + + Model: + + + + + F/W: + + + + + TLA# + + + + + S/N: + + + + + Buffer: + + + + + IFace: + + + + + Loader: + + + + + RPC Phase: + + + + + Region: + + + + + Changes: + + + + + Resets: + + + + + Discs loaded: + + + + + CD read: + + + + + CD write: + + + + + DVD read: + + + + + DVD write: + + + + + Media R/W Capabilities + ВозможноÑти чтениÑ/запиÑи + + + + Generic Capabilities + Общие возможноÑти + + + + Error + Ошибка + + + + Error updating device info! + Ошибка Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ об уÑтройÑтве! + + + + qScan finished with non-zero exit code + qScan завершилÑÑ Ñ Ð½ÐµÐ½ÑƒÐ»ÐµÐ²Ñ‹Ð¼ кодом возврата + + + + Basic info + ОÑÐ½Ð¾Ð²Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ + + + + tabFETE + + + FE max + FE макÑ. + + + + TE max + TE макÑ. + + + + tabJB + + + Jitter + Джиттер + + + + Asymmetry + ÐÑÐ¸Ð¼Ð¼ÐµÑ‚Ñ€Ð¸Ñ + + + + tabMediaInfo + + + Type: + Тип: + + + + Disc Category: + КатегориÑ: + + + + Media ID: + Идентификатор: + + + + Layers: + Слоев: + + + + Erasable: + Стираемый: + + + + Protection: + Защита: + + + + Regions: + Регионы: + + + + Written on: + ЗапиÑан на: + + + + Readable: + ЗапиÑано: + + + + Free: + Свободно: + + + + Total: + Ð’Ñего: + + + + Disc state: + Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð´Ð¸Ñка: + + + + Session state: + Ð¡Ñ‚Ð°Ñ‚ÑƒÑ ÑеÑÑии: + + + + Read speeds: + СкороÑти чтениÑ: + + + + Write speeds (Device): + СкороÑти запиÑи (уÑтройÑтво): + + + + Write speeds (Media): + СкороÑти запиÑи (ноÑитель): + + + + RD max: + + + + + RD lst: + + + + + WR max: + + + + + WR lst: + + + + + Detailed info + ÐŸÐ¾Ð´Ñ€Ð¾Ð±Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ + + + + Error + Ошибка + + + + Error updating media info! + Ошибка Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ о ноÑителе! + + + + qScan finished with non-zero exit code + qScan завершилÑÑ Ñ Ð½ÐµÐ½ÑƒÐ»ÐµÐ²Ñ‹Ð¼ кодом возврата + + + + tabTA + + + + + Layer + Слой + + + + Zone + ОблаÑÑ‚ÑŒ + + + + Inner + ВнутреннÑÑ + + + + Middle + СреднÑÑ + + + + Outer + ВнешнÑÑ + + + + Pit + + + + + Land + + + + + Time + Ð’Ñ€ÐµÐ¼Ñ + + + + tabTransfer + + + Start + Ðачало + + + + End + Конец + + + + Average + Среднее + + + + Write + ЗапиÑÑŒ + + + + Read + Чтение + + + diff --git a/gui/qpxtool.desktop b/gui/qpxtool.desktop new file mode 100644 index 0000000..3d97743 --- /dev/null +++ b/gui/qpxtool.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Name=QPxTool +GenericName=CD/DVD media quality checker +GenericName[ru]=Проверка качеÑтва ноÑителей CD/DVD +Type=Application +Exec=qpxtool +Icon=qpxtool +Comment=CD/DVD media quality checker +Comment[ru]=Проверка качеÑтва ноÑителей CD/DVD +Categories=System; +StartupNotify=true +Terminal=false +Keywords=CD/DVD;quality;test; diff --git a/gui/qpxtool.ico b/gui/qpxtool.ico new file mode 100644 index 0000000000000000000000000000000000000000..3379f1a0c62a32ea62e4db9e42d07b9585f93621 GIT binary patch literal 16958 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 Y^xHsmcXzC9S2VN>Gq|g>Kl}atKZtG#4*&oF literal 0 HcmV?d00001 diff --git a/gui/qpxtool.pri b/gui/qpxtool.pri new file mode 100644 index 0000000..62e39f2 --- /dev/null +++ b/gui/qpxtool.pri @@ -0,0 +1,19 @@ +isEmpty(QMAKE_LRELEASE) { + + win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\lrelease.exe + + else:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease + +} + +updateqm.input = TRANSLATIONS + +updateqm.output = ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}.qm + +updateqm.commands = $$QMAKE_LRELEASE ${QMAKE_FILE_IN} -qm ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}.qm + +updateqm.CONFIG += no_link + +QMAKE_EXTRA_COMPILERS += updateqm + +PRE_TARGETDEPS += compiler_updateqm_make_all diff --git a/gui/qpxtool.pro b/gui/qpxtool.pro new file mode 100644 index 0000000..dd1f934 --- /dev/null +++ b/gui/qpxtool.pro @@ -0,0 +1,104 @@ +###################################################################### +# Automatically generated by qmake (2.01a) ?? ????. 9 10:47:57 2010 +###################################################################### + +MOC_DIR=moc +OBJECTS_DIR=obj +QT+=widgets network sql printsupport +CONFIG+=thread +INCLUDEPATH+=../lib/include + +TEMPLATE = app +TARGET = qpxtool +DEPENDPATH += . include locale src +INCLUDEPATH += . include + +QMAKE_CXXFLAGS += $$(CXXFLAGS) +QMAKE_LFLAGS += $$(LDFLAGS) + +# Input +HEADERS += include/about.h \ + include/abstractpreview.h \ + include/colorlabel.h \ + include/db_connection.h \ + include/db_report_selection.h \ + include/defs.h \ + include/device.h \ + include/devsettings.h \ + include/devsettings_widgets.h \ + include/dpi_metrics.h \ + include/errc_detailed.h \ + include/graphtab.h \ + include/hostedit_dialog.h \ + include/image_label.h \ + include/images_list.h \ + include/mainwidget.h \ + include/mainwindow.h \ + include/mcapwidget.h \ + include/mwatcher.h \ + include/pref_colors.h \ + include/pref_common.h \ + include/pref_devices.h \ + include/pref_reports.h \ + include/preferences.h \ + include/printpreview.h \ + include/progresswidget.h \ + include/qpxgraph.h \ + include/qpxiodevice.h \ + include/qpxsettings.h \ + include/resultsio.h \ + include/splitbutton.h \ + include/tab_devinfo.h \ + include/tab_errc.h \ + include/tab_fete.h \ + include/tab_jb.h \ + include/tab_mediainfo.h \ + include/tab_ta.h \ + include/tab_transfer.h \ + include/tattoowidget.h \ + include/testdialog.h \ + include/textslider.h \ + include/version.h +SOURCES += src/about.cpp \ + src/abstractpreview.cpp \ + src/colorlabel.cpp \ + src/db_connection.cpp \ + src/db_report_selection.cpp \ + src/device.cpp \ + src/devsettings.cpp \ + src/devsettings_widgets.cpp \ + src/errc_detailed.cpp \ + src/graphtab.cpp \ + src/hostedit_dialog.cpp \ + src/image_label.cpp \ + src/images_list.cpp \ + src/main.cpp \ + src/mainwidget.cpp \ + src/mainwindow.cpp \ + src/mcapwidget.cpp \ + src/mwatcher.cpp \ + src/pref_colors.cpp \ + src/pref_common.cpp \ + src/pref_devices.cpp \ + src/pref_reports.cpp \ + src/preferences.cpp \ + src/printpreview.cpp \ + src/progresswidget.cpp \ + src/qpxgraph.cpp \ + src/qpxiodevice.cpp \ + src/qpxsettings.cpp \ + src/resultsio.cpp \ + src/splitbutton.cpp \ + src/tab_devinfo.cpp \ + src/tab_errc.cpp \ + src/tab_fete.cpp \ + src/tab_jb.cpp \ + src/tab_mediainfo.cpp \ + src/tab_ta.cpp \ + src/tab_transfer.cpp \ + src/tattoowidget.cpp \ + src/testdialog.cpp \ + src/textslider.cpp +RESOURCES += qpxtool.qrc +TRANSLATIONS += locale/qpxtool.ru_RU.ts \ + locale/qpxtool.de_DE.ts diff --git a/gui/qpxtool.qrc b/gui/qpxtool.qrc new file mode 100644 index 0000000..b02698c --- /dev/null +++ b/gui/qpxtool.qrc @@ -0,0 +1,82 @@ + + + images/media/cd.png + images/media/cd_r.png + images/media/cd_rw.png + + images/media/dvd-rom.png + images/media/dvd-ram.png + images/media/dvd-r.png + images/media/dvd-r_dl.png + images/media/dvd-rw.png + images/media/dvd+r.png + images/media/dvd+r_dl.png + images/media/dvd+rw.png + + images/media/hddvd-rom.png + images/media/hddvd-ram.png + images/media/hddvd-r.png + images/media/hddvd-rw.png + + images/media/bd.png + + images/q.png + images/x.png + images/ok.png + images/add.png + images/edit.png + images/edit-redo.png + images/edit-undo.png + images/edit-clear.png + images/logo.png + images/splash.png + images/info.png + images/pdf.png + images/html.png + images/document.png + images/save.png + images/directory.png + images/fileopen.png + images/printer.png + images/page-setup.png + images/zoom-in.png + images/zoom-out.png + images/zoom-orig.png + + images/disc.png + images/device.png + images/cdwriter.png + images/colors.png + images/loej.png + images/lock.png + + images/test_rt.png + images/test_wt.png + images/test_errc.png + images/test_jb.png + images/test_ft.png + images/test_ta.png + + images/settings2.png + images/varirec.png + images/gigarec.png + images/password.png + images/sound.png + images/eraser.png + images/disc-eraser.png + images/tattoo.png + + images/search.png + + images/scan.png + images/stop.png + images/refresh.png + images/refresh-info.png + images/refresh-media.png + images/settings.png + images/exit.png + + ../COPYING + about.html + + diff --git a/gui/qpxtool.rc b/gui/qpxtool.rc new file mode 100644 index 0000000..46e74e6 --- /dev/null +++ b/gui/qpxtool.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "qpxtool.ico" diff --git a/gui/sql/db_create.sql b/gui/sql/db_create.sql new file mode 100644 index 0000000..e2f6546 --- /dev/null +++ b/gui/sql/db_create.sql @@ -0,0 +1,65 @@ +-- +-- This file is part of the QPxTool project. +-- Copyright (C) 2010 Gennady 'ShultZ' Kozlov +-- +-- 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. +-- + +CREATE TABLE media_types ( + id bigint NOT NULL, -- identifier, values from libqpxtransport + name varchar(32) NOT NULL, -- human-readable media type name + + CONSTRAINT media_types_pkey PRIMARY KEY (id) +); + +CREATE TABLE dev_vendors ( + id serial NOT NULL, + name char(8), + + CONSTRAINT dev_vendors_pkey PRIMARY KEY (id), + CONSTRAINT dev_vendors_uniq UNIQUE (name) +); + +CREATE TABLE dev_models ( + id serial NOT NULL, + id_vendor integer NOT NULL, + name char(16), + plugin varchar, + + CONSTRAINT dev_models_pkey PRIMARY KEY (id), + CONSTRAINT dev_models_ref_vendors FOREIGN KEY (id_vendor) + REFERENCES dev_vendors (id) MATCH FULL + ON UPDATE RESTRICT ON DELETE RESTRICT, + CONSTRAINT dev_models_uniq UNIQUE (id_vendor,name) +); + +CREATE TABLE reports ( + id serial, +-- media info + id_media_type bigint NOT NULL, -- media type + label varchar(128) NOT NULL, -- media label + copy_idx integer NOT NULL DEFAULT 0, -- backup copy index + +-- device info + dev_id integer, -- device model + dev_fw char(4), -- device firmware + + md5 char(32), -- MD5 sum of written media + metadata text, -- metadata + data_xml bytea, -- report data + datetime timestamp without time zone NOT NULL DEFAULT now(), -- db insertion timestamp + + CONSTRAINT reports_pkey PRIMARY KEY (id), + CONSTRAINT reports_ref_media FOREIGN KEY (id_media_type) + REFERENCES media_types (id) MATCH FULL + ON UPDATE RESTRICT ON DELETE RESTRICT, + CONSTRAINT reports_ref_device FOREIGN KEY (dev_id) + REFERENCES dev_models (id) MATCH FULL + ON UPDATE RESTRICT ON DELETE RESTRICT, + CONSTRAINT reports_label_uniq UNIQUE (label, copy_idx) +); + diff --git a/gui/sql/db_drop.sql b/gui/sql/db_drop.sql new file mode 100644 index 0000000..484630a --- /dev/null +++ b/gui/sql/db_drop.sql @@ -0,0 +1,15 @@ +-- +-- This file is part of the QPxTool project. +-- Copyright (C) 2010 Gennady 'ShultZ' Kozlov +-- +-- 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. +-- + +DROP TABLE reports; +DROP TABLE dev_models; +DROP TABLE dev_vendors; +DROP TABLE media_types; diff --git a/gui/sql/db_media_types.sql b/gui/sql/db_media_types.sql new file mode 100644 index 0000000..5fbb95c --- /dev/null +++ b/gui/sql/db_media_types.sql @@ -0,0 +1,55 @@ +-- +-- This file is part of the QPxTool project. +-- Copyright (C) 2010 Gennady 'ShultZ' Kozlov +-- +-- 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. +-- + + +-- filling media types list... +INSERT INTO media_types (id,name) VALUES ( 1, 'CD-ROM'); +INSERT INTO media_types (id,name) VALUES ( 2, 'CD-R'); +INSERT INTO media_types (id,name) VALUES ( 4, 'CD-RW'); +INSERT INTO media_types (id,name) VALUES ( 12, 'CD-RW HS'); +INSERT INTO media_types (id,name) VALUES ( 20, 'CD-RW US'); +INSERT INTO media_types (id,name) VALUES ( 28, 'CD-RW US+'); + +INSERT INTO media_types (id,name) VALUES ( 64, 'DVD-ROM'); +INSERT INTO media_types (id,name) VALUES ( 128, 'DVD-RAM'); +INSERT INTO media_types (id,name) VALUES ( 256, 'DVD-R'); +INSERT INTO media_types (id,name) VALUES ( 512, 'DVD-RW'); +INSERT INTO media_types (id,name) VALUES ( 1024, 'DVD-RW Sequential'); +INSERT INTO media_types (id,name) VALUES ( 2048, 'DVD-RW Restricted'); +INSERT INTO media_types (id,name) VALUES ( 4096, 'DVD-R DL'); +INSERT INTO media_types (id,name) VALUES ( 8192, 'DVD-R DL (Layer Jump)'); + +INSERT INTO media_types (id,name) VALUES ( 16384, 'DVD+RW'); +INSERT INTO media_types (id,name) VALUES ( 32768, 'DVD+RW DL'); +INSERT INTO media_types (id,name) VALUES ( 65536, 'DVD+R'); +INSERT INTO media_types (id,name) VALUES ( 131072, 'DVD+R DL'); + +INSERT INTO media_types (id,name) VALUES ( 262144, 'DD CD-ROM'); +INSERT INTO media_types (id,name) VALUES ( 524288, 'DD CD-R'); +INSERT INTO media_types (id,name) VALUES ( 1048576, 'DD CD-RW'); + + +INSERT INTO media_types (id,name) VALUES ( 2097152, 'BD-ROM'); +INSERT INTO media_types (id,name) VALUES ( 4194304, 'BD-R Sequential'); +INSERT INTO media_types (id,name) VALUES ( 8388608, 'BD-R Random Write'); +INSERT INTO media_types (id,name) VALUES ( 16777216, 'BD-RE'); + +INSERT INTO media_types (id,name) VALUES ( 33554432, 'HD DVD-ROM'); +INSERT INTO media_types (id,name) VALUES ( 67108864, 'HD DVD-R'); +INSERT INTO media_types (id,name) VALUES ( 134217728, 'HD DVD-RAM'); +INSERT INTO media_types (id,name) VALUES ( 268435456, 'HD DVD-RW'); +INSERT INTO media_types (id,name) VALUES ( 536870912, 'HD DVD-R DL'); +INSERT INTO media_types (id,name) VALUES ( 1073741824, 'HD DVD-RW DL'); + +INSERT INTO media_types (id,name) VALUES ( 2147483648, 'DVD-RW DL'); -- (1ULL << 31) + + +-- (1ULL << 63) -- 'Unknown media' diff --git a/gui/src/about.cpp b/gui/src/about.cpp new file mode 100644 index 0000000..9a2bb6e --- /dev/null +++ b/gui/src/about.cpp @@ -0,0 +1,64 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include + +#include "about.h" +#include "version.h" + +AboutDialog::AboutDialog(QWidget *p, Qt::WindowFlags fl) + : QDialog(p,fl) +{ + QFile f; + + setWindowTitle(tr("About QPxTool")); + + layout = new QVBoxLayout(this); + layout->setMargin(3); + layout->setSpacing(3); + + lversion = new QLabel("QPxTool version " VERSION, this); + lversion->setAlignment(Qt::AlignCenter); + layout->addWidget(lversion); + + tw = new QTabWidget(this); + layout->addWidget(tw); + + te_about = new QTextEdit(this); + te_about->setReadOnly(true); + te_licence = new QTextEdit(this); + te_licence->setReadOnly(true); + te_licence->setFontFamily("Monospace"); + + f.setFileName(":about.html"); + f.open(QIODevice::ReadOnly); + te_about->setHtml(f.readAll()); + f.close(); + + f.setFileName(":COPYING"); + f.open(QIODevice::ReadOnly); + te_licence->setPlainText(f.readAll()); + f.close(); + + tw->addTab(te_about, tr("About QPxTool")); + tw->addTab(te_licence, tr("Licence")); + + resize(600,400); +} + +AboutDialog::~AboutDialog() {} + + diff --git a/gui/src/abstractpreview.cpp b/gui/src/abstractpreview.cpp new file mode 100644 index 0000000..a79b8c7 --- /dev/null +++ b/gui/src/abstractpreview.cpp @@ -0,0 +1,1609 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Maxim Aldanov , Gennady "ShultZ" Kozlov + * Copyright (C) 2008 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "abstractpreview.h" +//-------------------------------------------------------------- +#define DEFAULT_MARGIN_LEFT 15 +#define DEFAULT_MARGIN_RIGHT 10 +#define DEFAULT_MARGIN_TOP 10 +#define DEFAULT_MARGIN_BOTTOM 10 +//-------------------------------------------------------------- + +#define DEFAULT_SCALE_STEP 1.414213562 + +#define TILE_MAX_WIDTH 512 +#define TILE_MAX_HEIGHT 512 +#define CACHE_SIZE_MAX (16 << 20) + +#define PRINTER_USE_FULLPAGE 1 + +#ifndef QT_NO_DEBUG +//#define DEBUG_PAINT_TIME +#define DEBUG_PRINT_TIME +#endif +//-------------------------------------------------------------- +class AbstractPreview; +class AbstractPreviewPrivate; +//-------------------------------------------------------------- +int QPageInfo::page() const { return fpage; } +QRect QPageInfo::rect() const { return frect; } +//-------------------------------------------------------------- +class QMousePressData +{ +public: + QMousePressData() {press = false;} + bool press; + QPoint pos; +}; +//-------------------------------------------------------------- +class QPageData +{ +public: + QPageData() { + reDraw = true; + index = -1; + } + QPageData(const QPageData &data) { + index = data.index; + reDraw = data.reDraw; + this->data = data.data; + } + ~QPageData() {} + QPageData &operator=(const QPageData &data) { + if(this == &data)return *this; + index = data.index; + reDraw = data.reDraw; + this->data = data.data; + return *this; + } + + bool isValid() { + return (bool)(index == -1); + } + + int index; + QVariant data; + bool reDraw; +protected: +private: +}; +//-------------------------------------------------------------- +//-------------------------------------------------------------- +class TilesCache +{ +private: + class Tile + { + public: + Tile(QPixmap *pix, int p, int x, int y) : data(pix), page(p), xidx(x), yidx(y) {}; + ~Tile() { if (data) delete data; }; + + QPixmap *data; + int page, + xidx, + yidx; + }; + +public: + TilesCache(); + ~TilesCache(); + void clear(); + void setTileSize(int maxW, int maxH); + void insertPages(int start, int count); + void deletePages(int start, int count); + void addTile(QPixmap*, int page, int x, int y); + QPixmap* getTile(int page, int x, int y); +private: + int maxWidth, + maxHeight; + int maxTiles; + QList tiles; +}; +//-------------------------------------------------------------- +class AbstractPreviewPrivate +{ +friend class AbstractPreview; +public: + AbstractPreviewPrivate(AbstractPreview *preview, QPrinter *printer); + ~AbstractPreviewPrivate(); + + void changeScale(); + void setPageDefaults(); + void setup(); + + void calcParamDraw(); + inline int paperWidthToScale(); + inline int paperHeightToScale(); + inline int paperWidth(); + inline int paperHeight(); + + int addPages(int count = 1); + int insertPages(int &index, int count = 1); + int deletePages(int index, int count = 1); + QPageData *pageData(int index); + void clear(); + void flushCache(); + void flushPage(int page); + +protected: + void inits(); + void recalcMarginsFromMM(); +private: + int tileWidth, + tileHeight; + int tileCols, + tileRows; + TilesCache cache; + + AbstractPreview *preview; + QPrinter *printer; + QPaintDevice *currentDevice; + //Определение цветов Ð´Ð»Ñ Ñ€Ð°Ð¼ÐºÐ¸ Ñтраницы и тени + QColor bgc; + QColor cl; + QColor sh1, sh2, sh3; + qreal s1, s2, s3; + + //Параметры размера Ñтраницы + QSizeF paperSizeMM; //Оригинальный размер Ñтраницы в мм + QSize paperSize; //Размер Ñтраницы Ñ Ð¾ÐºÑ€ÑƒÐ³Ð»ÐµÐ½Ð¸ÐµÐ¼ и в пикÑелах + QSize paperSizeScaled; //Размер Ñтраницы Ñ ÑƒÑ‡Ñ‘Ñ‚Ð¾Ð¼ округлений и маÑштаба + + QSizeF pageSizeMM; //ПрÑмоугольник рабочей облаÑти Ñтраницы в мм + QSize pageSize; //то же, но Ñ ÑƒÑ‡ÐµÑ‚Ð¾Ð¼ Ð¾ÐºÑ€ÑƒÐ³Ð»ÐµÐ½Ð¸Ñ Ð¸ в пикÑелах + QSize pageSizeScaled; // Ñ ÑƒÑ‡ÐµÑ‚Ð¾Ð¼ маÑштаба + + QVector marginRects; + int w, h; //выÑота и ширина рабочей облаÑти Ñ ÑƒÑ‡Ñ‘Ñ‚Ð¾Ð¼ Ð¾ÐºÑ€ÑƒÐ³Ð»ÐµÐ½Ð¸Ñ + //ОтÑтупы Ñтраницы и раÑÑтоÑние между Ñтраницами + int pageMargin, interPageSpacing; + int pageLabelHeight; + QRect pageLabelRect; + + // отÑтупы на лиÑте в мм + qreal mm_left, + mm_right, + mm_top, + mm_bottom; + // то же, но в пикÑелах + int mp_left, + mp_right, + mp_top, + mp_bottom; + // Ñ ÑƒÑ‡ÐµÑ‚Ð¾Ð¼ маÑштаба + int ms_left, + ms_top; +// int ms_right, +// ms_bottom; + //Общее количеÑтво Ñтраниц, колонок и Ñтрок Ñтраниц + int cols, rows; + //Параметры Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ Ñтраниц мышкой + QMousePressData mousePressData; + QPoint scrollBarValueOnMousePress; + //МаÑтштаб + double scale, + scaleStep, + scaleMax, + scaleMin; + + //Координаты Ð´Ð»Ñ Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð¾Ð¹ Ñтраницы + qreal xx, yy; + int offsXpage; + int offsYpage; + + //Ðеобходимое количеÑтво Ñтрок Ñтраниц Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ñовки и номер начальной + //Ñтраницы Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð¹ начинаетÑÑ Ð¾Ñ‚Ñ€Ð¸Ñовка документа + int countPagesRow, beginPageNumber; + + QList pagesData; + + AbstractPreview::PreviewMode viewMode; + int selectPageIndex; + QPoint topLeftPage; + QVector pagesRect; + QRect currentPageRect; + QPageInfo enterPageInfo; + + bool mouseTrackingPage; + bool mouseEnterLeavePage; + + bool movedContents; +}; +//-------------------------------------------------------------- + +TilesCache::TilesCache() +{ + maxWidth = TILE_MAX_WIDTH; + maxHeight = TILE_MAX_HEIGHT; + maxTiles = CACHE_SIZE_MAX / maxWidth / maxHeight; +#ifndef QT_NO_DEBUG + qDebug() << "maxTiles: " << maxTiles; +#endif +} + +TilesCache::~TilesCache() +{ + clear(); +} + +void TilesCache::clear() +{ +#ifndef QT_NO_DEBUG + qDebug() << "TilesCache::clear()"; +#endif + for (int i=tiles.size(); i>0; i--) + delete tiles.takeLast(); + tiles.clear(); +} + +void TilesCache::setTileSize(int maxW, int maxH) +{ +#ifndef QT_NO_DEBUG + qDebug() << "TilesCache::setTileSize() " << maxW << "x" << maxH; +#endif + clear(); + maxWidth = maxW; + maxHeight = maxH; + maxTiles = CACHE_SIZE_MAX / maxWidth / maxHeight; +#ifndef QT_NO_DEBUG + qDebug() << "maxTiles: " << maxTiles; +#endif +} + +void TilesCache::insertPages(int start, int count) +{ +#ifndef QT_NO_DEBUG + qDebug() << "TilesCache::insertPages() start: " << start << " count: " << count; +#endif + for (int i=tiles.size()-1; i>=0; i--) + if (tiles[i]->page >= start) tiles[i]->page+=count; +} + +void TilesCache::deletePages(int start, int count) +{ + int end = start+count-1; +#ifndef QT_NO_DEBUG + qDebug() << "TilesCache::deletePages() start: " << start << " count: " << count; +#endif + for (int i=tiles.size()-1; i>=0; i--) { + if (tiles[i]->page > end) tiles[i]->page-=count; + else if (tiles[i]->page >= start) delete tiles.takeAt(i); + } +} + +QPixmap* TilesCache::getTile(int page, int xidx, int yidx) +{ + Tile *tile; +#ifndef QT_NO_DEBUG +// qDebug() << "TilesCache::getTile(): " << page << " x:" << xidx << " y:" << yidx; +#endif + for (int i=0; ipage == page && tile->xidx == xidx && tile->yidx == yidx) { + if (i) { + tile = tiles.takeAt(i); + tiles.prepend(tile); + } +#ifndef QT_NO_DEBUG +// qDebug() << "Tile found at index " << i; +#endif + return tile->data; + } + } + return NULL; +} + +void TilesCache::addTile(QPixmap *pix, int page, int x, int y) +{ + Tile *tile = new Tile(pix,page,x,y); +#ifndef QT_NO_DEBUG +// qDebug() << "TilesCache::addTile(): " << tile->page << " x:" << tile->xidx << " y:" << tile->yidx; +#endif + tiles.prepend(tile); + if (tiles.size()>maxTiles) { +#ifndef QT_NO_DEBUG + qDebug() << "maxTiles reached. removing last"; +#endif + delete tiles.takeLast(); + } +} + +//-------------------------------------------------------------- +AbstractPreviewPrivate::AbstractPreviewPrivate(AbstractPreview *preview, QPrinter *printer) +{ + mouseTrackingPage = false; + mouseEnterLeavePage = true; + movedContents = true; + + this->preview = preview; + this->printer = printer; + + interPageSpacing = 8; + pageMargin = 5; + pageLabelHeight = 0; + + offsXpage = pageMargin; + offsYpage = pageMargin; + scale = 1.0; + scaleStep = DEFAULT_SCALE_STEP; + scaleMax = 2; + scaleMin = 0.25; + viewMode = AbstractPreview::Mode_Normal; + selectPageIndex = 0; + + currentDevice = preview; + setPageDefaults(); + changeScale(); +} +//-------------------------------------------------------------- +AbstractPreviewPrivate::~AbstractPreviewPrivate() +{ + clear(); +} +//-------------------------------------------------------------- +void AbstractPreviewPrivate::inits() +{ + preview->viewport()->setBackgroundRole(QPalette::Dark); + + bgc = preview->viewport()->palette().color(QPalette::Dark); + cl = QColor(bgc.red() * 0.9, bgc.green() * 0.9, bgc.blue() * 0.9); + sh1 = QColor(bgc.red() * 0.4, bgc.green() * 0.4, bgc.blue() * 0.4); + sh2 = QColor(bgc.red() * 0.6, bgc.green() * 0.6, bgc.blue() * 0.6); + sh3 = cl; + + interPageSpacing = 8; + pageMargin = 5; +} +//-------------------------------------------------------------- +void AbstractPreviewPrivate::recalcMarginsFromMM() +{ + mp_left = inchesToPixelsX ( mmToInches(mm_left), currentDevice); + mp_right = inchesToPixelsX ( mmToInches(mm_right), currentDevice); + mp_top = inchesToPixelsY ( mmToInches(mm_top), currentDevice); + mp_bottom = inchesToPixelsY ( mmToInches(mm_bottom), currentDevice); +} +//-------------------------------------------------------------- +void AbstractPreviewPrivate::changeScale() +{ + qreal fw,fh,pw,ph; +// QRect rect; + flushCache(); + ms_left = mp_left * scale; + ms_top = mp_top * scale; +// ms_right = mp_right * scale; +// ms_bottom = mp_bottom * scale; + + s1 = 1 / scale; + s2 = 2 / scale; + s3 = 3 / scale; + + fw = paperSize.width() * scale; + if(fw - ((qreal)(int)fw) > 0.0) paperSizeScaled.setWidth((int)fw + 1); + else paperSizeScaled.setWidth((int)fw); + + fh = paperSize.height() * scale; + if(fh - ((qreal)(int)fh) > 0.0) paperSizeScaled.setHeight((int)fh + 1); + else paperSizeScaled.setHeight((int)fh); + + pw = pageSize.width() * scale; + if(pw - ((qreal)(int)pw) > 0.0) pageSizeScaled.setWidth((int)pw + 1); + else pageSizeScaled.setWidth((int)pw); + + ph = pageSize.height() * scale; + if(ph - ((qreal)(int)ph) > 0.0) pageSizeScaled.setHeight((int)ph + 1); + else pageSizeScaled.setHeight((int)ph); + + fh = paperSizeScaled.height(); + fw = paperSizeScaled.width(); + ph = pageSizeScaled.height(); + pw = pageSizeScaled.width(); + + marginRects.clear(); + marginRects << QRect( QPoint(1,1), QSize( ms_left-1, fh-1)); + marginRects << QRect( QPoint(ms_left + pw,1), QSize( fw - pw - ms_left, fh-1)); + marginRects << QRect( QPoint(ms_left,1), QSize( pw, ms_top-1)); + marginRects << QRect( QPoint(ms_left, ph + ms_top), QSize( pw, fh - ph - ms_top)); + + + tileWidth = qMin(pageSizeScaled.width()+1, TILE_MAX_WIDTH); + tileCols = pageSizeScaled.width() / tileWidth; + if (pageSizeScaled.width() % tileWidth) tileCols++; + + tileHeight = qMin(pageSizeScaled.height()+1, TILE_MAX_HEIGHT); + tileRows = pageSizeScaled.height() / tileHeight; + if (pageSizeScaled.height() % tileHeight) tileRows++; + + cache.setTileSize(tileWidth, tileHeight); + + +#ifndef QT_NO_DEBUG + qDebug() << "page tiles: " << tileCols << "x" << tileRows; +#endif +} + +//-------------------------------------------------------------- +void AbstractPreviewPrivate::setPageDefaults() +{ + mm_left = DEFAULT_MARGIN_LEFT; + mm_right = DEFAULT_MARGIN_RIGHT; + mm_top = DEFAULT_MARGIN_TOP; + mm_bottom = DEFAULT_MARGIN_BOTTOM; + recalcMarginsFromMM(); + + printer->setPaperSize(QPrinter::A4); + printer->setPageMargins(mm_left, mm_top, mm_right, mm_bottom, QPrinter::Millimeter); +#ifdef PRINTER_USE_FULLPAGE + printer->setFullPage(true); +#else + printer->setFullPage(false); +#endif + setup(); +} +//-------------------------------------------------------------- +void AbstractPreviewPrivate::setup() +{ + paperSizeMM = printer->paperRect(QPrinter::Millimeter).size(); + paperSize = QSize( + inchesToPixelsX ( mmToInches(paperSizeMM.width()), currentDevice) +1, + inchesToPixelsY ( mmToInches(paperSizeMM.height()), currentDevice) +1 + ); + +// if(paperSizeMM.width() - ((qreal)(int)paperSize.width()) > 0.0) paperSize.setWidth ((int)paperSize.width() + 1); +// if(paperSizeMM.height() - ((qreal)(int)paperSize.height()) > 0.0) paperSize.setHeight((int)paperSize.height() + 1); + +// if(pageSizeMM.width() - ((qreal)(int)pageSize.width()) > 0.0) pageSize.setWidth ((int)pageSize.width() + 1); +// if(pageSizeMM.height() - ((qreal)(int)pageSize.height()) > 0.0) pageSize.setHeight((int)pageSize.height() + 1); + + printer->getPageMargins(&mm_left, &mm_top, &mm_right, &mm_bottom, QPrinter::Millimeter); + recalcMarginsFromMM(); + +#if (PRINTER_USE_FULLPAGE == 1) || (QT_VERSION < 0x040500) + pageSizeMM = QSize( + paperSizeMM.width() - mm_left - mm_right, + paperSizeMM.height() - mm_top - mm_bottom + ); +#else + pageSizeMM = printer->pageRect(QPrinter::Millimeter).size(); +#endif + + pageSize = QSize( + inchesToPixelsX ( mmToInches(pageSizeMM.width()), currentDevice) + 1, + inchesToPixelsY ( mmToInches(pageSizeMM.height()), currentDevice) + 1 + ); + + w = paperSize.width(); + h = paperSize.height(); + +#ifndef QT_NO_DEBUG + qDebug() << "device DPI (phy) X:" << currentDevice->physicalDpiX() << " Y:" << currentDevice->physicalDpiY(); + qDebug() << "device DPI (log) X:" << currentDevice->logicalDpiX() << " Y:" << currentDevice->logicalDpiY(); + qDebug() << "paperSize (mm): " << paperSizeMM.width() << "x" << paperSizeMM.height(); + qDebug() << "pageSize (mm): " << pageSizeMM.width() << "x" << pageSizeMM.height(); + qDebug() << "pageSize (px): " << w << "x"<< h; +#endif +} +//-------------------------------------------------------------- +void AbstractPreviewPrivate::calcParamDraw() +{ +#ifndef QT_NO_DEBUG +// qDebug() << "calcParamRedraw(): preview @ "<< preview; +#endif + const int startRow = (preview->verticalScrollBar()->value() - pageMargin) / + (paperHeightToScale() + interPageSpacing + pageLabelHeight); + const double hsp = paperHeightToScale() + interPageSpacing; + const int vph = preview->viewport()->height(); + + yy = ((paperHeightToScale() + interPageSpacing + pageLabelHeight) * startRow + pageMargin); + yy = (qreal)preview->verticalScrollBar()->value() - yy; + xx = offsXpage; + + if(!preview->horizontalScrollBar()->value()) { + if(preview->viewport()->width() < paperWidthToScale()) + xx = offsXpage; + else { + if(cols == 1) + xx = (preview->viewport()->width() - paperWidthToScale()) / 2; + else xx = (preview->viewport()->width() - (paperWidthToScale() * cols + interPageSpacing * (cols - 1))) / 2; + } + } + + countPagesRow = 1; + int t = (qreal)(-yy); + t += hsp; + while(t < vph) { + t += hsp; + countPagesRow++; + } + + beginPageNumber = startRow * cols; +} +//-------------------------------------------------------------- + +int AbstractPreviewPrivate::paperWidthToScale() { return paperSizeScaled.width(); } +int AbstractPreviewPrivate::paperHeightToScale() { return paperSizeScaled.height(); } +int AbstractPreviewPrivate::paperWidth() { return paperSize.width(); } +int AbstractPreviewPrivate::paperHeight() { return paperSize.height(); } + +//-------------------------------------------------------------- +void AbstractPreviewPrivate::flushCache() +{ + cache.clear(); +} +//-------------------------------------------------------------- +int AbstractPreviewPrivate::addPages(int count/* = 1*/) +{ + if(count < 1)return 0; + int c = 0; + for(int i = 0; i < count; i++) { + QPageData *pd = new QPageData; + pd->index = pagesData.count(); + pd->reDraw = true; + pagesData.append(pd); + c++; + } + return c; +} +//-------------------------------------------------------------- +int AbstractPreviewPrivate::insertPages(int &index, int count/* = 1*/) +{ + if(count < 1)return 0; + if(index >= pagesData.count()) { + index = pagesData.count(); + return addPages(count); + } + index = qMax(index, 0); + int c = 0; + for(int i = 0; i < count; i++) { + QPageData *pd = new QPageData; + pd->index = pagesData.count(); + pd->reDraw = true; + pagesData.insert(index, pd); + c++; + } + cache.insertPages(index, c); + return c; +} +//-------------------------------------------------------------- +int AbstractPreviewPrivate::deletePages(int index, int count/* = 1*/) +{ + if(count < 1)return 0; + if(index >= pagesData.count())return 0; + int c = 0; + while(count) { + delete pagesData.takeAt(index); + count--; + c++; + } + cache.deletePages(index, c); + return c; +} +//-------------------------------------------------------------- +void AbstractPreviewPrivate::flushPage(int page) +{ + cache.deletePages(page, 1); +} +//-------------------------------------------------------------- +QPageData *AbstractPreviewPrivate::pageData(int index) +{ + if(index < 0 || index >= pagesData.count())return NULL; + return pagesData[index]; +} +//-------------------------------------------------------------- +void AbstractPreviewPrivate::clear() +{ + flushCache(); + while(pagesData.count()) { + delete pagesData.takeLast(); + } +} +//-------------------------------------------------------------- +AbstractPreview::AbstractPreview(QWidget *parent, QPrinter *printer) + : QAbstractScrollArea(parent) +{ + setMouseTracking(true); + + dv = new AbstractPreviewPrivate(this, printer); + dp = NULL; + d = dv; + + verticalScrollBar()->setSingleStep(20); + horizontalScrollBar()->setSingleStep(20); +// d->addPage(); + + d->inits(); +} +//-------------------------------------------------------------- +AbstractPreview::AbstractPreview(QWidget *parent, QPrinter *printer, int _countPage) + : QAbstractScrollArea(parent) +{ + setMouseTracking(true); + + dv = new AbstractPreviewPrivate(this, printer); + dp = NULL; + d = dv; + + verticalScrollBar()->setSingleStep(20); + horizontalScrollBar()->setSingleStep(20); + + if(_countPage < 0)_countPage = 1; + d->addPages(_countPage); + + d->inits(); + + sizeToCountPage(); +} +//-------------------------------------------------------------- +AbstractPreview::~AbstractPreview() +{ + delete d; +} +//-------------------------------------------------------------- +int AbstractPreview::sizeToCountPage() +{ + if(!d->pagesData.count()) { + verticalScrollBar()->setRange(0, 0); + horizontalScrollBar()->setRange(0, 0); + d->cols = 0; + d->rows = 0; + return 0; + } + + int cols; + if(d->viewMode == AbstractPreview::Mode_Normal) { + cols = (int)(viewport()->width() - 2 * d->pageMargin + d->interPageSpacing) / + (d->paperSize.width() * d->scale + d->interPageSpacing); + } else { + cols = 1; + } + if(!cols)cols = 1; + if(cols > d->pagesData.count())cols = d->pagesData.count(); + + int rows = (d->pagesData.count() + cols - 1) / cols; + int h = qRound(rows * (d->paperHeightToScale() + d->pageLabelHeight) + + (rows - 1.) * d->interPageSpacing + + 2. * d->pageMargin); + int w = qRound(cols * d->paperWidthToScale() + 2. * d->pageMargin); + horizontalScrollBar()->setRange(0, w - viewport()->width()); + horizontalScrollBar()->setPageStep(viewport()->width()); + verticalScrollBar()->setRange(0, h - viewport()->height()); + verticalScrollBar()->setPageStep(viewport()->height()); + + d->cols = cols; + d->rows = rows; + return 0; +} +//-------------------------------------------------------------- +void AbstractPreview::autoscaleThumbs() +{ +// qDebug() << "autoscaleThumbs(): w=" << d->w << " viewport()->width()=" << viewport()->width(); + int w = viewport()->width() - 2 * d->pageMargin + d->interPageSpacing - 10; + if (w<1) w=1; + qreal scale = w / (float) d->paperSize.width(); + + if (scale != d->scale) { + d->scale = scale; + d->changeScale(); + d->pageLabelRect = QRect(1,d->paperSizeScaled.height() + 3, d->paperSizeScaled.width(), d->pageLabelHeight); + } + sizeToCountPage(); +} +//-------------------------------------------------------------- +void AbstractPreview::resizeEvent(QResizeEvent *) +{ + d->enterPageInfo = QPageInfo(); + + if(d->viewMode == AbstractPreview::Mode_Thumbs) { + autoscaleThumbs(); + } else { + sizeToCountPage(); + } + d->calcParamDraw(); +} +//-------------------------------------------------------------- +void AbstractPreview::scrollContentsBy(int dx, int dy) +{ + d->enterPageInfo = QPageInfo(); + + d->offsXpage += dx; + d->offsYpage += dy; + if(dx || dy) d->calcParamDraw(); + updatePreview(); +} +//-------------------------------------------------------------- +void AbstractPreview::paintBorder(QPainter *p) +{ +#if 0 + p->setPen(d->cl); + p->drawRect( QRect( QPoint(0,0), d->paperSize) ); + +// p->setPen(Qt::red); +// p->drawRect( QRect( QPoint(d->mp_left, d->mp_top), d->pageSize - QSize(1,1) ) ); +#endif + + p->setPen(d->sh1); + p->drawLine(QLineF(d->w + d->s1, d->s3, d->w + d->s1, d->h + d->s1)); + p->drawLine(QLineF(d->s3, d->h + d->s1, d->w + d->s1, d->h + d->s1)); + + p->setPen(d->sh2); + p->drawLine(QLineF(d->w + d->s2, d->s3, d->w + d->s2, d->h + d->s2)); + p->drawLine(QLineF(d->s3, d->h + d->s2, d->w + d->s2, d->h + d->s2)); + + p->setPen(d->sh3); + p->drawLine(QLineF(d->w + d->s3, d->s3, d->w + d->s3, d->h + d->s3)); + p->drawLine(QLineF(d->s3, d->h + d->s3, d->w + d->s3, d->h + d->s3)); +} +//-------------------------------------------------------------- + +QPixmap* AbstractPreview::getTile(int page, int tx, int ty) +{ + QPixmap *pix = d->cache.getTile(page,tx,ty); + if(!pix) { +#ifndef QT_NO_DEBUG +// qDebug() << "Tile not found in cache, rendering new one"; +#endif + // tile->data = new QPixmap(d->paperWidthToScale() + 1, d->paperHeightToScale() + 1); + pix = new QPixmap( + qMin(d->tileWidth, (d->pageSizeScaled.width() - d->tileWidth * tx)), + qMin(d->tileHeight, (d->pageSizeScaled.height() - d->tileHeight * ty)) + ); +// qDebug() << "page " << page << " tile: " << ty << "," << tx << " " << pix->width() << "x" << pix->height(); + //tile->data->fill(d->bkg); + QPainter pp(pix); + // pp.setPen(d->cl); + pp.setPen(Qt::white); + pp.setBrush(Qt::white); + pp.drawRect( QRect(QPoint(0,0), pix->size()) ); + pp.scale(d->scale, d->scale); +// pp.translate(d->mp_left, d->mp_top); + + paintPage(&pp, page, QRect( QPoint(d->tileWidth * tx, d->tileHeight * ty) / d->scale, pix->size() / d->scale ) ); + + d->cache.addTile(pix, page, tx, ty); + } +/* +#ifndef QT_NO_DEBUG + qDebug() << "QPainter @" << (void*)p; + qDebug() << "Tile @" << (void*)tile; + if (tile) { + qDebug() << "Tile data @" << (void*)tile->data; + } +#endif +*/ + return pix; +} +void AbstractPreview::updatePage(int page, QRect rect) +{ + //Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ ÑƒÑ‡Ð°Ñтка Ñтраницы + QPixmap pix(rect.size()); + pix.fill(Qt::white); + QPainter p(&pix); + paintPage(&p, page, rect); + + QPageInfo info = pageInfo(page); + int ty_sta, ty_end, tx_sta, tx_end; + const int yy = info.rect().top(), xx = info.rect().left(); + const int yy2 = yy + d->pageSizeScaled.height(), + xx2 = xx + d->pageSizeScaled.width(); + ty_sta = (yy < 0) ? (-yy / d->tileHeight) : 0; + ty_end = (yy2 < viewport()->height()) ? d->tileRows : + ((viewport()->height() - yy) / d->tileHeight) + 1; + + tx_sta = (xx<0) ? (-xx / d->tileWidth) : 0; + tx_end = (xx2 < viewport()->width()) ? d->tileCols : + ((viewport()->width() - xx) / d->tileWidth) + 1; + + for(int ty = ty_sta; ty < ty_end; ty++) { + for(int tx = tx_sta; tx < tx_end; tx++) { + QRect tileRect(d->tileWidth * tx, d->tileHeight * ty, + d->tileWidth, d->tileHeight); + QRect sec = tileRect.intersected(rect); + if(!sec.isValid()) continue; + QPixmap *px = getTile(page, tx, ty); + if(!px)continue; + QPainter pp(px); + pp.drawPixmap(sec.topLeft() - tileRect.topLeft(), + pix, + QRect(sec.topLeft() - rect.topLeft(), + sec.size())); + } + } + + viewport()->update(); +} + +void AbstractPreview::paintEvent(QPaintEvent *) +{ + d->pagesRect.clear();//Буфер координат текущих отриÑованных Ñтраниц + + QPainter *p = new QPainter(viewport()); +// QPixmap *pix = NULL; + int page = d->beginPageNumber; + const QColor bkg = viewport()->backgroundRole(); + int xx = d->xx, yy = -d->yy; + int xx2,yy2; + int ty_sta,ty_end,tx_sta,tx_end; + QRect mrect; + + p->save(); + + p->translate(xx, yy); + yy += (d->ms_top); + + for(int y = 0; y < d->countPagesRow; y++) { + xx = d->xx + d->ms_left; + yy2 = yy+d->pageSizeScaled.height(); + ty_sta = (yy<0) ? (-yy / d->tileHeight) : 0; + ty_end = (yy2height()) ? d->tileRows : ( (viewport()->height()-yy) / d->tileHeight)+1; + + p->save(); + for(int x = 0; x < d->cols; x++) { + xx2 = xx+d->pageSizeScaled.width(); + tx_sta = (xx<0) ? (-xx / d->tileWidth) : 0; + tx_end = (xx2width()) ? d->tileCols : ( (viewport()->width()-xx) / d->tileWidth)+1; + + p->save(); + p->translate(d->ms_left, d->ms_top); +#ifdef DEBUG_PAINT_TIME + timeval tb,te; + gettimeofday(&tb, NULL); +#endif + for(int ty=ty_sta; tydrawPixmap(d->tileWidth * tx, d->tileHeight * ty, *(getTile(page, tx, ty))); + } + } +#ifdef DEBUG_PAINT_TIME + gettimeofday(&te, NULL); + qDebug() << "Page: " << page << " " << tx_sta << "." << ty_sta << " - " << tx_end << "." << ty_end + << QString(" time: %1 s").arg(te.tv_sec - tb.tv_sec + (te.tv_usec - tb.tv_usec)/1000000.0,0,'f',4); +#endif +// p->drawPixmap(0, 0, *(getTile(page, 0, 0))); + p->restore(); + + mrect = QRect( QPoint(-xx + d->ms_left, -yy + d->ms_top), viewport()->size() ); + for (int i=0; imarginRects.size(); i++) { + if (!i && d->viewMode == AbstractPreview::Mode_Normal) { + // qDebug() << "page #" << page << mrect << d->marginRects[i] << d->marginRects[i].intersected(mrect); + } + p->fillRect(d->marginRects[i].intersected(mrect), QBrush(Qt::white)); + } + + p->save(); + p->scale(d->scale, d->scale); + + if(d->viewMode == AbstractPreview::Mode_Thumbs && d->selectPageIndex == page) { +#ifndef QT_NO_DEBUG + qDebug() << "Thumbs selected page: " << page; +#endif + QRect rr = QRect( QPoint(0,0), d->paperSize); + //p->setCompositionMode(QPainter::CompositionMode_Multiply); + //p->setCompositionMode(QPainter::CompositionMode_Xor); + QColor col = palette().color(QPalette::Highlight); + QColor colb = palette().color(QPalette::Highlight); + colb.setAlpha(0x80); + p->setPen(col); + p->setBrush(colb); + p->drawRect(rr); + //p->setCompositionMode(QPainter::CompositionMode_SourceOver); + } + paintBorder(p); + + //СохранÑем параметры прориÑованных Ñтраниц + QPageInfo pg(page, + QRect(xx - d->ms_left, + yy - d->ms_top, + d->paperWidthToScale(), + d->paperHeightToScale())); + d->pagesRect.append(pg); + + //Ð”Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¿Ñ€Ð¾Ñ€Ð¸Ñовка Ñтраницы + QRect rr(0, 0, viewport()->width() - 1, viewport()->height() - 1); + QRect rr1 = pg.rect().intersected(rr); + p->save(); + additionalPaintEvent(p, page, rr1); //Ð”Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¿Ñ€Ð¾Ñ€Ð¸Ñовка Ñтраниц + p->restore(); + + p->restore(); + + if (d->pageLabelHeight) { + p->setPen(palette().color(QPalette::Text)); + p->drawText( d->pageLabelRect, Qt::AlignCenter, QString::number(page+1)); + } + + page++; + if(page == d->pagesData.count()) break; + xx += (d->paperWidthToScale() + d->interPageSpacing); + p->translate((d->paperWidthToScale() + d->interPageSpacing), 0); + } //end for(int x = 0; x < d->cols; x++) + + if(d->viewMode != Mode_Thumbs) { + int _y = yy - d->ms_top; + if(_y < (viewport()->height()>>1)) { + d->selectPageIndex = page - 1; + } + } + + p->restore(); + if(page == d->pagesData.count())break; + p->translate(0, d->paperHeightToScale() + d->interPageSpacing + d->pageLabelHeight); + yy += (d->paperHeightToScale() + d->interPageSpacing + d->pageLabelHeight); + } //end for(int y = 0; y < d->countPagesRow; y++) + p->restore(); +// for(int i = 0; i < d->pagesRect.count(); i++) { +// p->setPen(Qt::red); +// p->drawRect(d->pagesRect[i].rect()); +// } + delete p; + + if(d->viewMode != Mode_Thumbs && d->selectPageIndex >= 0) { + emit currentPage(d->selectPageIndex); + } +} +//-------------------------------------------------------------- +void AbstractPreview::additionalPaintEvent(QPainter *p, int page, const QRect &rect) +{ + Q_UNUSED(p); + Q_UNUSED(page); +} +//-------------------------------------------------------------- +QSize AbstractPreview::getPaperSize() { return d->paperSize; } +QSize AbstractPreview::getPageSize() { return d->pageSize; } +//-------------------------------------------------------------- +int AbstractPreview::page(QPoint point) +{ + const int count = d->pagesRect.count(); + int sel = -1; + for(int i = 0; i < count; i++) { + if(d->pagesRect[i].rect().contains(point)) { + sel = d->pagesRect[i].page(); + break; + } + } + + if(sel >= 0) return sel; + return -1; +} +//-------------------------------------------------------------- +QRect AbstractPreview::pageRect(QPoint point) +{ + const int count = d->pagesRect.count(); + int sel = -1; + for(int i = 0; i < count; i++) { + if(d->pagesRect[i].rect().contains(point)) { + return d->pagesRect[i].rect(); + break; + } + } + return QRect(); +} +//-------------------------------------------------------------- +QPoint AbstractPreview::toPage(QPoint point) +{ + const int count = d->pagesRect.count(); + for(int i = 0; i < count; i++) { + if(d->pagesRect[i].rect().contains(point)) { + return point - d->pagesRect[i].rect().topLeft(); + } + } + + return QPoint(); +} +//-------------------------------------------------------------- +QPoint AbstractPreview::toCurrentPage(QPoint point) +{ + if(!d->currentPageRect.contains(point))return QPoint(); + return point - d->currentPageRect.topLeft(); +} +//-------------------------------------------------------------- +QPageInfo AbstractPreview::pageInfo(int page) +{ + if(d->enterPageInfo.isValid() && d->enterPageInfo.page() == page) + return d->enterPageInfo; + + const int count = d->pagesRect.count(); + QPageInfo info; + //QRect rect; + for(int i = 0; i < count; i++) { + if(d->pagesRect[i].page() == page) { + info = QPageInfo(page, d->pagesRect[i].rect()); + return info; + } + } + return info; +} +//-------------------------------------------------------------- +QPageInfo AbstractPreview::pageInfo(const QPoint &point) +{ + int page = -1; + QRect rect; + if(!d->enterPageInfo.rect().contains(point)) { + const int count = d->pagesRect.count(); + for(int i = 0; i < count; i++) { + rect = d->pagesRect[i].rect(); + if(rect.contains(point)) { + page = d->pagesRect[i].page(); + break; + } + } + if(page == -1) d->enterPageInfo = QPageInfo(); + else d->enterPageInfo = QPageInfo(page, rect); + } + return d->enterPageInfo; +} +//-------------------------------------------------------------- +QVector AbstractPreview::visiblePages() +{ + return d->pagesRect; +} +//-------------------------------------------------------------- +void AbstractPreview::startMoveContext(const QPoint &point) +{ + d->mousePressData.press = true; + d->mousePressData.pos = point; + d->scrollBarValueOnMousePress.rx() = horizontalScrollBar()->value(); + d->scrollBarValueOnMousePress.ry() = verticalScrollBar()->value(); + //d->scrollBarValueOnMousePress = point; +} +//-------------------------------------------------------------- +void AbstractPreview::moveContext(const QPoint &point) +{ + if(!d->mousePressData.press && d->scrollBarValueOnMousePress.isNull())return; + + horizontalScrollBar()->setValue(d->scrollBarValueOnMousePress.x() - + point.x() + d->mousePressData.pos.x()); + verticalScrollBar()->setValue(d->scrollBarValueOnMousePress.y() - + point.y() + d->mousePressData.pos.y()); + d->calcParamDraw(); + horizontalScrollBar()->update(); +} +//-------------------------------------------------------------- +void AbstractPreview::endMoveContext() +{ + d->mousePressData.press = false; + d->mousePressData.pos = QPoint(); + + d->scrollBarValueOnMousePress = QPoint(); +} +//-------------------------------------------------------------- +void AbstractPreview::mousePressEvent(QMouseEvent *e) +{ + startMoveContext(e->pos()); + + const int count = d->pagesRect.count(); + int sel = -1; + for(int i = 0; i < count; i++) { + if(d->pagesRect[i].rect().contains(e->pos())) { + sel = d->pagesRect[i].page(); + d->currentPageRect = d->pagesRect[i].rect(); + break; + } + } + + if(sel >= 0) { +#warning disabled due to enable multiple selection of same page (i.e. to return to viewed page) +// if(d->selectPageIndex == sel) return; + d->selectPageIndex = sel; + //Вызываем Ñобытие клика мышкой на Ñтранице + mousePressPageEvent(e, d->selectPageIndex, d->currentPageRect, toCurrentPage(e->pos())); + + emit pageSelected(d->selectPageIndex); + updatePreview(); + } else { + if(d->selectPageIndex == -1) return; + d->selectPageIndex = -1; + updatePreview(); + } +} +//-------------------------------------------------------------- +void AbstractPreview::mouseMoveEvent(QMouseEvent *e) +{ + if(!d->mousePressData.press) { + pageInfo(e->pos()); + if(d->enterPageInfo.isValid()) + mouseMovePageEvent(e, d->enterPageInfo); + + e->ignore(); + return; + } + + if(d->movedContents) + moveContext(e->pos()); +} +//-------------------------------------------------------------- +void AbstractPreview::mouseReleaseEvent(QMouseEvent *e) +{ + Q_UNUSED(e); + endMoveContext(); +} +//-------------------------------------------------------------- +void AbstractPreview::mousePressPageEvent(QMouseEvent *event, int page, + QRect rect, QPoint point) +{ + Q_UNUSED(event); + Q_UNUSED(page); + Q_UNUSED(rect); + Q_UNUSED(point); +} +//-------------------------------------------------------------- +void AbstractPreview::mouseMovePageEvent(QMouseEvent *event, const QPageInfo &info) +{ + Q_UNUSED(event); + Q_UNUSED(info); +} +//-------------------------------------------------------------- +void AbstractPreview::wheelEvent(QWheelEvent * event) +{ + if(event->modifiers() != Qt::ControlModifier) { + QAbstractScrollArea::wheelEvent(event); + return; + } + + if (event->delta() > 0) scaleIn(); + else scaleOut(); +} +//-------------------------------------------------------------- +void AbstractPreview::setInterPageSpacing(int spacing) +{ + if(d->interPageSpacing == spacing)return; + d->interPageSpacing = spacing; + sizeToCountPage(); + updatePreview(); +} +//-------------------------------------------------------------- +int AbstractPreview::interPageSpacing() +{ + return d->interPageSpacing; +} +//-------------------------------------------------------------- +void AbstractPreview::setPageMargin(int margin) +{ + if(d->pageMargin == margin)return; + d->pageMargin = margin; + sizeToCountPage(); + updatePreview(); +} +//-------------------------------------------------------------- +int AbstractPreview::pageMargin() +{ + return d->pageMargin; +} +//-------------------------------------------------------------- +int AbstractPreview::pageCount() +{ + return d->pagesData.count(); +} +//-------------------------------------------------------------- + +void AbstractPreview::setMargins(qreal ml, qreal mt, qreal mr, qreal mb) +{ + if(d->mm_left == ml && d->mm_right == mr && + d->mm_top == mt && d->mm_bottom == mb)return; + d->mm_left = ml; + d->mm_right = mr; + d->mm_top = mt; + d->mm_bottom = mb; + + d->mp_left = inchesToPixelsX ( mmToInches(d->mm_left), this); + d->mp_right = inchesToPixelsX ( mmToInches(d->mm_right), this); + d->mp_bottom = inchesToPixelsX ( mmToInches(d->mm_bottom), this); + d->mp_top = inchesToPixelsX ( mmToInches(d->mm_top), this); + updatePreview(); +} +//-------------------------------------------------------------- +void AbstractPreview::setMarginLeft(qreal ml) +{ + if(d->mm_left == ml)return; + d->mm_left = ml; + d->mp_left = inchesToPixelsX ( mmToInches(d->mm_left), this); + updatePreview(); +} +//-------------------------------------------------------------- +void AbstractPreview::setMarginTop(qreal mt) +{ + if(d->mm_top == mt)return; + d->mm_top = mt; + d->mp_top = inchesToPixelsX ( mmToInches(d->mm_top), this); + updatePreview(); +} +//-------------------------------------------------------------- +void AbstractPreview::setMarginRight(qreal mr) +{ + if(d->mm_right == mr)return; + d->mm_right = mr; + d->mp_right = inchesToPixelsX ( mmToInches(d->mm_right), this); + updatePreview(); +} +//-------------------------------------------------------------- +void AbstractPreview::setMarginBottom(qreal mb) +{ + if(d->mm_bottom == mb)return; + d->mm_bottom = mb; + d->mp_bottom = inchesToPixelsX ( mmToInches(d->mm_bottom), this); + updatePreview(); +} +//-------------------------------------------------------------- + +qreal AbstractPreview::marginLeft() { return d->mm_left; } +qreal AbstractPreview::marginTop() { return d->mm_top; } +qreal AbstractPreview::marginRight() { return d->mm_right; } +qreal AbstractPreview::marginBottom() { return d->mm_bottom; } + +int AbstractPreview::marginLeftPx() { return d->mp_left; } +int AbstractPreview::marginTopPx() { return d->mp_top; } +int AbstractPreview::marginRightPx() { return d->mp_right; } +int AbstractPreview::marginBottomPx() { return d->mp_bottom; } + +//-------------------------------------------------------------- +void AbstractPreview::addedPages(int count, int begin) +{ +} +//-------------------------------------------------------------- +int AbstractPreview::addPages(int count/* = 1*/) +{ + int cc = d->pagesData.count(); + int c = d->addPages(count); + addedPages(c, cc); + sizeToCountPage(); + updatePreview(); + return c; +} +//-------------------------------------------------------------- +int AbstractPreview::insertPages(int index, int count/* = 1*/) +{ + int c = d->insertPages(index, count); + addedPages(c, index); + updatePreview(); + return c; +} +//-------------------------------------------------------------- +int AbstractPreview::deletePages(int index, int count/* = 1*/) +{ + int c = d->deletePages(index, count); + updatePreview(); + return c; +} +//-------------------------------------------------------------- +void AbstractPreview::pageSetup() +{ + QPageSetupDialog dlg(d->printer, this); + if (dlg.exec() == QDialog::Accepted) { + setupPageFormat(); + emit pageFormatChanged(); + } +} +//-------------------------------------------------------------- +void AbstractPreview::setPageData(int index, QVariant data) +{ + if(index < 0 || index >= d->pagesData.count())return; + d->pagesData[index]->data = data; +} +//-------------------------------------------------------------- +QVariant AbstractPreview::pageData(int index) +{ + if(index < 0 || index >= d->pagesData.count())return QVariant(); + return d->pagesData[index]->data; +} +//-------------------------------------------------------------- +void AbstractPreview::setViewMode(AbstractPreview::PreviewMode mode) +{ + if(d->viewMode == mode)return; + if (mode == Mode_Thumbs) { + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + d->pageLabelHeight = 18; + } else { + setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + d->pageLabelHeight = 0; + } + d->viewMode = mode; + sizeToCountPage(); + d->calcParamDraw(); + updatePreview(); +} +//-------------------------------------------------------------- +AbstractPreview::PreviewMode AbstractPreview::viewMode() +{ + return d->viewMode; +} +//-------------------------------------------------------------- +#ifdef PRINTER_CHANGE_DEVICE +void AbstractPreview::changeDevice(QPaintDevice* device) +{ + if (device == d->printer) { + if (!dp) dp = new AbstractPreviewPrivate(this, d->printer); + d = dp; + d->currentDevice = device; + deviceChanged(d->currentDevice); + setupPageFormat(); + } else { + d = dv; + if (dp) { + delete dp; + dp = NULL; + } + d->currentDevice = device; + deviceChanged(d->currentDevice); + } +} +#endif +//-------------------------------------------------------------- +void AbstractPreview::print() +{ +#ifndef QT_NO_DEBUG + qDebug("STA: AbstractPreview::print()"); +#endif + QPrintDialog *dlg = new QPrintDialog(d->printer, NULL); +// d->printer->setOutputFormat(QPrinter::PdfFormat); + if(dlg->exec() == QDialog::Accepted) { + printPages(d->printer); + } + delete dlg; +#ifndef QT_NO_DEBUG + qDebug("END: AbstractPreview::print()"); +#endif +} + +void AbstractPreview::print(QPrinter *printer) +{ + printPages(printer); +} + +void AbstractPreview::printPages(QPrinter *printer) +{ +#ifdef DEBUG_PRINT_TIME + timeval tpb,tpe; +#ifdef PRINTER_CHANGE_DEVICE + timeval tsb; +#endif +#endif + int fromPage = printer->fromPage(); + int toPage = printer->toPage(); + int count = d->pagesData.count(); + if (fromPage) fromPage--; + if (!toPage || toPage>count) toPage = count; + +#ifdef PRINTER_CHANGE_DEVICE +#ifdef DEBUG_PRINT_TIME + gettimeofday(&tsb, NULL); +#endif + changeDevice(printer); +#endif +#ifdef DEBUG_PRINT_TIME + gettimeofday(&tpb, NULL); +#endif + QPainter p; + p.begin(printer); +#ifndef PRINTER_CHANGE_DEVICE + float scaleX = (float) d->printer->logicalDpiX() / d->preview->logicalDpiX(); + float scaleY = (float) d->printer->logicalDpiY() / d->preview->logicalDpiY(); + qDebug() << "Printer scale: "<< scaleX << "x" << scaleY; + p.scale(scaleX, scaleY); +#endif +#if (PRINTER_USE_FULLPAGE == 1) + p.translate(d->mp_left, d->mp_top); +#endif + for(int i = fromPage; i < toPage; i++) { +#ifndef QT_NO_DEBUG + qDebug() << "printing page " << i << " ..."; +#endif + if (i != fromPage) d->printer->newPage(); + paintPage(&p, i, QRect( QPoint(0,0), d->pageSize)); + } + p.end(); +#ifdef DEBUG_PRINT_TIME + gettimeofday(&tpe, NULL); +#endif + +#ifdef PRINTER_CHANGE_DEVICE + changeDevice(d->preview); +#ifdef DEBUG_PRINT_TIME + qDebug() << "Setup time: " << QString(" %1 s").arg(tpb.tv_sec - tsb.tv_sec + (tpb.tv_usec - tsb.tv_usec)/1000000.0,0,'f',4); +#endif +#endif + +#ifdef DEBUG_PRINT_TIME + qDebug() << "Pages time: " << QString(" %1 s").arg(tpe.tv_sec - tpb.tv_sec + (tpe.tv_usec - tpb.tv_usec)/1000000.0,0,'f',4); +#endif +} +//-------------------------------------------------------------- +void AbstractPreview::repaintPreview() +{ + d->flushCache(); + updatePreview(); +} +//-------------------------------------------------------------- +void AbstractPreview::repaintPage(int page) +{ + d->flushPage(page); + updatePreview(); +} +//-------------------------------------------------------------- +void AbstractPreview::scaleIn() +{ + if(d->scale >= d->scaleMax)return; + setScale( d->scale * d->scaleStep ); +} +//-------------------------------------------------------------- +void AbstractPreview::scaleOut() +{ + if(d->scale <= d->scaleMin)return; + setScale( d->scale / d->scaleStep ); +} +//-------------------------------------------------------------- +void AbstractPreview::scaleOrig() { setScale(1.0); } + +void AbstractPreview::setScale(double z) +{ + if(d->scale == z) return; + qSwap(d->scale, z); + + d->changeScale(); + sizeToCountPage(); + d->calcParamDraw(); + updatePreview(); + + scaleEvent(z, d->scale); +} +//-------------------------------------------------------------- +void AbstractPreview::setScaleStep(double step) +{ + d->scaleStep = step; +} +//-------------------------------------------------------------- +double AbstractPreview::scaleStep() +{ + return d->scaleStep; +} +//-------------------------------------------------------------- +void AbstractPreview::setScaleRange(double zmin, double zmax) +{ + if (zmin > zmax) { qSwap(zmin,zmax); } + d->scaleMin = zmin; + d->scaleMax = zmax; + if(d->scale < zmin) { + setScale(zmin); + return; + } + if(d->scale > zmax) { + setScale(zmax); + return; + } +} +//-------------------------------------------------------------- +void AbstractPreview::setScaleMaximum(double zmax) +{ + if (zmax < d->scaleMin) zmax = d->scaleMin; + d->scaleMax = zmax; + if(d->scale > zmax) setScale(zmax); +} +//-------------------------------------------------------------- +void AbstractPreview::setScaleMinimum(double zmin) +{ + if (zmin > d->scaleMax) zmin = d->scaleMax; + d->scaleMin = zmin; + if(d->scale < zmin) setScale(zmin); +} +//-------------------------------------------------------------- +double AbstractPreview::scaleMaximum() +{ + return d->scaleMax; +} +//-------------------------------------------------------------- +double AbstractPreview::scaleMinimum() +{ + return d->scaleMin; +} +//-------------------------------------------------------------- +void AbstractPreview::scaleEvent(const double &scaleOld, const double &scaleNew) +{ + Q_UNUSED(scaleOld); + emit scaleChanged(scaleNew); +} +//-------------------------------------------------------------- +void AbstractPreview::gotoPage(int index) +{ + int row = index / d->cols; + int y = row * (d->paperHeightToScale() + d->interPageSpacing + d->pageLabelHeight); + int y2 = height() - d->paperHeightToScale() - 2*d->pageLabelHeight; + + if (d->viewMode == Mode_Thumbs) { + d->selectPageIndex = index; + updatePreview(); + if (y<=verticalScrollBar()->value()) { + verticalScrollBar()->setValue(y); + } else if (y-y2 > verticalScrollBar()->value()) { + verticalScrollBar()->setValue(y-y2); + } + } else { + verticalScrollBar()->setValue(y); + } +} +//-------------------------------------------------------------- +QSize AbstractPreview::paperSize() +{ + return d->paperSize; +} +//-------------------------------------------------------------- +void AbstractPreview::clear() +{ + d->clear(); + clearEvent(); + + d->changeScale(); + sizeToCountPage(); + d->calcParamDraw(); + updatePreview(); +} +//-------------------------------------------------------------- +void AbstractPreview::setupPageFormat() +{ +#ifndef QT_NO_DEBUG + qDebug("setupPageFormat()"); +#endif + d->setup(); + if (d->viewMode == Mode_Thumbs) { + autoscaleThumbs(); + } + updatePageFormat(); + d->changeScale(); + sizeToCountPage(); + d->calcParamDraw(); + updatePreview(); +} +//-------------------------------------------------------------- +int AbstractPreview::currentPage() +{ + return d->selectPageIndex; +} +//-------------------------------------------------------------- +qreal AbstractPreview::scale() +{ + return d->scale; +} +//-------------------------------------------------------------- +QPixmap *AbstractPreview::grabPage(int page, const QRectF &rect) +{ + if(!(page >= 0 && page < pageCount()))return NULL; + return NULL; +} +//-------------------------------------------------------------- +QPixmap *AbstractPreview::grabPage(int page, qreal left, qreal top, + qreal width, qreal height) +{ + if(!(page >= 0 && page < pageCount()))return NULL; + return NULL; +} +//-------------------------------------------------------------- +void AbstractPreview::setMouseTrackingPage(bool mtp) +{ + d->mouseTrackingPage = mtp; + setMouseTracking(d->mouseTrackingPage); +} +//-------------------------------------------------------------- +bool AbstractPreview::isMouseTrackingPage() { return d->mouseTrackingPage; } +QPageInfo AbstractPreview::enterPageInfo() { return d->enterPageInfo; } +void AbstractPreview::setMouseEnterLeavePage(bool melp) { d->mouseEnterLeavePage = melp; } +bool AbstractPreview::mouseEnterLeavePage() { return d->mouseEnterLeavePage; } +//-------------------------------------------------------------- +void AbstractPreview::setMovedContents(bool move) { d->movedContents = move; } +bool AbstractPreview::movedContents() { return d->movedContents; } +//-------------------------------------------------------------- diff --git a/gui/src/colorlabel.cpp b/gui/src/colorlabel.cpp new file mode 100644 index 0000000..326c4a0 --- /dev/null +++ b/gui/src/colorlabel.cpp @@ -0,0 +1,65 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include + +#include "colorlabel.h" +#include + +ColorLabel::ColorLabel(QColor icol, QString itext, bool ieditable, QWidget *p, Qt::WindowFlags f) + : QWidget(p,f) +{ + col = icol; + text = itext; + editable = ieditable; + setMinimumSize(40,20); +} + +ColorLabel::~ColorLabel() +{ +} + +void ColorLabel::mousePressEvent(QMouseEvent* e) +{ + if (!editable) return; +#ifndef QT_NO_DEBUG + qDebug() << "x: " << e->x() << " y: " << e->y(); +#endif +// if (e->x() < height() && e->x() > 1 && e->y() > 1 && e->y() < height()) { + if (e->button() == Qt::LeftButton && e->x() < (height()+2)) { + col = QColorDialog::getRgba(col.rgba(), NULL, this); + update(); + } +} + +void ColorLabel::paintEvent(QPaintEvent*) +{ +//#ifndef QT_NO_DEBUG +// wDebug("ColorLabel::paintEvent()"); +//#endif + QRect rect(1, 1, height()-2, height()-2); + QRect trect(height()+4, 1, width()-height()-6, height()-2); + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing, true); + p.setRenderHint(QPainter::TextAntialiasing, true); + + p.setPen( QPen(col, 2)); + p.fillRect(rect.adjusted(1,1,-1,-1), QBrush(col) ); + + p.setPen( QPen(QColor(Qt::black), 2)); + p.drawRoundRect(rect, 20, 20); + + p.drawText(trect, Qt::AlignLeft | Qt::AlignVCenter, text); + //p.drawRect(rect); +} + diff --git a/gui/src/db_connection.cpp b/gui/src/db_connection.cpp new file mode 100644 index 0000000..55b6595 --- /dev/null +++ b/gui/src/db_connection.cpp @@ -0,0 +1,109 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2010 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#include + +DBParams::DBParams() +{ + driver = "QPSQL"; + host = "localhost"; + port = 5432; + conn_name = "new connection"; +}; + +DBParams::DBParams(QString idriver, + QString ihost, + QString iname, + QString iuser, + QString ipass, + int iport + ) +{ + driver = idriver; + host = ihost; + name = iname; + user = iuser; + pass = ipass; + port = iport; + conn_name = ""; +}; + +bool SqlProbeConnection(const DBParams& par, const QString& CONNECTION) +{ + QSqlDatabase Sql_DB; + QString r; + if (SqlOpenConnection(par, CONNECTION)) { + SqlCloseConnection(CONNECTION); + return 1; + } else { + return 0; + } +} + +bool SqlOpenConnection(const DBParams& par, const QString& CONNECTION) +{ +#ifndef QT_NO_DEBUG + qDebug("Connect..."); +#endif + QSqlDatabase db; + if (CONNECTION.isEmpty()) { + db = QSqlDatabase::addDatabase(par.driver); + } else { + db = QSqlDatabase::addDatabase(par.driver, CONNECTION); + } + db.setHostName( par.host ); + db.setDatabaseName( par.name ); + db.setUserName( par.user ); + db.setPassword( par.pass ); + db.setPort( par.port ); + if ( !db.open() ) { +#ifndef QT_NO_DEBUG + qDebug() << "Failed to open database: (" << par.driver << ")" << par.name << "@" << par.host << ":"; + qDebug() << db.lastError().driverText(); + qDebug() << db.lastError().databaseText(); +#endif + QMessageBox::critical(0, + "Error", + db.lastError().text()); + return 0; + } + return 1; +} + +void SqlCloseConnection(const QString& CONNECTION) +{ +#ifndef QT_NO_DEBUG + qDebug( "Disconnecting..." ); +#endif + { + QSqlDatabase db; + if (CONNECTION.isEmpty()) { + db = QSqlDatabase::database(); + } else { + db = QSqlDatabase::database(CONNECTION); + } + if (db.isOpen()) db.close(); + } +// Sql_DB.removeDatabase(CONNECTION); + if (!CONNECTION.isEmpty()) QSqlDatabase::removeDatabase(CONNECTION); +} + diff --git a/gui/src/db_report_selection.cpp b/gui/src/db_report_selection.cpp new file mode 100644 index 0000000..dfa1eb7 --- /dev/null +++ b/gui/src/db_report_selection.cpp @@ -0,0 +1,423 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2010 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "db_report_selection.h" + +#include +#include +#include +#include + +#include + +int ListModel::rowCount(const QModelIndex& p) const +{ +// qDebug() << "ListModel::rowCount() "<< tdata.size(); + if (!p.isValid()) return tdata.size(); + return 0; +} + +int ListModel::columnCount(const QModelIndex& p) const +{ +// qDebug() << "ListModel::columnCount() "<< cols; + return 1; +} + +QVariant ListModel::data(const QModelIndex& p, int role) const +{ + int r = p.row(); + if (p.column()) return QVariant(); +// qDebug() << "ListModel::data("<< r << ","<< c << ")" << role; + if (r<0 || r >= tdata.size() ) return QVariant(); + switch (role) { + case Qt::DisplayRole: + case Qt::ToolTipRole: + case Qt::EditRole: + return QVariant(tdata[r]); + case Qt::DecorationRole: + return QVariant(idata[r]); +// case Qt::FontRole: +// case Qt::TextAlignMentRole: +// case Qt::CheckStateRole: + default: + break; + } + return QVariant(); +} + +void ListModel::clear() { + tdata.clear(); + idata.clear(); + keys.clear(); +} + +void ListModel::addRow(int key, QString text, QIcon icon) +{ + tdata << text; + idata << icon; + keys << key; +} + +bool ListModel::setData(int row, int role, QVariant d) +{ + if (row<0 || row>=tdata.size()) return false; + + switch (role) { + case Qt::DisplayRole: + case Qt::ToolTipRole: + case Qt::EditRole: + tdata[row] = d.toString(); + return true; + case Qt::DecorationRole: + idata[row] = d.value(); + return true; + default: + break; + } + return false; +} + + +void ListModel::removeRow(int row) +{ + if (row<0 || row>=tdata.size()) return; + tdata.removeAt(row); + idata.removeAt(row); + keys.removeAt(row); +} + +int ListModel::getKey(int row) +{ + if (keys.size() <= row) return -1; + return keys[row]; +} + +//******************* +// dialog +//******************* + + +DbReportSelection::DbReportSelection(QString conn, QWidget *parent, Qt::WindowFlags fl) + :QDialog(parent, fl) +{ + report_id = -1; + model = new ListModel(this); + db = QSqlDatabase::database(conn); + winit(); +} + +DbReportSelection::~DbReportSelection() +{ + +} + +void DbReportSelection::winit() +{ + setWindowTitle(tr("Select report...")); + setMinimumWidth(400); + + layout = new QVBoxLayout(this); + layout->setMargin(3); + layout->setSpacing(3); + + layout_l = new QHBoxLayout(); + layout_l->setMargin(0); + layout_l->setSpacing(3); + layout->addLayout(layout_l); +// search filter + llabel = new QLabel(tr("Label:"),this); + layout_l->addWidget(llabel); + + elabel = new QLineEdit(this); + elabel->setMaxLength(128); + elabel->setMinimumHeight(22); + elabel_clear = new QToolButton(elabel); + elabel_clear->setCursor(Qt::ArrowCursor); + elabel_clear->setIcon(QIcon(":images/edit-clear.png")); + elabel_clear->setStyleSheet("QToolButton { border: none; padding: 0px; } " + "QToolButton{" + "border:none;" + "}" + "QToolButton:pressed{" + "background-color:none;" + "}"); + + elabel_search = new QToolButton(this); +// elabel_search->setCursor(Qt::ArrowCursor); + elabel_search->setIcon(QIcon(":images/search.png")); +/* + elabel_search->setStyleSheet("QToolButton { border: none; padding: 0px; } " + "QToolButton{" + "border:none;" + "}" + "QToolButton:pressed{" + "background-color:none;" + "}"); +*/ + elabel->installEventFilter(this); + layout_l->addWidget(elabel); + layout_l->addWidget(elabel_search); + +// search results + list = new QListView(this); + layout->addWidget(list); + + info = new QTextBrowser(this); + info->setMaximumHeight(150); + layout->addWidget(info); + +// buttons + layout_pb = new QHBoxLayout(); + layout_pb->setMargin(0); + layout_pb->setSpacing(3); + layout->addLayout(layout_pb); + + layout_pb->addStretch(10); + pb_load = new QPushButton(QIcon(":images/ok.png"), tr("Load"), this); + pb_load->setEnabled(false); + layout_pb->addWidget(pb_load); + pb_cancel = new QPushButton(QIcon(":images/x.png"), tr("Cancel"), this); + layout_pb->addWidget(pb_cancel); + + + connect(elabel_clear, SIGNAL(clicked()), elabel, SLOT(clear())); + connect(elabel_search, SIGNAL(clicked()), this, SLOT(search())); + + connect(list, SIGNAL(clicked(const QModelIndex)), this, SLOT(itemActivated(const QModelIndex))); + + connect(pb_load, SIGNAL(clicked()), this, SLOT(load())); + connect(pb_cancel, SIGNAL(clicked()), this, SLOT(reject())); + + search(); +} + +void DbReportSelection::search() +{ + QSqlQuery *q; + QIcon icon; + + pb_load->setEnabled(false); + list->setModel(NULL); + model->clear(); + + q = new QSqlQuery(QSqlDatabase::database("reports")); +// qDebug() << "Label pattern:" << elabel->text(); + q->prepare("SELECT \ + reports.id,id_media_type,label,copy_idx,datetime \ + FROM \ + reports \ + WHERE \ + label ILIKE '%"+elabel->text()+"%' \ + ORDER BY datetime"); + if (!q->exec()) { + qDebug() << q->lastError().text(); + goto err; + } + while (q->next()) { + icon = QPixmap::fromImage(getMediaLogo(q->value(1).toULongLong())); + if (!q->value(3).toInt()) + model->addRow( q->value(0).toInt(), q->value(2).toString(), icon); + else + model->addRow( q->value(0).toInt(), q->value(2).toString() + ", "+ tr("copy") + " #" + q->value(3).toString(), icon); + } + list->setModel(model); + +err: + delete q; +} + +void DbReportSelection::load() +{ + accept(); +} + +void DbReportSelection::itemActivated(const QModelIndex idx) +{ + QSqlQuery *q; + QString html; + QString md5; + report_id = model->getKey(idx.row()); + pb_load->setEnabled(true); + + q = new QSqlQuery(QSqlDatabase::database("reports")); + q->prepare("SELECT \ + label,id_media_type,media_types.name, \ + dev_vendors.name,dev_models.name,dev_fw,datetime, \ + copy_idx,md5,metadata \ + FROM \ + reports,media_types,dev_vendors,dev_models \ + WHERE \ + media_types.id=id_media_type AND \ + dev_models.id=dev_id AND \ + dev_vendors.id=id_vendor AND \ + reports.id=:id"); + q->bindValue(":id",report_id); + + if (!q->exec() || !q->next()) { + qDebug() << q->lastError().text(); + goto err; + } + + info->document()->addResource( QTextDocument::ImageResource, + QUrl("images//:media_logo.png"), + getMediaLogo(q->value(1).toULongLong()).scaled( QSize(64,64), Qt::KeepAspectRatio, Qt::SmoothTransformation) ); + md5 = q->value(8).toString().simplified(); + + html = ""; + html+= ""; + html+= ""; + + html+= ""; + html+= ""; + html+=q->value(9).toString(); + html+= "

"+q->value(0).toString()+"
"; + html+= q->value(2).toString()+", "+tr("copy")+"# "+q->value(7).toString()+"
"; + if (!md5.isEmpty()) { + html+= "MD5: " + md5; + } + html+= "
"+tr("Device")+"" + +q->value(3).toString() + +q->value(4).toString() + +q->value(5).toString() + +"
"+tr("Date")+"" + +q->value(6).toDateTime().toString("yyyy.MM.dd hh:mm") + +"
"; + + info->setHtml(html); +err: + delete q; +} + +bool DbReportSelection::eventFilter(QObject *o, QEvent *e) +{ + if (o == elabel) { + if (e->type() == QEvent::Resize) { + int frameWidth = elabel->style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + elabel_clear->resize(elabel->height() - (frameWidth<<1), elabel->height() - (frameWidth<<1)); + elabel_clear->move(elabel->width() - elabel_clear->width() - frameWidth, frameWidth); +/* + elabel_search->resize(elabel->height() - (frameWidth<<1), elabel->height() - (frameWidth<<1)); + elabel_search->move(elabel->width() - elabel_clear->width() - frameWidth, frameWidth); + elabel_clear->resize(elabel->height() - (frameWidth<<1), elabel->height() - (frameWidth<<1)); + elabel_clear->move(elabel->width() - elabel_clear->width() - elabel_search->width() - frameWidth - 10, frameWidth); + elabel->setStyleSheet(QString("QLineEdit { padding-right: %1px; }").arg(elabel_clear->width()+elabel_search->width()+10)); +*/ + elabel->setStyleSheet(QString("QLineEdit { padding-right: %1px; }").arg(elabel_clear->width()+1)); + return true; + } else { + return false; + } + } else { + return QDialog::eventFilter(o,e); + } +} + +QImage DbReportSelection::getMediaLogo(quint64 id) +{ + QImage logo; + switch (id) { +// CD media + case DISC_CDROM: + logo = QImage(":images/media/cd.png"); + break; + case DISC_CDR: + logo = QImage(":images/media/cd_r.png"); + break; + case DISC_CDRW: + logo = QImage(":images/media/cd_rw.png"); + break; +// DVD media + case DISC_DVDROM: + logo = QImage(":images/media/dvd-rom.png"); + break; + case DISC_DVDRAM: + logo = QImage(":images/media/dvd-ram.png"); + break; +// DVD- media + case DISC_DVDmR: + logo = QImage(":images/media/dvd-r.png"); + break; + case DISC_DVDmRW: + case DISC_DVDmRWS: + case DISC_DVDmRWR: + logo = QImage(":images/media/dvd-rw.png"); + break; + case DISC_DVDmRDL: + case DISC_DVDmRDLJ: + logo = QImage(":images/media/dvd-r_dl.png"); + break; + case DISC_DVDmRWDL: + logo = QImage(":images/media/dvd-rw_dl.png"); + break; + +// DVD+ media + case DISC_DVDpR: + logo = QImage(":images/media/dvd+r.png"); + break; + case DISC_DVDpRDL: + logo = QImage(":images/media/dvd+r_dl.png"); + break; + case DISC_DVDpRW: + logo = QImage(":images/media/dvd+rw.png"); + break; + case DISC_DVDpRWDL: + logo = QImage(":images/media/dvd+rw_dl.png"); + break; + + +// DD-CD media + case DISC_DDCD_ROM: + case DISC_DDCD_R: + case DISC_DDCD_RW: + logo = QImage(":images/media/ddcd.png"); + break; + +// Blu-Ray media + case DISC_BD_ROM: + case DISC_BD_R_SEQ: + case DISC_BD_R_RND: + case DISC_BD_RE: + logo = QImage(":images/media/bd.png"); + break; + +// HD DVD media + case DISC_HDDVD_ROM: + logo = QImage(":images/media/hddvd-rom.png"); + break; + case DISC_HDDVD_R: + case DISC_HDDVD_RDL: + logo = QImage(":images/media/hddvd-r.png"); + break; + case DISC_HDDVD_RAM: + logo = QImage(":images/media/hddvd-ram.png"); + break; + case DISC_HDDVD_RW: + case DISC_HDDVD_RWDL: + logo = QImage(":images/media/hddvd-rw.png"); + break; + default: + logo = QImage(); + } + return logo; +} + diff --git a/gui/src/device.cpp b/gui/src/device.cpp new file mode 100644 index 0000000..3a68b2f --- /dev/null +++ b/gui/src/device.cpp @@ -0,0 +1,2162 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include "device.h" + +#include +#include +#include + +#ifndef QT_NO_DEBUG +#include +static int devcnt=0; +#endif + +#if defined(_WIN32) +#include +//#if !defined(__MINGW32__) +//#endif +#endif + +void ErrcADD(Errc *tot, const Errc& o) { + for (int i=0; i<8; i++) + if (o.raw.err[i] > 0) tot->raw.err[i] += o.raw.err[i]; +}; + +void ErrcMAX(Errc *max, const Errc& o) { + for (int i=0; i<8; i++) + if (max->raw.err[i] < o.raw.err[i]) max->raw.err[i] = o.raw.err[i]; +}; + +void CDErrcAVG(Errc *avg, Errc *tot, uint64_t blocks) { + if (!blocks) blocks=1; + for (int i=0; i<8; i++) + avg->raw.err[i] = (float) tot->raw.err[i] / blocks; +}; + +void DVDErrcAVG(Errc *avg, Errc *tot, uint64_t blocks) { + avg->dvd.pie = (float) tot->dvd.pie / blocks; + avg->dvd.pif = (float) tot->dvd.pif / blocks; + avg->dvd.poe = (float) tot->dvd.poe / blocks; + avg->dvd.pof = (float) tot->dvd.pof / blocks; + avg->dvd.uncr = (float) tot->dvd.uncr / blocks; + if (blocks >= 8) { + avg->dvd.pi8 = (float) tot->dvd.pi8 / ( blocks >> 3); + avg->dvd.po8 = (float) tot->dvd.po8 / ( blocks >> 3); + } +}; + +void BDErrcAVG(Errc *avg, Errc *tot, uint64_t blocks) { + avg->bd.ldc = (float) tot->bd.ldc / blocks; + avg->bd.bis = (float) tot->bd.bis / blocks; + avg->bd.uncr = (float) tot->bd.uncr / blocks; +}; + +static device NullDev(NULL); + +/* + * + * Device List + * + */ + +devlist::devlist() + : QList() +{ +#ifndef QT_NO_DEBUG + qDebug("* STA: devlist()"); +#endif + devidx=-1; +#ifndef QT_NO_DEBUG + qDebug("* END: devlist()"); +#endif +}; + +devlist::~devlist() { +#ifndef QT_NO_DEBUG + qDebug("* STA: ~devlist()"); +#endif + clear(); +#ifndef QT_NO_DEBUG + qDebug("* END: ~devlist()"); +#endif +}; + +int devlist::idx() { return devidx; }; + +void devlist::setIdx(int iidx) { + if (iidx<0 || iidx>=size()) { + devidx=-1; + return; + } + devidx=iidx; +}; + +void devlist::clear() { + device *dev; + while (size()) { + dev = takeLast(); + dev->stopWatcher(); + delete dev; + } + devidx=-1; +}; + +device* devlist::current() { + if (devidx<0 || devidx>=size()) return &NullDev; + return (*this)[devidx]; +}; + +/* + * + * Device + * + */ + +device::device(QObject* p) + : QObject(p) +{ +#ifndef QT_NO_DEBUG + qDebug() << "* STA: device(): " << this << " #"<< devcnt++ << " parent: "<< p; +#endif + preserveMediaInfo=0; + running=0; + type = DevtypeNone; + host = ""; + port = 0; + info_set = 0; + + rpc_phase = -1; + rpc_reg = -1; + rpc_ch = -1; + rpc_rst = -1; + + plextor_lock = 0; + + asdb.clear(); + clearMinfo(); + + cap = 0; + cap_rd = 0; + cap_wr = 0; + + life_dn = -1; + features.supported = 0; + features.enabled = 0; + features.grec = 1.0; + features.vrec_cd_pwr = 0; + features.vrec_cd_str = 0; + features.vrec_dvd_pwr = 0; + features.vrec_dvd_str = 0; + features.tattoo_inner = 22; + features.tattoo_outer = 54; + + features.sm_cd_rd = 0; + features.sm_cd_wr = 0; + features.sm_dvd_rd = 0; + features.psm_cd_rd = 0; + features.psm_cd_wr = 0; + features.psm_dvd_rd = 0; + + features.as_mode = AS_MODE_AUTO; + features.pioq_quiet = PIOQ_STD; + + autoupdate = 0; + + pprocess = 0.0; + nprocess = ""; + + test_cap=0; + test_req=0; + tests = 0; + ctest = 0; + WT_simul = 1; + + tspeeds.rt = 1; + tspeeds.wt = 1; + tspeeds.errc = 1; + tspeeds.jb = 1; + tspeeds.ft = 1; + + io = new QPxIODevice(this); + proc = NULL; + sock = NULL; + mwatcher = NULL; + mutex = new QMutex(); + io_mutex = new QMutex(); + + resReader = new ResultsReader(this); + resWriter = new ResultsWriter(this); + +if (!p) { +#ifndef QT_NO_DEBUG + qDebug() << "device: NULL parent!"; +#endif + } else { + connect(this, SIGNAL(doneMInfo(int)), p, SLOT(mediaUpdated(int))); + connect(this, SIGNAL(process_started()), p, SLOT(process_started())); + connect(this, SIGNAL(process_finished()), p, SLOT(process_finished())); + connect(this, SIGNAL(process_progress()), p, SLOT(process_progress())); + } + connect(resReader, SIGNAL(finished()), this, SLOT(resLoaderDone())); + +#ifndef QT_NO_DEBUG + qDebug("* END: device()"); +#endif +}; + +device::~device() +{ +#ifndef QT_NO_DEBUG + qDebug() << "* STA: ~device(): " << this << " #"<< --devcnt; +#endif + stopWatcher(); + if (mwatcher) { + delete mwatcher; + } + QTreeWidgetItem* item; +#ifdef MINFO_TREE + while (info_media.size()) { + item = info_media.takeLast(); + if (item) delete item; + } +/* + if (running) { + qDebug() << "device: waiting for child process..."; + while (running) { + msleep(100); + } + } +*/ +#endif +// mutex->unlock(); + delete mutex; +#ifndef QT_NO_DEBUG + qDebug() << "* END: ~device()"; +#endif +}; + +bool device::isRunning() +{ + return running; +}; + +void device::clearMinfo() +{ + QTreeWidgetItem* item; + + testData.clear(); + +// media.isCD = 0; +// media.isDVD = 0; + media.label = ""; + media.type = "-"; + media.category = "-"; + media.mid = "-"; + media.erasable = "-"; + media.layers = "-"; + media.ilayers = 1; + media.prot = "-"; + media.regions = "-"; + media.creads = 0; + media.creadm = 0; + media.creadmsf = ""; + media.cfrees = 0; + media.cfreem = 0; + media.cfreemsf = ""; + media.ctots = 0; + media.ctotm = 0; + media.ctotmsf = ""; + media.dstate = "-"; + media.sstate = "-"; + media.writer = "-"; + media.grec = 0.0; + media.rspeeds.clear(); + media.wspeedsd.clear(); + media.wspeedsm.clear(); + + media.tdata_errc = 0; + media.tspeeds_errc.clear(); + media.tspeeds_jb.clear(); +#ifdef MINFO_TREE + while (info_media.size()) { + item = info_media.takeLast(); + if (item) delete item; + } +#endif +}; + +bool device::start() +{ +#ifndef QT_NO_DEBUG + qDebug() << "STA: device::start(" << threadType << ")"; +#endif + if (!mutex->tryLock()) { +#ifndef QT_NO_DEBUG + qDebug() << "Device busy: " << id; + qDebug() << "END: device::start()"; +#endif + return false; + } + stop=0; + running=1; + switch (threadType) { + case threadDevice: + case threadMedia: + case threadGetFeatures: + case threadGetASDB: + case threadMQCK: + case threadAScre: + case threadDestruct: + case threadTattoo: + start_update_info(); + break; + case threadTest: + next_test(); + break; + default: + mutex->unlock(); +#ifndef QT_NO_DEBUG + qDebug() << "END: device::start()"; +#endif + running=0; + return false; + } + +#ifndef QT_NO_DEBUG + qDebug() << "END: device::start()"; +#endif + return true; +} + +bool device::start_update_info() +{ +#ifndef QT_NO_DEBUG + qDebug() << "STA: device::start_update_info(" << threadType << ")"; +#endif + if (threadType==threadMedia && !preserveMediaInfo) { + clearMinfo(); +// mwidget->update(); +#warning !!! mwidget->clearMedia() call + //mwidget->clearMedia(); + } + + //if (devices.idx()<0 || devices.idx()>= devices.size() || (threadType!=infoDevice && threadType!=infoMedia)) { + if (type == DevtypeNone) { + goto update_info_err; + } +// if (threadType == infoDevice) { +// +// } + if (proc) { + delete proc; + proc = NULL; + } + if (sock) { + delete sock; + sock = NULL; + } + + QObject::connect(io, SIGNAL(readyReadLine()), + this, SLOT(qscan_process_info())); + + if (type == DevtypeLocal) { +#ifndef QT_NO_DEBUG + qDebug() << "device: LOCAL"; +#endif + proc = new QProcess(this); +#ifndef QT_NO_DEBUG + qDebug() << "process created"; +#endif + io->setIODevice(proc); + proc->setReadChannel(QProcess::StandardOutput); + +// QObject::connect(proc, SIGNAL(readyReadStandardOutput()), +// this, SLOT(qscan_process_info())); + + switch (threadType) { + case threadDevice: + proc->start("qscan", QStringList() << "-d" << path << "-Ip"); + break; + case threadMedia: + if (plugin.isEmpty()) { + proc->start("qscan", QStringList() << "-d" << path << "-m"); + } else { + proc->start("qscan", QStringList() << "-d" << path << "--force-plugin" << plugin << "-m"); + } + break; + case threadGetFeatures: + proc->start("cdvdcontrol", QStringList() << "-d" << path << "-c"); + break; + case threadGetASDB: + proc->start("cdvdcontrol", QStringList() << "-d" << path << "--as-list"); + break; + case threadMQCK: + { + QStringList cdvdopts; + cdvdopts << "-d" << path; + cdvdopts << "--mqck" << ((features.as_act_mode & ASMQCK_ADV) ? "advanced" : "quick"); + cdvdopts << "--mqck-speed" << QString::number(features.as_mqckspd); + +#ifndef QT_NO_DEBUG + qDebug() << cdvdopts; +#endif + proc->start("cdvdcontrol", cdvdopts ); + } + break; + case threadAScre: + { + QStringList cdvdopts; + cdvdopts << "-d" << path << "--as-create"; + cdvdopts << ((features.as_act_mode & ASCRE_FULL) ? "f" : "q"); + cdvdopts << ((features.as_act_mode & ASCRE_REPLACE) ? "r" : "a"); + +#ifndef QT_NO_DEBUG + qDebug() << cdvdopts; +#endif + proc->start("cdvdcontrol", cdvdopts ); + } + break; + case threadDestruct: + proc->start("cdvdcontrol", QStringList() << "-d" << path << "--destruct" << (features.as_act_mode ? "full" : "quick")); + break; + case threadTattoo: + proc->start("f1tattoo", QStringList() << "-d" << path << "--tattoo-raw" << features.tattoo_file); + break; + default: + goto update_info_err; + } + if (!proc->waitForStarted(10000)) { +#ifndef QT_NO_DEBUG + qDebug("Can't start qscan!"); +#endif + goto update_info_err; + } + QObject::connect(proc, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(qscan_callback_info())); +#ifndef QT_NO_DEBUG + qDebug("qscan (local) started"); +#endif + goto update_info_end; + } else if (type == device::DevtypeTCP) { + if ((threadType != threadDevice) && (threadType != threadMedia)) + goto update_info_err; + +#ifndef QT_NO_DEBUG + qDebug("device: TCP"); +#endif + sock = new QTcpSocket(this); +#ifndef QT_NO_DEBUG + qDebug("socket created"); +#endif + io->setIODevice(sock); + +// QObject::connect(sock, SIGNAL(readyRead()), +// this, SLOT(qscan_process_info())); + + sock->connectToHost(host, port); + if (!sock->waitForConnected(5000)) { +#ifndef QT_NO_DEBUG + qDebug("Unable to connect to host!"); +#endif + goto update_info_err; + } + + sock->write("set dev=" + path.toLatin1() + "\n"); + switch (threadType) { + case threadDevice: + sock->write("dinfo\n"); + sock->write("close\n"); + break; + case threadMedia: + sock->write("minfo\n"); + sock->write("close\n"); + break; + default: + goto update_info_err; + } + QObject::connect(sock, SIGNAL(disconnected()), + this, SLOT(qscan_callback_info())); +#ifndef QT_NO_DEBUG + qDebug("qscan (TCP) started"); +#endif + goto update_info_end; + } + +update_info_err: + qscan_callback_info(); +#ifndef QT_NO_DEBUG + qDebug() << "END: device::start_update_info(" << threadType << ")"; +#endif + return false; +update_info_end: +#ifndef QT_NO_DEBUG + qDebug() << "END: device::start_update_info(" << threadType << ")"; +#endif + return true; +} + +void device::qscan_callback_info() +{ + int xcode = 0; + ThreadType ttype = threadType; +#ifndef QT_NO_DEBUG + qDebug() << "STA: device::qscan_callback_info() " << this; +#endif + io_mutex->lock(); + + QObject::disconnect(io, SIGNAL(readyReadLine()), + this, SLOT(qscan_process_info())); + + if (type == DevtypeLocal) { +// QObject::disconnect(proc, SIGNAL(finished(int, QProcess::ExitStatus)), +// this, SLOT(qscan_callback_info())); + xcode = proc->exitCode(); +#ifndef QT_NO_DEBUG + qDebug() << "qscan (local) finished: " << xcode; +#endif + + disconnect(proc); + io->setIODevice(NULL); + } else if (type == device::DevtypeTCP) { +// QObject::disconnect(sock, SIGNAL(disconnected()), +// this, SLOT(qscan_callback_info())); + sock->disconnectFromHost(); +#ifndef QT_NO_DEBUG + qDebug("qscan (TCP) finished"); +#endif + disconnect(sock); + io->setIODevice(NULL); + } + io_mutex->unlock(); + threadType = threadNone; + running=0; + mutex->unlock(); + + nprocess = ""; + emit process_finished(); + + switch (ttype) { + case threadDevice: + emit doneDInfo(xcode); + break; + case threadMedia: + emit doneMInfo(xcode); + break; + case threadGetFeatures: + emit doneGetFeatures(xcode); + break; + case threadGetASDB: + emit doneGetASDB(xcode); + break; + default: + break; + } + +#ifndef QT_NO_DEBUG + qDebug("END: device::qscan_callback_info()"); +#endif +} + +bool device::update_device_info() +{ + if (running) return false; + plugin_names.clear(); + plugin_infos.clear(); + threadType = threadDevice; + return start(); +} + +bool device::update_media_info() +{ +#ifndef QT_NO_DEBUG + qDebug("device::update_media_info()"); +#endif + if (running) return false; + preserveMediaInfo = false; + threadType = threadMedia; + nprocess = tr("Updating media info..."); + emit process_started(); + return start(); +} + +void device::clear_media_info() +{ +#ifndef QT_NO_DEBUG + qDebug("device::clear_media_info()"); +#endif + clearMinfo(); + emit doneMInfo(0); +} + +bool device::update_plugin_info() +{ + if (running) return false; + preserveMediaInfo = true; + threadType = threadMedia; + return start(); +} + +bool device::getFeatures() +{ + if (running) return false; + threadType = threadGetFeatures; + return start(); +} + +bool device::getASDB() +{ + if (running) return false; + threadType = threadGetASDB; + asdb.clear(); + return start(); +} + +bool device::startMqck() +{ + if (running) return false; + threadType = threadMQCK; + return start(); +} + +bool device::startAScre() +{ + if (running) return false; + threadType = threadAScre; + return start(); +} + +bool device::startDestruct() +{ + if (running) return false; + threadType = threadDestruct; + return start(); +} + +bool device::startTattoo() +{ + if (running) return false; + threadType = threadTattoo; + return start(); +} + +bool device::setFeature(int f, bool en) +{ + int r=1; +#ifndef QT_NO_DEBUG + qDebug() << "device::setFeature: " << f << en; +#endif + if (!mutex->tryLock()) { +#ifndef QT_NO_DEBUG + qDebug() << "Device busy: " << id; +#endif + return false; + } + + if (type == DevtypeLocal) { + QStringList cdvdopts; + + pauseWatcher(); + + cdvdopts << "-d" << path; + switch(f) { + case FEATURE_LOEJ: + if (en) { + cdvdopts << "--load" << "--loej-immed"; + } else { + cdvdopts << "--eject" << "--loej-immed"; + } + break; + case FEATURE_LOEJ_TOGGLE: + cdvdopts << "--loej" << "--loej-immed"; + break; + case FEATURE_LOCK: + if (en) { + cdvdopts << "--lock"; + } else { + cdvdopts << "--unlock"; + } + break; + case FEATURE_LOCK_TOGGLE: + cdvdopts << "--lockt"; + break; + case FEATURE_POWEREC: + cdvdopts << "--powerec"; + cdvdopts << (en ? "on" : "off"); + break; + case FEATURE_HIDECDR: + cdvdopts << "--hcdr"; + cdvdopts << (en ? "on" : "off"); + break; + case FEATURE_SINGLESESSION: + cdvdopts << "--sss"; + cdvdopts << (en ? "on" : "off"); + break; + case FEATURE_SPEEDREAD: + cdvdopts << "--spdread"; + cdvdopts << (en ? "on" : "off"); + break; + case FEATURE_BITSETR: + cdvdopts << "--bitset+r"; + cdvdopts << (en ? "on" : "off"); + break; + case FEATURE_BITSETRDL: + cdvdopts << "--bitset+rdl"; + cdvdopts << (en ? "on" : "off"); + break; + case FEATURE_SIMULPLUS: + cdvdopts << "--dvd+testwrite"; + cdvdopts << (en ? "on" : "off"); + break; + default: + mutex->unlock(); + startWatcher(); + return false; + } +#ifndef QT_NO_DEBUG + qDebug() << cdvdopts.join(" "); +#endif + r = QProcess::execute("cdvdcontrol", cdvdopts); + unpauseWatcher(); + } else if (type == device::DevtypeTCP) { + switch(f) { + default: + mutex->unlock(); + return false; + } + } + mutex->unlock(); + return !!r; +} + +bool device::setComplexFeature(int f, DevFeatures* data) +{ + int r=1; +#ifndef QT_NO_DEBUG + qDebug() << "device::setComplexFeature: " << f; +#endif + if (!data) { +#ifndef QT_NO_DEBUG + qDebug() << "Data pointer is NULL"; +#endif + return 1; + } + if (!mutex->tryLock()) { + qDebug() << "Device busy: " << id; + return 1; + } + + if (type == DevtypeLocal) { + QStringList cdvdopts; + + cdvdopts << "-d" << path; + switch(f) { + case FEATURE_GIGAREC: + if (data->enabled & FEATURE_GIGAREC) { + cdvdopts << "--gigarec" << QString("%1").arg(data->grec, 3, 'f', 1); + } else { + cdvdopts << "--gigarec" << "off"; + } + break; + case FEATURE_VARIREC_CD: + if (data->enabled & FEATURE_VARIREC_CD) { + cdvdopts << "--varirec-cd" << QString::number(data->vrec_cd_pwr); + if (data->supported & FEATURE_VARIREC_CDEXT) { + cdvdopts << "--varirec-cd-strategy" << QString::number(data->vrec_cd_str-1); + } + } else { + cdvdopts << "--varirec-cd" << "off"; + } + break; + case FEATURE_VARIREC_DVD: + if (data->enabled & FEATURE_VARIREC_DVD) { + cdvdopts << "--varirec-dvd" << QString::number(data->vrec_dvd_pwr); + cdvdopts << "--varirec-dvd-strategy" << QString::number(data->vrec_dvd_str-1); + } else { + cdvdopts << "--varirec-dvd" << "off"; + } + break; + case FEATURE_SECUREC: + if (data->enabled & FEATURE_SECUREC) { + cdvdopts << "--securec" << data->sr_pass; + } else { + cdvdopts << "--nosecurec"; + } + break; + case FEATURE_SILENT: + if (data->enabled & FEATURE_SILENT) { + cdvdopts << "--silent" << "on"; + cdvdopts << "--sm-cd-rd" << QString::number(data->sm_cd_rd); + cdvdopts << "--sm-cd-wr" << QString::number(data->sm_cd_wr); + if (cap_rd & DEVICE_DVD) { + cdvdopts << "--sm-dvd-rd" << QString::number(data->sm_dvd_rd); +// cdvdopts << "--sm-dvd-wr" << QString::number(data->sm_dvd_wr); + } + cdvdopts << "--sm-access" << (data->sm_access ? "fast" : "slow"); + cdvdopts << "--sm-load" << QString::number(data->sm_trayl); + cdvdopts << "--sm-eject" << QString::number(data->sm_traye); + } else { + cdvdopts << "--silent" << "off"; + } + if (data->sm_nosave) cdvdopts << "--sm-nosave"; + break; + case FEATURE_AS: + switch (data->as_action) { + case AS_ACTION_MODE: + cdvdopts << "--as-mode"; + switch (data->as_mode) { + case AS_MODE_OFF: + cdvdopts << "off"; + break; + case AS_MODE_AUTO: + cdvdopts << "auto"; + break; + case AS_MODE_ON: + cdvdopts << "on"; + break; + case AS_MODE_FORCED: + cdvdopts << "forced"; + break; + default: + mutex->unlock(); + return 1; + } + break; + case AS_ACTION_ACT: + cdvdopts << "--as-on" << QString::number(data->as_idx); + break; + case AS_ACTION_DEACT: + cdvdopts << "--as-off" << QString::number(data->as_idx); + break; + case AS_ACTION_DEL: + cdvdopts << "--as-del" << QString::number(data->as_idx); + break; + case AS_ACTION_CLEAR: + cdvdopts << "--as-clear"; + break; + default: + mutex->unlock(); + return 1; + } + break; + case FEATURE_PIOQUIET: + cdvdopts << "--pio-quiet"; + switch(data->pioq_quiet) { + case PIOQ_QUIET: + cdvdopts << "quiet"; + break; + case PIOQ_STD: + cdvdopts << "std"; + break; + case PIOQ_PERF: + cdvdopts << "perf"; + break; + default: + mutex->unlock(); + return 1; + } + cdvdopts << "--pio-limit"; + cdvdopts << ((features.enabled & FEATURE_PIOLIMIT) ? "on" : "off"); + if (data->pioq_nosave) + cdvdopts << "--pio-nosave"; + break; + default: + mutex->unlock(); + return 1; + } +#ifndef QT_NO_DEBUG + qDebug() << cdvdopts.join(" "); +#endif + r = QProcess::execute("cdvdcontrol", cdvdopts); + } else if (type == device::DevtypeTCP) { + switch(f) { + default: + mutex->unlock(); + return 1; + } + } + mutex->unlock(); + return !!r; +} + +bool device::start_tests() +{ + if (running) return false; + threadType = threadTest; + tests = test_req; + return start(); +} + +bool device::stop_tests() +{ + if (!running) return false; + tests = 0; + if (type == DevtypeLocal) { + if (!proc) return false; + Q_PID pid = proc->pid(); +#if defined(__unix) || defined(__unix__) + kill(pid, SIGINT); +#elif defined(_WIN32) + TerminateProcess(pid, 0); +#endif + } else if (type == device::DevtypeTCP) { + if (!sock) return false; +#ifndef QT_NO_DEBUG + qDebug("Scan terminate not implemented on network devices"); +#endif + sock->disconnectFromHost(); + } + return true; +} + +bool device::next_test() +{ + ctest=0; + QString stest; +#ifndef QT_NO_DEBUG + qDebug("STA: device::next_test()"); +#endif + pprocess = 0.0; + if (tests & TEST_RT) { + ctest = TEST_RT; + stest = "rt"; + nprocess = tr("Read Transfer"); + testData.clearRT(); + test_spd = tspeeds.rt; + } else if (tests & TEST_WT) { + ctest = TEST_WT; + stest = "wt"; + nprocess = tr("Write Transfer"); + testData.clearWT(); + test_spd = tspeeds.wt; + } else if (tests & TEST_ERRC) { + ctest = TEST_ERRC; + stest = "errc"; + nprocess = tr("Error Correction"); + testData.clearErrc(); + test_spd = tspeeds.errc; + } else if (tests & TEST_JB) { + ctest = TEST_JB; + stest = "jb"; + nprocess = tr("Jitter/Asymmetry"); + testData.clearJB(); + test_spd = tspeeds.jb; + } else if (tests & TEST_FT) { + ctest = TEST_FT; + stest = "ft"; + nprocess = tr("Focus/Tracking"); + testData.clearFT(); + test_spd = tspeeds.ft; + } else if (tests & TEST_TA) { + ctest = TEST_TA; + stest = "ta"; + nprocess = tr("Time Analyser"); + testData.clearTA(); +// test_spd = tspeeds.ta; + } + + tests &= ~ctest; + if (!ctest) { + threadType = threadNone; + running=0; + mutex->unlock(); +#ifndef QT_NO_DEBUG + qDebug("END: device::next_test(): to tests remaining"); +#endif +// nprocess = ""; + emit testsDone(); + return false; + } + +#ifndef QT_NO_DEBUG + qDebug() << "device::next_test(): starting test " << stest << " at speed " << test_spd; +#endif +/* + run_test("rt"); + run_test("errc"); + run_test("jb"); + run_test("ft"); + run_test("ta"); +*/ + + if (type == DevtypeNone) { + goto next_test_err; + } + if (proc) { + delete proc; + proc = NULL; + } + if (sock) { + delete sock; + sock = NULL; + } + + emit process_started(); + + QObject::connect(io, SIGNAL(readyReadLine()), + this, SLOT(qscan_process_test())); + + if (type == DevtypeLocal) { + QStringList qopts; +#ifndef QT_NO_DEBUG + qDebug("device: LOCAL"); +#endif + proc = new QProcess(this); +#ifndef QT_NO_DEBUG + qDebug("process created"); +#endif + io->setIODevice(proc); + proc->setReadChannel(QProcess::StandardOutput); + +// QObject::connect(proc, SIGNAL(readyReadStandardOutput()), +// this, SLOT(qscan_process_test())); + + qopts << "-d" << path << "-t" << stest << "-s" << QString::number(test_spd); + if (stest == "wt" && !WT_simul) + qopts << "-W"; + if (stest != "rt" && stest != "wt" && !plugin.isEmpty()) { + qopts << "--force-plugin" << plugin; + } + +#if (!defined(QT_NO_DEBUG) && 0) + for (int i=0;istart("qscan", qopts); + + if (!proc->waitForStarted(10000)) { +#ifndef QT_NO_DEBUG + qDebug("Can't run qscan!"); +#endif + goto next_test_err; + } + QObject::connect(proc, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(qscan_callback_test())); + gettimeofday(&timeSta, NULL); +#ifndef QT_NO_DEBUG + qDebug("qscan (local) started"); +#endif + goto next_test_end; + } else if (type == device::DevtypeTCP) { +#ifndef QT_NO_DEBUG + qDebug("device: TCP"); +#endif + sock = new QTcpSocket(this); +#ifndef QT_NO_DEBUG + qDebug("socket created"); +#endif + io->setIODevice(sock); + +// QObject::connect(sock, SIGNAL(readyRead()), +// this, SLOT(qscan_process_test())); + + sock->connectToHost(host, port); + if (!sock->waitForConnected(5000)) { +#ifndef QT_NO_DEBUG + qDebug("Unable to connect to host!"); +#endif + goto next_test_err; + } + QObject::connect(sock, SIGNAL(disconnected()), + this, SLOT(qscan_callback_test())); + + sock->write("set dev=" + path.toLatin1() + "\n"); + sock->write("set test=" + stest.toLatin1() + "\n"); + sock->write("set speed=" + QString::number(test_spd).toLatin1() + "\n"); + if (stest == "wt") + sock->write("set simul=" + QString::number(WT_simul).toLatin1() + "\n"); + sock->write("run\n"); + sock->write("close\n"); + gettimeofday(&timeSta, NULL); +#ifndef QT_NO_DEBUG + qDebug("qscan (TCP) started"); +#endif + goto next_test_end; + } + +next_test_err: + qscan_callback_info(); +#ifndef QT_NO_DEBUG + qDebug("END: device::next_test()"); +#endif + return false; +next_test_end: +#ifndef QT_NO_DEBUG + qDebug("END: device::next_test()"); +#endif + return true; +} + +void device::qscan_process_info() +{ + QString qout; + +#ifndef QT_NO_DEBUG + qDebug("STA: qscan_process_info()"); +#endif + if (!io->IODevice()) { +#ifndef QT_NO_DEBUG + qDebug("END: qscan_process_info(): QIODevice is NULL"); +#endif + return; + } + if (!io_mutex->tryLock()) { +#ifndef QT_NO_DEBUG + qDebug("END: qscan_process_info(): Can't lock I/O Mutex"); +#endif + return; + } + while (io->linesAvailable() ) { + qout = io->readLine(); +// qout.remove("\n"); + switch (threadType) { + case threadDevice: + case threadMedia: + qscan_process_line(qout); + break; + case threadGetFeatures: + cdvdcontrol_process_line(qout); + break; + case threadGetASDB: + cdvdcontrol_process_asdb(qout); + break; + + case threadMQCK: + case threadAScre: + case threadDestruct: + cdvdcontrol_process_line(qout); + case threadTattoo: + default: +#ifndef QT_NO_DEBUG + qDebug() << qout; +#endif + break; + } + // qDebug(qout.remove("\n")); + } // while (io->bytesAvailable() ) + io_mutex->unlock(); +#ifndef QT_NO_DEBUG + qDebug("END: qscan_process_info()"); +#endif +} + +void device::qscan_process_line(QString& qout) +{ + QStringList sl; +#ifdef MINFO_TREE + QTreeWidgetItem *info; +#endif + QIcon ico_ok(":images/ok.png"); + QIcon ico_x (":images/x.png"); + QIcon ico_rd(":images/disc.png"); + QIcon ico_wr(":images/cdwriter.png"); + + if (threadType==threadMedia && preserveMediaInfo) { + if (!qout.startsWith("IM:")) return; + // device inquiry string + qout.remove(0,4); +#ifndef QT_NO_DEBUG + qDebug() << qout; +#endif + sl = qout.split(':'); + if (sl.size() <2 ) return; + sl[1].remove('\''); + while (!sl[1].isEmpty() && sl[1][0] == ' ') sl[1].remove(0,1); + + + if (sl[0].contains("Available quality tests", Qt::CaseInsensitive)) { + if (sl.size()>=2) { + QStringList slt = sl[1].split(' ', QString::SkipEmptyParts); + test_cap = 0; + for (int ii=0; ii=2 ) { + sl[1].remove('\''); + while (!sl[1].isEmpty() && sl[1][0] == ' ') sl[1].remove(0,1); + + if (sl[0].contains("Device", Qt::CaseInsensitive) && !sl[0].contains("capabilities", Qt::CaseInsensitive)) { +// l_dev->setText(sl[1]); +#if 0 + } else if (sl[0].contains("Vendor", Qt::CaseInsensitive)) { + l_vendor->setText(sl[1]); + } else if (sl[0].contains("Model", Qt::CaseInsensitive)) { + l_model->setText(sl[1]); + } else if (sl[0].contains("F/W", Qt::CaseInsensitive)) { + l_fw->setText(sl[1]); +#endif + } else if (sl[0].contains("TLA", Qt::CaseInsensitive)) { + if (sl[1].isEmpty()) { + tla = "N/A"; + } else { + tla = sl[1]; + } + } else if (sl[0].contains("Discs loaded", Qt::CaseInsensitive)) { + life_dn = sl[1].toInt(); + } else if (sl[0].contains("CD Rd", Qt::CaseInsensitive)) { + sl.removeAt(0); + life_cr = sl.join(":"); + } else if (sl[0].contains("CD Wr", Qt::CaseInsensitive)) { + sl.removeAt(0); + life_cw = sl.join(":"); + } else if (sl[0].contains("DVD Rd", Qt::CaseInsensitive)) { + sl.removeAt(0); + life_dr = sl.join(":"); + } else if (sl[0].contains("DVD Wr", Qt::CaseInsensitive)) { + sl.removeAt(0); + life_dw = sl.join(":"); + } else if (sl[0].contains("S/N", Qt::CaseInsensitive)) { + if (sl[1].isEmpty()) { + sn = "N/A"; + } else { + sn = sl[1]; + } + } else if (sl[0].contains("Buffer", Qt::CaseInsensitive)) { + buf = sl[1]; + } else if (sl[0].contains("IFace", Qt::CaseInsensitive)) { + iface = sl[1]; + } else if (sl[0].contains("Loader", Qt::CaseInsensitive)) { + loader = sl[1]; + } else if (sl[0].contains("RPC Phase", Qt::CaseInsensitive)) { + rpc_phase = sl[1].toInt(); + } else if (sl[0].contains("Region", Qt::CaseInsensitive)) { + if (sl[1].contains("not set", Qt::CaseInsensitive)) + rpc_reg = -2; + else + rpc_reg = sl[1].toInt(); + } else if (sl[0].contains("Changes left", Qt::CaseInsensitive)) { + rpc_ch = sl[1].toInt(); + } else if (sl[0].contains("Resets left", Qt::CaseInsensitive)) { + rpc_rst = sl[1].toInt(); + } else if (sl[0].contains("Device Generic capabilities", Qt::CaseInsensitive)) { + cap = sl[1].toULongLong(0,16); + } else if (sl[0].contains("Device Read capabilities", Qt::CaseInsensitive)) { + cap_rd = sl[1].toULongLong(0,16); + } else if (sl[0].contains("Device Write capabilities", Qt::CaseInsensitive)) { + cap_wr = sl[1].toULongLong(0,16); + } + } + } else if (qout.startsWith("CD:")) { + // generic device capabilities + /* + qout.remove(0,4); + proc->setReadChannel(QProcess::StandardOutput); + sl = qout.split(':'); + if (sl.size() >=2 ) { + + } + */ + } else if (qout.startsWith("CM:")) { + // media R/W capabilities + /* + bool rd,wr; + qout.remove(0,4); + sl = qout.split(':'); + if (sl.size() >=2 ) { + sl[1].remove('\''); + rd = sl[1].contains('R', Qt::CaseInsensitive); + wr = sl[1].contains('W', Qt::CaseInsensitive); +#ifndef QT_NO_DEBUG +// qDebug("|" + sl[0] + "|" + sl[1]); +#endif + } + */ + } else if (qout.startsWith("IM:")) { + // device inquiry string + qout.remove(0,4); +#ifndef QT_NO_DEBUG + qDebug() << qout; +#endif + + sl = qout.split(':'); + if (sl.size() >=2 ) { + sl[1].remove('\''); + while (!sl[1].isEmpty() && sl[1][0] == ' ') sl[1].remove(0,1); +#ifndef QT_NO_DEBUG +// qDebug("|" + sl[0] + "|" + sl[1] + "|"); +#endif + if (sl[0].contains("Media type", Qt::CaseInsensitive)) { + if (sl[1].contains("No Media", Qt::CaseInsensitive)) { + media.type = "-"; + media.spd1X = 1; + } else { + media.type = sl[1]; + if (media.type.startsWith("CD")) { + media.spd1X = 150; + } else if (media.type.startsWith("DDCD")) { + media.spd1X = 150; + } else if (media.type.startsWith("DVD")) { + media.spd1X = 1385; + } else if (media.type.startsWith("BD")) { + media.spd1X = 4495; + } + } + } else if (sl[0].contains("Disc Category", Qt::CaseInsensitive)) { + media.category = sl[1]; + } else if (sl[0].contains("Layers", Qt::CaseInsensitive)) { + media.layers = sl[1]; + media.ilayers = sl[1].toInt(); + if (media.ilayers <= 0) + media.ilayers = 1; + } else if (sl[0].contains("Protection", Qt::CaseInsensitive)) { + media.prot = sl[1]; + } else if (sl[0].contains("Regions", Qt::CaseInsensitive)) { + media.regions = sl[1]; + } else if (sl[0].contains("Erasable", Qt::CaseInsensitive)) { + media.erasable = sl[1]; + } else if (sl[0].contains("Disc state", Qt::CaseInsensitive)) { + media.dstate = sl[1]; + } else if (sl[0].contains("Session state", Qt::CaseInsensitive)) { + media.sstate = sl[1]; + } else if (sl[0].contains("Read capacity", Qt::CaseInsensitive)) { + sl.removeFirst(); + QStringList sl2 = sl.join(":").split("/"); + if (sl2.size()>=3) { + media.creads = sl2[0].remove("sectors").toInt(); + media.creadm = sl2[1].remove("MB").toInt(); + media.creadmsf = sl2[2]; + } + } else if (sl[0].contains("Free capacity", Qt::CaseInsensitive)) { + sl.removeFirst(); + QStringList sl2 = sl.join(":").split("/"); + if (sl2.size()>=3) { + media.cfrees = sl2[0].remove("sectors").toInt(); + media.cfreem = sl2[1].remove("MB").toInt(); + media.cfreemsf = sl2[2]; + } + } else if (sl[0].contains("Total capacity", Qt::CaseInsensitive)) { + sl.removeFirst(); + QStringList sl2 = sl.join(":").split("/"); + if (sl2.size()>=3) { + media.ctots = sl2[0].remove("sectors").toInt(); + media.ctotm = sl2[1].remove("MB").toInt(); + media.ctotmsf = sl2[2]; + } + } else if (sl[0].contains("Media ID", Qt::CaseInsensitive)) { + sl.removeFirst(); + media.mid = sl.join(":"); + if (media.type.startsWith("DVD")) media.mid.remove(' '); + } else if (sl[0].contains("Written on", Qt::CaseInsensitive)) { + media.writer = sl[1]; +#ifdef SHOW_SPEEDS + } else if (sl[0].contains("RD speed max", Qt::CaseInsensitive)) { + l_rd_max->setText(sl[1]); + } else if (sl[0].contains("RD speed #", Qt::CaseInsensitive)) { + c_rd_lst->addItem(sl[1]); + } else if (sl[0].contains("D WR speed max", Qt::CaseInsensitive)) { + l_wr_max->setText(sl[1]); + } else if (sl[0].contains("D WR speed #", Qt::CaseInsensitive)) { + c_wr_lst->addItem(sl[1]); +#endif +/* + } else if (sl[0].contains("Available quality tests", Qt::CaseInsensitive)) { + if (sl.size()>=2) { + QStringList slt = sl[1].split(' ', QString::SkipEmptyParts); + test_cap = 0; + for (int ii=0; ii=2 ) { + sl[1].remove('\''); + if (sl[0].contains("RD speed #", Qt::CaseInsensitive)) { + sl[1].remove(QRegExp("\\([a-z,A-Z,0-9, /]*\\)")); + sl[1].remove(' '); + media.rspeeds.append(sl[1]); + } else if (sl[0].contains("D WR speed #", Qt::CaseInsensitive)) { + sl[1].remove(QRegExp("\\([a-z,A-Z,0-9, /]*\\)")); + sl[1].remove(' '); + media.wspeedsd.prepend(sl[1]); + } else if (sl[0].contains("M WR speed #", Qt::CaseInsensitive)) { + sl[1].remove(' '); + media.wspeedsm.append(sl[1]); + } + +#ifdef MINFO_TREE + info = new QTreeWidgetItem(QStringList(qout)); + info_media.append(info); +#endif + } + } else if (qout.startsWith("Found plugin:")) { + int spidx; + QString pn,pi; + qout.remove(0,13); + qout = qout.simplified(); + spidx = qout.indexOf(" "); + pn = qout.mid(0,spidx).simplified(); + pi = qout.mid(spidx+1); + pi.remove("("); pi.remove(")"); + + if (!preserveMediaInfo) { +#ifndef QT_NO_DEBUG + qDebug() << "Plugin: " << pn << pi; +#endif + plugin_names.append(pn); + plugin_infos.append(pi); + } + } else if (qout.contains("WARNING!!! Detected locked PX-755/PX-760")) { + plextor_lock = 1; + } +} + +void device::cdvdcontrol_process_line(QString& qout) +{ + QStringList sl; + // vendor-specific device features +#ifndef QT_NO_DEBUG + qDebug() << qout; +#endif + sl = qout.split(':'); + if (sl.size() >=2 ) { + sl[1] = sl[1].simplified(); + if (sl[0].contains("Lock state", Qt::CaseInsensitive)) { + if (sl[1] != "-") { + features.supported |= FEATURE_LOCK; + features.enabled |= (sl[1].contains("ON") ? FEATURE_LOCK : 0); + } + } else if (sl[0].contains("Hide-CDR", Qt::CaseInsensitive)) { + if (sl[1] != "-") { + features.supported |= FEATURE_HIDECDR; + features.enabled |= (sl[1].contains("ON") ? FEATURE_HIDECDR : 0); + } + } else if (sl[0].contains("SingleSession", Qt::CaseInsensitive)) { + if (sl[1] != "-") { + features.supported |= FEATURE_SINGLESESSION; + features.enabled |= (sl[1].contains("ON") ? FEATURE_SINGLESESSION : 0); + } + } else if (sl[0].contains("SpeedRead", Qt::CaseInsensitive)) { + if (sl[1] != "-") { + features.supported |= FEATURE_SPEEDREAD; + features.enabled |= (sl[1].contains("ON") ? FEATURE_SPEEDREAD : 0); + } + } else if (sl[0].contains("PSM Silent State", Qt::CaseInsensitive)) { + if (sl[1] != "-") { + features.supported |= FEATURE_SILENT; + features.enabled |= (sl[1].contains("ON") ? FEATURE_SILENT : 0); + } + } else if (sl[0].contains("PSM CD Read speed", Qt::CaseInsensitive)) { + features.psm_cd_rd = sl[1].remove("X").toInt(); + } else if (sl[0].contains("PSM CD Write speed", Qt::CaseInsensitive)) { + features.psm_cd_wr = sl[1].remove("X").toInt(); + } else if (sl[0].contains("PSM DVD Read speed", Qt::CaseInsensitive)) { + features.psm_dvd_rd = sl[1].remove("X").toInt(); +// } else if (sl[0].contains("SM DVD Write speed", Qt::CaseInsensitive)) { +// features.sm_dvd_wr = sl[1].toInt(); + } else if (sl[0].contains("PSM Access time", Qt::CaseInsensitive)) { + features.psm_access = !!sl[1].contains("FAST"); + } else if (sl[0].contains("PSM Load speed", Qt::CaseInsensitive)) { + features.psm_trayl = sl[1].toInt(); + } else if (sl[0].contains("PSM Eject speed", Qt::CaseInsensitive)) { + features.psm_traye = sl[1].toInt(); + } else if (sl[0].contains("SM Read speed", Qt::CaseInsensitive)) { + if (media.type.startsWith("DVD")) { + features.sm_dvd_rd = sl[1].remove("X").toInt(); + } else { + features.sm_cd_rd = sl[1].remove("X").toInt(); + } + } else if (sl[0].contains("SM Write speed", Qt::CaseInsensitive)) { + if (!media.type.startsWith("DVD")) { + features.sm_cd_wr = sl[1].remove("X").toInt(); + } + } else if (sl[0].contains("SM Access time", Qt::CaseInsensitive)) { + features.sm_access = !!sl[1].contains("FAST"); + } else if (sl[0].contains("SM Load speed", Qt::CaseInsensitive)) { + features.sm_trayl = sl[1].toInt(); + } else if (sl[0].contains("SM Eject speed", Qt::CaseInsensitive)) { + features.sm_traye = sl[1].toInt(); + + } else if (sl[0].contains("PoweRec", Qt::CaseInsensitive) && !sl[0].contains("Speed", Qt::CaseInsensitive) ) { + if (sl[1] != "-") { + features.supported |= FEATURE_POWEREC; + features.enabled |= (sl[1].contains("ON") ? FEATURE_POWEREC : 0); + } + } else if (sl[0].contains("PoweRec Speed", Qt::CaseInsensitive)) { + features.prec_spd = sl[1].remove("X").remove("(CD)").remove("(DVD)").toInt(); + } else if (sl[0].contains("VariRec CD power", Qt::CaseInsensitive)) { + features.vrec_cd_pwr = (char) sl[1].toInt(); + } else if (sl[0].contains("VariRec CD strategy", Qt::CaseInsensitive)) { + int offs = sl[1].indexOf('['); + if (offs>=0) { + sl[1].remove(0,offs+1); + sl[1].remove(']'); + features.vrec_cd_str = (char) sl[1].toInt(); + } + } else if (sl[0].contains("VariRec CD", Qt::CaseInsensitive)) { + if (sl[1] != "-") { + features.supported |= FEATURE_VARIREC_CDBASE; + if (dev.startsWith("DVDR") || dev.startsWith("CD-R PREMIUM")) + features.supported |= FEATURE_VARIREC_CDEXT; + features.enabled |= (sl[1].contains("OFF") ? 0 : FEATURE_VARIREC_CD); + } + } else if (sl[0].contains("VariRec DVD power", Qt::CaseInsensitive)) { + features.vrec_dvd_pwr = (char) sl[1].toInt(); + } else if (sl[0].contains("VariRec DVD strategy", Qt::CaseInsensitive)) { + int offs = sl[1].indexOf('['); + if (offs>=0) { + sl[1].remove(0,offs+1); + sl[1].remove(']'); + features.vrec_dvd_str = (char) sl[1].toInt(); + } + } else if (sl[0].contains("VariRec DVD", Qt::CaseInsensitive)) { + if (sl[1] != "-") { + features.supported |= FEATURE_VARIREC_DVD; + features.enabled |= (sl[1].contains("OFF") ? 0 : FEATURE_VARIREC_DVD); + } + } else if (sl[0].contains("GigaRec state", Qt::CaseInsensitive)) { + features.supported |= FEATURE_GIGAREC; + if (sl[1].contains("OFF")) { + features.enabled &= ~FEATURE_GIGAREC; + features.grec = 1.0; + } else { + features.enabled |= FEATURE_GIGAREC; + features.grec = sl[1].toFloat(); + } + } else if (sl[0].contains("Disc GigaRec rate", Qt::CaseInsensitive)) { + if (sl[1].contains("OFF")) { + media.grec = 1.0; + } else { + media.grec = sl[1].toFloat(); + } + } else if (sl[0].contains("SecuRec", Qt::CaseInsensitive)) { + if (sl[1] != "-") { + features.supported |= FEATURE_SECUREC; + features.enabled |= (sl[1].contains("ON") ? FEATURE_SECUREC : 0); + } + } else if (sl[0].contains("DVD+R bitset", Qt::CaseInsensitive)) { + if (sl[1] != "-") { + features.supported |= FEATURE_BITSETR; + features.enabled |= (sl[1].contains("ON") ? FEATURE_BITSETR : 0); + } + } else if (sl[0].contains("DVD+R DL bitset", Qt::CaseInsensitive)) { + if (sl[1] != "-") { + features.supported |= FEATURE_BITSETRDL; + features.enabled |= (sl[1].contains("ON") ? FEATURE_BITSETRDL : 0); + } + } else if (sl[0].contains("DVD+R(W) testwrite", Qt::CaseInsensitive)) { + if (sl[1] != "-") { + features.supported |= FEATURE_SIMULPLUS; + features.enabled |= (sl[1].contains("ON") ? FEATURE_SIMULPLUS : 0); + } + } else if (sl[0].contains("AutoStrategy mode", Qt::CaseInsensitive)) { + features.supported |= FEATURE_AS_BASE; + if (dev.startsWith("DVDR PX-755") || dev.startsWith("DVDR PX-760")) + features.supported |= FEATURE_AS_EXT; + if (sl[1].contains("OFF", Qt::CaseInsensitive)) { + features.as_mode = AS_MODE_OFF; + } else if (sl[1].contains("AUTO", Qt::CaseInsensitive)) { + features.as_mode = AS_MODE_AUTO; + } else if (sl[1].contains("ON", Qt::CaseInsensitive)) { + features.as_mode = AS_MODE_ON; + } else if (sl[1].contains("FORCED", Qt::CaseInsensitive)) { + features.as_mode = AS_MODE_FORCED; + } + } else if (sl[0].contains("Pioneer QuietMode", Qt::CaseInsensitive)) { + if (sl[1] != "-") { + features.supported |= FEATURE_PIOQUIET; + features.supported |= FEATURE_PIOLIMIT; + } + } else if (sl[0].contains("PlexEraser", Qt::CaseInsensitive)) { + if (sl[1] != "-") { + features.supported |= FEATURE_DESTRUCT; + } + } else if (sl[0].contains("Yamaha DiscT@2", Qt::CaseInsensitive)) { + sl[1].remove("inner"); + sl[1].replace("outer", " "); + sl[1].replace("image", " "); + QStringList sl2 = sl[1].split(" ", QString::SkipEmptyParts); +#ifndef QT_NO_DEBUG + qDebug () << "*** DISC T@2 ***\nsl[1]: " << sl[1] << "\nsl2 size: " << sl2.size(); +#endif + if (sl2.size() >= 3) { + features.supported |= FEATURE_F1TATTOO; + features.tattoo_inner = sl2[0].toInt(); + features.tattoo_outer = sl2[1].toInt(); + qDebug() << "Yamaha DiscT@2 radius: inner " << features.tattoo_inner << ", outer " << features.tattoo_outer; + } +// } else if (sl[0].contains("MQCK", Qt::CaseInsensitive)) { + } else if (sl[0] == "MQCK") { + features.as_mqckres = sl[1]; + } + } +} + +void device::cdvdcontrol_process_asdb(QString& qout) +{ + ASDB_item it; + QStringList sl; +/* + it.present=1; + it.active = 1; + it.type = "DVD+R"; + it.speed = QString::number(8); + it.mid = "TYG02"; + it.writes = QString::number(25); + asdb.append(it); +*/ +#ifndef QT_NO_DEBUG + qDebug("process_asdb"); +#endif + if (!qout.startsWith("S#")) return; + qout.remove(0,2); + qout.remove(" "); + + sl = qout.split('|'); + if (sl.size() <6) return; + + it.present=1; + it.active = sl[1] == "*"; +// it.type = sl[2].remove(QRegExp("[]")); + it.type = sl[2].remove("[A1]").remove("[25]"); + it.speed = sl[3]; + it.mid = sl[4]; + it.writes = sl[5]; + + asdb.append(it); +} + +void device::qscan_callback_test() +{ + int xcode = 0; +#ifndef QT_NO_DEBUG + qDebug("STA: qscan_callback_test()"); +#endif + timeval timeEnd; + int time; + + qscan_process_test(); + io_mutex->lock(); + + QObject::disconnect(io, SIGNAL(readyReadLine()), + this, SLOT(qscan_process_test())); + + if (type == DevtypeLocal) { +// QObject::disconnect(proc, SIGNAL(finished(int, QProcess::ExitStatus)), +// this, SLOT(qscan_callback_info())); + xcode = proc->exitCode(); +#ifndef QT_NO_DEBUG + qDebug() << "qscan (local) finished" << xcode; +#endif + disconnect(proc); + io->setIODevice(NULL); + } else if (type == device::DevtypeTCP) { +// QObject::disconnect(sock, SIGNAL(disconnected()), +// this, SLOT(qscan_callback_info())); + sock->disconnectFromHost(); +#ifndef QT_NO_DEBUG + qDebug("qscan (TCP) finished"); +#endif + disconnect(sock); + io->setIODevice(NULL); + } + io_mutex->unlock(); + nprocess = ""; + emit process_finished(); + + gettimeofday(&timeEnd, NULL); + time = (int) ((timeEnd.tv_sec - timeSta.tv_sec) + (timeEnd.tv_usec - timeSta.tv_usec)/1000000.0); + + switch(ctest) { + case TEST_RT: + testData.rt_time = time; + emit block_RT(); + break; + case TEST_WT: + testData.wt_time = time; + emit block_WT(); + break; + case TEST_ERRC: + testData.errc_time = time; + emit block_ERRC(); + break; + case TEST_JB: + testData.jb_time = time; + emit block_JB(); + break; + case TEST_FT: + testData.ft_time = time; + emit block_FT(); + break; + case TEST_TA: + testData.ta_time = time; + emit block_TA(); + break; + default: + break; + } + + if (xcode) { + emit testsError(); + } + + next_test(); + +#ifndef QT_NO_DEBUG + qDebug("END: qscan_callback_test()"); +#endif +} + +void device::qscan_process_test() +{ + timeval timeEnd; + float time; + QString qout; + QStringList sl; +#ifndef QT_NO_DEBUG +// qDebug("STA: qscan_process_test()"); +#endif + if (!io->IODevice()) { +#ifndef QT_NO_DEBUG + qDebug("END: qscan_process_test(): QIODevice is NULL"); +#endif + return; + } + if (!io_mutex->tryLock()) { +#ifndef QT_NO_DEBUG + qDebug("END: qscan_process_test(): Can't lock I/O Mutex"); +#endif + return; + } + while (io->linesAvailable()) { + qout = io->readLine(); + qout.remove("\n"); + qout.remove("\r"); +#ifndef QT_NO_DEBUG + qDebug() << qout; +#endif + gettimeofday(&timeEnd, NULL); + time = (timeEnd.tv_sec - timeSta.tv_sec) + (timeEnd.tv_usec - timeSta.tv_usec)/1000000.0; + switch(ctest) { + case TEST_RT: +#ifndef DISABLE_INTERNAL_WT + case TEST_WT: +#endif + if (qout.startsWith("lba") && qout.contains("speed")) { +// qDebug("RT"); + DI_Transfer di; + sl = qout.split(" ", QString::SkipEmptyParts); + // for (int i=0; i=6) { + di.lba = sl[1].toLongLong(); + di.spdx = sl[3].toDouble(); + di.spdk = sl[5].toInt(); +#ifndef DISABLE_INTERNAL_WT + if (ctest == TEST_WT) { + testData.wt.append(di); + testData.wt_time = time; + emit block_WT(); + + pprocess = 100.0 * (float) di.lba / media.ctots; + emit process_progress(); + } else if (ctest == TEST_RT) +#endif + { + testData.rt.append(di); + testData.rt_time = time; + emit block_RT(); + + pprocess = 100.0 * (float) di.lba / media.creads; + emit process_progress(); + } + } + // qDebug(QString("lba: %1, spdx: %2, spdk: %3").arg(di.lba).arg(di.spdx).arg(di.spdk)); + } else if (qout.startsWith("Reading blocks") || qout.startsWith("Starting write")) { + gettimeofday(&timeSta, NULL); + } + break; +#ifdef DISABLE_INTERNAL_WT + case TEST_WT: + if (qout.startsWith("Track ") && qout.contains("MB written")) { + DI_Transfer di; + sl = qout.split(" ", QString::SkipEmptyParts); + // for (int i=0; i=10) { + di.lba = sl[2].toLongLong() << 9; + if (sl[3] == "of") { + if (sl.size() >=12) { + di.spdx = sl[11].remove("x.").toDouble(); + di.spdk = (uint32_t)(di.spdx * media.spd1X); + testData.wt.append(di); + testData.wt_time = time; + emit block_WT(); + } + } else { + if (sl.size() >=10) { + di.spdx = sl[9].remove("x.").toDouble(); + di.spdk = (uint32_t)(di.spdx * media.spd1X); + testData.wt.append(di); + testData.wt_time = time; + emit block_WT(); + } + } + pprocess = 100.0 * (float) di.lba / media.ctots; + emit process_progress(); +// qDebug( sl[1] + " " + sl[3] + " " + sl[5] ); + } + // qDebug(QString("lba: %1, spdx: %2, spdk: %3").arg(di.lba).arg(di.spdx).arg(di.spdk)); + } + break; +#endif + case TEST_ERRC: + if (qout.startsWith("cur")) { + DI_Errc di; + sl = qout.split(QRegExp("[:\\ |]"), QString::SkipEmptyParts); + // for (int i=0; i=14) { + di.cd.lba = sl[1].toInt(); + di.cd.spdx= sl[2].toFloat(); + + di.cd.bler = sl[6].toInt(); + di.cd.e11 = sl[7].toInt(); + di.cd.e21 = sl[8].toInt(); + di.cd.e31 = sl[9].toInt(); + di.cd.e12 = sl[10].toInt(); + di.cd.e22 = sl[11].toInt(); + di.cd.e32 = sl[12].toInt(); + di.cd.uncr = sl[13].toInt(); + + ErrcADD(&testData.errcTOT, di); + ErrcMAX(&testData.errcMAX, di); + CDErrcAVG(&testData.errcAVG, &testData.errcTOT, di.cd.lba/75); + testData.errc.append(di); + testData.errc_time = time; + emit block_ERRC(); + + pprocess = 100.0 * (float) di.cd.lba / media.creads; + emit process_progress(); + } else if (media.type.startsWith("DVD") && sl.size()>=13) { + di.dvd.lba = sl[1].toInt(); + di.dvd.spdx = sl[2].toFloat(); + + di.dvd.res = 0; + di.dvd.pie = sl[6].toInt(); + di.dvd.pi8 = sl[7].toInt(); + di.dvd.pif = sl[8].toInt(); + di.dvd.poe = sl[9].toInt(); + di.dvd.po8 = sl[10].toInt(); + di.dvd.pof = sl[11].toInt(); + di.dvd.uncr = sl[12].toInt(); + + ErrcADD(&testData.errcTOT, di); + ErrcMAX(&testData.errcMAX, di); + DVDErrcAVG(&testData.errcAVG, &testData.errcTOT, di.dvd.lba >> 4); + testData.errc.append(di); + testData.errc_time = time; + emit block_ERRC(); + + pprocess = 100.0 * (float) di.dvd.lba / media.creads; + emit process_progress(); + } else if (media.type.startsWith("BD") && sl.size()>=9) { + di.bd.lba = sl[1].toInt(); + di.bd.spdx = sl[2].toFloat(); + + di.bd.res0 = 0; + di.bd.ldc = sl[6].toInt(); + di.bd.res1 = 0; + di.bd.res2 = 0; + di.bd.bis = sl[7].toInt(); + di.bd.res3 = 0; + di.bd.res4 = 0; + di.bd.uncr = sl[8].toInt(); + + ErrcADD(&testData.errcTOT, di); + ErrcMAX(&testData.errcMAX, di); + DVDErrcAVG(&testData.errcAVG, &testData.errcTOT, di.dvd.lba >> 4); + testData.errc.append(di); + testData.errc_time = time; + emit block_ERRC(); + + pprocess = 100.0 * (float) di.bd.lba / media.creads; + emit process_progress(); + } + } + break; + case TEST_JB: + if (qout.startsWith("cur")) { + DI_JB di; + sl = qout.split(QRegExp("[:\\ |]"), QString::SkipEmptyParts); +// for (int i=0; i=8) { + di.lba = sl[1].toInt(); + di.spdx = sl[2].toFloat(); + + di.jitter = sl[6].toFloat(); + di.asymm = sl[7].toFloat(); + + if (!testData.jb.size()) { + testData.jbMM.jmin = di.jitter; + testData.jbMM.jmax = di.jitter; + testData.jbMM.bmin = di.asymm; + testData.jbMM.bmax = di.asymm; + } else { + if (testData.jbMM.jmin > di.jitter) testData.jbMM.jmin = di.jitter; + if (testData.jbMM.jmax < di.jitter) testData.jbMM.jmax = di.jitter; + if (testData.jbMM.bmin > di.asymm) testData.jbMM.bmin = di.asymm; + if (testData.jbMM.bmax < di.asymm) testData.jbMM.bmin = di.asymm; + } + + testData.jb.append(di); + testData.jb_time = time; + emit block_JB(); + + pprocess = 100.0 * (float) di.lba / media.creads; + emit process_progress(); + } + } + break; + case TEST_FT: + if (qout.startsWith("cur")) { + DI_FT di; + sl = qout.split(QRegExp("[:\\ |]"), QString::SkipEmptyParts); +// for (int i=0; i=8) { + di.lba = sl[1].toInt(); + di.spdx= sl[2].toFloat(); + + di.fe = sl[6].toInt(); + di.te = sl[7].toInt(); + + if (testData.ftMAX.fe < di.fe) testData.ftMAX.fe = di.fe; + if (testData.ftMAX.te < di.te) testData.ftMAX.te = di.te; + + testData.ft.append(di); + testData.ft_time = time; + emit block_FT(); + + pprocess = 100.0 * (float) di.lba / media.ctots; + emit process_progress(); + } + } + break; + case TEST_TA: + if (qout.startsWith("TA")) { + if (taIdx <0 || taIdx>5) break; + DI_TA di; + sl = qout.split(" ", QString::SkipEmptyParts); + if (sl.size() >=4) { + di.idx = sl[1].toInt(); + di.pit = sl[2].toInt(); + di.land = sl[3].toInt(); + // if (!di.idx) testData.ta[taIdx].clear(); + testData.ta[taIdx].append(di); + testData.ta_time = time; + emit block_TA(); + } + } else if (qout.startsWith("Running TA on")) { + int taLayer=0; + int taZone=0; + sl = qout.split(" ", QString::SkipEmptyParts); + if (sl.size() >=6) { + sl[3].remove("L"); +#ifndef QT_NO_DEBUG + qDebug() << "TA Layer: " << sl[3]; +#endif + taLayer = sl[3].toInt(); + if (sl[4] == "inner") { + taZone = 0; + } else if (sl[4] == "middle") { + taZone = 1; + } else if (sl[4] == "outer") { + taZone = 2; + } + taIdx = taLayer*3 + taZone; + + pprocess = 100.0 * (float) taIdx / ( media.ilayers*3); + emit process_progress(); + } + } + // parsing TA test output... + + break; + default: + break; + } + + } + io_mutex->unlock(); +#ifndef QT_NO_DEBUG +// qDebug("END: qscan_process_test()"); +#endif +} + +void device::save(QIODevice *f) +{ +#ifndef QT_NO_DEBUG + qDebug("device::save()"); +#endif + resWriter->setIO(f); + resWriter->start(); +} + +bool device::isSaving() { return resWriter->isRunning(); }; +bool device::saveResult() { return resWriter->result(); }; + +void device::load(QIODevice *f) +{ +#ifndef QT_NO_DEBUG + qDebug("device::load()"); +#endif + resReader->setIO(f); + resReader->start(); +} + +bool device::isLoading() { return resReader->isRunning(); }; +bool device::loadResult() { return resReader->result(); }; + +void device::startWatcher() +{ + if (mwatcher) return; + mwatcher = new MediaWatcher(this); + connect(mwatcher, SIGNAL(started()), this, SLOT(watcherStarted())); + connect(mwatcher, SIGNAL(finished()), this, SLOT(watcherStoped())); + + connect(mwatcher, SIGNAL(mediaLoading()), this, SLOT(watcherEventLoading())); + connect(mwatcher, SIGNAL(mediaRemoved()), this, SLOT(watcherEventRemoved())); + connect(mwatcher, SIGNAL(mediaNew()), this, SLOT(watcherEventNew())); + connect(mwatcher, SIGNAL(mediaNoMedia()), this, SLOT(watcherEventNoMedia())); + + mwatcher->start(); +} + +void device::stopWatcher() +{ + if (!mwatcher) return; + mwatcher->stop(); + mwatcher->wait(3000); +} + +void device::pauseWatcher() { if (!mwatcher) return; mwatcher->pause(); } +void device::unpauseWatcher() { if (!mwatcher) return; mwatcher->unPause(); } + +void device::watcherStarted() +{ +#ifndef QT_NO_DEBUG + qDebug() << "device: " << path << ": watcher started"; +#endif +} + +void device::watcherStoped() +{ +#ifndef QT_NO_DEBUG + qDebug() << "device: " << path << ": watcher stoped"; +#endif + mwatcher->disconnect(); + mwatcher->deleteLater(); + mwatcher = NULL; +} + +void device::watcherEventLoading() +{ + nprocess = tr("Loading media..."); + emit process_started(); +} + +void device::watcherEventRemoved() +{ +#ifndef QT_NO_DEBUG + qDebug("device::watcherEventRemoved()"); +#endif + autoupdate = 1; + clear_media_info(); +} + +void device::watcherEventNew() +{ +#ifndef QT_NO_DEBUG + qDebug("device::watcherEventNew()"); +#endif + autoupdate = 1; + update_media_info(); +} + +void device::watcherEventNoMedia() +{ + nprocess = ""; + emit process_finished(); +} + +void device::resLoaderDone() { + if (!resReader->result()) return; + + emit doneDInfo(0); + emit doneMInfo(0); +}; + diff --git a/gui/src/devsettings.cpp b/gui/src/devsettings.cpp new file mode 100644 index 0000000..2ad8b80 --- /dev/null +++ b/gui/src/devsettings.cpp @@ -0,0 +1,135 @@ + /* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Gennady "ShultZ" Kozlov + * + * 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 "devsettings.h" + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#define TAB_COMMON 0 +#define TAB_VARIREC 1 +#define TAB_GIGAREC 2 +#define TAB_SECUREC 3 +#define TAB_SILENT_PLEX 4 +#define TAB_SILENT_PIO 5 +#define TAB_DESTRUCT 6 +#define TAB_TATTOO 7 + +devSettings::devSettings(QPxSettings *iset, device *idev, QWidget *p, Qt::WindowFlags fl) + : QDialog(p,fl) +{ + dev = idev; + set = iset; + setWindowTitle("QPxTool - "+tr("Device Controls")); + cpage = NULL; + + layout = new QHBoxLayout(this); + layout->setMargin(3); + layout->setSpacing(3); + setLayout(layout); + + ilist = new ImagesList(80,32,32,this); +// ilist->hideText(); + layout->addWidget(ilist,1); + + ilist->addLabel(tr("Common"), QImage(":images/settings2.png"), TAB_COMMON); + if (set->show_allctl || dev->features.supported & FEATURE_VARIREC) + ilist->addLabel(tr("VariRec"), QImage(":images/varirec.png"), TAB_VARIREC); + if (set->show_allctl || dev->features.supported & FEATURE_GIGAREC) + ilist->addLabel(tr("GigaRec"), QImage(":images/gigarec.png"), TAB_GIGAREC); + if (set->show_allctl || dev->features.supported & FEATURE_SECUREC) + ilist->addLabel(tr("SecuRec"), QImage(":images/password.png"), TAB_SECUREC); + if (set->show_allctl || dev->features.supported & FEATURE_SILENT) + ilist->addLabel(tr("Silent mode"), QImage(":images/sound.png"), TAB_SILENT_PLEX); + if (set->show_allctl || dev->features.supported & FEATURE_PIOQUIET) + ilist->addLabel(tr("PioQuiet"), QImage(":images/sound.png"), TAB_SILENT_PIO); + if (set->show_allctl || dev->features.supported & FEATURE_DESTRUCT) + ilist->addLabel(tr("Destruction"), QImage(":images/disc-eraser.png"), TAB_DESTRUCT); + if (set->show_allctl || dev->features.supported & FEATURE_F1TATTOO) + ilist->addLabel(tr("Disc T@2"), QImage(":images/tattoo.png"), TAB_TATTOO); + + layoutc = new QVBoxLayout(); + layoutc->setMargin(0); + layoutc->setSpacing(3); + layout->addLayout(layoutc,4); + + setPage(0); + + connect(ilist, SIGNAL(selected(int)), this, SLOT(setPage(int))); + + setMinimumSize(450,380); + setMaximumSize(450,580); +} + +devSettings::~devSettings() {} + +void devSettings::setPage(int page) +{ +#ifndef QT_NO_DEBUG + qDebug() << "setPage: " << page; +#endif + if (cpage) { + delete cpage; + cpage = NULL; + } + switch (page) { + case TAB_COMMON: + cpage = new devctlCommon(dev, this); + layoutc->addWidget(cpage); + break; + case TAB_VARIREC: + cpage = new devctlVarirec(dev, this); + layoutc->addWidget(cpage); + break; + case TAB_GIGAREC: + cpage = new devctlGigarec(dev, this); + layoutc->addWidget(cpage); + break; + case TAB_SECUREC: + cpage = new devctlSecurec(dev, this); + layoutc->addWidget(cpage); + break; + case TAB_SILENT_PLEX: + cpage = new devctlSilent(dev, this); + layoutc->addWidget(cpage); + break; + case TAB_SILENT_PIO: + cpage = new devctlPioquiet(dev, this); + layoutc->addWidget(cpage); + break; + case TAB_DESTRUCT: + cpage = new devctlDestruct(dev, this); + layoutc->addWidget(cpage); + break; + case TAB_TATTOO: + cpage = new devctlF1Tattoo(dev, this); + layoutc->addWidget(cpage); + break; + default: + qDebug() << "Invalid page num: " << page; + } +} + diff --git a/gui/src/devsettings_widgets.cpp b/gui/src/devsettings_widgets.cpp new file mode 100644 index 0000000..cb427d5 --- /dev/null +++ b/gui/src/devsettings_widgets.cpp @@ -0,0 +1,1227 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "devsettings_widgets.h" + +#include + +#include +#include + +devctlCommon::devctlCommon(device* idev, QWidget* p) + :QGroupBox(p) +{ + dev = idev; + layout = new QGridLayout(this); + layout->setMargin(3); + layout->setSpacing(3); + + ck_prec = new QCheckBox("PoweRec",this); + layout->addWidget(ck_prec, 0,0); + pl_prec = new QLabel("Speed:",this); + pl_prec->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout->addWidget(pl_prec,0,1); + l_prec = new QLabel(this); + l_prec->setFrameStyle(QFrame::Sunken | QFrame::StyledPanel); + layout->addWidget(l_prec,0,2); + + ck_hcdr = new QCheckBox("Hide CD-R",this); + layout->addWidget(ck_hcdr, 1,0); + ck_ss = new QCheckBox("SingleSession",this); + layout->addWidget(ck_ss,2,0); + ck_spdrd = new QCheckBox("SpeedRead",this); + layout->addWidget(ck_spdrd,3,0); + + + ck_bitset = new QCheckBox("Change BookType on DVD+R",this); + layout->addWidget(ck_bitset,4,0,1,3); + ck_bitset_dl = new QCheckBox("Change BookType on DVD+R DL",this); + layout->addWidget(ck_bitset_dl,5,0,1,3); + ck_simulplus = new QCheckBox("Testwrite on DVD+R(W)",this); + layout->addWidget(ck_simulplus,6,0,1,3); + + as = new devctlAutostrategy(dev,this); + layout->addWidget(as,8,0,1,3); + layout->setRowStretch(7,10); + layout->setRowStretch(8,6); + + ck_prec->setEnabled(dev->features.supported & FEATURE_POWEREC); + if (dev->features.supported & FEATURE_POWEREC) + l_prec->setText(QString::number(dev->features.prec_spd)+"X"); + ck_hcdr->setEnabled(dev->features.supported & FEATURE_HIDECDR); + ck_ss->setEnabled(dev->features.supported & FEATURE_SINGLESESSION); + ck_spdrd->setEnabled(dev->features.supported & FEATURE_SPEEDREAD); + ck_bitset->setEnabled(dev->features.supported & FEATURE_BITSETR); + ck_bitset_dl->setEnabled(dev->features.supported & FEATURE_BITSETRDL); + ck_simulplus->setEnabled(dev->features.supported & FEATURE_SIMULPLUS); + + ck_prec->setChecked(dev->features.enabled & FEATURE_POWEREC); + ck_hcdr->setChecked(dev->features.enabled & FEATURE_HIDECDR); + ck_ss->setChecked(dev->features.enabled & FEATURE_SINGLESESSION); + ck_spdrd->setChecked(dev->features.enabled & FEATURE_SPEEDREAD); + ck_bitset->setChecked(dev->features.enabled & FEATURE_BITSETR); + ck_bitset_dl->setChecked(dev->features.enabled & FEATURE_BITSETRDL); + ck_simulplus->setChecked(dev->features.enabled & FEATURE_SIMULPLUS); + + connect(ck_prec, SIGNAL(toggled(bool)), this, SLOT(set_prec(bool))); + connect(ck_hcdr, SIGNAL(toggled(bool)), this, SLOT(set_hcdr(bool))); + connect(ck_ss, SIGNAL(toggled(bool)), this, SLOT(set_ss(bool))); + connect(ck_spdrd, SIGNAL(toggled(bool)), this, SLOT(set_spdrd(bool))); + connect(ck_bitset, SIGNAL(toggled(bool)), this, SLOT(set_bitset(bool))); + connect(ck_bitset_dl, SIGNAL(toggled(bool)), this, SLOT(set_bitset_dl(bool))); + connect(ck_simulplus, SIGNAL(toggled(bool)), this, SLOT(set_simulplus(bool))); + + +} + +void devctlCommon::set_prec(bool en) { dev->setFeature(FEATURE_POWEREC, en); } +void devctlCommon::set_hcdr(bool en) { dev->setFeature(FEATURE_HIDECDR, en); } +void devctlCommon::set_ss(bool en) { dev->setFeature(FEATURE_SINGLESESSION, en); } +void devctlCommon::set_spdrd(bool en) { dev->setFeature(FEATURE_SPEEDREAD, en); } +void devctlCommon::set_bitset(bool en){ dev->setFeature(FEATURE_BITSETR, en); } +void devctlCommon::set_bitset_dl(bool en) { dev->setFeature(FEATURE_BITSETRDL, en); } +void devctlCommon::set_simulplus(bool en) { dev->setFeature(FEATURE_SIMULPLUS, en); } + +// GigaRec control + +devctlGigarec::devctlGigarec(device* idev, QWidget* p) + :QGroupBox("GigaRec",p) +{ + QString gc; + + dev = idev; + layout = new QVBoxLayout(this); + layout->setMargin(3); + layout->setSpacing(3); + + setCheckable(true); + bool ratio_14 = dev->dev.startsWith("CD-R PREMIUM"); + bool ratio_11 = dev->dev.startsWith("DVDR PX-755") || dev->dev.startsWith("DVDR PX-760") || dev->dev.startsWith("CD-R PREMIUM2"); + + grec_ratio = new TextSlider(Qt::Vertical,this); + grec_ratio->setTickPosition(QSlider::TicksBelow); + grec_ratio->setUpsideDown(1); + + gc = QString::number((int)(dev->media.ctots*0.6) >> 9) + " MB"; + grec_ratio->addItem("0.6",ratio_14); + gc = QString::number((int)(dev->media.ctots*0.7) >> 9) + " MB"; + grec_ratio->addItem("0.7",1); + gc = QString::number((int)(dev->media.ctots*0.8) >> 9) + " MB"; + grec_ratio->addItem("0.8",1); + gc = QString::number((int)(dev->media.ctots*0.9) >> 9) + " MB"; + grec_ratio->addItem("0.9",ratio_11); + //grec_ratio->addItem("OFF",1); + grec_ratio->addItem("",0); + gc = QString::number((int)(dev->media.ctots*1.1) >> 9) + " MB"; + grec_ratio->addItem("1.1",ratio_11); + gc = QString::number((int)(dev->media.ctots*1.2) >> 9) + " MB"; + grec_ratio->addItem("1.2",1); + gc = QString::number((int)(dev->media.ctots*1.3) >> 9) + " MB"; + grec_ratio->addItem("1.3",1); + gc = QString::number((int)(dev->media.ctots*1.4) >> 9) + " MB"; + grec_ratio->addItem("1.4",ratio_14); + grec_ratio->setValue(4); + + layout->addWidget(grec_ratio); + + l_cap = new QLabel(this); + l_cap->setMinimumHeight(24); +// l_cap->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + layout->addWidget(l_cap); + l_ncap = new QLabel(this); + l_ncap->setMinimumHeight(24); +// l_ncap->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + layout->addWidget(l_ncap); + + l_hint = new QLabel( + tr("GigaRec limits maximum write speed to 8X\n" + "only DAO write mode can be used with GigaRec"), this); + layout->addWidget(l_hint,10); + + //setEnabled(dev->features.supported & FEATURE_GIGAREC && dev->media.dstate == "Blank"); + setEnabled(dev->features.supported & FEATURE_GIGAREC); + setChecked(dev->features.enabled & FEATURE_GIGAREC); + + grec_ratio->setCurrentItem(QString("%1").arg(dev->features.grec,0,'f',1)); + l_cap->setText(tr("Original capacity: ") + QString::number(dev->media.ctots >> 9) + " MB"); + l_ncap->setText(tr("GigaRec capacity: ") + QString::number((int)(dev->media.ctots * dev->features.grec) >> 9) + " MB"); + //grec_ratio->setCurrentItem("1.2"); +#ifndef QT_NO_DEBUG + qDebug() << QString("GigaRec : %1").arg(dev->features.grec,0,'f',1); +#endif + connect(this, SIGNAL(toggled(bool)), this, SLOT(set(bool))); + connect(grec_ratio, SIGNAL(valueChanged(int)), this, SLOT(set())); +} + +void devctlGigarec::set() { set(isEnabled()); } + +void devctlGigarec::set(bool en) +{ + QStringList sl; + if (en) { + dev->features.enabled |= FEATURE_GIGAREC; + sl = grec_ratio->text().split(' '); + if (sl.size() > 0) + dev->features.grec = sl[0].toFloat(); + else + qDebug("empty GigaRec value!"); + } else { + dev->features.enabled &= ~FEATURE_GIGAREC; + } + if (!dev->setComplexFeature(FEATURE_GIGAREC, &dev->features)) { +// l_cap->setText(tr("Original capacity: ") + QString::number(dev->media.ctots >> 9) + " MB"); + l_ncap->setText(tr("GigaRec capacity: ") + QString::number((int)(dev->media.ctots * dev->features.grec) >> 9) + " MB"); + } +} + +// VariRec control + +devctlVarirecBase::devctlVarirecBase(device* idev, QString title, QWidget* p) + :QGroupBox(title, p) +{ + dev = idev; + layout = new QVBoxLayout(this); + layout->setMargin(3); + layout->setSpacing(3); + + setCheckable(true); + + pl_pwr = new QLabel("Power:", this); + layout->addWidget(pl_pwr); + + pwr = new TextSlider(Qt::Horizontal,this); + pwr->setTickPosition(QSlider::TicksBelow); + pwr->setUpsideDown(0); + pwr->addItem("-4",1); + pwr->addItem("-3",1); + pwr->addItem("-2",1); + pwr->addItem("-1",1); + pwr->addItem("0",1); + pwr->addItem("+1",1); + pwr->addItem("+2",1); + pwr->addItem("+3",1); + pwr->addItem("+4",1); + pwr->setValue(4); + layout->addWidget(pwr); + + layouts = new QHBoxLayout(); + layouts->setMargin(3); + layouts->setSpacing(3); + layout->addLayout(layouts); + + pl_str = new QLabel("Strategy:", this); + layouts->addWidget(pl_str,1); + str = new QComboBox(this); + layouts->addWidget(str,10); + + l_hint = new QLabel(this); + layout->addWidget(l_hint,10); +} + +devctlVarirecCD::devctlVarirecCD(device* idev, QWidget* p) + :devctlVarirecBase(idev, "Varirec CD", p) +{ + bool ext = dev->features.supported & FEATURE_VARIREC_CDEXT; + if (ext) + l_hint->setText(tr("VariRec limits maximum CD write speed to 8X")); + else + l_hint->setText(tr("VariRec limits maximum CD write speed to 4X")); + + str->addItem("default"); + str->addItem("AZO"); + str->addItem("Cyanine"); + str->addItem("Phtalocyanine A"); + str->addItem("Phtalocyanine B"); + str->addItem("Phtalocyanine C"); + str->addItem("Phtalocyanine D"); + + setEnabled(dev->features.supported & FEATURE_VARIREC_CD); + setChecked(dev->features.enabled & FEATURE_VARIREC_CD); + + pl_str->setEnabled(ext); + str->setEnabled(ext); + + pwr->setItemEnabled(0,ext); + pwr->setItemEnabled(1,ext); + pwr->setItemEnabled(7,ext); + pwr->setItemEnabled(8,ext); + + pwr->setValue(dev->features.vrec_cd_pwr+4); + str->setCurrentIndex(dev->features.vrec_cd_str); + + connect(pwr, SIGNAL(valueChanged(int)), this, SLOT(set())); + connect(str, SIGNAL(currentIndexChanged(int)), this, SLOT(set())); + connect(this, SIGNAL(toggled(bool)), this, SLOT(set(bool))); +} + +void devctlVarirecCD::set() { set(isEnabled()); } + +void devctlVarirecCD::set(bool en) +{ + if (en) { + dev->features.enabled |= FEATURE_VARIREC_CD; + dev->features.vrec_cd_pwr = pwr->text().toInt(); + dev->features.vrec_cd_str = str->currentIndex(); + } else { + dev->features.enabled &= ~FEATURE_VARIREC_CD; + } + dev->setComplexFeature(FEATURE_VARIREC_CD, &dev->features); +} + +devctlVarirecDVD::devctlVarirecDVD(device* idev, QWidget* p) + :devctlVarirecBase(idev, "Varirec DVD", p) +{ + l_hint->setText(tr("VariRec limits maximum DVD write speed to 4X")); + str->addItem("default"); + for (int s=0; s<8; s++) + str->addItem(QString("Strategy %1").arg(s)); + + setEnabled(dev->features.supported & FEATURE_VARIREC_DVD); + setChecked(dev->features.enabled & FEATURE_VARIREC_DVD); + + pwr->setValue(dev->features.vrec_dvd_pwr+4); + str->setCurrentIndex(dev->features.vrec_dvd_str); + + connect(pwr, SIGNAL(valueChanged(int)), this, SLOT(set())); + connect(str, SIGNAL(currentIndexChanged(int)), this, SLOT(set())); + connect(this, SIGNAL(toggled(bool)), this, SLOT(set(bool))); +} + +void devctlVarirecDVD::set() { set(isEnabled()); } + +void devctlVarirecDVD::set(bool en) +{ + if (en) { + dev->features.enabled |= FEATURE_VARIREC_DVD; + dev->features.vrec_dvd_pwr = pwr->text().toInt(); + dev->features.vrec_dvd_str = str->currentIndex(); + } else { + dev->features.enabled &= ~FEATURE_VARIREC_DVD; + } + dev->setComplexFeature(FEATURE_VARIREC_DVD, &dev->features); +} + +devctlVarirec::devctlVarirec(device* idev, QWidget* p) + :QWidget(p) +{ + layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(3); + + vrec_cd = new devctlVarirecCD(idev, this); + layout->addWidget(vrec_cd); + if (idev->features.supported & FEATURE_VARIREC_DVD) { + vrec_dvd = new devctlVarirecDVD(idev, this); + layout->addWidget(vrec_dvd); + } else { + vrec_dvd = NULL; + layout->addStretch(1); + } + +// qDebug() << "Varirec CD : " << (int)idev->features.vrec_cd_pwr << ", " << idev->features.vrec_cd_str; +// qDebug() << "Varirec DVD : " << (int)idev->features.vrec_dvd_pwr << ", " << idev->features.vrec_dvd_str; +} + +devctlSecurec::devctlSecurec(device* idev, QWidget* p) + :QGroupBox("SecuRec",p) +{ + dev = idev; + + layout = new QGridLayout(this); + layout->setMargin(3); + layout->setSpacing(3); + + l_pwd[0] = new QLabel(tr("Password"),this); + layout->addWidget(l_pwd[0], 0,0); + e_pwd[0] = new QLineEdit(this); + e_pwd[0]->setEchoMode(QLineEdit::Password); + layout->addWidget(e_pwd[0], 0,1,1,3); + l_pwd[1] = new QLabel(tr("Confirm"),this); + layout->addWidget(l_pwd[1], 1,0); + e_pwd[1] = new QLineEdit(this); + e_pwd[1]->setEchoMode(QLineEdit::Password); + layout->addWidget(e_pwd[1], 1,1,1,3); + + hline0 = new QFrame(this); + hline0->setFrameStyle(QFrame::HLine | QFrame::Sunken); + layout->addWidget(hline0, 2,0,1,4); + + l_active = new QLabel("SecuRec ACTIVE"); + l_active->setAlignment(Qt::AlignCenter); + layout->addWidget(l_active, 3, 0, 1, 2); + pb_set = new QPushButton(QIcon(":images/ok.png"),tr("Set"),this); + pb_set->setMinimumWidth(100); + layout->addWidget(pb_set, 3, 2); + pb_reset = new QPushButton(QIcon(":images/edit-clear.png"),tr("Reset"),this); + pb_reset->setMinimumWidth(100); + layout->addWidget(pb_reset, 3, 3); + + l_hint = new QLabel( + tr("Password length must me between 4 and 10 characters.\n\n" + "To read SecuRec-protected CD you have to activate SecuRec\n" + "with same password as used for writing"), this); + layout->addWidget(l_hint,4,0,1,4); + layout->setRowStretch(4,10); + + setEnabled(dev->features.supported & FEATURE_SECUREC); + l_active->setVisible(dev->features.enabled & FEATURE_SECUREC); + + connect(pb_set, SIGNAL(clicked()), this, SLOT(set())); + connect(pb_reset, SIGNAL(clicked()), this, SLOT(reset())); +} + +int devctlSecurec::validate() +{ + if (e_pwd[0]->text() != e_pwd[1]->text()) return 1; + if (e_pwd[0]->text().length() < 4 || e_pwd[0]->text().length() > 10) return 2; + return 0; +} + +void devctlSecurec::set() +{ + switch (validate()) { + case 0: + break; + case 1: + QMessageBox::warning(this,"Different passwords","Passwords are not identical"); + return; + case 2: + QMessageBox::warning(this,"Invalid password","Invalid password!\nLength must be between 4 and 10 chars"); + return; + } + dev->features.enabled |= FEATURE_SECUREC; + dev->features.sr_pass = e_pwd[0]->text(); + dev->setComplexFeature(FEATURE_SECUREC, &dev->features); + l_active->setVisible(dev->features.enabled & FEATURE_SECUREC); +} + +void devctlSecurec::reset() +{ + dev->features.enabled &= ~FEATURE_SECUREC; + dev->setComplexFeature(FEATURE_SECUREC, &dev->features); + e_pwd[0]->clear(); + e_pwd[1]->clear(); + l_active->setVisible(dev->features.enabled & FEATURE_SECUREC); +} + +int sm_rcd[] = {48,40,32,24,8,4,0}; +int sm_wcd[] = {48,32,24,16,8,4,0}; +int sm_rdvd[] = {16,12,8,5,2,0}; +// int sm_wdvd[] = {16,12,8,6,4,0}; + +devctlSilent::devctlSilent(device* idev, QWidget* p) + :QWidget(p) +{ + dev = idev; + int idx; + bool cd=0; + bool dvd=0; + + cw = new QGroupBox("Silent Mode",this); + cw->setCheckable(true); + +// R/W Speeds + h_speed = new QLabel(tr("Speed Limits"), cw); + h_speed->setAlignment(Qt::AlignCenter); + h_speed->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + + spdc = new QLabel(tr("CD"), cw); + spdd = new QLabel(tr("DVD"), cw); + spdr = new QLabel(tr("Read"), cw); + spdw = new QLabel(tr("Write"), cw); + box_rcd = new QComboBox(cw); + for (int i=0; sm_rcd[i]>0; i++) box_rcd->addItem(QString::number(sm_rcd[i]) + "X"); + box_wcd = new QComboBox(cw); + for (int i=0; sm_wcd[i]>0; i++) box_wcd->addItem(QString::number(sm_wcd[i]) + "X"); + box_rdvd = new QComboBox(cw); + for (int i=0; sm_rdvd[i]>0; i++) box_rdvd->addItem(QString::number(sm_rdvd[i]) + "X"); + box_wdvd = new QComboBox(cw); +// for (int i=0; sm_wdvd[i]>0; i++) box_wdvd->addItem(QString::number(sm_wdvd[i]) + "X"); + + if (!(dev->cap_rd & DEVICE_DVD)) { + spdd->setEnabled(false); + box_rdvd->setEnabled(false); + } + box_wdvd->setEnabled(false); + +// Access time + h_access = new QLabel(tr("Access Time"), cw); + h_access->setAlignment(Qt::AlignCenter); + h_access->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + + a_slow = new QRadioButton(tr("Slow"), cw); + a_fast = new QRadioButton(tr("Fast"), cw); + grp_access = new QButtonGroup(cw); + grp_access->addButton(a_slow,0); + grp_access->addButton(a_fast,1); + a_slow->setChecked(true); + +// Tray speed + h_tray = new QLabel(tr("Tray Speed"), cw); + h_tray->setAlignment(Qt::AlignCenter); + h_tray->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + + l_load = new QLabel(tr("Load"), cw); + l_eject = new QLabel(tr("Eject"), cw); + s_load = new QSlider(Qt::Horizontal,cw); + s_load->setRange(0,80); + s_eject = new QSlider(Qt::Horizontal,cw); + s_eject->setRange(0,80); + +// Buttons + ck_perm = new QCheckBox(tr("Permanent"), this); + pb_save = new QPushButton(QIcon(":images/ok.png"),tr("Save"), this); + pb_save->setMinimumWidth(100); + +// Layouts + layoutm = new QVBoxLayout(this); + layoutm->setMargin(0); + layoutm->setSpacing(3); + layoutm->addWidget(cw); + + layout = new QGridLayout(cw); + layout->setMargin(3); + layout->setSpacing(3); + + layout->addWidget(h_speed, 0,0,1,4); + + layout->addWidget(spdc,1,1,1,2); + layout->addWidget(spdd,1,3); + layout->addWidget(spdr,2,0); + layout->addWidget(box_rcd,2,1,1,2); + layout->addWidget(box_rdvd,2,3); + layout->addWidget(spdw,3,0); + layout->addWidget(box_wcd,3,1,1,2); + layout->addWidget(box_wdvd,3,3); + + layout->addWidget(h_access, 4,0,1,4); + + layout->addWidget(a_slow, 5,0, 1,2); + layout->addWidget(a_fast, 5,2, 1,2); + + layout->addWidget(h_tray, 6,0,1,4); + + layout->addWidget(l_load, 7,0); + layout->addWidget(s_load, 7,1,1,3); + layout->addWidget(l_eject, 8,0); + layout->addWidget(s_eject, 8,1,1,3); + + layout->setColumnStretch(0,2); + layout->setColumnStretch(1,1); + layout->setColumnStretch(2,1); + layout->setColumnStretch(3,2); + layout->setRowStretch(10,10); + + layoutb = new QHBoxLayout(); + layoutb->setMargin(0); + layoutb->setSpacing(3); + layoutm->addLayout(layoutb); + + layoutb->addStretch(10); + layoutb->addWidget(ck_perm); + layoutb->addWidget(pb_save); + + cd = dev->media.type.startsWith("CD"); + dvd = dev->media.type.startsWith("DVD"); +/* + qDebug() << " SM RD CD :" << dev->features.sm_cd_rd; + qDebug() << " SM WR CD :" << dev->features.sm_cd_wr; + qDebug() << " SM RD DVD:" << dev->features.sm_dvd_rd; + qDebug() << "PSM RD CD :" << dev->features.psm_cd_rd; + qDebug() << "PSM WR CD :" << dev->features.psm_cd_wr; + qDebug() << "PSM RD DVD:" << dev->features.psm_dvd_rd; +*/ + idx = box_rcd->findText(QString::number(dvd ? dev->features.psm_cd_rd : dev->features.sm_cd_rd)+"X"); + if (idx>=0) box_rcd->setCurrentIndex(idx); + idx = box_wcd->findText(QString::number(dvd ? dev->features.psm_cd_wr : dev->features.sm_cd_wr)+"X"); + if (idx>=0) box_wcd->setCurrentIndex(idx); + + idx = box_rdvd->findText(QString::number(dvd ? dev->features.sm_dvd_rd : dev->features.psm_dvd_rd)+"X"); + if (idx>=0) box_rdvd->setCurrentIndex(idx); +// idx = box_wdvd->findText(QString::number(dvd ? dev->features.sm_wr : dev->features.sm_dvd_wr)+"X"); +// if (idx>=0) box_wdvd->setCurrentIndex(idx); + + if (dev->features.sm_access) + a_fast->setChecked(true); + else + a_slow->setChecked(true); + s_load->setValue(dev->features.sm_trayl); + s_eject->setValue(dev->features.sm_traye); +/* + connect(box_rcd, SIGNAL(currentIndexChanged(int)), this, SLOT(set())); + connect(box_wcd, SIGNAL(currentIndexChanged(int)), this, SLOT(set())); + connect(box_rdvd, SIGNAL(currentIndexChanged(int)), this, SLOT(set())); + connect(box_wdvd, SIGNAL(currentIndexChanged(int)), this, SLOT(set())); + + connect(a_fast, SIGNAL(toggled(bool)), this, SLOT(set())); + + connect(s_load, SIGNAL(valueChanged(int)), this, SLOT(set())); + connect(s_eject, SIGNAL(valueChanged(int)), this, SLOT(set())); +*/ + + setEnabled(dev->features.supported & FEATURE_SILENT); + cw->setChecked(dev->features.enabled & FEATURE_SILENT); + + connect(pb_save, SIGNAL(clicked()), this, SLOT(set())); +// connect(this, SIGNAL(toggled(bool)), this, SLOT(set(bool))); +} + +void devctlSilent::set() +{ + bool en = cw->isChecked(); + if (en) { + dev->features.enabled |= FEATURE_SILENT; + dev->features.sm_cd_rd = box_rcd->currentText().remove("X").toInt(); + dev->features.sm_cd_wr = box_wcd->currentText().remove("X").toInt(); + dev->features.sm_dvd_rd = box_rdvd->currentText().remove("X").toInt(); +// dev->features.sm_dvd_wr = box_wdvd->currentText().remove("X").toInt(); + dev->features.sm_access = a_fast->isChecked(); + dev->features.sm_trayl = s_load->value(); + dev->features.sm_traye = s_eject->value(); + } else { + dev->features.enabled &= ~FEATURE_SILENT; + } + + dev->features.sm_nosave = !ck_perm->isChecked(); + dev->setComplexFeature(FEATURE_SILENT, &dev->features); +} + +devctlAutostrategy::devctlAutostrategy(device* idev, QWidget* p) + :QWidget(p) +{ + dev = idev; + setEnabled(dev->features.supported & FEATURE_AS); + layout = new QGridLayout(this); + layout->setMargin(0); + layout->setSpacing(3); + +// AS mode selection + box_as = new QGroupBox(tr("AutoStrategy"), this); + layout->addWidget(box_as); + + layout_as = new QGridLayout(box_as); + layout_as->setMargin(3); + layout_as->setSpacing(3); + + rb_as_off = new QRadioButton("Off", box_as); + rb_as_auto = new QRadioButton("Auto", box_as); + rb_as_on = new QRadioButton("On", box_as); + rb_as_forced = new QRadioButton("Forced", box_as); + rb_as_on->setEnabled(dev->features.supported & FEATURE_AS_EXT); + rb_as_forced->setEnabled(dev->features.supported & FEATURE_AS_EXT); + + grp_mode = new QButtonGroup(box_as); + grp_mode->addButton(rb_as_off, AS_MODE_OFF); + grp_mode->addButton(rb_as_auto, AS_MODE_AUTO); + grp_mode->addButton(rb_as_on, AS_MODE_ON); + grp_mode->addButton(rb_as_forced, AS_MODE_FORCED); + rb_as_off->setChecked( dev->features.as_mode == AS_MODE_OFF ); + rb_as_auto->setChecked( dev->features.as_mode == AS_MODE_AUTO ); + rb_as_on->setChecked( dev->features.as_mode == AS_MODE_ON ); + rb_as_forced->setChecked( dev->features.as_mode == AS_MODE_FORCED ); + + layout_as->addWidget(rb_as_off, 0, 0); + layout_as->addWidget(rb_as_auto, 0, 1); + layout_as->addWidget(rb_as_on, 0, 2); + layout_as->addWidget(rb_as_forced, 0, 3); + + pb_asdb = new QPushButton(tr("AutoStrategy DataBase"), box_as); + layout_as->addWidget(pb_asdb, 1,2,1,2); + +// MQCK + box_mqck = new QGroupBox(tr("Media Quality Check"), this); + layout->addWidget(box_mqck); + + layout_mqck = new QGridLayout(box_mqck); + layout_mqck->setMargin(3); + layout_mqck->setSpacing(3); + + rb_mqck_q = new QRadioButton(tr("Quick"), box_mqck); + rb_mqck_a = new QRadioButton(tr("Advanced"), box_mqck); + grp_mqck = new QButtonGroup(box_mqck); + grp_mqck->addButton(rb_mqck_q); + grp_mqck->addButton(rb_mqck_a); + rb_mqck_q->setChecked(true); + + lmqck_spd = new QLabel(tr("Speed:"), box_mqck); + lmqck_spd->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + bmqck_spd = new QComboBox(box_mqck); + bmqck_spd->addItems( dev->media.wspeedsd ); + + pb_mqck = new QPushButton(tr("Check Media"), box_mqck); + lmqck_res = new QLabel(box_mqck); + lmqck_res->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + lmqck_res->setMinimumHeight(22); + + layout_mqck->addWidget(rb_mqck_q, 0,0); + layout_mqck->addWidget(rb_mqck_a, 0,1); + layout_mqck->addWidget(lmqck_spd, 0,2); + layout_mqck->addWidget(bmqck_spd, 0,3); + layout_mqck->addWidget(pb_mqck, 1,2,1,2); + layout_mqck->addWidget(lmqck_res, 2,0,1,4); + +// layout->setRowStretch(2,4); + + connect(grp_mode, SIGNAL(buttonClicked(int)), this, SLOT(set_mode(int))); + connect(pb_asdb, SIGNAL(clicked()), this, SLOT(asdb())); + connect(pb_mqck, SIGNAL(clicked()), this, SLOT(run_mqck())); +} + +void devctlAutostrategy::asdb() +{ + qDebug("AS: DB..."); + devctlAutostrategyDB *asdb = new devctlAutostrategyDB(dev, this); + asdb->exec(); + delete asdb; +} + +void devctlAutostrategy::set_mode(int mode) +{ + qDebug("AS: set mode..."); + dev->features.as_action = AS_ACTION_MODE; + dev->features.as_mode = mode; + dev->setComplexFeature(FEATURE_AS, &dev->features); +} + +void devctlAutostrategy::run_mqck() +{ + ProgressWidget *progress; + qDebug("AS: MQCK..."); + lmqck_res->setText( "" ); + dev->features.as_action = AS_ACTION_MQCK; + dev->features.as_act_mode = rb_mqck_a->isChecked() ? ASMQCK_ADV : 0; + dev->features.as_mqckspd = bmqck_spd->currentText().remove(QRegExp(".[0-9][Xx]")).toInt(); + dev->features.as_mqckres = ""; + if (!dev->startMqck()) { + QMessageBox::warning(this, "MQCK error", tr("Can't run cdvdcontrol!") ); + return; + } + + progress = new ProgressWidget(10,3,this); + progress->setText(tr("Checking media quality...")); + progress->show(); + while(dev->isRunning()) { msleep ( 1 << 5); qApp->processEvents(); } + delete progress; + if (dev->features.as_mqckres.isEmpty()) { + lmqck_res->setText( tr("Unknown result") ); + } else { + lmqck_res->setText( dev->features.as_mqckres); + } +} + +devctlAutostrategyDB::devctlAutostrategyDB(device* idev, QWidget* p) + :QDialog(p) +{ + dev = idev; + setWindowTitle(tr("AutoStrategy DataBase")); + + tree = new QTreeWidget(this); + tree->setRootIsDecorated(false); + tree->setHeaderLabels( QStringList() << "Act" << tr("Type") << tr("Media ID") << tr("Speed") << tr("Writes")); + tree->setColumnWidth(0,30); + tree->setColumnWidth(1,50); + tree->setColumnWidth(2,150); + tree->setColumnWidth(3,50); + tree->setColumnWidth(4,50); + + pb_act = new QPushButton(tr("Activate"), this); + pb_deact = new QPushButton(tr("Deactivate"), this); + pb_del = new QPushButton(QIcon(":images/x.png"),tr("Remove"), this); + pb_clear = new QPushButton(QIcon(":images/edit-clear.png"),tr("Clear"), this); + + layout = new QVBoxLayout(this); + layout->setMargin(3); + layout->setSpacing(3); + layout->addWidget(tree,10); + + layoutb = new QHBoxLayout(); + layout->addLayout(layoutb); + layoutb->setMargin(0); + layoutb->setSpacing(3); + layoutb->addWidget(pb_act,1); + layoutb->addWidget(pb_deact,1); + layoutb->addWidget(pb_del,1); + layoutb->addWidget(pb_clear,1); + +// Strategy creation + box_ascre = new QGroupBox(tr("Strategy creation"), this); + box_ascre->setEnabled(dev->features.supported & FEATURE_AS_EXT); +// box_ascre->setAlignment(Qt::AlignCenter); +// box_ascre->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + layout->addWidget(box_ascre); + + grp_ascre_mode = new QGroupBox(tr("Mode"), box_ascre); + grp_ascre_act = new QGroupBox(tr("DB Action"), box_ascre); + ascre_start = new QPushButton(tr("Create"), box_ascre); + + layout_ascre = new QGridLayout(box_ascre); + layout_ascre->setMargin(0); + layout_ascre->setSpacing(3); + layout_ascre->addWidget(grp_ascre_mode, 0,0,1,2); + layout_ascre->addWidget(grp_ascre_act, 0,2,1,2); + layout_ascre->addWidget(ascre_start, 1,3); + layout_ascre->setRowStretch(0,2); + + rb_ascre_quick = new QRadioButton(tr("Quick"), grp_ascre_mode); + rb_ascre_full = new QRadioButton(tr("Full"), grp_ascre_mode); + rb_ascre_quick->setChecked(true); + layout_crem = new QVBoxLayout(grp_ascre_mode); + layout_crem->setMargin(3); + layout_crem->setSpacing(3); + layout_crem->addWidget(rb_ascre_quick); + layout_crem->addWidget(rb_ascre_full); + + rb_ascre_add = new QRadioButton(tr("Add"), grp_ascre_act); + rb_ascre_replace = new QRadioButton(tr("Replace"), grp_ascre_act); + rb_ascre_add->setChecked(true); + layout_crea = new QVBoxLayout(grp_ascre_act); + layout_crea->setMargin(3); + layout_crea->setSpacing(3); + layout_crea->addWidget(rb_ascre_add); + layout_crea->addWidget(rb_ascre_replace); + + setMinimumWidth(360); + + connect(tree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(db_toggle(QTreeWidgetItem*))); + + connect(pb_act, SIGNAL(clicked()), this, SLOT(db_act())); + connect(pb_deact, SIGNAL(clicked()), this, SLOT(db_deact())); + connect(pb_del, SIGNAL(clicked()), this, SLOT(db_del())); + connect(pb_clear, SIGNAL(clicked()), this, SLOT(db_clear())); + + connect(ascre_start, SIGNAL(clicked()), this, SLOT(run_ascre())); +} + +void devctlAutostrategyDB::setVisible(bool v) +{ + QDialog::setVisible(v); + //if (v && !dev->asdb.size()) db_update(); + if (v) db_update(); +} + +void devctlAutostrategyDB::db_update(int idx) +{ + QTreeWidgetItem *it; + ProgressWidget *progress; + + connect(dev, SIGNAL(doneGetASDB(bool)), this, SLOT(db_update_done(bool))); + + dev->getASDB(); + + progress = new ProgressWidget(10,3,this); + progress->setText(tr("Retrieving ASDB...")); + progress->show(); + while(dev->isRunning()) { msleep ( 1 << 5); qApp->processEvents(); } +#if 0 + for (int i=0; i<10; i++) { + msleep ( 1 << 7); + progress->step(); + qApp->processEvents(); + } +#endif + delete progress; + +// clear tree + while (tree->topLevelItemCount()) { + it = tree->takeTopLevelItem(0); + if (it) delete it; + } +// show updated db + for (int i=0; iasdb.size(); i++) { + if (dev->asdb[i].present) { + it = new QTreeWidgetItem(tree, + QStringList() << "" << dev->asdb[i].type << dev->asdb[i].mid << dev->asdb[i].speed << dev->asdb[i].writes); + if (dev->asdb[i].active) it->setIcon(0,QIcon(":images/ok.png")); + tree->addTopLevelItem(it); + } + } + + if (idx>=0) { + if (idx >= dev->asdb.size()) idx = dev->asdb.size()-1; + it = tree->topLevelItem(idx); + if (it) tree->setCurrentItem(it); + } + + for (int i=0; i<4; i++) + tree->resizeColumnToContents(i); + + disconnect(dev, SIGNAL(doneGetADSB(bool)), this, SLOT(db_update_done(bool))); +} + +void devctlAutostrategyDB::db_update_done(bool fail) +{ + if (fail) { + QMessageBox::warning(this, tr("Error"), tr("Error requesting ASDB!")+"\n"+tr("cdvdcontrol finished with non-zero exit code")); + } +} + +void devctlAutostrategyDB::db_toggle(QTreeWidgetItem* item) +{ + int idx = tree->indexOfTopLevelItem(item); + if (dev->asdb[idx].active) { + db_deact(idx); + } else { + db_act(idx); + } +} + +void devctlAutostrategyDB::db_act() +{ + db_act(tree->indexOfTopLevelItem(tree->currentItem())); +} + +void devctlAutostrategyDB::db_act(int idx) +{ + if (idx<0 || idx>31) return; + dev->features.as_action = AS_ACTION_ACT; + dev->features.as_idx = idx+1; + dev->setComplexFeature(FEATURE_AS, &dev->features); + db_update(idx); +} + +void devctlAutostrategyDB::db_deact() +{ + db_deact(tree->indexOfTopLevelItem(tree->currentItem())); +} + +void devctlAutostrategyDB::db_deact(int idx) +{ + if (idx<0 || idx>31) return; + dev->features.as_action = AS_ACTION_DEACT; + dev->features.as_idx = idx+1; + dev->setComplexFeature(FEATURE_AS, &dev->features); + db_update(idx); +} + +void devctlAutostrategyDB::db_del() +{ + int idx = tree->indexOfTopLevelItem(tree->currentItem()); + if (idx<0) return; + if (QMessageBox::warning(this, + tr("Are you sure?"), + tr("Are you sure to delete strategy #%1?").arg(idx), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel) != QMessageBox::Yes) + return; + + dev->features.as_action = AS_ACTION_DEL; + dev->features.as_idx = idx+1; + dev->setComplexFeature(FEATURE_AS, &dev->features); + db_update(idx); +} + +void devctlAutostrategyDB::db_clear() +{ + if (QMessageBox::warning(this, + tr("Are you sure?"), + tr("Are you sure delete all strategies?"), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel) != QMessageBox::Yes) + return; + dev->features.as_action = AS_ACTION_CLEAR; + dev->setComplexFeature(FEATURE_AS, &dev->features); + db_update(); +} + +void devctlAutostrategyDB::run_ascre() +{ + ProgressWidget *progress; + qDebug("AS: creating strategy..."); +// dev-> + if (dev->media.creads || !dev->media.type.startsWith("DVD") || dev->media.erasable == "yes") { + QMessageBox::warning(this, + tr("Can't create strategy!"), + tr("Strategy creation does not supported on current media: ") + dev->media.type + "\n"+ + tr("supported media types: DVD+R(DL) and DVD-R(DL)") + ); + return; + } + + + dev->features.as_action = AS_ACTION_CRE; + dev->features.as_act_mode = (rb_ascre_full->isChecked() ? ASCRE_FULL : 0) | (rb_ascre_replace->isChecked() ? ASCRE_REPLACE : 0); + dev->startAScre(); + + progress = new ProgressWidget(10,3,this); + progress->setText(tr("Creating strategy...")); + progress->show(); + while(dev->isRunning()) { msleep ( 1 << 5); qApp->processEvents(); } + delete progress; + + db_update(); +} + +devctlDestruct::devctlDestruct(device* idev, QWidget* p) + :QGroupBox(tr("Data destruction"), p) +{ + dev = idev; + setEnabled(dev->features.supported & FEATURE_DESTRUCT); + rb_quick = new QRadioButton(tr("Quick"), this); + pl_quick = new QLabel(tr("Destruct Lead-in & TOC"), this); + rb_full = new QRadioButton(tr("Full"), this); + pl_full = new QLabel(tr("Destruct entire disc"), this); + pb_start = new QPushButton(QIcon(":images/eraser.png"),tr("Destruct"), this); + pb_start->setMinimumWidth(100); + rb_quick->setChecked(true); + + layout = new QGridLayout(this); + layout->setMargin(3); + layout->setSpacing(3); + layout->addWidget(rb_quick, 0,0,1,3); + layout->addWidget(pl_quick, 1,1,1,2); + layout->addWidget(rb_full, 2,0,1,3); + layout->addWidget(pl_full, 3,1,1,2); + layout->addWidget(pb_start, 5,2); + + layout->setColumnStretch(0,1); + layout->setColumnStretch(1,10); + layout->setColumnStretch(2,1); + layout->setRowStretch(4,10); + + rb_quick->setChecked(true); + + connect(pb_start, SIGNAL(clicked()), this, SLOT(start())); +} + +void devctlDestruct::start() +{ + ProgressWidget *progress; + if (QMessageBox::warning(this, + tr("Are you sure?"), + tr("Media will be unreadable after this operation!"), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel) != QMessageBox::Yes) + return; + + qDebug("Destructing media..."); + dev->features.as_act_mode = !!rb_full->isChecked(); + dev->startDestruct(); + + progress = new ProgressWidget(10,3,this); + progress->setText(tr("Destructing data...")); + progress->show(); + while(dev->isRunning()) { msleep ( 1 << 5); qApp->processEvents(); } + delete progress; +} + +// Pioneer QuietMode + +devctlPioquiet::devctlPioquiet(device* idev, QWidget* p) + :QGroupBox(tr("Pioneer Quiet Mode"),p) +{ + dev = idev; + setEnabled( dev->features.supported & FEATURE_PIOQUIET ); + + layout = new QGridLayout(this); + layout->setMargin(3); + layout->setSpacing(3); + + box_mode = new QGroupBox(tr("Profile"),this); + layout->addWidget(box_mode,0,0,1,3); + + layoutm = new QVBoxLayout(box_mode); + layoutm->setMargin(3); + layoutm->setSpacing(3); + + rb_quiet = new QRadioButton(tr("Quiet"), box_mode); + rb_std = new QRadioButton(tr("Standard"), box_mode); + rb_perf = new QRadioButton(tr("Performance"), box_mode); + layoutm->addWidget(rb_quiet); + layoutm->addWidget(rb_std); + layoutm->addWidget(rb_perf); + rb_quiet->setChecked( dev->features.pioq_quiet == PIOQ_QUIET ); + rb_perf->setChecked( dev->features.pioq_quiet == PIOQ_PERF ); + rb_std->setChecked( dev->features.pioq_quiet == PIOQ_STD ); + + ck_limit = new QCheckBox(tr("Limit read speed to 24X for CD and 8X for DVD"),this); + layout->addWidget(ck_limit, 1,0,1,3); + ck_limit->setChecked( dev->features.enabled & FEATURE_PIOLIMIT ); + + ck_perm = new QCheckBox(tr("Permanent"),this); + layout->addWidget(ck_perm, 3,1); + pb_set = new QPushButton(QIcon(":images/ok.png"),tr("Set"),this); + pb_set->setMinimumWidth(100); + layout->addWidget(pb_set, 3,2); + + layout->setRowStretch(0,2); + layout->setRowStretch(1,1); + layout->setRowStretch(2,10); + layout->setRowStretch(3,1); + + connect(pb_set, SIGNAL(clicked()), this, SLOT(set())); +} + +void devctlPioquiet::set() +{ + dev->features.pioq_nosave = !ck_perm->isChecked(); + if (ck_limit->isChecked()) + dev->features.enabled |= FEATURE_PIOLIMIT; + else + dev->features.enabled &= ~FEATURE_PIOLIMIT; + + if (rb_quiet->isChecked()) { + dev->features.pioq_quiet = PIOQ_QUIET; + } else if (rb_perf->isChecked()) { + dev->features.pioq_quiet = PIOQ_PERF; + } else { + dev->features.pioq_quiet = PIOQ_STD; + } + dev->setComplexFeature(FEATURE_PIOQUIET, &dev->features); +} + +// Yamaha CRW-F1 Disc T@2 + +devctlF1Tattoo::devctlF1Tattoo(device* idev, QWidget* p) + :QGroupBox("Yamaha CRW-F1 Disc T@2",p) +{ + dev = idev; + setEnabled(dev->features.supported & FEATURE_F1TATTOO); + + layout = new QGridLayout(this); + layout->setMargin(3); + layout->setSpacing(3); + + tw = new TattooWidget(this); + layout->addWidget(tw,0,0,1,2); + + r0=dev->features.tattoo_inner; + r1=dev->features.tattoo_outer; + tw->setRadius(r0,r1); + + layoutb = new QHBoxLayout(); + layoutb->setMargin(0); + layoutb->setSpacing(3); + layout->addLayout(layoutb,1,0,1,2); + + l_file = new QLabel(this); + l_file->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + layoutb->addWidget(l_file,20); + pb_load = new QPushButton(this); + pb_load->setMaximumSize(24,24); + pb_load->setIcon(QIcon(":images/fileopen.png")); + layoutb->addWidget(pb_load,1); + + pb_burn = new QPushButton(QIcon(":images/tattoo.png"), tr("Burn T@2"), this); + pb_burn->setMinimumWidth(100); + pb_burn->setEnabled(false); + layout->addWidget(pb_burn,2,1); + + layout->setRowStretch(0,20); + layout->setColumnStretch(0,5); + layout->setColumnStretch(1,1); + + connect(pb_load, SIGNAL(clicked()), this, SLOT(loadImage())); + connect(pb_burn, SIGNAL(clicked()), this, SLOT(burn())); +} + +void devctlF1Tattoo::loadImage() +{ + + QString fn = QFileDialog::getOpenFileName(this, tr("Select T@2 image...")); + if (fn == QString::null) return; + + if (!srcimg.load(fn)) { + QMessageBox::warning(this, tr("Error"), tr("Can't load image: ") + fn); + return; + } + qDebug() << "Loaded image: " << srcimg.width() << "x" << srcimg.height(); + l_file->setText(fn); + + img = srcimg.scaled(F1TATTOOW, (r1-r0), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + qDebug() << "Scaled image: " << img.width() << "x" << img.height(); + tw->setImage(img); + + pb_burn->setEnabled(srcimg.width() && srcimg.height()); +} + +void devctlF1Tattoo::burn() +{ + ProgressWidget *progress; + QString tfn; + QFile tf; + QByteArray line(F1TATTOOW, 0); + + qDebug("Generating T@2 data..."); + //QImage timg = img.scaled(F1TATTOOW, (r1-r0+1), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + qDebug() << "T@2 image size: " << img.width() << "x" << img.height(); + + QStringList env = QProcess::systemEnvironment(); + int hidx = env.indexOf(QRegExp("HOME=(.*)")); + if (hidx>=0) { + tfn = env[hidx].remove(0,5); + } else { + tfn = "/tmp"; + } + tfn += "/tmp.tattoo"; + qDebug() << "T@2 temporary file: " << tfn; +// img.save(tfn + ".png"); + + tf.setFileName(tfn); + if (!tf.open(QIODevice::WriteOnly)) + { + QMessageBox::critical(this, tr("Error"), tr("Can't create DiscT@2 temporary file!")); + return; + } + + for(int y=img.height()-1; y>=0; y--) { + for (int x=0; xfeatures.tattoo_file = tfn; + dev->startTattoo(); + + + progress = new ProgressWidget(10,3,this); + progress->setText(tr("Burning DiscT@2 image...")); + progress->show(); + while(dev->isRunning()) { msleep ( 1 << 5); qApp->processEvents(); } + +#if 0 + for(int i=0; i<16; i++) { + msleep ( 1 << 7); + progress->step(); + qApp->processEvents(); + } +#endif + delete progress; +} + diff --git a/gui/src/errc_detailed.cpp b/gui/src/errc_detailed.cpp new file mode 100644 index 0000000..f9ffa6c --- /dev/null +++ b/gui/src/errc_detailed.cpp @@ -0,0 +1,225 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009-2012 Gennady "ShultZ" Kozlov + * + * 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 +//#include +#include +#include + +#include +#include + +#include "errc_detailed.h" +#include + + +static const char* labels_cd[8] = { + "BLER", + "E11", + "E21", + "E31", + "E12", + "E22", + "E32", + "UNCR" +}; + +static const char* labels_dvd[8] = { + "", + "PIE", + "PI8", + "PIF", + "POE", + "PO8", + "POF", + "UNCR" +}; + +static const char* labels_bd[8] = { + "", + "LDC", + "", + "", + "BIS", + "", + "", + "UNCR" +}; + +static const char* labels_null[8] = { "", "", "", "", "", "", "", "" }; + +ErrcDetailedDialog::ErrcDetailedDialog(QPxSettings *iset, devlist *idev, QWidget *p, Qt::WindowFlags fl) + : QDialog(p,fl) +{ +#ifndef QT_NO_DEBUG + qDebug("ErrcDetailedDialog()"); +#endif + devices = idev; + device *dev = devices->current(); + settings = iset; + + if (dev->media.type.startsWith("CD-")) { + labels = labels_cd; + } else if (dev->media.type.startsWith("DVD")) { + labels = labels_dvd; + } else if (dev->media.type.startsWith("BD-")) { + labels = labels_bd; + } else { + labels = labels_null; + } + + setWindowTitle("QPxTool - " + tr("Detailed Error Correction")); + + layout = new QGridLayout(this); + layout->setMargin(3); + layout->setSpacing(3); + layout_top = new QGridLayout(); + layout_top->setMargin(0); + layout_top->setSpacing(3); + layout->addLayout(layout_top, 0, 0, 1, 3); + + settings->loadScale("XERRC"); + + graph[0] = new QPxGraph(iset, idev, "XERRC", TEST_ERRC, this); + layout_top->addWidget(graph[0], 0, 1); + graph[7] = new QPxGraph(iset, idev, "XERRC", TEST_ERRC, this); + layout_top->addWidget(graph[7], 0, 2); + + for (int i=0; i<6; i++) { + graph[i+1] = new QPxGraph(iset, idev, "XERRC", TEST_ERRC, this); + layout->addWidget(graph[i+1], i/3+1, i%3); + } + + for (int i=0; i<8; i++) { + graph[i]->setErrcList(1<setShowSpeed(0); + graph[i]->setRightMarginHidden(true); + } + + layout_summary = new QGridLayout(); + layout_summary->setMargin(0); + layout_summary->setHorizontalSpacing(3); + layout_summary->setVerticalSpacing(1); + layout_top->addLayout(layout_summary, 0, 0); + + layout_top->setColumnStretch(0,1); + layout_top->setColumnStretch(1,1); + layout_top->setColumnStretch(2,1); + + pl_tot = new QLabel("Tot",this); + pl_tot->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_summary->addWidget(pl_tot,0,1, 1,2); + pl_max = new QLabel("Max",this); + pl_max->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_summary->addWidget(pl_max,0,3); + pl_avg = new QLabel("Avg",this); + pl_avg->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_summary->addWidget(pl_avg,0,4); + + hline0 = new QFrame(this); + hline0->setFrameStyle(QFrame::Sunken | QFrame::HLine); + layout_summary->addWidget(hline0, 1,0, 1,5); + + for (int i=0; i<8; i++) { + pl_name[i] = new QLabel(labels[i], this); + pl_name[i]->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + layout_summary->addWidget(pl_name[i], i+2, 0); + l_tot[i] = new QLabel(this); + l_tot[i]->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_summary->addWidget(l_tot[i], i+2, 1, 1,2); + l_max[i] = new QLabel(this); + l_max[i]->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_summary->addWidget(l_max[i], i+2, 3); + l_avg[i] = new QLabel(this); + l_avg[i]->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_summary->addWidget(l_avg[i], i+2, 4); + } + + hline1 = new QFrame(this); + hline1->setFrameStyle(QFrame::Sunken | QFrame::HLine); + layout_summary->addWidget(hline1, 10,0, 1,5); + + layout_summary->setRowStretch(11,10); + + for (int i=0; i<8; i++) + connect(graph[i], SIGNAL(scaleChanged()), this, SLOT(changeScale())); +} + +ErrcDetailedDialog::~ErrcDetailedDialog() +{ +#ifndef QT_NO_DEBUG + qDebug("~ErrcDetailedDialog()"); +#endif +} + +void ErrcDetailedDialog::changeScale() +{ + QObject *sgraph = sender(); + for (int i=0; i<8; i++) { + if (sgraph != graph[i]) graph[i]->changeScale(); + } +} + +void ErrcDetailedDialog::updateAll() +{ + device *dev = devices->current(); + if (dev->media.type.startsWith("CD-")) { + labels = labels_cd; + } else if (dev->media.type.startsWith("DVD")) { + labels = labels_dvd; + } else if (dev->media.type.startsWith("BD-")) { + labels = labels_bd; + } else { + labels = labels_null; + } + + for (int i=0; i<8; i++) { + pl_name[i]->setText(labels[i]); + graph[i]->setErrcList(1<current(); + } else { + dev = idev; + } + + graph[0]->setVisible(labels == labels_cd); + graph[2]->setVisible(labels != labels_bd); + graph[3]->setVisible(labels != labels_bd); + graph[5]->setVisible(labels != labels_bd); + graph[6]->setVisible(labels != labels_bd); + for (int i=0; i<8; i++) { +// if (!dev->media.tdata_errc || (dev->media.tdata_errc & (1<setText( QString::number(dev->testData.errcTOT.raw.err[i]) ); + l_max[i]->setNum( dev->testData.errcMAX.raw.err[i] ); + l_avg[i]->setText( QString::number(dev->testData.errcAVG.raw.err[i], 'f', 2) ); + // graph[i]->setErrcList(1<update(); +// } else { +// graph[i]->hide(); +// } + } +} + +void ErrcDetailedDialog::hideEvent(QHideEvent* e) +{ + emit closed(); + QDialog::hideEvent(e); +} + diff --git a/gui/src/graphtab.cpp b/gui/src/graphtab.cpp new file mode 100644 index 0000000..8f94cb7 --- /dev/null +++ b/gui/src/graphtab.cpp @@ -0,0 +1,138 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include + +#include +#include "graphtab.h" + +#include +#include +#include + +#include + +GraphTab::GraphTab(QPxSettings *iset, devlist *idev, QString iname, int test, QWidget *p, Qt::WindowFlags fl) + : QWidget(p,fl) +{ +#ifndef QT_NO_DEBUG + qDebug("STA: GraphTab()"); +#endif + settings = iset; + devices = idev; + name = iname; +// prevTvalid = 0; + gettimeofday(&prevT, NULL); + settings->loadScale(name); + + layout = new QHBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(3); + + split = new SplitButton(Qt::Vertical, this); + layout->addWidget(split); + connect(split, SIGNAL(clicked()), this, SLOT(infoToggle())); + + lw = new QWidget(this); + lw->setMinimumWidth(80); + layout->addWidget(lw); + + layoutl = new QVBoxLayout(lw); + layoutl->setMargin(0); + layoutl->setSpacing(3); +// layout->addLayout(layoutl); + + infow = new QWidget(lw); + infow->setMinimumWidth(80); + layoutl->addWidget(infow,20); +// layoutl->addStretch(1); + + grp_time = new QGroupBox(tr("Time"), lw); + grp_time->setMinimumWidth(100); + layoutl->addWidget(grp_time,1); + + layoutt = new QVBoxLayout(grp_time); + layoutt->setMargin(3); + layoutt->setSpacing(0); + + ltime = new QLabel(grp_time); + ltime->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + ltime->setMinimumHeight(22); + QFont tfont = ltime->font(); + tfont.setFamily("Monospace"); + ltime->setFont( tfont ); + + layoutt->addWidget(ltime); + + vline0 = new QFrame(this); + vline0->setFrameStyle(QFrame::VLine | QFrame::Sunken); + layout->addWidget(vline0); + + graph = new QPxGraph(iset, idev, name, test, this); +// graph->setDataNames(QStringList() << "speed_rt" << "speed_wt"); + layout->addWidget(graph); + + clear(); +#ifndef QT_NO_DEBUG + qDebug("END: GraphTab()"); +#endif +} + +GraphTab::~GraphTab() {} + +void GraphTab::clear() {} + +void GraphTab::infoToggle() { lw->setVisible(!lw->isVisible()); } + +void GraphTab::updateLast(int time, bool *Tvalid, bool force) +{ + timeval curT; + float dt; + gettimeofday(&curT, NULL); + +// if (prevTvalid) + dt = curT.tv_sec - prevT.tv_sec + (curT.tv_usec - prevT.tv_usec) / 1000000.0; +// if (!prevTvalid || dt>0.5) { + if (force || dt>0.5) { + int s = time % 60; + int m = (time - s) / 60; + ltime->setText(QString("%1:%2").arg(m).arg(s, 2,10, QChar('0'))); + + gettimeofday(&prevT, NULL); + // prevTvalid = 1; + if (force) { + graph->update(); + } else { + int x = graph->getLastX(); + graph->update(x, 0, graph->width()-x, graph->height()); + } + if (Tvalid) *Tvalid=1; + return; + } + if (Tvalid) *Tvalid=0; +} + +void GraphTab::drawGraph(QImage& img, device *dev, int ttype, int eflags) +{ + int w = img.width(); + int h = img.height(); + QSize s(w,h); + QRect r(0, 0, w, h); + QPainter p(&img); + + graph->drawGraph(&p, s, dev, ttype, r, (ttype == TEST_ERRC) ? eflags : 0, FORCE_REPAINT); +} + diff --git a/gui/src/hostedit_dialog.cpp b/gui/src/hostedit_dialog.cpp new file mode 100644 index 0000000..cd7805d --- /dev/null +++ b/gui/src/hostedit_dialog.cpp @@ -0,0 +1,80 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include + +#include "hostedit_dialog.h" + +hostEditDialog::hostEditDialog(QString host, int port, QWidget* p, Qt::WindowFlags f) + : QDialog(p,f) +{ + setWindowTitle(tr("Add host")); + + layout = new QGridLayout(this); + layout->setMargin(3); + layout->setSpacing(3); + + l_host = new QLabel(tr("Host:"), this); + layout->addWidget(l_host, 0, 0); + + e_host = new QLineEdit(this); + e_host->setText(host); +// e_host->setInputMask("000.000.000.000; "); + layout->addWidget(e_host, 0, 1, 1, 2); + + l_port = new QLabel(tr("Port:"), this); + layout->addWidget(l_port, 1, 0); + + e_port = new QSpinBox(this); + e_port->setMinimum(1); + e_port->setMaximum(65535); + e_port->setValue(port); + layout->addWidget(e_port, 1, 1); + + bdef = new QPushButton(this); + bdef->setMaximumSize(22,22); + bdef->setIcon(QIcon(":images/edit-undo.png")); + layout->addWidget(bdef, 1, 2); + + bbox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal , this); + layout->addWidget(bbox, 2, 1, 1, 2); + + layout->setColumnStretch(0,2); + layout->setColumnStretch(1,20); + layout->setColumnStretch(2,1); + + connect(e_host, SIGNAL(textChanged(QString&)), this, SLOT(hostChanged(QString&))); + connect(bdef, SIGNAL(clicked()), this, SLOT(setPortDfl())); + connect(bbox, SIGNAL(accepted()), this, SLOT(accept())); + connect(bbox, SIGNAL(rejected()), this, SLOT(reject())); +} + +hostEditDialog::~hostEditDialog() +{ + +} + +void hostEditDialog::setPortDfl() +{ + e_port->setValue(46660); +} + +void hostEditDialog::hostChanged(QString& h) +{ + QPushButton *pb = bbox->button(QDialogButtonBox::Ok); + if (!pb) return; + pb->setEnabled(!h.isEmpty()); +} + diff --git a/gui/src/image_label.cpp b/gui/src/image_label.cpp new file mode 100644 index 0000000..82ab031 --- /dev/null +++ b/gui/src/image_label.cpp @@ -0,0 +1,142 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 +//#include +#include + +#include "image_label.h" +#include + +#include + +#define MAX_IMAGE_SIZE 64 + +ImageLabel::ImageLabel(int iminW, QString label, QImage image, int idx, QWidget *parent) + : QWidget (parent) +{ +#ifndef QT_NO_DEBUG + qDebug("ImageLabel()"); +#endif + iconw = 64; + iconh = 64; + minW = qMax(iconw+4, iminW); + setMinimumSize(minW, iconh+24); + name = label; + img = image; + id = idx; + focus = 0; + if (!id) ck=1; else ck=0; +} + + +ImageLabel::ImageLabel(int iminW, QSize is, QString label, QImage image, int idx, QWidget *parent) + : QWidget (parent) +{ +#ifndef QT_NO_DEBUG + qDebug("ImageLabel()"); +#endif + iconw = is.width(); + iconh = is.height(); + minW = qMax(iconw+4, iminW); + setMinimumSize(minW, iconh+24); + name = label; + img = image; + id = idx; + focus = 0; + if (!id) ck=1; else ck=0; +} + +ImageLabel::ImageLabel(int iminW, int iw, int ih, QString label, QImage image, int idx, QWidget *parent) + : QWidget (parent) +{ +#ifndef QT_NO_DEBUG + qDebug("ImageLabel()"); +#endif + iconw = iw; + iconh = ih; + minW = qMax(iconw+4, iminW); + setMinimumSize(minW, iconh+24); + name = label; + img = image; + id = idx; + focus = 0; + if (!id) ck=1; else ck=0; +} + +ImageLabel::~ImageLabel() +{ +#ifndef QT_NO_DEBUG + qDebug("~ImageLabel()"); +#endif +} + +void ImageLabel::mousePressEvent(QMouseEvent*) +{ + if (ck) return; + ck = 1; + update(); + emit selected(id); +} + +void ImageLabel::select(int idx) +{ + if (ck == ((idx == id))) return; +#ifndef QT_NO_DEBUG +// qDebug(QString("ImageLabel::select(%1) %2").arg(idx).arg(id)); +#endif + ck = (idx == id); + update(); +}; + +#ifdef HIGHLIGHT_FOCUSED +void ImageLabel::enterEvent(QEvent*) +{ + focus = 1; + update(); +} + +void ImageLabel::leaveEvent(QEvent*) +{ + focus = 0; + update(); +} +#endif + +void ImageLabel::paintEvent(QPaintEvent*) +{ +// qDebug("paintEvent()"); + QPainter p(this); +// qDebug("ImageLabel::paintEvent()"); + +// p.setRenderHint(QPainter::Antialiasing, true); + if (ck) + p.fillRect( QRect(1,1, width()-2, height()-2), QBrush( palette().color(QPalette::Highlight) ) ); + +#ifdef HIGHLIGHT_FOCUSED + if (focus) { + if (ck) + p.setPen( QPen( palette().color(QPalette::HighlightedText), 2, Qt::SolidLine) ); + else + p.setPen( QPen( palette().color(QPalette::Highlight), 2, Qt::SolidLine) ); + p.drawRect( QRect(1,1, width()-2, height()-2) ); + } +#endif + p.drawImage( ((width()-img.width()) >> 1), ((iconh-img.height()) >> 1) + 2, img); + + if (ck) + p.setPen( QPen( palette().color(QPalette::HighlightedText), 2, Qt::SolidLine) ); + else + p.setPen( QPen( palette().color(QPalette::Text), 2, Qt::SolidLine) ); + p.drawText(2, iconh+3, width()-4, height()-iconh, Qt::AlignHCenter | Qt::AlignVCenter, name); + +} + diff --git a/gui/src/images_list.cpp b/gui/src/images_list.cpp new file mode 100644 index 0000000..eec8a1e --- /dev/null +++ b/gui/src/images_list.cpp @@ -0,0 +1,197 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include + +#include +#include + +#include + +#include +#include "images_list.h" +#include + +ImagesList::ImagesList(int iminW, QWidget *parent, ListDir orient) + :QScrollArea(parent) +{ +#ifdef __images_list_debug + qDebug("ImagesList()"); +#endif + iconw = 64; + iconh = 64; + minW = qMax(iconw+4, iminW); + winit(orient); + +} + +ImagesList::ImagesList(int iminW, QSize isize, QWidget *parent, ListDir orient) + :QScrollArea(parent) +{ +#ifdef __images_list_debug + qDebug("ImagesList()"); +#endif + iconw = isize.width(); + iconh = isize.height(); + minW = qMax(iconw+4, iminW); + winit(orient); + +} + +ImagesList::ImagesList(int iminW, int iw, int ih, QWidget *parent, ListDir orient) + :QScrollArea(parent) +{ +#ifdef __images_list_debug + qDebug("ImagesList()"); +#endif + iconw = iw; + iconh = ih; + minW = qMax(iconw+4, iminW); + winit(orient); + +} + +void ImagesList::winit(ListDir orient) +{ + if (iconw<0) iconw=0; + if (iconh<0) iconh=0; + + current = -1; + cwidget = new QWidget(this); +// cwidget->resize(80,700); + setWidget(cwidget); + if (orient == Vertical) { + setMaximumWidth(minW + 30); + setMinimumWidth(minW + 30); + setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding)); + clayout = new QVBoxLayout(cwidget); + } else { + setMaximumHeight(iconh + 26); + setMinimumHeight(iconh + 26); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + clayout = new QHBoxLayout(cwidget); + } + clayout->setMargin(1); + clayout->setSpacing(4); + cwidget->setLayout(clayout); +}; + +ImagesList::~ImagesList() +{ +#ifdef __images_list_debug + qDebug("~ImagesList()"); +#endif + clear_img(); +}; + +int ImagesList::addLabel(QString label, QImage image, int imgid) +{ +#ifndef QT_NO_DEBUG + qDebug("Adding ImageLabel..."); +#endif + ImageLabel* l; + int idx = images_l.size(); + //cwidget->resize(70, images_l.size() * 94 - 4); + cwidget->resize(minW+8, (idx+1) * (iconh+30) - 4); + l = new ImageLabel( + minW, + iconw,iconh, + label, + image.scaled(iconw, iconh, Qt::KeepAspectRatio, Qt::SmoothTransformation), + (imgid < 0) ? idx : imgid, + cwidget + ); + images_l.append(l); + clayout->addWidget(l); + if (current<0) current=0; + QObject::connect(l, SIGNAL(selected(int)), this, SLOT(clicked(int)) ); + QObject::connect(this, SIGNAL(selected(int)), l, SLOT(select(int)) ); + return idx; +}; + +void ImagesList::clicked(int idx) { + current = idx; +// for (int i=0; iselect(idx); + emit selected(idx); +}; + +void ImagesList::clear() { + clear_img(); +// hide(); +}; + +void ImagesList::clear_img() { +// return; + ImageLabel *l; + int s = images_l.size(); +#ifdef __images_list_debug + qDebug("ImagesView::clear_img()"); +#endif + for (int i=0 ; i < s ; i++) { +#ifdef __images_list_debug + qDebug(QString("Removing img #%1").arg(i)); +#endif + l = images_l.takeFirst(); + l->disconnect(); + clayout->removeWidget(l); + delete(l); + } + images_l.clear(); +// cwidget->adjustSize(); +} + +void ImagesList::keyPressEvent(QKeyEvent* e) +{ +#ifndef QT_NO_DEBUG +// qDebug("ImagesList::keyPressEvent()"); +#endif + + switch (e->key()) { + case Qt::Key_Up: + if (current>0) { + current--; + ensureWidgetVisible(images_l[current],0,0); + emit selected(current); + } + e->accept(); + break; + case Qt::Key_Down: + if (current<(images_l.size()-1)) { + current++; + ensureWidgetVisible(images_l[current],0,0); + emit selected(current); + } + e->accept(); + break; + +/* + case Qt::Key_Home: + e->accept(); + current= (images_l.size()) ? 0 : -1; + emit selected(current); + break; + case Qt::Key_End: + e->accept(); + current=images_l.size()-1; + emit selected(current); + break; +*/ + + case Qt::Key_Escape: + QScrollArea::keyPressEvent(e); + break; + default: + e->ignore(); + } +} + diff --git a/gui/src/main.cpp b/gui/src/main.cpp new file mode 100644 index 0000000..481c50a --- /dev/null +++ b/gui/src/main.cpp @@ -0,0 +1,74 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include + +#include +#include + +#include + +#include "../config.h" + +int main(int ac, char** av) +{ + int r; + QApplication *QPxTool; + QPxToolMW *mainwin; + QTranslator *translator; + QSplashScreen *splash; + QString locale = QLocale::system().name(); + QPixmap *pix; + QPxTool = new QApplication(ac,av); +#ifndef QT_NO_DEBUG + qDebug("Creating splash screen..."); +#endif + pix = new QPixmap (":images/splash.png"); + splash = new QSplashScreen( *pix ); + splash->show(); + + translator = new QTranslator; + +#ifndef QT_NO_DEBUG + qDebug("* Loading translator..."); +#endif +#ifdef _WIN32 + if (!translator->load("qpxtool."+locale, "locale")) { +#else + if (!translator->load("qpxtool."+locale, INSTALL_PREFIX"/share/qpxtool/locale")) { +#endif + qDebug() << "** Can't load translation for current locale: " << locale; + } else { + QPxTool->installTranslator(translator); + } + + QPxTool->setWindowIcon( QIcon(":images/q.png") ); + + mainwin = new QPxToolMW(ac,av); + + splash->finish(mainwin); + mainwin->show(); + r = QPxTool->exec(); + + delete mainwin; + delete translator; + delete splash; + delete pix; + delete QPxTool; + + return r; +} + diff --git a/gui/src/mainwidget.cpp b/gui/src/mainwidget.cpp new file mode 100644 index 0000000..aeb558e --- /dev/null +++ b/gui/src/mainwidget.cpp @@ -0,0 +1,180 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include +#include + +//#define bstyle "QPushButton::flat { border: none; }" +#define bstyle "QPushButton::flat { text-align: left; }" + +#define addTabButton(pb,icon,name,tabidx,layout) \ +{ \ + pb = new QPushButton(QIcon(icon),name,bframe); \ + pb->setStyleSheet(bstyle); \ + pb->setFlat(true); \ + pb->setCheckable(true); \ + pb->setIconSize(QSize(24,24)); \ + pb->setMinimumHeight(26); \ + pb->setFocusPolicy(Qt::NoFocus); \ + QFont f = pb->font(); \ + f.setPointSize(f.pointSize()+1); \ + pb->setFont(f); \ + grp->addButton(pb,tabidx++); \ + layout->addWidget(pb); \ +} + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "mainwidget.h" + +#include + +QPxMainWidget::QPxMainWidget(QPxSettings *iset, devlist *idev, QWidget *p) + : QWidget(p) +{ + int tabidx=0; +#ifndef QT_NO_DEBUG + qDebug("STA: QPxMainWidget()"); +#endif + + settings = iset; + devices = idev; + + QPushButton *pb; + layout = new QHBoxLayout(this); + layout->setMargin(3); + layout->setSpacing(3); + + bframe = new QFrame(this); + bframe->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + layout->addWidget(bframe); + + layout_buttons = new QVBoxLayout(bframe); + layout_buttons->setMargin(3); + layout_buttons->setSpacing(0); +// layout->addLayout(layout_buttons); + + grp = new QButtonGroup(bframe); + + stack = new QStackedLayout(); + layout->addLayout(stack); + + tab_DevInfo = new tabDevInfo(iset, idev, this); + stack->addWidget(tab_DevInfo); + addTabButton(pb,":images/device.png", "Device", tabidx,layout_buttons); + pb->setChecked(true); + connect(this, SIGNAL(deviceSelected()), tab_DevInfo, SLOT(selectDevice())); + + tab_MediaInfo = new tabMediaInfo(iset, idev, this); + stack->addWidget(tab_MediaInfo); + addTabButton(pb,":images/disc.png", "Media", tabidx,layout_buttons); + connect(this, SIGNAL(deviceSelected()), tab_MediaInfo, SLOT(selectDevice())); + + tab_RT = new tabTransfer(iset, idev, "RT", 0, this); + stack->addWidget(tab_RT); + addTabButton(pb,":images/test_rt.png", "Read Transfer", tabidx,layout_buttons); + connect(this, SIGNAL(configured()), tab_RT, SLOT(reconfig())); + connect(this, SIGNAL(deviceSelected()), tab_RT, SLOT(selectDevice())); + + tab_WT = new tabTransfer(iset, idev, "WT", 1, this); + stack->addWidget(tab_WT); + addTabButton(pb,":images/test_wt.png", "Write Transfer", tabidx,layout_buttons); + connect(this, SIGNAL(configured()), tab_WT, SLOT(reconfig())); + connect(this, SIGNAL(deviceSelected()), tab_WT, SLOT(selectDevice())); + + tab_ERRC = new tabERRC(iset, idev, "ERRC", this); + stack->addWidget(tab_ERRC); + addTabButton(pb,":images/test_errc.png", "Error Correction", tabidx,layout_buttons); + connect(this, SIGNAL(configured()), tab_ERRC, SLOT(reconfig())); + connect(this, SIGNAL(deviceSelected()), tab_ERRC, SLOT(selectDevice())); + + tab_JB = new tabJB(iset, idev, "JB", this); + stack->addWidget(tab_JB); + addTabButton(pb,":images/test_jb.png", "Jitter/Asymmetry", tabidx,layout_buttons); + connect(this, SIGNAL(configured()), tab_JB, SLOT(reconfig())); + connect(this, SIGNAL(deviceSelected()), tab_JB, SLOT(selectDevice())); + + tab_FETE = new tabFETE(iset, idev, "FETE", this); + stack->addWidget(tab_FETE); + addTabButton(pb,":images/test_ft.png", "FE/TE", tabidx,layout_buttons); + connect(this, SIGNAL(configured()), tab_FETE, SLOT(reconfig())); + connect(this, SIGNAL(deviceSelected()), tab_FETE, SLOT(selectDevice())); + + tab_TA = new tabTA(iset, idev, "TA", this); + stack->addWidget(tab_TA); + addTabButton(pb,":images/test_ta.png", "Time Analyser", tabidx,layout_buttons); + connect(this, SIGNAL(configured()), tab_TA, SLOT(reconfig())); + connect(this, SIGNAL(deviceSelected()), tab_TA, SLOT(selectDevice())); + + connect(grp, SIGNAL(buttonClicked(int)), stack, SLOT(setCurrentIndex(int))); + layout_buttons->addStretch(10); + + + bframe->setVisible(settings->show_sidebar); +#ifndef QT_NO_DEBUG + qDebug("END: QPxMainWidget()"); +#endif +} + +QPxMainWidget::~QPxMainWidget() +{ +#ifndef QT_NO_DEBUG + qDebug("STA: ~QPxMainWidget()"); + qDebug("END: ~QPxMainWidget()"); +#endif +} + +void QPxMainWidget::setSidebarVisible(bool en) { settings->show_sidebar = en; bframe->setVisible(en); } +void QPxMainWidget::selectTab(int idx) { grp->button(idx)->setChecked(true); stack->setCurrentIndex(idx); } +void QPxMainWidget::reconfig() { emit configured(); } +void QPxMainWidget::clearDev() { tab_MediaInfo->clear(); tab_DevInfo->clear(); } +void QPxMainWidget::clearMedia() { tab_MediaInfo->clear(); } +void QPxMainWidget::selectDevice() { emit deviceSelected(); } + +void QPxMainWidget::drawGraph(QImage& img, device *dev, int ttype, int eflags) +{ + switch(ttype) { + case TEST_RT: + tab_RT->drawGraph(img, dev, TEST_RT); + break; + case TEST_WT: + tab_WT->drawGraph(img, dev, TEST_WT); + break; + case TEST_ERRC: + tab_ERRC->drawGraph(img, dev, TEST_ERRC, eflags); + break; + case TEST_JB: + tab_JB->drawGraph(img, dev, TEST_JB); + break; + case TEST_FT: + tab_FETE->drawGraph(img, dev, TEST_FT); + break; + case TEST_TA: + tab_TA->drawGraph(img, dev, TEST_TA, eflags); + break; + } +} + diff --git a/gui/src/mainwindow.cpp b/gui/src/mainwindow.cpp new file mode 100644 index 0000000..520d44e --- /dev/null +++ b/gui/src/mainwindow.cpp @@ -0,0 +1,2049 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "version.h" +#include "mainwindow.h" + +#include + +#include + +#define PRINT_ERRC_DETAILED + +#define PRINT_GRAPH_SCALE 2.0 + +#define HTML_GRAPH_W 600 +#define HTML_GRAPH_H 300 +#define HTML_GRAPH_WQ "600" +#define HTML_GRAPH_HQ "300" +#define HTML_GRAPH_HQ2 "150" + +const QString errcNameCD[8] = { "BLER", "E11", "E21", "E31", "E12", "E22", "E32", "UNCR" }; +const QString errcNameDVD[8] = { "res", "PIE", "PI8", "PIF", "POE", "PO8", "POF", "UNCR" }; +const QString errcNameBD[8] = { "res", "LDC", "res", "res", "BIS", "res", "res", "UNCR" }; + +#include +#include + +static struct option long_options[] = { + {"load", 1, NULL, 'l'}, + {"help", 0, NULL, 'h'}, + {0,0,0,0} +}; + +QPxToolMW::QPxToolMW(int ac, char **av, QWidget *p, Qt::WindowFlags fl) + : QMainWindow(p, fl) +{ + char c; + QString lname; + splash = 1; + testDialog = NULL; +#ifndef QT_NO_DEBUG + qDebug("STA: QPxToolMW()"); +#endif + setWindowTitle("QPxTool - " VERSION); + settings_load(); + + while (1) { + c = getopt_long(ac, av, "hl:", long_options, NULL); + if (c == -1) + break; + switch(c) { + case 'h': + printf("qpxtool options:\n"); + printf("\t-l FILE\tload data from file\n"); + printf("\t-h\t\tshow this help\n"); + return; + case 'l': + lname = QString(optarg); + break; + default: + break; + } + } + + scanbusio = new QPxIODevice(this); + dhost = ""; + + if (set.use_reports_db) { + qDebug() << "Connecting to database..."; + if (!SqlOpenConnection(set.db, "reports")) { + qDebug() << "Error connecting to database!"; + set.use_reports_db = false; + } + } + + winit(); + + if (set.geometry_mw.width() > 0 && set.geometry_mw.height() > 0) + setGeometry(set.geometry_mw); + scanbus(); + + if (!lname.isEmpty()) { + load_results(lname); + } +#ifndef QT_NO_DEBUG + qDebug("END: QPxToolMW()"); +#endif + splash = 0; + + setAcceptDrops(true); +} + +QPxToolMW::~QPxToolMW () +{ +#ifndef QT_NO_DEBUG + qDebug("STA: ~QPxToolMW()"); +#endif + if (QSqlDatabase::database("reports").isOpen()) { + qDebug() << "Closing database connection..."; + SqlCloseConnection("reports"); + } + settings_save(); +#ifndef QT_NO_DEBUG + qDebug("Waiting for devices..."); +#endif + mutex_dev.lock(); + mutex_dev.unlock(); +#ifndef QT_NO_DEBUG + qDebug("END: ~QPxToolMW()"); +#endif +} + +void QPxToolMW::winit() +{ + create_actions(); + winit_menubar(); + winit_toolbar(); + winit_statusbar(); + + cwidget = new QWidget(this); + setCentralWidget(cwidget); + layout = new QVBoxLayout(cwidget); + layout->setMargin(3); + layout->setSpacing(3); + +// device + layout_dev = new QHBoxLayout; + layout_dev->setMargin(0); + layout_dev->setSpacing(3); + layout->addLayout(layout_dev); + + l_dev = new QLabel(tr("Device:"),cwidget); + l_dev->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_dev->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + layout_dev->addWidget(l_dev); + + c_dev = new QComboBox(cwidget); + c_dev->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + QFont dfont = c_dev->font(); + //dfont.setFamily("Courier"); + dfont.setFamily("Monospace"); + c_dev->setFont( dfont ); + layout_dev->addWidget(c_dev); + + pb_loej = new QPushButton(cwidget); + pb_loej->setIcon(QIcon(":images/loej.png")); + pb_loej->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + layout_dev->addWidget(pb_loej); + + pb_lock = new QPushButton(cwidget); + pb_lock->setIcon(QIcon(":images/lock.png")); + pb_lock->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + layout_dev->addWidget(pb_lock); + +/* +// separator + hline = new QFrame(cwidget); + hline->setFrameStyle(QFrame::HLine | QFrame::Sunken); + layout->addWidget(hline); +*/ + +// main widget + mwidget = new QPxMainWidget(&set, &devices, cwidget); + layout->addWidget(mwidget); + + connect(act_sb, SIGNAL(toggled(bool)), mwidget, SLOT(setSidebarVisible(bool))); + + connect(pb_loej, SIGNAL(clicked()), this, SLOT(loejToggle())); + connect(pb_lock, SIGNAL(clicked()), this, SLOT(lockToggle())); +} + +void QPxToolMW::create_actions() +{ + bool db_open = QSqlDatabase::database("reports").isOpen(); + + act_exit = new QAction(QIcon(":images/exit.png"), tr("Exit"), this); + act_scanbus = new QAction(QIcon(":images/refresh.png"), tr("Rescan local bus and network"), this); + act_minfo = new QAction(QIcon(":images/refresh-media.png"), tr("Update media info"), this); + act_devctl = new QAction(QIcon(":images/device.png"), tr("Device controls"), this); + act_devctl->setShortcut( QKeySequence("Ctrl+D") ); + act_test = new QAction(QIcon(":images/scan.png"), tr("Run tests..."), this); + act_test->setShortcut( QKeySequence("Ctrl+R") ); + act_stop = new QAction(QIcon(":images/stop.png"), tr("Stop tests"), this); + act_pref = new QAction(QIcon(":images/settings.png"), tr("Preferences"), this); + act_pref->setShortcut( QKeySequence("Ctrl+C") ); + act_print = new QAction(QIcon(":images/printer.png"), tr("Print test results"), this); + act_print->setShortcut( QKeySequence("Ctrl+P") ); + act_report = new QAction(QIcon(":images/pdf.png"), tr("Export results to PDF"), this); + act_export = new QAction(QIcon(":images/html.png"), tr("Export results to HTML"), this); + act_save = new QAction(QIcon(":images/save.png"), tr("Save results..."), this); + act_save->setShortcut( QKeySequence("Ctrl+S") ); + act_load = new QAction(QIcon(":images/fileopen.png"), tr("Load results..."), this); + act_load->setShortcut( QKeySequence("Ctrl+O") ); + act_save_db = new QAction(QIcon(":images/db_save.png"), tr("Save results (DB)..."), this); + act_load_db = new QAction(QIcon(":images/db_load.png"), tr("Load results (DB)..."), this); + + act_save_db->setEnabled(db_open); + act_load_db->setEnabled(db_open); + + act_sb = new QAction(QIcon(":images/"), tr("Show sidebar"), this); + act_sb->setShortcut( QKeySequence("Alt+B") ); + act_sb->setCheckable(true); + act_sb->setChecked(set.show_sidebar); + + QAction *act; + act_sbgrp = new QActionGroup(this); + + act = act_sbgrp->addAction(QIcon(":images/device.png"), "Device"); + act_sblist << act; act->setCheckable(true); + act->setShortcut( QKeySequence("Alt+1") ); + connect(act, SIGNAL(triggered()), this, SLOT(selectTab())); + act->setChecked(true); + act = act_sbgrp->addAction(QIcon(":images/disc.png"), "Media"); + act_sblist << act; act->setCheckable(true); + act->setShortcut( QKeySequence("Alt+2") ); + connect(act, SIGNAL(triggered()), this, SLOT(selectTab())); + act = act_sbgrp->addAction(QIcon(":images/test_rt.png"), "Read Transfer"); + act_sblist << act; act->setCheckable(true); + act->setShortcut( QKeySequence("Alt+3") ); + connect(act, SIGNAL(triggered()), this, SLOT(selectTab())); + act = act_sbgrp->addAction(QIcon(":images/test_wt.png"), "Write Transfer"); + act_sblist << act; act->setCheckable(true); + act->setShortcut( QKeySequence("Alt+4") ); + connect(act, SIGNAL(triggered()), this, SLOT(selectTab())); + act = act_sbgrp->addAction(QIcon(":images/test_errc.png"), "Error Correction"); + act_sblist << act; act->setCheckable(true); + act->setShortcut( QKeySequence("Alt+5") ); + connect(act, SIGNAL(triggered()), this, SLOT(selectTab())); + act = act_sbgrp->addAction(QIcon(":images/test_jb.png"), "Jitter/Asymmetry"); + act_sblist << act; act->setCheckable(true); + act->setShortcut( QKeySequence("Alt+6") ); + connect(act, SIGNAL(triggered()), this, SLOT(selectTab())); + act = act_sbgrp->addAction(QIcon(":images/test_ft.png"), "FE/TE"); + act_sblist << act; act->setCheckable(true); + act->setShortcut( QKeySequence("Alt+7") ); + connect(act, SIGNAL(triggered()), this, SLOT(selectTab())); + act = act_sbgrp->addAction(QIcon(":images/test_ta.png"), "Time Analyser"); + act_sblist << act; act->setCheckable(true); + act->setShortcut( QKeySequence("Alt+8") ); + connect(act, SIGNAL(triggered()), this, SLOT(selectTab())); + + connect(act_exit, SIGNAL(triggered()), this, SLOT(exit())); + connect(act_scanbus, SIGNAL(triggered()), this, SLOT(scanbus())); + connect(act_minfo, SIGNAL(triggered()), this, SLOT(update_media_info())); + connect(act_devctl, SIGNAL(triggered()), this, SLOT(device_settings())); + connect(act_test, SIGNAL(triggered()), this, SLOT(select_tests())); + connect(act_stop, SIGNAL(triggered()), this, SLOT(terminate_tests())); + connect(act_pref, SIGNAL(triggered()), this, SLOT(preferences())); + connect(act_print, SIGNAL(triggered()), this, SLOT(print_results())); + connect(act_export, SIGNAL(triggered()), this, SLOT(export_results())); + connect(act_report, SIGNAL(triggered()), this, SLOT(save_report())); + connect(act_save, SIGNAL(triggered()), this, SLOT(save_results())); + connect(act_load, SIGNAL(triggered()), this, SLOT(load_results())); + connect(act_save_db, SIGNAL(triggered()), this, SLOT(save_results_db())); + connect(act_load_db, SIGNAL(triggered()), this, SLOT(load_results_db())); +} + +void QPxToolMW::winit_menubar() +{ + QMenu *menu; +// QMenu *submenu; + menubar = menuBar(); + +// menu "file" + menu = new QMenu(tr("File"),this); + menu->addAction(act_load_db); + menu->addAction(act_save_db); + menu->addSeparator(); + menu->addAction(act_load); + menu->addAction(act_save); + menu->addAction(act_export); + menu->addAction(act_report); + menu->addAction(act_print); + menu->addSeparator(); + menu->addAction(act_pref); + menu->addSeparator(); + menu->addAction(act_exit); + + menubar->addMenu(menu); + +// menu "view" + menu = new QMenu(tr("View"),this); + + menu->addActions(act_sblist); + menu->addSeparator(); + menu->addAction(act_sb); + + menubar->addMenu(menu); + +// menu "device" + menu = new QMenu(tr("Device"),this); + menu->addAction(act_scanbus); + menu->addAction(act_minfo); + menu->addAction(act_devctl); + menu->addSeparator(); + menu->addAction(act_test); + menu->addAction(act_stop); + + menubar->addMenu(menu); + +// menu "delp" + menu = new QMenu(tr("Help"),this); + menu->addAction(QIcon(":images/info.png"), tr("About"), this, SLOT(about())); + + menubar->addMenu(menu); +} + +void QPxToolMW::winit_toolbar() +{ +// QToolButton *tb; + toolbar = new QToolBar("Main toolbar",this); + toolbar->setIconSize(QSize(24,24)); + toolbar->setContextMenuPolicy(Qt::NoContextMenu); + toolbar->setFloatable(false); + toolbar->setMovable(false); + +// toolbar->addAction(act_exit); // exit button +// toolbar->addSeparator(); + toolbar->addAction(act_scanbus); // scanbus button + toolbar->addAction(act_minfo); // media info update + toolbar->addAction(act_devctl); // device controls + toolbar->addAction(act_test); // run tests + toolbar->addAction(act_stop); // terminate tests + toolbar->addSeparator(); + toolbar->addAction(act_pref); // settings button + + addToolBar(toolbar); +} + +void QPxToolMW::winit_statusbar() +{ + status_progress = new QProgressBar(this); + status_progress->setMaximumHeight(20); + status_progress->setRange(0,1000); + status_progress->reset(); + + status_process = new QLabel(this); + status_process->setMaximumHeight(20); + + status_media = new QLabel(this); + status_media->setMaximumHeight(20); + + status_mid = new QLabel(this); + status_mid->setMaximumHeight(20); + + statusBar()->addWidget(status_progress,2); + statusBar()->addWidget(status_process,2); + statusBar()->addWidget(status_media,2); + statusBar()->addWidget(status_mid,2); +} + +void QPxToolMW::exit() +{ +/* +#ifndef QT_NO_DEBUG + qDebug("QPxToolMW::exit()"); +#endif + if (QMessageBox::question(this, + "Exit QPxTool", + "Are you sure to exit?", + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Yes) == QMessageBox::Yes) +*/ + close(); +} + +void QPxToolMW::selectTab() +{ + int idx; + QAction *act = (QAction*) sender(); + idx = act_sblist.indexOf(act); + if (act<0) return; + mwidget->selectTab(idx); +} + +void QPxToolMW::settings_load() +{ + set.load(); +} + +void QPxToolMW::settings_save() +{ + set.geometry_mw = geometry(); + set.save(); +} + +void QPxToolMW::select_tests() +{ + device *dev; + if (!devices.size()) { + QMessageBox::warning(this, tr("No devices"), tr("No devices found!\nCan't run tests")); + return; + } + dev = devices.current(); + select_tests(dev); +} + +void QPxToolMW::select_tests(device *dev) +{ + if (testDialog) return; + +#ifndef QT_NO_DEBUG + qDebug("select_tests()"); +#endif + if (!mutex_dev.tryLock()) { + QMessageBox::warning(this, tr("Device busy"), tr("Bus scan in progress")); + return; + } + if (!dev->mutex->tryLock()) { + mutex_dev.unlock(); + QMessageBox::warning(this, tr("Device busy"), tr("It seems device already performing test\nor info updating in progress")); + return; + } + dev->mutex->unlock(); + + testDialog = new TestDialog(&set, dev, this); + testDialog->setModal(true); + //testDialog->setModal(false); + + connect (testDialog, SIGNAL(accepted()), this, SLOT(run_tests())); + connect (testDialog, SIGNAL(rejected()), this, SLOT(cancel_run_tests())); + + testDialog->show(); +} + +void QPxToolMW::run_tests() +//void QPxToolMW::run_tests(device *dev) +{ + if (!testDialog) return; + device *dev = testDialog->getDevice(); + testDialog->deleteLater(); + testDialog = NULL; + + connect( dev, SIGNAL(testsDone()), this, SLOT(tests_done()) ); + connect( dev, SIGNAL(testsError()), this, SLOT(tests_error()) ); + + set.tests = dev->test_req; +// dev->mutex->unlock(); + dev->start_tests(); + mutex_dev.unlock(); +} + +void QPxToolMW::cancel_run_tests() +//void QPxToolMW::cancel_run_tests(device *dev) +{ + if (!testDialog) return; +// device *dev = testDialog->getDevice(); + testDialog->deleteLater(); + testDialog = NULL; + +// dev->mutex->unlock(); + mutex_dev.unlock(); +} + +void QPxToolMW::terminate_tests() +{ + device *dev = devices.current(); + if (dev->type == device::DevtypeNone) return; + dev->stop_tests(); +} + +void QPxToolMW::tests_done() +{ + device *dev = (device*) sender(); + disconnect( dev, SIGNAL(testsDone()), this, SLOT(tests_done()) ); + disconnect( dev, SIGNAL(testsError()), this, SLOT(tests_error()) ); + + if (set.report_autosave || set.report_autosave_db) { + dev->mutex->lock(); + if (set.report_autosave) autosave_report(dev); + if (set.report_autosave_db) save_results_db(dev); + dev->mutex->unlock(); + if (set.actions_flags & AFLAG_EJECT_AFTER) dev->setFeature(FEATURE_LOEJ, 0); + } + QMessageBox::information(this, tr("Done"), dev->id + ":\n"+ tr("All tests finished")); +} + +void QPxToolMW::tests_error() +{ + QMessageBox::warning(this, tr("Error"), tr("Error performing test!")+"\n"+tr("qScan finished with non-zero exit code")); +} + +void QPxToolMW::scanbus() +{ + int previdx; + QString previd; + device *dev; +#ifndef QT_NO_DEBUG + qDebug("STA: QPxToolMW::scanbus()"); +#endif +// mwidget->setDevice(NULL); + if (!mutex_dev.tryLock()) { + QMessageBox::information(this, tr("Device busy"), tr("Can't scan bus: some device(s) busy!")); + return; + } + if (scanbusio->IODevice()) { mutex_dev.unlock(); return; } + +// try lock all devices to ensure they not running any tests + for (int i=0; imutex->tryLock()) { + for (int j=0; jmutex->unlock(); + mutex_dev.unlock(); + QMessageBox::information(this, tr("Device busy"), tr("Can't scan bus: some device(s) busy!")); + return; + } + } + c_dev->setEnabled(false); + pb_loej->setEnabled(false); + pb_lock->setEnabled(false); + + act_scanbus->setEnabled(false); + + act_export->setEnabled(false); + act_save->setEnabled(false); + act_load->setEnabled(false); + act_print->setEnabled(false); + act_minfo->setEnabled(false); + act_devctl->setEnabled(false); + act_test->setEnabled(false); + act_stop->setEnabled(false); + + disconnect(c_dev, SIGNAL(currentIndexChanged(int)), this, SLOT(setDevice(int))); +// if (scanbusDis) return; +// scanbusDis = 1; + previd = devices.current()->id; + mwidget->clearDev(); + devices.setIdx(-1); + devices.clear(); + c_dev->clear(); + + if (!splash) { + progress = new ProgressWidget(10,3,this); + progress->setText(tr("Searching devices...")); + progress->show(); + } + + dev = new device(this); + dev->type = device::DevtypeVirtual; +/* + dev->host = dhost; + dev->port = dport; + + dev->path = outsl[1]; + dev->ven = outsl[3]; + dev->dev = outsl[5]; + dev->fw = outsl[7]; +*/ + dev->id = "RESULTS VIEWER [Virtual Device]"; + devices.append(dev); + c_dev->addItem(dev->id); + + + if (set.useLocal) scanbus_local(); + if (set.useRemote) scanbus_remote(); +// scanbusDis = 0; + if (!devices.size()) { + c_dev->addItem(tr("No devices found!")); + } + + mutex_dev.unlock(); + previdx = c_dev->findText(previd, Qt::MatchCaseSensitive); +#ifndef QT_NO_DEBUG + qDebug() << "Prev device [" << previdx << "] '" << previd << "'"; +#endif +// unlocking all devices +// for (int i=0; imutex->unlock(); + +// selecting device + setDevice( (previdx < 0) ? 0 : previdx ); + connect(c_dev, SIGNAL(currentIndexChanged(int)), this, SLOT(setDevice(int))); + c_dev->setCurrentIndex( (previdx < 0) ? 0 : previdx ); +#ifndef QT_NO_DEBUG + qDebug("END: QPxToolMW::scanbus()"); +#endif + if (!splash) delete progress; + c_dev->setEnabled( devices.size() ); + + if (devices.size()) { + act_load->setEnabled(true); +// act_save->setEnabled(true); + act_export->setEnabled(true); + act_print->setEnabled(true); +/* + act_minfo->setEnabled(true); + act_devctl->setEnabled(true); + act_test->setEnabled(true); + act_stop->setEnabled(true); +*/ + } + + act_scanbus->setEnabled(true); +} + +void QPxToolMW::scanbus_local() +{ + QProcess *proc; +#ifndef QT_NO_DEBUG + qDebug("STA: QPxToolMW::scanbus_local()"); +#endif + if (scanbusio->IODevice()) goto scanbus_local_end; + proc = new QProcess(this); + scanbusio->setIODevice(proc); + dt = device::DevtypeLocal; + dhost = ""; + +// connect(proc, SIGNAL(readyReadStandardOutput()), +// this, SLOT(qscan_process_scanbus())); + connect(scanbusio, SIGNAL(readyReadLine()), + this, SLOT(qscan_process_scanbus())); + + proc->start("qscan", QStringList("-l")); + if (!proc->waitForStarted(10000)) { +#ifndef QT_NO_DEBUG + qDebug("Can't run qscan!"); +#endif + if (!splash) progress->hide(); + QMessageBox::warning(this, tr("Warning"), tr("Unable to start qscan!\nLocal devices will not work")); + if (!splash) progress->show(); + scanbusio->setIODevice(NULL); + delete proc; + goto scanbus_local_end; + } + proc->setReadChannel(QProcess::StandardOutput); + + while(proc->state() == QProcess::Running) { + msleep(1<<5); + qApp->processEvents(); +// if (!splash) progress->step(); + } + + proc->waitForFinished(10000); + + disconnect(proc); + disconnect(scanbusio, SIGNAL(readyReadLine()), + this, SLOT(qscan_process_scanbus())); + scanbusio->setIODevice(NULL); + delete proc; +scanbus_local_end: +#ifndef QT_NO_DEBUG + qDebug("END: QPxToolMW::scanbus_local()"); +#endif + return; +} + +void QPxToolMW::scanbus_remote() +{ + QTcpSocket *sock; +#ifndef QT_NO_DEBUG + qDebug("STA: QPxToolMW::scanbus_remote()"); +#endif + int hosts_cnt = set.hosts.size(); + if (scanbusio->IODevice()) goto scanbus_remote_end; + sock = new QTcpSocket(this); + scanbusio->setIODevice(sock); + dt = device::DevtypeTCP; +// connect(sock, SIGNAL(readyRead()), +// this, SLOT(qscan_process_scanbus())); + connect(scanbusio, SIGNAL(readyReadLine()), + this, SLOT(qscan_process_scanbus())); + + for (int i=0; iconnectToHost(dhost, dport); + if (!sock->waitForConnected(5000)) { +#ifndef QT_NO_DEBUG + qDebug("Unable to connect to host!"); +#endif + if (!splash) progress->hide(); + QMessageBox::warning(this, tr("Warning"), tr("Unable to connect to qscand at %1:%2").arg(dhost).arg(dport)); + if (!splash) progress->show(); + continue; + } + qDebug() << "connection state: " << sock->state(); + sock->write("list\n"); + sock->write("close\n"); + + while(sock->state() == QAbstractSocket::ConnectedState) { + msleep(1<<5); + qApp->processEvents(); +// if (!splash) progress->step(); + } + sock->disconnectFromHost(); +#ifndef QT_NO_DEBUG + qDebug("Disconnected from QSCAND"); +#endif + } + disconnect(sock); + disconnect(scanbusio, SIGNAL(readyReadLine()), + this, SLOT(qscan_process_scanbus())); + scanbusio->setIODevice(NULL); + delete sock; +scanbus_remote_end: +#ifndef QT_NO_DEBUG + qDebug("END: QPxToolMW::scanbus_remote()"); +#endif + return; +} + +void QPxToolMW::qscan_process_scanbus() +{ + QString qout; + QStringList outsl; + device *dev; + +#ifndef QT_NO_DEBUG + qDebug("STA: QPxToolMW::qscan_process_scanbus()"); +#endif + if (!scanbusio->IODevice()) goto scanbus_process_qscan_end; + while (scanbusio->linesAvailable() ) { + qout = scanbusio->readLine(); +// qout.remove("\n"); +#ifndef QT_NO_DEBUG + qDebug() << qout; +#endif + if (qout.startsWith("D:")) { + dev = new device(this); + outsl = qout.split('\''); + + dev->type = dt; + if (dt == device::DevtypeTCP) { + dev->host = dhost; + dev->port = dport; + } + dev->path = outsl[1]; + dev->ven = outsl[3]; + dev->dev = outsl[5]; + dev->fw = outsl[7]; + + if (dt == device::DevtypeLocal) { + dev->id = QString("%1 %2 %3 [ %4 ]") + .arg(dev->ven).arg(dev->dev).arg(dev->fw) + .arg(dev->path); + } else if (dt == device::DevtypeTCP) { + dev->id = QString("%1 %2 %3 [ %4@%5:%6 ]") + .arg(dev->ven).arg(dev->dev).arg(dev->fw) + .arg(dev->path) + .arg(dev->host).arg(dev->port); + } + devices.append(dev); + c_dev->addItem(dev->id); + dev->startWatcher(); + } + } +scanbus_process_qscan_end: +#ifndef QT_NO_DEBUG + qDebug("END: QPxToolMW::qscan_process_scanbus()"); +#endif + return; +} + +void QPxToolMW::setDevice(int idx) +{ + bool devact_en; + device *dev; +#ifndef QT_NO_DEBUG + qDebug() << "STA: QPxToolMW::setDevice: " << idx; +#endif + if (idx<0 || idx >= devices.size() || devices.idx() == idx) return; +// devices.current()->disconnect(); + devices.setIdx(idx); + mwidget->selectDevice(); + dev = devices.current(); + + devact_en = (dev->type == device::DevtypeLocal) || (dev->type == device::DevtypeTCP); + + act_minfo->setEnabled(devact_en); + act_test->setEnabled(devact_en); + act_stop->setEnabled(devact_en); + act_save->setEnabled(devact_en); + act_devctl->setEnabled(dev->type == device::DevtypeLocal); + pb_loej->setEnabled(dev->type == device::DevtypeLocal); + pb_lock->setEnabled(dev->type == device::DevtypeLocal); + + if (devact_en) { + if (!dev->info_set) { + dev->update_device_info(); + while( dev->isRunning() ) { + msleep ( 1 << 5); + qApp->processEvents(); + } + dev->update_media_info(); + dev->info_set = 1; + } + if (dev->plextor_lock) { + QMessageBox::warning( + this, + tr("Warning"), + dev->id + "\n" + tr("Found locked Plextor device!\nQuality check and some other features will not work")); + } + } + if (dev->media.type == "-") { + status_media->setText("No Media"); + status_mid->clear(); + } else { + status_media->setText(dev->media.dstate + " "+ dev->media.type); + status_mid->setText(dev->media.mid); + } + status_process->setText(dev->nprocess); + status_progress->setValue(dev->pprocess * 10); + + +#ifndef QT_NO_DEBUG + qDebug("END: QPxToolMW::setDevice()"); +#endif + return; +} + +void QPxToolMW::mediaUpdated(int) { + device *dev = (device*) sender(); +#ifndef QT_NO_DEBUG + qDebug() << "QPxTool::mediaUpdated()" << dev->autoupdate; +#endif + if ( typeid(*dev) != typeid(device)) return; + + if (dev == devices.current()) { +// mwidget->closeChilds(); + if (dev->media.type == "-") { + status_media->setText("No Media"); + status_mid->clear(); + return; + } else { + status_media->setText(dev->media.dstate + " "+ dev->media.type); + status_mid->setText(dev->media.mid); + } + } + + if (!dev->autoupdate) return; + dev->autoupdate = 0; + + if (testDialog) testDialog->mediaChanged(); + + if (!testDialog) { + if (dev->media.type == "-") return; + if ( + ((set.actions_flags & AFLAG_AUTOSTART_W) && dev->media.creads ) + || ((set.actions_flags & AFLAG_AUTOSTART_B) && + (!dev->media.creads || dev->media.type.startsWith("DVD+RW") || dev->media.type.startsWith("DVD-RAM")) ) + ) { + select_tests(dev); + } + } +} + +void QPxToolMW::update_device_info() { devices.current()->update_device_info(); } +void QPxToolMW::update_media_info() { update_media_info(devices.current()); } + +void QPxToolMW::update_media_info(device* dev) { + if (!dev->update_media_info()) + QMessageBox::warning(this, tr("Error"), dev->id + "\n" + tr("Can't update media info! Device busy")); +} + +void QPxToolMW::process_started() +{ + device *dev = (device*) sender(); + if ( dev != devices.current()) return; + status_process->setText(dev->nprocess); + status_progress->setValue(0); +} + +void QPxToolMW::process_finished() +{ + device *dev = (device*) sender(); + if ( dev != devices.current()) return; + status_process->setText(dev->nprocess); + status_progress->reset(); +} + +void QPxToolMW::process_progress() +{ + device *dev = (device*) sender(); + if ( dev != devices.current()) return; + status_progress->setValue(dev->pprocess * 10); +} + +void QPxToolMW::loejToggle() +{ + device *dev = devices.current(); + qDebug("QPxToolMW::loejToggle()"); + if (dev->type != device::DevtypeLocal) return; + dev->setFeature(FEATURE_LOEJ_TOGGLE, 0); +} + +void QPxToolMW::lockToggle() +{ + device *dev = devices.current(); + qDebug("QPxToolMW::lockToggle()"); + if (dev->type != device::DevtypeLocal) return; + dev->setFeature(FEATURE_LOCK_TOGGLE, 0); +} + +void QPxToolMW::device_settings() +{ + device *dev; + devSettings *devSet; +#ifndef QT_NO_DEBUG + qDebug("QPxToolMW::device_settings()"); +#endif + if (!devices.size()) { + QMessageBox::warning(this, tr("No devices"), tr("No devices found!\nNothing to configure\n")); + return; + } + dev = devices.current(); + if (!mutex_dev.tryLock()) { + QMessageBox::warning(this, tr("Device busy"), tr("Bus scan in progress")); + return; + } + if (!dev->mutex->tryLock()) { + mutex_dev.unlock(); + QMessageBox::warning(this, tr("Device busy"), tr("Selected device busy")); + return; + } + dev->mutex->unlock(); + + progress = new ProgressWidget(10,3,this); + progress->setText(tr("Retrieving device parameters...")); + progress->show(); + + connect(dev, SIGNAL(doneGetFeatures(int)), this, SLOT(update_features_done(int))); + + if (!dev->getFeatures()) { + delete progress; + mutex_dev.unlock(); + QMessageBox::warning(this, tr("Warning"), tr("Can't get device features!")); + return; + } + + while(dev->isRunning()) { msleep ( 1 << 5); qApp->processEvents(); } + delete progress; + disconnect(dev, SIGNAL(doneGetFeatures(int)), this, SLOT(update_features_done(int))); + + devSet = new devSettings(&set, dev, this); + devSet->exec(); + delete devSet; + mutex_dev.unlock(); +} + +void QPxToolMW::update_features_done(int exitcode) +{ + if (exitcode) { + QMessageBox::warning(this, + tr("Error"), + tr("Error requesting device settings!")+"\n"+tr("cdvdcontrol finished with non-zero exit code")); + } +} + +void QPxToolMW::preferences() +{ + QPxPreferences *pref; +#ifndef QT_NO_DEBUG + qDebug("QPxToolMW::preferences()"); +#endif + pref = new QPxPreferences(&set, this); + pref->exec(); + delete pref; + + if (QSqlDatabase::database("reports").isOpen()) { + qDebug() << "Closing database connection..."; + SqlCloseConnection("reports"); + } + if (set.use_reports_db) { + qDebug() << "Connecting to database..."; + if (!SqlOpenConnection(set.db, "reports")) { + qDebug() << "Error connecting to database!"; + return; + } + } + bool dbOpen = QSqlDatabase::database("reports").isOpen(); + act_save_db->setEnabled(dbOpen); + act_load_db->setEnabled(dbOpen); + + mwidget->reconfig(); +} + +void QPxToolMW::save_report() +{ + device *dev = devices.current(); + QString fname; + QFileInfo finfo; + fname = QFileDialog::getSaveFileName(this, tr("Export results to PDF"), set.last_res_path_pdf, "PDF files (*pdf *.PDF)"); + if (fname.isEmpty()) return; + + finfo.setFile(fname); + set.last_res_path_pdf = finfo.absoluteDir().canonicalPath(); + qDebug() << "Saving PDF to: " << fname; + + save_report(dev, fname); +} + +void QPxToolMW::autosave_report(device *dev) +{ +#ifndef QT_NO_DEBUG + qDebug("Autosaving report..."); +#endif + QString fpath,fname; + QDateTime dt = QDateTime::currentDateTime(); + QDir dir; + if (set.report_path.isEmpty()) { +#ifndef QT_NO_DEBUG + qDebug() << "Reports path not defined!"; +#endif + return; + } +#warning !!! hardcoded report name structure + fpath = set.report_path + "/" + dt.toString("yyyy-MM-dd"); + fname = dt.toString("/hh-mm-ss_") + dev->media.type + ".pdf"; + + if (!dir.exists(fpath) && !dir.mkpath(fpath)) { + QMessageBox::warning(this, tr("Can't save report"), tr("Direcrory not exists and I can't create it") + ":\n" + fpath); + return; + } + save_report(dev, fpath + "/" + fname); +} + +void QPxToolMW::save_report(device *dev, QString fname) +{ + QPrinter printer(QPrinter::HighResolution); + QTextDocument *doc = new QTextDocument(this); + prepare_report(dev, doc); + + printer.setOutputFormat(QPrinter::PdfFormat); + printer.setPaperSize(QPrinter::A4); + printer.setOutputFileName(fname); + + PrintPreview::printDoc(this, &printer, doc); + + delete doc; +} + +void QPxToolMW::print_results() +{ +#ifndef QT_NO_DEBUG + qDebug("QPxToolMW::print_results()"); +#endif + device *dev = devices.current(); + QTextDocument *doc = new QTextDocument(this); + PrintPreview *preview; + + prepare_report(dev, doc); + + preview = new PrintPreview(this, doc); + preview->exec(); + delete preview; + + delete doc; +} + +void QPxToolMW::prepare_report(device *dev, QTextDocument* doc) +{ + QImage img; + progress = new ProgressWidget(10,3,this); + progress->setText(tr("Preparing report...")); + progress->show(); + + doc->setHtml( generate_html(dev, "") ); + +// img = QImage(":images/logo.png"); +// doc->addResource(QTextDocument::ImageResource, QUrl("images/logo.png"), img); + + qApp->processEvents(); + if (dev->testData.rt.size() > 0) { + img = QImage(HTML_GRAPH_W,HTML_GRAPH_H,QImage::Format_ARGB32); + mwidget->drawGraph(img, dev, TEST_RT); + doc->addResource(QTextDocument::ImageResource, QUrl("images/rt.png"), img); + + qApp->processEvents(); + } + if (dev->testData.wt.size() > 0) { + img = QImage(HTML_GRAPH_W,HTML_GRAPH_H,QImage::Format_ARGB32); + mwidget->drawGraph(img, dev, TEST_WT); + doc->addResource(QTextDocument::ImageResource, QUrl("images/wt.png"), img); + + qApp->processEvents(); + } +// if (1) { + if (dev->testData.errc.size() > 0) { + img = QImage(HTML_GRAPH_W,HTML_GRAPH_H,QImage::Format_ARGB32); + mwidget->drawGraph(img, dev, TEST_ERRC); + doc->addResource(QTextDocument::ImageResource, QUrl("images/errc.png"), img); + +#ifdef PRINT_ERRC_DETAILED + if (dev->media.type.startsWith("CD")) { + for (int i=0; i<8; i++) { + if (!dev->media.tdata_errc || (dev->media.tdata_errc & (1<drawGraph(img, dev, TEST_ERRC, (1 << i)); + doc->addResource(QTextDocument::ImageResource, QUrl("images/"+errcNameCD[i]+".png"), img); + } + qApp->processEvents(); + } + } else if (dev->media.type.startsWith("DVD")) { + for (int i=1; i<8; i++) { + if (!dev->media.tdata_errc || (dev->media.tdata_errc & (1<drawGraph(img, dev, TEST_ERRC, (1 << i)); + doc->addResource(QTextDocument::ImageResource, QUrl("images/"+errcNameDVD[i]+".png"), img); + } + qApp->processEvents(); + } + } else if (dev->media.type.startsWith("BD")) { + int residx[] = {1,4,7}; + for (int i=0; i<3; i++) { + if (!dev->media.tdata_errc || (dev->media.tdata_errc & (1<drawGraph(img, dev, TEST_ERRC, (1 << residx[i])); + doc->addResource(QTextDocument::ImageResource, QUrl("images/"+errcNameBD[residx[i]]+".png"), img); + } + qApp->processEvents(); + } + } +#endif + + } + if (dev->testData.jb.size() > 0) { + img = QImage(HTML_GRAPH_W, HTML_GRAPH_H,QImage::Format_ARGB32); + mwidget->drawGraph(img, dev, TEST_JB); + doc->addResource(QTextDocument::ImageResource, QUrl("images/jb.png"), img); + + qApp->processEvents(); + } + if (dev->testData.ft.size() > 0) { + img = QImage(HTML_GRAPH_W, HTML_GRAPH_H,QImage::Format_ARGB32); + mwidget->drawGraph(img, dev, TEST_FT); + doc->addResource(QTextDocument::ImageResource, QUrl("images/ft.png"), img); + + qApp->processEvents(); + } + + if (dev->test_req & TEST_TA) { + for (int l=0; lmedia.ilayers; l++) + { + for (int z=0; z<3; z++) + { + img = QImage(HTML_GRAPH_W,HTML_GRAPH_H/2,QImage::Format_ARGB32); + mwidget->drawGraph(img, dev, TEST_TA, (l << 28) | (z<<4) | 0); + doc->addResource(QTextDocument::ImageResource, QUrl(QString("images/ta%1_%2_p.png").arg(l).arg(z)), img); + + qApp->processEvents(); + + img = QImage(HTML_GRAPH_W,HTML_GRAPH_H/2,QImage::Format_ARGB32); + mwidget->drawGraph(img, dev, TEST_TA, (l << 28) | (z<<4) | 1); + doc->addResource(QTextDocument::ImageResource, QUrl(QString("images/ta%1_%2_l.png").arg(l).arg(z)), img); + + qApp->processEvents(); + } + } + } + + delete progress; +} + +void QPxToolMW::export_results() +{ +#ifndef QT_NO_DEBUG + qDebug("QPxToolMW::export_results()"); +#endif + QImage img; + QDir dir; + QString fname, idir; + QString html; + QFile f; + QFileInfo finfo; + device *dev = devices.current(); + + fname = QFileDialog::getSaveFileName(this, tr("Export results to HTML"), set.last_res_path_html, "HTML files (*.html)"); + if (fname.isEmpty()) return; + + progress = new ProgressWidget(10,3,this); + progress->setText(tr("Preparing report...")); + progress->show(); + + f.setFileName(fname); + finfo.setFile(f); + set.last_res_path_html = finfo.absoluteDir().canonicalPath(); + qDebug() << "Saving HTML to: " << fname; + + int idx = fname.lastIndexOf("/"); + idir = fname.mid(idx); + if (idx>=0) { + fname.truncate(idx); + } + idir.remove("/"); + if (idir.endsWith(".html")) idir.remove(".html"); + idir+= ".images/"; + + qDebug() << "path: " << fname; + qDebug() << "idir: " << idir; + + fname += "/" + idir; + dir.mkpath(fname); + + qDebug() << "Images path: " << fname; + + html = generate_html(dev, idir); + if(!f.open(QIODevice::WriteOnly)) { + QMessageBox::warning(this, tr("Error"), tr("Unable to create file:\n")+fname); + return; + } + f.write(html.toLatin1()); + f.close(); + + img = QImage(":images/logo.png"); + img.save(fname+"logo.png"); + +// prepare_images(true); + + if (dev->testData.rt.size() > 0) { + img = QImage(HTML_GRAPH_W,HTML_GRAPH_H,QImage::Format_ARGB32); + mwidget->drawGraph(img, dev, TEST_RT); + img.save(fname+"rt.png"); + + qApp->processEvents(); + } + if (dev->testData.wt.size() > 0) { + img = QImage(HTML_GRAPH_W,HTML_GRAPH_H,QImage::Format_ARGB32); + mwidget->drawGraph(img, dev, TEST_WT); + img.save(fname+"wt.png"); + + qApp->processEvents(); + } +// if (1) { + if (dev->testData.errc.size() > 0) { + img = QImage(HTML_GRAPH_W,HTML_GRAPH_H,QImage::Format_ARGB32); + mwidget->drawGraph(img, dev, TEST_ERRC); + img.save(fname+"errc.png"); + qApp->processEvents(); + + qDebug() << COL_RED << "tdata_errc:" << dev->media.tdata_errc << COL_NORM; +#ifdef PRINT_ERRC_DETAILED + if (dev->media.type.startsWith("CD")) { + for (int i=0; i<8; i++) { + if (!dev->media.tdata_errc || (dev->media.tdata_errc & (1<drawGraph(img, dev, TEST_ERRC, (1 << i)); + img.save(fname+errcNameCD[i]+".png"); + } + qApp->processEvents(); + } + } else if (dev->media.type.startsWith("DVD")) { + for (int i=1; i<8; i++) { + if (!dev->media.tdata_errc || (dev->media.tdata_errc & (1<drawGraph(img, dev, TEST_ERRC, (1 << i)); + img.save(fname+errcNameDVD[i]+".png"); + } + qApp->processEvents(); + } + } else if (dev->media.type.startsWith("BD")) { + int residx[] = {1,4,7}; + for (int i=0; i<3; i++) { + if (!dev->media.tdata_errc || (dev->media.tdata_errc & (1<drawGraph(img, dev, TEST_ERRC, (1 << residx[i])); + img.save(fname+errcNameBD[residx[i]]+".png"); + } + qApp->processEvents(); + } + } +#endif + + } + if (dev->testData.jb.size() > 0) { + img = QImage(HTML_GRAPH_W,HTML_GRAPH_H,QImage::Format_ARGB32); + mwidget->drawGraph(img, dev, TEST_JB); + img.save(fname+"jb.png"); + + qApp->processEvents(); + } + if (dev->testData.ft.size() > 0) { + img = QImage(HTML_GRAPH_W,HTML_GRAPH_H,QImage::Format_ARGB32); + mwidget->drawGraph(img, dev, TEST_FT); + img.save(fname+"ft.png"); + + qApp->processEvents(); + } + + if (dev->test_req & TEST_TA) { + for (int l=0; lmedia.ilayers; l++) + { + for (int z=0; z<3; z++) + { + img = QImage(HTML_GRAPH_W,HTML_GRAPH_H/2,QImage::Format_ARGB32); + mwidget->drawGraph(img, dev, TEST_TA, (l << 28) | (z<<4) | 0); + img.save( QString("images/ta%1_%2_p.png").arg(l).arg(z) ); + + qApp->processEvents(); + + img = QImage(HTML_GRAPH_W,HTML_GRAPH_H/2,QImage::Format_ARGB32); + mwidget->drawGraph(img, dev, TEST_TA, (l << 28) | (z<<4) | 1); + img.save( QString("images/ta%1_%2_l.png").arg(l).arg(z) ); + + qApp->processEvents(); + } + } + } + delete progress; +} + + +/* +void QPxToolMW::prepare_images(QString path) +{ + bool save; + QString idir; + if (path.isEmpty()) { + idir=":images/"; + save = 0; + } else { + idir=path; + save = 1; + } + +} +*/ + +QString QPxToolMW::generate_html(device *dev, QString idir) +{ + QString r; + QString bgcolor; + bool print = idir.isEmpty(); + + r= "\n"; + r+= "\n"; + r+= "\tQPxTool scan results\n"; + r+= "\n"; + r+= "\n"; + + r+= "

\n"; + + if (print) { + idir = "images"; + r+= "\t\n"; + } else { + r+= "\t\n"; + } + + r+= "\t
\n"; + + r+= "\t\n"; + r+= "\t\n"; + r+= "\t\t\n"; + r+= "\t\t\t\n"; + if (dev->host.isEmpty()) { + r+= "\t\t\t\n"; + } else { + r+= "\t\t\t\n"; + } + r+= "\t\t\n"; + r+= "\t\t\n"; + r+= "\t\t\t\n"; + r+= "\t\t\t\n"; + r+= "\t\t\n"; + r+= "\t
Device Path" + dev->path + "" + dev->path + "@" + dev->host + ":"+ QString::number(dev->port) + "
Device ID" + dev->ven + dev->dev + dev->fw + "
\n"; + r+= "\t
\n"; +// r+= "\t
\n"; + + r+= "\t\n"; + r+= "\t\t\n"; + r+= "\t\t\t\n"; + r+= "\t\t\t\n"; + r+= "\t\t\n"; + r+= "\t\t\n"; + r+= "\t\t\t\n"; + r+= "\t\t\t\n"; + r+= "\t\t\n"; + r+= "\t\t\n"; + r+= "\t\t\t\n"; + r+= "\t\t\t\n"; + r+= "\t\t\n"; + r+= "\t\t\n"; + r+= "\t\t\t\n"; + r+= "\t\t\t\n"; + r+= "\t\t\n"; + r+= "\t\t\n"; + r+= "\t\t\t\n"; + r+= "\t\t\t\n"; + r+= "\t\t\n"; + r+= "\t\t\n"; + r+= "\t\t\t\n"; + r+= "\t\t\t\n"; + r+= "\t\t\n"; + r+= "\t
TLA# " + dev->tla + "
Serial# " + dev->sn + "
Media/Category:" + dev->media.type + " / "+ dev->media.category + "
Media ID:" + dev->media.mid + "
Written on:" + dev->media.writer + "
Capacity (readable):" + QString::number(dev->media.creadm) + "MB (" + QString::number(dev->media.creads) + " sectors)
Capacity (total):" + QString::number(dev->media.ctotm) + "MB (" + QString::number(dev->media.ctots) + " sectors)
\n"; + r+= "\t
\n"; + r+= "
\n"; + + if (dev->testData.rt.size() > 0) { + r+= "

\n"; + r+= "\tRead Transfer @ "+QString::number(dev->tspeeds.rt)+"X
\n"; + r+= QString("\tTest time: %1:%2
\n") + .arg( ((int)dev->testData.rt_time) / 60) + .arg( ((int)dev->testData.rt_time) % 60, 2, 10, QChar('0')); + r+= "

\n"; + r+= "\n"; + r+= "\t\n"; + r+= "\t\t\n"; + r+= "\t\t\n"; + r+= "\t\t\n"; + r+= "\t\n"; + + r+= QString("\t\n") + .arg(dev->testData.rt.first().spdx) + .arg(dev->testData.rt.last().spdx) + .arg(dev->testData.rt.last().lba * 2 / dev->testData.rt_time / dev->media.spd1X, 0, 'f', 2); + r+= QString("\t\n") + .arg(dev->testData.rt.first().spdk) + .arg(dev->testData.rt.last().spdk) + .arg(dev->testData.rt.last().lba * 2 / dev->testData.rt_time, 0, 'f', 2); + + r+= "
StartEndAvg
%1 X%2 X%3 X
%1 kB/s%2 kB/s%3 kB/s
\n"; + r+= "
\n"; + + r+= "\n\t\n
"; + r+= "\"Read"; +// r+= "
Read Transfer"; + r+= "

\n"; + } + + if (dev->testData.wt.size() > 0) { + r+= "

\n"; + r+= "\tWrite Transfer @ "+QString::number(dev->tspeeds.wt)+"X
\n"; + r+= QString("\tTest time: %1:%2
\n") + .arg( ((int)dev->testData.wt_time) / 60) + .arg( ((int)dev->testData.wt_time) % 60, 2, 10, QChar('0')); + r+= "

\n"; + r+= "\n"; + r+= "\t\n"; + r+= "\t\t\n"; + r+= "\t\t\n"; + r+= "\t\t\n"; + r+= "\t\n"; + r+= QString("\t\n") + .arg(dev->testData.wt.first().spdx) + .arg(dev->testData.wt.last().spdx) + .arg(dev->testData.wt.last().lba * 2 / dev->testData.rt_time / dev->media.spd1X, 0, 'f', 2); + r+= QString("\t\n") + .arg(dev->testData.wt.first().spdk) + .arg(dev->testData.wt.last().spdk) + .arg(dev->testData.wt.last().lba * 2 / dev->testData.rt_time, 0, 'f', 2); + + r+= "
StartEndAvg
%1 X%2 X%3 X
%1 kB/s%2 kB/s%3 kB/s
\n"; + r+= "
\n"; + + r+= "\n\t\n
"; + r+= "\"Write"; +// r+= "
Write Transfer"; + r+= "

\n"; + } + + if (dev->testData.errc.size() > 0) { + r+= "

\n"; + r+= "\tError Correction @ "+QString::number(dev->tspeeds.errc)+"X
\n"; + r+= QString("\tTest time: %1:%2
\n") + .arg( ((int)dev->testData.errc_time) / 60) + .arg( ((int)dev->testData.errc_time) % 60, 2, 10, QChar('0')); + r+= "

\n"; + r+= "\n"; + r+= "\t\n"; + r+= "\t\t\n"; + r+= "\t\t\n"; + r+= "\t\t\n"; + r+= "\t\t\n"; + r+= "\t\n"; + + if (dev->media.type.startsWith("CD")) { + for (int i=0; i<8; i++) { + if (!dev->media.tdata_errc || (dev->media.tdata_errc & (1<\n") + .arg(bgcolor) + .arg(errcNameCD[i]) + .arg(dev->testData.errcTOT.raw.err[i]) + .arg(dev->testData.errcMAX.raw.err[i]) + .arg(dev->testData.errcAVG.raw.err[i], 0, 'f', 2); + } + } + } else if (dev->media.type.startsWith("DVD")) { + for (int i=1; i<8; i++) { + if (!dev->media.tdata_errc || (dev->media.tdata_errc & (1<\n") + .arg(bgcolor) + .arg(errcNameDVD[i]) + .arg(dev->testData.errcTOT.raw.err[i]) + .arg(dev->testData.errcMAX.raw.err[i]) + .arg(dev->testData.errcAVG.raw.err[i], 0, 'f', 2); + } + } + } else if (dev->media.type.startsWith("BD")) { + int residx[] = {1,4,7}; + for (int i=0; i<3; i++) { + if (!dev->media.tdata_errc || (dev->media.tdata_errc & (1<\n") + .arg(bgcolor) + .arg(errcNameBD[residx[i]]) + .arg(dev->testData.errcTOT.raw.err[residx[i]]) + .arg(dev->testData.errcMAX.raw.err[residx[i]]) + .arg(dev->testData.errcAVG.raw.err[residx[i]], 0, 'f', 2); + } + } + } + + r+= "
TotalMaxAvg
%2%3%4%5
%2%3%4%5
%2%3%4%5
\n"; + r+= "
\n"; + + r+= "\n\t
"; + r+= "\"Error\n"; + r+= "
Overall ERRC Graph"; + r+= "

\n"; + +#ifdef PRINT_ERRC_DETAILED + if (dev->media.type.startsWith("CD")) { + for (int i=0; i<8; i++) { + if (!dev->media.tdata_errc || (dev->media.tdata_errc & (1<\n\t"; + r+= "\t"; + r+= "
"+errcNameCD[i]; + r+= "\n
\n"; + } + } + } else if (dev->media.type.startsWith("DVD")) { + for (int i=1; i<8; i++) { + if (!dev->media.tdata_errc || (dev->media.tdata_errc & (1<\n\t"; + r+= "\t"; + r+= "
"+errcNameDVD[i]; + r+= "\n
\n"; + } + } + } else if (dev->media.type.startsWith("BD")) { + int residx[] = {1,4,7}; + for (int i=0; i<3; i++) { + if (!dev->media.tdata_errc || (dev->media.tdata_errc & (1<\n\t"; + r+= "\t"; + r+= "
"+errcNameBD[residx[i]]; + r+= "\n
\n"; + } + } + } +#endif + } + + if (dev->testData.jb.size() > 0) { + r+= "

\n"; + r+= "\tJitter/Asymmetry @ "+QString::number(dev->tspeeds.jb)+"X
\n"; + r+= QString("\tTest time: %1:%2
\n") + .arg( ((int)dev->testData.jb_time) / 60) + .arg( ((int)dev->testData.jb_time) % 60, 2, 10, QChar('0')); + r+= "

\n"; + r+= "\t\n"; + r+= "\t\t"; + r+= QString("\t\t") + .arg(dev->testData.jbMM.jmin) + .arg(dev->testData.jbMM.jmax); + r+= QString("\t\t") + .arg(dev->testData.jbMM.bmin) + .arg(dev->testData.jbMM.bmax); + r+= "\t
MinMax
Jitter%1%2
Asymmetry%1%2
\n"; + r+= "
\n"; + r+= "\n\t\n
\n"; + r+= ""; + r+= "

\n"; + } + + if (dev->testData.ft.size() > 0) { + r+= "

\n"; + r+= "\tFocus/Tracking Errors @ "+QString::number(dev->tspeeds.ft)+"X
\n"; + r+= QString("\tTest time: %1:%2
\n") + .arg( ((int)dev->testData.ft_time) / 60) + .arg( ((int)dev->testData.ft_time) % 60, 2, 10, QChar('0')); + r+= "

\n"; + r+= "\t\n"; + r+= "\t\t"; + r+= "\t\t"; + r+= "\t
FE Max"+QString::number(dev->testData.ftMAX.fe)+"
TE Max"+QString::number(dev->testData.ftMAX.te)+"
\n"; + r+= "
\n"; + r+= "\n\t
\n"; + r+= ""; + r+= "

\n"; + } + + if (dev->test_req & TEST_TA) { + r+= "

\n"; + r+= "\tTime Analyser
\n"; + r+= QString("\tTest time: %1:%2
\n") + .arg( ((int)dev->testData.ta_time) / 60) + .arg( ((int)dev->testData.ta_time) % 60, 2, 10, QChar('0')); + r+= "

\n"; + r+= "\t\n"; +#warning TA analysis data + r+= "\t
\n"; + + for (int l=0; lmedia.ilayers; l++) + { + for (int z=0; z<3; z++) + { + r+= "\n\t\n\t\n
"; + r+= ""; + r+= "
"; + r+= ""; + r+= "

\n"; + } + } + } + + r+= "\t

\n"; + r+= "\t\n"; + r+= "

\n"; + r+= "\tGenerated by QPxTool v"VERSION" (c) Gennady \"ShultZ\" Kozlov\n"; + r+= "\thttp://qpxtool.sourceforge.net
\n"; + r+= "

\n"; + r+= "\n\n"; + return r; +} + +void QPxToolMW::about() +{ +#ifndef QT_NO_DEBUG + qDebug("QPxToolMW::about()"); +#endif + AboutDialog *about = new AboutDialog(this); + about->exec(); + delete about; +} + +void QPxToolMW::save_results() +{ +#ifndef QT_NO_DEBUG + qDebug("QPxToolMW::save_results()"); + timeval tb,te; +#endif + QString fname; + QFile f; + QFileInfo finfo; + + if (!devices.size()) return; + device *dev = devices.current(); + + fname = QFileDialog::getSaveFileName(this, tr("Save results to file..."), set.last_res_path_native, "QPxTool data files (*.qpx)"); + if (fname.isEmpty()) return; + + f.setFileName(fname); + finfo.setFile(f); + set.last_res_path_native = finfo.absoluteDir().canonicalPath(); + qDebug() << "Saving data to: " << fname; + + if (!f.open(QIODevice::WriteOnly)) { + QMessageBox::warning(this, tr("Error"), tr("Unable to create file:\n")+fname); + return; + } +#ifndef QT_NO_DEBUG + gettimeofday(&tb, NULL); +#endif + + progress = new ProgressWidget(10,3,this); + progress->setText(tr("Saving results...")); + progress->show(); + + dev->save(&f); + while(dev->isSaving()) { msleep ( 1 << 5); qApp->processEvents(); } + + delete progress; + if (!dev->saveResult()) { + QMessageBox::warning(this, tr("Warning"), tr("Error saving tests data!")); +#ifndef QT_NO_DEBUG + } else { + gettimeofday(&te, NULL); + double t = (te.tv_sec - tb.tv_sec) + (te.tv_usec - tb.tv_usec)/1000000.0; + QMessageBox::information(this, tr("Info"), tr("Tests data saved in %1 sec").arg(t,0,'f',2)); +#endif + } + f.close(); +} + +void QPxToolMW::save_results_db(device *idev) +{ +#ifndef QT_NO_DEBUG + qDebug("QPxToolMW::save_results_db()"); + timeval tb,te; +#endif + QByteArray ba; + QSqlQuery *q = NULL; + QBuffer buf(&ba, this); + device *dev = idev; + int id_vendor, id_model, id_media; + + + if (!devices.size()) return; + if (!dev) dev = devices.current(); + +#warning !!! set media label before saving to database + if (dev->media.label.isEmpty()) { + // + } + + progress = new ProgressWidget(10,3,this); + progress->setText(tr("Saving results...")); + progress->show(); + + q = new QSqlQuery(QSqlDatabase::database("reports")); +// read media id + q->prepare("SELECT id FROM media_types WHERE name=:name"); + q->bindValue(":name", dev->media.type); + if (!q->exec()) { + qDebug() << q->lastError().text(); + goto close_db; + } + if (!q->next()) { + qDebug() << "No ID found for current media"; + goto close_db; + } + id_media = q->value(0).toInt(); + qDebug() << "id_media : " << id_media; + +// check&update vendor + q->prepare("SELECT id FROM dev_vendors WHERE name=:name"); + q->bindValue(":name", dev->ven); + if (!q->exec()) { + qDebug() << q->lastError().text(); + goto close_db; + } + if (!q->next()) { + q->prepare("INSERT INTO dev_vendors (name) VALUES (:name) RETURNING id"); + q->bindValue(":name", dev->ven); + if (!q->exec()) { + qDebug() << q->lastError().text(); + goto close_db; + } + q->next(); + qDebug() << "added vendor id " << q->value(0).toInt(); + } + id_vendor = q->value(0).toInt(); + qDebug() << "id_vendor: " << id_vendor; + +// check&update model + q->prepare("SELECT id FROM dev_models WHERE name=:name AND id_vendor=:id_vendor"); + q->bindValue(":id_vendor", id_vendor); + q->bindValue(":name", dev->dev); + if (!q->exec()) { + qDebug() << q->lastError().text(); + goto close_db; + } + if (!q->next()) { +// q->prepare("INSERT INTO dev_models (id_vendor,name) VALUES ("+QString::number(id_vendor)+",'"+dev->dev+"') RETURNING id"); + q->prepare("INSERT INTO dev_models (id_vendor,name) VALUES (:id_vendor,:name) RETURNING id"); + q->bindValue(":id_vendor", id_vendor); + q->bindValue(":name", dev->dev); + if (!q->exec()) { + qDebug() << q->lastError().text(); + goto close_db; + } + q->next(); + qDebug() << "added model id " << q->value(0).toInt(); + } + id_model = q->value(0).toInt(); + qDebug() << "id_model : " << id_model; + +// preparing report data... + qDebug() << "Filling buffer with report data..."; + + if (!buf.open(QIODevice::WriteOnly)) { + QMessageBox::warning(this, tr("Error"), tr("Unable to open buffer!\n")); + goto close_db; + } +#ifndef QT_NO_DEBUG + gettimeofday(&tb, NULL); +#endif + + dev->save(&buf); + while(dev->isSaving()) { msleep ( 1 << 5); qApp->processEvents(); } + + + if (!dev->saveResult()) { + QMessageBox::warning(this, tr("Warning"), tr("Error saving tests data!")); +#ifndef QT_NO_DEBUG + } else { + gettimeofday(&te, NULL); + double t = (te.tv_sec - tb.tv_sec) + (te.tv_usec - tb.tv_usec)/1000000.0; + QMessageBox::information(this, tr("Info"), tr("Tests data saved in %1 sec").arg(t,0,'f',2)); +#endif + } + buf.close(); + + qDebug() << "Buffer prepared, writing to database..."; + +// writing results... + q->prepare("INSERT INTO reports ( \ + dev_id, dev_fw, \ + id_media_type, label, copy_idx, \ + md5, data_xml \ + ) VALUES (\ + :dev_id, :dev_fw, \ + :id_media_type, :label, :copy_idx, \ + :md5, :data_xml \ + ) RETURNING id"); + +#warning empty fields in report saving query!!! + q->bindValue(":dev_id", id_model); + q->bindValue(":dev_fw", dev->fw); + q->bindValue(":id_media_type", id_media); + q->bindValue(":label", dev->media.label); + q->bindValue(":copy_idx", 0); + q->bindValue(":md5", ""); + q->bindValue(":data_xml", ba); + if (!q->exec()) { + qDebug() << q->lastError().text(); + } else { + q->next(); + qDebug() << "added report id " << q->value(0).toInt(); + } + +close_db: + if (q) delete q; + delete progress; +} + +void QPxToolMW::load_results() +{ + QString fname; + QFileInfo finfo; + if (!devices.size()) return; + fname = QFileDialog::getOpenFileName(this, tr("Load results from file..."), set.last_res_path_native, "QPxTool data files (*.qpx)"); + if (fname.isEmpty()) return; + + finfo.setFile(fname); + set.last_res_path_native = finfo.absoluteDir().canonicalPath(); + qDebug() << "Loading data from: " << fname; + + load_results(fname); +} + +void QPxToolMW::load_results(QString fname) +{ +#ifndef QT_NO_DEBUG + qDebug("QPxToolMW::load_results()"); + timeval tb,te; +#endif + QFile f; + + if (!devices.size()) return; + device *dev = devices[0]; + + if (fname.isEmpty()) return; +// c_dev->setCurrentIndex(0); +// setDevice(0); + + f.setFileName(fname); + qDebug() << "Loading data from: " << fname; + + if (!f.open(QIODevice::ReadOnly)) { + QMessageBox::warning(this, tr("Error"), tr("Unable to open file:\n")+fname); + return; + } +#ifndef QT_NO_DEBUG + gettimeofday(&tb, NULL); +#endif + + progress = new ProgressWidget(10,3,this); + progress->setText(tr("Loading results...")); + progress->show(); + + dev->load(&f); + while(dev->isLoading()) { msleep ( 1 << 5); qApp->processEvents(); } + + delete progress; + if (!dev->loadResult()) { + QMessageBox::warning(this, tr("Warning"), tr("Do tests data found in this file!")); + } else { + c_dev->setCurrentIndex(0); +#ifndef QT_NO_DEBUG + gettimeofday(&te, NULL); + double t = (te.tv_sec - tb.tv_sec) + (te.tv_usec - tb.tv_usec)/1000000.0; + QMessageBox::information(this, tr("Info"), tr("Tests data loaded in %1 sec").arg(t,0,'f',2)); +#endif + } + f.close(); +// setDevice(0); +} + +void QPxToolMW::load_results_db() +{ +#ifndef QT_NO_DEBUG + qDebug("QPxToolMW::load_results_db()"); + timeval tb,te; +#endif + QByteArray ba; + QSqlQuery *q = NULL; + QBuffer buf(this); + DbReportSelection *rsel; + int id_report; + + if (!devices.size()) return; + device *dev = devices[0]; + +#ifndef QT_NO_DEBUG + gettimeofday(&tb, NULL); +#endif + + progress = new ProgressWidget(10,3,this); + progress->setText(tr("Loading results...")); +// report selection... + rsel = new DbReportSelection("report", this); + if (rsel->exec() == QDialog::Rejected) { + delete rsel; + return; + } + id_report = rsel->getReportID(); + delete rsel; + + if (id_report<0) { + qDebug() << "Invalid report ID!"; + goto close_db; + } + + progress->show(); + qApp->processEvents(); +// reading report from database + qDebug() << "Reading report from database..."; + + q = new QSqlQuery(QSqlDatabase::database("reports")); + qDebug() << "Report ID:" << id_report; + q->prepare("SELECT data_xml FROM reports WHERE id=:id"); + q->bindValue(":id", id_report); + if (!q->exec()) { + qDebug() << q->lastError().text(); + goto close_db; + } + if (!q->next()) { + qDebug() << "Invalid report ID!"; + goto close_db; + } + + qApp->processEvents(); + ba = q->value(0).toByteArray(); + +// parsing data... + buf.setBuffer(&ba); + if (!buf.open(QIODevice::ReadOnly)) { + QMessageBox::warning(this, tr("Error"), tr("Unable to open buffer!\n")); + goto close_db; + } + + dev->load(&buf); + while(dev->isLoading()) { msleep ( 1 << 5); qApp->processEvents(); } + + if (!dev->loadResult()) { + QMessageBox::warning(this, tr("Warning"), tr("No tests data found in this file!")); + } else { + c_dev->setCurrentIndex(0); +#ifndef QT_NO_DEBUG + gettimeofday(&te, NULL); + double t = (te.tv_sec - tb.tv_sec) + (te.tv_usec - tb.tv_usec)/1000000.0; + QMessageBox::information(this, tr("Info"), tr("Tests data loaded in %1 sec").arg(t,0,'f',2)); +#endif + } + +close_db: + if (q) delete q; + delete progress; +} + +void QPxToolMW::dragEnterEvent(QDragEnterEvent* e) +{ + qDebug() << "dragEnterEvent()"; + if (e->mimeData()->hasFormat("text/plain")) { + QUrl url(e->mimeData()->text().simplified()); + if (url.path().endsWith(".qpx")) + e->acceptProposedAction(); + } +} + +void QPxToolMW::dropEvent(QDropEvent* e) +{ + qDebug() << "dropEvent()"; + QUrl url(e->mimeData()->text().simplified()); + load_results(url.path()); + e->acceptProposedAction(); +} + + diff --git a/gui/src/mcapwidget.cpp b/gui/src/mcapwidget.cpp new file mode 100644 index 0000000..0656c8c --- /dev/null +++ b/gui/src/mcapwidget.cpp @@ -0,0 +1,106 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 + +#include "mcapwidget.h" + +#define SHOW_X +#define MAX_ICON_SIZE 16 + +MediaCapWidget::MediaCapWidget(QString itext, bool tri, quint64 df, QWidget *p, Qt::WindowFlags f) + : QWidget(p,f) +{ + tristate = tri; + text = itext; + cf = df; + rd=0, wr=0; + setMinimumSize(128, MAX_ICON_SIZE+4); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); +}; + +MediaCapWidget::~MediaCapWidget() {}; + +void MediaCapWidget::setText(QString itext) +{ + text=itext; + update(); +}; + +QSize MediaCapWidget::sizeHint() const +{ + return QSize( MAX_ICON_SIZE + 20 + fontMetrics().width(text), MAX_ICON_SIZE+2); +} + +void MediaCapWidget::setR(quint64 r) { + if (!tristate) return; + rd= ((cf & r)); + setRW(); +}; + +void MediaCapWidget::setW(quint64 w) { + if (!tristate) return; + wr= ((cf & w)); + setRW(); +}; + +void MediaCapWidget::setRW() { + if (wr) { + icon.load(":images/cdwriter.png"); + } else if (rd) { + icon.load(":images/disc.png"); + } else { +#ifdef SHOW_X + icon.load(":images/x.png"); +#else + icon = QImage(); +#endif + } + + icon = icon.scaled( QSize(MAX_ICON_SIZE, MAX_ICON_SIZE), Qt::KeepAspectRatio, Qt::SmoothTransformation ); + update(); +}; + +void MediaCapWidget::setCap(quint64 s) { + if (tristate) return; + rd= (( cf & s)); + + if (rd) { + icon.load(":images/ok.png"); + icon = QImage(":images/ok.png"); + } else { +#ifdef SHOW_X + icon.load(":images/x.png"); + //icon = QImage(":images/x.png"); +#else + icon = QImage(); + //icon.clear(); +#endif + } + + icon = icon.scaled( QSize(MAX_ICON_SIZE, MAX_ICON_SIZE), Qt::KeepAspectRatio, Qt::SmoothTransformation ); + update(); +}; + +void MediaCapWidget::clear() { + icon = QImage(); + //icon.clear(); + update(); +}; + +void MediaCapWidget::paintEvent(QPaintEvent*) +{ + QPainter p(this); + if (cf) p.drawImage( 4, ((height()-icon.height()) >> 1) + 2, icon); + p.drawText(MAX_ICON_SIZE + 9, 4, width()-MAX_ICON_SIZE-13, height()-4, Qt::AlignLeft | Qt::AlignVCenter, text); +}; + diff --git a/gui/src/mwatcher.cpp b/gui/src/mwatcher.cpp new file mode 100644 index 0000000..1892ccd --- /dev/null +++ b/gui/src/mwatcher.cpp @@ -0,0 +1,102 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2010 Gennady "ShultZ" Kozlov + * + * 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 "mwatcher.h" +#include + +#include +#include + + +MediaWatcher::MediaWatcher(device *qdev) +{ +#ifndef QT_NO_DEBUG + qDebug("MediaWatcher()"); +#endif + if (qdev->type == device::DevtypeLocal) { + QByteArray ba; + ba+=qdev->path; + dev = new drive_info(ba.data()); + if (inquiry(dev)) { + delete dev; + dev = NULL; + } + } else { + dev = NULL; + } +} + +MediaWatcher::~MediaWatcher() +{ +#ifndef QT_NO_DEBUG + qDebug("~MediaWatcher()"); +#endif + if (dev) delete dev; +} + +void MediaWatcher::stop() { sreq=1; } +void MediaWatcher::pause() { preq=1; } +void MediaWatcher::unPause() { preq=0; } + +void MediaWatcher::run() +{ + if (!dev) { + qDebug("Can't start watcher: NULL device!"); + return; + } + + int op,cp; +#ifndef QT_NO_DEBUG + qDebug() << dev->device << ": watcher started"; +#endif + sreq = 0; preq = 0; +// initialising surrent status + op = cp = test_unit_ready(dev); + while (!sreq) { + if (!preq) { + cp = test_unit_ready(dev); + + if (cp!=op) { + switch (cp) { + case -1: + cp = op; + break; + case 0x00000: // media found + qDebug() << dev->device << ": new media found"; + emit mediaNew(); + break; + case 0x23A01: // no media, tray closed + qDebug() << dev->device << ": tray closed, but no media found"; + emit mediaNoMedia(); + break; + case 0x23A02: // no media, tray open + qDebug() << dev->device << ": media removed"; + emit mediaRemoved(); + break; + case 0x20401: // media recognition + emit mediaLoading(); + qDebug() << dev->device << ": loading media"; + break; + default: + print_sense(cp); printf("\n"); + break; + } + op = cp; + } + } + sleep(1); + } +#ifndef QT_NO_DEBUG + qDebug() << dev->device << ": watcher stoped"; +#endif +} + diff --git a/gui/src/pref_colors.cpp b/gui/src/pref_colors.cpp new file mode 100644 index 0000000..57be74b --- /dev/null +++ b/gui/src/pref_colors.cpp @@ -0,0 +1,403 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2012 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "pref_colors.h" +#include + +prefColors::prefColors(QPxSettings *iset, QWidget *p, Qt::WindowFlags fl) + : QWidget(p,fl) +{ + QAction *tact; +#ifndef QT_NO_DEBUG + qDebug("STA: prefColors()"); +#endif + set = iset; + QFrame *f; + int idx=0; + + layout = new QGridLayout(this); + layout->setMargin(0); + layout->setSpacing(3); + + lc_bg = new ColorLabel(set->col_bg, tr("Background"), 1, this); + layout->addWidget(lc_bg,idx,0); + lc_grid = new ColorLabel(set->col_grid, tr("Grid"), 1, this); + layout->addWidget(lc_grid,idx,1); + idx++; + + f = new QFrame(this); + f->setFrameStyle(QFrame::HLine | QFrame::Sunken); + layout->addWidget(f,idx,0,1,2); + idx++; + + lc_rspeed = new ColorLabel(set->col_rspeed, tr("Read Speed"), 1, this); + layout->addWidget(lc_rspeed,idx,0); + lc_wspeed = new ColorLabel(set->col_wspeed, tr("Write Speed"), 1, this); + layout->addWidget(lc_wspeed,idx,1); + idx++; + + f = new QFrame(this); + f->setFrameStyle(QFrame::HLine | QFrame::Sunken); + layout->addWidget(f,idx,0,1,2); + idx++; + + lc_jitter = new ColorLabel(set->col_jitter, tr("Jitter"), 1, this); + layout->addWidget(lc_jitter,idx,0); + lc_asymm = new ColorLabel(set->col_asymm, tr("Asymmetry"), 1, this); + layout->addWidget(lc_asymm,idx,1); + idx++; + + f = new QFrame(this); + f->setFrameStyle(QFrame::HLine | QFrame::Sunken); + layout->addWidget(f,idx,0,1,2); + idx++; + + lc_errc[0] = new ColorLabel(*set->col_errc.raw[0], "BLER", 1, this); + layout->addWidget(lc_errc[0],idx,0); + lc_errc[4] = new ColorLabel(*set->col_errc.raw[4], "E12/POE/BIS", 1, this); + layout->addWidget(lc_errc[4],idx,1); + idx++; + lc_errc[1] = new ColorLabel(*set->col_errc.raw[1], "E11/PIE/LDC", 1, this); + layout->addWidget(lc_errc[1],idx,0); + lc_errc[5] = new ColorLabel(*set->col_errc.raw[5], "E22/POsum8", 1, this); + layout->addWidget(lc_errc[5],idx,1); + idx++; + lc_errc[2] = new ColorLabel(*set->col_errc.raw[2], "E21/PIsum8", 1, this); + layout->addWidget(lc_errc[2],idx,0); + lc_errc[6] = new ColorLabel(*set->col_errc.raw[6], "E32/POF", 1, this); + layout->addWidget(lc_errc[6],idx,1); + idx++; + lc_errc[3] = new ColorLabel(*set->col_errc.raw[3], "E31/PIF", 1, this); + layout->addWidget(lc_errc[3],idx,0); + lc_errc[7] = new ColorLabel(*set->col_errc.raw[7], "UNCR", 1, this); + layout->addWidget(lc_errc[7],idx,1); + idx++; + + f = new QFrame(this); + f->setFrameStyle(QFrame::HLine | QFrame::Sunken); + layout->addWidget(f,idx,0,1,2); + idx++; + + lc_fe = new ColorLabel(set->col_fe, tr("Focus Errors"), 1, this); + layout->addWidget(lc_fe,idx,0); + lc_te = new ColorLabel(set->col_te,tr("Tracking errors"), 1, this); + layout->addWidget(lc_te,idx,1); + idx++; + + f = new QFrame(this); + f->setFrameStyle(QFrame::HLine | QFrame::Sunken); + layout->addWidget(f,idx,0,1,2); + idx++; + + lc_tapit = new ColorLabel(set->col_tapit, "TA pits", 1, this); + layout->addWidget(lc_tapit,idx,0); + lc_taland = new ColorLabel(set->col_taland,"TA lands", 1, this); + layout->addWidget(lc_taland,idx,1); + idx++; + + layout->setRowStretch(idx, 10); + idx++; + + f = new QFrame(this); + f->setFrameStyle(QFrame::HLine | QFrame::Sunken); + layout->addWidget(f,idx,0,1,2); + idx++; + + loadPresets(); + + pb_preset = new QPushButton(tr("Presets"),this); +// pb_preset->setFlat(true); + menu_preset = new QMenu(this); + act_default = menu_preset->addAction(tr("Default"), this, SLOT(usePreset())); + menu_preset->addSeparator(); + + for (int i=0; iaddAction(presets[i].name, this, SLOT(usePreset())); + act_presets.append(tact); + } + pb_preset->setMenu(menu_preset); + e_preset = new QLineEdit(this); + e_preset->setMinimumWidth(120); + e_preset->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + pb_add = new QPushButton(QIcon(":images/add.png"),"",this); + pb_del = new QPushButton(QIcon(":images/x.png"),"",this); + pb_replace = new QPushButton(QIcon(":images/save.png"),"",this); + + connect(pb_add, SIGNAL(clicked()), this, SLOT(addPreset())); + connect(pb_del, SIGNAL(clicked()), this, SLOT(delPreset())); + connect(pb_replace, SIGNAL(clicked()), this, SLOT(replacePreset())); + connect(e_preset, SIGNAL(textChanged(QString)), this, SLOT(presetNameValidate(QString))); + pb_add->setEnabled(false); + pb_del->setEnabled(false); + pb_replace->setEnabled(false); + + layout_presets = new QHBoxLayout(); + layout->addLayout(layout_presets, idx, 0, 1, 2); + layout_presets->addWidget(pb_preset); + layout_presets->addWidget(e_preset); + layout_presets->addWidget(pb_replace); + layout_presets->addWidget(pb_add); + layout_presets->addWidget(pb_del); + + +#ifndef QT_NO_DEBUG + qDebug("END: prefColors()"); +#endif +} + +prefColors::~prefColors() +{ +#ifndef QT_NO_DEBUG + qDebug("STA: ~prefColors()"); +#endif + savePresets(); + + set->col_bg = lc_bg->color(); + set->col_bginv = QColor( (~set->col_bg.red()) & 0xFF, (~set->col_bg.green()) & 0xFF, (~set->col_bg.blue()) & 0xFF ); + set->col_grid = lc_grid->color(); + + set->col_rspeed = lc_rspeed->color(); + set->col_wspeed = lc_wspeed->color(); + + for (int i=0;i<8;i++) + *set->col_errc.raw[i] = lc_errc[i]->color(); + + set->col_jitter = lc_jitter->color(); + set->col_asymm = lc_asymm->color(); + set->col_fe = lc_fe->color(); + set->col_te = lc_te->color(); + set->col_tapit = lc_tapit->color(); + set->col_taland = lc_taland->color(); +#ifndef QT_NO_DEBUG + qDebug("END: ~prefColors()"); +#endif +} + +void prefColors::addPreset() +{ + QAction *act; + colorSet newSet; + + newSet.name = e_preset->text(); + newSet.bg = lc_bg->color(); + newSet.grid = lc_grid->color(); + newSet.rspeed = lc_rspeed->color(); + newSet.wspeed = lc_wspeed->color(); + + for (int i=0;i<8;i++) + newSet.errc[i] = lc_errc[i]->color(); + + newSet.jitter = lc_jitter->color(); + newSet.asymm = lc_asymm->color(); + newSet.fe = lc_fe->color(); + newSet.te = lc_te->color(); + newSet.tapit = lc_tapit->color(); + newSet.taland = lc_taland->color(); + + act = menu_preset->addAction(newSet.name, this, SLOT(usePreset())); + presets.append(newSet); + act_presets.append(act); + + presetNameValidate(newSet.name); +} + +void prefColors::delPreset() +{ + QString name = e_preset->text(); + QAction *act; + int idx = -1; + for (int p=0; idx<0 && premoveAction(act); + delete act; + + presetNameValidate(name); +} + +void prefColors::replacePreset() +{ + QString name = e_preset->text(); + colorSet *newSet; + int idx = -1; + for (int p=0; idx<0 && pname = e_preset->text(); + newSet->bg = lc_bg->color(); + newSet->grid = lc_grid->color(); + newSet->rspeed = lc_rspeed->color(); + newSet->wspeed = lc_wspeed->color(); + + for (int i=0;i<8;i++) + newSet->errc[i] = lc_errc[i]->color(); + + newSet->jitter = lc_jitter->color(); + newSet->asymm = lc_asymm->color(); + newSet->fe = lc_fe->color(); + newSet->te = lc_te->color(); + newSet->tapit = lc_tapit->color(); + newSet->taland = lc_taland->color(); +} + +void prefColors::presetNameValidate(QString name) +{ + int idx = -1; + if (name.isEmpty() || name == "Default") { + pb_add->setEnabled(false); + pb_del->setEnabled(false); + pb_replace->setEnabled(false); + return; + } + for (int p=0; idx<0 && psetEnabled( (idx<0) ); + pb_del->setEnabled( (idx>=0) ); + pb_replace->setEnabled( (idx>=0) ); +} + +void prefColors::usePreset() +{ + int idx=-1; + QAction *act = (QAction*) sender(); + const colorSet *preset; + colorSet newSet; + + if (act == act_default) { + preset = &defColors; + e_preset->setText(preset->name); +#ifndef QT_NO_DEBUG + qDebug() << "Preset: default"; +#endif + } else { + for (int p=0; psetText(preset->name); +#ifndef QT_NO_DEBUG + qDebug() << "Preset #" << idx << ":" << preset->name; +#endif + } + + lc_bg->setColor(preset->bg); + lc_grid->setColor(preset->grid); + + lc_rspeed->setColor(preset->rspeed); + lc_wspeed->setColor(preset->wspeed); + + for (int i=0;i<8;i++) + lc_errc[i]->setColor(preset->errc[i]); + + lc_jitter->setColor(preset->jitter); + lc_asymm->setColor(preset->asymm); + lc_fe->setColor(preset->fe); + lc_te->setColor(preset->te); + lc_tapit->setColor(preset->tapit); + lc_taland->setColor(preset->taland); +} + + +void prefColors::loadPresets() +{ + QSettings *settings; + QStringList list; + colorSet newSet; +#ifndef QT_NO_DEBUG + qDebug("Loading color presets..."); +#endif + +// settings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, "qpxtool"); + settings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, "QPxTool", "qpxtool"); + settings->beginGroup("/colors"); + list = settings->value("presets_list", 0).toStringList(); + for (int p=0; pbeginGroup("/preset_"+newSet.name); + + newSet.bg = settings->value("graph_bg", defColors.bg).toInt(); + newSet.grid = settings->value("graph_grid", defColors.grid).toInt(); + newSet.rspeed = settings->value("rspeed", defColors.rspeed).toInt(); + newSet.wspeed = settings->value("wspeed", defColors.wspeed).toInt(); + for(int i=0; i<8; i++) + newSet.errc[i] = settings->value(QString("errc%1").arg(i), defColors.errc[i]).toInt(); + newSet.jitter = settings->value("jitter", defColors.jitter).toInt(); + newSet.asymm = settings->value("asymm", defColors.asymm).toInt(); + newSet.fe = settings->value("fe", defColors.fe).toInt(); + newSet.te = settings->value("te", defColors.te).toInt(); + newSet.tapit = settings->value("ta_pit", defColors.tapit).toInt(); + newSet.taland = settings->value("ta_land", defColors.taland).toInt(); + + settings->endGroup(); + presets.append(newSet); + } + settings->endGroup(); + + delete settings; +} + +void prefColors::savePresets() +{ + QSettings *settings; + QStringList list; +#ifndef QT_NO_DEBUG + qDebug("Saving color presets..."); +#endif + +// settings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, "qpxtool"); + settings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, "QPxTool", "qpxtool"); + settings->beginGroup("/colors"); + for (int p=0; pbeginGroup("/preset_"+presets[p].name); + + settings->setValue("graph_bg", presets[p].bg.rgb()); + settings->setValue("graph_grid", presets[p].grid.rgb()); + settings->setValue("rspeed", presets[p].rspeed.rgb()); + settings->setValue("wspeed", presets[p].wspeed.rgb()); + for(int i=0; i<8; i++) + settings->setValue(QString("errc%1").arg(i), presets[p].errc[i].rgb()); + settings->setValue("jitter", presets[p].jitter.rgb()); + settings->setValue("asymm", presets[p].asymm.rgb()); + settings->setValue("fe", presets[p].fe.rgb()); + settings->setValue("te", presets[p].te.rgb()); + settings->setValue("ta_pit", presets[p].tapit.rgb()); + settings->setValue("ta_land", presets[p].taland.rgb()); + + settings->endGroup(); + + } + settings->setValue("presets_list", list); + + settings->endGroup(); + + delete settings; +} + diff --git a/gui/src/pref_common.cpp b/gui/src/pref_common.cpp new file mode 100644 index 0000000..0e20208 --- /dev/null +++ b/gui/src/pref_common.cpp @@ -0,0 +1,115 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2010 Gennady "ShultZ" Kozlov + * + * 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 +#include "pref_common.h" +#include + +#include +#include +#include + +#include +#include + +prefCommon::prefCommon(QPxSettings *iset, QWidget *p, Qt::WindowFlags fl) + : QWidget(p,fl) +{ +#ifndef QT_NO_DEBUG + qDebug("STA: prefCommon()"); +#endif + set = iset; + + layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(3); + +// default actions + ck_autow = new QCheckBox(tr("Autostart tests on written media inserted"), this); + ck_autob = new QCheckBox(tr("Autostart tests on blank media inserted"), this); + + layout->addWidget(ck_autow); + layout->addWidget(ck_autob); + +// default tests for written media + pl_testsw = new QLabel("" + tr("Default tests for written media") + "",this); + pl_testsw->setAlignment(Qt::AlignCenter); + pl_testsw->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + layout->addWidget(pl_testsw); + + ck_rt = new QCheckBox(tr("Read Transfer Rate"),this); + ck_errc = new QCheckBox(tr("Error Correction"),this); + ck_jb = new QCheckBox(tr("Jitter/Asymmetry"),this); + ck_ftw = new QCheckBox(tr("Focus/Tracking"),this); + ck_ta = new QCheckBox(tr("Time Analyser"),this); + layout->addWidget(ck_rt); + layout->addWidget(ck_errc); + layout->addWidget(ck_jb); + layout->addWidget(ck_ftw); + layout->addWidget(ck_ta); + +// default tests for blank media + pl_testsb = new QLabel("" + tr("Default tests for blank media") + "",this); + pl_testsb->setAlignment(Qt::AlignCenter); + pl_testsb->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + layout->addWidget(pl_testsb); + layout_testsb = new QGridLayout(); + layout_testsb->setMargin(0); + layout_testsb->setSpacing(3); + layout->addLayout(layout_testsb); + + ck_wt = new QCheckBox(tr("Write Transfer Rate"),this); + ck_wt_simul = new QCheckBox(tr("Simulation"),this); ck_wt_simul->setEnabled(false); + ck_ftb = new QCheckBox(tr("Focus/Tracking"),this); + layout_testsb->addWidget(ck_wt, 0,0,1,2); + layout_testsb->addWidget(ck_wt_simul,1,1); + layout_testsb->addWidget(ck_ftb, 2,0,1,2); + + layout_testsb->setColumnStretch(0,1); + layout_testsb->setColumnStretch(1,10); + layout->addStretch(10); + +// creating objects connections... + connect(ck_wt, SIGNAL(toggled(bool)), ck_wt_simul, SLOT(setEnabled(bool))); + +// applying settings... + ck_autow->setChecked(set->actions_flags & AFLAG_AUTOSTART_W); + ck_autob->setChecked(set->actions_flags & AFLAG_AUTOSTART_B); + + ck_rt->setChecked(set->actions_flags & AFLAG_DTEST_RT); + ck_wt->setChecked(set->actions_flags & AFLAG_DTEST_WT); + ck_wt_simul->setChecked(set->actions_flags & AFLAG_DTEST_WT_SIMUL); + ck_errc->setChecked(set->actions_flags & AFLAG_DTEST_ERRC); + ck_jb->setChecked(set->actions_flags & AFLAG_DTEST_JB); + ck_ftw->setChecked(set->actions_flags & AFLAG_DTEST_FT_W); + ck_ftb->setChecked(set->actions_flags & AFLAG_DTEST_FT_B); + ck_ta->setChecked(set->actions_flags & AFLAG_DTEST_TA); +#ifndef QT_NO_DEBUG + qDebug("END: prefCommon()"); +#endif +} + +prefCommon::~prefCommon() +{ + if (ck_autow->isChecked()) set->actions_flags |= AFLAG_AUTOSTART_W; else set->actions_flags &= ~AFLAG_AUTOSTART_W; + if (ck_autob->isChecked()) set->actions_flags |= AFLAG_AUTOSTART_B; else set->actions_flags &= ~AFLAG_AUTOSTART_B; + + if (ck_rt->isChecked()) set->actions_flags |= AFLAG_DTEST_RT; else set->actions_flags &= ~AFLAG_DTEST_RT; + if (ck_wt->isChecked()) set->actions_flags |= AFLAG_DTEST_WT; else set->actions_flags &= ~AFLAG_DTEST_WT; + if (ck_wt_simul->isChecked()) set->actions_flags |= AFLAG_DTEST_WT_SIMUL; else set->actions_flags &= ~AFLAG_DTEST_WT_SIMUL; + if (ck_errc->isChecked()) set->actions_flags |= AFLAG_DTEST_ERRC; else set->actions_flags &= ~AFLAG_DTEST_ERRC; + if (ck_jb->isChecked()) set->actions_flags |= AFLAG_DTEST_JB; else set->actions_flags &= ~AFLAG_DTEST_JB; + if (ck_ftw->isChecked()) set->actions_flags |= AFLAG_DTEST_FT_W; else set->actions_flags &= ~AFLAG_DTEST_FT_W; + if (ck_ftb->isChecked()) set->actions_flags |= AFLAG_DTEST_FT_B; else set->actions_flags &= ~AFLAG_DTEST_FT_B; + if (ck_ta->isChecked()) set->actions_flags |= AFLAG_DTEST_TA; else set->actions_flags &= ~AFLAG_DTEST_TA; +} + diff --git a/gui/src/pref_devices.cpp b/gui/src/pref_devices.cpp new file mode 100644 index 0000000..38c849e --- /dev/null +++ b/gui/src/pref_devices.cpp @@ -0,0 +1,211 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "pref_devices.h" +#include + +prefDevices::prefDevices(QPxSettings *iset, QWidget *p, Qt::WindowFlags f) + : QWidget(p,f) +{ +#ifndef QT_NO_DEBUG + qDebug("STA: prefDevices()"); +#endif + set = iset; + + layout = new QVBoxLayout(this); + layout->setMargin(0); + + ck_local = new QCheckBox(tr("Use local devices"),this); + ck_local->setChecked(set->useLocal); + layout->addWidget(ck_local); + + ck_remote = new QCheckBox(tr("Use network devices"),this); + ck_remote->setChecked(set->useRemote); + layout->addWidget(ck_remote); + +// g_hosts = new QGroupBox(tr("Hosts"), this); + //g_hosts->setCheckable(true); +// layout->addWidget(g_hosts); + +// l_hosts = new QVBoxLayout(g_hosts); + + //lst_hosts = new QTreeWidget(g_hosts); + lst_hosts = new QTreeWidget(this); + lst_hosts->setRootIsDecorated(false); + lst_hosts->setColumnCount(2); + lst_hosts->setColumnWidth(0, 130); + lst_hosts->setHeaderLabels(QStringList() << tr("host") << tr("qscand port")); + layout->addWidget(lst_hosts); + + QTreeWidgetItem *hitem; + for (int i=0; ihosts.size(); i++) { + //hitem = new QTreeWidgetItem(lst_hosts, QStringList(set->hosts[i]) ); + hitem = new QTreeWidgetItem(lst_hosts); + hitem->setFlags( hitem->flags() | Qt::ItemIsUserCheckable); + hitem->setCheckState(0, (set->hosts[i][0] == '*') ? Qt::Checked : Qt::Unchecked); + + hitem->setText(0, set->hosts[i].remove(0,1) ); + if (iports.size()) + hitem->setText(1, set->ports[i] ); + else + hitem->setText(1, "46660" ); + lst_hosts->addTopLevelItem(hitem); + } +// lst_hosts->sortItems(0,Qt::AscendingOrder); + lst_hosts->installEventFilter(this); +#ifndef QT_NO_DEBUG + qDebug("END: prefDevices()"); +#endif +} + +prefDevices::~prefDevices() +{ +#ifndef QT_NO_DEBUG + qDebug("STA: ~prefDevices()"); +#endif + int hcnt = lst_hosts->topLevelItemCount(); + QTreeWidgetItem *hitem; + + set->useLocal = ck_local->isChecked(); + set->useRemote = ck_remote->isChecked(); + set->hosts.clear(); + set->ports.clear(); + + for (int i=0; itopLevelItem(i); + + set->hosts.append( ((hitem->checkState(0) == Qt::Checked) ? "*" : "-") + hitem->text(0) ); + set->ports.append( hitem->text(1) ); + } + +#ifndef QT_NO_DEBUG + qDebug("END: ~prefDevices()"); +#endif +} + +bool prefDevices::eventFilter(QObject* obj, QEvent* e) +{ + if (obj == lst_hosts) { +#ifndef QT_NO_DEBUG +// qDebug(QString("lst_hosts::event: %1").arg(e->type())); +#endif + if (e->type() == QEvent::ContextMenu) { + hostsContextMenu( (QContextMenuEvent*)e ); + return true; +// } else if (e->type() == QEvent::MouseButtonDblClick) { +// hostEdit(); +// return true; + } else if (e->type() == QEvent::KeyPress) { + switch ( ((QKeyEvent*)e)->key()) { + // case Qt::Key_Enter: + case Qt::Key_Return: + hostEdit(); + return true; + case Qt::Key_Insert: + hostAdd(); + return true; + case Qt::Key_Delete: + hostRemove(); + return true; + default: + return false; + } + } else { + return false; + } + } else { + QWidget::eventFilter(obj,e); + } + return false; +} + +void prefDevices::hostsContextMenu(QContextMenuEvent* e) +{ + QMenu *cmenu; + QAction *act; +#ifndef QT_NO_DEBUG + qDebug("hostsContextMenu()"); +#endif + cmenu = new QMenu(this); + act = cmenu->addAction(QIcon(":images/edit.png"),tr("Edit host"), this, SLOT(hostEdit())); + if (!lst_hosts->currentItem()) act->setEnabled(false); + act = cmenu->addAction(QIcon(":images/add.png"), tr("Add host"), this, SLOT(hostAdd())); + act = cmenu->addAction(QIcon(":images/x.png"), tr("Remove host"), this, SLOT(hostRemove())); + if (!lst_hosts->currentItem()) act->setEnabled(false); + cmenu->exec(e->globalPos()); + delete cmenu; +} + +void prefDevices::hostAdd() +{ + QTreeWidgetItem *hitem; +#ifndef QT_NO_DEBUG + qDebug("hostAdd()"); +#endif + hostEditDialog *hadd = new hostEditDialog("", 46660, this); + if (hadd->exec() && !hadd->hostname().isEmpty()) { + hitem = new QTreeWidgetItem(lst_hosts); + hitem->setFlags(hitem->flags() | Qt::ItemIsUserCheckable); + hitem->setCheckState(0, Qt::Checked); + + hitem->setText(0, hadd->hostname() ); + hitem->setText(1, QString::number(hadd->port()) ); + lst_hosts->addTopLevelItem(hitem); + } + delete hadd; +} + +void prefDevices::hostEdit() +{ + QTreeWidgetItem *hitem = lst_hosts->currentItem(); +#ifndef QT_NO_DEBUG + qDebug("hostEdit()"); +#endif + if (!hitem) return; + hostEditDialog *hedit = new hostEditDialog(hitem->text(0), hitem->text(1).toInt(), this); + hedit->setWindowTitle(tr("Edit host")); + + if (hedit->exec() && !hedit->hostname().isEmpty()) { + hitem->setText(0, hedit->hostname() ); + hitem->setText(1, QString::number(hedit->port()) ); + } + delete hedit; +} + +void prefDevices::hostRemove() +{ + QTreeWidgetItem *hitem = lst_hosts->currentItem(); +#ifndef QT_NO_DEBUG + qDebug("hostRemove()"); +#endif + if (!hitem) return; + if (QMessageBox::warning(this, + tr("Remove host?"), + tr("You are about to remove host from list:\n%1:%2\nAre you sure?").arg(hitem->text(0)).arg(hitem->text(1)), + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) == QMessageBox::Ok) + delete lst_hosts->takeTopLevelItem( lst_hosts->indexOfTopLevelItem(hitem)); +} + diff --git a/gui/src/pref_reports.cpp b/gui/src/pref_reports.cpp new file mode 100644 index 0000000..f2db928 --- /dev/null +++ b/gui/src/pref_reports.cpp @@ -0,0 +1,229 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2010 Gennady "ShultZ" Kozlov + * + * 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 +#include "pref_reports.h" +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +prefReports::prefReports(QPxSettings *iset, QWidget *p, Qt::WindowFlags fl) + : QWidget(p,fl) +{ + +#ifndef QT_NO_DEBUG + qDebug("STA: prefCommon()"); +#endif + set = iset; + + layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(3); + +// reports -> files + box_rep = new QGroupBox(tr("Autosave reports"),this); + box_rep->setCheckable(true); + layout->addWidget(box_rep); + + layout_rep = new QVBoxLayout(box_rep); + layout_rep->setMargin(3); + layout_rep->setSpacing(3); + + layout_rep_path = new QHBoxLayout(); + layout_rep_path->setMargin(0); + layout_rep_path->setSpacing(3); + layout_rep->addLayout(layout_rep_path); + + l_rep_path = new QLabel(tr("Path:"),box_rep); + e_rep_path = new QLineEdit(box_rep); + pb_rep_path = new QPushButton(QIcon(":images/directory.png"), "", box_rep); + pb_rep_path->setToolTip(tr("Select directory for reports saving...")); + + layout_rep_path->addWidget(l_rep_path); + layout_rep_path->addWidget(e_rep_path); + layout_rep_path->addWidget(pb_rep_path); + +// reports -> database + box_rep_db = new QGroupBox(tr("Use reports database"),this); + box_rep_db->setCheckable(true); + layout->addWidget(box_rep_db); + + layout_db = new QGridLayout(box_rep_db); + layout_db->setMargin(3); + layout_db->setSpacing(3); +// DB driver + ldb_driver = new QLabel( tr("Driver:"), this); + ldb_driver->setMinimumSize(40,22); + ldb_driver->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_db->addWidget(ldb_driver, 0, 0); + + db_driver = new QComboBox(this); + db_driver->setMinimumSize(150,22); + db_driver->addItems(QSqlDatabase::drivers()); + layout_db->addWidget(db_driver, 0, 1, 1, 2); + +// DB host + ldb_host = new QLabel( tr("Host:"), this); + ldb_host->setMinimumSize(40,22); + ldb_host->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_db->addWidget(ldb_host, 1, 0); + + db_host = new QLineEdit(this); + db_host->setMinimumSize(150,22); + layout_db->addWidget(db_host, 1, 1, 1, 2); + +// DB port + ldb_port = new QLabel( tr("Port:"), this); + ldb_port->setMinimumSize(40,22); + ldb_port->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_db->addWidget(ldb_port, 2, 0); + + db_port = new QSpinBox(this); + db_port->setMinimumSize(40,22); + db_port->setMinimum(1); + db_port->setMaximum(65535); + layout_db->addWidget(db_port, 2, 1); + +// DB name + ldb_name = new QLabel( tr("DB name:"), this); + ldb_name->setMinimumSize(40,22); + ldb_name->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_db->addWidget(ldb_name, 3, 0); + + db_name= new QLineEdit(this); + db_name->setMinimumSize(150,22); + layout_db->addWidget(db_name, 3, 1, 1, 2); + +// user + ldb_user = new QLabel( tr("User:"), this); + ldb_user->setMinimumSize(40,22); + ldb_user->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_db->addWidget(ldb_user, 4, 0); + + db_user = new QLineEdit(this); + db_user->setMinimumSize(150,22); + layout_db->addWidget(db_user, 4, 1, 1, 2); + +// passwd + ldb_pass = new QLabel( tr("Password:"), this); + ldb_pass->setMinimumSize(40,22); + ldb_pass->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_db->addWidget(ldb_pass, 5, 0); + + db_pass = new QLineEdit(this); + db_pass->setMinimumSize(150,22); + db_pass->setEchoMode(QLineEdit::Password); + layout_db->addWidget(db_pass, 5, 1, 1, 2); + +// DB connection check button + pb_db_check = new QPushButton(QIcon(":images/ok.png"), tr("Check"), this); + pb_db_check->setMinimumSize(40,22); + layout_db->addWidget(pb_db_check, 6, 2); + + ck_autosave_db = new QCheckBox(tr("Autosave reports into database"), box_rep); + layout_db->addWidget(ck_autosave_db, 7, 0, 1, 3); +// common + ck_eject = new QCheckBox(tr("Eject media after tests finished"), box_rep); + layout->addWidget(ck_eject); + + layout->addStretch(10); + +// creating objects connections... + connect(box_rep, SIGNAL(toggled(bool)), this, SLOT(box_rep_toggled(bool))); + connect(box_rep_db, SIGNAL(toggled(bool)), this, SLOT(box_rep_toggled(bool))); + connect(ck_autosave_db, SIGNAL(toggled(bool)), this, SLOT(box_rep_toggled(bool))); + + connect(pb_rep_path, SIGNAL(clicked()), this, SLOT(select_rep_path())); + connect(pb_db_check, SIGNAL(clicked()), this, SLOT(check_db_connection())); + +// applying settings... + + box_rep->setChecked(set->report_autosave); + e_rep_path->setText(set->report_path); + box_rep_db->setChecked(set->use_reports_db); + ck_autosave_db->setChecked(set->report_autosave_db); + + db_driver->setCurrentIndex( QSqlDatabase::drivers().indexOf(set->db.driver) ); + db_host->setText(set->db.host); + db_port->setValue(set->db.port); + db_name->setText(set->db.name); + db_user->setText(set->db.user); + db_pass->setText(set->db.pass); + + ck_eject->setChecked(set->actions_flags & AFLAG_EJECT_AFTER); +} + +prefReports::~prefReports() +{ + if (ck_eject->isChecked()) set->actions_flags |= AFLAG_EJECT_AFTER; else set->actions_flags &= ~AFLAG_EJECT_AFTER; + + set->report_path = e_rep_path->text(); + set->report_autosave = box_rep->isChecked(); + set->use_reports_db = box_rep_db->isChecked(); + set->report_autosave_db = set->use_reports_db && ck_autosave_db->isChecked(); +} + +void prefReports::select_rep_path() +{ +#ifndef QT_NO_DEBUG + qDebug() << __func__; +#endif + QString path = QFileDialog::getExistingDirectory(this, tr("Select directory..."), e_rep_path->text()); + if (path.isEmpty()) return; + e_rep_path->setText(path); +} + +void prefReports::box_rep_toggled(bool) +{ + ck_eject->setEnabled(box_rep->isChecked() || (box_rep_db->isChecked() && ck_autosave_db->isChecked())); + if(!ck_eject->isEnabled()) ck_eject->setChecked(false); +} + +void prefReports::check_db_connection() +{ +#ifndef QT_NO_DEBUG + qDebug() << __func__; +#endif + DBParams params; + + params.driver = db_driver->currentText(); + params.host = db_host->text(); + params.port = db_port->value(); + params.name = db_name->text(); + params.user = db_user->text(); + params.pass = db_pass->text(); + + if (SqlProbeConnection(params, "test connection")) { + set->db = params; + QMessageBox::information(this, + tr("Success!"), + tr("Database connection successfully\nestablished with given parameters")); + } +} + diff --git a/gui/src/preferences.cpp b/gui/src/preferences.cpp new file mode 100644 index 0000000..3e4c24a --- /dev/null +++ b/gui/src/preferences.cpp @@ -0,0 +1,183 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include "preferences.h" +#include + +QPxPreferences::QPxPreferences(QPxSettings *iset, QWidget *p, Qt::WindowFlags f) + : QDialog (p,f) +{ +#ifndef QT_NO_DEBUG + qDebug("QPxPreferences()"); +#endif + setWindowTitle("QPxTool - " + tr("Preferences")); + set_old = iset; + set = *iset; + if (set_old->geometry_pref.width() > 0 && set_old->geometry_pref.height() > 0) + setGeometry(set_old->geometry_pref); + + for (int i=0; igeometry_pref = geometry(); +} + +void QPxPreferences::winit() +{ + layout = new QHBoxLayout(); + //layout = new QHBoxLayout(); + setLayout(layout); + layout->setMargin(3); + + ilist = new ImagesList(80,this); + layout->addWidget(ilist); + + ilist->addLabel(tr("Common"), QImage(":images/settings.png")); + ilist->addLabel(tr("Devices"), QImage(":images/disc.png")); + ilist->addLabel(tr("Colors"), QImage(":images/colors.png")); + ilist->addLabel(tr("Reports"), QImage(":images/document.png")); + + QObject::connect(ilist, SIGNAL(selected(int)), this, SLOT(setPage(int))); + + parea = new QVBoxLayout(); + layout->addLayout(parea); +// parea->insertStretch(0,10); +// parea->insertStretch(1,10); + + hline0 = new QFrame(this); + hline0->setFrameStyle(QFrame::HLine | QFrame::Sunken); + parea->addWidget(hline0); + + layout_butt = new QHBoxLayout; + layout_butt->setMargin(0); + layout_butt->setSpacing(3); + parea->addLayout(layout_butt); + + layout_butt->addStretch(10); + + pb_save = new QPushButton( QIcon(":images/ok.png"), tr("Save"), this); + pb_cancel = new QPushButton( QIcon(":images/x.png"), tr("Cancel"), this); + pb_save->setMinimumWidth(100); + pb_cancel->setMinimumWidth(100); + + layout_butt->addWidget(pb_save,1); + layout_butt->addWidget(pb_cancel,1); + + connect( pb_save, SIGNAL(clicked()), this, SLOT(save()) ); + connect( pb_cancel, SIGNAL(clicked()), this, SLOT(cancel()) ); +// playout = new QVBoxLayout; +// playout->setMargin(0); +// layout->addLayout(playout); +} + +void QPxPreferences::setPage(int page) +{ +#ifndef QT_NO_DEBUG +// qDebug(QString("pageSelected: %1").arg(page)); +#endif + if (curPage == page) return; + if (curPage >=0 && pages[curPage]) { + + pages[curPage]->hide(); + // delete pages[curPage]; + // pages[curPage] = NULL; + } +/* + if (pages[curPage]) { + pages[curPage]->show(); + return; + } +*/ + switch(page) { + case 0: + setWindowTitle("QPxTool - " + tr("Preferences") + ": " + tr("Common")); + if (!pages[page]) pages[page] = new prefCommon(&set, this); + break; + case 1: + setWindowTitle("QPxTool - " + tr("Preferences") + ": " + tr("Devices")); + if (!pages[page]) pages[page] = new prefDevices(&set, this); + break; + case 2: + setWindowTitle("QPxTool - " + tr("Preferences") + ": " + tr("Colors")); + if (!pages[page]) pages[page] = new prefColors(&set, this); + break; + case 3: + setWindowTitle("QPxTool - " + tr("Preferences") + ": " + tr("Reports")); + if (!pages[page]) pages[page] = new prefReports(&set, this); + break; + default: + break; + } + parea->insertWidget(0,pages[page]); + pages[page]->show(); + curPage = page; +} + +void QPxPreferences::save() +{ +#ifndef QT_NO_DEBUG + qDebug("QPxPreferences::save()"); +#endif + close(); + *set_old = set; +} + +void QPxPreferences::cancel() +{ +#ifndef QT_NO_DEBUG + qDebug("QPxPreferences::cancel()"); +#endif + close(); +} + +void QPxPreferences::closeEvent(QCloseEvent* e) +{ +#ifndef QT_NO_DEBUG + qDebug("QPxPreferences::closeEvent()"); +#endif + for (int i=0; ikey() == Qt::Key_Escape) { + close(); + e->accept(); + } +} + diff --git a/gui/src/printpreview.cpp b/gui/src/printpreview.cpp new file mode 100644 index 0000000..e40798f --- /dev/null +++ b/gui/src/printpreview.cpp @@ -0,0 +1,318 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 "printpreview.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +//#define DEFAULT_THUMBS_SCALE 0.125 + +#ifndef QT_NO_DEBUG +//#define PP_DEBUG2 +//#define DEBUG_PAINT_TIME +#endif + +class PreviewView : public AbstractPreview +{ +public: + PreviewView(QWidget *p, QPrinter *printer, QTextDocument *idoc = NULL, + AbstractPreview::PreviewMode mode = AbstractPreview::Mode_Normal); + virtual ~PreviewView(); + void setDocument(QTextDocument *idoc); +protected: + virtual void paintPage(QPainter *p, int numberPage, const QRect&); + virtual void updatePageFormat(); +#ifdef PRINTER_CHANGE_DEVICE + virtual void deviceChanged(QPaintDevice* device); +#endif + virtual void clearEvent() {}; + +private: + QTextDocument *doc; +}; + +PreviewView::PreviewView(QWidget *p, QPrinter *printer, QTextDocument *idoc, AbstractPreview::PreviewMode mode) + : AbstractPreview(p, printer) +{ + setViewMode(mode); + setDocument(idoc); +} + +PreviewView::~PreviewView() {} + +void PreviewView::setDocument(QTextDocument *idoc) +{ +// flushCache(); + doc = idoc; + updatePageFormat(); + updatePreview(); +} + +#ifdef PRINTER_CHANGE_DEVICE +void PreviewView::deviceChanged(QPaintDevice* device) { + doc->documentLayout()->setPaintDevice(device); + doc->setPageSize( getPageSize() ); +}; +#endif + +void PreviewView::paintPage(QPainter *painter, int page, const QRect& rect) +{ +#ifdef PP_DEBUG2 + qDebug() << "STA: PreviewView::paintPage" << page << " rect: " << rect; +#endif +#ifdef DEBUG_PAINT_TIME + timeval tb,te; + gettimeofday(&tb, NULL); +#endif + const QSizeF pgSize = doc->pageSize(); + QColor col, bgc; + + bgc = palette().color((viewport()->backgroundRole())); + + painter->save(); + + QRectF docRect(QPointF(0, page * pgSize.height()) + rect.topLeft(), rect.size()); + QAbstractTextDocumentLayout::PaintContext ctx; + ctx.clip = docRect; + + ctx.palette.setColor(QPalette::Text, Qt::black); + + painter->translate( 0, -pgSize.height() * page); + painter->translate( -rect.topLeft()); + painter->setClipRect(docRect); + + painter->setRenderHint(QPainter::TextAntialiasing, true); + doc->documentLayout()->draw(painter, ctx); + + painter->restore(); + +#ifdef DEBUG_PAINT_TIME + gettimeofday(&te, NULL); + qDebug() << QString("Full page time: %1").arg(te.tv_sec - tb.tv_sec + (te.tv_usec - tb.tv_usec)/1000000.0,0,'f',4); +#endif +#ifdef PP_DEBUG2 + qDebug() << "END: PreviewView::paintPage()"; +#endif +} + +void PreviewView::updatePageFormat() +{ + clear(); + if (!doc) return; + + doc->setPageSize( getPageSize() ); + + addPages(doc->pageCount()); +#ifndef QT_NO_DEBUG + qDebug() << "pageCount: " << doc->pageCount(); +#endif +} + +PrintPreview::PrintPreview(QWidget *parent, QTextDocument *document) + : QDialog(parent), printer(QPrinter::HighResolution) +{ +#ifndef QT_NO_DEBUG + qDebug("STA: PrintPreview()"); +#endif +// printer.setOutputFormat(QPrinter::PdfFormat); + + QList ss; + ss.append(100); + ss.append(800); + + setWindowFlags(Qt::Window); + setWindowTitle(tr("Print Preview")); + + if (!document) { + doc = NULL; + } else { + doc = document; + } + + layout_main = new QVBoxLayout(this); + layout_main->setMargin(3); + layout_main->setSpacing(3); + setLayout(layout_main); + + layout_butt = new QHBoxLayout; + layout_butt->setMargin(0); + layout_butt->setSpacing(3); + layout_main->addLayout(layout_butt); + + split = new QSplitter(this); + layout_main->addWidget(split); + + QTextFrameFormat fmt = doc->rootFrame()->frameFormat(); + fmt.setLeftMargin(0); + fmt.setRightMargin(0); + fmt.setTopMargin(0); + fmt.setBottomMargin(0); + doc->rootFrame()->setFrameFormat(fmt); + + thumbs = new PreviewView(split, &printer, doc, AbstractPreview::Mode_Thumbs); + +#ifdef THUMBS_MULTICOLUMN + thumbs->setMinimumWidth( 100 ); + thumbs->setMaximumWidth( 300 ); +#else + thumbs->setMinimumWidth( 130 ); + thumbs->setMaximumWidth( 130 ); +#endif + + view = new PreviewView(split, &printer, doc, AbstractPreview::Mode_Normal); + view->setScaleRange(0.25,2.0); + + + pb_print = new QPushButton(this); + pb_print->setText(tr("Print")); + pb_print->setIcon(QIcon(":images/printer.png")); + layout_butt->addWidget(pb_print); + + pb_psetup = new QPushButton(this); + pb_psetup->setText(tr("Page Setup")); + pb_psetup->setIcon(QIcon(":images/page-setup.png")); + layout_butt->addWidget(pb_psetup); + + layout_butt->addStretch(10); + + l_scale = new QLabel(tr("Scale"),this); + layout_butt->addWidget(l_scale); + + box_scale = new QComboBox(this); + box_scale->setMaximumHeight(24); + box_scale->setMinimumHeight(24); + box_scale->addItem(tr("25%")); + box_scale->addItem(tr("50%")); + box_scale->addItem(tr("100%")); + box_scale->addItem(tr("200%")); +// box_scale->addItem(tr("400%")); + box_scale->setEditable(true); + layout_butt->addWidget(box_scale); + + pb_zoomin = new QPushButton(this); + pb_zoomin->setToolTip(tr("Zoom In")); + pb_zoomin->setIcon(QIcon(":images/zoom-in.png")); + layout_butt->addWidget(pb_zoomin); + + pb_zoomout = new QPushButton(this); + pb_zoomout->setToolTip(tr("Zoom Out")); + pb_zoomout->setIcon(QIcon(":images/zoom-out.png")); + layout_butt->addWidget(pb_zoomout); + + pb_zoom1 = new QPushButton(this); + pb_zoom1->setToolTip(tr("Original size")); + pb_zoom1->setIcon(QIcon(":images/zoom-orig.png")); + layout_butt->addWidget(pb_zoom1); + + resize(880, 650); + split->setSizes(ss); + + scaleChanged(1.0); + connect(thumbs, SIGNAL(pageSelected(int)), view, SLOT(gotoPage(int))); + connect(view, SIGNAL(scaleChanged(double)), this, SLOT(scaleChanged(double))); + connect(box_scale, SIGNAL(currentIndexChanged(QString)), this, SLOT(scaleChanged(QString))); + connect(pb_print, SIGNAL(clicked()), this, SLOT(print())); + connect(pb_psetup, SIGNAL(clicked()), this, SLOT(pageSetup())); + connect(pb_zoomin, SIGNAL(clicked()), view, SLOT(scaleIn())); + connect(pb_zoomout, SIGNAL(clicked()), view, SLOT(scaleOut())); + connect(pb_zoom1, SIGNAL(clicked()), view, SLOT(scaleOrig())); + + connect(view, SIGNAL(pageFormatChanged()), thumbs, SLOT(setupPageFormat())); + connect(view, SIGNAL(currentPage(int)), thumbs, SLOT(gotoPage(int))); + + view->scaleOrig(); + +#ifndef QT_NO_DEBUG + qDebug("END: PrintPreview()"); +#endif +} + +PrintPreview::~PrintPreview() +{ +#ifndef QT_NO_DEBUG + qDebug("~PrintPreview()"); +#endif +} + +void PrintPreview::setDocument(QTextDocument *document) +{ +#ifdef PP_DEBUG2 + qDebug() << "STA: PreviewView::setDocument(): document @" << document; +#endif + view->setDocument(NULL); + thumbs->setDocument(NULL); + + if (!document) return; + doc = document; + doc->documentLayout()->setPaintDevice(view->viewport()); + view->setDocument(doc); + thumbs->setDocument(doc); +} + +void PrintPreview::print() +{ + view->print(); +} + +void PrintPreview::printDoc(QWidget* parent, QPrinter* printer, QTextDocument* doc) +{ + PreviewView *view = new PreviewView(parent, printer, doc); + view->print(printer); + delete view; +} + +void PrintPreview::pageSetup() +{ + view->pageSetup(); +} + +void PrintPreview::scaleChanged(double scale) +{ + int idx = box_scale->findText(QString("%1%").arg(qRound((scale*100)))); + if (idx<0) { + box_scale->setEditText(QString("%1%").arg(qRound((scale*100)))); + } else { + box_scale->setCurrentIndex(idx); + } +} + +void PrintPreview::scaleChanged(QString scale) +{ +#ifndef QT_NO_DEBUG + qDebug("scaleChanged()"); +#endif + scale.remove('%'); + view->setScale(scale.toInt()/100.0); +} + diff --git a/gui/src/progresswidget.cpp b/gui/src/progresswidget.cpp new file mode 100644 index 0000000..11b9855 --- /dev/null +++ b/gui/src/progresswidget.cpp @@ -0,0 +1,136 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include + +#include "progresswidget.h" + +ProgressWidget::ProgressWidget(int iblocks, int ishown, QWidget *p) + :QWidget(p, Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint) +{ + QPoint offs = p->mapToGlobal( p->rect().topLeft() ); + blocks = iblocks; + if (ishown>=blocks) shown = blocks-1; + else shown = ishown; + + idx = 0; + dir = DirectionBoth; + dir2 = 0; + + col_frame = palette().color(QPalette::Text); + col_bg = palette().color(QPalette::Window); + col_text = palette().color(QPalette::Text); + col_rect = palette().color(QPalette::Highlight); + + timer.setSingleShot(false); + timer.setInterval(100); + + setGeometry(offs.x() + p->width()/2-150, offs.y() + p->height()/2-30, 300, 60); + setMinimumSize(300,60); + setMaximumSize(300,60); +} + +ProgressWidget::~ProgressWidget() +{ + timer.stop(); +} + +void ProgressWidget::setVisible(bool en) +{ + if (en) { + timer.start(); + connect(&timer, SIGNAL(timeout()), this, SLOT(step())); + } else { + timer.stop(); + disconnect(&timer, SIGNAL(timeout()), this, SLOT(step())); + } + QWidget::setVisible(en); +} + +void ProgressWidget::step() +{ + switch (dir) { + case DirectionForward: + idx++; + if ((idx+shown) > blocks) idx=0; + break; + case DirectionBackward: + idx--; + if (idx < 0) idx=blocks-shown; + break; + case DirectionBoth: + if (!dir2) { + if (idx+shown >= blocks) + dir2=1; + else + idx++; + } else { + if (idx<=0) + dir2=0; + else + idx--; + } + break; + } + update(); +} + +void ProgressWidget::setDirection(ProgressWidget::Direction idir) { dir = idir; } +void ProgressWidget::setText(QString t) { text = t; setWindowTitle(text); } + +void ProgressWidget::setColor(ProgressColor role, QColor color) +{ + switch (role) { + case BgColor: + col_bg = color; break; + case FrameColor: + col_frame = color; break; + case TextColor: + col_text = color; break; + case RectColor: + col_rect = color; break; + default: + return; + } +} + +void ProgressWidget::paintEvent(QPaintEvent*) +{ + QPainter p(this); + int blkw = (width() - 20) / blocks; + int offs = 10 + (int)(blkw * ( idx + 0.1 )); + + p.setPen(QPen(col_frame,1)); + p.drawRect(0,0,width()-1,height()-1); + + if (text.isEmpty()) { + for (int i=0; i> 1 , + (int)(blkw*0.8), (int)(blkw*0.8), + QBrush(col_rect)); + } else { + p.setPen(QPen(col_text,1)); + + QFont f = p.font(); + p.setFont(f); + + p.drawText(0,0, + width(), height()>>1, + Qt::AlignHCenter | Qt::AlignVCenter, text); + for (int i=0; i> 1, + (int)(blkw*0.8), (int)(blkw*0.8), + QBrush(col_rect)); + } +} + diff --git a/gui/src/qpxgraph.cpp b/gui/src/qpxgraph.cpp new file mode 100644 index 0000000..db523d4 --- /dev/null +++ b/gui/src/qpxgraph.cpp @@ -0,0 +1,1355 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include + +#include +#include "qpxgraph.h" + +#include + +#include + +#define SHOW_P95ERRC +//#define SHOW_P95JB + +#define MARGIN_DEFL 40 +#define MARGIN_DEFR 40 +#define MARGIN_DEFB 18 + +#define GRID_STYLE Qt::DotLine +//#define GRID_STYLE Qt::DashLine + +class IntList : public QList +{ +public: + IntList() : QList() {}; + + float M() { + float M = 0.0; + int s = size(); + if (!s) return 0.0; + for (int i=0; iscales.get(name[0])); + // settings->loadScale(name); + setScaleValue(256); + setContextMenuPolicy(Qt::DefaultContextMenu); + break; + case TEST_JB: + name[0] = "Jitter"; + name[1] = "Asymm"; + + scale[0] = new Scale(settings->scales.get( name[0] )); + scale[0]->type = Scale::Linear; + scale[1] = new Scale(settings->scales.get( name[1] )); + scale[1]->type = Scale::Linear; + setScaleValue(2,0); + setScaleValue(16,1); + setContextMenuPolicy(Qt::NoContextMenu); + break; + default: + setContextMenuPolicy(Qt::NoContextMenu); + break; + } + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +#ifndef QT_NO_DEBUG + qDebug("END: QPxGraph()"); +#endif +} + +QPxGraph::~QPxGraph() +{ +#ifndef QT_NO_DEBUG + qDebug("STA: ~QPxGraph()"); +#endif + for (int i=0; i<2; i++) if (scale[i]) delete scale[i]; +#ifdef CACHE_GRAPH + if (img) delete img; +#endif +#ifndef QT_NO_DEBUG + qDebug("END: ~QPxGraph()"); +#endif +} + +/* +void QPxGraph::setDataNames(QStringList dn) { dataNames = dn; update(); } +void QPxGraph::setZeroPos(float zp) { zeropos = zp; update(); } +float QPxGraph::zeroPos() { return zeropos; } +*/ + +void QPxGraph::setErrcList(int el, QString glabel) { errcList = el; label=glabel; } +void QPxGraph::setErrcShow(int err, bool disp) { if (disp) errcList |= err; else errcList &= ~err; } +void QPxGraph::setShowSpeed(bool s) { showspeed = s; } +void QPxGraph::setRightMarginHidden(bool hide) { margin_right = hide ? 0 : MARGIN_DEFR; } + +//void QPxGraph::forceUpdate() { forceAll = 1; } +void QPxGraph::resizeEvent(QResizeEvent*) +{ +#ifndef QT_NO_DEBUG +// qDebug("QPxGraph::resizeEvent()"); +#endif + forceAll = 1; +#ifdef CACHE_GRAPH + delete img; +#endif +} + +int QPxGraph::getLastX() +{ + return lastX + margin_left + 1; +} + +void QPxGraph::setZoneTA(int zone) { +#ifndef QT_NO_DEBUG + qDebug() << "Zone TA: " << zone; +#endif + taZone = zone; update(); +}; + +void QPxGraph::setLayerTA(int layer) { +#ifndef QT_NO_DEBUG + qDebug() << "LayerTA: " << layer ; +#endif + taLayer = layer; update(); +}; + +void QPxGraph::wheelEvent(QWheelEvent* e) +{ +#ifndef QT_NO_DEBUG +// qDebug(QString("QPxGraph::wheelEvent(): %1").arg(e->delta())); +#endif + if (e->modifiers() == Qt::NoModifier) { + if (!scale[0] || scale[0]->type != Scale::Linear) return; + if (e->delta() > 0) scaleIn(0); + else if (e->delta() < 0) scaleOut(0); + update(); + } else if (e->modifiers() == Qt::ControlModifier) { + if (test != TEST_JB || !scale[1] || scale[1]->type != Scale::Linear) return; + if (e->delta() > 0) scaleIn(1); + else if (e->delta() < 0) scaleOut(1); + update(); + } +} + +void QPxGraph::paintEvent(QPaintEvent* e) +{ +#ifdef CACHE_GRAPH + if (!e->rect().x() && !img) forceAll = 1; else forceAll = 0; +#else + if (!e->rect().x()) forceAll = 1; else forceAll = 0; +#endif +#ifdef CACHE_GRAPH + QPainter p(this); + if (!img) img = new QImage(size(), QImage::Format_RGB32); + img->fill(palette().color(QPalette::Window).rgb()); + QPainter ip(img); + if (!img) + drawGraph(&ip, size(),devices->current(),test, e->rect()); + else + drawGraph(&ip, size(),devices->current(),test, e->rect().adjusted(lastX,0,0,0)); + p.drawImage(0,0,*img); +#else + QPainter p(this); + drawGraph(&p, size(),devices->current(),test, e->rect()); +#endif +} + +void QPxGraph::drawGraph(QPainter *p, QSize s, device *dev, int ttype, const QRect& rect, int eflags, bool forceRepaint) +{ + int el = errcList; + bool ss = showspeed; + int taL=0, taZ=0; + QSize sg; + QRect grect = rect.intersected(geometry().adjusted(margin_left, 0, -margin_right, -margin_bottom)).translated(margin_left,0); + + forceAll |= forceRepaint; + + if (forceRepaint) + p->fillRect(rect, settings->col_bg); + + + if (eflags) { + if (ttype == TEST_TA) { + taL = taLayer; + taZ = taZone; + taLayer = (eflags & (0xF << 28)) >> 28; + taZone = (eflags & (0x0FFFFFF0)) >> 4; + } else { + errcList = eflags; + showspeed = 0; + } + } +#if 0 +#ifndef QT_NO_DEBUG + qDebug(QString("drawGraph: rect: %1.%2 - %3.%4") + .arg(rect.left()) + .arg(rect.top()) + .arg(rect.right()) + .arg(rect.bottom()) + ); + qDebug(QString("drawGraph: grect: %1.%2 - %3.%4") + .arg(grect.left()) + .arg(grect.top()) + .arg(grect.right()) + .arg(grect.bottom()) + ); +#endif +#endif + + p->save(); + if (ttype == TEST_TA) { + sg = QSize(s.width()-2, s.height()-margin_bottom-1); + if (forceAll) + p->fillRect(1, 1, + width()-2, height()-margin_bottom-2, + QBrush(settings->col_bg)); + } else { + sg = QSize(s.width() - (margin_left + margin_right +2), s.height()-margin_bottom-1); + if (forceAll || margin_left > rect.x()) + p->fillRect(margin_left+1, 1, + width() - (margin_left + margin_right +2), height()-margin_bottom-1, + QBrush(settings->col_bg)); + else + p->fillRect(rect.x(), 1, + width() - rect.x() - margin_left, height()-margin_bottom-1, + QBrush(settings->col_bg)); + + Vscale = (s.height() - margin_bottom) / 60.0; + if (dev->media.type.startsWith("CD")) { + HscaleLBA = 450000/sg.width(); + Vscale1X = Vscale; + } else if (dev->media.type.startsWith("DVD")) { + HscaleLBA = (1<<19) * 5 * dev->media.ilayers/sg.width(); + Vscale1X = Vscale * 3; + } else if (dev->media.type.startsWith("BD")) { + HscaleLBA = (1<<19) * 25 * dev->media.ilayers/sg.width(); + Vscale1X = Vscale * 4; + } + + p->translate(margin_left+1, 1); + } + + switch (ttype) { + case TEST_RT: + drawTransfer(p, sg, dev, grect, 0); + break; + case TEST_WT: + drawTransfer(p, sg, dev, grect, 1); + break; + case TEST_ERRC: + drawErrc(p, sg, dev, grect); + break; + case TEST_JB: + drawJB(p, sg, dev, grect); + break; + case TEST_FT: + drawFT(p, sg, dev, grect); + break; + case TEST_TA: + drawTA(p, sg, dev, grect); + break; + default: + break; + } + p->restore(); + if (ttype == TEST_TA) { + drawGridTA (p,s,dev, ttype); + } else { + drawGrid (p,s,dev,ttype); + } + if (eflags) { + if (ttype == TEST_TA) { + taLayer = taL; + taZone = taZ; + } else { + errcList = el; + showspeed = ss; + } + } +} + +void QPxGraph::drawTransfer(QPainter* p, const QSize& s, device *dev, const QRect& rect, bool rw) +{ + Q_UNUSED(rect); + + IntList xerr; + QPainterPath pp; + int x,xo=0; + int startidx=0; + int i; + uint64_t lastLBA=0; + bool first; +#ifndef QT_NO_DEBUG + qDebug("QPxGraph::drawTransfer()"); +#endif + if (!dev->testData.rt.size() && !dev->testData.wt.size()) return; +// qDebug(QString("hscale: %1, capX: %2").arg(hscale).arg(hscale * dev->media.cread)); +// qDebug(QString("hscale: %1, b: %2").arg(hscale).arg(b)); +// qDebug(QString("hscale: %1").arg(hscale)); +// p->scale(1, hscale); + + p->setRenderHint(QPainter::Antialiasing, true); + + if (!rw && dev->testData.rt.size()) { + startidx=0; + if (!forceAll) { + lastLBA = (uint64_t)(lastX * HscaleLBA); + for (i=dev->testData.rt.size()-1; i>=0 && dev->testData.rt[i].lba > lastLBA ; ) i--; + if (i>0) startidx = i; + } + xo=0; + xerr.clear(); + first = 1; + for (i=startidx; itestData.rt.size(); i++) { + x = (int) (dev->testData.rt[i].lba/HscaleLBA); + xerr.append((int)(dev->testData.rt[i].spdx * 100)); + if (x!=xo || i==(dev->testData.rt.size()-1)) { + if (first) { + pp.moveTo(xo, s.height() - xerr.M()*Vscale1X/100.0); + first=0; + } else { + pp.lineTo(xo, s.height() - xerr.M()*Vscale1X/100.0); + } + xerr.clear(); + xo = x; + } + } + p->setPen(QPen(settings->col_rspeed, 2)); + p->drawPath(pp); + + lastX = (uint64_t)(dev->testData.rt.last().lba / HscaleLBA); + if (lastX) lastX--; + } + + if (rw && dev->testData.wt.size()) { + startidx=0; + if (!forceAll) { + lastLBA = (uint64_t)(lastX * HscaleLBA); + for (i=dev->testData.wt.size()-1; i>=0 && dev->testData.wt[i].lba > lastLBA ; ) i--; + if (i>0) startidx = i; + } + + xo=0; + xerr.clear(); + first = 1; + for (i=startidx; itestData.wt.size(); i++) { + x = (int) (dev->testData.wt[i].lba/HscaleLBA); + xerr.append((int)(dev->testData.wt[i].spdx * 100)); + if (x!=xo || i==(dev->testData.wt.size()-1)) { + if (first) { + pp.moveTo(xo, s.height() - xerr.M()*Vscale1X/100.0); + first=0; + } else { + pp.lineTo(xo, s.height() - xerr.M()*Vscale1X/100.0); + } + xerr.clear(); + xo = x; + } + } + p->setPen(QPen(settings->col_wspeed, 2)); + p->drawPath(pp); + + lastX = (uint64_t)(dev->testData.wt.last().lba / HscaleLBA); + if (lastX) lastX--; + } +} + +void QPxGraph::drawErrc(QPainter* p, const QSize& s, device *dev, const QRect&) +{ + QPainterPath pps; + IntList xerr; +// bool isCD; + int x, xo; + int errc; + int errc_dfl = 0; +#ifdef SHOW_P95ERRC + int M,D; +// float M,D; +#endif + int min=-1,max=-1; + int i; + int startidx=0; + uint64_t lastLBA=0; + bool first; + + if (dev->media.type.startsWith("CD")) { + errc_dfl = GRAPH_DFL_CD; + } else if (dev->media.type.startsWith("DVD")) { + errc_dfl = GRAPH_DFL_DVD; + } else if (dev->media.type.startsWith("BD")) { + errc_dfl = GRAPH_DFL_BD; + } + errc = errcList ? errcList : errc_dfl; + +#ifndef QT_NO_DEBUG + timeval b,e; + double t0,t1; +// qDebug(QString("QPxGraph::drawErrc() : %1").arg(errc,8,2)); + gettimeofday(&b,NULL); +#endif + if (!dev->testData.errc.size()) return; + +#ifndef QT_NO_DEBUG + qDebug() << QString("lastBLA: %1, lastX: %2").arg(lastLBA).arg(lastX); +#endif + if (forceAll) { + startidx=0; + } else { + lastLBA = (int)(lastX * HscaleLBA); + for (i=dev->testData.errc.size()-1; i>=0 && dev->testData.errc[i].raw.lba > lastLBA ; ) i--; + if (i>0) startidx = i; +#ifndef QT_NO_DEBUG + qDebug() << QString("lastBLA: %1, startidx: %2, errc.size: %3") + .arg(lastLBA) + .arg(startidx) + .arg(dev->testData.errc.size()); +#endif + } + + p->setRenderHint(QPainter::Antialiasing, false); + xo = 0; + xerr.clear(); + for (int e = 0; e<8; e++) { + if ( (1<setPen(QPen(*settings->col_errc.raw[e], 1)); + for (i=startidx; itestData.errc.size(); i++) { +// if (i) i--; + // if ( (1<testData.errc.size(); i++) { + x = (int) (dev->testData.errc[i].raw.lba/HscaleLBA); + if (dev->testData.errc[i].raw.err[e] >=0 ) { +#ifdef SHOW_P95ERRC + xerr.append(dev->testData.errc[i].raw.err[e]); +#endif +// update min/max + if (min < 0 || min > dev->testData.errc[i].raw.err[e]) + min = dev->testData.errc[i].raw.err[e]; + if (max < dev->testData.errc[i].raw.err[e]) + max = dev->testData.errc[i].raw.err[e]; + } + + if (x!=xo || i==(dev->testData.errc.size()-1)) { +// min-max + p->setPen(QPen(*settings->col_errc.raw[e], 1)); + p->drawLine(xo, errc2h(s.height(), min), + xo, errc2h(s.height(), max)); + +// P=0.95 +#ifdef SHOW_P95ERRC + M = (int)xerr.M(); + D = (int)sqrt(xerr.dispers(M)); + p->setPen(QPen(settings->col_errc.raw[e]->darker(), 1)); + p->drawLine(xo, errc2h(s.height(), M-D), + xo, errc2h(s.height(), M+D)); + xerr.clear(); +#endif + min = -1; + max = -1; + xo = x; + } + } + } + } + +#ifndef QT_NO_DEBUG + gettimeofday(&e,NULL); + t0 = (e.tv_sec - b.tv_sec) + (e.tv_usec - b.tv_usec)/1000000.0; +#endif + + if (showspeed) + { +// draw speed +#ifndef QT_NO_DEBUG + gettimeofday(&b,NULL); +#endif + p->setRenderHint(QPainter::Antialiasing, true); + xo=0; + xerr.clear(); + first=1; + for (i=startidx; itestData.errc.size(); i++) { + x = (int) (dev->testData.errc[i].raw.lba/HscaleLBA); + + xerr.append( (int) (dev->testData.errc[i].raw.spdx * 100) ); + if (x!=xo || i==(dev->testData.errc.size()-1)) { + if (first) { + pps.moveTo(xo, s.height() - xerr.M()*Vscale1X/100.0); + first=0; + } else { + pps.lineTo(xo, s.height() - xerr.M()*Vscale1X/100.0); + } + xerr.clear(); + xo = x; + } + } + p->setPen(QPen(settings->col_bginv, 2)); + p->drawPath(pps); + +#ifndef QT_NO_DEBUG + gettimeofday(&e,NULL); + t1 = (e.tv_sec - b.tv_sec) + (e.tv_usec - b.tv_usec)/1000000.0; + qDebug() << QString("draw time(sec): %1 ERRC, %2 Speed") + .arg(t0,0,'f',6) + .arg(t1,0,'f',6); +#endif + } + + lastX = (uint64_t)(dev->testData.errc.last().raw.lba / HscaleLBA); + if (lastX) lastX--; +} + +void QPxGraph::drawJB(QPainter* p, const QSize& s, device *dev, const QRect&) +{ +#ifndef QT_NO_DEBUG + qDebug("QPxGraph::drawJB()"); +#endif + QPainterPath pps; + IntList xj, xb; + int x, xo; +#ifdef SHOW_P95JB + int M,D; +// float M,D; +#endif + float jmin=0, jmax=0; + float bmin=0, bmax=0; + int i; + int startidx=0; + uint64_t lastLBA=0; + bool first=1; + +#ifndef QT_NO_DEBUG + timeval b,e; + double t0,t1; +// qDebug(QString("QPxGraph::drawErrc() : %1").arg(errc,8,2)); + gettimeofday(&b,NULL); +#endif + if (!dev->testData.jb.size()) return; + +#ifndef QT_NO_DEBUG + qDebug() << QString("lastBLA: %1, lastX: %2").arg(lastLBA).arg(lastX); +#endif + if (forceAll) { + startidx=0; + } else { + lastLBA = (uint64_t)(lastX * HscaleLBA); + for (i=dev->testData.jb.size()-1; i>=0 && dev->testData.jb[i].lba > lastLBA ; ) i--; + if (i>0) startidx = i; +#ifndef QT_NO_DEBUG + qDebug() << QString("lastBLA: %1, startidx: %2, jb.size: %3") + .arg(lastLBA) + .arg(startidx) + .arg(dev->testData.jb.size()); +#endif + } + + p->setRenderHint(QPainter::Antialiasing, false); + xo = 0; + xj.clear(); + xb.clear(); + +// p->setPen(QPen(*settings->col_errc.raw[e], 1)); + for (i=startidx; itestData.jb.size(); i++) { + // if ( (1<testData.errc.size(); i++) { + x = (int) (dev->testData.jb[i].lba/HscaleLBA); + if (dev->testData.jb[i].jitter >=-100 && dev->testData.jb[i].asymm >=-100 ) { +#ifdef SHOW_P95JB + xj.append(dev->testData.jb[i].jitter * 100); + xj.append(dev->testData.jb[i].jitter * 100); + xb.append(dev->testData.jb[i].asymm * 100); + xb.append(dev->testData.jb[i].asymm * 100); +#endif +// update min/max + if (first) { + jmin = dev->testData.jb[i].jitter; + jmax = dev->testData.jb[i].jitter; + bmin = dev->testData.jb[i].asymm; + bmax = dev->testData.jb[i].asymm; + first=0; + } else { + if (jmin > dev->testData.jb[i].jitter) + jmin = dev->testData.jb[i].jitter; + if (jmax < dev->testData.jb[i].jitter) + jmax = dev->testData.jb[i].jitter; + if (bmin > dev->testData.jb[i].asymm) + bmin = dev->testData.jb[i].asymm; + if (bmax < dev->testData.jb[i].asymm) + bmax = dev->testData.jb[i].asymm; + } + } + + + if (x!=xo || i==(dev->testData.jb.size()-1)) { +#ifndef QT_NO_DEBUG + qDebug() << QString("i=%1 J: %2..%3 A: %4..%5") + .arg(i) + .arg(jmin) + .arg(jmax) + .arg(bmin) + .arg(bmin); +#endif +// ******* Jitter +// min-max + p->setPen(QPen(settings->col_jitter, 1)); + p->drawLine(xo, jitter2h(s.height(), jmin), + xo, jitter2h(s.height(), jmax)); +// P=0.95 +#ifdef SHOW_P95JB + M = xj.M(); + D = sqrt(xj.dispers(M)); + p->setPen(QPen(settings->col_jitter.darker(), 1)); + p->drawLine(xo, jitter2h(s.height(), (M-D)/100.0), + xo, jitter2h(s.height(), (M+D)/100.0)); + xj.clear(); +#endif + + +// ******* Asymmmethry +// min-max + p->setPen(QPen(settings->col_asymm, 1)); + p->drawLine(xo, asymm2h(s.height(), bmin), + xo, asymm2h(s.height(), bmax)); +// P=0.95 +#ifdef SHOW_P95JB + M = xb.M(); + D = sqrt(xb.dispers(M)); + p->setPen(QPen(settings->col_asymm.darker(), 1)); + p->drawLine(xo, asymm2h(s.height(), (M-D)/100.0), + xo, asymm2h(s.height(), (M+D)/100.0)); + xb.clear(); +#endif + xo = x; + + first = 1; + } + } + +#ifndef QT_NO_DEBUG + gettimeofday(&e,NULL); + t0 = (e.tv_sec - b.tv_sec) + (e.tv_usec - b.tv_usec)/1000000.0; + gettimeofday(&b,NULL); +#endif + +// draw speed + p->setRenderHint(QPainter::Antialiasing, true); + xo=0; + xj.clear(); + first=1; + for (i=startidx; itestData.jb.size(); i++) { + x = (int) (dev->testData.jb[i].lba/HscaleLBA); + + xj.append((int)(dev->testData.jb[i].spdx * 100)); + if (x!=xo || i==(dev->testData.jb.size()-1)) { + if (first) { + pps.moveTo(xo, s.height() - xj.M()*Vscale1X/100.0); + first=0; + } else { + pps.lineTo(xo, s.height() - xj.M()*Vscale1X/100.0); + } + xj.clear(); + xo = x; + } + } + p->setPen(QPen(settings->col_bginv, 2)); + p->drawPath(pps); + +#ifndef QT_NO_DEBUG + gettimeofday(&e,NULL); + t1 = (e.tv_sec - b.tv_sec) + (e.tv_usec - b.tv_usec)/1000000.0; + qDebug() << QString("draw time(sec): %1 ERRC, %2 Speed") + .arg(t0,0,'f',6) + .arg(t1,0,'f',6); +#endif + lastX = (uint64_t)(dev->testData.jb.last().lba / HscaleLBA); + if (lastX) lastX--; +} + +void QPxGraph::drawFT(QPainter* p, const QSize& s, device *dev, const QRect&) +{ + QPainterPath ppf,ppt, pps; + int x; +#ifndef QT_NO_DEBUG + qDebug("QPxGraph::drawFT()"); +#endif +/* + p->setPen(QPen(settings->col_wspeed, 2)); + p->drawLine(360000/HscaleLBA, 1, 360000/HscaleLBA, s.height()); +*/ + if (!dev->testData.ft.size()) return; + + pps.moveTo(0, s.height() - dev->testData.ft[0].spdx*Vscale1X); + ppf.moveTo(0, s.height() - dev->testData.ft[0].fe*Vscale); + ppt.moveTo(0, s.height() - dev->testData.ft[0].te*Vscale); + + for (int i=1; itestData.ft.size(); i++) { + x = (int)(dev->testData.ft[i].lba/HscaleLBA); + pps.lineTo( x, s.height() - dev->testData.ft[i].spdx*Vscale1X); + ppf.lineTo( x, s.height() - dev->testData.ft[i].fe*Vscale); + ppt.lineTo( x, s.height() - dev->testData.ft[i].te*Vscale); +// qDebug(QString("p: %1:%2").arg(x).arg(y)); + } + + p->setRenderHint(QPainter::Antialiasing, true); + + p->setPen(QPen(settings->col_bginv, 2)); + p->drawPath(pps); + + p->setPen(QPen(settings->col_fe, 2)); + p->drawPath(ppf); + + p->setPen(QPen(settings->col_te, 2)); + p->drawPath(ppt); +} + +void QPxGraph::drawTA(QPainter* p, const QSize& s, device *dev, const QRect&) +{ + int taIdx = taLayer*3 + taZone; + int h; + int x, prevh=0; + QPainterPath pp; +#ifndef QT_NO_DEBUG + qDebug("QPxGraph::drawTA()"); +#endif + if ( taIdx < 0 || taIdx > 5 ) return; + + Hscale = s.width() / 13.0; + + p->setPen(QPen( taMode ? settings->col_taland : settings->col_tapit, 1)); + for (int i=0; itestData.ta[taIdx].size(); i++) { + h = ta2h(s.height(), taMode ? dev->testData.ta[taIdx][i].land : dev->testData.ta[taIdx][i].pit); +#if 0 + if (h>0) { + x = (int)(((dev->testData.ta[taIdx][i].idx - 64) / 21.5454 + 1) * Hscale); + p->drawLine( x, s.height(), x, h ); + } +#else + x = (int)(((dev->testData.ta[taIdx][i].idx - 64) / 21.5454 + 1) * Hscale); + if (hdrawPath(pp); +} + +#define VGRIDS 10.0 + +void QPxGraph::drawGrid(QPainter* p, const QSize& s, device *dev, int ttype) +{ +#ifndef QT_NO_DEBUG + qDebug("STA: QPxGraph::drawGrid()"); + + timeval b,e; + gettimeofday(&b,NULL); + double t; +#endif +// int idxn, idxd; +// int scaleType; + float HscaleX; + bool isCD = 0; + int spdMax; + int GBperLayer; +// TestData* data; + QFont dfont = p->font(); + QFont lfont = dfont; + + lfont.setPointSizeF(lfont.pointSizeF()*1.3); +// lfont.setBold(true); +// lfont.setItalic(true); + + if (ttype & TEST_TA) return; + + p->setRenderHint(QPainter::Antialiasing, false); + p->setRenderHint(QPainter::TextAntialiasing, true); + +// graph border + p->setPen(QPen(palette().color(QPalette::Dark), 1)); + //p->setPen(QPen(palette().color(QPalette::Highlight), 1)); + p->drawRect(margin_left, 0, s.width()- (margin_left + margin_right) - 1, s.height()-margin_bottom); + if (dev->media.type == "-") return; +/* + p->drawLine(margin_L-1,s.height()-margin_bottom, + s.width(),s.height()-margin_bottom); + p->drawLine(margin_L-1,s.height()-margin_bottom, + margin_L-1,0); +*/ + if (dev->media.type.startsWith("CD")) { + isCD = 1; + } else if (dev->media.type.startsWith("DVD")) { + isCD = 0; + GBperLayer = 5; + } else if (dev->media.type.startsWith("BD")) { + isCD = 0; + GBperLayer = 25; + } + + if (isCD) { + Hscale = (s.width()-( margin_left + margin_right )-2) / 1.0 / 100/4500; + spdMax = 60; + } else { + Hscale = (s.width()-( margin_left + margin_right )-2) / 1.0 / GBperLayer / (1<<19) / dev->media.ilayers; + spdMax = 20; + } + + + HscaleX = (s.width() - (margin_left + margin_right )) / VGRIDS; + +// vertical grid lines + p->setPen( QPen(settings->col_grid, 1, GRID_STYLE) ); + for (int i=1; idrawLine(margin_left + (int)(i*HscaleX), s.height()-margin_bottom-1, + margin_left + (int)(i*HscaleX), 1); + + p->setPen( QPen(settings->col_bginv, 1, GRID_STYLE) ); +// capacity + if (dev->media.creads) { + p->drawLine(margin_left + (int)(Hscale*dev->media.creads) + 2, s.height()-margin_bottom-1, + margin_left + (int)(Hscale*dev->media.creads) + 2, 1); + } + p->drawLine(margin_left + (int)(Hscale*dev->media.ctots) + 2, s.height()-margin_bottom-1, + margin_left + (int)(Hscale*dev->media.ctots) + 2, 1); + +// bottom text labels + p->setPen(QPen(palette().color(QPalette::Text), 1)); + if (isCD) { + for (int i=1; idrawText( margin_left + (int)(HscaleX*(i-1)), + s.height()-margin_bottom+2, + (int)(HscaleX*2), + margin_bottom-2, + Qt::AlignVCenter | Qt::AlignHCenter, + QString::number(i*(100/VGRIDS))+" min"); + } + } else { + for (int i=1; idrawText( margin_left + (int)(HscaleX*(i-1)), + s.height()-margin_bottom+2, + (int)(HscaleX*2), + margin_bottom-2, + Qt::AlignVCenter | Qt::AlignHCenter, + QString::number(i*( dev->media.ilayers*GBperLayer/VGRIDS))+" GB"); + } + } + + p->setPen( QPen(settings->col_grid, 1, GRID_STYLE) ); +// horizontal grid lines and left labels + int h; + switch (ttype) { + case TEST_RT: + case TEST_WT: + for (int i=(isCD ? 4:2); idrawLine(margin_left+1, h, + s.width()-margin_right-2, h); + if (!(i & (isCD ? 7 : 1))) { + p->setPen(QPen(palette().color(QPalette::Text), 1)); + p->drawText( 2, h - 10, margin_left-4, 20, + Qt::AlignVCenter | Qt::AlignRight, + QString::number(i)); + p->setPen(QPen(settings->col_grid, 1, GRID_STYLE)); + } + } + break; + case TEST_ERRC: + if (scale[0]->type == Scale::Log) { + // LOG10: hlines with text labels + int *errc_logh = errc_logh_hres; + for (int i=0; errc_logh[i]>0; i++) { + h = errc2h(s.height()-margin_bottom,errc_logh[i]); + p->drawLine(margin_left+1, h, + s.width()-margin_right-2, h); + } + + if (s.height() < 250) errc_logh = errc_logh_lres; + + p->setPen(QPen(palette().color(QPalette::Text), 1)); + for (int i=0; errc_logh[i]>0; i++) { + h = errc2h(s.height()-margin_bottom,errc_logh[i]); + p->drawText( 2, h - 10, margin_left-4, 20, + Qt::AlignVCenter | Qt::AlignRight, + QString::number(errc_logh[i])); + } + } else { + int steps = (int)((float)(s.height() - margin_bottom) / 20); + if (!steps) steps=1; + int step = (scale[0]->value / steps + 9) / 10 * 10; +#ifndef QT_NO_DEBUG +// qDebug(QString("scale step: %1").arg(step)); +#endif + h = errc2h(s.height()-margin_bottom,step); + for (int i=step; h>10; i+=step) { + // LINEAR: hlines with text labels + h = errc2h(s.height()-margin_bottom,i); + p->drawLine(margin_left+1, h, + s.width()-margin_right-2, h); + p->setPen(QPen(palette().color(QPalette::Text), 1)); + if (h-10 >= 0) { + p->drawText( 2, h - 10, margin_left-4, 20, + Qt::AlignVCenter | Qt::AlignRight, + QString::number(i)); + p->setPen(QPen(settings->col_grid, 1, GRID_STYLE)); + } + } + } + if (!label.isEmpty()) { + p->setFont(lfont); + p->setPen(QPen( settings->col_bginv, 1)); + p->drawText(margin_left + 5,4,60,20, + Qt::AlignTop | Qt::AlignLeft, + label); + p->setFont(dfont); + } + // error limit hlines + p->setPen(QPen(Qt::red, 1, GRID_STYLE)); + if (isCD) { + h = errc2h(s.height() - margin_bottom, 220); + p->drawLine(margin_left+1, h, + s.width()-margin_right-2, h); + } else { + h = errc2h(s.height() - margin_bottom, 4); + p->drawLine(margin_left+1, h, + s.width()-margin_right-2, h); + h = errc2h(s.height() - margin_bottom, 280); + p->drawLine(margin_left+1, h, + s.width()-margin_right-2, h); + } + break; + case TEST_JB: + { + float dj = scale[0]->value/8.0; + float da = scale[1]->value/8.0; + float vj=0, va=-da*4; + // Jitter/asymm hlines & text labels + for ( int i=0; i<8; i++) { + h = jitter2h(s.height() - margin_bottom, vj); + p->drawLine(margin_left+1, h, + s.width()-margin_right-2, h); + + p->setPen(QPen(palette().color(QPalette::Text), 1)); + p->drawText( 2, h - 10, margin_left-4, 20, + Qt::AlignVCenter | Qt::AlignRight, + QString("%1/%2").arg(vj).arg(va)); + p->setPen(QPen(settings->col_grid, 1, GRID_STYLE)); + vj+=dj; + va+=da; + } + } + break; + case TEST_FT: + for (int i=4; i<60; i+=4) { + h = (int) (s.height() - margin_bottom - i*Vscale); + p->drawLine(margin_left+1, h, + s.width()-margin_right-2, h); + if (!(i & 7)) { + p->setPen(QPen(palette().color(QPalette::Text), 1)); + p->drawText( 2, h - 10, margin_left-4, 20, + Qt::AlignVCenter | Qt::AlignRight, + QString::number(i)); + p->setPen(QPen(settings->col_grid, 1, GRID_STYLE)); + } + } + break; + } + +// right speed labels for all graphs, except TA +// qDebug() << "Draw speed labels... showspeed:" << showspeed << " margin_right: " << margin_right; + if (showspeed && margin_right) { + + for (int i=(isCD ? 4:2); isetPen(QPen(palette().color(QPalette::Text), 1)); + h = (int)(s.height() - margin_bottom - i*Vscale1X); + if (!(i & (isCD ? 7 : 1))) { + p->drawText( s.width() - margin_right + 2, h - 10, margin_right-4, 20, + Qt::AlignVCenter | Qt::AlignLeft, + QString::number(i)); + } + } + } + +#ifndef QT_NO_DEBUG + gettimeofday(&e,NULL); + t = (e.tv_sec - b.tv_sec) + (e.tv_usec - b.tv_usec)/1000000.0; + qDebug() << QString("END: QPxGraph::drawGrid(): %1 sec").arg(t,0,'f',6); +#endif +} + +void QPxGraph::drawGridTA(QPainter* p, const QSize& s, device *dev, int ttype) +{ +// int idxn, idxd; +// int scaleType; + float Hscale; + int TAgrids; +// TestData* data; + +#ifndef QT_NO_DEBUG + qDebug("STA: QPxGraph::drawGridTA()"); +#endif +// draw background and border + if (ttype != TEST_TA) return; + + p->setRenderHint(QPainter::Antialiasing, false); + p->setRenderHint(QPainter::TextAntialiasing, true); + + p->setPen(QPen(palette().color(QPalette::Dark), 1)); +// p->setPen(QPen(settings->col_grid, 1)); + p->drawRect(0,0,s.width()-1, s.height()-margin_bottom); + + if (dev->media.type == "-") return; +// p->drawLine(0,s.height()-margin_bottom,s.width(),s.height()-margin_bottom); + + + TAgrids=14; +/* + if (dev->media.type.startsWith("CD")) { + TAgrids=11; + } else { + TAgrids=14; + } +*/ + +// Hscale = width() / ((float)TAgrids-1); + Hscale = s.width() / 13.0; + + p->setPen(QPen(palette().color(QPalette::Dark), 1, GRID_STYLE)); +// vertical grid lines + for (int i=3; i<=11; i++) + p->drawLine((int)((i-2)*Hscale),s.height()-margin_bottom, + (int)((i-2)*Hscale),0); + + if (dev->media.type.startsWith("DVD")) { + p->drawLine((int)(12*Hscale),s.height()-margin_bottom, + (int)(12*Hscale),0); + } +// bottom text labels + p->setPen(QPen(palette().color(QPalette::Text), 1)); + for (int i=3; i<=11; i++) { + p->drawText((int)(Hscale*(i-2.5)), + s.height()-margin_bottom+2, + (int)(Hscale), + margin_bottom-2, + Qt::AlignVCenter | Qt::AlignHCenter, + QString("T%1").arg(i)); + } + if (dev->media.type.startsWith("DVD")) { + p->drawText((int)(Hscale*(11.5)), + s.height()-margin_bottom+2, + (int)(Hscale), + margin_bottom-2, + Qt::AlignVCenter | Qt::AlignHCenter, + "T14"); + } + +// p->setPen(QPen( settings->col_bginv, 1)); + if (!taMode) { + p->setPen(QPen( settings->col_tapit, 1)); + p->drawText(10,10,200,30, + Qt::AlignTop | Qt::AlignLeft, + QString("Pit: Layer%1, Zone %2").arg(taLayer).arg(taZone)); + } else { + p->setPen(QPen( settings->col_taland, 1)); + p->drawText(10,10,200,30, + Qt::AlignTop | Qt::AlignLeft, + QString("Land: Layer%1, Zone %2").arg(taLayer).arg(taZone)); + } +#ifndef QT_NO_DEBUG + qDebug("END: QPxGraph::drawGridTA()"); +#endif +} + +void QPxGraph::contextMenuEvent(QContextMenuEvent *e) +{ + QMenu *cmenu; + QAction *act; + QIcon icon_ok(":images/ok.png"); + if (!scale) return; + + cmenu = new QMenu(this); + cmenu->addAction( + (scale[0]->type == Scale::Log) ? icon_ok : QIcon(), + tr("Logarithmic scale"), + this, SLOT(setScaleTypeLog())); + cmenu->addAction( + (scale[0]->type == Scale::Linear) ? icon_ok : QIcon(), + tr("Linear Scale"), + this, SLOT(setScaleTypeLin())); + cmenu->addSeparator(); + +/* + cmenu->addAction( + (settings->scales.get(name).policy == Scale::Auto) ? QIcon(":images/ok.png") : QIcon(), + tr("Auto scale"), + this, SLOT(setScalePolicyAuto())); + cmenu->addAction( + (settings->scales.get(name).policy == Scale::Fixed) ? QIcon(":images/ok.png") : QIcon(), + tr("Fixed scale"), + this, SLOT(setScalePolicyFixed())); + cmenu->addSeparator(); +*/ + act = cmenu->addAction( QIcon(":images/plus.png"), tr("Scale in"), this, SLOT(scaleIn())); + act->setEnabled(scale[0]->type == Scale::Linear); + act = cmenu->addAction( QIcon(":images/minus.png"), tr("Scale out"), this, SLOT(scaleOut())); + act->setEnabled(scale[0]->type == Scale::Linear); + +#if 0 + if (test == TEST_ERRC) { + qDebug() << "Errc List: " << errcList; + if (devices->current()->media.type.startsWith("CD-")) { + cmenu->addSeparator(); + cmenu->addAction( (errcList & GRAPH_BLER) ? icon_ok : QIcon(), "BLER"); + cmenu->addAction( (errcList & GRAPH_E11) ? icon_ok : QIcon(), "E11"); + cmenu->addAction( (errcList & GRAPH_E21) ? icon_ok : QIcon(), "E21"); + cmenu->addAction( (errcList & GRAPH_E31) ? icon_ok : QIcon(), "E31"); + cmenu->addAction( (errcList & GRAPH_E12) ? icon_ok : QIcon(), "E12"); + cmenu->addAction( (errcList & GRAPH_E22) ? icon_ok : QIcon(), "E22"); + cmenu->addAction( (errcList & GRAPH_E32) ? icon_ok : QIcon(), "E32"); + cmenu->addAction( (errcList & GRAPH_UNCR) ? icon_ok : QIcon(), "UNCR"); + } else if (devices->current()->media.type.startsWith("DVD-")) { + cmenu->addSeparator(); + cmenu->addAction( (errcList & GRAPH_PIE) ? icon_ok : QIcon(), "PIE"); + cmenu->addAction( (errcList & GRAPH_PI8) ? icon_ok : QIcon(), "PI8"); + cmenu->addAction( (errcList & GRAPH_PIF) ? icon_ok : QIcon(), "PIF"); + cmenu->addAction( (errcList & GRAPH_POE) ? icon_ok : QIcon(), "POE"); + cmenu->addAction( (errcList & GRAPH_PO8) ? icon_ok : QIcon(), "PO8"); + cmenu->addAction( (errcList & GRAPH_POF) ? icon_ok : QIcon(), "POF"); + cmenu->addAction( (errcList & GRAPH_UNCR) ? icon_ok : QIcon(), "UNCR"); + } + } +#endif + cmenu->exec( e->globalPos()); + delete cmenu; + update(); +} + +void QPxGraph::changeScale(int idx) { + if (idx<0 || idx>1 || !scale[idx]) return; + scale[idx]->type = settings->scales.get(name[idx]).type; + scale[idx]->policy = settings->scales.get(name[idx]).policy; + scale[idx]->value = settings->scales.get(name[idx]).value; + update(); +} + +void QPxGraph::setScaleTypeLog() { + if (!scale[0] || (scale[0]->type == Scale::Log)) return; + scale[0]->type = Scale::Log; + settings->scales.get(name[0]).type = scale[0]->type; +#ifdef CACHE_GRAPH + if (img) delete img; +#endif + emit scaleChanged(); +} + +void QPxGraph::setScaleTypeLin() { + if (!scale[0] || (scale[0]->type == Scale::Linear)) return; + scale[0]->type = Scale::Linear; + settings->scales.get(name[0]).type = scale[0]->type; +#ifdef CACHE_GRAPH + if (img) delete img; +#endif + emit scaleChanged(); +} + +void QPxGraph::setScalePolicyAuto() { + for (int i=0; i<2; i++) + if (scale[i]) { + scale[i]->policy = Scale::Auto; + settings->scales.get(name[i]).policy = scale[i]->policy; + } + emit scaleChanged(); +} + +void QPxGraph::setScalePolicyFixed() { + for (int i=0; i<2; i++) + if (scale[i]) { + scale[i]->policy = Scale::Fixed; + settings->scales.get(name[i]).policy = scale[i]->policy; + } + emit scaleChanged(); +} + +void QPxGraph::setScaleValue( int val, int idx) { + if (idx<0 || idx>1) { + for (int i=0; i<2; i++) + if (scale[i]) { + scale[i]->value = val; + settings->scales.get(name[i]).value = val; + } + } else { + if (scale[idx]) { + scale[idx]->value = val; + settings->scales.get(name[idx]).value = val; + } + } + +#ifdef CACHE_GRAPH + if (img) delete img; +#endif + emit scaleChanged(); +} + +void QPxGraph::scaleIn(int idx) +{ +#ifndef QT_NO_DEBUG + qDebug() << "scaleIn (" << idx << ")"; +#endif + if (idx<0 || idx>1 || !scale[idx]) return; + switch (test) { + case TEST_ERRC: + if (!scale[idx] || scale[idx]->value <= 64) return; + scale[idx]->value /= 2; + settings->scales.get(name[idx]).value = scale[idx]->value; + break; + case TEST_JB: + if (!idx) { + if (!scale[idx] || scale[idx]->value <= 2) return; // jitter + } else { + if (!scale[idx] || scale[idx]->value <= 8) return; // asymmetry + } + scale[idx]->value /= 2; + settings->scales.get(name[idx]).value = scale[idx]->value; + break; + default: + break; + } +#ifdef CACHE_GRAPH + if (img) delete img; +#endif + emit scaleChanged(); +} + +void QPxGraph::scaleOut(int idx) +{ +#ifndef QT_NO_DEBUG + qDebug() << "scaleOut (" << idx << ")"; +#endif + if (idx<0 || idx>1 || !scale[idx]) return; + switch (test) { + case TEST_ERRC: + if (!scale[idx] || scale[idx]->value >= 2048) return; + scale[idx]->value *= 2; + settings->scales.get(name[idx]).value = scale[idx]->value; + break; + case TEST_JB: + if (!idx) { + if (!scale[idx] || scale[idx]->value >= 8) return; // jitter + } else { + if (!scale[idx] || scale[idx]->value >= 32) return; // asymmetry + } + scale[idx]->value *= 2; + settings->scales.get(name[idx]).value = scale[idx]->value; + break; + default: + break; + } +#ifdef CACHE_GRAPH + if (img) delete img; +#endif + emit scaleChanged(); +} + +int QPxGraph::errc2h(int h, int val) +{ + if (val<=0) return h; + if (scale[0]->type == Scale::Log) { + return (int) (h - 10 - h*((float)log10(val)*0.31)); + } else { + return (int) (h - 2 - h*(float)val / scale[0]->value); + } +} + +int QPxGraph::jitter2h(int h, float val) { return (int) (h - h * val / scale[0]->value); } +int QPxGraph::asymm2h(int h, float val) { return (int) (h/2 - h * val / scale[1]->value); } + +int QPxGraph::ta2h(int h, int val) +{ + if (val<=0) return h; + return (int) (h - h*(float)log10(val)*0.2); +} + diff --git a/gui/src/qpxiodevice.cpp b/gui/src/qpxiodevice.cpp new file mode 100644 index 0000000..9d0f32c --- /dev/null +++ b/gui/src/qpxiodevice.cpp @@ -0,0 +1,114 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Gennady "ShultZ" Kozlov + * + * 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 "qpxiodevice.h" + +#include +#include + +#include + +#include + +#ifndef QT_NO_DEBUG +//#define QPXIO_DEBUG +#endif + +QPxIODevice::QPxIODevice(QObject* p) + :QObject(p) +{ + buf = ""; + io = NULL; +} + +QPxIODevice::~QPxIODevice() {} + +void QPxIODevice::setIODevice(QIODevice* iio) +{ +#ifdef QPXIO_DEBUG + qDebug("STA: QPxIODevice::setIODevice()"); +#endif + buf = ""; + io = iio; + if (io) { + if ( typeid(*io) == typeid(QProcess) ) + connect(io, SIGNAL(readyReadStandardOutput()), this, SLOT(splitInput())); + else if ( typeid(*io) == typeid(QTcpSocket) ) + connect(io, SIGNAL(readyRead()), this, SLOT(splitInput())); + } +#ifdef QPXIO_DEBUG + qDebug("END: QPxIODevice::setIODevice()"); +#endif +} + +QIODevice* QPxIODevice::IODevice() { return io; } + +QString QPxIODevice::readLine() +{ + QString ts=""; + if (lines.size()) { + ts = lines[0]; + lines.removeFirst(); + } + return ts; +} + +int QPxIODevice::linesAvailable() { return lines.size(); } + +void QPxIODevice::splitInput() +{ +#ifdef QPXIO_DEBUG + qDebug("STA: QPxIODevice::splitInput()"); +#endif + + int bi; + qint64 ba; + while ((ba = io->bytesAvailable())) { + //while ((io->bytesAvailable())) { +#ifdef QPXIO_DEBUG + qDebug() << "IODevice bytes: " << ba; +#endif + +/* +#ifdef QPXIO_DEBUG + if ( typeid(*io) == typeid(QTcpSocket) ) { +// QByteArray tb; + while ((io->bytesAvailable())) { + buf+=io->read(1).replace(0,'\n'); + } +// buf+=QString(tb); +// qDebug() << "ba.size(): " << tb.size(); +// qDebug() << "buf: " << buf; + } else { + buf+=io->readAll(); + } +#else +*/ + buf+=io->readAll().replace(0,'\n'); +/* +#endif +*/ + while ((bi = buf.indexOf(QRegExp("[\n\r]"),0)) >=0 ) { + if (bi>0) { + lines << buf.left(bi); + buf.remove(0,bi); + } + buf.remove(0,1); + } +// if (lines.size() > 4) emit readyReadLine(); + } + if (lines.size()) emit readyReadLine(); +#ifdef QPXIO_DEBUG + qDebug("END: QPxIODevice::splitInput()"); +#endif +} + diff --git a/gui/src/qpxsettings.cpp b/gui/src/qpxsettings.cpp new file mode 100644 index 0000000..3f5c4b6 --- /dev/null +++ b/gui/src/qpxsettings.cpp @@ -0,0 +1,352 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include "qpxsettings.h" + +#include + +Scale::Scale(QString iname) +{ + name=iname; + policy = Auto; + type = Linear; + value = 256; +} + +Scale::~Scale() +{ +} + +QPxSettings::QPxSettings() +{ +#ifndef QT_NO_DEBUG + qDebug() << "QPxSettings() " << this; +#endif + geometry_mw = QRect(0,0,0,0); + geometry_pref = QRect(0,0,0,0); +// geometry_testsel = QRect(0,0,0,0); + + show_sidebar = 0; + show_allctl = 0; + actions_flags = 0; + + useLocal = 1; + useRemote = 0; + hosts.clear(); + ports.clear(); + + for (int i=0; i<8; i++) { + col_errc.raw[i] = new QColor(); +#ifndef QT_NO_DEBUG +// qDebug(QString("new QColor %1").arg((unsigned int)col_errc.raw[i],8,16)); +#endif + } +} + +QPxSettings::QPxSettings(const QPxSettings& o) +{ +#ifndef QT_NO_DEBUG + qDebug() << "QPxSettings(const QPxSettings&) " << this; +#endif + for (int i=0; i<8; i++) { + col_errc.raw[i] = new QColor(*(o.col_errc.raw[i])); +#ifndef QT_NO_DEBUG +// qDebug(QString("new QColor %1").arg((unsigned int)col_errc.raw[i],8,16)); +#endif + } + *this = o; +} + +QPxSettings& QPxSettings::operator = (const QPxSettings& o) +{ +#ifndef QT_NO_DEBUG + qDebug() << "QPxSettings() " << this << " = " << &o; +#endif + geometry_mw = o.geometry_mw; + geometry_pref = o.geometry_pref; + + show_sidebar = o.show_sidebar; + show_allctl = o.show_allctl; + report_autosave = o.report_autosave; + report_path = o.report_path; + actions_flags = o.actions_flags; + + use_reports_db = o.use_reports_db; + report_autosave_db = o.report_autosave_db; + db = o.db; + + useLocal = o.useLocal; + useRemote = o.useRemote; + hosts = o.hosts; + ports = o.ports; + + tests = o.tests; + + col_bg = o.col_bg; + col_bginv = o.col_bginv; + col_grid = o.col_grid; + col_rspeed = o.col_rspeed; + col_wspeed = o.col_wspeed; + + for (int i=0; i<8; i++) *col_errc.raw[i] = *o.col_errc.raw[i]; + + col_jitter = o.col_jitter; + col_asymm = o.col_asymm; + col_fe = o.col_fe; + col_te = o.col_te; + col_tapit = o.col_tapit; + col_taland = o.col_taland; + + scales = o.scales; + return *this; +} + +QPxSettings::~QPxSettings() +{ +#ifndef QT_NO_DEBUG + qDebug() << "~QPxSettings() " << this; +#endif + for (int i=0; i<8; i++) { +#ifndef QT_NO_DEBUG +// qDebug(QString("del QColor %1").arg((unsigned int)col_errc.raw[i],8,16)); +#endif + delete col_errc.raw[i]; + } +} + +void QPxSettings::load() +{ + QSettings *settings; +#ifndef QT_NO_DEBUG + qDebug("Loading settings..."); +#endif + +// settings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, "qpxtool"); + settings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, "QPxTool", "qpxtool"); + settings->beginGroup("/common"); + show_sidebar = settings->value("show_sidebar", 0).toBool(); + show_allctl = settings->value("show_allctl", 0).toBool(); + report_autosave = settings->value("report_autosave", 0).toBool(); + report_path = settings->value("report_path", "").toString(); + actions_flags = settings->value("actions_flags", "0").toUInt(); + settings->endGroup(); + settings->beginGroup("/database"); + use_reports_db = settings->value("use_reports_db", 0).toBool(); + report_autosave_db = settings->value("report_autosave_db", 0).toBool(); + db.driver = settings->value("db_driver", "").toString(); + db.host = settings->value("db_host", "").toString(); + db.port = settings->value("db_port", "").toInt(); + db.name = settings->value("db_name", "").toString(); + db.user = settings->value("db_user", "").toString(); + db.pass = settings->value("db_pass", "").toString(); + settings->endGroup(); + + settings->beginGroup("/geometry"); + geometry_mw = settings->value("mainwindow", QRect(0,0,0,0) ).toRect(); + geometry_pref = settings->value("preferences", QRect(0,0,0,0) ).toRect(); +// geometry_testsel = settings->value("testselector", QRect(0,0,0,0) ).toRect(); + settings->endGroup(); + + settings->beginGroup("/devices"); + useLocal = settings->value("local", 1).toBool(); + useRemote = settings->value("remote", 0).toBool(); + hosts = settings->value("hosts", QStringList()).toStringList(); + ports = settings->value("ports", QStringList()).toStringList(); + hosts.removeAll(""); + tests = settings->value("tests", 0).toInt(); + settings->endGroup(); + + settings->beginGroup("/colors"); + col_bg = settings->value("graph_bg", defColors.bg.rgb()).toInt(); + col_bginv = QColor( (~col_bg.red()) & 0xFF, (~col_bg.green()) & 0xFF, (~col_bg.blue()) & 0xFF ); + col_grid = settings->value("graph_grid", defColors.grid.rgb()).toInt(); + + col_rspeed = settings->value("rspeed", defColors.rspeed.rgb()).toInt(); + col_wspeed = settings->value("wspeed", defColors.wspeed.rgb()).toInt(); + + for(int i=0; i<8; i++) + *col_errc.raw[i] = settings->value(QString("errc%1").arg(i), defColors.errc[i].rgb()).toInt(); + + col_jitter = settings->value("jitter", defColors.jitter.rgb()).toInt(); + col_asymm = settings->value("asymm", defColors.asymm.rgb()).toInt(); + + col_fe = settings->value("fe", defColors.fe.rgb()).toInt(); + col_te = settings->value("te", defColors.te.rgb()).toInt(); + + col_tapit = settings->value("ta_pit", defColors.tapit.rgb()).toInt(); + col_taland = settings->value("ta_land", defColors.taland.rgb()).toInt(); + settings->endGroup(); + settings->beginGroup("/paths"); + last_res_path_native = settings->value("last_res_native","").toString(); + last_res_path_html = settings->value("last_res_html","").toString(); + last_res_path_pdf = settings->value("last_res_pdf","").toString(); + settings->endGroup(); + delete settings; +} + +void QPxSettings::save() +{ + QSettings *settings; +#ifndef QT_NO_DEBUG + qDebug("Saving settings..."); +#endif +// settings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, "qpxtool"); + settings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, "QPxTool", "qpxtool"); + settings->beginGroup("/common"); + settings->setValue("show_sidebar", show_sidebar); + settings->setValue("show_allctl", show_allctl); + settings->setValue("report_autosave", report_autosave); + settings->setValue("report_path", report_path); + settings->setValue("actions_flags", actions_flags); + settings->endGroup(); + + settings->beginGroup("/database"); + settings->setValue("use_reports_db", use_reports_db); + settings->setValue("report_autosave_db", report_autosave_db); + settings->setValue("db_driver", db.driver); + settings->setValue("db_host", db.host); + settings->setValue("db_port", db.port); + settings->setValue("db_name", db.name); + settings->setValue("db_user", db.user); + settings->setValue("db_pass", db.pass); + settings->endGroup(); + + settings->beginGroup("/geometry"); + settings->setValue("mainwindow", geometry_mw); + settings->setValue("preferences", geometry_pref); +// settings->setValue("testselector", geometry_testsel); + settings->endGroup(); + + settings->beginGroup("/devices"); + settings->setValue("local", useLocal); + settings->setValue("remote", useRemote); + settings->setValue("hosts", hosts); + settings->setValue("ports", ports); + settings->setValue("tests", tests); + settings->endGroup(); + + settings->beginGroup("/colors"); + settings->setValue("graph_bg", col_bg.rgb()); + settings->setValue("graph_grid", col_grid.rgb()); + + settings->setValue("rspeed", col_rspeed.rgb()); + settings->setValue("wspeed", col_wspeed.rgb()); + + for(int i=0; i<8; i++) + settings->setValue(QString("errc%1").arg(i), col_errc.raw[i]->rgb()); + + settings->setValue("jitter", col_jitter.rgb()); + settings->setValue("asymm", col_asymm.rgb()); + + settings->setValue("fe", col_fe.rgb()); + settings->setValue("te", col_te.rgb()); + + settings->setValue("ta_pit", col_tapit.rgb()); + settings->setValue("ta_land", col_taland.rgb()); + + settings->endGroup(); + + settings->beginGroup("/paths"); + settings->setValue("last_res_native", last_res_path_native); + settings->setValue("last_res_html", last_res_path_html); + settings->setValue("last_res_pdf", last_res_path_pdf); + settings->endGroup(); + + delete settings; + saveScale(); +} + +void QPxSettings::loadScale(QString name) +{ + QSettings *settings; +// Scale scale(name); + +#ifndef QT_NO_DEBUG + qDebug() << "Loading scale: " << name; +#endif +// settings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, "qpxtool"); + settings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, "QPxTool", "qpxtool"); + + settings->beginGroup("/scale"); + settings->beginGroup(name); + scales.get(name).policy = settings->value("policy", Scale::Auto).toInt(); + scales.get(name).type = settings->value("type", Scale::Log).toInt(); + scales.get(name).value = settings->value("value", 0).toInt(); + settings->endGroup(); + settings->endGroup(); + +// scales.removeAll(name); +// scales.append(scale); + + delete settings; +} + +void QPxSettings::saveScale() +{ + QSettings *settings; +#ifndef QT_NO_DEBUG + qDebug("Saving scale..."); +#endif + settings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, "QPxTool", "qpxtool"); + + settings->beginGroup("/scale"); + for (int i=0; ibeginGroup(scales[i].name); + settings->setValue("policy", scales[i].policy); + settings->setValue("type", scales[i].type); + settings->setValue("value", scales[i].value); + settings->endGroup(); + } + settings->endGroup(); + + delete settings; +} + +void QPxSettings::setDefaultColors() +{ + col_bg = defColors.bg; + col_bginv = QColor( (~col_bg.red()) & 0xFF, (~col_bg.green()) & 0xFF, (~col_bg.blue()) & 0xFF ); + col_grid = defColors.grid; + + col_rspeed = defColors.rspeed; + col_wspeed = defColors.wspeed; + + for(int i=0; i<8; i++) *col_errc.raw[i] = defColors.errc[i]; + + col_jitter = defColors.jitter; + col_asymm = defColors.asymm; + + col_fe = defColors.fe; + col_te = defColors.te; + + col_tapit = defColors.tapit; + col_taland = defColors.taland; +} + +#if 0 +QPxSettings& QPxSettings::operator = (const QPxSettings& o) { + hosts = o.hosts; + wgeometry = o.wgeometry; + pgeometry = o.pgeometry; + + col_bg = o.col_bg; + col_grid = o.col_grid; + + scales = o.scales; + return (*this); +} +#endif + diff --git a/gui/src/resultsio.cpp b/gui/src/resultsio.cpp new file mode 100644 index 0000000..09f2428 --- /dev/null +++ b/gui/src/resultsio.cpp @@ -0,0 +1,521 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2010-2012 Gennady "ShultZ" Kozlov + * + * 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 "resultsio.h" + +#include +#include +#include +#include + +#include +#include +#include + +void ResultsReader::run() { + QXmlStreamReader xml; + QXmlStreamAttributes attr; + QString name; + QStringList data; + bool dataFound = 0, + isQpxData = 0, + isDevice = 0, + isMedia = 0, + isTests = 0; + int test = 0; + int taZone = -1; + + DI_Transfer d_tr; + DI_Errc d_errc; + DI_JB d_jb; + DI_FT d_ft; + DI_TA d_ta; + + res = false; + + if (!io || !io->isOpen() || !io->isReadable()) { return; } + xml.setDevice(io); +// xml.version(); + + dev->clearMinfo(); + while(!xml.atEnd()) { + switch (xml.readNext()) { + case QXmlStreamReader::StartElement: + name = xml.name().toString(); + if (xml.name() == "qpxdata") { + dataFound = 1; + isQpxData = 1; + } + if (isQpxData) { + if (test && (xml.name() == "d")) { + data = xml.readElementText().split(","); + switch (test) { + case TEST_RT: + if (data.size() == 3) { + d_tr.lba = data[0].toLongLong(); + d_tr.spdx = data[1].toFloat(); + d_tr.spdk = data[2].toInt(); + dev->testData.rt << d_tr; + } + break; + case TEST_WT: + if (data.size() == 3) { + d_tr.lba = data[0].toLongLong(); + d_tr.spdx = data[1].toFloat(); + d_tr.spdk = data[2].toInt(); + dev->testData.wt << d_tr; + } + break; + case TEST_ERRC: + if (data.size() == 10) { + d_errc.raw.lba = data[0].toLongLong(); + d_errc.raw.spdx = data[1].toFloat(); + for (int i=0; i<8; i++) + d_errc.raw.err[i] = data[2+i].toInt(); +#if RECALC_ON_LOAD + ErrcADD(&dev->testData.errcTOT, d_errc); + ErrcMAX(&dev->testData.errcMAX, d_errc); + if (dev->media.type.startsWith("CD")) { + CDErrcAVG(&dev->testData.errcAVG, &dev->testData.errcTOT, d_errc.cd.lba/75); + } else if (dev->media.type.startsWith("DVD")) { + DVDErrcAVG(&dev->testData.errcAVG, &dev->testData.errcTOT, d_errc.dvd.lba >> 4); + } else if (dev->media.type.startsWith("BD")) { + BDErrcAVG(&dev->testData.errcAVG, &dev->testData.errcTOT, d_errc.bd.lba >> 5); + } +#endif + dev->testData.errc << d_errc; + } + break; + case TEST_JB: + if (data.size() == 4) { + d_jb.lba = data[0].toLongLong(); + d_jb.spdx = data[1].toFloat(); + d_jb.jitter = data[2].toFloat(); + d_jb.asymm = data[3].toFloat(); +#if RECALC_ON_LOAD + if (!dev->testData.jb.size()) { + dev->testData.jbMM.jmin = d_jb.jitter; + dev->testData.jbMM.jmax = d_jb.jitter; + dev->testData.jbMM.bmin = d_jb.asymm; + dev->testData.jbMM.bmax = d_jb.asymm; + } else { + if (dev->testData.jbMM.jmin > d_jb.jitter) dev->testData.jbMM.jmin = d_jb.jitter; + if (dev->testData.jbMM.jmax < d_jb.jitter) dev->testData.jbMM.jmax = d_jb.jitter; + if (dev->testData.jbMM.bmin > d_jb.asymm) dev->testData.jbMM.bmin = d_jb.asymm; + if (dev->testData.jbMM.bmax < d_jb.asymm) dev->testData.jbMM.bmin = d_jb.asymm; + } +#endif + dev->testData.jb << d_jb; + } + break; + case TEST_FT: + if (data.size() == 4) { + d_ft.lba = data[0].toLongLong(); + d_ft.spdx= data[1].toFloat(); + d_ft.fe = data[2].toInt(); + d_ft.te = data[3].toInt(); +#if RECALC_ON_LOAD + if (dev->testData.ftMAX.fe < d_ft.fe) dev->testData.ftMAX.fe = d_ft.fe; + if (dev->testData.ftMAX.te < d_ft.te) dev->testData.ftMAX.te = d_ft.te; +#endif + dev->testData.ft << d_ft; + } + break; + case TEST_TA: + if (taZone>=0 && taZone<6 && data.size() == 3) { + d_ta.idx = data[0].toInt(); + d_ta.pit = data[1].toInt(); + d_ta.land = data[2].toInt(); + + dev->testData.ta[taZone] << d_ta; + } + break; + } + } else if (isDevice) { + // device information + attr = xml.attributes(); + if (xml.name() == "identify") { +// dev->type = attr.value("type").toInt(); + dev->path = attr.value("path").toString(); + dev->host = attr.value("host").toString(); + dev->port = attr.value("port").toString().toInt(); + dev->path = attr.value("path").toString(); +// dev->id = attr.value("id").toString(); + dev->ven = attr.value("ven").toString(); + dev->dev = attr.value("dev").toString(); + dev->sn = attr.value("sn").toString(); + dev->fw = attr.value("fw").toString(); + dev->tla = attr.value("tla").toString(); + } else if (xml.name() == "info") { + dev->buf = attr.value("buf").toString(); + dev->iface = attr.value("iface").toString(); + dev->loader = attr.value("loader").toString(); + dev->cap = attr.value("cap").toString().toULongLong(NULL, 16); + dev->cap_rd = attr.value("cap_rd").toString().toULongLong(NULL, 16); + dev->cap_wr = attr.value("cap_wr").toString().toULongLong(NULL, 16); + } else if (xml.name() == "rpc") { + dev->rpc_phase = attr.value("phase").toString().toInt(); + dev->rpc_reg = attr.value("region").toString().toInt(); + dev->rpc_ch = attr.value("changes").toString().toInt(); + dev->rpc_rst = attr.value("resets").toString().toInt(); + } else if (xml.name() == "lifetime") { + dev->life_dn = attr.value("count").toString().toInt(); + dev->life_cr = attr.value("cd_rd").toString(); + dev->life_cw = attr.value("cd_wr").toString(); + dev->life_dr = attr.value("dvd_rd").toString(); + dev->life_dw = attr.value("dvd_wr").toString(); + } + } else if (isMedia) { + // media information + attr = xml.attributes(); + if (xml.name() == "identify") { + dev->media.type = attr.value("type").toString(); + dev->media.category = attr.value("category").toString(); + dev->media.mid = attr.value("mid").toString(); + dev->media.label = attr.value("label").toString(); + } else if (xml.name() == "capacity") { + dev->media.creads = attr.value("read").toString().toInt(); + dev->media.cfrees = attr.value("free").toString().toInt(); + dev->media.ctots = attr.value("tot").toString().toInt(); + dev->media.creadm = attr.value("read_m").toString().toInt(); + dev->media.cfreem = attr.value("free_m").toString().toInt(); + dev->media.ctotm = attr.value("tot_m").toString().toInt(); + dev->media.creadmsf = attr.value("read_msf").toString(); + dev->media.cfreemsf = attr.value("free_msf").toString(); + dev->media.ctotmsf = attr.value("tot_msf").toString(); + } else if (xml.name() == "speeds") { + dev->media.rspeeds = attr.value("rspeeds" ).toString().split(" ", QString::SkipEmptyParts); + dev->media.wspeedsd = attr.value("wspeedsd").toString().split(" ", QString::SkipEmptyParts); + dev->media.wspeedsm = attr.value("wspeedsm").toString().split(" ", QString::SkipEmptyParts); + } else if (xml.name() == "misc") { + dev->media.writer = attr.value("writer").toString(); + dev->media.prot = attr.value("protection").toString(); + dev->media.regions = attr.value("regions").toString(); + dev->media.grec = attr.value("gigarec").toString().toDouble(); + dev->media.spd1X = attr.value("spd1X").toString().toInt(); + dev->media.layers = attr.value("layers").toString(); + dev->media.erasable= attr.value("erasable").toString(); + dev->media.ilayers = dev->media.layers.toInt(); + dev->media.dstate = attr.value("dstate").toString(); + dev->media.sstate = attr.value("sstate").toString(); + } + } else if (isTests) { + attr = xml.attributes(); + if (xml.name() == "rt") { + test = TEST_RT; + dev->tspeeds.rt = attr.value("speed").toString().toInt(); + dev->testData.rt_time = attr.value("time").toString().toDouble(); + } else if (xml.name() == "wt") { + test = TEST_WT; + dev->tspeeds.wt = attr.value("speed").toString().toInt(); + dev->testData.wt_time = attr.value("time").toString().toDouble(); + } else if (xml.name() == "errc") { + test = TEST_ERRC; + dev->tspeeds.errc = attr.value("speed").toString().toInt(); + dev->testData.errc_time = attr.value("time").toString().toDouble(); + dev->media.tdata_errc = attr.value("data").toString().toInt(); + } else if (xml.name() == "jb") { + test = TEST_JB; + dev->tspeeds.jb = attr.value("speed").toString().toInt(); + dev->testData.jb_time = attr.value("time").toString().toDouble(); + } else if (xml.name() == "ft") { + test = TEST_FT; + dev->tspeeds.ft = attr.value("speed").toString().toInt(); + dev->testData.ft_time = attr.value("time").toString().toDouble(); + } else if (xml.name() == "ta") { + test = TEST_TA; + // dev->tspeeds.ta = attr.value("speed").toString().toInt(); + dev->testData.ta_time = attr.value("time").toString().toDouble(); + } else if ((test == TEST_TA) && (xml.name() == "zone")) { + taZone = attr.value("idx").toString().toInt(); + } + } else if (xml.name() == "device") { + isDevice = 1; + } else if (xml.name() == "media") { + isMedia = 1; + } else if (xml.name() == "tests") { + isTests = 1; + } + } + break; + case QXmlStreamReader::EndElement: + if (isQpxData && xml.name() == "qpxdata") { + isQpxData = 0; + } else if (isDevice && xml.name() == "device") { + isDevice = 0; + } else if (isMedia && xml.name() == "media") { + isMedia = 0; + } else if (isTests && xml.name() == "tests") { + isTests = 0; + } else if (isTests) { + if (test == TEST_RT && xml.name() == "rt") { + test = 0; + } else if (test == TEST_WT && xml.name() == "wt") { + test = 0; + } else if (test == TEST_ERRC && xml.name() == "errc") { + test = 0; + } else if (test == TEST_JB && xml.name() == "jb") { + test = 0; + } else if (test == TEST_FT && xml.name() == "ft") { + test = 0; + } else if (test == TEST_TA && xml.name() == "ta") { + test = 0; + } else if (test == TEST_TA && xml.name() == "zone") { + taZone = -1; + } + } + case QXmlStreamReader::Invalid: + qDebug() << COL_RED << "ResultsReader: Invalid token @" << xml.characterOffset() << xml.name() << COL_NORM; + break; + case QXmlStreamReader::NoToken: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Characters: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + // default: + break; + } + } + res = dataFound; +} + + +void ResultsWriter::run() { + QXmlStreamWriter xml; + res = false; + + if (!io || !io->isOpen() || !io->isWritable()) { return; } + + xml.setDevice(io); + xml.setAutoFormatting(true); + + xml.writeStartDocument("1.0"); + xml.writeStartElement("qpxdata"); +// writing metadata +// device info... + xml.writeStartElement("device"); + xml.writeStartElement("identify"); + switch (dev->type) { + case device::DevtypeLocal: + xml.writeAttribute("type","local"); + break; + case device::DevtypeTCP: + xml.writeAttribute("type","tcp"); + xml.writeAttribute("host", dev->host); + xml.writeAttribute("port", QString::number(dev->port)); + break; + default: + break; + } + xml.writeAttribute("path", dev->path); +// xml.writeAttribute("id", dev->id); + xml.writeAttribute("ven", dev->ven); + xml.writeAttribute("dev", dev->dev); + xml.writeAttribute("sn", dev->sn); + xml.writeAttribute("fw", dev->fw); + xml.writeAttribute("tla", dev->tla); + xml.writeEndElement(); + + xml.writeStartElement("info"); + xml.writeAttribute("buf", dev->buf); + xml.writeAttribute("iface",dev->iface); + xml.writeAttribute("loader",dev->loader); + xml.writeAttribute("cap", QString("%1").arg(dev->cap,0,16)); + xml.writeAttribute("cap_rd", QString("%1").arg(dev->cap_rd,0,16)); + xml.writeAttribute("cap_wr", QString("%1").arg(dev->cap_wr,0,16)); + xml.writeEndElement(); + + if (dev->cap & CAP_DVD_CSS) + { + xml.writeStartElement("rpc"); + xml.writeAttribute("phase", QString::number(dev->rpc_phase)); + xml.writeAttribute("region", QString::number(dev->rpc_reg)); + xml.writeAttribute("changes", QString::number(dev->rpc_ch)); + xml.writeAttribute("resets", QString::number(dev->rpc_rst)); + xml.writeEndElement(); + } + +// if (life_dn) { + xml.writeStartElement("lifetime"); + xml.writeAttribute("count", QString::number(dev->life_dn)); + xml.writeAttribute("cd_rd", dev->life_cr); + xml.writeAttribute("cd_wr", dev->life_cw); + xml.writeAttribute("dvd_rd", dev->life_dr); + xml.writeAttribute("dvd_wr", dev->life_dw); + xml.writeEndElement(); +// } + + xml.writeEndElement(); // device + xml.writeStartElement("media"); +// media info + xml.writeStartElement("identify"); + xml.writeAttribute("type", dev->media.type); + xml.writeAttribute("category", dev->media.category); + xml.writeAttribute("mid", dev->media.mid); + xml.writeAttribute("label", dev->media.label); + xml.writeEndElement(); + + xml.writeStartElement("capacity"); + xml.writeAttribute("read", QString::number(dev->media.creads)); + xml.writeAttribute("free", QString::number(dev->media.cfrees)); + xml.writeAttribute("tot", QString::number(dev->media.ctots)); + + xml.writeAttribute("read_m", QString::number(dev->media.creadm)); + xml.writeAttribute("free_m", QString::number(dev->media.cfreem)); + xml.writeAttribute("tot_m", QString::number(dev->media.ctotm)); + + xml.writeAttribute("read_msf", dev->media.creadmsf); + xml.writeAttribute("free_msf", dev->media.cfreemsf); + xml.writeAttribute("tot_msf", dev->media.ctotmsf); + xml.writeEndElement(); // capacity + + xml.writeStartElement("speeds"); + xml.writeAttribute("rspeeds", dev->media.rspeeds.join(" ")); + xml.writeAttribute("wspeedsd", dev->media.wspeedsd.join(" ")); + xml.writeAttribute("wspeedsm", dev->media.wspeedsm.join(" ")); + xml.writeEndElement(); // misc + + xml.writeStartElement("misc"); + xml.writeAttribute("writer", dev->media.writer); + xml.writeAttribute("protection",dev->media.prot); + xml.writeAttribute("regions", dev->media.regions); + xml.writeAttribute("gigarec", QString("%1").arg(dev->media.grec,0,'f',1)); + + xml.writeAttribute("spd1X", QString::number(dev->media.spd1X)); + xml.writeAttribute("layers", QString::number(dev->media.ilayers)); + xml.writeAttribute("erasable", dev->media.erasable); + + xml.writeAttribute("dstate", dev->media.dstate); + xml.writeAttribute("sstate", dev->media.sstate); + xml.writeEndElement(); // misc + xml.writeEndElement(); // media +// test speeds + + xml.writeStartElement("tests"); + + if (dev->testData.rt.size()) { + int s = dev->testData.rt.size(); + xml.writeStartElement("rt"); + xml.writeAttribute("speed", QString::number(dev->tspeeds.rt)); + xml.writeAttribute("time", QString("%1").arg(dev->testData.rt_time,0,'f',2)); + + for (int i=0; itestData.rt[i].lba) + .arg(dev->testData.rt[i].spdx) + .arg(dev->testData.rt[i].spdk)); + } + xml.writeEndElement(); + } + + if (dev->testData.wt.size()) { + int s = dev->testData.rt.size(); + xml.writeStartElement("wt"); + xml.writeAttribute("speed", QString::number(dev->tspeeds.wt)); + xml.writeAttribute("time", QString("%1").arg(dev->testData.wt_time,0,'f',2)); + + for (int i=0; itestData.wt[i].lba) + .arg(dev->testData.wt[i].spdx) + .arg(dev->testData.wt[i].spdk)); + } + xml.writeEndElement(); + } + + if (dev->testData.errc.size()) { + int s = dev->testData.errc.size(); + xml.writeStartElement("errc"); + xml.writeAttribute("speed", QString::number(dev->tspeeds.errc)); + xml.writeAttribute("time", QString("%1").arg(dev->testData.errc_time,0,'f',2)); + xml.writeAttribute("data", QString::number(dev->media.tdata_errc)); + + for (int i=0; itestData.errc[i].raw.lba).arg(dev->testData.errc[i].raw.spdx,0,'f',2) + + QString("%1,%2,%3,%4,%5,%6,%7,%8") + .arg(dev->testData.errc[i].raw.err[0]) + .arg(dev->testData.errc[i].raw.err[1]) + .arg(dev->testData.errc[i].raw.err[2]) + .arg(dev->testData.errc[i].raw.err[3]) + .arg(dev->testData.errc[i].raw.err[4]) + .arg(dev->testData.errc[i].raw.err[5]) + .arg(dev->testData.errc[i].raw.err[6]) + .arg(dev->testData.errc[i].raw.err[7]) + ); + } + xml.writeEndElement(); + } + + if (dev->testData.jb.size()) { + int s = dev->testData.jb.size(); + xml.writeStartElement("jb"); + xml.writeAttribute("speed", QString::number(dev->tspeeds.jb)); + xml.writeAttribute("time", QString("%1").arg(dev->testData.jb_time,0,'f',2)); + + for (int i=0; itestData.jb[i].lba) + .arg(dev->testData.jb[i].spdx) + .arg(dev->testData.jb[i].jitter) + .arg(dev->testData.jb[i].asymm)); + } + xml.writeEndElement(); + } + + if (dev->testData.ft.size()) { + int s = dev->testData.ft.size(); + xml.writeStartElement("ft"); + xml.writeAttribute("speed", QString::number(dev->tspeeds.ft)); + xml.writeAttribute("time", QString("%1").arg(dev->testData.ft_time,0,'f',2)); + + for (int i=0; itestData.ft[i].lba) + .arg(dev->testData.ft[i].spdx) + .arg(dev->testData.ft[i].fe) + .arg(dev->testData.ft[i].te)); + } + xml.writeEndElement(); + } + + if (dev->testData.ta[0].size()) { + xml.writeStartElement("ta"); + xml.writeAttribute("time", QString("%1").arg(dev->testData.ta_time,0,'f',2)); + + for (int i=0; i<6; i++) { + int s = dev->testData.ta[i].size(); + xml.writeStartElement("zone"); + xml.writeAttribute("idx", QString::number(i)); + + for(int j=0;jtestData.ta[i][j].idx) + .arg(dev->testData.ta[i][j].pit) + .arg(dev->testData.ta[i][j].land) + ); + } + xml.writeEndElement(); + } + xml.writeEndElement(); + } + + xml.writeEndElement(); // tests + xml.writeEndElement(); // qpxdata + xml.writeEndDocument(); + + res = true; +} + diff --git a/gui/src/splitbutton.cpp b/gui/src/splitbutton.cpp new file mode 100644 index 0000000..c19488e --- /dev/null +++ b/gui/src/splitbutton.cpp @@ -0,0 +1,67 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 +#include "splitbutton.h" + +SplitButton::SplitButton(Qt::Orientation o, QWidget *p, Qt::WindowFlags f) + : QWidget(p,f) +{ + orient = o; + mouseFocus=0; + if (orient == Qt::Vertical) { + setMinimumWidth(6); + setMaximumWidth(6); + } else { + setMinimumHeight(6); + setMaximumHeight(6); + } +} + +SplitButton::~SplitButton() {} + +void SplitButton::paintEvent(QPaintEvent*) +{ +// wDebug("SplitButton::paintEvent()"); + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing, true); + + if (mouseFocus) { + p.fillRect(0, 0, width(), height(), QBrush(palette().color(QPalette::Light))); + } else { + p.fillRect(0, 0, width(), height(), QBrush(palette().color(QPalette::Button))); + } + p.setPen(QPen(palette().color(QPalette::Dark),1)); + p.drawRect(0, 0, width(), height()); +} + +void SplitButton::mousePressEvent(QMouseEvent*) +{ +// wDebug("SplitButton::mousePressEvent()"); + emit clicked(); +} + +void SplitButton::mouseReleaseEvent(QMouseEvent*) +{ + +} + +void SplitButton::enterEvent(QEvent*) { +// wDebug("SplitButton::enterEvent()"); + mouseFocus=1; update(); +} + +void SplitButton::leaveEvent(QEvent*) { +// wDebug("SplitButton::leaveEvent()"); + mouseFocus=0; update(); +} + diff --git a/gui/src/tab_devinfo.cpp b/gui/src/tab_devinfo.cpp new file mode 100644 index 0000000..0e5e4da --- /dev/null +++ b/gui/src/tab_devinfo.cpp @@ -0,0 +1,344 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include +#include + +#include + +#include + +#include "tab_devinfo.h" +#include + +#include + +tabDevInfo::tabDevInfo(QPxSettings *iset, devlist *idev, QWidget *p, Qt::WindowFlags fl) + : QWidget(p,fl) +{ +#ifndef QT_NO_DEBUG + qDebug("STA: tabDevInfo()"); +#endif + settings = iset; + devices = idev; + + layout = new QHBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(3); + + layout_left = new QGridLayout(); + layout_left->setVerticalSpacing(1); + layout_left->setHorizontalSpacing(6); + layout->addLayout(layout_left); + + label_left = new QLabel(""+tr("Basic info")+"",this); + label_left->setFrameStyle(QFrame::Sunken | QFrame::StyledPanel); + label_left->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + label_left->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + layout_left->addWidget(label_left, 0, 0, 1, 2); + + pl_vendor = new QLabel(tr("Vendor:"), this); + pl_vendor->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_vendor, 1, 0); + l_vendor = new QLabel(this); + l_vendor->setMinimumWidth(120); + layout_left->addWidget(l_vendor, 1, 1); + + pl_model = new QLabel(tr("Model:"), this); + pl_model->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_model, 2, 0); + l_model = new QLabel(this); + l_model->setMinimumWidth(120); + layout_left->addWidget(l_model, 2, 1); + + pl_fw = new QLabel(tr("F/W:"), this); + pl_fw->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_fw, 3, 0); + l_fw = new QLabel(this); + l_fw->setMinimumWidth(120); + layout_left->addWidget(l_fw, 3, 1); + + pl_tla = new QLabel(tr("TLA#"), this); + pl_tla->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_tla, 4, 0); + l_tla = new QLabel(this); + l_tla->setMinimumWidth(120); + layout_left->addWidget(l_tla, 4, 1); + + pl_sn = new QLabel(tr("S/N:"), this); + pl_sn->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_sn, 5, 0); + l_sn = new QLabel(this); + l_sn->setMinimumWidth(120); + layout_left->addWidget(l_sn, 5, 1); + + pl_buf = new QLabel(tr("Buffer:"), this); + pl_buf->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_buf, 6, 0); + l_buf = new QLabel(this); + l_buf->setMinimumWidth(120); + layout_left->addWidget(l_buf, 6, 1); + + pl_iface = new QLabel(tr("IFace:"), this); + pl_iface->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_iface, 7, 0); + l_iface = new QLabel(this); + l_iface->setMinimumWidth(120); + layout_left->addWidget(l_iface, 7, 1); + + pl_loader = new QLabel(tr("Loader:"), this); + pl_loader->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_loader, 8, 0); + l_loader = new QLabel(this); + l_loader->setMinimumWidth(120); + layout_left->addWidget(l_loader, 8, 1); + + pl_rpc_phase = new QLabel(tr("RPC Phase:"), this); + pl_rpc_phase->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_rpc_phase, 9, 0); + l_rpc_phase = new QLabel(this); + l_rpc_phase->setMinimumWidth(120); + layout_left->addWidget(l_rpc_phase, 9, 1); + + pl_rpc_reg = new QLabel(tr("Region:"), this); + pl_rpc_reg->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_rpc_reg, 10, 0); + l_rpc_reg = new QLabel(this); + l_rpc_reg->setMinimumWidth(120); + layout_left->addWidget(l_rpc_reg, 10, 1); + + pl_rpc_ch = new QLabel(tr("Changes:"), this); + pl_rpc_ch->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_rpc_ch, 11, 0); + l_rpc_ch = new QLabel(this); + l_rpc_ch->setMinimumWidth(120); + layout_left->addWidget(l_rpc_ch, 11, 1); + + pl_rpc_rst = new QLabel(tr("Resets:"), this); + pl_rpc_rst->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_rpc_rst, 12, 0); + l_rpc_rst = new QLabel(this); + l_rpc_rst->setMinimumWidth(120); + layout_left->addWidget(l_rpc_rst, 12, 1); + +// Plextor Lifetime + hline0 = new QFrame(this); + hline0->setFrameStyle(QFrame::HLine | QFrame::Sunken); + layout_left->addWidget(hline0,13,0,1,2); + + pl_life_dn = new QLabel(tr("Discs loaded:"), this); + pl_life_dn->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_life_dn, 14, 0); + l_life_dn = new QLabel(this); +// l_life_dn->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_life_dn->setMinimumWidth(120); + layout_left->addWidget(l_life_dn, 14, 1); + + pl_life_cr = new QLabel(tr("CD read:"), this); + pl_life_cr->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_life_cr, 15, 0); + l_life_cr = new QLabel(this); +// l_life_cr->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_life_cr->setMinimumWidth(120); + layout_left->addWidget(l_life_cr, 15, 1); + + pl_life_cw = new QLabel(tr("CD write:"), this); + pl_life_cw->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_life_cw, 16, 0); + l_life_cw = new QLabel(this); +// l_life_cw->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_life_cw->setMinimumWidth(120); + layout_left->addWidget(l_life_cw, 16, 1); + + pl_life_dr = new QLabel(tr("DVD read:"), this); + pl_life_dr->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_life_dr, 17, 0); + l_life_dr = new QLabel(this); +// l_life_dr->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_life_dr->setMinimumWidth(120); + layout_left->addWidget(l_life_dr, 17, 1); + + pl_life_dw = new QLabel(tr("DVD write:"), this); + pl_life_dw->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_life_dw, 18, 0); + l_life_dw = new QLabel(this); +// l_life_dw->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_life_dw->setMinimumWidth(120); + layout_left->addWidget(l_life_dw, 18, 1); + + layout_left->setColumnStretch(0,2); + layout_left->setColumnStretch(1,3); + layout_left->setRowStretch(19,20); + + int hidx=0, vidx=0, hidx0 = 0, rows=0; + MediaCapWidget *cap; + + cap_grid = new QGridLayout; + layout->addLayout(cap_grid); + + lc_media = new QLabel(""+tr("Media R/W Capabilities")+"",this); + lc_media->setFrameStyle(QFrame::Sunken | QFrame::StyledPanel); + lc_media->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + lc_media->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + cap_grid->addWidget(lc_media, hidx, 0, 1, CAP_COLS); + hidx++; + vidx=0; hidx0=0; + rows = (sizeof(rw_capabilities) / sizeof (desc64) - 2 + CAP_COLS) / CAP_COLS ; + for (int idx=0; rw_capabilities[idx].id || strlen(rw_capabilities[idx].name); idx++) { + if (rw_capabilities[idx].id) { + cap = new MediaCapWidget(rw_capabilities[idx].name, 1, rw_capabilities[idx].id, this); + cap_media.append(cap); + cap_grid->addWidget(cap, hidx+hidx0, vidx); + } + hidx0 = (hidx0+1) % rows; + if (!hidx0) vidx++; + } + hidx+=rows; + + + lc_generic = new QLabel(""+tr("Generic Capabilities")+"",this); + lc_generic->setFrameStyle(QFrame::Sunken | QFrame::StyledPanel); + lc_generic->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + lc_generic->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + cap_grid->addWidget(lc_generic, hidx, 0, 1, CAP_COLS); + hidx++; + vidx=0; hidx0=0; + rows = (sizeof(capabilities) / sizeof (desc64) - 2 + CAP_COLS) / CAP_COLS ; + for (int idx=0; capabilities[idx].id || strlen(capabilities[idx].name); idx++) { + if (capabilities[idx].id) { + cap = new MediaCapWidget(capabilities[idx].name, 0, capabilities[idx].id , this); + cap_generic.append(cap); + cap_grid->addWidget(cap, hidx+hidx0, vidx); + } + hidx0 = (hidx0+1) % rows; + if (!hidx0) vidx++; + } + hidx+=rows; + + cap_grid->setRowStretch(hidx, 10); + cap_grid->setColumnStretch(0, 10); + cap_grid->setColumnStretch(1, 10); + cap_grid->setColumnStretch(2, 10); + + layout->setStretchFactor(layout_left, 1); + layout->setStretchFactor(cap_grid, 3); + + clear(); +#ifndef QT_NO_DEBUG + qDebug("END: tabDevInfo()"); +#endif +} + +tabDevInfo::~tabDevInfo() +{ +#ifndef QT_NO_DEBUG + qDebug("STA: ~tabDevInfo()"); +#endif + +#ifndef QT_NO_DEBUG + qDebug("END: ~tabDevInfo()"); +#endif +} + +void tabDevInfo::clear() +{ +#ifndef QT_NO_DEBUG + qDebug("STA: tabDevInfo::clear()"); +#endif + l_vendor->setText("-"); + l_model->setText("-"); + l_fw->setText("-"); + l_tla->setText("-"); + l_sn->setText("-"); + l_buf->setText("-"); + l_iface->setText("-"); + l_loader->setText("-"); + l_rpc_phase->setText("-"); + l_rpc_reg->setText("-"); + l_rpc_ch->setText("-"); + l_rpc_rst->setText("-"); + + for (int idx=0; idx < cap_media.size(); idx++) + cap_media[idx]->clear(); + for (int idx=0; idx < cap_generic.size(); idx++) + cap_generic[idx]->clear(); +#ifndef QT_NO_DEBUG + qDebug("END: tabDevInfo::clear()"); +#endif +} + +void tabDevInfo::selectDevice() +{ +#ifndef QT_NO_DEBUG + qDebug("STA: tabDevInfo::selectDevice()"); +#endif + clear(); + updateData(); + QObject::connect( devices->current(), SIGNAL(doneDInfo(int)), this, SLOT(updateData(int)) ); +#ifndef QT_NO_DEBUG + qDebug("END: tabDevInfo::selectDevice()"); +#endif +} + +void tabDevInfo::updateData(int xcode) +{ +#ifndef QT_NO_DEBUG + qDebug("STA: tabDevInfo::updateData()"); +#endif + const device *dev = devices->current(); + l_vendor->setText(dev->ven); + l_model->setText(dev->dev); + l_fw->setText(dev->fw); + l_tla->setText(dev->tla); + l_sn->setText(dev->sn); + l_buf->setText(dev->buf); + l_iface->setText(dev->iface); + l_loader->setText(dev->loader); + + l_rpc_phase->setText((dev->rpc_phase < 0) ? "-" : QString::number(dev->rpc_phase) ); + l_rpc_reg->setText((dev->rpc_reg < 0) ? ((dev->rpc_reg == -2) ? "not set" : "-") : QString::number(dev->rpc_reg) ); + l_rpc_ch->setText((dev->rpc_ch < 0) ? "-" : QString::number(dev->rpc_ch) ); + l_rpc_rst->setText((dev->rpc_rst < 0) ? "-" : QString::number(dev->rpc_rst) ); + + if (dev->life_dn>=0) { + l_life_dn->setNum(dev->life_dn); + l_life_cr->setText(dev->life_cr); + l_life_cw->setText(dev->life_cw); + l_life_dr->setText(dev->life_dr); + l_life_dw->setText(dev->life_dw); + } else { + l_life_dn->setText("-"); + l_life_cr->setText("-"); + l_life_cw->setText("-"); + l_life_dr->setText("-"); + l_life_dw->setText("-"); + } + + for (int idx=0; idx < cap_generic.size(); idx++) + cap_generic[idx]->setCap(dev->cap); + for (int idx=0; idx < cap_media.size(); idx++) { + cap_media[idx]->setR(dev->cap_rd); + cap_media[idx]->setW(dev->cap_wr); + } + + if (xcode) { + QMessageBox::warning(this, tr("Error"), tr("Error updating device info!")+"\n"+tr("qScan finished with non-zero exit code")); + } +#ifndef QT_NO_DEBUG + qDebug("END: tabDevInfo::updateData()"); +#endif +} + diff --git a/gui/src/tab_errc.cpp b/gui/src/tab_errc.cpp new file mode 100644 index 0000000..64b5f2f --- /dev/null +++ b/gui/src/tab_errc.cpp @@ -0,0 +1,276 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2012 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include + +#include +#include +#include +#include "tab_errc.h" +#include + +tabERRC::tabERRC(QPxSettings *iset, devlist *idev, QString iname, QWidget *p, Qt::WindowFlags fl) + : GraphTab(iset, idev, iname, TEST_ERRC, p, fl) +{ +#ifndef QT_NO_DEBUG + qDebug("STA: tabERRC()"); +#endif + xerrc = NULL; + + layout_info = new QVBoxLayout(infow); + layout_info->setMargin(0); + layout_info->setSpacing(3); + +#ifdef __LEGEND_SHOW_SPEED + pl_spd = new ColorLabel(QColor(Qt::black),"Speed", 0, infow); + pl_spd->setMinimumSize(100,20); + layout_info->addWidget(pl_spd); +#endif + pl_e0 = new ColorLabel(QColor(Qt::black),"BLER/PI8", 0, infow); +// pl_e0->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); +// pl_e0->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + layout_info->addWidget(pl_e0); + + l_e0t = new QLabel(infow); + l_e0t->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_e0t->setMinimumHeight(22); + layout_info->addWidget(l_e0t); + + l_e0m = new QLabel(infow); + l_e0m->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_e0m->setMinimumHeight(22); + layout_info->addWidget(l_e0m); + + l_e0a = new QLabel(infow); + l_e0a->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_e0a->setMinimumHeight(22); + layout_info->addWidget(l_e0a); + + + pl_e1 = new ColorLabel(QColor(Qt::black),"E22/PIF", 0, infow); +// pl_e1->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); +// pl_e1->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + layout_info->addWidget(pl_e1); + + l_e1t = new QLabel(infow); + l_e1t->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_e1t->setMinimumHeight(22); + layout_info->addWidget(l_e1t); + + l_e1m = new QLabel(infow); + l_e1m->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_e1m->setMinimumHeight(22); + layout_info->addWidget(l_e1m); + + l_e1a = new QLabel(infow); + l_e1a->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_e1a->setMinimumHeight(22); + layout_info->addWidget(l_e1a); + + + pl_e2 = new ColorLabel(QColor(Qt::black),"E32/POF", 0, infow); +// pl_e2->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); +// pl_e2->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + layout_info->addWidget(pl_e2); + + l_e2t = new QLabel(infow); + l_e2t->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_e2t->setMinimumHeight(22); + layout_info->addWidget(l_e2t); + + l_e2m = new QLabel(infow); + l_e2m->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_e2m->setMinimumHeight(22); + layout_info->addWidget(l_e2m); + + l_e2a = new QLabel(infow); + l_e2a->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_e2a->setMinimumHeight(22); + layout_info->addWidget(l_e2a); + + layout_info->addStretch(10); + + pb_xerrc = new QPushButton("Detailed"); + layout_info->addWidget(pb_xerrc); + + QObject::connect(pb_xerrc, SIGNAL(clicked()), this, SLOT(toggleXErrc())); + + clear(); +#ifndef QT_NO_DEBUG + qDebug("END: tabERRC()"); +#endif +} + +tabERRC::~tabERRC() +{ +#ifndef QT_NO_DEBUG + qDebug("STA: ~tabERRC()"); + qDebug("END: ~tabERRC()"); +#endif +} +/* +void tabERRC::clear() +{ + qDebug("tabERRC::clear()"); +} +*/ +void tabERRC::selectDevice() +{ +#ifndef QT_NO_DEBUG + qDebug("tabERRC::selectDevice()"); +#endif + device *dev = devices->current(); + updateAll(); +// GraphTab::updateLast((int)time, NULL, 1); +// updateSummary(dev); + + // QObject::connect( devices->current(), SIGNAL(block_ERRC()), this, SLOT(update()) ); + QObject::connect( dev, SIGNAL(doneMInfo(int)), this, SLOT(updateAll()) ); + QObject::connect( dev, SIGNAL(block_ERRC()), this, SLOT(updateLast()) ); +} + +void tabERRC::updateAll() +{ + bool show_e2 = true; + device *dev = devices->current(); + GraphTab::updateLast((int)(dev->testData.errc_time), NULL, 1); + updateSummary(dev); + if (dev->media.type.startsWith("CD")) { + pl_e0->setText("BLER"); + pl_e1->setText("E22"); + pl_e2->setText("E32"); + } else if (dev->media.type.startsWith("DVD")) { + pl_e0->setText("PI8"); + pl_e1->setText("PIF"); + pl_e2->setText("POF"); + } else if (dev->media.type.startsWith("BD")) { + pl_e0->setText("LDC"); + pl_e1->setText("BIS"); + show_e2 = false; + } else { + pl_e0->setText("BLER/PI8"); + pl_e1->setText("E22/PIF"); + pl_e2->setText("E32/POF"); + } + pl_e2->setVisible(show_e2); + l_e2t->setVisible(show_e2); + l_e2m->setVisible(show_e2); + l_e2a->setVisible(show_e2); + + updateLegend(); + if (xerrc) xerrc->updateAll(); +} + +void tabERRC::updateLegend() +{ + device *dev = devices->current(); + if (dev->media.type.startsWith("CD")) { + pl_e0->setColor( *(settings->col_errc.cd.bler)); + pl_e1->setColor( *(settings->col_errc.cd.e22)); + pl_e2->setColor( *(settings->col_errc.cd.e32)); + } else if (dev->media.type.startsWith("DVD")) { + pl_e0->setColor( *(settings->col_errc.dvd.pi8)); + pl_e1->setColor( *(settings->col_errc.dvd.pif)); + pl_e2->setColor( *(settings->col_errc.dvd.pof)); + } else if (dev->media.type.startsWith("BD")) { + pl_e0->setColor( *(settings->col_errc.bd.ldc)); + pl_e1->setColor( *(settings->col_errc.bd.bis)); + } else { + pl_e0->setColor( QColor(Qt::black) ); + pl_e1->setColor( QColor(Qt::black) ); + pl_e2->setColor( QColor(Qt::black) ); + } +} + +void tabERRC::updateGraph() +{ + graph->update(); +} + +void tabERRC::updateLast() +{ + bool show; + device *dev = devices->current(); + int time = (int) (dev->testData.errc_time); + GraphTab::updateLast(time, &show); + if (!show) return; + updateSummary(dev); + + if (xerrc && xerrc->isVisible()) xerrc->updateAll(); +} + +void tabERRC::updateSummary(device *dev) +{ + if (!dev->testData.errc.size()) { + l_e0t->clear(); l_e0m->clear(); l_e0a->clear(); + l_e1t->clear(); l_e1m->clear(); l_e1a->clear(); + l_e2t->clear(); l_e2m->clear(); l_e2a->clear(); + return; + } + if (dev->media.type.startsWith("CD")) { + l_e0t->setText( QString::number(dev->testData.errcTOT.cd.bler) ); + l_e0m->setNum( dev->testData.errcMAX.cd.bler ); + l_e0a->setText( QString::number(dev->testData.errcAVG.cd.bler, 'f', 2) ); + + l_e1t->setText( QString::number(dev->testData.errcTOT.cd.e22) ); + l_e1m->setNum( dev->testData.errcMAX.cd.e22 ); + l_e1a->setText( QString::number(dev->testData.errcAVG.cd.e22, 'f', 2) ); + + l_e2t->setText( QString::number(dev->testData.errcTOT.cd.e32) ); + l_e2m->setNum( dev->testData.errcMAX.cd.e32 ); + l_e2a->setText( QString::number(dev->testData.errcAVG.cd.e32, 'f', 2) ); + } else if (dev->media.type.startsWith("DVD")) { + l_e0t->setText( QString::number(dev->testData.errcTOT.dvd.pi8) ); + l_e0m->setNum( dev->testData.errcMAX.dvd.pi8 ); + l_e0a->setText( QString::number(dev->testData.errcAVG.dvd.pi8, 'f', 2) ); + + l_e1t->setText( QString::number(dev->testData.errcTOT.dvd.pif) ); + l_e1m->setNum( dev->testData.errcMAX.dvd.pif ); + l_e1a->setText( QString::number(dev->testData.errcAVG.dvd.pif, 'f', 2) ); + + l_e2t->setText( QString::number(dev->testData.errcTOT.dvd.pof) ); + l_e2m->setNum( dev->testData.errcMAX.dvd.pof ); + l_e2a->setText( QString::number(dev->testData.errcAVG.dvd.pof, 'f', 2) ); + } else if (dev->media.type.startsWith("BD")) { + l_e0t->setText( QString::number(dev->testData.errcTOT.bd.ldc) ); + l_e0m->setNum( dev->testData.errcMAX.bd.ldc ); + l_e0a->setText( QString::number(dev->testData.errcAVG.bd.ldc, 'f', 2) ); + + l_e1t->setText( QString::number(dev->testData.errcTOT.bd.bis) ); + l_e1m->setNum( dev->testData.errcMAX.bd.bis ); + l_e1a->setText( QString::number(dev->testData.errcAVG.bd.bis, 'f', 2) ); + } +} + +void tabERRC::toggleXErrc() +{ + if (!xerrc) { + xerrc = new ErrcDetailedDialog(settings,devices,this); + QObject::connect(xerrc, SIGNAL(closed()), this, SLOT(XerrcClosed())); + xerrc->show(); + xerrc->updateAll(); + } else { + xerrc->hide(); + delete xerrc; + xerrc = NULL; + } +} + +void tabERRC::XerrcClosed() +{ + xerrc->disconnect(); + xerrc->deleteLater(); + xerrc = NULL; +} + diff --git a/gui/src/tab_fete.cpp b/gui/src/tab_fete.cpp new file mode 100644 index 0000000..de94bcd --- /dev/null +++ b/gui/src/tab_fete.cpp @@ -0,0 +1,138 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include + +#include +#include +#include +#include "tab_fete.h" +#include + +tabFETE::tabFETE(QPxSettings *iset, devlist *idev, QString iname, QWidget *p, Qt::WindowFlags fl) + : GraphTab(iset, idev, iname, TEST_FT, p, fl) +{ +#ifndef QT_NO_DEBUG + qDebug("STA: tabFETE()"); +#endif + layout_info = new QVBoxLayout(infow); + layout_info->setMargin(0); + layout_info->setSpacing(3); + +#ifdef __LEGEND_SHOW_SPEED + pl_spd = new ColorLabel(QColor(Qt::black),"Speed", 0, infow); + pl_spd->setMinimumSize(100,20); + layout_info->addWidget(pl_spd); +#endif + pl_fmax = new ColorLabel(settings->col_fe, tr("FE max"), 0, infow); +// pl_fmax->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); +// pl_fmax->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + pl_fmax->setMinimumSize(80,20); + layout_info->addWidget(pl_fmax); + l_fmax = new QLabel(infow); + l_fmax->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_fmax->setMinimumSize(80,22); + layout_info->addWidget(l_fmax); + + + pl_tmax = new ColorLabel(settings->col_te,tr("TE max"), 0, infow); +// pl_tmax->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); +// pl_tmax->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + pl_tmax->setMinimumSize(80,20); + layout_info->addWidget(pl_tmax); + l_tmax = new QLabel(infow); + l_tmax->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_tmax->setMinimumSize(80,22); + layout_info->addWidget(l_tmax); + + + layout_info->addStretch(10); + + clear(); +#ifndef QT_NO_DEBUG + qDebug("END: tabFETE()"); +#endif +} + +tabFETE::~tabFETE() +{ +#ifndef QT_NO_DEBUG + qDebug("STA: ~tabFETE()"); + qDebug("END: ~tabFETE()"); +#endif +} +/* +void tabFETE::clear() +{ + qDebug("tabFETE::clear()"); + +} +*/ +void tabFETE::selectDevice() +{ +#ifndef QT_NO_DEBUG + qDebug("tabFETE::selectDevice()"); +#endif + device *dev = devices->current(); + GraphTab::updateLast((int)dev->testData.ft_time, NULL, 1); + updateSummary(); + + QObject::connect( dev, SIGNAL(doneMInfo(int)), this, SLOT(updateAll()) ); + QObject::connect( dev, SIGNAL(block_FT()), this, SLOT(updateLast()) ); +} + +void tabFETE::updateLast() +{ + bool show; + device *dev = devices->current(); + GraphTab::updateLast((int)dev->testData.ft_time, &show); + if (!show) return; + updateSummary(); +} + +void tabFETE::updateAll() +{ + GraphTab::updateLast((int)devices->current()->testData.ft_time, NULL, 1); + updateSummary(); +} + +void tabFETE::updateLegend() +{ + pl_fmax->setColor(settings->col_fe); + pl_tmax->setColor(settings->col_te); +} + +void tabFETE::updateGraph() +{ + graph->update(); +} + +void tabFETE::updateSummary() +{ + device *dev = devices->current(); + if (!dev->testData.jb.size()) { + l_fmax->clear(); l_tmax->clear(); +#ifdef FT_AVG + l_favg->clear(); l_tavg->clear(); +#endif + return; + } +// setting values... + l_fmax->setText(QString::number(dev->testData.ftMAX.fe,'f',2)); + l_tmax->setText(QString::number(dev->testData.ftMAX.te,'f',2)); +#ifdef FT_AVG + l_favg->setText(QString::number(dev->testData.ftMAX.favg,'f',2)); + l_tavg->setText(QString::number(dev->testData.ftMAX.tavg,'f',2)); +#endif +} + diff --git a/gui/src/tab_jb.cpp b/gui/src/tab_jb.cpp new file mode 100644 index 0000000..00c9fae --- /dev/null +++ b/gui/src/tab_jb.cpp @@ -0,0 +1,143 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include + +#include +#include +#include +#include "tab_jb.h" +#include + +tabJB::tabJB(QPxSettings *iset, devlist *idev, QString iname, QWidget *p, Qt::WindowFlags fl) + : GraphTab(iset, idev, iname, TEST_JB, p, fl) +{ +#ifndef QT_NO_DEBUG + qDebug("STA: tabJB()"); +#endif + layout_info = new QVBoxLayout(infow); + layout_info->setMargin(0); + layout_info->setSpacing(3); + +#ifdef __LEGEND_SHOW_SPEED + pl_spd = new ColorLabel(QColor(Qt::black),"Speed", 0, infow); + pl_spd->setMinimumSize(100,20); + layout_info->addWidget(pl_spd); +#endif + pl_jitter = new ColorLabel(settings->col_jitter, tr("Jitter"), 0, infow); +// pl_jitter->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); +// pl_jitter->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + pl_jitter->setMinimumSize(100,20); + layout_info->addWidget(pl_jitter); + + l_jmax = new QLabel(infow); + l_jmax->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_jmax->setMinimumSize(80,22); + layout_info->addWidget(l_jmax); + + l_jmin = new QLabel(infow); + l_jmin->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_jmin->setMinimumSize(80,22); + layout_info->addWidget(l_jmin); + + pl_asymm = new ColorLabel(settings->col_asymm, tr("Asymmetry"), 0, infow); +// pl_asymm->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); +// pl_asymm->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + pl_asymm->setMinimumSize(100,20); + layout_info->addWidget(pl_asymm); + + l_amax = new QLabel(infow); + l_amax->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_amax->setMinimumSize(80,22); + layout_info->addWidget(l_amax); + + l_amin = new QLabel(infow); + l_amin->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_amin->setMinimumSize(80,22); + layout_info->addWidget(l_amin); + + layout_info->addStretch(10); + + clear(); +#ifndef QT_NO_DEBUG + qDebug("END: tabJB()"); +#endif +} + +tabJB::~tabJB() +{ +#ifndef QT_NO_DEBUG + qDebug("STA: ~tabJB()"); + qDebug("END: ~tabJB()"); +#endif +} +/* +void tabJB::clear() +{ + qDebug("tabJB::clear()"); +} +*/ +void tabJB::selectDevice() +{ +#ifndef QT_NO_DEBUG + qDebug("tabJB::selectDevice()"); +#endif + device *dev = devices->current(); + GraphTab::updateLast((int)dev->testData.jb_time, NULL, 1); + updateSummary(); + + QObject::connect( dev, SIGNAL(doneMInfo(int)), this, SLOT(updateLast()) ); + QObject::connect( dev, SIGNAL(block_JB()), this, SLOT(updateLast()) ); +} +void tabJB::updateLast() +{ + bool show; + device *dev = devices->current(); + GraphTab::updateLast((int)dev->testData.jb_time, &show); + if (!show) return; + updateSummary(); +} + +void tabJB::updateAll() +{ + GraphTab::updateLast((int)devices->current()->testData.jb_time, NULL, 1); + updateSummary(); +} + +void tabJB::updateLegend() +{ + pl_jitter->setColor(settings->col_jitter); + pl_asymm->setColor(settings->col_asymm); +} + +void tabJB::updateGraph() +{ + graph->update(); +} + +void tabJB::updateSummary() +{ + device *dev = devices->current(); + if (!dev->testData.jb.size()) { + l_jmax->clear(); l_jmin->clear(); + l_amax->clear(); l_amin->clear(); + return; + } +// setting values... + l_jmax->setText(QString::number(dev->testData.jbMM.jmax,'f',2)); + l_jmin->setText(QString::number(dev->testData.jbMM.jmin,'f',2)); + + l_amax->setText(QString::number(dev->testData.jbMM.bmax,'f',2)); + l_amin->setText(QString::number(dev->testData.jbMM.bmin,'f',2)); +} + diff --git a/gui/src/tab_mediainfo.cpp b/gui/src/tab_mediainfo.cpp new file mode 100644 index 0000000..1ee3177 --- /dev/null +++ b/gui/src/tab_mediainfo.cpp @@ -0,0 +1,346 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2012 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "tab_mediainfo.h" +#include + +tabMediaInfo::tabMediaInfo(QPxSettings *iset, devlist *idev, QWidget *p, Qt::WindowFlags fl) + : QWidget(p,fl) +{ +#ifndef QT_NO_DEBUG + qDebug("STA: tabMediaInfo()"); +#endif + settings = iset; + devices = idev; + + layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(3); + + layout_info = new QGridLayout; + layout_info->setVerticalSpacing(1); + layout_info->setHorizontalSpacing(6); + layout->addLayout(layout_info); + + pl_type = new QLabel(tr("Type:"), this); + layout_info->addWidget(pl_type,0,0); + l_type = new QLabel(this); + layout_info->addWidget(l_type,0,1); + pl_book = new QLabel(tr("Disc Category:"), this); + layout_info->addWidget(pl_book,1,0); + l_book = new QLabel(this); + layout_info->addWidget(l_book,1,1); + pl_mid = new QLabel(tr("Media ID:"), this); + layout_info->addWidget(pl_mid,2,0); + l_mid = new QLabel(this); + layout_info->addWidget(l_mid,2,1); + pl_layers = new QLabel(tr("Layers:"), this); + layout_info->addWidget(pl_layers,3,0); + l_layers = new QLabel(this); + layout_info->addWidget(l_layers,3,1); + pl_erasable = new QLabel(tr("Erasable:"), this); + layout_info->addWidget(pl_erasable,4,0); + l_erasable = new QLabel(this); + layout_info->addWidget(l_erasable,4,1); + pl_prot = new QLabel(tr("Protection:"), this); + layout_info->addWidget(pl_prot,5,0); + l_prot = new QLabel(this); + layout_info->addWidget(l_prot,5,1); + pl_regions = new QLabel(tr("Regions:"), this); + layout_info->addWidget(pl_regions,6,0); + l_regions = new QLabel(this); + layout_info->addWidget(l_regions,6,1); + pl_writer = new QLabel(tr("Written on:"), this); + layout_info->addWidget(pl_writer, 7,0); + l_writer = new QLabel(this); + layout_info->addWidget(l_writer, 7,1); + + pl_cread = new QLabel(tr("Readable:"), this); + layout_info->addWidget(pl_cread,0,2); + pl_cfree = new QLabel(tr("Free:"), this); + layout_info->addWidget(pl_cfree,1,2); + pl_ctot = new QLabel(tr("Total:"), this); + layout_info->addWidget(pl_ctot,2,2); + + l_cap = new QGridLayout(); + layout_info->addLayout(l_cap, 0,3,3,1); + + l_creads = new QLabel(this); +// l_creads->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + l_cap->addWidget(l_creads,0,0); + l_creadm = new QLabel(this); +// l_creadm->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + l_cap->addWidget(l_creadm,0,1); + l_creadmsf = new QLabel(this); + l_creadmsf->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + l_cap->addWidget(l_creadmsf,0,2); + l_cfrees = new QLabel(this); +// l_cfrees->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + l_cap->addWidget(l_cfrees,1,0); + l_cfreem = new QLabel(this); +// l_cfreem->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + l_cap->addWidget(l_cfreem,1,1); + l_cfreemsf = new QLabel(this); + l_cfreemsf->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + l_cap->addWidget(l_cfreemsf,1,2); + l_ctots = new QLabel(this); +// l_ctots->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + l_cap->addWidget(l_ctots,2,0); + l_ctotm = new QLabel(this); +// l_ctotm->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + l_cap->addWidget(l_ctotm,2,1); + l_ctotmsf = new QLabel(this); + l_ctotmsf->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + l_cap->addWidget(l_ctotmsf,2,2); + + + pl_dstate = new QLabel(tr("Disc state:"), this); + layout_info->addWidget(pl_dstate,3,2); + l_dstate = new QLabel(this); + layout_info->addWidget(l_dstate,3,3); + pl_sstate = new QLabel(tr("Session state:"), this); + layout_info->addWidget(pl_sstate,4,2); + l_sstate = new QLabel(this); + layout_info->addWidget(l_sstate,4,3); + pl_rspeeds = new QLabel(tr("Read speeds:"), this); + layout_info->addWidget(pl_rspeeds,5,2); + l_rspeeds = new QLabel(this); + layout_info->addWidget(l_rspeeds,5,3); + pl_wspeedsd = new QLabel(tr("Write speeds (Device):"), this); + layout_info->addWidget(pl_wspeedsd,6,2); + l_wspeedsd = new QLabel(this); + layout_info->addWidget(l_wspeedsd,6,3); + pl_wspeedsm = new QLabel(tr("Write speeds (Media):"), this); + layout_info->addWidget(pl_wspeedsm,7,2); + l_wspeedsm = new QLabel(this); + layout_info->addWidget(l_wspeedsm,7,3); + + layout_info->setColumnStretch(0,1); + layout_info->setColumnStretch(1,3); + layout_info->setColumnStretch(2,1); + layout_info->setColumnStretch(3,3); + + hline0 = new QFrame(this); + hline0->setFrameStyle(QFrame::HLine | QFrame::Sunken); + layout->addWidget(hline0); + +#ifdef SHOW_SPEEDS + pl_rd_max = new QLabel(tr("RD max:"), this); + pl_rd_max->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_rd_max, 9, 0); + l_rd_max = new QLabel(this); + l_rd_max->setMinimumWidth(120); + layout_left->addWidget(l_rd_max, 9, 1, 1, 2); + + pl_rd_lst = new QLabel(tr("RD lst:"), this); + pl_rd_lst->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_rd_lst, 10, 0); + c_rd_lst = new QComboBox(this); + c_rd_lst->setMinimumWidth(120); + layout_left->addWidget(c_rd_lst, 10, 1, 1, 2); + + pl_wr_max = new QLabel(tr("WR max:"), this); + pl_wr_max->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_wr_max, 11, 0); + l_wr_max = new QLabel(this); + l_wr_max->setMinimumWidth(120); + layout_left->addWidget(l_wr_max, 11, 1, 1, 2); + + pl_wr_lst = new QLabel(tr("WR lst:"), this); + pl_wr_lst->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout_left->addWidget(pl_wr_lst, 12, 0); + c_wr_lst = new QComboBox(this); + c_wr_lst->setMinimumWidth(120); + layout_left->addWidget(c_wr_lst, 12, 1, 1, 2); +#endif + +#ifdef MINFO_TREE + QFont mifont; + + minfo = new QTreeWidget(this); + minfo->header()->hide(); + minfo->setRootIsDecorated(false); + minfo->setColumnCount(1); + mifont = minfo->font(); + mifont.setFamily("Monospace"); +// mifont.setBold(false); +// mifont.setItalic(false); + minfo->setFont(mifont); + layout->addWidget(minfo); + + minfo_detail = new QTreeWidgetItem(minfo, QStringList() << tr("Detailed info") << ""); + minfo_detail->setIcon(0,QIcon(":images/info.png")); + minfo_detail->setExpanded(true); + minfo->addTopLevelItem(minfo_detail); +#else + layout->addStretch(10); +#endif + clear(); +#ifndef QT_NO_DEBUG + qDebug("END: tabMediaInfo()"); +#endif +} + +tabMediaInfo::~tabMediaInfo() +{ +#ifndef QT_NO_DEBUG + qDebug("STA: ~tabMediaInfo()"); +#endif +#ifndef QT_NO_DEBUG + qDebug("END: ~tabMediaInfo()"); +#endif +} + +void tabMediaInfo::clear() +{ +#ifndef QT_NO_DEBUG + qDebug("tabMediaInfo::clear()"); +#endif + l_type->setText("-"); + l_book->setText("-"); + l_mid->setText("-"); + l_layers->setText("-"); + l_erasable->setText("-"); + l_prot->setText("-"); + l_regions->setText("-"); + l_writer->setText("-"); + + l_dstate->setText("-"); + l_sstate->setText("-"); + l_creads->setText("-"); + l_creadm->clear(); + l_creadmsf->clear(); + l_cfrees->setText("-"); + l_cfreem->clear(); + l_cfreemsf->clear(); + l_ctots->setText("-"); + l_ctotm->clear(); + l_ctotmsf->clear(); + l_rspeeds->setText("-"); + l_wspeedsd->setText("-"); + l_wspeedsm->setText("-"); + +#ifdef SHOW_SPEEDS + l_rd_max->setText("-"); + c_rd_lst->clear(); + l_wr_max->setText("-"); + c_wr_lst->clear(); +#endif + +#ifdef MINFO_TREE + minfo_detail->takeChildren(); +#endif +} + +void tabMediaInfo::selectDevice() +{ +#ifndef QT_NO_DEBUG + qDebug("STA: tabMediaInfo::selectDevice()"); + qDebug() << "SIGNAL sender: " << sender(); +#endif + clear(); + + updateData(); + QObject::connect( devices->current(), SIGNAL(doneMInfo(int)), this, SLOT(updateData(int)) ); +#ifndef QT_NO_DEBUG + qDebug("END: tabMediaInfo::selectDevice()"); +#endif +} + +void tabMediaInfo::updateData(int xcode) +{ +#ifndef QT_NO_DEBUG + qDebug("STA: tabMediaInfo::updateData()"); +#endif + const device *dev = devices->current(); + bool showMSF = dev->media.type.startsWith("CD"); + + l_type->setText(dev->media.type); + l_book->setText(dev->media.category); + //l_layers->setNum(dev->media.layers); + l_layers->setText(dev->media.layers); + l_prot->setText(dev->media.prot); + l_regions->setText(dev->media.regions); + l_erasable->setText( dev->media.erasable); //? "Yes" : "No"); + + l_dstate->setText(dev->media.dstate); + l_sstate->setText(dev->media.sstate); + + if (dev->media.creads) { + l_creads->setText(QString::number(dev->media.creads)); + l_creadm->setText(QString("%1 MB").arg(dev->media.creadm)); + l_creadmsf->setText(dev->media.creadmsf); + } else { + l_creads->setText("-"); + l_creadm->clear(); + l_creadmsf->clear(); + } + if (dev->media.cfrees) { + l_cfrees->setText(QString::number(dev->media.cfrees)); + l_cfreem->setText(QString("%1 MB").arg(dev->media.cfreem)); + l_cfreemsf->setText(dev->media.cfreemsf); + } else { + l_cfrees->setText("-"); + l_cfreem->clear(); + l_cfreemsf->clear(); + } + if (dev->media.ctots) { + l_ctots->setText(QString::number(dev->media.ctots)); + l_ctotm->setText(QString("%1 MB").arg(dev->media.ctotm)); + l_ctotmsf->setText(dev->media.ctotmsf); + } else { + l_ctots->setText("-"); + l_ctotm->clear(); + l_ctotmsf->clear(); + } + l_creadmsf->setVisible(showMSF); + l_cfreemsf->setVisible(showMSF); + l_ctotmsf->setVisible(showMSF); + + l_mid->setText(dev->media.mid); + if (!dev->media.rspeeds.size()) + l_rspeeds->setText("-"); + else + l_rspeeds->setText(dev->media.rspeeds.join(" - ")); + if (!dev->media.wspeedsd.size()) + l_wspeedsd->setText("-"); + else + l_wspeedsd->setText(dev->media.wspeedsd.join(" - ")); + if (!dev->media.wspeedsm.size()) + l_wspeedsm->setText("-"); + else + l_wspeedsm->setText(dev->media.wspeedsm.join(" - ")); + + l_writer->setText(dev->media.writer); + +#ifdef MINFO_TREE + minfo_detail->addChildren( dev->info_media); +#endif + if (xcode) { + QMessageBox::warning(this, tr("Error"), tr("Error updating media info!")+"\n"+tr("qScan finished with non-zero exit code")); + } +#ifndef QT_NO_DEBUG + qDebug("END: tabMediaInfo::updateData()"); +#endif +} + diff --git a/gui/src/tab_ta.cpp b/gui/src/tab_ta.cpp new file mode 100644 index 0000000..20acde1 --- /dev/null +++ b/gui/src/tab_ta.cpp @@ -0,0 +1,201 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include "tab_ta.h" +#include + +#include + +tabTA::tabTA(QPxSettings *iset, devlist *idev, QString iname, QWidget *p, Qt::WindowFlags fl) + : QWidget(p, fl) +{ +#ifndef QT_NO_DEBUG + qDebug("STA: tabTA()"); +#endif + settings = iset; + devices = idev; + name = iname; +// prevTvalid = 0; + gettimeofday(&prevT, NULL); + settings->loadScale(name); + + layout = new QHBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(3); + + layoutl = new QVBoxLayout(); + layoutl->setMargin(0); + layoutl->setSpacing(3); + layout->addLayout(layoutl); + + layoutg = new QVBoxLayout(); + layoutg->setMargin(0); + layoutg->setSpacing(3); + layout->addLayout(layoutg); + +// Layer selection + box_layer = new QGroupBox(tr("Layer"),this); + layoutl->addWidget(box_layer); + lay_layer = new QVBoxLayout(box_layer); + + grp_layer = new QButtonGroup(box_layer); + layer0 = new QRadioButton(tr("Layer")+" 0", box_layer); layer0->setChecked(true); + lay_layer->addWidget(layer0); + grp_layer->addButton(layer0, 0); + layer1 = new QRadioButton(tr("Layer")+" 1", box_layer); + lay_layer->addWidget(layer1); + grp_layer->addButton(layer1, 1); + +// Zone Selection + box_zone = new QGroupBox(tr("Zone"),this); + layoutl->addWidget(box_zone); + lay_zone = new QVBoxLayout(box_zone); + + grp_zone = new QButtonGroup(box_zone); + zone0 = new QRadioButton(tr("Inner"), box_zone); zone0->setChecked(true); + lay_zone->addWidget(zone0); + grp_zone->addButton(zone0, 0); + zone1 = new QRadioButton(tr("Middle"), box_zone); + lay_zone->addWidget(zone1); + grp_zone->addButton(zone1, 1); + zone2 = new QRadioButton(tr("Outer"), box_zone); + lay_zone->addWidget(zone2); + grp_zone->addButton(zone2, 2); + +// Legend +#ifdef __LEGEND_SHOW_TA + cl_pit = new ColorLabel(settings->col_tapit, tr("Pit"), 0, this); + cl_pit->setMinimumSize(100,20); + layoutl->addWidget(cl_pit); + + cl_land = new ColorLabel(settings->col_taland, tr("Land"), 0, this); + cl_land->setMinimumSize(100,20); + layoutl->addWidget(cl_land); +#endif + + layoutl->addStretch(10); + +// Test time + grp_time = new QGroupBox(tr("Time"), this); + grp_time->setMinimumWidth(100); + layoutl->addWidget(grp_time); + + layoutt = new QVBoxLayout(grp_time); + layoutt->setMargin(3); + layoutt->setSpacing(0); + + ltime = new QLabel("0:00", grp_time); + ltime->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + ltime->setMinimumHeight(22); + QFont tfont = ltime->font(); + tfont.setFamily("Monospace"); + ltime->setFont( tfont ); + + layoutt->addWidget(ltime); + +// Graphs... + graphPit = new QPxGraph(iset, idev, name, TEST_TA, this); + graphPit->setModeTA(0); + layoutg->addWidget(graphPit); + + graphLand = new QPxGraph(iset, idev, name, TEST_TA, this); + graphLand->setModeTA(1); + layoutg->addWidget(graphLand); + + connect(grp_layer, SIGNAL(buttonClicked(int)), graphPit, SLOT(setLayerTA(int))); + connect(grp_layer, SIGNAL(buttonClicked(int)), graphLand, SLOT(setLayerTA(int))); + + connect(grp_zone , SIGNAL(buttonClicked(int)), graphPit, SLOT(setZoneTA(int))); + connect(grp_zone , SIGNAL(buttonClicked(int)), graphLand, SLOT(setZoneTA(int))); + + clear(); +#ifndef QT_NO_DEBUG + qDebug("END: tabTA()"); +#endif +} + +tabTA::~tabTA() +{ +#ifndef QT_NO_DEBUG + qDebug("STA: ~tabTA()"); + qDebug("END: ~tabTA()"); +#endif +} + +void tabTA::clear() +{ +#ifndef QT_NO_DEBUG + qDebug("tabTA::clear()"); +#endif +} + + +void tabTA::selectDevice() +{ +#ifndef QT_NO_DEBUG + qDebug("tabTA::selectDevice()"); +#endif + device *dev = devices->current(); + graphPit->update(); + graphLand->update(); + QObject::connect( dev, SIGNAL(doneMInfo(int)), this, SLOT(updateAll()) ); + QObject::connect( dev, SIGNAL(block_TA()), this, SLOT(updateLast()) ); +} + +void tabTA::updateAll() +{ + if (devices->current()->media.layers.toInt() < 2) { + layer0->setChecked(true); + layer1->setEnabled(false); + } else { + layer1->setEnabled(true); + } + updateLast(); +} + +void tabTA::updateLast() +{ + graphPit->update(); + graphLand->update(); +} + +void tabTA::reconfig() +{ + graphPit->update(); + graphLand->update(); +} + +void tabTA::drawGraph(QImage& img, device *dev, int ttype, int eflags) +{ + int w = img.width(); + int h = img.height(); + QSize s(w,h); + QRect r(0, 0, w, h); + QPainter p(&img); + + if (eflags & 1) { + graphLand->drawGraph(&p, s, dev, ttype, r, eflags & (~1), FORCE_REPAINT); + } else { + graphPit->drawGraph(&p, s, dev, ttype, r, eflags & (~1), FORCE_REPAINT); + } +} + diff --git a/gui/src/tab_transfer.cpp b/gui/src/tab_transfer.cpp new file mode 100644 index 0000000..2c09649 --- /dev/null +++ b/gui/src/tab_transfer.cpp @@ -0,0 +1,182 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2008-2010 Gennady "ShultZ" Kozlov + * + * 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 +#include + +#include +#include +#include "tab_transfer.h" +#include + +tabTransfer::tabTransfer(QPxSettings *iset, devlist *idev, QString iname, bool irw, + QWidget *p, Qt::WindowFlags fl) + : GraphTab(iset, idev, iname, irw ? TEST_WT : TEST_RT, p, fl) +{ +#ifndef QT_NO_DEBUG + qDebug("STA: tabTransfer()"); +#endif + rw = irw; + layout_info = new QVBoxLayout(infow); + layout_info->setMargin(0); + layout_info->setSpacing(3); + + pl_sta = new QLabel(tr("Start"), infow); + pl_sta->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + pl_sta->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + layout_info->addWidget(pl_sta); + l_sta_x = new QLabel(infow); + l_sta_x->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_sta_x->setMinimumHeight(22); + layout_info->addWidget(l_sta_x); + l_sta_kb = new QLabel(infow); + l_sta_kb->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_sta_kb->setMinimumHeight(22); + layout_info->addWidget(l_sta_kb); + + pl_end = new QLabel(tr("End"), infow); + pl_end->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + pl_end->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + layout_info->addWidget(pl_end); + l_end_x = new QLabel(infow); + l_end_x->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_end_x->setMinimumHeight(22); + layout_info->addWidget(l_end_x); + l_end_kb = new QLabel(infow); + l_end_kb->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_end_kb->setMinimumHeight(22); + layout_info->addWidget(l_end_kb); + + pl_avg = new QLabel(tr("Average"), infow); + pl_avg->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + pl_avg->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + layout_info->addWidget(pl_avg); + l_avg_x = new QLabel(infow); + l_avg_x->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_avg_x->setMinimumHeight(22); + layout_info->addWidget(l_avg_x); + l_avg_kb = new QLabel(infow); + l_avg_kb->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + l_avg_kb->setMinimumHeight(22); + layout_info->addWidget(l_avg_kb); + +#ifdef __LEGEND_SHOW_SPEED + pl_spd = new ColorLabel(irw ? settings->col_wspeed : settings->col_rspeed, + irw ? tr("Write") : tr("Read"), + 0, infow); + pl_spd->setMinimumSize(100,20); + layout_info->addWidget(pl_spd); +#endif + + layout_info->addStretch(10); + + clear(); +#ifndef QT_NO_DEBUG + qDebug("END: tabTransfer()"); +#endif +} + +tabTransfer::~tabTransfer() +{ +#ifndef QT_NO_DEBUG + qDebug("STA: ~tabTransfer()"); + qDebug("END: ~tabTransfer()"); +#endif +} + +/* +void tabTransfer::clear() +{ + qDebug("tabTransfer::clear()"); + +} +*/ + +void tabTransfer::selectDevice() +{ +#ifndef QT_NO_DEBUG + qDebug("tabTransfer::selectDevice()"); +#endif + device *dev = devices->current(); + float time = rw ? dev->testData.wt_time : dev->testData.rt_time; +// graph->update(); + GraphTab::updateLast((int)time, NULL, 1); + updateSummary(dev, time); + + QObject::connect( dev, SIGNAL(doneMInfo(int)), this, SLOT(updateLast()) ); + if (!rw) { + QObject::connect( dev, SIGNAL(block_RT()), this, SLOT(updateLast()) ); + } else { + QObject::connect( dev, SIGNAL(block_WT()), this, SLOT(updateLast()) ); + } +} + +void tabTransfer::updateLast() +{ + bool show; + device *dev = devices->current(); + float time = rw ? dev->testData.wt_time : dev->testData.rt_time; + GraphTab::updateLast((int)time, &show); + if (!show) return; + updateSummary(dev,time); +} + +void tabTransfer::updateLegend() +{ +#ifdef __LEGEND_SHOW_SPEED + pl_spd->setColor(rw ? settings->col_wspeed : settings->col_rspeed); +#endif +} + +void tabTransfer::updateGraph() +{ + graph->update(); +} + +void tabTransfer::updateSummary(device* dev, float time) +{ +#ifndef QT_NO_DEBUG + qDebug() << "tabTransfer::updateSummary(): " << (rw ? "WT" : "RT") << " device@" << dev; +#endif + float avg_x; + uint32_t avg_kb; + if (!(rw ? dev->testData.wt.size() : dev->testData.rt.size())) { + l_sta_x->clear(); + l_sta_kb->clear(); + l_end_x->clear(); + l_end_kb->clear(); + l_avg_x->clear(); + l_avg_kb->clear(); + return; + } + if (!time) time=1; + + l_sta_x->setText( QString::number( rw ? dev->testData.wt.first().spdx : dev->testData.rt.first().spdx, 'f', 2 ) + " X"); + l_sta_kb->setText( QString::number( rw ? dev->testData.wt.first().spdk : dev->testData.rt.first().spdk) + " kB/s"); + + l_end_x->setText( QString::number( rw ? dev->testData.wt.last().spdx : dev->testData.rt.last().spdx, 'f', 2 ) + " X"); + l_end_kb->setText( QString::number( rw ? dev->testData.wt.last().spdk : dev->testData.rt.last().spdk) + " kB/s"); + + avg_kb = (int) ((rw ? dev->testData.wt.last().lba : dev->testData.rt.last().lba) * 2 / time); + if (dev->media.spd1X) + avg_x = (float)avg_kb / dev->media.spd1X; + else + avg_x = (float)avg_kb / 150.0; + +#ifndef QT_NO_DEBUG + printf("avg: %u KB, %.2f X, %d KB/X\n", avg_kb, avg_x, dev->media.spd1X); +#endif + + l_avg_x->setText( QString::number( avg_x, 'f', 2) + " X"); + l_avg_kb->setText( QString::number(avg_kb) + " kB/s"); +} + diff --git a/gui/src/tattoowidget.cpp b/gui/src/tattoowidget.cpp new file mode 100644 index 0000000..832d41f --- /dev/null +++ b/gui/src/tattoowidget.cpp @@ -0,0 +1,127 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Gennady "ShultZ" Kozlov + * + * 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 + +#include +#include + +#include "tattoowidget.h" + +TattooWidget::TattooWidget( QWidget *p) + : QWidget(p) +{ + r0mm = F1TATTOOR/3; + r1mm = F1TATTOOR; +} + +TattooWidget::~TattooWidget() {} + +void TattooWidget::setRadius(int ir0, int ir1) +{ + r0mm = ir0; r1mm = ir1; + convertImage(); + update(); +} + +void TattooWidget::setImage(QImage& iimg) +{ + timg = iimg; + convertImage(); + update(); +} + +#define CONVERT_DIRECT + +void TattooWidget::convertImage() +{ + x0 = width()/2; + y0 = height()/2; + if (width() < height()) { + r1p = x0 - 4; + } else { + r1p = y0 - 4; + } + + + r0p = (int)((float)r1p * r0mm / r1mm); + + dimg = QImage(r1p<<1, r1p<<1, QImage::Format_ARGB32); + dimg.fill(0xFFFFFFFF); + + float ang, rad; + int h=timg.height(); + int w=timg.width(); + +#ifndef CONVERT_DIRECT + QPainter p; + p.begin(&dimg); + p.setRenderHint(QPainter::Antialiasing); +#endif + float x1,y1; + if (w & h) { +#ifndef QT_NO_DEBUG + qDebug() << "Converting image " << w << "x" << h << " to radial..."; +#endif + for(int y=0; y + * + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "testdialog.h" + +#include +#include +#include +#include +#include + +#include + +TestDialog::TestDialog(QPxSettings *iset, device *idev, QWidget *p, Qt::WindowFlags f) + : QDialog(p,f) +{ + settings = iset; + dev = idev; + setWindowTitle(tr("Select tests...")); + + winit(); + checkSimul(); + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + +// setting default selection for tests autostart + dev->test_req = 0; + if(!dev->media.creads || dev->media.type.startsWith("DVD+RW") || dev->media.type.startsWith("DVD-RAM")) { + dev->test_req |= (settings->actions_flags & AFLAG_DTEST_WT) ? TEST_WT : 0; + dev->test_req |= (settings->actions_flags & AFLAG_DTEST_FT_W) ? TEST_FT : 0; + } + if (!dev->media.creads || dev->media.type.startsWith("DVD+RW")) { + dev->WT_simul = (settings->actions_flags & AFLAG_DTEST_WT_SIMUL) ? !!noSimul : 0; + } + if(dev->media.creads) { + dev->test_req |= (settings->actions_flags & AFLAG_DTEST_RT) ? TEST_RT : 0; + dev->test_req |= (settings->actions_flags & AFLAG_DTEST_ERRC) ? TEST_ERRC : 0; + dev->test_req |= (settings->actions_flags & AFLAG_DTEST_JB) ? TEST_JB : 0; + dev->test_req |= (settings->actions_flags & AFLAG_DTEST_FT_B) ? TEST_FT : 0; + dev->test_req |= (settings->actions_flags & AFLAG_DTEST_TA) ? TEST_TA : 0; + } + updateData(false); +} + +TestDialog::~TestDialog() +{ + +} + +device* TestDialog::getDevice() +{ + return dev; +} + +void TestDialog::winit() +{ + layout = new QGridLayout(this); + layout->setMargin(3); + layout->setSpacing(6); +/* + layout_dev = new QHBoxLayout(); + layout_dev->setMargin(3); + layout_dev->setSpacing(3); + layout->addLayout(layout_dev, 0,0,1,4); +*/ + ldev = new QLabel(tr("Device:"),this); + ldev->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout->addWidget(ldev,0,0); + + devid = new QLabel(this); + devid->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + layout->addWidget(devid,0,1); + + lmedia = new QLabel(tr("Media:"),this); + lmedia->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout->addWidget(lmedia,1,0); + + media = new QLabel(this); + media->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + layout->addWidget(media,1,1); + + llabel = new QLabel(tr("Label:"),this); + llabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + layout->addWidget(llabel,2,0); + + elabel = new QLineEdit(this); + elabel->setMaxLength(128); + layout->addWidget(elabel,2,1); + +// tests selection + +// grp_tests = new QGroupBox(tr("Tests"),this); + grp_tests = new QGroupBox(this); + layout->addWidget(grp_tests, 3,0, 1,2); + layout_tests = new QGridLayout(grp_tests); + layout_tests->setMargin(3); + layout_tests->setSpacing(3); +// grp_tests->setLayout(layout_tests); + + l_tests = new QLabel(tr("Tests:"),this); + layout_tests->addWidget(l_tests, 0,0); + l_speeds = new QLabel(tr("Speeds:"),this); + layout_tests->addWidget(l_speeds, 0,1); + + ck_RT = new QCheckBox(tr("Read Transfer Rate"), this); + layout_tests->addWidget(ck_RT, 1,0); + spd_RT = new QComboBox(this); + layout_tests->addWidget(spd_RT, 1,1); + + ck_WT = new QCheckBox(tr("Write Transfer Rate"), this); + layout_tests->addWidget(ck_WT, 2,0); + spd_WT = new QComboBox(this); + layout_tests->addWidget(spd_WT, 2,1); + ck_WT_simul = new QCheckBox(tr("Simulation"), this); + ck_WT_simul->setEnabled(false); + layout_tests->addWidget(ck_WT_simul, 3,1); + + hline0 = new QFrame(this); + hline0->setFrameStyle(QFrame::HLine | QFrame::Sunken); + layout_tests->addWidget(hline0, 4,0,1,2); + + + ck_ERRC = new QCheckBox(tr("Error Correction"), this); + layout_tests->addWidget(ck_ERRC, 5,0); + spd_ERRC = new QComboBox(this); + layout_tests->addWidget(spd_ERRC, 5,1); + + + ck_JB = new QCheckBox(tr("Jitter/Asymmetry"), this); + layout_tests->addWidget(ck_JB, 6,0); + spd_JB = new QComboBox(this); + layout_tests->addWidget(spd_JB, 6,1); + + ck_FT = new QCheckBox(tr("Focus/Tracking"), this); + layout_tests->addWidget(ck_FT, 7,0); + spd_FT = new QComboBox(this); + layout_tests->addWidget(spd_FT, 7,1); + + + ck_TA = new QCheckBox(tr("Time Analyser"), this); + layout_tests->addWidget(ck_TA, 8,0); +/* + spd_TA = new QComboBox(this); + layout_tests->addWidget(spd_TA, 5,1); +*/ + + hline1 = new QFrame(this); + hline1->setFrameStyle(QFrame::HLine | QFrame::Sunken); + layout_tests->addWidget(hline1, 9,0,1,2); + + l_plugin = new QLabel(tr("qScan plugin:"), this); + layout_tests->addWidget(l_plugin, 10,0); + + cb_plugin = new QComboBox(this); +// cb_plugin->setEnabled(false); + layout_tests->addWidget(cb_plugin, 10,1); + + l_plugin_info = new QLabel(this); + layout_tests->addWidget(l_plugin_info, 11,0,1,2); + + layout_tests->setRowStretch(12,10); + +/* +// media summary + grp_media = new QGroupBox(tr("Media summary:"),this); + layout->addWidget(grp_media, 1,2, 1,2); + layout_media = new QHBoxLayout(grp_media); + layout_media->setMargin(3); + layout_media->setSpacing(3); +// grp_media->setLayout(layout_media); + + media = new QTextBrowser(grp_media); + layout_media->addWidget(media); + layout->setRowStretch(1,10); +*/ + layout_butt = new QHBoxLayout; + layout_butt->setMargin(0); + layout_butt->setSpacing(3); + layout->addLayout(layout_butt, 4, 0, 1, 2); + + layout_butt->addStretch(3); + butt_run = new QPushButton(tr("Run"),this); + butt_run->setIcon(QIcon(":images/scan.png")); + layout_butt->addWidget(butt_run,1); + butt_cancel = new QPushButton(tr("Cancel"),this); + butt_cancel->setIcon(QIcon(":images/x.png")); + layout_butt->addWidget(butt_cancel,1); + + layout->setRowStretch(0,1); + layout->setRowStretch(1,1); + layout->setRowStretch(2,1); + layout->setRowStretch(3,20); + layout->setRowStretch(4,1); + + connect( ck_RT, SIGNAL(clicked(bool)), spd_RT,SLOT(setEnabled(bool))); + connect( ck_WT, SIGNAL(clicked(bool)), spd_WT,SLOT(setEnabled(bool))); + connect( ck_WT, SIGNAL(clicked(bool)), this,SLOT(WTchecked(bool))); + connect( ck_ERRC, SIGNAL(clicked(bool)), spd_ERRC,SLOT(setEnabled(bool))); + connect( ck_JB, SIGNAL(clicked(bool)), spd_JB,SLOT(setEnabled(bool))); + connect( ck_FT, SIGNAL(clicked(bool)), spd_FT,SLOT(setEnabled(bool))); +// connect( ck_TA, SIGNAL(clicked(bool)), spd_TA,SLOT(setEnabled(bool))); +// connect( ck_plugin, SIGNAL(clicked(bool)), cb_plugin,SLOT(setEnabled(bool))); + connect( cb_plugin, SIGNAL(activated(int)), this, SLOT(pluginChanged(int))); + + connect( butt_run, SIGNAL(clicked()), this, SLOT(start()) ); + connect( butt_cancel, SIGNAL(clicked()), this, SLOT(reject()) ); +} + +void TestDialog::WTchecked(bool en) +{ + if (noSimul) return; + ck_WT_simul->setEnabled(en); +} + +void TestDialog::checkSimul() +{ + noSimul = 1; + if ((dev->media.type.startsWith("CD-") && (dev->cap & CAP_TEST_WRITE_CD)) || + (dev->media.type.startsWith("DVD-") && !dev->media.type.startsWith("DVD-RAM") && (dev->cap & CAP_TEST_WRITE_DVD)) || + (dev->media.type.startsWith("DVD+") && (dev->cap & CAP_TEST_WRITE_DVD_PLUS)) ) + { + noSimul = 0; + ck_WT_simul->setEnabled( ck_WT->isChecked() ); + } else { + ck_WT_simul->setEnabled(false); + } +} + +void TestDialog::start() +{ + saveData(); + if (elabel->text().isEmpty()) { + QMessageBox::information(this, tr("Media label is empty!"), tr("You have to define a media label!")); + return; + } + if (!dev->test_req) { + QMessageBox::information(this, tr("No tests selected!"), tr("You have selected no tests!")); + return; + } +#ifndef QT_NO_DEBUG + qDebug() << "Selected plugin: "<< dev->plugin; +#endif + dev->media.label = elabel->text(); + accept(); +} + +void TestDialog::mediaChanged() +{ + qDebug() << "TestDialog::mediaChanged()"; + updateData(true, true); +} + +void TestDialog::updateData(bool save, bool setPlugin) +{ + if (save) saveData(); + devid->setText(dev->id); + if (dev->media.type == "-") { + grp_tests->setEnabled(false); + butt_run->setEnabled(false); + media->setText("No Media"); + return; + } + butt_run->setEnabled(true); + + media->setText(dev->media.dstate + " "+ dev->media.type); + grp_tests->setEnabled(true); + + ck_RT->setEnabled(dev->media.creads); + ck_RT->setChecked(dev->test_req & TEST_RT); + + ck_WT->setEnabled(dev->media.dstate.contains("Blank", Qt::CaseInsensitive) + || dev->media.type.startsWith("DVD+RW") + || dev->media.type.startsWith("DVD-RAM") + ); + ck_WT->setChecked(dev->test_req & TEST_WT); + ck_WT_simul->setChecked(dev->WT_simul); + + ck_ERRC->setEnabled(dev->test_cap & TEST_ERRC && dev->media.creads); + ck_ERRC->setChecked(ck_ERRC->isEnabled() && (dev->test_req & TEST_ERRC)); + + ck_JB->setEnabled(dev->test_cap & TEST_JB && dev->media.creads); + ck_JB->setChecked(ck_JB->isEnabled() && (dev->test_req & TEST_JB)); + + ck_FT->setEnabled(dev->test_cap & TEST_FT && dev->media.type!="-" && !dev->media.type.contains("-ROM", Qt::CaseInsensitive)); + ck_FT->setChecked(ck_FT->isEnabled() && (dev->test_req & TEST_FT)); + + ck_TA->setEnabled(dev->test_cap & TEST_TA && dev->media.creads); + ck_TA->setChecked(ck_TA->isEnabled() && (dev->test_req & TEST_TA)); + + spd_RT->clear(); spd_RT->setEnabled(ck_RT->isChecked()); + spd_WT->clear(); spd_WT->setEnabled(ck_WT->isChecked()); ck_WT_simul->setEnabled(ck_WT->isChecked()); + spd_ERRC->clear(); spd_ERRC->setEnabled(ck_ERRC->isChecked()); + spd_JB->clear(); spd_JB->setEnabled(ck_JB->isChecked()); + spd_FT->clear(); spd_FT->setEnabled(ck_FT->isChecked()); +// spd_TA->clear(); spd_TA->setEnabled(ck_TA->isChecked()); + + int idx; + + spd_RT->addItems(dev->media.rspeeds); spd_RT->addItem("Maximum"); + idx = spd_RT->findText(QString::number(dev->tspeeds.rt)+".",Qt::MatchStartsWith); +#ifndef QT_NO_DEBUG + qDebug() << "spd_RT: " << dev->tspeeds.rt <<"idx:" << idx; +#endif + if (idx > 0) spd_RT->setCurrentIndex( idx ); + + spd_WT->addItems(dev->media.wspeedsd); spd_WT->addItem("Maximum"); + idx = spd_WT->findText(QString::number(dev->tspeeds.wt)+".",Qt::MatchStartsWith); +#ifndef QT_NO_DEBUG + qDebug() << "spd_WT: " << dev->tspeeds.wt <<"idx:" << idx; +#endif + if (idx > 0) spd_WT->setCurrentIndex( idx ); + + spd_ERRC->addItems(dev->media.tspeeds_errc); spd_ERRC->addItem("Maximum"); + idx = spd_ERRC->findText(QString::number(dev->tspeeds.errc)+"X",Qt::MatchStartsWith); +#ifndef QT_NO_DEBUG + qDebug() << "spd_ERRC: " << dev->tspeeds.errc <<"idx:" << idx; +#endif + if (idx > 0) spd_ERRC->setCurrentIndex( idx ); + + spd_JB->addItems(dev->media.tspeeds_jb); spd_JB->addItem("Maximum"); + idx = spd_JB->findText(QString::number(dev->tspeeds.jb)+"X",Qt::MatchStartsWith); +#ifndef QT_NO_DEBUG + qDebug() << "spd_JB: " << dev->tspeeds.jb <<"idx:" << idx; +#endif + if (idx > 0) spd_JB->setCurrentIndex( idx ); + + spd_FT->addItems(dev->media.wspeedsd); spd_FT->addItem("Maximum"); + idx = spd_FT->findText(QString::number(dev->tspeeds.ft)+".",Qt::MatchStartsWith); +#ifndef QT_NO_DEBUG + qDebug() << "spd_FT: " << dev->tspeeds.ft <<"idx:" << idx; +#endif + if (idx > 0) spd_FT->setCurrentIndex( idx ); + +// spd_TA->addItems(dev->media.rspeeds); + + if (setPlugin) { + cb_plugin->addItem(tr("< Autodetect >")); cb_plugin->addItems(dev->plugin_names); + idx = cb_plugin->findText(dev->plugin); + if (idx > 0) + cb_plugin->setCurrentIndex( idx ); + else + pluginChanged(0); + } +} + +void TestDialog::saveData() +{ + dev->test_req = + (ck_RT->isChecked() ? TEST_RT : 0) | + (ck_WT->isChecked() ? TEST_WT : 0) | + (ck_ERRC->isChecked() ? TEST_ERRC : 0) | + (ck_JB->isChecked() ? TEST_JB : 0) | + (ck_FT->isChecked() ? TEST_FT : 0) | + (ck_TA->isChecked() ? TEST_TA : 0); + dev->WT_simul = noSimul ? 0 : ck_WT_simul->isChecked(); + + dev->tspeeds.rt = (int) spd_RT->currentText().remove(QRegExp("[Xx]")).toFloat(); + dev->tspeeds.wt = (int) spd_WT->currentText().remove(QRegExp("[Xx]")).toFloat(); + dev->tspeeds.errc = (int) spd_ERRC->currentText().remove(QRegExp("[Xx]")).toFloat(); + dev->tspeeds.jb = (int) spd_JB->currentText().remove(QRegExp("[Xx]")).toFloat(); + dev->tspeeds.ft = (int) spd_FT->currentText().remove(QRegExp("[Xx]")).toFloat(); + dev->plugin = (cb_plugin->currentIndex() ? cb_plugin->currentText() : ""); +// dev->tspeeds.ta = (int) spd_TA->currentText().remove(QRegExp("[Xx]")).toFloat(); +#ifndef QT_NO_DEBUG + qDebug() << "spd RT : " << dev->tspeeds.rt; + qDebug() << "spd WT : " << dev->tspeeds.wt; + qDebug() << "spd ERRC: " << dev->tspeeds.errc; + qDebug() << "spd JB : " << dev->tspeeds.jb; + qDebug() << "spd FT : " << dev->tspeeds.ft; +#endif +} + +void TestDialog::pluginChanged(int idx) +{ + ProgressWidget *progress; +// bool relock=0; +// bool preservePluginsList = dev->preservePluginsList; + + +#ifndef QT_NO_DEBUG + qDebug() << "pluginChanged()"; +#endif + + if (idx > (dev->plugin_infos.size()+2)) { + l_plugin_info->setText(tr("plugin info error")); + return; + } + + if (!idx) { + l_plugin_info->setText(tr("qScan will probe plugin for your drive")); + } else { + if (!dev->plugin_infos[idx-1].isEmpty()) { + l_plugin_info->setText(dev->plugin_infos[idx-1]); + } else { + l_plugin_info->setText(tr("no plugin info")); + } + } + + saveData(); +/* + if (!dev->mutex->tryLock()) { + relock = 1; + dev->mutex->unlock(); + } +*/ + + progress = new ProgressWidget(10,3, isVisible() ? this : (QWidget*)parent() ); + progress->setText(tr("Retrieving test capabilities...")); + progress->show(); + + dev->update_plugin_info(); + while(dev->isRunning()) { msleep ( 1 << 5); qApp->processEvents(); } + delete progress; + +// dev->preservePluginsList = preservePluginsList; + +// if (relock) dev->mutex->lock(); + + updateData(false, false); +} + diff --git a/gui/src/textslider.cpp b/gui/src/textslider.cpp new file mode 100644 index 0000000..87bf19d --- /dev/null +++ b/gui/src/textslider.cpp @@ -0,0 +1,389 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Gennady "ShultZ" Kozlov + * + * 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 +#include + +#include +#include +#include + +#include +#include "textslider.h" + +#define VITEMSIZE 16 +#define HITEMSIZE 30 + +TextSlider::TextSlider(QWidget *p) + : QAbstractSlider(p) +{ + initDefaults(); +} + +TextSlider::TextSlider(Qt::Orientation orient, QWidget *p) + : QAbstractSlider(p) +{ + initDefaults(); + op.orientation = orient; + if (orient == Qt::Horizontal) + setMinimumHeight(40); +} + +void TextSlider::initDefaults() +{ + en = 1; + op.state = QStyle::State_Active | QStyle::State_Enabled; + op.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle; + op.activeSubControls = op.subControls; + op.orientation = Qt::Vertical; + op.pageStep = 1; + op.singleStep = 1; + op.tickInterval = 1; + op.tickPosition = QSlider::TicksLeft; + op.upsideDown = true; + op.minimum = 0; + op.maximum = 0; + op.sliderPosition = 0; + items.clear(); + setFocusPolicy(Qt::StrongFocus); +} + +void TextSlider::addItem(QString item, bool en) +{ + if (!items.contains(item)) items << SliderItem(item,en); + op.maximum = items.size() -1; + if (items.size()) op.subControls |= QStyle::SC_SliderTickmarks; + + moveToEnabled(); + if (op.orientation == Qt::Vertical) { + setMinimumHeight(items.size() * VITEMSIZE); + } else { + setMinimumWidth(items.size() * HITEMSIZE); + } + update(); +} + +void TextSlider::removeItem(int idx) +{ + if (idx < 0 || idx >=items.size()) return; + items.removeAt(idx); + op.maximum = items.size() -1; + if (!items.size()) op.subControls &= ~QStyle::SC_SliderTickmarks; + + moveToEnabled(); + if (op.orientation == Qt::Vertical) { + setMinimumHeight(items.size() * VITEMSIZE); + } else { + setMinimumWidth(items.size() * HITEMSIZE); + } + update(); +} + +void TextSlider::setItemEnabled(int idx, bool en) +{ + if (idx<0 || idx>=items.size()) return; + items[idx].enabled = en; + if(!en) moveToEnabled(); + update(); +} + +void TextSlider::setItemEnabled(QString s, bool en) +{ + int idx = items.indexOf(s); + if (idx < 0) return; + items[idx].enabled = en; + if(!en) moveToEnabled(); + update(); +} + +void TextSlider::moveToEnabled() +{ + int idx = op.sliderPosition; + if (!items.size()) return; // list empty + if (idx<0 || idx>=items.size()) { // index out of range + idx = 0; + } else { + if (items[idx].enabled) return; // current item enabled + } + if (!prev()) next(); +// if (idx != op.sliderPosition) +// emit valueChanged(op.sliderPosition); +} + +void TextSlider::setTickPosition(QSlider::TickPosition position) +{ +// if (position == QSlider::TicksLeft || position == QSlider::TicksRight) op.tickPosition = position; + if ( op.tickPosition == position ) return; + op.tickPosition = position; + update(); +} + +void TextSlider::setUpsideDown(bool v) +{ + op.upsideDown = v; + update(); +} + +void TextSlider::setOrientation(Qt::Orientation orient) +{ + if (op.orientation == orient) return; + op.orientation = orient; + + if (op.orientation == Qt::Vertical) { + setMinimumHeight(items.size() * VITEMSIZE); + } else { + setMinimumWidth(items.size() * HITEMSIZE); + setMinimumHeight(40); + } + update(); +} + +void TextSlider::first() +{ + int pos = op.sliderPosition; + op.sliderPosition = 0; + moveToEnabled(); + update(); + if (op.sliderPosition != pos) emit valueChanged(op.sliderPosition); +} + +void TextSlider::last() +{ + int pos = op.sliderPosition; + op.sliderPosition = items.size()-1; + moveToEnabled(); + update(); + if (op.sliderPosition != pos) emit valueChanged(op.sliderPosition); +} + +bool TextSlider::prev() +{ + int pos = op.sliderPosition; + pos++; + while (pos < op.maximum && !items[pos].enabled) pos++; + if (pos > op.maximum || !items[pos].enabled) return 1; + + if (op.sliderPosition == pos) return 1; + op.sliderPosition = pos; + update(); + emit valueChanged(op.sliderPosition); + return 0; +} + +bool TextSlider::next() +{ + int pos = op.sliderPosition; + pos--; + while (pos > op.minimum && !items[pos].enabled) pos--; + if (pos < op.minimum || !items[pos].enabled) return 1; + + if (op.sliderPosition == pos) return 1; + op.sliderPosition = pos; + update(); + emit valueChanged(op.sliderPosition); + return 0; +} + +int TextSlider::value() +{ + if (op.sliderPosition <0 || op.sliderPosition >= items.size()) return -1; + return op.sliderPosition; +} + +QString TextSlider::text() +{ + if (op.sliderPosition <0 || op.sliderPosition >= items.size()) return QString::null; + return items[op.sliderPosition].text; +} + +void TextSlider::setValue(int val) +{ + op.sliderPosition = val; + moveToEnabled(); + update(); +} + +void TextSlider::setCurrentItem(const QString& text) +{ + int idx=-1; + for (int i=0; ipos().y()-10, height()-21, op.upsideDown); + } else { + int itemw = width() / items.size(); + pos = QStyle::sliderValueFromPosition(op.minimum, op.maximum, e->pos().x()-itemw/2, width() - itemw , op.upsideDown); + } + + if (items[pos].enabled) { + op.sliderPosition = pos; + update(); + } + if (oldpos != op.sliderPosition) { +// qDebug() << "mouseReleaseEvent() oldpos: " << oldpos << " pos: " << op.sliderPosition; + emit valueChanged(op.sliderPosition); + } +} + +void TextSlider::mouseMoveEvent(QMouseEvent* e) +{ + int pos; + if (!items.size()) return; + + if (op.orientation == Qt::Vertical) { + pos = QStyle::sliderValueFromPosition(op.minimum, op.maximum, e->pos().y()-10, height()-21, op.upsideDown); + } else { + int itemw = width() / items.size(); + pos = QStyle::sliderValueFromPosition(op.minimum, op.maximum, e->pos().x()-itemw/2, width() - itemw , op.upsideDown); + } + + if (items[pos].enabled) { + op.sliderPosition = pos; + update(); + } +} + +void TextSlider::wheelEvent(QWheelEvent* e) +{ + int pos = op.sliderPosition; + if (e->delta() * (op.upsideDown ? -1:1) * (op.orientation == Qt::Vertical ? 1:-1) < 0) { + prev(); + } else if (e->delta() * (op.upsideDown ? -1:1) * (op.orientation == Qt::Vertical ? 1:-1) > 0) { + next(); + } else { + return; + } + if (op.sliderPosition != pos) emit valueChanged(op.sliderPosition); +// qDebug() << "slider pos: " << op.sliderPosition; +} + +void TextSlider::keyPressEvent(QKeyEvent* e) +{ + switch (e->key()) { + case Qt::Key_Up: + case Qt::Key_Right: + if ((op.upsideDown ? -1:1) * (op.orientation == Qt::Vertical ? 1:-1) < 0) prev(); else next(); + break; + case Qt::Key_Down: + case Qt::Key_Left: + if ((op.upsideDown ? -1:1) * (op.orientation == Qt::Vertical ? 1:-1) < 0) next(); else prev(); + break; + case Qt::Key_Home: + if ((op.upsideDown ? -1:1) * (op.orientation == Qt::Vertical ? 1:-1) < 0) first(); else last(); + break; + case Qt::Key_End: + if ((op.upsideDown ? -1:1) * (op.orientation == Qt::Vertical ? 1:-1) < 0) last(); else first(); + break; + default: + e->ignore(); + break; + } +} + +void TextSlider::focusInEvent(QFocusEvent*) +{ + op.state |= QStyle::State_HasFocus; + update(); +} + +void TextSlider::focusOutEvent(QFocusEvent*) +{ + op.state &= ~QStyle::State_HasFocus; + update(); +} + +void TextSlider::paintEvent(QPaintEvent*) +{ +// qWarning("TextSlider::paintEvent()"); + QPainter p(this); + +// p.fillRect(0,0,width(),height(), QBrush(Qt::red)); + + int pos; + int ctl = style()->pixelMetric( QStyle::PM_SliderControlThickness, &op, this); + + //qDebug() << "Qt::WA_ForceDisabled: " << testAttribute(Qt::WA_ForceDisabled); + //qDebug() << "Qt::WA_Disabled: " << testAttribute(Qt::WA_Disabled); + bool ena = en && !testAttribute(Qt::WA_Disabled); + + if (op.orientation == Qt::Vertical) { + int itemh = height() - VITEMSIZE - ctl; + if (items.size()) + itemh /= items.size(); + for (int i=0; i < items.size(); i++) { + pos = QStyle::sliderPositionFromValue(op.minimum, op.maximum, i, height() - VITEMSIZE - ctl, op.upsideDown) + VITEMSIZE/2 + ctl/2; + p.setPen( QPen( palette().color( (ena && items[i].enabled) ? QPalette::Active : QPalette::Disabled, QPalette::Text), 1) ); + + // p.drawLine(0, pos, width(), pos); + if (op.tickPosition == QSlider::TicksLeft || op.tickPosition == QSlider::TicksBothSides ) + p.drawText(0, pos-itemh/2, width()-30, itemh+2, Qt::AlignRight | Qt::AlignVCenter, items[i].text); + else + p.drawText(30, pos-itemh/2, width()-30, itemh+2, Qt::AlignLeft | Qt::AlignVCenter, items[i].text); + } + if (op.tickPosition == QSlider::TicksLeft || op.tickPosition == QSlider::TicksBothSides ) + p.translate(width()-26, VITEMSIZE/2); + else + p.translate(0, VITEMSIZE/2); + op.rect.setWidth(26); + op.rect.setHeight(height() - VITEMSIZE); + } else { + int itemw = width() - ctl; + if (items.size()) + itemw /= items.size(); + for (int i=0; i < items.size(); i++) { + pos = QStyle::sliderPositionFromValue(op.minimum, op.maximum, i, width()-itemw-ctl, op.upsideDown) + itemw/2 + ctl/2; + p.setPen( QPen( palette().color( (ena && items[i].enabled) ? QPalette::Active : QPalette::Disabled, QPalette::Text), 1) ); + + // p.drawLine(pos, 0, pos, height()); + if (op.tickPosition == QSlider::TicksLeft || op.tickPosition == QSlider::TicksBothSides ) + p.drawText(pos-itemw/2, 0, itemw, height()-20, Qt::AlignHCenter | Qt::AlignBottom, items[i].text); + else + p.drawText(pos-itemw/2, 26, itemw, height()-20, Qt::AlignHCenter | Qt::AlignTop, items[i].text); + } + if (op.tickPosition == QSlider::TicksLeft || op.tickPosition == QSlider::TicksBothSides ) + p.translate(itemw/2, height()-26); + else + p.translate(itemw/2, 0); + op.rect.setWidth(width() - itemw); + op.rect.setHeight(26); + } + style()->drawComplexControl( QStyle::CC_Slider, &op, &p, this); +} + diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..b009be4 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,84 @@ +all: qpxtransport qpxplextor qpxpioneer qpxyamaha qpxscan + + +#build targets +qpxtransport: + $(MAKE) -C qpxtransport DIR=qpxtransport + +qpxplextor: qpxtransport + $(MAKE) -C qpxplextor DIR=qpxplextor + +qpxpioneer: qpxtransport + $(MAKE) -C qpxpioneer DIR=qpxpioneer + +qpxyamaha: qpxtransport + $(MAKE) -C qpxyamaha DIR=qpxyamaha + +qpxscan: qpxtransport qpxplextor + $(MAKE) -C qpxscan DIR=qpxscan + + +#clean targets +clean: qpxtransport-clean qpxplextor-clean qpxpioneer-clean qpxyamaha-clean qpxscan-clean + +qpxtransport-clean: + $(MAKE) -C qpxtransport DIR=qpxtransport clean + +qpxplextor-clean: + $(MAKE) -C qpxplextor DIR=qpxplextor clean + +qpxpioneer-clean: + $(MAKE) -C qpxpioneer DIR=qpxpioneer clean + +qpxyamaha-clean: + $(MAKE) -C qpxyamaha DIR=qpxyamaha clean + +qpxscan-clean: + $(MAKE) -C qpxscan DIR=qpxscan clean + + +#install targets +install: install-prereq qpxtransport-inst qpxplextor-inst qpxpioneer-inst qpxyamaha-inst qpxscan-inst + +install-prereq: + mkdir -p $(DESTDIR)$(INCDIR)/qpxtool + + +qpxtransport-inst: + $(MAKE) -C qpxtransport DIR=qpxtransport install + +qpxplextor-inst: + $(MAKE) -C qpxplextor DIR=qpxplextor install + +qpxpioneer-inst: + $(MAKE) -C qpxpioneer DIR=qpxpioneer install + +qpxyamaha-inst: + $(MAKE) -C qpxyamaha DIR=qpxyamaha install + +qpxscan-inst: + $(MAKE) -C qpxscan DIR=qpxscan install + + +#uninstall targets +uninstall: qpxtransport-uninst qpxplextor-uninst qpxpioneer-uninst qpxyamaha-uninst qpxscan-uninst + rm -rf $(DESTDIR)$(INCDIR)/qpxtool + +qpxtransport-uninst: + $(MAKE) -C qpxtransport DIR=qpxtransport uninstall + +qpxplextor-uninst: + $(MAKE) -C qpxplextor DIR=qpxplextor uninstall + +qpxpioneer-uninst: + $(MAKE) -C qpxpioneer DIR=qpxpioneer uninstall + +qpxyamaha-uninst: + $(MAKE) -C qpxyamaha DIR=qpxyamaha uninstall + +qpxscan-uninst: + $(MAKE) -C qpxscan DIR=qpxscan uninstall + + +.PHONY: all clean install install-prereq uninstall qpxtransport qpxplextor qpxpioneer qpxyamaha qpxscan + diff --git a/lib/include/colors.h b/lib/include/colors.h new file mode 100644 index 0000000..14dd41b --- /dev/null +++ b/lib/include/colors.h @@ -0,0 +1 @@ +#include "../qpxtransport/include/colors.h" diff --git a/lib/include/common_functions.h b/lib/include/common_functions.h new file mode 100644 index 0000000..74b07fa --- /dev/null +++ b/lib/include/common_functions.h @@ -0,0 +1 @@ +#include "../qpxtransport/include/common_functions.h" diff --git a/lib/include/pioneer_spdctl.h b/lib/include/pioneer_spdctl.h new file mode 100644 index 0000000..076ce4c --- /dev/null +++ b/lib/include/pioneer_spdctl.h @@ -0,0 +1 @@ +#include "../qpxpioneer/include/pioneer_spdctl.h" diff --git a/lib/include/plextor_features.h b/lib/include/plextor_features.h new file mode 100644 index 0000000..b7f05ca --- /dev/null +++ b/lib/include/plextor_features.h @@ -0,0 +1 @@ +#include "../qpxplextor/include/plextor_features.h" diff --git a/lib/include/qpx_mmc.h b/lib/include/qpx_mmc.h new file mode 100644 index 0000000..8900269 --- /dev/null +++ b/lib/include/qpx_mmc.h @@ -0,0 +1 @@ +#include "../qpxtransport/include/qpx_mmc.h" diff --git a/lib/include/qpx_mmc_defs.h b/lib/include/qpx_mmc_defs.h new file mode 100644 index 0000000..04edbb0 --- /dev/null +++ b/lib/include/qpx_mmc_defs.h @@ -0,0 +1 @@ +#include "../qpxtransport/include/qpx_mmc_defs.h" diff --git a/lib/include/qpx_opcodes.h b/lib/include/qpx_opcodes.h new file mode 100644 index 0000000..a6bb1ef --- /dev/null +++ b/lib/include/qpx_opcodes.h @@ -0,0 +1 @@ +#include "../qpxtransport/include/qpx_opcodes.h" diff --git a/lib/include/qpx_scan.h b/lib/include/qpx_scan.h new file mode 100644 index 0000000..ff6d501 --- /dev/null +++ b/lib/include/qpx_scan.h @@ -0,0 +1 @@ +#include "../qpxscan/include/qpx_scan.h" diff --git a/lib/include/qpx_scan_plugin_api.h b/lib/include/qpx_scan_plugin_api.h new file mode 100644 index 0000000..22ec2d4 --- /dev/null +++ b/lib/include/qpx_scan_plugin_api.h @@ -0,0 +1 @@ +#include "../qpxscan/include/qpx_scan_plugin_api.h" diff --git a/lib/include/qpx_transport.h b/lib/include/qpx_transport.h new file mode 100644 index 0000000..fea5e21 --- /dev/null +++ b/lib/include/qpx_transport.h @@ -0,0 +1 @@ +#include "../qpxtransport/include/qpx_transport.h" diff --git a/lib/include/threads.h b/lib/include/threads.h new file mode 100644 index 0000000..e5fa39a --- /dev/null +++ b/lib/include/threads.h @@ -0,0 +1 @@ +#include "../qpxtransport/include/threads.h" diff --git a/lib/include/yamaha_features.h b/lib/include/yamaha_features.h new file mode 100644 index 0000000..681bb1c --- /dev/null +++ b/lib/include/yamaha_features.h @@ -0,0 +1 @@ +#include "../qpxyamaha/include/yamaha_features.h" diff --git a/lib/qpxpioneer/Makefile b/lib/qpxpioneer/Makefile new file mode 100644 index 0000000..b679da5 --- /dev/null +++ b/lib/qpxpioneer/Makefile @@ -0,0 +1,13 @@ +SRC = pioneer_spdctl +HDRS = include/pioneer_spdctl.h +LIBN = qpxpioneer +SRCS = $(patsubst %,%.cpp, $(SRC)) +OBJS = $(patsubst %.cpp,%.o,$(SRCS)) + +VER_MAJOR = 0 +VER_MINOR = 7 +VER_MICRO = 0 + +LDLIBS += -lqpxtransport -L../lib + +include ../Makefile.lib diff --git a/lib/qpxpioneer/include/pioneer_spdctl.h b/lib/qpxpioneer/include/pioneer_spdctl.h new file mode 100644 index 0000000..e55d5f8 --- /dev/null +++ b/lib/qpxpioneer/include/pioneer_spdctl.h @@ -0,0 +1,45 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2006 Gennady "ShultZ" Kozlov + * + * 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 __pioneer_spdctl +#define __pioneer_spdctl + +#define PIO_SILENT_STD 0x00 +#define PIO_SILENT_PERF 0x01 +#define PIO_SILENT_QUIET 0x02 + +const str_dev pioneer_silent_tbl[]={ + "Standard", + "Performance", + "Quiet"}; + +#define PIO_SPD_NOLIMIT 0x00 +#define PIO_SPD_LIMIT 0x01 + +#define PIO_PURE_READ_STD 0x00 +#define PIO_PURE_READ_MASTER 0x01 +#define PIO_PURE_READ_PERFECT 0x02 + +const str_dev pioneer_pureread_tbl[]={ + "Standard", + "Master", + "Perfect"}; + +extern int pioneer_get_quiet(drive_info* drive); +extern int pioneer_set_quiet(drive_info* drive, char silent, bool limit, bool save); + +extern int pioneer_set_silent(drive_info* drive, char silent, bool save); +extern int pioneer_set_spdlim(drive_info* drive, bool limit, bool save); +extern int pioneer_set_peakpower(drive_info* drive, bool en, bool save); +extern int pioneer_set_pureread(drive_info* drive, char pr); + +#endif + diff --git a/lib/qpxpioneer/pioneer_spdctl.cpp b/lib/qpxpioneer/pioneer_spdctl.cpp new file mode 100644 index 0000000..81074c6 --- /dev/null +++ b/lib/qpxpioneer/pioneer_spdctl.cpp @@ -0,0 +1,70 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2006 Gennady "ShultZ" Kozlov + * + * 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 + +#include +#include +#include "pioneer_spdctl.h" + +int pioneer_get_quiet(drive_info* drive) +{ + drive->cmd[0] = 0x3C; + drive->cmd[1] = 0x01; + drive->cmd[2] = 0xF4; + drive->cmd[7] = 0x01; + if ((drive->err = drive->cmd.transport(READ, drive->rd_buf, 256))) + {if (!drive->silent) sperror("PIO_GET_QUIET", drive->err); return drive->err;} + drive->pioneer.limit = drive->rd_buf[0]; + drive->pioneer.silent = drive->rd_buf[2]; + return 0; +} + +int pioneer_set_quiet(drive_info* drive, char silent, bool limit, bool save) +{ + drive->cmd[0] = 0xBB; + drive->cmd[1] = save? 0x0C : 0x04; + drive->cmd[2] = limit? 0x00 : 0xFF; + drive->cmd[3] = limit? 0x00 : 0xFF; + drive->cmd[10]= (save? 0xC0 : 0x80) | silent; + if ((drive->err = drive->cmd.transport(NONE, NULL, 0))) + {if (!drive->silent) sperror("PIO_SET_QUIET", drive->err); return drive->err;} + return 0; +} + +int pioneer_set_silent(drive_info* drive, char silent, bool save) +{ + pioneer_get_quiet(drive); + if (!drive->silent) printf("Setting Quiet mode to \"%s\"... ", pioneer_silent_tbl[(int)silent]); + pioneer_set_quiet(drive, silent, drive->pioneer.limit, save); + pioneer_get_quiet(drive); + if (!drive->silent) printf("%s\n", (drive->pioneer.silent == silent) ? "OK":"FAIL"); + return drive->err; +} + +int pioneer_set_spdlim(drive_info* drive, bool limit, bool save) +{ + pioneer_get_quiet(drive); + if (!drive->silent) printf("Setting SpeedLimit %s... ", limit ? "ON":"OFF"); + pioneer_set_quiet(drive, drive->pioneer.silent, limit, save); + pioneer_get_quiet(drive); + if (!drive->silent) printf("%s\n", (drive->pioneer.limit == limit) ? "OK":"FAIL"); + return drive->err; +} + +int pioneer_set_peakpower(drive_info* drive, bool en, bool save) +{ +} + +int pioneer_set_pureread(drive_info* drive, char pr) +{ +} + diff --git a/lib/qpxplextor/Makefile b/lib/qpxplextor/Makefile new file mode 100644 index 0000000..303a6a3 --- /dev/null +++ b/lib/qpxplextor/Makefile @@ -0,0 +1,14 @@ +SRC = plextor_features +HDRS = include/plextor_features.h + +LIBN = qpxplextor +SRCS = $(patsubst %,%.cpp, $(SRC)) +OBJS = $(patsubst %.cpp,%.o,$(SRCS)) + +VER_MAJOR = 0 +VER_MINOR = 7 +VER_MICRO = 0 + +LDLIBS += -lqpxtransport -L../lib + +include ../Makefile.lib diff --git a/lib/qpxplextor/include/plextor_features.h b/lib/qpxplextor/include/plextor_features.h new file mode 100644 index 0000000..6d284b3 --- /dev/null +++ b/lib/qpxplextor/include/plextor_features.h @@ -0,0 +1,298 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2007 Gennady "ShultZ" Kozlov + * + * 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. + * + * + * Some Plextor commands got from PxScan and CDVDlib (C) Alexander Noe` + * + */ + + +#ifndef __PLEXTOR_FEATURES_H +#define __PLEXTOR_FEATURES_H + +const unsigned char PLEX_GET_MODE = 0x00; +const unsigned char PLEX_SET_MODE = 0x10; + +const unsigned char PLEX_MODE_SS_HIDE = 0x01; +const unsigned char PLEX_MODE_VARIREC = 0x02; +const unsigned char PLEX_MODE_GIGAREC = 0x04; +const unsigned char PLEX_MODE_SILENT_DISC = 0x06; +const unsigned char PLEX_MODE_SILENT_TRAY = 0x07; +const unsigned char PLEX_MODE_SILENT = 0x08; +const unsigned char PLEX_MODE_TESTWRITE_DVDPLUS = 0x21; +const unsigned char PLEX_MODE_BITSET = 0x22; +const unsigned char PLEX_MODE_SECUREC = 0xD5; + +const unsigned char PLEX_BITSET_R = 0x0A; +const unsigned char PLEX_BITSET_RDL = 0x0E; +const unsigned char PLEX_MODE_SPDREAD = 0xBB; + +typedef struct { + const unsigned char val; + char name[4]; +} REC; + +// GigaRec definitions +const unsigned char GIGAREC_06 = 0x83; +const unsigned char GIGAREC_07 = 0x82; +const unsigned char GIGAREC_08 = 0x81; +const unsigned char GIGAREC_09 = 0x84; +const unsigned char GIGAREC_10 = 0x00; +const unsigned char GIGAREC_11 = 0x04; +const unsigned char GIGAREC_12 = 0x01; +const unsigned char GIGAREC_13 = 0x02; +const unsigned char GIGAREC_14 = 0x03; +const unsigned char GIGAREC_OFF = GIGAREC_10; + +const REC gigarec_tbl[]={ + { GIGAREC_06, "0.6" }, + { GIGAREC_07, "0.7" }, + { GIGAREC_08, "0.8" }, + { GIGAREC_09, "0.9" }, + { GIGAREC_10, "OFF" }, + { GIGAREC_11, "1.1" }, + { GIGAREC_12, "1.2" }, + { GIGAREC_13, "1.3" }, + { GIGAREC_14, "1.4" }, + { 0xFF,"N/A" } +}; + +// VariRec definitions +const unsigned char VARIREC_CD = 0x00; +const unsigned char VARIREC_DVD = 0x10; + +const unsigned char VARIREC_CD_STRATEGY = 0x03; +const unsigned char VARIREC_DVD_STRATEGY = 0x04; + + +const unsigned char VARIREC_PLUS_1 = 0x01; +const unsigned char VARIREC_PLUS_2 = 0x02; +const unsigned char VARIREC_PLUS_3 = 0x03; +const unsigned char VARIREC_PLUS_4 = 0x04; +const unsigned char VARIREC_NULL = 0x00; +const unsigned char VARIREC_MINUS_1 = 0x81; +const unsigned char VARIREC_MINUS_2 = 0x82; +const unsigned char VARIREC_MINUS_3 = 0x83; +const unsigned char VARIREC_MINUS_4 = 0x84; + +const REC varirec_pwr_tbl[]={ + { VARIREC_MINUS_4,"-4" }, + { VARIREC_MINUS_3,"-3" }, + { VARIREC_MINUS_2,"-2" }, + { VARIREC_MINUS_1,"-1" }, + { VARIREC_NULL, " 0" }, + { VARIREC_PLUS_1, "+1" }, + { VARIREC_PLUS_2, "+2" }, + { VARIREC_PLUS_3, "+3" }, + { VARIREC_PLUS_4, "+4" }, + { 0xFF,"N/A" } +}; + +typedef char str16[16]; + +const unsigned char varirec_max_str_cd=7; +const str16 varirec_str_cd_tbl[]={ + "Default", + "Azo", + "Cyanine", + "PhtaloCyanine A", + "PhtaloCyanine B", + "PhtaloCyanine C", + "PhtaloCyanine D" +}; + +const unsigned char varirec_max_str_dvd=9; +const str16 varirec_str_dvd_tbl[]={ + "Default", + "Strategy0", + "Strategy1", + "Strategy2", + "Strategy3", + "Strategy4", + "Strategy5", + "Strategy6", + "Strategy7" +}; + +// Silent Mode definitions +const unsigned char SILENT_CD_WR_48X = 0x08; +const unsigned char SILENT_CD_WR_32X = 0x06; +const unsigned char SILENT_CD_WR_24X = 0x05; +const unsigned char SILENT_CD_WR_16X = 0x03; +const unsigned char SILENT_CD_WR_8X = 0x01; +const unsigned char SILENT_CD_WR_4X = 0x00; + +const REC silent_cd_wr_tbl[]={ + { SILENT_CD_WR_48X, "48X" }, + { SILENT_CD_WR_32X, "32X" }, + { SILENT_CD_WR_24X, "24X" }, + { SILENT_CD_WR_16X, "16X" }, + { SILENT_CD_WR_8X, "8X" }, + { SILENT_CD_WR_4X, "4X" }, + { 0xFF, "max" } +}; + +const unsigned char SILENT_CD_RD_48X = 0x05; +const unsigned char SILENT_CD_RD_40X = 0x04; +const unsigned char SILENT_CD_RD_32X = 0x03; +const unsigned char SILENT_CD_RD_24X = 0x02; +const unsigned char SILENT_CD_RD_8X = 0x01; +const unsigned char SILENT_CD_RD_4X = 0x00; + +const REC silent_cd_rd_tbl[]={ + { SILENT_CD_RD_48X, "48X" }, + { SILENT_CD_RD_40X, "40X" }, + { SILENT_CD_RD_32X, "32X" }, + { SILENT_CD_RD_24X, "24X" }, + { SILENT_CD_RD_8X, "8X" }, + { SILENT_CD_RD_4X, "4X" }, + { 0xFF, "max" } +}; + +/* +const unsigned char SILENT_DVD_WR_18X = 0x08; +const unsigned char SILENT_DVD_WR_16X = 0x06; +const unsigned char SILENT_DVD_WR_12X = 0x05; +const unsigned char SILENT_DVD_WR_8X = 0x03; +const unsigned char SILENT_DVD_WR_6X = 0x01; +const unsigned char SILENT_DVD_WR_4X = 0x00; + +const REC silent_dvd_wr_tbl[]={ + { SILENT_DVD_WR_18X, "18X" }, + { SILENT_DVD_WR_16X, "16X" }, + { SILENT_DVD_WR_12X, "12X" }, + { SILENT_DVD_WR_8X, "8X" }, + { SILENT_DVD_WR_6X, "6X" }, + { SILENT_DVD_WR_4X, "4X" }, + { 0xFF, "max" } +}; +*/ + +const unsigned char SILENT_DVD_RD_16X = 0x04; +const unsigned char SILENT_DVD_RD_12X = 0x03; +const unsigned char SILENT_DVD_RD_8X = 0x02; +const unsigned char SILENT_DVD_RD_5X = 0x01; +const unsigned char SILENT_DVD_RD_2X = 0x00; + +const REC silent_dvd_rd_tbl[]={ + { SILENT_DVD_RD_16X, "16X" }, + { SILENT_DVD_RD_12X, "12X" }, + { SILENT_DVD_RD_8X, "8X" }, + { SILENT_DVD_RD_5X, "5X" }, + { SILENT_DVD_RD_2X, "2X" }, + { 0xFF, "max" } +}; + +const unsigned char SILENT_CD = 0x01; +const unsigned char SILENT_DVD = 0x05; + +const unsigned char SILENT_ACCESS_FAST = 0x00; +const unsigned char SILENT_ACCESS_SLOW = 0x02; + +// PX-716 Autostrategy definitions +const unsigned char AS_OFF = 0x00; +const unsigned char AS_AUTO = 0x01; +// PX-755/PX-760 AS extentions +const unsigned char AS_FORCED = 0x04; +const unsigned char AS_ON = 0x08; + +const unsigned char ASDB_ENABLE = 0x01; +const unsigned char ASDB_DISABLE = 0x00; +const unsigned char ASDB_DELETE = 0xFF; + +const unsigned char ASDB_CRE_QUICK = 0x01; +const unsigned char ASDB_CRE_FULL = 0x05; +const unsigned char ASDB_REPLACE = 0x00; +const unsigned char ASDB_ADD = 0x02; + +const unsigned char AS_MEDIACK_QUICK = 0x11; +const unsigned char AS_MEDIACK_ADV = 0x31; + +const unsigned char PLEXERASER_QUICK = 0x11; +const unsigned char PLEXERASER_FULL = 0x31; + +extern int plextor_reboot(drive_info* drive); +extern int plextor_get_TLA(drive_info* drive); +// +extern int plextor_read_eeprom(drive_info* drive, int* len=NULL); +extern int plextor_get_life(drive_info* drive); + +// PoweRec +extern int plextor_set_powerec(drive_info* drive); +extern int plextor_get_powerec(drive_info* drive); +extern int plextor_get_speeds(drive_info* drive); +// silent mode +extern void plextor_print_silentmode_state(drive_info* drive); +extern int plextor_get_silentmode(drive_info* drive); +extern int plextor_set_silentmode_tray(drive_info* drive, int disc_type, int permanent); +extern int plextor_set_silentmode_disc(drive_info* drive, int disc_type, int permanent); +extern int plextor_set_silentmode_disable(drive_info* drive, int permanent); +// GigaRec +extern void print_gigarec_value(drive_info* drive); +extern int plextor_set_gigarec(drive_info* drive); +extern int plextor_get_gigarec(drive_info* drive); +// VariRec +extern void print_varirec(drive_info* drive, int disc_type); +extern int plextor_set_varirec(drive_info* drive, int disc_type); +extern int plextor_get_varirec(drive_info* drive, int disc_type); +// SecuRec +extern void print_securec_state(drive_info* drive); +extern int plextor_get_securec_state(drive_info* drive); +extern int plextor_set_securec(drive_info* drive, char len, char* passwd); +// SpeedRead +extern void print_speedread_state(drive_info* drive); +extern int plextor_set_speedread(drive_info* drive, int state); +extern int plextor_get_speedread(drive_info* drive); +// Hide-CDR / SingleSession +extern void print_hcdr_state(drive_info* drive); +extern void print_sss_state(drive_info* drive); +extern int plextor_get_hidecdr_singlesession(drive_info* drive); +extern int plextor_set_hidecdr(drive_info* drive, int state); +extern int plextor_set_singlesession(drive_info* drive, int state); +// Bitsetting +extern int plextor_get_bitset(drive_info* drive, int disc_type); +extern int plextor_set_bitset(drive_info* drive, int disc_type); +// Simulation on DVD+ +extern int plextor_get_testwrite_dvdplus(drive_info* drive); +extern int plextor_set_testwrite_dvdplus(drive_info* drive); +// PlexEraser +extern int plextor_plexeraser(drive_info* drive); +// AUTOSTRATEGY +// get/set autostrategy mode +extern int plextor_print_autostrategy_state(drive_info* drive); +extern int plextor_get_autostrategy(drive_info* drive); +extern int plextor_set_autostrategy(drive_info* drive); +// read autostrategy database +extern int plextor_get_autostrategy_db_entry_count(drive_info* drive); +extern int plextor_get_autostrategy_db(drive_info* drive); + +// returns detailed strategies data +extern int plextor_get_strategy(drive_info* drive); +// loads strategy to drive +extern int plextor_add_strategy(drive_info* drive); +// clears autostrategy database +extern int plextor_clear_autostrategy_db(drive_info* drive); + +// use action = ASDB_ENABLE/DISABLE/DELETE +extern int plextor_modify_autostrategy_db(drive_info* drive, int index, int action); +extern int plextor_create_strategy(drive_info* drive, int mode); +extern int plextor_media_check(drive_info* drive, int mode); + +#if 0 +// PX-755/760 Auth +extern int px755_do_auth(drive_info* drive); +extern int px755_get_auth_code(drive_info* drive,unsigned char* auth_code); +extern int px755_send_auth_code(drive_info* drive,unsigned char* auth_code); +extern int px755_clear_auth_status(drive_info* drive); +extern int px755_calc_auth_code(unsigned char* auth_code); +#endif + +#endif //__PLEXTOR_FEATURES + diff --git a/lib/qpxplextor/plextor_features.cpp b/lib/qpxplextor/plextor_features.cpp new file mode 100644 index 0000000..747c7e2 --- /dev/null +++ b/lib/qpxplextor/plextor_features.cpp @@ -0,0 +1,1091 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2007 Gennady "ShultZ" Kozlov + * + * 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. + * + * + * Some Plextor commands got from PxScan and CDVDlib (C) Alexander Noe` + * + */ + +#include +#include +#include +//#include +#include + +#include +#include +#include "plextor_features.h" + +static const char progress[] = { '-', '\\', '|', '/', 0 }; + +#define ASDB_SAVE_DEBUG +#define ASDB_LOAD_DEBUG +// #define DEBUG_SECUREC +//#define SHOW_RAW_TIME + +int plextor_reboot(drive_info* drive) { + drive->cmd[0] = 0xEE; + if ((drive->err=drive->cmd.transport(NONE,NULL,0) )) + { sperror ("reset",drive->err); return 1; } + return 0; +} + +int plextor_get_TLA(drive_info* drive) { + if (strncmp(drive->ven,"PLEXTOR ",8) || !strncmp(drive->dev,"CD-R PREMIUM2",15)) { + strcpy(drive->TLA,"N/A\0"); + return 1; + } + drive->cmd[0] = PLEXTOR_EEPROM_READ; + drive->cmd[8] = 0x01; + drive->cmd[9] = 0x00; + drive->cmd[10] = 0x00; + drive->cmd[11] = 0x00; + // The Plextor PX-716 does not understand this command.... + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,0x100))) + { + //printf("Possible PX-716...\n"); + drive->cmd[0] = 0xF1; + drive->cmd[1] = 0x01; + drive->cmd[8] = 0x01; + drive->cmd[9] = 0x00; + drive->cmd[10] = 0x00; + drive->cmd[11] = 0x00; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,0x100) )) + { + sperror ("Plextor get TLA",drive->err); + strcpy(drive->TLA,"N/A\0"); + return 1; + } + } + memcpy(drive->TLA,drive->rd_buf+0x29,4); + drive->TLA[4] = 0; + return 0; +} + +int plextor_read_eeprom_CDR(drive_info* drive) { +// unsigned int i,j; + drive->cmd[0] = 0xF1; + drive->cmd[1] = 0x00; + + drive->cmd[7] = 0x00; + drive->cmd[8] = 0x01; + drive->cmd[9] = 0x00; + drive->cmd[10] = 0x00; + drive->cmd[11] = 0x00; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,256) )) + { sperror ("Plextor read CDR EEPROM",drive->err); return drive->err; } + return 0; +} + +int plextor_read_eeprom_PX712(drive_info* drive) { +// unsigned int i,j; + drive->cmd[0] = 0xF1; + drive->cmd[1] = 0x00; + + drive->cmd[7] = 0x00; + drive->cmd[8] = 0x02; + drive->cmd[9] = 0x00; + drive->cmd[10] = 0x00; + drive->cmd[11] = 0x00; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,512) )) + { sperror ("Plextor read PX712 EEPROM",drive->err); return drive->err; } + return 0; +} + +int plextor_read_eeprom_block(drive_info* drive, unsigned char idx, unsigned int sz) { +// unsigned int i,j; + int offs=idx*sz; +// unsigned char* buf=drive->rd_buf+offs; + drive->cmd[0] = 0xF1; + drive->cmd[1] = 0x01; + drive->cmd[7] = idx; + drive->cmd[8] = (sz >> 8) & 0xFF; + drive->cmd[9] = sz & 0xFF; + drive->cmd[10] = 0x00; + drive->cmd[11] = 0x00; +// if ((drive->err=drive->cmd.transport(READ,buf,sz) )) + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf + offs,sz) )) + { sperror ("Plextor read EEPROM",drive->err); return drive->err; } +/* + printf("EEPROM block #%d:\n",idx); + for(i=0;i<(sz/0x10);i++) { + printf("| %X0 | ", i); + for(j=0;j<0x10;j++) printf("%02X ",buf[i*0x10+j]); + printf("|"); + for(j=0;j<0x10;j++) { + if (buf[i*0x10+j] > 0x20) printf("%c",buf[i*0x10+j]); + else printf(" "); + } + printf("|\n"); + }; +*/ + return 0; +} + +int plextor_read_eeprom(drive_info* drive, int* len) { + int l=0; + if (!isPlextor(drive)) { + if (len) *len=0; + return 1; + } + switch(drive->dev_ID) { + case PLEXTOR_OLD: + case PLEXTOR_4824: + case PLEXTOR_5224: + case PLEXTOR_PREMIUM: + case PLEXTOR_PREMIUM2: + plextor_read_eeprom_CDR(drive); + if ( drive->err ) { + if (len) *len=0; + return 1; + } + l = 256; + break; + case PLEXTOR_708: + case PLEXTOR_708A2: + case PLEXTOR_712: + plextor_read_eeprom_PX712(drive); + if ( drive->err ) { + if (len) *len=0; + return 1; + } + l = 512; + break; + case PLEXTOR_714: + case PLEXTOR_716: + case PLEXTOR_716AL: + case PLEXTOR_755: + case PLEXTOR_760: + for (unsigned char idx=0; idx<4; idx++) { + plextor_read_eeprom_block(drive, idx, 256); + if ( drive->err ) { + if (len) *len=l; + return 1; + } + l+=256; + } + break; + default: + printf("Don't know how to read eeprom from this device: %04X:%04X\n", drive->ven_ID, drive->dev_ID); + if (len) *len=0; + break; + } + if (len) *len=l; + return 0; +} + +int plextor_get_life(drive_info* drive) +{ + uint32_t ucr=0; + uint32_t ucw=0; + uint32_t udr=0; + uint32_t udw=0; + drive->life.ok=0; +// printf("plextor_get_life()\n"); + if (drive->ven_ID != DEV_PLEXTOR) return 1; + if (plextor_read_eeprom(drive)) { + printf("Error reading eeprom!\n"); + return 1; + } + switch (drive->dev_ID) { + case PLEXTOR_OLD: + case PLEXTOR_4824: + case PLEXTOR_5224: + case PLEXTOR_PREMIUM: + case PLEXTOR_PREMIUM2: + drive->life.dn = ntoh16(drive->rd_buf+0x0078); + ucr = ntoh32(drive->rd_buf+0x006C); + ucw = ntoh32(drive->rd_buf+0x007A); + break; + default: + drive->life.dn = ntoh16(drive->rd_buf+0x0120); + ucr = ntoh32(drive->rd_buf+0x0122); + ucw = ntoh32(drive->rd_buf+0x0126); + + udr = ntoh32(drive->rd_buf+0x012A); + udw = ntoh32(drive->rd_buf+0x012E); + break; + } + drive->life.ok=1; + int2hms(ucr, &drive->life.cr); + int2hms(ucw, &drive->life.cw); + int2hms(udr, &drive->life.dr); + int2hms(udw, &drive->life.dw); + return 0; +} + +int plextor_get_speeds(drive_info* drive) { + int sel, max, last; + drive->cmd[0]=PLEXTOR_PREC_SPD; + drive->cmd[9]=0x0A; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,0x0A) )) + { if (!drive->silent) sperror ("GET_SPEEDS",drive->err);return drive->err;} +// sel = (drive->rd_buf[5]<<8) | drive->rd_buf[4]; +// max = (drive->rd_buf[7]<<8) | drive->rd_buf[6]; +// last = (drive->rd_buf[9]<<8) | drive->rd_buf[8]; + sel = ntoh16(drive->rd_buf+4); + max = ntoh16(drive->rd_buf+6); + last = ntoh16(drive->rd_buf+8); + if (!drive->silent) { + printf("Selected write speed : %5d kB/s (%d X)\n", sel, sel/177); + printf("Max for this media : %5d kB/s (%d X)\n", max, max/177); + printf("Last actual speed : %5d kB/s (%d X)\n", last, last/177); + } + return 0; +} + +int plextor_get_powerec(drive_info* drive) { + drive->cmd[0]=PLEXTOR_MODE2; + drive->cmd[1]=PLEX_GET_MODE; + drive->cmd[2]=0x00; + drive->cmd[9]=0x08; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8) )) + { if (!drive->silent) sperror ("GET_POWEREC",drive->err); return drive->err; } + drive->plextor.powerec_state = drive->rd_buf[2]; +// drive->plextor.powerec_spd = ((drive->rd_buf[4] & 0xFF)<<8) | (drive->rd_buf[5] & 0xFF); + drive->plextor.powerec_spd = ntoh16(drive->rd_buf+4); + if (!drive->silent) printf("\tPoweRec %s, Recommended speed: %d kB/s\n", + drive->plextor.powerec_state ? "ON" : "OFF",drive->plextor.powerec_spd); +// printf("\t"); for (int i=0; i<8; i++) printf("0x%02X ",drive->rd_buf[i]&0xFF); printf("\n"); + return 0; +} + +int plextor_set_powerec(drive_info* drive) { + printf("\tTurning PoweRec %s\n",drive->plextor.powerec_state ? "ON" : "OFF"); + drive->cmd[0]=PLEXTOR_MODE2; + drive->cmd[1]=PLEX_SET_MODE | (drive->plextor.powerec_state?1:0); + drive->cmd[2]=0x00; + drive->cmd[9]=0x08; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8) )) + { if (!drive->silent) sperror ("SET_POWEREC",drive->err);return drive->err;} + drive->plextor.powerec_state = drive->rd_buf[2] & 0xFF; +// drive->plextor.powerec_spd = ((drive->rd_buf[4] & 0xFF)<<8) | (drive->rd_buf[5] & 0xFF); + drive->plextor.powerec_spd = ntoh16(drive->rd_buf+4); +// printf("\t"); for (int i=0; i<8; i++) printf("0x%02X ",drive->rd_buf[i]&0xFF); printf("\n"); +// plextor_get_speeds(drive); + return 0; +} + +void plextor_print_silentmode_state(drive_info* drive) { + int val, idx; + val=drive->plextor_silent.rd; idx=0; + printf("Active SilentMode settings:\n"); + printf("\tSM Read speed : "); + if ( drive->media.type & DISC_DVD ) { + while ((silent_dvd_rd_tbl[idx].val!=0xFF) & (silent_dvd_rd_tbl[idx].val!=(val & 0xFF))) idx++; + printf("%s\n",silent_dvd_rd_tbl[idx].name); +// printf("%s : %d\n",silent_dvd_rd_tbl[idx].name, val); + } else { + while ((silent_cd_rd_tbl[idx].val!=0xFF) & (silent_cd_rd_tbl[idx].val!=(val & 0xFF))) idx++; + printf("%s\n",silent_cd_rd_tbl[idx].name); +// printf("%s : %d\n",silent_cd_rd_tbl[idx].name, val); + } + val=drive->plextor_silent.wr; idx=0; + printf("\tSM Write speed : "); + if ( drive->media.type & DISC_DVD ) { +// while ((silent_dvd_wr_tbl[idx].val!=0xFF) & (silent_dvd_wr_tbl[idx].val!=(val & 0xFF))) idx++; +// printf("%s : %d\n",silent_dvd_wr_tbl[idx].name, val); + printf("MAX\n"); + } else { + while ((silent_cd_wr_tbl[idx].val!=0xFF) & (silent_cd_wr_tbl[idx].val!=(val & 0xFF))) idx++; + printf("%s\n",silent_cd_wr_tbl[idx].name); +// printf("%s : %d\n",silent_cd_wr_tbl[idx].name, val); + } + printf("\tSM Access time : %s\n",drive->plextor_silent.access?"SLOW":"FAST"); + printf("\tSM Load speed : %d\n",drive->plextor_silent.load); + printf("\tSM Eject speed : %d\n",drive->plextor_silent.eject); + + if (!drive->plextor_silent.psaved) { + printf("Saved SilentMode settings not found\n"); + return; + } + + printf("Saved SilentMode settings:\n"); + + printf("\tPSM Silent State : %s\n",drive->plextor_silent.pstate?"ON":"OFF"); + + printf("\tPSM CD Read speed : %dX\n",drive->plextor_silent.prd_cd); + printf("\tPSM CD Write speed : %dX\n",drive->plextor_silent.pwr_cd); + printf("\tPSM DVD Read speed : %dX\n",drive->plextor_silent.prd_dvd); +// printf("\tPSM DVD Write speed: %d\n",drive->plextor_silent.pwr_dvd); + + printf("\tPSM Access time : %s\n",drive->plextor_silent.paccess?"SLOW":"FAST"); + printf("\tPSM Load speed : %d\n",drive->plextor_silent.pload); + printf("\tPSM Eject speed : %d\n",drive->plextor_silent.peject); +} + +void plextor_get_silentmode_saved(drive_info* drive) { + int len; + if (plextor_read_eeprom(drive, &len) || len<0x110) return; + + drive->plextor_silent.psaved = 1; + drive->plextor_silent.pstate = (drive->rd_buf[0x100] == 1); + drive->plextor_silent.paccess = (drive->rd_buf[0x101] == 2); + + drive->plextor_silent.prd_cd = drive->rd_buf[0x102]; + drive->plextor_silent.prd_dvd = drive->rd_buf[0x103]; + drive->plextor_silent.pwr_cd = drive->rd_buf[0x104]; +// drive->plextor_silent.pwr_dvd = 16; + +// = drive->rd_buf[0x106]; // ??? + +/* + drive->plextor_silent.pload = drive->rd_buf[0x108] - 47; + drive->plextor_silent.peject = 48 - drive->rd_buf[0x107]; +*/ + drive->plextor_silent.pload = drive->rd_buf[0x108] - 47; + drive->plextor_silent.peject = -(drive->rd_buf[0x107]+48); +} + +int plextor_get_silentmode(drive_info* drive) { + drive->plextor_silent.psaved = 0; + drive->cmd[0]=PLEXTOR_MODE; + drive->cmd[1]=PLEX_GET_MODE; + drive->cmd[2]=PLEX_MODE_SILENT; + drive->cmd[3]=0x04; + drive->cmd[10]=0x08; + if (( drive->err=drive->cmd.transport(READ,drive->rd_buf,8) )) + { if (!drive->silent) sperror ("GET_SILENT_MODE",drive->err); return drive->err; } + if (( drive->err=drive->cmd.transport(READ,(void*)&(drive->plextor_silent),8) )) + { if (!drive->silent) sperror ("GET_SILENT_MODE",drive->err); return drive->err; } +// printf("\t"); for (int i=0; i<8; i++) printf("0x%02X ",drive->rd_buf[i]&0xFF); printf("\n"); + plextor_get_silentmode_saved(drive); + return 0; +} + +int plextor_set_silentmode_tray(drive_info* drive, int disc_type, int permanent) { + drive->cmd[0]=PLEXTOR_MODE; + drive->cmd[1]=PLEX_GET_MODE; + drive->cmd[2]=PLEX_MODE_SILENT_TRAY; + drive->cmd[3]=disc_type | 2*!!permanent; + drive->cmd[4]=drive->plextor_silent.eject; + drive->cmd[6]=drive->plextor_silent.load; + if ((drive->err=drive->cmd.transport(NONE, NULL, 0))) + { if (!drive->silent) sperror ("SET_SILENT_MODE_DISC",drive->err);return drive->err;} +// printf("\t"); for (int i=0; i<8; i++) printf("0x%02X ",drive->rd_buf[i]&0xFF); printf("\n"); + return 0; +} + +int plextor_set_silentmode_disc(drive_info* drive, int disc_type, int permanent) { + drive->cmd[0]=PLEXTOR_MODE; + drive->cmd[1]=PLEX_GET_MODE; + drive->cmd[2]=PLEX_MODE_SILENT_DISC; + drive->cmd[3]=disc_type | 2*!!permanent; + drive->cmd[4]=drive->plextor_silent.rd; + if (disc_type == SILENT_CD) { + drive->cmd[5]=drive->plextor_silent.wr; + } else { + drive->cmd[5]=0xFF; + } + drive->cmd[6]=drive->plextor_silent.access; + if ((drive->err=drive->cmd.transport(NONE, NULL, 0))) + { if (!drive->silent) sperror ("SET_SILENT_MODE_DISC",drive->err);return drive->err;} +// printf("\t"); for (int i=0; i<8; i++) printf("0x%02X ",drive->rd_buf[i]&0xFF); printf("\n"); + return 0; +} + +int plextor_set_silentmode_disable(drive_info* drive, int permanent) { + drive->plextor_silent.rd=0x07; + drive->plextor_silent.wr=0x07; + drive->plextor_silent.access=0; + plextor_set_silentmode_disc(drive, 0, permanent); + drive->plextor_silent.eject=0x50; + drive->plextor_silent.load=0x50; + plextor_set_silentmode_tray(drive, 0, permanent); + return 0; +} + +void print_gigarec_value(drive_info* drive) { +// plextor_get_gigarec(drive); + int g,i; + printf("GigaRec state : "); + i=drive->plextor.gigarec; g=0; + while ((gigarec_tbl[g].val!=0xFF) & (gigarec_tbl[g].val!=(i & 0xFF))) g++; + printf("%s\nDisc GigaRec rate : ",gigarec_tbl[g].name); + i=drive->plextor.gigarec_disc; g=0; + while ((gigarec_tbl[g].val!=0xFF) & (gigarec_tbl[g].val!=(i & 0xFF))) g++; + printf("%s\n",gigarec_tbl[g].name); +} + +int plextor_set_gigarec(drive_info* drive) { +// printf(" applying gigarec setting... "); +// print_gigarec_value(i); + drive->cmd[0]=PLEXTOR_MODE; + drive->cmd[1]=PLEX_SET_MODE; + drive->cmd[2]=PLEX_MODE_GIGAREC; + drive->cmd[3]=(drive->plextor.gigarec?1:0); + drive->cmd[4]=drive->plextor.gigarec; + drive->cmd[10]=0x08; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8) )) + { if (!drive->silent) sperror ("SET_GIGAREC",drive->err); return drive->err;} + drive->plextor.gigarec = drive->rd_buf[3]; + drive->plextor.gigarec_disc = drive->rd_buf[4]; +// print_gigarec_value(drive); +// printf("\t"); for (int i=0; i<8; i++) printf("0x%02X ",drive->rd_buf[i]&0xFF); printf("\n"); + return 0; +} + +int plextor_get_gigarec(drive_info* drive) { + drive->cmd[0]=PLEXTOR_MODE; + drive->cmd[1]=PLEX_GET_MODE; + drive->cmd[2]=PLEX_MODE_GIGAREC; + drive->cmd[10]=0x08; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8) )) + { if (!drive->silent) sperror ("GET_GIGAREC",drive->err); return drive->err;} + drive->plextor.gigarec = drive->rd_buf[3]; + drive->plextor.gigarec_disc = drive->rd_buf[4]; +// printf("\t"); for (int i=0; i<8; i++) printf("0x%02X ",drive->rd_buf[i]&0xFF); printf("\n"); + return 0; +} + +void print_varirec(drive_info* drive, int disc_type) { + int v,i,s; + if (disc_type == VARIREC_DVD) { + i=drive->plextor.varirec_pwr_dvd; + s=drive->plextor.varirec_str_dvd; + } else { + i=drive->plextor.varirec_pwr_cd; + s=drive->plextor.varirec_str_cd; + } + v=0; + while ((varirec_pwr_tbl[v].val!=0xFF) & (varirec_pwr_tbl[v].val!=(i & 0xFF))) v++; + printf("\tVariRec %s power : %s\n", + (disc_type == VARIREC_DVD)? "DVD":"CD ", + varirec_pwr_tbl[v].name); + printf("\tVariRec %s strategy : %s [%d]\n", + (disc_type == VARIREC_DVD)? "DVD":"CD ", + (disc_type == VARIREC_DVD)? varirec_str_dvd_tbl[s] : varirec_str_cd_tbl[s], s); +} + +int plextor_set_varirec(drive_info* drive, int disc_type) { + drive->cmd[0]=PLEXTOR_MODE; + drive->cmd[1]=PLEX_SET_MODE; + drive->cmd[2]=PLEX_MODE_VARIREC; + if (disc_type == VARIREC_DVD) { + drive->cmd[3]=disc_type + 2*!!drive->plextor.varirec_state_dvd; + drive->cmd[4]=drive->plextor.varirec_pwr_dvd; + drive->cmd[5]=drive->plextor.varirec_str_dvd; + }else{ + drive->cmd[3]=disc_type + 2*!!drive->plextor.varirec_state_cd; + drive->cmd[4]=drive->plextor.varirec_pwr_cd; + drive->cmd[5]=drive->plextor.varirec_str_cd; + } + drive->cmd[10]=0x08; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8) )) + { if (!drive->silent) sperror ("SET_VARIREC",drive->err);return drive->err;} + if (disc_type == VARIREC_DVD) { + drive->plextor.varirec_state_dvd = drive->rd_buf[2]; + drive->plextor.varirec_pwr_dvd = drive->rd_buf[3]; + drive->plextor.varirec_str_dvd = drive->rd_buf[5]; + }else{ + drive->plextor.varirec_state_cd = drive->rd_buf[2]; + drive->plextor.varirec_pwr_cd = drive->rd_buf[3]; + drive->plextor.varirec_str_cd = drive->rd_buf[5]; + } +// printf("\t"); for (int i=0; i<8; i++) printf("0x%02X ",drive->rd_buf[i]&0xFF); printf("\n"); + return 0; +} + +int plextor_get_varirec(drive_info* drive, int disc_type) { + drive->cmd[0]=PLEXTOR_MODE; + drive->cmd[1]=PLEX_GET_MODE; + drive->cmd[2]=PLEX_MODE_VARIREC; + drive->cmd[3]=0x02 | disc_type; + drive->cmd[10]=0x08; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8) )) + { if (!drive->silent) sperror ("GET_VARIREC",drive->err); return drive->err;} + if (disc_type == VARIREC_DVD) { + drive->plextor.varirec_state_dvd = drive->rd_buf[2]; + drive->plextor.varirec_pwr_dvd = drive->rd_buf[3]; + drive->plextor.varirec_str_dvd = drive->rd_buf[5]; + }else{ + drive->plextor.varirec_state_cd = drive->rd_buf[2]; + drive->plextor.varirec_pwr_cd = drive->rd_buf[3]; + drive->plextor.varirec_str_cd = drive->rd_buf[5]; + } +// printf("\t"); for (int i=0; i<8; i++) printf("0x%02X ",drive->rd_buf[i]&0xFF); printf("\n"); + return 0; +} + +void print_securec_state(drive_info* drive) { + printf("SecuRec state : "); + printf("%s\n", drive->plextor.securec ? "ON" : "OFF" ); + printf("Disc is protected : "); + printf("%s\n", drive->plextor.securec ? "YES" : "NO" ); +} + +int plextor_get_securec_state(drive_info* drive) +{ + drive->cmd[0]=PLEXTOR_MODE; + drive->cmd[2]=PLEX_MODE_SECUREC; + drive->cmd[10]=0x08; + if ((drive->err = drive->cmd.transport(READ, drive->rd_buf, 8))) + { if (!drive->silent) sperror("PLEXTOR_GET_SECUREC", drive->err); return drive->err;} + drive->plextor.securec = drive->rd_buf[3]; + drive->plextor.securec_disc = drive->rd_buf[4]; +// drive->plextor.gigarec = (drive->rd_buf[3] & 0xFF); +// drive->plextor.gigarec_disc = (drive->rd_buf[4] & 0xFF); + +#ifdef DEBUG_SECUREC + printf("get_securec() data: "); + for (int i=0; i<8; i++) printf("0x%02X ",drive->rd_buf[i]&0xFF); printf("\n"); +#endif // DEBUG_SECUREC + return 0; +} + +int plextor_set_securec(drive_info* drive, char len, char* passwd) +{ + int i; + drive->cmd[0]=PLEXTOR_SEND_AUTH; + if ((passwd) && (len)) { + printf("Turning SecuRec ON\n"); +// printf("Setting SecuRec passwd to '%s' (len = %d)\n", passwd, len); + drive->cmd[2]=0x01; + drive->cmd[3]=0x01; + drive->cmd[4]=0x02; + drive->cmd[10]=0x10; + + drive->rd_buf[0]=0; + drive->rd_buf[1]=len; + for(i=0;i<0x0E;i++) + if (ird_buf[i+2]=passwd[i]; + else drive->rd_buf[i+2]=0; + if ((drive->err = drive->cmd.transport(WRITE, drive->rd_buf, 0x10))) + {if (!drive->silent) sperror("PLEXTOR_SET_SECUREC", drive->err); return drive->err;} + }else{ + printf("Turning SecuRec OFF\n"); + if ((drive->err = drive->cmd.transport(NONE, NULL, 0))) + {if (!drive->silent) sperror("PLEXTOR_SET_SECUREC", drive->err); return drive->err;} + } +// if ((drive->dev_ID == PLEXTOR_760)) plextor_px755_do_auth(drive); + return 0; +} + +void print_speedread_state(drive_info* drive){ + printf("\tSpeedRead: %s\n",drive->plextor.spdread ? "on" : "off" ); +} + +int plextor_set_speedread(drive_info* drive, int state) { + drive->cmd[0]=PLEXTOR_MODE; + drive->cmd[1]=PLEX_SET_MODE; + drive->cmd[2]=PLEX_MODE_SPDREAD; + drive->cmd[3]=!!state; + drive->cmd[10]=0x08; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8) )) + { if (!drive->silent) sperror ("SET_SPDREAD",drive->err); return drive->err; } +// printf("** SPDREAD: "); for (int i=0; i<8; i++) printf("0x%02X ",drive->rd_buf[i]&0xFF); printf("\n"); + drive->plextor.spdread=drive->rd_buf[2]; + return 0; +} + +int plextor_get_speedread(drive_info* drive) { +// return 1; + drive->cmd[0]=PLEXTOR_MODE; + drive->cmd[1]=PLEX_GET_MODE; + drive->cmd[2]=PLEX_MODE_SPDREAD; + drive->cmd[3]=0; + drive->cmd[10]=0x08; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8) )) + { if (!drive->silent) sperror ("GET_SPDREAD",drive->err); return drive->err; } + drive->plextor.spdread=drive->rd_buf[2]; + return 0; +} + +void print_hcdr_state(drive_info* drive) { + printf("\tHide CD-R: %s\n",drive->plextor.hcdr ? "on" : "off"); +} + +void print_sss_state(drive_info* drive) { + printf("\tSingleSession: %s\n",drive->plextor.sss ? "on" : "off"); +} + +int plextor_get_hidecdr_singlesession(drive_info* drive) { + drive->cmd[0]=PLEXTOR_MODE; + drive->cmd[1]=PLEX_GET_MODE; + drive->cmd[2]=PLEX_MODE_SS_HIDE; + drive->cmd[9]=0x08; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8))) + { if (!drive->silent) sperror ("GET_HCDR_SSS",drive->err); return drive->err; } + drive->plextor.hcdr = !!(drive->rd_buf[2] & 0x02); + drive->plextor.sss = (drive->rd_buf[2]) & 0x01; + return 0; +} + +int plextor_set_hidecdr_singlesession(drive_info* drive, int hidecdr_state, int singlesession_state) { + if (plextor_get_hidecdr_singlesession(drive)) return 1; +// printf("Trying to change SS/HIDE state to %d...\n",2*!!hidecdr_state + !!singlesession_state); + drive->cmd[0]=PLEXTOR_MODE; + drive->cmd[1]=PLEX_SET_MODE; + drive->cmd[2]=PLEX_MODE_SS_HIDE; + drive->cmd[3]=2*!!hidecdr_state + !!singlesession_state; + drive->cmd[9]=0x08; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8) )) + { if (!drive->silent) sperror ("SET_HCDR_SSS",drive->err); return drive->err;} +// printf("** HCDR_SSS: "); for (int i=0; i<8; i++) printf("0x%02X ",drive->rd_buf[i]&0xFF); printf("\n"); + drive->plextor.hcdr = !!(drive->rd_buf[2] & 0x02); + drive->plextor.sss = (drive->rd_buf[2]) & 0x01; + return 0; +} + +int plextor_set_hidecdr(drive_info* drive, int state) { + if (plextor_get_hidecdr_singlesession(drive)) return 1; + drive->plextor.hcdr = !!state; + return plextor_set_hidecdr_singlesession(drive, drive->plextor.hcdr, drive->plextor.sss); +} + +int plextor_set_singlesession(drive_info* drive, int state) { + if (plextor_get_hidecdr_singlesession(drive)) return 1; + drive->plextor.sss = !!state; + return plextor_set_hidecdr_singlesession(drive, drive->plextor.hcdr, drive->plextor.sss); +} + +int plextor_get_bitset(drive_info* drive, int disc_type) +{ + drive->cmd[0]=PLEXTOR_MODE; + drive->cmd[1]=PLEX_GET_MODE; + drive->cmd[2]=PLEX_MODE_BITSET; + drive->cmd[3]=disc_type; + drive->cmd[9]=0x08; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8) )) + { if (!drive->silent) sperror ("PLEXTOR_GET_BITSET",drive->err); return drive->err; } + switch (disc_type) + { + case PLEX_BITSET_R: drive->book_plus_r = ((drive->rd_buf[2] & 0x02) == 0x02); break; + case PLEX_BITSET_RDL: drive->book_plus_rdl = ((drive->rd_buf[2] & 0x01) == 0x01); break; + } + return 0; +} + +int plextor_set_bitset(drive_info* drive, int disc_type) +{ + char book; + switch (disc_type) + { + case PLEX_BITSET_R: book = (drive->book_plus_r); break; + case PLEX_BITSET_RDL: book = (drive->book_plus_rdl); break; + default: printf("PLEXTOR_SET_BITSET: Invalid disc_type"); return 1; + } + drive->cmd[0]=PLEXTOR_MODE; + drive->cmd[1]=PLEX_SET_MODE; + drive->cmd[2]=PLEX_MODE_BITSET; + drive->cmd[3]=disc_type; + drive->cmd[5]=book; + drive->cmd[9]=0x08; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8) )) + { if (!drive->silent) sperror ("PLEXTOR_SET_BITSET",drive->err); return drive->err;} + return 0; +} + +int plextor_get_testwrite_dvdplus(drive_info* drive) +{ + drive->cmd[0]=PLEXTOR_MODE; + drive->cmd[1]=PLEX_GET_MODE; + drive->cmd[2]=PLEX_MODE_TESTWRITE_DVDPLUS; + drive->cmd[10]= 0x08; + if ((drive->err=drive->cmd.transport(READ, drive->rd_buf, 8) )) + { if (!drive->silent) sperror ("PLEXTOR_GET_TESTWRITE_DVDPLUS",drive->err); return drive->err; } + drive->plextor.testwrite_dvdplus = !!drive->rd_buf[2]; + return 0; +} + +int plextor_set_testwrite_dvdplus(drive_info* drive) +{ + drive->cmd[0] = PLEXTOR_MODE; + drive->cmd[1] = PLEX_SET_MODE; + drive->cmd[2] = PLEX_MODE_TESTWRITE_DVDPLUS; + drive->cmd[3] = drive->plextor.testwrite_dvdplus; + if ((drive->err=drive->cmd.transport(NONE, NULL, 0) )) + { if (!drive->silent) sperror ("PLEXTOR_SET_TESTWRITE_DVDPLUS",drive->err); return drive->err;} + return 0; +} + +int plextor_plexeraser(drive_info* drive) +{ + long i = 0; + printf("Destucting disc [mode=%02X]... \n",drive->plextor.plexeraser); +// return 0; + drive->cmd[0] = PLEXTOR_PLEXERASER; + drive->cmd[1] = 0x06; + drive->cmd[2] = drive->plextor.plexeraser; + if ((drive->err=drive->cmd.transport(NONE, NULL, 0) )) + { if (!drive->silent) sperror ("PLEXTOR_DO_PLEXERASER",drive->err); return drive->err;} + while (test_unit_ready(drive)) { + msleep(1000); + i++; + } + return 0; +} + + //-----------------// + // AUTOSTRATEGY // + //-----------------// + +int plextor_print_autostrategy_state(drive_info* drive) +{ + printf("AutoStrategy mode : "); + switch (drive->astrategy.state) + { + case AS_OFF: printf("OFF"); break; + case AS_AUTO: printf("AUTO"); break; + case AS_FORCED: printf("FORCED"); break; + case AS_ON: printf("ON"); break; + default: printf("???"); + } + printf(" [%d]\n",drive->astrategy.state); + return 0; +} + +int plextor_get_autostrategy(drive_info* drive) +{ + drive->cmd[0] = PLEXTOR_AS_RD; + drive->cmd[10]= 0x08; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8) )) + { if (!drive->silent) sperror ("PLEXTOR_GET_AUTOSTRATEGY",drive->err); return drive->err;} +// printf("** GET AS: "); for (int i=0; i<8; i++) printf("0x%02X ",drive->rd_buf[i]&0xFF); printf("\n"); + drive->astrategy.state = (drive->rd_buf[2] & 0x0F); + return 0; +} + +int plextor_set_autostrategy(drive_info* drive) +{ + drive->cmd[0] = PLEXTOR_AS_RD; + drive->cmd[2] = PLEX_SET_MODE + (drive->astrategy.state & 0x0F); + drive->cmd[10]= 0x08; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8) )) + { if (!drive->silent) sperror ("PLEXTOR_SET_AUTOSTRATEGY",drive->err); return drive->err;} +// printf("** SET AS: "); for (int i=0; i<8; i++) printf("0x%02X ",drive->rd_buf[i]&0xFF); printf("\n"); + drive->astrategy.state = (drive->rd_buf[2] & 0x0F); + plextor_print_autostrategy_state(drive); + return 0; +} + +int plextor_get_autostrategy_db_entry_count(drive_info* drive) +{ + drive->cmd[0] = PLEXTOR_AS_RD; + drive->cmd[1] = 0x02; + drive->cmd[10]= 0x08; + if ((drive->err=drive->cmd.transport(READ,(void*)&(drive->astrategy),8) )) + { if (!drive->silent) sperror ("PLEXTOR_GET_ASDB_ENTRY_COUNT",drive->err); return drive->err;} +// drive->astrategy.dbcnt = drive->rd_buf[6]; +// printf ("\t AS DB entries: %d\n", drive->astrategy.dbcnt); + return 0; +} + +int plextor_get_autostrategy_db(drive_info* drive)//, void* database) +{ + int size = 8 + (int)drive->astrategy.dbcnt * 32; + int i,j; + drive->cmd[0] = PLEXTOR_AS_RD; // 0xE4 + drive->cmd[1] = 0x02; + drive->cmd[9]= (size >> 8) & 0xFF ; + drive->cmd[10]= size & 0xFF ; + if ((drive->err=drive->cmd.transport(READ,(void*)&(drive->astrategy),size) )) + { if (!drive->silent) sperror ("PLEXTOR_GET_ASDB",drive->err); return drive->err;} + printf("** AS DB entries: %d\n",drive->astrategy.dbcnt); +//* + for (j=0; jastrategy.dbcnt; j++) { + for (i=0; i<12;i++) if (drive->astrategy.entry[j].MID[i] < 0x20) drive->astrategy.entry[j].MID[i] = 0x20; +// for (i=0; i<12;i++) if (drive->astrategy.entry[j].MID[i] < 0x20 && drive->astrategy.entry[j].MID[i]) drive->astrategy.entry[j].MID[i] = 0x20; + drive->astrategy.entry[j].crap2 = 0; + + printf("S#%02d |%c| DVD%cR [%02X] | %3dX | %12s | %d\n", + drive->astrategy.entry[j].number, + drive->astrategy.entry[j].enabled ? '*':' ', + (drive->astrategy.entry[j].type == 0xA1)? '+':'-', drive->astrategy.entry[j].type, + drive->astrategy.entry[j].speed, + drive->astrategy.entry[j].MID, + (drive->astrategy.entry[j].counter[0] <<8) | drive->astrategy.entry[j].counter[1]); + } +//*/ +/* + char ch; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,size) )) + { sperror ("PLEXTOR_GET_ASDB",drive->err); return drive->err;} + printf("ASDB dump:\n"); + for (i=0; i<8; i++) printf("%02X ", drive->rd_buf[i] & 0xFF); + printf("\n"); + for (j=0; jastrategy.dbcnt; j++) { + for (i=0; i<32; i++) printf("%02X ", drive->rd_buf[j*32+i+8] & 0xFF); + printf("\n"); + for (i=0; i<32; i++) { + if (drive->rd_buf[j*32+i+8] > 0x1F) + ch = drive->rd_buf[j*32+i+8]; + else + ch = 0x20; + printf("%c", ch); + } + printf("\n"); + } +*/ + return 0; +} + +// EXPERIMENTAL STRATEGY SAVE + +int plextor_get_strategy(drive_info* drive){ + int cnt,acnt; + int i,s,offs; + unsigned char *entry; + unsigned char *entry_data; + + printf("RETR AS cnt...\n"); + drive->cmd[0] = PLEXTOR_AS_RD; // 0xE4 + drive->cmd[1] = 0x02; + drive->cmd[2] = 0x03; + drive->cmd[10]= 0x08; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,0x08) )) + { if (!drive->silent) sperror ("PLEXTOR_GET_STRATEGY",drive->err); return drive->err;} + acnt= drive->rd_buf[0]; + cnt= drive->rd_buf[6]; + drive->astrategy.dbcnt = cnt; + for (i=0; i<8; i++) printf("%02X ", drive->rd_buf[i]); + printf("\nStrategies count: %d\n", drive->astrategy.dbcnt); + + printf("RETR AS data...\n"); + drive->cmd[0] = PLEXTOR_AS_RD; // 0xE4 + drive->cmd[1] = 0x02; + drive->cmd[2] = 0x03; + drive->cmd[9] = cnt; + drive->cmd[10]= 0x10; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,cnt*0x100 + 0x10) )) + { if (!drive->silent) sperror ("PLEXTOR_GET_STRATEGY_DATA",drive->err); return drive->err;} + + +#ifdef ASDB_SAVE_DEBUG + printf("DB HDR0:\n"); + for (i=0; i<8; i++) printf("%02X ", drive->rd_buf[i]); printf("\n"); + for (s=0; srd_buf[offs+i]); + printf("\n"); + } + offs = 8 + cnt*0x20; + printf("DB HDR1:\n"); + for (i=0; i<8; i++) printf("%02X ", drive->rd_buf[offs+i]); printf("\n"); + for (s=0; srd_buf[offs+i]); + printf("\n"); + } + } +#endif + + for (s=0; sastrategy.entry[s]); + entry_data = (unsigned char*)&(drive->astrategy.entry_data[s]); + + offs = 8 + s*0x20; + for (i=0; i<0x20; i++) entry[i] = drive->rd_buf[offs+i]; + + offs = 0x10 + cnt*0x20 + s*0xE0; + for (i=0; i<(0x20*7); i++) entry_data[i] = drive->rd_buf[offs+i]; + } + return 0; +} + +// EXPERIMENTAL STRATEGY LOAD + +int plextor_add_strategy(drive_info* drive){ + int i, cnt = drive->astrategy.dbcnt; + int offs; + unsigned char* entry; + + plextor_clear_autostrategy_db(drive); + printf("Adding strategy...\n"); + +// sending strategy headers + for (i=0; i<(8+cnt*0x20); i++) drive->rd_buf[i]=0; + + drive->rd_buf[0]= (cnt >> 3) & 0xFF; + drive->rd_buf[1]= (cnt*0x20 + 6) & 0xFF; + drive->rd_buf[2]=0x02; + drive->rd_buf[3]=0x80; + drive->rd_buf[6]= cnt; + drive->rd_buf[7]=0x20; + + entry = (unsigned char*) &drive->astrategy.entry; + for (i=0; ird_buf[8+i] = entry[i]; + for (i=0; ird_buf[8+i*0x20] = (unsigned char)((i+1) & 0xFF); + +#ifdef ASDB_LOAD_DEBUG + printf("DB HDR0:\n"); + for (i=0; i<8; i++) printf("%02X ", drive->rd_buf[i]); printf("\n"); + for (int s=0; srd_buf[offs+i]); + printf("\n"); + } +#endif + + drive->cmd[0] = PLEXTOR_AS_WR; // 0xE5 + drive->cmd[1] = 0x02; + drive->cmd[9] = (cnt >> 3) & 0xFF; + drive->cmd[10]= (cnt*0x20 + 0x08) & 0xFF; + if ((drive->err=drive->cmd.transport(WRITE,drive->rd_buf,cnt*0x20+8) )) + { if (!drive->silent) sperror ("PLEXTOR_ADD_STRATEGY_HDR",drive->err); return drive->err;} + +// sending strategy parameters + for (i=0; i<(8+cnt*0xE0); i++) drive->rd_buf[i]=0; + + drive->rd_buf[0]= ((cnt*0xE) >> 4) & 0xFF; + drive->rd_buf[1]= (cnt*0xE0 + 6) & 0xFF; + drive->rd_buf[2]=0x02; + drive->rd_buf[3]=0x81; + drive->rd_buf[6]= cnt * 7; + drive->rd_buf[7]=0x20; + + entry = (unsigned char*) &drive->astrategy.entry_data; + for (i=0; ird_buf[8+i] = entry[i]; + for (i=0; ird_buf[i*0xE0+s1*0x20+8] = ((i*7+s1)>>8) & 0xFF; + drive->rd_buf[i*0xE0+s1*0x20+9] = (i*7+s1) & 0xFF; + } + +#ifdef ASDB_LOAD_DEBUG + printf("DB HDR1:\n"); + + + for (i=0; i<8; i++) printf("%02X ", drive->rd_buf[i]); printf("\n"); + for (int s=0; srd_buf[offs+i]); + printf("\n"); + } + } +#endif + + drive->cmd[0] = PLEXTOR_AS_WR; // 0xE5 + drive->cmd[1] = 0x02; + drive->cmd[9] = ((cnt*0xE) >> 4) & 0xFF; + drive->cmd[10]= (cnt*0xE0 + 0x08) & 0xFF; + if ((drive->err=drive->cmd.transport(WRITE,drive->rd_buf,cnt*0xE0+8) )) + { if (!drive->silent) sperror ("PLEXTOR_ADD_STRATEGY_DATA",drive->err); return drive->err;} + return 0; +} + +int plextor_clear_autostrategy_db(drive_info* drive) { + for (int i=0; i<8; i++) drive->rd_buf[i]=0; + drive->rd_buf[1]=0x06; + drive->rd_buf[2]=0x02; + drive->rd_buf[3]=0xFF; + drive->cmd[0] = PLEXTOR_AS_WR; // 0xE5 + drive->cmd[1] = 0x02; + drive->cmd[10]= 0x08; + if ((drive->err=drive->cmd.transport(WRITE,drive->rd_buf,0x08) )) + { if (!drive->silent) sperror ("PLEXTOR_CLEAR_ASTRATEGY_DB",drive->err); return drive->err;} + return 0; +} + +// +int plextor_modify_autostrategy_db(drive_info* drive, int index, int action) +{ + drive->rd_buf[0]=0x00; + drive->rd_buf[1]=0x08; + drive->rd_buf[2]=0x02; + drive->rd_buf[3]=0x00; + drive->rd_buf[4]=0x00; + drive->rd_buf[5]=0x00; + drive->rd_buf[6]=0x01; + drive->rd_buf[7]=0x02; + drive->rd_buf[8]=index; + drive->rd_buf[9]=action; + + drive->cmd[0] = PLEXTOR_AS_WR; // 0xE5 + drive->cmd[1] = 0x02; + drive->cmd[10]= 0x0A; + if ((drive->err=drive->cmd.transport(WRITE,drive->rd_buf,0x0A) )) + { if (!drive->silent) sperror ("PLEXTOR_MODIFY_ASDB",drive->err); return drive->err;} + return 0; +} + +int plextor_create_strategy(drive_info* drive, int mode) +{ + int i=0; + int p=0; +// if (!drive->silent) printf("AS create: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", (CMD_PLEX_AS_RD) & 0xFF, 4, mode & 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0); + drive->cmd[0] = PLEXTOR_AS_RD; + drive->cmd[1] = 0x04; + drive->cmd[2] = mode; + if ((drive->err=drive->cmd.transport(NONE, NULL, 0) )) + { if (!drive->silent) sperror ("PLEXTOR_CREATE_STRATEGY_START",drive->err); return drive->err;} + + if (!drive->silent) printf("AS CRE START...\n"); + + drive->cmd[0] = PLEXTOR_AS_RD; + drive->cmd[1] = 0x01; + drive->cmd[10]= 0x12; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,0x12) )) + { if (!drive->silent) sperror ("PLEXTOR_AS_GET_STATUS",drive->err); return drive->err;} + + if (!drive->silent) printf(" AS CRE: "); for (i=0; i<0x12; i++) printf("%02X ", drive->rd_buf[i] & 0x0FF); printf("\n"); + +// Waiting until Strategy is created + while (test_unit_ready(drive)) { + sleep(1); + printf("%c\r", progress[p++]); if (p==4) p=0; + i++; + } + printf("Strategy creation time: %d sec\n",i); + + drive->cmd[0] = PLEXTOR_AS_RD; + drive->cmd[1] = 0x01; + drive->cmd[10]= 0x12; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,0x12) )) + { if (!drive->silent) sperror ("PLEXTOR_AS_GET_STATUS",drive->err); return drive->err;} + +// if (!drive->silent) printf(" AS CRE DONE: "); for (i=0; i<0x12; i++) printf("%02X ", drive->rd_buf[i]) & 0x0FF; printf("\n"); + + return 0; +} + +int plextor_media_check(drive_info* drive, int mode) +{ + int i=0; + int p=0; +// if (!drive->silent) printf("MQCK CDB: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", +// (PLEXTOR_AS_RD) & 0xFF, 1, mode & 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + if ( !(drive->media.type & (DISC_DVD)) ) { + printf("Media Quality Check supported on DVD media only!\n"); + return -1; + } + + drive->cmd[0] = PLEXTOR_AS_RD; + drive->cmd[1] = 0x01; + drive->cmd[2] = mode; + if ((drive->err=drive->cmd.transport(NONE, NULL, 0))) + { if (!drive->silent) sperror ("PLEXTOR_MEDIA_QUALITY_CHECK_START",drive->err); return drive->err;} + + printf("Starting MQCK...\n"); + +// Waiting until Media is checked + while (test_unit_ready(drive)) { + sleep(1); + printf("%c\r", progress[p++]); if (p==4) p=0; + i++; + } + printf("\nMedia Check time: %d sec", i); + + drive->cmd[0] = PLEXTOR_AS_RD; + drive->cmd[1] = 0x01; + drive->cmd[10]= 0x12; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,0x12) )) + { if (!drive->silent) sperror ("PLEXTOR_AS_GET_STATUS",drive->err); return drive->err; } + + if(!drive->silent) + { printf("Media Check result RAW: "); for (i=0; i<0x12; i++) printf("%02X ", drive->rd_buf[i]); printf("\n"); } + return 0; +} + diff --git a/lib/qpxscan/Makefile b/lib/qpxscan/Makefile new file mode 100644 index 0000000..492bfaa --- /dev/null +++ b/lib/qpxscan/Makefile @@ -0,0 +1,19 @@ +SRC = qpx_scan \ + qpx_scan_algo \ + qpx_writer + +HDRS = include/qpx_scan.h \ + include/qpx_scan_plugin_api.h \ + include/qpx_writer.h + +LIBN = qpxscan +SRCS = $(patsubst %,%.cpp, $(SRC)) +OBJS = $(patsubst %.cpp,%.o,$(SRCS)) + +VER_MAJOR = 0 +VER_MINOR = 7 +VER_MICRO = 0 + +LDLIBS += $(LIBS_DL) -lqpxtransport -lqpxplextor -L../lib $(LIBS_INET) + +include ../Makefile.lib diff --git a/lib/qpxscan/include/qpx_scan.h b/lib/qpxscan/include/qpx_scan.h new file mode 100644 index 0000000..8184e4a --- /dev/null +++ b/lib/qpxscan/include/qpx_scan.h @@ -0,0 +1,117 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2008, Gennady "ShultZ" Kozlov + * + * 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 __QPX_SCAN_H +#define __QPX_SCAN_H + +#include +#include + +#include + +//class drive_info; + +typedef char path[128]; + +static const path ppaths[] = { +#if defined (__unix) || defined (__unix__) || (defined(__APPLE__) && defined(__MACH__)) + "/usr/lib/qpxtool", + "/usr/local/lib/qpxtool", + "/usr/lib64/qpxtool", + "/usr/local/lib64/qpxtool", +#elif defined (_WIN32) + "plugins", +#endif + "" +}; + +class qpxwriter; + +//static const int ppaths_cnt = sizeof(ppaths) / sizeof(path); + +class qscanner { +public: + qscanner(drive_info* idev); + ~qscanner(); + drive_info* device() { return dev; }; + void setTestSpeed(int); + bool setTestWrite(bool); + int run(char*); + int check_test(unsigned int); + int errc_data(); + int* get_test_speeds(unsigned int); +// int plugins_probe(); + int plugins_probe(bool test, bool probe_enable); + int plugin_attach_fallback(); + int plugin_attach(char* name); + int plugin_attach(char* pname, bool probe_enable, bool no_detach, bool silent=1); + void plugin_detach(); +// int plugin_info(); + const char* plugin_name(); + const char* plugin_desc(); + bool is_plugin_attached() { return attached; }; +// int detect_check_capabilities(); + inline void setInterval(int sta, int end) { lba_sta = sta; lba_end = end; }; + void stop(); + void stat(); + +private: + bool stop_req, stat_req; + struct timeval s,e,blks,blke; + long lba_sta, lba_end; + int spd1X; + int spdKB; + float spdX; + bool WT_simul; + + //void show_avg(struct timeval s, struct timeval e, long lba); + void show_avg_speed(long lba); + //void calc_cur_speed(long sects, int* spdKB, float* spdX); + void calc_cur_speed(long sects); + + int readline(int fd, char *buf, int maxlen); + + int run_rd_transfer(); + int run_wr_transfer(); + int run_fete(); + + int run_cd_errc(); + int run_cd_jb(); + int run_cd_ta(); + + int run_dvd_errc(); + int run_dvd_jb(); + int run_dvd_ta(); + + int run_bd_errc(); + + int speed; + bool attached; + drive_info *dev; + scan_plugin *plugin; + qpxwriter *writer; + +#if defined (_WIN32) + HINSTANCE__ *pluginlib; +#else + void *pluginlib; +#endif + + scan_plugin* (*plugin_create) (drive_info*); + void (*plugin_destroy) (scan_plugin*); + bool listed; + // unsigned int chk_features; // media check features + char tchar; +}; + +#endif // __QPX_SCAN_H + diff --git a/lib/qpxscan/include/qpx_scan_plugin_api.h b/lib/qpxscan/include/qpx_scan_plugin_api.h new file mode 100644 index 0000000..6ea0036 --- /dev/null +++ b/lib/qpxscan/include/qpx_scan_plugin_api.h @@ -0,0 +1,304 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2008, Gennady "ShultZ" Kozlov + * + * 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 __QPX_SCAN_PLUGIN_API_H +#define __QPX_SCAN_PLUGIN_API_H + + +#include +#include + +/* +typedef enum testID{ + none = 0, + cd_errc, + cd_jb, + cd_fete, + cd_ta, + dvd_errc, + dvd_jb, + dvd_fete, + dvd_ta +}; +*/ + +#define CHK_RD_CD 0x00000001 +#define CHK_WR_CD 0x00000002 +#define CHK_RD_DVD 0x00000004 +#define CHK_WR_DVD 0x00000008 + +#define CHK_ERRC 0x00000010 // Error correction +#define CHK_JB 0x00000020 // Jitter/Asymmetry +#define CHK_FETE 0x00000040 // Focus/Tracking +#define CHK_TA 0x00000080 // Time Analyser + +#define CHK_ERRC_CD 0x00000100 +#define CHK_JB_CD 0x00000200 +#define CHK_FT_CD 0x00000400 +#define CHK_TA_CD 0x00000800 +#define CHK_TA_CDROM 0x00001800 + +#define CHK_ERRC_DVD 0x00002000 +#define CHK_JB_DVD 0x00004000 +#define CHK_FT_DVD 0x00008000 +#define CHK_TA_DVD 0x00010000 +#define CHK_TA_DVDROM 0x00020000 + +#define CHK_RD_BD 0x00040000 +#define CHK_WR_BD 0x00080000 +#define CHK_ERRC_BD 0x00100000 + +// ERRC returned data +#define ERRC_DATA_BLER 0x01 +#define ERRC_DATA_E11 0x02 +#define ERRC_DATA_E21 0x04 +#define ERRC_DATA_E31 0x08 +#define ERRC_DATA_E12 0x10 +#define ERRC_DATA_E22 0x20 +#define ERRC_DATA_E32 0x40 + +#define ERRC_DATA_PIE 0x06 +#define ERRC_DATA_PIF 0x08 +#define ERRC_DATA_POE 0x30 +#define ERRC_DATA_POF 0x40 + +#define ERRC_DATA_LDC 0x02 +#define ERRC_DATA_BIS 0x10 + +#define ERRC_DATA_UNCR 0x80 + +static const char errc_names_cd[][5] = { "BLER", "E11", "E21", "E31", "E12", "E22", "E32", "UNCR" }; +static const char errc_names_dvd[][5] = { "---", "PIE", "PI8", "PIF", "POE", "PO8", "POF", "UNCR" }; +static const char errc_names_bd[][5] = { "---", "LDC", "---", "---", "BIS", "---", "---", "UNCR" }; + +#define DEV_OK 0 +#define DEV_PROBED 1 +#define DEV_FAIL 2 + +struct drivedesc{ + char ven[9]; + int ven_ID; + char dev[17]; + int dev_ID; + int tests; +}; + +typedef struct drivedesc drivedesclist[]; + +class cd_errc { +public: + cd_errc() : bler(0), e11(0), e21(0), e31(0), e12(0), e22(0), e32(0), uncr(0) {}; + ~cd_errc() {}; + inline cd_errc& EMAX(cd_errc& o) { + if (bler jitter) jitter = o.jitter; + if (o.asymm > asymm) asymm = o.asymm; + } + return *this; + }; + inline cdvd_jb& EMIN(cdvd_jb& o) { + if (!set) { + jitter = o.jitter; + asymm = o.asymm; + set = 1; + } else { + if (o.jitter < jitter) jitter = o.jitter; + if (o.asymm < asymm) asymm = o.asymm; + } + return *this; + }; +private: + bool set; +}; + +class cdvd_ft { +public: + cdvd_ft() {fe=0; te=0;} + ~cdvd_ft() {}; + inline cdvd_ft& EMAX(cdvd_ft& o) { + if (o.fe > fe) fe = o.fe; + if (o.te > te) te = o.te; + return *this; + }; + inline cdvd_ft& operator= (cdvd_ft& o) { fe = o.fe; te = o.te; return *this; }; + inline cdvd_ft& operator+= (cdvd_ft& o) { fe += o.fe; te += o.te; return *this; }; + int fe; + int te; +}; + +#define TA_HIST_SIZE 512 + +class cdvd_ta { +public: + cdvd_ta() { pass=-1; clear(); }; + ~cdvd_ta() {}; + + void clear() { + memset(pit, 0, sizeof(int32_t)*TA_HIST_SIZE); + memset(land, 0, sizeof(int32_t)*TA_HIST_SIZE); + }; + + int pass; + int32_t pit[TA_HIST_SIZE]; + int32_t land[TA_HIST_SIZE]; +}; + +class drive_info; + +class scan_plugin { +public: + scan_plugin(drive_info* idev=NULL) { devlist = NULL; blklist = NULL; } + virtual ~scan_plugin() {} + virtual int probe_drive() { return DEV_FAIL; } +// virtual int check_drive()=0; + virtual int check_test(unsigned int test)=0; + virtual int errc_data()=0; + virtual int* get_test_speeds(unsigned int test) { return NULL; }; + virtual int start_test(unsigned int test, long slba, int &speed)=0; + virtual int scan_block(void* data, long* ilba)=0; + virtual int end_test()=0; + + virtual const char* name()=0; + virtual const char* desc()=0; + drivedesc *devlist; + drivedesc *blklist; + +protected: + + void set_read_speed(int &speed) { + dev->parms.read_speed_kb = (int) (speed * dev->parms.speed_mult); + set_rw_speeds(dev); + get_rw_speeds(dev); + speed = (int) (dev->parms.read_speed_kb / dev->parms.speed_mult); + } + void set_write_speed(int &speed) { + dev->parms.write_speed_kb = (int) (speed * dev->parms.speed_mult); + set_rw_speeds(dev); + get_rw_speeds(dev); + speed = (int) (dev->parms.write_speed_kb / dev->parms.speed_mult); + } + drive_info* dev; + unsigned int test; +}; + +extern "C" { + scan_plugin* plugin_create(drive_info* idev); + void plugin_destroy(scan_plugin* iplugin); +} + +//typedef char drive_supported(drive_info* drive); + +#endif // __QPX_SCAN_PLUGIN_API_H + diff --git a/lib/qpxscan/include/qpx_writer.h b/lib/qpxscan/include/qpx_writer.h new file mode 100644 index 0000000..3a92e34 --- /dev/null +++ b/lib/qpxscan/include/qpx_writer.h @@ -0,0 +1,109 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009-2010 Gennady "ShultZ" Kozlov + * + * 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 QPXWRITER_H +#define QPXWRITER_H + +class drive_info; + +class qpxwriter { +public: + qpxwriter(drive_info *idev); + virtual ~qpxwriter() {}; + + virtual void setSimul(bool isimul); + virtual int send_opc(); + virtual int open_session(); + virtual int open_track(uint32_t size); + virtual int close_track(); + virtual int fixate(); + virtual int write_data(int32_t lba, int sects); + + void stop(); +protected: + virtual int mmc_write(int32_t lba, int sects); + drive_info *dev; + bool stop_req; + bool simul; +}; + +// CD-R/W +// mostly usable, SAO only, tested on CD-RW +class qpxwriter_cd : public qpxwriter { +public: + qpxwriter_cd(drive_info *idev) : qpxwriter(idev) {}; + virtual ~qpxwriter_cd() {}; + + virtual int send_opc(); +// virtual int open_session(); + virtual int open_track(uint32_t size); + virtual int close_track(); + virtual int fixate(); +protected: + int set_write_parameters_def(bool bfree, bool simul); + int send_cue_sheet(uint32_t tsize); + int write_lead_in(); +}; + +// DVD-R(W) [/DL] +// tested on single layer media only, both -R and -RW OK +class qpxwriter_dvdminus : public qpxwriter { +public: + qpxwriter_dvdminus(drive_info *idev) : qpxwriter(idev) {}; + virtual ~qpxwriter_dvdminus() {}; + + virtual int open_session(); + virtual int open_track(uint32_t size); + virtual int close_track(); + virtual int fixate(); +}; + +// DVD+R(W) [/DL] +// tested on single layer media only, both +R and +RW OK +class qpxwriter_dvdplus : public qpxwriter { +public: + qpxwriter_dvdplus(drive_info *idev) : qpxwriter(idev) {}; + virtual ~qpxwriter_dvdplus() {}; + + virtual int open_session(); + virtual int open_track(uint32_t size); + virtual int close_track(); + virtual int fixate(); +private: + int fixate_rw(); + int fixate_r(); +}; + +// DVD-RAM +// OK +class qpxwriter_dvdram : public qpxwriter { +public: + qpxwriter_dvdram(drive_info *idev) : qpxwriter(idev) {}; + virtual ~qpxwriter_dvdram() {}; +}; + +// BD-R/RE +// untested!!! +class qpxwriter_bd : public qpxwriter { +public: + qpxwriter_bd(drive_info *idev) : qpxwriter(idev) {}; + virtual ~qpxwriter_bd() {}; + virtual int open_track(uint32_t size); + virtual int close_track(); + virtual int fixate(); +private: + int fixate_re(); + int fixate_r(); +}; + +#endif + diff --git a/lib/qpxscan/qpx_scan.cpp b/lib/qpxscan/qpx_scan.cpp new file mode 100644 index 0000000..a9cdbe6 --- /dev/null +++ b/lib/qpxscan/qpx_scan.cpp @@ -0,0 +1,438 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2009, Gennady "ShultZ" Kozlov + * + * 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 +#include + +#if defined (__unix) || defined (__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#include +#elif defined (_WIN32) +#include + +#define dlopen(n,f) LoadLibraryA(n) +#define dlsym(l,n) GetProcAddress(l,n) +#define dlclose(l) FreeLibrary(l) + +char serr[255]; + +char* dlerror() +{ + int err = GetLastError(); + if (!err) return 0; + sprintf(serr, "Library load error %d", err); + return serr; +}; + +#endif + +#include +#include + + +//#include +#include "qpx_scan.h" + +#include +#include + +#define FALLBACK_PLUGIN_NAME "C2P" + +qscanner::qscanner(drive_info* idev) { + dev=idev; + writer=NULL; + plugin=NULL; + pluginlib=NULL; + plugin_create=NULL; + plugin_destroy=NULL; + attached=0; + listed = 0; + speed = -1; + lba_sta=0; + lba_end=-1; + tchar=-1; +} + +qscanner::~qscanner() { + if (attached) plugin_detach(); +} + +void qscanner::setTestSpeed(int ispeed) { speed = ispeed; } + +bool qscanner::setTestWrite(bool simul) { + if (dev->media.type & DISC_DVDplus) { + if (isPlextor(dev)) { + if (isPlextorLockPresent(dev) || !plextor_px755_do_auth(dev) ) { + printf("Turning PLEXTOR DVD+R(W) TestWrite %s\n", simul ? "ON" : "OFF"); + WT_simul = 0; + dev->plextor.testwrite_dvdplus = simul; + return plextor_set_testwrite_dvdplus(dev); + } else { + printf("Found locked PLEXTOR drive. Can't handle DVD+R(W) TestWrite!\n"); + return 1; + } + } else { + if (simul) { + printf("TestWrite on DVD+R(W) supported on PLEXTOR drives only!\n"); + WT_simul = 0; + return 1; + } else { + WT_simul = 0; + return 0; + } + } + } else { + if ((dev->media.type & DISC_CD) && (dev->capabilities & CAP_TEST_WRITE_CD)) { + printf("Turning TestWrite (CD) %s\n", simul ? "ON" : "OFF"); + WT_simul = simul; + return 0; + } else if ((dev->media.type & DISC_DVDminus) && (dev->capabilities & CAP_TEST_WRITE_DVD)) { + printf("Turning TestWrite (DVD) %s\n", simul ? "ON" : "OFF"); + WT_simul = simul; + return 0; + } else { + WT_simul = 0; + if (simul) { + if ((dev->media.type & DISC_DVDRAM)) { + printf("DVD-RAM media doesn't support TestWrite!\n"); + } else { + printf("Drive doesn't support TestWrite on this media!\n"); + } + return 1; + } else { + return 0; + } + } + } +} + +void qscanner::stop() { + stop_req=1; + if (writer) writer->stop(); +}; + +void qscanner::stat() { + stat_req=1; +}; + + +int qscanner::run(char *test) { + int r=-1; + stop_req=0; + stat_req=0; + if (!dev->media.type) { + printf("No media detected!\n"); + return 1; + } + if (!(dev->media.type & (DISC_CD | DISC_DVD | DISC_BD))) { + printf("Unsupported media!\n"); + return 2; + } +// set_speed(dev,speed); + if (!strcmp(test, "rt")) { + if (lba_end<0 || lba_end>dev->media.capacity) lba_end = dev->media.capacity-1; + r=run_rd_transfer(); + } else if (!strcmp(test, "wt")) { + if (lba_end<0 || lba_end>dev->media.capacity_total) lba_end = dev->media.capacity_total-1; + r=run_wr_transfer(); + } else if (!strcmp(test, "errc")) { + if (lba_end<0 || lba_end>dev->media.capacity) lba_end = dev->media.capacity-1; + if (dev->media.type & DISC_CD) { + r=run_cd_errc(); + } else if (dev->media.type & DISC_DVD) { + r=run_dvd_errc(); + } else if (dev->media.type & DISC_BD) { + r=run_bd_errc(); + } + } else if (!strcmp(test, "jb")) { + if (lba_end<0 || lba_end>dev->media.capacity) lba_end = dev->media.capacity-1; + if (dev->media.type & DISC_CD) { + r=run_cd_jb(); + } else if (dev->media.type & DISC_DVD) { + r=run_dvd_jb(); + } + } else if (!strcmp(test, "ft")) { + if (lba_end<0 || lba_end>dev->media.capacity_total) lba_end = dev->media.capacity_total-1; + r=run_fete(); + } else if (!strcmp(test, "ta")) { + if (dev->media.type & DISC_CD) { + r=run_cd_ta(); + } else if (dev->media.type & DISC_DVD) { + r=run_dvd_ta(); + } + } + return r; +} + +int qscanner::check_test(unsigned int test) { + if (!attached) return -1; + return plugin->check_test(test); +} + +int qscanner::errc_data() { + if (!attached) return -1; + return plugin->errc_data(); +} + +int* qscanner::get_test_speeds(unsigned int test) { + if (!attached) return NULL; + return plugin->get_test_speeds(test); +} + +int qscanner::plugins_probe(bool test, bool probe_enable) { + char *pname; + char *ppath; + DIR *dir; + struct dirent *dentry; + int i; + int r=1; + for (i=0; strlen(ppaths[i]) && !attached; i++) { + ppath = (char*) ppaths[i]; + if (!dev->silent) + printf("Looking for plugins in %s...\n", ppath); + dir = opendir(ppath); + if (dir) { + dentry = readdir(dir); + while(dentry && !attached) { + if (!strncmp(dentry->d_name,"libqscan_",9)) { + if (!dev->silent) printf("FOUND: %s\n", dentry->d_name); + pname = (char*) malloc (strlen(dentry->d_name) + strlen(ppath) +2 ); +#ifdef _WIN32 + sprintf(pname, "%s\\%s", ppath, dentry->d_name); +#else + sprintf(pname, "%s/%s", ppath, dentry->d_name); +#endif + plugin_attach(pname, probe_enable, 0, !test); + if (attached) { + r=0; + if (test) { + plugin_detach(); + } else { + if (!strcmp(plugin->name(), FALLBACK_PLUGIN_NAME)) { + // printf("Found fallback plugin, return...\n"); + plugin_detach(); + r=1; + } + } + } + free(pname); + } + dentry = readdir(dir); + } + closedir(dir); + } + } + return r; +} + +int qscanner::plugin_attach_fallback() { return plugin_attach(FALLBACK_PLUGIN_NAME); } + +int qscanner::plugin_attach(char* name) { + char *pname; + char *ppath; + DIR *dir; + struct dirent *dentry; + int i; + int r=1; + if (attached || !name) return 2; + + for (i=0; strlen(ppaths[i]) && !attached; i++) { + ppath = (char*) ppaths[i]; + if (!dev->silent) printf("Looking for plugins in %s...\n", ppath); + dir = opendir(ppath); + if (dir) { + dentry = readdir(dir); + while(dentry && !attached) { + if (!strncmp(dentry->d_name,"libqscan_",9)) { + if (!dev->silent) printf("FOUND: %s\n", dentry->d_name); + pname = (char*) malloc (strlen(dentry->d_name) + strlen(ppath) +2 ); +#ifdef _WIN32 + sprintf(pname, "%s\\%s", ppath, dentry->d_name); +#else + sprintf(pname, "%s/%s", ppath, dentry->d_name); +#endif + plugin_attach(pname, 0, 1, 1); + + if (attached) { + if ( strcmp(plugin->name(), name)) plugin_detach(); + else r=0; + } + free(pname); + } + dentry = readdir(dir); + } + closedir(dir); + } + } + if (!attached) + printf("Can't find plugin '%s'\n", name); + else + if (!strcmp(plugin->name(), FALLBACK_PLUGIN_NAME)) { + printf("Fallback plugin loaded: '%s'\n", name); + } else { + printf("Forced plugin loaded: '%s'\n", name); + } + return r; +} + +int qscanner::plugin_attach(char* pname, bool probe_enable, bool no_detach, bool silent) { + bool blacklisted=0; + drivedesc* devlist; + if (attached) return 2; + listed=0; + +// pluginlib = dlopen( pname, RTLD_NOW | RTLD_GLOBAL); + pluginlib = dlopen( pname, RTLD_LAZY | RTLD_GLOBAL); + if (!pluginlib) { + printf("0 dlopen err: %s\n",dlerror()); +// printf("can't open library!\n"); + goto plugin_attach_liberr; + } +// if (!dev->silent) printf("pluginlib = %p\n", pluginlib); +#ifndef _WIN32 + if (dlerror()) { + printf("1 dlopen err: %s\n",dlerror()); + goto plugin_attach_err; + } +#endif + if (!dev->silent) printf("plugin lib opened: %s\n",pname); + plugin_create = (scan_plugin* (*) (drive_info*)) dlsym(pluginlib, "plugin_create"); +#ifndef _WIN32 + if (dlerror()) { +#else + if (!plugin_create) { +#endif + printf("error searching symbol \"plugin_create\" : %s\n",dlerror()); + goto plugin_attach_err; + } +// printf("symbol \"plugin_create\" found!\n"); + //*(void **) (&plugin_destroy) = dlsym(pluginlib, "plugin_destroy"); + plugin_destroy = (void (*) (scan_plugin*)) dlsym(pluginlib, "plugin_destroy"); +#ifndef _WIN32 + if (dlerror()) { +#else + if (!plugin_destroy) { +#endif + printf("error searching symbol \"plugin_destroy\" : %s\n", dlerror()); + goto plugin_attach_err; + } +// printf("symbol \"plugin_destroy\" found!\n"); +/* + slist = (drivedesc*) dlsym(pluginlib, "devlist"); + e = dlerror(); + if (e){ + printf("%s\nerror searching symbol \"devlist\"\n",e); + goto plugin_attach_err; + } +*/ +// printf("creating plugin\n"); + plugin = plugin_create(dev); +// printf("plugin info\n"); + if (!silent) printf("Found plugin: %s (%s)\n",plugin->name(),plugin->desc()); + + if (plugin->blklist) { + devlist = plugin->blklist; + if (!dev->silent) { + printf("Devices in blacklist:\n"); + for(int d=0; devlist[d].ven_ID>0; d++) { + printf(" %s %s*\n",devlist[d].ven,devlist[d].dev); + } + } + + for(int d=0; !blacklisted && devlist[d].ven_ID>0; d++) { + if (!strncmp(dev->ven, devlist[d].ven, strlen(devlist[d].ven)) && !strncmp(dev->dev,devlist[d].dev, strlen(devlist[d].dev))) + blacklisted=1; + } + + devlist = NULL; + if (blacklisted) { + printf("Plugin %s: device '%s' '%s' blacklisted! Detaching plugin...\n", plugin->name(), dev->ven, dev->dev); + attached=1; + plugin_detach(); + return 1; + } + } + + if (!probe_enable && plugin->devlist) { + devlist = plugin->devlist; + dev->chk_features = 0; + + if (!dev->silent) { + printf("Devices supported by this plugin:\n"); + for(int d=0; devlist[d].ven_ID>0 && devlist[d].dev_ID>0;d++) { + printf(" %s %s\n",devlist[d].ven,devlist[d].dev); + } + } + + for(int d=0; !listed && devlist[d].ven_ID>0 && devlist[d].dev_ID>0;d++) { + if (!strncmp(dev->ven, devlist[d].ven, strlen(devlist[d].ven)) && !strncmp(dev->dev,devlist[d].dev, strlen(devlist[d].dev))) { + dev->ven_ID = devlist[d].ven_ID; + dev->dev_ID = devlist[d].dev_ID; + dev->chk_features = devlist[d].tests; + listed = 1; + if (!silent) printf("device listed as: %s %s\n",devlist[d].ven,devlist[d].dev); + } + } + } + + if (!no_detach && !listed && (!probe_enable || plugin->probe_drive() == DEV_FAIL)) { + if (!dev->silent) { + if (probe_enable) + printf("Device probe failed! detaching plugin\n"); + else + printf("Device not listed! detaching plugin\n"); + } + attached=1; + plugin_detach(); + return 1; + } + attached=1; + if (!dev->silent) printf("plugin attached: %s\n", pname); + return 0; + +plugin_attach_err: + dlclose(pluginlib); + +plugin_attach_liberr: + attached=0; + printf("error attaching scan plugin %s\n", pname); + plugin=NULL; + plugin_create=NULL; + plugin_destroy=NULL; + listed=0; + return -1; +} + +void qscanner::plugin_detach() { + if (!dev->silent) printf("detaching plugin...\n"); + if (!attached) return; +// if (plugin_destroy!=NULL && plugin!=NULL) + (*plugin_destroy) (plugin); + attached=0; + dlclose(pluginlib); + plugin=NULL; + pluginlib=NULL; + plugin_create=NULL; + plugin_destroy=NULL; +} + +//int qscanner::plugin_info() {} + +const char* qscanner::plugin_name() { + if (!attached) return NULL; + return plugin->name(); +} + +const char* qscanner::plugin_desc() { + if (!attached) return NULL; + return plugin->desc(); +} + diff --git a/lib/qpxscan/qpx_scan_algo.cpp b/lib/qpxscan/qpx_scan_algo.cpp new file mode 100644 index 0000000..3bd28e8 --- /dev/null +++ b/lib/qpxscan/qpx_scan_algo.cpp @@ -0,0 +1,901 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2009, Gennady "ShultZ" Kozlov + * + * 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 +#include +#include + +#include +#include +#include +#include + +#include + +#define EMAXLINE 1024 + +#define USE_FFLUSH + +//void qscanner::show_avg(struct timeval s, struct timeval e, long lba) +void qscanner::show_avg_speed(long lba) +{ + double btime; + int spdKB; + float spdX; + btime = (e.tv_sec - s.tv_sec) + (e.tv_usec - s.tv_usec)/1000000.0; + spdKB = (int) (((lba - lba_sta + 1) << 1) / btime); + spdX = spdKB/(float)spd1X; + printf("\nTest time: %6.2fs\navg speed: %5.3f X %5d kB/s\n", btime, spdX, spdKB); +} + +void qscanner::calc_cur_speed(long sects) +{ + double btime; +// int spdKB; +// float spdX; + btime = (blke.tv_sec - blks.tv_sec) + (blke.tv_usec - blks.tv_usec)/1000000.0; + spdKB = (int) ((sects << 1) / btime); + spdX = spdKB/(float)spd1X; +// printf("\nTest time: %6.2fs\navg speed: %5.3f X %5d kB/s\n", btime, spdX, spdKB); +} + +int qscanner::readline(int fd, char *buf, int maxlen) { + int cnt=0; + char *cbuf=buf; + int r; + int sret; + fd_set rd_set; + timeval tv; + + FD_ZERO(&rd_set); + if (tchar>=0) { + cbuf[0] = tchar; + cnt++; + cbuf++; + tchar = -1; + } + while ( !stop_req && (cnt<(maxlen-1))) { + FD_SET(fd, &rd_set); + tv.tv_sec = 1; + tv.tv_usec = 0; + +#ifndef _WIN32 + sret = select(fd+1, &rd_set, NULL, NULL, &tv); +#else +// sret = WaitForSingleObject((void*)fd, 1000); +// errno = GetLastError(); + sret = 1; + errno = 0; +#endif +// printf("readline select(): %d, errno = [%d] %s\n", sret, errno, strerror(errno)); + if (sret < 0) { + printf("select(): %s\n", strerror(errno)); + if (errno == EINTR) continue; + return -1; + } + + else if (sret > 0 && FD_ISSET(fd, &rd_set)) { + r = read(fd, cbuf, 1); + if (r<0) { + printf("read = %d, %d, %s\n", r, errno, strerror(errno)); + switch (errno) { + case EAGAIN: + printf("EAGAIN\n"); + continue; + case EINTR: + printf("EINTR\n"); + continue; + default: + return -1; + } + } + if (!r) return -1; + // look for CR/LF/CR+LF + if (cnt && ((buf[cnt-1] == 0x0A) || (buf[cnt-1] == 0x0D))) { + if (buf[cnt] != 0x0A && buf[cnt] != 0x0D) { + tchar = buf[cnt]; + // } else { + // printf("cr+lf\n"); + } + buf[cnt-1]='\n'; + buf[cnt]=0; + return cnt; + } + cnt++; + cbuf++; + } + } + if (stop_req) return -1; + buf[cnt]='\n'; + buf[cnt+1]=0; + return cnt+1; +} + +// +// CD/DVD read transfer rate +// + +int qscanner::run_rd_transfer() +{ +// int flushcnt = FLUSH_LINES; + bool use_readcd = 0; + int bsize = -1; + int rsize = -1; + long lba; +// double btime; + int err=0; + int br =0; + dev->parms.read_speed_kb = (int) (speed * dev->parms.speed_mult); + set_rw_speeds(dev); + get_rw_speeds(dev); + speed = (int) (dev->parms.read_speed_kb / (dev->parms.speed_mult - 0.5)); + if (dev->media.type & DISC_CD) { + printf("Running READ transfer rate test on CD at speed %d...\n", speed); + if (dev->capabilities & CAP_DAE) use_readcd = 1; + rsize=15; + //bsize=75; + bsize = rsize*10; // 2 sec + spd1X = 150; + } else if (dev->media.type & DISC_DVD) { + printf("Running READ transfer rate test on DVD at speed %d...\n", speed); + rsize=16; + bsize= rsize * 64; // 2M + spd1X = 1385; + // check if DVD is region-protected + // dev->silent++; + get_rpc_state(dev); + read_disc_regions(dev); + //dev->media.dvdcss.method = DVDCSS_METHOD_DISC; + switch (dev->media.dvdcss.protection) { + case 0: // unprotected DVD + dev->media.dvdcss.method = DVDCSS_METHOD_NONE; + break; + case 1: // CSS/CPPM protected DVD + case 2: // CPRM - protected DVD + // just to auth to be able read data + dev->media.dvdcss.method = DVDCSS_METHOD_KEY; + if (css_disckey(dev)) { + printf("DVD auth failure!\n"); + return -1; + } + break; + default: + printf("Unknown DVD protection scheme: %02X\n",dev->media.dvdcss.protection); + dev->media.dvdcss.method = DVDCSS_METHOD_NONE; + break; + } + //dev->silent--; + } else if (dev->media.type & DISC_BD) { + printf("Running READ transfer rate test on DVD at speed %d...\n", speed); + rsize=16; + bsize= rsize * 64; // 2M + spd1X = 4500; + } else { + printf("Can't run read transfer rate test: unsupported media!\n"); + return -1; + } + if (stop_req) return 0; + printf("Using %s command\n", use_readcd ? "READ CD" : "READ"); +// set_read_speed(); +// wait_unit_ready(dev, 6); + + spinup(dev, 4); // spin up (4 seconds) + + // flush_cache(dev); + seek(dev, lba_sta); +// wait_unit_ready(dev, 6); + msleep(300); + gettimeofday(&s, NULL); + printf("Reading blocks: %ld - %ld (%ld MB)\n", lba_sta, lba_end, (lba_end-lba_sta) >> 9); + gettimeofday(&blks, NULL); +#ifdef USE_FFLUSH + fflush(stdout); +#endif + for (lba=lba_sta; (!stop_req) && !err && lba=lba_end) rsize = lba_end-lba; + if (use_readcd) + err = read_cd(dev, dev->rd_buf, lba, rsize, 0xF8); + else + err = read(dev, dev->rd_buf, lba, rsize); + br += rsize; + if (err) { + if ((err & 0x7FF00) == 0x23A00){ + printf("Media removed! Terminating scan...\n"); + } else { + printf("Read error! Terminating scan...\n"); + } + } +// printf("lba=%d +%d\n",lba,rsize); + if (lba>lba_sta && (!(lba%bsize) || lba+rsize == lba_end || stat_req)) { + gettimeofday(&blke, NULL); + /* + btime=(blke.tv_sec - blks.tv_sec) + (blke.tv_usec - blks.tv_usec)/1000000.0; + spdKB = (bsize << 1) / btime; + spdX = spdKB/(float)spd1X; + */ + calc_cur_speed( br ); + printf("lba: %7ld speed: %6.2f X %6d kB/s\r", lba, spdX, spdKB); +#ifdef USE_FFLUSH + fflush(stdout); +#endif + gettimeofday(&blks, NULL); + stat_req=0; + br=0; + } + } + gettimeofday(&e, NULL); + show_avg_speed(lba); + return 0; +} + +#ifdef DISABLE_INTERNAL_WT +int qscanner::run_wr_transfer() +{ + ssize_t n; + pid_t cpid; + pipe_t pipefd; + char linei[EMAXLINE+1]; + char lines[16]; + char linef[64]; + + int argc; + char **argv; + + printf("Running WRITE transfer rate test for CD/DVD/BD at speed %d...\n", speed); + +// creating argv... + argc = 0; + argv = (char**) malloc(sizeof(char*)); + argv[0] = NULL; + + // execlp("cat", "cat", "/home/kgs/cdrecord.log.cd", NULL); + argv = add_arg(argv, &argc, "cdrecord"); + + sprintf(linei, "dev=%s", dev->device); + argv = add_arg(argv, &argc, linei); + + argv = add_arg(argv, &argc, "-v"); + + if (WT_simul) + argv = add_arg(argv, &argc, "-dummy"); + + argv = add_arg(argv, &argc, "gracetime=2"); + argv = add_arg(argv, &argc, "driveropts=burnfree"); + + sprintf(lines, "speed=%d", speed); + argv = add_arg(argv, &argc, lines); + + if (dev->media.type & DISC_DVDpRW) { + sprintf(linef, "tsize=%dk", dev->media.capacity_total * 2); + } else { + sprintf(linef, "tsize=%dk", dev->media.capacity_free * 2); + } + argv = add_arg(argv, &argc, linef); + + argv = add_arg(argv, &argc, "-sao"); +#ifndef _WIN32 + argv = add_arg(argv, &argc, "/dev/zero"); +#else + argv = add_arg(argv, &argc, "ZERO"); +#endif + + printf("qscan: starting cdrecord...\n"); + printf("cdrecord args: \n"); + for(int i=0; argv[i]; i++) + printf("%s ",argv[i]); + printf("\n"); + +// printf("DUMMY mode, not executing cdrecord\n"); +// return 0; + + if ((cpid = createChildProcess(argv, &pipefd, NULL)) == -1) { + printf("qscan: can't create child process\n"); + return -1; + } + printf("qscan: child created, reading from pipe...\n"); + int wn, woffs; + while((n = readline((int)pipefd[0], linei, EMAXLINE))>=0) { + // while((n = read(pipefd[0], lineo, EMAXLINE)) > 0) { + // sprintf(linei,"\nread #%d: %d bytes\n\0",idx, n); + // write(sockfd, linei, strlen(linei)); + // idx++; +// printf(lineo); + woffs=0; + while (woffsmedia.type & (DISC_CD)) { + writer = new qpxwriter_cd(dev); + wsize=25; bsize=150; + spd1X = 176; + } else if (dev->media.type & (DISC_DVDminus)) { + writer = new qpxwriter_dvdminus(dev); + wsize=32; bsize=1024; + spd1X = 1385; + } else if (dev->media.type & (DISC_DVDplus)) { + writer = new qpxwriter_dvdplus(dev); + wsize=32; bsize=1024; + spd1X = 1385; + } else if (dev->media.type & (DISC_DVDRAM)) { + writer = new qpxwriter_dvdram(dev); + wsize=32; bsize=1024; + spd1X = 1385; + } else if (dev->media.type & (DISC_BD) && dev->media.type != DISC_BD_ROM) { + writer = new qpxwriter_bd(dev); + wsize=32; bsize=1024; + spd1X = 4500; + } else { + printf("Internal write transfer rate test not implemented for mounted media!"); + return -1; + } +// lba_end = 4096; + + get_wbuffer_capacity(dev,&ubuft,&ubuff); + printf("Write buffer capacity: %u kB\n", ubuft >> 10); + + wait_unit_ready(dev, 6); + printf("Writing blocks: %ld - %ld (%ld MB)\n", lba_sta, lba_end, (lba_end-lba_sta) >> 9); + + dev->parms.write_speed_kb = speed * spd1X; + set_rw_speeds(dev); + get_rw_speeds(dev); + writer->setSimul(WT_simul); + if (dev->media.type & DISC_CD) { + printf("Running write transfer rate test on CD at speed %d...\n", dev->parms.write_speed_kb / spd1X); + spd1X = 150; + } else if (dev->media.type & (DISC_DVD | DISC_BD)) { + printf("Running write transfer rate test on DVD/BD at speed %d...\n", dev->parms.write_speed_kb / spd1X); + } + + if (writer->open_session()) { + printf("Can't open session!\n"); + goto write_cleanup; + } + + if (writer->send_opc()) { + printf("OPC failed!\n"); + goto write_cleanup; + } + + if (writer->open_track(lba_end)) { + printf("Can't start new track!\n"); + goto write_cleanup; + } + + printf("Starting write...\n"); + memset(dev->rd_buf,0,bufsz_rd); + + gettimeofday(&s, NULL); + gettimeofday(&blks, NULL); + for (lba=lba_sta; !stop_req && !err && lba=lba_end) wsize = lba_end-lba; + + get_wbuffer_capacity(dev,&ubuft,&ubuff); + // if (ubuff >=0 && ubuff<(wsize<<11)) { + if ((ubuff >> 11)<((uint32_t)wsize)) { + msleep(20); + } + if (wsize && writer->write_data(lba, wsize)) { + printf("\nWrite error at sector %d (wsize=%d)\n", lba, wsize); + stop_req = 1; + } + if (!(lba%bsize) || lba+wsize == lba_end || stat_req || stop_req || !wsize) { + ubufp = (uint32_t) (ubuft ? 100*( 1.0f-ubuff/(float)ubuft) : 0); + gettimeofday(&blke, NULL); + /* + btime=(blke.tv_sec - blks.tv_sec) + (blke.tv_usec - blks.tv_usec)/1000000.0; + spdKB = (bsize << 1) / btime; + spdX = spdKB/(float)spd1X; + */ + calc_cur_speed(((lba-1)%bsize) + 1); + printf("lba: %7d speed: %6.2f X %6d kB/s, written: %4ldMB/%4ldMB, Ubuf: %3u%%\r", + lba, spdX, spdKB, (lba-lba_sta) >> 9, (lba_end-lba_sta) >> 9, ubufp); + gettimeofday(&blks, NULL); + stat_req=0; +#ifdef USE_FFLUSH + fflush(stdout); +#endif + } + } + printf("\n"); + gettimeofday(&e, NULL); + show_avg_speed(lba); + writer->close_track(); + + writer->fixate(); + start_stop(dev,0); + start_stop(dev,1); + delete writer; writer = NULL; + return 0; + +write_cleanup: + printf("Errors before writing! cleaning up...\n"); + flush_cache(dev,true); + start_stop(dev,0); + start_stop(dev,1); + delete writer; writer = NULL; + return 1; +} + +#endif // #ifdef DISABLE_INTERNAL_WT + +// +// CD algo's +// + +int qscanner::run_cd_errc() +{ + cd_errc err, err_tot, err_max; + int errc_data; + long lba=lba_sta; + long lbao; + if (!attached) return -1; + if (!(dev->media.type & DISC_CD)) return 1; + lba=0; + errc_data = plugin->errc_data(); +// seek(dev,lba); + if (plugin->start_test(CHK_ERRC_CD,lba,speed)) { + printf("CD ERRC test init failed!\n"); + return 2; + } + printf("Running CD Error Correction test at speed %d...\n", speed); + + spd1X = 150; + gettimeofday(&s, NULL); + wait_unit_ready(dev, 6); + printf("\nTesting %ld sectors: %ld - %ld\n", lba_end-lba_sta+1, lba_sta, lba_end); + printf(" lba | speed | BLER | E11 E21 E31 | E12 E22 E32 | UNCR\n"); + for (; (!stop_req) && lbascan_block((void*)&err,&lba)) + { printf("\nBlock scan error! terminating...\n"); stop_req=1; } + gettimeofday(&blke, NULL); + calc_cur_speed(lba-lbao); + printf("cur : %6ld | %6.2f X %5d kB/s | %5ld | %5ld %5ld %5ld | %5ld %5ld %5ld | %5ld\r", lba, spdX, spdKB, + err.bler, + err.e11,err.e21,err.e31, + err.e12,err.e22,err.e32, + err.uncr); + err_tot+=err; + err_max.EMAX(err); +#ifdef USE_FFLUSH + fflush(stdout); +#endif + } + plugin->end_test(); + gettimeofday(&e, NULL); + show_avg_speed(lba); + printf("\n%ld sectors tested: %ld - %ld\n", lba-lba_sta, lba_sta, lba-1); + printf("Test summary:\n"); + printf(" BLER | E11 E21 E31 | E12 E22 E32 | UNCR\n"); + printf("tot : %5ld | %5ld %5ld %5ld | %5ld %5ld %5ld | %5ld\n", + err_tot.bler, + err_tot.e11,err_tot.e21,err_tot.e31, + err_tot.e21,err_tot.e22,err_tot.e32, + err_tot.uncr); + printf("max : %5ld | %5ld %5ld %5ld | %5ld %5ld %5ld | %5ld\n", + err_max.bler, + err_max.e11,err_max.e21,err_max.e31, + err_max.e21,err_max.e22,err_max.e32, + err_max.uncr); + printf("avg : %5.2f | %5.2f %5.2f %5.2f | %5.2f %5.2f %5.2f | %5.2f\n", + err_tot.bler/(float)lba, + err_tot.e11/(float)lba,err_tot.e21/(float)lba,err_tot.e31/(float)lba, + err_tot.e21/(float)lba,err_tot.e22/(float)lba,err_tot.e32/(float)lba, + err_tot.uncr/(float)lba); +#ifdef USE_FFLUSH + fflush(stdout); +#endif + return 0; +} + +int qscanner::run_cd_jb() +{ + cdvd_jb jb, jb_min, jb_max; + long lba=lba_sta; + long lbao; + if (!attached) return -1; + if (!(dev->media.type & DISC_CD)) return 1; +// seek(dev,lba); + if (plugin->start_test(CHK_JB_CD,lba,speed)) { + printf("CD Jitter/Asymm test init failed!\n"); + return 2; + } + printf("Running CD Jitter/Asymm test at speed %d...\n", speed); + + spd1X = 150; + gettimeofday(&s, NULL); + wait_unit_ready(dev, 6); + printf("\nTesting %ld sectors: %ld - %ld\n", lba_end-lba_sta+1, lba_sta, lba_end); + printf(" lba | speed | Jitter | Asymm\n"); + for (; (!stop_req) && lbascan_block((void*)&jb,&lba)) + { printf("\nBlock scan error! terminating...\n"); stop_req=1; } + gettimeofday(&blke, NULL); + calc_cur_speed(lba-lbao); + printf("cur : %6ld | %6.2f X %5d kB/s | %6.2f | %6.2f\r", lba, spdX, spdKB, jb.jitter/1000.0, jb.asymm/10.0); + jb_min.EMIN(jb); + jb_max.EMAX(jb); +#ifdef USE_FFLUSH + fflush(stdout); +#endif + } + plugin->end_test(); + gettimeofday(&e, NULL); + show_avg_speed(lba); + printf("\n%ld sectors tested: %ld - %ld\n", lba-lba_sta, lba_sta, lba-1); + printf("Test summary:\n"); + printf(" Jitter | Asymm\n"); + printf(" min : %6.2f | %6.2f\n", jb_min.jitter/100.0, jb_min.asymm/10.0); + printf(" max : %6.2f | %6.2f\n", jb_max.jitter/100.0, jb_max.asymm/10.0); +// printf(" avg : %6.2f | %6.2f", lba, jb_max.jitter/100.0, jb_max.asymm/10.0); +#ifdef USE_FFLUSH + fflush(stdout); +#endif + return 0; +} + +int qscanner::run_cd_ta() +{ + if (!attached) return -1; + if (!(dev->media.type & DISC_CD)) return 1; + printf("Running CD Time Analyser test...\n"); + + printf("Can't start test: not implemented!\n"); + return -1; +} + +// +// DVD algo's +// + +int qscanner::run_dvd_errc() +{ + dvd_errc err, err_tot, err_max; + int errc_data; + long lba=lba_sta; + long lbas; + long lbao; + long pi8=0, pi8_max=0; + long po8=0, po8_max=0; + if (!attached) return -1; + if (!(dev->media.type & DISC_DVD)) return 1; +// lba=0; slba=0; + errc_data = plugin->errc_data(); +// seek(dev,lba); + if (plugin->start_test(CHK_ERRC_DVD,lba,speed)) { + printf("DVD ERRC test init failed!\n"); + return 2; + } + printf("Running DVD Error Correction test at speed %d...\n", speed); + + spd1X = 1385; + gettimeofday(&s, NULL); + wait_unit_ready(dev, 6); + lbas = lba; + printf("\nTesting %ld sectors: %ld - %ld\n", lba_end-lba_sta+1, lba_sta, lba_end); + printf(" lba | speed | PIE PI8 PIF | POE PO8 POF | UNCR\n"); + for (; (!stop_req) && lbascan_block((void*)&err,&lba)) + { printf("\nBlock scan error! terminating...\n"); stop_req=1; } + err_tot+=err; + err_max.EMAX(err); + pi8+=err.pie; + po8+=err.poe; + gettimeofday(&blke, NULL); + calc_cur_speed(lba-lbao); +// if (!(lba & 0x7F)) { + if ((lba-lbas) >= 0x80) { + if (pi8_maxend_test(); + gettimeofday(&e, NULL); + show_avg_speed(lba); + printf("\n%ld sectors tested: %ld - %ld\n", lba-lba_sta, lba_sta, lba-1); + printf("Test summary:\n"); + printf(" PIE PI8 PIF | POE PO8 POF | UNCR\n"); + printf("tot : %5ld %5ld %5ld | %5ld %5ld %5ld | %5ld\n", + err_tot.pie,err_tot.pie,err_tot.pif, + err_tot.poe,err_tot.poe,err_tot.pof, + err_tot.uncr); + printf("max : %5ld %5ld %5ld | %5ld %5ld %5ld | %5ld\n", + err_max.pie,pi8_max,err_max.pif, + err_max.poe,po8_max,err_max.pof, + err_max.uncr); + printf("avg : %5.2f %5.2f %5.2f | %5.2f %5.2f %5.2f | %5.2f\n", + err_tot.pie/(float)(lba>>4),err_tot.pie/(float)(lba>>7),err_tot.pif/(float)(lba>>4), + err_tot.poe/(float)(lba>>4),err_tot.poe/(float)(lba>>7),err_tot.pof/(float)(lba>>4), + err_tot.uncr/(float)(lba>>4)); +#ifdef USE_FFLUSH + fflush(stdout); +#endif + return 0; +} + +int qscanner::run_dvd_jb() +{ + cdvd_jb jb, jb_min, jb_max; + long lba=lba_sta; + long lbao; + if (!attached) return -1; + if (!(dev->media.type & DISC_DVD)) return 1; +// seek(dev,lba); + if (plugin->start_test(CHK_JB_DVD,lba,speed)) { + printf("DVD Jitter/Asymm test init failed!\n"); + return 2; + } + printf("Running DVD Jitter/Asymm test at speed %d...\n", speed); + + spd1X = 1385; + gettimeofday(&s, NULL); + wait_unit_ready(dev, 6); + printf("\nTesting %ld sectors: %ld - %ld\n", lba_end-lba_sta+1, lba_sta, lba_end); + printf(" lba | speed | Jitter | Asymm\n"); + for (; (!stop_req) && lbascan_block((void*)&jb,&lba)) + { printf("\nBlock scan error! terminating...\n"); stop_req=1; } + gettimeofday(&blke, NULL); + calc_cur_speed(lba-lbao); + printf("cur : %6ld | %6.2f X %5d kB/s | %6.2f | %6.2f\r", lba, spdX, spdKB, jb.jitter/1000.0, jb.asymm/10.0); + jb_min.EMIN(jb); + jb_max.EMAX(jb); +#ifdef USE_FFLUSH + fflush(stdout); +#endif + } + plugin->end_test(); + gettimeofday(&e, NULL); + show_avg_speed(lba); + printf("\n%ld sectors tested: %ld - %ld\n", lba-lba_sta, lba_sta, lba-1); + printf("Test summary:\n"); + printf(" Jitter | Asymm\n"); + printf(" min : %6.2f | %6.2f\n", jb_min.jitter/100.0, jb_min.asymm/10.0); + printf(" max : %6.2f | %6.2f\n", jb_max.jitter/100.0, jb_max.asymm/10.0); +// printf(" avg : %6.2f | %6.2f", lba, jb_max.jitter/100.0, jb_max.asymm/10.0); +#ifdef USE_FFLUSH + fflush(stdout); +#endif + return 0; +} + +#define MAX_RETRY 16 +int qscanner::run_fete() +{ + struct cdvd_ft ft, ft_max, ft_min; + long lba=lba_sta; + long lbao; + int retry = MAX_RETRY; + if (!attached) return -1; + if (dev->media.type & DISC_CD) { + printf("Running FE/TE test for CD at speed %d...\n", speed); +// if (dev->capabilities & CAP_DAE) use_readcd = 1; +// rsize=15; bsize=75; + spd1X = 150; + } else if (dev->media.type & DISC_DVD) { + printf("Running FE/TE test for DVD at speed %d...\n", speed); + spd1X = 1385; + } else if (dev->media.type & DISC_BD) { + printf("Running FE/TE test for BD at speed %d...\n", speed); + spd1X = 4500; + } else { + printf("Can't run FE/TE test: unsupported media!\n"); + return 1; + } + wait_unit_ready(dev, 6); + if (plugin->start_test(CHK_FETE,lba,speed)) { + printf("Scan init failed!\n"); + return 2; + } + gettimeofday(&s, NULL); +// wait_unit_ready(dev, 6); + printf("\nTesting %ld sectors: %ld - %ld\n", lba_end-lba_sta+1, lba_sta, lba_end); + printf(" lba | speed | FE | TE\n"); + gettimeofday(&blks, NULL); + for (; (!stop_req) && lbascan_block((void*)&ft,&lba)) + { printf("\nBlock scan error! terminating...\n"); stop_req=1; } + if(lba<0) { + if (retry--) + goto block_retry; + printf("\nDrive returned negative LBA %d times! terminating...\n", MAX_RETRY); + stop_req=1; + } + if(lba == lbao) { + if (retry--) + goto block_retry; + printf("\nDrive returned same LBA %d times! terminating...\n", MAX_RETRY); + stop_req=1; + } + retry = MAX_RETRY; + gettimeofday(&blke, NULL); + ft_max.EMAX(ft); + calc_cur_speed(lba-lbao); +// show current data + printf("cur : %6ld | %6.2f X %5d kB/s | %4d | %4d\n", lba, spdX, spdKB, ft.fe, ft.te); + blks.tv_sec = blke.tv_sec; + blks.tv_usec = blke.tv_usec; +#ifdef USE_FFLUSH + fflush(stdout); +#endif + } + plugin->end_test(); + gettimeofday(&e, NULL); + show_avg_speed(lba); + printf("\n%ld sectors tested: %ld - %ld\n", lba-lba_sta, lba_sta, lba-1); + printf("Test summary:\n"); + printf(" FE | TE\n"); + printf("max : %4d | %4d\n", ft_max.fe, ft_max.te); +#ifdef USE_FFLUSH + fflush(stdout); +#endif + return 0; +} + +int qscanner::run_dvd_ta() +{ + struct cdvd_ta ta; + long lba; + if (!attached) return -1; + if (!(dev->media.type & DISC_DVD)) return 1; + printf("Running DVD Time Analyser test...\n"); + if (plugin->start_test(CHK_TA,lba,speed)) { + printf("Scan init failed!\n"); + return 2; + } + + for (int i=0; i<3*dev->media.layers; i++) { + ta.pass = i; + plugin->scan_block(&ta, &lba); + printf(" idx pits lands\n"); + for (int idx=0; idxmedia.type & DISC_BD)) return 1; +// lba=0; slba=0; + errc_data = plugin->errc_data(); +// seek(dev,lba); + if (plugin->start_test(CHK_ERRC_BD,lba,speed)) { + printf("BD ERRC test init failed!\n"); + return 2; + } + printf("Running BD Error Correction test at speed %d...\n", speed); + + spd1X = 4500; + gettimeofday(&s, NULL); + wait_unit_ready(dev, 6); + lbas = lba; + printf("\nTesting %ld sectors: %ld - %ld\n", lba_end-lba_sta+1, lba_sta, lba_end); + printf(" lba | speed | LDC BIS | UNCR\n"); + for (; (!stop_req) && lbascan_block((void*)&err,&lba)) + { printf("\nBlock scan error! terminating...\n"); stop_req=1; } + err_tot+=err; + err_max.EMAX(err); + gettimeofday(&blke, NULL); + calc_cur_speed(lba-lbao); +// if (!(lba & 0x7F)) { + printf("cur : %7ld | %6.2f X %5d kB/s | %5ld %5ld | %5ld\r", + lba, spdX, spdKB, + err.ldc,err.bis, + err.uncr); +#ifdef USE_FFLUSH + fflush(stdout); +#endif + } + plugin->end_test(); + gettimeofday(&e, NULL); + show_avg_speed(lba); + printf("\n%ld sectors tested: %ld - %ld\n", lba-lba_sta, lba_sta, lba-1); + printf("Test summary:\n"); + printf(" LDC BIS | UNCR\n"); + printf("tot : %5ld %5ld | %5ld\n", + err_tot.ldc,err_tot.bis, + err_tot.uncr); + printf("max : %5ld %5ld | %5ld\n", + err_max.ldc,err_max.bis, + err_max.uncr); + printf("avg : %5.2f %5.2f | %5.2f\n", + err_tot.ldc/(float)(lba>>4),err_tot.bis/(float)(lba>>4), + err_tot.uncr/(float)(lba>>4)); +#ifdef USE_FFLUSH + fflush(stdout); +#endif + return 0; +} + diff --git a/lib/qpxscan/qpx_writer.cpp b/lib/qpxscan/qpx_writer.cpp new file mode 100644 index 0000000..2ba4e46 --- /dev/null +++ b/lib/qpxscan/qpx_writer.cpp @@ -0,0 +1,417 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009-2010 Gennady "ShultZ" Kozlov + * + * 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 + +#include +#include "qpx_writer.h" + +#define WTYPE_CD_DEFAULT WTYPE_TAO + +qpxwriter::qpxwriter(drive_info *idev) +{ + dev = idev; + stop_req = 0; +} + +void qpxwriter::stop() { + stop_req=1; + printf("\nqpxwriter: terminating...\n"); +} + +void qpxwriter::setSimul(bool isimul) { simul = isimul; }; +int qpxwriter::send_opc() { return 0; } +int qpxwriter::open_session() { return 0; } +int qpxwriter::open_track(uint32_t size) { return 0; } +int qpxwriter::close_track() { return 0; } +int qpxwriter::fixate() { return 0; } + +int qpxwriter::mmc_write(int32_t lba, int sects) +{ +// printf("qpxwriter: sector %ld (wsize=%d)\n", lba, sects); + dev->cmd[0] = MMC_WRITE; + + dev->cmd[2] = (lba >> 24) & 0xFF; + dev->cmd[3] = (lba >> 16) & 0xFF; + dev->cmd[4] = (lba >> 8) & 0xFF; + dev->cmd[5] = lba & 0xFF; + + dev->cmd[7] = (sects >> 8); + dev->cmd[8] = sects & 0xFF; + if ((dev->err=dev->cmd.transport(WRITE, dev->rd_buf, sects * 2048))) { +// printf("\nqpxwriter: Write error at sector %ld (wsize=%d)\n", lba, sects); + if (dev->err != 0x20408) sperror ("MMC_WRITE",dev->err); + return (dev->err); + } + return 0; +} + +int qpxwriter::write_data(int32_t lba, int sects) +{ + int r=0; +/* + if (lba>=0) { + bool f=0; + do { + if (f) usleep(20000); else f=1; + get_wbuffer_capacity(dev,&ubuft,&ubuff); + // if (ubuft) printf("%8d buf: %d %%\n", lba, 100*(ubuft-ubuff) / ubuft); + //} while ( dev->err == 0x20408 || (ubuff < sects*2048)); + } while ( !stop_req && !dev->err && (ubuff < sects*2048)); + } + if (stop_req) return -1; +*/ +// printf("test_unit_ready: %d\n",test_unit_ready(dev)); + if (lba>=0) for (int i=0; ird_buf[i*2048+0] = ((lba+i) >> 24) & 0xFF; + dev->rd_buf[i*2048+1] = ((lba+i) >> 16) & 0xFF; + dev->rd_buf[i*2048+2] = ((lba+i) >> 8) & 0xFF; + dev->rd_buf[i*2048+3] = (lba+i) & 0xFF; + } + do { + if (r) { + // printf("waiting...\n"); + msleep(500); + } + r = mmc_write(lba,sects); + } while (r == 0x20408); // for some drives like Plextor & LiteOn + return r; +} + +int qpxwriter_cd::send_opc() +{ + printf("Performing OPC...\n"); + + dev->cmd[0] = MMC_SEND_OPC_INFORMATION; + dev->cmd[1] = 0x01; + if ((dev->err=dev->cmd.transport(NONE, NULL, 0))) + {sperror ("SEND_OPC",dev->err); return (dev->err);} + + return (wait_unit_ready(dev, 60)); +} + +int qpxwriter_cd::set_write_parameters_def(bool bfree, bool simul) { + printf("Setting write parameters...\n"); +// printf("simul: %s\n", simul ? "ON":"OFF"); + + if (mode_sense(dev, MODE_PAGE_WRITE_PARAMETERS, 0, 60)) return -1; + + if (!!(dev->rd_buf[8+2] & 0x40) == bfree) { + printf("BURN-Free is %s\n", (dev->rd_buf[8+2] & 0x40) ? "ON" : "OFF" ); + } else { + printf("Turning BURN-Free %s\n", bfree ? "ON" : "OFF" ); + } +/* + printf("TestWrite is %s\n", (dev->rd_buf[8+2] & 0x10) ? "ON" : "OFF" ); + if (!!(dev->rd_buf[8+2] & 0x10) != simul) { + + } +*/ +/* + for (int i=0; i<52; i++) { + if (!(i%8)) printf("\n"); + printf(" %02X", dev->rd_buf[i+8]); + } + printf("\n"); +*/ + + memset(dev->rd_buf, 0, 60); + dev->rd_buf[8+0] = 0x05; + dev->rd_buf[8+1] = 0x32; + + dev->rd_buf[8+2] = ( (bfree ? 0x40 : 0) | (simul ? 0x10 : 0) ); // setting BURN-Free and TestWrite + dev->rd_buf[8+2] |= WTYPE_CD_DEFAULT; // write type = SAO + + dev->rd_buf[8+3] = 0x04; // track mode + dev->rd_buf[8+4] = 0x08; // data block type: Mode1 + + dev->rd_buf[8+8] = 0; // session format + dev->rd_buf[8+15] = 0x96; // audio pause length = 150 frames (2 sec) +/* + for (int i=0; i<52; i++) { + if (!(i%8)) printf("\n"); + printf(" %02X", dev->rd_buf[i+8]); + } + printf("\n"); +*/ + return (mode_select(dev,60)); +} + +int qpxwriter_cd::send_cue_sheet(uint32_t tsize) +{ + int foffs=0; + int lout = tsize+150; + printf("Sending CUE sheet...\n"); + + memset(dev->rd_buf,0,bufsz_rd); +// lead-in + dev->rd_buf[foffs+0] = 0x41; + dev->rd_buf[foffs+3] = 0x14; // for lead-in data form = 0x14 + foffs+=8; + +// track 0 pregap + dev->rd_buf[foffs+0] = 0x41; + dev->rd_buf[foffs+1] = 0x01; // track #1 + dev->rd_buf[foffs+2] = 0x00; // index = 0 - pregap + dev->rd_buf[foffs+3] = 0x10; // data form = 0x10 + // addr 00:00.00 msf + dev->rd_buf[foffs+5] = 0x00; + dev->rd_buf[foffs+6] = 0x00; + dev->rd_buf[foffs+7] = 0x00; + foffs+=8; + +// track 0 start + dev->rd_buf[foffs+0] = 0x41; + dev->rd_buf[foffs+1] = 0x01; // track #1 + dev->rd_buf[foffs+2] = 0x01; // index = 1 - track start + dev->rd_buf[foffs+3] = 0x10; // data form = 0x10 + // addr 00:02.00 msf + dev->rd_buf[foffs+5] = 0x00; + dev->rd_buf[foffs+6] = 0x02; + dev->rd_buf[foffs+7] = 0x00; + foffs+=8; + +// lead-out + dev->rd_buf[foffs+0] = 0x41; + dev->rd_buf[foffs+1] = 0xAA; // AA - lead-out + dev->rd_buf[foffs+2] = 0x01; // for lead-out index = 1 + dev->rd_buf[foffs+3] = 0x14; // for lead-in data form = 0x14 + // lead-out end addr + dev->rd_buf[foffs+5] = lout/4500; + dev->rd_buf[foffs+6] = (lout/75)%60; + dev->rd_buf[foffs+7] = lout%75; +/* + printf("Lead-Out pos: %02d:%02d.%02d\n", + dev->rd_buf[foffs+5], + dev->rd_buf[foffs+6], + dev->rd_buf[foffs+7] + ); +*/ + foffs+=8; + + dev->cmd[0] = MMC_SEND_CUE_SHEET; + dev->cmd[6] = (foffs >> 16) & 0xFF; + dev->cmd[7] = (foffs >> 8) & 0xFF; + dev->cmd[8] = foffs & 0xFF; + + if ((dev->err=dev->cmd.transport(WRITE, dev->rd_buf, foffs))) + {sperror ("SEND_CUE_SHEET",dev->err); return (dev->err);} + return 0; +} + +int qpxwriter_cd::write_lead_in() +{ + memset(dev->rd_buf,0,bufsz_rd); + int32_t lba = -150; + printf("Writing Lead-In...\n"); + for(int i=0; i<6; i++) { + if (write_data(lba,25)) return dev->err; + lba+=25; + } + return 0; +} + +/* +int qpxwriter_cd::open_session() +{ + return (set_write_parameters_def(0,0)); +} +*/ + +int qpxwriter_cd::open_track(uint32_t size) +{ +#if (WTYPE_CD_DEFAULT == WTYPE_SAO) + printf("Writing in SAO mode\n"); +#elif (WTYPE_CD_DEFAULT == WTYPE_TAO) + printf("Writing in TAO mode\n"); +#endif + if (set_write_parameters_def( ((dev->capabilities & CAP_BURN_FREE)) ,simul)) { + return 1; + } +/* + mode_sense(dev, 0x05, 0, 60); + for (int i=0; i<52; i++) { + if (!(i%8)) printf("\n"); + printf(" %02X", dev->rd_buf[i+8]); + } +*/ +#if (WTYPE_CD_DEFAULT == WTYPE_SAO) + if (send_cue_sheet(size)) return 1; + if (write_lead_in()) return 1; +#endif + return 0; +} + +int qpxwriter_cd::close_track() +{ + printf("Closing track...\n"); + if (flush_cache(dev, true)) return 1; + wait_unit_ready(dev, 300, 1); + +#if (WTYPE_CD_DEFAULT == WTYPE_TAO) + close_track_session(dev,1, CLOSE_TRACK); // close track +#endif +/* + do { + usleep(20000); + get_buffer_capacity(dev,&ubuft,&ubuff); + printf("buf: %d %%\r", 100*(ubuft-ubuff) / ubuft); + } while (ubuffcapabilities & CAP_BURN_FREE)); + bfree = 1; + if (mode_sense(dev, MODE_PAGE_WRITE_PARAMETERS, 0, 60)) return -1; + if (!!(dev->rd_buf[8+2] & 0x40) == bfree) { + printf("BURN-Free is %s\n", (dev->rd_buf[8+2] & 0x40) ? "ON" : "OFF" ); + } else { + printf("Turning BURN-Free %s\n", bfree ? "ON" : "OFF" ); + } + dev->rd_buf[8+2] = ( (bfree ? 0x40 : 0) | (simul ? 0x10 : 0) ); // setting BURN-Free and TestWrite + dev->rd_buf[8+2] |= WTYPE_SAO; // write type = SAO + return (mode_select(dev,60)); +} + +int qpxwriter_dvdminus::open_track(uint32_t size) { + return (reserve_track(dev,size)); +} + +int qpxwriter_dvdminus::close_track() { +/* + if (dev->media.type & (DISC_DVDmR | DISC_DVDmRDL)) { + printf("\nClosing track...\n"); + close_track_session(dev,1, CLOSE_TRACK); // close track + wait_unit_ready(dev, 1200, 1); + } +*/ + return 0; +} + +int qpxwriter_dvdminus::fixate() { + flush_cache(dev, 1); + printf("\nWaiting for drive to become ready...\n"); + wait_unit_ready(dev, 300, 1); + printf("\nClosing session...\n"); + close_track_session(dev,1, CLOSE_SESSION); // close session + wait_unit_ready(dev, 300, 1); + return 0; +} + +// +// DVD+ functions... +// +int qpxwriter_dvdplus::open_session() { + printf("Setting write parameters...\n"); + if (mode_sense(dev, MODE_PAGE_WRITE_PARAMETERS, 0, 60)) return -1; + dev->rd_buf[8+2] &= 0xF0; + dev->rd_buf[8+2] |= WTYPE_SAO; // write type = SAO + return (mode_select(dev,60)); +} + +int qpxwriter_dvdplus::open_track(uint32_t size) { + return 0; +} + +int qpxwriter_dvdplus::close_track() { + flush_cache(dev, 1); + wait_unit_ready(dev, 300, 1); + return 0; +} + +int qpxwriter_dvdplus::fixate() { + if (dev->media.type & (DISC_DVDpRW | DISC_DVDpRWDL)) { + return fixate_rw(); + } else if (dev->media.type & (DISC_DVDpR | DISC_DVDpRDL)) { + return fixate_r(); + } + return 0; +} + +int qpxwriter_dvdplus::fixate_rw() { + close_track_session(dev,1, CLOSE_SESSION); // close session + wait_unit_ready(dev, 300, 1); + return 0; +} + +int qpxwriter_dvdplus::fixate_r() { + printf("Closing track...\n"); + close_track_session(dev,1, CLOSE_TRACK); // close track + wait_unit_ready(dev, 300, 1); + printf("Closing session...\n"); + close_track_session(dev,1, CLOSE_SESSION); // close session + wait_unit_ready(dev, 300, 1); + return 0; +} + +// +// BD-R/RE fonctions +// +int qpxwriter_bd::open_track(uint32_t size) +{ + return (reserve_track(dev,size)); +} + +int qpxwriter_bd::close_track() +{ + flush_cache(dev, 1); + printf("\nWaiting for drive to become ready...\n"); + wait_unit_ready(dev, 300, 1); + return 0; +} + +int qpxwriter_bd::fixate() +{ + if (dev->media.type & (DISC_BD_RE)) { + return fixate_re(); + } else if (dev->media.type & (DISC_BD_R_SEQ | DISC_BD_R_RND)) { + return fixate_r(); + } + return 0; +} + +int qpxwriter_bd::fixate_re() +{ + return 0; +} + +int qpxwriter_bd::fixate_r() +{ + wait_unit_ready(dev, 300, 1); + printf("\nClosing track...\n"); + close_track_session(dev,1, CLOSE_TRACK); // close track + wait_unit_ready(dev, 300, 1); + + printf("\nClosing session...\n"); + close_track_session(dev,1, CLOSE_FINALIZE); // finalize disc + wait_unit_ready(dev, 300, 1); + return 0; +} + diff --git a/lib/qpxtransport/Makefile b/lib/qpxtransport/Makefile new file mode 100644 index 0000000..9e83d0c --- /dev/null +++ b/lib/qpxtransport/Makefile @@ -0,0 +1,28 @@ +SRC = common_functions \ + qpx_transport \ + qpx_mmc \ + qpx_mmc_css \ + threads \ + sense + +HDRS = include/qpx_mmc.h \ + include/qpx_mmc_defs.h \ + include/qpx_opcodes.h \ + include/qpx_transport.h \ + include/common_functions.h \ + include/threads.h \ + include/sense.h \ + include/csstables.h \ + include/colors.h + +LIBN = qpxtransport +SRCS = $(patsubst %,%.cpp, $(SRC)) +OBJS = $(patsubst %.cpp,%.o,$(SRCS)) + +VER_MAJOR = 0 +VER_MINOR = 7 +VER_MICRO = 0 + +LDLIBS += $(LIBS_HW) $(LIBS_THREAD) $(LIBS_INET) + +include ../Makefile.lib diff --git a/lib/qpxtransport/common_functions.cpp b/lib/qpxtransport/common_functions.cpp new file mode 100644 index 0000000..5e74293 --- /dev/null +++ b/lib/qpxtransport/common_functions.cpp @@ -0,0 +1,254 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2010, Gennady "ShultZ" Kozlov + * + * 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 +#include + +#include "common_functions.h" + +// 64-bit wide functions + +int64_t bswap64__internal(char* c) { + int64_t *d64; + char d[8]; + d[0]=c[7]; d[1]=c[6]; d[2]=c[5]; d[3]=c[4]; + d[4]=c[3]; d[5]=c[2]; d[6]=c[1]; d[7]=c[0]; + d64 = (int64_t*)d; + return *d64; +} + +uint64_t bswap64u__internal(char* c) { + uint64_t *d64; + char d[8]; + d[0]=c[7]; d[1]=c[6]; d[2]=c[5]; d[3]=c[4]; + d[4]=c[3]; d[5]=c[2]; d[6]=c[1]; d[7]=c[0]; + d64 = (uint64_t*)d; + return *d64; +} + +int64_t balign64__internal(char* c) { + int64_t *d64; + char d[8]; + d[0]=c[0]; d[1]=c[1]; d[2]=c[2]; d[3]=c[3]; + d[4]=c[4]; d[5]=c[5]; d[6]=c[6]; d[7]=c[7]; + d64 = (int64_t*)d; + return *d64; +} + +uint64_t balign64u__internal(char* c) { + uint64_t *d64; + char d[8]; + d[0]=c[0]; d[1]=c[1]; d[2]=c[2]; d[3]=c[3]; + d[4]=c[4]; d[5]=c[5]; d[6]=c[6]; d[7]=c[7]; + d64 = (uint64_t*)d; + return *d64; +} + +int64_t qpx_bswap64(char* c) { return bswap64__internal(c); }; +int64_t qpx_bswap64(unsigned char* c) { return bswap64__internal((char*)c); }; +int64_t qpx_bswap64(int64_t c) { return bswap64__internal((char*)&c); } +int64_t qpx_bswap64(uint64_t c) { return bswap64__internal((char*)&c); } + +uint64_t qpx_bswap64u(char* c) { return bswap64u__internal(c); }; +uint64_t qpx_bswap64u(unsigned char* c) { return bswap64u__internal((char*)c); }; +uint64_t qpx_bswap64u(int64_t c) { return bswap64u__internal((char*)&c); } +uint64_t qpx_bswap64u(uint64_t c) { return bswap64u__internal((char*)&c); } + +int64_t to64(char* c) { return balign64__internal(c); }; +int64_t to64(unsigned char* c) { return balign64__internal((char*)c); }; +int64_t to64(int64_t c) { return c; } +int64_t to64(uint64_t c) { return (int64_t)c; } + +uint64_t to64u(char* c) { return balign64u__internal(c); }; +uint64_t to64u(unsigned char* c) { return balign64u__internal((char*)c); }; +uint64_t to64u(int64_t c) { return (uint64_t)c; } +uint64_t to64u(uint64_t c) { return c; } + + +// 32-bit wide functions + +int32_t bswap32__internal(char* c) { + int32_t *d32; + char d[4]; + d[0]=c[3]; d[1]=c[2]; d[2]=c[1]; d[3]=c[0]; + d32 = (int32_t*)d; + return *d32; +} + +uint32_t bswap32u__internal(char* c) { + uint32_t *d32; + char d[4]; + d[0]=c[3]; d[1]=c[2]; d[2]=c[1]; d[3]=c[0]; + d32 = (uint32_t*)d; + return *d32; +} + +int32_t balign32__internal(char* c) { + int32_t *d32; + char d[4]; + d[0]=c[0]; d[1]=c[1]; d[2]=c[2]; d[3]=c[3]; + d32 = (int32_t*)d; + return *d32; +} + +uint32_t balign32u__internal(char* c) { + uint32_t *d32; + char d[4]; + d[0]=c[0]; d[1]=c[1]; d[2]=c[2]; d[3]=c[3]; + d32 = (uint32_t*)d; + return *d32; +} + +int32_t qpx_bswap32(char* c) { return bswap32__internal(c); }; +int32_t qpx_bswap32(unsigned char* c){ return bswap32__internal((char*)c); }; +int32_t qpx_bswap32(int32_t c) { return bswap32__internal((char*)&c); } +int32_t qpx_bswap32(uint32_t c) { return bswap32__internal((char*)&c); } + +uint32_t qpx_bswap32u(char* c) { return bswap32u__internal(c); }; +uint32_t qpx_bswap32u(unsigned char* c) { return bswap32u__internal((char*)c); }; +uint32_t qpx_bswap32u(int32_t c) { return bswap32u__internal((char*)&c); } +uint32_t qpx_bswap32u(uint32_t c) { return bswap32u__internal((char*)&c); } + +int32_t to32(char* c) { return balign32__internal(c); }; +int32_t to32(unsigned char* c) { return balign32__internal((char*)c); }; +int32_t to32(int32_t c) { return c; } +int32_t to32(uint32_t c) { return (int32_t)c; } + +uint32_t to32u(char* c) { return balign32u__internal(c); }; +uint32_t to32u(unsigned char* c) { return balign32u__internal((char*)c); }; +uint32_t to32u(int32_t c) { return *(uint32_t*)c; } +uint32_t to32u(uint32_t c) { return c; } + +// 16-bit wide functions + +int16_t bswap16__internal(char* c) { + int16_t *d16; + char d[2]; + d[0]=c[1]; d[1]=c[0]; + d16 = (int16_t*)d; + return *d16; +} + +uint16_t bswap16u__internal(char* c) { + uint16_t *d16; + char d[2]; + d[0]=c[1]; d[1]=c[0]; + d16 = (uint16_t*)d; + return *d16; +} + +int16_t balign16__internal(char* c) { + int16_t *d16; + char d[2]; + d[0]=c[0]; d[1]=c[1]; + d16 = (int16_t*)d; + return *d16; +} + +uint16_t balign16u__internal(char* c) { + uint16_t *d16; + char d[2]; + d[0]=c[0]; d[1]=c[1]; + d16 = (uint16_t*)d; + return *d16; +} + +int16_t qpx_bswap16(char* c) { return bswap16__internal(c); } +int16_t qpx_bswap16(unsigned char* c) { return bswap16__internal((char*)c); } +int16_t qpx_bswap16(int16_t c) { return bswap16__internal((char*)&c); } +int16_t qpx_bswap16(uint16_t c) { return bswap16__internal((char*)&c); } +uint16_t qpx_bswap16u(char* c) { return bswap16u__internal(c); } +uint16_t qpx_bswap16u(unsigned char* c) { return bswap16u__internal((char*)c); } +uint16_t qpx_bswap16u(int16_t c) { return bswap16__internal((char*)&c); } +uint16_t qpx_bswap16u(uint16_t c) { return bswap16__internal((char*)&c); } + + +int16_t to16(char* c) { return balign16__internal(c); }; +int16_t to16(unsigned char* c) { return balign16__internal((char*)c); }; +int16_t to16(int16_t c) { return c; } +int16_t to16(uint16_t c) { return (int16_t)c; } +uint16_t to16u(char* c) { return balign16u__internal(c); }; +uint16_t to16u(unsigned char* c) { return balign16u__internal((char*)c); }; +uint16_t to16u(int16_t c) { return (uint16_t)c; } +uint16_t to16u(uint16_t c) { return c; } + +void lba2msf(int lba, msf* time){ + time->m = lba/4500; + time->s = (lba/75)%60; + time->f = lba % 75; +} + +int msf2lba(msf time){ + return time.m * 4500 + time.s * 75 + time.f; +} + +void int2hms(int intt, hms* time){ + time->h = intt/3600; + time->m = (intt/60)%60; + time->s = intt % 60; +} + +#ifndef _WIN32 + +int min(int a, int b) +{ + if (a<=b) + return a; + return b; +} + +int max(int a, int b) +{ + if (a>b) + return a; + return b; +} + +#else + +int inet_aton(const char *cp, struct in_addr *addr) +{ + addr->s_addr = inet_addr(cp); + return 1; +} + +#endif + +void remove_double_spaces(char* str) +{ + int len = strlen(str); + while(len--) { + if (*str == 0x20 && str[1] == 0x20) + strcpy(str, str+1); + else + str++; + } +} + +void remove_end_spaces(char* str) +{ + int len = strlen(str); + while (str[--len] == 0x20) + str[len] = 0; +} + +int dispers(int m, int* arr, int l) { + if ((!m) || (!arr) || (l<2)) return 0; + long disp = 0; + int i; + int q; + for (i=0; i + * + * 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 _COLORS_H_INCLUDED +#define _COLORS_H_INCLUDED + +#define COL_GR0 "\E[30;60m" +#define COL_RED "\E[31;60m" +#define COL_GRN "\E[32;60m" +#define COL_YEL "\E[33;60m" +#define COL_BLU "\E[34;60m" +#define COL_VIOL "\E[35;60m" +#define COL_CYAN "\E[36;60m" +#define COL_GR1 "\E[37;60m" +#define COL_NO__ "\E[38;60m" +#define COL_WHITE "\E[39;60m" + +#define COL_NORM "\E[0m" + +#endif + diff --git a/lib/qpxtransport/include/common_functions.h b/lib/qpxtransport/include/common_functions.h new file mode 100644 index 0000000..432d41f --- /dev/null +++ b/lib/qpxtransport/include/common_functions.h @@ -0,0 +1,253 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2010 Gennady "ShultZ" Kozlov + * + * 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 __common_functions_h +#define __common_functions_h + +//#warning "COMMON_FUNCTIONS_H" + +#include + +#if defined(__unix) || defined(__unix__) + +//#warning "UNIX" + + +#if defined(__APPLE__) && defined(__MACH__) +#include +#elif defined(__linux) +#include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include +#endif + + +#include + +#elif defined(_WIN32) + +#include +#include + +#ifndef _SOCKLEN_T +typedef int socklen_t; +#define EADDRINUSE WSAEADDRINUSE +#define ECONNABORTED WSAECONNABORTED + +#define SHUT_RD SD_RECEIVE +#define SHUT_WR SD_SEND +#define SHUT_RDWR SD_BOTH +#endif + +#endif + +typedef struct{ + int m; + int s; + int f; +} msf; + +typedef struct{ + int h; + int m; + int s; +} hms; + +extern int64_t qpx_bswap64(char* c); +extern int64_t qpx_bswap64(unsigned char* c); +extern int64_t qpx_bswap64(int64_t c_); +extern int64_t qpx_bswap64(uint64_t c_); +extern uint64_t qpx_bswap64u(char* c); +extern uint64_t qpx_bswap64u(unsigned char* c); +extern uint64_t qpx_bswap64u(int64_t c_); +extern uint64_t qpx_bswap64u(uint64_t c_); + +extern int32_t qpx_bswap32(char* c); +extern int32_t qpx_bswap32(unsigned char* c); +extern int32_t qpx_bswap32(int32_t c_); +extern int32_t qpx_bswap32(uint32_t c_); +extern uint32_t qpx_bswap32u(char* c); +extern uint32_t qpx_bswap32u(unsigned char* c); +extern uint32_t qpx_bswap32u(int32_t c_); +extern uint32_t qpx_bswap32u(uint32_t c_); + +extern int16_t qpx_bswap16(char* c); +extern int16_t qpx_bswap16(unsigned char* c); +extern int16_t qpx_bswap16(int16_t c); +extern int16_t qpx_bswap16(uint16_t c); +extern uint16_t qpx_bswap16u(char* c); +extern uint16_t qpx_bswap16u(unsigned char* c); +extern uint16_t qpx_bswap16u(int16_t c); +extern uint16_t qpx_bswap16u(uint16_t c); + + + +extern int64_t to64(char* c); +extern int64_t to64(unsigned char* c); +extern int64_t to64(int64_t c_); +extern int64_t to64(uint64_t c_); + +extern uint64_t to64u(char* c); +extern uint64_t to64u(unsigned char* c); +extern uint64_t to64u(int64_t c_); +extern uint64_t to64u(uint64_t c_); + +extern int32_t to32(char* c); +extern int32_t to32(unsigned char* c); +extern int32_t to32(int32_t c_); +extern int32_t to32(uint32_t c_); + +extern uint32_t to32u(char* c); +extern uint32_t to32u(unsigned char* c); +extern uint32_t to32u(int32_t c_); +extern uint32_t to32u(uint32_t c_); + +extern int16_t to16(char* c); +extern int16_t to16(unsigned char* c); +extern int16_t to16(int16_t c_); +extern int16_t to16(uint16_t c_); + +extern uint16_t to16u(char* c); +extern uint16_t to16u(unsigned char* c); +extern uint16_t to16u(int16_t c_); +extern uint16_t to16u(uint16_t c_); + + +//#if qpx_BYTE_ORDER == qpx_BIG_ENDIAN +#if BYTE_ORDER == BIG_ENDIAN + +//#warning "BIG ENDIAN" +#define ntoh64(x) to64(x) +#define ntoh64u(x) to64u(x) +#define ntoh32(x) to32(x) +#define ntoh32u(x) to32u(x) +#define ntoh16(x) to16(x) +#define ntoh16u(x) to16u(x) + +#define hton64(x) to64(x) +#define hton64u(x) to64u(x) +#define hton32(x) to32(x) +#define hton32u(x) to32u(x) +#define hton16(x) to16(x) +#define hton16u(x) to16u(x) + + +#define be2cpu64(x) hton64(x) +#define be2cpu64u(x) hton64u(x) +#define be2cpu32(x) hton32(x) +#define be2cpu32u(x) hton32u(x) +#define be2cpu16(x) hton16(x) +#define be2cpu16u(x) hton16u(x) + +#define cpu2be64(x) hton64(x) +#define cpu2be64u(x) hton64u(x) +#define cpu2be32(x) hton32(x) +#define cpu2be32u(x) hton32u(x) +#define cpu2be16(x) hton16(x) +#define cpu2be16u(x) hton16u(x) + + +#define le2cpu64(x) qpx_bswap64(x) +#define le2cpu64u(x) qpx_bswap64u(x) +#define le2cpu32(x) qpx_bswap32(x) +#define le2cpu32u(x) qpx_bswap32u(x) +#define le2cpu16(x) qpx_bswap16(x) +#define le2cpu16u(x) qpx_bswap16u(x) + +#define cpu2le64(x) qpx_bswap64(x) +#define cpu2le64u(x) qpx_bswap64u(x) +#define cpu2le32(x) qpx_bswap32(x) +#define cpu2le32u(x) qpx_bswap32u(x) +#define cpu2le16(x) qpx_bswap16(x) +#define cpu2le16u(x) qpx_bswap16u(x) + +//#elif qpx_BYTE_ORDER == qpx_LITTLE_ENDIAN +#elif BYTE_ORDER == LITTLE_ENDIAN + +//#warning "LITTLE ENDIAN" +#define ntoh64(x) qpx_bswap64(x) +#define ntoh64u(x) qpx_bswap64u(x) +#define ntoh32(x) qpx_bswap32(x) +#define ntoh32u(x) qpx_bswap32u(x) +#define ntoh16(x) qpx_bswap16(x) +#define ntoh16u(x) qpx_bswap16u(x) + +#define hton64(x) qpx_bswap64(x) +#define hton64u(x) qpx_bswap64u(x) +#define hton32(x) qpx_bswap32(x) +#define hton32u(x) qpx_bswap32u(x) +#define hton16(x) qpx_bswap16(x) +#define hton16u(x) qpx_bswap16u(x) + + +#define be2cpu64(x) qpx_bswap64(x) +#define be2cpu64u(x) qpx_bswap64u(x) +#define be2cpu32(x) qpx_bswap32(x) +#define be2cpu32u(x) qpx_bswap32u(x) +#define be2cpu16(x) qpx_bswap16(x) +#define be2cpu16u(x) qpx_bswap16u(x) + +#define cpu2be64(x) qpx_bswap64(x) +#define cpu2be64u(x) qpx_bswap64u(x) +#define cpu2be32(x) qpx_bswap32(x) +#define cpu2be32u(x) qpx_bswap32u(x) +#define cpu2be16(x) qpx_bswap16(x) +#define cpu2be16u(x) qpx_bswap16u(x) + + +#define le2cpu64(x) to64(x) +#define le2cpu64u(x) to64u(x) +#define le2cpu32(x) to32(x) +#define le2cpu32u(x) to32u(x) +#define le2cpu16(x) to16(x) +#define le2cpu16u(x) to16u(x) + +#define cpu2le64(x) to64(x) +#define cpu2le64u(x) to64u(x) +#define cpu2le32(x) to32(x) +#define cpu2le32u(x) to32u(x) +#define cpu2le16(x) to16(x) +#define cpu2le16u(x) to16u(x) + + +//#elif qpx_BYTE_ORDER == qpx_PDP_ENDIAN +#elif BYTE_ORDER == PDP_ENDIAN + +#error "PDP endian byte order not supported!" + +#endif + + +extern void lba2msf(int lba, msf* time); +extern int msf2lba(msf time); +extern void int2hms(int intt, hms* time); + + +#if defined (_WIN32) +#include +#define msleep(t) Sleep(t) +#define sleep(t) Sleep((t) << 10) +extern int inet_aton(const char *cp, struct in_addr *addr); +#else +#define msleep(t) usleep((t) << 10) +extern int min(int a, int b); +extern int max(int a, int b); +#endif + + +extern void remove_double_spaces(char* str); +extern void remove_end_spaces(char* str); +//extern void file_path_name(char* str, char* fpath, char* fname); +//extern void file_suf_rm(char* str); +extern int dispers(int m, int* arr, int l); + +#endif + diff --git a/lib/qpxtransport/include/csstables.h b/lib/qpxtransport/include/csstables.h new file mode 100644 index 0000000..429f439 --- /dev/null +++ b/lib/qpxtransport/include/csstables.h @@ -0,0 +1,392 @@ +/***************************************************************************** + * csstables.h: CSS Tables for DVD unscrambling + ***************************************************************************** + * Copyright (C) 1999-2001 VideoLAN + * $Id: csstables.h 20629 2006-11-03 12:25:56Z diego $ + * + * Author: Stéphane Borel + * + * based on: + * - css-auth by Derek Fawcus + * - DVD CSS ioctls example program by Andrew T. Veliath + * - The Divide and conquer attack by Frank A. Stevenson + * - DeCSSPlus by Ethan Hawke + * - DecVOB + * see http://www.lemuria.org/DeCSS/ by Tom Vogt for more information. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + + +static unsigned char p_css_tab1[ 256 ] = +{ + 0x33, 0x73, 0x3b, 0x26, 0x63, 0x23, 0x6b, 0x76, + 0x3e, 0x7e, 0x36, 0x2b, 0x6e, 0x2e, 0x66, 0x7b, + 0xd3, 0x93, 0xdb, 0x06, 0x43, 0x03, 0x4b, 0x96, + 0xde, 0x9e, 0xd6, 0x0b, 0x4e, 0x0e, 0x46, 0x9b, + 0x57, 0x17, 0x5f, 0x82, 0xc7, 0x87, 0xcf, 0x12, + 0x5a, 0x1a, 0x52, 0x8f, 0xca, 0x8a, 0xc2, 0x1f, + 0xd9, 0x99, 0xd1, 0x00, 0x49, 0x09, 0x41, 0x90, + 0xd8, 0x98, 0xd0, 0x01, 0x48, 0x08, 0x40, 0x91, + 0x3d, 0x7d, 0x35, 0x24, 0x6d, 0x2d, 0x65, 0x74, + 0x3c, 0x7c, 0x34, 0x25, 0x6c, 0x2c, 0x64, 0x75, + 0xdd, 0x9d, 0xd5, 0x04, 0x4d, 0x0d, 0x45, 0x94, + 0xdc, 0x9c, 0xd4, 0x05, 0x4c, 0x0c, 0x44, 0x95, + 0x59, 0x19, 0x51, 0x80, 0xc9, 0x89, 0xc1, 0x10, + 0x58, 0x18, 0x50, 0x81, 0xc8, 0x88, 0xc0, 0x11, + 0xd7, 0x97, 0xdf, 0x02, 0x47, 0x07, 0x4f, 0x92, + 0xda, 0x9a, 0xd2, 0x0f, 0x4a, 0x0a, 0x42, 0x9f, + 0x53, 0x13, 0x5b, 0x86, 0xc3, 0x83, 0xcb, 0x16, + 0x5e, 0x1e, 0x56, 0x8b, 0xce, 0x8e, 0xc6, 0x1b, + 0xb3, 0xf3, 0xbb, 0xa6, 0xe3, 0xa3, 0xeb, 0xf6, + 0xbe, 0xfe, 0xb6, 0xab, 0xee, 0xae, 0xe6, 0xfb, + 0x37, 0x77, 0x3f, 0x22, 0x67, 0x27, 0x6f, 0x72, + 0x3a, 0x7a, 0x32, 0x2f, 0x6a, 0x2a, 0x62, 0x7f, + 0xb9, 0xf9, 0xb1, 0xa0, 0xe9, 0xa9, 0xe1, 0xf0, + 0xb8, 0xf8, 0xb0, 0xa1, 0xe8, 0xa8, 0xe0, 0xf1, + 0x5d, 0x1d, 0x55, 0x84, 0xcd, 0x8d, 0xc5, 0x14, + 0x5c, 0x1c, 0x54, 0x85, 0xcc, 0x8c, 0xc4, 0x15, + 0xbd, 0xfd, 0xb5, 0xa4, 0xed, 0xad, 0xe5, 0xf4, + 0xbc, 0xfc, 0xb4, 0xa5, 0xec, 0xac, 0xe4, 0xf5, + 0x39, 0x79, 0x31, 0x20, 0x69, 0x29, 0x61, 0x70, + 0x38, 0x78, 0x30, 0x21, 0x68, 0x28, 0x60, 0x71, + 0xb7, 0xf7, 0xbf, 0xa2, 0xe7, 0xa7, 0xef, 0xf2, + 0xba, 0xfa, 0xb2, 0xaf, 0xea, 0xaa, 0xe2, 0xff +}; + +static unsigned char p_css_tab2[ 256 ] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, + 0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15, + 0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c, + 0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23, + 0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a, + 0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31, + 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, + 0x49, 0x48, 0x4b, 0x4a, 0x4d, 0x4c, 0x4f, 0x4e, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x5b, 0x5a, 0x59, 0x58, 0x5f, 0x5e, 0x5d, 0x5c, + 0x52, 0x53, 0x50, 0x51, 0x56, 0x57, 0x54, 0x55, + 0x6d, 0x6c, 0x6f, 0x6e, 0x69, 0x68, 0x6b, 0x6a, + 0x64, 0x65, 0x66, 0x67, 0x60, 0x61, 0x62, 0x63, + 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, + 0x76, 0x77, 0x74, 0x75, 0x72, 0x73, 0x70, 0x71, + 0x92, 0x93, 0x90, 0x91, 0x96, 0x97, 0x94, 0x95, + 0x9b, 0x9a, 0x99, 0x98, 0x9f, 0x9e, 0x9d, 0x9c, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x89, 0x88, 0x8b, 0x8a, 0x8d, 0x8c, 0x8f, 0x8e, + 0xb6, 0xb7, 0xb4, 0xb5, 0xb2, 0xb3, 0xb0, 0xb1, + 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xa4, 0xa5, 0xa6, 0xa7, 0xa0, 0xa1, 0xa2, 0xa3, + 0xad, 0xac, 0xaf, 0xae, 0xa9, 0xa8, 0xab, 0xaa, + 0xdb, 0xda, 0xd9, 0xd8, 0xdf, 0xde, 0xdd, 0xdc, + 0xd2, 0xd3, 0xd0, 0xd1, 0xd6, 0xd7, 0xd4, 0xd5, + 0xc9, 0xc8, 0xcb, 0xca, 0xcd, 0xcc, 0xcf, 0xce, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf6, 0xf7, 0xf4, 0xf5, 0xf2, 0xf3, 0xf0, 0xf1, + 0xed, 0xec, 0xef, 0xee, 0xe9, 0xe8, 0xeb, 0xea, + 0xe4, 0xe5, 0xe6, 0xe7, 0xe0, 0xe1, 0xe2, 0xe3 +}; + +static unsigned char p_css_tab3[ 512 ] = +{ + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff, + 0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff +}; + +static unsigned char p_css_tab4[ 256 ] = +{ + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + +static unsigned char p_css_tab5[ 256 ] = +{ + 0xff, 0x7f, 0xbf, 0x3f, 0xdf, 0x5f, 0x9f, 0x1f, + 0xef, 0x6f, 0xaf, 0x2f, 0xcf, 0x4f, 0x8f, 0x0f, + 0xf7, 0x77, 0xb7, 0x37, 0xd7, 0x57, 0x97, 0x17, + 0xe7, 0x67, 0xa7, 0x27, 0xc7, 0x47, 0x87, 0x07, + 0xfb, 0x7b, 0xbb, 0x3b, 0xdb, 0x5b, 0x9b, 0x1b, + 0xeb, 0x6b, 0xab, 0x2b, 0xcb, 0x4b, 0x8b, 0x0b, + 0xf3, 0x73, 0xb3, 0x33, 0xd3, 0x53, 0x93, 0x13, + 0xe3, 0x63, 0xa3, 0x23, 0xc3, 0x43, 0x83, 0x03, + 0xfd, 0x7d, 0xbd, 0x3d, 0xdd, 0x5d, 0x9d, 0x1d, + 0xed, 0x6d, 0xad, 0x2d, 0xcd, 0x4d, 0x8d, 0x0d, + 0xf5, 0x75, 0xb5, 0x35, 0xd5, 0x55, 0x95, 0x15, + 0xe5, 0x65, 0xa5, 0x25, 0xc5, 0x45, 0x85, 0x05, + 0xf9, 0x79, 0xb9, 0x39, 0xd9, 0x59, 0x99, 0x19, + 0xe9, 0x69, 0xa9, 0x29, 0xc9, 0x49, 0x89, 0x09, + 0xf1, 0x71, 0xb1, 0x31, 0xd1, 0x51, 0x91, 0x11, + 0xe1, 0x61, 0xa1, 0x21, 0xc1, 0x41, 0x81, 0x01, + 0xfe, 0x7e, 0xbe, 0x3e, 0xde, 0x5e, 0x9e, 0x1e, + 0xee, 0x6e, 0xae, 0x2e, 0xce, 0x4e, 0x8e, 0x0e, + 0xf6, 0x76, 0xb6, 0x36, 0xd6, 0x56, 0x96, 0x16, + 0xe6, 0x66, 0xa6, 0x26, 0xc6, 0x46, 0x86, 0x06, + 0xfa, 0x7a, 0xba, 0x3a, 0xda, 0x5a, 0x9a, 0x1a, + 0xea, 0x6a, 0xaa, 0x2a, 0xca, 0x4a, 0x8a, 0x0a, + 0xf2, 0x72, 0xb2, 0x32, 0xd2, 0x52, 0x92, 0x12, + 0xe2, 0x62, 0xa2, 0x22, 0xc2, 0x42, 0x82, 0x02, + 0xfc, 0x7c, 0xbc, 0x3c, 0xdc, 0x5c, 0x9c, 0x1c, + 0xec, 0x6c, 0xac, 0x2c, 0xcc, 0x4c, 0x8c, 0x0c, + 0xf4, 0x74, 0xb4, 0x34, 0xd4, 0x54, 0x94, 0x14, + 0xe4, 0x64, 0xa4, 0x24, 0xc4, 0x44, 0x84, 0x04, + 0xf8, 0x78, 0xb8, 0x38, 0xd8, 0x58, 0x98, 0x18, + 0xe8, 0x68, 0xa8, 0x28, 0xc8, 0x48, 0x88, 0x08, + 0xf0, 0x70, 0xb0, 0x30, 0xd0, 0x50, 0x90, 0x10, + 0xe0, 0x60, 0xa0, 0x20, 0xc0, 0x40, 0x80, 0x00 +}; + +static unsigned char p_crypt_tab0[ 256 ] = +{ + 0xB7, 0xF4, 0x82, 0x57, 0xDA, 0x4D, 0xDB, 0xE2, + 0x2F, 0x52, 0x1A, 0xA8, 0x68, 0x5A, 0x8A, 0xFF, + 0xFB, 0x0E, 0x6D, 0x35, 0xF7, 0x5C, 0x76, 0x12, + 0xCE, 0x25, 0x79, 0x29, 0x39, 0x62, 0x08, 0x24, + 0xA5, 0x85, 0x7B, 0x56, 0x01, 0x23, 0x68, 0xCF, + 0x0A, 0xE2, 0x5A, 0xED, 0x3D, 0x59, 0xB0, 0xA9, + 0xB0, 0x2C, 0xF2, 0xB8, 0xEF, 0x32, 0xA9, 0x40, + 0x80, 0x71, 0xAF, 0x1E, 0xDE, 0x8F, 0x58, 0x88, + 0xB8, 0x3A, 0xD0, 0xFC, 0xC4, 0x1E, 0xB5, 0xA0, + 0xBB, 0x3B, 0x0F, 0x01, 0x7E, 0x1F, 0x9F, 0xD9, + 0xAA, 0xB8, 0x3D, 0x9D, 0x74, 0x1E, 0x25, 0xDB, + 0x37, 0x56, 0x8F, 0x16, 0xBA, 0x49, 0x2B, 0xAC, + 0xD0, 0xBD, 0x95, 0x20, 0xBE, 0x7A, 0x28, 0xD0, + 0x51, 0x64, 0x63, 0x1C, 0x7F, 0x66, 0x10, 0xBB, + 0xC4, 0x56, 0x1A, 0x04, 0x6E, 0x0A, 0xEC, 0x9C, + 0xD6, 0xE8, 0x9A, 0x7A, 0xCF, 0x8C, 0xDB, 0xB1, + 0xEF, 0x71, 0xDE, 0x31, 0xFF, 0x54, 0x3E, 0x5E, + 0x07, 0x69, 0x96, 0xB0, 0xCF, 0xDD, 0x9E, 0x47, + 0xC7, 0x96, 0x8F, 0xE4, 0x2B, 0x59, 0xC6, 0xEE, + 0xB9, 0x86, 0x9A, 0x64, 0x84, 0x72, 0xE2, 0x5B, + 0xA2, 0x96, 0x58, 0x99, 0x50, 0x03, 0xF5, 0x38, + 0x4D, 0x02, 0x7D, 0xE7, 0x7D, 0x75, 0xA7, 0xB8, + 0x67, 0x87, 0x84, 0x3F, 0x1D, 0x11, 0xE5, 0xFC, + 0x1E, 0xD3, 0x83, 0x16, 0xA5, 0x29, 0xF6, 0xC7, + 0x15, 0x61, 0x29, 0x1A, 0x43, 0x4F, 0x9B, 0xAF, + 0xC5, 0x87, 0x34, 0x6C, 0x0F, 0x3B, 0xA8, 0x1D, + 0x45, 0x58, 0x25, 0xDC, 0xA8, 0xA3, 0x3B, 0xD1, + 0x79, 0x1B, 0x48, 0xF2, 0xE9, 0x93, 0x1F, 0xFC, + 0xDB, 0x2A, 0x90, 0xA9, 0x8A, 0x3D, 0x39, 0x18, + 0xA3, 0x8E, 0x58, 0x6C, 0xE0, 0x12, 0xBB, 0x25, + 0xCD, 0x71, 0x22, 0xA2, 0x64, 0xC6, 0xE7, 0xFB, + 0xAD, 0x94, 0x77, 0x04, 0x9A, 0x39, 0xCF, 0x7C +}; + +static unsigned char p_crypt_tab1[ 256 ] = +{ + 0x8C, 0x47, 0xB0, 0xE1, 0xEB, 0xFC, 0xEB, 0x56, + 0x10, 0xE5, 0x2C, 0x1A, 0x5D, 0xEF, 0xBE, 0x4F, + 0x08, 0x75, 0x97, 0x4B, 0x0E, 0x25, 0x8E, 0x6E, + 0x39, 0x5A, 0x87, 0x53, 0xC4, 0x1F, 0xF4, 0x5C, + 0x4E, 0xE6, 0x99, 0x30, 0xE0, 0x42, 0x88, 0xAB, + 0xE5, 0x85, 0xBC, 0x8F, 0xD8, 0x3C, 0x54, 0xC9, + 0x53, 0x47, 0x18, 0xD6, 0x06, 0x5B, 0x41, 0x2C, + 0x67, 0x1E, 0x41, 0x74, 0x33, 0xE2, 0xB4, 0xE0, + 0x23, 0x29, 0x42, 0xEA, 0x55, 0x0F, 0x25, 0xB4, + 0x24, 0x2C, 0x99, 0x13, 0xEB, 0x0A, 0x0B, 0xC9, + 0xF9, 0x63, 0x67, 0x43, 0x2D, 0xC7, 0x7D, 0x07, + 0x60, 0x89, 0xD1, 0xCC, 0xE7, 0x94, 0x77, 0x74, + 0x9B, 0x7E, 0xD7, 0xE6, 0xFF, 0xBB, 0x68, 0x14, + 0x1E, 0xA3, 0x25, 0xDE, 0x3A, 0xA3, 0x54, 0x7B, + 0x87, 0x9D, 0x50, 0xCA, 0x27, 0xC3, 0xA4, 0x50, + 0x91, 0x27, 0xD4, 0xB0, 0x82, 0x41, 0x97, 0x79, + 0x94, 0x82, 0xAC, 0xC7, 0x8E, 0xA5, 0x4E, 0xAA, + 0x78, 0x9E, 0xE0, 0x42, 0xBA, 0x28, 0xEA, 0xB7, + 0x74, 0xAD, 0x35, 0xDA, 0x92, 0x60, 0x7E, 0xD2, + 0x0E, 0xB9, 0x24, 0x5E, 0x39, 0x4F, 0x5E, 0x63, + 0x09, 0xB5, 0xFA, 0xBF, 0xF1, 0x22, 0x55, 0x1C, + 0xE2, 0x25, 0xDB, 0xC5, 0xD8, 0x50, 0x03, 0x98, + 0xC4, 0xAC, 0x2E, 0x11, 0xB4, 0x38, 0x4D, 0xD0, + 0xB9, 0xFC, 0x2D, 0x3C, 0x08, 0x04, 0x5A, 0xEF, + 0xCE, 0x32, 0xFB, 0x4C, 0x92, 0x1E, 0x4B, 0xFB, + 0x1A, 0xD0, 0xE2, 0x3E, 0xDA, 0x6E, 0x7C, 0x4D, + 0x56, 0xC3, 0x3F, 0x42, 0xB1, 0x3A, 0x23, 0x4D, + 0x6E, 0x84, 0x56, 0x68, 0xF4, 0x0E, 0x03, 0x64, + 0xD0, 0xA9, 0x92, 0x2F, 0x8B, 0xBC, 0x39, 0x9C, + 0xAC, 0x09, 0x5E, 0xEE, 0xE5, 0x97, 0xBF, 0xA5, + 0xCE, 0xFA, 0x28, 0x2C, 0x6D, 0x4F, 0xEF, 0x77, + 0xAA, 0x1B, 0x79, 0x8E, 0x97, 0xB4, 0xC3, 0xF4 +}; + +static unsigned char p_crypt_tab2[ 256 ] = +{ + 0xB7, 0x75, 0x81, 0xD5, 0xDC, 0xCA, 0xDE, 0x66, + 0x23, 0xDF, 0x15, 0x26, 0x62, 0xD1, 0x83, 0x77, + 0xE3, 0x97, 0x76, 0xAF, 0xE9, 0xC3, 0x6B, 0x8E, + 0xDA, 0xB0, 0x6E, 0xBF, 0x2B, 0xF1, 0x19, 0xB4, + 0x95, 0x34, 0x48, 0xE4, 0x37, 0x94, 0x5D, 0x7B, + 0x36, 0x5F, 0x65, 0x53, 0x07, 0xE2, 0x89, 0x11, + 0x98, 0x85, 0xD9, 0x12, 0xC1, 0x9D, 0x84, 0xEC, + 0xA4, 0xD4, 0x88, 0xB8, 0xFC, 0x2C, 0x79, 0x28, + 0xD8, 0xDB, 0xB3, 0x1E, 0xA2, 0xF9, 0xD0, 0x44, + 0xD7, 0xD6, 0x60, 0xEF, 0x14, 0xF4, 0xF6, 0x31, + 0xD2, 0x41, 0x46, 0x67, 0x0A, 0xE1, 0x58, 0x27, + 0x43, 0xA3, 0xF8, 0xE0, 0xC8, 0xBA, 0x5A, 0x5C, + 0x80, 0x6C, 0xC6, 0xF2, 0xE8, 0xAD, 0x7D, 0x04, + 0x0D, 0xB9, 0x3C, 0xC2, 0x25, 0xBD, 0x49, 0x63, + 0x8C, 0x9F, 0x51, 0xCE, 0x20, 0xC5, 0xA1, 0x50, + 0x92, 0x2D, 0xDD, 0xBC, 0x8D, 0x4F, 0x9A, 0x71, + 0x2F, 0x30, 0x1D, 0x73, 0x39, 0x13, 0xFB, 0x1A, + 0xCB, 0x24, 0x59, 0xFE, 0x05, 0x96, 0x57, 0x0F, + 0x1F, 0xCF, 0x54, 0xBE, 0xF5, 0x06, 0x1B, 0xB2, + 0x6D, 0xD3, 0x4D, 0x32, 0x56, 0x21, 0x33, 0x0B, + 0x52, 0xE7, 0xAB, 0xEB, 0xA6, 0x74, 0x00, 0x4C, + 0xB1, 0x7F, 0x82, 0x99, 0x87, 0x0E, 0x5E, 0xC0, + 0x8F, 0xEE, 0x6F, 0x55, 0xF3, 0x7E, 0x08, 0x90, + 0xFA, 0xB6, 0x64, 0x70, 0x47, 0x4A, 0x17, 0xA7, + 0xB5, 0x40, 0x8A, 0x38, 0xE5, 0x68, 0x3E, 0x8B, + 0x69, 0xAA, 0x9B, 0x42, 0xA5, 0x10, 0x01, 0x35, + 0xFD, 0x61, 0x9E, 0xE6, 0x16, 0x9C, 0x86, 0xED, + 0xCD, 0x2E, 0xFF, 0xC4, 0x5B, 0xA0, 0xAE, 0xCC, + 0x4B, 0x3B, 0x03, 0xBB, 0x1C, 0x2A, 0xAC, 0x0C, + 0x3F, 0x93, 0xC7, 0x72, 0x7A, 0x09, 0x22, 0x3D, + 0x45, 0x78, 0xA9, 0xA8, 0xEA, 0xC9, 0x6A, 0xF7, + 0x29, 0x91, 0xF0, 0x02, 0x18, 0x3A, 0x4E, 0x7C +}; + +static unsigned char p_crypt_tab3[ 288 ] = +{ + 0x73, 0x51, 0x95, 0xE1, 0x12, 0xE4, 0xC0, 0x58, + 0xEE, 0xF2, 0x08, 0x1B, 0xA9, 0xFA, 0x98, 0x4C, + 0xA7, 0x33, 0xE2, 0x1B, 0xA7, 0x6D, 0xF5, 0x30, + 0x97, 0x1D, 0xF3, 0x02, 0x60, 0x5A, 0x82, 0x0F, + 0x91, 0xD0, 0x9C, 0x10, 0x39, 0x7A, 0x83, 0x85, + 0x3B, 0xB2, 0xB8, 0xAE, 0x0C, 0x09, 0x52, 0xEA, + 0x1C, 0xE1, 0x8D, 0x66, 0x4F, 0xF3, 0xDA, 0x92, + 0x29, 0xB9, 0xD5, 0xC5, 0x77, 0x47, 0x22, 0x53, + 0x14, 0xF7, 0xAF, 0x22, 0x64, 0xDF, 0xC6, 0x72, + 0x12, 0xF3, 0x75, 0xDA, 0xD7, 0xD7, 0xE5, 0x02, + 0x9E, 0xED, 0xDA, 0xDB, 0x4C, 0x47, 0xCE, 0x91, + 0x06, 0x06, 0x6D, 0x55, 0x8B, 0x19, 0xC9, 0xEF, + 0x8C, 0x80, 0x1A, 0x0E, 0xEE, 0x4B, 0xAB, 0xF2, + 0x08, 0x5C, 0xE9, 0x37, 0x26, 0x5E, 0x9A, 0x90, + 0x00, 0xF3, 0x0D, 0xB2, 0xA6, 0xA3, 0xF7, 0x26, + 0x17, 0x48, 0x88, 0xC9, 0x0E, 0x2C, 0xC9, 0x02, + 0xE7, 0x18, 0x05, 0x4B, 0xF3, 0x39, 0xE1, 0x20, + 0x02, 0x0D, 0x40, 0xC7, 0xCA, 0xB9, 0x48, 0x30, + 0x57, 0x67, 0xCC, 0x06, 0xBF, 0xAC, 0x81, 0x08, + 0x24, 0x7A, 0xD4, 0x8B, 0x19, 0x8E, 0xAC, 0xB4, + 0x5A, 0x0F, 0x73, 0x13, 0xAC, 0x9E, 0xDA, 0xB6, + 0xB8, 0x96, 0x5B, 0x60, 0x88, 0xE1, 0x81, 0x3F, + 0x07, 0x86, 0x37, 0x2D, 0x79, 0x14, 0x52, 0xEA, + 0x73, 0xDF, 0x3D, 0x09, 0xC8, 0x25, 0x48, 0xD8, + 0x75, 0x60, 0x9A, 0x08, 0x27, 0x4A, 0x2C, 0xB9, + 0xA8, 0x8B, 0x8A, 0x73, 0x62, 0x37, 0x16, 0x02, + 0xBD, 0xC1, 0x0E, 0x56, 0x54, 0x3E, 0x14, 0x5F, + 0x8C, 0x8F, 0x6E, 0x75, 0x1C, 0x07, 0x39, 0x7B, + 0x4B, 0xDB, 0xD3, 0x4B, 0x1E, 0xC8, 0x7E, 0xFE, + 0x3E, 0x72, 0x16, 0x83, 0x7D, 0xEE, 0xF5, 0xCA, + 0xC5, 0x18, 0xF9, 0xD8, 0x68, 0xAB, 0x38, 0x85, + 0xA8, 0xF0, 0xA1, 0x73, 0x9F, 0x5D, 0x19, 0x0B, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x33, 0x72, 0x39, 0x25, 0x67, 0x26, 0x6D, 0x71, + 0x36, 0x77, 0x3C, 0x20, 0x62, 0x23, 0x68, 0x74, + 0xC3, 0x82, 0xC9, 0x15, 0x57, 0x16, 0x5D, 0x81 +}; + diff --git a/lib/qpxtransport/include/qpx_mmc.h b/lib/qpxtransport/include/qpx_mmc.h new file mode 100644 index 0000000..a7ab354 --- /dev/null +++ b/lib/qpxtransport/include/qpx_mmc.h @@ -0,0 +1,509 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2009 Gennady "ShultZ" Kozlov + * + * 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 __qpxtool_mmc_h +#define __qpxtool_mmc_h + +//#include +#include "qpx_transport.h" +#include "common_functions.h" + +//#ifdef HAVE_LIMITS_H +#include +//#endif +#include + +#include "qpx_mmc_defs.h" + +#define bufsz_dev 0x0000FF +#define bufsz_rd 0x010000 +#define bufsz_ATIP 0x000800 + +typedef struct{ + int n; + int session; + int track_mode; + int data_mode; + int start; + msf msf_start; + int next_writable; + msf msf_next; + int free; + msf msf_free; + int packet_size; + int size; + msf msf_size; + int last; + msf msf_last; +// int end; +// msf msf_end; +} trk; + +typedef struct{ + int test; + int idx; + int32_t lba; + int block,blocks; + float speed_kb; + float speed_x; + int speed_h; + int err_total; + int err_max; + int err_cur; + int err_min; + int err_m; + int err_d; + float err_avg; + int pit; + int land; + float jmax; + float jmin; + float bmax; + float bmin; + long time; + int nte, ote; + int nfe, ofe; + int ext; +} block_data; + +#define DVD_KEY_SIZE 5 + +#define DVDCSS_KEY_CACHE 0 + +#define DVDCSS_METHOD_NONE 0 +#define DVDCSS_METHOD_KEY 1 +#define DVDCSS_METHOD_DISC 2 +#define DVDCSS_METHOD_TITLE 3 + +#define DVDCSS_BLOCK_SIZE 2048 + +#define DVDCSS_NOFLAGS 0 +#define DVDCSS_READ_DECRYPT (1 << 0) +#define DVDCSS_SEEK_MPEG (1 << 0) +#define DVDCSS_SEEK_KEY (1 << 1) + +typedef uint8_t dvd_key_t[DVD_KEY_SIZE]; + +typedef struct dvd_title_s +{ + int i_startlb; + dvd_key_t p_key; + struct dvd_title_s *p_next; +} dvd_title_t; + +typedef struct { + bool asf; /* Authenication Success Flag */ + uint8_t agid; /* Current Authenication Grant ID */ + dvd_key_t BK; /* Current session key (Bus Key) */ + dvd_key_t DK; /* This DVD disc's key */ + uint8_t CK[2*DVD_KEY_SIZE]; + dvd_key_t K1; + dvd_key_t K2; + dvd_key_t TK; /* Current title key */ + dvd_title_s *p_titles; + + uint8_t protection; + uint8_t regmask; + int method; + int pos; +#if (DVDCSS_KEY_CACHE > 0) + char psz_cachefile[PATH_MAX]; + char *psz_block; +#endif +} dvdcss_t; + +#define MID_RAW_MAX 1024 + +enum mid_type_t { + MID_type_NONE = 0, + MID_type_CD = 1, + MID_type_DVDp = 2, + MID_type_DVDm = 4, + MID_type_DVDRAM = 8, + MID_type_BD = 16 +}; + +typedef struct { + char MID[48]; // MediaID for DVD, manufacturer for CD + mid_type_t MID_type; + uint8_t ATIP[bufsz_ATIP]; + int ATIP_size; + uint16_t MID_size; + unsigned char MID_raw[4+MID_RAW_MAX]; + uint64_t type; // Media subtype + uint8_t book_type; // Book type (DVD) + uint8_t max_rate; + uint8_t disc_size; // indicates 120/80mm disc + uint8_t polarity; // Push-Pull polarity flags per layer for BD (indicates HtL or LtH) + uint8_t layers; // Layers num (!CD) + int sectsize; + int32_t capacity; // Recorded capacity in sectors + msf capacity_msf; + int32_t capacity_free; // Free sectors + msf capacity_free_msf; + int32_t capacity_total; // Total sectors + msf capacity_total_msf; + int spare_psa_total, + spare_ssa_total, + spare_psa_free, + spare_ssa_free; + int last_lead_out; + int dstatus; // Empty/Apeendable/Complete + int sstatus; + int sessions; + int tracks; + int erasable; + char writer[0x3F]; + trk track[0xFF]; + dvdcss_t dvdcss; +} media_info; + +#define speed_tbl_size 64 +#define STATUS_OPEN 0x0001 +#define STATUS_MEDIA_PRESENT 0x0002 +#define STATUS_LOCK 0x0004 + +typedef struct { + uint8_t status; + uint8_t event; + int interval; + int tests; + int8_t spindown_idx; + int16_t speed_idx; + float speed_mult; + int16_t speed_tbl[speed_tbl_size]; + int32_t speed_tbl_kb[speed_tbl_size]; + int16_t wr_speed_tbl[speed_tbl_size]; + int32_t wr_speed_tbl_kb[speed_tbl_size]; + float wr_speed_tbl_media[speed_tbl_size]; + int32_t scan_speed_cd; + int32_t scan_speed_dvd; + int32_t read_speed_kb; + int32_t read_speed_cd; + int32_t read_speed_dvd; + int32_t max_read_speed_kb; + int32_t max_read_speed_cd; + int32_t max_read_speed_dvd; + int32_t max_write_speed_kb; + int32_t max_write_speed_cd; + int32_t max_write_speed_dvd; + int32_t write_speed_kb; + int32_t write_speed_cd; + int32_t write_speed_dvd; +} drive_parms; + +typedef struct { + int32_t lba_s; + int32_t spd_s; + int32_t lba_e; + int32_t spd_e; +} perf_desc; + +typedef struct { + int max; + int min; + int m; + int d; +} err; + +/* +typedef struct { + err* BLER; + err* E11; + err* E21; + err* E31; + err* E12; + err* E22; + err* E32; + int tot[7]; + int max[7]; + float avg[7]; + uint32_t color[7]; + uint32_t colorl[7]; +} _Exx; +*/ + +typedef struct +{ + char type; + char len; + char state; + char rd; + char wr; + char access; + char eject; + char load; + + char psaved; + char pstate; + char prd_cd; + char prd_dvd; + char pwr_cd; +// char pwr_dvd; + char paccess; + char peject; + char pload; +} plex_silent; + +typedef struct { + uint8_t number; + uint8_t type; // 0x25 == -R, 0xA1 == +R + char MID[12]; + uint8_t crap2; + uint8_t enabled; + char counter[2]; + uint8_t speed; + uint8_t crap3[13]; +} as_entry; + +typedef struct { + uint8_t number[2]; + uint8_t crap[30]; +} as_data; + +typedef struct { + char sizeb[2]; + char crap1[4]; + char dbcnt; + char entry_size; + as_entry entry[32]; + as_data entry_data[32][7]; + char state; + int size; +} plex_as; + +typedef struct { + bool ok; + uint16_t dn; + hms cr, + cw, + dr, + dw; +} plex_life; + +typedef struct { + char gigarec; // Current GigaRec value + char gigarec_disc; // GigaRec value of inserted CD + char powerec_state; // Current PoweRec state + uint16_t powerec_spd; // Current PoweRec recomended speed + char varirec_state_cd; // Current VariRec CD state + char varirec_pwr_cd; // Current VariRec CD LaserPower + char varirec_str_cd; // Current VariRec CD Strategy + char varirec_state_dvd; // Current VariRec DVD state + char varirec_pwr_dvd; // Current VariRec DVD LaserPower + char varirec_str_dvd; // Current VariRec DVD Strategy + char hcdr; // Hide CD-R State + char securec; + char securec_disc; + char sss; // SingleSession State + char spdread; // SpeedRead + char testwrite_dvdplus; // Simulation on DVD+R(W) + char plexeraser; // PlexEraser mode +} plex_features; + +typedef struct { + char amqr; + char forcespeed; + uint32_t tattoo_i; + uint32_t tattoo_o; + uint32_t tattoo_rows; +} yamaha_features; + +typedef struct { + char silent; + char limit; + bool peakpower; + char pureread; +} pio_quiet; + +typedef struct { + uint8_t phase; + uint8_t region; + uint8_t ch_u; + uint8_t ch_v; +} rpc_state; + +class drive_info { +public: + drive_info(const char* _device); + ~drive_info(); + +// bool isBusy(); +// bool lock(); +// bool unlock(); +// void wait_free(); + + Scsi_Command cmd; + int err; + + char* device; // device adress + char ven[9]; // vendor string + uint32_t ven_ID; // drive vendor ID + char dev[17]; // model string + uint32_t dev_ID; // model ID + char fw[5]; // FirmWare + char serial[17]; // drive serial# + char TLA[5]; // TLA# - only rof Plextor PX-712, PX-716 + uint16_t ver_id[8]; + +// int z; + + uint32_t buffer_size; // drive buffer size + uint64_t capabilities; // common capabilities + uint64_t rd_capabilities; // read capabilities + uint64_t wr_capabilities; // write capabilities + uint32_t wr_modes; // write modes + uint32_t ven_features; // vendor-specific features + uint32_t chk_features; // media check features + + uint32_t iface_id; + str_if iface; + uint8_t loader_id; + + short book_plus_r; + short book_plus_rw; + short book_plus_rdl; + + yamaha_features yamaha; + plex_features plextor; + plex_life life; + plex_as astrategy; + plex_silent plextor_silent; + pio_quiet pioneer; + media_info media; + drive_parms parms; + perf_desc perf; + + uint8_t* rd_buf; + char mmc; + + rpc_state rpc; + + bool get_performance_fail; + char silent; +private: + bool busy; +}; + +static const int drive_info_size=sizeof(drive_info); + +extern int print_sense (int err); +extern int print_opcode (uint8_t opcode); + +extern int scanbus(int vendor_mask=0); +extern int inquiry(drive_info* drive); +extern int isPlextor(drive_info* drive); +extern int isPlextorLockPresent(drive_info* drive); +extern int isYamaha(drive_info* drive); +extern int isPioneer(drive_info* drive); + +extern int test_unit_ready(drive_info* drive); +extern int wait_unit_ready(drive_info* drive, int secs, bool need_media=1); +extern int check_burnfree(drive_info* drive); +extern int check_write_modes(drive_info* drive); +extern int reserve_track(drive_info* drive, uint32_t size); +extern int close_track_session(drive_info* drive, int n, int cltype); +extern int wait_fix(drive_info* drive, int secs); +extern int request_sense(drive_info* drive, char add); +extern int get_configuration(drive_info* drive, int feature_number, uint32_t* data_length, int* current, uint8_t ReqType = 0x02); +extern void detect_iface(drive_info* drive); + +extern int write_buffer(drive_info* drive, uint8_t mode, uint8_t buff_id, uint32_t offs, uint32_t len); +extern int read_buffer(drive_info* drive, uint8_t mode, uint8_t buff_id, uint32_t offs, uint32_t len); +extern int test_dma_speed(drive_info* drive, long msecs = 250); +extern int flush_cache(drive_info* drive, bool IMMED); +extern int set_cache(drive_info* drive, bool rd, bool wr); +extern int get_cache(drive_info* drive, bool *rd = NULL, bool *wr = NULL); + +// some DVD related functions +extern int get_rpc_state(drive_info* drive); +extern int read_disc_regions(drive_info* drive); + +extern int css_disckey( drive_info* drive); +extern int css_title ( drive_info* drive, int32_t lba); +//extern int css_titlekey( drive_info* drive, int lba, dvd_key_t p_title_key ); +//extern int css_unscramble( dvd_key_t p_key, uint8_t *p_sec ); +//extern void css_printkey (char *, uint8_t const * ); + +// device capabilities detection functions +extern int get_profiles_list(drive_info* drive); +extern int get_features_list(drive_info* drive); +extern int get_mode_pages_list(drive_info* drive); +extern void detect_capabilities(drive_info* drive); + +// media information functions +extern int read_atip(drive_info* drive, int silent); +extern int read_toc(drive_info* drive, int silent); +extern int read_track_info(drive_info* drive, trk* track, uint32_t track_n); +extern int get_track_list(drive_info* drive); +extern int read_disc_information(drive_info* drive); + +extern int read_capacities(drive_info* drive); +extern int read_disc_info(drive_info* drive, int len); +extern int determine_cd_type(drive_info* drive); +//extern int determine_cdrw_subtype(drive_info* drive); +extern int read_mediaid_dvd(drive_info* drive); +extern int read_mediaid_dvdram(drive_info* drive); +extern int read_mediaid_dvdminus(drive_info* drive); +extern int read_mediaid_dvdplus(drive_info* drive); +extern int read_mediaid_bd(drive_info* drive); +extern int determine_disc_type(drive_info* drive); + +extern int mode_sense(drive_info* drive, int page, int page_control, int dest_len); +extern int mode_select(drive_info* drive, int dest_len); + +// speed settings +extern int get_spindown(drive_info* drive); +extern int set_spindown(drive_info* drive); +extern int get_performance(drive_info* drive, bool rw, uint8_t type); +extern int get_write_speed_tbl(drive_info* drive); +extern int set_streaming(drive_info* drive); +extern int get_rw_speeds(drive_info* drive); +extern int set_rw_speeds(drive_info* drive); +extern int detect_speeds(drive_info *drive); + +// media change detection, media lock, load/eject +extern int get_media_status(drive_info* drive); +extern int start_stop(drive_info* drive, bool start); +extern int load_eject(drive_info* drive, bool load, bool IMMED); +extern int load_eject(drive_info* drive, bool IMMED); +extern int get_lock(drive_info* drive); +extern int set_lock(drive_info* drive); + +//extern void spinup(drive_info* drive); +extern void spinup(drive_info* drive, uint8_t secs = 2); +extern int seek(drive_info* drive, int32_t lba, uint8_t flags = 0); +extern int play_audio_msf(drive_info* drive, msf beg, msf end); +extern int play_audio(drive_info* drive, int32_t beg, short int len); +extern int read_cd(drive_info* drive, uint8_t *data, int32_t lba, int sector_count, uint8_t flags, uint8_t FUA = 0); +extern int read(drive_info* drive, uint8_t *data, int32_t lba, int sector_count, uint8_t FUA = 0); +extern int read_one_ecc_block(drive_info* drive, uint8_t *data, int32_t lba); +extern int get_drive_serial_number(drive_info* drive); +extern int get_buffer_capacity(drive_info* drive); +extern int get_wbuffer_capacity(drive_info* drive, uint32_t *btot, uint32_t *bfree); +extern int read_writer_info(drive_info* drive); + +extern int read_dvd(drive_info* drive, uint8_t *data, int32_t lba, int sector_count, int flags = 0); +extern int seek_dvd( drive_info* drive, int32_t lba, int flags ); + +extern int detect_mm_capabilities(drive_info* drive); +//extern int detect_check_capabilities(drive_info* drive); +//extern int convert_to_ID (drive_info* drive); + + +extern int plextor_px755_do_auth(drive_info* dev); +extern int plextor_px755_get_auth_code(drive_info* dev, unsigned char* auth_code); +extern int plextor_px755_send_auth_code(drive_info* dev, unsigned char* auth_code); +// extern int cmd_px755_clear_auth_status(drive_info* dev); +extern int plextor_px755_calc_auth_code(drive_info* dev, unsigned char* auth_code); + +#endif + diff --git a/lib/qpxtransport/include/qpx_mmc_defs.h b/lib/qpxtransport/include/qpx_mmc_defs.h new file mode 100644 index 0000000..7f7b0d5 --- /dev/null +++ b/lib/qpxtransport/include/qpx_mmc_defs.h @@ -0,0 +1,1032 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2009 Gennady "ShultZ" Kozlov + * + * 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 __qpxtool_mmc_defs_h +#define __qpxtool_mmc_defs_h + +//#ifdef HAVE_LIMITS_H +#include +//#endif +#include + +#include "common_functions.h" +#include "qpx_opcodes.h" + +typedef char str_vendor[9]; +typedef char str_dev[16]; + +struct dev_desc { + str_dev name; + int len; +}; + +typedef char str_if[16]; +typedef char str_lo[64]; +typedef char str8[8]; +typedef char str_32[32]; + +typedef struct { + uint8_t id; + str_lo name; +} desc8; + +typedef struct { + uint16_t id; + str_lo name; +} desc16; + +typedef struct { + uint32_t id; + str_lo name; +} desc32; + +typedef struct { + uint64_t id; + str_lo name; +} desc64; + +typedef struct{ + msf lin; + str_lo name; +} manuf; + +static const uint32_t rpc_phase_max=2; +static const str8 rpc_phase[rpc_phase_max+1]={ + "n/a", + "RPC-I", + "RPC-II" +}; + +static const uint32_t spindowns=16; +static const str8 spindown_tbl[spindowns+1]={ + "vendor","125ms","250ms","500ms","1s","2s","4s","8s", + "16s","32s","1min","2min","4min","8min","16min","32min", + "unknown" +}; + +static const uint32_t iface_id_max=8; +static const str_if iface_list[iface_id_max+2]={ + "Unspecified\0", + "SCSI\0", + "ATAPI\0", + "IEEE1394-1995\0", + "IEEE1394A\0", + "Fibre Channel\0", + "IEEE1394B\0", + "Serial ATAPI\0", + "USB (1.1/2.0)\0", + "\0", +}; + +static const str_if disc_status_list[4]= { + "Blank", + "Incomplete", + "Finalized", + "Random Access", +// "[READ_DISK_INFO error]", +}; + +static const str_if session_status_list[4]= { + "Empty", + "Incomplete", + "Reserved", + "Complete", +// "[READ_DISK_INFO error]", +}; + +static const str_lo loader_list[8]={ + "Caddy/Slot", + "Tray", + "Pop-up", + "", + "Changer (individual discs)", + "Changer (cartridge mechanism)", + "", + "" +}; + +static const str8 track_mode[16]={ + "Blank","<1>","Audio","<3>", + "Data","<5>","<6>","Packet", + "<8>","<9>","<10>","<11>", + "<12>","<13>","<14>","<15>" +}; + +static const str8 data_mode[16]={ + "<0>","Mode1","Mode2","<3>", + "<4>","<5>","<6>","<7>", + "<8>","<9>","<10>","<11>", + "<12>","<13>","<14>","<15>" +}; + +#define EVENT_NO_CHANGE 0x00 +#define EVENT_EJECT 0x01 +#define EVENT_MEDIA_NEW 0x02 +#define EVENT_MEDIA_REMOVED 0x03 +#define EVENT_MEDIA_CHANGED 0x04 +#define EVENT_MEDIA_BG_DONE 0x05 +#define EVENT_MEDIA_BG_REST 0x06 + +static const str_32 media_event[16]={ + "No Change", "Eject requested", "New Media", "Media Removal", + "Media Changed", "BGformat done", "BGformat restart", "", + "", "", "", "", + "", "", "", "" +}; + +static const str_32 cdrw_subtype_tbl[8]={ + "Normal Speed", + "High Speed", + "Ultra Speed", + "Ultra Speed+", + "", + "", + "", + "" +}; + +static const str_32 cdr_subtype_tbl[8]={ + "", + "", + "Type A, low beta (A-)", + "Type A, high beta (A+)", + "Type B, low beta (B-)", + "Type B, high beta (B+)", + "Type C, low beta (C-)", + "Type C, high beta (C+)" +}; + + +static const manuf mi[]={ + { { 97, 22, 60 }, "Acer" }, + { { 97, 45, 20 }, "Acer" }, + { { 97, 22, 20 }, "Advanced Digital Media" }, + { { 97, 42, 20 }, "Advanced Digital Media" }, + { { 97, 25, 50 }, "AMS" }, + { { 97, 23, 30 }, "AUDIO DISTRIBUTORS" }, + { { 97, 28, 30 }, "Auvistar" }, + { { 97, 46, 50 }, "Auvistar" }, + + { { 97, 22, 40 }, "CIS Technology" }, + { { 97, 45, 40 }, "CIS Technology" }, + { { 97, 26, 60 }, "CMC Magnetics" }, + { { 97, 46, 60 }, "CMC Magnetics" }, + { { 97, 23, 60 }, "Customer Pressing Oosterhout" }, + + { { 97, 28, 50 }, "DELPHI" }, + { { 97, 27, 0 }, "DIGITAL STORAGE" }, + { { 97, 48, 40 }, "DIGITAL STORAGE" }, + { { 97, 23, 10 }, "Doremi" }, + { { 97, 13, 30 }, "DST" }, + + { { 97, 22, 30 }, "EXIMPO" }, + + { { 97, 26, 0 }, "FORNET" }, + { { 97, 45, 0 }, "FORNET" }, + { { 97, 26, 40 }, "FUJI" }, + { { 97, 46, 40 }, "FUJI" }, + + { { 97, 29, 50 }, "General Magnetics" }, + { { 97, 28, 10 }, "GIGASTORAGE" }, + { { 97, 49, 10 }, "GIGASTORAGE" }, + { { 97, 16, 30 }, "Grand Advance" }, + { { 97, 31, 30 }, "Grand Advance" }, + { { 97, 51, 10 }, "Grand Advance" }, + { { 97, 45, 50 }, "Guann Yinn" }, + { { 97, 24, 50 }, "Guann Yinn" }, + + { { 97, 24, 60 }, "Harmonic Hall" }, + { { 97, 29, 0 }, "Harmonic Hall" }, + { { 97, 29, 30 }, "Hile Optical Disc" }, + { { 97, 51, 50 }, "Hile Optical Disc" }, + { { 97, 25, 20 }, "Hitachi Maxell" }, + { { 97, 47, 10 }, "Hitachi Maxell" }, + + { { 97, 25, 30 }, "INFODISC" }, + { { 97, 51, 20 }, "INFODISC" }, + + { { 97, 24, 40 }, "kdg mediatech" }, + { { 97, 28, 40 }, "King Pro" }, + { { 97, 49, 20 }, "King Pro" }, + { { 97, 27, 40 }, "Kodak" }, + { { 97, 48, 10 }, "Kodak" }, + + { { 97, 26, 50 }, "Lead Data" }, + { { 97, 48, 60 }, "Lead Data" }, + + { { 97, 23, 0 }, "Matsushita" }, + { { 97, 49, 60 }, "Matsushita" }, + { { 97, 15, 20 }, "Mitsubishi" }, + { { 97, 34, 20 }, "Mitsubishi" }, + { { 97, 50, 20 }, "Mitsubishi" }, + { { 97, 27, 50 }, "Mitsui" }, + { { 97, 48, 50 }, "Mitsui" }, + { { 97, 17, 0 }, "Moser Baer India" }, + { { 97, 25, 0 }, "MPO" }, + { { 97, 28, 20 }, "MultiMediaMasters & Machinery" }, + { { 97, 46, 20 }, "MultiMediaMasters & Machinery" }, + + { { 97, 23, 20 }, "Nacar Media srl" }, + { { 97, 15, 30 }, "NAN-YA" }, + + { { 97, 28, 0 }, "Opti.Me.S. S.p.A." }, + { { 97, 49, 30 }, "Opti.Me.S. S.p.A." }, + { { 97, 21, 40 }, "Optical Disc Manuf." }, + { { 97, 26, 30 }, "OPTICAL DISC CORPRATION" }, + { { 97, 23, 50 }, "OPTROM.INC." }, + + { { 97, 27, 30 }, "Pioneer" }, + { { 97, 48, 30 }, "Pioneer" }, + { { 97, 27, 10 }, "Plasmon" }, + { { 97, 48, 20 }, "Plasmon" }, + { { 97, 26, 10 }, "POSTECH" }, + { { 97, 47, 40 }, "POSTECH" }, + { { 97, 27, 20 }, "Princo" }, + { { 97, 47, 20 }, "Princo" }, + { { 96, 43, 20 }, "Prodisc" }, + { { 96, 43, 30 }, "Prodisc" }, // Ritek ? + { { 97, 32, 10 }, "Prodisc" }, + { { 97, 47, 60 }, "Prodisc" }, + + { { 97, 27, 60 }, "Ricoh" }, + { { 97, 48, 0 }, "Ricoh" }, + { { 97, 15, 10 }, "Ritek" }, + { { 97, 31, 0 }, "Ritek" }, + { { 97, 47, 50 }, "Ritek" }, + + { { 97, 22, 10 }, "Seantram" }, + { { 97, 16, 20 }, "SHENZEN SG&GAST" }, + { { 97, 26, 20 }, "SKC" }, + { { 97, 24, 10 }, "SONY" }, + { { 97, 46, 10 }, "SONY" }, + + { { 97, 29, 0 }, "Taeil Media" }, + { { 97, 24, 0 }, "Taiyo Yuden" }, + { { 97, 46, 0 }, "Taiyo Yuden" }, + { { 97, 18, 60 }, "TAROKO" }, + { { 97, 15, 0 }, "TDK Corporation" }, + { { 97, 32, 0 }, "TDK Corporation" }, + { { 97, 49, 0 }, "TDK Corporation" }, + + { { 97, 29, 20 }, "UNIDISC" }, + { { 97, 24, 30 }, "UNITECH" }, + { { 97, 45, 10 }, "UNITECH" }, + + { { 97, 29, 10 }, "Vanguard" }, + { { 97, 50, 10 }, "Vanguard" }, + { { 97, 49, 40 }, "VICTOR" }, + { { 97, 23, 40 }, "VICTOR" }, + { { 97, 29, 40 }, "VIVA MAGNETICS" }, + { { 97, 25, 40 }, "VIVASTAR" }, + + { { 97, 25, 60 }, "Xcitec" }, + { { 97, 45, 60 }, "Xcitec" }, + + { { 0, 0, 0 }, "UNKNOWN" } +}; + +#define MODE_PAGE_RW_ERR_CTL 0x01 +#define MODE_PAGE_MTRAINIER 0x02 +#define MODE_PAGE_WRITE_PARAMETERS 0x05 +#define MODE_PAGE_VERIFY_ERR_RECOVERY 0x07 +#define MODE_PAGE_CACHING 0x08 +#define MODE_PAGE_MEDIUM_SUPPORTED 0x0B +#define MODE_PAGE_CD_DEV_PARANETERS 0x0D +#define MODE_PAGE_CD_AUDIO_CTL 0x0E +#define MODE_PAGE_POWER_CONDITION 0x1A +#define MODE_PAGE_FAULT_REPORTING 0x1C +#define MODE_PAGE_TIMEOUT_PROTECT 0x1D +#define MODE_PAGE_MM_CAP_STATUS 0x2A +#define MODE_PAGE_MTRAINIER2 0x2C +#define MODE_PAGE_YAMAHA_TATTOO 0x31 + +static const desc8 MODE_PAGES[]={ +// { "" , 0x00}, + + { 0x00, "vendor"}, + { 0x01, "R/W error control"}, + { 0x02, "Mt.Rainier"}, + { 0x05, "Write parameters"}, + { 0x07, "Verify error recovery"}, + { 0x08, "Caching"}, + { 0x0B, "Medium types supported"}, + { 0x0D, "CD device parameters"}, + { 0x0E, "CD Audio control"}, + { 0x1A, "Power condition"}, + { 0x1C, "Fault/Failure reporting"}, + { 0x1D, "Time-out & Protect"}, + { 0x2A, "MM Capabilities & Mechanical status"}, + { 0x2C, "Mt.Rainier"}, + { 0x31, "Yamaha DiscT@2"}, + + { 0x3F, "???"} +}; + +#define FEATURE_PROFILE_LIST 0x000 +#define FEATURE_CORE 0x001 +#define FEATURE_MORPHING 0x002 +#define FEATURE_REMOVABLE_MEDIA 0x003 +#define FEATURE_WRITE_PROTECT 0x004 +#define FEATURE_RANDOM_READABLE 0x010 +#define FEATURE_MULTI_READ 0x01D +#define FEATURE_CD_READ 0x01E +#define FEATURE_DVD_READ 0x01F +#define FEATURE_RANDOM_WRITABLE 0x020 +#define FEATURE_INCREMENTAL_STREAMING_WRITABLE 0x021 +#define FEATURE_SECTOR_ERASABLE 0x022 +#define FEATURE_FORMATTABLE 0x023 +#define FEATURE_DEFECT_MANAGEMENT 0x024 +#define FEATURE_WRITE_ONCE 0x025 +#define FEATURE_RESTRICTED_OVERWRITE 0x026 +#define FEATURE_CD_RW_CAV_WRITE 0x027 +#define FEATURE_MRW 0x028 +#define FEATURE_ENHANCED_DEFECT_REPORTING 0x029 +#define FEATURE_DVD_PLUS_RW 0x02A +#define FEATURE_DVD_PLUS_R 0x02B +#define FEATURE_RIGID_RESTRICTED_OVERWRITE 0x02C +#define FEATURE_CD_TRACK_AT_ONCE 0x02D +#define FEATURE_CD_MASTERING 0x02E +#define FEATURE_DVD_R_RW_WRITE 0x02F +#define FEATURE_DDCD_READ 0x030 +#define FEATURE_DDCD_R_WRITE 0x031 +#define FEATURE_DDCD_RW_WRITE 0x032 +#define FEATURE_LAYER_JUMP_RECORDING 0x033 +#define FEATURE_CD_RW_MEDIA_WRITE_SUPPORT 0x037 +#define FEATURE_BD_R_POW 0x038 +#define FEATURE_DVD_PLUS_RW_DOUBLE_LAYER 0x03A +#define FEATURE_DVD_PLUS_R_DOUBLE_LAYER 0x03B +#define FEATURE_BD_READ 0x040 +#define FEATURE_BD_WRITE 0x041 +#define FEATURE_HDDVD_READ 0x050 +#define FEATURE_HDDVD_WRITE 0x051 +#define FEATURE_HDDVD_RW_FRAGREC 0x052 +#define FEATURE_POWER_MANAGEMENT 0x100 +#define FEATURE_SMART 0x101 +#define FEATURE_EMBEDDED_CHANGER 0x102 +#define FEATURE_CD_AUDIO_ANALOG_PLAY 0x103 +#define FEATURE_MICROCODE_UPGRADE 0x104 +#define FEATURE_TIMEOUT 0x105 +#define FEATURE_DVD_CSS 0x106 +#define FEATURE_REAL_TIME_STREAMING 0x107 +#define FEATURE_LOGICAL_UNIT_SERIAL_NUMBER 0x108 +#define FEATURE_MEDIA_SERIAL_NUMBER 0x109 +#define FEATURE_DISC_CONTROL_BLOCKS 0x10A +#define FEATURE_DVD_CPRM 0x10B +#define FEATURE_FIRMWARE_DATE 0x10C + +static const desc16 FEATURES[]={ + { 0x0000, "PROFILE_LIST"}, + { 0x0001, "CORE"}, + { 0x0002, "MORPHING"}, + { 0x0003, "REMOVABLE MEDIA"}, + { 0x0004, "WRITE PROTECT"}, + { 0x0010, "RANDOM READABLE"}, + { 0x001D, "MULTI READ"}, + { 0x001E, "CD READ"}, + { 0x001F, "DVD READ"}, + { 0x0020, "RANDOM WRITABLE"}, + { 0x0021, "INCREMENTAL STREAMING WRITABLE"}, + { 0x0022, "SECTOR ERASABLE"}, + { 0x0023, "FORMATTABLE"}, + { 0x0024, "DEFECT MANAGEMENT"}, + { 0x0025, "WRITE ONCE"}, + { 0x0026, "RESTRICTED OVERWRITE"}, + { 0x0027, "CD-RW CAV WRITE"}, + { 0x0028, "MRW"}, + { 0x0029, "ENHANCED DEFECT REPORTING"}, + { 0x002A, "DVD+RW"}, + { 0x002B, "DVD+R"}, + { 0x002C, "RIGID RESTRICTED OVERWRITE"}, + { 0x002D, "CD TRACK AT ONCE"}, + { 0x002E, "CD MASTERING (SESSION AT ONCE)"}, + { 0x002F, "DVD+R(W) WRITE"}, + { 0x0030, "DDCD READ"}, + { 0x0031, "DDCD-R WRITE"}, + { 0x0032, "DDCD-RW WRITE"}, + { 0x0033, "LAYER JUMP RECORDING"}, + { 0x0037, "CD-RW MEDIA WRITE SUPPORT"}, + { 0x0038, "BD-R PSEUDO-OVERWRITE (POW)"}, + { 0x003A, "DVD+RW DOUBLE LAYER"}, + { 0x003B, "DVD+R DOUBLE LAYER"}, + { 0x0040, "BD READ"}, + { 0x0041, "BD WRITE"}, + { 0x0042, "TIME SAFE RECORDING (TSR)"}, + { 0x0050, "HD-DVD READ"}, + { 0x0051, "HD-DVD WRITE"}, + { 0x0080, "HYBRID DISC READ"}, + { 0x0100, "POWER MANAGEMENT"}, + { 0x0101, "S.M.A.R.T."}, + { 0x0102, "EMBEDDED CHANGER"}, + { 0x0103, "CD AUDIO ANALOG PLAY"}, + { 0x0104, "MICROCODE UPGRADE"}, + { 0x0105, "TIME-OUT"}, + { 0x0106, "DVD CSS"}, + { 0x0107, "REAL TIME STREAMING"}, + { 0x0108, "LOGICAL UNIT SERIAL NUMBER"}, + { 0x0109, "MEDIA SERIAL NUMBER"}, + { 0x010A, "DISC CONTROL BLOCKS"}, + { 0x010B, "DVD CPRM"}, + { 0x010C, "FIRMWARE DATE"}, + { 0x010D, "AACS"}, + { 0x0110, "VCPS"}, + + { 0xFF00, "PLEXTOR Autostrategy"}, + { 0xFFFF, "???"} +}; + +#define PROFILE_NONE 0x0000 +#define PROFILE_NON_REMOVABLE 0x0001 +#define PROFILE_REMOVABLE 0x0002 +#define PROFILE_MOPTIC_E 0x0003 +#define PROFILE_OPTIC_WO 0x0004 +#define PROFILE_AS_MO 0x0005 +#define PROFILE_CD_ROM 0x0008 +#define PROFILE_CD_R 0x0009 +#define PROFILE_CD_RW 0x000A +#define PROFILE_DVD_ROM 0x0010 +#define PROFILE_DVD_R_SEQ 0x0011 +#define PROFILE_DVD_RAM 0x0012 +#define PROFILE_DVD_RW_RESTOV 0x0013 +#define PROFILE_DVD_RW_SEQ 0x0014 +#define PROFILE_DVD_R_DL_SEQ 0x0015 +#define PROFILE_DVD_R_DL_JUMP 0x0016 +#define PROFILE_DVD_RW_DL 0x0017 +#define PROFILE_DVD_PLUS_RW 0x001A +#define PROFILE_DVD_PLUS_R 0x001B +#define PROFILE_DVD_PLUS_RW_DL 0x002A +#define PROFILE_DVD_PLUS_R_DL 0x002B +#define PROFILE_DDCD_ROM 0x0020 +#define PROFILE_DDCD_R 0x0021 +#define PROFILE_DDCD_RW 0x0022 + +#define PROFILE_BD_ROM 0x040 +#define PROFILE_BD_R_SEQ 0x041 +#define PROFILE_BD_R_RND 0x042 +#define PROFILE_BD_RE 0x043 + +#define PROFILE_HDDVD_ROM 0x050 +#define PROFILE_HDDVD_R 0x051 +#define PROFILE_HDDVD_RAM 0x052 +#define PROFILE_HDDVD_RW 0x053 +#define PROFILE_HDDVD_R_DL 0x058 +#define PROFILE_HDDVD_RW_DL 0x05A + +#define PROFILE_NO_PROFILE 0xFFFF + +static const desc16 PROFILES[]={ + { 0x0000, "NONE"}, + { 0x0001, "NON_REMOVABLE"}, + { 0x0002, "REMOVABLE"}, + { 0x0003, "MO-ERASABLE"}, + { 0x0004, "MO-WO"}, + { 0x0005, "AS-MO"}, + { 0x0008, "CD-ROM"}, + { 0x0009, "CD-R"}, + { 0x000A, "CD-RW"}, + + { 0x0010, "DVD-ROM"}, + { 0x0011, "DVD-R Sequential"}, + { 0x0012, "DVD-RAM"}, + { 0x0013, "DVD-RW Restricted Overwrite"}, + { 0x0014, "DVD-RW Sequential"}, + { 0x0015, "DVD-R/DL Sequential"}, + { 0x0016, "DVD-R/DL Layer Jump"}, + { 0x0017, "DVD-RW/DL"}, + { 0x001A, "DVD+RW"}, + { 0x001B, "DVD+R"}, + + { 0x0020, "DD CD-ROM"}, + { 0x0021, "DD CD-R"}, + { 0x0022, "DD CD-RW"}, + { 0x002A, "DVD+RW/DL"}, + { 0x002B, "DVD+R/DL"}, + + { 0x0040, "BD-ROM"}, + { 0x0041, "BD-R Sequential"}, + { 0x0042, "BD-R Random"}, + { 0x0043, "BD-RE"}, + + { 0x0050, "HD DVD-ROM"}, + { 0x0051, "HD DVD-R"}, + { 0x0052, "HD DVD-RAM"}, + { 0x0053, "HD DVD-RW"}, + { 0x0058, "HD DVD-R/DL"}, + { 0x005A, "HD DVD-RW/DL"}, + + { 0xFFFF, "NO PROFILE"} +}; + +static const dev_desc _devtbl[]= { +#if defined(__linux) + { "hd\0", 3}, // IDE devices + { "scd\0", 0}, // SCSI devices, kernel 2.4 + { "sr\0", 0 }, // SCSI devices, kernel 2.6 +#elif defined(__OpenBSD__) || defined(__NetBSD__) + { "rcd\0", 0 }, +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + { "acd\0", 0 }, + { "cd\0", 0 }, +#elif (defined (__APPLE__) && defined(__MACH__)) + { "disk\0", 0 }, +#elif defined(_WIN32) + { "C:", 0 }, + { "D:", 0 }, + { "E:", 0 }, + { "F:", 0 }, + { "G:", 0 }, + { "H:", 0 }, + { "I:", 0 }, + { "J:", 0 }, + { "K:", 0 }, + { "L:", 0 }, + { "M:", 0 }, + { "N:", 0 }, + { "O:", 0 }, + { "P:", 0 }, + { "Q:", 0 }, + { "R:", 0 }, + { "S:", 0 }, + { "T:", 0 }, + { "U:", 0 }, + { "V:", 0 }, + { "W:", 0 }, + { "X:", 0 }, + { "Y:", 0 }, + { "Z:", 0 }, +#endif + { "", -1 } +}; + +#if defined (_WIN32) +#define DEVTBL_DIRECT +#endif + +#define ERR_NO_DEV 0x0002 +#define ERR_NO_SCSI 0x0003 +#define ERR_NO_MMC 0x0004 + + +#define WTYPE_PACKET 0 +#define WTYPE_TAO 1 +#define WTYPE_SAO 2 +#define WTYPE_RAW 3 +#define WTYPE_LJ 4 + +typedef struct { + signed char wtype; + signed char dtype; + uint32_t id; + char name[32]; +} wr_mode; + +// write modes +static const wr_mode wr_modes[] = +{ + { 0, 8, 0x00000001, "Packet"}, + { 1, 8, 0x00000002, "TAO"}, + { 2, 8, 0x00000004, "SAO"}, + + { 2, 0, 0x00000010, "SAO/RAW"}, + { 2, 1, 0x00000020, "SAO/16"}, + { 2, 2, 0x00000040, "SAO/96R"}, + { 2, 3, 0x00000080, "SAO/96P"}, + + { 3, 1, 0x00000100, "RAW/16"}, + { 3, 2, 0x00000200, "RAW/96R"}, + { 3, 3, 0x00000400, "RAW/96P"}, + + { 4, 8, 0x00000800, "LayerJump"}, + + { -1, -1, 0x00, ""} +}; + +// track/session close modes +#define CLOSE_NONE 0x00 +#define CLOSE_TRACK 0x01 +#define CLOSE_SESSION 0x02 +#define CLOSE_FINALIZE 0x06 + + +#define DISC_NODISC 0 + +#define DISC_CDROM 1ULL +#define DISC_CDR (1ULL << 1) +#define DISC_CDRW (1ULL << 2) +#define DISC_CD (DISC_CDROM | DISC_CDR | DISC_CDRW) + +#define DISC_CDRWSUBT (7ULL << 3) +#define DISC_CDRWNS (0ULL << 3) +#define DISC_CDRWHS (1ULL << 3) +#define DISC_CDRWUS (2ULL << 3) +#define DISC_CDRWUSP (3ULL << 3) + +#define DISC_DVDROM (1ULL << 6) +#define DISC_DVDRAM (1ULL << 7) +#define DISC_DVDmR (1ULL << 8) +#define DISC_DVDmRW (1ULL << 9) +#define DISC_DVDmRWS (1ULL << 10) +#define DISC_DVDmRWR (1ULL << 11) +#define DISC_DVDmRDL (1ULL << 12) +#define DISC_DVDmRDLJ (1ULL << 13) + +#define DISC_DVDpRW (1ULL << 14) +#define DISC_DVDpRWDL (1ULL << 15) +#define DISC_DVDpR (1ULL << 16) +#define DISC_DVDpRDL (1ULL << 17) + +#define DISC_DVDmRWDL (1ULL << 31) + +#define DISC_DVDminus (DISC_DVDmR | DISC_DVDmRW | DISC_DVDmRWS | DISC_DVDmRWR | DISC_DVDmRWDL | DISC_DVDmRDL | DISC_DVDmRDLJ) +#define DISC_DVDplus (DISC_DVDpR | DISC_DVDpRW | DISC_DVDpRDL | DISC_DVDpRWDL) +#define DISC_DVD (DISC_DVDROM | DISC_DVDRAM | DISC_DVDminus | DISC_DVDplus) + +#define DISC_DDCD_ROM (1ULL << 18) +#define DISC_DDCD_R (1ULL << 19) +#define DISC_DDCD_RW (1ULL << 20) +#define DISC_DDCD (DISC_DDCD_ROM | DISC_DDCD_R | DISC_DDCD_RW) + +#define DISC_BD_ROM (1ULL << 21) +#define DISC_BD_R_SEQ (1ULL << 22) +#define DISC_BD_R_RND (1ULL << 23) +#define DISC_BD_RE (1ULL << 24) +#define DISC_BD (DISC_BD_ROM | DISC_BD_R_SEQ | DISC_BD_R_RND | DISC_BD_RE) + +#define DISC_HDDVD_ROM (1ULL << 25) +#define DISC_HDDVD_R (1ULL << 26) +#define DISC_HDDVD_RAM (1ULL << 27) +#define DISC_HDDVD_RW (1ULL << 28) +#define DISC_HDDVD_RDL (1ULL << 29) +#define DISC_HDDVD_RWDL (1ULL << 30) +#define DISC_HDDVD (DISC_HDDVD_ROM | DISC_HDDVD_R | DISC_HDDVD_RAM | DISC_HDDVD_RW | DISC_HDDVD_RDL | DISC_HDDVD_RWDL) + +#define DISC_UN (1ULL << 63) + +static const desc64 MEDIA[]={ + { DISC_NODISC, "No Media"}, + { DISC_CDROM, "CD-ROM" }, + { DISC_CDR, "CD-R" }, + { DISC_CDRW, "CD-RW" }, +/* +#define DISC_CDRWSUBT = { 0x70000000, "" }, +#define DISC_CDRWMS = { 0x10000000, "" }, +#define DISC_CDRWHS = { 0x20000000, "" }, +#define DISC_CDRWUS = { 0x30000000, "" }, +#define DISC_CDRWUSP = { 0x40000000, "" }, +*/ + { DISC_DVDROM, "DVD-ROM" }, + { DISC_DVDRAM, "DVD-RAM" }, + { DISC_DVDmR, "DVD-R" }, + { DISC_DVDmRW, "DVD-RW (unknown subtype)" }, + { DISC_DVDmRWS, "DVD-RW (Sequential)" }, + { DISC_DVDmRWR, "DVD-RW (Restricted overwrite)" }, + { DISC_DVDmRDL, "DVD-R DL (Sequential)" }, + { DISC_DVDmRDLJ, "DVD-R DL (Layer Jump)" }, + + { DISC_DVDpRW, "DVD+RW" }, + { DISC_DVDpRWDL, "DVD+RW DL" }, + { DISC_DVDpR, "DVD+R" }, + { DISC_DVDpRDL, "DVD+R DL" }, + + { DISC_DDCD_ROM, "DDCD-ROM" }, + { DISC_DDCD_R, "DDCD-R" }, + { DISC_DDCD_RW, "DDCD-RW" }, + + { DISC_BD_ROM, "BD-ROM" }, + { DISC_BD_R_SEQ, "BD-R (Sequential)" }, + { DISC_BD_R_RND, "BD-R (Random)" }, + { DISC_BD_RE, "BD-RE" }, + + { DISC_HDDVD_ROM, "HDDVD-ROM" }, + { DISC_HDDVD_RAM, "HDDVD-RAM" }, + { DISC_HDDVD_R, "HDDVD-R" }, + { DISC_HDDVD_RW, "HDDVD-RW" }, + { DISC_HDDVD_RDL, "HDDVD-R DL" }, + { DISC_HDDVD_RWDL,"HDDVD-RW DL" }, + + { DISC_UN, "unknown" }, + { 0xFFFFFFFFULL, "???" } +}; + +#define BOOK_DVD_ROM 0x00 +#define BOOK_DVD_RAM 0x01 +#define BOOK_DVD_R 0x02 +#define BOOK_DVD_RW 0x03 +#define BOOK_HDDVD_ROM 0x04 +#define BOOK_HDDVD_RAM 0x05 +#define BOOK_HDDVD_R 0x06 +#define BOOK_DVD_PRW 0x09 +#define BOOK_DVD_PR 0x0A +#define BOOK_DVD_PRW_DL 0x0D +#define BOOK_DVD_PR_DL 0x0E + +static const str_dev book_type_tbl[16]={ + "DVD-ROM", "DVD-RAM", "DVD-R", "DVD-RW", + "HD DVD-ROM", "HD DVD-RAM", "HD DVD-R ","unknown", + "unknown", "DVD+RW", "DVD+R", "unknown", + "unknown", "DVD+RW DL", "DVD+R DL", "unknown" +}; + +static const str_dev max_rate_tbl[16]={ + "2.52 Mbps", "5.04 Mbps", "10.08 Mbps", "20.16 Mbps", + "30.24 Mbps", "Reserved", "Reserved", "Reserved", + "Reserved", "Reserved", "Reserved", "Reserved", + "Reserved", "Reserved", "Reserved", "Not specified" +}; + +#define COMMAND_FAILED -0x01 + +#define Media_BLANK 0x80 +#define Media_NOTBLANK 0x7F +#define Media_NoMedia 0x00 +#define Media_CD 0x01 +#define Media_DVD 0x02 + +#define CAP_REMOVABLE_MEDIA 1ULL +#define CAP_MORPHING (1ULL << 1) +#define CAP_EMBEDDED_CHANGER (1ULL << 2) +#define CAP_MICROCODE_UPGRADE (1ULL << 3) +#define CAP_SMART (1ULL << 4) +#define CAP_REAL_TIME_STREAMING (1ULL << 5) +#define CAP_POWER_MANAGEMENT (1ULL << 6) +#define CAP_DEFECT_MANAGEMENT (1ULL << 7) +#define CAP_DVD_CSS (1ULL << 8) +#define CAP_DVD_CPRM (1ULL << 9) +#define CAP_C2 (1ULL << 10) +#define CAP_CD_TEXT (1ULL << 11) +#define CAP_CD_AUDIO (1ULL << 12) +#define CAP_DAE (1ULL << 13) +#define CAP_ACCURATE_STREAM (1ULL << 14) + +#define CAP_COMPOSITE (1ULL << 15) +#define CAP_DIGITAL_PORT_1 (1ULL << 16) +#define CAP_DIGITAL_PORT_2 (1ULL << 17) +#define CAP_MULTISESSION (1ULL << 18) +#define CAP_MODE2_FORM1 (1ULL << 19) +#define CAP_MODE2_FORM2 (1ULL << 20) +#define CAP_TEST_WRITE_CD (1ULL << 21) +#define CAP_READ_BAR_CODE (1ULL << 22) +#define CAP_UPC (1ULL << 23) +#define CAP_ISRC (1ULL << 24) +#define CAP_SIDE_CHANGE (1ULL << 25) +#define CAP_LOCK (1ULL << 26) +#define CAP_EJECT (1ULL << 27) +#define CAP_TEST_WRITE_DVD (1ULL << 28) +#define CAP_SSA (1ULL << 29) + +//#define CAP_ = 0x00000000; + +#define CAP_SET_CD_SPEED (1ULL << 30) +#define NCAP_SET_CD_SPEED (~CAP_SET_CD_SPEED); + +#define CAP_TEST_WRITE_DVD_PLUS (1ULL << 31) +#define CAP_BURN_FREE (1ULL << 32) + +static const desc64 capabilities[] = { + { CAP_REMOVABLE_MEDIA, "Removable media" }, + { CAP_COMPOSITE, "Composite out" }, + { CAP_DIGITAL_PORT_1, "Digital port 1" }, + { CAP_DIGITAL_PORT_2, "Digital port 2" }, + { CAP_MULTISESSION, "Multisession" }, + { CAP_MODE2_FORM1, "Mode 2 Form 1" }, + { CAP_MODE2_FORM2, "Mode 2 Form 2" }, + { CAP_READ_BAR_CODE, "Bar Code Read" }, + { CAP_BURN_FREE, "BURN-free" }, + { CAP_TEST_WRITE_CD, "Test Write CD" }, + { CAP_TEST_WRITE_DVD, "Test Write DVD-" }, + { CAP_TEST_WRITE_DVD_PLUS, "Test Write DVD+" }, + { CAP_UPC, "UPC" }, + { CAP_ISRC, "ISRC" }, + { CAP_SIDE_CHANGE, "Side Change" }, + { CAP_EJECT, "Media Eject" }, + { CAP_LOCK, "Media Lock" }, + { CAP_SMART, "S.M.A.R.T." }, + { CAP_MICROCODE_UPGRADE, "Firmware upgrade" }, + { CAP_MORPHING, "Morphing" }, + { CAP_POWER_MANAGEMENT, "Power Management" }, + { CAP_EMBEDDED_CHANGER, "Embedded Changer" }, + { CAP_C2, "C2 Pointers" }, + { CAP_CD_AUDIO, "Audio-CD" }, + { CAP_DAE, "DAE" }, + { CAP_CD_TEXT, "CD-text" }, + { CAP_ACCURATE_STREAM, "Accurate Stream" }, + { CAP_DEFECT_MANAGEMENT, "Defect Management" }, + { CAP_REAL_TIME_STREAMING, "Realtime Streaming" }, + { CAP_SSA, "Spare Area Info" }, + { CAP_DVD_CSS, "DVD CSS" }, + { CAP_DVD_CPRM, "DVD CPRM" }, + + { 0, "" } +}; + +// RW +#define DEVICE_CD_ROM 1ULL // + +#define DEVICE_CD_R (1ULL << 1) // ++ +#define DEVICE_CD_RW (1ULL << 2) // ++ +#define DEVICE_DVD_ROM (1ULL << 3) // + +#define DEVICE_DVD_RAM (1ULL << 4) // ++ +#define DEVICE_DVD_R (1ULL << 5) // ++ +#define DEVICE_DVD_RW (1ULL << 6) // ++ + +//#define DEVICE_DVD_R_DL_SEQ (1ULL << 7) // ++ +//#define DEVICE_DVD_R_DL_LJ (1ULL << 8) // ++ +//#define DEVICE_DVD_R_DL (DEVICE_DVD_R_DL_SEQ | DEVICE_DVD_R_DL_LJ) // ++ +#define DEVICE_DVD_R_DL (1ULL << 7) // ++ + +#define DEVICE_DVD_RW_DL (1ULL << 9) // ++ +#define DEVICE_DVD_PLUS_R (1ULL << 10) // ++ +#define DEVICE_DVD_PLUS_RW (1ULL << 11) // ++ +#define DEVICE_DVD_PLUS_R_DL (1ULL << 12) // ++ +#define DEVICE_DVD_PLUS_RW_DL (1ULL << 13) // ++ +#define DEVICE_DVD (DEVICE_DVD_ROM | DEVICE_DVD_RAM \ + | DEVICE_DVD_R | DEVICE_DVD_RW | DEVICE_DVD_R_DL | DEVICE_DVD_RW_DL \ + | DEVICE_DVD_PLUS_R | DEVICE_DVD_PLUS_RW | DEVICE_DVD_PLUS_R_DL | DEVICE_DVD_PLUS_RW_DL) +#define DEVICE_MRW (1ULL << 14) // ++ +#define DEVICE_MRW_DVD (1ULL << 15) // ++ +#define DEVICE_DDCD_R (1ULL << 16) // ++ +#define DEVICE_DDCD_RW (1ULL << 17) // ++ +#define DEVICE_BD_ROM (1ULL << 18) // + +#define DEVICE_BD_R (1ULL << 19) // ++ +#define DEVICE_BD_RE (1ULL << 20) // ++ +#define DEVICE_BD (DEVICE_BD_ROM | DEVICE_BD_R | DEVICE_BD_RE) + +#define DEVICE_HDDVD_ROM (1ULL << 21) // + +#define DEVICE_HDDVD_R (1ULL << 22) // ++ +#define DEVICE_HDDVD_RAM (1ULL << 23) // ++ +#define DEVICE_HDDVD_RW (1ULL << 24) // +#define DEVICE_HDDVD_R_DL (1ULL << 25) // +#define DEVICE_HDDVD_RW_DL (1ULL << 26) // +#define DEVICE_HDDVD (DEVICE_HDDVD_ROM \ + | DEVICE_HDDVD_RAM | DEVICE_HDDVD_R | DEVICE_HDDVD_RW \ + | DEVICE_HDDVD_R_DL | DEVICE_HDDVD_RW_DL) + +#define CD_SPEED_MULT 177 +#define DVD_SPEED_MULT 1385 + +static const desc64 rw_capabilities[] = { +// { DEVICE_CD_ROM, "CD-ROM" }, + { DEVICE_CD_R, "CD-R" }, + { DEVICE_CD_RW, "CD-RW" }, + { DEVICE_MRW, "CD-MRW" }, + { DEVICE_DDCD_R, "DDCD-R" }, + { DEVICE_DDCD_RW, "DDCD-RW" }, + { 0, "-" }, + + { DEVICE_DVD_ROM, "DVD-ROM" }, + { DEVICE_DVD_RAM, "DVD-RAM" }, + { DEVICE_DVD_R, "DVD-R" }, + { DEVICE_DVD_RW, "DVD-RW" }, + +// { DEVICE_DVD_R_DL_SEQ (1ULL << 7) // ++ +// { DEVICE_DVD_R_DL_LJ (1ULL << 8) // ++ +// { DEVICE_DVD_R_DL (DEVICE_DVD_R_DL_SEQ | DEVICE_DVD_R_DL_LJ) // ++ + { DEVICE_DVD_R_DL, "DVD-R/DL" }, + { DEVICE_DVD_RW_DL, "DVD-RW/DL" }, + { DEVICE_DVD_PLUS_R, "DVD+R" }, + { DEVICE_DVD_PLUS_RW, "DVD+RW" }, + { DEVICE_DVD_PLUS_R_DL, "DVD+R/DL" }, + { DEVICE_DVD_PLUS_RW_DL,"DVD+RW/DL" }, + { DEVICE_MRW_DVD, "DVD+MRW" }, + { 0, "-" }, + + { DEVICE_BD_ROM, "BD-ROM" }, + { DEVICE_BD_R, "BD-R" }, + { DEVICE_BD_RE, "BD-RE" }, + + { DEVICE_HDDVD_ROM, "HDDVD-ROM" }, + { DEVICE_HDDVD_R, "HDDVD-R" }, + { DEVICE_HDDVD_RAM, "HDDVD-RAM" }, +// { DEVICE_HDDVD_RW, "HDDVD-RW" }, +// { DEVICE_HDDVD_R_DL, "HDDVD-R/DL" }, +// { DEVICE_HDDVD_RW_DL, "HDDVD-RW/DL"}, + + { 0, "" } +}; + +#define ERR_INVALID_OPCODE 0x00052000 +#define ERR_NO_MEDIUM 0x00023A00 +#define ERR_NO_ERROR 0x00000000 + +// **** Vendors definition +#define DEV_GENERIC 0x00000001 +#define DEV_PLEXTOR 0x00000002 +#define DEV_PIONEER 0x00000004 +#define DEV_NEC 0x00000008 +#define DEV_LITEON 0x00000010 +#define DEV_BENQ_RD 0x00000020 +#define DEV_BENQ_WR 0x00000040 +#define DEV_YAMAHA 0x00000080 +#define DEV_ASUS 0x00000100 +#define DEV_TSST 0x00000200 +#define DEV_LG 0x00000400 +#define DEV_TEAC 0x00000800 + +// Vendor-specififc features +#define PX_POWEREC 0x00000001 +#define PX_GIGAREC 0x00000002 +#define PX_VARIREC_CD 0x00000004 +#define PX_VARIREC_DVD 0x00000008 +#define PX_HCDRSS 0x00000010 +#define PX_SPDREAD 0x00000020 +#define PX_SECUREC 0x00000040 +#define PX_SILENT 0x00000080 +#define PX_ASTRATEGY 0x00000100 +#define PX_BITSET_R 0x00000200 +#define PX_BITSET_RDL 0x00000400 +#define PX_SIMUL_PLUS 0x00000800 +#define PX_ERASER 0x00001000 +#define PIO_QUIET 0x00002000 +#define YMH_AMQR 0x00004000 +#define YMH_FORCESPEED 0x00008000 +#define YMH_TATTOO 0x00010000 +#define LTN_ERASER 0x00020000 + +// Yamaha devices +#define YAMAHA_OLD 0x00000001 +#define YAMAHA_F1 0x00000002 + +// Plextor devices +#define PLEXTOR_OLD 0x00000001 + +#define PLEXTOR_4824 0x00000100 +#define PLEXTOR_5224 0x00000200 +#define PLEXTOR_PREMIUM 0x00000400 +#define PLEXTOR_708 0x00000800 +#define PLEXTOR_708A2 0x00001000 +#define PLEXTOR_712 0x00002000 +#define PLEXTOR_714 0x00004000 +#define PLEXTOR_716 0x00008000 +#define PLEXTOR_716AL 0x00010000 +#define PLEXTOR_755 0x00020000 +#define PLEXTOR_760 0x00040000 +#define PLEXTOR_PREMIUM2 0x00080000 + +//Pioneer devices +#define PIO_OLD 0x00000001 +#define PIO_DVR_106 0x00000002 +#define PIO_DVR_107 0x00000004 +#define PIO_DVR_108 0x00000008 +#define PIO_DVR_109 0x00000010 +#define PIO_DVR_110 0x00000020 +#define PIO_DVR_111 0x00000040 +#define PIO_DVR_112 0x00000080 +#define PIO_BDR 0x00000100 + +//Asus devices +#define ASUS_1612 0x00000001 +#define ASUS_2014 0x00000002 + +//Nec devices +#define NEC_OLD 0x00000001 +#define NEC_3520 0x00000002 +#define NEC_3530 0x00000004 +#define NEC_3540 0x00000008 +#define NEC_4550 0x00000010 +#define NEC_4570 0x00000020 +#define NEC_4650 0x00000040 +#define NEC_7170 0x00000080 +#define NEC_7200 0x00000100 + +//LiteOn devices +#define LTN_OLD 0x00000001 +#define LTN_CDR_G7 0x00000002 +#define LTN_SDVDR_G1 0x00000004 +#define LTN_SDVDR_G2 0x00000008 +#define LTN_SDVDR_G3 0x00000010 +#define LTN_DVDR_G1 0x00000020 +#define LTN_DVDR_G2 0x00000040 +#define LTN_DVDR_G3 0x00000080 +#define LTN_DVDR_G4 0x00000100 +#define LTN_DVDR_G5 0x00000200 +#define LTN_DVDR_G6 0x00000400 +#define LTN_DVDR_G7 0x00000800 +#define LTN_DVDR_G8 0x00001000 +#define LTN_BDR 0x00002000 + +#define LTN_iHAx1 0x00002000 +#define LTN_iHAx2 0x00004000 +#define LTN_iHAx3 0x00008000 +#define LTN_iHAx4 0x00010000 + +//BenQ devices +#define BENQ_OLD 0x00000001 +#define BENQ_DV1650V 0x00000002 +#define BENQ_DW1620 0x00000004 +#define BENQ_DW1625 0x00000008 +#define BENQ_DW1640 0x00000010 +#define BENQ_DW1650 0x00000020 +#define BENQ_DW1655 0x00000040 + +#define TSST_H2 0x00000002 + +// **** end of devices list + +#endif + diff --git a/lib/qpxtransport/include/qpx_opcodes.h b/lib/qpxtransport/include/qpx_opcodes.h new file mode 100644 index 0000000..2386818 --- /dev/null +++ b/lib/qpxtransport/include/qpx_opcodes.h @@ -0,0 +1,141 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2007 Gennady "ShultZ" Kozlov + * + * 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 __qpxtool_opcodes_h +#define __qpxtool_opcodes_h + +/* +const char SPC_ = 0x; +const char MMC_ = 0x; +*/ + +/* SCSI primary commands */ + +const char SPC_TEST_UNIT_READY = 0x00; +const char SPC_REQUEST_SENSE = 0x03; +const char SPC_INQUIRY = 0x12; +const char SPC_RESERVE_6 = 0x16; +const char SPC_RELEASE_6 = 0x17; +const char SPC_RECEIVE_DIAGNOSTIC_RESULTS = 0x1C; +const char SPC_SEND_DIAGNOSTIC = 0x1D; +const char SPC_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1E; +const char SPC_WRITE_BUFFER = 0x3B; +const char SPC_READ_BUFFER = 0x3C; +const char SPC_LOG_SELECT = 0x4C; +const char SPC_LOG_SENSE = 0x4D; +const char SPC_RESERVE_10 = 0x56; +const char SPC_RELEASE_10 = 0x57; +const char SPC_PERSISTENT_RESERVE_IN = 0x5E; +const char SPC_PERSISTENT_RESERVE_OUT = 0x5F; + +const char SPC_EXTENDED_COPY = 0x83; +const char SPC_RECEIVE_COPY_RESULTS = 0x84; +const char SPC_ACCESS_CONTROL_IN = 0x86; +const char SPC_ACCESS_CONTROL_OUT = 0x87; +const char SPC_READ_ATTRIBUTE = 0x8C; +const char SPC_WRITE_ATTRIBUTE = 0x8D; + +const char SPC_REPORT_LUNS = 0xA0; +const char SPC_SECURITY_PROTOCOL_IN = 0xA2; +const char SPC_SECURITY_PROTOCOL_OUT = 0xB5; + + +const char SMC_MOVE_MEDIUM_ATTACHED = 0xA7; +const char SMC_READ_ELEMENT_STATUS_ATTACHED = 0xB4; + +/* SCSI controller commands */ + +const char SCC_SPARE_IN = 0xBC; +const char SCC_SPARE_OUT = 0xBD; +const char SCC_VOLUME_SET_IN = 0xBE; +const char SCC_VOLUME_SET_OUT = 0xBF; + +/* SCSI multimedia commands */ + +const char MMC_FORMAT_UNIT = 0x04; + +const char MMC_MODE_SELECT_6 = 0x15; +const char MMC_MODE_SENSE_6 = 0x1A; +const char MMC_START_STOP_UNIT = 0x1B; + +const char MMC_READ_FORMAT_CAPACITIES = 0x23; +const char SBC_READ_CAPACITY = 0x25; +const char MMC_READ = 0x28; +const char MMC_WRITE = 0x2A; +const char MMC_SEEK = 0x2B; +const char MMC_WRITE_AND_VERIFY = 0x2E; +const char MMC_VERIFY = 0x2F; + +const char MMC_SYNC_CACHE = 0x35; + +const char MMC_READ_SUB_CHANNEL = 0x42; +const char MMC_READ_TOC_PMA_ATIP = 0x43; +const char MMC_READ_HEADER = 0x44; +const char MMC_PLAY_AUDIO = 0x45; +const char MMC_GET_CONFIGURATION = 0x46; +const char MMC_PLAY_AUDIO_MSF = 0x47; +const char MMC_GET_EVENT_STATUS_NOTIFICATION = 0x4A; +const char MMC_PAUSE_RESUME = 0x4B; +const char MMC_STOP_PLAY_SCAN = 0x4E; + +const char MMC_READ_DISC_INFORMATION = 0x51; +const char MMC_READ_TRACK_INFORMATION = 0x52; +const char MMC_RESERVE_TRACK = 0x53; +const char MMC_SEND_OPC_INFORMATION = 0x54; +const char MMC_MODE_SELECT_10 = 0x55; +const char MMC_REPAIR_TRACK = 0x58; +const char MMC_READ_MASTER_CUE = 0x59; +const char MMC_MODE_SENSE_10 = 0x5A; +const char MMC_CLOSE_TRACK_SESSION = 0x5B; +const char MMC_READ_BUFFER_CAPACITY = 0x5C; +const char MMC_SEND_CUE_SHEET = 0x5D; +const char MMC_BLANK = 0xA1; +const char MMC_SEND_EVENT = 0xA2; +const char MMC_SEND_KEY = 0xA3; +const char MMC_REPORT_KEY = 0xA4; +const char MMC_PLAY_AUDIO_12 = 0xA5; +const char MMC_LOAD_UNLOAD = 0xA6; +const char MMC_SET_READ_AHEAD = 0xA7; +const char MMC_READ_DVD = 0xA8; +const char MMC_GET_PERFORMANCE = 0xAC; +const char MMC_READ_DVD_STRUCTURE = 0xAD; + +const char MMC_SET_STREAMING = 0xB6; +const char MMC_READ_CD_MSF = 0xB9; +const char MMC_SCAN = 0xBA; +const char MMC_SET_SPEED = 0xBB; +const char MMC_PLAY_CD = 0xBC; +const char MMC_MECHANISM_STATUS = 0xBD; +const char MMC_READ_CD = 0xBE; + +const char PLEXTOR_GET_AUTH = 0xD4; +const char PLEXTOR_SEND_AUTH = 0xD5; + +//const char = 0xD8; + +const char PLEXTOR_PLEXERASER = 0xE3; +const char PLEXTOR_AS_RD = 0xE4; +const char PLEXTOR_AS_WR = 0xE5; + +const char SBC_FLUSH_CACHE = 0xE7; + +const char PLEXTOR_MODE = 0xE9; +const char PLEXTOR_QCHECK = 0xEA; +const char PLEXTOR_PREC_SPD = 0xEB; +const char PLEXTOR_MODE2 = 0xED; + +const char PLEXTOR_EEPROM_READ = 0xF1; + +const char PLEXTOR_SCAN_TA_FETE = 0xF3; +const char PLEXTOR_FETE_READOUT = 0xF5; + +#endif // __qpxtool_opcodes_h + diff --git a/lib/qpxtransport/include/qpx_transport.h b/lib/qpxtransport/include/qpx_transport.h new file mode 100644 index 0000000..1a21567 --- /dev/null +++ b/lib/qpxtransport/include/qpx_transport.h @@ -0,0 +1,402 @@ +// +// This is part of dvd+rw-tools by Andy Polyakov +// +// Use-it-on-your-own-risk, GPL bless... +// +// For further details see http://fy.chalmers.se/~appro/linux/DVD+RW/ +// + + +// +// modified to use with QPxTool http://qpxtool.sf.net (C) 2005-2009,2012, Gennady "ShultZ" Kozlov +// + +#include + +#ifndef __qpx_transport_h +#define __qpx_transport_h + +#if defined(__unix) || defined(__unix__) + +extern long getmsecs(); +#include +#include +#include +#ifndef EMEDIUMTYPE +#define EMEDIUMTYPE EINVAL +#endif +#ifndef ENOMEDIUM +#define ENOMEDIUM ENODEV +#endif + +//* +#elif defined(_WIN32) + +#include +#include +#include +#include +#define ssize_t LONG_PTR +#define off64_t __int64 + +#if !defined(__MINGW32__) +#include "win32err.h" +#endif + +#define poll(a,b,t) Sleep(t) +#define getmsecs() GetTickCount() + +#include +#define ENV_LOCALE ".OCP" +//*/ +#endif + +#define CREAM_ON_ERRNO_NAKED(s) \ + switch ((s)[12]) \ + { case 0x04: errno=EAGAIN; break; \ + case 0x20: errno=ENODEV; break; \ + case 0x21: if ((s)[13]==0) errno=ENOSPC; \ + else errno=EINVAL; \ + break; \ + case 0x30: errno=EMEDIUMTYPE; break; \ + case 0x3A: errno=ENOMEDIUM; break; \ + } +#define CREAM_ON_ERRNO(s) do { CREAM_ON_ERRNO_NAKED(s) } while(0) + +#define FATAL_START(er) (0x80|(er)) + +#define ERRCODE_FIXED(s) ((((s)[2]&0x0F)<<16)|((s)[12]<<8)|((s)[13])) +#define ERRCODE_DESCR(s) ((((s)[1]&0x0F)<<16)|((s)[2]<<8)|((s)[3])) +#define ERRCODE(s) ((s)[0] == 0x70 || (s)[0] == 0x71 ? \ + ERRCODE_FIXED(s) : \ + ((s)[0] == 0x72 || (s)[0] == 0x73 ? \ + ERRCODE_DESCR(s) : 0)) + +//#define ERRCODE(s) ((((s)[2]&0x0F)<<16)|((s)[12]<<8)|((s)[13])) + +#define SK(errcode) (((errcode)>>16)&0xF) +#define ASC(errcode) (((errcode)>>8)&0xFF) +#define ASCQ(errcode) ((errcode)&0xFF) + +extern void sperror (const char *cmd, int err); //, Scsi_Command *scsi); + +class autofree { + private: + unsigned char *ptr; + public: + autofree(); + ~autofree(); + unsigned char *operator=(unsigned char *str) { return ptr=str; } + operator unsigned char *() { return ptr; } +}; + +#if defined(__linux) || defined(__GNU__) + +//#include +#if defined(__linux) +#include +#elif defined(__GNU__) +#include +#endif +//#include +//#include +//#include +#if defined(__linux) +#include +#endif +#if !defined(SG_FLAG_LUN_INHIBIT) +# if defined(SG_FLAG_UNUSED_LUN_INHIBIT) +# define SG_FLAG_LUN_INHIBIT SG_FLAG_UNUSED_LUN_INHIBIT +# else +# define SG_FLAG_LUN_INHIBIT 0 +# endif +#endif +#ifndef CHECK_CONDITION +#define CHECK_CONDITION 0x01 +#endif + +typedef enum { NONE=CGC_DATA_NONE, // 3 + READ=CGC_DATA_READ, // 2 + WRITE=CGC_DATA_WRITE // 1 + } Direction; +#ifdef SG_IO + +static const int Dir_xlate [4] = { // should have been defined + // private in USE_SG_IO scope, + // but it appears to be too + 0, // implementation-dependent... + SG_DXFER_TO_DEV, // 1,CGC_DATA_WRITE + SG_DXFER_FROM_DEV, // 2,CGC_DATA_READ + SG_DXFER_NONE }; // 3,CGC_DATA_NONE + +class USE_SG_IO { +private: + int yes_or_no; +public: + USE_SG_IO(); + ~USE_SG_IO(); + operator int() const { return yes_or_no; } + int operator[] (Direction dir) const { return Dir_xlate[dir]; } +}; + +static const class USE_SG_IO use_sg_io; + +#endif + +class Scsi_Command { +private: +// long cmd_time; + int fd,autoclose; + char *filename; + struct cdrom_generic_command cgc; + union sense_union { + struct request_sense s; + unsigned char u[18]; + } _sense; +#ifdef SG_IO + struct sg_io_hdr sg_io; +#else + struct { int cmd_len,timeout; } sg_io; +#endif +public: + Scsi_Command(); + Scsi_Command(int f); + Scsi_Command(void*f); + ~Scsi_Command(); + int associate (const char *file,const struct stat *ref); + unsigned char &operator[] (size_t i); + unsigned char &operator()(size_t i); + unsigned char *sense(); + void timeout(int i); + size_t residue(); + int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0); + int umount(int f); + int is_reload_needed (); +}; + +#elif defined(__OpenBSD__) || defined(__NetBSD__) + +#include +#include +#include +#include +#include + +typedef off_t off64_t; +#define stat64 stat +#define fstat64 fstat +#define open64 open +#define pread64 pread +#define pwrite64 pwrite +#define lseek64 lseek + +typedef enum { NONE=0, + READ=SCCMD_READ, + WRITE=SCCMD_WRITE + } Direction; + +class Scsi_Command { +private: + int fd,autoclose; + char *filename; + scsireq_t req; +public: + Scsi_Command(); + Scsi_Command(int f); + Scsi_Command(void*f); + ~Scsi_Command(); + int associate (const char *file,const struct stat *ref); + unsigned char &operator[] (size_t i); + unsigned char &operator()(size_t i); + unsigned char *sense(); + void timeout(int i); + size_t residue(); + int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0); + int umount(int f); + int is_reload_needed (); +}; + +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef off_t off64_t; +#define stat64 stat +#define fstat64 fstat +#define open64 open +#define pread64 pread +#define pwrite64 pwrite +#define lseek64 lseek + +#define ioctl_fd (((struct cam_device *)ioctl_handle)->fd) + +typedef enum { NONE=CAM_DIR_NONE, + READ=CAM_DIR_IN, + WRITE=CAM_DIR_OUT + } Direction; + +class Scsi_Command { +private: + int fd,autoclose; + char *filename; + struct cam_device *cam; + union ccb ccb; +public: + Scsi_Command(); + Scsi_Command(int f); + Scsi_Command(void *f); + ~Scsi_Command(); + + int associate (const char *file,const struct stat *ref); + unsigned char &operator[] (size_t i); + unsigned char &operator()(size_t i); + unsigned char *sense(); + void timeout(int i); + size_t residue(); + int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0); + int umount(int f); +#define RELOAD_NEVER_NEEDED // according to Matthew Dillon + int is_reload_needed (); +}; + +//* +#elif defined(_WIN32) + +#if defined(__MINGW32__) +#include +#define FSCTL_LOCK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM,6,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define FSCTL_UNLOCK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM,7,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define FSCTL_DISMOUNT_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM,8,METHOD_BUFFERED,FILE_ANY_ACCESS) +#else +#include +#include +#endif + +typedef enum { NONE=SCSI_IOCTL_DATA_UNSPECIFIED, + READ=SCSI_IOCTL_DATA_IN, + WRITE=SCSI_IOCTL_DATA_OUT + } Direction; + +typedef struct { + SCSI_PASS_THROUGH_DIRECT spt; + unsigned char sense[18]; +} SPKG; + +class Scsi_Command { +private: + HANDLE fd; + int autoclose; + char *filename; + SPKG p; +public: + Scsi_Command(); + Scsi_Command(void*f); + ~Scsi_Command(); + int associate (const char *file,const struct stat *ref=NULL); + unsigned char &operator[] (size_t i); + unsigned char &operator()(size_t i); + unsigned char *sense(); + void timeout(int i); + size_t residue() { return 0; } // bogus + int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0); + int umount (int f=-1); + +#define RELOAD_NEVER_NEEDED + int is_reload_needed (); +}; +//*/ + +#elif defined (__APPLE__) && defined (__MACH__) + +// +// This code targets Darwin Kernel Version 6.x, a.k.a. Mac OS X v10.2, +// or later, but upon initial release was tested only on PowerPC under +// Darwin Kernel Version 8.7.0, a.k.a. Mac OS X v10.4.7 (Tiger). +// + +typedef off_t off64_t; +#define stat64 stat +#define fstat64 fstat +#define open64 open +#define pread64 pread +#define pwrite64 pwrite +#define lseek64 lseek + +#include +#include +#include +#include +#include + +static int iokit_err (IOReturn ioret,SCSITaskStatus stat, + const unsigned char *sense); + +// NB! ellipsis is GCC-ism, but conveniently Apple ships only gcc:-) +#define MMCIO(h,func,...) ({ \ + MMCDeviceInterface **di = (MMCDeviceInterface **)h;\ + SCSITaskStatus stat; \ + union { \ + SCSI_Sense_Data s; \ + unsigned char u[18]; \ + } sense; \ + IOReturn ioret; \ + memset (&sense,0,sizeof(sense)); \ + ioret = (*di)->func(di,__VA_ARGS__,&stat,&sense.s); \ + iokit_err (ioret,stat,sense.u); }) + +typedef enum { NONE=kSCSIDataTransfer_NoDataTransfer, + READ=kSCSIDataTransfer_FromTargetToInitiator, + WRITE=kSCSIDataTransfer_FromInitiatorToTarget + } Direction; + +class Scsi_Command { +private: + int autoclose,_timeout; + char *filename; + io_object_t scsiob; + IOCFPlugInInterface **plugin; + MMCDeviceInterface **mmcdif; + SCSITaskDeviceInterface **taskif; + unsigned char cdb[16]; + union { + SCSI_Sense_Data s; + unsigned char u[18]; + } _sense; + size_t cdblen,resid; +public: + Scsi_Command(); + Scsi_Command(void *f); + ~Scsi_Command(); + + int associate (const char *file,const struct stat *ref=NULL); + + unsigned char &operator[] (size_t i); + unsigned char &operator() (size_t i); + unsigned char *sense(); + void timeout(int i); + size_t residue(); + int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0); + int umount(int f=-1); +#define RELOAD_NEVER_NEEDED + int is_reload_needed(int not_used); +}; + +#else + +#error "Unsupported OS" +#undef ERRCODE +#undef CREAM_ON_ERRNO +#undef CREAM_ON_ERRNO_NAKED + +#endif + +#endif + diff --git a/lib/qpxtransport/include/sense.h b/lib/qpxtransport/include/sense.h new file mode 100644 index 0000000..80af7ef --- /dev/null +++ b/lib/qpxtransport/include/sense.h @@ -0,0 +1,21 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2007 Gennady "ShultZ" Kozlov + * + * 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 __sense_h +#define __sense_h + +#define SK(errcode) (((errcode)>>16)&0xF) +#define ASC(errcode) (((errcode)>>8)&0xFF) +#define ASCQ(errcode) ((errcode)&0xFF) + +extern int sense2str(int err, char* str); + +#endif diff --git a/lib/qpxtransport/include/threads.h b/lib/qpxtransport/include/threads.h new file mode 100644 index 0000000..0f1780f --- /dev/null +++ b/lib/qpxtransport/include/threads.h @@ -0,0 +1,67 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Gennady "ShultZ" Kozlov + * + * 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 DR_THREADS_H +#define DR_THREADS_H + +#if defined (__unix) || defined (__unix__) + +#include +#include +class Mutex { +public: + Mutex(); + ~Mutex(); + void lock(); + void unlock(); +private: + pthread_mutex_t m; +}; + +#define thread_t pthread_t +#define thread_create(id,attr,func,arg) pthread_create(id,attr,func,arg) +#define thread_join(id,f) pthread_join(id, f) +#define thread_exit(r) pthread_exit((void*)(r)) + +#elif defined (_WIN32) + +#include +class Mutex { +public: + Mutex(); + ~Mutex(); + void lock(); + void unlock(); +private: + CRITICAL_SECTION m; +}; + +#define thread_t HANDLE +extern int WIN32_thread_create(HANDLE *tid, void *attr, void*(*func)(void*), void* arg); +extern int WIN32_thread_join(HANDLE& tid, void **ret); + +#define thread_create(id,attr,func,arg) WIN32_thread_create(id,attr,func,arg) +#define thread_join(id,f) WIN32_thread_join(id, f) +#define thread_exit(r) ExitThread(r) + +extern int close(HANDLE h); + +#endif + +typedef int pipe_t[2]; + +extern char **add_arg(char **args, int *argc, const char *arg); +//extern int createchild(char **argv, pipe_t &rdpipe, bool r, pipe_t &wrpipe, bool w); +extern int createChildProcess(char **argv, pipe_t *rdpipe = NULL, pipe_t *wrpipe = NULL); + +#endif // DR_THREADS_H + diff --git a/lib/qpxtransport/qpx_mmc.cpp b/lib/qpxtransport/qpx_mmc.cpp new file mode 100644 index 0000000..022420f --- /dev/null +++ b/lib/qpxtransport/qpx_mmc.cpp @@ -0,0 +1,3156 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2012 Gennady "ShultZ" Kozlov + * + * original version of CD-R(W) manufacturer identification code got from cdrecord, (C) Joerg Schilling + * + * 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 +#include +#include +#include + +#include +//#include + +#include "qpx_mmc.h" +#include "colors.h" + +#define SPINUP_REVERSE +//#define MODE_PAGES_DEBUG +//#define FEATURE_DEBUG + +#ifndef DEVTBL_DIRECT +#include +#endif + +int convert_to_ID (drive_info* drive); + +drive_info::drive_info(const char* _device){ + device=(char*)malloc(bufsz_dev); + strcpy(device,_device); + rd_buf=(unsigned char*)malloc(bufsz_rd); + if (!cmd.associate(device, NULL)) { +// printf("** Can't open device: %16s\n",_device); + err=1; + mmc=-1; + return; + } + mmc=0; + life.ok=0; + parms.interval=1; + parms.tests=0; + capabilities=0; + rd_capabilities=0; + wr_capabilities=0; + wr_modes=0; + get_performance_fail=0; + ven_features=0; + chk_features=0; + plextor.gigarec=0; + plextor.varirec_state_cd=0; + plextor.varirec_pwr_cd=0; + plextor.varirec_str_cd=0; + plextor.varirec_state_dvd=0; + plextor.varirec_pwr_dvd=0; + plextor.varirec_str_dvd=0; + plextor.powerec_state=0; + plextor.plexeraser=0; + ven_ID=0; + dev_ID=0; + iface_id=0; + iface[0]=0; + loader_id=0; + parms.scan_speed_cd=8; + parms.scan_speed_dvd=5; + parms.spindown_idx=0; + parms.speed_mult=176; + silent=0; + rpc.phase=0; + rpc.region=0; + + plextor_silent.pstate = 0; + plextor_silent.prd_cd = 0; + plextor_silent.pwr_cd = 0; + plextor_silent.prd_dvd = 0; +// plextor_silent.pwr_dvd = 0; + plextor_silent.paccess = 0; + plextor_silent.peject = 0; + plextor_silent.pload = 0; + + media.MID_type = MID_type_NONE; + media.MID_size = 0; + + media.dvdcss.protection=0; + media.dvdcss.p_titles=NULL; +#if (DVDCSS_KEY_CACHE > 0) + media.dvdcss.psz_cachefile[0]=0; + media.dvdcss.psz_block=0; +#endif +} + + +drive_info::~drive_info(){ +// delete urd_buf; + busy=1; +// delete pthread_t; + free(rd_buf); + free(device); +} + + +/* +bool drive_info::lock(){ + if (!busy) {busy=true; return true; } + else { return false; } +} + +bool drive_info::unlock(){ + busy=false; + return true; +} + +bool drive_info::isBusy(){ + return busy; +} + +void drive_info::wait_free(){ + while (busy); +} +*/ + +int print_sense (int err) { + char str[128]; + strcpy(str,"[unknown error]"); + switch (SK(err)) { + case 0x1: + switch (ASC(err)) { + case 0x0B: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"WARNING"); break; + case 0x01: strcpy(str,"WARNING - SPECIFIED TEMPERATURE EXCEEDED"); break; + case 0x02: strcpy(str,"WARNING - ENCLOSURE DEGRADED"); break; + + default: sprintf(str,"WARNING, ASCQ=%02X",ASCQ(err)); break; + } + break; + case 0x17: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"RECOVERED DATA WITH NO ERROR CORRECTION APPLIED"); break; + case 0x01: strcpy(str,"RECOVERED DATA WITH RETRIES"); break; + case 0x02: strcpy(str,"RECOVERED DATA WITH POSITIVE HEAD OFFSET"); break; + case 0x03: strcpy(str,"RECOVERED DATA WITH NEGATIVE HEAD OFFSET"); break; + case 0x04: strcpy(str,"RECOVERED DATA WITH RETRIES AND/OR CIRC APPLIED"); break; + case 0x05: strcpy(str,"RECOVERED DATA USING PREVIOUS SECTOR ID"); break; + case 0x07: strcpy(str,"RECOVERED DATA WITHOUT ECC - RECOMMEND REASSIGNMENT"); break; + case 0x08: strcpy(str,"RECOVERED DATA WITHOUT ECC - RECOMMEND REWRITE"); break; + case 0x09: strcpy(str,"RECOVERED DATA WITHOUT ECC - DATA REWRITTEN"); break; + + default: strcpy(str,"RECOVERED DATA WITH NO ERROR CORRECTION APPLIED"); break; + } + break; + case 0x18: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"RECOVERED DATA WITH ERROR CORRECTION APPLIED"); break; + case 0x01: strcpy(str,"RECOVERED DATA WITH ERROR CORR. & RETRIES APPLIED"); break; + case 0x02: strcpy(str,"RECOVERED DATA - DATA AUTO-REALLOCATED"); break; + case 0x03: strcpy(str,"RECOVERED DATA WITH CIRC"); break; + case 0x04: strcpy(str,"RECOVERED DATA WITH L-EC"); break; + case 0x05: strcpy(str,"RECOVERED DATA - RECOMMEND REASSIGNMENT"); break; + case 0x06: strcpy(str,"RECOVERED DATA - RECOMMEND REWRITE"); break; + case 0x08: strcpy(str,"RECOVERED DATA WITH LINKING"); break; + + default: strcpy(str,"RECOVERED DATA WITH ERROR CORRECTION APPLIED"); break; + } + break; + case 0x5D: + switch (ASCQ(err)) { + case 0x01: strcpy(str,"FAILURE PREDICTION THRESHOLD EXCEEDED - Predicted Media failure"); break; + case 0x02: strcpy(str,"LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED"); break; + case 0x03: strcpy(str,"FAILURE PREDICTION THRESHOLD EXCEEDED - Predicted Spare Area Exhaustion"); break; + case 0xFF: strcpy(str,"FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)"); break; + + default: strcpy(str,"LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED"); break; + } + break; + case 0x73: + switch (ASCQ(err)) { + case 0x01: strcpy(str,"POWER CALIBRATION AREA ALMOST FULL"); break; + case 0x06: strcpy(str,"RMA/PMA IS ALMOST FULL"); break; + } + break; + } + case 0x2: + switch (ASC(err)) { + case 0x04: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE"); break; + case 0x01: strcpy(str,"LOGICAL UNIT IS IN PROCESS OF BECOMING READY"); break; + case 0x02: strcpy(str,"LOGICAL UNIT NOT READY, INITIALIZING CMD. REQUIRED"); break; + case 0x03: strcpy(str,"LOGICAL UNIT NOT READY, MANUAL INTERVENTION REQUIRED"); break; + case 0x04: strcpy(str,"LOGICAL UNIT NOT READY, FORMAT IN PROGRESS"); break; + case 0x07: strcpy(str,"LOGICAL UNIT NOT READY, OPERATION IN PROGRESS"); break; + case 0x08: strcpy(str,"LOGICAL UNIT NOT READY, LONG WRITE IN PROGRESS"); break; + + default: strcpy(str,"LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE"); break; + } + break; + case 0x30: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"INCOMPATIBLE MEDIUM INSTALLED"); break; + case 0x01: strcpy(str,"CANNOT READ MEDIUM - UNKNOWN FORMAT"); break; + case 0x02: strcpy(str,"CANNOT READ MEDIUM - INCOMPATIBLE FORMAT"); break; + case 0x03: strcpy(str,"CLEANING CARTRIDGE INSTALLED"); break; + case 0x04: strcpy(str,"CANNOT WRITE MEDIUM - UNKNOWN FORMAT"); break; + case 0x05: strcpy(str,"CANNOT WRITE MEDIUM - INCOMPATIBLE FORMAT"); break; + case 0x06: strcpy(str,"CANNOT FORMAT MEDIUM - INCOMPATIBLE MEDIUM"); break; + case 0x07: strcpy(str,"CLEANING FAILURE"); break; + case 0x11: strcpy(str,"CANNOT WRITE MEDIUM - UNSUPPORTED MEDIUM VERSION"); break; + + default: strcpy(str,"INCOMPATIBLE MEDIUM INSTALLED"); break; + } + break; + case 0x3A: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"MEDIUM NOT PRESENT"); break; + case 0x01: strcpy(str,"MEDIUM NOT PRESENT - TRAY CLOSED"); break; + case 0x02: strcpy(str,"MEDIUM NOT PRESENT - TRAY OPEN"); break; + + default: strcpy(str,"MEDIUM NOT PRESENT"); break; + } + break; + case 0x3E: strcpy(str,"LOGICAL UNIT HAS NOT SELF-CONFIGURED YET"); break; /* ASCQ=00: */ + } + break; + case 0x3: + switch (ASC(err)) { + case 0x02: strcpy(str,"NO SEEK COMPLETE"); break; /* ASCQ = 0x00 */ + case 0x06: strcpy(str,"NO REFERENCE POSITION FOUND"); break; + case 0x0C: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"WRITE ERROR"); break; + case 0x07: strcpy(str,"WRITE ERROR - RECOVERY NEEDED"); break; + case 0x08: strcpy(str,"WRITE ERROR - RECOVERY FAILED"); break; + case 0x09: strcpy(str,"WRITE ERROR - LOSS OF STREAMING"); break; + case 0x0A: strcpy(str,"WRITE ERROR - PADDING BLOCKS ADDED"); break; + + default: strcpy(str,"WRITE ERROR"); break; + } + break; + case 0x11: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"UNRECOVERED READ ERROR"); break; + case 0x01: strcpy(str,"READ RETRIES EXHAUSTED"); break; + case 0x02: strcpy(str,"ERROR TOO LONG TO CORRECT"); break; + case 0x05: strcpy(str,"L-EC UNCORRECTABLE ERROR"); break; + case 0x06: strcpy(str,"CIRC UNRECOVERED ERROR"); break; + case 0x0F: strcpy(str,"ERROR READING UPC/EAN NUMBER"); break; + case 0x10: strcpy(str,"ERROR READING ISRC NUMBER"); break; + + default: strcpy(str,"UNRECOVERED READ ERROR"); break; + } + break; + case 0x15: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"RANDOM POSITIONING ERROR"); break; + case 0x01: strcpy(str,"MECHANICAL POSITIONING ERROR"); break; + case 0x02: strcpy(str,"POSITIONING ERROR DETECTED BY READ OF MEDIUM"); break; + + default: strcpy(str,"RANDOM POSITIONING ERROR"); break; + } + break; + case 0x31: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"MEDIUM FORMAT CORRUPTED"); break; + case 0x01: strcpy(str,"FORMAT COMMAND FAILED"); break; + case 0x02: strcpy(str,"ZONED FORMATTING FAILED DUE TO SPARE LINKING"); break; + + default: strcpy(str,"MEDIUM FORMAT CORRUPTED"); break; + } + break; + case 0x51: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"ERASE FAILURE"); break; + case 0x01: strcpy(str,"ERASE FAILURE - INCOMPLETE ERASE OPERATION DETECTED"); break; + + default: strcpy(str,"ERASE FAILURE"); break; + } + break; + case 0x57: strcpy(str,"UNABLE TO RECOVER TABLE-OF-CONTENTS"); break; /* ASCQ = 00 */ + case 0x72: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"SESSION FIXATION ERROR"); break; + case 0x01: strcpy(str,"SESSION FIXATION ERROR WRITING LEAD-IN"); break; + case 0x02: strcpy(str,"SESSION FIXATION ERROR WRITING LEAD-OUT"); break; + + default: strcpy(str,"SESSION FIXATION ERROR"); break; + } + break; + case 0x73: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"CD CONTROL ERROR"); break; + case 0x02: strcpy(str,"POWER CALIBRATION AREA IS FULL"); break; + case 0x03: strcpy(str,"POWER CALIBRATION AREA ERROR"); break; + case 0x04: strcpy(str,"PROGRAM MEMORY AREA UPDATE FAILURE"); break; + case 0x05: strcpy(str,"PROGRAM MEMORY AREA IS FULL"); break; + + default: strcpy(str,"CD CONTROL ERROR"); break; + } + break; + } + break; + case 0x4: + switch (ASC(err)) { + case 0x00: strcpy(str,"CLEANING REQUESTED"); break; /* ASCQ = 0x17 */ + case 0x05: strcpy(str,"LOGICAL UNIT DOES NOT RESPOND TO SELECTION"); break; /* ASCQ = 0x00 */ + case 0x08: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"LOGICAL UNIT COMMUNICATION FAILURE"); break; + case 0x01: strcpy(str,"LOGICAL UNIT COMMUNICATION TIMEOUT"); break; + case 0x02: strcpy(str,"LOGICAL UNIT COMMUNICATION PARITY ERROR"); break; + case 0x03: strcpy(str,"LOGICAL UNIT COMMUNICATION CRC ERROR (ULTRA-DMA/32)"); break; + } + break; + case 0x09: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"TRACK FOLLOWING ERROR"); break; + case 0x01: strcpy(str,"TRACKING SERVO FAILURE"); break; + case 0x02: strcpy(str,"FOCUS SERVO FAILURE"); break; + case 0x03: strcpy(str,"SPINDLE SERVO FAILURE"); break; + case 0x04: strcpy(str,"HEAD SELECT FAULT"); break; + + default: strcpy(str,"TRACKING ERROR"); break; + } + break; + case 0x15: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"RANDOM POSITIONING ERROR"); break; + case 0x01: strcpy(str,"MECHANICAL POSITIONING ERROR"); break; + + default: strcpy(str,"RANDOM POSITIONING ERROR"); break; + } + break; + case 0x1B: strcpy(str,"SYNCHRONOUS DATA TRANSFER ERROR"); break; /* ASCQ = 0x00 */ + case 0x3B: strcpy(str,"MECHANICAL POSITIONING OR CHANGER ERROR"); break; /* ASCQ = 0x16 */ + case 0x3E: + switch (ASCQ(err)) { + case 0x01: strcpy(str,"LOGICAL UNIT FAILURE"); break; + case 0x02: strcpy(str,"TIMEOUT ON LOGICAL UNIT"); break; + + default: strcpy(str,"LOGICAL UNIT FAILURE"); break; + } + break; + case 0x40: strcpy(str,"DIAGNOSTIC FAILURE ON COMPONENT NN (80H-FFH)"); break; + case 0x44: strcpy(str,"INTERNAL TARGET FAILURE"); break; + case 0x46: strcpy(str,"UNSUCCESSFUL SOFT RESET"); break; + case 0x47: strcpy(str,"SCSI PARITY ERROR"); break; + case 0x4A: strcpy(str,"COMMAND PHASE ERROR"); break; + case 0x4B: strcpy(str,"DATA PHASE ERROR"); break; + case 0x4C: strcpy(str,"LOGICAL UNIT FAILED SELF-CONFIGURATION"); break; + case 0x53: strcpy(str,"MEDIA LOAD OR EJECT FAILED"); break; + case 0x65: strcpy(str,"VOLTAGE FAULT"); break; + } + break; + case 0x5: + switch (ASC(err)) { + case 0x07: strcpy(str,"MULTIPLE PERIPHERAL DEVICES SELECTED"); break; /* ASCQ = 0x00 */ + case 0x1A: strcpy(str,"PARAMETER LIST LENGTH ERROR"); break; /* ASCQ = 0x00 */ + case 0x20: strcpy(str,"INVALID COMMAND OPERATION CODE"); break; /* ASCQ = 0x00 */ + case 0x21: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"LOGICAL BLOCK ADDRESS OUT OF RANGE"); break; + case 0x01: strcpy(str,"INVALID ELEMENT ADDRESS"); break; + case 0x02: strcpy(str,"INVALID ADDRESS FOR WRITE"); break; + + default: strcpy(str,"LOGICAL BLOCK ADDRESS OUT OF RANGE"); break; + } + break; + case 0x24: strcpy(str,"INVALID FIELD IN CDB"); break; + case 0x25: strcpy(str,"LOGICAL UNIT NOT SUPPORTED"); break; + case 0x26: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"INVALID FIELD IN PARAMETER LIST"); break; + case 0x01: strcpy(str,"PARAMETER NOT SUPPORTED"); break; + case 0x02: strcpy(str,"PARAMETER VALUE INVALID"); break; + case 0x03: strcpy(str,"THRESHOLD PARAMETERS NOT SUPPORTED"); break; + } + break; + case 0x2B: strcpy(str,"COPY CANNOT EXECUTE SINCE INITIATOR CANNOT DISCONNECT"); break; /* ASCQ = 0x00 */ + case 0x2C: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"COMMAND SEQUENCE ERROR"); break; + case 0x03: strcpy(str,"CURRENT PROGRAM AREA IS NOT EMPTY"); break; + case 0x04: strcpy(str,"CURRENT PROGRAM AREA IS EMPTY"); break; + } + break; + case 0x30: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"INCOMPATIBLE MEDIUM INSTALLED"); break; + case 0x01: strcpy(str,"CANNOT READ MEDIUM - UNKNOWN FORMAT"); break; + case 0x02: strcpy(str,"CANNOT READ MEDIUM - INCOMPATIBLE FORMAT"); break; + case 0x03: strcpy(str,"CLEANING CARTRIDGE INSTALLED"); break; + case 0x04: strcpy(str,"CANNOT WRITE MEDIUM - UNKNOWN FORMAT"); break; + case 0x05: strcpy(str,"CANNOT WRITE MEDIUM - INCOMPATIBLE FORMAT"); break; + case 0x06: strcpy(str,"CANNOT FORMAT MEDIUM - INCOMPATIBLE MEDIUM"); break; + case 0x07: strcpy(str,"CLEANING FAILURE"); break; + case 0x08: strcpy(str,"CANNOT WRITE - APPLICATION CODE MISMATCH"); break; + case 0x09: strcpy(str,"CURRENT SESSION NOT FIXATED FOR APPEND"); break; + case 0x10: strcpy(str,"MEDIUM NOT FORMATTED"); break; + } + break; + case 0x39: strcpy(str,"SAVING PARAMETERS NOT SUPPORTED"); break; /* ASCQ = 0x00 */ + case 0x3D: strcpy(str,"INVALID BITS IN IDENTIFY MESSAGE"); break; /* ASCQ = 0x00 */ + case 0x43: strcpy(str,"MESSAGE ERROR"); break; /* ASCQ = 0x00 */ + case 0x53: strcpy(str,"MEDIUM REMOVAL PREVENTED"); break; /* ASCQ = 0x02 */ + case 0x64: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"ILLEGAL MODE FOR THIS TRACK"); break; + case 0x01: strcpy(str,"INVALID PACKET SIZE"); break; + } + break; + case 0x6F: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"COPY PROTECTION KEY EXCHANGE FAILURE - AUTHENTICATION FAILURE"); break; + case 0x01: strcpy(str,"COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT PRESENT"); break; + case 0x02: strcpy(str,"COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT ESTABLISHED"); break; + case 0x03: strcpy(str,"READ OF SCRAMBLED SECTOR WITHOUT AUTHENTICATION"); break; + case 0x04: strcpy(str,"MEDIA REGION CODE IS MISMATCHED TO LOGICAL UNIT REGION"); break; + case 0x05: strcpy(str,"LOGICAL UNIT REGION MUST BE PERMANENT/REGION RESET COUNT ERROR"); break; + } + break; + case 0x72: + switch (ASCQ(err)) { + case 0x03: strcpy(str,"SESSION FIXATION ERROR . INCOMPLETE TRACK IN SESSION"); break; + case 0x04: strcpy(str,"EMPTY OR PARTIALLY WRITTEN RESERVED TRACK"); break; + case 0x05: strcpy(str,"NO MORE TRACK RESERVATIONS ALLOWED"); break; + } + break; + } + break; + case 0x6: + switch (ASC(err)) { + case 0x28: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED"); break; + case 0x01: strcpy(str,"IMPORT OR EXPORT ELEMENT ACCESSED"); break; + } + break; + case 0x29: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"POWER ON, RESET, OR BUS DEVICE RESET OCCURRED"); break; + case 0x01: strcpy(str,"POWER ON OCCURRED"); break; + case 0x02: strcpy(str,"BUS RESET OCCURRED"); break; + case 0x03: strcpy(str,"BUS DEVICE RESET FUNCTION OCCURRED"); break; + case 0x04: strcpy(str,"DEVICE INTERNAL RESET"); break; + } + break; + case 0x2A: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"PARAMETERS CHANGED"); break; + case 0x01: strcpy(str,"MODE PARAMETERS CHANGED"); break; + case 0x02: strcpy(str,"LOG PARAMETERS CHANGED"); break; + case 0x03: strcpy(str,"RESERVATIONS PREEMPTED"); break; + } + break; + case 0x2E: strcpy(str,"INSUFFICIENT TIME FOR OPERATION"); break; + case 0x2F: strcpy(str,"COMMANDS CLEARED BY ANOTHER INITIATOR"); break; + case 0x3B: + switch (ASCQ(err)) { + case 0x0D: strcpy(str,"MEDIUM DESTINATION ELEMENT FULL"); break; + case 0x0E: strcpy(str,"MEDIUM SOURCE ELEMENT EMPTY"); break; + case 0x0F: strcpy(str,"END OF MEDIUM REACHED"); break; + case 0x11: strcpy(str,"MEDIUM MAGAZINE NOT ACCESSIBLE"); break; + case 0x12: strcpy(str,"MEDIUM MAGAZINE REMOVED"); break; + case 0x13: strcpy(str,"MEDIUM MAGAZINE INSERTED"); break; + case 0x14: strcpy(str,"MEDIUM MAGAZINE LOCKED"); break; + case 0x15: strcpy(str,"MEDIUM MAGAZINE UNLOCKED"); break; + } + break; + case 0x3F: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"TARGET OPERATING CONDITIONS HAVE CHANGED"); break; + case 0x01: strcpy(str,"MICROCODE HAS BEEN CHANGED"); break; + case 0x02: strcpy(str,"CHANGED OPERATING DEFINITION"); break; + case 0x03: strcpy(str,"INQUIRY DATA HAS CHANGED"); break; + } + break; + case 0x5A: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"OPERATOR REQUEST OR STATE CHANGE INPUT"); break; + case 0x01: strcpy(str,"OPERATOR MEDIUM REMOVAL REQUEST"); break; + case 0x02: strcpy(str,"OPERATOR SELECTED WRITE PROTECT"); break; + case 0x03: strcpy(str,"OPERATOR SELECTED WRITE PERMIT"); break; + } + break; + case 0x5B: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"LOG EXCEPTION"); break; + case 0x01: strcpy(str,"THRESHOLD CONDITION MET"); break; + case 0x02: strcpy(str,"LOG COUNTER AT MAXIMUM"); break; + case 0x03: strcpy(str,"LOG LIST CODES EXHAUSTED"); break; + } + break; + case 0x5E: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"LOW POWER CONDITION ON"); break; + case 0x01: strcpy(str,"IDLE CONDITION ACTIVATED BY TIMER"); break; + case 0x02: strcpy(str,"STANDBY CONDITION ACTIVATED BY TIMER"); break; + case 0x03: strcpy(str,"IDLE CONDITION ACTIVATED BY COMMAND"); break; + case 0x04: strcpy(str,"STANDBY CONDITION ACTIVATED BY COMMAND"); break; + } + break; + } + break; + case 0x7: + switch (ASC(err)) { + case 0x27: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"WRITE PROTECTED"); break; + case 0x01: strcpy(str,"HARDWARE WRITE PROTECTED"); break; + case 0x02: strcpy(str,"LOGICAL UNIT SOFTWARE WRITE PROTECTED"); break; + case 0x03: strcpy(str,"ASSOCIATED WRITE PROTECT"); break; + case 0x04: strcpy(str,"PERSISTENT WRITE PROTECT"); break; + case 0x05: strcpy(str,"PERMANENT WRITE PROTECT"); break; + case 0x06: strcpy(str,"CONDITIONAL WRITE PROTECT"); break; + + default: strcpy(str,"WRITE PROTECTED"); break; + } + break; + } + break; + case 0x8: strcpy(str,"BLANK CHECK"); break; + case 0xB: + switch (ASC(err)) { + case 0x00: strcpy(str,"I/O PROCESS TERMINATED"); break; /* ASCQ = 06 */ + case 0x11: strcpy(str,"READ ERROR - LOSS OF STREAMING"); break; /* ASCQ = 11 */ + case 0x45: strcpy(str,"SELECT OR RESELECT FAILURE"); break; /* ASCQ = 00 */ + case 0x48: strcpy(str,"INITIATOR DETECTED ERROR MESSAGE RECEIVED"); break; /* ASCQ = 00 */ + case 0x49: strcpy(str,"INVALID MESSAGE ERROR"); break; /* ASCQ = 00 */ + case 0x4D: strcpy(str,"TAGGED OVERLAPPED COMMANDS (NN = QUEUE TAG)"); break; /* ASCQ = xx */ + } + break; + } + printf("[%05X] %s", err, str); + return 0; +} + +int print_opcode (unsigned char opcode) { + char str[128]; + switch (opcode) { + +// case 0x00: strcpy(str," "); break; + + + case 0x00: strcpy(str,"SPC_TEST_UNIT_READY "); break; + case 0x03: strcpy(str,"SPC_REQUEST_SENSE "); break; + case 0x04: strcpy(str,"MMC_FORMAT_UNIT "); break; + + case 0x12: strcpy(str,"SPC_INQUIRY "); break; + case 0x15: strcpy(str,"MMC_MODE_SELECT6 "); break; + case 0x16: strcpy(str,"SPC_RESERVE_6 "); break; + case 0x17: strcpy(str,"SPC_RELEASE_6 "); break; + case 0x1A: strcpy(str,"MMC_MODE_SENSE6 "); break; + case 0x1B: strcpy(str,"MMC_START_STOP_UNIT "); break; + case 0x1C: strcpy(str,"SPC_RECEIVE_DIAGNOSTIC_RESULTS "); break; + case 0x1D: strcpy(str,"SPC_SEND_DIAGNOSTIC "); break; + case 0x1E: strcpy(str,"SPC_PREVENT_ALLOW_MEDIUM_REMIVAL "); break; + + + case 0x23: strcpy(str,"MMC_READ_FORMAT_CAPACITIES "); break; + case 0x25: strcpy(str,"MMC_READ_RECORDED_CAPACITY "); break; + case 0x28: strcpy(str,"MMC_READ "); break; + case 0x2A: strcpy(str,"MMC_WRITE "); break; + case 0x2B: strcpy(str,"MMC_SEEK "); break; + case 0x2E: strcpy(str,"MMC_WRITE_AND_VERIFY "); break; + case 0x2F: strcpy(str,"MMC_VERIFY "); break; + + case 0x35: strcpy(str,"MMC_SYNC_CACHE "); break; + case 0x3B: strcpy(str,"SPC_WRITE_BUFFER "); break; + case 0x3C: strcpy(str,"SPC_READ_BUFFER "); break; + + case 0x42: strcpy(str,"MMC_READ_SUB_CHANNEL "); break; + case 0x43: strcpy(str,"MMC_READ_TOC_PMA_ATIP "); break; + case 0x44: strcpy(str,"MMC_READ_HEADER "); break; + case 0x45: strcpy(str,"MMC_PLAY_AUDIO "); break; + case 0x46: strcpy(str,"MMC_GET_CONFIGURATION "); break; + case 0x47: strcpy(str,"MMC_PLAY_AUDIO_MSF "); break; + case 0x4A: strcpy(str,"MMC_GET_EVENT_STATUS_NOTIFICATION "); break; + case 0x4B: strcpy(str,"MMC_PAUSE_RESUME "); break; + case 0x4C: strcpy(str,"SPC_LOG_SELECT "); break; + case 0x4D: strcpy(str,"SPC_LOG_SENSE "); break; + case 0x4E: strcpy(str,"MMC_STOP_PLAY_SCAN "); break; + + case 0x51: strcpy(str,"MMC_READ_DISC_INFORMATION "); break; + case 0x52: strcpy(str,"MMC_READ_TRACK_INFORMATION "); break; + case 0x53: strcpy(str,"MMC_RESERVE_TRACK "); break; + case 0x54: strcpy(str,"MMC_SEND_OPC_INFORMATION "); break; + case 0x55: strcpy(str,"MMC_MODE_SELECT10 "); break; + case 0x56: strcpy(str,"SPC_RESERVE_10 "); break; + case 0x57: strcpy(str,"SPC_RELEASE_10 "); break; + case 0x58: strcpy(str,"MMC_REPAIR_TRACK "); break; + case 0x59: strcpy(str,"MMC_READ_MASTER_CUE "); break; + case 0x5A: strcpy(str,"MMC_MODE_SENSE10 "); break; + case 0x5B: strcpy(str,"MMC_CLOSE_TRACK_SESSION "); break; + case 0x5C: strcpy(str,"MMC_READ_BUFFER_CAPACITY "); break; + case 0x5D: strcpy(str,"MMC_SEND_CUE_SHEET "); break; + case 0x5E: strcpy(str,"SPC_PERSISTENT_RESERVE_IN "); break; + case 0x5F: strcpy(str,"SPC_PERSISTENT_RESERVE_OUT "); break; + + case 0x83: strcpy(str,"SPC_EXTENDED_COPY "); break; + case 0x84: strcpy(str,"SPC_RECEIVE_COPY_RESULTS "); break; + case 0x86: strcpy(str,"SPC_ACCESS_CONTROL_IN "); break; + case 0x87: strcpy(str,"SPC_ACCESS_CONTROL_OUT "); break; + case 0x8C: strcpy(str,"SPC_READ_ATTRIBUTE "); break; + case 0x8D: strcpy(str,"SPC_WRITE_ATTRIBUTE "); break; + + case 0xA0: strcpy(str,"SPC_REPORT_LUNS "); break; + case 0xA1: strcpy(str,"MMC_BLANK "); break; + case 0xA2: strcpy(str,"MMC_SEND_EVENT / SPC_SECURITY_PROTOCOL_IN "); break; + case 0xA3: strcpy(str,"MMC_SEND_KEY "); break; + case 0xA4: strcpy(str,"MMC_REPORT_KEY "); break; + case 0xA5: strcpy(str,"MMC_PLAY_AUDIO_12 "); break; + case 0xA6: strcpy(str,"MMC_LOAD_UNLOAD "); break; + case 0xA7: strcpy(str,"MMC_SET_READ_AHEAD / SMC_MOVE_MEDIUM_ATTACHED "); break; + case 0xA8: strcpy(str,"MMC_READ_DVD "); break; + case 0xAC: strcpy(str,"MMC_GET_PERFORMANCE "); break; + case 0xAD: strcpy(str,"MMC_READ_DVD_STRUCTURE "); break; + + case 0xB4: strcpy(str,"SMC_READ_ELEMENT_STATUS_ATTACHED "); break; + case 0xB5: strcpy(str,"SPC_SECURITY_PROTOCOL_OUT "); break; + case 0xB6: strcpy(str,"MMC_SET_STREAMING "); break; + case 0xB9: strcpy(str,"MMC_READ_CD_MSF "); break; + case 0xBA: strcpy(str,"MMC_SCAN "); break; + case 0xBB: strcpy(str,"MMC_SET_SPEED "); break; + case 0xBC: strcpy(str,"MMC_PLAY_CD / SCC_SPARE_IN "); break; + case 0xBD: strcpy(str,"MMC_MECHANISM_STATUS / SCC_SPARE_OUT "); break; + case 0xBE: strcpy(str,"MMC_READ_CD / SCC_VOLUME_SET_IN "); break; + case 0xBF: strcpy(str,"SCC_VOLUME_SET_OUT "); break; + + case 0xD4: strcpy(str,"PLEXTOR_GET_AUTH "); break; + case 0xD5: strcpy(str,"PLEXTOR_SEND_AUTH "); break; + +//const char = 0xD8; + + case 0xE3: strcpy(str,"PLEXTOR_ERASER "); break; + case 0xE4: strcpy(str,"PLEXTOR_AS_RD "); break; + case 0xE5: strcpy(str,"PLEXTOR_AS_WR "); break; + + case 0xE7: strcpy(str,"SBC_FLUSH_CACHE "); break; + + case 0xE9: strcpy(str,"PLEXTOR_MODE "); break; + case 0xEA: strcpy(str,"PLEXTOR_QCHECK "); break; + case 0xEB: strcpy(str,"PLEXTOR_PREC_SPD "); break; + case 0xED: strcpy(str,"PLEXTOR_MODE2 "); break; + + case 0xEE: strcpy(str,"PLEXTOR_RESTART "); break; +// case 0xEF: strcpy(str,"PLEXTOR REBOOT ??? "); break; + + case 0xF1: strcpy(str,"PLEXTOR_EEPROM_READ "); break; + + case 0xF3: strcpy(str,"PLEXTOR_SCAN_TA_FETE "); break; + case 0xF5: strcpy(str,"PLEXTOR_FETE_READOUT "); break; + default: strcpy(str,"*unknown* "); + } + printf("[%02X] %s", opcode, str); + return 0; +} + +int probe_drive(const char* path, int idx) +{ + drive_info* drive; + int inq; +// printf("Trying device: %s\n", path); + drive = new drive_info(path); + drive->silent++; + inq = inquiry(drive); + drive->silent--; + switch (inq) { + case 0x00: +#if 0 +// strcpy(drvtbl[drvcnt],_devtbl[i]); + printf("[%02d] %s %s: %s %s %s\n",(drive->ven_ID & vendor_mask) ? "*" : " ", + drvcnt, drive->device, drive->ven, drive->dev, drive->fw); +#else + printf("D: [%02d] '%s': '%s' '%s' '%s'\n", + idx, drive->device, drive->ven, drive->dev, drive->fw); +#endif + break; + case ERR_NO_DEV: + // if (!drive->silent) printf("%s: no device found\n",drive->device); + break; + case ERR_NO_SCSI: + // if (!drive->silent) printf("%s: not a valid SCSI device\n",drive->device); + break; + case ERR_NO_MMC: + // printf("%s: %s %s %s",drive->device,drive->ven,drive->dev,drive->fw); + // printf(": device is not MMC compliant\n"); + // for (j=0; j<8; j++) + // printf("Ver ID [%d] = 0x%04X\n",j,drive->ver_id[j]); + break; + default: + // printf("%s: ???\n",drive->device); + break; + } + delete drive; + return inq; +} + +int scanbus(int vendor_mask) +{ + int i; +// int inq; + int drvcnt=0; + +#ifndef DEVTBL_DIRECT + DIR *dir; + struct dirent *dentry; + struct stat st; + str_dev devstr; + int dlen; +#endif + + printf("** scanning IDE/SATA/SCSI buses...\n"); + +#ifdef DEVTBL_DIRECT // used in win32 only + for (i=0; strlen(_devtbl[i].name)>0 ; i++) { + if (!probe_drive(_devtbl[i].name, drvcnt)) drvcnt++; + } +#else + for (i=0; strlen(_devtbl[i].name)>0 ; i++) { + dlen = strlen(_devtbl[i].name); + dir = opendir("/dev"); + if (dir) + { + dentry = readdir(dir); + while (dentry) { + if (!strncmp(dentry->d_name, _devtbl[i].name, dlen) && + (!_devtbl[i].len || (strlen(dentry->d_name) == (size_t) _devtbl[i].len)) ) { + sprintf(devstr, "/dev/%s", dentry->d_name); + if (!lstat(devstr, &st) && S_ISBLK(st.st_mode) && !probe_drive(devstr, drvcnt)) drvcnt++; + } + dentry = readdir(dir); + } + closedir(dir); + } + } +#endif + printf("** Scan complete: %d device(s) found\n", drvcnt); + return (drvcnt); +} + +void spinup(drive_info* drive, unsigned char secs) { + long st, et; + int err=0; + char use_readcd = 0; + const int addt = 25; +#ifdef SPINUP_REVERSE + int32_t lba = drive->media.capacity-1; +#else + int32_t lba = 0; +#endif + int blk = 16; + st = getmsecs() + addt; + if (drive->media.type & DISC_CD) { + blk = 15; + if (drive->capabilities & CAP_DAE) use_readcd = 1; + } + printf("SpinUp using READ%s command...\n", use_readcd ? " CD" : "" ); +// if (use_readcd) read_cd(drive, 0, 1, 0xF8, 1); + for ( et = getmsecs(); !err && (et-st) < (secs*1000); et = getmsecs() ) { +#ifdef SPINUP_REVERSE + lba-=blk; +#else + lba+=blk; +#endif +// err = seek(drive, lba); + if (!drive->silent) printf("Remaining: %.3f sec...\r", secs - ((et-st) / 1000.0)); + if (use_readcd) err = read_cd(drive, drive->rd_buf, lba, blk, 0xF8); + else err = read(drive, drive->rd_buf, lba, blk); + } + + if (use_readcd) read_cd(drive, drive->rd_buf, 0, 1, 0xF8); + else read(drive, drive->rd_buf, 0, 1); +// seek(drive, 0); + + msleep( addt ); +} + +int inquiry(drive_info* drive) { + unsigned char add_len; + int i; + if (drive->mmc == -1) return ERR_NO_DEV; + drive->cmd[0] = SPC_INQUIRY; + drive->cmd[4] = 36; + drive->cmd[5] = 0; + drive->err=drive->cmd.transport(READ,drive->rd_buf,36); + if (drive->err) { + if (!drive->silent) sperror("INQUIRY", drive->err); + return ERR_NO_SCSI; + } + + memcpy(drive->ven,drive->rd_buf+8,8); + drive->ven[8] = 0; + memcpy(drive->dev,drive->rd_buf+16,16); + drive->dev[16] = 0; + memcpy(drive->fw,drive->rd_buf+32,4); + drive->fw[4] = 0; + add_len = drive->rd_buf[4]; + + for (i=0; i<8; i++) drive->ver_id[i]=0; + if (add_len >= 91) { + drive->cmd[0] = SPC_INQUIRY; + drive->cmd[4] = 5+add_len; + drive->cmd[5] = 0; + drive->err=drive->cmd.transport(READ,drive->rd_buf,5+add_len); + if (drive->err) { + if (!drive->silent) sperror("INQUIRY ADD", drive->err); + } else { + for (i=0; i<8; i++) drive->ver_id[i]= ntoh16(drive->rd_buf+58+i*2); + } + } + if ((drive->rd_buf[0]&0x1F) != 5) return ERR_NO_MMC; + drive->mmc=1; + convert_to_ID(drive); + return 0; +} + +int isPlextor(drive_info* drive) +{ + if (!strncmp(drive->ven,"PLEXTOR ",8)) { + if( !strncmp(drive->dev,"CD-R ", 7)) return 1; + if( !strncmp(drive->dev,"DVDR PX-708A",14) || + !strncmp(drive->dev,"DVDR PX-708A2",15) || + !strncmp(drive->dev,"DVDR PX-712A",14) || + !strncmp(drive->dev,"DVDR PX-714A",14) || + !strncmp(drive->dev,"DVDR PX-716A ",15) || + !strncmp(drive->dev,"DVDR PX-716AL",15) || + !strncmp(drive->dev,"DVDR PX-755A",14) || + !strncmp(drive->dev,"DVDR PX-760A",14) + ) + return 1; + } + return 0; +} + +int isPlextorLockPresent(drive_info* drive) +{ + if( !strncmp(drive->dev,"CD-R PREMIUM2",15) || + !strncmp(drive->dev,"DVDR PX-755A",14) || + !strncmp(drive->dev,"DVDR PX-760A",14) + ) + return 1; + else + return 0; +} + +int isYamaha(drive_info* drive) { return !strncmp(drive->ven,"YAMAHA ", 8); } + +int isPioneer(drive_info* drive) { return !strncmp(drive->ven,"PIONEER ", 8); } + +int test_unit_ready(drive_info* drive) { + drive->cmd[0] = SPC_TEST_UNIT_READY; + drive->cmd[3] = 0; + return drive->err=drive->cmd.transport(NONE, NULL, 0); +} + +int wait_unit_ready(drive_info* drive, int secs, bool need_media) { + long st, et; + st = getmsecs(); + for ( et = getmsecs(); (et-st) < ((long)secs*1000); et = getmsecs() ) { + if (!drive->silent) printf("Remaining: %.3f sec...\r", secs - ((et-st) / 1000.0)); + if (!test_unit_ready(drive)) return 0; + if (!need_media) { + if (drive->err == 0x23A01) return 0; + if (drive->err == 0x23A02) return 0; + } + msleep(100); +// i++; + } + printf("wait_unit_ready(): Time Out (%ds)\n", secs); + return 1; +} + +int wait_fix(drive_info* drive, int secs){ + long st, et; + st = getmsecs(); + for ( et = getmsecs(); (et-st) < ((long)secs*1000); et = getmsecs() ) { + if (!drive->silent) printf("Remaining: %.3f sec...\r", secs - ((et-st) / 1000.0)); + if (!read_disc_info(drive, 16)) return 0; + // if not SC_NOT_READY or SC_UNIT_ATTENTION return + if ((drive->err & 0xF0000) != 0x20000 && (drive->err & 0xF0000) != 0x60000 ) return 1; + msleep(100); +// i++; + } + printf("wait_fix(): Time Out (%ds)\n", secs); + return 1; +} + + +int check_burnfree(drive_info* drive) +{ + if (mode_sense(drive, MODE_PAGE_WRITE_PARAMETERS, 0, 60)) return -1; + drive->rd_buf[8+2] |= 0x40; + if (mode_select(drive, 60) || !(drive->rd_buf[8+2] & 0x40)) { + printf("BURN-free seems to not supported for this media!\n"); + } else { +// printf("BURN-free supported:)\n"); + drive->capabilities|=CAP_BURN_FREE; + } + return 0; +} + +int check_write_modes(drive_info* drive) +{ + drive->wr_modes = 0; + if (mode_sense(drive, MODE_PAGE_WRITE_PARAMETERS, 0, 60)) return -1; + + for (int i=0; wr_modes[i].id; i++) { + drive->rd_buf[8+2] &= 0xF0; + drive->rd_buf[8+2] |= wr_modes[i].wtype; + drive->rd_buf[8+4] &= 0xF0; + drive->rd_buf[8+4] |= wr_modes[i].dtype; + if (!mode_select(drive, 60)) { + drive->wr_modes |= wr_modes[i].id; + } + } + check_burnfree(drive); + return 0; +} + +int reserve_track(drive_info* drive, uint32_t size) +{ + drive->cmd[0] = MMC_RESERVE_TRACK; + drive->cmd[5] = (size >> 24) & 0xFF; + drive->cmd[6] = (size >> 16) & 0xFF; + drive->cmd[7] = (size >> 8) & 0xFF; + drive->cmd[8] = size & 0xFF; + + if ((drive->err=drive->cmd.transport(NONE, NULL, 0))) + {sperror ("RESERVE_TRACK",drive->err); return (drive->err);} + return 0; +} + +int close_track_session(drive_info* drive, int n, int cltype) +{ + drive->cmd[0] = MMC_CLOSE_TRACK_SESSION; +// drive->cmd[1] = immed ? 0x01 : 0x00; + drive->cmd[1] = 0x01; + drive->cmd[2] = cltype; + drive->cmd[4] = (n >> 8) & 0xFF; + drive->cmd[5] = n & 0xFF; + if ((drive->err=drive->cmd.transport(NONE, NULL, 0))) + {sperror ("MMC_CLOSE_TRACK_SESSION",drive->err); return (drive->err);} + return 0; +} + +int request_sense(drive_info* drive, char add){ + drive->cmd[0]= SPC_REQUEST_SENSE; + drive->cmd[4]= 0x12+add; + drive->cmd[5]=0; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,0x12) )) + { if (!drive->silent) sperror ("REQUEST_SENSE",drive->err); return (drive->err); } + return 0; +} + +/* +int mode_sense6(drive_info* drive, int page, int page_control, short dest_len) { + drive->cmd[0]=MMC_MODE_SENSE6; + drive->cmd[1]= 0x10; + drive->cmd[2]=page_control << 6 | page; + drive->cmd[4]=dest_len & 0xFF; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,dest_len) )) + {sperror ("MODE_SENSE(6)",drive->err); return (drive->err); } + return 0; +} +*/ + +int mode_sense(drive_info* drive, int page, int page_control, int dest_len) { + drive->cmd[0]=MMC_MODE_SENSE_10; + drive->cmd[2]=page_control << 6 | page; + drive->cmd[7]=(dest_len >> 8) & 0xFF; + drive->cmd[8]=dest_len & 0xFF; + drive->cmd[9]=0; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,dest_len) )) + { if (!drive->silent) sperror ("MODE_SENSE(10)",drive->err); return (drive->err); } + return 0; +} + +/* +int mode_select6(drive_info* drive, short dest_len) { + drive->cmd[0]=MMC_MODE_SELECT6; +// drive->cmd[1]= 0x10; + drive->cmd[4]=dest_len & 0xFF; + if ((drive->err=drive->cmd.transport(WRITE,drive->rd_buf,dest_len) )) + {sperror ("MODE_SELECT(6)",drive->err); return (drive->err); } + return 0; +} +*/ + +int mode_select(drive_info* drive, int dest_len) { + drive->cmd[0]=MMC_MODE_SELECT_10; + drive->cmd[1]= 0x10; +/* + drive->cmd[5]= drive->rd_buf[0]; + drive->cmd[6]= drive->rd_buf[1]; +*/ + drive->cmd[7]=(dest_len >> 8) & 0xFF; + drive->cmd[8]=dest_len & 0xFF; + drive->cmd[9]=0; + if ((drive->err=drive->cmd.transport(WRITE,drive->rd_buf,dest_len) )) + { if (!drive->silent) sperror ("MODE_SELECT(10)",drive->err); return (drive->err); } +// { sperror ("MODE_SELECT(10)",drive->err); return (drive->err); } + return 0; +} + +int get_configuration(drive_info* drive, int feature_number, unsigned int* data_length, int* current, unsigned char ReqType) { + if (data_length) *data_length = 0; + if (current) *current = 0; + drive->cmd[0] = MMC_GET_CONFIGURATION; + drive->cmd[1] = ReqType; + drive->cmd[2] = (feature_number >> 8) & 0xFF; + drive->cmd[3] = feature_number & 0xFF; + drive->cmd[7] = 0; + drive->cmd[8] = 8; + drive->cmd[9] = 0; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8))) + { if (!drive->silent) sperror ("GET_CONFIGURATION (LENGTH)",drive->err); return (drive->err);} + if (data_length) + { + *data_length = ntoh32u(drive->rd_buf); + drive->cmd[7] = ((*data_length+4) >> 8) & 0xFF; + drive->cmd[8] = (*data_length+4) & 0xFF; + drive->cmd[9] = 0; + if (*data_length > 4) + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,*data_length+4))) + { if (!drive->silent) sperror ("GET_CONFIGURATION",drive->err); return (drive->err);} + } + if (current) *current = drive->rd_buf[10] & 0x01; + return 0; +} + +void detect_iface(drive_info* drive) +{ + unsigned int len=0; + get_configuration(drive, FEATURE_CORE, &len, NULL); + drive->iface_id= (drive->rd_buf[12] << 12) | (drive->rd_buf[13] << 8) | (drive->rd_buf[14] << 4) | drive->rd_buf[15]; + if (drive->iface_idiface,iface_list[drive->iface_id]); + else + strcpy(drive->iface,iface_list[iface_id_max+1]); +} + + +int write_buffer(drive_info* drive, uint8_t mode, uint8_t buff_id, uint32_t offs, uint32_t len) +{ + drive->cmd[0] = SPC_WRITE_BUFFER; + drive->cmd[1] = (mode & 0x0F); + drive->cmd[2] = buff_id; + + drive->cmd[3] = (offs >> 16) & 0xFF; + drive->cmd[4] = (offs >> 8) & 0xFF; + drive->cmd[5] = (offs) & 0xFF; + + drive->cmd[6] = (len >> 16) & 0xFF; + drive->cmd[7] = (len >> 8) & 0xFF; + drive->cmd[8] = (len) & 0xFF; + drive->cmd[9] = 0; + + if ((drive->err=drive->cmd.transport(WRITE,drive->rd_buf, len) )) + { if (!drive->silent) sperror ("WRITE_BUFFER",drive->err); return (drive->err); } + + return 0; +} + +int read_buffer(drive_info* drive, uint8_t mode, uint8_t buff_id, uint32_t offs, uint32_t len) +{ + printf("read buffer: mode %x, id %x, offs %x, len %x\n", mode, buff_id,offs,len); + drive->cmd[0] = SPC_READ_BUFFER; + drive->cmd[1] = (mode & 0x0F); + drive->cmd[2] = buff_id; + + drive->cmd[3] = (offs >> 16) & 0xFF; + drive->cmd[4] = (offs >> 8) & 0xFF; + drive->cmd[5] = (offs) & 0xFF; + + drive->cmd[6] = (len >> 16) & 0xFF; + drive->cmd[7] = (len >> 8) & 0xFF; + drive->cmd[8] = (len) & 0xFF; + drive->cmd[9] = 0; + + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf, len) )) + { if (!drive->silent) sperror ("READ_BUFFER",drive->err); return (drive->err); } + + return 0; +} + +int read_echo_buffer_size(drive_info* drive) +{ + drive->cmd[0] = SPC_READ_BUFFER; +// drive->cmd[1] = 0x00; + drive->cmd[1] = 0x0B; // echo buffer descriptor + drive->cmd[8] = 4; + drive->cmd[9] = 0; + + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf, 4) )) + { if (!drive->silent) sperror ("READ_ECHO_BUFFER_SIZE",drive->err); return (0); } + + return ntoh16(drive->rd_buf+2) & 0x1FFF; +} + +int test_dma_speed(drive_info* drive, long msecs) +{ + long st, et; + long speed=0; + + int cnt =0; + drive->silent--; + + uint32_t buf_size = read_echo_buffer_size(drive); + printf("Echo buffer size: %u\n", buf_size); + + buf_size = 4096; + + if (!buf_size) { + drive->silent++; + return 0; + } + + if (!drive->silent) + printf("** Testing DMA speed...\n"); + st = getmsecs(); + for ( et = getmsecs(); (et-st) < (msecs); et = getmsecs() ) { + if (write_buffer(drive, 0x0A, 0, 0, buf_size)) + { + if (!drive->silent) printf("WRITE BUFFER error! DMA speed test aborted!\n"); + drive->silent++; + return 1; + } + if (read_buffer(drive, 0x0A, 0, 0, buf_size)) + { + if (!drive->silent) printf("READ BUFFER error! DMA speed test aborted!\n"); + drive->silent++; + return 1; + } + cnt++; + } + speed = (buf_size * cnt / msecs); + printf("DMA speed: %6ld kB/s (%d buffers of %u bytes in %ld msecs)\n", speed, cnt, buf_size, msecs); + drive->silent++; + return 0; +} + +int flush_cache(drive_info* drive, bool IMMED) { +// drive->cmd[0] = SBC_FLUSH_CACHE; + drive->cmd[0] = MMC_SYNC_CACHE; + drive->cmd[1] = IMMED ? 0x02 : 0; + + if ((drive->err=drive->cmd.transport(NONE, NULL, 0) )) + { if (!drive->silent) sperror ("SBC_FLUSH_CACHE",drive->err); return (drive->err); } +// { sperror ("SBC_FLUSH_CACHE",drive->err); return (drive->err); } + + return 0; +} + +int set_cache(drive_info* drive, bool rd, bool wr) +{ + memset(drive->rd_buf, 0, 20); + drive->rd_buf[8] = MODE_PAGE_CACHING; + drive->rd_buf[9] = 0x0A; + drive->rd_buf[10] = (wr ? 0x04 : 0) | (rd ? 0 : 0x01); + + return (mode_select(drive, 0x20)); +} + +int get_cache(drive_info* drive, bool *rd, bool *wr) +{ + bool re,we; + if (mode_sense(drive, MODE_PAGE_CACHING, 0, 20) || drive->rd_buf[8] != MODE_PAGE_CACHING) return 1; + re = !(drive->rd_buf[10] & 0x01); + we = !!(drive->rd_buf[10] & 0x04); + printf("Cache: RD %s, WR %s\n", re ? "EN":"DIS", we ? "EN":"DIS"); + if (rd) (*rd) = re; + if (wr) (*wr) = we; + return 0; +} + +int get_mode_pages_list(drive_info* drive) { + unsigned int len, i, ii; + unsigned char ml=0, mn=0; + if (!drive->silent) printf("\n** Reading supported mode pages...\n"); + if (mode_sense(drive, 0x3F, 2, 0x4000)) return 1; + len = ntoh16u (drive->rd_buf); +// printf("data len: %4X (%4d), Header:\n", len, len); +// for (i=0; i<8; i++) printf(" 0x%02X",drive->rd_buf[i] & 0xFF); printf("\n"); + for (i=8; (ird_buf[i] & 0x3F; + ml = drive->rd_buf[i+1] & 0xFF; + ii = 0; + while ((MODE_PAGES[ii].id != mn) && (MODE_PAGES[ii].id < 0x3F)) ii++; + if (!drive->silent) { + printf("Mode Page: 0x%02X [%s]", mn, MODE_PAGES[ii].name); +#ifdef MODE_PAGES_DEBUG + for (ii=0; ii<(ml+2); ii++) { if (!(ii%32)) printf("\n"); printf(" %02X",drive->rd_buf[i+ii] & 0xFF); } +#endif + printf("\n"); + } + ml += 2; + } + return 0; +} + +int get_features_list(drive_info* drive) { + unsigned int len, i, ii; + unsigned short fn; + unsigned char fv; + unsigned int fl; + if (!drive->silent) printf("\n** Reading supported features...\n"); + if (get_configuration(drive, 0 , &len, NULL, 0)) return 1; +#if 0 +//#ifdef FEATURE_DEBUG + printf("data len: %4X (%4d), Header:\n", len, len); + for (i=0; i<8; i++) printf(" 0x%02X",drive->rd_buf[i] & 0xFF); printf("\n"); +#endif + for (i=8; (ird_buf+i); + fv = drive->rd_buf[i+2]; + fl = (unsigned int) drive->rd_buf[i+3]; + ii = 0; + while ((FEATURES[ii].id != fn) && (FEATURES[ii].id < 0xFFFF)) ii++; + if (!drive->silent) { + printf("Feature: 0x%04X, ver %2X [%s]", fn, fv, FEATURES[ii].name); +#ifdef FEATURE_DEBUG + for (ii=0; ii<(fl+4); ii++) { + if (!(ii%32)) printf("\n"); + printf(" %02X",drive->rd_buf[i+ii] & 0xFF); + } +#endif + printf("\n"); + } + fl += 4; + } + return 0; +} + +int get_profiles_list(drive_info* drive) { + unsigned int len, i, ii; + unsigned short profile; + if (!drive->silent) printf("\n** Reading supported profiles...\n"); + if (get_configuration(drive, FEATURE_PROFILE_LIST , &len, NULL, 0x02)) return 1; + + for( i = 0; (i < len-8) && (i<0x8000); i+=4 ) { + profile = ntoh16u (drive->rd_buf+i+12); + ii=0; + while ((PROFILES[ii].id != profile) && (PROFILES[ii].id < 0xFFFF)) ii++; + if (!drive->silent) printf("Profile: 0x%04X [%s]\n", profile, PROFILES[ii].name); + +#if 0 + switch (profile) { + case 0x08: // CD-ROM + drive->rd_capabilities |= DEVICE_CD_ROM; + drive->wr_capabilities |= DEVICE_CD_ROM; + break; + case 0x09: // CD-R + drive->rd_capabilities |= DEVICE_CD_R; + drive->wr_capabilities |= DEVICE_CD_R; + break; + case 0x0A: // CD-RW + drive->rd_capabilities |= DEVICE_CD_RW; + drive->wr_capabilities |= DEVICE_CD_RW; + break; + case 0x10: // DVD-ROM + drive->rd_capabilities |= DEVICE_DVD_ROM; + break; + case 0x11: // DVD-R Sequential + drive->rd_capabilities |= DEVICE_DVD_R; + drive->wr_capabilities |= DEVICE_DVD_R; + break; + case 0x12: // DVD-RAM + drive->rd_capabilities |= DEVICE_DVD_RAM; +// drive->wr_capabilities |= DEVICE_DVD_RAM; + break; + case 0x13: // DVD-RW Restricted Overwrite + drive->rd_capabilities |= DEVICE_DVD_RW; + drive->wr_capabilities |= DEVICE_DVD_RW; + break; + case 0x14: // DVD-RW Sequential + drive->rd_capabilities |= DEVICE_DVD_RW; + drive->wr_capabilities |= DEVICE_DVD_RW; + break; + case 0x15: // DVD-R DL Sequential + drive->rd_capabilities |= DEVICE_DVD_R_DL; + drive->wr_capabilities |= DEVICE_DVD_R_DL; + break; + case 0x16: // DVD-R DL Layer Jump + drive->rd_capabilities |= DEVICE_DVD_R_DL; + drive->wr_capabilities |= DEVICE_DVD_R_DL; + break; + case 0x17: // DVD-RW DL + drive->rd_capabilities |= DEVICE_DVD_RW_DL; + drive->wr_capabilities |= DEVICE_DVD_RW_DL; + break; + case 0x1A: // DVD+RW + drive->rd_capabilities |= DEVICE_DVD_PLUS_RW; + drive->wr_capabilities |= DEVICE_DVD_PLUS_RW; + break; + case 0x1B: // DVD+R + drive->rd_capabilities |= DEVICE_DVD_PLUS_R; + drive->wr_capabilities |= DEVICE_DVD_PLUS_R; + break; + case 0x2A: // DVD+RW DL + drive->rd_capabilities |= (DEVICE_DVD_PLUS_RW_DL); + drive->wr_capabilities |= (DEVICE_DVD_PLUS_RW_DL); + break; + case 0x2B: // DVD+R DL + drive->rd_capabilities |= (DEVICE_DVD_PLUS_R_DL); + drive->wr_capabilities |= (DEVICE_DVD_PLUS_R_DL); + break; + case 0x40: // BD-ROM + drive->rd_capabilities |= (DEVICE_BD_ROM); + break; + case 0x41: // BD-R Sequential + drive->rd_capabilities |= (DEVICE_BD_R); + drive->wr_capabilities |= (DEVICE_BD_R); + break; + case 0x42: // BD-R Random + drive->rd_capabilities |= (DEVICE_BD_R); + drive->wr_capabilities |= (DEVICE_BD_R); + break; + case 0x43: // BD-RE + drive->rd_capabilities |= (DEVICE_BD_RE); + drive->wr_capabilities |= (DEVICE_BD_RE); + break; + } +#endif + } + profile = ntoh16(drive->rd_buf+6); + ii=0; + while ((PROFILES[ii].id != profile) && (PROFILES[ii].id < 0xFFFF)) ii++; + if (!drive->silent) printf("Current: 0x%04X [%s]\n", profile, PROFILES[ii].name); + + return 0; +} + +void detect_capabilities(drive_info* drive){ + unsigned int len=4; + drive->capabilities=CAP_SET_CD_SPEED; + drive->rd_capabilities=0; + drive->wr_capabilities=0; + detect_mm_capabilities(drive); + detect_iface(drive); + get_drive_serial_number(drive); + get_mode_pages_list(drive); + if (drive->mmc>1) { + // LU is MMC2 or later. Detecting capabilities by GET_CONFIGURATION + get_profiles_list(drive); + get_features_list(drive); + get_configuration(drive, FEATURE_REMOVABLE_MEDIA , &len, NULL); + if (len >= 0x0C) drive->capabilities|=CAP_REMOVABLE_MEDIA; + get_configuration(drive, FEATURE_SMART , &len, NULL); + if (len >= 0x08) drive->capabilities|=CAP_SMART; + get_configuration(drive, FEATURE_MICROCODE_UPGRADE , &len, NULL); + if (len >= 0x08) drive->capabilities|=CAP_MICROCODE_UPGRADE; + get_configuration(drive, FEATURE_MORPHING , &len, NULL); + if (len >= 0x08) drive->capabilities|=CAP_MORPHING; + get_configuration(drive, FEATURE_POWER_MANAGEMENT , &len, NULL); + if (len >= 0x08) drive->capabilities|=CAP_POWER_MANAGEMENT; + get_configuration(drive, FEATURE_EMBEDDED_CHANGER , &len, NULL); + if (len >= 0x08) drive->capabilities|=CAP_EMBEDDED_CHANGER; + get_configuration(drive, FEATURE_DEFECT_MANAGEMENT , &len, NULL); + if (len >= 0x08) { + drive->capabilities|=CAP_DEFECT_MANAGEMENT; + if (len >= 0x0C) { + if (drive->rd_buf[12]&0x80) drive->capabilities|=CAP_SSA; + } + } + get_configuration(drive, FEATURE_REAL_TIME_STREAMING , &len, NULL); + if (len >= 0x08) drive->capabilities|=CAP_REAL_TIME_STREAMING; + get_configuration(drive, FEATURE_MRW , &len, NULL); + if (len >= 0x0C) { + drive->rd_capabilities|=DEVICE_MRW; + if (drive->rd_buf[12]&0x01) drive->wr_capabilities|=DEVICE_MRW; + if (drive->rd_buf[12]&0x02) drive->rd_capabilities|=DEVICE_MRW_DVD; + if (drive->rd_buf[12]&0x04) drive->wr_capabilities|=DEVICE_MRW_DVD; + } + get_configuration(drive, FEATURE_CD_READ , &len, NULL); // LU can operate with CD's + if (len >= 0x0C) { +// drive->rd_capabilities|=DEVICE_CD_ROM; + if (drive->rd_buf[12]&0x1) drive->capabilities|=CAP_CD_TEXT; + if (drive->rd_buf[12]&0x2) drive->capabilities|=CAP_C2; + } + get_configuration(drive, FEATURE_DDCD_READ , &len, NULL); // LU can Reed/Write DDCD's + if (len >= 0x0C) { + drive->rd_capabilities|=DEVICE_DDCD_R; + drive->rd_capabilities|=DEVICE_DDCD_RW; + get_configuration(drive, FEATURE_DDCD_R_WRITE , &len, NULL); + if (len >= 0x0C) drive->wr_capabilities|=DEVICE_DDCD_R; + get_configuration(drive, FEATURE_DDCD_RW_WRITE , &len, NULL); + if (len >= 0x0C) drive->wr_capabilities|=DEVICE_DDCD_RW; + } + get_configuration(drive, FEATURE_DVD_READ , &len, NULL); // LU can operate with DVD's + if (len >= 0x08) { + if ((drive->rd_buf[10] >> 2) > 0) drive->mmc=5; + if (len >= 0x0C) { + if (drive->rd_buf[14]&0x01) drive->rd_capabilities|=DEVICE_DVD_R_DL; + if (drive->rd_buf[14]&0x02) drive->rd_capabilities|=DEVICE_DVD_RW_DL; + } + get_configuration(drive, FEATURE_DVD_CPRM , &len, NULL); + if (len >= 0x08) drive->capabilities|=CAP_DVD_CPRM; + get_configuration(drive, FEATURE_DVD_CSS , &len, NULL); + if (len >= 0x08) drive->capabilities|=CAP_DVD_CSS; + get_configuration(drive, FEATURE_DVD_R_RW_WRITE , &len, NULL); + if (len >= 0x08) { + drive->wr_capabilities|=DEVICE_DVD_R; + if (len >= 0x0C) { + if (drive->rd_buf[12]&0x02) drive->wr_capabilities|=DEVICE_DVD_RW; + if (drive->rd_buf[12]&0x04) drive->capabilities|= CAP_TEST_WRITE_DVD; + //if (drive->rd_buf[12]&0x08) drive->wr_capabilities|=DEVICE_DVD_R_DL_SEQ; + if (drive->rd_buf[12]&0x08) drive->wr_capabilities|=DEVICE_DVD_R_DL; + } + } + get_configuration(drive, FEATURE_LAYER_JUMP_RECORDING , &len, NULL); + //if (len >= 0x08) drive->wr_capabilities|=DEVICE_DVD_R_DL_LJ; + if (len >= 0x08) drive->wr_capabilities|=DEVICE_DVD_R_DL; + + get_configuration(drive, FEATURE_DVD_PLUS_R , &len, NULL); + if (len >= 0x08) { +// drive->mmc=4; + drive->rd_capabilities|=DEVICE_DVD_PLUS_R; + if (drive->rd_buf[12]&0x01) drive->wr_capabilities|=DEVICE_DVD_PLUS_R; + } + get_configuration(drive, FEATURE_DVD_PLUS_RW , &len, NULL); + if (len >= 0x0C) { + drive->rd_capabilities|=DEVICE_DVD_PLUS_RW; + if (drive->rd_buf[12]&0x01) drive->wr_capabilities|=DEVICE_DVD_PLUS_RW; + } + get_configuration(drive, FEATURE_DVD_PLUS_R_DOUBLE_LAYER , &len, NULL); + if (len >= 0x0C) { +// drive->mmc=5; + drive->rd_capabilities|=DEVICE_DVD_PLUS_R_DL; + if (drive->rd_buf[12]&0x01) drive->wr_capabilities|=DEVICE_DVD_PLUS_R_DL; + } + get_configuration(drive, FEATURE_DVD_PLUS_RW_DOUBLE_LAYER , &len, NULL); + if (len >= 0x0C) { +// drive->mmc=5; + drive->rd_capabilities|=DEVICE_DVD_PLUS_RW_DL; + if (drive->rd_buf[12]&0x01) drive->wr_capabilities|=DEVICE_DVD_PLUS_RW_DL; + } + } + if (drive->wr_capabilities & DEVICE_DVD_RW) drive->rd_capabilities|=DEVICE_DVD_RW; + if (drive->wr_capabilities & DEVICE_DVD_R_DL) drive->rd_capabilities|=DEVICE_DVD_R_DL; + get_configuration(drive, FEATURE_BD_READ , &len, NULL); + if ((len >=0x08) && (drive->rd_buf[11] >=28) ) { + if (drive->rd_buf[16] | drive->rd_buf[17] | drive->rd_buf[18] | drive->rd_buf[19] | + drive->rd_buf[20] | drive->rd_buf[21] | drive->rd_buf[22] | drive->rd_buf[23]) + drive->rd_capabilities |= (DEVICE_BD_RE); + if (drive->rd_buf[24] | drive->rd_buf[25] | drive->rd_buf[26] | drive->rd_buf[27] | + drive->rd_buf[28] | drive->rd_buf[29] | drive->rd_buf[30] | drive->rd_buf[31]) + drive->rd_capabilities |= (DEVICE_BD_R); + if (drive->rd_buf[32] | drive->rd_buf[33] | drive->rd_buf[34] | drive->rd_buf[35] | + drive->rd_buf[36] | drive->rd_buf[37] | drive->rd_buf[38] | drive->rd_buf[39]) + drive->rd_capabilities |= (DEVICE_BD_ROM); + } + get_configuration(drive, FEATURE_BD_WRITE , &len, NULL); + if ((len >=0x08) && (drive->rd_buf[11] >=20) ) { + if (drive->rd_buf[16] | drive->rd_buf[17] | drive->rd_buf[18] | drive->rd_buf[19] | + drive->rd_buf[20] | drive->rd_buf[21] | drive->rd_buf[22] | drive->rd_buf[23]) + drive->wr_capabilities |= (DEVICE_BD_RE); + if (drive->rd_buf[24] | drive->rd_buf[25] | drive->rd_buf[26] | drive->rd_buf[27] | + drive->rd_buf[28] | drive->rd_buf[29] | drive->rd_buf[30] | drive->rd_buf[31]) + drive->wr_capabilities |= (DEVICE_BD_R); + } + get_configuration(drive, FEATURE_HDDVD_READ , &len, NULL); + if ((len >=0x08) ) { + drive->rd_capabilities|=DEVICE_HDDVD_ROM; + if ((len >=0x0C) ) { + if (drive->rd_buf[12]&0x01) drive->rd_capabilities|=DEVICE_HDDVD_R; + if (drive->rd_buf[14]&0x01) drive->rd_capabilities|=DEVICE_HDDVD_RAM; + } + } + get_configuration(drive, FEATURE_HDDVD_WRITE , &len, NULL); + if ((len >=0x0C) ) { + if (drive->rd_buf[12]&0x01) drive->wr_capabilities|=DEVICE_HDDVD_R; + if (drive->rd_buf[14]&0x01) drive->wr_capabilities|=DEVICE_HDDVD_RAM; + } + if (!drive->silent) printf("** Device is MMC-%d\n",drive->mmc); + } +} + +int read_atip(drive_info* drive) { + unsigned char data[4]; + int size = 0; + int i; + drive->media.ATIP_size = 0; + drive->cmd[0]=MMC_READ_TOC_PMA_ATIP; + drive->cmd[1]=0; + drive->cmd[2]=4; // ATIP + drive->cmd[3]=0; + drive->cmd[8]=4; + drive->cmd[9]=0; + if ((drive->err=drive->cmd.transport(READ,data,4) )) + {if (!drive->silent) sperror ("READ_ATIP",drive->err); drive->media.ATIP_size = 0; return 1;} + size = ntoh16u((char*)data); + size += 2; + drive->cmd[0]=MMC_READ_TOC_PMA_ATIP; + drive->cmd[1]=0; + drive->cmd[2]=4; // ATIP + drive->cmd[3]=0; + drive->cmd[7]=(size >> 8) & 0xFF; + drive->cmd[8]=size & 0xFF; + drive->cmd[9]=0; + if ((drive->err=drive->cmd.transport(READ,drive->media.ATIP,size) )) + {sperror ("READ_ATIP",drive->err); drive->media.ATIP_size = 0; return 1;} + drive->media.ATIP_size = size; + if (!drive->silent) { + printf("ATIP (%d bytes):\n",size); + for (i=0; i<(std::min(size, 4)); i++) printf(" %3d (%02X)",drive->media.ATIP[i],drive->media.ATIP[i]); + if (size > 4) for (i=0; i<(size-4); i++) { + if (!(i % 8)) printf("\n"); + else if (!(i % 4)) printf(" "); + printf(" %3d (%02X)",drive->media.ATIP[i+4] & 0xFF,drive->media.ATIP[i+4] & 0xFF); + } + printf("\n"); + } + return 0; +} + +int read_toc(drive_info* drive) { + unsigned char data[4]; + int size = 0; + int i; + drive->cmd[0]=MMC_READ_TOC_PMA_ATIP; + drive->cmd[1]=0; + drive->cmd[2]=0; // TOC + drive->cmd[3]=0; + drive->cmd[8]=4; + drive->cmd[9]=0; + if ((drive->err=drive->cmd.transport(READ,data,4) )) + {sperror ("READ_TOC",drive->err); return 1;} +// if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,2048) )) +// {sperror ("READ_TOC",drive->err); return 1;} + size = ntoh16u((char*)data); + size += 2; + + drive->cmd[0]=MMC_READ_TOC_PMA_ATIP; + drive->cmd[1]=0; + drive->cmd[2]= 0; // TOC + drive->cmd[3]=0; + drive->cmd[7]=(size >> 8) & 0xFF; + drive->cmd[8]=size & 0xFF; + drive->cmd[9]=0; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,size) )) + {if (!drive->silent) sperror ("READ_TOC",drive->err); return 1;} + + if (!drive->silent) { + printf("TOC (%d bytes):\n",size); + for (i=0; i<(std::min(size, 4)); i++) printf(" %3d (%02X)",drive->rd_buf[i] & 0xFF,drive->rd_buf[i] & 0xFF); + if (size > 4) for (i=0; i<(size-4); i++) { + if (!(i % 8)) printf("\n"); + else if (!(i % 4)) printf(" "); + printf(" %3d (%02X)",drive->rd_buf[i+4] & 0xFF,drive->rd_buf[i+4] & 0xFF); + } + printf("\n"); + } + return 0; +} + +int read_disc_info(drive_info* drive, int len) { + drive->cmd[0] = MMC_READ_DISC_INFORMATION; + drive->cmd[7] = (len >> 8) & 0xFF; + drive->cmd[8] = len & 0xFF; + drive->cmd[9]=0; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,len) )) + {if (!drive->silent) sperror ("READ_DISC_INFO",drive->err); return 1;} + return 0; +} + +int read_track_info(drive_info* drive, trk* track, unsigned int track_n){ + int size = 2048; + drive->cmd[0] = MMC_READ_TRACK_INFORMATION; + drive->cmd[1] = 0x01; + drive->cmd[2] = (track_n >> 24) & 0xFF; + drive->cmd[3] = (track_n >> 16) & 0xFF; + drive->cmd[4] = (track_n >> 8) & 0xFF; + drive->cmd[5] = track_n & 0xFF ; + drive->cmd[7] = (size >> 8) & 0xFF ; + drive->cmd[8] = size & 0xFF ; + drive->cmd[9] = 0; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,size) )) + {if (!drive->silent) sperror ("READ_TRACK_INFO",drive->err); return 1;} + +#if 0 + int i, len; + len = ( drive->rd_buf[0] << 8 ) | drive->rd_buf[1]; + printf("\nTrack #%d info:\n ",track->n); + for (i=0; ird_buf[i] & 0xFF); if (!((i+1)%8)) printf("\n ");} + if (((i)%8)) printf("\n"); +#endif + + track->n = ((drive->rd_buf[32]&0xFF) << 8) | (drive->rd_buf[2]&0xFF); + track->session = ((drive->rd_buf[33]&0xFF) << 8) | (drive->rd_buf[3]&0xFF); + track->track_mode = drive->rd_buf[5] & 0x0F; + track->data_mode = drive->rd_buf[6] & 0x0F; + track->start = ntoh32(drive->rd_buf+8); + track->next_writable = ntoh32(drive->rd_buf+12); + track->free = ntoh32(drive->rd_buf+16); + track->size = ntoh32(drive->rd_buf+24); + track->last = ntoh32(drive->rd_buf+28); +// track->end = track->start+track->size-1; + + lba2msf(track->start,&track->msf_start); + lba2msf(track->next_writable,&track->msf_next); + lba2msf(track->free,&track->msf_free); + lba2msf(track->size,&track->msf_size); + lba2msf(track->last,&track->msf_last); +// lba2msf(track->end,&track->msf_end); + + return 0; +} + +int get_track_list(drive_info* drive){ + int i; + int tf,tl; + + if (read_track_info(drive, &drive->media.track[0], 1)) { +// if (!drive->silent) + printf("READ TRACK INFO failed! Trying to read TOC...\n"); + read_toc(drive); + tf = drive->rd_buf[2]; + tl = drive->rd_buf[3]; + drive->media.tracks = tl - tf +1; + for (i=0; imedia.tracks; i++) { + drive->media.track[i].n = i+1; + drive->media.track[i].session = 1; + drive->media.track[i].start = ntoh32(drive->rd_buf+i*8+8); + drive->media.track[i].last = ntoh32(drive->rd_buf+i*8+16); + drive->media.track[i].size = drive->media.track[i].last - drive->media.track[i].start + 1; + drive->media.track[i].free = 0; + drive->media.track[i].track_mode = drive->rd_buf[i*8+5] & 0x0F; +// drive->media.track[i].data_mode = 0; + drive->media.track[i].data_mode = (drive->rd_buf[i*8+5] >> 4) & 0x0F; + + lba2msf(drive->media.track[i].start, &drive->media.track[i].msf_start); + lba2msf(drive->media.track[i].next_writable, &drive->media.track[i].msf_next); + lba2msf(drive->media.track[i].last, &drive->media.track[i].msf_last); + lba2msf(drive->media.track[i].free, &drive->media.track[i].msf_free); +// lba2msf(drive->media.track[i].end, &drive->media.track[i].msf_end); + lba2msf(drive->media.track[i].size, &drive->media.track[i].msf_size); + } + } else { + read_disc_information(drive); + for (i=0; imedia.tracks; i++) + read_track_info(drive, &drive->media.track[i], i+1); + } + if (!drive->silent) printf("tracks: %d\n",drive->media.tracks); + if ((drive->media.tracks) && (!drive->silent)) for (i=0; imedia.tracks; i++) { + printf("\nTrack # : %d\n", drive->media.track[i].n); + printf("Session #: %d\n", drive->media.track[i].session); + printf("Track mode : %d\n",drive->media.track[i].track_mode); + printf("Data mode : %d\n",drive->media.track[i].data_mode); + printf("Track start : %d\n",drive->media.track[i].start); + printf("Next writable : %d\n",drive->media.track[i].next_writable); + printf("Free : %d\n",drive->media.track[i].free); + printf("Size : %d\n",drive->media.track[i].size); + printf("Last recorded : %d\n",drive->media.track[i].last); + } + return 0; +} + +int read_capacity(drive_info* drive) { + unsigned char data[8]; memset(data, 0, 8); + drive->cmd[0] = SBC_READ_CAPACITY; + drive->cmd[9] = 0; + if ((drive->cmd.transport (READ,data,8))) { + if (!drive->silent) sperror ("READ_CAPACITY",drive->err); + drive->media.capacity = 0; + drive->media.sectsize = 2048; + } else { + drive->media.capacity = ntoh32(data); + if(drive->media.capacity) drive->media.capacity++; + drive->media.sectsize = ntoh32(data+4); + } + lba2msf(drive->media.capacity, &drive->media.capacity_msf); + return 0; +} + +int read_capacity_free(drive_info* drive) { + trk track; + if (drive->media.dstatus == 2) { + drive->media.capacity_free = 0; + return 0; + } + + read_disc_information(drive); + if (!read_track_info(drive, &track, drive->media.tracks)) { + //printf("track %d free: %d\n",drive->media.tracks,track.free); + + drive->media.capacity_free = track.free; + lba2msf(drive->media.capacity_free,&drive->media.capacity_free_msf); + return 0; + } +/* + if (drive->media.type & DISC_CD) { + //if (0) { + if (((drive->media.last_lead_out >> 24) & 0xFF ) == 0xFF) { + drive->media.capacity_free = 0; + lba2msf(drive->media.capacity_free,&drive->media.capacity_free_msf); + return 0; + } else { + drive->media.capacity_free = drive->media.last_lead_out - drive->media.capacity - 150 - 2; + lba2msf(drive->media.capacity_free,&drive->media.capacity_free_msf); + return 0; + } + } else if (drive->media.type & DISC_DVD) { +// track.n = drive->media.sessions+1; + read_track_info(drive, &track, drive->media.sessions+1); + drive->media.capacity_free = track.free; + lba2msf(drive->media.capacity_free,&drive->media.capacity_free_msf); + return 0; + } +*/ + drive->media.capacity_free = 0; + return 1; +} + +int read_capacity_total(drive_info* drive) { + unsigned int len; +// char header[40]; + unsigned int phsta=0, phend=0; +// union { unsigned char _e[4+40],_11[4+256]; } dvd; + drive->media.capacity_total = 0; + int doffs; + + if (drive->media.type & DISC_CD) { + msf lout; + int ilout; +// int atippr = read_atip(drive); +// if (!atippr) drive->ATIP_len+=4; else return 1; + if (!drive->media.ATIP_size) { + drive->media.capacity_total = drive->media.capacity + drive->media.capacity_free; + return 0; + } else { + lout.m=drive->media.ATIP[12]; + lout.s=drive->media.ATIP[13]; + lout.f=drive->media.ATIP[14]; + ilout = msf2lba(lout); + if (!drive->silent) printf("CD-R(W) Lead-Out: %02d:%02d.%02d\n", lout.m, lout.s, lout.f); + drive->media.capacity_total = ilout-150; + +// lba2msf(drive->media.capacity_total, &lout); +// ilout = msf2lba(lout); +// if (!drive->silent) printf("CD-R(W) Capacity: %02d:%02d.%02d (sector %d)\n", lout.m, lout.s, lout.f, ilout); + + return 0; + } + } else if (drive->media.type & (DISC_DVDminus)) { + if (drive->media.type & (DISC_DVDmRWR | DISC_DVDmRWS | DISC_DVDmRWDL)) { + // DVD-RW + len= 12+256; + drive->cmd[0] = MMC_READ_FORMAT_CAPACITIES; + drive->cmd[7] = len>>8; + drive->cmd[8] = len & 0xFF; + if ((drive->err = drive->cmd.transport(READ,drive->rd_buf,len))) + {if (!drive->silent) sperror ("READ_FORMAT_CAPACITIES",drive->err); goto read_total_dvdmr; } + + printf ( "Capacity descriptors: %02x\n",drive->rd_buf[3] >> 3); + doffs = 12; + while (!drive->media.capacity_total) { + if (!(drive->rd_buf[doffs+4] >> 2)) + drive->media.capacity_total = ntoh32(drive->rd_buf+doffs); + } + if (!drive->media.capacity_total) { + switch(drive->rd_buf[8] & 0x03) { + case 0: + printf("* Reserved\n"); + goto read_total_dvdmr; + case 1: + printf("* Unformatted or blank media\n"); + break; + case 2: + printf("* Formatted media\n"); + break; + case 3: + printf("* No media or unknown capacity\n"); + goto read_total_dvdmr; + } + drive->media.capacity_total = ntoh32(drive->rd_buf+4); + } + return 0; + } else { + // DVD-R +read_total_dvdmr: + len= 44; + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[7] = 0x10; + drive->cmd[8] = len>>8; + drive->cmd[9] = len & 0xFF; + drive->cmd[11] = 0; + if ((drive->err = drive->cmd.transport(READ,drive->rd_buf,len))) + {if (!drive->silent) sperror ("READ_DVD_STRUCTURE 10",drive->err); return 1;} + + phsta = ntoh32(drive->rd_buf+8); + if ((drive->rd_buf[6] & 0x60) == 0) + phend = ntoh32(drive->rd_buf+12); + else + phend = ntoh32(drive->rd_buf+16); + + drive->media.capacity_total = phend - phsta + 1; + return 0; + } + } else if (drive->media.type & (DISC_DVDplus | DISC_BD)) { + if (drive->media.type & (DISC_DVDpRW | DISC_DVDpRWDL)) { +#warning DVD+RW total sectors reading + } else { + len= 44; + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[7] = 0x00; + drive->cmd[8] = len>>8; + drive->cmd[9] = len & 0xFF; + drive->cmd[11] = 0; + if ((drive->err = drive->cmd.transport(READ,drive->rd_buf,len))) + {if (!drive->silent) sperror ("READ_DVD_STRUCTURE 00",drive->err); return 1;} + + phsta = ntoh32(drive->rd_buf+8); + if ((drive->rd_buf[6] & 0x60) == 0) + phend = ntoh32(drive->rd_buf+12); + else + phend = ntoh32(drive->rd_buf+16); + + drive->media.capacity_total = phend - phsta + 1; +// printf("Phy start: %6X %d\n",phsta,phsta); +// printf("Phy end : %6X %d\n",phend,phend); + return 0; + + } + } else if (drive->media.type & DISC_DVDRAM) { + drive->media.capacity_total = drive->media.capacity + drive->media.capacity_free; + return 0; + } + printf("Unknown media type, can't get available total capacity! Assuming value from READ_CAPACITY.\n"); + drive->media.capacity_total = drive->media.capacity + drive->media.capacity_free; + return 0; +} + +int read_spare_capacities(drive_info* drive) { + if (!(drive->media.type & (DISC_DVDRAM | DISC_HDDVD_RAM) )) { + drive->media.spare_psa_total = 0; + drive->media.spare_psa_free = 0; + drive->media.spare_ssa_total = 0; + drive->media.spare_ssa_free = 0; + return 0; + } + + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[7] = 0x00; + drive->cmd[8] = 0; + drive->cmd[9] = 16; + drive->cmd[11] = 0; + if ((drive->err = drive->cmd.transport(READ,drive->rd_buf,20))) + if (!drive->silent) { sperror ("READ_DVD_STRUCTURE 00",drive->err); return 1;} + drive->media.spare_psa_total = (drive->rd_buf[5]&0xF0) ? 5120 : 12800; + + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[7] = 0x0A; + drive->cmd[8] = 0; + drive->cmd[9] = 16; + drive->cmd[11] = 0; + if ((drive->err = drive->cmd.transport(READ,drive->rd_buf,16))) + if (!drive->silent) { sperror ("READ_DVD_STRUCTURE 0A",drive->err); return 1;} + + drive->media.spare_psa_free = ntoh32(drive->rd_buf+4); + drive->media.spare_ssa_free = ntoh32(drive->rd_buf+8); + drive->media.spare_ssa_total = ntoh32(drive->rd_buf+12); + return 0; +} + +int read_capacities(drive_info* drive) { + if (!drive->media.type) return 0; +// read_disc_info(drive, 24); +// drive->media.last_lead_out = (drive->rd_buf[20]&0xFF) << 24 | drive->rd_buf[21]*75*60 + drive->rd_buf[22]*75 + drive->rd_buf[23]; + if (read_capacity(drive)) printf("Error reading used capacity\n"); + if (read_capacity_free(drive)) printf("Error reading free capacity\n"); + if (read_capacity_total(drive)) { +// printf("Error reading total capacity\n"); + drive->media.capacity_total = drive->media.capacity + drive->media.capacity_free; + } + read_spare_capacities(drive); + return 0; +} + +int read_disc_information(drive_info* drive) { + int i=0,len=0; + drive->cmd[0] = MMC_READ_DISC_INFORMATION; + drive->cmd[7] = 0x08; + drive->cmd[8] = 0x00; + drive->cmd[9] = 0x00; + drive->cmd.transport (READ,drive->rd_buf,2048); + len= (drive->rd_buf[0]<<8)|drive->rd_buf[1]; + if (!drive->silent) printf("Disc info length: 0x%04X\n ",len); + if (!drive->silent) for (i=0; ird_buf[i] & 0xFF); + if (!((i+1)%8)) printf("\n "); + } + if (((i)%8)) printf("\n"); +// if (len < 0x20) { + if (len < 0x16) { + drive->media.erasable = 0; + drive->media.dstatus = 0; + drive->media.sstatus = 0; + drive->media.sessions = 0; + drive->media.tracks = 0; + return 1; + } + drive->media.erasable = (drive->rd_buf[2]&0x10); + drive->media.dstatus = drive->rd_buf[2]&0x03; + drive->media.sstatus = (drive->rd_buf[2]>>2)&0x03; + drive->media.sessions = (drive->rd_buf[4]|(drive->rd_buf[9]<<8)); +// if (!drive->media.sstatus) drive->media.sessions--; + drive->media.tracks = drive->rd_buf[6]|(drive->rd_buf[11]<<8); + if (!drive->silent) { + printf(" first track# on disc: %d\n", drive->rd_buf[3]); + printf(" first track# in last session: %d\n", drive->rd_buf[5]|(drive->rd_buf[10]<<8)); + printf(" last track# in last session: %d\n", drive->media.tracks); + printf(" disc type: %02X\n", drive->rd_buf[8]&0xFF); + printf(" disc ID: %08X\n", ntoh32(drive->rd_buf[12])); + printf(" Last session lead-in start: %d:%02d.%02d\n", + (drive->rd_buf[16]<<8)|drive->rd_buf[17],drive->rd_buf[18],drive->rd_buf[19]); + drive->media.last_lead_out = ((drive->rd_buf[20]&0xFF) << 24) | (drive->rd_buf[21]*75*60 + drive->rd_buf[22]*75 + drive->rd_buf[23]); + printf(" Last possible lead-out start: %d:%02d.%02d (sector 0x%08X)\n", + (drive->rd_buf[20]<<8)|drive->rd_buf[21],drive->rd_buf[22],drive->rd_buf[23],drive->media.last_lead_out); + } +/* + if (!drive->media.sstatus) { + drive->media.sessions--; + drive->media.tracks--; + } +*/ + return 0; +} + +int determine_cd_type(drive_info* drive) { +// unsigned char* ATIP; +// int ATIP_len; +// int i; + int ratip = read_atip(drive); + if (ratip) { + if (!drive->silent) printf("no ATIP found, assuming disc type: CD-ROM\n"); + return DISC_CDROM; // CD-ROM +// } else { +// drive->ATIP_len += 4; + } + if (drive->media.ATIP_size < 8) { + if (!drive->silent) printf("ATIP too small, assuming disc type: CD-ROM\n"); + return DISC_CDROM; // CD-ROM + } +/* + printf("ATIP_len=%d\nATIP data:",drive->ATIP_len); + for (i=0; i< drive->ATIP_len; i++) printf("%4d",(drive->ATIP[i])&0xFF); + printf("\n"); +*/ + // CD-RW? + int cdrw = !!(drive->media.ATIP[6] & 0x40); + int cd_subtype = (drive->media.ATIP[6] & 0x38); + if (cdrw) { + if (!drive->silent) printf("disc type: CD-RW\n"); + return (DISC_CDRW | cd_subtype); + } + if (!drive->silent) printf("disc type: CD-R\n"); + return (DISC_CDR | cd_subtype); +} + +int read_mediaid_bd(drive_info* drive) { + int i; + unsigned int len; + memset(drive->media.MID, 0, 20); + + for (int i=0; iparms.wr_speed_tbl_media[i] = -1; + + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[1] = 1; // media type = BD + drive->cmd[7] = 0x00; + drive->cmd[8] = 0; + drive->cmd[9] = 4; + drive->cmd[11] = 0; + if ((drive->err = drive->cmd.transport(READ,drive->rd_buf,4))) + {if (!drive->silent) sperror ("READ_BD_STRUCTURE 00",drive->err); return 1;} + len = (drive->rd_buf[0]<<8|drive->rd_buf[1]) + 2; + if (len>128) len= 128; + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[1] = 1; // media type = BD + drive->cmd[7] = 0x00; + drive->cmd[8] = len>>8; + drive->cmd[9] = len & 0xFF; + drive->cmd[11] = 0; + if ((drive->err = drive->cmd.transport(READ,drive->media.MID_raw,len))) + {if (!drive->silent) sperror ("READ_BD_STRUCTURE 00",drive->err); return 1;} + drive->media.MID_size = len-4; + + if(drive->media.MID_raw[4] != 'D' || drive->media.MID_raw[4+1] != 'I') { + printf("READ_BD_STRUCTURE: got some data, but not disc info\n"); + return 2; + } + drive->media.disc_size = drive->media.MID_raw[4+11] >> 6; + drive->media.polarity = drive->media.MID_raw[4+14]; + + if (drive->media.type & DISC_BD_ROM) { + if (!drive->silent) + printf(COL_YEL "BD-ROM does not contain media ID" COL_NORM "\n"); + return 0; + } + memcpy(drive->media.MID, drive->media.MID_raw + 4+100,6); + i = strlen(drive->media.MID); + drive->media.MID[i++]='-'; + memcpy(drive->media.MID+i, drive->media.MID_raw + 4+100+6,3); + i = strlen(drive->media.MID); + drive->media.MID[i++]='/'; + sprintf(drive->media.MID+i, "%03d", drive->media.MID_raw[4+111]); + + drive->media.MID_type = MID_type_BD; + return 0; +} + +int read_mediaid_dvd(drive_info* drive){ + memset(drive->media.MID, 0, 20); + for (int i=0; iparms.wr_speed_tbl_media[i] = -1; + // + if (drive->media.type & DISC_DVDRAM) { + read_mediaid_dvdram(drive); + if (!drive->err) return 0; + } if (drive->media.type & DISC_DVDminus) { + read_mediaid_dvdminus(drive); + if (!drive->err) return 0; + } else if (drive->media.type & DISC_DVDplus) { + read_mediaid_dvdplus(drive); + if (!drive->err) return 0; +#if 1 + } else { + // try to read Media ID even media is reported as DVD-ROM + if (read_mediaid_dvdplus(drive)) + return read_mediaid_dvdminus(drive); +#endif + } + return 0; +} + +int read_mediaid_dvdram(drive_info* drive) { + int i; + unsigned int len; + char header[40]; +// union { unsigned char _e[4+40],_11[4+256]; } dvd; + + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[7] = 0x00; + drive->cmd[8] = 0; + drive->cmd[9] = 4; + drive->cmd[11] = 0; + if ((drive->err = drive->cmd.transport(READ,header,4))) + {if (!drive->silent) sperror ("READ_DVD_STRUCTURE 00",drive->err); return 1;} + len = (header[0]<<8|header[1]) + 2; + if (len>762) len= 762; + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[7] = 0x00; + drive->cmd[8] = len>>8; + drive->cmd[9] = len & 0xFF; + drive->cmd[11] = 0; + if ((drive->err = drive->cmd.transport(READ,drive->media.MID_raw,len))) + {if (!drive->silent) sperror ("READ_DVD_STRUCTURE 00",drive->err); return 1;} + + drive->media.MID_size = len-4; + + memcpy(drive->media.MID, drive->media.MID_raw + 4+597,16); + + for (i=0; i<12; i++) if(drive->media.MID[i] == 0) drive->media.MID[i]=0x20; + + drive->media.MID_type = MID_type_DVDRAM; + return 0; +} + +int read_mediaid_dvdminus(drive_info* drive){ + int i; + unsigned int len; + char header[40]; +// union { unsigned char _e[4+40],_11[4+256]; } dvd; + + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[7] = 0x0e; + drive->cmd[8] = 0; + drive->cmd[9] = 4; + drive->cmd[11] = 0; + if ((drive->err = drive->cmd.transport(READ,header,4))) + {if (!drive->silent) sperror ("READ_DVD_STRUCTURE 0E",drive->err); return 1;} + len = (header[0]<<8|header[1]) + 2; + if (len>68) len= 68; + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[7] = 0x0e; + drive->cmd[8] = len>>8; + drive->cmd[9] = len & 0xFF; + drive->cmd[11] = 0; + if ((drive->err = drive->cmd.transport(READ,drive->media.MID_raw,len))) + {if (!drive->silent) sperror ("READ_DVD_STRUCTURE 0E",drive->err); return 1;} + + drive->media.MID_size = len-4; + + memcpy(drive->media.MID, drive->media.MID_raw + 4+17,6); + i = strlen(drive->media.MID); + drive->media.MID[i++]='-'; + memcpy(drive->media.MID+i, drive->media.MID_raw + 4+25,6); +/* + i = strlen(drive->media.MID); + drive->media.MID[i++]='-'; + memcpy(drive->media.MID+i, drive->media.MID_raw + 4+33,6); +*/ + drive->media.MID_type = MID_type_DVDm; + return 0; +} + +int read_mediaid_dvdplus(drive_info* drive){ + int i; + unsigned int len; + char header[40]; + + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[7] = 0x11; + drive->cmd[8] = 0; + drive->cmd[9] = 4; + drive->cmd[11] = 0; + if ((drive->err = drive->cmd.transport(READ,header,4))) + {if (!drive->silent) sperror ("READ_DVD_STRUCTURE 11",drive->err); return 1;} + len = (header[0]<<8|header[1]) + 2; + if (len>260) len= 260; + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[7] = 0x11; + drive->cmd[8] = len>>8; + drive->cmd[9] = len & 0xFF; + drive->cmd[11] = 0; + if ((drive->err = drive->cmd.transport(READ,drive->media.MID_raw,len))) + {if (!drive->silent) sperror ("READ_DVD_STRUCTURE 11",drive->err); return 1;} + + drive->media.MID_size = len-4; + + memcpy(drive->media.MID, drive->media.MID_raw + 4+19,8); + i = strlen(drive->media.MID); + drive->media.MID[i++]='-'; + memcpy(drive->media.MID+i, drive->media.MID_raw + 4+27,3); + i = strlen(drive->media.MID); + drive->media.MID[i++]='/'; + sprintf(drive->media.MID+i, "%03d", drive->media.MID_raw[4+30]); + + drive->media.MID_type = MID_type_DVDp; + +// process manufacturer defined write speeds... + int wr_idx = 0; + if (drive->media.MID_raw[4+31] > 32) { + drive->parms.wr_speed_tbl_media[wr_idx] = drive->media.MID_raw[4+32] / 14.58; + wr_idx++; + } + for (int i=0; i<6;i++) { + //if ((drive->media.MID_raw[4+18] & (1 << i)) && (drive->media.MID_raw[4+64+i*32] == (i+1) )) { + if ((drive->media.MID_raw[4+18] & (1 << i)) && (drive->media.MID_raw[4+64+i*32] >0)) { + // printf("EI #%d\n",i); + drive->parms.wr_speed_tbl_media[wr_idx] = drive->media.MID_raw[4+67+i*32] / 13.95; + wr_idx++; + } + } + return 0; +} + +int read_mediaid_cd(drive_info* drive) +{ +// printf("read_mediaid_cd()\n"); + msf lin; + int type; +// int atippr = read_atip(drive); +// if (!atippr) drive->ATIP_len+=4; else return 1; + if (!drive->media.ATIP_size) return 1; + lin.m=drive->media.ATIP[8]; + lin.s=drive->media.ATIP[9]; + lin.f=drive->media.ATIP[10]; + type = lin.f % 10; + lin.f -= type; + int idx=0; + int nf=1; + while (mi[idx].lin.m && nf) + if (lin.m == mi[idx].lin.m && lin.s == mi[idx].lin.s && lin.f == mi[idx].lin.f) nf=0; + else idx++; + +// strncpy(drive->media.MID,mi[idx].name,47)); + sprintf(drive->media.MID,"[%02d:%02d.%02d] %s",lin.m,lin.s,lin.f,mi[idx].name); + drive->media.MID_type = MID_type_CD; + return 0; +} + +int determine_disc_type(drive_info* drive) { +// int current = 0; +// int i=0; + drive->media.type = DISC_NODISC; + drive->media.book_type = 0; + drive->media.layers = 1; + drive->media.max_rate = 0x0F; + drive->media.disc_size = 0; + drive->media.MID_type = MID_type_NONE; + drive->media.MID_size = 0; +// drive->media.type = Media_NoMedia; + if (drive->mmc>1) { + get_configuration(drive, FEATURE_PROFILE_LIST, NULL, 0); + switch (drive->rd_buf[7]) { + case 0: drive->media.type = DISC_NODISC; break; + case PROFILE_CD_ROM: drive->media.type = DISC_CDROM; break; + case PROFILE_CD_R: drive->media.type = DISC_CDR; break; + case PROFILE_CD_RW: drive->media.type = DISC_CDRW; break; + case PROFILE_DVD_ROM: drive->media.type = DISC_DVDROM; break; + case PROFILE_DVD_R_SEQ: drive->media.type = DISC_DVDmR; break; + case PROFILE_DVD_RAM: drive->media.type = DISC_DVDRAM; break; + case PROFILE_DVD_RW_RESTOV: drive->media.type = DISC_DVDmRWR; break; + case PROFILE_DVD_RW_SEQ: drive->media.type = DISC_DVDmRWS; break; + case PROFILE_DVD_RW_DL: drive->media.type = DISC_DVDmRWDL; break; + case PROFILE_DVD_R_DL_SEQ: drive->media.type = DISC_DVDmRDL; break; + case PROFILE_DVD_R_DL_JUMP: drive->media.type = DISC_DVDmRDLJ; break; + case PROFILE_DVD_PLUS_RW: drive->media.type = DISC_DVDpRW; break; + case PROFILE_DVD_PLUS_R: drive->media.type = DISC_DVDpR; break; + case PROFILE_DVD_PLUS_R_DL: drive->media.type = DISC_DVDpRDL; break; + case PROFILE_DVD_PLUS_RW_DL: drive->media.type = DISC_DVDpRWDL; break; + + case PROFILE_BD_ROM: drive->media.type = DISC_BD_ROM; break; + case PROFILE_BD_R_SEQ: drive->media.type = DISC_BD_R_SEQ; break; + case PROFILE_BD_R_RND: drive->media.type = DISC_BD_R_RND; break; + case PROFILE_BD_RE: drive->media.type = DISC_BD_RE; break; + + case PROFILE_HDDVD_ROM: drive->media.type = DISC_HDDVD_ROM; break; + case PROFILE_HDDVD_R: drive->media.type = DISC_HDDVD_R; break; + case PROFILE_HDDVD_RAM: drive->media.type = DISC_HDDVD_RAM; break; + case PROFILE_HDDVD_RW: drive->media.type = DISC_HDDVD_RW; break; + case PROFILE_HDDVD_R_DL: drive->media.type = DISC_HDDVD_RDL; break; + case PROFILE_HDDVD_RW_DL: drive->media.type = DISC_HDDVD_RWDL; break; + + default: drive->media.type = DISC_UN; break; + } + if (!drive->media.type) return 0; + read_disc_information(drive); + if (drive->media.type & DISC_CD) { + drive->media.type = determine_cd_type(drive); + read_mediaid_cd(drive); + if (!drive->silent) printf("** MID: '%s'\n",drive->media.MID); + return 0; + } else if (drive->media.type & DISC_DVD) { + drive->rd_buf[4]=0; + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[7] = 0;//0x11; //dvd_dash; + drive->cmd[9] = 36; + drive->cmd[11] = 0; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,36))) + if (!drive->silent) sperror ("READ_DVD_STRUCTURE",drive->err); + drive->media.book_type = (drive->rd_buf[4] & 0xFF); + drive->media.max_rate = (drive->rd_buf[5] & 0x0F); + drive->media.disc_size = ((drive->rd_buf[5] & 0xF0) >> 4); + drive->media.layers = 1 + ((drive->rd_buf[6] & 0x60) >> 5); + read_mediaid_dvd(drive); + if (!drive->silent) printf("** MID: '%s'\n",drive->media.MID); + if ((!(drive->wr_capabilities & DEVICE_DVD)) && (drive->media.type & DISC_DVDROM)) { + if (!drive->silent) { + printf("Device can't write DVD's or media detected as DVD-ROM,\n"); + printf("trying to corectly detect DVD type...\n"); + } + switch ((drive->media.book_type>>4)&0x0F){ + case BOOK_DVD_R: + if (drive->media.layers == 1) + drive->media.type = DISC_DVDmR; + else + drive->media.type = DISC_DVDmRDL; + break; + case BOOK_DVD_RW: + drive->media.type = DISC_DVDmRW; break; + case BOOK_DVD_PR: + drive->media.type = DISC_DVDpR; break; + case BOOK_DVD_PRW: + drive->media.type = DISC_DVDpRW; break; + case BOOK_DVD_PR_DL: + drive->media.type = DISC_DVDpRDL; break; + case BOOK_DVD_ROM: + switch (drive->media.MID_type) { + case MID_type_DVDRAM: + drive->media.type = DISC_DVDRAM; + break; + case MID_type_DVDm: + if (drive->media.erasable) { + if (drive->media.layers == 1) drive->media.type = DISC_DVDmRW; + else drive->media.type = DISC_DVDmRWDL; + } else { + if (drive->media.layers == 1) drive->media.type = DISC_DVDmR; + else drive->media.type = DISC_DVDmRDL; + } + break; + + case MID_type_DVDp: + if (drive->media.erasable) { + if (drive->media.layers == 1) drive->media.type = DISC_DVDpRW; + else drive->media.type = DISC_DVDpRWDL; + } else { + if (drive->media.layers == 1) drive->media.type = DISC_DVDpR; + else drive->media.type = DISC_DVDpRDL; + } + break; + default: + break; + } + break; + default: + break; + } + } + if (drive->media.type & DISC_DVDminus) + read_writer_info(drive); +// if (!drive->silent) printf("** Writer used: '%s'\n",drive->media.writer); + + read_disc_regions(drive); +/* + printf("DVD Copyright info: "); + for (i=0;i<4;i++) printf("0x%02X ",drive->rd_buf[i]); + printf("\n"); +*/ + return 0; + } else if (drive->media.type & DISC_BD) { + drive->rd_buf[4]=0; + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[7] = 0;//0x11; //dvd_dash; + drive->cmd[9] = 36; + drive->cmd[11] = 0; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,36))) + if (!drive->silent) sperror ("READ_DVD_STRUCTURE",drive->err); + drive->media.book_type = 0; + drive->media.layers = 1 + ((drive->rd_buf[6] & 0x60) >> 5); + read_mediaid_bd(drive); + if (!drive->silent) printf("** MID: '%s'\n",drive->media.MID); + } + } else { + read_capacity(drive); + if (drive->media.capacity) { + drive->media.type = DISC_CDROM; + read_disc_information(drive); + } + return 0; + } + return 1; +} + +int get_spindown(drive_info* drive) { + mode_sense(drive, 0x0D, 00, 192); + if (drive->err) + {drive->parms.spindown_idx=spindowns; return (drive->err);} + drive->parms.spindown_idx = drive->rd_buf[11] & 0x0F; + return 0; +} + +int set_spindown(drive_info* drive) { + memset(drive->rd_buf,0,16); + drive->rd_buf[8] = 0x0D; + drive->rd_buf[9] = 0x06; + drive->rd_buf[11] = drive->parms.spindown_idx & 0x0F; + drive->rd_buf[13] = 0x3C; + drive->rd_buf[15] = 0x4B; + mode_select(drive, 16); + return 0; +} + +int get_performance(drive_info* drive, bool rw, uint8_t type) { + const int max_descs=52; + const int desc_len=16; + uint32_t len, descn; + uint32_t r,w,lba; +// int i; + int j,offs; + drive->cmd[0] = MMC_GET_PERFORMANCE; + drive->cmd[1] = (!type) ? (0x04 * rw) : 0x00; + drive->cmd[8] = (max_descs >> 8) & 0xFF; + drive->cmd[9] = max_descs & 0xFF; + drive->cmd[10] = type; + drive->cmd[11] = 0x00; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf, 1024))) + { if (!drive->silent) sperror ("GET_PERFORMANCE",drive->err); return (drive->err); } +// return 1; + len = ntoh32(drive->rd_buf); + descn = len/desc_len; +// printf("Performance data length: %d; decriptors: %d\n",len, descn); + +// printf("GET_PERFORMANCE response dump:\n"); +// for (j=0; j*8rd_buf[i]); +// printf("\n"); +// } + + if (type == 0x03) for (j=0; jparms.wr_speed_tbl_kb[j] = -1; + + for (j=0; jperf.lba_s = ntoh32(drive->rd_buf+offs); + offs = 8+j*desc_len+4; + drive->perf.spd_s = ntoh32(drive->rd_buf+offs); + offs = 8+j*desc_len+8; + drive->perf.lba_e = ntoh32(drive->rd_buf+offs); + offs = 8+j*desc_len+12; + drive->perf.spd_e = ntoh32(drive->rd_buf+offs); + + offs = 8+j*desc_len+12; + if (!rw) + drive->parms.read_speed_kb = ntoh32(drive->rd_buf+offs); + else + drive->parms.write_speed_kb = ntoh32(drive->rd_buf+offs); + +// printf("\t%dkB/s@%d -> %dkB/s@%d\n", +// drive->perf.spd_s,drive->perf.lba_s,drive->perf.spd_e,drive->perf.lba_e); + } else if (type == 0x03) { + offs = 8+j*desc_len+4; + lba = ntoh32(drive->rd_buf+offs); + offs = 8+j*desc_len+8; + r = ntoh32(drive->rd_buf+offs); + offs = 8+j*desc_len+12; + w = ntoh32(drive->rd_buf+offs); + +// printf("LBA %d: \tW %dkB/s R %dkB/s\n", lba, w, r); + + drive->parms.wr_speed_tbl_kb[j] = ntoh32(drive->rd_buf+offs); + drive->parms.max_write_speed_kb = std::max(drive->parms.max_write_speed_kb, drive->parms.wr_speed_tbl_kb[j]); + } + } + return 0; +} + +int detect_speeds(drive_info *drive) +{ + int idx, spd, prev_spd, rspd_kb, wspd_kb; +// bool gp=1; + if (!drive->silent) printf("== Detecting supported read speeds...\n"); + for (idx=1; idxparms.speed_tbl[idx]=-1; + drive->parms.speed_tbl_kb[idx]=-1; + } + if (get_rw_speeds(drive)) { + drive->parms.read_speed_kb = 1; + drive->parms.write_speed_kb = 1; + drive->parms.speed_mult = 1; + return 1; + } + rspd_kb = drive->parms.read_speed_kb; + wspd_kb = drive->parms.write_speed_kb; + +// speedidx = 0; +// printf("media.type & DISC_CD = %LX\n", drive->media.type & DISC_CD ); +// printf("media.type & DISC_DVD = %LX\n", drive->media.type & DISC_DVD ); + if ( drive->media.type & (DISC_DVD | DISC_BD)) { + bool BD = !!(drive->media.type & DISC_BD); + drive->parms.read_speed_kb= BD ? 71920 : 22162; + if (!set_rw_speeds(drive)) { + get_rw_speeds(drive); + if (!drive->silent) printf("Max DVD speed via GET_CD_SPEED: %dkB/s\n", + drive->parms.read_speed_kb); + //if ( (!drive->wr_capabilities) && (drive->capabilities & CAP_REAL_TIME_STREAMING) && (!get_performance(drive))) { + if ( (drive->capabilities & CAP_REAL_TIME_STREAMING) + // && !(drive->media.type & DISC_DVDRAM) + && !(get_performance(drive, 0, 0))) { + if (drive->perf.spd_e > 1) + drive->parms.max_read_speed_kb = + drive->perf.spd_e; +// (int)(drive->perf.spd_e*((float)2294912/(float)drive->perf.lba_e)); +// drive->parms.max_read_speed_kb = drive->perf.spd_e; + drive->parms.max_read_speed_dvd = (drive->parms.max_read_speed_kb/1350); + if (!drive->silent) printf("Max DVD speed via GET_PERFORMANCE: %d X, %dkB/s\n", + drive->parms.max_read_speed_dvd, + drive->parms.max_read_speed_kb); + drive->parms.speed_mult = drive->parms.read_speed_kb/drive->parms.max_read_speed_dvd; + if (drive->parms.speed_mult < 600) { + drive->parms.speed_mult = 176.4; + } else { + drive->parms.max_read_speed_dvd = (drive->parms.max_read_speed_kb/ (BD ? 4494 : 1384) ); + drive->parms.speed_mult = BD ? 4495 : 1385; + } + } else { + drive->parms.max_read_speed_kb = drive->parms.read_speed_kb; + if (!drive->silent) printf("GET_PERFORMANCE error: using default multiplier\n"); + drive->parms.speed_mult = BD ? 4495 : 1385; + drive->parms.max_read_speed_dvd = (drive->parms.max_read_speed_kb/drive->parms.speed_mult); + } +// drive->parms.read_speed_kb = spd_kb; +// set_rw_speeds(drive); +// drive->parms.speed_mult= drive->parms.max_read_speed_kb/drive->parms.max_read_speed_dvd; + if (!drive->silent) { + printf("1X multiplier: %.1f kB/s\n", drive->parms.speed_mult); + printf("Max spd: %d X, %d kB/s\n", + drive->parms.max_read_speed_dvd, + drive->parms.max_read_speed_kb); + } + + idx=0; prev_spd=0; + for (spd=1; ((idxparms.max_read_speed_dvd+2))); spd++) { +// for (spd=1; ((idxparms.read_speed_kb = (int)(spd * (drive->parms.speed_mult+1)); + if (!drive->silent) printf("Trying: %dX (%d kB/s)\n", spd, drive->parms.read_speed_kb); + set_rw_speeds(drive); + get_rw_speeds(drive); + drive->parms.read_speed_dvd = (int) (drive->parms.read_speed_kb / drive->parms.speed_mult); + if (prev_spd != drive->parms.read_speed_dvd) { +// spd = drive->parms.read_speed_dvd; + spd = std::max(spd, drive->parms.read_speed_dvd); + drive->parms.speed_tbl[idx] = drive->parms.read_speed_dvd; + drive->parms.speed_tbl_kb[idx] = drive->parms.read_speed_kb; + if (!drive->silent) printf(" RD speed: %dX (%d kB/s)\n", + drive->parms.speed_tbl[idx], + drive->parms.speed_tbl_kb[idx]); + prev_spd = drive->parms.read_speed_dvd; + idx++; +// if (drive->capabilities & CAP_REAL_TIME_STREAMING) get_performance(drive); + } + } +#ifdef speedidx + } else { + speedidx=-1; +#endif + } + } else { + drive->parms.read_speed_kb=-1; + if (!set_rw_speeds(drive)) { + get_rw_speeds(drive); + drive->parms.max_read_speed_kb = drive->parms.read_speed_kb; + drive->parms.max_read_speed_cd = (drive->parms.max_read_speed_kb/176); +// drive->parms.speed_mult=drive->parms.max_read_speed_kb/drive->parms.max_read_speed_cd; + drive->parms.speed_mult=176.4; + if (!drive->silent) + printf("Maximum CD speed: %dX, %5d kB/s; 1X = %.1f kB/s\nSpeeds:\n", + drive->parms.max_read_speed_cd, + drive->parms.max_read_speed_kb, + drive->parms.speed_mult); + idx=0; prev_spd=0; + for (spd=1; ((idxparms.max_read_speed_cd+2))); spd++) { +// for (spd=1; ((idxparms.read_speed_kb = (int) (spd * (drive->parms.speed_mult+1)); + if (!drive->silent) printf("Trying: %dX (%5d kB/s)\n", spd, drive->parms.read_speed_kb); + set_rw_speeds(drive); + get_rw_speeds(drive); + drive->parms.read_speed_cd = (int) (drive->parms.read_speed_kb / drive->parms.speed_mult); + if (prev_spd != drive->parms.read_speed_cd) { +// spd = drive->parms.read_speed_cd; + spd = std::max(spd, drive->parms.read_speed_cd); + drive->parms.speed_tbl[idx] = drive->parms.read_speed_cd; + drive->parms.speed_tbl_kb[idx] = drive->parms.read_speed_kb; + if (!drive->silent) printf(" RD speed: %dX (%5d kB/s)\n", + drive->parms.speed_tbl[idx], + drive->parms.speed_tbl_kb[idx]); + prev_spd = drive->parms.read_speed_cd; + idx++; +// if (drive->capabilities & CAP_REAL_TIME_STREAMING) get_performance(drive); + } + } +#ifdef speedidx + } else { + speedidx=-1; +#endif + } + } +// combo_Speed->clear(); + idx=0; +#ifdef speedidx + if (!speedidx) { + while ((idxparms.speed_tbl[idx]>0)){ + int mlt = 176; + if ( drive->media.type & DISC_DVD ) mlt=1385; + +// combo_Speed->insertItem(QString().sprintf("%2dX (%dkB/s)", +// drive->parms.speed_tbl[idx],mlt*drive->parms.speed_tbl[idx])); + if ((spd_kb/drive->parms.speed_mult)==drive->parms.speed_tbl[idx]) speedidx = idx; + idx++; + } + } else { + speedidx = 0; + } +#endif +// combo_Speed->insertItem(tr("max")); + + get_write_speed_tbl(drive); + for (int i=0; iparms.wr_speed_tbl_kb[i] > 0; i++) { + if (!drive->silent) + printf(" WR speed: %.1fX (%d kB/s)\n", + drive->parms.wr_speed_tbl_kb[i] / drive->parms.speed_mult, + drive->parms.wr_speed_tbl_kb[i]); + } + + drive->parms.read_speed_kb = rspd_kb; + drive->parms.write_speed_kb = wspd_kb; + set_rw_speeds(drive); +// combo_Speed->setCurrentItem(speedidx); +// show_read_speed(drive); + return 0; +} + +int get_write_speed_tbl(drive_info* drive) { + int offs; + int i, spdcnt; + drive->parms.max_write_speed_kb = 0; + if (drive->capabilities & CAP_REAL_TIME_STREAMING) { + get_performance(drive, 0, 0x03); + } else { + mode_sense(drive, MODE_PAGE_MM_CAP_STATUS, 00, 256); + offs=0; while (((drive->rd_buf[offs]) & 0x3F) != 0x2A) offs++; +// drive->parms.write_speed_kb = ntoh16(drive->rd_buf+offs+28); + spdcnt = ntoh16(drive->rd_buf+offs+30); + for (i=0; iparms.wr_speed_tbl_kb[i] = -1; +// printf("== Write speeds: %d\n",spdcnt); + for (i=0; (iparms.wr_speed_tbl_kb[i] = ntoh16(drive->rd_buf+offs+32+i*4+2); + drive->parms.max_write_speed_kb = std::max(drive->parms.max_write_speed_kb, drive->parms.wr_speed_tbl_kb[i]); +// printf(" Speed #%02d: %d kB/s\n",i,drive->parms.wr_speed_tbl_kb[i]); + } + } + return 0; +} + +int get_rw_speeds(drive_info* drive) { + int offs; +/* + + PLEXTOR drives always returns maximum read speed via "get performance", not current! + +*/ + if (!drive->get_performance_fail && isPlextor(drive)) { + drive->get_performance_fail=1; + } + + //if ((drive->capabilities & CAP_REAL_TIME_STREAMING) && !(drive->media.type & DISC_DVDRAM)) { + //if (!drive->get_performance_fail && (drive->capabilities & CAP_REAL_TIME_STREAMING)) { + if (!drive->get_performance_fail +// && !(drive->media.type & DISC_CD) + && (drive->capabilities & CAP_REAL_TIME_STREAMING)) { + if (!drive->silent) printf("Requesting curerent speeds via GET_PERFORMANCE command...\n"); + if (!get_performance(drive, 0, 0) && !get_performance(drive, 1, 0)) + return 0; + drive->get_performance_fail=1; + } + + if (!drive->silent) printf("Requesting curerent speeds via page 2A...\n"); + if (!mode_sense(drive, MODE_PAGE_MM_CAP_STATUS, 00, 256)) { + offs=0; while (((drive->rd_buf[offs]) & 0x3F) != 0x2A) offs++; + drive->parms.read_speed_kb = ntoh16u(drive->rd_buf+offs+14); + drive->parms.write_speed_kb = ntoh16u(drive->rd_buf+offs+28); + return 0; + } + drive->parms.read_speed_kb = 0; + drive->parms.write_speed_kb = 0; + return 1; +} + +int set_streaming(drive_info* drive) { + char data[28]; memset(data, 0, 28); + + uint32_t* start_lba = (uint32_t*)&data[4]; + uint32_t* end_lba = (uint32_t*)&data[8]; + uint32_t* read_size = (uint32_t*)&data[12]; + uint32_t* read_time = (uint32_t*)&data[16]; + uint32_t* write_size = (uint32_t*)&data[20]; + uint32_t* write_time = (uint32_t*)&data[24]; + uint32_t speed_rd = 0xFFFFFFFF; + uint32_t speed_wr = 0xFFFFFFFF; + if (drive->parms.read_speed_kb) speed_rd = drive->parms.read_speed_kb; + if (drive->parms.write_speed_kb) speed_wr = drive->parms.write_speed_kb; + +// if (read_capacity(drive)) return drive->err; + *start_lba = ntoh32(0); + *end_lba = ntoh32(drive->media.capacity); +// *end_lba = ntoh32(0); + *read_time = ntoh32(1000); + *read_size = ntoh32(speed_rd); + *write_time = ntoh32(1000); + *write_size = ntoh32(speed_wr); + drive->cmd[0] = MMC_SET_STREAMING; + drive->cmd[10] = 28; + drive->cmd[11] = 0; + if ((drive->err=drive->cmd.transport(WRITE,data,28))) + { if(!drive->silent) sperror ("SET_STREAMING",drive->err); return (drive->err); } + return 0; +} + +int set_cd_speed(drive_info* drive) { + uint16_t speed_rd = 0xFFFF; + uint16_t speed_wr = 0xFFFF; + if (drive->parms.read_speed_kb) speed_rd = drive->parms.read_speed_kb; + if (drive->parms.write_speed_kb) speed_wr = drive->parms.write_speed_kb; + + drive->cmd[0] = MMC_SET_SPEED; + drive->cmd[1] = 0x01; + drive->cmd[2] = (speed_rd >> 8) & 0xFF; + drive->cmd[3] = speed_rd & 0xFF; +// drive->cmd[4] = 0xFF; +// drive->cmd[5] = 0xFF; + drive->cmd[4] = (speed_wr >> 8) & 0xFF; + drive->cmd[5] = speed_wr & 0xFF; + drive->cmd[11] = 0; + if ((drive->err=drive->cmd.transport(NONE,NULL,0) )) { +// if (drive->err != 0x23A02) drive->capabilities&=(NCAP_SET_CD_SPEED); + if (!drive->silent) sperror ("SET_CD_SPEED",drive->err); return (drive->err); + } + return 0; +} + +//#define __STREAMING_PRIOR +int set_rw_speeds(drive_info* drive) { + int rez=1; + + if ((drive->capabilities & CAP_REAL_TIME_STREAMING)) { + //if ((drive->capabilities & CAP_REAL_TIME_STREAMING) && !(drive->media.type & DISC_CD)) { + //if ((drive->capabilities & CAP_REAL_TIME_STREAMING) && !(drive->media.type & DISC_DVDRAM)) { + if (!drive->silent) printf("Setting speeds via SET_STREAMING command...\n"); + rez = set_streaming(drive); + } + if (rez) { + if (!drive->silent) printf("Setting speeds via SET_CD_SPEED command...\n"); + rez = set_cd_speed(drive); + } +/* +#ifdef __STREAMING_PRIOR + if (drive->media.type & DISC_CD) { + rez = set_cd_speed(drive); + } else if (drive->media.type & DISC_DVD) { + { if ((rez = set_cd_speed(drive))) rez = set_streaming(drive); } + } +#else + if ((drive->capabilities & CAP_SET_CD_SPEED) )//&& (drive->media.type & DISC_CD)) + { if ((rez = set_cd_speed(drive))) rez = set_streaming(drive); } + else if (drive->capabilities & CAP_REAL_TIME_STREAMING) + { rez = set_streaming(drive); } +#endif*/ + return rez; +} + +int get_media_status(drive_info* drive){ + drive->cmd[0]=MMC_GET_EVENT_STATUS_NOTIFICATION; + drive->cmd[1]=0x01; + drive->cmd[4]=0x10; + drive->cmd[7]=0; + drive->cmd[8]=8; + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,8))) + { sperror ("GET_EVENT_STATUS",drive->err); return (drive->err); } + if (drive->rd_buf[5] & 0x01) drive->parms.status |= STATUS_OPEN; + else drive->parms.status &= (~STATUS_OPEN); + if (drive->rd_buf[5] & 0x02) drive->parms.status |= STATUS_MEDIA_PRESENT; + else drive->parms.status &= (~STATUS_MEDIA_PRESENT); + drive->parms.event = drive->rd_buf[4] & 0x0F; + return 0; +} + +int start_stop(drive_info* drive, bool start) { + drive->cmd[0]=MMC_START_STOP_UNIT; + drive->cmd[4]= start ? 0x01 : 0; + if ((drive->err=drive->cmd.transport(NONE,NULL,0))) + { sperror ("START_STOP_UNIT",drive->err); return (drive->err); } + return 0; +} + +int load_eject(drive_info* drive, bool load, bool IMMED) { + drive->cmd[0]=MMC_START_STOP_UNIT; + drive->cmd[1]= IMMED ? 0x01 : 0; + drive->cmd[4]= load ? 0x02 : 0; + if ((drive->err=drive->cmd.transport(NONE,NULL,0))) + { + if (drive->err != 0x55302) + { sperror ("LOAD_EJECT",drive->err); return (drive->err); } + + printf("Trying to unlock media...\n"); + drive->parms.status &= (~STATUS_LOCK); + set_lock(drive); + + drive->cmd[0]=MMC_START_STOP_UNIT; + drive->cmd[1]= IMMED ? 0x01 : 0; + drive->cmd[4]= load ? 0x02 : 0; + if ((drive->err=drive->cmd.transport(NONE,NULL,0))) + { sperror ("LOAD_EJECT",drive->err); return (drive->err); } + } + return 0; +} + +int load_eject(drive_info* drive, bool IMMED) { + get_media_status(drive); + printf("Tray state: %s\n" ,(drive->parms.status & STATUS_OPEN) ? "open" : "close"); + load_eject(drive, drive->parms.status & STATUS_OPEN, IMMED); + return 0; +} + +int get_lock(drive_info* drive){ +// printf("get_lock()\n"); + int offs; + if (mode_sense(drive, 0x2A, 0, 256)) + { sperror ("GET_LOCK",drive->err); return (drive->err); } + offs=0; while (((drive->rd_buf[offs]) & 0x3F) != 0x2A) offs++; + if (drive->rd_buf[offs+6] & 0x02) drive->parms.status |= STATUS_LOCK; + else drive->parms.status &= (~STATUS_LOCK); +//#ifndef __PXCONTROL +// if (!drive->silent) printf("--- Disc %slocked\n",(drive->parms.status & STATUS_LOCK) ? "" : "UN") +//#endif + return 0; +} + +int set_lock(drive_info* drive){ +// printf("--- %slocking disc...\n",(drive->parms.status & STATUS_LOCK) ? "" : "UN"); + drive->cmd[0]=SPC_PREVENT_ALLOW_MEDIUM_REMOVAL; + drive->cmd[4]= (drive->parms.status & STATUS_LOCK) ? 1 : 0; + if ((drive->err=drive->cmd.transport(NONE, NULL, 0))) + { sperror ("SET_LOCK",drive->err); get_lock(drive); return (drive->err); } + get_lock(drive); + return 0; +} + +int play_audio_msf(drive_info* drive, msf beg, msf end){ + drive->cmd[0]=MMC_PLAY_AUDIO_MSF; + drive->cmd[3]=beg.m; + drive->cmd[4]=beg.s; + drive->cmd[5]=beg.f; + drive->cmd[6]=end.m; + drive->cmd[7]=end.s; + drive->cmd[8]=end.f; + if ((drive->err=drive->cmd.transport(NONE, NULL, 0))) + { sperror ("PLAY_AUDIO_MSF",drive->err); return (drive->err); } + return 0; +} + +int play_audio(drive_info* drive, int32_t beg, short int len){ + drive->cmd[0]=MMC_PLAY_AUDIO; + drive->cmd[2]=(beg>>24) & 0xFF; + drive->cmd[3]=(beg>>16) & 0xFF; + drive->cmd[4]=(beg>>8) & 0xFF; + drive->cmd[5]=beg & 0xFF; + drive->cmd[7]=(len>>8) & 0xFF; + drive->cmd[8]=len & 0xFF; + if ((drive->err=drive->cmd.transport(NONE, NULL, 0))) + { sperror ("PLAY_AUDIO",drive->err); return (drive->err); } + return 0; +} + +int seek(drive_info* drive, int32_t lba, unsigned char flags){ + drive->cmd[0]=MMC_SEEK; + drive->cmd[2]=(lba>>24) & 0xFF; + drive->cmd[3]=(lba>>16) & 0xFF; + drive->cmd[4]=(lba>>8) & 0xFF; + drive->cmd[5]=lba & 0xFF; + + drive->cmd[9]=flags; + if ((drive->err=drive->cmd.transport(NONE, NULL, 0))) + { sperror ("SEEK",drive->err); return (drive->err); } + return 0; +} + + +int read_cd(drive_info* drive, unsigned char *data, int32_t lba, int sector_count, unsigned char flags, unsigned char FUA) { +// int transfer_length = sector_count * 3072; + +// int sect_data = 2352; + int sect_data = 3072; + + int transfer_length = sector_count * sect_data; + if (sector_count<0) return -1; + +// printf("lba: %d, cnt: %d\n", lba,sector_count); + drive->cmd[0]=MMC_READ_CD; + drive->cmd[1]= FUA ? 0x08 : 0x00; + drive->cmd[2]=(lba>>24) & 0xFF; + drive->cmd[3]=(lba>>16) & 0xFF; + drive->cmd[4]=(lba>>8) & 0xFF; + drive->cmd[5]=lba & 0xFF; + drive->cmd[8]=sector_count; + drive->cmd[9]=flags; + if ((drive->err=drive->cmd.transport(READ, data, transfer_length))) + { sperror ("READ_CD",drive->err); + return (drive->err); } + return 0; +} + +int read(drive_info* drive, unsigned char *data, int32_t lba, int sector_count, unsigned char FUA) { +// int transfer_length = sector_count * 3072; + int transfer_length = sector_count * 2048; + if (sector_count<0) return -1; + drive->cmd[0]=MMC_READ; + drive->cmd[1]= FUA ? 0x08 : 0x00; + drive->cmd[2]=(lba>>24) & 0xFF; + drive->cmd[3]=(lba>>16) & 0xFF; + drive->cmd[4]=(lba>>8) & 0xFF; + drive->cmd[5]=lba & 0xFF; + drive->cmd[8]=sector_count; + if ((drive->err=drive->cmd.transport(READ, data, transfer_length))) + {sperror ("READ",drive->err); return (drive->err);} + return 0; +} + +int read_one_ecc_block(drive_info* drive, unsigned char *data, int32_t lba) { + drive->cmd[0] = MMC_READ; + drive->cmd[2] = (lba>>24) & 0xFF; + drive->cmd[3] = (lba>>16) & 0xFF; + drive->cmd[4] = (lba>>8) & 0xFF; + drive->cmd[5] = lba & 0xFF; + drive->cmd[8] = 0x10; + if ((drive->err=drive->cmd.transport(READ, data, 0x8000))) + {sperror ("READ_ONE_ECC_BLOCK",drive->err); return (drive->err);} +// if ((drive->err=drive->cmd.transport(READ,(void*)((unsigned char*)drive->rd_buf+(0x0001<<14)),0x34))) +// {sperror ("READ_ONE_ECC_BLOCK",drive->err); return (drive->err);} + return 0; +} + +int get_drive_serial_number(drive_info* drive) { +// char data[2048]; memset(data, 0, sizeof(data)); + unsigned int data_length; + unsigned int length; + get_configuration(drive, FEATURE_LOGICAL_UNIT_SERIAL_NUMBER, &data_length, NULL); + if (drive->err) return -1; + length = drive->rd_buf[11]; drive->rd_buf[12+length]=0; + if (data_length>8) strncpy(drive->serial, (char*)drive->rd_buf+12, 16); + else drive->serial[0]=0; + return 0; +} + +int get_buffer_capacity(drive_info* drive){ + int offs; + if (mode_sense(drive, MODE_PAGE_MM_CAP_STATUS, 0, 192)) return 1; + offs=0; while (((drive->rd_buf[offs]) & 0x3F) != 0x2A) offs++; + drive->buffer_size= ntoh16u(drive->rd_buf+offs+0x0C); +// printf("Buffer capacity: 0x%04X (%d)KB\n", drive->buffer_size, drive->buffer_size); + return 0; +} + +int get_wbuffer_capacity(drive_info* drive, uint32_t *btot, uint32_t *bfree){ + uint8_t td[0x0C]; memset(td,0,0x0C); + drive->cmd[0]=MMC_READ_BUFFER_CAPACITY; + drive->cmd[8]=0x0C; + if ((drive->err = drive->cmd.transport(READ,td,0x0C))) + {sperror ("READ_BUFFER_CAPACITY",drive->err); return (drive->err);} + + (*btot) = ntoh32u(td+4); + (*bfree) = ntoh32u(td+8); + return 0; +} + +int read_writer_info(drive_info* drive) +{ + if (!(drive->media.type & DISC_DVDminus)) { + strcpy(drive->media.writer, "n/a (only for DVD-R(W))"); + return 1; + } + char format=0x0D; + drive->media.writer[0]=0; + drive->rd_buf[8]=0; + drive->cmd[0]=MMC_READ_DVD_STRUCTURE; + drive->cmd[5]=0x02; + drive->cmd[7]=format; + drive->cmd[8]=8; + drive->cmd[9]=8; + if ((drive->err = drive->cmd.transport(READ,drive->rd_buf,2056)) || (!drive->rd_buf[8])) { + printf("Read Writer Info failed\n"); + return 1; + } + for (int i=0; i<0x3F; i++) { + if (!drive->rd_buf[8+i]) drive->rd_buf[8+i]=0x20; + } + strncpy(drive->media.writer, (char*)drive->rd_buf+8, 0x3F); + remove_double_spaces(drive->media.writer); +// remove_end_spaces(drive->media.writer); + return 0; +} + +int detect_mm_capabilities(drive_info* drive){ + char len; + int offs; + int i,j; + if (mode_sense(drive, MODE_PAGE_MM_CAP_STATUS, 0, 256)) return 1; + offs=0; while (((drive->rd_buf[offs]) & 0x3F) != 0x2A) offs++; + len=drive->rd_buf[offs+1]; + if (!drive->silent) printf("CD parameters page length: 0x%02X\n",len); + if (len >= 28) {drive->mmc=3;} + else if (len >= 24) {drive->mmc=2;} + else {drive->mmc=1;} + if (!drive->silent) for (i=offs; i<(offs+len+2);i+=8){ + for (j=0;j<8;j++) printf(" %02X",drive->rd_buf[i+j] & 0xFF); + printf("\n"); + } +// if (drive->mmc) drive->rd_capabilities|=DEVICE_CD_ROM; + if (drive->rd_buf[offs+2] & 0x01)drive->rd_capabilities|=DEVICE_CD_R; + if (drive->rd_buf[offs+2] & 0x02)drive->rd_capabilities|=DEVICE_CD_RW; + + if (drive->rd_buf[offs+3] & 0x01)drive->wr_capabilities|=DEVICE_CD_R; + if (drive->rd_buf[offs+3] & 0x02)drive->wr_capabilities|=DEVICE_CD_RW; + if (drive->rd_buf[offs+3] & 0x04)drive->capabilities|=CAP_TEST_WRITE_CD; + + if (drive->rd_buf[offs+4] & 0x01)drive->capabilities|=CAP_CD_AUDIO; + if (drive->rd_buf[offs+4] & 0x02)drive->capabilities|=CAP_COMPOSITE; + if (drive->rd_buf[offs+4] & 0x04)drive->capabilities|=CAP_DIGITAL_PORT_1; + if (drive->rd_buf[offs+4] & 0x08)drive->capabilities|=CAP_DIGITAL_PORT_2; + if (drive->rd_buf[offs+4] & 0x10)drive->capabilities|=CAP_MODE2_FORM1; + if (drive->rd_buf[offs+4] & 0x20)drive->capabilities|=CAP_MODE2_FORM2; + if (drive->rd_buf[offs+4] & 0x40)drive->capabilities|=CAP_MULTISESSION; + if (drive->rd_buf[offs+4] & 0x80)drive->capabilities|=CAP_BURN_FREE; + + if (drive->rd_buf[offs+5] & 0x01)drive->capabilities|=CAP_DAE; + if (drive->rd_buf[offs+5] & 0x02)drive->capabilities|=CAP_ACCURATE_STREAM; + if (drive->rd_buf[offs+5] & 0x10)drive->capabilities|=CAP_C2; + if (drive->rd_buf[offs+5] & 0x20)drive->capabilities|=CAP_ISRC; + if (drive->rd_buf[offs+5] & 0x40)drive->capabilities|=CAP_UPC; + if (drive->rd_buf[offs+5] & 0x80)drive->capabilities|=CAP_READ_BAR_CODE; + + if (drive->rd_buf[offs+6] & 0x01)drive->capabilities|=CAP_LOCK; + if (drive->rd_buf[offs+6] & 0x08)drive->capabilities|=CAP_EJECT; + + drive->loader_id = (drive->rd_buf[offs+6] >> 5) & 0x07; + + if (drive->rd_buf[offs+7] & 0x10)drive->capabilities|=CAP_SIDE_CHANGE; + + switch (drive->mmc) { + case 3: + drive->parms.write_speed_kb = ntoh16u(drive->rd_buf+36); + case 2: + if (drive->rd_buf[offs+2] & 0x08)drive->rd_capabilities|=DEVICE_DVD_ROM; + if (drive->rd_buf[offs+2] & 0x10)drive->rd_capabilities|=DEVICE_DVD_R; + if (drive->rd_buf[offs+2] & 0x20)drive->rd_capabilities|=DEVICE_DVD_RAM; + if (drive->rd_buf[offs+3] & 0x10)drive->wr_capabilities|=DEVICE_DVD_R; + if (drive->rd_buf[offs+3] & 0x20)drive->wr_capabilities|=DEVICE_DVD_RAM; + case 1: + drive->parms.max_read_speed_kb = ntoh16u(drive->rd_buf+offs+8); + drive->parms.read_speed_kb = ntoh16u(drive->rd_buf+offs+14); + drive->parms.max_write_speed_kb = ntoh16u(drive->rd_buf+offs+18); + if (drive->mmc < 3) + drive->parms.write_speed_kb = ntoh16u(drive->rd_buf+offs+20); + break; + } + if (!drive->silent) printf("Max speeds:\tR@%dKBps / W@%dKBps\nCurrent speeds:\tR@%dKBps / W@%dKBps\n", + drive->parms.max_read_speed_kb, drive->parms.max_write_speed_kb, + drive->parms.read_speed_kb, drive->parms.write_speed_kb); + + if (isPlextor(drive) && strncmp(drive->dev,"CD-R", 4)) + drive->capabilities|=CAP_TEST_WRITE_DVD_PLUS; + return 0; +} + +int convert_to_ID (drive_info* drive) { +// printf("convert_to_ID() : %s %s\n", drive->ven, drive->dev); + if (!strncmp(drive->ven,"PLEXTOR ",8)) { + drive->ven_ID=DEV_PLEXTOR; + if(!strncmp(drive->dev,"CD-R PX-W4824A",16)) + drive->dev_ID=PLEXTOR_4824; + else + if(!strncmp(drive->dev,"CD-R PX-W5224A",16)) + drive->dev_ID=PLEXTOR_5224; + else + if(!strncmp(drive->dev,"CD-R PREMIUM2",15)) + drive->dev_ID=PLEXTOR_PREMIUM2; + else + if(!strncmp(drive->dev,"CD-R PREMIUM",14)) + drive->dev_ID=PLEXTOR_PREMIUM; + else + if(!strncmp(drive->dev,"DVDR PX-708A2",15)) + drive->dev_ID=PLEXTOR_708A2; + else + if(!strncmp(drive->dev,"DVDR PX-712A",14)) + drive->dev_ID=PLEXTOR_712; + else + if(!strncmp(drive->dev,"DVDR PX-714A",14)) + drive->dev_ID=PLEXTOR_716; + else + if(!strncmp(drive->dev,"DVDR PX-716A ",15)) + drive->dev_ID=PLEXTOR_716; + else + if(!strncmp(drive->dev,"DVDR PX-716AL",15)) + drive->dev_ID=PLEXTOR_716AL; + else + if(!strncmp(drive->dev,"DVDR PX-755A",14)) + drive->dev_ID=PLEXTOR_760; + else + if(!strncmp(drive->dev,"DVDR PX-760A",14)) + drive->dev_ID=PLEXTOR_760; + else + if(!strncmp(drive->dev,"CD-R ",8)) + drive->dev_ID=PLEXTOR_OLD; + else + { drive->ven_ID = DEV_GENERIC; drive->dev_ID = 0; } + } + else if (!strncmp(drive->ven,"YAMAHA ",8)) { + drive->ven_ID=DEV_YAMAHA; + if(!strncmp(drive->dev,"CRW-F1",6)) + drive->dev_ID=YAMAHA_F1; + else + drive->dev_ID=YAMAHA_OLD; + } + + else { drive->ven_ID = DEV_GENERIC; drive->dev_ID = 0; } + +// printf("convert_to_ID() : %04X:%04X\n", drive->ven_ID, drive->dev_ID); + return 0; +} + + //----------------// + // PX-755 AUTH // + //----------------// + +int plextor_px755_do_auth(drive_info* dev) +{ + if (!isPlextorLockPresent(dev)) + { if (!dev->silent) printf("Plextor dev is older than PX-755, auth not needed\n"); return 0; } +// cmd_px755_clear_auth_status(); + plextor_px755_get_auth_code(dev, dev->rd_buf); + plextor_px755_calc_auth_code(dev, dev->rd_buf); + if (plextor_px755_send_auth_code(dev, dev->rd_buf)) { + printf(" _______________________________________________________ \n"); + printf("| |\n"); + printf("| WARNING!!! Detected locked PX-755/PX-760 |\n"); + printf("| or Premium-II |\n"); + printf("| Device has 'protected' commands |\n"); + printf("| you'll not get full fucntionality of this drive |\n"); + printf("|_______________________________________________________|\n"); + return 1; + } else { + if (!dev->silent) printf("PX-755/PX-760/Premium-II auth successful:)\n"); + return 0; + } +} + +int plextor_px755_get_auth_code(drive_info* dev, unsigned char* auth_code) +{ + dev->cmd[0] = PLEXTOR_GET_AUTH; + dev->cmd[10]= 0x10; + if ((dev->err=dev->cmd.transport(READ,auth_code,16) )) + { if (!dev->silent) sperror ("PLEXTOR_PX755_GET_AUTH_CODE",dev->err); return dev->err;} + if (!dev->silent) { + printf("** Get PX755 auth: "); + for (int i=0; i<16; i++) printf("0x%02X ",dev->rd_buf[i]&0xFF); printf("\n"); + } + return 0; +} + +int plextor_px755_send_auth_code(drive_info* dev, unsigned char* auth_code) +{ + dev->cmd[0] = PLEXTOR_SEND_AUTH; + dev->cmd[1] = 0x01; + dev->cmd[2] = 0x01; + dev->cmd[10]= 0x10; + if ((dev->err=dev->cmd.transport(WRITE,auth_code,16) )) + { if (!dev->silent) sperror ("PLEXTOR_PX755_SEND_AUTH_CODE",dev->err); return dev->err;} + return 0; +} + +/* +int scan_plextor::cmd_px755_clear_auth_status(drive_info* dev) +{ + dev->cmd[0] = PLEXTOR_SEND_AUTH; + dev->cmd[1] = 0x01; + dev->cmd[2] = 0x00; + dev->cmd[10]= 0x10; + if ((dev->err=dev->cmd.transport(NONE,NULL,0) )) + { if (!dev->silent) sperror ("PLEXTOR_PX755_CLEAR_AUTH_STATUS",dev->err); return dev->err;} + return 0; +} +*/ + +int plextor_px755_calc_auth_code(drive_info* dev, unsigned char* auth_code) +{ + return 0; +} + diff --git a/lib/qpxtransport/qpx_mmc_css.cpp b/lib/qpxtransport/qpx_mmc_css.cpp new file mode 100644 index 0000000..a821302 --- /dev/null +++ b/lib/qpxtransport/qpx_mmc_css.cpp @@ -0,0 +1,2029 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Gennady "ShultZ" Kozlov + * + * 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. + * + * DVD authentication and descrambling based on functions from libdvdcss + * + */ + +/***************************************************************************** + * css.c: Functions for DVD authentication and descrambling + ***************************************************************************** + * Copyright (C) 1999-2003 VideoLAN + * $Id: css.c 20629 2006-11-03 12:25:56Z diego $ + * + * Authors: Stéphane Borel + * HÃ¥kan Hjort + * + * based on: + * - css-auth by Derek Fawcus + * - DVD CSS ioctls example program by Andrew T. Veliath + * - The Divide and conquer attack by Frank A. Stevenson + * (see http://www-2.cs.cmu.edu/~dst/DeCSS/FrankStevenson/index.html) + * - DeCSSPlus by Ethan Hawke + * - DecVOB + * see http://www.lemuria.org/DeCSS/ by Tom Vogt for more information. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#include +#include +#include +#include + +#include +//#include + +//#include "transport.hxx" +#include + +#include "csstables.h" + +#define DVD_DISCKEY_SIZE 2048 + +// device commands +int report_key(drive_info* drive, unsigned char key_class, unsigned char key_format, int len, unsigned int lba = 0); +int read_disc_key(drive_info* drive, unsigned char DK[DVD_DISCKEY_SIZE]); + +int css_report_agid(drive_info* ); +int cprm_report_agid(drive_info* ); +int css_invalidate_agid(drive_info* ); +int css_report_challenge(drive_info* ); +int css_send_challenge(drive_info* ); +int css_report_key1(drive_info* ); +int css_send_key2(drive_info* ); +int css_report_title_key(drive_info* drive, int lba, unsigned char* key); +int css_report_asf(drive_info* ); + +// some functions from libdvdcss + +//int css_disckey( drive_info* drive); +//int css_title ( drive_info* drive, int lba ); +int css_titlekey( drive_info* drive, int lba, dvd_key_t p_title_key ); +int css_unscramble( dvd_key_t p_key, unsigned char *p_sec ); + +int css_get_bus_key(drive_info* drive); + +static void css_CryptKey( int i_key_type, int i_variant, + unsigned char const *p_challenge, unsigned char *p_key ); +static void css_DecryptKey( unsigned char invert, unsigned char const *p_key, + unsigned char const *p_crypted, unsigned char *p_result ); + +static int css_DecryptDiscKey ( drive_info*, unsigned char const *, dvd_key_t ); +static int css_CrackDiscKey ( drive_info*, unsigned char * ); + +static int css_RecoverTitleKey( int i_start, unsigned char const *p_crypted, + unsigned char const *p_decrypted, + unsigned char const *p_sector_seed, unsigned char *p_key ); +static void css_DecryptTitleKey( dvd_key_t p_disc_key, dvd_key_t p_titlekey ); +static int css_CrackTitleKey( drive_info*, int i_pos, int i_len, dvd_key_t p_titlekey ); +static int css_AttackPattern( unsigned char const p_sec[ DVDCSS_BLOCK_SIZE ], int i_pos, unsigned char *p_key ); + +static void css_printkey (char *, unsigned char const * ); + +int i_tries = 0; +int i_success = 0; + + +/** + * \brief Seek in the disc and change the current key if requested. + * + * \param dvdcss a \e libdvdcss instance. + * \param i_blocks an absolute block offset to seek to. + * \param i_flags #DVDCSS_NOFLAGS, optionally ored with one of #DVDCSS_SEEK_KEY + * or #DVDCSS_SEEK_MPEG. + * \return the new position in blocks, or a negative value in case an error + * happened. + * + * This function seeks to the requested position, in logical blocks. + * + * You typically set \p i_flags to #DVDCSS_NOFLAGS when seeking in a .IFO. + * + * If #DVDCSS_SEEK_MPEG is specified in \p i_flags and if \e libdvdcss finds it + * reasonable to do so (ie, if the dvdcss method is not "title"), the current + * title key will be checked and a new one will be calculated if necessary. + * This flag is typically used when reading data from a VOB. + * + * If #DVDCSS_SEEK_KEY is specified, the title key will be always checked, + * even with the "title" method. This is equivalent to using the now + * deprecated dvdcss_title() call. This flag is typically used when seeking + * in a new title. + */ +int seek_dvd ( drive_info* drive, int lba, int flags ) +{ + /* title cracking method is too slow to be used at each seek */ + if( ( ( flags & DVDCSS_SEEK_MPEG ) + && ( drive->media.dvdcss.method != DVDCSS_METHOD_TITLE ) ) + || ( flags & DVDCSS_SEEK_KEY ) ) + { + /* check the title key */ + if( css_title( drive, lba ) ) + { + return -1; + } + } + + return seek( drive, lba ); +} + +/** + * \brief Read from the disc and decrypt data if requested. + * + * \param dvdcss a \e libdvdcss instance. + * \param p_buffer a buffer that will contain the data read from the disc. + * \param i_blocks the amount of blocks to read. + * \param i_flags #DVDCSS_NOFLAGS, optionally ored with #DVDCSS_READ_DECRYPT. + * \return the amount of blocks read, or a negative value in case an + * error happened. + * + * This function reads \p i_blocks logical blocks from the DVD. + * + * You typically set \p i_flags to #DVDCSS_NOFLAGS when reading data from a + * .IFO file on the DVD. + * + * If #DVDCSS_READ_DECRYPT is specified in \p i_flags, dvdcss_read() will + * automatically decrypt scrambled sectors. This flag is typically used when + * reading data from a .VOB file on the DVD. It has no effect on unscrambled + * discs or unscrambled sectors, and can be safely used on those. + * + * \warning dvdcss_read() expects to be able to write \p i_blocks * + * #DVDCSS_BLOCK_SIZE bytes in \p p_buffer. + */ + +int read_dvd(drive_info* drive, unsigned char* data, int lba, int sector_count, int flags) + +// LIBDVDCSS_EXPORT int dvdcss_read ( dvdcss_t dvdcss, void *p_buffer, +// int i_blocks, +// int i_flags ) +{ + unsigned char* p_buffer; + int i_ret=-1, i_index; + +// if (flags & DVDCSS_READ_DECRYPT) { +// seek_dvd(drive,lba, DVDCSS_SEEK_KEY); +// } + + if ( !read( drive, data, lba, sector_count )) { + //i_ret = sector_count * DVDCSS_BLOCK_SIZE; + i_ret = sector_count; + } else { + if (drive->err == 0x52100) i_ret = 0; + } + + if( i_ret <= 0 + || drive->media.dvdcss.protection != 0x01 + || !(flags & DVDCSS_READ_DECRYPT) ) + { + return i_ret; + } + + p_buffer = data; + + if( ! memcmp( drive->media.dvdcss.TK, "\0\0\0\0\0", 5 ) ) + { + /* For what we believe is an unencrypted title, + * check that there are no encrypted blocks */ + for( i_index = i_ret; i_index; i_index-- ) + { + if( p_buffer[0x14] & 0x30 ) + { + printf( "no key but found encrypted block\n" ); + /* Only return the initial range of unscrambled blocks? */ + /* or fail completely? return 0; */ + return -1; + break; + } + p_buffer = p_buffer + DVDCSS_BLOCK_SIZE; + } + } + else + { + /* Decrypt the blocks we managed to read */ + for( i_index = i_ret; i_index; i_index-- ) + { +// printf("Decrypting data...\n"); + css_unscramble( drive->media.dvdcss.TK, p_buffer ); + p_buffer[0x14] &= 0x8f; + p_buffer = p_buffer + DVDCSS_BLOCK_SIZE; + } + } + + return i_ret; +} + +// ********************************* + + +int read_disc_regions(drive_info* drive) +{ +//#warning "read_disc_regions()" + int len=8; +// unsigned char enc; +// unsigned char regmask; + int i; + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[7] = 0x01; + drive->cmd[8] = len >> 8; // -| + drive->cmd[9] = len & 0xFF; // -- transfer length + drive->cmd[11] = 0; + if (( drive->err = drive->cmd.transport(READ,drive->rd_buf,len) )) + { if (!drive->silent) sperror ("READ_DISC_REGIONS",drive->err); return drive->err; } +#if 0 + if (!drive->silent) { + printf("READ_DISC_REGIONS data: "); + for(i=0; ird_buf[i] & 0xFF); + printf("\n"); + } +#endif + drive->media.dvdcss.protection = drive->rd_buf[4]; + drive->media.dvdcss.regmask = drive->rd_buf[5]; + + if (!drive->media.dvdcss.protection) { + // printf("DVD is NOT protected\n"); + return 0; + } else { + /* + switch (drive->media.dvdcss.protection) { + case 0x01: + printf("DVD is CSS-protected\n"); + break; + case 0x02: + printf("DVD is CPRM-protected\n"); + break; + default: + printf("Unknown DVD protection shceme!\n"); + break; + }*/ + +// printf("Disc regions : "); + if (drive->media.dvdcss.regmask != 0xFF) { + for (i=0; i<8; i++) + if (!((drive->media.dvdcss.regmask >> i) & 1)) + { + // printf("%d",i+1); + drive->rpc.region = i+1; + } +// printf("\n"); +// } else { +// printf("Invalid region mask!\n"); + } + } + + return 0; +} + + +int read_disc_key(drive_info* drive, unsigned char DK[DVD_DISCKEY_SIZE]) +{ + int len = 2052; // 4 (header) + 2048 (key) + if (!(drive->rd_capabilities & DEVICE_DVD) || !(drive->capabilities & CAP_DVD_CSS)) return -1; + + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; + drive->cmd[7] = 0x02; + drive->cmd[8] = len >> 8; // -| + drive->cmd[9] = len & 0xFF; // -- transfer length + drive->cmd[10] = drive->media.dvdcss.agid & 0xC0; + drive->cmd[11] = 0; + if (( drive->err = drive->cmd.transport(READ,drive->rd_buf,len) )) + { if (!drive->silent) sperror ("READ_DISC_KEY",drive->err); return drive->err; } + + memcpy(DK, drive->rd_buf+4, 2048); + return 0; +} + +int report_key(drive_info* drive, unsigned char key_class, unsigned char key_format, int len, unsigned int lba) +{ + if (!(drive->rd_capabilities & DEVICE_DVD) || !(drive->capabilities & CAP_DVD_CSS)) return -1; + + drive->cmd[0] = MMC_REPORT_KEY; + + drive->cmd[2] = (lba >> 24) & 0xFF; + drive->cmd[3] = (lba >> 16) & 0xFF; + drive->cmd[4] = (lba >> 8) & 0xFF; + drive->cmd[5] = lba & 0xFF; + + drive->cmd[7] = key_class; + drive->cmd[8] = len>>8; + drive->cmd[9] = len & 0xFF; + drive->cmd[10] = (drive->media.dvdcss.agid & 0xC0) | (key_format & 0x3F); + drive->cmd[11] = 0; + if (( drive->err = drive->cmd.transport(READ,drive->rd_buf,len) )) + { if (!drive->silent) sperror ("MMC REPORT KEY",drive->err); return drive->err; } + return 0; +} + +int get_rpc_state(drive_info* drive){ +// int len=8; + int i; + unsigned char regmask; + unsigned char t;//,vl,ul; + unsigned char sh; + + if (!(drive->rd_capabilities & DEVICE_DVD) || !(drive->capabilities & CAP_DVD_CSS)) + {drive->rpc.phase = 0; return -1;} +/* + drive->cmd[0] = MMC_REPORT_KEY; + drive->cmd[7] = 0; // key class = 0 : DVD CSS/CPRM + drive->cmd[8] = len>>8; + drive->cmd[9] = len & 0xFF; + drive->cmd[10] = 0x08; // key format = 8 : RPC info + drive->cmd[11] = 0; +*/ + report_key(drive, 0, 0x08, 8); + if (drive->err) + { + if (drive->err == 0x52400) { + drive->rpc.phase = 1; + return 0; + } else { + if (!drive->silent) sperror ("READ_RPC_STATE",drive->err); + drive->rpc.phase = 0; + return drive->err; + } + } +/* printf("MMC_REPORT_KEY data: "); + for(i=0; ird_buf[i] & 0xFF); + printf("\n");*/ + if (ntoh16(drive->rd_buf) < 6) return 1; + + drive->rpc.ch_u = drive->rd_buf[4] & 0x07; + drive->rpc.ch_v = (drive->rd_buf[4] >> 3) & 0x07; + t = (drive->rd_buf[4] >> 6) & 0x03; + regmask = drive->rd_buf[5]; + sh = drive->rd_buf[6]; + + drive->rpc.phase = 2; +// printf("\n** Unit is RPC-II\n"); +// printf("Current region : "); + if (regmask != 0xFF) { + for (i=0; i<8; i++) + if (!((regmask >> i) & 1)) { +// printf("%d",i+1); + drive->rpc.region = i+1; + } +// printf("\n"); + } else { +// printf("does not set\n"); + drive->rpc.region = 0; + } +// printf("User changes left : %d\n",drive->rpc.ch_u); +// printf("Vendor resets left : %d\n",drive->rpc.ch_v); + return 0; +} + +int css_invalidate_agid(drive_info* drive) +{ + if (!(drive->capabilities & CAP_DVD_CSS)) return -1; + + drive->cmd[0] = MMC_REPORT_KEY; + drive->cmd[7] = 0; + drive->cmd[8] = 0; + drive->cmd[9] = 0; + drive->cmd[10] = (drive->media.dvdcss.agid & 0xC0) | 0x3F; + drive->cmd[11] = 0; + if (( drive->err = drive->cmd.transport(NONE, NULL, 0) )) + { if (!drive->silent) sperror ("MMC REPORT KEY (INVALIDATE AGID)",drive->err); return drive->err; } + return 0; +} + +int css_report_agid(drive_info* drive) +{ + if (!(drive->capabilities & CAP_DVD_CSS)) return -1; + + report_key(drive, 0, 0x00, 8); + if (drive->err) return -1; + drive->media.dvdcss.agid = drive->rd_buf[7] & 0xC0; + printf("CSS: AGID=%x\n",drive->media.dvdcss.agid >> 6); + return 0; +} + +int cprm_report_agid(drive_info* drive) +{ + if (!(drive->capabilities & CAP_DVD_CSS)) return -1; + + report_key(drive, 0, 0x11, 8); + if (drive->err) return -1; + drive->media.dvdcss.agid = drive->rd_buf[7] & 0xC0; + return 0; +} + +int css_report_challenge(drive_info* drive) +{ + if (!(drive->capabilities & CAP_DVD_CSS)) return -1; + + report_key(drive, 0, 0x01, 16); + if (drive->err) return -1; + + for( int i = 0 ; i < (2*DVD_KEY_SIZE) ; i++ ) + { + drive->media.dvdcss.CK[i] = drive->rd_buf[13-i]; + } + printf("Report Challenge: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + drive->media.dvdcss.CK[0],drive->media.dvdcss.CK[1], + drive->media.dvdcss.CK[2],drive->media.dvdcss.CK[3], + drive->media.dvdcss.CK[4],drive->media.dvdcss.CK[5], + drive->media.dvdcss.CK[6],drive->media.dvdcss.CK[7], + drive->media.dvdcss.CK[8],drive->media.dvdcss.CK[9]); + return 0; +} + +int css_send_challenge(drive_info* drive) +{ + int len = 16; + if (!(drive->capabilities & CAP_DVD_CSS)) return -1; + printf("Send Challenge: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + drive->media.dvdcss.CK[0],drive->media.dvdcss.CK[1], + drive->media.dvdcss.CK[2],drive->media.dvdcss.CK[3], + drive->media.dvdcss.CK[4],drive->media.dvdcss.CK[5], + drive->media.dvdcss.CK[6],drive->media.dvdcss.CK[7], + drive->media.dvdcss.CK[8],drive->media.dvdcss.CK[9]); + + drive->cmd[0] = MMC_SEND_KEY; + drive->cmd[7] = 0; // key class = 0 : DVD CSS/CPRM + drive->cmd[8] = len>>8; + drive->cmd[9] = len & 0xFF; + drive->cmd[10] = (drive->media.dvdcss.agid & 0xC0) | 0x01; // key format = 1 : Challenge key + drive->cmd[11] = 0; + + drive->rd_buf[0] = 0x00; + drive->rd_buf[1] = 0x0E; + drive->rd_buf[2] = 0; + drive->rd_buf[3] = 0; + + for( int i = 0 ; i < (2*DVD_KEY_SIZE) ; i++ ) + { + drive->rd_buf[13-i] = drive->media.dvdcss.CK[i]; + } + drive->rd_buf[14]= 0; + drive->rd_buf[15]= 0; + + if (( drive->err = drive->cmd.transport(WRITE,drive->rd_buf,len) )) + { if (!drive->silent) sperror ("MMC SEND KEY (CHALLENGE)",drive->err); return drive->err; } + +// printf("Challenge KEY: "); +// for (int i=0; i<10; i++) printf("%02X ",drive->media.dvdcss.CK[i]); +// printf("\n"); + return 0; +} + +int css_report_key1(drive_info* drive) +{ + if (!(drive->capabilities & CAP_DVD_CSS)) return -1; + + report_key(drive, 0, 0x02, 12); + if (drive->err) return drive->err; + + for( int i = 0 ; i < DVD_KEY_SIZE ; i++ ) + { + drive->media.dvdcss.K1[i] = drive->rd_buf[8-i]; + } + printf("Report KEY1: %02X:%02X:%02X:%02X:%02X\n", + drive->media.dvdcss.K1[0], + drive->media.dvdcss.K1[1], + drive->media.dvdcss.K1[2], + drive->media.dvdcss.K1[3], + drive->media.dvdcss.K1[4]); + return 0; +} + +int css_send_key2(drive_info* drive) +{ + int len = 12; + if (!(drive->capabilities & CAP_DVD_CSS)) return -1; + + printf("Send KEY2: %02X:%02X:%02X:%02X:%02X\n", + drive->media.dvdcss.K2[0], + drive->media.dvdcss.K2[1], + drive->media.dvdcss.K2[2], + drive->media.dvdcss.K2[3], + drive->media.dvdcss.K2[4]); + + drive->cmd[0] = MMC_SEND_KEY; + drive->cmd[7] = 0; // key class = 0 : DVD CSS/CPRM + drive->cmd[8] = len>>8; + drive->cmd[9] = len & 0xFF; + drive->cmd[10] = (drive->media.dvdcss.agid & 0xC0) | 0x03; // key format = 2 : KEY 2 + drive->cmd[11] = 0; + + drive->rd_buf[0] = 0x00; + drive->rd_buf[1] = 0x0A; + drive->rd_buf[2] = 0; + drive->rd_buf[3] = 0; + for( int i = 0 ; i < DVD_KEY_SIZE ; i++ ) + { + drive->rd_buf[8-i] = drive->media.dvdcss.K2[i]; + } + drive->rd_buf[9] = 0; + drive->rd_buf[10]= 0; + drive->rd_buf[11]= 0; + + if (( drive->err = drive->cmd.transport(WRITE,drive->rd_buf,len) )) + { if (!drive->silent) sperror ("MMC SEND KEY (KEY2)",drive->err); return drive->err; } + +// printf("Challenge KEY: "); +// for (int i=0; i<10; i++) printf("%02X ",drive->media.dvdcss.CK[i]); +// printf("\n"); + return 0; +} + +int css_report_title_key(drive_info* drive, int lba, unsigned char* key) +{ + if (!(drive->capabilities & CAP_DVD_CSS)) return -1; + + report_key(drive, 0, 0x04, 12, lba); + if (drive->err) return drive->err; + + //memcpy((void*)drive->media.dvdcss.TK, drive->rd_buf+5, 5); + memcpy(key, drive->rd_buf+5, 5); + + printf("Report Title KEY: "); + //for (int i=0; i<5; i++) printf("%02X ",drive->media.dvdcss.TK[i]); + for (int i=0; i<5; i++) printf("%02X ", key[i]); + printf("\n"); + return 0; +} + +int css_report_asf(drive_info* drive) +{ + if (!(drive->capabilities & CAP_DVD_CSS)) return -1; + + report_key(drive, 0, 0x05, 8); + if (drive->err) return -1; + drive->media.dvdcss.asf = drive->rd_buf[7] & 0x01; + if (drive->media.dvdcss.asf) return 0; else return 1; +} + +/* + * + * functions from libdvdcss (modified) + * + */ + +/***************************************************************************** + * _dvdcss_disckey: get disc key. + ***************************************************************************** + * This function should only be called if DVD ioctls are present. + * It will set dvdcss->i_method = DVDCSS_METHOD_TITLE if it fails to find + * a valid disc key. + * Two decryption methods are offered: + * -disc key hash crack, + * -decryption with player keys if they are available. + *****************************************************************************/ + +int css_disckey( drive_info* drive ) +{ + unsigned char p_buffer[ DVD_DISCKEY_SIZE ]; + unsigned char p_disc_key[DVD_KEY_SIZE]; + int i; + +#warning clean title keys + + if( css_get_bus_key( drive ) ) + { + return -1; + } + + /* Get encrypted disc key */ + if( read_disc_key( drive, p_buffer ) ) + { + printf( "CSS: ReadDiscKey failed\n" ); + return -1; + } + + /* This should have invaidated the AGID and got us ASF=1. */ + if( css_report_asf( drive ) !=0 ) + { + /* Region mismatch (or region not set) is the most likely source. */ + printf( "CSS: ASF not 1 after reading disc key (region mismatch?)\n" ); + css_invalidate_agid( drive ); + return -1; + } + + /* Shuffle disc key using bus key */ + for( i = 0 ; i < DVD_DISCKEY_SIZE ; i++ ) + { + p_buffer[ i ] ^= drive->media.dvdcss.BK[ 4 - (i % DVD_KEY_SIZE) ]; + } + +#if 1 + /* Decrypt disc key */ + switch( drive->media.dvdcss.method ) + { + case DVDCSS_METHOD_KEY: + + /* Decrypt disc key with player key. */ + css_printkey( (char*)("CSS: decrypting disc key "), p_buffer ); + if( ! css_DecryptDiscKey( drive, p_buffer, p_disc_key ) ) + { + css_printkey( (char*)("CSS: decrypted disc key is "), p_disc_key ); + break; + } + printf( "CSS: failed to decrypt the disc key, " + "faulty drive/kernel? " + "cracking title keys instead\n" ); + + /* Fallback, but not to DISC as the disc key might be faulty */ + drive->media.dvdcss.method = DVDCSS_METHOD_TITLE; + break; + + case DVDCSS_METHOD_DISC: + + /* Crack Disc key to be able to use it */ + memcpy( p_disc_key, p_buffer, DVD_KEY_SIZE ); + css_printkey( (char*)("CSS: cracking disc key "), p_disc_key ); + if( ! css_CrackDiscKey( drive, p_disc_key ) ) + { + css_printkey( (char*)("CSS: cracked disc key is "), p_disc_key ); + break; + } + printf( "CSS: failed to crack the disc key\n" ); + memset( drive->media.dvdcss.DK, 0, DVD_KEY_SIZE ); + drive->media.dvdcss.method = DVDCSS_METHOD_TITLE; + break; + + default: + + printf( "CSS: disc key needs not be decrypted\n" ); + memset( drive->media.dvdcss.DK, 0, DVD_KEY_SIZE ); + break; + } + + memcpy( drive->media.dvdcss.DK, p_disc_key, DVD_KEY_SIZE ); +#endif + return 0; +} + +/***************************************************************************** + * _dvdcss_unscramble: does the actual descrambling of data + ***************************************************************************** + * sec : sector to unscramble + * key : title key for this sector + *****************************************************************************/ +int css_unscramble( dvd_key_t p_key, unsigned char *p_sec ) +{ + unsigned int i_t1, i_t2, i_t3, i_t4, i_t5, i_t6; + unsigned char *p_end = p_sec + DVDCSS_BLOCK_SIZE; + + /* PES_scrambling_control */ + if( !(p_sec[0x14] & 0x30) ) + { + return 0; + } + + i_t1 = (p_key[0] ^ p_sec[0x54]) | 0x100; + i_t2 = p_key[1] ^ p_sec[0x55]; + i_t3 = (p_key[2] | (p_key[3] << 8) | + (p_key[4] << 16)) ^ (p_sec[0x56] | + (p_sec[0x57] << 8) | (p_sec[0x58] << 16)); + i_t4 = i_t3 & 7; + i_t3 = i_t3 * 2 + 8 - i_t4; + p_sec += 0x80; + i_t5 = 0; + + while( p_sec != p_end ) + { + i_t4 = p_css_tab2[i_t2] ^ p_css_tab3[i_t1]; + i_t2 = i_t1>>1; + i_t1 = ( ( i_t1 & 1 ) << 8 ) ^ i_t4; + i_t4 = p_css_tab5[i_t4]; + i_t6 = ((((((( i_t3 >> 3 ) ^ i_t3 ) >> 1 ) ^ + i_t3 ) >> 8 ) ^ i_t3 ) >> 5 ) & 0xff; + i_t3 = (i_t3 << 8 ) | i_t6; + i_t6 = p_css_tab4[i_t6]; + i_t5 += i_t6 + i_t4; + *p_sec = p_css_tab1[*p_sec] ^ ( i_t5 & 0xff ); + p_sec++; + i_t5 >>= 8; + } + + return 0; +} + + +/***************************************************************************** + * GetBusKey : Go through the CSS Authentication process + ***************************************************************************** + * It simulates the mutual authentication between logical unit and host, + * and stops when a session key (called bus key) has been established. + * Always do the full auth sequence. Some drives seem to lie and always + * respond with ASF=1. For instance the old DVD roms on Compaq Armada says + * that ASF=1 from the start and then later fail with a 'read of scrambled + * block without authentication' error. + *****************************************************************************/ + +int css_get_bus_key(drive_info* drive) +{ +// unsigned char p_buffer[10]; +// unsigned char p_challenge[2*DVD_KEY_SIZE]; +// dvd_key_t p_key1; +// dvd_key_t p_key2; +// dvd_key_t p_key_check; + unsigned char p_key_check[DVD_KEY_SIZE]; + unsigned char i_variant = 0; + int i_ret = -1; + int i; + + printf( "CSS: requesting AGID..\n" ); + i_ret = css_report_agid( drive ); + + /* We might have to reset hung authentication processes in the drive + * by invalidating the corresponding AGID'. As long as we haven't got + * an AGID, invalidate one (in sequence) and try again. */ + for( i = 0; i_ret == -1 && i < 4 ; ++i ) + { + printf( "CSS: ReportAgid failed, " + "invalidating AGID %d\n", i ); + + /* This is really _not good_, should be handled by the OS. + * Invalidating an AGID could make another process fail somewhere + * in its authentication process. */ + drive->media.dvdcss.agid = i; + i_ret = css_invalidate_agid( drive ); + + printf( "CSS: requesting AGID\n" ); + i_ret = css_report_agid( drive ); + } + + /* Unable to authenticate without AGID */ + if( i_ret == -1 ) + { + printf( "CSS: ReportAgid failed, fatal\n" ); + return -1; + } + + /* Setup a challenge, any values should work */ + for( i = 0 ; i < 10; ++i ) + { + drive->media.dvdcss.CK[i] = i; + } + + /* Get challenge from host */ +// for( i = 0 ; i < 10 ; ++i ) +// { +// p_buffer[9-i] = p_challenge[i]; +// } + + /* Send challenge to LU */ + if( css_send_challenge( drive ) ) + { + printf( "CSS: SendChallenge failed\n" ); + css_invalidate_agid( drive ); + return -1; + } + + /* Get key1 from LU */ + if( css_report_key1( drive ) ) + { + printf( "CSS: ReportKey1 failed\n" ); + css_invalidate_agid( drive ); + return -1; + } + + /* Send key1 to host */ +// for( i = 0 ; i < DVD_KEY_SIZE ; i++ ) +// { +// p_key1[i] = p_buffer[4-i]; +// } + + for( i = 0 ; i < 32 ; ++i ) + { + css_CryptKey( 0, i, drive->media.dvdcss.CK, p_key_check ); + + if( memcmp( p_key_check, drive->media.dvdcss.K1, DVD_KEY_SIZE ) == 0 ) + { + printf( "CSS: drive authenticated, using variant %d\n", i ); + i_variant = i; + break; + } + } + + if( i == 32) + { + printf( "CSS: drive would not authenticate\n" ); + css_invalidate_agid( drive ); + return -1; + } + + /* Get challenge from LU */ + if( css_report_challenge( drive)) + { + printf( "CSS: ReportKeyChallenge failed\n" ); + css_invalidate_agid( drive ); + return -1; + } + + /* Send challenge to host */ +// for( i = 0 ; i < 10 ; ++i ) +// { +// p_challenge[i] = p_buffer[9-i]; +// } + + css_CryptKey( 1, i_variant, drive->media.dvdcss.CK, drive->media.dvdcss.K2 ); + + /* Get key2 from host */ +// for( i = 0 ; i < DVD_KEY_SIZE ; ++i ) +// { +// p_buffer[4-i] = p_key2[i]; +// } + + /* Send key2 to LU */ + if( css_send_key2( drive ) ) + { + printf( "CSS: SendKey2 failed\n" ); + css_invalidate_agid( drive ); + return -1; + } + + /* The drive has accepted us as authentic. */ + printf( "CSS: authentication established\n" ); + + memcpy( drive->media.dvdcss.CK, drive->media.dvdcss.K1, DVD_KEY_SIZE ); + memcpy( drive->media.dvdcss.CK + DVD_KEY_SIZE, drive->media.dvdcss.K2, DVD_KEY_SIZE ); + +// css_CryptKey( 2, i_variant, p_challenge, dvdcss->css.p_bus_key ); + css_CryptKey( 2, i_variant, drive->media.dvdcss.CK, drive->media.dvdcss.BK ); + + return 0; +} + +/***************************************************************************** + * CryptKey : shuffles bits and unencrypt keys. + ***************************************************************************** + * Used during authentication and disc key negociation in GetBusKey. + * i_key_type : 0->key1, 1->key2, 2->buskey. + * i_variant : between 0 and 31. + *****************************************************************************/ +static void css_CryptKey( int i_key_type, int i_variant, + unsigned char const *p_challenge, unsigned char *p_key ) +{ + /* Permutation table for challenge */ + unsigned char pp_perm_challenge[3][10] = + { { 1, 3, 0, 7, 5, 2, 9, 6, 4, 8 }, + { 6, 1, 9, 3, 8, 5, 7, 4, 0, 2 }, + { 4, 0, 3, 5, 7, 2, 8, 6, 1, 9 } }; + + /* Permutation table for variant table for key2 and buskey */ + unsigned char pp_perm_variant[2][32] = + { { 0x0a, 0x08, 0x0e, 0x0c, 0x0b, 0x09, 0x0f, 0x0d, + 0x1a, 0x18, 0x1e, 0x1c, 0x1b, 0x19, 0x1f, 0x1d, + 0x02, 0x00, 0x06, 0x04, 0x03, 0x01, 0x07, 0x05, + 0x12, 0x10, 0x16, 0x14, 0x13, 0x11, 0x17, 0x15 }, + { 0x12, 0x1a, 0x16, 0x1e, 0x02, 0x0a, 0x06, 0x0e, + 0x10, 0x18, 0x14, 0x1c, 0x00, 0x08, 0x04, 0x0c, + 0x13, 0x1b, 0x17, 0x1f, 0x03, 0x0b, 0x07, 0x0f, + 0x11, 0x19, 0x15, 0x1d, 0x01, 0x09, 0x05, 0x0d } }; + + unsigned char p_variants[32] = + { 0xB7, 0x74, 0x85, 0xD0, 0xCC, 0xDB, 0xCA, 0x73, + 0x03, 0xFE, 0x31, 0x03, 0x52, 0xE0, 0xB7, 0x42, + 0x63, 0x16, 0xF2, 0x2A, 0x79, 0x52, 0xFF, 0x1B, + 0x7A, 0x11, 0xCA, 0x1A, 0x9B, 0x40, 0xAD, 0x01 }; + + /* The "secret" key */ + unsigned char p_secret[5] = { 0x55, 0xD6, 0xC4, 0xC5, 0x28 }; + + unsigned char p_bits[30], p_scratch[10], p_tmp1[5], p_tmp2[5]; + unsigned char i_lfsr0_o; /* 1 bit used */ + unsigned char i_lfsr1_o; /* 1 bit used */ + unsigned char i_css_variant, i_cse, i_index, i_combined, i_carry; + unsigned char i_val = 0; + unsigned int i_lfsr0, i_lfsr1; + int i_term = 0; + int i_bit; + int i; + + for (i = 9; i >= 0; --i) + p_scratch[i] = p_challenge[pp_perm_challenge[i_key_type][i]]; + + i_css_variant = ( i_key_type == 0 ) ? i_variant : + pp_perm_variant[i_key_type-1][i_variant]; + + /* + * This encryption engine implements one of 32 variations + * one the same theme depending upon the choice in the + * variant parameter (0 - 31). + * + * The algorithm itself manipulates a 40 bit input into + * a 40 bit output. + * The parameter 'input' is 80 bits. It consists of + * the 40 bit input value that is to be encrypted followed + * by a 40 bit seed value for the pseudo random number + * generators. + */ + + /* Feed the secret into the input values such that + * we alter the seed to the LFSR's used above, then + * generate the bits to play with. + */ + for( i = 5 ; --i >= 0 ; ) + { + p_tmp1[i] = p_scratch[5 + i] ^ p_secret[i] ^ p_crypt_tab2[i]; + } + + /* + * We use two LFSR's (seeded from some of the input data bytes) to + * generate two streams of pseudo-random bits. These two bit streams + * are then combined by simply adding with carry to generate a final + * sequence of pseudo-random bits which is stored in the buffer that + * 'output' points to the end of - len is the size of this buffer. + * + * The first LFSR is of degree 25, and has a polynomial of: + * x^13 + x^5 + x^4 + x^1 + 1 + * + * The second LSFR is of degree 17, and has a (primitive) polynomial of: + * x^15 + x^1 + 1 + * + * I don't know if these polynomials are primitive modulo 2, and thus + * represent maximal-period LFSR's. + * + * + * Note that we take the output of each LFSR from the new shifted in + * bit, not the old shifted out bit. Thus for ease of use the LFSR's + * are implemented in bit reversed order. + * + */ + + /* In order to ensure that the LFSR works we need to ensure that the + * initial values are non-zero. Thus when we initialise them from + * the seed, we ensure that a bit is set. + */ + i_lfsr0 = ( p_tmp1[0] << 17 ) | ( p_tmp1[1] << 9 ) | + (( p_tmp1[2] & ~7 ) << 1 ) | 8 | ( p_tmp1[2] & 7 ); + i_lfsr1 = ( p_tmp1[3] << 9 ) | 0x100 | p_tmp1[4]; + + i_index = sizeof(p_bits); + i_carry = 0; + + do + { + for( i_bit = 0, i_val = 0 ; i_bit < 8 ; ++i_bit ) + { + + i_lfsr0_o = ( ( i_lfsr0 >> 24 ) ^ ( i_lfsr0 >> 21 ) ^ + ( i_lfsr0 >> 20 ) ^ ( i_lfsr0 >> 12 ) ) & 1; + i_lfsr0 = ( i_lfsr0 << 1 ) | i_lfsr0_o; + + i_lfsr1_o = ( ( i_lfsr1 >> 16 ) ^ ( i_lfsr1 >> 2 ) ) & 1; + i_lfsr1 = ( i_lfsr1 << 1 ) | i_lfsr1_o; + + i_combined = !i_lfsr1_o + i_carry + !i_lfsr0_o; + /* taking bit 1 */ + i_carry = ( i_combined >> 1 ) & 1; + i_val |= ( i_combined & 1 ) << i_bit; + } + + p_bits[--i_index] = i_val; + } while( i_index > 0 ); + + /* This term is used throughout the following to + * select one of 32 different variations on the + * algorithm. + */ + i_cse = p_variants[i_css_variant] ^ p_crypt_tab2[i_css_variant]; + + /* Now the actual blocks doing the encryption. Each + * of these works on 40 bits at a time and are quite + * similar. + */ + i_index = 0; + for( i = 5, i_term = 0 ; --i >= 0 ; i_term = p_scratch[i] ) + { + i_index = p_bits[25 + i] ^ p_scratch[i]; + i_index = p_crypt_tab1[i_index] ^ ~p_crypt_tab2[i_index] ^ i_cse; + + p_tmp1[i] = p_crypt_tab2[i_index] ^ p_crypt_tab3[i_index] ^ i_term; + } + p_tmp1[4] ^= p_tmp1[0]; + + for( i = 5, i_term = 0 ; --i >= 0 ; i_term = p_tmp1[i] ) + { + i_index = p_bits[20 + i] ^ p_tmp1[i]; + i_index = p_crypt_tab1[i_index] ^ ~p_crypt_tab2[i_index] ^ i_cse; + + p_tmp2[i] = p_crypt_tab2[i_index] ^ p_crypt_tab3[i_index] ^ i_term; + } + p_tmp2[4] ^= p_tmp2[0]; + + for( i = 5, i_term = 0 ; --i >= 0 ; i_term = p_tmp2[i] ) + { + i_index = p_bits[15 + i] ^ p_tmp2[i]; + i_index = p_crypt_tab1[i_index] ^ ~p_crypt_tab2[i_index] ^ i_cse; + i_index = p_crypt_tab2[i_index] ^ p_crypt_tab3[i_index] ^ i_term; + + p_tmp1[i] = p_crypt_tab0[i_index] ^ p_crypt_tab2[i_index]; + } + p_tmp1[4] ^= p_tmp1[0]; + + for( i = 5, i_term = 0 ; --i >= 0 ; i_term = p_tmp1[i] ) + { + i_index = p_bits[10 + i] ^ p_tmp1[i]; + i_index = p_crypt_tab1[i_index] ^ ~p_crypt_tab2[i_index] ^ i_cse; + + i_index = p_crypt_tab2[i_index] ^ p_crypt_tab3[i_index] ^ i_term; + + p_tmp2[i] = p_crypt_tab0[i_index] ^ p_crypt_tab2[i_index]; + } + p_tmp2[4] ^= p_tmp2[0]; + + for( i = 5, i_term = 0 ; --i >= 0 ; i_term = p_tmp2[i] ) + { + i_index = p_bits[5 + i] ^ p_tmp2[i]; + i_index = p_crypt_tab1[i_index] ^ ~p_crypt_tab2[i_index] ^ i_cse; + + p_tmp1[i] = p_crypt_tab2[i_index] ^ p_crypt_tab3[i_index] ^ i_term; + } + p_tmp1[4] ^= p_tmp1[0]; + + for(i = 5, i_term = 0 ; --i >= 0 ; i_term = p_tmp1[i] ) + { + i_index = p_bits[i] ^ p_tmp1[i]; + i_index = p_crypt_tab1[i_index] ^ ~p_crypt_tab2[i_index] ^ i_cse; + + p_key[i] = p_crypt_tab2[i_index] ^ p_crypt_tab3[i_index] ^ i_term; + } + + return; +} + +/***************************************************************************** + * DecryptKey: decrypt p_crypted with p_key. + ***************************************************************************** + * Used to decrypt the disc key, with a player key, after requesting it + * in _dvdcss_disckey and to decrypt title keys, with a disc key, requested + * in _dvdcss_titlekey. + * The player keys and the resulting disc key are only used as KEKs + * (key encryption keys). + * Decryption is slightly dependant on the type of key: + * -for disc key, invert is 0x00, + * -for title key, invert if 0xff. + *****************************************************************************/ +static void css_DecryptKey( unsigned char invert, unsigned char const *p_key, + unsigned char const *p_crypted, unsigned char *p_result ) +{ + unsigned int i_lfsr1_lo; + unsigned int i_lfsr1_hi; + unsigned int i_lfsr0; + unsigned int i_combined; + unsigned char o_lfsr0; + unsigned char o_lfsr1; + unsigned char k[5]; + int i; + + i_lfsr1_lo = p_key[0] | 0x100; + i_lfsr1_hi = p_key[1]; + + i_lfsr0 = ( ( p_key[4] << 17 ) + | ( p_key[3] << 9 ) + | ( p_key[2] << 1 ) ) + + 8 - ( p_key[2] & 7 ); + i_lfsr0 = ( p_css_tab4[i_lfsr0 & 0xff] << 24 ) | + ( p_css_tab4[( i_lfsr0 >> 8 ) & 0xff] << 16 ) | + ( p_css_tab4[( i_lfsr0 >> 16 ) & 0xff] << 8 ) | + p_css_tab4[( i_lfsr0 >> 24 ) & 0xff]; + + i_combined = 0; + for( i = 0 ; i < DVD_KEY_SIZE ; ++i ) + { + o_lfsr1 = p_css_tab2[i_lfsr1_hi] ^ p_css_tab3[i_lfsr1_lo]; + i_lfsr1_hi = i_lfsr1_lo >> 1; + i_lfsr1_lo = ( ( i_lfsr1_lo & 1 ) << 8 ) ^ o_lfsr1; + o_lfsr1 = p_css_tab4[o_lfsr1]; + + o_lfsr0 = ((((((( i_lfsr0 >> 8 ) ^ i_lfsr0 ) >> 1 ) + ^ i_lfsr0 ) >> 3 ) ^ i_lfsr0 ) >> 7 ); + i_lfsr0 = ( i_lfsr0 >> 8 ) | ( o_lfsr0 << 24 ); + + i_combined += ( o_lfsr0 ^ invert ) + o_lfsr1; + k[i] = i_combined & 0xff; + i_combined >>= 8; + } + + p_result[4] = k[4] ^ p_css_tab1[p_crypted[4]] ^ p_crypted[3]; + p_result[3] = k[3] ^ p_css_tab1[p_crypted[3]] ^ p_crypted[2]; + p_result[2] = k[2] ^ p_css_tab1[p_crypted[2]] ^ p_crypted[1]; + p_result[1] = k[1] ^ p_css_tab1[p_crypted[1]] ^ p_crypted[0]; + p_result[0] = k[0] ^ p_css_tab1[p_crypted[0]] ^ p_result[4]; + + p_result[4] = k[4] ^ p_css_tab1[p_result[4]] ^ p_result[3]; + p_result[3] = k[3] ^ p_css_tab1[p_result[3]] ^ p_result[2]; + p_result[2] = k[2] ^ p_css_tab1[p_result[2]] ^ p_result[1]; + p_result[1] = k[1] ^ p_css_tab1[p_result[1]] ^ p_result[0]; + p_result[0] = k[0] ^ p_css_tab1[p_result[0]]; + + return; +} + + +/***************************************************************************** + * player_keys: alternate DVD player keys + ***************************************************************************** + * These player keys were generated using Frank A. Stevenson's PlayerKey + * cracker. A copy of his article can be found here: + * http://www-2.cs.cmu.edu/~dst/DeCSS/FrankStevenson/mail2.txt + *****************************************************************************/ +static const dvd_key_t player_keys[] = +{ + { 0x01, 0xaf, 0xe3, 0x12, 0x80 }, + { 0x12, 0x11, 0xca, 0x04, 0x3b }, + { 0x14, 0x0c, 0x9e, 0xd0, 0x09 }, + { 0x14, 0x71, 0x35, 0xba, 0xe2 }, + { 0x1a, 0xa4, 0x33, 0x21, 0xa6 }, + { 0x26, 0xec, 0xc4, 0xa7, 0x4e }, + { 0x2c, 0xb2, 0xc1, 0x09, 0xee }, + { 0x2f, 0x25, 0x9e, 0x96, 0xdd }, + { 0x33, 0x2f, 0x49, 0x6c, 0xe0 }, + { 0x35, 0x5b, 0xc1, 0x31, 0x0f }, + { 0x36, 0x67, 0xb2, 0xe3, 0x85 }, + { 0x39, 0x3d, 0xf1, 0xf1, 0xbd }, + { 0x3b, 0x31, 0x34, 0x0d, 0x91 }, + { 0x45, 0xed, 0x28, 0xeb, 0xd3 }, + { 0x48, 0xb7, 0x6c, 0xce, 0x69 }, + { 0x4b, 0x65, 0x0d, 0xc1, 0xee }, + { 0x4c, 0xbb, 0xf5, 0x5b, 0x23 }, + { 0x51, 0x67, 0x67, 0xc5, 0xe0 }, + { 0x53, 0x94, 0xe1, 0x75, 0xbf }, + { 0x57, 0x2c, 0x8b, 0x31, 0xae }, + { 0x63, 0xdb, 0x4c, 0x5b, 0x4a }, + { 0x7b, 0x1e, 0x5e, 0x2b, 0x57 }, + { 0x85, 0xf3, 0x85, 0xa0, 0xe0 }, + { 0xab, 0x1e, 0xe7, 0x7b, 0x72 }, + { 0xab, 0x36, 0xe3, 0xeb, 0x76 }, + { 0xb1, 0xb8, 0xf9, 0x38, 0x03 }, + { 0xb8, 0x5d, 0xd8, 0x53, 0xbd }, + { 0xbf, 0x92, 0xc3, 0xb0, 0xe2 }, + { 0xcf, 0x1a, 0xb2, 0xf8, 0x0a }, + { 0xec, 0xa0, 0xcf, 0xb3, 0xff }, + { 0xfc, 0x95, 0xa9, 0x87, 0x35 } +}; + +/***************************************************************************** + * DecryptDiscKey + ***************************************************************************** + * Decryption of the disc key with player keys: try to decrypt the disc key + * from every position with every player key. + * p_struct_disckey: the 2048 byte DVD_STRUCT_DISCKEY data + * p_disc_key: result, the 5 byte disc key + *****************************************************************************/ +static int css_DecryptDiscKey( drive_info* drive, unsigned char const *p_struct_disckey, + dvd_key_t p_disc_key ) +{ + dvd_key_t p_verify; + unsigned int i, n = 0; + + /* Decrypt disc key with the above player keys */ + for( n = 0; n < sizeof(player_keys) / sizeof(dvd_key_t); n++ ) + { + css_printkey( (char*) ("CSS: trying player key "), player_keys[n] ); + + for( i = 1; i < 409; i++ ) + { + /* Check if player key n is the right key for position i. */ + css_DecryptKey( 0, player_keys[n], p_struct_disckey + 5 * i, + p_disc_key ); + + /* The first part in the struct_disckey block is the + * 'disc key' encrypted with itself. Using this we + * can check if we decrypted the correct key. */ + css_DecryptKey( 0, p_disc_key, p_struct_disckey, p_verify ); + + /* If the position / player key pair worked then return. */ + if( memcmp( p_disc_key, p_verify, DVD_KEY_SIZE ) == 0 ) + { + return 0; + } + } + } + + /* Have tried all combinations of positions and keys, + * and we still didn't succeed. */ + memset( p_disc_key, 0, DVD_KEY_SIZE ); + return -1; +} + +static int css_investigate( unsigned char *hash, unsigned char *ckey ) +{ + dvd_key_t key; + css_DecryptKey( 0, ckey, hash, key ); + return memcmp( key, ckey, DVD_KEY_SIZE ); +} + +#define K1TABLEWIDTH 10 + +static int css_CrackDiscKey( drive_info* drive, unsigned char *p_disc_key ) +{ + unsigned char B[5] = { 0,0,0,0,0 }; /* Second Stage of mangle cipher */ + unsigned char C[5] = { 0,0,0,0,0 }; /* Output Stage of mangle cipher + * IntermediateKey */ + unsigned char k[5] = { 0,0,0,0,0 }; /* Mangling cipher key + * Also output from CSS( C ) */ + unsigned char out1[5]; /* five first output bytes of LFSR1 */ + unsigned char out2[5]; /* five first output bytes of LFSR2 */ + unsigned int lfsr1a; /* upper 9 bits of LFSR1 */ + unsigned int lfsr1b; /* lower 8 bits of LFSR1 */ + unsigned int tmp, tmp2, tmp3, tmp4,tmp5; + int i,j; + unsigned int nStepA; /* iterator for LFSR1 start state */ + unsigned int nStepB; /* iterator for possible B[0] */ + unsigned int nTry; /* iterator for K[1] possibilities */ + unsigned int nPossibleK1; /* #of possible K[1] values */ + unsigned char* K1table; /* Lookup table for possible K[1] */ + unsigned int* BigTable; /* LFSR2 startstate indexed by + * 1,2,5 output byte */ + + /* + * Prepare tables for hash reversal + */ + + /* initialize lookup tables for k[1] */ + K1table = (unsigned char*) malloc( 65536 * K1TABLEWIDTH ); + if( K1table == NULL ) + { + return -1; + } + memset( K1table, 0 , 65536 * K1TABLEWIDTH ); + + tmp = p_disc_key[0] ^ p_css_tab1[ p_disc_key[1] ]; + for( i = 0 ; i < 256 ; i++ ) /* k[1] */ + { + tmp2 = p_css_tab1[ tmp ^ i ]; /* p_css_tab1[ B[1] ]*/ + + for( j = 0 ; j < 256 ; j++ ) /* B[0] */ + { + tmp3 = j ^ tmp2 ^ i; /* C[1] */ + tmp4 = K1table[ K1TABLEWIDTH * ( 256 * j + tmp3 ) ]; /* count of entries here */ + tmp4++; +/* + if( tmp4 == K1TABLEWIDTH ) + { + print_debug( dvdcss, "Table disaster %d", tmp4 ); + } +*/ + if( tmp4 < K1TABLEWIDTH ) + { + K1table[ K1TABLEWIDTH * ( 256 * j + tmp3 ) + tmp4 ] = i; + } + K1table[ K1TABLEWIDTH * ( 256 * j + tmp3 ) ] = tmp4; + } + } + + /* Initing our Really big table */ + BigTable = (unsigned int*) malloc( 16777216 * sizeof(int) ); + if( BigTable == NULL ) + { + return -1; + } + memset( BigTable, 0 , 16777216 * sizeof(int) ); + + tmp3 = 0; + + printf( "CSS: initializing the big table\n" ); + + for( i = 0 ; i < 16777216 ; i++ ) + { + tmp = (( i + i ) & 0x1fffff0 ) | 0x8 | ( i & 0x7 ); + + for( j = 0 ; j < 5 ; j++ ) + { + tmp2=((((((( tmp >> 3 ) ^ tmp ) >> 1 ) ^ tmp ) >> 8 ) + ^ tmp ) >> 5 ) & 0xff; + tmp = ( tmp << 8) | tmp2; + out2[j] = p_css_tab4[ tmp2 ]; + } + + j = ( out2[0] << 16 ) | ( out2[1] << 8 ) | out2[4]; + BigTable[j] = i; + } + + /* + * We are done initing, now reverse hash + */ + tmp5 = p_disc_key[0] ^ p_css_tab1[ p_disc_key[1] ]; + + for( nStepA = 0 ; nStepA < 65536 ; nStepA ++ ) + { + lfsr1a = 0x100 | ( nStepA >> 8 ); + lfsr1b = nStepA & 0xff; + + /* Generate 5 first output bytes from lfsr1 */ + for( i = 0 ; i < 5 ; i++ ) + { + tmp = p_css_tab2[ lfsr1b ] ^ p_css_tab3[ lfsr1a ]; + lfsr1b = lfsr1a >> 1; + lfsr1a = ((lfsr1a&1)<<8) ^ tmp; + out1[ i ] = p_css_tab4[ tmp ]; + } + + /* cumpute and cache some variables */ + C[0] = nStepA >> 8; + C[1] = nStepA & 0xff; + tmp = p_disc_key[3] ^ p_css_tab1[ p_disc_key[4] ]; + tmp2 = p_css_tab1[ p_disc_key[0] ]; + + /* Search through all possible B[0] */ + for( nStepB = 0 ; nStepB < 256 ; nStepB++ ) + { + /* reverse parts of the mangling cipher */ + B[0] = nStepB; + k[0] = p_css_tab1[ B[0] ] ^ C[0]; + B[4] = B[0] ^ k[0] ^ tmp2; + k[4] = B[4] ^ tmp; + nPossibleK1 = K1table[ K1TABLEWIDTH * (256 * B[0] + C[1]) ]; + + /* Try out all possible values for k[1] */ + for( nTry = 0 ; nTry < nPossibleK1 ; nTry++ ) + { + k[1] = K1table[ K1TABLEWIDTH * (256 * B[0] + C[1]) + nTry + 1 ]; + B[1] = tmp5 ^ k[1]; + + /* reconstruct output from LFSR2 */ + tmp3 = ( 0x100 + k[0] - out1[0] ); + out2[0] = tmp3 & 0xff; + tmp3 = tmp3 & 0x100 ? 0x100 : 0xff; + tmp3 = ( tmp3 + k[1] - out1[1] ); + out2[1] = tmp3 & 0xff; + tmp3 = ( 0x100 + k[4] - out1[4] ); + out2[4] = tmp3 & 0xff; /* Can be 1 off */ + + /* test first possible out2[4] */ + tmp4 = ( out2[0] << 16 ) | ( out2[1] << 8 ) | out2[4]; + tmp4 = BigTable[ tmp4 ]; + C[2] = tmp4 & 0xff; + C[3] = ( tmp4 >> 8 ) & 0xff; + C[4] = ( tmp4 >> 16 ) & 0xff; + B[3] = p_css_tab1[ B[4] ] ^ k[4] ^ C[4]; + k[3] = p_disc_key[2] ^ p_css_tab1[ p_disc_key[3] ] ^ B[3]; + B[2] = p_css_tab1[ B[3] ] ^ k[3] ^ C[3]; + k[2] = p_disc_key[1] ^ p_css_tab1[ p_disc_key[2] ] ^ B[2]; + + if( ( B[1] ^ p_css_tab1[ B[2] ] ^ k[ 2 ] ) == C[ 2 ] ) + { + if( ! css_investigate( &p_disc_key[0] , &C[0] ) ) + { + goto end; + } + } + + /* Test second possible out2[4] */ + out2[4] = ( out2[4] + 0xff ) & 0xff; + tmp4 = ( out2[0] << 16 ) | ( out2[1] << 8 ) | out2[4]; + tmp4 = BigTable[ tmp4 ]; + C[2] = tmp4 & 0xff; + C[3] = ( tmp4 >> 8 ) & 0xff; + C[4] = ( tmp4 >> 16 ) & 0xff; + B[3] = p_css_tab1[ B[4] ] ^ k[4] ^ C[4]; + k[3] = p_disc_key[2] ^ p_css_tab1[ p_disc_key[3] ] ^ B[3]; + B[2] = p_css_tab1[ B[3] ] ^ k[3] ^ C[3]; + k[2] = p_disc_key[1] ^ p_css_tab1[ p_disc_key[2] ] ^ B[2]; + + if( ( B[1] ^ p_css_tab1[ B[2] ] ^ k[ 2 ] ) == C[ 2 ] ) + { + if( ! css_investigate( &p_disc_key[0] , &C[0] ) ) + { + goto end; + } + } + } + } + } + +end: + + memcpy( p_disc_key, &C[0], DVD_KEY_SIZE ); + + free( K1table ); + free( BigTable ); + + return 0; +} + +/***************************************************************************** + * RecoverTitleKey: (title) key recovery from cipher and plain text + * Function designed by Frank Stevenson + ***************************************************************************** + * Called from Attack* which are in turn called by CrackTitleKey. Given + * a guessed(?) plain text and the cipher text. Returns -1 on failure. + *****************************************************************************/ +static int css_RecoverTitleKey( int i_start, unsigned char const *p_crypted, + unsigned char const *p_decrypted, + unsigned char const *p_sector_seed, unsigned char *p_key ) +{ + unsigned char p_buffer[10]; + unsigned int i_t1, i_t2, i_t3, i_t4, i_t5, i_t6; + unsigned int i_try; + unsigned int i_candidate; + unsigned int i, j; + int i_exit = -1; + + for( i = 0 ; i < 10 ; i++ ) + { + p_buffer[i] = p_css_tab1[p_crypted[i]] ^ p_decrypted[i]; + } + + for( i_try = i_start ; i_try < 0x10000 ; i_try++ ) + { + i_t1 = i_try >> 8 | 0x100; + i_t2 = i_try & 0xff; + i_t3 = 0; /* not needed */ + i_t5 = 0; + + /* iterate cipher 4 times to reconstruct LFSR2 */ + for( i = 0 ; i < 4 ; i++ ) + { + /* advance LFSR1 normaly */ + i_t4 = p_css_tab2[i_t2] ^ p_css_tab3[i_t1]; + i_t2 = i_t1 >> 1; + i_t1 = ( ( i_t1 & 1 ) << 8 ) ^ i_t4; + i_t4 = p_css_tab5[i_t4]; + /* deduce i_t6 & i_t5 */ + i_t6 = p_buffer[i]; + if( i_t5 ) + { + i_t6 = ( i_t6 + 0xff ) & 0x0ff; + } + if( i_t6 < i_t4 ) + { + i_t6 += 0x100; + } + i_t6 -= i_t4; + i_t5 += i_t6 + i_t4; + i_t6 = p_css_tab4[ i_t6 ]; + /* feed / advance i_t3 / i_t5 */ + i_t3 = ( i_t3 << 8 ) | i_t6; + i_t5 >>= 8; + } + + i_candidate = i_t3; + + /* iterate 6 more times to validate candidate key */ + for( ; i < 10 ; i++ ) + { + i_t4 = p_css_tab2[i_t2] ^ p_css_tab3[i_t1]; + i_t2 = i_t1 >> 1; + i_t1 = ( ( i_t1 & 1 ) << 8 ) ^ i_t4; + i_t4 = p_css_tab5[i_t4]; + i_t6 = ((((((( i_t3 >> 3 ) ^ i_t3 ) >> 1 ) ^ + i_t3 ) >> 8 ) ^ i_t3 ) >> 5 ) & 0xff; + i_t3 = ( i_t3 << 8 ) | i_t6; + i_t6 = p_css_tab4[i_t6]; + i_t5 += i_t6 + i_t4; + if( ( i_t5 & 0xff ) != p_buffer[i] ) + { + break; + } + + i_t5 >>= 8; + } + + if( i == 10 ) + { + /* Do 4 backwards steps of iterating t3 to deduce initial state */ + i_t3 = i_candidate; + for( i = 0 ; i < 4 ; i++ ) + { + i_t1 = i_t3 & 0xff; + i_t3 = ( i_t3 >> 8 ); + /* easy to code, and fast enough bruteforce + * search for byte shifted in */ + for( j = 0 ; j < 256 ; j++ ) + { + i_t3 = ( i_t3 & 0x1ffff ) | ( j << 17 ); + i_t6 = ((((((( i_t3 >> 3 ) ^ i_t3 ) >> 1 ) ^ + i_t3 ) >> 8 ) ^ i_t3 ) >> 5 ) & 0xff; + if( i_t6 == i_t1 ) + { + break; + } + } + } + + i_t4 = ( i_t3 >> 1 ) - 4; + for( i_t5 = 0 ; i_t5 < 8; i_t5++ ) + { + if( ( ( i_t4 + i_t5 ) * 2 + 8 - ( (i_t4 + i_t5 ) & 7 ) ) + == i_t3 ) + { + p_key[0] = i_try>>8; + p_key[1] = i_try & 0xFF; + p_key[2] = ( ( i_t4 + i_t5 ) >> 0 ) & 0xFF; + p_key[3] = ( ( i_t4 + i_t5 ) >> 8 ) & 0xFF; + p_key[4] = ( ( i_t4 + i_t5 ) >> 16 ) & 0xFF; + i_exit = i_try + 1; + } + } + } + } + + if( i_exit >= 0 ) + { + p_key[0] ^= p_sector_seed[0]; + p_key[1] ^= p_sector_seed[1]; + p_key[2] ^= p_sector_seed[2]; + p_key[3] ^= p_sector_seed[3]; + p_key[4] ^= p_sector_seed[4]; + } + + return i_exit; +} + +/***************************************************************************** + * DecryptTitleKey + ***************************************************************************** + * Decrypt the title key using the disc key. + * p_disc_key: result, the 5 byte disc key + * p_titlekey: the encrypted title key, gets overwritten by the decrypted key + *****************************************************************************/ +static void css_DecryptTitleKey( dvd_key_t p_disc_key, dvd_key_t p_titlekey ) +{ + css_DecryptKey( 0xff, p_disc_key, p_titlekey, p_titlekey ); +} + +/***************************************************************************** + * CrackTitleKey: try to crack title key from the contents of a VOB. + ***************************************************************************** + * This function is called by _dvdcss_titlekey to find a title key, if we've + * chosen to crack title key instead of decrypting it with the disc key. + * The DVD should have been opened and be in an authenticated state. + * i_pos is the starting sector, i_len is the maximum number of sectors to read + *****************************************************************************/ +static int css_CrackTitleKey( drive_info* drive, int i_pos, int i_len, + dvd_key_t p_titlekey ) +{ + unsigned char p_buf[ DVDCSS_BLOCK_SIZE ]; + const unsigned char p_packstart[4] = { 0x00, 0x00, 0x01, 0xba }; + int i_reads = 0; + int i_encrypted = 0; + int b_stop_scanning = 0; + int b_read_error = 0; + int i_ret; + + printf( "CSS: cracking title key at block %x\n", i_pos ); + + i_tries = 0; + i_success = 0; + + do + { +// i_ret = dvdcss->pf_seek( dvdcss, i_pos ); + i_ret = seek_dvd( drive, i_pos, DVDCSS_NOFLAGS ); + + if( i_ret ) + { + printf( "CSS: seek failed\n" ); + } + + i_ret = read_dvd(drive, drive->rd_buf, i_pos, 1, DVDCSS_NOFLAGS); + memcpy(p_buf, drive->rd_buf, DVDCSS_BLOCK_SIZE); +// i_ret = dvdcss_read( dvdcss, p_buf, 1, DVDCSS_NOFLAGS ); + + /* Either we are at the end of the physical device or the auth + * have failed / were not done and we got a read error. */ + if( i_ret <= 0 ) + { + if( i_ret == 0 ) + { + printf( "CSS: read returned 0 (end of device?)\n" ); + } + else if( !b_read_error ) + { + printf( "CSS: read error at block %i, resorting to " + "secret arcanes to recover\n", i_pos ); + + /* Reset the drive before trying to continue */ +#warning "Reset the drive before trying to continue" +// _dvdcss_close( dvdcss ); +// _dvdcss_open( dvdcss ); + + b_read_error = 1; + continue; + } + break; + } + + /* Stop when we find a non MPEG stream block. + * (We must have reached the end of the stream). + * For now, allow all blocks that begin with a start code. */ + if( memcmp( p_buf, p_packstart, 3 ) ) + { + printf( "CSS: non MPEG block found at block %i " + "(end of title)\n", i_pos ); + break; + } + + if( p_buf[0x0d] & 0x07 ) + printf( "CSS: stuffing in pack header\n" ); + + /* PES_scrambling_control does not exist in a system_header, + * a padding_stream or a private_stream2 (and others?). */ + if( p_buf[0x14] & 0x30 && ! ( p_buf[0x11] == 0xbb + || p_buf[0x11] == 0xbe + || p_buf[0x11] == 0xbf ) ) + { + i_encrypted++; + + if( css_AttackPattern(p_buf, i_reads, p_titlekey) > 0 ) + { + b_stop_scanning = 1; + } +#if 0 + if( AttackPadding(p_buf, i_reads, p_titlekey) > 0 ) + { + b_stop_scanning = 1; + } +#endif + } + + i_pos++; + i_len--; + i_reads++; + + /* Emit a progress indication now and then. */ + if( !( i_reads & 0xfff ) ) + { + printf( "CSS: at block %i, still cracking...\n", i_pos ); + } + + /* Stop after 2000 blocks if we haven't seen any encrypted blocks. */ + if( i_reads >= 2000 && i_encrypted == 0 ) break; + + } while( !b_stop_scanning && i_len > 0); + + if( !b_stop_scanning ) + { + printf( "CSS: end of title reached\n" ); + } + + /* Print some statistics. */ + printf( "CSS: successful attempts %d/%d, scrambled blocks %d/%d\n", + i_success, i_tries, i_encrypted, i_reads ); + + if( i_success > 0 /* b_stop_scanning */ ) + { + printf( "CSS: vts key initialized\n" ); + return 1; + } + + if( i_encrypted == 0 && i_reads > 0 ) + { + memset( p_titlekey, 0, DVD_KEY_SIZE ); + printf( "CSS: no scrambled sectors found\n" ); + return 0; + } + + memset( p_titlekey, 0, DVD_KEY_SIZE ); + return -1; +} + +/****************************************************************************** + * The original Ethan Hawke (DeCSSPlus) attack (modified). + ****************************************************************************** + * Tries to find a repeating pattern just before the encrypted part starts. + * Then it guesses that the plain text for first encrypted bytes are + * a contiuation of that pattern. + *****************************************************************************/ +static int css_AttackPattern( unsigned char const p_sec[ DVDCSS_BLOCK_SIZE ], int i_pos, unsigned char *p_key ) +{ + unsigned int i_best_plen = 0; + unsigned int i_best_p = 0; + unsigned int i, j; + + /* For all cycle length from 2 to 48 */ + for( i = 2 ; i < 0x30 ; i++ ) + { + /* Find the number of bytes that repeats in cycles. */ + for( j = i + 1; + j < 0x80 && ( p_sec[0x7F - (j%i)] == p_sec[0x7F - j] ); + j++ ) + { + /* We have found j repeating bytes with a cycle length i. */ + if( j > i_best_plen ) + { + i_best_plen = j; + i_best_p = i; + } + } + } + + /* We need at most 10 plain text bytes?, so a make sure that we + * have at least 20 repeated bytes and that they have cycled at + * least one time. */ + if( ( i_best_plen > 3 ) && ( i_best_plen / i_best_p >= 2) ) + { + int res; + + i_tries++; + memset( p_key, 0, DVD_KEY_SIZE ); + res = css_RecoverTitleKey( 0, &p_sec[0x80], + &p_sec[ 0x80 - (i_best_plen / i_best_p) * i_best_p ], + &p_sec[0x54] /* key_seed */, p_key ); + i_success += ( res >= 0 ); +#if 0 + if( res >= 0 ) + { + fprintf( stderr, "key is %02x:%02x:%02x:%02x:%02x ", + p_key[0], p_key[1], p_key[2], p_key[3], p_key[4] ); + fprintf( stderr, "at block %5d pattern len %3d period %3d %s\n", + i_pos, i_best_plen, i_best_p, (res>=0?"y":"n") ); + } +#endif + return ( res >= 0 ); + } + + return 0; +} + + +/***************************************************************************** + * _dvdcss_title: crack or decrypt the current title key if needed + ***************************************************************************** + * This function should only be called by dvdcss->pf_seek and should eventually + * not be external if possible. + *****************************************************************************/ +int css_title ( drive_info* drive, int lba) +{ + dvd_title_t *p_title; + dvd_title_t *p_newtitle; + dvd_key_t p_title_key; + int i_ret = -1; +#if (DVDCSS_KEY_CACHE > 0) + int i_fd, b_cache = 0; +#endif + + if( ! drive->media.dvdcss.protection == 0x01 ) + { + return 0; + } + + /* Check if we've already cracked this key */ + p_title = drive->media.dvdcss.p_titles; + while( p_title != NULL + && p_title->p_next != NULL + && p_title->p_next->i_startlb <= lba ) + { + p_title = p_title->p_next; + } + + if( p_title != NULL + && p_title->i_startlb == lba ) + { + /* We've already cracked this key, nothing to do */ + memcpy( drive->media.dvdcss.TK, p_title->p_key, sizeof(dvd_key_t) ); + return 0; + } + +#if (DVDCSS_KEY_CACHE > 0) + + /* Check whether the key is in our disk cache */ + if( drive->media.dvdcss.psz_cachefile[0] ) + { + /* XXX: be careful, we use sprintf and not snprintf */ + sprintf( drive->media.dvdcss.psz_block, "%.10x", lba ); + i_fd = open( drive->media.dvdcss.psz_cachefile, O_RDONLY ); + b_cache = 1; + + if( i_fd >= 0 ) + { + char psz_key[DVD_KEY_SIZE * 3]; + unsigned int k0, k1, k2, k3, k4; + + psz_key[DVD_KEY_SIZE * 3 - 1] = '\0'; + + if( read( i_fd, psz_key, DVD_KEY_SIZE * 3 - 1 ) == DVD_KEY_SIZE * 3 - 1 + && sscanf( psz_key, "%x:%x:%x:%x:%x", + &k0, &k1, &k2, &k3, &k4 ) == 5 ) + { + p_title_key[0] = k0; + p_title_key[1] = k1; + p_title_key[2] = k2; + p_title_key[3] = k3; + p_title_key[4] = k4; + css_printkey( (char*) "CSS: title key found in cache ", p_title_key ); + + /* Don't try to save it again */ + b_cache = 0; + i_ret = 1; + } + + close( i_fd ); + } + } +#endif + + /* Crack or decrypt CSS title key for current VTS */ + if( i_ret < 0 ) + { + i_ret = css_titlekey( drive, lba, p_title_key ); + + if( i_ret < 0 ) + { + printf ( "CSS: fatal error in vts css key\n" ); + return i_ret; + } + + if( i_ret == 0 ) + { + printf( "CSS: unencrypted title\n" ); + /* We cache this anyway, so we don't need to check again. */ + } + } + +#if (DVDCSS_KEY_CACHE > 0) + /* Key is valid, we store it on disk. */ + if( drive->media.dvdcss.psz_cachefile[0] && b_cache ) + { + i_fd = open( drive->media.dvdcss.psz_cachefile, O_RDWR|O_CREAT, 0644 ); + if( i_fd >= 0 ) + { + char psz_key[DVD_KEY_SIZE * 3 + 2]; + + sprintf( psz_key, "%02x:%02x:%02x:%02x:%02x\r\n", + p_title_key[0], p_title_key[1], p_title_key[2], + p_title_key[3], p_title_key[4] ); + + write( i_fd, psz_key, DVD_KEY_SIZE * 3 + 1 ); + close( i_fd ); + } + } +#endif + + /* Find our spot in the list */ + p_newtitle = NULL; + p_title = drive->media.dvdcss.p_titles; + while( ( p_title != NULL ) && ( p_title->i_startlb < lba ) ) + { + p_newtitle = p_title; + p_title = p_title->p_next; + } + + /* Save the found title */ + p_title = p_newtitle; + + /* Write in the new title and its key */ + p_newtitle = (dvd_title_t*) malloc( sizeof( dvd_title_t ) ); + p_newtitle->i_startlb = lba; + memcpy( p_newtitle->p_key, p_title_key, DVD_KEY_SIZE ); + + /* Link it at the head of the (possibly empty) list */ + if( p_title == NULL ) + { + p_newtitle->p_next = drive->media.dvdcss.p_titles; + drive->media.dvdcss.p_titles = p_newtitle; + } + /* Link the new title inside the list */ + else + { + p_newtitle->p_next = p_title->p_next; + p_title->p_next = p_newtitle; + } + + memcpy( drive->media.dvdcss.TK, p_title_key, DVD_KEY_SIZE ); + return 0; +} + + +/***************************************************************************** + * _dvdcss_titlekey: get title key. + *****************************************************************************/ +int css_titlekey( drive_info* drive, int lba, dvd_key_t p_title_key ) +{ +// static unsigned char p_garbage[ DVDCSS_BLOCK_SIZE ]; /* we never read it back */ + unsigned char p_key[ DVD_KEY_SIZE ]; + int i, i_ret = 0; + + if( drive->media.dvdcss.method == DVDCSS_METHOD_KEY || drive->media.dvdcss.method == DVDCSS_METHOD_DISC ) + { + /* We have a decrypted Disc key and the ioctls are available, + * read the title key and decrypt it. + */ + + printf( "CSS: getting title key at block %i the classic way\n", lba ); + + /* We need to authenticate again every time to get a new session key */ + if( css_get_bus_key( drive ) ) + { + return -1; + } + + /* Get encrypted title key */ + if (css_report_title_key(drive, lba, p_key)) + //if( ioctl_ReadTitleKey( dvdcss->i_fd, &dvdcss->css.i_agid, + // lba, p_key ) < 0 ) + { + printf( "CSS: ReadTitleKey failed (region mismatch?)\n" ); + i_ret = -1; + } + + /* Test ASF, it will be reset to 0 if we got a Region error */ + //switch( GetASF( dvdcss ) ) + switch( css_report_asf( drive ) ) + { + case -1: + /* An error getting the ASF status, something must be wrong. */ + printf( "CSS: lost ASF requesting title key\n" ); + css_invalidate_agid(drive); +// ioctl_InvalidateAgid( dvdcss->i_fd, &dvdcss->css.i_agid ); + i_ret = -1; + break; + + case 1: + /* This might either be a title that has no key, + * or we encountered a region error. */ + printf( "CSS: lost ASF requesting title key\n" ); + break; + + case 2: + /* Drive status is ok. */ + /* If the title key request failed, but we did not loose ASF, + * we might stil have the AGID. Other code assume that we + * will not after this so invalidate it(?). */ + if( i_ret < 0 ) + { + css_invalidate_agid(drive); + // ioctl_InvalidateAgid( dvdcss->i_fd, &dvdcss->css.i_agid ); + } + break; + } + + if( !( i_ret < 0 ) ) + { + /* Decrypt title key using the bus key */ + for( i = 0 ; i < DVD_KEY_SIZE ; i++ ) + { + p_key[ i ] ^= drive->media.dvdcss.BK[ 4 - (i % DVD_KEY_SIZE) ]; + } + + /* If p_key is all zero then there really wasn't any key present + * even though we got to read it without an error. */ + if( !( p_key[0] | p_key[1] | p_key[2] | p_key[3] | p_key[4] ) ) + { + i_ret = 0; + } + else + { + css_printkey( (char*) "CSS: initial disc key ", drive->media.dvdcss.DK ); + css_DecryptTitleKey( drive->media.dvdcss.DK, p_key ); + css_printkey( (char*) "CSS: decrypted title key ", p_key ); + i_ret = 1; + } + + /* All went well either there wasn't a key or we have it now. */ + memcpy( p_title_key, p_key, DVD_KEY_SIZE ); + css_printkey( (char*)"CSS: title key is ", p_title_key ); + + return i_ret; + } + + /* The title key request failed */ + printf( "CSS: resetting drive and cracking title key\n" ); + + /* Read an unscrambled sector and reset the drive */ + read(drive, drive->rd_buf, 0, 1); +// dvdcss->pf_seek( dvdcss, 0 ); +// dvdcss->pf_read( dvdcss, p_garbage, 1 ); +// dvdcss->pf_seek( dvdcss, 0 ); + css_disckey( drive ); + + /* Fallback */ + } + + /* METHOD is TITLE, we can't use the ioctls or requesting the title key + * failed above. For these cases we try to crack the key instead. */ + + /* For now, the read limit is 9Gb / 2048 = 4718592 sectors. */ + i_ret = css_CrackTitleKey( drive, lba, 4718592, p_key ); + + memcpy( p_title_key, p_key, DVD_KEY_SIZE ); + css_printkey( (char*) "CSS: title key is ", p_title_key ); + + return i_ret; +} + + + +/***************************************************************************** + * PrintKey : debug function that dumps a key value + *****************************************************************************/ +static void css_printkey( char *prefix, unsigned char const *data ) +{ + printf( "%s%02X:%02X:%02X:%02X:%02X\n", prefix, + data[0], data[1], data[2], data[3], data[4] ); +} + diff --git a/lib/qpxtransport/qpx_transport.cpp b/lib/qpxtransport/qpx_transport.cpp new file mode 100644 index 0000000..c20a210 --- /dev/null +++ b/lib/qpxtransport/qpx_transport.cpp @@ -0,0 +1,1126 @@ +// +// This is part of dvd+rw-tools by Andy Polyakov +// +// Use-it-on-your-own-risk, GPL bless... +// +// For further details see http://fy.chalmers.se/~appro/linux/DVD+RW/ +// + + +// +// modified to use with QPxTool http://qpxtool.sf.net (C) 2005-2009, Gennady "ShultZ" Kozlov +// + +#include +#include "colors.h" +#include "qpx_transport.h" + +const unsigned char scsi_command_size[8] = +{ + 6, 10, 10, 12, + 16, 12, 10, 10 +}; + +#define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7] + + +#if defined(__unix) || defined(__unix__) +#include +#include +#include + +//#include +//#include +#ifdef open64 +#undef open64 +#endif +#include +//#include +#include + + +long getmsecs() +{ + struct timeval tv; + gettimeofday (&tv,NULL); + return tv.tv_sec*1000+tv.tv_usec/1000; +} + +long getusecs() +{ + struct timeval tv; + gettimeofday (&tv,NULL); + return tv.tv_sec*1000000+tv.tv_usec; +} + +#include + +#ifndef EMEDIUMTYPE +#define EMEDIUMTYPE EINVAL +#endif + +#ifndef ENOMEDIUM +#define ENOMEDIUM ENODEV +#endif + +//* + +#elif defined(_WIN32) +#include +#include + +#define EINVAL ERROR_BAD_ARGUMENTS +#define ENOMEM ERROR_OUTOFMEMORY +#define EMEDIUMTYPE ERROR_MEDIA_INCOMPATIBLE +#define ENOMEDIUM ERROR_MEDIA_OFFLINE +#define ENODEV ERROR_BAD_COMMAND +#define EAGAIN ERROR_NOT_READY +#define ENOSPC ERROR_DISK_FULL +#define EIO ERROR_NOT_SUPPORTED +#define ENXIO ERROR_GEN_FAILURE + +static class _win32_errno { + public: + operator int() { return GetLastError(); } + int operator=(int e) { SetLastError(e); return e; } +} _sys_errno; + +#ifdef errno +#undef errno +#endif + +#define errno _sys_errno + +inline void perror (const char *str) +{ LPVOID lpMsgBuf; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + 0, // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + if (str) + fprintf (stderr, COL_RED "%s: %s" COL_NORM,str,lpMsgBuf); + else + fprintf (stderr, COL_RED "%s" COL_NORM,lpMsgBuf); + + LocalFree(lpMsgBuf); +} + +#define exit(e) ExitProcess(e) + +#endif // _WIN32 + +void sperror (const char *cmd,int err) //, Scsi_Command *scsi) +{ + int saved_errno=errno; + char sense_str[255]; + sense2str(err, sense_str); + + if (err==-1) { + fprintf (stderr, COL_RED "\n:-( unable to %s : [%d] " COL_NORM, cmd, saved_errno); + errno=saved_errno, perror (NULL); + } else + fprintf (stderr, COL_RED "\n:-[ %s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh]: %s\n" COL_NORM, + cmd, SK(err), ASC(err), ASCQ(err),sense_str); +} + +autofree::autofree() + { ptr=NULL; } +autofree::~autofree() + { if (ptr) free(ptr); } + +#if defined(__linux) || defined(__GNU__) + +#include +#if defined(__linux) +#include +#elif defined(__GNU__) +#include +#endif +#include +#include +#include +#include +#include +#if defined(__linux) +#include +#endif +#if !defined(SG_FLAG_LUN_INHIBIT) +# if defined(SG_FLAG_UNUSED_LUN_INHIBIT) +# define SG_FLAG_LUN_INHIBIT SG_FLAG_UNUSED_LUN_INHIBIT +# else +# define SG_FLAG_LUN_INHIBIT 0 +# endif +#endif +#ifndef CHECK_CONDITION +#define CHECK_CONDITION 0x01 +#endif + +#ifdef SG_IO + +USE_SG_IO::USE_SG_IO() +{ + struct utsname buf; + uname (&buf); + // was CDROM_SEND_PACKET declared dead in 2.5? + yes_or_no=(strcmp(buf.release,"2.5.43")>=0); +} + +USE_SG_IO::~USE_SG_IO(){} + +#endif +Scsi_Command::Scsi_Command() { fd=-1, autoclose=1; filename=NULL; } +Scsi_Command::Scsi_Command(int f) { fd=f, autoclose=0; filename=NULL; } +Scsi_Command::Scsi_Command(void*f) { fd=(long)f, autoclose=0; filename=NULL; } +Scsi_Command::~Scsi_Command() +{ + if (fd>=0 && autoclose) close(fd),fd=-1; + if (filename) free(filename),filename=NULL; +} + +int Scsi_Command::associate (const char *file,const struct stat *ref=NULL) +{ + struct stat sb; + /* + * O_RDWR is expected to provide for none set-root-uid + * execution under Linux kernel 2.6[.8]. Under 2.4 it + * falls down to O_RDONLY... + */ + if ((fd=open (file,O_RDWR|O_NONBLOCK)) < 0 && + (fd=open (file,O_RDONLY|O_NONBLOCK)) < 0) return 0; + if (fstat(fd,&sb) < 0) return 0; + if (!S_ISBLK(sb.st_mode)) { errno=ENOTBLK;return 0; } + if (ref && (!S_ISBLK(ref->st_mode) || ref->st_rdev!=sb.st_rdev)) + { errno=ENXIO; return 0; } + filename=strdup(file); + return 1; +} + +int Scsi_Command::transport(Direction dir,void *buf,size_t sz) +{ + int ret = 0; +#ifdef SG_IO +#define KERNEL_BROKEN 0 + if (use_sg_io) + { + sg_io.dxferp = buf; + sg_io.dxfer_len = sz; + sg_io.dxfer_direction = use_sg_io[dir]; + /* cmd length fix */ +// printf("opcode: %02X, cmd.len: %d\n",sg_io.cmdp[0],sg_io.cmd_len); + sg_io.cmd_len = (sg_io.cmd_len < COMMAND_SIZE(sg_io.cmdp[0])) ? COMMAND_SIZE(sg_io.cmdp[0]):sg_io.cmd_len; +// printf("new cmd.len: %d\n",sg_io.cmd_len); + + if (ioctl (fd,SG_IO,&sg_io)) return -1; +#if !KERNEL_BROKEN + if ((sg_io.info&SG_INFO_OK_MASK) != SG_INFO_OK) +#else + if (sg_io.status) +#endif + { + errno=EIO; ret=-1; +#if !KERNEL_BROKEN + if (sg_io.masked_status&CHECK_CONDITION) +#endif + { + ret = ERRCODE(sg_io.sbp); + if (ret==0) ret=-1; + else CREAM_ON_ERRNO(sg_io.sbp); + } + } + return ret; + } + else +#undef KERNEL_BROKEN +#endif + { + cgc.buffer = (unsigned char *)buf; + cgc.buflen = sz; + cgc.data_direction = dir; +#if !defined(__GNU__) + if (ioctl (fd,CDROM_SEND_PACKET,&cgc)) + { + ret = ERRCODE(_sense.u); + if (ret==0) ret=-1; + } +#endif + } + return ret; +} + +unsigned char &Scsi_Command::operator[] (size_t i) +{ + if (i==0) + { + memset(&cgc,0,sizeof(cgc)), memset(&_sense,0,sizeof(_sense)); + cgc.quiet = 1; + cgc.sense = &_sense.s; +#ifdef SG_IO + if (use_sg_io) + { + memset(&sg_io,0,sizeof(sg_io)); + sg_io.interface_id= 'S'; + sg_io.mx_sb_len = sizeof(_sense); + sg_io.cmdp = cgc.cmd; + sg_io.sbp = _sense.u; + sg_io.flags = SG_FLAG_LUN_INHIBIT|SG_FLAG_DIRECT_IO; + } +#endif + } + sg_io.cmd_len = i+1; + return cgc.cmd[i]; +} + +unsigned char &Scsi_Command::operator()(size_t i) { return _sense.u[i]; } +unsigned char *Scsi_Command::sense() { return _sense.u; } + +void Scsi_Command::timeout(int i) { cgc.timeout=sg_io.timeout=i*1000; } +#ifdef SG_IO +size_t Scsi_Command::residue() { return use_sg_io?sg_io.resid:0; } +#else +size_t Scsi_Command::residue() { return 0; } +#endif + +int Scsi_Command::umount(int f) +{ + struct stat fsb,msb; + struct mntent *mb; + FILE *fp; + pid_t pid,rpid; + int ret=0,rval; + + if (f==-1) f=fd; + if (fstat (f,&fsb) < 0) return -1; + if ((fp=setmntent ("/proc/mounts","r"))==NULL) return -1; + + while ((mb=getmntent (fp))!=NULL) + { + if (stat (mb->mnt_fsname,&msb) < 0) continue; // corrupted line? + if (msb.st_rdev == fsb.st_rdev) + { + ret = -1; + if ((pid = fork()) == (pid_t)-1) break; + if (pid == 0) execl ("/bin/umount","umount",mb->mnt_dir,NULL); + while (1) + { + rpid = waitpid (pid,&rval,0); + if (rpid == (pid_t)-1) + { + if (errno==EINTR) continue; + else break; + } + else if (rpid != pid) + { + errno = ECHILD; + break; + } + if (WIFEXITED(rval)) + { + if (WEXITSTATUS(rval) == 0) ret=0; + else errno=EBUSY; // most likely + break; + } + else + { + errno = ENOLINK; // some phony errno + break; + } + } + break; + } + } + endmntent (fp); + return ret; +} + +int Scsi_Command::is_reload_needed () +{ return ioctl (fd,CDROM_MEDIA_CHANGED,CDSL_CURRENT) == 0; } + +#elif defined(__OpenBSD__) || defined(__NetBSD__) + +#include +#include +#include +#include +#include + +typedef off_t off64_t; +#define stat64 stat +#define fstat64 fstat +#define open64 open +#define pread64 pread +#define pwrite64 pwrite +#define lseek64 lseek + +Scsi_Command::Scsi_Command() { fd=-1, autoclose=1; filename=NULL; } +Scsi_Command::Scsi_Command(int f) { fd=f, autoclose=0; filename=NULL; } +Scsi_Command::Scsi_Command(void*f){ fd=(long)f, autoclose=0; filename=NULL; } +Scsi_Command::~Scsi_Command() +{ + if (fd>=0 && autoclose) close(fd),fd=-1; + if (filename) free(filename),filename=NULL; +} + +int Scsi_Command::associate (const char *file,const struct stat *ref) +{ + struct stat sb; + + fd=open(file,O_RDWR|O_NONBLOCK); + // this is --^^^^^^-- why we have to run set-root-uid... + + if (fd < 0) return 0; + if (fstat(fd,&sb) < 0) return 0; + if (!S_ISCHR(sb.st_mode)) { errno=EINVAL; return 0; } + + if (ref && (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev)) + { errno=ENXIO; return 0; } + + filename=strdup(file); + + return 1; +} + +unsigned char &Scsi_Command::operator[] (size_t i) +{ + if (i==0) + { memset(&req,0,sizeof(req)); + req.flags = SCCMD_ESCAPE; + req.timeout = 30000; + req.senselen = 18; //sizeof(req.sense); + } + req.cmdlen = i+1; + return req.cmd[i]; +} + +unsigned char &Scsi_Command::operator()(size_t i) { return req.sense[i]; } + +unsigned char *Scsi_Command::sense() { return req.sense; } + +void Scsi_Command::timeout(int i) { req.timeout=i*1000; } +size_t Scsi_Command::residue() { return req.datalen-req.datalen_used; } +int Scsi_Command::transport(Direction dir,void *buf,size_t sz) +{ + int ret=0; + /* cmd length fix */ +// printf("CMD: (%2d) %02x\n", req.cmdlen, req.cmd[0]); + req.cmdlen = (req.cmdlen < COMMAND_SIZE(req.cmd[0])) ? COMMAND_SIZE(req.cmd[0]):req.cmdlen; + + req.databuf = (caddr_t)buf; + req.datalen = sz; + req.flags |= dir; + if (ioctl (fd,SCIOCCOMMAND,&req) < 0) return -1; + if (req.retsts==SCCMD_OK) return 0; + + errno=EIO; ret=-1; + if (req.retsts==SCCMD_SENSE) + { + ret = ERRCODE(req.sense); + if (ret==0) ret=-1; + else CREAM_ON_ERRNO(req.sense); + } + return ret; +} + +// this code is basically redundant... indeed, we normally want to +// open device O_RDWR, but we can't do that as long as it's mounted. +// in other words, whenever this routine is invoked, device is not +// mounted, so that it could as well just return 0; +int Scsi_Command::umount(int f) +{ + struct stat fsb,msb; +#if defined(__NetBSD__) + struct statvfs *mntbuf; +#else + struct statfs *mntbuf; +#endif + int ret=0,mntsize,i; + + if (f==-1) f=fd; + + if (fstat (f,&fsb) < 0) return -1; + if ((mntsize=getmntinfo(&mntbuf,MNT_NOWAIT))==0)return -1; + + for (i=0;i +#include +#include +#include +#include +#include +#include +#include + +typedef off_t off64_t; +#define stat64 stat +#define fstat64 fstat +#define open64 open +#define pread64 pread +#define pwrite64 pwrite +#define lseek64 lseek + +#define ioctl_fd (((struct cam_device *)ioctl_handle)->fd) + +Scsi_Command::Scsi_Command() +{ cam=NULL, fd=-1, autoclose=1; filename=NULL; } + +Scsi_Command::Scsi_Command(int f) +{ + char pass[32]; // periph_name is 16 chars long + + cam=NULL, fd=-1, autoclose=1, filename=NULL; + + memset (&ccb,0,sizeof(ccb)); + ccb.ccb_h.func_code = XPT_GDEVLIST; + if (ioctl (f,CAMGETPASSTHRU,&ccb) < 0) return; + + sprintf (pass,"/dev/%.15s%u",ccb.cgdl.periph_name,ccb.cgdl.unit_number); + cam=cam_open_pass (pass,O_RDWR,NULL); +} + +Scsi_Command::Scsi_Command(void *f) +{ cam=(struct cam_device *)f, autoclose=0; fd=-1; filename=NULL; } + +Scsi_Command::~Scsi_Command() +{ + if (cam && autoclose) cam_close_device(cam), cam=NULL; + if (fd>=0) close(fd); + if (filename) free(filename), filename=NULL; +} + +int Scsi_Command::associate (const char *file,const struct stat *ref) +{ + struct stat sb; + char pass[32]; // periph_name is 16 chars long + + fd=open(file,O_RDONLY|O_NONBLOCK); + + // all if (ref) code is actually redundant, it never runs + // as long as RELOAD_NEVER_NEEDED... + if (ref && fd<0 && errno==EPERM) + { + // expectedly we would get here if file is /dev/passN + if (stat(file,&sb) < 0) return 0; + if (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev) + return (errno=ENXIO,0); + fd=open(file,O_RDWR); + } + + if (fd < 0) return 0; + if (fstat(fd,&sb) < 0) return 0; + if (!S_ISCHR(sb.st_mode)) return (errno=EINVAL,0); + + if (ref && (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev)) + return (errno=ENXIO,0); + + memset (&ccb,0,sizeof(ccb)); + ccb.ccb_h.func_code = XPT_GDEVLIST; + if (ioctl(fd,CAMGETPASSTHRU,&ccb)<0) return (close(fd),fd=-1,0); + + sprintf (pass,"/dev/%.15s%u",ccb.cgdl.periph_name,ccb.cgdl.unit_number); + cam=cam_open_pass (pass,O_RDWR,NULL); + if (cam==NULL) return (close(fd),fd=-1,0); + + filename=strdup(file); + + return 1; +} + +unsigned char& Scsi_Command::operator[] (size_t i) +{ + if (i==0) + { + memset(&ccb,0,sizeof(ccb)); + ccb.ccb_h.path_id = cam->path_id; + ccb.ccb_h.target_id = cam->target_id; + ccb.ccb_h.target_lun = cam->target_lun; + cam_fill_csio (&(ccb.csio), + + 1, // retries + NULL, // cbfncp + CAM_DEV_QFRZDIS, // flags + MSG_SIMPLE_Q_TAG, // tag_action + NULL, // data_ptr + 0, // dxfer_len + sizeof(ccb.csio.sense_data), // sense_len + 0, // cdb_len + 30*1000); // timeout + } + ccb.csio.cdb_len = i+1; + return ccb.csio.cdb_io.cdb_bytes[i]; +} + +unsigned char& Scsi_Command::operator()(size_t i) + { return ((unsigned char *)&ccb.csio.sense_data)[i]; } + +unsigned char *Scsi_Command::sense() { return (unsigned char*)&ccb.csio.sense_data; } + +void Scsi_Command::timeout(int i) { ccb.ccb_h.timeout=i*1000; } +size_t Scsi_Command::residue() { return ccb.csio.resid; } +int Scsi_Command::transport(Direction dir,void *buf,size_t sz) +{ + int ret=0; + + ccb.csio.ccb_h.flags |= dir; + ccb.csio.data_ptr = (u_int8_t *)buf; + ccb.csio.dxfer_len = sz; + + if ((ret = cam_send_ccb(cam, &ccb)) < 0) + return -1; + + if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) + return 0; + + /* cmd length fix */ +// printf("CMD: (%2d) %02x\n", ccb.csio.cdb_len, ccb.csio.cdb_io.cdb_bytes[0]); + ccb.csio.cdb_len = (ccb.csio.cdb_len < COMMAND_SIZE(ccb.csio.cdb_io.cdb_bytes[0])) ? COMMAND_SIZE(ccb.csio.cdb_io.cdb_bytes[0]):ccb.csio.cdb_len; + + unsigned char *sense=(unsigned char *)&ccb.csio.sense_data; + + errno = EIO; + // FreeBSD 5-CURRENT since 2003-08-24, including 5.2 fails to + // pull sense data automatically, at least for ATAPI transport, + // so I reach for it myself... + if ((ccb.csio.scsi_status==SCSI_STATUS_CHECK_COND) && + !(ccb.ccb_h.status&CAM_AUTOSNS_VALID)) + { u_int8_t _sense[18]; + u_int32_t resid=ccb.csio.resid; + + memset(_sense,0,sizeof(_sense)); + + operator[](0) = 0x03; // REQUEST SENSE + ccb.csio.cdb_io.cdb_bytes[4] = sizeof(_sense); + ccb.csio.cdb_len = 6; + ccb.csio.ccb_h.flags |= CAM_DIR_IN|CAM_DIS_AUTOSENSE; + ccb.csio.data_ptr = _sense; + ccb.csio.dxfer_len = sizeof(_sense); + ccb.csio.sense_len = 0; + ret = cam_send_ccb(cam, &ccb); + + ccb.csio.resid = resid; + if (ret<0) return -1; + if ((ccb.ccb_h.status&CAM_STATUS_MASK) != CAM_REQ_CMP) + return errno=EIO,-1; + + memcpy(sense,_sense,sizeof(_sense)); + } + + ret = ERRCODE(sense); + if (ret == 0) ret = -1; + else CREAM_ON_ERRNO(sense); + + return ret; +} + +int Scsi_Command::umount(int f) +{ + struct stat fsb,msb; + struct statfs *mntbuf; + int ret=0,mntsize,i; + + if (f==-1) f=fd; + + if (fstat (f,&fsb) < 0) return -1; + if ((mntsize=getmntinfo(&mntbuf,MNT_NOWAIT))==0)return -1; + + for (i=0;i1) + DeviceIoControl(fd,FSCTL_UNLOCK_VOLUME, + NULL,0,NULL,0,&junk,NULL); + CloseHandle (fd),fd=INVALID_HANDLE_VALUE; + } + if (filename) free(filename),filename=NULL; +} + +int Scsi_Command::associate (const char *file,const struct stat *ref) +{ + char dev[32]; + sprintf(dev,"%.*s\\",sizeof(dev)-2,file); + if (GetDriveType(dev)!=DRIVE_CDROM) + return errno=EINVAL,0; + sprintf(dev,"\\\\.\\%.*s",sizeof(dev)-5,file); + fd=CreateFile (dev,GENERIC_WRITE|GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL,OPEN_EXISTING,0,NULL); + if (fd!=INVALID_HANDLE_VALUE) + filename=strdup(dev); + return fd!=INVALID_HANDLE_VALUE; +} + + +unsigned char &Scsi_Command::operator[] (size_t i) +{ + if (i==0) + { + memset(&p,0,sizeof(p)); + p.spt.Length = sizeof(p.spt); + p.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; + p.spt.TimeOutValue = 30; + p.spt.SenseInfoLength = sizeof(p.sense); + p.spt.SenseInfoOffset = offsetof(SPKG,sense); + } + p.spt.CdbLength = i+1; + return p.spt.Cdb[i]; +} + +unsigned char &Scsi_Command::operator()(size_t i) { return p.sense[i]; } +unsigned char *Scsi_Command::sense() { return p.sense; }; + +void Scsi_Command::timeout(int i) { p.spt.TimeOutValue=i; } +int Scsi_Command::transport(Direction dir,void *buf,size_t sz) +{ + DWORD bytes; + int ret=0; + + /* cmd length fix */ +// printf("CMD: (%2d) %02x\n", p.spt.CdbLength, p.spt.Cdb[0]); + p.spt.CdbLength = (p.spt.CdbLength < COMMAND_SIZE(p.spt.Cdb[0])) ? COMMAND_SIZE(p.spt.Cdb[0]):p.spt.CdbLength; + + p.spt.DataBuffer = buf; + p.spt.DataTransferLength = sz; + p.spt.DataIn = dir; + + if (DeviceIoControl (fd,IOCTL_SCSI_PASS_THROUGH_DIRECT, + &p,sizeof(p.spt), + &p,sizeof(p), + &bytes,FALSE) == 0) return -1; + + if (p.sense[0]&0x70) + { + SetLastError (ERROR_GEN_FAILURE); + ret = ERRCODE(p.sense); + if (ret==0) ret=-1; + else CREAM_ON_ERRNO(p.sense); + } +#if 0 + else if (p.spt.Cdb[0] == 0x00) // TEST UNIT READY + { unsigned char _sense[18]; + + operator[](0) = 0x03; // REQUEST SENSE + p.spt.Cdb[4] = sizeof(_sense); + p.spt.CdbLength = 6; + + p.spt.DataBuffer = _sense; + p.spt.DataTransferLength = sizeof(_sense); + p.spt.DataIn = READ; + + if (DeviceIoControl (fd,IOCTL_SCSI_PASS_THROUGH_DIRECT, + &p,sizeof(p.spt), + &p,sizeof(p), + &bytes,FALSE) == 0) return -1; + + if ((ret = ERRCODE(_sense))) CREAM_ON_ERRNO(_sense); + } +#endif + return ret; +} + +int Scsi_Command::umount (int f) +{ + DWORD junk; + HANDLE h = (f==-1) ? fd : (HANDLE)f; + + if (DeviceIoControl(h,FSCTL_LOCK_VOLUME,NULL,0,NULL,0,&junk,NULL) && + DeviceIoControl(h,FSCTL_DISMOUNT_VOLUME,NULL,0,NULL,0,&junk,NULL)) + { + if (h==fd) autoclose++; + return 0; + } + return -1; +} + +int Scsi_Command::is_reload_needed () { return 0; } + +//*/ + +#elif defined (__APPLE__) && defined (__MACH__) + + +static int iokit_err (IOReturn ioret,SCSITaskStatus stat, + const unsigned char *sense) +{ int ret=-1; + + if (ioret==kIOReturnSuccess) ret = 0; + else if (ioret==kIOReturnNoDevice) errno = ENXIO; + else if (ioret==kIOReturnNoMemory) errno = ENOMEM; + else if (ioret==kIOReturnExclusiveAccess) errno = EBUSY; + else errno = EIO; + + if (ret) return ret; + + if (stat==kSCSITaskStatus_CHECK_CONDITION) + { ret = ERRCODE(sense); + if (ret==0) errno=EIO, ret=-1; + else CREAM_ON_ERRNO(sense); + } + else if (stat!=kSCSITaskStatus_GOOD) + errno = EIO, ret = -1; + + return ret; +} + +Scsi_Command::Scsi_Command() +{ + scsiob=IO_OBJECT_NULL, plugin=NULL, mmcdif=NULL, taskif=NULL; + autoclose=1; filename=NULL; +} + +Scsi_Command::Scsi_Command(void *f) +{ + taskif = (SCSITaskDeviceInterface **)f, autoclose=0; filename=NULL; +} + +Scsi_Command::~Scsi_Command() +{ + if (autoclose) + { if (taskif) (*taskif)->ReleaseExclusiveAccess(taskif), + (*taskif)->Release(taskif), taskif=NULL; + if (mmcdif) (*mmcdif)->Release(mmcdif), mmcdif=NULL; + if (plugin) IODestroyPlugInInterface(plugin), plugin=NULL; + if (scsiob) IOObjectRelease(scsiob), scsiob=IO_OBJECT_NULL; + } + if (filename) free(filename), filename=NULL; +} + +int Scsi_Command::associate (const char *file,const struct stat *ref) +{ + struct stat sb; + io_object_t scsiob=IO_OBJECT_NULL,parent; + CFMutableDictionaryRef match,bsddev; + CFNumberRef num; + int i; + + if (ref) sb = *ref; + else if (stat(file,&sb)) return 0; + + if (!(S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode))) + return !(errno=ENOTBLK); + + if ((match = CFDictionaryCreateMutable(kCFAllocatorDefault,0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)) + == NULL) return !(errno=ENOMEM); + if ((bsddev = CFDictionaryCreateMutable(kCFAllocatorDefault,0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)) + == NULL) return CFRelease(match),!(errno=ENOMEM); + + i = major(sb.st_rdev); + num = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,&i); + CFDictionarySetValue(bsddev,CFSTR("BSD Major"),num); + CFRelease(num); + + i = minor(sb.st_rdev); + num = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,&i); + CFDictionarySetValue(bsddev,CFSTR("BSD Minor"),num); + CFRelease(num); + + CFDictionarySetValue(match,CFSTR(kIOPropertyMatchKey),bsddev); + CFRelease(bsddev); + + if ((scsiob = IOServiceGetMatchingService(kIOMasterPortDefault,match)) + == IO_OBJECT_NULL) return !(errno=ENXIO); + + // traverse up to "SCSITaskAuthoringDevice" + kern_return_t kret; + while ((kret=IORegistryEntryGetParentEntry(scsiob,kIOServicePlane, + &parent)) == kIOReturnSuccess) + { + CFStringRef uclient; + const char *s; + int cmp; + + IOObjectRelease(scsiob); + scsiob = parent; + uclient = (CFStringRef)IORegistryEntryCreateCFProperty(scsiob, + CFSTR(kIOPropertySCSITaskDeviceCategory), + kCFAllocatorDefault,0); + if (uclient) + { + s = CFStringGetCStringPtr(uclient,kCFStringEncodingMacRoman); + cmp = strcmp(s,kIOPropertySCSITaskAuthoringDevice); + CFRelease(uclient); + if (cmp==0) break; + } + } + if (kret!=kIOReturnSuccess) + { + if (scsiob!=IO_OBJECT_NULL) IOObjectRelease(scsiob); + return !(errno=ENXIO); + } + + SInt32 score=0; + if (IOCreatePlugInInterfaceForService(scsiob, + kIOMMCDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, + &plugin,&score) != kIOReturnSuccess) + { + IOObjectRelease(scsiob); + return !(errno=ENXIO); + } + if ((*plugin)->QueryInterface(plugin, + CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID), + (void**)&mmcdif) != S_OK) + { + IODestroyPlugInInterface(plugin), plugin=NULL; + IOObjectRelease(scsiob); + return !(errno=ENXIO); + } + if ((taskif = (*mmcdif)->GetSCSITaskDeviceInterface(mmcdif)) == NULL) + { + (*mmcdif)->Release(mmcdif), mmcdif=NULL; + IODestroyPlugInInterface(plugin), plugin=NULL; + IOObjectRelease(scsiob); + return !(errno=ENXIO); + } + + // + // Note that in order to ObtainExclusiveAccess no corresponding + // /dev/[r]diskN may remain open by that time. For reference, + // acquiring exclusive access temporarily removes BSD block + // storage device from I/O registry as well as corresponding + // /dev entries. + // + if ((*taskif)->ObtainExclusiveAccess(taskif) != kIOReturnSuccess) + { + (*taskif)->Release(taskif), taskif=NULL; + (*mmcdif)->Release(mmcdif), mmcdif=NULL; + IODestroyPlugInInterface(plugin), plugin=NULL; + IOObjectRelease(scsiob), scsiob=IO_OBJECT_NULL; + return !(errno=EBUSY); + } + + filename=strdup(file); + + return 1; +} + +unsigned char& Scsi_Command::operator[] (size_t i) +{ + if (i==0) + { + memset (cdb,0,sizeof(cdb)); + memset (&_sense,0,sizeof(_sense)); + _timeout = 30; + } + cdblen = i+1; + return cdb[i]; +} + +unsigned char& Scsi_Command::operator()(size_t i) { return _sense.u[i]; } +unsigned char* Scsi_Command::sense() { return _sense.u; } + +void Scsi_Command::timeout(int i) { _timeout=i; } +size_t Scsi_Command::residue() { return resid; } + +int Scsi_Command::transport(Direction dir,void *buf,size_t sz) +{ + int ret=0; + SCSITaskInterface **cmd; + SCSITaskStatus stat; + UInt64 bytes; + IOVirtualRange range = { (IOVirtualAddress)buf, sz }; + + /* cmd length fix */ +// printf("CMD: (%2d) %02x\n", cdblen, cdb[0]); + cdblen = (cdblen < COMMAND_SIZE(cdb[0])) ? COMMAND_SIZE(cdb[0]):cdblen; + + resid = sz; + cmd = (*taskif)->CreateSCSITask(taskif); + if (cmd==NULL) return (errno=ENOMEM),-1; + + (*cmd)->SetCommandDescriptorBlock(cmd,cdb,cdblen); + (*cmd)->SetScatterGatherEntries(cmd,&range,1,sz,dir); + (*cmd)->SetTimeoutDuration(cmd,_timeout*1000); + + if ((*cmd)->ExecuteTaskSync(cmd,&_sense.s,&stat,&bytes) + != kIOReturnSuccess) + errno=EIO, ret=-1; + else if (stat==kSCSITaskStatus_GOOD) + { + resid = sz - bytes; + } + else if (stat==kSCSITaskStatus_CHECK_CONDITION) + { + ret = ERRCODE(_sense.u); + if (ret==0) errno=EIO, ret=-1; + else CREAM_ON_ERRNO(_sense.u); + } + else + { + //SCSIServiceResponse resp; + //(*taskif)->GetSCSIServiceResponse(taskif,&resp); + errno=EIO, ret=-1; + } + + (*cmd)->Release(cmd); + return ret; +} + + +int Scsi_Command::umount(int f) +{ + struct stat sb; + dev_t ref; + int i,n; + + if (f>=0) + { + if (fstat (f,&sb)) return -1; + if (!S_ISCHR(sb.st_mode)) return errno=ENOTBLK,-1; + ref = sb.st_rdev; + // /dev/rdiskN and /dev/diskN have same st_rdev + } + else + { + char bsdname [16]; + CFStringRef devname = (CFStringRef)IORegistryEntrySearchCFProperty ( + scsiob,kIOServicePlane, + CFSTR("BSD Name"), + kCFAllocatorDefault, + kIORegistryIterateRecursively); + if (devname==NULL) return 0; // already exclusive + + sprintf (bsdname,"/dev/%.*s",(int)(sizeof(bsdname)-6), + CFStringGetCStringPtr (devname,0)); + CFRelease (devname); + + if (stat (bsdname,&sb)) return -1; + ref = sb.st_rdev; + } + + if ((n=getfsstat (NULL,0,MNT_NOWAIT)) < 0) return -1; + n += 4, n *= sizeof(struct statfs); + + struct statfs *p = (struct statfs *)alloca(n); + if ((n=getfsstat (p,n,MNT_NOWAIT)) < 0) return -1; + + for (i=0;if_mntfromname,&sb)==0 && + S_ISBLK(sb.st_mode) && + sb.st_rdev==ref) +#if 0 // looks neat, but causes irritaing popups on console... + return unmount (p->f_mntonname,0); +#else + { + int ret=0,rval; + pid_t pid,rpid; + + ret = -1; + if ((pid = fork()) == (pid_t)-1) return -1; + if (pid == 0) // if diskutil will be proven broken, + // don't allow growisofs to be used as + // attack vector... + setuid (getuid ()), + execl ("/usr/sbin/diskutil", + "diskutil","unmount", + p->f_mntonname,(void*)NULL), + exit (errno); + while (1) + { + rpid = waitpid (pid,&rval,0); + if (rpid == (pid_t)-1) + { + if (errno==EINTR) continue; + else break; + } + else if (rpid != pid) + { errno = ECHILD; + break; + } + if (WIFEXITED(rval)) + { + if (WEXITSTATUS(rval) == 0) ret=0; + else errno=EBUSY; // most likely + break; + } + else if (WIFSTOPPED(rval) || WIFCONTINUED(rval)) + continue; + else + { errno = ENOLINK; // some phony errno + break; + } + } + + // diskutil(8) seem to unmount only volfs-managed + // media, so I check if it managed to unmount and + // try the system call if it didn't... + if (ret==0) + { + struct statfs fs; + + if (statfs (p->f_mntonname,&fs)==0 && + !strcmp (fs.f_mntfromname,p->f_mntfromname)) + return unmount (p->f_mntonname,0); + } + return ret; + } +#endif + } + + return 0; // not mounted? +} + +#define RELOAD_NEVER_NEEDED +int Scsi_Command::is_reload_needed(int not_used) +{ return 0; } + + +#else +#error "Unsupported OS" +#endif + diff --git a/lib/qpxtransport/sense.cpp b/lib/qpxtransport/sense.cpp new file mode 100644 index 0000000..51826b3 --- /dev/null +++ b/lib/qpxtransport/sense.cpp @@ -0,0 +1,428 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2007 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include "sense.h" + +int sense2str(int err, char* str) { + strcpy(str,"[unknown error]"); + switch (SK(err)) { + case 0x1: + switch (ASC(err)) { + case 0x0B: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"WARNING"); break; + case 0x01: strcpy(str,"WARNING - SPECIFIED TEMPERATURE EXCEEDED"); break; + case 0x02: strcpy(str,"WARNING - ENCLOSURE DEGRADED"); break; + + default: sprintf(str,"WARNING, ASCQ=%02X",ASCQ(err)); break; + } + break; + case 0x17: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"RECOVERED DATA WITH NO ERROR CORRECTION APPLIED"); break; + case 0x01: strcpy(str,"RECOVERED DATA WITH RETRIES"); break; + case 0x02: strcpy(str,"RECOVERED DATA WITH POSITIVE HEAD OFFSET"); break; + case 0x03: strcpy(str,"RECOVERED DATA WITH NEGATIVE HEAD OFFSET"); break; + case 0x04: strcpy(str,"RECOVERED DATA WITH RETRIES AND/OR CIRC APPLIED"); break; + case 0x05: strcpy(str,"RECOVERED DATA USING PREVIOUS SECTOR ID"); break; + case 0x07: strcpy(str,"RECOVERED DATA WITHOUT ECC - RECOMMEND REASSIGNMENT"); break; + case 0x08: strcpy(str,"RECOVERED DATA WITHOUT ECC - RECOMMEND REWRITE"); break; + case 0x09: strcpy(str,"RECOVERED DATA WITHOUT ECC - DATA REWRITTEN"); break; + + default: strcpy(str,"RECOVERED DATA WITH NO ERROR CORRECTION APPLIED"); break; + } + break; + case 0x18: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"RECOVERED DATA WITH ERROR CORRECTION APPLIED"); break; + case 0x01: strcpy(str,"RECOVERED DATA WITH ERROR CORR. & RETRIES APPLIED"); break; + case 0x02: strcpy(str,"RECOVERED DATA - DATA AUTO-REALLOCATED"); break; + case 0x03: strcpy(str,"RECOVERED DATA WITH CIRC"); break; + case 0x04: strcpy(str,"RECOVERED DATA WITH L-EC"); break; + case 0x05: strcpy(str,"RECOVERED DATA - RECOMMEND REASSIGNMENT"); break; + case 0x06: strcpy(str,"RECOVERED DATA - RECOMMEND REWRITE"); break; + case 0x08: strcpy(str,"RECOVERED DATA WITH LINKING"); break; + + default: strcpy(str,"RECOVERED DATA WITH ERROR CORRECTION APPLIED"); break; + } + break; + case 0x5D: + switch (ASCQ(err)) { + case 0x01: strcpy(str,"FAILURE PREDICTION THRESHOLD EXCEEDED - Predicted Media failure"); break; + case 0x02: strcpy(str,"LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED"); break; + case 0x03: strcpy(str,"FAILURE PREDICTION THRESHOLD EXCEEDED - Predicted Spare Area Exhaustion"); break; + case 0xFF: strcpy(str,"FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)"); break; + + default: strcpy(str,"LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED"); break; + } + break; + case 0x73: + switch (ASCQ(err)) { + case 0x01: strcpy(str,"POWER CALIBRATION AREA ALMOST FULL"); break; + case 0x06: strcpy(str,"RMA/PMA IS ALMOST FULL"); break; + } + break; + } + case 0x2: + switch (ASC(err)) { + case 0x04: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE"); break; + case 0x01: strcpy(str,"LOGICAL UNIT IS IN PROCESS OF BECOMING READY"); break; + case 0x02: strcpy(str,"LOGICAL UNIT NOT READY, INITIALIZING CMD. REQUIRED"); break; + case 0x03: strcpy(str,"LOGICAL UNIT NOT READY, MANUAL INTERVENTION REQUIRED"); break; + case 0x04: strcpy(str,"LOGICAL UNIT NOT READY, FORMAT IN PROGRESS"); break; + case 0x07: strcpy(str,"LOGICAL UNIT NOT READY, OPERATION IN PROGRESS"); break; + case 0x08: strcpy(str,"LOGICAL UNIT NOT READY, LONG WRITE IN PROGRESS"); break; + + default: strcpy(str,"LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE"); break; + } + break; + case 0x30: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"INCOMPATIBLE MEDIUM INSTALLED"); break; + case 0x01: strcpy(str,"CANNOT READ MEDIUM - UNKNOWN FORMAT"); break; + case 0x02: strcpy(str,"CANNOT READ MEDIUM - INCOMPATIBLE FORMAT"); break; + case 0x03: strcpy(str,"CLEANING CARTRIDGE INSTALLED"); break; + case 0x04: strcpy(str,"CANNOT WRITE MEDIUM - UNKNOWN FORMAT"); break; + case 0x05: strcpy(str,"CANNOT WRITE MEDIUM - INCOMPATIBLE FORMAT"); break; + case 0x06: strcpy(str,"CANNOT FORMAT MEDIUM - INCOMPATIBLE MEDIUM"); break; + case 0x07: strcpy(str,"CLEANING FAILURE"); break; + case 0x11: strcpy(str,"CANNOT WRITE MEDIUM - UNSUPPORTED MEDIUM VERSION"); break; + + default: strcpy(str,"INCOMPATIBLE MEDIUM INSTALLED"); break; + } + break; + case 0x3A: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"MEDIUM NOT PRESENT"); break; + case 0x01: strcpy(str,"MEDIUM NOT PRESENT - TRAY CLOSED"); break; + case 0x02: strcpy(str,"MEDIUM NOT PRESENT - TRAY OPEN"); break; + + default: strcpy(str,"MEDIUM NOT PRESENT"); break; + } + break; + case 0x3E: strcpy(str,"LOGICAL UNIT HAS NOT SELF-CONFIGURED YET"); break; /* ASCQ=00: */ + } + break; + case 0x3: + switch (ASC(err)) { + case 0x02: strcpy(str,"NO SEEK COMPLETE"); break; /* ASCQ = 0x00 */ + case 0x06: strcpy(str,"NO REFERENCE POSITION FOUND"); break; + case 0x0C: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"WRITE ERROR"); break; + case 0x07: strcpy(str,"WRITE ERROR - RECOVERY NEEDED"); break; + case 0x08: strcpy(str,"WRITE ERROR - RECOVERY FAILED"); break; + case 0x09: strcpy(str,"WRITE ERROR - LOSS OF STREAMING"); break; + case 0x0A: strcpy(str,"WRITE ERROR - PADDING BLOCKS ADDED"); break; + + default: strcpy(str,"WRITE ERROR"); break; + } + break; + case 0x11: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"UNRECOVERED READ ERROR"); break; + case 0x01: strcpy(str,"READ RETRIES EXHAUSTED"); break; + case 0x02: strcpy(str,"ERROR TOO LONG TO CORRECT"); break; + case 0x05: strcpy(str,"L-EC UNCORRECTABLE ERROR"); break; + case 0x06: strcpy(str,"CIRC UNRECOVERED ERROR"); break; + case 0x0F: strcpy(str,"ERROR READING UPC/EAN NUMBER"); break; + case 0x10: strcpy(str,"ERROR READING ISRC NUMBER"); break; + + default: strcpy(str,"UNRECOVERED READ ERROR"); break; + } + break; + case 0x15: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"RANDOM POSITIONING ERROR"); break; + case 0x01: strcpy(str,"MECHANICAL POSITIONING ERROR"); break; + case 0x02: strcpy(str,"POSITIONING ERROR DETECTED BY READ OF MEDIUM"); break; + + default: strcpy(str,"RANDOM POSITIONING ERROR"); break; + } + break; + case 0x31: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"MEDIUM FORMAT CORRUPTED"); break; + case 0x01: strcpy(str,"FORMAT COMMAND FAILED"); break; + case 0x02: strcpy(str,"ZONED FORMATTING FAILED DUE TO SPARE LINKING"); break; + + default: strcpy(str,"MEDIUM FORMAT CORRUPTED"); break; + } + break; + case 0x51: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"ERASE FAILURE"); break; + case 0x01: strcpy(str,"ERASE FAILURE - INCOMPLETE ERASE OPERATION DETECTED"); break; + + default: strcpy(str,"ERASE FAILURE"); break; + } + break; + case 0x57: strcpy(str,"UNABLE TO RECOVER TABLE-OF-CONTENTS"); break; /* ASCQ = 00 */ + case 0x72: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"SESSION FIXATION ERROR"); break; + case 0x01: strcpy(str,"SESSION FIXATION ERROR WRITING LEAD-IN"); break; + case 0x02: strcpy(str,"SESSION FIXATION ERROR WRITING LEAD-OUT"); break; + + default: strcpy(str,"SESSION FIXATION ERROR"); break; + } + break; + case 0x73: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"CD CONTROL ERROR"); break; + case 0x02: strcpy(str,"POWER CALIBRATION AREA IS FULL"); break; + case 0x03: strcpy(str,"POWER CALIBRATION AREA ERROR"); break; + case 0x04: strcpy(str,"PROGRAM MEMORY AREA UPDATE FAILURE"); break; + case 0x05: strcpy(str,"PROGRAM MEMORY AREA IS FULL"); break; + + default: strcpy(str,"CD CONTROL ERROR"); break; + } + break; + } + break; + case 0x4: + switch (ASC(err)) { + case 0x00: strcpy(str,"CLEANING REQUESTED"); break; /* ASCQ = 0x17 */ + case 0x05: strcpy(str,"LOGICAL UNIT DOES NOT RESPOND TO SELECTION"); break; /* ASCQ = 0x00 */ + case 0x08: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"LOGICAL UNIT COMMUNICATION FAILURE"); break; + case 0x01: strcpy(str,"LOGICAL UNIT COMMUNICATION TIMEOUT"); break; + case 0x02: strcpy(str,"LOGICAL UNIT COMMUNICATION PARITY ERROR"); break; + case 0x03: strcpy(str,"LOGICAL UNIT COMMUNICATION CRC ERROR (ULTRA-DMA/32)"); break; + } + break; + case 0x09: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"TRACK FOLLOWING ERROR"); break; + case 0x01: strcpy(str,"TRACKING SERVO FAILURE"); break; + case 0x02: strcpy(str,"FOCUS SERVO FAILURE"); break; + case 0x03: strcpy(str,"SPINDLE SERVO FAILURE"); break; + case 0x04: strcpy(str,"HEAD SELECT FAULT"); break; + + default: strcpy(str,"TRACKING ERROR"); break; + } + break; + case 0x15: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"RANDOM POSITIONING ERROR"); break; + case 0x01: strcpy(str,"MECHANICAL POSITIONING ERROR"); break; + + default: strcpy(str,"RANDOM POSITIONING ERROR"); break; + } + break; + case 0x1B: strcpy(str,"SYNCHRONOUS DATA TRANSFER ERROR"); break; /* ASCQ = 0x00 */ + case 0x3B: strcpy(str,"MECHANICAL POSITIONING OR CHANGER ERROR"); break; /* ASCQ = 0x16 */ + case 0x3E: + switch (ASCQ(err)) { + case 0x01: strcpy(str,"LOGICAL UNIT FAILURE"); break; + case 0x02: strcpy(str,"TIMEOUT ON LOGICAL UNIT"); break; + + default: strcpy(str,"LOGICAL UNIT FAILURE"); break; + } + break; + case 0x40: strcpy(str,"DIAGNOSTIC FAILURE ON COMPONENT NN (80H-FFH)"); break; + case 0x44: strcpy(str,"INTERNAL TARGET FAILURE"); break; + case 0x46: strcpy(str,"UNSUCCESSFUL SOFT RESET"); break; + case 0x47: strcpy(str,"SCSI PARITY ERROR"); break; + case 0x4A: strcpy(str,"COMMAND PHASE ERROR"); break; + case 0x4B: strcpy(str,"DATA PHASE ERROR"); break; + case 0x4C: strcpy(str,"LOGICAL UNIT FAILED SELF-CONFIGURATION"); break; + case 0x53: strcpy(str,"MEDIA LOAD OR EJECT FAILED"); break; + case 0x65: strcpy(str,"VOLTAGE FAULT"); break; + } + break; + case 0x5: + switch (ASC(err)) { + case 0x07: strcpy(str,"MULTIPLE PERIPHERAL DEVICES SELECTED"); break; /* ASCQ = 0x00 */ + case 0x1A: strcpy(str,"PARAMETER LIST LENGTH ERROR"); break; /* ASCQ = 0x00 */ + case 0x20: strcpy(str,"INVALID COMMAND OPERATION CODE"); break; /* ASCQ = 0x00 */ + case 0x21: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"LOGICAL BLOCK ADDRESS OUT OF RANGE"); break; + case 0x01: strcpy(str,"INVALID ELEMENT ADDRESS"); break; + case 0x02: strcpy(str,"INVALID ADDRESS FOR WRITE"); break; + + default: strcpy(str,"LOGICAL BLOCK ADDRESS OUT OF RANGE"); break; + } + break; + case 0x24: strcpy(str,"INVALID FIELD IN CDB"); break; + case 0x25: strcpy(str,"LOGICAL UNIT NOT SUPPORTED"); break; + case 0x26: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"INVALID FIELD IN PARAMETER LIST"); break; + case 0x01: strcpy(str,"PARAMETER NOT SUPPORTED"); break; + case 0x02: strcpy(str,"PARAMETER VALUE INVALID"); break; + case 0x03: strcpy(str,"THRESHOLD PARAMETERS NOT SUPPORTED"); break; + } + break; + case 0x2B: strcpy(str,"COPY CANNOT EXECUTE SINCE INITIATOR CANNOT DISCONNECT"); break; /* ASCQ = 0x00 */ + case 0x2C: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"COMMAND SEQUENCE ERROR"); break; + case 0x03: strcpy(str,"CURRENT PROGRAM AREA IS NOT EMPTY"); break; + case 0x04: strcpy(str,"CURRENT PROGRAM AREA IS EMPTY"); break; + } + break; + case 0x30: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"INCOMPATIBLE MEDIUM INSTALLED"); break; + case 0x01: strcpy(str,"CANNOT READ MEDIUM - UNKNOWN FORMAT"); break; + case 0x02: strcpy(str,"CANNOT READ MEDIUM - INCOMPATIBLE FORMAT"); break; + case 0x03: strcpy(str,"CLEANING CARTRIDGE INSTALLED"); break; + case 0x04: strcpy(str,"CANNOT WRITE MEDIUM - UNKNOWN FORMAT"); break; + case 0x05: strcpy(str,"CANNOT WRITE MEDIUM - INCOMPATIBLE FORMAT"); break; + case 0x06: strcpy(str,"CANNOT FORMAT MEDIUM - INCOMPATIBLE MEDIUM"); break; + case 0x07: strcpy(str,"CLEANING FAILURE"); break; + case 0x08: strcpy(str,"CANNOT WRITE - APPLICATION CODE MISMATCH"); break; + case 0x09: strcpy(str,"CURRENT SESSION NOT FIXATED FOR APPEND"); break; + case 0x10: strcpy(str,"MEDIUM NOT FORMATTED"); break; + } + break; + case 0x39: strcpy(str,"SAVING PARAMETERS NOT SUPPORTED"); break; /* ASCQ = 0x00 */ + case 0x3D: strcpy(str,"INVALID BITS IN IDENTIFY MESSAGE"); break; /* ASCQ = 0x00 */ + case 0x43: strcpy(str,"MESSAGE ERROR"); break; /* ASCQ = 0x00 */ + case 0x53: strcpy(str,"MEDIUM REMOVAL PREVENTED"); break; /* ASCQ = 0x02 */ + case 0x64: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"ILLEGAL MODE FOR THIS TRACK"); break; + case 0x01: strcpy(str,"INVALID PACKET SIZE"); break; + } + break; + case 0x6F: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"COPY PROTECTION KEY EXCHANGE FAILURE - AUTHENTICATION FAILURE"); break; + case 0x01: strcpy(str,"COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT PRESENT"); break; + case 0x02: strcpy(str,"COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT ESTABLISHED"); break; + case 0x03: strcpy(str,"READ OF SCRAMBLED SECTOR WITHOUT AUTHENTICATION"); break; + case 0x04: strcpy(str,"MEDIA REGION CODE IS MISMATCHED TO LOGICAL UNIT REGION"); break; + case 0x05: strcpy(str,"LOGICAL UNIT REGION MUST BE PERMANENT/REGION RESET COUNT ERROR"); break; + } + break; + case 0x72: + switch (ASCQ(err)) { + case 0x03: strcpy(str,"SESSION FIXATION ERROR . INCOMPLETE TRACK IN SESSION"); break; + case 0x04: strcpy(str,"EMPTY OR PARTIALLY WRITTEN RESERVED TRACK"); break; + case 0x05: strcpy(str,"NO MORE TRACK RESERVATIONS ALLOWED"); break; + } + break; + } + break; + case 0x6: + switch (ASC(err)) { + case 0x28: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED"); break; + case 0x01: strcpy(str,"IMPORT OR EXPORT ELEMENT ACCESSED"); break; + } + break; + case 0x29: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"POWER ON, RESET, OR BUS DEVICE RESET OCCURRED"); break; + case 0x01: strcpy(str,"POWER ON OCCURRED"); break; + case 0x02: strcpy(str,"BUS RESET OCCURRED"); break; + case 0x03: strcpy(str,"BUS DEVICE RESET FUNCTION OCCURRED"); break; + case 0x04: strcpy(str,"DEVICE INTERNAL RESET"); break; + } + break; + case 0x2A: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"PARAMETERS CHANGED"); break; + case 0x01: strcpy(str,"MODE PARAMETERS CHANGED"); break; + case 0x02: strcpy(str,"LOG PARAMETERS CHANGED"); break; + case 0x03: strcpy(str,"RESERVATIONS PREEMPTED"); break; + } + break; + case 0x2E: strcpy(str,"INSUFFICIENT TIME FOR OPERATION"); break; + case 0x2F: strcpy(str,"COMMANDS CLEARED BY ANOTHER INITIATOR"); break; + case 0x3B: + switch (ASCQ(err)) { + case 0x0D: strcpy(str,"MEDIUM DESTINATION ELEMENT FULL"); break; + case 0x0E: strcpy(str,"MEDIUM SOURCE ELEMENT EMPTY"); break; + case 0x0F: strcpy(str,"END OF MEDIUM REACHED"); break; + case 0x11: strcpy(str,"MEDIUM MAGAZINE NOT ACCESSIBLE"); break; + case 0x12: strcpy(str,"MEDIUM MAGAZINE REMOVED"); break; + case 0x13: strcpy(str,"MEDIUM MAGAZINE INSERTED"); break; + case 0x14: strcpy(str,"MEDIUM MAGAZINE LOCKED"); break; + case 0x15: strcpy(str,"MEDIUM MAGAZINE UNLOCKED"); break; + } + break; + case 0x3F: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"TARGET OPERATING CONDITIONS HAVE CHANGED"); break; + case 0x01: strcpy(str,"MICROCODE HAS BEEN CHANGED"); break; + case 0x02: strcpy(str,"CHANGED OPERATING DEFINITION"); break; + case 0x03: strcpy(str,"INQUIRY DATA HAS CHANGED"); break; + } + break; + case 0x5A: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"OPERATOR REQUEST OR STATE CHANGE INPUT"); break; + case 0x01: strcpy(str,"OPERATOR MEDIUM REMOVAL REQUEST"); break; + case 0x02: strcpy(str,"OPERATOR SELECTED WRITE PROTECT"); break; + case 0x03: strcpy(str,"OPERATOR SELECTED WRITE PERMIT"); break; + } + break; + case 0x5B: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"LOG EXCEPTION"); break; + case 0x01: strcpy(str,"THRESHOLD CONDITION MET"); break; + case 0x02: strcpy(str,"LOG COUNTER AT MAXIMUM"); break; + case 0x03: strcpy(str,"LOG LIST CODES EXHAUSTED"); break; + } + break; + case 0x5E: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"LOW POWER CONDITION ON"); break; + case 0x01: strcpy(str,"IDLE CONDITION ACTIVATED BY TIMER"); break; + case 0x02: strcpy(str,"STANDBY CONDITION ACTIVATED BY TIMER"); break; + case 0x03: strcpy(str,"IDLE CONDITION ACTIVATED BY COMMAND"); break; + case 0x04: strcpy(str,"STANDBY CONDITION ACTIVATED BY COMMAND"); break; + } + break; + } + break; + case 0x7: + switch (ASC(err)) { + case 0x27: + switch (ASCQ(err)) { + case 0x00: strcpy(str,"WRITE PROTECTED"); break; + case 0x01: strcpy(str,"HARDWARE WRITE PROTECTED"); break; + case 0x02: strcpy(str,"LOGICAL UNIT SOFTWARE WRITE PROTECTED"); break; + case 0x03: strcpy(str,"ASSOCIATED WRITE PROTECT"); break; + case 0x04: strcpy(str,"PERSISTENT WRITE PROTECT"); break; + case 0x05: strcpy(str,"PERMANENT WRITE PROTECT"); break; + case 0x06: strcpy(str,"CONDITIONAL WRITE PROTECT"); break; + + default: strcpy(str,"WRITE PROTECTED"); break; + } + break; + } + break; + case 0x8: strcpy(str,"BLANK CHECK"); break; + case 0xB: + switch (ASC(err)) { + case 0x00: strcpy(str,"I/O PROCESS TERMINATED"); break; /* ASCQ = 06 */ + case 0x11: strcpy(str,"READ ERROR - LOSS OF STREAMING"); break; /* ASCQ = 11 */ + case 0x45: strcpy(str,"SELECT OR RESELECT FAILURE"); break; /* ASCQ = 00 */ + case 0x48: strcpy(str,"INITIATOR DETECTED ERROR MESSAGE RECEIVED"); break; /* ASCQ = 00 */ + case 0x49: strcpy(str,"INVALID MESSAGE ERROR"); break; /* ASCQ = 00 */ + case 0x4D: strcpy(str,"TAGGED OVERLAPPED COMMANDS (NN = QUEUE TAG)"); break; /* ASCQ = xx */ + } + break; + } + return 0; +} + diff --git a/lib/qpxtransport/threads.cpp b/lib/qpxtransport/threads.cpp new file mode 100644 index 0000000..9d55bb3 --- /dev/null +++ b/lib/qpxtransport/threads.cpp @@ -0,0 +1,308 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +#include +#include "threads.h" + +#include + +/* + * sync_pipe_add_arg & protect_arg functions from ethereal + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * +*/ + +/* Append an arg (realloc) to an argc/argv array */ +/* (add a string pointer to a NULL-terminated array of string pointers) */ +char ** +add_arg(char **args, int *argc, const char *arg) +{ + /* Grow the array; "*argc" currently contains the number of string + pointers, *not* counting the NULL pointer at the end, so we have + to add 2 in order to get the new size of the array, including the + new pointer and the terminating NULL pointer. */ + args = (char**) realloc( (void*) args, (*argc + 2) * sizeof (char *)); + + /* Stuff the pointer into the penultimate element of the array, which + is the one at the index specified by "*argc". */ + args[*argc] = (char*) arg; + + /* Now bump the count. */ + (*argc)++; + + /* We overwrite the NULL pointer; put it back right after the + element we added. */ + args[*argc] = NULL; + + return args; +} + +#ifdef _WIN32 +/* Quote the argument element if necessary, so that it will get + * reconstructed correctly in the C runtime startup code. Note that + * the unquoting algorithm in the C runtime is really weird, and + * rather different than what Unix shells do. See stdargv.c in the C + * runtime sources (in the Platform SDK, in src/crt). + * + * Stolen from GLib's protect_argv(), an internal routine that quotes + * string in an argument list so that they arguments will be handled + * correctly in the command-line string passed to CreateProcess() + * if that string is constructed by gluing those strings together. + */ +char * +protect_arg (const char *argv) +{ + char *new_arg; + const char *p = argv; + char *q; + int len = 0; + bool need_dblquotes = FALSE; + + while (*p) { + if (*p == ' ' || *p == '\t') + need_dblquotes = TRUE; + else if (*p == '"') + len++; + else if (*p == '\\') { + const char *pp = p; + + while (*pp && *pp == '\\') + pp++; + if (*pp == '"') + len++; + } + len++; + p++; + } + + q = new_arg = (char*) malloc (len + need_dblquotes*2 + 1); + p = argv; + + if (need_dblquotes) + *q++ = '"'; + + while (*p) { + if (*p == '"') + *q++ = '\\'; + else if (*p == '\\') { + const char *pp = p; + + while (*pp && *pp == '\\') + pp++; + if (*pp == '"') + *q++ = '\\'; + } + *q++ = *p; + p++; + } + + if (need_dblquotes) + *q++ = '"'; + *q++ = '\0'; + + return new_arg; +} + +void string_append_c(char* args, char c) +{ + int slen = strlen(args); + args[slen] = c; + args[slen+1] = 0; +} + +void string_append(char* args, char* arg) +{ + int slen = strlen(args); + int alen = strlen(arg); + memcpy(args+slen, arg, alen); + args[slen+alen] = 0; +} + +int close(HANDLE h) { CloseHandle(h); } +#endif + + +#if defined (__unix) || defined (__unix__) + +Mutex::Mutex() { pthread_mutex_init(&m, NULL); } +Mutex::~Mutex() { pthread_mutex_destroy(&m); } +void Mutex::lock() { +// printf("%p mutex.lock()\n", this); + pthread_mutex_lock(&m); +} + +void Mutex::unlock() { +// printf("%p mutex.unlock()\n", this); + pthread_mutex_unlock(&m); +} + +#elif defined (_WIN32) + +Mutex::Mutex() { InitializeCriticalSection(&m); } +Mutex::~Mutex() { DeleteCriticalSection(&m); } +void Mutex::lock() { EnterCriticalSection(&m); } +void Mutex::unlock() { LeaveCriticalSection(&m); } + +int WIN32_thread_create(HANDLE *tid, void *attr, void*(*func)(void*), void* arg) +{ + *tid = CreateThread(NULL,0, (DWORD WINAPI (*)(void*)) func,arg,0,NULL); + if (!(*tid)) return 1; + else return 0; +}; + +int WIN32_thread_join(HANDLE& tid, void **ret) +{ + DWORD tret; + if (WaitForSingleObject(tid, INFINITE) == WAIT_FAILED) return -1; + if (ret) { + if (!GetExitCodeThread(tid, &tret)) return -1; + *ret = (void*) tret; + } + CloseHandle(tid); + tid = NULL; + return 0; +}; + +#endif + +//int createchild(char **argv, pipe_t &rdpipe, bool r, pipe_t &wrpipe, bool w) +int createChildProcess(char **argv, pipe_t *rdpipe, pipe_t *wrpipe) +{ + printf("createchild(): pipes: %p, %p\n", rdpipe, wrpipe); + +#if defined (__unix) || defined (__unix__) + int cpid; + + if (rdpipe) { + if (pipe(*rdpipe)) { + printf("Can't create pipe for stdout/stderr\n"); + return -1; + } + printf("rdpipe = %d, %d\n", (*rdpipe)[0], (*rdpipe)[1]); + } + + if (wrpipe) { + if (pipe(*wrpipe)) { + printf("Can't create pipe for stdout/stderr\n"); + if (rdpipe) { close(*rdpipe[0]); close(*rdpipe[1]); } + return -1; + } + printf("wrpipe = %d, %d\n", (*wrpipe)[0], (*wrpipe)[1]); + } + + if ((cpid = fork()) == -1) { + printf("Can't fork()\n"); + if (rdpipe) { close((*rdpipe)[0]); close((*rdpipe)[1]); } + if (wrpipe) { close((*wrpipe)[0]); close((*wrpipe)[1]); } + return -1; + } + + if (!cpid) { + printf("child: fork() ok:)\n"); + if (rdpipe) { + close((*rdpipe)[0]); // unused read end + close(STDOUT_FILENO); + close(STDERR_FILENO); + dup2((*rdpipe)[1], STDOUT_FILENO); + dup2((*rdpipe)[1], STDERR_FILENO); + } + if (wrpipe) { + close((*wrpipe)[1]); // unused write end + close(STDIN_FILENO); + dup2((*wrpipe)[0], STDIN_FILENO); + } + printf("starting %s...\n", argv[0]); + execvp(argv[0], argv); + printf("Can't exec() %s: %s\n", argv[0], strerror(errno)); + _exit(0); + } else { + if (rdpipe) close((*rdpipe)[1]); // unused write end + if (wrpipe) close((*wrpipe)[0]); // unused read end + } + return cpid; + +#elif defined (_WIN32) + char args[1024] = ""; + char *quoted_arg; + SECURITY_ATTRIBUTES sa; + STARTUPINFO si; + PROCESS_INFORMATION pi; + HANDLE ipiper; + HANDLE ipipew; + HANDLE opiper; + HANDLE opipew; + + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + if (rdpipe) { + if (!CreatePipe(&ipiper, &ipipew, &sa, 5120)) + { + printf("Can't create pipe for stdout/stderr\n"); + return -1; + } + // printf("rdpipeH = %d, %d\n", (int)ipiper, (int)ipipew); + (*rdpipe)[0] = _open_osfhandle((long)ipiper, _O_BINARY); + (*rdpipe)[1] = _open_osfhandle((long)ipipew, _O_BINARY); + printf("rdpipeF = %d, %d\n", (*rdpipe)[0], (*rdpipe)[1]); + } + + if (wrpipe) { + if (!CreatePipe(&opiper, &opipew, &sa, 5120)) + { + if (rdpipe) { close((*rdpipe)[0]); close((*rdpipe)[1]); } + printf("Can't create pipe for stdin\n"); + return -1; + } + // printf("wrpipeH = %d, %d\n", (int)opipew, (int)opipew); + (*wrpipe)[0] = _open_osfhandle((long)opiper, _O_BINARY); + (*wrpipe)[1] = _open_osfhandle((long)opipew, _O_BINARY); + printf("wrpipeF = %d, %d\n", (*wrpipe)[0], (*wrpipe)[1]); + } + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; /* this hides the console window */ + if (wrpipe) si.hStdInput = opiper; + if (rdpipe) { si.hStdOutput = ipipew; si.hStdError = ipipew; } + + for(int i=0; argv[i] != 0; i++) { + if(i != 0) string_append_c(args, ' '); // don't prepend a space before the path!!! + quoted_arg = protect_arg(argv[i]); + string_append(args, quoted_arg); + free(quoted_arg); + } + + if(!CreateProcessA(NULL, args, NULL, NULL, TRUE, + CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { + if (rdpipe) { close((*rdpipe)[0]); close((*rdpipe)[1]); } + if (wrpipe) { close((*wrpipe)[0]); close((*wrpipe)[1]); } + errno = GetLastError(); + printf("Can't CreateProcess():\n%s\nERR: [%d] %s\n", args, errno, strerror(errno)); + return -1; + } + + if (rdpipe) close((*rdpipe)[1]); + if (wrpipe) close((*wrpipe)[0]); + return (int) pi.hProcess; +#endif +} + diff --git a/lib/qpxyamaha/Makefile b/lib/qpxyamaha/Makefile new file mode 100644 index 0000000..3d5b567 --- /dev/null +++ b/lib/qpxyamaha/Makefile @@ -0,0 +1,14 @@ +SRC = yamaha_features +HDRS = include/yamaha_features.h +LIBN = qpxyamaha +SRCS = $(patsubst %,%.cpp, $(SRC)) +OBJS = $(patsubst %.cpp,%.o,$(SRCS)) + +VER_MAJOR = 0 +VER_MINOR = 7 +VER_MICRO = 0 + +LDLIBS += -lqpxtransport -L../lib + +include ../Makefile.lib + diff --git a/lib/qpxyamaha/include/yamaha_features.h b/lib/qpxyamaha/include/yamaha_features.h new file mode 100644 index 0000000..154403c --- /dev/null +++ b/lib/qpxyamaha/include/yamaha_features.h @@ -0,0 +1,31 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2007 Gennady "ShultZ" Kozlov + * + * 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 __YAMAHA_FEATURES_H +#define __YAMAHA_FEATURES_H + +typedef char tattoo_row[3744]; + +// AMQR +extern int yamaha_check_amqr(drive_info* drive); +extern int yamaha_set_amqr(drive_info* drive); + +// Force Speed +extern int yamaha_check_forcespeed(drive_info* drive); +extern int yamaha_set_forcespeed(drive_info* drive); + +// DiscT@2 +extern int yamaha_f1_get_tattoo(drive_info* drive); +extern int yamaha_f1_do_tattoo(drive_info* drive, unsigned char *iimage, uint32_t tsize); +#endif + diff --git a/lib/qpxyamaha/yamaha_features.cpp b/lib/qpxyamaha/yamaha_features.cpp new file mode 100644 index 0000000..871a88e --- /dev/null +++ b/lib/qpxyamaha/yamaha_features.cpp @@ -0,0 +1,288 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2007 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +#include +//#include +#include + +#include +#include +#include "yamaha_features.h" + +int yamaha_check_amqr(drive_info* drive) { +// unsigned char len=0; + unsigned int ilen=0; + int offs=0; + int r; +// int i; + + mode_sense(drive, MODE_PAGE_WRITE_PARAMETERS, 00, 256); + ilen = ntoh16u (drive->rd_buf); +/* + printf("data len: %4X (%4d), Header:\n", ilen, ilen); + for (i=0; i<8; i++) printf(" %02X",drive->rd_buf[i] & 0xFF); printf("\n"); +*/ + + offs=0; while (((drive->rd_buf[offs]) & 0x3F) != MODE_PAGE_WRITE_PARAMETERS) offs++; +/* len=drive->rd_buf[offs+1]; + + printf("Page:\n"); + for (i=0; i<(len+2); i++) { if (!(i%8)) printf("\n"); printf(" %02X",drive->rd_buf[i+8] & 0xFF); } + printf("\n"); +*/ + + if (!ilen) return 1; +// drive->silent++; + +/* + drive->rd_buf[offs+2] &= 0xB0; + drive->rd_buf[offs+2] |= 0x08; + drive->rd_buf[offs+3] &= 0xF0; + drive->rd_buf[offs+4] &= 0xF0; + drive->rd_buf[offs+8] = 0x00; +*/ + +/* + drive->rd_buf[offs+2] = 0x32; + + drive->rd_buf[offs+2] = 0x88; + drive->rd_buf[offs+3] = 0x00; + drive->rd_buf[offs+4] = 0x00; + drive->rd_buf[offs+8] = 0x00; +*/ + r = mode_select(drive, ilen); +// drive->silent--; + return r; +} + +int yamaha_set_amqr(drive_info* drive) { + return 1; +} + +int yamaha_check_forcespeed(drive_info* drive) { + int offs; + int speed; + int speed_wr; + mode_sense(drive, MODE_PAGE_MM_CAP_STATUS, 00, 256); + offs=0; while (((drive->rd_buf[offs]) & 0x3F) != MODE_PAGE_MM_CAP_STATUS) offs++; + speed = ntoh16(drive->rd_buf+offs+14); + speed_wr = ntoh16(drive->rd_buf+offs+28); + + drive->cmd[0] = MMC_SET_SPEED; + drive->cmd[1] = 0x01; + drive->cmd[2] = (speed >> 8) & 0xFF; + drive->cmd[3] = speed & 0xFF; +// drive->cmd[4] = 0xFF; +// drive->cmd[5] = 0xFF; + drive->cmd[4] = (speed_wr >> 8) & 0xFF; + drive->cmd[5] = speed_wr & 0xFF; + drive->cmd[11] = 0x80; + if ((drive->err=drive->cmd.transport(NONE,NULL,0) )) { return (drive->err); } + return 0; +} + +int yamaha_set_forcespeed(drive_info* drive) { + int speed = 0xFFFF; + int speed_wr = 0xFFFF; + if (drive->parms.read_speed_kb) speed = drive->parms.read_speed_kb; + if (drive->parms.write_speed_kb) speed_wr = drive->parms.write_speed_kb; + + drive->cmd[0] = MMC_SET_SPEED; + drive->cmd[1] = 0x01; + drive->cmd[2] = (speed >> 8) & 0xFF; + drive->cmd[3] = speed & 0xFF; +// drive->cmd[4] = 0xFF; +// drive->cmd[5] = 0xFF; + drive->cmd[4] = (speed_wr >> 8) & 0xFF; + drive->cmd[5] = speed_wr & 0xFF; + drive->cmd[11] = 0x80; + if ((drive->err=drive->cmd.transport(NONE,NULL,0) )) { return (drive->err); } + return 0; +} + +int yamaha_f1_get_tattoo(drive_info* drive) { + unsigned int ilen=0; + int offs=0; + drive->yamaha.tattoo_i=0; + drive->yamaha.tattoo_o=0; + drive->yamaha.tattoo_rows=0; + + drive->silent++; + mode_sense(drive, MODE_PAGE_YAMAHA_TATTOO, 00, 256); + drive->silent--; + if (drive->err) { + if (drive->err == 0x52400) return 1; + return 0; + } + + ilen = ntoh16u (drive->rd_buf); + if (ilen < 22) return 1; + offs=0; while (((drive->rd_buf[offs]) & 0x3F) != MODE_PAGE_YAMAHA_TATTOO) offs++; +/* + unsigned char len=0; + int r; + int i; + printf("data len: %4X (%4d), Header:\n", ilen, ilen); + for (i=0; i<8; i++) printf(" %02X",drive->rd_buf[i] & 0xFF); printf("\n"); + + len=drive->rd_buf[offs+1]; + + printf("Page:"); + for (i=0; i<(len+2); i++) { if (!(i%8)) printf("\n"); printf(" %02X",drive->rd_buf[i+8] & 0xFF); } + printf("\n"); +*/ + drive->yamaha.tattoo_i = (drive->rd_buf[offs+4] << 16) | (drive->rd_buf[offs+5] << 8) | drive->rd_buf[offs+6]; + drive->yamaha.tattoo_o = (drive->rd_buf[offs+7] << 16) | (drive->rd_buf[offs+8] << 8) | drive->rd_buf[offs+9]; + drive->yamaha.tattoo_rows = drive->yamaha.tattoo_o - drive->yamaha.tattoo_i; + return 0; +} + +int yamaha_write(drive_info* drive, char mode, char bufid, int offs, int plen) { +// printf("Mode: %d, BufID: %d, offs: %d, plen: %d, blen: %d\n", mode, bufid, offs, plen, blen); +// drive->cmd[0] = SPC_WRITE_BUFFER; + drive->cmd[0] = 0x3B; + drive->cmd[1] = mode & 7; + drive->cmd[2] = bufid; +// drive->cmd[1] = 0x01; +// drive->cmd[2] = 0; + + drive->cmd[3] = (offs >> 16) & 0xFF; + drive->cmd[4] = (offs >> 8) & 0xFF; + drive->cmd[5] = offs & 0xFF; + + drive->cmd[6] = (plen >> 16) & 0xFF; + drive->cmd[7] = (plen >> 8) & 0xFF; + drive->cmd[8] = plen & 0xFF; + if ((drive->err=drive->cmd.transport(WRITE,drive->rd_buf, plen << 11) )) + { if (!drive->silent) sperror ("YAMAHA_WRITE", drive->err); return (drive->err); } + return 0; +} + + +int yamaha_set_tattoo_speed(drive_info* drive) { + int speed = 0x06E5; + int speed_wr = 0x02C2; + drive->cmd[0] = MMC_SET_SPEED; + drive->cmd[1] = 0x00; + drive->cmd[2] = (speed >> 8) & 0xFF; + drive->cmd[3] = speed & 0xFF; + drive->cmd[4] = (speed_wr >> 8) & 0xFF; + drive->cmd[5] = speed_wr & 0xFF; + if ((drive->err=drive->cmd.transport(NONE,NULL,0) )) + { if (!drive->silent) sperror ("YAMAHA_SET_SPEED",drive->err); return (drive->err); } + return 0; +} + +int yamaha_f1_do_tattoo(drive_info* drive, unsigned char *iimage, uint32_t bsize){ + uint32_t maxbuf = 20480; + char *crow; + char *imagec; + uint32_t i,j; + uint32_t tattoo_size; + uint32_t blen = 2048; + + drive->parms.status |= STATUS_LOCK; + set_lock(drive); + + yamaha_f1_get_tattoo(drive); + + if (!drive->yamaha.tattoo_rows) { + printf("No space left on CD! Can't write zero size tattoo!\n"); + drive->parms.status &= (~STATUS_LOCK); + set_lock(drive); + return 2; + } + + tattoo_size = drive->yamaha.tattoo_rows * sizeof (tattoo_row); + if (iimage) { +// image = (tattoo_row*) iimage; + if (bsize != tattoo_size) { + printf("yamaha_f1_do_tattoo(): RAW image size must be exactly %u bytes (3744x%d)\n", + tattoo_size,drive->yamaha.tattoo_rows); + return 1; + } + imagec = (char*) iimage; + } else { + printf("yamaha_f1_do_tattoo(): got no image buffer! writing test image...\n"); +// image = (tattoo_row*) malloc( tattoo_size ); + imagec = (char*) malloc( tattoo_size ); + for (i=0; iyamaha.tattoo_rows; i++) { + for (j=0; j< sizeof(tattoo_row); j++) { + if (j<20) imagec[i*3744+j]=0xFF; + else if ((j>1000) && (j<1040)) imagec[i*3744+j]=0xFF; + else imagec[i*3744+j]=0; +// image[i][j]=0xFF; + } + } + } + + drive->parms.status |= STATUS_LOCK; + set_lock(drive); + + yamaha_set_tattoo_speed(drive); + + printf("Sending T@2 data (%d rows)...\n", drive->yamaha.tattoo_rows); + +// int b=0; + crow = imagec; + i = 0; + + while (ird_buf, 0, maxbuf); + blen = std::min(maxbuf, tattoo_size-i); + memcpy(drive->rd_buf, crow+i, blen); +// printf("block #%2d: %5d bytes / %2d sect\n",b,blen,blen/2048); + +// printf("block %d\n",i); + if (!i) { + if(yamaha_write(drive, 1, 0, drive->yamaha.tattoo_i, maxbuf/2048)) goto tattoo_err; + } else { + // blen = (blen+2047) / 2048 * 2048; + // yamaha_write(drive, 1, 0, 0, 10, blen); + if(yamaha_write(drive, 1, 0, 0, maxbuf/2048)) goto tattoo_err; + } +// b++; + printf("."); + i+=maxbuf; + } + if (yamaha_write(drive, 1, 0, drive->yamaha.tattoo_o, 0)) goto tattoo_err; + printf(".\n"); + + printf("Burning T@2...\n"); +// printf("\nwait_unit_ready()...\n"); + if (wait_unit_ready(drive, 1000)) printf("Error %05X...\n", drive->err); +// printf("\nwait_fix()...\n"); + if (wait_fix(drive, 1000)) printf("Error %05X...\n", drive->err); + + printf("\nDone!\n"); + goto tattoo_done; + +tattoo_err: + sperror("Error writing T@2", drive->err); + +tattoo_done: + drive->parms.status &= (~STATUS_LOCK); + set_lock(drive); + + if (drive->parms.status & STATUS_LOCK) + { drive->parms.status &= (~STATUS_LOCK); + set_lock(drive); } + load_eject(drive, false, false); + + if (!iimage) free(imagec); + return 0; +} + diff --git a/man/Makefile b/man/Makefile new file mode 100644 index 0000000..020ca18 --- /dev/null +++ b/man/Makefile @@ -0,0 +1,56 @@ +MAN1DIR = $(DESTDIR)$(MANDIR)/man1 +MAN8DIR = $(DESTDIR)$(MANDIR)/man8 + +all: f1tattoo.1.gz pxfw.8.gz cdvdcontrol.1.gz qscan.1.gz qscand.1.gz qpxtool.1.gz readdvd.1.gz + +f1tattoo.1.gz: + gzip -c f1tattoo.1 > f1tattoo.1.gz + +pxfw.8.gz: + gzip -c pxfw.8 > pxfw.8.gz + +cdvdcontrol.1.gz: + gzip -c cdvdcontrol.1 > cdvdcontrol.1.gz + +qscan.1.gz: + gzip -c qscan.1 > qscan.1.gz + +qscand.1.gz: + gzip -c qscand.1 > qscand.1.gz + +qpxtool.1.gz: + gzip -c qpxtool.1 > qpxtool.1.gz + +readdvd.1.gz: + gzip -c readdvd.1 > readdvd.1.gz + +clean: + rm -f f1tattoo.1.gz + rm -f pxfw.8.gz + rm -f cdvdcontrol.1.gz + rm -f qscan.1.gz + rm -f qscand.1.gz + rm -f qpxtool.1.gz + rm -f readdvd.1.gz + +install: f1tattoo.1.gz pxfw.8.gz cdvdcontrol.1.gz qscan.1.gz qscand.1.gz qpxtool.1.gz readdvd.1.gz + mkdir -p $(MAN1DIR) + mkdir -p $(MAN8DIR) + install -m 0644 f1tattoo.1.gz $(MAN1DIR) + install -m 0644 pxfw.8.gz $(MAN8DIR) + install -m 0644 cdvdcontrol.1.gz $(MAN1DIR) + install -m 0644 qscan.1.gz $(MAN1DIR) + install -m 0644 qscand.1.gz $(MAN1DIR) + install -m 0644 qpxtool.1.gz $(MAN1DIR) + install -m 0644 readdvd.1.gz $(MAN1DIR) + +uninstall: + rm -f $(MAN1DIR)/f1tattoo.1.gz + rm -f $(MAN8DIR)/pxfw.8.gz + rm -f $(MAN1DIR)/cdvdcontrol.1.gz + rm -f $(MAN1DIR)/qscan.1.gz + rm -f $(MAN1DIR)/qscand.1.gz + rm -f $(MAN1DIR)/qpxtool.1.gz + rm -f $(MAN1DIR)/readdvd.1.gz + +.PHONY: all clean install uninstall diff --git a/man/cdvdcontrol.1 b/man/cdvdcontrol.1 new file mode 100644 index 0000000..74848ec --- /dev/null +++ b/man/cdvdcontrol.1 @@ -0,0 +1,423 @@ +.\" cdvdcontrol is released under the GNU GENERAL PUBLIC LICENSE. +.TH "cdvdcontrol" "1" "26 Feb 2014" +.SH "NAME" +cdvdcontrol \- Tool to get full control on your Plextor optical device +.SH "SYNOPSIS" +\fBcdvdcontrol \-l\fR +.br +\fBcdvdcontrol [\-d DEVICE] [OPTION]\fR +.SH "DESCRIPTION" +pxcontrol gives you access on all features that are supported by your Plextor drive. This tool is what you want, when you use optical devices which were manufactured by Plextor. + +\fB\-l\fR scan busses for all available optical devices +.br +.SH "FEATURES" +cdvdcontrol supports the following features: \fBAutoStrategy, BitSetting, DiagnoseMode, GigaRec, Hide-CDR, +MediaLock, PlexEraser, PoweRec, SecuRec, SilentMode, SingleSession, Simulation, SpeedRead and VariRec\fR. + +The available features depend on your drive. Take a look at http://www.plextor.com or simply read the glossar on: +.br +http://qpxtool.sourceforge.net/glossar.html to see supported features. + +.SH "DEVICE" +can be an IDE, SCSI, SATA, USB or FireWire connected optical drive. Not all SATA controller support all Plextor features. + +\fBLinux:\fR +.br +.I /dev/hdX: +IDE device +.br +.I /dev/scdX: +Linux 2.4: SATA, SCSI, USB device, or IDE device via ide-scsi emulation +.br +.I /dev/srX: +Linux 2.6: IDE device via new ATA layer, SCSI or USB device +.br + +\fBOpenBSD/NetBSD:\fR +.br +.I /dev/rcdX +.br + +\fBFreeBSD:\fR +.br +.I /dev/cd: +SCSI device +.br +.I /dev/acd: +ATA device +.br + +\fBMacOS X:\fR +.br +.I /dev/disk: +.br + +\fBwin32:\fR +.br +.I C:,D:,E:, ... X:,Y:,Z: +.br + +.SH "AutoStrategy" +AutoStrategy (AS) is a Feature to determine a writing strategy for an inserted empty DVD media. Writing strategies are saved in the AutoStrategy DataBase (ASDB). This command needs a specified \fBDEVICE\fR. + +.B \-\-as-mode +.I mode +.br + select the AutoStrategy mode + +\fB auto\fR drive selects the strategy from the ASDB or from +.br + the firmware. If there is no entry available, a +.br + new one is created. +.br +\fB forced\fR creates a new ASDB entry, even if there is already +.br + one available in the ASDB. +.br +\fB on\fR an existing AS from the DB is used. If there is no + entry, the strategy from the firmware is used. +.br +\fB off\fR only strategy from the firmware is used. If there + is no entry available, the default strategy is used. + +.B \-\-as-list + view complete ASDB list. + +.B \-\-as-on +.I # +.br + activate ASDB entry number X (value between 1\-10). + +.B \-\-as-off +.I # +.br + deactivate ASDB entry number X (value between 1\-10). + +.B \-\-as-del +.I # +.br + delete ASDB entry number X (value between 1\-10). + +.B \-\-as-clear +.br + remove all ASDB entries form the ASDB. + +.B \-\-as-create +.I mode +.br + creates an AS database entry. +.br + Mode should be combination if [q|f] and [a|r] + +\fB q\fR quick AS creating. +.br +\fB f\fR full AS creating. Media is written. +.br +\fB a\fR AS is appended to the list in the ASDB. +.br +\fB r\fR AS entry replaces relying ASDB entry. + + +.B \-\-as-load +.I # +.br + load an ASDB from a file. +.br +.I Experimental + +.B \-\-as-save +.I # +.br + save ASDB into file. For PX-755/PX-760 only! +.br + PX\-755 export maybe buggy. +.br +.I Experimental + + +.SH "BitSetting" +due to some compatibility problems of some DVD readers, it could make sense to use BitSetting, which sets the Booktype of media to DVD\-ROM. This \fBOPTION\fR requiers a specified \fBDEVIVE\fR. + +.B \-\-bitset+r +.I mode +.br + enable / disable Bitsetting for DVD+R. +.br +\fB on\fR defines DVD+R as DVD\-ROM media. +.br +\fB off\fR keeps DVD+R media as what it is. + +.B \-\-bitset+rdl +.I mode +.br + enable / disable Bitsetting for DVD+R DL. + +\fB on\fR define DVD+R DL media as DVD\-ROM media. +.br +\fB off\fR keeps DVD+R DL media as what it is. + +.SH "DiagnoseMode" +are \fBOPTIONS\fR to detect the drive itself, the current settings and the available features. + +\fB\-c, \-\-current\fR show current status of selected \fBDEVICE\fR. + +\fB\-h, \-\-help\fR show help. + +\fB\-l, \-\-scan\fR search all busses and list detected drives. + +\fB\-s, \-\-supported\fR show features supported by selected \fBDEVICE\fR. + +\fB\-v\fR show output in verbose mode. + +.SH "GigaRec" +Increase (higher value) or Decrease (lower value) the regular capacity, which can be written on CD media. Ejecting the disc results in flushing the GigaRec settings. Buffer UnderrunProof is disabled while GigaRec is active. The recording speed is limited to 4x or 8x DAO. +Use cdrdao or cdrskin for writing media with GigaRec function. Otherwise if you use cdrecord your values are flushed. GigaRec settings need a specified \fBDEVICE\fR. + +.B \-\-gigarec +.I # +.br + set GigaRec to , which is a value in the +.br + range of 0.6, 0.7, ..., 1.3, 1.4 or off. + +.SH "Hide-CDR" +prevents media from being detected by an application as CD\-R to defeat the ATIP protection. Hide\-CDR settings need a specified \fBDEVICE\fR. + +.B \-\-hcdr +.I [on|off] +.br + enable / disable Hide\-CDR. +.br +\fB on\fR enable Hide\-CDR. Defeating ATIP protection. +.br +\fB off\fR disable Hide\-CDR. +.SH "MediaLock" +can lock your media to prevent an accidentally opening. It is always a good idea lock the drive. This \fBOPTION\fR needs a specified \fBDEVICE\fR. +.br +.B \-\-lock +.br + enable lock modus. Media can not be removed. +.br +.B \-\-unlock +.br + disable lock modus. Media can be removed. + +.SH "PlexEraser" +destroys the data on your CD/DVD and makes it unreadable. \fBDEVICE\fR needs to be specified for this command. + +.B \-\-destruct +.I mode +.br + WARNING: command destroys your media. + +\fB quick\fR destroy only the lead in of CD/DVD media. +.br +\fB full\fR destroy complete CD/DVD media. + +.SH "PoweRec" +\fBP\fRlextor \fBO\fRptimised \fBW\fRriting \fBE\fRrror \fBRe\fRduction \fBC\fRontrol (PoweRec) should detect the optimized writing speed for your media\-combination. Higher recording speeds than recommended can result in increased writing errors. PoweRec should prevent you from these errors. \fBDEVICE\fR needs to be specified for this command. + +.B \-\-powerec +.I [on|off] +.br + enable / disable PoweRec. +.br +\fB on\fR enable PoweRec. enabled by default. +.br +\fB off\fR disable PoweRec. + +.SH "SecuRec" +Creates a password protected CD\-R. To access a password protected CD\-R, it is necessary to enter the correct password and to specify the \fBDEVICE\fR + +.BI \-\-nosecurec + disable SecuRec. + +.B \-\-securec +.I +.br + enable SecuRec and use a given password. +.br + must have at least four, but not more than ten characters. +.SH "SilentMode" +Allows some speed finetuning on your drives. Lower values result in more silence. You can specify read speed and tray loading speed. \fBDEVICE\fR must be specified for this command. All settings will be active after reboot. ;\-) pxfw /dev/hdX \-r + +.B \-\-silent +.I [on|off] +.br + enable / disable SilentMode + +\fB on\fR enables SilentMode. This will give you access to +.br + further speed and read settings. Default is on. +.br +\fB off\fR disables SilentMode. + +.B \-\-sm-access +.I mode +.br + set access time to slow or fast. This has only an affect in combination with CD/DVD speed setting. + +\fB fast\fR enables fast access mode. Use with speed setting. +.br +\fB slow\fR enables slow access mode. Use with speed setting. + +.B \-\-sm-cd-rd +.I # +.br + set maximum read speed for CDs. Default is 32x. +.br + Select speed value from 4, 8, 24, 32, 40, 48. + +.B \-\-sm-cd-wr +.I # +.br + set maximum write speed for CDs. Default is 32x. +.br + Select speed value from 4, 8, 16, 32, 48. + +.B \-\-sm-dvd-rd +.I # +.br + set maximum read speed for DVDs. Default is 12x. +.br + Select speed value from 2, 5, 8, 12, 16. + +.B \-\-sm-eject +.I # +.br + set tray eject speed. Default is 0. +.br + Select speed value from 0, 1, ..., 80. + +.B \-\-sm-load +.I # +.br + set tray load speed. Default is 63. +.br + Select speed value from 0, 1, ... , 80. + +.B \-\-sm-nosave + do not save SilentMode settings + +.SH "SingleSession" +shows only the first session of the CD. You have to enable this feature before you insert the media. This is a useful option for copy protected audio discs. \fBDEVICE\fR must be specified for this command. + +.B \-\-sss +.I mode +.br + enable / disable SingleSession. Default is off. + +\fB on\fR enable SingleSession mode for CD. +.br +\fB off\fR disable SingleSession mode for CD. + +.SH "Simulation" +allows DVD+R writing simulation. \fBDEVICE\fR must be specified for this command. + +.B \-\-dvd-testwrite +.I [on|off] +.br + mode enable / disable DVD+R writing simulation +\fB on\fR enable DVD+R writing simulation mode +.br +\fB off\fR disable DVD+R writing simulation mode +.SH "SpeedRead" +allows increasing the read speed. This deactivates the so called Rip Lock for Video\-DVDs. You can also deativate this feature by keeping the open button of your empty DVD drive pressed for three seconds. SpeedRead can increase vibrations and read errors. \fBDEVICE\fR must be specified for this command. + +.B \-\-spdread +.I mode +.br + enable / disable SpeedRead + +\fB on\fR enable SpeedRead. Disable Rip Lock. +.br +\fB off\fR disable SpeedRead. Set Rip Lock. +.SH "VariRec" +allows some laser power calibration in a range from \-4 to +4. The recording speed is limited to 4x or 8x on CD\-R media and to 2x, 2.4x or 4x on DVD media. The recording strategy can also be changed relying on the used media dye. The writing strategies are determining and documenting your own strategies. You would determine the differences by selecting a stretegy and doimg a writing test. +VariRec settings persist after a reboot. \fBDEVICE\fR must be specified for this command. +Use cdrdao or cdrskin for writing media with VariRec function. Otherwise if you use cdrecord, your values are flushed. + +.B \-\-varirec-cd +.I # +.br + set VariRec power for CD writing or set it to off. +.br + Select value \fB\-4, \-3, \-2, \-1, 0, +2, +2, +3, +4\fR + +.B \-\-varirec\-cd\-strategy +.I mode +.br + select writing strategy for CDs depending +.br + on the used dye (Azo, Cyanine, PhtaloCyanine): +.br + select: \fBdefault, azo, cya, pha, phb, phc, phd\fR + +.B \-\-varirec-dvd +.I # +.br + set VariRec power value for DVD writing or set it to off. +.br + Select value \fB\-4, \-3, \-2, \-1, 0, +2, +2, +3, +4\fR + +.B \-\-varirec-dvd-strategy +.I # +.br + select a predefined DVD writing strategy. +.br + select value \fB0, 1, 2, 3, 4, 5, 6, 7\fR +.br +.SH "PioQuiet" +Pioneer QuietMode feature. +.br +.B \-\-pio-limit +.I [on|off] +.br + turns on/off read speed limit by 24X for CD and 8X for DVD + +.B \-\-pio-quiet +.I mode +.br + sets Pioneer performance profile +.br + available values: quiet, perf, std +.br + \fBquiet\fR turn drive into silent mode +.br + \fBperf\fR turn drive into performance ptomized mode +.br + \fBstd\fR reset drive to standard mode +.br + +.B \-\-pio-nosave +.br + don't make Pioquiet setting permanent + +.SH "EXAMPLES" +\fBcdvdcontrol \-l\fR +.br + list all available optical devices. + +\fBcdvdcontrol \-d /dev/hdc \-\-supported\fR +.br + show features that are supported by drive hdc. + +\fBcdvdcontrol \-d /dev/hda \-\-as\-create q a\fR +.br + create a new ASDB entry for device hda and the +.br + inserted DVD media. Strategy is created quickly +.br + and appended to the already existing ASDB list. + +\fBcdvdcontrol \-d /dev/sr2 \-\-gigarec 0.9\fR +.br + prepare CD for GigaRec 0.9 recording. Use cdrskin +.br + or cdrdao/k3b for writing process. +\fR \fB +\fR \fB +\fR \fB +\fRplease report man page improvements to T.Maguin@web.de\fR diff --git a/man/f1tattoo.1 b/man/f1tattoo.1 new file mode 100644 index 0000000..78d771a --- /dev/null +++ b/man/f1tattoo.1 @@ -0,0 +1,107 @@ +.\" f1tattoo is released under the GNU GENERAL PUBLIC LICENSE. +.TH "f1tattoo" "1" "26 Feb 2014" +.br +.SH "NAME" +f1tattoo \- disc T@2 feature for Yamaha F1 CD-RW devices +.SH "SYNOPSIS" +.br +\fBf1tattoo [\-l] [\-h] \fr +.br +\fBf1tattoo \-d DEVICE [\-c] [\-s] [\-\-tattoo-test]\fR +.br +\fBf1tattoo \-d DEVICE \-\-tattoo-raw \fR +.br +\fBf1tattoo \-d DEVICE \-\-tattoo-png \fR +.br +.SH "DESCRIPTION" +f1tattoo is the linux console tool to use the Yamaha disc T@2 feature. You won't get satisfying results on Phtalocyanin dye. +.SH "FEATURES" +You can disc T@2 with these drives: +.br +Yamaha F1 CD-RW, Yamaha F1DX + +.SH "DEVICE" +can be an IDE, SCSI, SATA, USB or FireWire connected optical drive. Not all SATA controller support all Plextor features. + +\fBLinux:\fR +.br +.I /dev/hdX: +IDE device +.br +.I /dev/scdX: +Linux 2.4: SATA, SCSI, USB device, or IDE device via ide-scsi emulation +.br +.I /dev/srX: +Linux 2.6: IDE device via new ATA layer, SCSI or USB device +.br + +\fBOpenBSD/NetBSD:\fR +.br +.I /dev/rcdX +.br + +\fBFreeBSD:\fR +.br +.I /dev/cd: +SCSI device +.br +.I /dev/acd: +ATA device +.br + +\fBMacOS X:\fR +.br +.I /dev/disk: +.br + +\fBwin32:\fR +.br +.I C:,D:,E:, ... X:,Y:,Z: +.br + +.SH "OPTIONS" + +\fB\-l, \-\-scanbus\fR +.br + scan busses for all available CD and DVD devices. + +\fB\-h, \-\-help\fR +.br + help. show available options. + +\fB\-c, \-\-current\fR +.br + show current drive settings of selected device + +\fB\-s, \-\-supported\fR +.br + show supported features of selected device + +\fB\-\-tattoo-test\fR +.br + write DiscT@2 test image + +\fB\-\-tattoo-raw tattoo.raw\fR +.br + write DiscT@2 using raw data from tattoo.raw file + +\fB\-\-tattoo-png tattoo.png\fR +.br + write DiscT@2 using png image from tattoo.png file + check if f1tattoo compiled with png support using \-\-help option + +\fB\-v, \-\-verbose\fR +.br + give verbose output +.SH "EXAMPLES" +\fBf1tattoo \-l\fR +.br + list all available CD- and DVD-devices\fR + +\fBf1tattoo \-d /dev/hdc \-\-tattoo-raw filename.raw\fR +.br + writes DiscT@2 using filename.raw file on media in device /dev/hdc +\fR \fB +\fR \fB +\fR \fB +\fRplease report man page improvements to T.Maguin@web.de\fR diff --git a/man/pxfw.8 b/man/pxfw.8 new file mode 100644 index 0000000..a10a46f --- /dev/null +++ b/man/pxfw.8 @@ -0,0 +1,96 @@ +.\" pxfw is released under the GNU GENERAL PUBLIC LICENSE. +.TH "pxfw" "8" "26 Feb 2014" +.SH "NAME" +pxfw \- Firmware flashing tool for Plextor CD/DVD devices +.SH "SYNOPSIS" +\fBpxfw \-l +.br +\fBpxfw \-d DEVICE [\-if\fR firmware.bin\fB] [\-u] [\-f] [\-v]\fR +.br +\fBpxfw \-d DEVICE [\-e] [\-r] [\-t] [\-v]\fR +.br +.SH "DESCRIPTION" +pxfw is the linux firmware flasher for Plextor CD and DVD drives. Remove any disks before flashing. +.SH "FEATURES" +You can flash the firmware of these drives: +.br +Plextor Premium, Premium-2, PX\-712, PX\-716, PX\-755 and PX\-760. +.br +.SH "DEVICE" +can be an IDE, SCSI, SATA, USB or FireWire connected optical drive. Not all SATA controller support all Plextor features. + +\fBLinux:\fR +.br +.I /dev/hdX: +IDE device +.br +.I /dev/scdX: +Linux 2.4: SATA, SCSI, USB device, or IDE device via ide-scsi emulation +.br +.I /dev/srX: +Linux 2.6: IDE device via new ATA layer, SCSI or USB device +.br + +\fBOpenBSD/NetBSD:\fR +.br +.I /dev/rcdX +.br + +\fBFreeBSD:\fR +.br +.I /dev/cd: +SCSI device +.br +.I /dev/acd: +ATA device +.br + +\fBMacOS X:\fR +.br +.I /dev/disk: +.br + +\fBwin32:\fR +.br +.I C:,D:,E:, ... X:,Y:,Z: +.br + +.SH "OPTIONS" + +\fB\-l\fR scan busses for all available CD and DVD devices +.br + +\fB\-if\fR select inputfile. Specify the firmware binary file, which should +.br + be written to DEVICE. + +\fB\-u\fR proceed update. When the firmware checksum test has succeeded, +.br + write firmware to DEVICE. + +\fB\-f\fR force flashing. Even if DEVICE is not recognized, or firmware. +.br + checksum has failed, firmware writing will be forced. +.br + Handle with care. + +\fB\-e\fR read EEPROM from DEVICE. + +\fB\-r\fR reboot the device. + +\fB\-t\fR test which opcodes are supported by the device. + +\fB\-v\fR debug. + +.SH "EXAMPLES" +\fBpxfw \-d /dev/hdc \-if 755_1.07.bin \-u\fR +.br +writes firmware file 755_1.07.bin into Master Drive on Secondary IDE\-Port. + +\fBpxfw \-d /dev/sr3 \-e \-oe file.foo\fR +.br +reades EEPROM from device /dev/sr3 and writes it to file.foo. +\fR \fB +\fR \fB +\fR \fB +\fRplease report man page improvements to T.Maguin@web.de\fB diff --git a/man/qpxtool.1 b/man/qpxtool.1 new file mode 100644 index 0000000..6c0523c --- /dev/null +++ b/man/qpxtool.1 @@ -0,0 +1,14 @@ +.\" qpxtool is released under the GNU GENERAL PUBLIC LICENSE. +.TH "qpxtool" "1" "26 Feb 2014" +.SH "NAME" +qpxtool \- a gui to control qpxtool command line interfaces (cli). +.SH "SYNOPSIS" +\fBqpxtool +.br +.SH "DESCRIPTION" +qpxtool is a gui to control qscan and cdvdcontrol. + +\fR \fB +\fR \fB +\fR \fB +\fRplease report man page improvements to T.Maguin@web.de\fB diff --git a/man/qscan.1 b/man/qscan.1 new file mode 100644 index 0000000..7a51594 --- /dev/null +++ b/man/qscan.1 @@ -0,0 +1,177 @@ +.\" qscan is released under the GNU GENERAL PUBLIC LICENSE. +.TH "qscan" "1" "26 Feb 2014" +.SH "NAME" +qscan \- console tool for quality measurement of optical media +.SH "SYNOPSIS" +\fBqscan [\-l] [\-h] +.br +\fBqscan \-d DEVICE [\-S] [\-i] [\-m] \fR +.br +\fBqscan \-d DEVICE [\-p] [\-r] [\-w] [\-v] \fR +.br +\fBqscan \-d DEVICE [\-f] [\-s] [\-t] [\-v] \fR +.br +\fBqscan \-d DEVICE \-t wt [\-s #] [\-W] \fR +.br +.SH "DESCRIPTION" +qscan is the linux console tool for error correction measuring with optical devices. +The related plugin allows one to use vendor specific commands on your optical device. +Currently supported vendors are Asus, Benq, Liteon, NEC, Pioneer and Plextor. +For detailed information look at http://qpxtool.sourceforge.net/supported.html +.SH "DEVICE" +can be an IDE, SCSI, SATA, USB or FireWire connected optical drive. Not all SATA controller support all Plextor features. + +\fBLinux:\fR +.br +.I /dev/hdX: +IDE device +.br +.I /dev/scdX: +Linux 2.4: SATA, SCSI, USB device, or IDE device via ide-scsi emulation +.br +.I /dev/srX: +Linux 2.6: IDE device via new ATA layer, SCSI or USB device +.br + +\fBOpenBSD/NetBSD:\fR +.br +.I /dev/rcdX +.br + +\fBFreeBSD:\fR +.br +.I /dev/cd: +SCSI device +.br +.I /dev/acd: +ATA device +.br + +\fBMacOS X:\fR +.br +.I /dev/disk: +.br + +\fBwin32:\fR +.br +.I C:,D:,E:, ... X:,Y:,Z: +.br + +.SH "OPTIONS" + +\fB\-l, \-\-scanbus\fR +.br + scan busses for all available optical devices + +\fB\-h, \-\-help\fR +.br + show help + +\fB\-S, \-\-speeds\fR +.br + detect available read and write speeds for selected \-d DEVICE and the relating media + +\fB\-i, \-\-info\fR +.br + show device info of selected \-d DEVICE + +\fB\-m, \-\-media\fR +.br + show media info of selected \-d DEVICE + +\fB\-p, \-\-plugins\fR +.br + list all available plugins + +\fB\-f, \-\-force-plugin PLUGIN [ ASUS | BENQ | BENQ_DVDROM |GENERIC |LITEON |NEC |PIONEER |PLEXTOR ]\fR +.br + force usage of the selected plugin to use the vendor specific commands for the slected device. +.br + Default behaviour auto detection modus. + +\fB [ASUS]\fR use Asus vendor specific commands +.br +\fB [BENQ]\fR use Benq optical writer vendor specific commands +.br +\fB [BENQ_DVDROM]\fR use Benq optical reader vendor specific commands +.br +\fB [GENERIC]\fR use generic mmc commands +.br +\fB [LITEON]\fR use Liteon vendor specific commands +.br +\fB [NEC]\fR use NEC vendor specific commands +.br +\fB [PIONEER]\fR use Pioneer vendor specific commands +.br +\fB [PLEXTOR]\fR use Plextor vendor specific commands +.br + +\fB\-r #, \-\-rspeed #\fR +.br + set read performance speed to selected value. +.br + Use \-S option before, if you are unsure which speeds are supported + +\fB\-s #, \-\-speed #\fR +.br + set measurement speed to selected value. +.br + Use \-S option before, if you don't know what speeds are supported by your device. +.br + Don't use this with \-r or \-w option. + +\fB\-t, \-\-test [rt|wt|errc|ft|jb|ta] \fR +.br + select the q-check you want to be proceeded. Not all tests are supported by all devices. +.br + For detailed information about the terms read the project page: +.br + http://qpxtool.sourceforge.net/glossar.html + +\fB [rt]\fR read transfer rate measurement +.br +\fB [wt]\fR write transfer rate on blank media +.br +\fB [errc]\fR error correction \- Cx-scan / PIE, PIF, POE, POF +.br +\fB [ft]\fR focus and tracking error measurement +.br +\fB [jb]\fR jitter and beta measurement +.br +\fB [ta]\fR time analyzer measurement +.br + +\fB\-w #, \-\-wspeed #\fR +.br + set write speed to selected value. +.br + Use \-S option before, if you don't know what write speeds are supported by your device. + +\fB\-v, \-\-verbose\fR +.br + gives verbose output and debug information + +\fB\-W, \-\-write\fR +.br + perform real write instead simulation for write transfer rate + +.SH "EXAMPLES" +\fBqscan \-l\fR +.br + scan all buses for available optical devices + +\fBqscan \-d /dev/sr2 \-S\fR +.br + Show available speed steps of device /dev/sr2. + +\fBqscan \-d /dev/sr2 \-t errc \-s 5\fR +.br + Proceed an error correction scan on device /dev/sr2 with 5x speed. + +\fBqscan \-d /dev/sr0 \-t wt \-s 12\fR +.br + Proceed a write performance test in simulation mode on device /dev/sr0 (dummy mode) with 12x speed +\fR \fB +\fR \fB +\fR \fB +\fRplease report man page improvements to T.Maguin@web.de\fB diff --git a/man/qscand.1 b/man/qscand.1 new file mode 100644 index 0000000..4e62c08 --- /dev/null +++ b/man/qscand.1 @@ -0,0 +1,40 @@ +.\" qscand is released under the GNU GENERAL PUBLIC LICENSE. +.TH "qscand" "1" "26 Feb 2014" +.SH "NAME" +qscand \- network wrapper for qscan +.SH "SYNOPSIS" +\fBqscand [\-h] +.br +\fBqscand [\-n] [\-d] +.br +\fBqscand \-i NW-INTERFACE-ADDRESS \-p LISTEN-PORT \fR +.br +.SH "DESCRIPTION" +qscand is a network wrapper which gives you access to your devices through the TCP/IP stacK. + +.SH "OPTIONS" + +\fB\-h, \-\-help\fR +.br + show help + +\fB\-n, \-\-nodaemon\fR +.br + don't daemonize qscand + +\fB\-d, \-\-debug\fR +.br + run qscand in debug mode + +\fB\-i, \-\-iface\fR +.br + select ip address for listening + +\fB\-p, \-\-port\fR +.br + select port for listening + +\fR \fB +\fR \fB +\fR \fB +\fRplease report man page improvements to T.Maguin@web.de\fB diff --git a/man/readdvd.1 b/man/readdvd.1 new file mode 100644 index 0000000..65813dd --- /dev/null +++ b/man/readdvd.1 @@ -0,0 +1,80 @@ +.\" readdvd is released under the GNU GENERAL PUBLIC LICENSE. +.TH "readdvd" "1" "26 Feb 2014" +.SH "NAME" +readdvd \- is creating an image of your source dvd media or medias even if it has / they have corrupted blocks +.SH "SYNOPSIS" +\fBreaddvd [\-l] [\-h] +.br +\fBreaddvd \-d DEVICE1 [\-d DEVICE2] [\-d ...] \-o\fR file.iso\fB [\-s #] [\-v] [\-vv]\fR +.br +.SH "DESCRIPTION" +readdvd reads even a corrupted dvd and writes the the result into a new image file on your harddisk. +.br +.SH "DEVICE" +can be an IDE, SCSI, SATA, USB or FireWire connected optical drive. Not all SATA controller support all Plextor features. + +\fBLinux:\fR +.br +.I /dev/hdX: +IDE device +.br +.I /dev/scdX: +Linux 2.4: SATA, SCSI, USB device, or IDE device via ide-scsi emulation +.br +.I /dev/srX: +Linux 2.6: IDE device via new ATA layer, SCSI or USB device +.br + +\fBOpenBSD/NetBSD:\fR +.br +.I /dev/rcdX +.br + +\fBFreeBSD:\fR +.br +.I /dev/cd: +SCSI device +.br +.I /dev/acd: +ATA device +.br + +\fBMacOS X:\fR +.br +.I /dev/disk: +.br + +\fBwin32:\fR +.br +.I C:,D:,E:, ... X:,Y:,Z: +.br + +.SH "OPTIONS" + +\fB\-l\fR scan busses for all available CD and DVD devices + +\fB\-h\fR help shows available options. + +\fB\-o file.iso\fR +.br + write data to imagefile named file.iso + +\fB\-s \fR# read source media with selected speed + +\fB\-v\fR use verbose mode + +\fB\-vv\fR use extended verbose mode + +.SH "INTERACTIVE MODUS" +\fBq\fR stop reading media and exit + +\fBw\fR save sector map which is currently read and continue with the next one + +.SH "EXAMPLES" +\fBreaddvd \-d /dev/sr0 \-o filename.iso \-s 8 \-v\fR +.br + create an image filename.bin of inserted media in device /dev/sr0 with read speed 8 in verbose mode. +\fR \fB +\fR \fB +\fR \fB +\fRplease report man page improvements to T.Maguin@web.de\fB diff --git a/patches/01-ArtKarMOD.patch b/patches/01-ArtKarMOD.patch new file mode 100644 index 0000000..8d49eec --- /dev/null +++ b/patches/01-ArtKarMOD.patch @@ -0,0 +1,533 @@ +diff -Naur qpxtool-0.8.1.orig/console/qscan/qscan.cpp qpxtool-0.8.1/console/qscan/qscan.cpp +--- qpxtool-0.8.1.orig/console/qscan/qscan.cpp 2021-07-26 01:32:03.862541745 +0200 ++++ qpxtool-0.8.1/console/qscan/qscan.cpp 2021-07-26 01:35:29.049213007 +0200 +@@ -628,7 +628,11 @@ + } + } + printf(IMEDIA "Layers : %d\n", dev->media.layers); +- ++ ++ if ( dev->media.type & (DISC_BD) ) { ++ printf(IMEDIA "GB per Layer : %d\n", dev->media.gbpl); ++ } ++ + if ( dev->media.type & (DISC_DVD) ) { + // read_disc_regions(drive); + if (!dev->media.dvdcss.protection) { +diff -Naur qpxtool-0.8.1.orig/console/qscan/version.h qpxtool-0.8.1/console/qscan/version.h +--- qpxtool-0.8.1.orig/console/qscan/version.h 2021-07-26 01:32:03.862541745 +0200 ++++ qpxtool-0.8.1/console/qscan/version.h 2021-07-26 01:36:02.528995974 +0200 +@@ -9,5 +9,5 @@ + * See the file "COPYING" for the exact licensing terms. + */ + +-#define VERSION "0.7.2" ++#define VERSION "0.8.1 modded by ArtKar" + +diff -Naur qpxtool-0.8.1.orig/gui/include/device.h qpxtool-0.8.1/gui/include/device.h +--- qpxtool-0.8.1.orig/gui/include/device.h 2021-07-26 01:32:03.862541745 +0200 ++++ qpxtool-0.8.1/gui/include/device.h 2021-07-26 01:36:43.196732273 +0200 +@@ -345,6 +345,9 @@ + QString mid; + QString layers; + int ilayers; ++ QString gbpl; ++ int igbpl; ++ + QString prot; + QString regions; + +diff -Naur qpxtool-0.8.1.orig/gui/include/mainwidget.h qpxtool-0.8.1/gui/include/mainwidget.h +--- qpxtool-0.8.1.orig/gui/include/mainwidget.h 2021-07-26 01:32:03.862541745 +0200 ++++ qpxtool-0.8.1/gui/include/mainwidget.h 2021-07-26 01:37:04.020597217 +0200 +@@ -50,6 +50,7 @@ + void selectDevice(); + void reconfig(); + void setSidebarVisible(bool); ++ void setSimpleGraph(bool); + void selectTab(int); + //inline void reconfig() { emit configured(); }; + +diff -Naur qpxtool-0.8.1.orig/gui/include/mainwindow.h qpxtool-0.8.1/gui/include/mainwindow.h +--- qpxtool-0.8.1.orig/gui/include/mainwindow.h 2021-07-26 01:32:03.862541745 +0200 ++++ qpxtool-0.8.1/gui/include/mainwindow.h 2021-07-26 01:37:24.044467332 +0200 +@@ -167,6 +167,7 @@ + *act_about; + + QAction *act_sb; ++ QAction *act_sg; + QList act_sblist; + QActionGroup *act_sbgrp; + +diff -Naur qpxtool-0.8.1.orig/gui/include/qpxsettings.h qpxtool-0.8.1/gui/include/qpxsettings.h +--- qpxtool-0.8.1.orig/gui/include/qpxsettings.h 2021-07-26 01:32:03.862541745 +0200 ++++ qpxtool-0.8.1/gui/include/qpxsettings.h 2021-07-26 01:37:41.604353417 +0200 +@@ -166,6 +166,7 @@ + + // general options + bool show_sidebar; ++ bool show_simplegraph; + bool show_allctl; + bool report_autosave; + QString report_path; +diff -Naur qpxtool-0.8.1.orig/gui/include/version.h qpxtool-0.8.1/gui/include/version.h +--- qpxtool-0.8.1.orig/gui/include/version.h 2021-07-26 01:32:03.862541745 +0200 ++++ qpxtool-0.8.1/gui/include/version.h 2021-07-26 01:37:54.604269075 +0200 +@@ -13,7 +13,7 @@ + #ifndef _QPX_VERSION_H + #define _QPX_VERSION_H + +-#define VERSION "0.7.2" ++#define VERSION "0.8.1 modded by ArtKar" + + #endif + +diff -Naur qpxtool-0.8.1.orig/gui/src/db_report_selection.cpp qpxtool-0.8.1/gui/src/db_report_selection.cpp +--- qpxtool-0.8.1.orig/gui/src/db_report_selection.cpp 2021-07-26 01:32:03.858541771 +0200 ++++ qpxtool-0.8.1/gui/src/db_report_selection.cpp 2021-07-26 01:38:20.116103540 +0200 +@@ -228,7 +228,7 @@ + reports \ + WHERE \ + label ILIKE '%"+elabel->text()+"%' \ +- ORDER BY datetime"); ++ ORDER BY label"); + if (!q->exec()) { + qDebug() << q->lastError().text(); + goto err; +diff -Naur qpxtool-0.8.1.orig/gui/src/device.cpp qpxtool-0.8.1/gui/src/device.cpp +--- qpxtool-0.8.1.orig/gui/src/device.cpp 2021-07-26 01:32:03.862541745 +0200 ++++ qpxtool-0.8.1/gui/src/device.cpp 2021-07-26 01:41:00.831060231 +0200 +@@ -274,6 +274,8 @@ + media.erasable = "-"; + media.layers = "-"; + media.ilayers = 1; ++ media.gbpl = "-"; ++ media.igbpl = 25; + media.prot = "-"; + media.regions = "-"; + media.creads = 0; +@@ -1358,6 +1360,11 @@ + media.ilayers = sl[1].toInt(); + if (media.ilayers <= 0) + media.ilayers = 1; ++ } else if (sl[0].contains("GB per Layer", Qt::CaseInsensitive)) { ++ media.gbpl = sl[1]; ++ media.igbpl = sl[1].toInt(); ++ if (media.igbpl <= 0) ++ media.igbpl = 25; + } else if (sl[0].contains("Protection", Qt::CaseInsensitive)) { + media.prot = sl[1]; + } else if (sl[0].contains("Regions", Qt::CaseInsensitive)) { +diff -Naur qpxtool-0.8.1.orig/gui/src/mainwidget.cpp qpxtool-0.8.1/gui/src/mainwidget.cpp +--- qpxtool-0.8.1.orig/gui/src/mainwidget.cpp 2021-07-26 01:32:03.858541771 +0200 ++++ qpxtool-0.8.1/gui/src/mainwidget.cpp 2021-07-26 01:41:18.982942347 +0200 +@@ -148,6 +148,7 @@ + } + + void QPxMainWidget::setSidebarVisible(bool en) { settings->show_sidebar = en; bframe->setVisible(en); } ++void QPxMainWidget::setSimpleGraph(bool en) { settings->show_simplegraph = en; } + void QPxMainWidget::selectTab(int idx) { grp->button(idx)->setChecked(true); stack->setCurrentIndex(idx); } + void QPxMainWidget::reconfig() { emit configured(); } + void QPxMainWidget::clearDev() { tab_MediaInfo->clear(); tab_DevInfo->clear(); } +diff -Naur qpxtool-0.8.1.orig/gui/src/mainwindow.cpp qpxtool-0.8.1/gui/src/mainwindow.cpp +--- qpxtool-0.8.1.orig/gui/src/mainwindow.cpp 2021-07-26 01:32:03.858541771 +0200 ++++ qpxtool-0.8.1/gui/src/mainwindow.cpp 2021-07-26 02:01:11.147353167 +0200 +@@ -70,11 +70,12 @@ + + #define PRINT_GRAPH_SCALE 2.0 + +-#define HTML_GRAPH_W 600 +-#define HTML_GRAPH_H 300 +-#define HTML_GRAPH_WQ "600" +-#define HTML_GRAPH_HQ "300" +-#define HTML_GRAPH_HQ2 "150" ++#define HTML_GRAPH_W 1800 ++#define HTML_GRAPH_H 600 ++#define HTML_GRAPH_WQ "1800" ++#define HTML_GRAPH_HQ "600" ++#define HTML_GRAPH_HQ2 "300" ++ + + const QString errcNameCD[8] = { "BLER", "E11", "E21", "E31", "E12", "E22", "E32", "UNCR" }; + const QString errcNameDVD[8] = { "res", "PIE", "PI8", "PIF", "POE", "PO8", "POF", "UNCR" }; +@@ -222,6 +223,7 @@ + layout->addWidget(mwidget); + + connect(act_sb, SIGNAL(toggled(bool)), mwidget, SLOT(setSidebarVisible(bool))); ++ connect(act_sg, SIGNAL(toggled(bool)), mwidget, SLOT(setSimpleGraph(bool))); + + connect(pb_loej, SIGNAL(clicked()), this, SLOT(loejToggle())); + connect(pb_lock, SIGNAL(clicked()), this, SLOT(lockToggle())); +@@ -259,6 +261,11 @@ + act_sb->setShortcut( QKeySequence("Alt+B") ); + act_sb->setCheckable(true); + act_sb->setChecked(set.show_sidebar); ++ act_sg = new QAction(QIcon(":images/"), tr("Show simple graph"), this); ++ act_sg->setShortcut( QKeySequence("Alt+G") ); ++ act_sg->setCheckable(true); ++ act_sg->setChecked(set.show_simplegraph); ++ + + QAction *act; + act_sbgrp = new QActionGroup(this); +@@ -342,6 +349,8 @@ + menu->addActions(act_sblist); + menu->addSeparator(); + menu->addAction(act_sb); ++ menu->addSeparator(); ++ menu->addAction(act_sg); + + menubar->addMenu(menu); + +diff -Naur qpxtool-0.8.1.orig/gui/src/qpxgraph.cpp qpxtool-0.8.1/gui/src/qpxgraph.cpp +--- qpxtool-0.8.1.orig/gui/src/qpxgraph.cpp 2021-07-26 01:32:03.858541771 +0200 ++++ qpxtool-0.8.1/gui/src/qpxgraph.cpp 2021-07-26 01:46:41.224848460 +0200 +@@ -30,8 +30,8 @@ + #define MARGIN_DEFR 40 + #define MARGIN_DEFB 18 + +-#define GRID_STYLE Qt::DotLine +-//#define GRID_STYLE Qt::DashLine ++//#define GRID_STYLE Qt::DotLine ++#define GRID_STYLE Qt::DashLine + + class IntList : public QList + { +@@ -307,7 +307,7 @@ + HscaleLBA = (1<<19) * 5 * dev->media.ilayers/sg.width(); + Vscale1X = Vscale * 3; + } else if (dev->media.type.startsWith("BD")) { +- HscaleLBA = (1<<19) * 25 * dev->media.ilayers/sg.width(); ++ HscaleLBA = (1<<19) * dev->media.igbpl * dev->media.ilayers/sg.width(); + Vscale1X = Vscale * 4; + } + +@@ -504,7 +504,9 @@ + x = (int) (dev->testData.errc[i].raw.lba/HscaleLBA); + if (dev->testData.errc[i].raw.err[e] >=0 ) { + #ifdef SHOW_P95ERRC ++if (!(settings->show_simplegraph)) { + xerr.append(dev->testData.errc[i].raw.err[e]); ++} + #endif + // update min/max + if (min < 0 || min > dev->testData.errc[i].raw.err[e]) +@@ -516,17 +518,24 @@ + if (x!=xo || i==(dev->testData.errc.size()-1)) { + // min-max + p->setPen(QPen(*settings->col_errc.raw[e], 1)); ++if (settings->show_simplegraph) { ++ p->drawLine(xo, errc2h(s.height(), 0), ++ xo, errc2h(s.height(), max)); ++} else { ++ + p->drawLine(xo, errc2h(s.height(), min), + xo, errc2h(s.height(), max)); +- ++} + // P=0.95 + #ifdef SHOW_P95ERRC ++if (!(settings->show_simplegraph)) { + M = (int)xerr.M(); + D = (int)sqrt(xerr.dispers(M)); + p->setPen(QPen(settings->col_errc.raw[e]->darker(), 1)); + p->drawLine(xo, errc2h(s.height(), M-D), + xo, errc2h(s.height(), M+D)); + xerr.clear(); ++} + #endif + min = -1; + max = -1; +@@ -866,7 +875,7 @@ + GBperLayer = 5; + } else if (dev->media.type.startsWith("BD")) { + isCD = 0; +- GBperLayer = 25; ++ GBperLayer = dev->media.igbpl; + } + + if (isCD) { +diff -Naur qpxtool-0.8.1.orig/gui/src/qpxsettings.cpp qpxtool-0.8.1/gui/src/qpxsettings.cpp +--- qpxtool-0.8.1.orig/gui/src/qpxsettings.cpp 2021-07-26 01:32:03.858541771 +0200 ++++ qpxtool-0.8.1/gui/src/qpxsettings.cpp 2021-07-26 01:47:47.560417210 +0200 +@@ -37,6 +37,7 @@ + // geometry_testsel = QRect(0,0,0,0); + + show_sidebar = 0; ++ show_simplegraph = 0; + show_allctl = 0; + actions_flags = 0; + +@@ -76,6 +77,7 @@ + geometry_pref = o.geometry_pref; + + show_sidebar = o.show_sidebar; ++ show_simplegraph = o.show_simplegraph; + show_allctl = o.show_allctl; + report_autosave = o.report_autosave; + report_path = o.report_path; +@@ -135,6 +137,7 @@ + settings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, "QPxTool", "qpxtool"); + settings->beginGroup("/common"); + show_sidebar = settings->value("show_sidebar", 0).toBool(); ++ show_simplegraph = settings->value("show_simplegraph", 0).toBool(); + show_allctl = settings->value("show_allctl", 0).toBool(); + report_autosave = settings->value("report_autosave", 0).toBool(); + report_path = settings->value("report_path", "").toString(); +@@ -204,6 +207,7 @@ + settings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, "QPxTool", "qpxtool"); + settings->beginGroup("/common"); + settings->setValue("show_sidebar", show_sidebar); ++ settings->setValue("show_simplegraph", show_simplegraph); + settings->setValue("show_allctl", show_allctl); + settings->setValue("report_autosave", report_autosave); + settings->setValue("report_path", report_path); +diff -Naur qpxtool-0.8.1.orig/gui/src/resultsio.cpp qpxtool-0.8.1/gui/src/resultsio.cpp +--- qpxtool-0.8.1.orig/gui/src/resultsio.cpp 2021-07-26 01:32:03.858541771 +0200 ++++ qpxtool-0.8.1/gui/src/resultsio.cpp 2021-07-26 01:49:08.763889233 +0200 +@@ -203,7 +203,9 @@ + dev->media.grec = attr.value("gigarec").toString().toDouble(); + dev->media.spd1X = attr.value("spd1X").toString().toInt(); + dev->media.layers = attr.value("layers").toString(); ++ dev->media.gbpl = attr.value("gbpl").toString(); + dev->media.erasable= attr.value("erasable").toString(); ++ dev->media.igbpl = dev->media.gbpl.toInt(); + dev->media.ilayers = dev->media.layers.toInt(); + dev->media.dstate = attr.value("dstate").toString(); + dev->media.sstate = attr.value("sstate").toString(); +@@ -396,6 +398,7 @@ + + xml.writeAttribute("spd1X", QString::number(dev->media.spd1X)); + xml.writeAttribute("layers", QString::number(dev->media.ilayers)); ++ xml.writeAttribute("gbpl", QString::number(dev->media.igbpl)); + xml.writeAttribute("erasable", dev->media.erasable); + + xml.writeAttribute("dstate", dev->media.dstate); +diff -Naur qpxtool-0.8.1.orig/gui/src/testdialog.cpp qpxtool-0.8.1/gui/src/testdialog.cpp +--- qpxtool-0.8.1.orig/gui/src/testdialog.cpp 2021-07-26 01:32:03.858541771 +0200 ++++ qpxtool-0.8.1/gui/src/testdialog.cpp 2021-07-26 02:30:39.729747142 +0200 +@@ -319,6 +319,7 @@ + int idx; + + spd_RT->addItems(dev->media.rspeeds); spd_RT->addItem("Maximum"); ++ spd_RT->addItem("1X");spd_RT->addItem("2X");spd_RT->addItem("4X");spd_RT->addItem("6X");spd_RT->addItem("8X");spd_RT->addItem("10X");spd_RT->addItem("12X");spd_RT->addItem("16X"); + idx = spd_RT->findText(QString::number(dev->tspeeds.rt)+".",Qt::MatchStartsWith); + #ifndef QT_NO_DEBUG + qDebug() << "spd_RT: " << dev->tspeeds.rt <<"idx:" << idx; +@@ -333,6 +334,7 @@ + if (idx > 0) spd_WT->setCurrentIndex( idx ); + + spd_ERRC->addItems(dev->media.tspeeds_errc); spd_ERRC->addItem("Maximum"); ++ spd_ERRC->addItem("1X");spd_ERRC->addItem("2X");spd_ERRC->addItem("4X");spd_ERRC->addItem("6X");spd_ERRC->addItem("8X");spd_ERRC->addItem("10X");spd_ERRC->addItem("12X");spd_ERRC->addItem("16X"); + idx = spd_ERRC->findText(QString::number(dev->tspeeds.errc)+"X",Qt::MatchStartsWith); + #ifndef QT_NO_DEBUG + qDebug() << "spd_ERRC: " << dev->tspeeds.errc <<"idx:" << idx; +diff -Naur qpxtool-0.8.1.orig/lib/qpxtransport/include/qpx_mmc.h qpxtool-0.8.1/lib/qpxtransport/include/qpx_mmc.h +--- qpxtool-0.8.1.orig/lib/qpxtransport/include/qpx_mmc.h 2021-07-26 01:32:03.854541797 +0200 ++++ qpxtool-0.8.1/lib/qpxtransport/include/qpx_mmc.h 2021-07-26 01:54:47.605685495 +0200 +@@ -144,6 +144,7 @@ + uint8_t disc_size; // indicates 120/80mm disc + uint8_t polarity; // Push-Pull polarity flags per layer for BD (indicates HtL or LtH) + uint8_t layers; // Layers num (!CD) ++ uint8_t gbpl; // Layers num (!CD) + int sectsize; + int32_t capacity; // Recorded capacity in sectors + msf capacity_msf; +diff -Naur qpxtool-0.8.1.orig/lib/qpxtransport/qpx_mmc.cpp qpxtool-0.8.1/lib/qpxtransport/qpx_mmc.cpp +--- qpxtool-0.8.1.orig/lib/qpxtransport/qpx_mmc.cpp 2021-07-26 01:32:03.854541797 +0200 ++++ qpxtool-0.8.1/lib/qpxtransport/qpx_mmc.cpp 2021-07-26 01:54:20.213863672 +0200 +@@ -2317,13 +2317,19 @@ + } else if (drive->media.type & DISC_BD) { + drive->rd_buf[4]=0; + drive->cmd[0] = MMC_READ_DVD_STRUCTURE; +- drive->cmd[7] = 0;//0x11; //dvd_dash; +- drive->cmd[9] = 36; +- drive->cmd[11] = 0; ++ drive->cmd[1] = 1; ++ drive->cmd[7] = 0; ++ drive->cmd[9] = 66; ++ + if ((drive->err=drive->cmd.transport(READ,drive->rd_buf,36))) + if (!drive->silent) sperror ("READ_DVD_STRUCTURE",drive->err); + drive->media.book_type = 0; +- drive->media.layers = 1 + ((drive->rd_buf[6] & 0x60) >> 5); ++ drive->media.layers = 0 + ((drive->rd_buf[16] & 0xF0) >> 4); ++ if ((drive->rd_buf[17] & 0x0F) == 0) drive->media.gbpl = 25; ++ else if ((drive->rd_buf[17] & 0x0F) == 1) drive->media.gbpl = 25; ++ else if ((drive->rd_buf[17] & 0x0F) == 2) drive->media.gbpl = 27; ++ else if ((drive->rd_buf[17] & 0x0F) == 5) drive->media.gbpl = 33; ++ + read_mediaid_bd(drive); + if (!drive->silent) printf("** MID: '%s'\n",drive->media.MID); + } +@@ -2692,7 +2698,11 @@ + drive->cmd[11] = 0; + if ((drive->err=drive->cmd.transport(NONE,NULL,0) )) { + // if (drive->err != 0x23A02) drive->capabilities&=(NCAP_SET_CD_SPEED); +- if (!drive->silent) sperror ("SET_CD_SPEED",drive->err); return (drive->err); ++ if (!drive->silent) { ++ sperror ("SET_CD_SPEED",drive->err); ++ return (drive->err); ++ } ++ + } + return 0; + } +@@ -3120,7 +3130,11 @@ + { if (!dev->silent) sperror ("PLEXTOR_PX755_GET_AUTH_CODE",dev->err); return dev->err;} + if (!dev->silent) { + printf("** Get PX755 auth: "); +- for (int i=0; i<16; i++) printf("0x%02X ",dev->rd_buf[i]&0xFF); printf("\n"); ++ for (int i=0; i<16; i++) { ++ printf("0x%02X ",dev->rd_buf[i]&0xFF); ++ printf("\n"); ++ } ++ + } + return 0; + } +diff -Naur qpxtool-0.8.1.orig/plugins/liteon/qscan_cmd.cpp qpxtool-0.8.1/plugins/liteon/qscan_cmd.cpp +--- qpxtool-0.8.1.orig/plugins/liteon/qscan_cmd.cpp 2021-07-26 01:32:03.854541797 +0200 ++++ qpxtool-0.8.1/plugins/liteon/qscan_cmd.cpp 2021-07-26 02:03:19.350589436 +0200 +@@ -276,25 +276,23 @@ + + int scan_liteon::cmd_cd_errc_block_new(cd_errc *data) + { +- dev->cmd[0] = 0xF3; +- dev->cmd[1] = 0x0E; +- dev->cmd[11]= 0x00; +- if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,10))){ +- sperror ("LiteOn_errc_cd_read_block",dev->err); return 1; +- } +- lba = dev->rd_buf[1] * 60 * 75 + +- dev->rd_buf[2] * 75 + +- dev->rd_buf[3]; +- +- data->bler = ntoh16(dev->rd_buf+4); +- data->e11 = 0; +- data->e21 = 0; +- data->e31 = 0; +- data->e12 = 0; +- data->e22 = ntoh16(dev->rd_buf+6); +- data->e32 = 0; +- data->uncr = 0; +- return 0; ++ dev->cmd[0] = 0xF3; ++ dev->cmd[1] = 0x0E; ++ dev->cmd[11]= 0x00; ++ if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,0x10))){ ++ sperror ("LiteOn_errc_cd_read_block",dev->err); return 1; ++ } ++ ++ lba = ntoh32(dev->rd_buf); ++ data->bler = ntoh16(dev->rd_buf+6); ++ data->e11 = 0; ++ data->e21 = 0; ++ data->e31 = 0; ++ data->e12 = 0; ++ data->e22 = ntoh16(dev->rd_buf+4); ++ data->e32 = dev->rd_buf[6]; ++ data->uncr = 0; ++ return 0; + } + + int scan_liteon::cmd_cd_errc_block(cd_errc *data) +@@ -305,26 +303,34 @@ + // ********************** DVD ERRC commands + int scan_liteon::cmd_dvd_errc_block(dvd_errc *data) + { ++ bool retry = false; ++ if (!lba) { ++ retry = true; ++ // if first sector scan requested ++ // we have to seek to first sector ++ dev->cmd[0] = MMC_SEEK; ++ if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,2048))){ ++ sperror ("READ",dev->err); return 1; ++ } ++ } ++ ++dvd_errc_repeat: + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x0E; + dev->cmd[8] = 0x10; + dev->cmd[11]= 0x00; +- if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,10))){ +- sperror ("LiteOn_errc_dvd_read_block",dev->err); return 1; ++ if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,0x10))) { ++ sperror ("LiteOn_errc_bd_read_block",dev->err); return 1; + } +-#if 0 +- for (int i=0; i<10; i++) { +- printf(" %02X",dev->rd_buf[i]); +- } +- printf("\n"); +-#endif +- +-// Data Received: +-// 00000000 00 00 00 8E 00 00 00 00 ...Ž.... + +-// lba = ((dev->rd_buf[1] << 16 )& 0xFF0000) + ((dev->rd_buf[2] << 8)&0xFF00 ) + (dev->rd_buf[3] & 0xFF); + lba = ntoh32(dev->rd_buf); + ++ if (!lba && retry) { ++ retry = false; ++ goto dvd_errc_repeat; ++ } ++ ++ + data->pie = ntoh16(dev->rd_buf+4); + data->pif = ntoh16(dev->rd_buf+6); + data->poe = 0; +@@ -348,8 +354,9 @@ + + bd_errc_repeat: + dev->cmd[0] = 0xF3; +- dev->cmd[1] = 0x0E; +- dev->cmd[11]= 0x00; ++ dev->cmd[1] = 1; ++ dev->cmd[7] = 0; ++ dev->cmd[9] = 66; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,0x10))) { + sperror ("LiteOn_errc_bd_read_block",dev->err); return 1; + } +diff -Naur qpxtool-0.8.1.orig/plugins/liteon/qscan_plugin.h qpxtool-0.8.1/plugins/liteon/qscan_plugin.h +--- qpxtool-0.8.1.orig/plugins/liteon/qscan_plugin.h 2021-07-26 01:32:03.854541797 +0200 ++++ qpxtool-0.8.1/plugins/liteon/qscan_plugin.h 2021-07-26 01:57:44.952548000 +0200 +@@ -112,6 +112,7 @@ + + { "SONY ", DEV_LITEON, "DVD RW DW-Q58A", LTN_SDVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "SONY ", DEV_LITEON, "DVD RW DW-Q60A", LTN_SDVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, ++ { "HL-DT-ST", DEV_LITEON, "BD-RE WH16NS58 ", LTN_BDR, CHK_ERRC_CD | CHK_ERRC_DVD | CHK_ERRC_BD }, + + { "", 0, "", 0} + }; +@@ -119,7 +120,6 @@ + static const drivedesclist blacklist = + { + { "TEAC ", DEV_TEAC, "CD-W552E", 0 }, +- { "HL-DT-ST", DEV_LG, "", 0 }, + { "TSSTcorp", DEV_TSST, "", 0 }, + + { "", 0, "", 0} +diff -Naur qpxtool-0.8.1.orig/README qpxtool-0.8.1/README +--- qpxtool-0.8.1.orig/README 2021-07-26 01:32:03.850541823 +0200 ++++ qpxtool-0.8.1/README 2021-07-26 02:29:17.443213628 +0200 +@@ -1,3 +1,17 @@ ++QPxTool 0.8.1 modded by ArtKar v1 ++ - QT5 ++ - hardcoded speeds ++ - corrected scan CD on LG WH16NS58 ++ - corrected problem with some BRs ++ - toggle P95 graph / simple graph ++ - BDXL in SQL ++ - added support for DVD Quality test on BD-RE WH16NS58 ++ - added support for BD-RE WH16NS58 ( only tested Blu Ray quality test ) ++ - added support for multilayer Blu Ray discs ++ - added support for BDXL ++ - changed graph style ++ ++ + ************************************************************ + + QPxTool -- CD/DVD quality checker diff --git a/patches/series b/patches/series new file mode 100644 index 0000000..bdb05bb --- /dev/null +++ b/patches/series @@ -0,0 +1 @@ +01-ArtKarMOD.patch diff --git a/plugins/Makefile b/plugins/Makefile new file mode 100644 index 0000000..82b5be2 --- /dev/null +++ b/plugins/Makefile @@ -0,0 +1,34 @@ +all: generic plextor pioneer nec liteon asus benq benq_dvdrom tsst + +clean install uninstall: + $(MAKE) -C generic DIR=generic $@ + $(MAKE) -C plextor DIR=plextor $@ + $(MAKE) -C pioneer DIR=pioneer $@ + $(MAKE) -C nec DIR=nec $@ + $(MAKE) -C liteon DIR=liteon $@ + $(MAKE) -C asus DIR=asus $@ + $(MAKE) -C benq DIR=benq $@ + $(MAKE) -C benq_dvdrom DIR=benq_dvdrom $@ + $(MAKE) -C tsst DIR=tsst $@ + +generic: + $(MAKE) -C generic DIR=generic +plextor: + $(MAKE) -C plextor DIR=plextor +pioneer: + $(MAKE) -C pioneer DIR=pioneer +nec: + $(MAKE) -C nec DIR=nec +liteon: + $(MAKE) -C liteon DIR=liteon +asus: + $(MAKE) -C asus DIR=asus +benq: + $(MAKE) -C benq DIR=benq +benq_dvdrom: + $(MAKE) -C benq_dvdrom DIR=benq_dvdrom +tsst: + $(MAKE) -C tsst DIR=tsst + + +.PHONY: all clean install uninstall generic plextor pioneer nec liteon asus benq benq_dvdrom tsst diff --git a/plugins/asus/Makefile b/plugins/asus/Makefile new file mode 100644 index 0000000..2a70101 --- /dev/null +++ b/plugins/asus/Makefile @@ -0,0 +1,3 @@ +LIBN = qscan_asus + +include ../Makefile.plugin diff --git a/plugins/asus/qscan_cmd.cpp b/plugins/asus/qscan_cmd.cpp new file mode 100644 index 0000000..a7352dd --- /dev/null +++ b/plugins/asus/qscan_cmd.cpp @@ -0,0 +1,130 @@ +/* + * qscan plugin for ASUS drives: + * 1612 + * 1814 + * 2014S1 + * 2014L1 + * + * This file is part of the QPxTool project. + * Copyright (C) 2008-2009 Gennady "ShultZ" Kozlov + * + * 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 +#include + +#include +#include +#include + +#include + +int scan_asus::probe_drive() +{ + dev->cmd[0] = 0x5A; + dev->cmd[2] = 0x38; + dev->cmd[3] = 0x41; + dev->cmd[4] = 0x53; + dev->cmd[5] = 0x10; + dev->cmd[9] = 0x04; + if ((dev->err=dev->cmd.transport(READ, dev->rd_buf, 20))){ + sperror ("asus_probe",dev->err); return DEV_FAIL; + } + if (strncmp((char*)dev->rd_buf,"ASUS",4)) return DEV_FAIL; + return DEV_PROBED; +} + +// ************* Scan init commands ********* +int scan_asus::cmd_errc_init() +{ + /* initialize scan mode */ + dev->cmd[0] = 0x5A; + dev->cmd[2] = 0x38; + dev->cmd[3] = 0x41; + dev->cmd[4] = 0x53; + dev->cmd[5] = 0x10; + dev->cmd[9] = 0x01; + if ((dev->err=dev->cmd.transport(NONE, NULL, 0))){ + sperror ("asus_errc_init",dev->err); return 1; + } + seek(dev,0); + return 0; +} + +int scan_asus::cmd_errc_getdata() +{ + /* initialize scan mode */ + dev->cmd[0] = 0x5A; + dev->cmd[2] = 0x38; + dev->cmd[3] = 0x41; + dev->cmd[4] = 0x53; + dev->cmd[5] = 0x10; + dev->cmd[9] = 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,8))){ + sperror ("asus_errc_getdata",dev->err); return 1; + } + return 0; +} + + + +// ********************** +int scan_asus::cmd_cd_errc_block(cd_errc *data) +{ +// seek(dev,lba); + data->e11 = 0; + data->e21 = 0; + data->e31 = 0; + data->e12 = 0; + data->e32 = 0; + data->uncr = 0; + + if (!cmd_errc_getdata()) { + data->bler = (dev->rd_buf[1] << 8) | dev->rd_buf[0]; + data->e22 = (dev->rd_buf[3] << 8) | dev->rd_buf[2]; + } else { + data->bler = 0; + data->e22 = 0; + } + lba=((int)dev->rd_buf[5] * 4500 + (int)dev->rd_buf[6] * 75 + (int)dev->rd_buf[7]); +// lba+=75; + return 0; +} + +int scan_asus::cmd_dvd_errc_block(dvd_errc *data) +{ + seek(dev,lba); + data->poe = 0; + data->pof = 0; + if (!cmd_errc_getdata()) { + data->pie = (dev->rd_buf[1] << 8) | dev->rd_buf[0]; + data->pif = (dev->rd_buf[3] << 8) | dev->rd_buf[2]; + } else { + data->pie = 0; + data->pif = 0; + } +// lba = swap4(dev->rd_buf+4); + lba+=16; + return 0; +} + +// ************* END SCAN COMMAND ********* +int scan_asus::cmd_errc_end() +{ + dev->cmd[0] = 0x5A; + dev->cmd[2] = 0x38; + dev->cmd[3] = 0x41; + dev->cmd[4] = 0x53; + dev->cmd[5] = 0x10; + dev->cmd[9] = 0x02; + if ((dev->err=dev->cmd.transport(NONE, NULL, 0))){ + sperror ("asus_errc_end",dev->err); return 1; + } + return 0; +} + diff --git a/plugins/asus/qscan_plugin.cpp b/plugins/asus/qscan_plugin.cpp new file mode 100644 index 0000000..99227bf --- /dev/null +++ b/plugins/asus/qscan_plugin.cpp @@ -0,0 +1,148 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009, Gennady "ShultZ" Kozlov + * + * 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 +#include + +scan_plugin* plugin_create(drive_info* idev) +{ + return new scan_asus(idev); +} + +void plugin_destroy(scan_plugin* iplugin) +{ + if (iplugin != NULL) delete iplugin; +} + +scan_asus::scan_asus(drive_info* idev) + : scan_plugin(), lba(0) +{ + dev = idev; + if (!dev->silent) printf("scan_asus()\n"); + devlist = (drivedesc*) &drivelist; + test=0; +} + +scan_asus::~scan_asus() +{ + if (!dev->silent) printf("~scan_asus()\n"); +} + +/* +int scan_asus::probe_drive() { + return DEV_FAIL; +} +*/ + +int scan_asus::errc_data() +{ + if (dev->media.type & DISC_CD) { + return (ERRC_DATA_BLER|ERRC_DATA_E22|ERRC_DATA_UNCR); + } else if (dev->media.type & DISC_DVD) { + return (ERRC_DATA_PIE|ERRC_DATA_PIF|ERRC_DATA_UNCR); + } + return 0; +} + +int scan_asus::check_test(unsigned int itest) +{ + switch (itest) { + case CHK_ERRC: + if (dev->media.type & ~DISC_DVDRAM) + return 0; + break; + default: + break; + } + return -1; +} + +int* scan_asus::get_test_speeds(unsigned int itest) +{ + switch (itest) { + case CHK_ERRC: +/* + if (dev->media.type & DISC_CD) + return (int*)SPEEDS_ERRC_CD; + if (dev->media.type & DISC_DVD) + return (int*)SPEEDS_ERRC_DVD; +*/ + break; + default: + break; + } + return NULL; +} + +int scan_asus::start_test(unsigned int itest, long ilba, int &speed) +{ + int r=-1; + switch (itest) { + case CHK_ERRC_CD: + case CHK_ERRC_DVD: + lba=ilba; + set_read_speed(speed); + r = cmd_errc_init(); + break; + default: + return -1; + } + if (!r) { + test = itest; + return r; + } else { + test = 0; + return r; + } +} + +int scan_asus::scan_block(void *data, long *ilba) +{ + int r=-1; + switch (test) { + case CHK_ERRC_CD: + r = cmd_cd_errc_block((cd_errc*)data); + if(ilba) *ilba = lba; + return r; + case CHK_ERRC_DVD: + r = cmd_dvd_errc_block((dvd_errc*)data); + if(ilba) *ilba = lba; + return r; + default: + return -1; + } +} + +int scan_asus::end_test() +{ + switch (test) { + case CHK_ERRC_CD: + case CHK_ERRC_DVD: + cmd_errc_end(); + break; + default: + break; + } + test=0; + return 0; +} + +/* +__attribute__((constructor)) void init() { + printf("init()\n"); +} + +__attribute__((destructor)) void exit() { + printf("exit()\n"); +} +*/ + diff --git a/plugins/asus/qscan_plugin.h b/plugins/asus/qscan_plugin.h new file mode 100644 index 0000000..fe5daab --- /dev/null +++ b/plugins/asus/qscan_plugin.h @@ -0,0 +1,65 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009, Gennady "ShultZ" Kozlov + * + * 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 __QSCAN_ASUS_H +#define __QSCAN_ASUS_H + +#include "qpx_scan_plugin_api.h" + +static const drivedesclist drivelist = +//static drivedesclist drivelist = +{ + { "ASUS ", DEV_ASUS, "DRW-1612", ASUS_1612, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "ASUS ", DEV_ASUS, "DRW-1814", ASUS_1612, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "ASUS ", DEV_ASUS, "DRW-2014S1", ASUS_2014, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "ASUS ", DEV_ASUS, "DRW-2014L1", ASUS_2014, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "", 0, "", 0} +}; + +static const char plugin_name[]="ASUS"; +static const char plugin_desc[]="Scan plugin for real ASUS devices (based on MediaTek chip)"; + +class drive_info; + +class scan_asus : public scan_plugin { +public: +// scan_asus(drive_info* idev=NULL); + scan_asus(drive_info* idev); + virtual ~scan_asus(); +// virtual int check_drive(); + virtual int probe_drive(); + virtual int errc_data(); + virtual int check_test(unsigned int test); + virtual int* get_test_speeds(unsigned int test); + virtual int start_test(unsigned int test, long slba, int &speed); + virtual int scan_block(void* data,long* ilba); + virtual int end_test(); + + virtual const char* name() { return plugin_name; }; + virtual const char* desc() { return plugin_desc; }; +private: + long lba; + + int cmd_errc_init(); + int cmd_errc_getdata(); + int cmd_errc_end(); +// CD ERRC methods + int cmd_cd_errc_block(cd_errc *data); + +// DVD ERRC methods + int cmd_dvd_errc_block(dvd_errc *data); +// end scan +}; + +#endif + diff --git a/plugins/benq/Makefile b/plugins/benq/Makefile new file mode 100644 index 0000000..6457266 --- /dev/null +++ b/plugins/benq/Makefile @@ -0,0 +1,3 @@ +LIBN = qscan_benq + +include ../Makefile.plugin diff --git a/plugins/benq/qscan_cmd.cpp b/plugins/benq/qscan_cmd.cpp new file mode 100644 index 0000000..7db10de --- /dev/null +++ b/plugins/benq/qscan_cmd.cpp @@ -0,0 +1,574 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2009 Gennady "ShultZ" Kozlov + * + * 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 +#include +#include +//#include + +#include +#include +#include + +#include + +//#define _BENQ_DEBUG 1 +#define _BENQ_DEBUG2 1 + + +int scan_benq::cmd_check_mode_init() +{ + dev->cmd[0] = 0xFD; + dev->cmd[1] = 0xF1; + dev->cmd[2] = 0x42; + dev->cmd[3] = 0x45; + dev->cmd[4] = 0x4E; + dev->cmd[5] = 0x51; +#ifdef _BENQ_DEBUG + printf("benq_check_mode_init\n"); +#endif + if ((dev->err=dev->cmd.transport(NONE,NULL,0))){ + if(!dev->silent) sperror ("benq_check_mode_init",dev->err); + return dev->err; + } + return 0; +} + +int scan_benq::cmd_check_mode_exit() +{ + dev->cmd[0] = 0xFD; + dev->cmd[1] = 0xF2; + dev->cmd[2] = 0x42; + dev->cmd[3] = 0x45; + dev->cmd[4] = 0x4E; + dev->cmd[5] = 0x51; +#ifdef _BENQ_DEBUG + printf("benq_check_mode_exit\n"); +#endif + if ((dev->err=dev->cmd.transport(NONE,NULL,0))){ + if(!dev->silent) sperror ("benq_check_mode_exit",dev->err); + return dev->err; + } + return 0; +} + +int scan_benq::cmd_get_result() +{ + dev->cmd[0] = 0xF8; + dev->cmd[8] = 0x02; +#ifdef _BENQ_DEBUG + printf("BENQ_CHECK_RESULT\n"); +#endif + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,2))){ + if(!dev->silent) sperror ("BENQ_CHECK_RESULT",dev->err); + return dev->err; + } + printf("BENQ Check result: %02d %02d\n", dev->rd_buf[0], dev->rd_buf[1]); + return ( (dev->rd_buf[0] << 8) | dev->rd_buf[1]); +} + +int scan_benq::cmd_set_speed(unsigned char sidx) +{ + dev->rd_buf[0] = 0xD2; + dev->rd_buf[1] = 0x0A; + dev->rd_buf[2] = sidx; + dev->rd_buf[3] = 0x00; + + dev->cmd[0] = 0xF9; + dev->cmd[8] = 0x04; +#ifdef _BENQ_DEBUG + printf("BENQ_SET_ERRC_SPEED\n"); +#endif + if ((dev->err=dev->cmd.transport(WRITE,dev->rd_buf,4))){ + if(!dev->silent) sperror ("BENQ_SET_ERRC_SPEED",dev->err); + return dev->err; + } + return (cmd_get_result()); +} + +int scan_benq::cmd_start_errc(int lba) +{ + dev->rd_buf[0] = 0xD4; + dev->rd_buf[1] = 0x91; + dev->rd_buf[2] = (lba >> 16) & 0xFF; + dev->rd_buf[3] = (lba >> 8) & 0xFF; + dev->rd_buf[4] = lba & 0xFF; + dev->rd_buf[5] = 0x00; + + dev->cmd[0] = 0xF9; + dev->cmd[8] = 0x06; +#ifdef _BENQ_DEBUG + printf("BENQ_ERRC_SEEK\n"); +#endif + if ((dev->err=dev->cmd.transport(WRITE,dev->rd_buf,6))){ + if(!dev->silent) sperror ("BENQ_ERRC_SEEK",dev->err); + return dev->err; + } + return (cmd_get_result()); +} + +int scan_benq::cmd_start_fete(int lba) +{ + dev->cmd[0] = 0xFD; + dev->cmd[1] = 0xFB; + dev->cmd[2] = 0x42; + dev->cmd[3] = 0x45; + dev->cmd[4] = 0x4E; + dev->cmd[5] = 0x51; + + dev->cmd[6] = 0x05; + dev->cmd[7] = 0x02; + dev->cmd[8] = (lba >> 16) & 0xFF; + dev->cmd[9] = (lba >> 8) & 0xFF; + dev->cmd[10]= lba & 0xFF; + dev->cmd[11]= 0x04; +#ifdef _BENQ_DEBUG + printf("BENQ_START_FETE\n"); +#endif + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,8))){ + if(!dev->silent) sperror ("BENQ_START_FETE",dev->err); + return dev->err; + } + return 0; +} + +// ************* Scan init commands ********* +int scan_benq::cmd_cd_errc_init(int &speed) +{ + int r; + if ((r = cmd_check_mode_init())) return r; +// ************ +// available test speeds for CD +// 0x00 - 8x +// 0x01 - 12x +// 0x06 - 24x +// 0x08 - 32x +// 0x09 - 40x +// 0x0B - 48x +// 0x10 - 8x CLV +// 0x12 - 12x CLV +// 0x13 - 16x CLV +// 0x14 - 24x P-CAV +// 0x15 - 32x P-CAV + + if (speed>=48) { speed = 48; sidx=0x0B; } // 48x + else if (speed>=40) { speed = 40; sidx=0x09; } // 40x + else if (speed>=32) { speed = 32; sidx=0x08; } // 32x + else if (speed>=24) { speed = 24; sidx=0x06; } // 24x + else if (speed>=16) { speed = 16; sidx=0x13; } // 16x CLV + else if (speed>=12) { speed = 12; sidx=0x12; } // 12x CLV + else { speed = 8; sidx=0x10; } // 8x CLV + + if (cmd_set_speed(sidx)) return dev->err; + +// ************ + memset(dev->rd_buf, 0, 10); + dev->rd_buf[0] = 0xC8; + dev->rd_buf[1] = 0x99; + dev->rd_buf[2] = 0x79; + + dev->cmd[0] = 0xF9; + dev->cmd[8] = 0x0A; +#ifdef _BENQ_DEBUG + printf("benq_init_cx_scan_3\n"); +#endif + if ((dev->err=dev->cmd.transport(WRITE,dev->rd_buf,10))){ + if(!dev->silent) sperror ("benq_init_cx_scan_3",dev->err); + return dev->err; + } + cmd_get_result(); + +// ************ + if (cmd_start_errc(0)) return 1; + return 0; +} + + +int scan_benq::cmd_dvd_errc_init(int &speed) +{ + int r; + if ((r = cmd_check_mode_init())) return r; +// ************ +// available test speeds for DVD +// 0x02 - 4x +// 0x04 - 6x +// 0x05 - 8x +// 0x08 - 12x +// 0x0A - 16x +// 0x0C - 1x CLV +// 0x0D - 2x CLV +// 0x0E - 4x CLV +// 0x0F - 6x CLV +// 0x10 - 8x P-CAV + if (speed >= 16) { speed = 16; sidx=0x0A; } // 16x CAV + else if (speed >= 12) { speed = 12; sidx=0x08; } // 12X CAV + else if (speed >= 8) { speed = 8; sidx=0x05; } // 8x CAV + else if (speed >= 6) { speed = 6; sidx=0x0F; } // 6x CLV + else if (speed >= 4) { speed = 4; sidx=0x0E; } // 4x CLV + else if (speed >= 2) { speed = 2; sidx=0x0D; } // 2x CLV + else { speed = 1; sidx=0x0C; } // 1x CLV + + if (cmd_set_speed(sidx)) return dev->err; + +// ************ + memset(dev->rd_buf, 0, 10); + dev->rd_buf[0] = 0xC8; + dev->rd_buf[1] = 0x99; + dev->rd_buf[2] = 0x79; + + dev->cmd[0] = 0xF9; + dev->cmd[8] = 0x0A; +#ifdef _BENQ_DEBUG + printf("benq_init_pi_scan_3\n"); +#endif + if ((dev->err=dev->cmd.transport(WRITE,dev->rd_buf,10))){ + if(!dev->silent) sperror ("benq_init_pi_scan_3",dev->err); + return dev->err; + } + if (cmd_get_result()) return 1; + +// ************ + if (cmd_start_errc(0x030000)) return 1; + return 0; +} + +int scan_benq::cmd_dvd_fete_init(int &speed) +{ + int r; + + dev->cmd[0] = 0xFD; + dev->cmd[1] = 0xF1; + dev->cmd[2] = 0x42; + dev->cmd[3] = 0x45; + dev->cmd[4] = 0x4E; + dev->cmd[5] = 0x51; +#ifdef _BENQ_DEBUG + printf("benq_check_mode_init\n"); +#endif + if ((dev->err=dev->cmd.transport(NONE,NULL,0))){ + if(!dev->silent) sperror ("benq_check_mode_init",dev->err); + return dev->err; + } + +// 0x13 16X +// 0x12 12X +// 0x11 10X ? +// 0x10 8X +// 0x0F 6X ? +// 0x0E 4X +// 0x0D 2.4X / 2X + if (speed >= 16) { speed = 16; sidx=0x13; } + else if (speed >= 12) { speed = 12; sidx=0x12; } + else if (speed >= 10) { speed = 10; sidx=0x11; } + else if (speed >= 8) { speed = 8; sidx=0x10; } + else if (speed >= 6) { speed = 6; sidx=0x0F; } + else if (speed >= 4) { speed = 4; sidx=0x0E; } + else { speed = 2; sidx=0x0D; } + + if ((r = cmd_check_mode_init())) return r; + + dev->cmd[0] = 0xFD; + dev->cmd[1] = 0xFB; + dev->cmd[2] = 0x42; + dev->cmd[3] = 0x45; + dev->cmd[4] = 0x4E; + dev->cmd[5] = 0x51; + + dev->cmd[6] = 0x05; + dev->cmd[7] = 0x02; + dev->cmd[8] = 0x25; + dev->cmd[9] = 0xC0; + dev->cmd[10]= 0x00; + dev->cmd[11]= 0x03; + + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,8))){ + if(!dev->silent) sperror ("benq_fete_init_1",dev->err); + return dev->err; + } + return (cmd_start_fete(0x030000)); +} + +// ************ Scan commands *********** +int scan_benq::cmd_read_block() +{ + dev->rd_buf[0] = 0xC1; + dev->rd_buf[1] = 0x9A; + dev->rd_buf[2] = 0x00; + dev->rd_buf[3] = 0x00; + + dev->cmd[0] = 0xF9; + dev->cmd[8] = 0x04; +#ifdef _BENQ_DEBUG + printf("benq_scan_block\n"); +#endif + if ((dev->err=dev->cmd.transport(WRITE,dev->rd_buf,4))){ + sperror ("benq_scan_block",dev->err); + return dev->err; + } + return 0; +} + +int scan_benq::cmd_getdata() +{ + dev->cmd[0] = 0xF8; + dev->cmd[7] = 0x01; + dev->cmd[8] = 0x02; +#ifdef _BENQ_DEBUG + printf("benq_read_err\n"); +#endif + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,258))){ + sperror ("benq_read_err",dev->err); + return dev->err; + } + return 0; +} + +// *********** +// CD tests +// *********** + +int scan_benq::cmd_cd_errc_block(cd_errc *data) +{ + int i,m,s,f,plba; +#ifdef _BENQ_DEBUG + printf("benq_cx_do_one_interval. LBA=%ld\n",lba); +#endif + int found=0; + int cnt=128; + do { + cmd_read_block(); + cmd_getdata(); + if ((!dev->rd_buf[0]) && (dev->rd_buf[1] == 0x63) && (dev->rd_buf[2] == 0x64) && (dev->rd_buf[3] == 0x6E)) { + found = 1; +#ifdef _BENQ_DEBUG2 + printf("\nData block found...\n"); + } else { +// printf("."); +// return 0; +#endif + } + msleep(20); + cnt--; + } while ((!found) && (cnt)); + if (!cnt) return 1; +#ifdef _BENQ_DEBUG2 + for (i=0; i<32; i++) { + if (!(i%8)) printf("| "); + printf("%02X ", dev->rd_buf[i] & 0xFF); + } + printf("|\n"); +#endif + + data->e11 = ntoh16(dev->rd_buf+0x0C); + data->e21 = ntoh16(dev->rd_buf+0x0E); // +0x0E or +0x30 ?? + data->e31 = ntoh16(dev->rd_buf+0x28); + data->bler = data->e11 + data->e21 + data->e31; + + data->e12 = ntoh16(dev->rd_buf+0x10); + data->e22 = ntoh16(dev->rd_buf+0x12); + data->e32 = ntoh16(dev->rd_buf+0x2A); +// *lba+=75; +// cnt++; + m = ((dev->rd_buf[7] >> 4) & 0x0F)*10 + (dev->rd_buf[7] & 0x0F); + s = ((dev->rd_buf[8] >> 4) & 0x0F)*10 + (dev->rd_buf[8] & 0x0F); + f = ((dev->rd_buf[9] >> 4) & 0x0F)*10 + (dev->rd_buf[9] & 0x0F); + plba = lba; + lba = (m*60 + s)*75 + f; +// printf("MSF: %02d:%02d.00 ; LBA: %d; C1:%4d; C2:%4d\n",m,s,*lba,*BLER,*E22); + if ((lba-plba) > 150) lba = plba+75; + if (lbard_buf[0] * 4500 + (int)dev->rd_buf[3] * 75 + (int)dev->rd_buf[2]); + *lba = (((dev->rd_buf[7] & 0xF0)*10 + (dev->rd_buf[7] & 0x0F))*60 + (dev->rd_buf[7] & 0xF0))*10 + (dev->rd_buf[7] & 0x0F); + return 0; +} +*/ + +// *********** +// DVD tests +// *********** + +int scan_benq::cmd_dvd_errc_block(dvd_errc *data) +{ + int i,plba; +// int pif; +#ifdef _BENQ_DEBUG + printf("benq_pie_pif_do_one_interval. LBA=%ld\n",lba); +#endif + int found=0; + int cnt=256; + do { + cmd_read_block(); + cmd_getdata(); + if ((!dev->rd_buf[0]) && (dev->rd_buf[1] == 0x64) && (dev->rd_buf[2] == 0x76) && (dev->rd_buf[3] == 0x64)) { + found = 1; +#ifdef _BENQ_DEBUG2 + printf("\nData block found...\n"); + } else { + printf("."); +// return 0; +#endif + } + msleep(20); + cnt--; + } while ((!found) && (cnt)); + if (!cnt) return 1; +#ifdef _BENQ_DEBUG2 + for (i=0; i<32; i++) { + if (!(i%8)) printf("| "); + printf("%02X ", dev->rd_buf[i] & 0xFF); + } + printf("|\n"); +#endif + // 1PIE/ROW + 2PIE/ROW + 3PIE/ROW + 4PIE/ROW + 5PIE/ROW + data->pie = ntoh16(dev->rd_buf+0x0C) + +ntoh16(dev->rd_buf+0x0E) + +ntoh16(dev->rd_buf+0x10) + +ntoh16(dev->rd_buf+0x12) + +ntoh16(dev->rd_buf+0x14); + data->pif = ntoh16(dev->rd_buf+0x16); + data->poe = +// +ntoh16(dev->rd_buf+0x18) + +ntoh16(dev->rd_buf+0x1A) + +ntoh16(dev->rd_buf+0x1C) + +ntoh16(dev->rd_buf+0x1E) + +ntoh16(dev->rd_buf+0x20) + +ntoh16(dev->rd_buf+0x22); + data->pof = ntoh16(dev->rd_buf+0x38); + plba = lba; + lba = (((dev->rd_buf[7]-3) << 16 )& 0xFF0000) + ((dev->rd_buf[8] << 8)&0xFF00 ) + (dev->rd_buf[9] & 0xFF); +// printf("LBA: %d; PIE:%4d; PIF:%4d\n",*lba,*pie, *pif); + if ((lba-plba) > 32) lba = plba+32; + if (lbacmd[0] = 0xFD; + dev->cmd[1] = 0xFB; + dev->cmd[2] = 0x42; + dev->cmd[3] = 0x45; + dev->cmd[4] = 0x4E; + dev->cmd[5] = 0x51; + + dev->cmd[6] = 0x05; + dev->cmd[7] = sidx; + dev->cmd[8] = (lba >> 16) & 0xFF + 0x03; + dev->cmd[9] = (lba >> 8) & 0xFF; + dev->cmd[10] = lba & 0xFF; + dev->cmd[11] = 0; + + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,8))){ + if(!dev->silent) sperror ("benq_fete_block",dev->err); + return dev->err; + } + + data->te = (ntoh16(dev->rd_buf+3) + 5) / 10; + data->fe = (ntoh16(dev->rd_buf+5) + 5) / 10; + lba+=0x1000; + return 0; +} + +// ************* END SCAN COMMAND ********* +int scan_benq::cmd_cd_end() +{ + int r; +#if 1 + memset(dev->rd_buf, 0, 10); + dev->rd_buf[0] = 0xD4; + dev->rd_buf[1] = 0x91; + + dev->cmd[0] = 0xF9; + dev->cmd[8] = 0x0A; + if ((dev->err=dev->cmd.transport(WRITE,dev->rd_buf,10))){ + sperror ("benq_end_scan_0",dev->err); + return dev->err; + } + cmd_get_result(); + +// ************ + dev->rd_buf[0] = 0xD4; + dev->rd_buf[1] = 0x91; + dev->rd_buf[2] = 0x00; + dev->rd_buf[3] = 0x02; + dev->rd_buf[4] = 0x00; + dev->rd_buf[5] = 0x00; + + dev->cmd[0] = 0xF9; + dev->cmd[8] = 0x06; + if ((dev->err=dev->cmd.transport(WRITE,dev->rd_buf,6))){ + sperror ("benq_end_scan_2",dev->err); + return dev->err; + } + + cmd_get_result(); +#endif +// ************ + if ((r = cmd_check_mode_exit())) return r; + return 0; +} + + +int scan_benq::cmd_dvd_end() +{ + int r; +#if 1 + memset(dev->rd_buf, 0, 10); + dev->rd_buf[0] = 0xD4; + dev->rd_buf[1] = 0x91; + + dev->cmd[0] = 0xF9; + dev->cmd[8] = 0x0A; + if ((dev->err=dev->cmd.transport(WRITE,dev->rd_buf,10))){ + sperror ("benq_end_scan_0",dev->err); + return dev->err; + } + cmd_get_result(); + +// ************ + dev->rd_buf[0] = 0xD4; + dev->rd_buf[1] = 0x91; + dev->rd_buf[2] = 0x03; + dev->rd_buf[3] = 0x00; + dev->rd_buf[4] = 0x00; + dev->rd_buf[5] = 0x00; + + dev->cmd[0] = 0xF9; + dev->cmd[8] = 0x06; + if ((dev->err=dev->cmd.transport(WRITE,dev->rd_buf,6))){ + sperror ("benq_end_scan_2",dev->err); + return dev->err; + } + cmd_get_result(); + +#endif +// ************ + if ((r = cmd_check_mode_exit())) return r; + return 0; +} + diff --git a/plugins/benq/qscan_plugin.cpp b/plugins/benq/qscan_plugin.cpp new file mode 100644 index 0000000..3fbd179 --- /dev/null +++ b/plugins/benq/qscan_plugin.cpp @@ -0,0 +1,201 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2009, Gennady "ShultZ" Kozlov + * + * 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 +#include + +static const int SPEEDS_ERRC_CD[] = { + 8*CD_SPEED_MULT, + 12*CD_SPEED_MULT, + 24*CD_SPEED_MULT, + 32*CD_SPEED_MULT, + 40*CD_SPEED_MULT, + 48*CD_SPEED_MULT, + 0 +}; + +static const int SPEEDS_ERRC_DVD[] = { + 1*DVD_SPEED_MULT, + 2*DVD_SPEED_MULT, + 4*DVD_SPEED_MULT, + 6*DVD_SPEED_MULT, + 8*DVD_SPEED_MULT, + 12*DVD_SPEED_MULT, + 16*DVD_SPEED_MULT, + 0 +}; + +scan_plugin* plugin_create(drive_info* idev) +{ + return new scan_benq(idev); +} + +void plugin_destroy(scan_plugin* iplugin) +{ + if (iplugin != NULL) delete iplugin; +} + +scan_benq::scan_benq(drive_info* idev) + : scan_plugin(), lba(0), sidx(0) +{ + dev = idev; + if (!dev->silent) printf("scan_benq()\n"); + devlist = (drivedesc*) &drivelist; + test=0; +} + +scan_benq::~scan_benq() +{ + if (!dev->silent) printf("~scan_benq()\n"); +} + + +int scan_benq::probe_drive() { + if (dev->media.type & DISC_CD) { + int spd=8; + if (cmd_cd_errc_init(spd)) return DEV_FAIL; + if (cmd_cd_end()) return DEV_FAIL; + } else if (dev->media.type & DISC_DVD) { + int spd=4; + if (cmd_dvd_errc_init(spd)) return DEV_FAIL; + if (cmd_dvd_end()) return DEV_FAIL; + } else { + return DEV_FAIL; + } + return DEV_PROBED; +} + + +int scan_benq::errc_data() +{ + if (dev->media.type & DISC_CD) { + return (ERRC_DATA_BLER|ERRC_DATA_E11|ERRC_DATA_E21|ERRC_DATA_E31|ERRC_DATA_E12|ERRC_DATA_E22|ERRC_DATA_E32|ERRC_DATA_UNCR); + } else if (dev->media.type & DISC_DVD) { + return (ERRC_DATA_PIE|ERRC_DATA_PIF|ERRC_DATA_POE|ERRC_DATA_POF|ERRC_DATA_UNCR); + } + return 0; +} + +int scan_benq::check_test(unsigned int itest) +{ + switch (itest) { + case CHK_ERRC: + return 0; + case CHK_FETE: +// if (dev->media.dstatus) return -1; + if (dev->media.type & DISC_DVD & ~DISC_DVDROM) + return 0; + break; + default: + break; + } + return -1; +} + +int* scan_benq::get_test_speeds(unsigned int itest) +{ + switch (itest) { + case CHK_ERRC: + case CHK_JB: + if (dev->media.type & DISC_CD) + return (int*)SPEEDS_ERRC_CD; + if (dev->media.type & DISC_DVD) + return (int*)SPEEDS_ERRC_DVD; + break; + case CHK_FETE: + if (dev->media.type & DISC_DVD) + // just use available write speeds for FE/TE test + return NULL; + break; + default: + break; + } + return NULL; +} + +int scan_benq::start_test(unsigned int itest, long ilba, int &speed) +{ + int r=-1; + sidx=0; + switch (itest) { + case CHK_ERRC_CD: + lba=ilba; + r = cmd_cd_errc_init(speed); + break; + case CHK_ERRC_DVD: + lba=ilba; + r = cmd_dvd_errc_init(speed); + break; + case CHK_FETE: +// if (dev->media.dstatus) return -1; + if (dev->media.type & DISC_DVD & ~DISC_DVDROM) { + lba=ilba; + r = cmd_dvd_fete_init(speed); + break; + } else { + return 0; + } + default: + return -1; + } + if (!r) { + test = itest; + return r; + } else { + test = 0; + return r; + } +} + +int scan_benq::scan_block(void *data, long *ilba) +{ + int r=-1; + switch (test) { + case CHK_ERRC_CD: + r = cmd_cd_errc_block((cd_errc*)data); + if(ilba) *ilba = lba; + return r; + case CHK_ERRC_DVD: + r = cmd_dvd_errc_block((dvd_errc*)data); + if(ilba) *ilba = lba; + return r; + case CHK_FETE: + if (dev->media.type & DISC_DVD & ~DISC_DVDROM) { + r = cmd_dvd_fete_block((cdvd_ft*)data); + } else { + return -1; + } + default: + return -1; + } +} + +int scan_benq::end_test() +{ + int r=0; + switch (test) { + case CHK_ERRC_CD: + r = cmd_cd_end(); + break; + case CHK_ERRC_DVD: + r = cmd_dvd_end(); + break; + case CHK_FETE: + r = cmd_check_mode_exit(); + break; + default: + break; + } + test=0; + return r; +} + diff --git a/plugins/benq/qscan_plugin.h b/plugins/benq/qscan_plugin.h new file mode 100644 index 0000000..cde966b --- /dev/null +++ b/plugins/benq/qscan_plugin.h @@ -0,0 +1,82 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2009, Gennady "ShultZ" Kozlov + * + * 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 __QSCAN_BENQ_H +#define __QSCAN_BENQ_H + +#include "qpx_scan_plugin_api.h" + +static const drivedesclist drivelist = +//static drivedesclist drivelist = +{ + { "BENQ ", DEV_BENQ_WR, "DVD DD DW1620", BENQ_DW1620, CHK_ERRC_CD | CHK_ERRC_DVD | CHK_FETE}, + { "BENQ ", DEV_BENQ_WR, "DVD DD DW1625", BENQ_DW1625, CHK_ERRC_CD | CHK_ERRC_DVD | CHK_FETE}, + { "BENQ ", DEV_BENQ_WR, "DVD DD DW1640", BENQ_DW1640, CHK_ERRC_CD | CHK_ERRC_DVD | CHK_FETE}, + { "BENQ ", DEV_BENQ_WR, "DVD DD DW1650", BENQ_DW1650, CHK_ERRC_CD | CHK_ERRC_DVD | CHK_FETE}, + { "BENQ ", DEV_BENQ_WR, "DVD DD DW1655", BENQ_DW1655, CHK_ERRC_CD | CHK_ERRC_DVD | CHK_FETE}, + +// // Plextor PX-740 only works if crossflashed to BENQ DW1640 +// { "PLEXTOR ", DEV_BENQ_WR, "DVDR PX-740", BENQ_DW1640, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "", 0, "", 0} +}; + +static const char plugin_name[]="BENQ"; +static const char plugin_desc[]="Scan plugin for BENQ and BENQ-based devices"; + +class drive_info; + +class scan_benq : public scan_plugin { +public: +// scan_benq(drive_info* idev=NULL); + scan_benq(drive_info* idev); + virtual ~scan_benq(); +// virtual int check_drive(); + virtual int probe_drive(); + virtual int errc_data(); + virtual int check_test(unsigned int test); + virtual int* get_test_speeds(unsigned int test); + virtual int start_test(unsigned int test, long slba, int &speed); + virtual int scan_block(void* data,long* ilba); + virtual int end_test(); + + virtual const char* name() { return plugin_name; }; + virtual const char* desc() { return plugin_desc; }; + +private: + long lba; + int cmd_check_mode_init(); + int cmd_check_mode_exit(); + int cmd_get_result(); + int cmd_set_speed(unsigned char sidx); + int cmd_start_errc(int lba); + int cmd_start_fete(int lba); + + int cmd_read_block(); + int cmd_getdata(); +// CD ERRC methods + int cmd_cd_errc_init(int &speed); + int cmd_cd_errc_block(cd_errc *data); + int cmd_cd_end(); + +// DVD ERRC methods + int cmd_dvd_errc_init(int &speed); + int cmd_dvd_errc_block(dvd_errc *data); + int cmd_dvd_end(); + + int cmd_dvd_fete_init(int &speed); + int cmd_dvd_fete_block(cdvd_ft *data); + + unsigned char sidx; +}; + +#endif diff --git a/plugins/benq_dvdrom/Makefile b/plugins/benq_dvdrom/Makefile new file mode 100644 index 0000000..f1b05df --- /dev/null +++ b/plugins/benq_dvdrom/Makefile @@ -0,0 +1,3 @@ +LIBN = qscan_benq_dvdrom + +include ../Makefile.plugin diff --git a/plugins/benq_dvdrom/qscan_cmd.cpp b/plugins/benq_dvdrom/qscan_cmd.cpp new file mode 100644 index 0000000..8b9df42 --- /dev/null +++ b/plugins/benq_dvdrom/qscan_cmd.cpp @@ -0,0 +1,187 @@ +/* + * BENQ DVDROM scan conmmands implementation + * + * This file is part of the QPxTool project. + * Copyright (C) 2005-2009 Gennady "ShultZ" Kozlov + * + * 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 +#include + +#include +#include +#include + +#include + +// ************* Scan init commands ********* +int scan_benqrom::cmd_scan_init() { + dev->cmd[0] = 0xF1; + dev->cmd[2] = 0xFF; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,4))){ + if(!dev->silent) sperror ("benq_rom_init_scan",dev->err); + return dev->err; + } + cnt=0; + return 0; +} + +int scan_benqrom::cmd_cd_errc_block(cd_errc *data) +{ + int len = 34; + if (!(cnt%cnt_max_cd)) { + dev->cmd[0] = 0x78; + if (!lba) { dev->cmd[5] = 0x01; dev->cmd[6] = 0x00; len=32; } + else { dev->cmd[5] = 0x00; dev->cmd[6] = 0x01; } + dev->cmd[8] = 0x20; + dev->cmd[10]= 0x10; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,len))){ + sperror ("benq_rom_cx_do_one_interval",dev->err); + return dev->err; + } + for (int i=0; i<4; i++) { + _c1[i] = ntoh16(dev->rd_buf+4+i*4); + _c2[i] = ntoh16(dev->rd_buf+6+i*4); +// _lba=((int)dev->rd_buf[0] * 4500 + (int)dev->rd_buf[3] * 75 + (int)dev->rd_buf[2]); + } + cnt = 0; + } + data->bler = _c1[cnt]; + data->e11 = 0; + data->e21 = 0; + data->e31 = 0; + data->e12 = 0; + data->e22 = _c2[cnt]; + data->e32 = 0; //_cu[cnt]; + data->uncr= 0; + lba+=75; + cnt++; +// *lba = _lba; + return 0; +} + +int scan_benqrom::cmd_cd_jb_block(cdvd_jb* data) +{ +//* + if (!lba) { + dev->cmd[0] = 0x78; + dev->cmd[5] = 0x01; + dev->cmd[6] = 0x00; + dev->cmd[8] = 0x20; + dev->cmd[10]= 0x10; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,32))){ + sperror ("benq_rom_jitter_cd_do_one_interval",dev->err); + return dev->err; + } + lba+=75; + } + dev->cmd[0] = 0x78; + dev->cmd[5] = 0x00; + dev->cmd[6] = 0x01; + dev->cmd[8] = 0x20; + dev->cmd[10]= 0x10; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,34))){ + sperror ("benq_rom_jitter_cd_do_one_interval",dev->err); + return dev->err; + } + data->jitter = 20*(int)dev->rd_buf[0x20]; + data->asymm = 0; +// *lba=((int)dev->rd_buf[0] * 4500 + (int)dev->rd_buf[3] * 75 + (int)dev->rd_buf[2]); + lba+=75; + return 0; +} + +int scan_benqrom::cmd_dvd_errc_block(dvd_errc *data) +{ + if (!(cnt%cnt_max_cd)) { + dev->cmd[0] = 0x78; + if (!lba) { dev->cmd[6] = 0x00; dev->cmd[7] = 0x10; } + else { dev->cmd[6] = 0x01; dev->cmd[7] = 0x00; } + dev->cmd[8] = 0x22; + dev->cmd[10]= 0x10; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,34))){ + sperror ("benq_rom_pie_pif_do_one_interval",dev->err); + return dev->err; + } + for (int i=0; i<4; i++) { + _pie[i] = ntoh16(dev->rd_buf+4+i*4); + _pif[i] = ntoh16(dev->rd_buf+6+i*4); +// _lba=((int)dev->rd_buf[0] * 4500 + (int)dev->rd_buf[3] * 75 + (int)dev->rd_buf[2]); + } + cnt = 0; + } + data->pie = _pie[cnt]; + data->pif = _pif[cnt]; + data->poe = 0; + data->pof = 0; + lba+=0x100; +// printf("BenQ Pi SCAN: cnt=%d\n",cnt); + cnt++; +// *lba = _lba; + return 0; +} + +int scan_benqrom::cmd_dvd_jb_block(cdvd_jb* data) +{ + dev->cmd[0] = 0x78; + if (!lba) { dev->cmd[6] = 0x00; dev->cmd[7] = 0x10; } + else { dev->cmd[6] = 0x01; dev->cmd[7] = 0x00; } + dev->cmd[8] = 0x22; + dev->cmd[10]= 0x10; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,34))){ + sperror ("benq_rom_cx_do_one_interval",dev->err); + return dev->err; + } + data->jitter = 20*(int)dev->rd_buf[0x20]; + data->asymm = 0; +// *lba=((int)dev->rd_buf[0] * 4500 + (int)dev->rd_buf[3] * 75 + (int)dev->rd_buf[2]); + lba+= 0x400; + return 0; +} + +// ************* END SCAN COMMAND ********* +int scan_benqrom::cmd_cd_end() +{ + dev->cmd[0] = 0x78; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,32))){ + sperror ("benq_rom_end_scan_cd",dev->err); + return dev->err; + } + printf("\n"); + return 0; +} + +int scan_benqrom::cmd_dvd_end() +{ + dev->cmd[0] = 0x78; + dev->cmd[7] = 0x40; + dev->cmd[8] = 0x22; + dev->cmd[10]= 0x10; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,34))){ + sperror ("benq_rom_end_scan_dvd",dev->err); + return dev->err; + } + return 0; +} + +/* +scan_commands commands_list_benq_rom = { + NULL, benq_rom_init_scan, benq_rom_cx_do_one_interval, benq_rom_cx_end_scan, + NULL, benq_rom_init_scan, benq_rom_jitter_CD_do_one_interval, benq_rom_cx_end_scan, + + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, benq_rom_init_scan, benq_rom_pie_pif_do_one_interval, benq_rom_pi_end_scan, + NULL, benq_rom_init_scan, benq_rom_jitter_DVD_do_16_ecc, benq_rom_pi_end_scan, +}; + +scan_commands commands_benq_rom() { return commands_list_benq_rom; } +*/ + diff --git a/plugins/benq_dvdrom/qscan_plugin.cpp b/plugins/benq_dvdrom/qscan_plugin.cpp new file mode 100644 index 0000000..0063a5e --- /dev/null +++ b/plugins/benq_dvdrom/qscan_plugin.cpp @@ -0,0 +1,154 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2009, Gennady "ShultZ" Kozlov + * + * 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 +#include + +scan_plugin* plugin_create(drive_info* idev) +{ + return new scan_benqrom(idev); +} + +void plugin_destroy(scan_plugin* iplugin) +{ + if (iplugin != NULL) delete iplugin; +} + +scan_benqrom::scan_benqrom(drive_info* idev) + : scan_plugin(), lba(0), cnt(0), _c1{}, _c2{}, _lba(0), _pie{}, _pif{} +{ + dev = idev; + if (!dev->silent) printf("scan_benqrom()\n"); + devlist = (drivedesc*) &drivelist; + test=0; +} + +scan_benqrom::~scan_benqrom() +{ + if (!dev->silent) printf("~scan_benqrom()\n"); +} + + +int scan_benqrom::probe_drive() { + if (dev->media.type & DISC_CD) { + if (cmd_scan_init()) return DEV_FAIL; + if (cmd_cd_end()) return DEV_FAIL; + } else if (dev->media.type & DISC_DVD) { + if (cmd_scan_init()) return DEV_FAIL; + if (cmd_dvd_end()) return DEV_FAIL; + } else { + return DEV_FAIL; + } + return DEV_PROBED; +} + +int scan_benqrom::errc_data() +{ + if (dev->media.type & DISC_CD) { + return (ERRC_DATA_BLER|ERRC_DATA_E22|ERRC_DATA_UNCR); + } else if (dev->media.type & DISC_DVD) { + return (ERRC_DATA_PIE|ERRC_DATA_PIF|ERRC_DATA_UNCR); + } + return 0; +} + +int scan_benqrom::check_test(unsigned int itest) +{ + switch (itest) { + case CHK_JB: + case CHK_ERRC: + if (dev->media.type & ~DISC_DVDRAM) + return 0; + break; + default: + break; + } + return -1; +} + +int scan_benqrom::start_test(unsigned int itest, long ilba, int &speed) +{ + int r=-1; + switch (itest) { + case CHK_ERRC_CD: + case CHK_ERRC_DVD: + case CHK_JB_CD: + case CHK_JB_DVD: + lba=ilba; + set_read_speed(speed); + r = cmd_scan_init(); + break; + default: + return -1; + } + if (!r) { + test = itest; + } else { + test = 0; + } + return r; +} + +int scan_benqrom::scan_block(void *data, long *ilba) +{ + int r=-1; + switch (test) { + case CHK_ERRC_CD: + r = cmd_cd_errc_block((cd_errc*)data); + if(ilba) *ilba = lba; + return r; + case CHK_JB_CD: + r = cmd_cd_jb_block((cdvd_jb*)data); + if(ilba) *ilba = lba; + return r; + case CHK_ERRC_DVD: + r = cmd_dvd_errc_block((dvd_errc*)data); + if(ilba) *ilba = lba; + return r; + case CHK_JB_DVD: + r = cmd_dvd_jb_block((cdvd_jb*)data); + if(ilba) *ilba = lba; + return r; + default: + return -1; + } +} + +int scan_benqrom::end_test() +{ + switch (test) { + case CHK_ERRC_CD: + case CHK_JB_CD: + cmd_cd_end(); + break; + case CHK_ERRC_DVD: + case CHK_JB_DVD: + cmd_dvd_end(); + break; + default: + break; + } +// cmd_scan_end(); + test=0; + return 0; +} + +/* +__attribute__((constructor)) void init() { + printf("init()\n"); +} + +__attribute__((destructor)) void exit() { + printf("exit()\n"); +} +*/ + diff --git a/plugins/benq_dvdrom/qscan_plugin.h b/plugins/benq_dvdrom/qscan_plugin.h new file mode 100644 index 0000000..f780157 --- /dev/null +++ b/plugins/benq_dvdrom/qscan_plugin.h @@ -0,0 +1,78 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2009, Gennady "ShultZ" Kozlov + * + * 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 __QSCAN_BENQDVDROM_H +#define __QSCAN_BENQDVDROM_H + +#include "qpx_scan_plugin_api.h" + +#define cnt_max_cd 4 +#define cnt_max_dvd 4 + +static const drivedesclist drivelist = +//static drivedesclist drivelist = +{ + { "PLEXTOR ", DEV_BENQ_RD, "DVD-ROM PX-130", BENQ_DV1650V, CHK_ERRC_CD | CHK_JB_CD | CHK_ERRC_DVD | CHK_JB_DVD }, + + { "", 0, "", 0} +}; + +static const char plugin_name[]="BENQ_DVDROM"; +static const char plugin_desc[]="Scan plugin for BENQ DVD-ROM devices"; + +class drive_info; + +class scan_benqrom : public scan_plugin { +public: +// scan_benqrom(drive_info* idev=NULL); + scan_benqrom(drive_info* idev); + virtual ~scan_benqrom(); +// virtual int check_drive(); + virtual int probe_drive(); + virtual int errc_data(); + virtual int check_test(unsigned int test); + virtual int start_test(unsigned int test, long slba, int &speed); + virtual int scan_block(void* data,long* ilba); + virtual int end_test(); + + virtual const char* name() { return plugin_name; }; + virtual const char* desc() { return plugin_desc; }; +private: + long lba; + + int cmd_scan_init(); + +// CD ERRC methods + int cmd_cd_errc_block(cd_errc *data); + int cmd_cd_jb_block(cdvd_jb *data); + +// DVD ERRC methods + int cmd_dvd_errc_block(dvd_errc *data); + int cmd_dvd_jb_block(cdvd_jb *data); + +// end scan + int cmd_cd_end(); + int cmd_dvd_end(); + + + + int cnt; + int _c1[cnt_max_cd]; + int _c2[cnt_max_cd]; + //int _cu[cnt_max_cd]; + int _lba; + int _pie[cnt_max_dvd]; + int _pif[cnt_max_dvd]; +}; + +#endif + diff --git a/plugins/generic/Makefile b/plugins/generic/Makefile new file mode 100644 index 0000000..0c31651 --- /dev/null +++ b/plugins/generic/Makefile @@ -0,0 +1,3 @@ +LIBN = qscan_generic + +include ../Makefile.plugin diff --git a/plugins/generic/qscan_cmd.cpp b/plugins/generic/qscan_cmd.cpp new file mode 100644 index 0000000..0394462 --- /dev/null +++ b/plugins/generic/qscan_cmd.cpp @@ -0,0 +1,84 @@ +/* + * generic MMC scan commands implementation ( C2 scan only ) + * + * This file is part of the QPxTool project. + * Copyright (C) 2005 Gennady "ShultZ" Kozlov + * + * 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 +#include + +#include +#include + +#include "qscan_plugin.h" + +//int scan_generic::cmd_cd_errc_block(cd_errc *data) +int scan_generic::c2calc(unsigned char* buf, unsigned int lba, unsigned char sects) +{ + const unsigned int sect_data = 2352; + const unsigned int sect_err = 294; + const unsigned int sect_size = sect_data + sect_err; + + unsigned int c2errs = 0; + unsigned int s, offs, i,ii, se; + + + for (s=0; s> ii) & 0x01 ) { +// if (!se) printf("C2 in sector %7d, first error in byte %4d ", lba+s, i*8+ii); + se++; + } + } +// if (se) printf(", %4d C2\n",se); + c2errs += se; + } + return c2errs; +} + +int scan_generic::cmd_cd_errc_block(cd_errc *data) +{ + int rsize=15; + data->bler = 0; + data->e11 = 0; + data->e21 = 0; + data->e31 = 0; + data->e12 = 0; + data->e22 = 0; + data->e32 = 0; + data->uncr=0; +// if (!lba) return -1; + for (int i=0; (i<5) && lbamedia.capacity; i++) { + if (lba + rsize > dev->media.capacity) + rsize = dev->media.capacity - lba; +// else +// rsize = 15; + + if (read_cd(dev, dev->rd_buf, lba, rsize, 0xFA)) + data->uncr++; + else + data->e22 += c2calc(dev->rd_buf, lba, rsize); + lba+=rsize; + } +// *lba = + return 0; +} + diff --git a/plugins/generic/qscan_plugin.cpp b/plugins/generic/qscan_plugin.cpp new file mode 100644 index 0000000..8915211 --- /dev/null +++ b/plugins/generic/qscan_plugin.cpp @@ -0,0 +1,108 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2008, Gennady "ShultZ" Kozlov + * + * 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 +#include + +scan_plugin* plugin_create(drive_info* idev){ + return new scan_generic(idev); +} + +void plugin_destroy(scan_plugin* iplugin){ + if (iplugin != NULL) delete iplugin; +} + +scan_generic::scan_generic(drive_info* idev) + : scan_plugin(), lba(0) +{ + dev = idev; + if (!dev->silent) printf("scan_generic()\n"); + devlist = (drivedesc*) &drivelist; + test=0; +} + +scan_generic::~scan_generic() { + if (!dev->silent) printf("~scan_generic()\n"); +} + +int scan_generic::probe_drive() { + if (dev->capabilities & CAP_C2) + return DEV_PROBED; + else + return DEV_FAIL; +} + +int scan_generic::errc_data() { + if (dev->media.type & DISC_CD) { + return (ERRC_DATA_E22|ERRC_DATA_UNCR); + } + return 0; +} + +int scan_generic::check_test(unsigned int itest) { + switch (itest) { + case CHK_ERRC: + if (dev->media.type & DISC_CD) + return 0; + break; + default: + break; + } + return -1; +} + +int scan_generic::start_test(unsigned int itest, long ilba, int &speed) { + int r=-1; + switch (itest) { + case CHK_ERRC_CD: + lba=ilba; + set_read_speed(speed); + r = (dev->capabilities & CAP_C2) ? 0 : 1; + break; + default: + return -1; + } + if (!r) { + test = itest; + } else { + test = 0; + } + return r; +} + +int scan_generic::scan_block(void *data, long *ilba) { + int r=-1; + switch (test) { + case CHK_ERRC_CD: + r = cmd_cd_errc_block((cd_errc*)data); + if(ilba) *ilba = lba; + return r; + default: + return -1; + } +} + +int scan_generic::end_test() { + test=0; + return 0; +} + +/* +__attribute__((constructor)) void init() { + printf("init()\n"); +} + +__attribute__((destructor)) void exit() { + printf("exit()\n"); +} +*/ + diff --git a/plugins/generic/qscan_plugin.h b/plugins/generic/qscan_plugin.h new file mode 100644 index 0000000..6b3e166 --- /dev/null +++ b/plugins/generic/qscan_plugin.h @@ -0,0 +1,53 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2008, Gennady "ShultZ" Kozlov + * + * 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 __QSCAN_GENERIC_H +#define __QSCAN_GENERIC_H + +#include "qpx_scan_plugin_api.h" + +static const drivedesclist drivelist = +//static drivedesclist drivelist = +{ + { "", 0, "", 0} +}; + +static const char plugin_name[]="C2P"; +static const char plugin_desc[]="Generic scan plugin for devices supported C2 pointers"; + +class drive_info; + +class scan_generic : public scan_plugin { +public: +// scan_generic(drive_info* idev=NULL); + scan_generic(drive_info* idev); + virtual ~scan_generic(); +// virtual int check_drive(); + virtual int probe_drive(); + virtual int errc_data(); + virtual int check_test(unsigned int test); + virtual int start_test(unsigned int test, long slba, int &speed); + virtual int scan_block(void* data,long* ilba); + virtual int end_test(); + + virtual const char* name() { return plugin_name; }; + virtual const char* desc() { return plugin_desc; }; +private: + long lba; + + int cmd_cd_errc_block(cd_errc *data); +// int c2calc(cd_errc *data); + int c2calc(unsigned char* buf, unsigned int lba, unsigned char sects); +}; + +#endif + diff --git a/plugins/liteon/Makefile b/plugins/liteon/Makefile new file mode 100644 index 0000000..5e5026c --- /dev/null +++ b/plugins/liteon/Makefile @@ -0,0 +1,3 @@ +LIBN = qscan_liteon + +include ../Makefile.plugin diff --git a/plugins/liteon/qscan_cmd.cpp b/plugins/liteon/qscan_cmd.cpp new file mode 100644 index 0000000..f30751e --- /dev/null +++ b/plugins/liteon/qscan_cmd.cpp @@ -0,0 +1,437 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2006 Gennady "ShultZ" Kozlov + * + * + * Thanks to Artur Kalimullin (Kaliy) for assistance in LiteOn Cx scan implementation + * + * + * 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 +#include + +#include +#include +#include +#include + +#include + +// ************* Scan init commands ********* +int scan_liteon::cmd_cd_errc_init_old() { + dev->cmd[0] = 0xDF; + dev->cmd[1] = 0xA3; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,256))){ + sperror ("LiteOn_init_A_cx_scan",dev->err); return 1; + } +// printf("LiteOn_init_A_cx_scan: OK\n"); + + dev->cmd[0] = 0xDF; + dev->cmd[1] = 0xA0; + dev->cmd[4] = 0x02; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,256))){ + sperror ("LiteOn_init_B_cx_scan",dev->err); return 1; + } +// printf("LiteOn_init_B_cx_scan: OK\n"); + + dev->cmd[0] = 0xDF; + dev->cmd[1] = 0xA0; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,256))){ + sperror ("LiteOn_init_C_cx_scan",dev->err); return 1; + } +// printf("LiteOn_init_C_cx_scan: OK\n"); + + dev->cmd[0] = 0xDF; + dev->cmd[1] = 0xA0; + dev->cmd[4] = 0x04; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,256))){ + sperror ("LiteOn_init_D_cx_scan",dev->err); return 1; + } +// printf("LiteOn_init_D_scan: OK\n"); + + dev->cmd[0] = 0xDF; + dev->cmd[1] = 0xA0; + dev->cmd[4] = 0x02; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,256))){ + sperror ("LiteOn_init_E_cx_scan",dev->err); return 1; + } + printf(COL_YEL "LiteOn: using OLD CD ERRC commands" COL_NORM "\n"); + return 0; +} + +int scan_liteon::cmd_cd_errc_init_new() { +// seek to first sector + if (seek(dev, 0)) + return 1; +// check if ERRC command works + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x0E; + dev->cmd[11] = 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,0x10))) { + sperror ("LiteOn_errc_cd_probe NEW",dev->err); + cd_errc_new = false; + return 1; + } + return 0; +} + +int scan_liteon::cmd_cd_errc_init() { + cd_errc_new = true; + if (cmd_cd_errc_init_new()) + return cmd_cd_errc_init_old(); + printf(COL_GRN "LiteOn: using new CD ERRC commands" COL_NORM "\n"); + return 0; +} + +int scan_liteon::cmd_dvd_errc_init() { +// seek to first sector + if (seek(dev, 0)) + return 1; +// check if ERRC command works + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x0E; + dev->cmd[8] = 0x10; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,0x10))){ + sperror ("LiteOn_errc_dvd_probe",dev->err); return 1; + } + return 0; +} + +int scan_liteon::cmd_bd_errc_init() { +// seek to first sector + if (seek(dev, 0)) + return 1; +// check if ERRC command works + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x0E; + dev->cmd[11] = 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,0x10))){ + sperror ("LiteOn_errc_bd_probe",dev->err); return 1; + } + return 0; +} + +// fe/te data redout +int scan_liteon::cmd_fete_get_data(cdvd_ft *data) { + dev->cmd[0] = 0xDF; + dev->cmd[1] = 0x08; + dev->cmd[2] = 0x02; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,65536))){ + sperror ("LiteOn_FETE get data",dev->err); return 1; + } + data->te=dev->rd_buf[0]; + data->fe=dev->rd_buf[1]; + return 0; +} + +// current position readout for fe/te +int scan_liteon::cmd_fete_get_position() { + dev->cmd[0] = 0xDF; + dev->cmd[1] = 0x02; + dev->cmd[2] = 0x09; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,65536))){ + sperror ("LiteOn_FETE get LBA",dev->err); return 1; + } + return 0; +} + +int scan_liteon::cmd_cd_fete_init(int& speed) { +// start scan command + dev->cmd[0] = 0xDF; + dev->cmd[1] = 0x08; + dev->cmd[2] = 0x01; + dev->cmd[4] = 0x02; // CD + + dev->cmd[7] = speed & 0xFF; + dev->cmd[11]= 0x00; + + memset(dev->rd_buf,0,16); + if ((dev->err=dev->cmd.transport(WRITE,dev->rd_buf,16))){ + sperror ("LiteOn_FETE_init scan",dev->err); return 1; + } + return 0; +} + +int scan_liteon::cmd_dvd_fete_init(int& speed) { +// start scan command + dev->cmd[0] = 0xDF; + dev->cmd[1] = 0x08; + dev->cmd[2] = 0x01; + dev->cmd[3] = 0x03; // DVD + + dev->cmd[7] = speed & 0xFF; + dev->cmd[11]= 0x00; + + memset(dev->rd_buf,0,16); + + if ((dev->err=dev->cmd.transport(WRITE,dev->rd_buf,16))){ + sperror ("LiteOn_FETE_init scan",dev->err); return 1; + } + return 0; +} + +int scan_liteon::cmd_bd_fete_init(int& speed) { +// start scan command + dev->cmd[0] = 0xDF; + dev->cmd[1] = 0x08; + dev->cmd[2] = 0x01; + + dev->cmd[7] = speed & 0xFF; + dev->cmd[11]= 0x00; + + memset(dev->rd_buf,0,16); + + if ((dev->err=dev->cmd.transport(WRITE,dev->rd_buf,16))){ + sperror ("LiteOn_FETE_init scan",dev->err); return 1; + } + return 0; +} + +int scan_liteon::cmd_fete_init(int& speed) { + if (dev->media.type & (DISC_CD & ~DISC_CDROM)) { + return cmd_cd_fete_init(speed); + } else if (dev->media.type & (DISC_DVD & ~DISC_DVDROM)) { + return cmd_dvd_fete_init(speed); + } else if (dev->media.type & (DISC_BD & ~DISC_BD_ROM)) { + return cmd_bd_fete_init(speed); + } + return -1; +} + +// ********************** CD ERRC commands + + +int scan_liteon::cmd_cd_errc_read() { + dev->cmd[0] = 0xDF; + dev->cmd[1] = 0x82; + dev->cmd[2] = 0x09; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,256))){ + sperror ("LiteOn_cx_do_one_interval_scan",dev->err); return 1; + } +// printf("LiteOn_cx_do_one_interval_scan: OK"); + return 0; +} + +int scan_liteon::cmd_cd_errc_getdata(cd_errc* data) { + dev->cmd[0] = 0xDF; + dev->cmd[1] = 0x82; + dev->cmd[2] = 0x05; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,256))){ + sperror ("LiteOn_cx_do_one_interval_readout",dev->err); return 1; + } +// printf("LiteOn_cx_do_one_interval_readout: OK"); +#if 0 + for (int i=0; i<32; i++) { + printf(" %02X",dev->rd_buf[i]); + } + printf("\n"); +#endif + data->bler = ntoh16(dev->rd_buf); + data->e11 = 0; + data->e21 = 0; + data->e31 = 0; + data->e12 = 0; + data->e22 = ntoh16(dev->rd_buf+2); +// data->e32 = ntoh16(dev->rd_buf+4); + data->e32 = dev->rd_buf[4]; + data->uncr = 0; + return 0; +} + +int scan_liteon::cmd_cd_errc_inteval_rst() { + dev->cmd[0] = 0xDF; + dev->cmd[1] = 0x97; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,256))){ + sperror ("LiteOn_cx_do_one_interval_rst",dev->err); return 1; + } + return 0; +} + +int scan_liteon::cmd_cd_errc_block_old(cd_errc *data) +{ + if (cmd_cd_errc_read()) return dev->err; + if (cmd_cd_errc_getdata(data)) return dev->err; + lba+=75; + if (cmd_cd_errc_inteval_rst()) return dev->err; +// printf("LiteOn_cx_do_one_interval_rst: OK"); + return 0; +} + +int scan_liteon::cmd_cd_errc_block_new(cd_errc *data) +{ + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x0E; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,10))){ + sperror ("LiteOn_errc_cd_read_block",dev->err); return 1; + } + lba = dev->rd_buf[1] * 60 * 75 + + dev->rd_buf[2] * 75 + + dev->rd_buf[3]; + + data->bler = ntoh16(dev->rd_buf+4); + data->e11 = 0; + data->e21 = 0; + data->e31 = 0; + data->e12 = 0; + data->e22 = ntoh16(dev->rd_buf+6); + data->e32 = 0; + data->uncr = 0; + return 0; +} + +int scan_liteon::cmd_cd_errc_block(cd_errc *data) +{ + return cd_errc_new ? cmd_cd_errc_block_new(data) : cmd_cd_errc_block_old(data); +} + +// ********************** DVD ERRC commands +int scan_liteon::cmd_dvd_errc_block(dvd_errc *data) +{ + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x0E; + dev->cmd[8] = 0x10; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,10))){ + sperror ("LiteOn_errc_dvd_read_block",dev->err); return 1; + } +#if 0 + for (int i=0; i<10; i++) { + printf(" %02X",dev->rd_buf[i]); + } + printf("\n"); +#endif + +// Data Received: +// 00000000 00 00 00 8E 00 00 00 00 ...Ž.... + +// lba = ((dev->rd_buf[1] << 16 )& 0xFF0000) + ((dev->rd_buf[2] << 8)&0xFF00 ) + (dev->rd_buf[3] & 0xFF); + lba = ntoh32(dev->rd_buf); + + data->pie = ntoh16(dev->rd_buf+4); + data->pif = ntoh16(dev->rd_buf+6); + data->poe = 0; + data->pof = 0; + return 0; +} + +// ********************** BD ERRC commands +int scan_liteon::cmd_bd_errc_block(bd_errc *data) +{ + bool retry = false; + if (!lba) { + retry = true; + // if first sector scan requested + // we have to seek to first sector + dev->cmd[0] = MMC_SEEK; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,2048))){ + sperror ("READ",dev->err); return 1; + } + } + +bd_errc_repeat: + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x0E; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,0x10))) { + sperror ("LiteOn_errc_bd_read_block",dev->err); return 1; + } + lba = ntoh32(dev->rd_buf); + + if (!lba && retry) { + retry = false; + goto bd_errc_repeat; + } + + data->ldc = ntoh16(dev->rd_buf+4); + data->bis = ntoh16(dev->rd_buf+6); + data->uncr = 0; + +// workaround to skip strange LDC/BIS data for last block + if (data->ldc > 9700 || data->bis >= 768) { + data->ldc = 0; + data->bis = 0; + } + return 0; +} + +// *************** FE/TE scan block + +int scan_liteon::cmd_cd_fete_block(cdvd_ft *data) { +// fe/te data redout + if (cmd_fete_get_data(data)) return 1; + +// current lba redout + if (cmd_fete_get_position()) return 1; + int tlba = (((dev->rd_buf[0] >> 4) & 0x0F) * 10 + (dev->rd_buf[0] & 0x0F)) * 60 * 75 + + (((dev->rd_buf[1] >> 4) & 0x0F) * 10 + (dev->rd_buf[1] & 0x0F)) * 75 + + (((dev->rd_buf[2] >> 4) & 0x0F) * 10 + (dev->rd_buf[2] & 0x0F)); + + if (tlba < lba) + return -1; + + lba = tlba; + return 0; +} + +int scan_liteon::cmd_dvd_fete_block(cdvd_ft *data) { +// fe/te data redout + if (cmd_fete_get_data(data)) return 1; + +// current lba redout + if (cmd_fete_get_position()) return 1; + lba = ntoh32(dev->rd_buf) >> 8; + return 0; +} + +int scan_liteon::cmd_bd_fete_block(cdvd_ft *data) { +// fe/te data redout + if (cmd_fete_get_data(data)) return 1; + +// current lba redout + if (cmd_fete_get_position()) return 1; + lba = ntoh32(dev->rd_buf); + return 0; +} + +// ************* END SCAN COMMANDS ********* +int scan_liteon::cmd_cd_errc_end() { + if (cd_errc_new) + return 0; + + dev->cmd[0] = 0xDF; + dev->cmd[1] = 0xA3; + dev->cmd[2] = 0x01; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,256))){ + sperror ("LiteOn_end_scan",dev->err); return 1; + } + return 0; +} + +int scan_liteon::cmd_dvd_errc_end() { + dev->err = 0; + return 0; +} + +int scan_liteon::cmd_bd_errc_end() { + dev->err = 0; + return 0; +} + diff --git a/plugins/liteon/qscan_plugin.cpp b/plugins/liteon/qscan_plugin.cpp new file mode 100644 index 0000000..0354f87 --- /dev/null +++ b/plugins/liteon/qscan_plugin.cpp @@ -0,0 +1,178 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2008, Gennady "ShultZ" Kozlov + * + * 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 +#include + +scan_plugin* plugin_create(drive_info* idev){ + return new scan_liteon(idev); +} + +void plugin_destroy(scan_plugin* iplugin){ + if (iplugin != NULL) delete iplugin; +} + +scan_liteon::scan_liteon(drive_info* idev) + : scan_plugin(), lba(0), cd_errc_new(false) +{ + dev = idev; + if (!dev->silent) printf("scan_liteon()\n"); + devlist = (drivedesc*) &drivelist; + blklist = (drivedesc*) &blacklist; + test=0; +} + +scan_liteon::~scan_liteon() { + if (!dev->silent) printf("~scan_liteon()\n"); +} + +int scan_liteon::probe_drive() { +#ifndef PLUGINS_LITEON_NOPROBE + if (dev->media.type & DISC_CD) { + if (cmd_cd_errc_init()) return DEV_FAIL; + if (cmd_cd_errc_end()) return DEV_FAIL; + } else if (dev->media.type & DISC_DVD) { + if (cmd_dvd_errc_init()) return DEV_FAIL; + if (cmd_dvd_errc_end()) return DEV_FAIL; + } else if (dev->media.type & DISC_BD) { + if (cmd_bd_errc_init()) return DEV_FAIL; + if (cmd_bd_errc_end()) return DEV_FAIL; + } else { + return DEV_FAIL; + } + return DEV_PROBED; +#else + return DEV_FAIL; +#endif +} + +int scan_liteon::errc_data() +{ + if (dev->media.type & DISC_CD) { + return (ERRC_DATA_BLER|ERRC_DATA_E22|ERRC_DATA_E32|ERRC_DATA_UNCR); + } else if (dev->media.type & DISC_DVD) { + return (ERRC_DATA_PIE|ERRC_DATA_PIF|ERRC_DATA_UNCR); + } else if (dev->media.type & DISC_BD) { + return (ERRC_DATA_LDC|ERRC_DATA_BIS|ERRC_DATA_UNCR); + } + return 0; +} + +int scan_liteon::check_test(unsigned int itest) +{ + switch (itest) { + case CHK_ERRC: + if (dev->media.type & ~DISC_DVDRAM) + return 0; + break; + case CHK_FETE: + if (dev->media.type & ~DISC_DVDROM) + return 0; + break; + default: + return -1; + } + return -1; +} + +int* scan_liteon::get_test_speeds(unsigned int itest) { return NULL; } + +int scan_liteon::start_test(unsigned int itest, long ilba, int &speed) +{ + int r=-1; + switch (itest) { + case CHK_ERRC_CD: + lba=ilba; + set_read_speed(speed); + r = cmd_cd_errc_init(); + break; + case CHK_ERRC_DVD: + lba=ilba; + set_read_speed(speed); + r = cmd_dvd_errc_init(); + break; + case CHK_ERRC_BD: + lba=ilba; + set_read_speed(speed); + r = cmd_bd_errc_init(); + break; + case CHK_FETE: + start_stop(dev,1); + seek(dev,0); + if (r = cmd_fete_init(speed)) + return -1; + r = cmd_fete_init(speed); + break; + default: + return -1; + } + if (!r) { + test = itest; + return r; + } else { + end_test(); + return r; + } +} + +int scan_liteon::scan_block(void *data, long *ilba) +{ + int r=-1; + switch (test) { + case CHK_ERRC_CD: + r = cmd_cd_errc_block((cd_errc*)data); + if(ilba) *ilba = lba; + return r; + case CHK_ERRC_DVD: + r = cmd_dvd_errc_block((dvd_errc*)data); + if(ilba) *ilba = lba; + return r; + case CHK_ERRC_BD: + r = cmd_bd_errc_block((bd_errc*)data); + if(ilba) *ilba = lba; + return r; + case CHK_FETE: + if (dev->media.type & DISC_CD) { + r = cmd_cd_fete_block((cdvd_ft*)data); + } else if (dev->media.type & DISC_DVD) { + r = cmd_dvd_fete_block((cdvd_ft*)data); + } else if (dev->media.type & DISC_BD) { + r = cmd_bd_fete_block((cdvd_ft*)data); + } + if(ilba) *ilba = lba; + return r; + default: + return -1; + } +} + +int scan_liteon::end_test() +{ + switch (test) { + case CHK_ERRC_CD: + cmd_cd_errc_end(); + break; + case CHK_ERRC_DVD: + cmd_dvd_errc_end(); + break; + case CHK_ERRC_BD: + cmd_bd_errc_end(); + break; + case CHK_FETE: + return 0; + break; + default: + break; + } + test=0; + return 0; +} diff --git a/plugins/liteon/qscan_plugin.h b/plugins/liteon/qscan_plugin.h new file mode 100644 index 0000000..75811fa --- /dev/null +++ b/plugins/liteon/qscan_plugin.h @@ -0,0 +1,193 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2008, Gennady "ShultZ" Kozlov + * + * 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 __QSCAN_LITEON_H +#define __QSCAN_LITEON_H + +#include "qpx_scan_plugin_api.h" + +static const drivedesclist drivelist = +//static drivedesclist drivelist = +{ + { "LITE-ON ", DEV_LITEON, "LTR-52327S", LTN_CDR_G7, CHK_ERRC_CD }, + + { "LITE-ON ", DEV_LITEON, "DVDRW LDW-811S", LTN_DVDR_G1, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "LITE-ON ", DEV_LITEON, "DVDRW LDW-451S", LTN_DVDR_G2, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW LDW-851S", LTN_DVDR_G2, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW SOHW-812S", LTN_DVDR_G2, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVD+RW SOHW-802S", LTN_DVDR_G2, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW SOHW-832S", LTN_DVDR_G2, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVD+RW SOHW-822S", LTN_DVDR_G2, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "LITE-ON ", DEV_LITEON, "DVDRW SOHW-1213S", LTN_DVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW SOHW-1613S", LTN_DVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW SOHW-1633S", LTN_DVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW SOHW-1653S", LTN_DVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "LITE-ON ", DEV_LITEON, "DVDRW SOHW-1673S", LTN_DVDR_G4, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW SOHW-1693S", LTN_DVDR_G4, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "LITE-ON ", DEV_LITEON, "DVDRW SHW-1635S", LTN_DVDR_G5, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW SHW-16H5S", LTN_DVDR_G5, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW SHW-160P6S", LTN_DVDR_G6, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW SHW-160H6S", LTN_DVDR_G6, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW SHM-165P6S", LTN_DVDR_G6, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW SHM-165H6S", LTN_DVDR_G6, CHK_ERRC_CD | CHK_ERRC_DVD }, + + + { "LITE-ON ", DEV_LITEON, "DVDRW SH-16A7", LTN_DVDR_G7, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW LH-16W1", LTN_DVDR_G7, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW LH-16A1", LTN_DVDR_G7, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW LH-18A1", LTN_DVDR_G7, CHK_ERRC_CD | CHK_ERRC_DVD }, + + + { "LITE-ON ", DEV_LITEON, "DVDRW LH-20A1", LTN_DVDR_G8, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW DH-20A3", LTN_DVDR_G8, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "LITE-ON ", DEV_LITEON, "DVDRW DH-20A4", LTN_DVDR_G8, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "ATAPI ", DEV_LITEON, "iHBS112", LTN_BDR, CHK_ERRC_CD | CHK_ERRC_DVD | CHK_ERRC_BD }, + { "ATAPI ", DEV_LITEON, "iHBS212", LTN_BDR, CHK_ERRC_CD | CHK_ERRC_DVD | CHK_ERRC_BD }, + { "ATAPI ", DEV_LITEON, "iHBS312", LTN_BDR, CHK_ERRC_CD | CHK_ERRC_DVD | CHK_ERRC_BD }, + + { "SONY ", DEV_LITEON, "DVD RW DW-U18A", LTN_DVDR_G2, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "SONY ", DEV_LITEON, "DVD RW DRU-700A", LTN_DVDR_G2, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "SONY ", DEV_LITEON, "DVD RW DW-D18A", LTN_DVDR_G2, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "SONY ", DEV_LITEON, "DVD RW DW-U20A", LTN_DVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "SONY ", DEV_LITEON, "DVD RW DW-U21A", LTN_DVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "SONY ", DEV_LITEON, "DVD RW DRU-710A", LTN_DVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "SONY ", DEV_LITEON, "DVD RW DW-D22A", LTN_DVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "SONY ", DEV_LITEON, "DVD RW DW-D23A", LTN_DVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "SONY ", DEV_LITEON, "DVD RW DRU-720A", LTN_DVDR_G4, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "SONY ", DEV_LITEON, "DVD RW DW-D26A", LTN_DVDR_G4, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "SONY ", DEV_LITEON, "DVD RW DRU-800A", LTN_DVDR_G4, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "SONY ", DEV_LITEON, "DVD RW DW-Q28A", LTN_DVDR_G4, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "SONY ", DEV_LITEON, "DVD RW DW-Q30A", LTN_DVDR_G5, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "SONY ", DEV_LITEON, "DVD RW DW-Q31A", LTN_DVDR_G5, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "SONY ", DEV_LITEON, "DVD RW DW-Q120A", LTN_DVDR_G6, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "SONY ", DEV_LITEON, "DVD RW DW-G120A", LTN_DVDR_G6, CHK_ERRC_CD | CHK_ERRC_DVD }, + + + { "TEAC ", DEV_LITEON, "DV-W58G", LTN_DVDR_G1, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "TEAC ", DEV_LITEON, "DV-W58G-A", LTN_DVDR_G2, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "TEAC ", DEV_LITEON, "DV-W512G", LTN_DVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "TEAC ", DEV_LITEON, "DV-W516G", LTN_DVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "TEAC ", DEV_LITEON, "DV-W516GA", LTN_DVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "TEAC ", DEV_LITEON, "DV-W516GB", LTN_DVDR_G4, CHK_ERRC_CD | CHK_ERRC_DVD }, + +// { "ASUS ", DEV_LITEON, "DRW-20A1", LTN_DVDR_G7, CHK_ERRC_CD | CHK_ERRC_DVD }, +// { "ASUS ", DEV_LITEON, "DRW-20B1", LTN_DVDR_G7, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "PLEXTOR ", DEV_LITEON, "DVR PX-806", LTN_iHAx1, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "PLEXTOR ", DEV_LITEON, "DVR PX-850", LTN_iHAx3, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "Optiarc ", DEV_LITEON, "DVD RW AD-7191", LTN_DVDR_G6, CHK_ERRC_CD | CHK_ERRC_DVD }, + +// Slimtype + { "Slimtype", DEV_LITEON, "DVD+RW SDW-421S", LTN_SDVDR_G1, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "Slimtype", DEV_LITEON, "DVDRW SDW-431S", LTN_SDVDR_G1, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "Slimtype", DEV_LITEON, "DVDRW SOSW-852S", LTN_SDVDR_G2, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "Slimtype", DEV_LITEON, "DVD+RW SOSW-862S", LTN_SDVDR_G2, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "Slimtype", DEV_LITEON, "DVDRW SOSW-813S", LTN_SDVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "Slimtype", DEV_LITEON, "DVDRW SOSW-833S", LTN_SDVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "Slimtype", DEV_LITEON, "DVD A DS8A2S ", LTN_SDVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "SONY ", DEV_LITEON, "DVD RW DW-D56A", LTN_SDVDR_G2, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "SONY ", DEV_LITEON, "DVD+RW DW-R56A", LTN_SDVDR_G2, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "SONY ", DEV_LITEON, "DVD RW DW-Q58A", LTN_SDVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "SONY ", DEV_LITEON, "DVD RW DW-Q60A", LTN_SDVDR_G3, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "", 0, "", 0} +}; + +static const drivedesclist blacklist = +{ + { "TEAC ", DEV_TEAC, "CD-W552E", 0 }, + { "HL-DT-ST", DEV_LG, "", 0 }, + { "TSSTcorp", DEV_TSST, "", 0 }, + + { "", 0, "", 0} +}; + +static const char plugin_name[]="LITEON"; +static const char plugin_desc[]="Scan plugin for LITE-ON and LITE-ON-based devices"; + +class drive_info; + +class scan_liteon : public scan_plugin { +public: +// scan_liteon(drive_info* idev=NULL); + scan_liteon(drive_info* idev); + virtual ~scan_liteon(); +// virtual int check_drive(); + virtual int probe_drive(); + virtual int errc_data(); + virtual int check_test(unsigned int test); + virtual int* get_test_speeds(unsigned int test); + virtual int start_test(unsigned int test, long slba, int &speed); + virtual int scan_block(void* data,long* ilba); + virtual int end_test(); + + virtual const char* name() { return plugin_name; }; + virtual const char* desc() { return plugin_desc; }; +private: + long lba; + bool cd_errc_new; + +// CD ERRC methods + int cmd_cd_errc_init_old(); + int cmd_cd_errc_init_new(); + int cmd_cd_errc_init(); + + int cmd_cd_errc_read(); + int cmd_cd_errc_getdata(cd_errc *data); + int cmd_cd_errc_inteval_rst(); + int cmd_cd_errc_block_old(cd_errc *data); + int cmd_cd_errc_block_new(cd_errc *data); + int cmd_cd_errc_block(cd_errc *data); + + int cmd_cd_errc_end(); + +// DVD ERRC methods + int cmd_dvd_errc_init(); + int cmd_dvd_errc_block(dvd_errc *data); + int cmd_dvd_errc_end(); + +// BD ERRC methods + int cmd_bd_errc_init(); + int cmd_bd_errc_block(bd_errc *data); + int cmd_bd_errc_end(); + +// FE/TE test methods + int cmd_fete_init(int& speed); + int cmd_fete_get_data(cdvd_ft *data); + int cmd_fete_get_position(); + + int cmd_cd_fete_init(int& speed); + int cmd_cd_fete_block(cdvd_ft *data); + + int cmd_dvd_fete_init(int& speed); + int cmd_dvd_fete_block(cdvd_ft *data); + + int cmd_bd_fete_init(int& speed); + int cmd_bd_fete_block(cdvd_ft *data); +}; + +#endif + diff --git a/plugins/nec/Makefile b/plugins/nec/Makefile new file mode 100644 index 0000000..91abb72 --- /dev/null +++ b/plugins/nec/Makefile @@ -0,0 +1,3 @@ +LIBN = qscan_nec + +include ../Makefile.plugin diff --git a/plugins/nec/qscan_cmd.cpp b/plugins/nec/qscan_cmd.cpp new file mode 100644 index 0000000..8b48011 --- /dev/null +++ b/plugins/nec/qscan_cmd.cpp @@ -0,0 +1,149 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2006 Gennady "ShultZ" Kozlov + * + * + * NEC Cx scan commands got from readcd path by Alexander Noe` + * + * + * 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 +#include + +#include +#include +#include + +#include +//#include "media_check_nec.h" + +// ************* Scan init commands ********* +int scan_nec::cmd_cd_errc_init() { + /* initialize scan mode */ + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x01; + if ((dev->err=dev->cmd.transport(NONE, NULL, 0))){ + if(!dev->silent) sperror ("nec_init_errc_scan",dev->err); return 1; + } + /* set scan interval = 75 sectors */ + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x02; + dev->cmd[8] = 75; // interval in sectors + if ((dev->err=dev->cmd.transport(NONE, NULL, 0))){ + if(!dev->silent) sperror ("nec_set_scan_interval",dev->err); return 1; + } + return 0; +} + +int scan_nec::cmd_dvd_errc_init() { + /* initialize scan mode */ + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x01; + if ((dev->err=dev->cmd.transport(NONE, NULL, 0))){ + if(!dev->silent) sperror ("nec_init_errc_scan",dev->err); return 1; + } + /* set scan interval = 16 sectors = 1 ECC */ + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x02; + dev->cmd[8] = 0x01; // interval in ECC blocks + if ((dev->err=dev->cmd.transport(NONE, NULL, 0))){ + if(!dev->silent) sperror ("nec_set_scan_interval",dev->err); return 1; + } + return 0; +} + +int scan_nec::cmd_bd_errc_init() { + /* initialize scan mode */ + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x01; + if ((dev->err=dev->cmd.transport(NONE, NULL, 0))){ + if(!dev->silent) sperror ("nec_init_errc_scan",dev->err); return 1; + } + /* set scan interval = 32 sectors (equivalent to 2 ECC blocks on DVD) */ + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x02; + dev->cmd[8] = 0x02; // interval + if ((dev->err=dev->cmd.transport(NONE, NULL, 0))){ + if(!dev->silent) sperror ("nec_set_scan_interval",dev->err); return 1; + } + return 0; +} + +// ********************** +int scan_nec::cmd_cd_errc_block(cd_errc *data) +{ + long tlba; + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x03; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,8))){ + sperror ("nec_cd_errc_do_one_interval",dev->err); + if (dev->err == 0x30200) return 1; + else return -1; + } + data->bler = ntoh16(dev->rd_buf+4); + data->e11 = 0; + data->e21 = 0; + data->e31 = 0; + data->e12 = 0; + data->e22 = ntoh16(dev->rd_buf+6); + data->e32 = 0; + data->uncr = 0; + lba+=75; + tlba=((int)dev->rd_buf[1] * 4500 + (int)dev->rd_buf[2] * 75 + (int)dev->rd_buf[3]); + if (lbacmd[0] = 0xF3; + dev->cmd[1] = 0x03; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,8))){ + sperror ("nec_dvd_errc_do_one_interval",dev->err); + if (dev->err == 0x30200) return 1; + else return -1; + } + data->pie = ntoh16(dev->rd_buf+4); +// data->pi8 = data->pie; + data->pif = ntoh16(dev->rd_buf+6); + data->poe = 0; +// data->po8 = data->poe; + data->pof = 0; + lba=ntoh32(dev->rd_buf); +// *lba+=0x80; + return 0; +} + +int scan_nec::cmd_bd_errc_block(bd_errc *data) +{ + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x03; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,8))){ + sperror ("nec_bd_errc_do_one_interval",dev->err); + if (dev->err == 0x30200) return 1; + else return -1; + } + data->ldc = ntoh16(dev->rd_buf+4); + data->bis = ntoh16(dev->rd_buf+6); + lba=ntoh32(dev->rd_buf); +// *lba+=0x80; + return 0; +} + +// ************* END SCAN COMMAND ********* +int scan_nec::cmd_scan_end() +{ + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x0F; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,8))){ + sperror ("nec_end_scan",dev->err); return 1; + } + return 0; +} + diff --git a/plugins/nec/qscan_plugin.cpp b/plugins/nec/qscan_plugin.cpp new file mode 100644 index 0000000..ffcc618 --- /dev/null +++ b/plugins/nec/qscan_plugin.cpp @@ -0,0 +1,177 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2008, Gennady "ShultZ" Kozlov + * + * 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 +#include + +static const int SPEEDS_ERRC_CD[] = { +/* + 4*CD_SPEED_MULT, + 8*CD_SPEED_MULT, + 24*CD_SPEED_MULT, + 32*CD_SPEED_MULT, + 40*CD_SPEED_MULT, +*/ + 0 +}; + +static const int SPEEDS_ERRC_DVD[] = { + 2*DVD_SPEED_MULT, + 5*DVD_SPEED_MULT, + 8*DVD_SPEED_MULT, + 12*DVD_SPEED_MULT, + 16*DVD_SPEED_MULT, + 0 +}; + +scan_plugin* plugin_create(drive_info* idev) +{ + return new scan_nec(idev); +} + +void plugin_destroy(scan_plugin* iplugin) +{ + if (iplugin != NULL) delete iplugin; +} + +scan_nec::scan_nec(drive_info* idev) + : scan_plugin(), lba(0) +{ + dev = idev; + if (!dev->silent) printf("scan_nec()\n"); + devlist = (drivedesc*) &drivelist; + test=0; +} + +scan_nec::~scan_nec() +{ + if (!dev->silent) printf("~scan_nec()\n"); +} + +int scan_nec::probe_drive() { + if (!strncmp(dev->ven,"TSSTcorp", 8)) return DEV_FAIL; + if (dev->media.type & DISC_CD) { + if (cmd_cd_errc_init()) return DEV_FAIL; + if (cmd_scan_end()) return DEV_FAIL; + } else if (dev->media.type & DISC_DVD) { + if (cmd_dvd_errc_init()) return DEV_FAIL; + if (cmd_scan_end()) return DEV_FAIL; + } else if (dev->media.type & DISC_BD) { + if (cmd_bd_errc_init()) return DEV_FAIL; + if (cmd_scan_end()) return DEV_FAIL; + } else { + return DEV_FAIL; + } + return DEV_PROBED; +} + +int scan_nec::errc_data() +{ + if (dev->media.type & DISC_CD) { + return (ERRC_DATA_BLER|ERRC_DATA_E22|ERRC_DATA_UNCR); + } else if (dev->media.type & DISC_DVD) { + return (ERRC_DATA_PIE|ERRC_DATA_PIF|ERRC_DATA_UNCR); + } else if (dev->media.type & DISC_BD) { + return (ERRC_DATA_LDC|ERRC_DATA_BIS|ERRC_DATA_UNCR); + } + return 0; +} + +int scan_nec::check_test(unsigned int itest) +{ + switch (itest) { + case CHK_ERRC: + if (dev->media.type & ~DISC_DVDRAM) + return 0; + break; + default: + break; + } + return -1; +} + +int* scan_nec::get_test_speeds(unsigned int itest) +{ + switch (itest) { + case CHK_ERRC: + if (dev->media.type & DISC_CD) + return NULL; +// return (int*)SPEEDS_ERRC_CD; + if (dev->media.type & DISC_DVD) + return NULL; +// return (int*)SPEEDS_ERRC_DVD; + break; + default: + break; + } + return NULL; +} + +int scan_nec::start_test(unsigned int itest, long ilba, int &speed) +{ + int r=-1; + switch (itest) { + case CHK_ERRC_CD: + lba=ilba; + set_read_speed(speed); + r = cmd_cd_errc_init(); + break; + case CHK_ERRC_DVD: + lba=ilba; + set_read_speed(speed); + r = cmd_dvd_errc_init(); + break; + default: + return -1; + } + if (!r) { + test = itest; + } else { + test = 0; + } + return r; +} + +int scan_nec::scan_block(void *data, long *ilba) +{ + int r=-1; + switch (test) { + case CHK_ERRC_CD: + r = cmd_cd_errc_block((cd_errc*)data); + if(ilba) *ilba = lba; + return r; + case CHK_ERRC_DVD: + r = cmd_dvd_errc_block((dvd_errc*)data); + if(ilba) *ilba = lba; + return r; + default: + return -1; + } +} + +int scan_nec::end_test() +{ + cmd_scan_end(); + test=0; + return 0; +} + +/* +__attribute__((constructor)) void init() { + printf("init()\n"); +} + +__attribute__((destructor)) void exit() { + printf("exit()\n"); +} +*/ + diff --git a/plugins/nec/qscan_plugin.h b/plugins/nec/qscan_plugin.h new file mode 100644 index 0000000..238f307 --- /dev/null +++ b/plugins/nec/qscan_plugin.h @@ -0,0 +1,77 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2008, Gennady "ShultZ" Kozlov + * + * 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 __QSCAN_NEC_H +#define __QSCAN_NEC_H + + +//#include +//#include +#include "qpx_scan_plugin_api.h" + +static const drivedesclist drivelist = +{ + { "_NEC ", DEV_NEC, "DVD_RW ND-3520", NEC_3520, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "_NEC ", DEV_NEC, "DVD_RW ND-353", NEC_3530, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "_NEC ", DEV_NEC, "DVD_RW ND-354", NEC_3540, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "_NEC ", DEV_NEC, "DVD_RW ND-355", NEC_4550, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "_NEC ", DEV_NEC, "DVD_RW ND-357", NEC_4570, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "_NEC ", DEV_NEC, "DVD_RW ND-365", NEC_4650, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "_NEC ", DEV_NEC, "DVD_RW ND-455", NEC_4550, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "_NEC ", DEV_NEC, "DVD_RW ND-457", NEC_4570, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "_NEC ", DEV_NEC, "DVD_RW ND-465", NEC_4650, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "Optiarc ", DEV_NEC, "DVD RW AD-717", NEC_4650, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "Optiarc ", DEV_NEC, "DVD RW AD-7260", NEC_4650, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "", 0, "", 0} +}; + +static const char plugin_name[]="NEC"; +static const char plugin_desc[]="Scan plugin for NEC devices"; + +class drive_info; + +class scan_nec : public scan_plugin { +public: +// scan_nec(drive_info* idev=NULL); + scan_nec(drive_info* idev); + virtual ~scan_nec(); +// virtual int check_drive(); + virtual int probe_drive(); + virtual int errc_data(); + virtual int check_test(unsigned int test); + virtual int* get_test_speeds(unsigned int test); + virtual int start_test(unsigned int test, long slba, int &speed); + virtual int scan_block(void* data,long* ilba); + virtual int end_test(); + + virtual const char* name() { return plugin_name; }; + virtual const char* desc() { return plugin_desc; }; +private: + long lba; + +// CD ERRC methods + int cmd_cd_errc_init(); + int cmd_cd_errc_block(cd_errc *data); + +// DVD ERRC methods + int cmd_dvd_errc_init(); + int cmd_dvd_errc_block(dvd_errc *data); + +// BD ERRC methods + int cmd_bd_errc_init(); + int cmd_bd_errc_block(bd_errc *data); + + int cmd_scan_end(); +}; + +#endif // __QSCAN_NEC_H + diff --git a/plugins/pioneer/Makefile b/plugins/pioneer/Makefile new file mode 100644 index 0000000..1d70261 --- /dev/null +++ b/plugins/pioneer/Makefile @@ -0,0 +1,3 @@ +LIBN = qscan_pioneer + +include ../Makefile.plugin diff --git a/plugins/pioneer/qscan_cmd.cpp b/plugins/pioneer/qscan_cmd.cpp new file mode 100644 index 0000000..fe39e82 --- /dev/null +++ b/plugins/pioneer/qscan_cmd.cpp @@ -0,0 +1,247 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2006 Gennady "ShultZ" Kozlov + * + * 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 +#include + +#include + +#include +//#include +#include + +//#define _PIO_ERRC_DEBUG + +int scan_pioneer::cmd_cd_errc_read(unsigned char sects) +{ +/* +CDB: +00000000 3B 02 E1 00 00 00 00 00 20 00 ;.á..... . + +Data Sent: +00000000 FF 01 00 00 00 60 00 00 00 00 4B 00 00 00 4B 00 ÿ............... +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +*/ + +// lba += 0x006000; + + int i; + for (i=0; i<32; i++) dev->rd_buf[i]=0; + + dev->rd_buf[0] = 0xFF; + dev->rd_buf[1] = 0x01; + + dev->rd_buf[4] = ((lba+0x006000) >> 16) & 0xFF; + dev->rd_buf[5] = ((lba+0x006000) >> 8) & 0xFF; + dev->rd_buf[6] = (lba) & 0xFF; +// dev->rd_buf[6] = 0x01; + + dev->rd_buf[8] = (sects >> 16) & 0xFF; + dev->rd_buf[9] = (sects >> 8) & 0xFF; + dev->rd_buf[10] = sects & 0xFF; + dev->rd_buf[12] = (sects >> 16) & 0xFF; + dev->rd_buf[13] = (sects >> 8) & 0xFF; + dev->rd_buf[14] = sects & 0xFF; +/* + printf("Pioneer scan Cx CMD data:"); + for (i=0; i<16; i++) { + if (!(i%0x20)) printf("\n"); + printf(" %02X",dev->rd_buf[i] & 0xFF); + } + printf("\n"); +*/ + dev->cmd[0] = 0x3B; + dev->cmd[1] = 0x02; + dev->cmd[2] = 0xE1; + dev->cmd[8] = 0x20; + if ((dev->err=dev->cmd.transport(WRITE,dev->rd_buf,32))) { + if(!dev->silent) sperror ("pioneer_send_scan_cx",dev->err); return dev->err; + } + return 0; +} + + +int scan_pioneer::cmd_dvd_errc_read(unsigned char nECC) +{ +/* +CDB: +00000000 3B 02 E1 00 00 00 00 00 20 00 ;.á..... . + +Data Sent: +00000000 FF 01 00 00 03 00 01 00 00 00 80 00 00 00 08 00 ÿ............... +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +*/ + int i; + int sects = nECC*16; +// lba += 0x030000; + + for (i=0; i<32; i++) dev->rd_buf[i]=0; + dev->rd_buf[0] = 0xFF; + dev->rd_buf[1] = 0x01; + dev->rd_buf[4] = ((lba >> 16) + 3) & 0xFF; + dev->rd_buf[5] = (lba >> 8) & 0xFF; + dev->rd_buf[6] = (lba) & 0xFF; +// dev->rd_buf[6] = 0x01; + +// dev->rd_buf[9] = 0x01; +// dev->rd_buf[9] = (nECC >> 4 ) & 0xFF; +// dev->rd_buf[10] = (nECC << 4 ) & 0xFF; +// dev->rd_buf[14] = nECC; +// dev->rd_buf[14] = 0x08; + + +// dev->rd_buf[9] = (sects >> 8) & 0xFF; + dev->rd_buf[10] = (sects) & 0xFF; +// dev->rd_buf[13] = (sects >> 12) & 0xFF; +// dev->rd_buf[14] = nECC; + dev->rd_buf[14] = 1; +/* + printf("Pioneer scan Pi CMD data:"); + for (i=0; i<16; i++) { + if (!(i%0x20)) printf("\n"); + printf(" %02X",dev->rd_buf[i] & 0xFF); + } + printf("\n"); +*/ + dev->cmd[0] = 0x3B; + dev->cmd[1] = 0x02; + dev->cmd[2] = 0xE1; + dev->cmd[8] = 0x20; + if ((dev->err=dev->cmd.transport(WRITE,dev->rd_buf,32))) { + if(!dev->silent) sperror ("pioneer_send_scan_pi",dev->err); return dev->err; + } + return 0; +} + + +int scan_pioneer::cmd_cd_errc_getdata(cd_errc *data) +{ + int i; + for (i=0; i<32; i++) dev->rd_buf[i]=0; + dev->cmd[0] = 0x3C; + dev->cmd[1] = 0x02; + dev->cmd[2] = 0xE1; + dev->cmd[8] = 0x20; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,32))) { + sperror ("pioneer_read_error_info",dev->err); return dev->err; + } +#ifdef _PIO_ERRC_DEBUG + printf("Read error info RSP data:"); + for (i=0; i<32; i++) { + if (!(i%0x20)) printf("\n"); + printf(" %02X",dev->rd_buf[i] & 0xFF); + } + printf("\n"); +#endif + if ((ntoh16(dev->rd_buf+13) > 300) || (ntoh16(dev->rd_buf+5) > 300)) { + data->bler = 0; + data->e22 = 0; + } else { + //data->bler = ntoh16(dev->rd_buf+13) - ntoh16(dev->rd_buf+5); + data->bler = ntoh16(dev->rd_buf+13); + data->e22 = ntoh16(dev->rd_buf+5); + } + data->e11 = 0; + data->e21 = 0; + data->e31 = 0; + data->e12 = 0; + data->e32 = 0; + data->uncr = 0; + + return 0; +} + +int scan_pioneer::cmd_dvd_errc_getdata(dvd_errc *data) +{ + int i; + for (i=0; i<32; i++) dev->rd_buf[i]=0; + dev->cmd[0] = 0x3C; + dev->cmd[1] = 0x02; + dev->cmd[2] = 0xE1; + dev->cmd[8] = 0x20; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,32))) { + sperror ("pioneer_read_error_info",dev->err); return dev->err; + } +#ifdef _PIO_ERRC_DEBUG + printf("Read error info RSP data:"); + for (i=0; i<32; i++) { + if (!(i%0x20)) printf("\n"); + printf(" %02X",dev->rd_buf[i] & 0xFF); + } + printf("\n"); +#endif + + if (dev->dev_ID < PIO_DVR_111) { + data->pie = max( 0, (ntoh16(dev->rd_buf+13) - ntoh16(dev->rd_buf+5)) / 10); + data->pif = ntoh16(dev->rd_buf+13) / 200; + } else { + data->pie = max( 0, (ntoh16(dev->rd_buf+13) - ntoh16(dev->rd_buf+5))); + data->pif = ntoh16(dev->rd_buf+13) / 20; + } +// data->pi8=data->pie; + data->poe=0; +// data->po8=0; + data->pof=0; + return 0; +} + +int scan_pioneer::cmd_cd_errc_block(cd_errc *data) +{ + int r; + unsigned char interval = 75; + r = cmd_cd_errc_read(interval); + if (!r) r = cmd_cd_errc_getdata(data); + lba += interval; + return r; +} + +int scan_pioneer::cmd_dvd_errc_block(dvd_errc *data) +{ + int r; + const char interval = 1; + r = cmd_dvd_errc_read(interval); + if (!r) r = cmd_dvd_errc_getdata(data); +// *pie = max( 0, (ntoh16(dev->rd_buf+13) - 1.5 * ntoh16(dev->rd_buf+5)) / 40); + lba+= 16*interval; + return r; +} + +int scan_pioneer::cmd_cd_errc_init() +{ + int r; + cd_errc data; +// spinup(dev, 2); + seek(dev, 0); + r = cmd_cd_errc_read(75); + if (!r) r = cmd_cd_errc_getdata(&data); + return r; +} + + +int scan_pioneer::cmd_dvd_errc_init() +{ + int r; + dvd_errc data; +// spinup(dev, 2); + seek(dev, 0); + r = cmd_dvd_errc_read(1); + if (!r) r = cmd_dvd_errc_getdata(&data); + return r; +} + +/* +int scan_pioneer dummy(dev_info* dev) +{ + dev->err = 0; + return 0; +} +*/ + diff --git a/plugins/pioneer/qscan_plugin.cpp b/plugins/pioneer/qscan_plugin.cpp new file mode 100644 index 0000000..fbb05ec --- /dev/null +++ b/plugins/pioneer/qscan_plugin.cpp @@ -0,0 +1,163 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2008, Gennady "ShultZ" Kozlov + * + * 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 +#include + +static const int SPEEDS_ERRC_CD[] = { +/* + 4*CD_SPEED_MULT, + 8*CD_SPEED_MULT, + 24*CD_SPEED_MULT, + 32*CD_SPEED_MULT, + 40*CD_SPEED_MULT, +*/ + 0 +}; + +static const int SPEEDS_ERRC_DVD[] = { + 3*DVD_SPEED_MULT, + 0 +}; + +scan_plugin* plugin_create(drive_info* idev) +{ + return new scan_pioneer(idev); +} + +void plugin_destroy(scan_plugin* iplugin) +{ + if (iplugin != NULL) delete iplugin; +} + +scan_pioneer::scan_pioneer(drive_info* idev) + : scan_plugin(), lba(0) +{ + dev = idev; + if (!dev->silent) printf("scan_pioneer()\n"); + devlist = (drivedesc*) &drivelist; + test=0; +} + +scan_pioneer::~scan_pioneer() +{ + if (!dev->silent) printf("~scan_pioneer()\n"); +} + +int scan_pioneer::probe_drive() { + if (dev->media.type & DISC_CD) { + if (cmd_cd_errc_init()) return DEV_FAIL; + } else if (dev->media.type & DISC_DVD) { + if (cmd_dvd_errc_init()) return DEV_FAIL; + } else { + return DEV_FAIL; + } + return DEV_PROBED; +} + +int scan_pioneer::errc_data() +{ + if (dev->media.type & DISC_CD) { + return (ERRC_DATA_BLER|ERRC_DATA_E22|ERRC_DATA_UNCR); + } else if (dev->media.type & DISC_DVD) { + return (ERRC_DATA_PIE|ERRC_DATA_PIF|ERRC_DATA_UNCR); + } + return 0; +} + +int scan_pioneer::check_test(unsigned int itest) +{ + switch (itest) { + case CHK_ERRC: + if (dev->media.type & ~DISC_DVDRAM) + return 0; + break; + default: + break; + } + return -1; +} + +int* scan_pioneer::get_test_speeds(unsigned int itest) +{ + switch (itest) { + case CHK_ERRC: + if (dev->media.type & DISC_CD) + return (int*)SPEEDS_ERRC_CD; + if (dev->media.type & DISC_DVD) + return (int*)SPEEDS_ERRC_DVD; + break; + default: + break; + } + return NULL; +} + +int scan_pioneer::start_test(unsigned int itest, long ilba, int &speed) +{ + int r=-1; + switch (itest) { + case CHK_ERRC_CD: + lba=ilba; + set_read_speed(speed); + r = cmd_cd_errc_init(); + break; + case CHK_ERRC_DVD: + lba=ilba; + set_read_speed(speed); + r = cmd_dvd_errc_init(); + break; + default: + return -1; + } + if (!r) { + test = itest; + } else { + test = 0; + } + return r; +} + +int scan_pioneer::scan_block(void *data, long *ilba) +{ + int r=-1; + switch (test) { + case CHK_ERRC_CD: + r = cmd_cd_errc_block((cd_errc*)data); + if(ilba) *ilba = lba; + return r; + case CHK_ERRC_DVD: + r = cmd_dvd_errc_block((dvd_errc*)data); + if(ilba) *ilba = lba; + return r; + default: + return -1; + } +} + +int scan_pioneer::end_test() +{ +// cmd_scan_end(); + test=0; + return 0; +} + +/* +__attribute__((constructor)) void init() { + printf("init()\n"); +} + +__attribute__((destructor)) void exit() { + printf("exit()\n"); +} +*/ + diff --git a/plugins/pioneer/qscan_plugin.h b/plugins/pioneer/qscan_plugin.h new file mode 100644 index 0000000..3dbcd0b --- /dev/null +++ b/plugins/pioneer/qscan_plugin.h @@ -0,0 +1,92 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2008, Gennady "ShultZ" Kozlov + * + * 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 __QSCAN_PIONEER_H +#define __QSCAN_PIONEER_H + +#include "qpx_scan_plugin_api.h" + +static const drivedesclist drivelist = +//static drivedesclist drivelist = +{ +/* + { "PIONEER ", DEV_PIONEER, "DVD-RW DVR-102", PIO_OLD, 0 }, + { "PIONEER ", DEV_PIONEER, "DVD-RW DVR-103", PIO_OLD, 0 }, + { "PIONEER ", DEV_PIONEER, "DVD-RW DVR-104", PIO_OLD, 0 }, + { "PIONEER ", DEV_PIONEER, "DVD-RW DVR-105", PIO_OLD, 0 }, +*/ + { "PIONEER ", DEV_PIONEER, "DVD-RW DVR-106", PIO_DVR_106, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "PIONEER ", DEV_PIONEER, "DVD-RW DVR-107", PIO_DVR_107, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "PIONEER ", DEV_PIONEER, "DVD-RW DVR-108", PIO_DVR_108, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "PIONEER ", DEV_PIONEER, "DVD-RW DVR-109", PIO_DVR_109, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "PIONEER ", DEV_PIONEER, "DVD-RW DVR-110", PIO_DVR_110, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "PIONEER ", DEV_PIONEER, "DVD-RW DVR-111", PIO_DVR_111, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "PIONEER ", DEV_PIONEER, "DVD-RW DVR-112", PIO_DVR_112, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "PIONEER ", DEV_PIONEER, "", PIO_DVR_112, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "ASUS ", DEV_PIONEER, "DRW-0402P", PIO_DVR_106, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "ASUS ", DEV_PIONEER, "DRW-0804P", PIO_DVR_107, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "ASUS ", DEV_PIONEER, "DRW-1604P", PIO_DVR_108, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "ASUS ", DEV_PIONEER, "DRW-1608P", PIO_DVR_109, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "ASUS ", DEV_PIONEER, "DRW-1608P2", PIO_DVR_110, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "ASUS ", DEV_PIONEER, "DRW-1608P3", PIO_DVR_111, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "TEAC ", DEV_PIONEER, "DV-W50D", PIO_DVR_106, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "TEAC ", DEV_PIONEER, "DV-W58D", PIO_DVR_107, CHK_ERRC_CD | CHK_ERRC_DVD }, + { "TEAC ", DEV_PIONEER, "DV-W516D", PIO_DVR_108, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "PLEXTOR ", DEV_PIONEER, "DVR PX-810", PIO_DVR_112, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "", 0, "", 0} +}; + +static const char plugin_name[]="PIONEER"; +static const char plugin_desc[]="Scan plugin for PIONEER and PIONEER-based devices"; + +class drive_info; + +class scan_pioneer : public scan_plugin { +public: +// scan_pioneer(drive_info* idev=NULL); + scan_pioneer(drive_info* idev); + virtual ~scan_pioneer(); +// virtual int check_drive(); + virtual int probe_drive(); + virtual int errc_data(); + virtual int check_test(unsigned int test); + virtual int* get_test_speeds(unsigned int test); + virtual int start_test(unsigned int test, long slba, int &speed); + virtual int scan_block(void* data,long* ilba); + virtual int end_test(); + + virtual const char* name() { return plugin_name; }; + virtual const char* desc() { return plugin_desc; }; +private: + long lba; + +// CD ERRC methods + int cmd_cd_errc_init(); + int cmd_cd_errc_block(cd_errc *data); + int cmd_cd_errc_read(unsigned char sects); + int cmd_cd_errc_getdata(cd_errc *data); + +// DVD ERRC methods + int cmd_dvd_errc_init(); + int cmd_dvd_errc_block(dvd_errc *data); + int cmd_dvd_errc_read(unsigned char nECC); + int cmd_dvd_errc_getdata(dvd_errc *data); +// end scan +// int cmd_scan_end(); +}; + +#endif + diff --git a/plugins/plextor/Makefile b/plugins/plextor/Makefile new file mode 100644 index 0000000..ee73f5f --- /dev/null +++ b/plugins/plextor/Makefile @@ -0,0 +1,3 @@ +LIBN = qscan_plextor + +include ../Makefile.plugin diff --git a/plugins/plextor/qscan_cmd.cpp b/plugins/plextor/qscan_cmd.cpp new file mode 100644 index 0000000..615495e --- /dev/null +++ b/plugins/plextor/qscan_cmd.cpp @@ -0,0 +1,736 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2009 Gennady "ShultZ" Kozlov + * + * + * Some Plextor commands got from PxScan and CDVDlib (C) Alexander Noe` + * + * + * 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 +#include +#include +#include +#include + +#include +#include + +#include + +#define FETE_START_LBA + +#define DEBUG 1 +//#define _debug_cx +//#define _debug_pi +//#define _debug_jb +//#define _debug_fete + +//*********************************// +// +// commands to start tests +// +//*********************************// + +const char PLEX_QCHECK_START = 0x15; +const char PLEX_QCHECK_READOUT = 0x16; +const char PLEX_QCHECK_END = 0x17; + +int scan_plextor::cmd_cd_errc_init() +{ + dev->cmd[0] = PLEXTOR_QCHECK; //0xEA; + dev->cmd[1] = PLEX_QCHECK_START; //0x15; + dev->cmd[2] = 0x00; + dev->cmd[3] = 0x01; + +// dev->cmd[8] = 0x0F; + dev->cmd[9] = 0x02; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport() )) + { if (!dev->silent) sperror ("PLEXTOR_START_CX", dev->err); return dev->err;} +#ifdef _debug_cx + printf("00 18 01 01 00 4B | LBA | BLER E31 E21 E11 E32 ??? E22 E12\n"); +#endif + printf("scan init OK!\n"); + return 0; +} + +int scan_plextor::cmd_dvd_errc_init() +{ + dev->cmd[0] = PLEXTOR_QCHECK; //0xEA; + dev->cmd[1] = PLEX_QCHECK_START; //0x15; + dev->cmd[2] = 0x00; +// dev->cmd[3] = 0x09; + dev->cmd[3] = 0x01; + + dev->cmd[8] = 0x01; +// dev->cmd[9] = 0x13; + dev->cmd[9] = 0x12; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport() )) + { if(!dev->silent) sperror ("PLEXTOR_START_PIPO",dev->err); return dev->err;} + printf("scan init OK!\n"); + return 0; +} + +#if 0 +int plextor_start_pie(drive_info* dev) { + dev->cmd[0] = PLEXTOR_QCHECK; //0xEA; + dev->cmd[1] = PLEX_QCHECK_START; //0x15; + dev->cmd[2] = 0x00; + dev->cmd[3] = 0x00; + dev->cmd[8] = 0x08; // scan interval (ECC blocks) + dev->cmd[9] = 0x10; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport() )) + {sperror ("PLEXTOR_START_PISUM8",dev->err);return dev->err;} + return 0; +} + +int plextor_start_pie_poe(drive_info* dev) { + dev->cmd[0] = PLEXTOR_QCHECK; //0xEA; + dev->cmd[1] = PLEX_QCHECK_START; //0x15; + dev->cmd[2] = 0x00; + dev->cmd[3] = 0x00; + dev->cmd[8] = 0x08; // scan interval (ECC blocks) + dev->cmd[9] = 0x11; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport() )) + {sperror ("PLEXTOR_START_PISUM8_POE",dev->err);return dev->err;} + return 0; +} + +int plextor_start_pif(drive_info* dev) { + dev->cmd[0] = PLEXTOR_QCHECK; //0xEA; + dev->cmd[1] = PLEX_QCHECK_START; //0x15; + dev->cmd[2] = 0x00; + dev->cmd[3] = 0x00; + dev->cmd[8] = 0x01; // scan interval (ECC blocks) + dev->cmd[9] = 0x12; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport() )) + {sperror ("PLEXTOR_START_PIF",dev->err); return dev->err;} + return 0; +} +#endif + +int scan_plextor::cmd_cd_jb_init() +{ + dev->cmd[0] = PLEXTOR_QCHECK; //0xEA; + dev->cmd[1] = PLEX_QCHECK_START; //0x15; + dev->cmd[2] = 0x10; + dev->cmd[3] = 0x01; // CD + +// dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport() )) + {sperror ("PLEXTOR_START_JB_CD",dev->err);return dev->err;} + printf("scan init OK!\n"); + return 0; +} + +int scan_plextor::cmd_dvd_jb_init() +{ + dev->cmd[0] = PLEXTOR_QCHECK; //0xEA; + dev->cmd[1] = PLEX_QCHECK_START; //0x15; + dev->cmd[2] = 0x10; + dev->cmd[3] = 0x00; // DVD + dev->cmd[8] = 0x10; // scan interval (ECC blocks) + +// dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport() )) + {sperror ("PLEXTOR_START_JB_DVD",dev->err);return dev->err;} + printf("scan init OK!\n"); + return 0; +} + +int scan_plextor::cmd_fete_init() +{ + fete_idx=-1; + dev->cmd[0] = PLEXTOR_SCAN_TA_FETE; // 0xF3; + dev->cmd[1] = 0x1F; + dev->cmd[2] = 0x03; + dev->cmd[3] = 0x01; +#ifdef _debug_fete +#ifdef FETE_START_LBA + printf("scan_plextor::cmd_fete_init: lba %ld - %d\n", lba, dev->media.capacity_total-1); +#else + printf("scan_plextor::cmd_fete_init: lba %d - %d\n", 0, dev->media.capacity_total-1); +#endif +#endif + if (dev->media.type & DISC_CD) { + fete_rsize = 4500; // one FE/TE info block per 60 seconds + + int sect; + msf sect_msf; + lba2msf(lba,§_msf); +// start address +#ifdef FETE_START_LBA + dev->cmd[4] = sect_msf.m; + dev->cmd[5] = sect_msf.s; + dev->cmd[6] = sect_msf.f; +#else + dev->cmd[4] = 0x00; + dev->cmd[5] = 0x00; + dev->cmd[6] = 0x00; +#endif +// end address + sect = dev->media.capacity_total-1; + lba2msf(sect,§_msf); + dev->cmd[7] = sect_msf.m; + dev->cmd[8] = sect_msf.s; + dev->cmd[9] = sect_msf.f; + } else if (dev->media.type & DISC_DVD) { + fete_rsize = 25600; // one FE/TE info block per 1600 (?) ECC blocks + +// start address +#ifdef FETE_START_LBA + dev->cmd[4] = (lba >> 16) & 0xFF; + dev->cmd[5] = (lba >> 8) & 0xFF; + dev->cmd[6] = lba & 0xFF; +#else + dev->cmd[4] = 0x00; + dev->cmd[5] = 0x00; + dev->cmd[6] = 0x00; +#endif +// end address + dev->cmd[7] = ((dev->media.capacity_total-1) >> 16) & 0xFF; + dev->cmd[8] = ((dev->media.capacity_total-1) >> 8) & 0xFF; + dev->cmd[9] = (dev->media.capacity_total-1) & 0xFF; + } else { + return -1; + } + if ((dev->err=dev->cmd.transport() )) + {sperror ("PLEXTOR_START_FETE",dev->err);return dev->err;} + return 0; +} + +//*********************************// +// +// end scan commands +// +//*********************************// + +int scan_plextor::cmd_scan_end() +{ + dev->cmd[0] = PLEXTOR_QCHECK; //0xEA; + dev->cmd[1] = PLEX_QCHECK_END; //0x17; + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport() )) + {sperror ("PLEXTOR_END_SCAN",dev->err); return dev->err;} + return 0; +} + +int scan_plextor::cmd_fete_end() +{ + dev->cmd[0] = PLEXTOR_SCAN_TA_FETE; // 0xF3; + dev->cmd[1] = 0x1F; + dev->cmd[2] = 0x04; + dev->cmd[9] = 0x00; + if ((dev->err=dev->cmd.transport() )) + {sperror ("PLEXTOR_END_FETE",dev->err); return dev->err;} + return 0; +} + +//*********************************// +// +// test data readout commands +// +//*********************************// + +int scan_plextor::cmd_cd_errc_getdata(cd_errc *data) +{ + dev->cmd[0] = PLEXTOR_QCHECK; //0xEA; + dev->cmd[1] = PLEX_QCHECK_READOUT; // 0x16; + dev->cmd[2] = 0x01; + dev->cmd[10]= 0x1A; + + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,0x1A))) + {sperror ("PLEXTOR_READ_CD_ERROR_INFO", dev->err);return dev->err;} + + data->bler = ntoh16(dev->rd_buf+10); + data->e31 = ntoh16(dev->rd_buf+12); + data->e21 = ntoh16(dev->rd_buf+14); + data->e11 = ntoh16(dev->rd_buf+16); + + data->uncr = ntoh16(dev->rd_buf+18); // check where drive returns E32 + data->e32 = ntoh16(dev->rd_buf+20); // and where is UNCR + data->e22 = ntoh16(dev->rd_buf+22); + data->e12 = ntoh16(dev->rd_buf+24); + +#ifdef _debug_cx + int i; + for (i=0x00; i<0x06; i++) printf("%02X ", dev->rd_buf[i] & 0xFF); printf("| "); + for (i=0x06; i<0x0A; i++) printf("%02X ", dev->rd_buf[i] & 0xFF); printf("| "); + for (i=0x0A; i<0x1A; i+=2) { if (ntoh16(dev->rd_buf+i)) printf("%5d ", ntoh16(dev->rd_buf+i)); else printf("_____ "); } + printf("|\n"); +#endif + return 0; +} + +int scan_plextor::cmd_dvd_errc_getdata(dvd_errc *data) +{ + dev->cmd[0] = PLEXTOR_QCHECK; //0xEA; + dev->cmd[1] = PLEX_QCHECK_READOUT; // 0x16; + dev->cmd[2] = 0x00; + dev->cmd[10]= 0x34; + + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,0x34))) + {sperror ("PLEXTOR_READ_DVD_ERROR_INFO",dev->err); return dev->err;} +/* +test 0x12: + 10 UNCR + 14 PIE + 18 POE + 1C + 20 + 24 PIF + 28 POF + 2C + 30 +*/ + + data->pie = ntoh32(dev->rd_buf+0x14); +// data->pi8 = data->pie; + data->pif = ntoh32(dev->rd_buf+0x24); + + data->poe = ntoh32(dev->rd_buf+0x18)>>4; +// data->po8 = data->poe; + data->pof = ntoh32(dev->rd_buf+0x28); + +/* + data->pie = ntoh32(dev->rd_buf+0x14); + data->pi8 = ntoh32(dev->rd_buf+0x18); + data->pif = ntoh32(dev->rd_buf+0x1C); + + data->poe = ntoh32(dev->rd_buf+0x20); + data->po8 = ntoh32(dev->rd_buf+0x24); + data->pof = ntoh32(dev->rd_buf+0x28); + data->uncr = ntoh32(dev->rd_buf+0x10); +*/ + return 0; +} + +#if 0 +int plextor_read_pi_info(drive_info* dev) { + dev->cmd[0] = PLEXTOR_QCHECK; //0xEA; + dev->cmd[1] = PLEX_QCHECK_READOUT; // 0x16; + dev->cmd[2] = 0x00; + dev->cmd[10]= 0x34; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,0x34))) + {sperror ("PLEXTOR_READ_PI",dev->err); return dev->err;} +/* + printf("READ PI:"); + for (int i=0; i<0x34; i++) { + if (!(i%0x10)) printf("\n"); + printf(" %02X",dev->rd_buf[i] & 0xFF); + } + printf("\n"); + +*/ +#ifdef _debug_pi + int i; +/* +// printf("\n| "); + for (i=0x00; i<0x34; i++) { + if (!(i%0x20))printf("\n| "); + printf("%02X ", dev->rd_buf[i] & 0xFF); + } +*/ + for (i=0x00; i<0x06; i++) printf("%02X ", dev->rd_buf[i] & 0xFF); printf("|"); + printf(" %6X |", ntoh32(dev->rd_buf+0x06) - 0x030000); + printf(" %4X |", ntoh16(dev->rd_buf+0x0A)); + printf(" %4X", ntoh16(dev->rd_buf+0x0C)); + + for (i=0x20; i<0x34; i+=4) { + if (!(i%0x10))printf(" |"); + if (ntoh32(dev->rd_buf+i)) + printf(" %8d", ntoh32(dev->rd_buf+i)); + else + printf(" ________"); + } +// printf("\n"); +#endif + return 0; +} +#endif + +int scan_plextor::cmd_jb_getdata(cdvd_jb *data) +{ + dev->cmd[0] = PLEXTOR_QCHECK; //0xEA; + dev->cmd[1] = PLEX_QCHECK_READOUT; // 0x16; + dev->cmd[2] = 0x10; + dev->cmd[10]= 0x10; + + dev->cmd[11]= 0x00; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,0x10))) + {sperror ("PLEXTOR_READ_JB",dev->err); return dev->err;} +#ifdef _debug_jb + int i; + printf("\n| J/B data: | "); + for (i=0x00; i<0x10; i++) printf("%02X ", dev->rd_buf[i] & 0xFF); + printf("|\n"); +#endif + +// if (dev->dev_ID > PLEXTOR_716) { + data->asymm = ntoh16(dev->rd_buf+10); + data->jitter = ntoh16(dev->rd_buf+12); +// } else { +// data->asymm = ntoh16(dev->rd_buf+10); +// data->jitter = ntoh16(dev->rd_buf+12); +// } + return 0; +} + +int scan_plextor::cmd_cd_errc_block(cd_errc *data) +{ + int rsize=15; + data->uncr=0; +// if (!lba) return -1; + for (int i=0; (i<5) && lbamedia.capacity; i++) { + if (lba + 15 > dev->media.capacity) + rsize = dev->media.capacity - lba; +// else +// rsize = 15; + + if (read_cd(dev, dev->rd_buf, lba, rsize, 0xFA)) data->uncr++; + lba+=rsize; + } + cmd_cd_errc_getdata(data); +// *lba = + return 0; +} + +int scan_plextor::cmd_dvd_errc_block(dvd_errc *data) +{ + if (read_one_ecc_block(dev, dev->rd_buf, lba)) data->uncr++; + read_one_ecc_block(dev, dev->rd_buf, lba); + lba+= 0x10; + cmd_dvd_errc_getdata(data); + return 0; +} + +#if 0 +int plextor_pif_do_one_ecc_block(drive_info* dev, int* lba, int* pif) { + read_one_ecc_block(dev, *lba); + *lba+= 0x10; + plextor_read_pi_info(dev); +// *lba = ntoh32(dev->rd_buf+0x06) - 0x00030000; + *pif = ntoh32(dev->rd_buf+0x24); +#ifdef _debug_pi + printf(" * %4d\n", *pif); +#endif + return 0; +} + +int plextor_pisum8_do_eight_ecc_blocks(drive_info* dev, int* lba, int* pie, int* pof) { + for (int i=0;i<8;i++) { + if ((dev->err = read_one_ecc_block(dev, *lba)));// i = 8; + *lba+= 0x10; + } + plextor_read_pi_info(dev); +// *lba = ntoh32(dev->rd_buf+0x06) - 0x00030000; + *pie = ntoh32(dev->rd_buf+0x24); + *pof = ntoh32(dev->rd_buf+0x10); +// *poe = ntoh32(dev->rd_buf+0x28); +// *pif = 0; +#ifdef _debug_pi + printf(" * %4d\n", *pie); +#endif + return 0; +} + +int plextor_burst_do_eight_ecc_blocks(drive_info* dev, int* lba, int* pie, int* poe, int* pof) { + for (int i=0;i<8;i++) { + if ((dev->err = read_one_ecc_block(dev, *lba)));// i = 8; + *lba+= 0x10; + } + plextor_read_pi_info(dev); +// *lba = ntoh32(dev->rd_buf+0x06) - 0x00030000; + *pie = ntoh32(dev->rd_buf+0x24); +// + if (ntoh16(dev->rd_buf+0x0C) > ntoh16(dev->rd_buf+0x0A)) + *poe = ntoh32(dev->rd_buf+0x28) >> 1; + else + *poe = 0; + *pof = ntoh32(dev->rd_buf+0x10); + +#ifdef _debug_pi + printf(" * %4d * %4d\n", *pie, *poe); +#endif + return 0; +} +#endif + +int scan_plextor::cmd_dvd_jb_block(cdvd_jb *data) +{ + for (int i=0;i<16;i++) { + int j = read_one_ecc_block(dev, dev->rd_buf, lba); + if (j == COMMAND_FAILED) i=16; + lba+= 0x10; + } + + cmd_jb_getdata(data); + if (dev->dev_ID > PLEXTOR_716) { + data->jitter = 3200 - 2*data->jitter; + } else { + data->jitter = 3200 - (int)(2.4*data->jitter); + } + return 0; +// return (!(dev->rd_buf[2])); +} + +int scan_plextor::cmd_cd_jb_block(cdvd_jb *data) +{ + int rsize=15; + for (int i=0;i<5;i++) { + if (lba + 15 > dev->media.capacity) + rsize = dev->media.capacity - lba; + + int j = read_cd(dev, dev->rd_buf, lba, rsize, 0xFA); + lba+= rsize; + if (j == COMMAND_FAILED) i++; + } + + cmd_jb_getdata(data); + if (dev->dev_ID > PLEXTOR_716) { + data->jitter = 4800 - 2*(data->jitter); + } else { + data->jitter = 3600 - (int)(2.4*(data->jitter)); + } + return 0; +// return (!(dev->rd_buf[2])); +} + +int scan_plextor::cmd_fete_getdata() +{ + dev->cmd[0] = PLEXTOR_FETE_READOUT; // 0xF5; + dev->cmd[3] = 0x0C; + dev->cmd[9] = 0xCE; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,0xCE))) + {sperror ("PLEXTOR_FETE_READOUT",dev->err); return dev->err;} +#ifdef _debug_fete + for (int i=0; i<0xCE; i++) { + if (!(i % 0x20)) printf("\n"); + printf("%02X ",dev->rd_buf[i] & 0xFF); + } + printf("\n"); +#endif + return 0; +} + +int scan_plextor::cmd_fete_block(cdvd_ft* data) +{ + int rdy = test_unit_ready(dev); + int offs=((++fete_idx) << 1) + 8; + if (rdy != 0x20408) { + printf("test unit ready = %05X, return\n", rdy); + return -1; + } +// if (cmd_fete_getdata()) return -1; +// data->te=dev->rd_buf[offs]; +// data->fe=dev->rd_buf[offs+1]; + data->te=0; + data->fe=0; + if (fete_idx>99) return 1; + lba = (fete_idx+1) * fete_rsize; + if (lba > (dev->media.capacity_total-1)) lba = dev->media.capacity_total-1; + + while ((data->fe==0 || data->te==0) && (fete_idx<=99) && (rdy == 0x20408)) { + if (cmd_fete_getdata()) return -1; + data->te=dev->rd_buf[offs]; + data->fe=dev->rd_buf[offs+1]; + if (data->fe==0 || data->te==0) { + rdy = test_unit_ready(dev); +#ifdef _debug_fete + printf("test unit ready = %05X\n", rdy); +#endif + msleep(10); + } + } + return 0; +} + +int scan_plextor::build_TA_histogram_px716(unsigned char* response_data, int* dest_pit, int* dest_land, int len) { + int* dest[] = { dest_land, dest_pit }; + int count = ntoh16(response_data+2); +// printf("PX-716 Histogram... %d\n",count); + int idx=28; + int v, pit; + for (int i=0;ipit, data->land }; + + for (int k=0;k<2;k++) { + j1=0; j2=0; local_max = 0; + for (i=40;i<330;i++) { + if (src[k][i-1] <= src[k][i] && src[k][i+1] <= src[k][i] && src[k][i] > 20 && src[k][i] > local_max) { + peaks[k][j1] = i; + local_max = src[k][i]; + next_peak = 1; + } else if (peak_found) + if (/*src[k][i-3] >= src[k][i-1] && src[k][i-2] > src[k][i-1] && */ + src[k][i-1] > src[k][i] && src[k][i+1] >= src[k][i]) { + mins[k][j2] = i; + if (j2<13) j2++; + peak_found = 0; + } + + if (local_max > 2*src[k][i]) { + local_max = 2*src[k][i]; + if (next_peak) { + next_peak = 0; + if (j1<13) { + j1++; + peak_found=1; + // printf("%4d",i); + } + } + } + } + + int min_count = j2; + for (i=0;imedia.layers); + int i, r, m,j; + float sum; + unsigned char scan_cmd[6][2] = { + { 0x04, 0x00 }, { 0x10, 0x00 }, { 0x20, 0x00 }, + { 0xFA, 0x28 }, { 0xEA, 0x28 }, { 0xDE, 0x28 }}; + const char* scan_txt[] = { + "Running TA on L0 inner zone ", "Running TA on L0 middle zone", "Running TA on L0 outer zone", + "Running TA on L1 inner zone ", "Running TA on L1 middle zone", "Running TA on L1 outer zone" + }; +// int ta_response_pit[6][512]; int ta_response_land[6][512]; + int peaks_lands[15], peaks_pits[15]; int mins_lands[15], mins_pits[15]; + int* peaks[] = { peaks_pits, peaks_lands }; + int* mins[] = { mins_pits+1, mins_lands+1 }; + + if (data->pass<0 || data->pass>6) + return -1; + + wait_unit_ready(dev, 6); +// for (int pass=0;(pass<3*dev->media.layers) && (!ctl->skip());pass++) { + printf("%s\n",scan_txt[data->pass]); + + data->clear(); + for (i=0;i<9;i++) { + dev->cmd[0] = PLEXTOR_SCAN_TA_FETE; // 0xF3; + dev->cmd[1] = 0x1F; + dev->cmd[2] = 0x23; + dev->cmd[3] = 0x00; + dev->cmd[4] = 0x00; + dev->cmd[5] = scan_cmd[data->pass][0]; + dev->cmd[6] = scan_cmd[data->pass][1]; + dev->cmd[7] = i<<4; + dev->cmd[8] = 0xFF; + dev->cmd[9] = 0xFE; + dev->cmd[10] = 0x04*!i; + dev->cmd[11] = 0x00; + dev->cmd.transport(READ, dev->rd_buf, 65534); + printf(".\n"); + if (!strncmp(dev->dev,"DVDR PX-714A",14) || + !strncmp(dev->dev,"DVDR PX-716A",14)) + build_TA_histogram_px716(dev->rd_buf, data->pit, data->land, 512); + else + build_TA_histogram_px755(dev->rd_buf, data->pit, data->land, 512, dev->media.type); + // dev->parms.scan_speed_dvd); + } ////// + mins_lands[0] = 0; mins_pits[0]=0; + + int p0,p1; + int l0,l1; + for (j=1;j<400;j++) { +// if ((j>40) && (j<360)) { + if ( data->pit[j] == 0 ) { + p0 = data->pit[j-1]; p1 = data->pit[j+1]; + if ((p0>0) && (p1>0)) data->pit[j] = (p0+p1)/2; + } + if ( data->land[j] == 0 ) { + l0 = data->land[j-1]; l1 = data->land[j+1]; + if ((l0>0) && (l1>0)) data->land[j] = (l0+l1)/2; + } +// } + +// block.pit=ta_response_pit[pass][j]; +// block.land=ta_response_land[pass][j]; +// ctl->event_block_done(event_block_done_ta, block); + } + + evaluate_histogramme(data, peaks, mins); + + printf("peak shift pits : "); + sum=0; + for (m=0;m<10;m++) { + r = (int)((float)peaks_pits[m] - 21.5454 * ((m<9)?m:11) - 64); sum+=sqrt(abs(r)); + printf("%4d", r); + } + printf(" sum %f \n",sum); + printf("peak shift lands: "); + + sum=0; + for (m=0;m<10;m++) { + r = (int)((float)peaks_lands[m] - 21.5454 * ((m<9)?m:11) - 64); sum+=sqrt(abs(r)); + printf("%4d", r); + } + printf(" sum %f \n",sum); + + +// printf("TA test finished\n"); +// cmd_scan_end(); + return 0; +} + diff --git a/plugins/plextor/qscan_plugin.cpp b/plugins/plextor/qscan_plugin.cpp new file mode 100644 index 0000000..fde93ea --- /dev/null +++ b/plugins/plextor/qscan_plugin.cpp @@ -0,0 +1,277 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2009, Gennady "ShultZ" Kozlov + * + * 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 +#include + +static const int SPEEDS_ERRC_CD[] = { + 4*CD_SPEED_MULT, + 8*CD_SPEED_MULT, + 24*CD_SPEED_MULT, + 32*CD_SPEED_MULT, + 40*CD_SPEED_MULT, + 0 +}; + +static const int SPEEDS_ERRC_DVD[] = { + 2*DVD_SPEED_MULT, + 5*DVD_SPEED_MULT, + 8*DVD_SPEED_MULT, + 12*DVD_SPEED_MULT, + 0 +}; + +static const int SPEEDS_JB_CD[] = { + 4*CD_SPEED_MULT, + 0 +}; + +static const int SPEEDS_JB_DVD[] = { + 2*DVD_SPEED_MULT, + 0 +}; + +scan_plugin* plugin_create(drive_info* idev) +{ + return new scan_plextor(idev); +} + +void plugin_destroy(scan_plugin* iplugin) +{ + if (iplugin != NULL) delete iplugin; +} + +scan_plextor::scan_plextor(drive_info* idev) + : scan_plugin(), lba(0), fete_idx(0), fete_rsize(0) +{ + dev = idev; + if (!dev->silent) printf("scan_plextor()\n"); + devlist = (drivedesc*) &drivelist; + test=0; +} + +scan_plextor::~scan_plextor() +{ + if (!dev->silent) printf("~scan_plextor()\n"); +} + + +int scan_plextor::probe_drive() { + if (isPlextor(dev)) + plextor_px755_do_auth(dev); + if (dev->media.type & DISC_CD) { + if (cmd_cd_errc_init()) return DEV_FAIL; + if (cmd_scan_end()) return DEV_FAIL; + } else if (dev->media.type & DISC_DVD) { + if (cmd_dvd_errc_init()) return DEV_FAIL; + if (cmd_scan_end()) return DEV_FAIL; + } else { + return DEV_FAIL; + } + return DEV_PROBED; +} + +int scan_plextor::errc_data() +{ + if (dev->media.type & ~DISC_DVDRAM) return 0; + + if (dev->media.type & DISC_CD) { + return (ERRC_DATA_BLER|ERRC_DATA_E11|ERRC_DATA_E21|ERRC_DATA_E31|ERRC_DATA_E12|ERRC_DATA_E22|ERRC_DATA_E32|ERRC_DATA_UNCR); + } else if (dev->media.type & DISC_DVD) { + return (ERRC_DATA_PIE|ERRC_DATA_PIF|ERRC_DATA_POE|ERRC_DATA_POF|ERRC_DATA_UNCR); + } + return 0; +} + +int scan_plextor::check_test(unsigned int itest) +{ + switch (itest) { + case CHK_ERRC: + case CHK_JB: + if (dev->media.type & ~DISC_DVDRAM) + return 0; + break; + case CHK_FETE: + if (dev->media.type & (DISC_CD | DISC_DVD) & ~DISC_CDROM & ~DISC_DVDROM) + return 0; + break; + case CHK_TA: + if (dev->media.type & (DISC_DVD & ~DISC_DVDROM)) + //if (dev->media.type & DISC_DVD) + return 0; + break; + default: + break; + } + return -1; +} + +int* scan_plextor::get_test_speeds(unsigned int itest) +{ + switch (itest) { + case CHK_ERRC: + if (dev->media.type & DISC_CD) + return (int*)SPEEDS_ERRC_CD; + if (dev->media.type & DISC_DVD) + return (int*)SPEEDS_ERRC_DVD; + break; + case CHK_JB: + if (dev->media.type & DISC_CD) + return (int*)SPEEDS_JB_CD; + if (dev->media.type & DISC_DVD) + return (int*)SPEEDS_JB_DVD; + break; + default: + break; + } + return NULL; +} + +int scan_plextor::start_test(unsigned int itest, long ilba, int &speed) +{ + int r=-1; + plextor_px755_do_auth(dev); + switch (itest) { + case CHK_ERRC_CD: + lba=ilba; + set_read_speed(speed); + r = cmd_cd_errc_init(); + break; + case CHK_JB_CD: + lba=ilba; + set_read_speed(speed); + r = cmd_cd_jb_init(); + break; + case CHK_ERRC_DVD: + lba=ilba; + set_read_speed(speed); + r = cmd_dvd_errc_init(); + break; + case CHK_JB_DVD: + lba=ilba; + set_read_speed(speed); + r = cmd_dvd_jb_init(); + break; +#if 1 + case CHK_FETE: +#else + case CHK_FETE_CD: + case CHK_FETE_CDROM: + case CHK_FETE_DVD: + case CHK_FETE_DVDROM: +#endif + lba=ilba; + set_write_speed(speed); + r = cmd_fete_init(); + break; + case CHK_TA: + if (dev->media.type & (DISC_DVD & ~DISC_DVDROM)) { + r = 0; + test = CHK_TA_DVD; + // TA test should run at minimum available speed (2X for DVD-, 2.4X for DVD+) + // setting 2X for DVD+ will be 2.4 + int taspd=2; + set_read_speed(taspd); + return 0; + } + break; + default: + return -1; + } + if (!r) { + test = itest; + } else { + test = 0; + } + return r; +} + +int scan_plextor::scan_block(void *data, long *ilba) +{ + int r=-1; + switch (test) { + case CHK_ERRC_CD: + r = cmd_cd_errc_block((cd_errc*)data); + if(ilba) *ilba = lba; + return r; + case CHK_JB_CD: + r = cmd_cd_jb_block((cdvd_jb*)data); + if(ilba) *ilba = lba; + return r; + case CHK_ERRC_DVD: + r = cmd_dvd_errc_block((dvd_errc*)data); + if(ilba) *ilba = lba; + return r; + case CHK_JB_DVD: + r = cmd_dvd_jb_block((cdvd_jb*)data); + if(ilba) *ilba = lba; + return r; +#if 1 + case CHK_FETE: +#else + case CHK_FETE_CD: + case CHK_FETE_CDROM: + case CHK_FETE_DVD: + case CHK_FETE_DVDROM: +#endif + r = cmd_fete_block((cdvd_ft*)data); + if(ilba) *ilba = lba; + return r; + case CHK_TA_DVD: + cmd_dvd_ta_block((cdvd_ta*)data); + return r; + default: + return -1; + } +} + +int scan_plextor::end_test() +{ + switch (test) { + case CHK_ERRC_CD: + case CHK_ERRC_DVD: + case CHK_JB_CD: + case CHK_JB_DVD: + cmd_scan_end(); + break; +#if 1 + case CHK_FETE: +#else + case CHK_FETE_CD: + case CHK_FETE_CDROM: + case CHK_FETE_DVD: + case CHK_FETE_DVDROM: +#endif + cmd_fete_end(); + break; + case CHK_TA: + case CHK_TA_CD: + case CHK_TA_CDROM: + case CHK_TA_DVD: + case CHK_TA_DVDROM: + break; + default: + break; + } + test=0; + return 0; +} + +/* +__attribute__((constructor)) void init() { + printf("init()\n"); +} + +__attribute__((destructor)) void exit() { + printf("exit()\n"); +} +*/ diff --git a/plugins/plextor/qscan_plugin.h b/plugins/plextor/qscan_plugin.h new file mode 100644 index 0000000..113f993 --- /dev/null +++ b/plugins/plextor/qscan_plugin.h @@ -0,0 +1,114 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2009, Gennady "ShultZ" Kozlov + * + * 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 __QSCAN_PLEXTOR_H +#define __QSCAN_PLEXTOR_H + + +//#include +//#include +#include "qpx_scan_plugin_api.h" + +static const drivedesclist drivelist = +//static drivedesclist drivelist = +{ + { "PLEXTOR ", DEV_PLEXTOR, "CD-R PREMIUM ", PLEXTOR_PREMIUM, + CHK_ERRC_CD | CHK_JB_CD | CHK_FETE }, + { "PLEXTOR ", DEV_PLEXTOR, "CD-R PREMIUM2", PLEXTOR_PREMIUM2, + CHK_ERRC_CD | CHK_JB_CD | CHK_FETE }, + { "PLEXTOR ", DEV_PLEXTOR, "DVDR PX-708A2", PLEXTOR_708A2, + CHK_ERRC_CD | CHK_JB_CD | CHK_ERRC_DVD | CHK_JB_DVD | CHK_TA_DVD | CHK_FETE }, + { "PLEXTOR ", DEV_PLEXTOR, "DVDR PX-712A ", PLEXTOR_712, + CHK_ERRC_CD | CHK_JB_CD | CHK_ERRC_DVD | CHK_JB_DVD | CHK_TA_DVD | CHK_FETE }, + { "PLEXTOR ", DEV_PLEXTOR, "DVDR PX-714A ", PLEXTOR_716, + CHK_ERRC_CD | CHK_JB_CD | CHK_ERRC_DVD | CHK_JB_DVD | CHK_TA_DVD | CHK_FETE }, + { "PLEXTOR ", DEV_PLEXTOR, "DVDR PX-716A ", PLEXTOR_716, + CHK_ERRC_CD | CHK_JB_CD | CHK_ERRC_DVD | CHK_JB_DVD | CHK_TA_DVD | CHK_FETE }, + { "PLEXTOR ", DEV_PLEXTOR, "DVDR PX-716AL", PLEXTOR_716AL, + CHK_ERRC_CD | CHK_JB_CD | CHK_ERRC_DVD | CHK_JB_DVD | CHK_TA_DVD | CHK_FETE }, + { "PLEXTOR ", DEV_PLEXTOR, "DVDR PX-755A ", PLEXTOR_755, + CHK_ERRC_CD | CHK_JB_CD | CHK_ERRC_DVD | CHK_JB_DVD | CHK_TA_DVD | CHK_FETE }, + { "PLEXTOR ", DEV_PLEXTOR, "DVDR PX-760A ", PLEXTOR_760, + CHK_ERRC_CD | CHK_JB_CD | CHK_ERRC_DVD | CHK_JB_DVD | CHK_TA_DVD | CHK_FETE }, + { "", 0, "", 0} +}; + +// CHK_CD_ERRC | CHK_CD_JB | CHK_FETE_CD | CHK_DVD_ERRC | CHK_DVD_JB | CHK_FETE_DVD | CHK_TA_DVD }, +// CHK_CD_ERRC | CHK_CD_JB | CHK_DVD_ERRC | CHK_DVD_JB | CHK_TA_DVD | CHK_FETE }, + +static const char plugin_name[]="PLEXTOR"; +static const char plugin_desc[]="Scan plugin for PLEXTOR devices (based on SANYO chips)"; + +class drive_info; + +class scan_plextor : public scan_plugin { +public: +// scan_plextor(drive_info* idev=NULL); + scan_plextor(drive_info* idev); + virtual ~scan_plextor(); +// virtual int check_drive(); + virtual int probe_drive(); + virtual int errc_data(); + virtual int check_test(unsigned int test); + virtual int* get_test_speeds(unsigned int test); + virtual int start_test(unsigned int test, long slba, int &speed); + virtual int scan_block(void* data,long* ilba); + virtual int end_test(); + + virtual const char* name() { return plugin_name; }; + virtual const char* desc() { return plugin_desc; }; +private: + long lba; + int fete_idx; + int fete_rsize; +/* + int plextor_init_fete(fete *data); + int plextor_init_dvd_ta(); +*/ + +// CD ERRC methods + int cmd_cd_errc_init(); + int cmd_cd_errc_block(cd_errc *data); + int cmd_cd_errc_getdata(cd_errc *data); + +// CD J/B methods + int cmd_cd_jb_init(); + int cmd_cd_jb_block(cdvd_jb *data); + int cmd_jb_getdata(cdvd_jb *data); + +// DVD ERRC methods + int cmd_dvd_errc_init(); + int cmd_dvd_errc_block(dvd_errc *data); + int cmd_dvd_errc_getdata(dvd_errc *data); + +// DVD J/B methods + int cmd_dvd_jb_init(); + int cmd_dvd_jb_block(cdvd_jb *data); +// end scan + int cmd_scan_end(); + +// FE/TE commands + int cmd_fete_init(); + int cmd_fete_block(cdvd_ft *data); + int cmd_fete_getdata(); + int cmd_fete_end(); + +// Time Analyser commands + int cmd_dvd_ta_block(cdvd_ta *data); + + int build_TA_histogram_px716(unsigned char* response_data, int* dest_pit, int* dest_land, int len); + int build_TA_histogram_px755(unsigned char* response_data, int* dest_pit, int* dest_land, int len, int dt); + int evaluate_histogramme(cdvd_ta* data, int** peaks, int** mins); +}; + +#endif // __QSCAN_PLEXTOR_H + diff --git a/plugins/tsst/Makefile b/plugins/tsst/Makefile new file mode 100644 index 0000000..71a3de9 --- /dev/null +++ b/plugins/tsst/Makefile @@ -0,0 +1,3 @@ +LIBN = qscan_tsst + +include ../Makefile.plugin diff --git a/plugins/tsst/qscan_cmd.cpp b/plugins/tsst/qscan_cmd.cpp new file mode 100644 index 0000000..77e946f --- /dev/null +++ b/plugins/tsst/qscan_cmd.cpp @@ -0,0 +1,110 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2005-2006 Gennady "ShultZ" Kozlov + * + * 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 +#include + +#include +#include +#include + +#include + +// ************* Scan init commands ********* +int scan_tsst::cmd_cd_errc_init() { + return seek(dev,0); +} + +// DVD part is same as Lite-On + +int scan_tsst::cmd_dvd_errc_init() { +// 2B 00 00 00 00 00 00 00 00 00 + dev->cmd[0] = 0x2B; + if ((dev->err=dev->cmd.transport(NONE,NULL,0))){ + sperror ("TSST INIT DVD ERRC",dev->err); return 1; + } + printf("TSST INIT DVD ERRC: OK\n"); + return 0; +} + +// ********************** +int scan_tsst::cmd_cd_errc_block(cd_errc *data) +{ + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x0E; + dev->cmd[8] = 0x4B; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,8))){ + sperror ("TSST Cx SCAN INTERVAL",dev->err); return 1; + } +#if 0 + for (int i=0; i<8; i++) { + printf(" %02X",dev->rd_buf[i]); + } + printf("\n"); +#endif + lba = ntoh32(dev->rd_buf); + + data->bler = ntoh16(dev->rd_buf+4); + data->e11 = 0; + data->e21 = 0; + data->e31 = 0; + data->e12 = 0; + data->e22 = ntoh16(dev->rd_buf+6); + data->e32 = 0; + data->uncr = 0; + return 0; +} + + +int scan_tsst::cmd_dvd_errc_block(dvd_errc *data) +{ +// int i; +// *pie = 0; + + dev->cmd[0] = 0xF3; + dev->cmd[1] = 0x0E; + dev->cmd[8] = 0x10; + if ((dev->err=dev->cmd.transport(READ,dev->rd_buf,10))){ + sperror ("TSST PI SCAN INTERVAL",dev->err); return 1; + } +#if 0 + for (int i=0; i<8; i++) { + printf(" %02X",dev->rd_buf[i]); + } + printf("\n"); +#endif + +// Data Received: +// 00000000 00 00 00 8E 00 00 00 00 ...Ž.... +// lba+=16; + +// lba = ((dev->rd_buf[1] << 16 )& 0xFF0000) + ((dev->rd_buf[2] << 8)&0xFF00 ) + (dev->rd_buf[3] & 0xFF); + lba = ntoh32(dev->rd_buf); + + data->pie = ntoh16(dev->rd_buf+4); +// data->pi8 = 0; + data->pif = ntoh16(dev->rd_buf+6); + data->poe = 0; +// data->po8 = 0; + data->pof = 0; + return 0; +} + +// ************* END SCAN COMMANDS ********* +int scan_tsst::cmd_cd_errc_end() { + dev->err = 0; + return 0; +} + +int scan_tsst::cmd_dvd_errc_end() { + dev->err = 0; + return 0; +} diff --git a/plugins/tsst/qscan_plugin.cpp b/plugins/tsst/qscan_plugin.cpp new file mode 100644 index 0000000..262816b --- /dev/null +++ b/plugins/tsst/qscan_plugin.cpp @@ -0,0 +1,141 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2007-2008, Gennady "ShultZ" Kozlov + * + * 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 +#include + +scan_plugin* plugin_create(drive_info* idev){ + return new scan_tsst(idev); +} + +void plugin_destroy(scan_plugin* iplugin){ + if (iplugin != NULL) delete iplugin; +} + +scan_tsst::scan_tsst(drive_info* idev) + : scan_plugin(), lba(0) +{ + dev = idev; + if (!dev->silent) printf("scan_tsst()\n"); + devlist = (drivedesc*) &drivelist; + test=0; +} + +scan_tsst::~scan_tsst() { + if (!dev->silent) printf("~scan_tsst()\n"); +} + +int scan_tsst::probe_drive() { + cd_errc tmp_errc; + if (strncmp(dev->ven,"TSSTcorp", 8)) return DEV_FAIL; + if (dev->media.type & DISC_CD) { + if (cmd_cd_errc_init()) return DEV_FAIL; + if (cmd_cd_errc_block(&tmp_errc)) return DEV_FAIL; + if (cmd_cd_errc_end()) return DEV_FAIL; + } else if (dev->media.type & DISC_DVD) { + if (cmd_dvd_errc_init()) return DEV_FAIL; + if (cmd_dvd_errc_end()) return DEV_FAIL; + } else { + return DEV_FAIL; + } + printf("TSST probe OK\n"); + return DEV_PROBED; +} + +int scan_tsst::errc_data() +{ + if (dev->media.type & DISC_CD) { + return (ERRC_DATA_BLER|ERRC_DATA_E32|ERRC_DATA_UNCR); + } else if (dev->media.type & DISC_DVD) { + return (ERRC_DATA_PIE|ERRC_DATA_PIF|ERRC_DATA_UNCR); + } + return 0; +} + +int scan_tsst::check_test(unsigned int itest) +{ + switch (itest) { + case CHK_ERRC: + if (dev->media.type & ~DISC_DVDRAM) + return 0; + break; + default: + break; + } + return -1; +} + +int scan_tsst::start_test(unsigned int itest, long ilba, int &speed){ + int r=-1; + switch (itest) { + case CHK_ERRC_CD: + lba=ilba; + set_read_speed(speed); + r = cmd_cd_errc_init(); + break; + case CHK_ERRC_DVD: + lba=ilba; + set_read_speed(speed); + r = cmd_dvd_errc_init(); + break; + default: + return -1; + } + if (!r) { + test = itest; + } else { + end_test(); + } + return r; +} + +int scan_tsst::scan_block(void *data, long *ilba) { + int r=-1; + switch (test) { + case CHK_ERRC_CD: + r = cmd_cd_errc_block((cd_errc*)data); + if(ilba) *ilba = lba; + return r; + case CHK_ERRC_DVD: + r = cmd_dvd_errc_block((dvd_errc*)data); + if(ilba) *ilba = lba; + return r; + default: + return -1; + } +} + +int scan_tsst::end_test() { + switch (test) { + case CHK_ERRC_CD: + cmd_cd_errc_end(); + break; + case CHK_ERRC_DVD: + cmd_dvd_errc_end(); + break; + default: + break; + } + test=0; + return 0; +} + +/* +__attribute__((constructor)) void init() { + printf("init()\n"); +} + +__attribute__((destructor)) void exit() { + printf("exit()\n"); +} +*/ + diff --git a/plugins/tsst/qscan_plugin.h b/plugins/tsst/qscan_plugin.h new file mode 100644 index 0000000..fbbfe86 --- /dev/null +++ b/plugins/tsst/qscan_plugin.h @@ -0,0 +1,59 @@ +/* + * This file is part of the QPxTool project. + * Copyright (C) 2009, Gennady "ShultZ" Kozlov + * + * 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 __QSCAN_TSST_H +#define __QSCAN_TSST_H + +#include "qpx_scan_plugin_api.h" + +static const drivedesclist drivelist = +{ + { "TSSTcorp", DEV_TSST, "CDDVDRW SH-S202N", TSST_H2, CHK_ERRC_CD | CHK_ERRC_DVD }, + + { "", 0, "", 0} +}; + +static const char plugin_name[]="TSST"; +static const char plugin_desc[]="Scan plugin for Toshiba-Samsung devices"; + +class drive_info; + +class scan_tsst : public scan_plugin { +public: + scan_tsst(drive_info* idev); + virtual ~scan_tsst(); +// virtual int check_drive(); + virtual int probe_drive(); + virtual int errc_data(); + virtual int check_test(unsigned int test); + virtual int start_test(unsigned int test, long slba, int &speed); + virtual int scan_block(void* data,long* ilba); + virtual int end_test(); + + virtual const char* name() { return plugin_name; }; + virtual const char* desc() { return plugin_desc; }; +private: + long lba; + +// CD ERRC methods + int cmd_cd_errc_init(); + int cmd_cd_errc_block(cd_errc *data); + int cmd_cd_errc_end(); + +// DVD ERRC methods + int cmd_dvd_errc_init(); + int cmd_dvd_errc_block(dvd_errc *data); + int cmd_dvd_errc_end(); + +}; + +#endif diff --git a/qpxtool.spec b/qpxtool.spec new file mode 100644 index 0000000..c24a90e --- /dev/null +++ b/qpxtool.spec @@ -0,0 +1,138 @@ +%define summary CD/DVD media check/drive control tools +%define name qpxtool +%define version 0.7.2 +%define release 1 +%define vendor Gennady "ShultZ" Kozlov +%define packager Gennady "ShultZ" Kozlov +%define email qpxtool@mail.ru + +Summary: %{summary} +Name: %{name} +Version: %{version} +Release: %{release} +Group: Applications/Media +License: GPL +URL: http://qpxtool.sourceforge.net +Vendor: %{vendor} +Packager: %{packager} %{email} +Source: %{name}-%{version}.tar.bz2 +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n) + +#BuildRequires: libpng-devel +Requires: %{name}-lib = %{version}-%{release} +#Requires: libpng + +%package lib +Summary: QPxTool libraries +Group: Development/Libraries + +%package gui +Summary: QPxTool GUI +Group: X11/Applications +Requires: %{name} = %{version}-%{release} +Requires: %{name}-lib = %{version}-%{release} + +%package devel +Summary: QPxTool development files +Group: Development/Libraries +Requires: %{name}-lib = %{version}-%{release} + +%description +QPxTool is the way to get full control over your CD/DVD drives. +It is the Open Source Solution which intends to give you access to all +available Quality Checks (Q-Checks) on written and blank media, that +are available for your drive. This will help you to find the right media +and the optimized writing speed for your hardware, which will increase +the chance for a long data lifetime. + +Console CD/DVD media check and drive control tools and libraries + +qscan - quality check tool +qscand - network quality check daemon (frontend to qscan) +readdvd - a little tool for reading damaged CD/DVD (multi-pass) +cdvdcontrol - extended drive features control tool +pxfw - firmware flasher for Plextor drives +f1tattoo - DiscT@2 tool for Yamaha CRW-F1 + +Authors: +-------- + Gennady "ShultZ" Kozlov + +%description lib +Required libraries for QPxTool CD/DVD media check and drive control tools + +Authors: +-------- + Gennady "ShultZ" Kozlov + +%description devel +Developnemt files for QPxTool CD/DVD media check and drive control tools + +Authors: +-------- + Gennady "ShultZ" Kozlov + +%description gui +Qt frontend for CD/DVD media check and drive control tools + +Authors: +-------- + Gennady "ShultZ" Kozlov + +%prep +%setup -q -n %{name} + +%build +#### +./configure --prefix=/usr --libdir=%{_libdir} --mandir=%{_mandir} +make %{?_smp_mflags} + +%install + +[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT + +make install DESTDIR=$RPM_BUILD_ROOT + +%post lib +/sbin/ldconfig + +%postun lib +/sbin/ldconfig + +%files +%defattr(-,root,root) +/usr/bin/* +/usr/sbin/* +%{_mandir}/man1/* +%{_mandir}/man8/* +%{_libdir}/qpxtool/libqscan_* +%doc AUTHORS COPYING ChangeLog README TODO +%exclude /usr/bin/qpxtool + +%files lib +%defattr(-,root,root) +%{_libdir}/libqpx* + +%files gui +%defattr(-,root,root) +/usr/bin/qpxtool +/usr/share/pixmaps/qpxtool.png +/usr/share/applications/qpxtool.desktop +/usr/share/qpxtool/locale/* + +%files devel +%defattr(-,root,root) +/usr/include/qpxtool/* + +%clean +[ "$RPM_BUILD_ROOT" != "/" ] && %__rm -rf $RPM_BUILD_ROOT + +%changelog +* Wed Nov 11 2009 Gennady "ShultZ" Kozlov +- main package splitted to main,lib + +* Wed Oct 21 2009 Gennady "ShultZ" Kozlov +- splitted packages: main, devel, gui + +* Tue Jul 21 2009 Gennady "ShultZ" Kozlov +- initial spec file created diff --git a/slack-desc b/slack-desc new file mode 100644 index 0000000..f365f48 --- /dev/null +++ b/slack-desc @@ -0,0 +1,13 @@ +qpxtool: +qpxtool: QPxTool - CD/DVD media check/drive control tools +qpxtool: +qpxtool: +qpxtool: qpxtool - Qt frontend for other tools +qpxtool: +qpxtool: qscan - quality check tool +qpxtool: qscand - network quality check daemon (frontend to qscan) +qpxtool: readdvd - a little tool for reading damaged CD/DVD (multi-pass) +qpxtool: cdvdcontrol - extended drive features control tool +qpxtool: pxfw - firmware flasher for Plextor drives +qpxtool: f1tattoo - DiscT@2 tool for Yamaha CRW-F1 +qpxtool: diff --git a/status.html b/status.html new file mode 100644 index 0000000..880d5e2 --- /dev/null +++ b/status.html @@ -0,0 +1,340 @@ + + + + + + +
+
+QPxTool 0.7 status: Release +
+
+ + + + + + + + + + + + + + + + + + + +
Platforms support status
OSarchstatusnotes
GNU/Linuxx86OK
x86-64OK
SPARCOK
MacOS XPPC
x86OK
FreeBSDOK
OpenBSDOK
NetBSDOK
win32x86OK + no daemon mode in qscand
+ no png support in f1tattoo
+ print preview does not work if no printer installed
+
+

+ + + + + + + + + + + + + + + + + + + + + + + + +
qpxtool: GUI controls
FeatureDetectControl
Hide CD-R++
Single Session++
SpeedRead++
DVD+R bitsetting++
DVD+R DL bitsetting++
DVD+R(W) testwrite++
PoweRec++
VariRec CD++
VariRec DVD++
GigaRec++
SecuRec++
Silent Mode++
AutoStrategy++
MQCK++
AS DB++
AS DB Save/Load
PlexEraser++
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
qscan: Scan algo status
AlgoCDDVDBD
Read transferOKOKOK
Write transferOKOK
Error correctionOKOKOK
Jitter/AsymmetryOKOK
Focus/TrackingOKOK
Time AnalyserOK
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
qscan: Scan plugins status
CDDVDBD
Pluginprobesectors per intervalERRCJBFE/TEERRCJBFE/TETAERRCJBFE/TE
CD
ideal is 75
DVD
ideal is 16
BD
ideal is 32
PLEXTORnever.
few models,
no clones
7516N/AFULLFULLFULLFULLFULLFULLOKN/AN/AN/A
PIONEER7516N/Abler,e22N/AN/Apie,pifN/AN/AN/AN/AN/AN/A
ASUS~75~16bler,e22N/AN/Apie,pifN/AN/AN/AN/AN/AN/A
NEC7516?bler,e22N/Apie,pifN/AN/AFULL
(untested)
N/A
LITE-ONdangerous751632bler,e22,e32N/AFULLpie,pifN/AFULLN/AFULLFULL
BENQ75?FULLFULLFULLN/A
BENQ ROM75256N/Abler,e22jitterN/Apie,pifjitterN/AN/AN/AN/AN/A
TSST??bler,e22N/Apie,pifN/AN/AN/A
Generic C275N/AN/AC2 onlyN/AN/AN/AN/AN/AN/AN/AN/AN/A
+
+ + + + + + + + + +
Legend
done
unfinished
untested
unstable
planning
+ + + +