/* cdrdao - write audio CD-Rs in disc-at-once mode * * Copyright (C) 1998-2002 Andreas Mueller * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include "CdrDriver.h" #include "PWSubChannel96.h" #include "Toc.h" #include "util.h" #include "CdTextItem.h" #include "data.h" #include "port.h" // all drivers #include "CDD2600.h" #include "PlextorReader.h" #include "PlextorReaderScan.h" #include "GenericMMC.h" #include "GenericMMCraw.h" #include "RicohMP6200.h" #include "TaiyoYuden.h" #include "YamahaCDR10x.h" #include "TeacCdr55.h" #include "SonyCDU920.h" #include "SonyCDU948.h" #include "ToshibaReader.h" // Paranoia DAE related #include "cdda_interface.h" #include "../paranoia/cdda_paranoia.h" typedef CdrDriver *(*CdrDriverConstructor)(ScsiIf *, unsigned long); struct DriverSelectTable { char *driverId; char *vendor; char *model; unsigned long options; struct DriverSelectTable *next; }; struct DriverTable { char *driverId; CdrDriverConstructor constructor; }; static DriverSelectTable *READ_DRIVER_TABLE = NULL; static DriverSelectTable *WRITE_DRIVER_TABLE = NULL; static DriverSelectTable BUILTIN_READ_DRIVER_TABLE[] = { { "generic-mmc", "ASUS", "CD-S340", 0, NULL }, { "generic-mmc", "ASUS", "CD-S400", 0, NULL }, { "generic-mmc", "ASUS", "CD-S500/A", OPT_MMC_NO_SUBCHAN, NULL }, { "generic-mmc", "ASUS", "DVD-ROM E608", 0, NULL }, { "generic-mmc", "E-IDE", "CD-950E/TKU", 0, NULL }, { "generic-mmc", "E-IDE", "CD-ROM 36X/AKU", 0, NULL }, { "generic-mmc", "E-IDE", "CD-ROM 52X/AKH", 0, NULL }, { "generic-mmc", "FUNAI", "E295X", 0, NULL }, { "generic-mmc", "Goldstar", "CD-RW CED-8042B", 0, NULL }, { "generic-mmc", "HITACHI", "CDR-7730", 0, NULL }, { "generic-mmc", "HITACHI", "CDR-8435", OPT_MMC_NO_SUBCHAN, NULL }, { "generic-mmc", "LG", "CD-ROM CRD-8480C", OPT_MMC_NO_SUBCHAN, NULL }, { "generic-mmc", "LG", "CD-ROM CRD-8482B", OPT_MMC_NO_SUBCHAN, NULL }, { "generic-mmc", "LG", "CD-ROM CRD-8521B", 0, NULL }, { "generic-mmc", "LG", "DVD-ROM DRN8080B", OPT_DRV_GET_TOC_GENERIC, NULL }, { "generic-mmc", "LITE-ON", "CD-ROM", OPT_DRV_GET_TOC_GENERIC, NULL }, { "generic-mmc", "LITE-ON", "LTD-163", 0, NULL }, { "generic-mmc", "LITEON", "DVD-ROM LTD163D", 0, NULL }, { "generic-mmc", "MATSHITA", "CD-ROM CR-588", 0, NULL }, { "generic-mmc", "MATSHITA", "CD-ROM CR-589", OPT_MMC_USE_PQ|OPT_MMC_PQ_BCD, NULL }, { "generic-mmc", "MATSHITA", "DVD-ROM SR-8585", OPT_MMC_USE_PQ|OPT_MMC_PQ_BCD, NULL }, { "generic-mmc", "MEMOREX", "CD-233E", 0, NULL }, { "generic-mmc", "MITSUMI", "CD-ROM FX4820", OPT_MMC_NO_SUBCHAN, NULL }, { "generic-mmc", "OPTICS_S", "8622", 0, NULL }, { "generic-mmc", "PHILIPS", "36X/AKU", 0, NULL }, { "generic-mmc", "PHILIPS", "CD-ROM PCCD052", 0, NULL }, { "generic-mmc", "PHILIPS", "E-IDE CD-ROM 36X", 0, NULL }, { "generic-mmc", "PIONEER", "CD-ROM DR-U32", OPT_DRV_GET_TOC_GENERIC|OPT_MMC_USE_PQ|OPT_MMC_PQ_BCD, NULL }, { "generic-mmc", "PIONEER", "DVD-103", OPT_MMC_USE_PQ|OPT_MMC_PQ_BCD, NULL }, { "generic-mmc", "PIONEER", "DVD-104", OPT_MMC_USE_PQ|OPT_MMC_PQ_BCD, NULL }, { "generic-mmc", "PIONEER", "DVD-105", OPT_MMC_USE_PQ|OPT_MMC_PQ_BCD, NULL }, { "generic-mmc", "SONY", "CD-ROM CDU31A-02", 0, NULL }, { "generic-mmc", "SONY", "CD-ROM CDU4821", 0, NULL }, { "generic-mmc", "SONY", "CDU5211", 0, NULL }, { "generic-mmc", "TEAC", "CD-524E", OPT_DRV_GET_TOC_GENERIC|OPT_MMC_USE_PQ|OPT_MMC_PQ_BCD, NULL }, { "generic-mmc", "TEAC", "CD-532E", OPT_MMC_USE_PQ|OPT_MMC_PQ_BCD, NULL }, { "generic-mmc", "TEAC", "CD-540E", 0, NULL }, { "generic-mmc", "TOSHIBA", "CD-ROM XM-3206B", 0, NULL }, { "generic-mmc", "TOSHIBA", "CD-ROM XM-6102B", 0, NULL }, { "generic-mmc", "TOSHIBA", "CD-ROM XM-6302B", 0, NULL }, { "generic-mmc", "TOSHIBA", "CD-ROM XM-6402B", 0, NULL }, { "generic-mmc", "TOSHIBA", "DVD-ROM SD-C2202", 0, NULL }, { "generic-mmc", "TOSHIBA", "DVD-ROM SD-C2302", 0, NULL }, { "generic-mmc", "TOSHIBA", "DVD-ROM SD-C2402", 0, NULL }, { "generic-mmc", "TOSHIBA", "DVD-ROM SD-M1102", 0, NULL }, { "generic-mmc", "TOSHIBA", "DVD-ROM SD-M1401", 0, NULL }, { "generic-mmc", "TOSHIBA", "DVD-ROM SD-M1402", 0, NULL }, { "plextor", "HITACHI", "DVD-ROM GD-2500", 0, NULL }, { "plextor", "MATSHITA", "CD-ROM CR-506", OPT_PLEX_DAE_D4_12, NULL }, { "plextor", "MATSHITA", "CR-8008", 0, NULL }, { "plextor", "NAKAMICH", "MJ-5.16S", 0, NULL }, { "plextor", "PIONEER", "CD-ROM DR-U03", OPT_DRV_GET_TOC_GENERIC, NULL }, { "plextor", "PIONEER", "CD-ROM DR-U06", OPT_DRV_GET_TOC_GENERIC, NULL }, { "plextor", "PIONEER", "CD-ROM DR-U10", OPT_DRV_GET_TOC_GENERIC, NULL }, { "plextor", "PIONEER", "CD-ROM DR-U12", OPT_DRV_GET_TOC_GENERIC, NULL }, { "plextor", "PIONEER", "CD-ROM DR-U16", OPT_DRV_GET_TOC_GENERIC, NULL }, { "plextor", "PIONEER", "DVD-303", 0, NULL }, { "plextor", "PIONEER", "DVD-305", 0, NULL }, { "plextor", "SAF", "CD-R2006PLUS", 0, NULL }, { "plextor", "SONY", "CD-ROM", 0, NULL }, { "plextor", "SONY", "CD-ROM CDU-76", 0, NULL }, { "plextor", "TOSHIBA", "XM-5401", 0, NULL }, { "plextor-scan", "PLEXTOR", "CD-ROM", 0, NULL }, { "plextor-scan", "PLEXTOR", "PX-40TS", 0, NULL }, { "plextor-scan", "PLEXTOR", "PX-40TW", 0, NULL }, { "plextor-scan", "PLEXTOR", "PX-63", 0, NULL }, { "plextor-scan", "TEAC", "CD-ROM CD-532S", OPT_PLEX_USE_PQ|OPT_PLEX_PQ_BCD, NULL }, { "teac-cdr55", "TEAC", "CD-532S", 0, NULL }, { "toshiba", "TOSHIBA", "1504", 0, NULL }, { "toshiba", "TOSHIBA", "CD-ROM XM-3601B", 0, NULL }, { "toshiba", "TOSHIBA", "CD-ROM XM-5302TA", 0, NULL }, { "toshiba", "TOSHIBA", "CD-ROM XM-5701TA", 0, NULL }, { "toshiba", "TOSHIBA", "CD-ROM XM-6201TA", 0, NULL }, { "toshiba", "TOSHIBA", "CD-ROM XM-6401TA", 0, NULL }, { "toshiba", "TOSHIBA", "DVD-ROM SD-2102", 0, NULL }, { NULL, NULL, NULL, 0, NULL }}; static DriverSelectTable BUILTIN_WRITE_DRIVER_TABLE[] = { { "cdd2600", "GRUNDIG", "CDR100IPW", 0, NULL }, { "cdd2600", "HP", "CD-Writer 4020", 0, NULL }, { "cdd2600", "HP", "CD-Writer 6020", 0, NULL }, { "cdd2600", "IMS", "522", 0, NULL }, { "cdd2600", "IMS", "CDD2000", 0, NULL }, { "cdd2600", "KODAK", "PCD-225", 0, NULL }, { "cdd2600", "PHILIPS", "CDD2000", 0, NULL }, { "cdd2600", "PHILIPS", "CDD2600", 0, NULL }, { "cdd2600", "PHILIPS", "CDD522", 0, NULL }, { "generic-mmc", "AOPEN", "CD-RW CRW1632", 0, NULL }, { "generic-mmc", "AOPEN", "CD-RW CRW2040", 0, NULL }, { "generic-mmc", "AOPEN", "CD-RW-241040", 0, NULL }, { "generic-mmc", "AOPEN", "CRW9624", 0, NULL }, { "generic-mmc", "CD-RW", "CDR-2440MB", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "CREATIVE", "CD-RW RW1210E", 0, NULL }, { "generic-mmc", "CREATIVE", "CD-RW RW4424", 0, NULL }, { "generic-mmc", "CREATIVE", "CD-RW RW8433E", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "CREATIVE", "CD5233E", 0, NULL }, { "generic-mmc", "DELTA", "OME-W141", 0, NULL }, { "generic-mmc", "GENERIC", "CRD-BP1600P", 0, NULL }, { "generic-mmc", "GENERIC", "CRD-R800S", 0, NULL }, { "generic-mmc", "GENERIC", "CRD-RW2", 0, NULL }, { "generic-mmc", "HL-DT-ST", "RW/DVD GCC-4120B", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "HP", "9510i", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "HP", "CD-Writer+ 7570", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "HP", "CD-Writer+ 8100", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "HP", "CD-Writer+ 8200", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "HP", "CD-Writer+ 8290", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "HP", "CD-Writer+ 9100", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "HP", "CD-Writer+ 9110", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "HP", "CD-Writer+ 9200", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "HP", "CD-Writer+ 9300", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "HP", "CD-Writer+ 9600", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "HP", "CD-Writer+ 9700", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "HP", "DVD Writer 100j", 0, NULL }, { "generic-mmc", "IDE-CD", "R/RW 16x10A", 0, NULL }, { "generic-mmc", "IMATION", "IMW121032IAB", 0, NULL }, { "generic-mmc", "LG", "8088B", 0, NULL }, { "generic-mmc", "LG", "8120B", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "LG", "CD-ROM CDR-8428B", 0, NULL }, { "generic-mmc", "LG", "CD-RW CED-8080B", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "LG", "CD-RW CED-8081B", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "LG", "CD-RW CED-8083B", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "LG", "CD-RW GCE-8240B", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "LG", "COMBO", 0, NULL }, { "generic-mmc", "LG", "HL-DT-ST RW/DVD GCC-4080N", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "LITE-ON", "LTR-0841", OPT_MMC_CD_TEXT|OPT_MMC_NO_SUBCHAN, NULL }, { "generic-mmc", "LITE-ON", "LTR-24102B", 0, NULL }, { "generic-mmc", "LITE-ON", "LTR-32125W", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "MATSHITA", "CD-R CW-7502", 0, NULL }, { "generic-mmc", "MATSHITA", "CD-R CW-7503", 0, NULL }, { "generic-mmc", "MATSHITA", "CD-R CW-7582", 0, NULL }, { "generic-mmc", "MATSHITA", "CD-R CW-7585", 0, NULL }, { "generic-mmc", "MATSHITA", "CD-R CW-7586", 0, NULL }, { "generic-mmc", "MATSHITA", "CDRRW01", 0, NULL }, { "generic-mmc", "MATSHITA", "UJDA360", 0, NULL }, { "generic-mmc", "MATSHITA", "UJDA710", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "MATSHITA", "UJDA720", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "MEMOREX", "24MAX 1040", 0, NULL }, { "generic-mmc", "MEMOREX", "40MAXX 1248AJ", 0, NULL }, { "generic-mmc", "MEMOREX", "CD-RW4224", 0, NULL }, { "generic-mmc", "MICROSOLUTIONS", "BACKPACK CD REWRITER", 0, NULL }, { "generic-mmc", "MITSUMI", "CR-4801", 0, NULL }, { "generic-mmc", "MITSUMI", "CR-48X5", 0, NULL }, { "generic-mmc", "MITSUMI", "CR-48X5TE", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "MITSUMI", "CR-48X8TE", 0, NULL }, { "generic-mmc", "MITSUMI", "CR-48XATE", 0, NULL }, { "generic-mmc", "OLYMPIC", "RWD RW4224", OPT_DRV_GET_TOC_GENERIC|OPT_MMC_USE_PQ, NULL }, { "generic-mmc", "PANASONIC", "CD-R CW-7582", 0, NULL }, { "generic-mmc", "PHILIPS", "CDRW1610A", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "PHILIPS", "CDRW2412A", 0, NULL }, { "generic-mmc", "PHILIPS", "PCA460RW", 0, NULL }, { "generic-mmc", "PIONEER", "DVD-ROM DVD-114", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "PLEXTOR", "CD-R PX-R412", OPT_MMC_USE_PQ|OPT_MMC_READ_ISRC, NULL }, { "generic-mmc", "PLEXTOR", "CD-R PX-R820", 0, NULL }, { "generic-mmc", "PLEXTOR", "CD-R PX-W1210", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "PLEXTOR", "CD-R PX-W124", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "PLEXTOR", "CD-R PX-W1610", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "PLEXTOR", "CD-R PX-W4220", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "PLEXTOR", "CD-R PX-W8220", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "PLEXTOR", "CD-R PX-W8432", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "PLEXTOR", "CD-R PX-W241040", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "PLEXTOR", "CD-R PX-W2410a", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "PLEXTOR", "CD-R PX-W4012A", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "RICOH", "CD-R/RW MP7040", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "RICOH", "CD-R/RW MP7060", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "RICOH", "CD-R/RW MP7063A", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "RICOH", "CD-R/RW MP7080", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "RICOH", "CD-R/RW MP7083A", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "RICOH", "DVD/CDRW MP9060", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "SAF", "CD-R8020", 0, NULL }, { "generic-mmc", "SAF", "CD-RW4224A", 0, NULL }, { "generic-mmc", "SAF", "CD-RW6424", 0, NULL }, { "generic-mmc", "SAMSUNG", "CD-R/RW SW-206", 0, NULL }, { "generic-mmc", "SAMSUNG", "CD-R/RW SW-408B", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "SAMSUNG", "CDRW/DVD SM-308B", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "SANYO", "CRD-BP3", 0, NULL }, { "generic-mmc", "SONY", "CD-RW CRX700E", 0, NULL }, { "generic-mmc", "SONY", "CRX-815", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "SONY", "CRX100", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "SONY", "CRX120", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "SONY", "CRX140", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "SONY", "CRX145", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "SONY", "CRX160E", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "SONY", "CRX175A1", 0, NULL }, { "generic-mmc", "SONY", "CRX175E", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "SONY", "CRX185E1", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "TDK", "4800", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "TDK", "CDRW121032", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "TDK", "CDRW321040B", 0, NULL }, { "generic-mmc", "TDK", "CDRW8432", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "TEAC", "CD-R56", OPT_MMC_USE_PQ|OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "TEAC", "CD-R58", OPT_MMC_USE_PQ|OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "TEAC", "CD-W216E", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "TEAC", "CD-W512EB", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "TEAC", "CD-W512SB", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "TEAC", "CD-W516EB", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "TEAC", "CD-W516EC", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "TEAC", "CD-W524E", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "TEAC", "CD-W54E", 0, NULL }, { "generic-mmc", "TORiSAN", "CDW-U4424", 0, NULL }, { "generic-mmc", "TOSHIBA", "DVD-ROM SD-M1612", 0, NULL }, { "generic-mmc", "TOSHIBA", "DVD-ROM SD-R1002", 0, NULL }, { "generic-mmc", "TOSHIBA", "DVD-ROM SD-R1202", 0, NULL }, { "generic-mmc", "TRAXDATA", "241040", 0, NULL }, { "generic-mmc", "TRAXDATA", "CDRW4260", 0, NULL }, { "generic-mmc", "WAITEC", "WT624", OPT_MMC_NO_SUBCHAN, NULL }, { "generic-mmc", "YAMAHA", "CDR200", 0, NULL }, { "generic-mmc", "YAMAHA", "CDR400", 0, NULL }, { "generic-mmc", "YAMAHA", "CRW2100", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "YAMAHA", "CRW2200", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "YAMAHA", "CRW2260", 0, NULL }, { "generic-mmc", "YAMAHA", "CRW3200", OPT_MMC_CD_TEXT, NULL }, { "generic-mmc", "YAMAHA", "CRW4001", 0, NULL }, { "generic-mmc", "YAMAHA", "CRW4260", 0, NULL }, { "generic-mmc", "YAMAHA", "CRW4416", 0, NULL }, { "generic-mmc", "YAMAHA", "CRW6416", 0, NULL }, { "generic-mmc", "YAMAHA", "CRW8424", 0, NULL }, { "generic-mmc", "YAMAHA", "CRW8824", 0, NULL }, { "generic-mmc", "_NEC", "NR-7700A", 0, NULL }, { "generic-mmc-raw", "ACER", "10x8x32", 0, NULL }, { "generic-mmc-raw", "ACER", "2010A", 0, NULL }, { "generic-mmc-raw", "ACER", "20x10x40", 0, NULL }, { "generic-mmc-raw", "ACER", "4406EU", 0, NULL }, { "generic-mmc-raw", "ACER", "4x4x6", 0, NULL }, { "generic-mmc-raw", "ACER", "8X4X32", 0, NULL }, { "generic-mmc-raw", "ACER", "CD-R/RW 4X4X32", OPT_MMC_NO_SUBCHAN, NULL }, { "generic-mmc-raw", "AOPEN", "CD-RW CRW3248", 0, NULL }, { "generic-mmc-raw", "AOPEN", "CRW1232", 0, NULL }, { "generic-mmc-raw", "ARTEC", "RW241040", 0, NULL }, { "generic-mmc-raw", "ARTEC", "WRA-WA48", 0, NULL }, { "generic-mmc-raw", "ARTEC", "WRR-4048", 0, NULL }, { "generic-mmc-raw", "ASUS", "CRW-1610A", 0, NULL }, { "generic-mmc-raw", "ASUS", "CRW-3212A", 0, NULL }, { "generic-mmc-raw", "ATAPI", "CD-R/RW 12X8X32", 0, NULL }, { "generic-mmc-raw", "ATAPI", "CD-R/RW 4X4X32", 0, NULL }, { "generic-mmc-raw", "ATAPI", "CD-R/RW CRW6206A", 0, NULL }, { "generic-mmc-raw", "BENQ", "CRW2410A", 0, NULL }, { "generic-mmc-raw", "BTC", "BCE1610IM", 0, NULL }, { "generic-mmc-raw", "BTC", "BCE2410IM", 0, NULL }, { "generic-mmc-raw", "BTC", "BCE621E", 0, NULL }, { "generic-mmc-raw", "CyberDrv", "CW018D", 0, NULL }, { "generic-mmc-raw", "CyberDrv", "CW038D", 0, NULL }, { "generic-mmc-raw", "CyberDrv", "CW058D", 0, NULL }, { "generic-mmc-raw", "Goldstar", "8120B", 0, NULL }, { "generic-mmc-raw", "HL-DT-ST", "CD-RW GCE-8160B", 0, NULL }, { "generic-mmc-raw", "HL-DT-ST", "CD-RW GCE-8320B", 0, NULL }, { "generic-mmc-raw", "HP", "CD-Writer+ 7100", 0, NULL }, { "generic-mmc-raw", "HP", "CD-Writer+ 7200", 0, NULL }, { "generic-mmc-raw", "HP", "DVD Writer 200j", 0, NULL }, { "generic-mmc-raw", "IDE-CD", "R/RW 2x2x24", 0, NULL }, { "generic-mmc-raw", "IDE-CD", "R/RW 4x4x24", 0, NULL }, { "generic-mmc-raw", "IDE-CD", "R/RW 4x4x32", 0, NULL }, { "generic-mmc-raw", "IDE-CD", "R/RW 8x4x32", 0, NULL }, { "generic-mmc-raw", "IDE-CD", "ReWritable-2x2x6", 0, NULL }, { "generic-mmc-raw", "IOMEGA", "ZIPCD 4x650", 0, NULL }, { "generic-mmc-raw", "LITE-ON", "LTR-12101B", 0, NULL }, { "generic-mmc-raw", "LITE-ON", "LTR-16101B", 0, NULL }, { "generic-mmc-raw", "LITE-ON", "LTR-16102C", 0, NULL }, { "generic-mmc-raw", "LITE-ON", "LTR-32123S", 0, NULL }, { "generic-mmc-raw", "LITE-ON", "LTR-40125S", 0, NULL }, { "generic-mmc-raw", "LITE-ON", "LTR-48125W", 0, NULL }, { "generic-mmc-raw", "MEMOREX", "CDRW-2216", 0, NULL }, { "generic-mmc-raw", "MEMOREX", "CR-622", 0, NULL }, { "generic-mmc-raw", "MEMOREX", "CRW-1662", 0, NULL }, { "generic-mmc-raw", "MITSUMI", "2801", 0, NULL }, { "generic-mmc-raw", "MITSUMI", "CR-4802", 0, NULL }, { "generic-mmc-raw", "MITSUMI", "CR-4804", 0, NULL }, { "generic-mmc-raw", "OTI", "-975 SOCRATES", 0, NULL }, { "generic-mmc-raw", "PHILIPS", "CDD 3801/31", 0, NULL }, { "generic-mmc-raw", "PHILIPS", "CDD3600", 0, NULL }, { "generic-mmc-raw", "PHILIPS", "CDD3610", 0, NULL }, { "generic-mmc-raw", "PHILIPS", "CDD4201", OPT_MMC_NO_SUBCHAN, NULL }, { "generic-mmc-raw", "PHILIPS", "CDD4801", 0, NULL }, { "generic-mmc-raw", "PHILIPS", "CDRW400", 0, NULL }, { "generic-mmc-raw", "PHILIPS", "PCRW1208", 0, NULL }, { "generic-mmc-raw", "PHILIPS", "PCRW120899", 0, NULL }, { "generic-mmc-raw", "PHILIPS", "PCRW404", 0, NULL }, { "generic-mmc-raw", "PHILIPS", "PCRW804", 0, NULL }, { "generic-mmc-raw", "QPS", "CRD-BP 1500P", 0, NULL }, { "generic-mmc-raw", "SAMSUNG", "CD-R/RW SW-204B", 0, NULL }, { "generic-mmc-raw", "SAMSUNG", "CD-R/RW SW-208", 0, NULL }, { "generic-mmc-raw", "SAMSUNG", "CD-R/RW SW-212B", 0, NULL }, { "generic-mmc-raw", "SAMSUNG", "CD-R/RW SW-224", 0, NULL }, { "generic-mmc-raw", "SAMSUNG", "SW-232", 0, NULL }, { "generic-mmc-raw", "SONY", "CRX195E1", 0, NULL }, { "generic-mmc-raw", "TEAC", "CD-W58E", OPT_MMC_USE_PQ|OPT_MMC_PQ_BCD, NULL }, { "generic-mmc-raw", "TOSHIBA", "R/RW 4x4x24", 0, NULL }, { "generic-mmc-raw", "TRAXDATA", "2832", 0, NULL }, { "generic-mmc-raw", "TRAXDATA", "CDRW2260+", 0, NULL }, { "generic-mmc-raw", "TRAXDATA", "CRW2260 PRO", 0, NULL }, { "generic-mmc-raw", "WAITEC", "WT2444EI", 0, NULL }, { "generic-mmc-raw", "WAITEC", "WT4424", 0, NULL }, { "generic-mmc-raw", "_NEC", "7900", 0, NULL }, { "generic-mmc-raw", "_NEC", "NR-7800A", 0, NULL }, { "ricoh-mp6200", "AOPEN", "CRW620", 0, NULL }, { "ricoh-mp6200", "MEMOREX", "CRW620", 0, NULL }, { "ricoh-mp6200", "PHILIPS", "OMNIWRITER26", 0, NULL }, { "ricoh-mp6200", "RICOH", "MP6200", 0, NULL }, { "ricoh-mp6200", "RICOH", "MP6201", 0, NULL }, { "sony-cdu920", "SONY", "CD-R CDU920", 0, NULL }, { "sony-cdu920", "SONY", "CD-R CDU924", 0, NULL }, { "sony-cdu948", "SONY", "CD-R CDU948", 0, NULL }, { "taiyo-yuden", "T.YUDEN", "CD-WO EW-50", 0, NULL }, { "teac-cdr55", "JVC", "R2626", 0, NULL }, { "teac-cdr55", "JVC", "XR-W2010", 0, NULL }, { "teac-cdr55", "SAF", "CD-R2006PLUS", 0, NULL }, { "teac-cdr55", "SAF", "CD-R4012", 0, NULL }, { "teac-cdr55", "SAF", "CD-RW 226", 0, NULL }, { "teac-cdr55", "TEAC", "CD-R50", 0, NULL }, { "teac-cdr55", "TEAC", "CD-R55", 0, NULL }, { "teac-cdr55", "TRAXDATA", "CDR4120", 0, NULL }, { "toshiba", "TOSHIBA", "DVD-ROM SD-R2002", 0, NULL }, { "toshiba", "TOSHIBA", "DVD-ROM SD-R2102", 0, NULL }, { "yamaha-cdr10x", "YAMAHA", "CDR100", 0, NULL }, { "yamaha-cdr10x", "YAMAHA", "CDR102", 0, NULL }, { NULL, NULL, NULL, 0, NULL }}; static DriverTable DRIVERS[] = { { "cdd2600", &CDD2600::instance }, { "generic-mmc", &GenericMMC::instance }, { "generic-mmc-raw", &GenericMMCraw::instance }, { "plextor", &PlextorReader::instance }, { "plextor-scan", &PlextorReaderScan::instance }, { "ricoh-mp6200", &RicohMP6200::instance }, { "sony-cdu920", &SonyCDU920::instance }, { "sony-cdu948", &SonyCDU948::instance }, { "taiyo-yuden", &TaiyoYuden::instance }, { "teac-cdr55", &TeacCdr55::instance }, { "toshiba", &ToshibaReader::instance }, { "yamaha-cdr10x", &YamahaCDR10x::instance }, { NULL, NULL }}; struct CDRVendorTable { char m1, s1, f1; // 1st vendor code char m2, s2, f2; // 2nd vendor code const char *id; // vendor ID }; static CDRVendorTable VENDOR_TABLE[] = { // permanent codes { 97,28,30, 97,46,50, "Auvistar Industry Co.,Ltd." }, { 97,26,60, 97,46,60, "CMC Magnetics Corporation" }, { 97,23,10, 0,0,0, "Doremi Media Co., Ltd." }, { 97,26,00, 97,45,00, "FORNET INTERNATIONAL PTE LTD." }, { 97,46,40, 97,46,40, "FUJI Photo Film Co., Ltd." }, { 97,26,40, 0,0,0, "FUJI Photo Film Co., Ltd." }, { 97,28,10, 97,49,10, "GIGASTORAGE CORPORATION" }, { 97,25,20, 97,47,10, "Hitachi Maxell, Ltd." }, { 97,27,40, 97,48,10, "Kodak Japan Limited" }, { 97,26,50, 97,48,60, "Lead Data Inc." }, { 97,27,50, 97,48,50, "Mitsui Chemicals, Inc." }, { 97,34,20, 97,50,20, "Mitsubishi Chemical Corporation" }, { 97,28,20, 97,46,20, "Multi Media Masters & Machinary SA" }, { 97,21,40, 0,0,0, "Optical Disc Manufacturing Equipment" }, { 97,27,30, 97,48,30, "Pioneer Video Corporation" }, { 97,27,10, 97,48,20, "Plasmon Data systems Ltd." }, { 97,26,10, 97,47,40, "POSTECH Corporation" }, { 97,27,20, 97,47,20, "Princo Corporation" }, { 97,32,10, 0,0,0, "Prodisc Technology Inc." }, { 97,27,60, 97,48,00, "Ricoh Company Limited" }, { 97,31,00, 97,47,50, "Ritek Co." }, { 97,26,20, 0,0,0, "SKC Co., Ltd." }, { 97,24,10, 0,0,0, "SONY Corporation" }, { 97,24,00, 97,46,00, "Taiyo Yuden Company Limited" }, { 97,32,00, 97,49,00, "TDK Corporation" }, { 97,25,60, 97,45,60, "Xcitek Inc." }, // tentative codes { 97,22,60, 97,45,20, "Acer Media Technology, Inc" }, { 97,25,50, 0,0,0, "AMS Technology Inc." }, { 97,23,30, 0,0,0, "AUDIO DISTRIBUTORS CO., LTD." }, { 97,21,30, 0,0,0, "Bestdisc Technology Corporation" }, { 97,30,10, 97,50,30, "CDA Datentraeger Albrechts GmbH" }, { 97,22,40, 97,45,40, "CIS Technology Inc." }, { 97,24,20, 97,46,30, "Computer Support Italy s.r.l." }, { 97,23,60, 0,0,0, "Customer Pressing Oosterhout" }, { 97,28,50, 0,0,0, "DELPHI TECHNOLOGY INC." }, { 97,27,00, 97,48,40, "DIGITAL STORAGE TECHNOLOGY CO.,LTD" }, { 97,22,30, 0,0,0, "EXIMPO" }, { 97,28,60, 0,0,0, "Friendly CD-Tek Co." }, { 97,31,30, 97,51,10, "Grand Advance Technology Ltd." }, { 97,29,50, 0,0,0, "General Magnetics Ld" }, { 97,24,50, 97,45,50, "Guann Yinn Co.,Ltd." }, { 97,29,00, 0,0,0, "Harmonic Hall Optical Disc Ltd." }, { 97,29,30, 97,51,50, "Hile Optical Disc Technology Corp." }, { 97,46,10, 97,22,50, "Hong Kong Digital Technology Co., Ltd." }, { 97,25,30, 97,51,20, "INFODISC Technology Co., Ltd." }, { 97,24,40, 0,0,0, "kdg mediatech AG" }, { 97,28,40, 97,49,20, "King Pro Mediatek Inc." }, { 97,23,00, 97,49,60, "Matsushita Electric Industrial Co., Ltd." }, { 97,15,20, 0,0,0, "Mitsubishi Chemical Corporation" }, { 97,25,00, 0,0,0, "MPO" }, { 97,23,20, 0,0,0, "Nacar Media sr" }, { 97,26,30, 0,0,0, "OPTICAL DISC CORPRATION" }, { 97,28,00, 97,49,30, "Opti.Me.S. S.p.A." }, { 97,23,50, 0,0,0, "OPTROM.INC." }, { 97,47,60, 0,0,0, "Prodisc Technology Inc." }, { 97,15,10, 0,0,0, "Ritek Co." }, { 97,22,10, 0,0,0, "Seantram Technology Inc." }, { 97,21,50, 0,0,0, "Sound Sound Multi-Media Development Limited" }, { 97,29,00, 0,0,0, "Taeil Media Co.,Ltd." }, { 97,18,60, 0,0,0, "TAROKO INTERNATIONAL CO.,LTD." }, { 97,15,00, 0,0,0, "TDK Corporation." }, { 97,29,20, 0,0,0, "UNIDISC TECHNOLOGY CO.,LTD" }, { 97,24,30, 97,45,10, "UNITECH JAPAN INC." }, { 97,29,10, 97,50,10, "Vanguard Disc Inc." }, { 97,49,40, 97,23,40, "VICTOR COMPANY OF JAPAN, LIMITED" }, { 97,29,40, 0,0,0, "VIVA MAGNETICS LIMITED" }, { 97,25,40, 0,0,0, "VIVASTAR AG" }, { 97,18,10, 0,0,0, "WEALTH FAIR INVESTMENT LIMITED" }, { 97,22,00, 0,0,0, "Woongjin Media corp." }, { 0, 0, 0, 0, 0, 0, NULL} }; unsigned char CdrDriver::syncPattern[12] = { 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0 }; char CdrDriver::REMOTE_MSG_SYNC_[4] = { 0xff, 0x00, 0xff, 0x00 }; /* Maps a string to the corresponding driver option value * Return: 0: string is not a valid driver option sting * else: option value for string */ static unsigned long string2DriverOption(const char *s) { if (strcmp(s, "OPT_DRV_GET_TOC_GENERIC") == 0) return OPT_DRV_GET_TOC_GENERIC; else if (strcmp(s, "OPT_DRV_SWAP_READ_SAMPLES") == 0) return OPT_DRV_SWAP_READ_SAMPLES; else if (strcmp(s, "OPT_DRV_NO_PREGAP_READ") == 0) return OPT_DRV_NO_PREGAP_READ; else if (strcmp(s, "OPT_DRV_RAW_TOC_BCD") == 0) return OPT_DRV_RAW_TOC_BCD; else if (strcmp(s, "OPT_DRV_RAW_TOC_HEX") == 0) return OPT_DRV_RAW_TOC_HEX; else if (strcmp(s, "OPT_MMC_USE_PQ") == 0) return OPT_MMC_USE_PQ; else if (strcmp(s, "OPT_MMC_PQ_BCD") == 0) return OPT_MMC_PQ_BCD; else if (strcmp(s, "OPT_MMC_READ_ISRC") == 0) return OPT_MMC_READ_ISRC; else if (strcmp(s, "OPT_MMC_SCAN_MCN") == 0) return OPT_MMC_SCAN_MCN; else if (strcmp(s, "OPT_MMC_CD_TEXT") == 0) return OPT_MMC_CD_TEXT; else if (strcmp(s, "OPT_MMC_NO_SUBCHAN") == 0) return OPT_MMC_NO_SUBCHAN; else if (strcmp(s, "OPT_MMC_NO_BURNPROOF") == 0) return OPT_MMC_NO_BURNPROOF; else if (strcmp(s, "OPT_MMC_NO_RW_PACKED") == 0) return OPT_MMC_NO_RW_PACKED; else if (strcmp(s, "OPT_MMC_USE_RAW_RW") == 0) return OPT_MMC_USE_RAW_RW; else if (strcmp(s, "OPT_MMC_YAMAHA_FORCE_SPEED") == 0) return OPT_MMC_YAMAHA_FORCE_SPEED; else if (strcmp(s, "OPT_PLEX_USE_PARANOIA") == 0) return OPT_PLEX_USE_PARANOIA; else if (strcmp(s, "OPT_PLEX_DAE_READ10") == 0) return OPT_PLEX_DAE_READ10; else if (strcmp(s, "OPT_PLEX_DAE_D4_12") == 0) return OPT_PLEX_DAE_D4_12; else if (strcmp(s, "OPT_PLEX_USE_PQ") == 0) return OPT_PLEX_USE_PQ; else if (strcmp(s, "OPT_PLEX_PQ_BCD") == 0) return OPT_PLEX_PQ_BCD; else if (strcmp(s, "OPT_PLEX_READ_ISRC") == 0) return OPT_PLEX_READ_ISRC; else return 0; } /* Checks if 'n' is a valid driver name and returns the driver id string * from the 'DRIVERS' on success. * Return: driver id string * NULL: if 'n' is not a valid driver id */ static const char *checkDriverName(const char *n) { DriverTable *run = DRIVERS; while (run->driverId != NULL) { if (strcmp(run->driverId, n) == 0) return run->driverId; run++; } return NULL; } /* Reads driver table from specified file. Return: 0: OK, driver table file could be opened 1: could not open driver table file */ #define MAX_DRIVER_TABLE_LINE_LEN 1024 static int readDriverTable(const char *filename) { FILE *fp; DriverSelectTable *ent; long i; int lineNr = 0; int count = 0; int rw; int err; char buf[MAX_DRIVER_TABLE_LINE_LEN]; char *p, *l; char *sep = "|"; char *vendor; char *model; char *driver; const char *lastDriverName = NULL; const char *driverName; unsigned long opt, options; if ((fp = fopen(filename, "r")) == NULL) return 1; message(4, "Reading driver table from file \"%s\".", filename); while (fgets(buf, MAX_DRIVER_TABLE_LINE_LEN, fp) != NULL) { lineNr++; vendor = model = driver = NULL; rw = 0; options = 0; err = 0; // remove comment if ((p = strchr(buf, '#')) != NULL) *p = 0; // remove leading white space for (l = buf; *l != 0 && isspace(*l); l++) ; // remove trailing white space for (i = strlen(l) - 1; i >= 0 && isspace(l[i]); i--) l[i] = 0; if ((p = strtok(l, sep)) != NULL) { if (strcmp(p, "R") == 0) { rw = 1; } else if (strcmp(p, "W") == 0) { rw = 2; } else { message(-1, "%s:%d: Expecting 'R' or 'W' as first token - line ignored.", filename, lineNr); } if (rw > 0) { if ((p = strtok(NULL, sep)) != NULL) { vendor = strdupCC(p); if ((p = strtok(NULL, sep)) != NULL) { model = strdupCC(p); if ((p = strtok(NULL, sep)) != NULL) { driver = strdupCC(p); if (lastDriverName == NULL || strcmp(lastDriverName, driver) != 0) { if ((driverName = checkDriverName(driver)) == NULL) { message(-1, "%s:%d: Driver '%s' not defined - line ignored.", filename, lineNr, driver); err = 1; } else { lastDriverName = driverName; } } while (!err && (p = strtok(NULL, sep)) != NULL) { if ((opt = string2DriverOption(p)) == 0) { message(-1, "%s:%d: Driver option string '%s' not defined - line ignored.", filename, lineNr, p); err = 1; } options |= opt; } if (!err) { ent = new DriverSelectTable; ent->vendor = vendor; vendor = NULL; ent->model = model; model = NULL; ent->driverId = driver; driver = NULL; ent->options = options; if (rw == 1) { ent->next = READ_DRIVER_TABLE; READ_DRIVER_TABLE = ent; } else { ent->next = WRITE_DRIVER_TABLE; WRITE_DRIVER_TABLE = ent; } count++; } } else { message(-1, "%s:%d: Missing driver name - line ignored.", filename, lineNr); } } else { message(-1, "%s:%d: Missing model name - line ignored.", filename, lineNr); } } else { message(-1, "%s:%d: Missing vendor name - line ignored.", filename, lineNr); } delete[] vendor; delete[] model; delete[] driver; } } } fclose(fp); message(4, "Found %d valid driver table entries.", count); return 0; } /* Create driver tables from built-in driver table data. */ static void createDriverTable() { DriverSelectTable *run; DriverSelectTable *ent; for (run = BUILTIN_READ_DRIVER_TABLE; run->driverId != NULL; run++) { ent = new DriverSelectTable; ent->driverId = strdupCC(run->driverId); ent->vendor = strdupCC(run->vendor); ent->model = strdupCC(run->model); ent->options = run->options; ent->next = READ_DRIVER_TABLE; READ_DRIVER_TABLE = ent; } for (run = BUILTIN_WRITE_DRIVER_TABLE; run->driverId != NULL; run++) { ent = new DriverSelectTable; ent->driverId = strdupCC(run->driverId); ent->vendor = strdupCC(run->vendor); ent->model = strdupCC(run->model); ent->options = run->options; ent->next = WRITE_DRIVER_TABLE; WRITE_DRIVER_TABLE = ent; } } /* Initialize driver table. First try to read the driver table from file * 'DRIVER_TABLE_FILE'. If it does not exist the built-in driver table data * will be used. After that an user specific driver table file is loaded if * available. */ static void initDriverTable() { static int initialized = 0; const char *home; char *path; if (initialized) return; if (readDriverTable(DRIVER_TABLE_FILE) != 0) { message(-1, "Cannot read driver table from file \"%s\" - using built-in table.", DRIVER_TABLE_FILE); createDriverTable(); } /* read driver table from $HOME/.cdrdao-drivers */ if ((home = getenv("HOME")) != NULL) { path = strdup3CC(home, "/.cdrdao-drivers", NULL); readDriverTable(path); delete[] path; } initialized = 1; } const char *CdrDriver::selectDriver(int readWrite, const char *vendor, const char *model, unsigned long *options) { DriverSelectTable *run; DriverSelectTable *match = NULL; unsigned int matchLen = 0; unsigned int len = 0; initDriverTable(); run = (readWrite == 0) ? READ_DRIVER_TABLE : WRITE_DRIVER_TABLE; while (run != NULL) { if (strcmp(run->vendor, vendor) == 0 && strstr(model, run->model) != NULL) { if (match == NULL || (len = strlen(run->model)) > matchLen) { matchLen = (match == NULL) ? strlen(run->model) : len; match = run; } } run = run->next; } if (match != NULL) { *options = match->options; return match->driverId; } return NULL; } CdrDriver *CdrDriver::createDriver(const char *driverId, unsigned long options, ScsiIf *scsiIf) { DriverTable *run = DRIVERS; while (run->driverId != NULL) { if (strcmp(run->driverId, driverId) == 0) return run->constructor(scsiIf, options); run++; } return NULL; } const char *CdrDriver::detectDriver(ScsiIf *scsiIf, unsigned long *options) { bool cd_r_read, cd_r_write, cd_rw_read, cd_rw_write; if (scsiIf->checkMmc(&cd_r_read, &cd_r_write, &cd_rw_read, &cd_rw_write)) { return "generic-mmc"; } return NULL; } void CdrDriver::printDriverIds() { DriverTable *run = DRIVERS; while (run->driverId != NULL) { message(0, "%s", run->driverId); run++; } } CdrDriver::CdrDriver(ScsiIf *scsiIf, unsigned long options) { size16 byteOrderTest = 1; char *byteOrderTestP = (char*)&byteOrderTest; options_ = options; scsiIf_ = scsiIf; toc_ = NULL; if (*byteOrderTestP == 1) hostByteOrder_ = 0; // little endian else hostByteOrder_ = 1; // big endian enableBufferUnderRunProtection_ = 1; enableWriteSpeedControl_ = 1; readCapabilities_ = 0; // reading capabilities are determined dynamically audioDataByteOrder_ = 0; // default to little endian fastTocReading_ = 0; rawDataReading_ = 0; mode2Mixed_ = 1; subChanReadMode_ = TrackData::SUBCHAN_NONE; taoSource_ = 0; taoSourceAdjust_ = 2; // usually we have 2 unreadable sectors between tracks // written in TAO mode padFirstPregap_ = 1; onTheFly_ = 0; onTheFlyFd_ = -1; multiSession_ = 0; encodingMode_ = 0; force_ = 0; remote_ = 0; remoteFd_ = -1; blockLength_ = 0; blocksPerWrite_ = 0; zeroBuffer_ = NULL; userCapacity_ = 0; fullBurn_ = 0; scsiMaxDataLen_ = scsiIf_->maxDataLen(); transferBuffer_ = new unsigned char[scsiMaxDataLen_]; maxScannedSubChannels_ = scsiMaxDataLen_ / (AUDIO_BLOCK_LEN + PW_SUBCHANNEL_LEN); scannedSubChannels_ = new SubChannel*[maxScannedSubChannels_]; paranoia_ = NULL; paranoiaDrive_ = NULL; paranoiaMode(3); // full paranoia but allow skip } CdrDriver::~CdrDriver() { toc_ = NULL; delete[] zeroBuffer_; zeroBuffer_ = NULL; delete[] transferBuffer_; transferBuffer_ = NULL; delete [] scannedSubChannels_; scannedSubChannels_ = NULL; } // Sets multi session mode. 0: close session, 1: open next session // Return: 0: OK // 1: multi session not supported by driver int CdrDriver::multiSession(int m) { multiSession_ = m != 0 ? 1 : 0; return 0; } // Sets number of adjust sectors for reading TAO source disks. void CdrDriver::taoSourceAdjust(int val) { if (val >= 0 && val < 100) { taoSourceAdjust_ = val; } } void CdrDriver::onTheFly(int fd) { if (fd >= 0) { onTheFly_ = 1; onTheFlyFd_ = fd; } else { onTheFly_ = 0; onTheFlyFd_ = -1; } } void CdrDriver::remote(int f, int fd) { if (f != 0 && fd >= 0) { int flags; remote_ = 1; remoteFd_ = fd; // switch 'fd' to non blocking IO mode if ((flags = fcntl(fd, F_GETFL)) == -1) { message(-1, "Cannot get flags of remote stream: %s", strerror(errno)); remote_ = 0; remoteFd_ = -1; return; } flags |= O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) < 0) { message(-1, "Cannot set flags of remote stream: %s", strerror(errno)); remote_ = 0; remoteFd_ = -1; } } else { remote_ = 0; remoteFd_ = -1; } } // Returns acceptable sub-channel encoding mode for given sub-channel type: // -1: writing of sub-channel type not supported at all // 0: accepts plain data without encoding // 1: accepts only completely encoded data int CdrDriver::subChannelEncodingMode(TrackData::SubChannelMode sm) const { if (sm == TrackData::SUBCHAN_NONE) return 0; else return -1; } int CdrDriver::cdrVendor(Msf &code, const char **vendorId, const char **mediumType) { CDRVendorTable *run = VENDOR_TABLE; *vendorId = NULL; char m = code.min(); char s = code.sec(); char f = code.frac(); char type = f % 10; f -= type; while (run->id != NULL) { if ((run->m1 == m && run->s1 == s && run->f1 == f) || (run->m2 == m && run->s2 == s && run->f2 == f)) { *vendorId = run->id; break; } run++; } if (*vendorId != NULL) { if (type < 5) { *mediumType = "Long Strategy Type, e.g. Cyanine"; } else { *mediumType = "Short Strategy Type, e.g. Phthalocyanine"; } return 1; } return 0; } // Sends SCSI command via 'scsiIf_'. // return: see 'ScsiIf::sendCmd()' int CdrDriver::sendCmd(const unsigned char *cmd, int cmdLen, const unsigned char *dataOut, int dataOutLen, unsigned char *dataIn, int dataInLen, int showErrorMsg) const { return scsiIf_->sendCmd(cmd, cmdLen, dataOut, dataOutLen, dataIn, dataInLen, showErrorMsg); } // checks if unit is ready // return: 0: OK // 1: scsi command failed // 2: not ready // 3: not ready, no disk in drive // 4: not ready, tray out int CdrDriver::testUnitReady(int ignoreUnitAttention) const { unsigned char cmd[6]; const unsigned char *sense; int senseLen; memset(cmd, 0, 6); switch (scsiIf_->sendCmd(cmd, 6, NULL, 0, NULL, 0, 0)) { case 1: return 1; case 2: sense = scsiIf_->getSense(senseLen); int code = sense[2] & 0x0f; if (code == 0x02) { // not ready return 2; } else if (code != 0x06) { scsiIf_->printError(); return 1; } else { return 0; } } return 0; } bool CdrDriver::rspeed(int a) { rspeed_ = a; return true; } int CdrDriver::speed2Mult(int speed) { return speed / 176; } int CdrDriver::mult2Speed(int mult) { return mult * 177; } // start unit ('startStop' == 1) or stop unit ('startStop' == 0) // return: 0: OK // 1: scsi command failed int CdrDriver::startStopUnit(int startStop) const { unsigned char cmd[6]; memset(cmd, 0, 6); cmd[0] = 0x1b; if (startStop != 0) { cmd[4] |= 0x01; } if (sendCmd(cmd, 6, NULL, 0, NULL, 0) != 0) { message(-2, "Cannot start/stop unit."); return 1; } return 0; } // blocks or unblocks tray // return: 0: OK // 1: scsi command failed int CdrDriver::preventMediumRemoval(int block) const { unsigned char cmd[6]; memset(cmd, 0, 6); cmd[0] = 0x1e; if (block != 0) { cmd[4] |= 0x01; } if (sendCmd(cmd, 6, NULL, 0, NULL, 0) != 0) { message(-2, "Cannot prevent/allow medium removal."); return 1; } return 0; } // reset device to initial state // return: 0: OK // 1: scsi command failed int CdrDriver::rezeroUnit(int showMessage) const { unsigned char cmd[6]; memset(cmd, 0, 6); cmd[0] = 0x01; if (sendCmd(cmd, 6, NULL, 0, NULL, 0, showMessage) != 0) { if (showMessage) message(-2, "Cannot rezero unit."); return 1; } return 0; } // Flushs cache of drive which inidcates end of write action. Errors resulting // from this command are ignored because everything is already done and // at most the last part of the lead-out track may be affected. // return: 0: OK int CdrDriver::flushCache() const { unsigned char cmd[10]; memset(cmd, 0, 10); cmd[0] = 0x35; // FLUSH CACHE // Print no message if the flush cache command fails because some drives // report errors even if all went OK. sendCmd(cmd, 10, NULL, 0, NULL, 0, 0); return 0; } // Reads the cd-rom capacity and stores the total number of available blocks // in 'length'. // return: 0: OK // 1: SCSI command failed int CdrDriver::readCapacity(long *length, int showMessage) { unsigned char cmd[10]; unsigned char data[8]; memset(cmd, 0, 10); memset(data, 0, 8); cmd[0] = 0x25; // READ CD-ROM CAPACITY if (sendCmd(cmd, 10, NULL, 0, data, 8, showMessage) != 0) { if (showMessage) message(-2, "Cannot read capacity."); return 1; } *length = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; // *length += 1; return 0; } int CdrDriver::blankDisk(BlankingMode) { message(-2, "Blanking is not supported by this driver."); return 1; } // Writes data to target, the block length depends on the actual writing mode // 'mode'. 'len' is number of blocks to write. // 'lba' specifies the next logical block address for writing and is updated // by this function. // return: 0: OK // 1: scsi command failed int CdrDriver::writeData(TrackData::Mode mode, TrackData::SubChannelMode sm, long &lba, const char *buf, long len) { assert(blocksPerWrite_ > 0); int writeLen = 0; unsigned char cmd[10]; long blockLength = blockSize(mode, sm); #if 0 long sum, i; sum = 0; for (i = 0; i < len * blockLength; i++) { sum += buf[i]; } message(0, "W: %ld: %ld, %ld, %ld", lba, blockLength, len, sum); #endif memset(cmd, 0, 10); cmd[0] = 0x2a; // WRITE1 while (len > 0) { writeLen = (len > blocksPerWrite_ ? blocksPerWrite_ : len); cmd[2] = lba >> 24; cmd[3] = lba >> 16; cmd[4] = lba >> 8; cmd[5] = lba; cmd[7] = writeLen >> 8; cmd[8] = writeLen & 0xff; if (sendCmd(cmd, 10, (unsigned char *)buf, writeLen * blockLength, NULL, 0) != 0) { message(-2, "Write data failed."); return 1; } buf += writeLen * blockLength; lba += writeLen; len -= writeLen; } return 0; } // Writes 'count' blocks with zero data with 'writeData'. // m: mode for encoding zero data // lba: logical block address for the write command, will be updated // encLba: logical block address used by the LE-C encoder for the // sector headers // count: number of zero blocks to write // Return: 0: OK // 1: SCSI error occured int CdrDriver::writeZeros(TrackData::Mode m, TrackData::SubChannelMode sm, long &lba, long encLba, long count) { assert(blocksPerWrite_ > 0); assert(zeroBuffer_ != NULL); int n, i; long cnt = 0; long total; long cntMb; long lastMb = 0; long blockLen; unsigned char *buf; blockLen = blockSize(m, sm); total = count * blockLen; #if 0 static int wcount = 0; char fname[100]; sprintf(fname, "zeros%d.out", wcount++); FILE *fp = fopen(fname, "w"); #endif while (count > 0) { n = (count > blocksPerWrite_ ? blocksPerWrite_ : count); buf = (unsigned char *)zeroBuffer_; for (i = 0; i < n; i++) { Track::encodeZeroData(encodingMode_, m, sm, encLba++, buf); if (encodingMode_ == 0 && bigEndianSamples() == 0) { // swap encoded data blocks swapSamples((Sample *)buf, SAMPLES_PER_BLOCK); } buf += blockLen; } //fwrite(zeroBuffer_, blockLen, n, fp); if (writeData(encodingMode_ == 0 ? TrackData::AUDIO : m, sm, lba, zeroBuffer_, n) != 0) { return 1; } cnt += n * blockLen; cntMb = cnt >> 20; if (cntMb > lastMb) { message(1, "Wrote %ld of %ld MB.\r", cntMb, total >> 20); fflush(stdout); lastMb = cntMb; } count -= n; } //fclose(fp); return 0; } // Requests mode page 'pageCode' from device and places it into given // buffer of maximum length 'bufLen'. // modePageHeader: if != NULL filled with mode page header (8 bytes) // blockDesc : if != NULL filled with block descriptor (8 bytes), // buffer is zeroed if no block descriptor is received // return: 0: OK // 1: scsi command failed // 2: buffer too small for requested mode page int CdrDriver::getModePage(int pageCode, unsigned char *buf, long bufLen, unsigned char *modePageHeader, unsigned char *blockDesc, int showErrorMsg) { unsigned char cmd[10]; long dataLen = bufLen + 8/*mode parameter header*/ + 100/*spare for block descriptors*/; unsigned char *data = new unsigned char[dataLen]; memset(cmd, 0, 10); memset(data, 0, dataLen); memset(buf, 0, bufLen); cmd[0] = 0x5a; // MODE SENSE cmd[2] = pageCode & 0x3f; cmd[7] = dataLen >> 8; cmd[8] = dataLen; if (sendCmd(cmd, 10, NULL, 0, data, dataLen, showErrorMsg) != 0) { delete[] data; return 1; } long modeDataLen = (data[0] << 8) | data[1]; long blockDescLen = (data[6] << 8) | data[7]; if (modePageHeader != NULL) memcpy(modePageHeader, data, 8); if (blockDesc != NULL) { if (blockDescLen >= 8) memcpy(blockDesc, data + 8, 8); else memset(blockDesc, 0, 8); } if (modeDataLen > blockDescLen + 6) { unsigned char *modePage = data + blockDescLen + 8; long modePageLen = modePage[1] + 2; if (modePageLen > bufLen) modePageLen = bufLen; memcpy(buf, modePage, modePageLen); delete[] data; return 0; } else { message(-2, "No mode page data received."); delete[] data; return 1; } } // Sets mode page in device specified in buffer 'modePage' // modePageHeader: if != NULL used as mode page header (8 bytes) // blockDesc : if != NULL used as block descriptor (8 bytes), // Return: 0: OK // 1: SCSI command failed int CdrDriver::setModePage(const unsigned char *modePage, const unsigned char *modePageHeader, const unsigned char *blockDesc, int showErrorMsg) { long pageLen = modePage[1] + 2; unsigned char cmd[10]; long dataLen = pageLen + 8/*mode parameter header*/; if (blockDesc != NULL) dataLen += 8; unsigned char *data = new unsigned char[dataLen]; memset(cmd, 0, 10); memset(data, 0, dataLen); if (modePageHeader != NULL) memcpy(data, modePageHeader, 8); data[0] = 0; data[1] = 0; data[4] = 0; data[5] = 0; if (blockDesc != NULL) { memcpy(data + 8, blockDesc, 8); memcpy(data + 16, modePage, pageLen); data[6] = 0; data[7] = 8; } else { memcpy(data + 8, modePage, pageLen); data[6] = 0; data[7] = 0; } cmd[0] = 0x55; // MODE SELECT cmd[1] = 1 << 4; cmd[7] = dataLen >> 8; cmd[8] = dataLen; if (sendCmd(cmd, 10, data, dataLen, NULL, 0, showErrorMsg) != 0) { delete[] data; return 1; } delete[] data; return 0; } // As above, but implemented with six byte mode commands // Requests mode page 'pageCode' from device and places it into given // buffer of maximum length 'bufLen'. // modePageHeader: if != NULL filled with mode page header (4 bytes) // blockDesc : if != NULL filled with block descriptor (8 bytes), // buffer is zeroed if no block descriptor is received // return: 0: OK // 1: scsi command failed // 2: buffer too small for requested mode page int CdrDriver::getModePage6(int pageCode, unsigned char *buf, long bufLen, unsigned char *modePageHeader, unsigned char *blockDesc, int showErrorMsg) { unsigned char cmd[6]; long dataLen = bufLen + 4/*mode parameter header*/ + 100/*spare for block descriptors*/; unsigned char *data = new unsigned char[dataLen]; memset(cmd, 0, 6); memset(data, 0, dataLen); memset(buf, 0, bufLen); cmd[0] = 0x1a; // MODE SENSE(6) cmd[2] = pageCode & 0x3f; cmd[4] = (dataLen > 255) ? 0 : dataLen; if (sendCmd(cmd, 6, NULL, 0, data, dataLen, showErrorMsg) != 0) { delete[] data; return 1; } long modeDataLen = data[0]; long blockDescLen = data[3]; if (modePageHeader != NULL) memcpy(modePageHeader, data, 4); if (blockDesc != NULL) { if (blockDescLen >= 8) memcpy(blockDesc, data + 4, 8); else memset(blockDesc, 0, 8); } if (modeDataLen > blockDescLen + 4) { unsigned char *modePage = data + blockDescLen + 4; long modePageLen = modePage[1] + 2; if (modePageLen > bufLen) modePageLen = bufLen; memcpy(buf, modePage, modePageLen); delete[] data; return 0; } else { message(-2, "No mode page data received."); delete[] data; return 1; } } // Sets mode page in device specified in buffer 'modePage' // modePageHeader: if != NULL used as mode page header (4 bytes) // blockDesc : if != NULL used as block descriptor (8 bytes), // Return: 0: OK // 1: SCSI command failed int CdrDriver::setModePage6(const unsigned char *modePage, const unsigned char *modePageHeader, const unsigned char *blockDesc, int showErrorMsg) { long pageLen = modePage[1] + 2; unsigned char cmd[6]; long dataLen = pageLen + 4/*mode parameter header*/; if (blockDesc != NULL) dataLen += 8; unsigned char *data = new unsigned char[dataLen]; memset(cmd, 0, 6); memset(data, 0, dataLen); if (modePageHeader != NULL) memcpy(data, modePageHeader, 4); data[0] = 0; if (blockDesc != NULL) { memcpy(data + 4, blockDesc, 8); memcpy(data + 12, modePage, pageLen); data[3] = 8; } else { memcpy(data + 4, modePage, pageLen); data[3] = 0; } cmd[0] = 0x15; // MODE SELECT(6) cmd[1] = 1 << 4; cmd[4] = dataLen; if (sendCmd(cmd, 6, data, dataLen, NULL, 0, showErrorMsg) != 0) { delete[] data; return 1; } delete[] data; return 0; } // Retrieves TOC data of inserted CD. It won't distinguish between different // sessions. // The track information is returend either for all sessions or for the // first session depending on the drive. 'cdTocLen' is filled with number // of entries including the lead-out track. // Return: 'NULL' on error, else array of 'CdToc' structures with '*cdTocLen' // entries CdToc *CdrDriver::getTocGeneric(int *cdTocLen) { unsigned char cmd[10]; unsigned short dataLen; unsigned char *data = NULL;; unsigned char reqData[4]; // buffer for requestion the actual length unsigned char *p = NULL; int i; CdToc *toc; int nTracks; // read disk toc length memset(cmd, 0, 10); cmd[0] = 0x43; // READ TOC cmd[6] = 0; // return info for all tracks cmd[8] = 4; if (sendCmd(cmd, 10, NULL, 0, reqData, 4) != 0) { message(-2, "Cannot read disk toc."); return NULL; } dataLen = (reqData[0] << 8) | reqData[1]; dataLen += 2; message(4, "getTocGeneric: data len %d", dataLen); if (dataLen < 12) { dataLen = (100 * 8) + 4; } data = new unsigned char[dataLen]; memset(data, 0, dataLen); // read disk toc cmd[7] = dataLen >> 8; cmd[8] = dataLen; if (sendCmd(cmd, 10, NULL, 0, data, dataLen) != 0) { message(-2, "Cannot read disk toc."); delete[] data; return NULL; } nTracks = data[3] - data[2] + 1; if (nTracks > 99) { message(-2, "Got illegal toc data."); delete[] data; return NULL; } toc = new CdToc[nTracks + 1]; for (i = 0, p = data + 4; i <= nTracks; i++, p += 8) { toc[i].track = p[2]; toc[i].adrCtl = p[1]; toc[i].start = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]; } *cdTocLen = nTracks + 1; delete[] data; return toc; } // Retrieves TOC data of inserted CD. The track information is returend for // specified session number only. The lead-out start is taken from the // correct session so that it can be used to calculate the length of the // last track. // 'cdTocLen' is filled with number of entries including the lead-out track. // Return: 'NULL' on error, else array of 'CdToc' structures with '*cdTocLen' // entries #define IS_BCD(v) (((v) & 0xf0) <= 0x90 && ((v) & 0x0f) <= 0x09) CdToc *CdrDriver::getToc(int sessionNr, int *cdTocLen) { int rawTocLen; int completeTocLen; CdToc *completeToc; // toc retrieved with generic method to verify with raw // toc data CdToc *cdToc; CdRawToc *rawToc; int i, j, tocEnt; int nTracks = 0; int trackNr; long trackStart; int isBcd = -1; int lastTrack; int min, sec, frame; if ((completeToc = getTocGeneric(&completeTocLen)) == NULL) return NULL; if (options_ & OPT_DRV_GET_TOC_GENERIC) { *cdTocLen = completeTocLen; return completeToc; } if ((rawToc = getRawToc(1, &rawTocLen)) == NULL) { *cdTocLen = completeTocLen; return completeToc; } // Try to determine if raw toc data contains BCD or HEX numbers. for (i = 0; i < rawTocLen; i++) { if ((rawToc[i].adrCtl & 0xf0) == 0x10) { // only process QMODE1 entries if (rawToc[i].point < 0xa0 && !IS_BCD(rawToc[i].point)) { isBcd = 0; } if (rawToc[i].point < 0xa0 || rawToc[i].point == 0xa2) { if (!IS_BCD(rawToc[i].pmin) || !IS_BCD(rawToc[i].psec) || !IS_BCD(rawToc[i].pframe)) { isBcd = 0; break; } } } } if (options_ & OPT_DRV_RAW_TOC_BCD) { if (isBcd == 0) { message(-2, "The driver option 0x%lx indicates that the raw TOC data", OPT_DRV_RAW_TOC_BCD); message(-2, "contains BCD values but a non BCD value was found."); message(-2, "Please adjust the driver options."); message(-1, "Using TOC data retrieved with generic method (no multi session support)."); delete[] rawToc; *cdTocLen = completeTocLen; return completeToc; } isBcd = 1; } else if (options_ & OPT_DRV_RAW_TOC_HEX) { isBcd = 0; } else { if (isBcd == -1) { // We still don't know if the values are BCD or HEX but we've ensured // so far that all values are valid BCD numbers. // Assume that we have BCD numbers and compare with the generic toc data. isBcd = 1; for (i = 0; i < rawTocLen && isBcd == 1; i++) { if ((rawToc[i].adrCtl & 0xf0) == 0x10 && // only process QMODE1 entries rawToc[i].point < 0xa0) { trackNr = SubChannel::bcd2int(rawToc[i].point); for (j = 0; j < completeTocLen; j++) { if (completeToc[j].track == trackNr) { break; } } if (j < completeTocLen) { min = SubChannel::bcd2int(rawToc[i].pmin); sec = SubChannel::bcd2int(rawToc[i].psec); frame = SubChannel::bcd2int(rawToc[i].pframe); if (min <= 99 && sec < 60 && frame < 75) { trackStart = Msf(min, sec, frame).lba() - 150; if (completeToc[j].start != trackStart) { // start does not match -> values are not BCD isBcd = 0; } } else { // bogus time code -> values are not BCD isBcd = 0; } } else { // track not found -> values are not BCD isBcd = 0; } } } if (isBcd == 1) { // verify last lead-out pointer trackStart = 0; // start of lead-out for (i = rawTocLen - 1; i >= 0; i--) { if ((rawToc[i].adrCtl & 0xf0) == 0x10 && // QMODE1 entry rawToc[i].point == 0xa2) { min = SubChannel::bcd2int(rawToc[i].pmin); sec = SubChannel::bcd2int(rawToc[i].psec); frame = SubChannel::bcd2int(rawToc[i].pframe); if (min <= 99 && sec < 60 && frame < 75) trackStart = Msf(min, sec, frame).lba() - 150; break; } } if (i < 0) { message(-1, "Found bogus toc data (no lead-out entry in raw data)."); message(-1, "Your drive probably does not support raw toc reading."); message(-1, "Using TOC data retrieved with generic method (no multi session support)."); message(-1, "Use driver option 0x%lx to suppress this message.", OPT_DRV_GET_TOC_GENERIC); delete[] rawToc; *cdTocLen = completeTocLen; return completeToc; } for (j = 0; j < completeTocLen; j++) { if (completeToc[j].track == 0xaa) { break; } } if (j < completeTocLen) { if (trackStart != completeToc[j].start) { // lead-out start does not match -> values are not BCD isBcd = 0; } } else { message(-2, "Found bogus toc data (no lead-out entry)."); delete[] completeToc; delete[] rawToc; return NULL; } } } if (isBcd == 0) { // verify that the decision is really correct. for (i = 0; i < rawTocLen && isBcd == 0; i++) { if ((rawToc[i].adrCtl & 0xf0) == 0x10 && // only process QMODE1 entries rawToc[i].point < 0xa0) { trackNr = rawToc[i].point; for (j = 0; j < completeTocLen; j++) { if (completeToc[j].track == trackNr) { break; } } if (j < completeTocLen) { min = rawToc[i].pmin; sec = rawToc[i].psec; frame = rawToc[i].pframe; if (min <= 99 && sec < 60 && frame < 75) { trackStart = Msf(min, sec, frame).lba() - 150; if (completeToc[j].start != trackStart) { // start does not match -> values are not HEX isBcd = -1; } } else { // bogus time code -> values are not HEX isBcd = -1; } } else { // track not found -> values are not BCD isBcd = -1; } } } // verify last lead-out pointer trackStart = 0; // start of lead-out for (i = rawTocLen - 1; i >= 0; i--) { if ((rawToc[i].adrCtl & 0xf0) == 0x10 && // QMODE1 entry rawToc[i].point == 0xa2) { min = rawToc[i].pmin; sec = rawToc[i].psec; frame = rawToc[i].pframe; if (min <= 99 && sec < 60 && frame < 75) trackStart = Msf(min, sec, frame).lba() - 150; break; } } if (i < 0) { message(-1, "Found bogus toc data (no lead-out entry in raw data)."); message(-1, "Your drive probably does not support raw toc reading."); message(-1, "Using TOC data retrieved with generic method (no multi session support)."); message(-1, "Use driver option 0x%lx to suppress this message.", OPT_DRV_GET_TOC_GENERIC); delete[] rawToc; *cdTocLen = completeTocLen; return completeToc; } for (j = 0; j < completeTocLen; j++) { if (completeToc[j].track == 0xaa) { break; } } if (j < completeTocLen) { if (trackStart != completeToc[j].start) { // lead-out start does not match -> values are not BCD isBcd = -1; } } else { message(-1, "Found bogus toc data (no lead-out entry)."); delete[] rawToc; delete[] completeToc; return NULL; } } if (isBcd == -1) { message(-1, "Could not determine if raw toc data is BCD or HEX. Please report!"); message(-1, "Using TOC data retrieved with generic method (no multi session support)."); message(-1, "Use driver option 0x%lx or 0x%lx to assume BCD or HEX data.", OPT_DRV_RAW_TOC_BCD, OPT_DRV_RAW_TOC_HEX); delete[] rawToc; *cdTocLen = completeTocLen; return completeToc; } } message(4, "Raw toc contains %s values.", isBcd == 0 ? "HEX" : "BCD"); for (i = 0; i < rawTocLen; i++) { if (rawToc[i].sessionNr == sessionNr && (rawToc[i].adrCtl & 0xf0) == 0x10 && /* QMODE1 entry */ rawToc[i].point < 0xa0) { nTracks++; } } if (nTracks == 0 || nTracks > 99) { message(-1, "Found bogus toc data (0 or > 99 tracks). Please report!"); message(-1, "Your drive probably does not support raw toc reading."); message(-1, "Using TOC data retrieved with generic method (no multi session support)."); message(-1, "Use driver option 0x%lx to suppress this message.", OPT_DRV_GET_TOC_GENERIC); delete[] rawToc; *cdTocLen = completeTocLen; return completeToc; } cdToc = new CdToc[nTracks + 1]; tocEnt = 0; lastTrack = -1; for (i = 0; i < rawTocLen; i++) { if (rawToc[i].sessionNr == sessionNr && (rawToc[i].adrCtl & 0xf0) == 0x10 && // QMODE1 entry rawToc[i].point < 0xa0) { if (isBcd) { trackNr = SubChannel::bcd2int(rawToc[i].point); trackStart = Msf(SubChannel::bcd2int(rawToc[i].pmin), SubChannel::bcd2int(rawToc[i].psec), SubChannel::bcd2int(rawToc[i].pframe)).lba(); } else { trackNr = rawToc[i].point; trackStart = Msf(rawToc[i].pmin, rawToc[i].psec, rawToc[i].pframe).lba(); } if (lastTrack != -1 && trackNr != lastTrack + 1) { message(-1, "Found bogus toc data (track number sequence). Please report!"); message(-1, "Your drive probably does not support raw toc reading."); message(-1, "Using TOC data retrieved with generic method (no multi session support)."); message(-1, "Use driver option 0x%lx to suppress this message.", OPT_DRV_GET_TOC_GENERIC); delete[] cdToc; delete[] rawToc; *cdTocLen = completeTocLen; return completeToc; } lastTrack = trackNr; cdToc[tocEnt].adrCtl = rawToc[i].adrCtl; cdToc[tocEnt].track = trackNr; cdToc[tocEnt].start = trackStart - 150; tocEnt++; } } // find lead-out pointer for (i = 0; i < rawTocLen; i++) { if (rawToc[i].sessionNr == sessionNr && (rawToc[i].adrCtl & 0xf0) == 0x10 && // QMODE1 entry rawToc[i].point == 0xa2 /* Lead-out pointer */) { if (isBcd) { trackStart = Msf(SubChannel::bcd2int(rawToc[i].pmin), SubChannel::bcd2int(rawToc[i].psec), SubChannel::bcd2int(rawToc[i].pframe)).lba(); } else { trackStart = Msf(rawToc[i].pmin, rawToc[i].psec, rawToc[i].pframe).lba(); } cdToc[tocEnt].adrCtl = rawToc[i].adrCtl; cdToc[tocEnt].track = 0xaa; cdToc[tocEnt].start = trackStart - 150; tocEnt++; break; } } if (tocEnt != nTracks + 1) { message(-1, "Found bogus toc data (no lead-out pointer for session). Please report!"); message(-1, "Your drive probably does not support raw toc reading."); message(-1, "Using TOC data retrieved with generic method (no multi session support)."); message(-1, "Use driver option 0x%lx to suppress this message.", OPT_DRV_GET_TOC_GENERIC); delete[] cdToc; delete[] rawToc; *cdTocLen = completeTocLen; return completeToc; } delete[] rawToc; delete[] completeToc; *cdTocLen = nTracks + 1; return cdToc; } static char *buildDataFileName(int trackNr, CdToc *toc, int nofTracks, const char *basename, const char *extension) { char buf[30]; int start, end; int run; int onlyOneAudioRange = 1; // don't modify the STDIN filename if (strcmp(basename, "-") == 0) return strdupCC(basename); if ((toc[trackNr].adrCtl & 0x04) != 0) { // data track sprintf(buf, "_%d", trackNr + 1); return strdup3CC(basename, buf, NULL); } // audio track, find continues range of audio tracks start = trackNr; while (start > 0 && (toc[start - 1].adrCtl & 0x04) == 0) start--; if (start > 0) { run = start - 1; while (run >= 0) { if ((toc[run].adrCtl & 0x04) == 0) { onlyOneAudioRange = 0; break; } run--; } } end = trackNr; while (end < nofTracks - 1 && (toc[end + 1].adrCtl & 0x04) == 0) end++; if (onlyOneAudioRange && end < nofTracks - 1) { run = end + 1; while (run < nofTracks) { if ((toc[run].adrCtl & 0x04) == 0) { onlyOneAudioRange = 0; break; } run++; } } if (onlyOneAudioRange) { return strdup3CC(basename, extension, NULL); } else { sprintf(buf, "_%d-%d", start + 1, end + 1); return strdup3CC(basename, buf, extension); } } /* Checks if drive's capabilites support the selected sub-channel reading mode for given track mode. mode: track mode caps: capabilities bits Return: 1: current sub-channel reading mode is supported 0: current sub-channel reading mode is not supported */ int CdrDriver::checkSubChanReadCaps(TrackData::Mode mode, unsigned long caps) { int ret = 0; switch (subChanReadMode_) { case TrackData::SUBCHAN_NONE: ret = 1; break; case TrackData::SUBCHAN_RW_RAW: if (mode == TrackData::AUDIO) { if ((caps & (CDR_READ_CAP_AUDIO_RW_RAW|CDR_READ_CAP_AUDIO_PW_RAW)) != 0) ret = 1; } else { if ((caps & CDR_READ_CAP_DATA_RW_RAW|CDR_READ_CAP_DATA_PW_RAW) != 0) ret = 1; } break; case TrackData::SUBCHAN_RW: if (mode == TrackData::AUDIO) { if ((caps & CDR_READ_CAP_AUDIO_RW_COOKED) != 0) ret = 1; } else { if ((caps & CDR_READ_CAP_DATA_RW_COOKED) != 0) ret = 1; } break; } return ret; } // Creates 'Toc' object for inserted CD. // session: session that should be analyzed // audioFilename: name of audio file that is placed into TOC // Return: newly allocated 'Toc' object or 'NULL' on error Toc *CdrDriver::readDiskToc(int session, const char *dataFilename) { int nofTracks = 0; int i, j; CdToc *cdToc = getToc(session, &nofTracks); Msf indexIncrements[98]; int indexIncrementCnt = 0; char isrcCode[13]; unsigned char trackCtl; // control nibbles of track int ctlCheckOk; char *fname; char *extension = NULL; char *p; TrackInfo *trackInfos; if (cdToc == NULL) { return NULL; } if (nofTracks <= 1) { message(-1, "No tracks on disk."); delete[] cdToc; return NULL; } message(1, ""); printCdToc(cdToc, nofTracks); message(1, ""); //return NULL; nofTracks -= 1; // do not count lead-out readCapabilities_ = getReadCapabilities(cdToc, nofTracks); fname = strdupCC(dataFilename); if ((p = strrchr(fname, '.')) != NULL) { extension = strdupCC(p); *p = 0; } trackInfos = new TrackInfo[nofTracks + 1]; memset(trackInfos, 0, (nofTracks + 1) * sizeof(TrackInfo)); for (i = 0; i < nofTracks; i++) { TrackData::Mode trackMode; if ((cdToc[i].adrCtl & 0x04) != 0) { if ((trackMode = getTrackMode(i + 1, cdToc[i].start)) == TrackData::MODE0) { message(-1, "Cannot determine mode of data track %d - asuming MODE1.", i + 1); trackMode = TrackData::MODE1; } if (rawDataReading_) { if (trackMode == TrackData::MODE1) { trackMode = TrackData::MODE1_RAW; } else if (trackMode == TrackData::MODE2) { trackMode = TrackData::MODE2_RAW; } else if (trackMode == TrackData::MODE2_FORM1 || trackMode == TrackData::MODE2_FORM2 || trackMode == TrackData::MODE2_FORM_MIX) { trackMode = TrackData::MODE2_RAW; } } else if (mode2Mixed_) { if (trackMode == TrackData::MODE2_FORM1 || trackMode == TrackData::MODE2_FORM2) { trackMode = TrackData::MODE2_FORM_MIX; } } } else { trackMode = TrackData::AUDIO; } if (!checkSubChanReadCaps(trackMode, readCapabilities_)) { message(-2, "This drive does not support %s sub-channel reading.", TrackData::subChannelMode2String(subChanReadMode_)); delete[] cdToc; delete[] trackInfos; delete[] fname; return NULL; } trackInfos[i].trackNr = cdToc[i].track; trackInfos[i].ctl = cdToc[i].adrCtl & 0x0f; trackInfos[i].mode = trackMode; trackInfos[i].start = cdToc[i].start; trackInfos[i].pregap = 0; trackInfos[i].fill = 0; trackInfos[i].indexCnt = 0; trackInfos[i].isrcCode[0] = 0; trackInfos[i].filename = buildDataFileName(i, cdToc, nofTracks, fname, extension); trackInfos[i].bytesWritten = 0; } // lead-out entry trackInfos[nofTracks].trackNr = 0xaa; trackInfos[nofTracks].ctl = 0; trackInfos[nofTracks].mode = trackInfos[nofTracks - 1].mode; trackInfos[nofTracks].start = cdToc[nofTracks].start; if (taoSource()) { trackInfos[nofTracks].start -= taoSourceAdjust_; } trackInfos[nofTracks].pregap = 0; trackInfos[nofTracks].fill = 0; trackInfos[nofTracks].indexCnt = 0; trackInfos[nofTracks].isrcCode[0] = 0; trackInfos[nofTracks].filename = NULL; trackInfos[nofTracks].bytesWritten = 0; long pregap = 0; long defaultPregap; long slba, elba; if (session == 1) { pregap = cdToc[0].start; // pre-gap of first track } for (i = 0; i < nofTracks; i++) { trackInfos[i].pregap = pregap; slba = trackInfos[i].start; elba = trackInfos[i + 1].start; defaultPregap = 0; if (taoSource()) { // assume always a pre-gap of 150 + # link blocks between two tracks // except between two audio tracks if ((trackInfos[i].mode != TrackData::AUDIO || trackInfos[i + 1].mode != TrackData::AUDIO) && i < nofTracks - 1) { defaultPregap = 150 + taoSourceAdjust_; } } else { // assume a pre-gap of 150 between tracks of different mode if (trackInfos[i].mode != trackInfos[i + 1].mode) { defaultPregap = 150; } } elba -= defaultPregap; Msf trackLength(elba - slba); message(1, "Analyzing track %02d (%s): start %s, ", i + 1, TrackData::mode2String(trackInfos[i].mode), Msf(cdToc[i].start).str()); message(1, "length %s...", trackLength.str()); if (pregap > 0) { message(2, "Found pre-gap: %s", Msf(pregap).str()); } isrcCode[0] = 0; indexIncrementCnt = 0; pregap = 0; trackCtl = 0; if (!fastTocReading_) { // Find index increments and pre-gap of next track if (trackInfos[i].mode == TrackData::AUDIO) { analyzeTrack(TrackData::AUDIO, i + 1, slba, elba, indexIncrements, &indexIncrementCnt, i < nofTracks - 1 ? &pregap : 0, isrcCode, &trackCtl); if (defaultPregap != 0) pregap = defaultPregap; } } else { if (trackInfos[i].mode == TrackData::AUDIO) { if (readIsrc(i + 1, isrcCode) != 0) { isrcCode[0] = 0; } } } if (pregap == 0) { pregap = defaultPregap; } if (isrcCode[0] != 0) { message(2, "Found ISRC code."); memcpy(trackInfos[i].isrcCode, isrcCode, 13); } for (j = 0; j < indexIncrementCnt; j++) trackInfos[i].index[j] = indexIncrements[j].lba(); trackInfos[i].indexCnt = indexIncrementCnt; if ((trackCtl & 0x80) != 0) { // Check track against TOC control nibbles ctlCheckOk = 1; if ((trackCtl & 0x01) != (cdToc[i].adrCtl & 0x01)) { message(-1, "Pre-emphasis flag of track differs from TOC - toc file contains TOC setting."); ctlCheckOk = 0; } if ((trackCtl & 0x08) != (cdToc[i].adrCtl & 0x08)) { message(-1, "2-/4-channel-audio flag of track differs from TOC - toc file contains TOC setting."); ctlCheckOk = 0; } if (ctlCheckOk) { message(2, "Control nibbles of track match CD-TOC settings."); } } } int padFirstPregap; if (onTheFly_) { if (session == 1 && (options_ & OPT_DRV_NO_PREGAP_READ) == 0) padFirstPregap = 0; else padFirstPregap = 1; } else { padFirstPregap = (session != 1) || padFirstPregap_; } Toc *toc = buildToc(trackInfos, nofTracks + 1, padFirstPregap); if (toc != NULL) { if ((options_ & OPT_DRV_NO_CDTEXT_READ) == 0) readCdTextData(toc); if (readCatalog(toc, trackInfos[0].start, trackInfos[nofTracks].start)) message(2, "Found disk catalogue number."); } // overwrite last time message message (1, " \t"); delete[] cdToc; delete[] trackInfos; delete[] fname; if (extension != NULL) delete[] extension; return toc; } // Implementation is based on binary search over all sectors of actual // track. ISRC codes are not extracted here. int CdrDriver::analyzeTrackSearch(TrackData::Mode, int trackNr, long startLba, long endLba, Msf *index, int *indexCnt, long *pregap, char *isrcCode, unsigned char *ctl) { isrcCode[0] = 0; *ctl = 0; if (pregap != NULL) { *pregap = findIndex(trackNr + 1, 0, startLba, endLba - 1); if (*pregap >= endLba) { *pregap = 0; } else if (*pregap > 0) { *pregap = endLba - *pregap; } } // check for index increments int ind = 2; long indexLba = startLba; *indexCnt = 0; do { if ((indexLba = findIndex(trackNr, ind, indexLba, endLba - 1)) > 0) { message(2, "Found index %d at %s", ind, Msf(indexLba).str()); if (*indexCnt < 98 && indexLba > startLba) { index[*indexCnt] = Msf(indexLba - startLba); *indexCnt += 1; } ind++; } } while (indexLba > 0 && indexLba < endLba); // Retrieve control nibbles of track, add 75 to track start so we // surely get a block of current track. int dummy, track; if (getTrackIndex(startLba + 75, &track, &dummy, ctl) == 0 && track == trackNr) { *ctl |= 0x80; } return 0; } int CdrDriver::getTrackIndex(long lba, int *trackNr, int *indexNr, unsigned char *ctl) { return 1; } // Scan from lba 'trackStart' to 'trackEnd' for the position at which the // index switchs to 'index' of track number 'track'. // return: lba of index start postion or 0 if index was not found long CdrDriver::findIndex(int track, int index, long trackStart, long trackEnd) { int actTrack; int actIndex; long start = trackStart; long end = trackEnd; long mid; //message(0, "findIndex: %ld - %ld", trackStart, trackEnd); while (start < end) { mid = start + ((end - start) / 2); //message(0, "Checking block %ld...", mid); if (getTrackIndex(mid, &actTrack, &actIndex, NULL) != 0) { return 0; } //message(0, "Found track %d, index %d", actTrack, actIndex); if ((actTrack < track || actIndex < index) && mid + 1 < trackEnd) { //message(0, " Checking block %ld...", mid + 1); if (getTrackIndex(mid + 1, &actTrack, &actIndex, NULL) != 0) { return 0; } //message(0, " Found track %d, index %d", actTrack, actIndex); if (actTrack == track && actIndex == index) { //message(0, "Found pregap at %ld", mid + 1); return mid; } else { start = mid + 1; } } else { end = mid; } } return 0; } int CdrDriver::analyzeTrackScan(TrackData::Mode, int trackNr, long startLba, long endLba, Msf *index, int *indexCnt, long *pregap, char *isrcCode, unsigned char *ctl) { SubChannel **subChannels; int n, i; int actIndex = 1; long length; long crcErrCnt = 0; long timeCnt = 0; int ctlSet = 0; int isrcCodeFound = 0; long trackStartLba = startLba; *isrcCode = 0; if (pregap != NULL) *pregap = 0; *indexCnt = 0; *ctl = 0; //startLba -= 75; if (startLba < 0) { startLba = 0; } length = endLba - startLba; while (length > 0) { n = (length > maxScannedSubChannels_) ? maxScannedSubChannels_ : length; if (readSubChannels(TrackData::SUBCHAN_NONE, startLba, n, &subChannels, NULL) != 0 || subChannels == NULL) { return 1; } for (i = 0; i < n; i++) { SubChannel *chan = subChannels[i]; //chan->print(); if (chan->checkCrc() && chan->checkConsistency()) { if (chan->type() == SubChannel::QMODE1DATA) { int t = chan->trackNr(); Msf time(chan->min(), chan->sec(), chan->frame()); // track rel time if (timeCnt > 74) { message(1, "%s\r", time.str()); timeCnt = 0; } if (t == trackNr && !ctlSet) { *ctl = chan->ctl(); *ctl |= 0x80; ctlSet = 1; } if (t == trackNr && chan->indexNr() == actIndex + 1) { actIndex = chan->indexNr(); message(2, "Found index %d at: %s", actIndex, time.str()); if ((*indexCnt) < 98) { index[*indexCnt] = time; *indexCnt += 1; } } else if (t == trackNr + 1) { if (chan->indexNr() == 0) { if (pregap != NULL) *pregap = time.lba(); if (crcErrCnt != 0) message(2, "Found %ld Q sub-channels with CRC errors.", crcErrCnt); return 0; } } } else if (chan->type() == SubChannel::QMODE3) { if (!isrcCodeFound && startLba > trackStartLba) { strcpy(isrcCode, chan->isrc()); isrcCodeFound = 1; } } } else { crcErrCnt++; #if 0 if (chan->type() == SubChannel::QMODE1DATA) { message(2, "Q sub-channel data at %02d:%02d:%02d failed CRC check - ignored", chan->min(), chan->sec(), chan->frame()); } else { message(2, "Q sub-channel data failed CRC check - ignored."); } chan->print(); #endif } timeCnt++; } length -= n; startLba += n; } if (crcErrCnt != 0) message(2, "Found %ld Q sub-channels with CRC errors.", crcErrCnt); return 0; } // Checks if toc is suitable for writing. Usually all tocs are OK so // return just 0 here. // Return: 0: OK // 1: toc may not be suitable // 2: toc is not suitable int CdrDriver::checkToc(const Toc *toc) { int ret = 0; if (multiSession_ && toc->tocType() != Toc::CD_ROM_XA) { message(-1, "The toc type should be set to CD_ROM_XA if a multi session"); message(-1, "CD is recorded."); ret = 1; } TrackIterator itr(toc); const Track *run; int tracknr; for (run = itr.first(), tracknr = 1; run != NULL; run = itr.next(), tracknr++) { if (run->subChannelType() != TrackData::SUBCHAN_NONE) { if (subChannelEncodingMode(run->subChannelType()) == -1) { message(-2, "Track %d: sub-channel writing mode is not supported by driver.", tracknr); ret = 2; } } } return ret; } // Returns block size for given mode and actual 'encodingMode_' that must // be used to send data to the recorder. long CdrDriver::blockSize(TrackData::Mode m, TrackData::SubChannelMode sm) const { long bsize = 0; if (encodingMode_ == 0) { // only audio blocks are written bsize = AUDIO_BLOCK_LEN; } else if (encodingMode_ == 1) { // encoding for SCSI-3/mmc drives in session-at-once mode switch (m) { case TrackData::AUDIO: bsize = AUDIO_BLOCK_LEN; break; case TrackData::MODE1: case TrackData::MODE1_RAW: bsize = MODE1_BLOCK_LEN; break; case TrackData::MODE2: case TrackData::MODE2_RAW: case TrackData::MODE2_FORM1: case TrackData::MODE2_FORM2: case TrackData::MODE2_FORM_MIX: bsize = MODE2_BLOCK_LEN; break; case TrackData::MODE0: message(-3, "Illegal mode in 'CdrDriver::blockSize()'."); break; } } else { message(-3, "Illegal encoding mode in 'CdrDriver::blockSize()'."); } bsize += TrackData::subChannelSize(sm); return bsize; } void CdrDriver::printCdToc(CdToc *toc, int tocLen) { int t; long len; message(1, "Track Mode Flags Start Length"); message(1, "------------------------------------------------------------"); for (t = 0; t < tocLen; t++) { if (t == tocLen - 1) { message(1, "Leadout %s %x %s(%6ld)", (toc[t].adrCtl & 0x04) != 0 ? "DATA " : "AUDIO", toc[t].adrCtl & 0x0f, Msf(toc[t].start).str(), toc[t].start); } else { len = toc[t + 1].start - toc[t].start; message(1, "%2d %s %x %s(%6ld) ", toc[t].track, (toc[t].adrCtl & 0x04) != 0 ? "DATA " : "AUDIO", toc[t].adrCtl & 0x0f, Msf(toc[t].start).str(), toc[t].start); message(1, " %s(%6ld)", Msf(len).str(), len); } } } TrackData::Mode CdrDriver::getTrackMode(int, long trackStartLba) { unsigned char cmd[10]; unsigned char data[2340]; int blockLength = 2340; TrackData::Mode mode; if (setBlockSize(blockLength) != 0) { return TrackData::MODE0; } memset(cmd, 0, 10); cmd[0] = 0x28; // READ10 cmd[2] = trackStartLba >> 24; cmd[3] = trackStartLba >> 16; cmd[4] = trackStartLba >> 8; cmd[5] = trackStartLba; cmd[8] = 1; if (sendCmd(cmd, 10, NULL, 0, data, blockLength) != 0) { setBlockSize(MODE1_BLOCK_LEN); return TrackData::MODE0; } setBlockSize(MODE1_BLOCK_LEN); mode = determineSectorMode(data); if (mode == TrackData::MODE0) { message(-2, "Found illegal mode in sector %ld.", trackStartLba); } return mode; } TrackData::Mode CdrDriver::determineSectorMode(unsigned char *buf) { switch (buf[3]) { case 1: return TrackData::MODE1; break; case 2: return analyzeSubHeader(buf + 4); break; } // illegal mode found return TrackData::MODE0; } // Analyzes given 8 byte sub head and tries to determine if it belongs // to a form 1, form 2 or a plain mode 2 sector. TrackData::Mode CdrDriver::analyzeSubHeader(unsigned char *sh) { if (sh[0] == sh[4] && sh[1] == sh[5] && sh[2] == sh[6] && sh[3] == sh[7]) { // check first copy //if (sh[0] < 8 && sh[1] < 8 && sh[2] != 0) { if ((sh[2] & 0x20) != 0) return TrackData::MODE2_FORM2; else return TrackData::MODE2_FORM1; //} #if 0 // check second copy if (sh[4] < 8 && sh[5] < 8 && sh[6] != 0) { if (sh[6] & 0x20 != 0) return TrackData::MODE2_FORM2; else return TrackData::MODE2_FORM1; } #endif } else { // no valid sub-header data, sector is a plain MODE2 sector return TrackData::MODE2; } } // Sets block size for read/write operation to given value. // blocksize: block size in bytes // density: (optional, default: 0) density code // Return: 0: OK // 1: SCSI command failed int CdrDriver::setBlockSize(long blocksize, unsigned char density) { unsigned char cmd[10]; unsigned char ms[16]; if (blockLength_ == blocksize) return 0; memset(ms, 0, 16); ms[3] = 8; ms[4] = density; ms[10] = blocksize >> 8; ms[11] = blocksize; memset(cmd, 0, 10); cmd[0] = 0x15; // MODE SELECT6 cmd[4] = 12; if (sendCmd(cmd, 6, ms, 12, NULL, 0) != 0) { message(-2, "Cannot set block size."); return 1; } blockLength_ = blocksize; return 0; } // Returns control flags for given track. unsigned char CdrDriver::trackCtl(const Track *track) { unsigned char ctl = 0; if (track->copyPermitted()) { ctl |= 0x20; } if (track->type() == TrackData::AUDIO) { // audio track if (track->preEmphasis()) { ctl |= 0x10; } if (track->audioType() == 1) { ctl |= 0x80; } } else { // data track ctl |= 0x40; } return ctl; } // Returns session format for point A0 toc entry depending on Toc type. unsigned char CdrDriver::sessionFormat() { unsigned char ret = 0; int nofMode1Tracks; int nofMode2Tracks; switch (toc_->tocType()) { case Toc::CD_DA: case Toc::CD_ROM: ret = 0x00; break; case Toc::CD_I: ret = 0x10; break; case Toc::CD_ROM_XA: /* The toc type can only be set to CD_ROM_XA if the session contains at least one data track. Otherwise the toc type must be CD_DA even in multi session mode. */ toc_->trackSummary(NULL, &nofMode1Tracks, &nofMode2Tracks); if (nofMode1Tracks + nofMode2Tracks > 0) ret = 0x20; else ret = 0x0; break; } message(3, "Session format: %x", ret); return ret; } // Generic method to read CD-TEXT packs according to the MMC-2 specification. // nofPacks: filled with number of found CD-TEXT packs // return: array of CD-TEXT packs or 'NULL' if no packs where retrieved CdTextPack *CdrDriver::readCdTextPacks(long *nofPacks) { unsigned char cmd[12]; unsigned char *data; unsigned char reqData[4]; #if 0 memset(cmd, 0, 12); cmd[0] = 0xbe; cmd[2] = 0xf0; cmd[3] = 0x00; cmd[4] = 0x00; cmd[5] = 0x00; cmd[8] = 15; cmd[9] = 0x0; cmd[10] = 0x1; long len1 = 15 * (AUDIO_BLOCK_LEN + 96); data = new unsigned char [len1]; if (sendCmd(cmd, 12, NULL, 0, data, len1) != 0) { message(1, "Cannot read raw CD-TEXT data."); } long i, j; unsigned char *p = data + AUDIO_BLOCK_LEN; message(0, "Raw CD-TEXT data"); for (i = 0; i < 15; i++) { unsigned char packs[72]; PWSubChannel96 chan(p); chan.getRawRWdata(packs); for (j = 0; j < 4; j++) { message(0, "%02x %02x %02x %02x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x CRC: %02x %02x", packs[j*18+0], packs[j*18+1], packs[j*18+2], packs[j*18+3], packs[j*18+4], packs[j*18+5], packs[j*18+6], packs[j*18+7], packs[j*18+8], packs[j*18+9], packs[j*18+10], packs[j*18+11], packs[j*18+12], packs[j*18+13], packs[j*18+14], packs[j*18+15], packs[j*18+16], packs[j*18+17]); } p += AUDIO_BLOCK_LEN + 96; } delete[] data; message(0, "Raw CD-TEXT data - end"); #endif memset(cmd, 0, 10); cmd[0] = 0x43; // READ TOC/PMA/ATIP cmd[2] = 5; // CD-TEXT cmd[8] = 4; if (sendCmd(cmd, 10, NULL, 0, reqData, 4, 0) != 0) { message(3, "Cannot read CD-TEXT data - maybe not supported by drive."); return NULL; } long len = ((reqData[0] << 8 ) | reqData[1]) + 2; message(4, "CD-TEXT data len: %ld", len); if (len <= 4) return NULL; if (len > scsiMaxDataLen_) { message(-2, "CD-TEXT data too big for maximum SCSI transfer length."); return NULL; } data = new unsigned char[len]; cmd[7] = len >> 8; cmd[8] = len; if (sendCmd(cmd, 10, NULL, 0, data, len, 1) != 0) { message(-2, "Reading of CD-TEXT data failed."); delete[] data; return NULL; } *nofPacks = (len - 4) / sizeof(CdTextPack); CdTextPack *packs = new CdTextPack[*nofPacks]; memcpy(packs, data + 4, *nofPacks * sizeof(CdTextPack)); delete[] data; return packs; } // Analyzes CD-TEXT packs and stores read data in given 'Toc' object. // Return: 0: OK // 1: error occured int CdrDriver::readCdTextData(Toc *toc) { long i, j; long nofPacks; CdTextPack *packs = readCdTextPacks(&nofPacks); unsigned char buf[256 * 12]; unsigned char lastType; int lastBlockNumber; int blockNumber; int pos; int actTrack; CdTextItem::PackType packType; CdTextItem *sizeInfoItem = NULL; CdTextItem *item; if (packs == NULL) return 1; message(1, "Found CD-TEXT data."); pos = 0; lastType = packs[0].packType; lastBlockNumber = (packs[0].blockCharacter >> 4) & 0x07; actTrack = 0; for (i = 0; i < nofPacks; i++) { CdTextPack &p = packs[i]; #if 1 message(4, "%02x %02x %02x %02x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x CRC: %02x %02x", p.packType, p.trackNumber, p.sequenceNumber, p.blockCharacter, p.data[0], p.data[1], p.data[2], p.data[3], p.data[4], p.data[5], p.data[6], p.data[7], p.data[8], p.data[9], p.data[10], p.data[11], p.crc0, p.crc1); #endif blockNumber = (p.blockCharacter >> 4) & 0x07; if (lastType != p.packType || lastBlockNumber != blockNumber) { if (lastType >= 0x80 && lastType <= 0x8f) { packType = CdTextItem::int2PackType(lastType); if (CdTextItem::isBinaryPack(packType)) { // finish binary data if (packType == CdTextItem::CDTEXT_GENRE) { // The two genre codes may be followed by a string. Adjust 'pos' // so that all extra 0 bytes at the end of the data are stripped // off. for (j = 2; j < pos && buf[j] != 0; j++) ; if (j < pos) pos = j + 1; } item = new CdTextItem(packType, lastBlockNumber, buf, pos); if (packType == CdTextItem::CDTEXT_SIZE_INFO) sizeInfoItem = item; toc->addCdTextItem(0, item); } } else { message(-2, "CD-TEXT: Found invalid pack type: %02x", lastType); delete[] packs; return 1; } lastType = p.packType; lastBlockNumber = blockNumber; pos = 0; actTrack = 0; } if (p.packType >= 0x80 && p.packType <= 0x8f) { packType = CdTextItem::int2PackType(p.packType); if (CdTextItem::isBinaryPack(packType)) { memcpy(buf + pos, p.data, 12); pos += 12; } else { // pack contains text -> read all string from it j = 0; while (j < 12 && actTrack <= toc->nofTracks()) { for (; j < 12 && p.data[j] != 0; j++) buf[pos++] = p.data[j]; if (j < 12) { // string is finished buf[pos] = 0; #if 0 message(0, "%02x %02x: %s", p.packType, p.trackNumber, buf); #endif toc->addCdTextItem(actTrack, new CdTextItem(packType, blockNumber, (char*)buf)); actTrack++; pos = 0; if (CdTextItem::isTrackPack(packType)) { j++; // skip zero } else { j = 12; // don't use remaining zeros to build track packs } } } } } else { message(-2, "CD-TEXT: Found invalid pack type: %02x", p.packType); delete[] packs; return 1; } } if (pos != 0 && lastType >= 0x80 && lastType <= 0x8f) { packType = CdTextItem::int2PackType(lastType); if (CdTextItem::isBinaryPack(packType)) { // finish binary data if (packType == CdTextItem::CDTEXT_GENRE) { // The two genre codes may be followed by a string. Adjust 'pos' // so that all extra 0 bytes at the end of the data are stripped // off. for (j = 2; j < pos && buf[j] != 0; j++) ; if (j < pos) pos = j + 1; } item = new CdTextItem(packType, lastBlockNumber, buf, pos); toc->addCdTextItem(0, item); if (packType == CdTextItem::CDTEXT_SIZE_INFO) sizeInfoItem = item; } } delete[] packs; // update language mapping from SIZE INFO pack data if (sizeInfoItem != NULL && sizeInfoItem->dataLen() >= 36) { const unsigned char *data = sizeInfoItem->data(); for (i = 0; i < 8; i++) { if (data[28 + i] > 0) toc->cdTextLanguage(i, data[28 + i]); else toc->cdTextLanguage(i, -1); } } else { message(-1, "Cannot determine language mapping from CD-TEXT data."); message(-1, "Using default mapping."); } return 0; } int CdrDriver::analyzeDataTrack(TrackData::Mode mode, int trackNr, long startLba, long endLba, long *pregap) { long maxLen = scsiMaxDataLen_ / AUDIO_BLOCK_LEN; long lba = startLba; long len = endLba - startLba; long actLen, n; *pregap = 0; while (len > 0) { n = len > maxLen ? maxLen : len; if ((actLen = readTrackData(mode, TrackData::SUBCHAN_NONE, lba, n, transferBuffer_)) < 0) { message(-2, "Analyzing of track %d failed.", trackNr); return 1; } message(1, "%s\r", Msf(lba).str()); if (actLen != n) { //message(0, "Data track pre-gap: %ld", len - actLen); *pregap = len - actLen; if (*pregap > 300) { message(-1, "The pre-gap of the following track appears to have length %s.", Msf(*pregap).str()); message(-1, "This value is probably bogus and may be caused by unexpected"); message(-1, "behavior of the drive. Try to verify with other tools how"); message(-1, "much data can be read from the current track and compare it"); message(-1, "to the value stored in the toc-file. Usually, the pre-gap"); message(-1, "should have length 00:02:00."); } return 0; } len -= n; lba += n; } return 0; } /* Reads toc and audio data from CD for specified 'session' number. * The data is written to file 'dataFilename' unless on-the-fly writing * is active in which case the data is written to the file descriptor * 'onTheFlyFd'. * Return: newly created 'Toc' object or 'NULL' if an error occured */ Toc *CdrDriver::readDisk(int session, const char *dataFilename) { int padFirstPregap = 1; int nofTracks = 0; int i; CdToc *cdToc = getToc(session, &nofTracks); //unsigned char trackCtl; // control nibbles of track //int ctlCheckOk; TrackInfo *trackInfos; TrackData::Mode trackMode; int fp = -1; char *fname = strdupCC(dataFilename); Toc *toc = NULL; int trs = 0; int tre = 0; long slba, elba; ReadDiskInfo info; if (cdToc == NULL) { return NULL; } if (nofTracks <= 1) { message(-1, "No tracks on disk."); delete[] cdToc; return NULL; } message(1, ""); printCdToc(cdToc, nofTracks); message(1, ""); //return NULL; nofTracks -= 1; // do not count lead-out readCapabilities_ = getReadCapabilities(cdToc, nofTracks); trackInfos = new TrackInfo[nofTracks + 1]; for (i = 0; i < nofTracks; i++) { if ((cdToc[i].adrCtl & 0x04) != 0) { if ((trackMode = getTrackMode(i + 1, cdToc[i].start)) == TrackData::MODE0) { message(-1, "Cannot determine mode of data track %d - asuming MODE1.", i + 1); trackMode = TrackData::MODE1; } if (rawDataReading_) { if (trackMode == TrackData::MODE1) { trackMode = TrackData::MODE1_RAW; } else if (trackMode == TrackData::MODE2_FORM1 || trackMode == TrackData::MODE2_FORM2 || trackMode == TrackData::MODE2_FORM_MIX) { trackMode = TrackData::MODE2_RAW; } } else if (mode2Mixed_) { if (trackMode == TrackData::MODE2_FORM1 || trackMode == TrackData::MODE2_FORM2 || trackMode == TrackData::MODE2_FORM_MIX) { trackMode = TrackData::MODE2_FORM_MIX; } } } else { trackMode = TrackData::AUDIO; } if (!checkSubChanReadCaps(trackMode, readCapabilities_)) { message(-2, "This drive does not support %s sub-channel reading.", TrackData::subChannelMode2String(subChanReadMode_)); goto fail; } trackInfos[i].trackNr = cdToc[i].track; trackInfos[i].ctl = cdToc[i].adrCtl & 0x0f; trackInfos[i].mode = trackMode; trackInfos[i].start = cdToc[i].start; trackInfos[i].pregap = 0; trackInfos[i].fill = 0; trackInfos[i].indexCnt = 0; trackInfos[i].isrcCode[0] = 0; trackInfos[i].filename = fname; trackInfos[i].bytesWritten = 0; } // lead-out entry trackInfos[nofTracks].trackNr = 0xaa; trackInfos[nofTracks].ctl = 0; trackInfos[nofTracks].mode = trackInfos[nofTracks - 1].mode; trackInfos[nofTracks].start = cdToc[nofTracks].start; if (taoSource()) { trackInfos[nofTracks].start -= taoSourceAdjust_; } trackInfos[nofTracks].pregap = 0; trackInfos[nofTracks].indexCnt = 0; trackInfos[nofTracks].isrcCode[0] = 0; trackInfos[nofTracks].filename = NULL; trackInfos[nofTracks].bytesWritten = 0; if (onTheFly_) { fp = onTheFlyFd_; } else { giveUpRootPrivileges(); #ifdef __CYGWIN__ if ((fp = open(dataFilename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666)) < 0) #else if ((fp = open(dataFilename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) #endif { message(-2, "Cannot open \"%s\" for writing: %s", dataFilename, strerror(errno)); delete[] cdToc; return NULL; } } info.tracks = nofTracks; info.startLba = trackInfos[0].start; info.endLba = trackInfos[nofTracks].start; while (trs < nofTracks) { if (trackInfos[trs].mode != TrackData::AUDIO) { if (trs == 0) { if (session == 1) trackInfos[trs].pregap = trackInfos[trs].start; } else { if (taoSource()) { trackInfos[trs].pregap = 150 + taoSourceAdjust_; } else { if (trackInfos[trs].mode != trackInfos[trs - 1].mode) trackInfos[trs].pregap = 150; } } slba = trackInfos[trs].start; elba = trackInfos[trs + 1].start; if (taoSource()) { if (trs < nofTracks - 1) elba -= 150 + taoSourceAdjust_; } else { if (trackInfos[trs].mode != trackInfos[trs + 1].mode) { elba -= 150; } } message(1, "Copying data track %d (%s): start %s, ", trs + 1, TrackData::mode2String(trackInfos[trs].mode), Msf(cdToc[trs].start).str()); message(1, "length %s to \"%s\"...", Msf(elba - slba).str(), trackInfos[trs].filename); if (readDataTrack(&info, fp, slba, elba, &trackInfos[trs]) != 0) goto fail; trs++; } else { // find continuous range of audio tracks tre = trs; while (tre < nofTracks && trackInfos[tre].mode == TrackData::AUDIO) tre++; if (trs == 0) { if (session == 1) trackInfos[trs].pregap = trackInfos[trs].start; } else { // previous track must be of different mode so assume a standard // pre-gap here if (taoSource()) { trackInfos[trs].pregap = 150 + taoSourceAdjust_; } else { trackInfos[trs].pregap = 150; } } slba = cdToc[trs].start; elba = cdToc[tre].start; // If we have the first track of the first session we can start ripping // from lba 0 to extract the pre-gap data. // But only if the drive supports it as indicated by 'options_'. if (session == 1 && trs == 0 && (options_ & OPT_DRV_NO_PREGAP_READ) == 0) { slba = 0; } // Assume that the pre-gap length conforms to the standard if the track // mode changes. if (taoSource()) { if (tre < nofTracks) elba -= 150 + taoSourceAdjust_; } else { if (trackInfos[tre - 1].mode != trackInfos[tre].mode) { elba -= 150; } } message(1, "Copying audio tracks %d-%d: start %s, ", trs + 1, tre, Msf(slba).str()); message(1, "length %s to \"%s\"...", Msf(elba - slba).str(), trackInfos[trs].filename); if (readAudioRange(&info, fp, slba, elba, trs, tre - 1, trackInfos) != 0) goto fail; trs = tre; } } // if the drive allows to read audio data from the first track's // pre-gap the data will be written to the output file and // 'buildToc()' must not create zero data for the pre-gap padFirstPregap = 1; if (session == 1 && (options_ & OPT_DRV_NO_PREGAP_READ) == 0) padFirstPregap = 0; toc = buildToc(trackInfos, nofTracks + 1, padFirstPregap); if (!onTheFly_ && toc != NULL) { if ((options_ & OPT_DRV_NO_CDTEXT_READ) == 0) readCdTextData(toc); if (readCatalog(toc, trackInfos[0].start, trackInfos[nofTracks].start)) { message(2, "Found disk catalogue number."); } } sendReadCdProgressMsg(RCD_EXTRACTING, nofTracks, nofTracks, 1000, 1000); fail: delete[] cdToc; delete[] trackInfos; delete[] fname; if (!onTheFly_ && fp >= 0) { if (close(fp) != 0) { message(-2, "Writing to \"%s\" failed: %s", dataFilename, strerror(errno)); delete toc; return NULL; } } return toc; } Toc *CdrDriver::buildToc(TrackInfo *trackInfos, long nofTrackInfos, int padFirstPregap) { long i, j; long nofTracks = nofTrackInfos - 1; int foundDataTrack = 0; int foundAudioTrack = 0; int foundXATrack = 0; long modeStartLba = 0; // start LBA for current mode unsigned long dataLen; TrackData::Mode trackMode; TrackData::Mode lastMode = TrackData::MODE0; // illegal in this context TrackData::SubChannelMode trackSubChanMode; int newMode; long byteOffset = 0; if (nofTrackInfos < 2) return NULL; Toc *toc = new Toc; // build the Toc for (i = 0; i < nofTracks; i++) { TrackInfo &ati = trackInfos[i]; // actual track info TrackInfo &nti = trackInfos[i + 1]; // next track info newMode = 0; trackMode = ati.mode; trackSubChanMode = subChanReadMode_; switch (trackMode) { case TrackData::AUDIO: foundAudioTrack = 1; break; case TrackData::MODE1: case TrackData::MODE1_RAW: case TrackData::MODE2: foundDataTrack = 1; break; case TrackData::MODE2_RAW: case TrackData::MODE2_FORM1: case TrackData::MODE2_FORM2: case TrackData::MODE2_FORM_MIX: foundXATrack = 1; break; case TrackData::MODE0: // should not happen break; } if (trackMode != lastMode) { newMode = 1; if (i == 0 && !padFirstPregap) modeStartLba = 0; else modeStartLba = ati.start; lastMode = trackMode; } Track t(trackMode, trackSubChanMode); t.preEmphasis(ati.ctl & 0x01); t.copyPermitted(ati.ctl & 0x02); t.audioType(ati.ctl & 0x08); if (ati.isrcCode[0] != 0) t.isrc(ati.isrcCode); if (trackMode == TrackData::AUDIO) { if (trackSubChanMode == TrackData::SUBCHAN_NONE) { if (newMode && (i > 0 || padFirstPregap)) { Msf trackLength(nti.start - ati.start - nti.pregap); if (ati.pregap > 0) { t.append(SubTrack(SubTrack::DATA, TrackData(Msf(ati.pregap).samples()))); } t.append(SubTrack(SubTrack::DATA, TrackData(ati.filename, byteOffset, 0, trackLength.samples()))); } else { Msf trackLength(nti.start - ati.start - nti.pregap + ati.pregap); t.append(SubTrack(SubTrack::DATA, TrackData(ati.filename, byteOffset, Msf(ati.start - modeStartLba - ati.pregap).samples(), trackLength.samples()))); } } else { if (newMode && (i > 0 || padFirstPregap)) { long trackLength = nti.start - ati.start - nti.pregap; if (ati.pregap > 0) { dataLen = ati.pregap * TrackData::dataBlockSize(trackMode, trackSubChanMode); t.append(SubTrack(SubTrack::DATA, TrackData(trackMode, trackSubChanMode, dataLen))); } dataLen = trackLength * TrackData::dataBlockSize(trackMode, trackSubChanMode); t.append(SubTrack(SubTrack::DATA, TrackData(trackMode, trackSubChanMode, ati.filename, byteOffset, dataLen))); } else { long trackLength = nti.start - ati.start - nti.pregap + ati.pregap; dataLen = trackLength * TrackData::dataBlockSize(trackMode, trackSubChanMode); long offset = (ati.start - modeStartLba - ati.pregap) * TrackData::dataBlockSize(trackMode, trackSubChanMode); t.append(SubTrack(SubTrack::DATA, TrackData(trackMode, trackSubChanMode, ati.filename, byteOffset + offset, dataLen))); } } t.start(Msf(ati.pregap)); } else { long trackLength = nti.start - ati.start - nti.pregap - ati.fill; if (ati.pregap != 0) { // add zero data for pre-gap dataLen = ati.pregap * TrackData::dataBlockSize(trackMode, trackSubChanMode); t.append(SubTrack(SubTrack::DATA, TrackData(trackMode, trackSubChanMode, dataLen))); } dataLen = trackLength * TrackData::dataBlockSize(trackMode, trackSubChanMode); t.append(SubTrack(SubTrack::DATA, TrackData(trackMode, trackSubChanMode, ati.filename, byteOffset, dataLen))); if (ati.fill > 0) { dataLen = ati.fill * TrackData::dataBlockSize(trackMode, trackSubChanMode); t.append(SubTrack(SubTrack::DATA, TrackData(trackMode, trackSubChanMode, dataLen))); } t.start(Msf(ati.pregap)); } for (j = 0; j < ati.indexCnt; j++) t.appendIndex(Msf(ati.index[j])); toc->append(&t); byteOffset += ati.bytesWritten; } if (foundXATrack) toc->tocType(Toc::CD_ROM_XA); else if (foundDataTrack) toc->tocType(Toc::CD_ROM); else toc->tocType(Toc::CD_DA); return toc; } // Reads a complete data track. // start: start of data track from TOC // end: start of next track from TOC // trackInfo: info about current track, updated by this function // Return: 0: OK // 1: error occured int CdrDriver::readDataTrack(ReadDiskInfo *info, int fd, long start, long end, TrackInfo *trackInfo) { long len = end - start; long totalLen = len; long lba; long lastLba; long blockLen = 0; long blocking; long burst; long iterationsWithoutError = 0; long n, ret; long act; int foundLECError; unsigned char *buf; TrackData::Mode mode = TrackData::AUDIO; switch (trackInfo->mode) { case TrackData::MODE1: mode = TrackData::MODE1; blockLen = MODE1_BLOCK_LEN; break; case TrackData::MODE1_RAW: mode = TrackData::MODE1_RAW; blockLen = AUDIO_BLOCK_LEN; break; case TrackData::MODE2: mode = TrackData::MODE2; blockLen = MODE2_BLOCK_LEN; break; case TrackData::MODE2_RAW: mode = TrackData::MODE2_RAW; blockLen = AUDIO_BLOCK_LEN; break; case TrackData::MODE2_FORM1: mode = TrackData::MODE2_FORM1; blockLen = MODE2_FORM1_DATA_LEN; break; case TrackData::MODE2_FORM2: mode = TrackData::MODE2_FORM2; blockLen = MODE2_FORM2_DATA_LEN; break; case TrackData::MODE2_FORM_MIX: mode = TrackData::MODE2_FORM_MIX; blockLen = MODE2_BLOCK_LEN; break; case TrackData::MODE0: case TrackData::AUDIO: message(-3, "CdrDriver::readDataTrack: Illegal mode."); return 1; break; } blockLen += TrackData::subChannelSize(subChanReadMode_); // adjust mode in 'trackInfo' trackInfo->mode = mode; trackInfo->bytesWritten = 0; blocking = scsiMaxDataLen_ / (AUDIO_BLOCK_LEN + PW_SUBCHANNEL_LEN); assert(blocking > 0); buf = new unsigned char[blocking * blockLen]; lba = lastLba = start; burst = blocking; while (len > 0) { if (burst != blocking && iterationsWithoutError > 2 * blocking) burst = blocking; n = (len > burst) ? burst : len; foundLECError = 0; if ((act = readTrackData(mode, subChanReadMode_, lba, n, buf)) == -1) { message(-2, "Read error while copying data from track."); delete[] buf; return 1; } if (act == -2) { // L-EC error encountered if (trackInfo->mode == TrackData::MODE1_RAW || trackInfo->mode == TrackData::MODE2_RAW) { if (n > 1) { // switch to single step mode iterationsWithoutError = 0; burst = 1; continue; } else { foundLECError = 1; act = n = 0; } } else { message(-2, "L-EC error around sector %ld while copying data from track.", lba); message(-2, "Use option '--read-raw' to ignore L-EC errors."); delete[] buf; return 1; } } if (foundLECError) { iterationsWithoutError = 0; message(2, "Found L-EC error at sector %ld - ignored.", lba); // create a dummy sector for the sector with L-EC errors Msf m(lba + 150); memcpy(buf, syncPattern, 12); buf[12] = SubChannel::bcd(m.min()); buf[13] = SubChannel::bcd(m.sec()); buf[14] = SubChannel::bcd(m.frac()); if (trackInfo->mode == TrackData::MODE1_RAW) buf[15] = 1; else buf[15] = 2; memcpy(buf + 16, SECTOR_ERROR_DATA, blockLen - 16); if ((ret = fullWrite(fd, buf, blockLen)) != blockLen) { if (ret < 0) message(-2, "Writing of data failed: %s", strerror(errno)); else message(-2, "Writing of data failed: Disk full"); delete[] buf; return 1; } trackInfo->bytesWritten += blockLen; lba += 1; len -= 1; } else { iterationsWithoutError++; if (act > 0) { if ((ret = fullWrite(fd, buf, blockLen * act)) != blockLen * act) { if (ret < 0) message(-2, "Writing of data failed: %s", strerror(errno)); else message(-2, "Writing of data failed: Disk full"); delete[] buf; return 1; } } trackInfo->bytesWritten += blockLen * act; if (lba > lastLba + 75) { Msf lbatime(lba); message(1, "%02d:%02d:00\r", lbatime.min(), lbatime.sec()); lastLba = lba; if (remote_) { long totalProgress; long progress; progress = (totalLen - len) * 1000; progress /= totalLen; totalProgress = lba - info->startLba; if (totalProgress > 0) { totalProgress *= 1000; totalProgress /= (info->endLba - info->startLba); } else { totalProgress = 0; } sendReadCdProgressMsg(RCD_EXTRACTING, info->tracks, trackInfo->trackNr, progress, totalProgress); } } lba += act; len -= act; if (act != n) break; } } // pad remaining blocks with zero data, e.g. for disks written in TAO mode if (len > 0) { message(-1, "Padding with %ld zero sectors.", len); if (mode == TrackData::MODE1_RAW || mode == TrackData::MODE2_RAW) { memcpy(buf, syncPattern, 12); if (mode == TrackData::MODE1_RAW) buf[15] = 1; else buf[15] = 2; memset(buf + 16, 0, blockLen - 16); } else { memset(buf, 0, blockLen); } while (len > 0) { if (mode == TrackData::MODE1_RAW || mode == TrackData::MODE2_RAW) { Msf m(lba + 150); buf[12] = SubChannel::bcd(m.min()); buf[13] = SubChannel::bcd(m.sec()); buf[14] = SubChannel::bcd(m.frac()); } if ((ret = fullWrite(fd, buf, blockLen)) != blockLen) { if (ret < 0) message(-2, "Writing of data failed: %s", strerror(errno)); else message(-2, "Writing of data failed: Disk full"); delete[] buf; return 1; } trackInfo->bytesWritten += blockLen; len--; lba++; } } delete[] buf; return 0; } // Tries to read the catalog number from the sub-channels starting at LBA 0. // If a catalog number is found it will be placed into the provided 14 byte // buffer 'mcnCode'. Otherwise 'mcnCode[0]' is set to 0. // Return: 0: OK // 1: SCSI error occured #define N_ELEM 11 #define MCN_LEN 13 #define MAX_MCN_SCAN_LENGTH 5000 static int cmpMcn(const void *p1, const void *p2) { const char *s1 = (const char *)p1; const char *s2 = (const char *)p2; return strcmp(s1, s2); } int CdrDriver::readCatalogScan(char *mcnCode, long startLba, long endLba) { SubChannel **subChannels; int n, i; long length; int mcnCodeFound = 0; char mcn[N_ELEM][MCN_LEN+1]; *mcnCode = 0; length = endLba - startLba; if (length > MAX_MCN_SCAN_LENGTH) length = MAX_MCN_SCAN_LENGTH; while ((length > 0) && (mcnCodeFound < N_ELEM)) { n = (length > maxScannedSubChannels_ ? maxScannedSubChannels_ : length); if (readSubChannels(TrackData::SUBCHAN_NONE, startLba, n, &subChannels, NULL) != 0 || subChannels == NULL) { return 1; } for (i = 0; i < n; i++) { SubChannel *chan = subChannels[i]; //chan->print(); if (chan->checkCrc() && chan->checkConsistency()) { if (chan->type() == SubChannel::QMODE2) { if (mcnCodeFound < N_ELEM) { strcpy(mcn[mcnCodeFound++], chan->catalog()); } } } } length -= n; startLba += n; } if(mcnCodeFound > 0) { qsort(mcn, mcnCodeFound, MCN_LEN + 1, cmpMcn); strcpy(mcnCode, mcn[(mcnCodeFound >> 1)]); } return 0; } #undef N_ELEM #undef MCN_LEN #undef MAX_MCN_SCAN_LENGTH // Sends a read cd progress message without blocking the actual process. void CdrDriver::sendReadCdProgressMsg(ReadCdProgressType type, int totalTracks, int track, int trackProgress, int totalProgress) { if (remote_) { int fd = remoteFd_; ProgressMsg p; p.status = type; p.totalTracks = totalTracks; p.track = track; p.trackProgress = trackProgress; p.totalProgress = totalProgress; p.bufferFillRate = 0; p.writerFillRate = 0; if (write(fd, REMOTE_MSG_SYNC_, sizeof(REMOTE_MSG_SYNC_)) != sizeof(REMOTE_MSG_SYNC_) || write(fd, (const char*)&p, sizeof(p)) != sizeof(p)) { message(-1, "Failed to send read CD remote progress message."); } } } // Sends a write cd progress message without blocking the actual process. int CdrDriver::sendWriteCdProgressMsg(WriteCdProgressType type, int totalTracks, int track, int trackProgress, int totalProgress, int bufferFillRate, int writeFill) { if (remote_) { int fd = remoteFd_; ProgressMsg p; p.status = type; p.totalTracks = totalTracks; p.track = track; p.trackProgress = trackProgress; p.totalProgress = totalProgress; p.bufferFillRate = bufferFillRate; p.writerFillRate = writeFill; if (write(fd, REMOTE_MSG_SYNC_, sizeof(REMOTE_MSG_SYNC_)) != sizeof(REMOTE_MSG_SYNC_) || write(fd, (const char*)&p, sizeof(p)) != sizeof(p)) { message(-1, "Failed to send write CD remote progress message."); return 1; } } return 0; } // Sends a blank cd progress message without blocking the actual process. int CdrDriver::sendBlankCdProgressMsg(int totalProgress) { if (remote_) { int fd = remoteFd_; ProgressMsg p; p.status = PGSMSG_BLK; p.totalTracks = 0; p.track = 0; p.trackProgress = 0; p.totalProgress = totalProgress; p.bufferFillRate = 0; p.writerFillRate = 0; if (write(fd, REMOTE_MSG_SYNC_, sizeof(REMOTE_MSG_SYNC_)) != sizeof(REMOTE_MSG_SYNC_) || write(fd, (const char*)&p, sizeof(p)) != sizeof(p)) { message(-1, "Failed to send write CD remote progress message."); return 1; } } return 0; } long CdrDriver::audioRead(TrackData::SubChannelMode sm, int byteOrder, Sample *buffer, long startLba, long len) { SubChannel **chans; int i; int swap; long blockLen = AUDIO_BLOCK_LEN + TrackData::subChannelSize(sm); if (readSubChannels(sm, startLba, len, &chans, buffer) != 0) { memset(buffer, 0, len * blockLen); audioReadError_ = 1; return len; } swap = (audioDataByteOrder_ == byteOrder) ? 0 : 1; if (options_ & OPT_DRV_SWAP_READ_SAMPLES) swap = !swap; if (swap) { unsigned char *b = (unsigned char*)buffer; for (i = 0; i < len; i++) { swapSamples((Sample *)b, SAMPLES_PER_BLOCK); b += blockLen; } } if (remote_ && startLba > audioReadLastLba_) { long totalTrackLen = audioReadTrackInfo_[audioReadActTrack_ + 1].start - audioReadTrackInfo_[audioReadActTrack_ ].start; long progress = startLba - audioReadTrackInfo_[audioReadActTrack_ ].start; long totalProgress; if (progress > 0) { progress *= 1000; progress /= totalTrackLen; } else { progress = 0; } totalProgress = startLba + len - audioReadInfo_->startLba; if (totalProgress > 0) { totalProgress *= 1000; totalProgress /= audioReadInfo_->endLba - audioReadInfo_->startLba; } else { totalProgress = 0; } sendReadCdProgressMsg(RCD_EXTRACTING, audioReadInfo_->tracks, audioReadActTrack_ + 1, progress, totalProgress); audioReadLastLba_ = startLba; } if (chans == NULL) { // drive does not provide sub channel data so that's all we could do here: if (startLba > audioReadTrackInfo_[audioReadActTrack_ + 1].start) { audioReadActTrack_++; message(1, "Track %d...", audioReadActTrack_ + 1); } if (startLba - audioReadProgress_ > 75) { audioReadProgress_ = startLba; Msf m(audioReadProgress_); message(1, "%02d:%02d:00\r", m.min(), m.sec()); } return len; } // analyze sub-channels to find pre-gaps, index marks and ISRC codes for (i = 0; i < len; i++) { SubChannel *chan = chans[i]; //chan->print(); if (chan->checkCrc() && chan->checkConsistency()) { if (chan->type() == SubChannel::QMODE1DATA) { int t = chan->trackNr() - 1; Msf atime = Msf(chan->amin(), chan->asec(), chan->aframe()); //message(0, "LastLba: %ld, ActLba: %ld", audioReadActLba_, atime.lba()); if (t >= audioReadStartTrack_ && t <= audioReadEndTrack_ && atime.lba() > audioReadActLba_ && atime.lba() - 150 < audioReadTrackInfo_[t + 1].start) { Msf time(chan->min(), chan->sec(), chan->frame()); // track rel time audioReadActLba_ = atime.lba(); if (audioReadActLba_ - audioReadProgress_ > 75) { audioReadProgress_ = audioReadActLba_; Msf m(audioReadProgress_ - 150); message(1, "%02d:%02d:00\r", m.min(), m.sec()); } if (t == audioReadActTrack_ && chan->indexNr() == audioReadActIndex_ + 1) { if (chan->indexNr() > 1) { message(2, "Found index %d at: %s", chan->indexNr(), time.str()); if (audioReadTrackInfo_[t].indexCnt < 98) { audioReadTrackInfo_[t].index[audioReadTrackInfo_[t].indexCnt] = time.lba(); audioReadTrackInfo_[t].indexCnt += 1; } } } else if (t == audioReadActTrack_ + 1) { message(1, "Track %d...", t + 1); //chan->print(); if (chan->indexNr() == 0) { audioReadTrackInfo_[t].pregap = time.lba(); message(2, "Found pre-gap: %s", time.str()); } } audioReadActIndex_ = chan->indexNr(); audioReadActTrack_ = t; } } else if (chan->type() == SubChannel::QMODE3) { if (audioReadTrackInfo_[audioReadActTrack_].isrcCode[0] == 0) { message(2, "Found ISRC code."); strcpy(audioReadTrackInfo_[audioReadActTrack_].isrcCode, chan->isrc()); } } } else { audioReadCrcCount_++; } } return len; } int CdrDriver::readAudioRangeStream(ReadDiskInfo *info, int fd, long start, long end, int startTrack, int endTrack, TrackInfo *trackInfo) { long startLba = start; long endLba = end - 1; long len, ret; long blocking, blockLen; long lba = startLba; unsigned char *buf; blockLen = AUDIO_BLOCK_LEN + TrackData::subChannelSize(subChanReadMode_); blocking = scsiMaxDataLen_ / blockLen; assert(blocking > 0); buf = new unsigned char[blocking * blockLen]; audioReadInfo_ = info; audioReadTrackInfo_ = trackInfo; audioReadStartTrack_ = startTrack; audioReadEndTrack_ = endTrack; audioReadLastLba_ = audioReadActLba_ = startLba + 149; audioReadActTrack_ = startTrack; audioReadActIndex_ = 1; audioReadCrcCount_ = 0; audioReadError_ = 0; audioReadProgress_ = 0; len = endLba - startLba + 1; message(1, "Track %d...", startTrack + 1); trackInfo[endTrack].bytesWritten = 0; while (len > 0) { long n = len > blocking ? blocking : len; long bytesToWrite = n * blockLen; CdrDriver::audioRead(subChanReadMode_, 1/*big endian byte order*/, (Sample *)buf, lba, n); lba += n; if ((ret = fullWrite(fd, buf, bytesToWrite)) != bytesToWrite) { if (ret < 0) message(-2, "Writing of data failed: %s", strerror(errno)); else message(-2, "Writing of data failed: Disk full"); delete[] buf; return 1; } trackInfo[endTrack].bytesWritten += bytesToWrite; len -= n; } if (audioReadCrcCount_ != 0) message(2, "Found %ld Q sub-channels with CRC errors.", audioReadCrcCount_); delete[] buf; return 0; } // read cdda paranoia related: void CdrDriver::paranoiaMode(int mode) { paranoiaMode_ = PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP; switch (mode) { case 0: paranoiaMode_ = PARANOIA_MODE_DISABLE; break; case 1: paranoiaMode_ |= PARANOIA_MODE_OVERLAP; paranoiaMode_ &= ~PARANOIA_MODE_VERIFY; break; case 2: paranoiaMode_ &= ~(PARANOIA_MODE_SCRATCH|PARANOIA_MODE_REPAIR); break; } } int CdrDriver::readAudioRangeParanoia(ReadDiskInfo *info, int fd, long start, long end, int startTrack, int endTrack, TrackInfo *trackInfo) { long startLba = start; long endLba = end - 1; long len, ret; size16 *buf; if (paranoia_ == NULL) { // first time -> allocate paranoia structure paranoiaDrive_ = new cdrom_drive; paranoiaDrive_->cdr = this; paranoiaDrive_->nsectors = maxScannedSubChannels_; paranoia_ = paranoia_init(paranoiaDrive_); } paranoia_set_range(paranoia_, startLba, endLba); paranoia_modeset(paranoia_, paranoiaMode_); audioReadInfo_ = info; audioReadTrackInfo_ = trackInfo; audioReadStartTrack_ = startTrack; audioReadEndTrack_ = endTrack; audioReadLastLba_ = audioReadActLba_ = startLba + 149; audioReadActTrack_ = startTrack; audioReadActIndex_ = 1; audioReadCrcCount_ = 0; audioReadError_ = 0; audioReadProgress_ = 0; len = endLba - startLba + 1; message(1, "Track %d...", startTrack + 1); trackInfo[endTrack].bytesWritten = 0; while (len > 0) { buf = paranoia_read(paranoia_, &CdrDriver::paranoiaCallback); // The returned samples are always in host byte order. We want to // output in big endian byte order so swap if we are a little // endian host. if (hostByteOrder_ == 0) swapSamples((Sample*)buf, SAMPLES_PER_BLOCK); if ((ret = fullWrite(fd, buf, AUDIO_BLOCK_LEN)) != AUDIO_BLOCK_LEN) { if (ret < 0) message(-2, "Writing of data failed: %s", strerror(errno)); else message(-2, "Writing of data failed: Disk full"); return 1; } trackInfo[endTrack].bytesWritten += AUDIO_BLOCK_LEN; len--; } if (audioReadCrcCount_ != 0) message(2, "Found %ld Q sub-channels with CRC errors.", audioReadCrcCount_); return 0; } long cdda_read(cdrom_drive *d, void *buffer, long beginsector, long sectors) { CdrDriver *cdr = (CdrDriver*)d->cdr; return cdr->audioRead(TrackData::SUBCHAN_NONE, cdr->hostByteOrder(), (Sample*)buffer, beginsector, sectors); } void CdrDriver::paranoiaCallback(long, int) { }