From 16a775d537b6ea33f1bf81b6a046650e2187b312 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Thu, 3 Sep 2009 05:10:19 -0400 Subject: Rudamentary dvd iso ripping. --- dvdimage.cpp | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dvdimage.h | 24 ++++++++ imagegui.cpp | 19 +++++++ imagegui.h | 18 ++++++ main.cpp | 51 +---------------- 5 files changed, 245 insertions(+), 48 deletions(-) create mode 100644 dvdimage.cpp create mode 100644 dvdimage.h create mode 100644 imagegui.cpp create mode 100644 imagegui.h diff --git a/dvdimage.cpp b/dvdimage.cpp new file mode 100644 index 0000000..7bccbec --- /dev/null +++ b/dvdimage.cpp @@ -0,0 +1,181 @@ +// Much of the CSS logic comes from Bryan Ford's Netsteria +// http://www.google.com/codesearch/p?hl=en&sa=N&cd=10&ct=rc#PY4_fj37fsw/uia/netsteria/dvd/read.cc&q=DVDCSS_SEEK_KEY + +#include "dvdimage.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int DVDImage::cmpvob(const void *p1, const void *p2) +{ + vobfile *v1 = (vobfile*)p1; + vobfile *v2 = (vobfile*)p2; + if (v1->start < v2->start) + return -1; + else if (v1->start > v2->start) + return 1; + else + return 0; +} + +bool DVDImage::saveImageToPath(const QString &dvdDevice, const QString &path) +{ + QFile file(path); + file.open(QFile::WriteOnly); + bool ret = saveImageToDevice(dvdDevice, file); + file.close(); + return ret; +} + +bool DVDImage::saveImageToDevice(const QString &dvdDevice, QIODevice &out) +{ + dvd_reader_t *dvdr = DVDOpen(dvdDevice.toStdString().c_str()); + if (!dvdr) { + qDebug() << "can't open DVD (dvdread)"; + return false; + } + + // Find the extents of all the potentially-encrypted VOB files + uint32_t discend = 0; + vobfile vobs[100*10]; + int nvobs = 0; + const int NBLOCKS = 16; + char buf[DVDCSS_BLOCK_SIZE * NBLOCKS]; + for (int i = 0; i < 100; i++) { + + // Find the IFO and BUP files for this titleset, + // just to make sure hiblock accounts for them. + for (int j = 0; j < 2; j++) { + char filename[30]; + const char *ext = j ? "BUP" : "IFO"; + if (i == 0) { + sprintf(filename, "/VIDEO_TS/VIDEO_TS.%s", ext); + } else { + sprintf(filename, "/VIDEO_TS/VTS_%02d_0.%s", i, ext); + } + uint32_t size; + uint32_t start = UDFFindFile(dvdr, filename, &size); + if (start == 0 || size == 0) + break; + uint32_t end = start + (size + 2047) / DVDCSS_BLOCK_SIZE; + qDebug() << ext << "at blocks" << start << "-" << end << "(size" << size << "bytes)"; + if (discend < end) + discend = end; + } + + // Find each VOB part for decryption + for (int j = 0; j < 10; j++) { + char filename[30]; + if (i == 0) { + if (j > 0) + break; // VIDEO_TS.VOB has only 1 part + sprintf(filename, "/VIDEO_TS/VIDEO_TS.VOB"); + } else { + sprintf(filename, "/VIDEO_TS/VTS_%02d_%d.VOB", i, j); + } + uint32_t size; + uint32_t start = UDFFindFile(dvdr, filename, &size); + if (start == 0 || size == 0) + break; // No more parts in this title set + uint32_t end = start + (size + (DVDCSS_BLOCK_SIZE - 1)) / DVDCSS_BLOCK_SIZE; + + qDebug() << "VOB at blocks" << start << "-" << end << "(size" << size << "bytes)"; + vobs[nvobs].start = start; + vobs[nvobs].end = end; + if (discend < end) + discend = end; + nvobs++; + } + } + qsort(&vobs, nvobs, sizeof(vobfile), cmpvob); + vobs[nvobs].start = vobs[nvobs].end = INT_MAX; + + DVDClose(dvdr); + dvdr = NULL; + + dvdcss_t dcss = dvdcss_open((char*)dvdDevice.toStdString().c_str()); + if (!dcss) { + qDebug() << "can't open DVD (dvdcss)"; + return false; + } + + int blkno = 0; + int curvob = 0; + while (1) { + //printf("% 3d%%: block %d of %d (byte %lld of %lld)\r", + // (int)((long long)blkno*100/discend), blkno, discend, + // (long long)blkno*DVDCSS_BLOCK_SIZE, (long long)discend*DVDCSS_BLOCK_SIZE); + //fflush(stdout); + emit extractProgress(blkno, discend); + + int maxblks; + int cssflags; + while (blkno >= vobs[curvob].end) + curvob++; + if (blkno < vobs[curvob].start) { + // We haven't yet reached the next VOB. + // Just read without decrypting. + cssflags = DVDCSS_NOFLAGS; + maxblks = vobs[curvob].start - blkno; + + } else if (blkno == vobs[curvob].start) { + // We're just starting a new VOB - re-key libdvdcss. + // (Probably only needed per-title set, but...) + qDebug() << "Re-keying at block" << blkno; + int actblk = dvdcss_seek(dcss, blkno, DVDCSS_SEEK_KEY); + if (actblk != blkno) { + qDebug() << "error seeking in DVD with dvdcss"; + return false; + } + + cssflags = DVDCSS_READ_DECRYPT; + maxblks = vobs[curvob].end - blkno; + + } else if (blkno < vobs[curvob].end) { + // We're in the middle of a VOB. Decrypt it. + cssflags = DVDCSS_READ_DECRYPT; + maxblks = vobs[curvob].end - blkno; + + } else { + qDebug() << "read past end"; + return false; + } + + if (maxblks > NBLOCKS) + maxblks = NBLOCKS; + + // Read some blocks + int actblks = dvdcss_read(dcss, buf, maxblks, cssflags); + if (actblks < 0) { + qDebug() << "read error at block" << blkno; + return false; + } + if ((cssflags & DVDCSS_READ_DECRYPT) && (buf[0x14] & 0x30)) + qDebug() << "block" << blkno << ": still scrambled!?"; + + // Write them + out.write(buf, (qint64)actblks * DVDCSS_BLOCK_SIZE); + + blkno += actblks; + + // XX dvdcss has a bug in libc_read(): + // on partial reads it seeks to the wrong position, + // so we can't iterate until actblks == 0. + if (actblks < maxblks) + break; + } + + if (blkno < (int)discend) { + qDebug() << "SHORT READ: only " << blkno << "of" << discend << "blocks copied"; + return false; + } + + qDebug() << "Success:" << blkno << "blocks copied (" << (long long)blkno * DVDCSS_BLOCK_SIZE << ") of" << discend << "expected"; + return true; +} diff --git a/dvdimage.h b/dvdimage.h new file mode 100644 index 0000000..0ee1275 --- /dev/null +++ b/dvdimage.h @@ -0,0 +1,24 @@ +#ifndef DVDIMAGE_H +#define DVDIMAGE_H + +#include +class QIODevice; + +class DVDImage : public QObject +{ + Q_OBJECT +public: + bool saveImageToDevice(const QString &dvdDevice, QIODevice &out); + bool saveImageToPath(const QString &dvdDevice, const QString &path); + +private: + static int cmpvob(const void *p1, const void *p2); + typedef struct vobfile { + int32_t start, end; + } vobfile; + +signals: + void extractProgress(int current, int total); +}; + +#endif // DVDIMAGE_H diff --git a/imagegui.cpp b/imagegui.cpp new file mode 100644 index 0000000..a248a1e --- /dev/null +++ b/imagegui.cpp @@ -0,0 +1,19 @@ +#include "imagegui.h" +#include "dvdimage.h" +#include +#include +#include +#include +#include + +ImageGui::ImageGui() +{ + DVDImage *dvdImage = new DVDImage; + connect(dvdImage, SIGNAL(extractProgress(int,int)), this, SLOT(extractProgress(int,int))); + QtConcurrent::run(dvdImage, &DVDImage::saveImageToPath, QLatin1String("/dev/dvd"), QLatin1String("image.iso")); +} +void ImageGui::extractProgress(int current, int maximum) +{ + setMaximum(maximum); + setValue(current); +} diff --git a/imagegui.h b/imagegui.h new file mode 100644 index 0000000..98016ab --- /dev/null +++ b/imagegui.h @@ -0,0 +1,18 @@ +#ifndef IMAGEGUI_H +#define IMAGEGUI_H + +#include +class DVDImage; + +class ImageGui : public QProgressBar +{ + Q_OBJECT + +public: + ImageGui(); + +private slots: + void extractProgress(int current, int maximum); +}; + +#endif // IMAGEGUI_H diff --git a/main.cpp b/main.cpp index 1073fa8..b061ad0 100644 --- a/main.cpp +++ b/main.cpp @@ -1,58 +1,13 @@ -#include "ripper.h" -#include -#include -#include +#include "imagegui.h" #include int main(int argc, char *argv[]) { - dvdcss_t dvdcss = dvdcss_open("/dev/dvd"); - if (!dvdcss) - qDebug() << "failed to open dvd"; - else - qDebug() << "opened dvd"; - const int blocksPerRead = 128; - uint8_t p_data[DVDCSS_BLOCK_SIZE * (blocksPerRead + 1)]; - uint8_t *p_buffer = p_data + DVDCSS_BLOCK_SIZE - ((intptr_t)p_data & (DVDCSS_BLOCK_SIZE - 1)); - QFile out("out.iso"); - out.open(QIODevice::WriteOnly); - int blocksOffset = 0; - int blocksRead; - int neededTitleKeys = 0; - while ((blocksRead = dvdcss_read(dvdcss, p_buffer, blocksPerRead, DVDCSS_READ_DECRYPT)) > 0) { - //Check if key retreval is neccessary for this block sector - uint8_t *p_scan = p_buffer; - bool neededAKey = false; - for (int i = 0; i < blocksRead; ++i) { - if(((uint8_t*)p_scan)[0x14] & 0x30) { - dvdcss_seek(dvdcss, blocksOffset + i, DVDCSS_SEEK_KEY); - ++neededTitleKeys; - neededAKey = true; - qDebug() << "getting title key" << neededTitleKeys; - } - p_scan = p_scan + DVDCSS_BLOCK_SIZE; - } - //Reread once we've gotten a key - if (neededAKey) { - dvdcss_seek(dvdcss, blocksOffset, DVDCSS_NOFLAGS); - dvdcss_read(dvdcss, p_buffer, blocksPerRead, DVDCSS_READ_DECRYPT); - } - out.write((char*)p_buffer, (quint64)(DVDCSS_BLOCK_SIZE * blocksRead)); - out.flush(); - blocksOffset += blocksRead; - } - dvdcss_close(dvdcss); - out.close(); - return 0; - - - - QApplication a(argc, argv); a.setApplicationName(QLatin1String("AnyRip")); a.setOrganizationName(QLatin1String("AnyClip")); a.setOrganizationDomain(QLatin1String("anyclip.com")); - Ripper w; - w.show(); + ImageGui i; + i.show(); return a.exec(); } -- cgit v1.2.3-59-g8ed1b