From f83e7addef46f851384bfb2f5a282f71052318b0 Mon Sep 17 00:00:00 2001 From: Li Date: Fri, 17 Mar 2023 19:45:03 +1300 Subject: [PATCH] add stuff --- DumpDVD/Dvd/DvdRipper.cpp | 135 +++++++++++++++++----------------- DumpDVD/Dvd/DvdRipper.hpp | 17 +++-- DumpDVD/Dvd/TitleKey.cpp | 28 +++++-- DumpDVD/Gui/DumpDVD.cpp | 14 +++- lib/libdvdcss.lib | Bin 290192 -> 290166 bytes libdvdcss/src/dvdcss/dvdcss.h | 2 + libdvdcss/src/libdvdcss.c | 22 +++--- 7 files changed, 124 insertions(+), 94 deletions(-) diff --git a/DumpDVD/Dvd/DvdRipper.cpp b/DumpDVD/Dvd/DvdRipper.cpp index ebc0ad1..fc6dfa5 100644 --- a/DumpDVD/Dvd/DvdRipper.cpp +++ b/DumpDVD/Dvd/DvdRipper.cpp @@ -1,4 +1,5 @@ #include "DvdRipper.hpp" +#include #include #include @@ -15,16 +16,10 @@ namespace Li::Dvd { uint32_t DvdRipper::FileSoFar() { - if (this->inFile) - return this->fileReadSoFar; - else - return this->nonFileReadSoFar; + return this->fileReadSoFar; } uint32_t DvdRipper::FileLen() { - if (this->inFile) - return this->fileRemain; - else - return this->nonFileRemain; + return this->fileRemain; } float DvdRipper::PercentDone() { @@ -33,13 +28,18 @@ namespace Li::Dvd { uint32_t DvdRipper::SectorsReadSoFar() { return this->sectorsReadSoFar; } - uint32_t DvdRipper::FileReadSoFar() { - return this->fileReadSoFar; - } std::string DvdRipper::OutputPath() { return this->outputPath; } + std::byte* DvdRipper::GetDiscKey() { + return this->discKey; + } + + std::byte* DvdRipper::GetTitleKey() { + return this->titleKey; + } + bool DvdRipper::Done() { return this->done; } @@ -49,102 +49,96 @@ namespace Li::Dvd { std::string DvdRipper::ErrorMessage() { return this->errorMsg; } - int DvdRipper::read1SectorATimeSkippingBadSectors(int toRead) { - memset(this->tmpBuffer, 0x00, this->sectorsAtOnce * DVDCSS_BLOCK_SIZE); + int DvdRipper::read1SectorATimeSkippingBadSectors(int toRead) { int nmRd = 0; for (int i = 0; i < toRead; i++) { for (int retry = 0; retry < 10; retry++) { - int dataRd = dvdcss_read(driveIoCtl->GetDvdCssHandle(), this->tmpBuffer + (i * DVDCSS_BLOCK_SIZE), 1, DVDCSS_READ_DECRYPT); + memset(this->tmpBuffer, 0x00, DVDCSS_BLOCK_SIZE); + int dataRd = dvdcss_read(driveIoCtl->GetDvdCssHandle(), this->tmpBuffer, 1, DVDCSS_READ_DECRYPT); if (dataRd < 0) { + dvdcss_seek(driveIoCtl->GetDvdCssHandle(), this->sectorsReadSoFar, DVDCSS_SEEK_KEY); + + if (retry+1 >= 10) + dvdcss_seek(driveIoCtl->GetDvdCssHandle(), this->sectorsReadSoFar + 1, DVDCSS_SEEK_KEY); + continue; // retry } + else { + this->iso->write((const char*)this->tmpBuffer, DVDCSS_BLOCK_SIZE * dataRd); + break; + } } nmRd += 1; + this->sectorsReadSoFar += 1; + this->fileReadSoFar += 1; + + if (this->sectorsReadSoFar > this->drive->Sectors()) // check if we've read the last sector ... + break; } return nmRd; } - void DvdRipper::readNonFileSectors() { - dvdcss_title(this->driveIoCtl->GetDvdCssHandle(), this->sectorsReadSoFar); - - this->nonFileRemain = Li::Dvd::TitleKey::GetDistanceToNextFile(this->sectorsReadSoFar); - if (this->nonFileRemain == (uint32_t)-1) { - this->nonFileRemain = (this->drive->Sectors() - this->sectorsReadSoFar); - } - if ((this->sectorsReadSoFar + this->nonFileRemain) > this->drive->Sectors()) { - this->nonFileRemain = (this->drive->Sectors() - this->sectorsReadSoFar); - } - this->nonFileReadSoFar = 0; + void DvdRipper::readSectors() { do { int toRead = this->sectorsAtOnce; - if ((this->nonFileReadSoFar + toRead) > this->nonFileRemain) { - toRead = this->nonFileRemain - this->nonFileReadSoFar; + if ((this->fileReadSoFar + toRead) > this->fileRemain) { + toRead = (this->fileRemain - this->fileReadSoFar); } + int numRead = dvdcss_read(driveIoCtl->GetDvdCssHandle(), this->tmpBuffer, toRead, DVDCSS_READ_DECRYPT); if (dvdcss_was_error(driveIoCtl->GetDvdCssHandle())) { this->errorMsg = std::string(dvdcss_error(driveIoCtl->GetDvdCssHandle())); } if (numRead < 0) { - dvdcss_seek(driveIoCtl->GetDvdCssHandle(), this->sectorsAtOnce, DVDCSS_SEEK_MPEG); - numRead = read1SectorATimeSkippingBadSectors(toRead); + dvdcss_seek(driveIoCtl->GetDvdCssHandle(), this->sectorsReadSoFar, DVDCSS_SEEK_KEY); + read1SectorATimeSkippingBadSectors(toRead); } - - this->iso->write((const char*)this->tmpBuffer, DVDCSS_BLOCK_SIZE * numRead); - - this->sectorsReadSoFar += numRead; - this->nonFileReadSoFar += numRead; - - } while (this->nonFileReadSoFar < this->nonFileRemain && this->keepRunning); + else { + this->iso->write((const char*)this->tmpBuffer, DVDCSS_BLOCK_SIZE * numRead); + this->sectorsReadSoFar += numRead; + this->fileReadSoFar += numRead; + } + } while ( (this->fileReadSoFar < this->fileRemain) && + (this->sectorsReadSoFar < this->drive->Sectors()) && + this->keepRunning); this->inFile = Li::Dvd::TitleKey::IsSectorInFile(this->sectorsReadSoFar); this->fileReadSoFar = 0; - this->nonFileReadSoFar = 0; + this->fileRemain = 0; } - void DvdRipper::readFileSectors() { + void DvdRipper::readNonFileSectors() { dvdcss_title(this->driveIoCtl->GetDvdCssHandle(), this->sectorsReadSoFar); + this->fileRemain = Li::Dvd::TitleKey::GetDistanceToNextFile(this->sectorsReadSoFar); - sectorInfo* sectorInf = Li::Dvd::TitleKey::GetSectorInfo(this->sectorsReadSoFar); - this->fileRemain = (sectorInf->endSector - sectorInf->startSector); - if ((this->sectorsReadSoFar + this->fileRemain) > this->drive->Sectors()) { this->fileRemain = (this->drive->Sectors() - this->sectorsReadSoFar); } this->fileReadSoFar = 0; - do { - int toRead = this->sectorsAtOnce; - if ((this->fileReadSoFar + toRead) > this->fileRemain) { - toRead = this->fileRemain - this->fileReadSoFar; - } - int numRead = dvdcss_read(driveIoCtl->GetDvdCssHandle(), this->tmpBuffer, toRead, DVDCSS_READ_DECRYPT); - - if (dvdcss_was_error(driveIoCtl->GetDvdCssHandle())) { - this->errorMsg = std::string(dvdcss_error(driveIoCtl->GetDvdCssHandle())); - } - if (numRead <= 0) { - numRead = read1SectorATimeSkippingBadSectors(toRead); - } + this->readSectors(); + } - iso->write((const char*)this->tmpBuffer, DVDCSS_BLOCK_SIZE * numRead); + void DvdRipper::readFileSectors() { + dvdcss_title(this->driveIoCtl->GetDvdCssHandle(), this->sectorsReadSoFar); + this->titleKey = (std::byte*)dvdcss_get_cur_titlekey(this->driveIoCtl->GetDvdCssHandle()); + sectorInfo* sectorInf = Li::Dvd::TitleKey::GetSectorInfo(this->sectorsReadSoFar); + this->fileRemain = (sectorInf->endSector - sectorInf->startSector); - this->sectorsReadSoFar += numRead; - this->fileReadSoFar += numRead; + if ((this->sectorsReadSoFar + this->fileRemain) > this->drive->Sectors()) { + this->fileRemain = (this->drive->Sectors() - this->sectorsReadSoFar); + } - } while ((this->fileReadSoFar < this->fileRemain) && this->keepRunning); - - this->inFile = Li::Dvd::TitleKey::IsSectorInFile(this->sectorsReadSoFar); this->fileReadSoFar = 0; - this->nonFileReadSoFar = 0; + + this->readSectors(); } void DvdRipper::ripThread() { - - this->sectorsReadSoFar = 0; Li::Dvd::TitleKey::GetTitleKeys(driveIoCtl->GetDvdCssHandle()); @@ -153,9 +147,9 @@ namespace Li::Dvd { do { if (this->inFile) - this->readFileSectors(); + this->readFileSectors(); // read & decrypt all files (including those associated w the filesystem) else - this->readNonFileSectors(); + this->readNonFileSectors(); // read the data not associated with any file on the filesystem. } while (this->sectorsReadSoFar < this->drive->Sectors() && this->keepRunning); @@ -172,9 +166,6 @@ namespace Li::Dvd { this->fileReadSoFar = 0; this->fileRemain = 0; - this->nonFileReadSoFar = 0; - this->nonFileRemain = 0; - this->inFile = false; this->done = false; this->error = false; @@ -182,7 +173,8 @@ namespace Li::Dvd { this->keepRunning = false; this->ripinThread = nullptr; - this->tmpBuffer = new uint8_t[DVDCSS_BLOCK_SIZE * this->sectorsAtOnce]; + this->tmpBuffer = new std::byte[DVDCSS_BLOCK_SIZE * this->sectorsAtOnce]; + } DvdRipper::~DvdRipper() { @@ -204,6 +196,11 @@ namespace Li::Dvd { this->driveIoCtl->AllowReadingPastDisc(); this->driveIoCtl->SetDriveSpeed(driveSpeed, driveSpeed); this->driveIoCtl->ExclusiveLockDrive(); + + this->discKey = (std::byte*)dvdcss_get_cur_disckey(this->driveIoCtl->GetDvdCssHandle()); + dvdcss_title(this->driveIoCtl->GetDvdCssHandle(), 0); + this->titleKey = (std::byte*)dvdcss_get_cur_titlekey(this->driveIoCtl->GetDvdCssHandle()); + this->iso = new std::ofstream(this->outputPath, std::ios::binary); this->keepRunning = true; diff --git a/DumpDVD/Dvd/DvdRipper.hpp b/DumpDVD/Dvd/DvdRipper.hpp index 32b23ec..88f5378 100644 --- a/DumpDVD/Dvd/DvdRipper.hpp +++ b/DumpDVD/Dvd/DvdRipper.hpp @@ -20,10 +20,6 @@ namespace Li::Dvd { uint32_t fileReadSoFar; uint32_t fileRemain; - uint32_t nonFileReadSoFar; - uint32_t nonFileRemain; - - uint32_t sectorsAtOnce; uint32_t sectorsReadSoFar; @@ -33,26 +29,33 @@ namespace Li::Dvd { bool keepRunning; std::ofstream* iso; - uint8_t* tmpBuffer; - + std::byte* tmpBuffer; + + std::byte* titleKey; + std::byte* discKey; + Li::Scsi::IoCtl* driveIoCtl; Li::Scsi::OpticalDrive* drive; void ripThread(); void readNonFileSectors(); void readFileSectors(); + + void readSectors(); int read1SectorATimeSkippingBadSectors(int toRead); public: DvdRipper(Li::Scsi::OpticalDrive* opticalDrive, std::string outputISO, uint32_t sectorsAtOnce); ~DvdRipper(); uint32_t SectorsReadSoFar(); - uint32_t FileReadSoFar(); float PercentDone(); std::string OutputPath(); uint32_t FileSoFar(); uint32_t FileLen(); + std::byte* GetTitleKey(); + std::byte* GetDiscKey(); + bool Done(); bool Error(); std::string ErrorMessage(); diff --git a/DumpDVD/Dvd/TitleKey.cpp b/DumpDVD/Dvd/TitleKey.cpp index 77c4721..b6860c2 100644 --- a/DumpDVD/Dvd/TitleKey.cpp +++ b/DumpDVD/Dvd/TitleKey.cpp @@ -29,12 +29,12 @@ namespace Li::Dvd { uint32_t TitleKey::GetDistanceToNextFile(uint32_t currentSector) { - uint32_t lowestDistance = -1; + uint32_t lowestDistance = 0xFFFFFFFF; for (sectorInfo* inf : sectorsList) { if (currentSector >= inf->startSector && currentSector < inf->endSector) return 0; - uint32_t distance = inf->startSector - currentSector; + int32_t distance = (inf->startSector - currentSector); if (distance < 0) continue; if (distance < lowestDistance) { @@ -69,18 +69,34 @@ namespace Li::Dvd { if (dent->name_len < 2) continue; + + uint32_t cSector = *(uint32_t*)dent->sector.le; + if ((dent->flags & DENT_ISDIR) != 0) { l9660_dir ndir; l9660_opendirat(&ndir, dir, dent->name); readDir(&ndir); } - else if (TitleKey::GetSectorInfo(*(uint32_t*)dent->size.le) == nullptr) { + else if (TitleKey::GetSectorInfo(cSector) == nullptr) { sectorInfo* file = new sectorInfo(); + file->startSector = cSector; - file->startSector = *(uint32_t*)dent->sector.le; - file->endSector = file->startSector + (*(uint32_t*)dent->size.le / DVDCSS_BLOCK_SIZE); + dvdcss_title(drv, file->startSector); + char* titleKey = dvdcss_get_cur_titlekey(drv); - sectorsList.push_back(file); + // check title is encrypted ... + if (memcmp(titleKey, "\0\0\0\0\0", 5)) { + // Round to sector boundary + uint32_t sz = (*(uint32_t*)dent->size.le); + if((sz % DVDCSS_BLOCK_SIZE) != 0) + sz += (DVDCSS_BLOCK_SIZE - (sz % DVDCSS_BLOCK_SIZE)); + file->endSector = (file->startSector + (sz / DVDCSS_BLOCK_SIZE)); + + sectorsList.push_back(file); + } + else { + delete file; + } } } diff --git a/DumpDVD/Gui/DumpDVD.cpp b/DumpDVD/Gui/DumpDVD.cpp index a6e264b..fd7154c 100644 --- a/DumpDVD/Gui/DumpDVD.cpp +++ b/DumpDVD/Gui/DumpDVD.cpp @@ -147,12 +147,24 @@ namespace Li::Gui { ImGui::ProgressBar(this->dvdRipper->PercentDone()); ImGui::Text("Sector %i / %i", this->dvdRipper->SectorsReadSoFar(), this->GetCurrentSelectedDrive()->Sectors()); - ImGui::Text("File: %i / %i", this->dvdRipper->FileReadSoFar(), this->dvdRipper->FileLen()); + ImGui::Text("File: %i / %i", this->dvdRipper->FileSoFar(), this->dvdRipper->FileLen()); + uint64_t szSoFar = (uint64_t)(this->dvdRipper->SectorsReadSoFar()) * (uint64_t)(DVDCSS_BLOCK_SIZE); uint64_t szTotal = (uint64_t)(this->GetCurrentSelectedDrive()->Sectors()) * (uint64_t)(DVDCSS_BLOCK_SIZE); ImGui::Text("Total %s / %s", Utils::HumanReadableByteStr(szSoFar).c_str(), Utils::HumanReadableByteStr(szTotal).c_str()); + + ImGui::SeparatorText("Content Scrambling System (CSS)"); + + std::byte* discKey = this->dvdRipper->GetDiscKey(); + std::byte* titleKey = this->dvdRipper->GetTitleKey(); + if (discKey != nullptr) + ImGui::Text("Disc Key: %02X%02X%02X%02X%02X", discKey[0], discKey[1], discKey[2], discKey[3], discKey[4]); + if (titleKey != nullptr) + ImGui::Text("Title Key: %02X%02X%02X%02X%02X", titleKey[0], titleKey[1], titleKey[2], titleKey[3], titleKey[4]); + + ImGui::SeparatorText("Output"); ImGui::Text("Output file: \"%s\"", this->outputFile); diff --git a/lib/libdvdcss.lib b/lib/libdvdcss.lib index 0af46a19174d849f0c6a189e0ac77cce3266b463..53a5d1e77c94f8a0a94506d12173de6e14acc791 100644 GIT binary patch delta 12983 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 diff --git a/libdvdcss/src/dvdcss/dvdcss.h b/libdvdcss/src/dvdcss/dvdcss.h index a38b1ad..7cb3604 100644 --- a/libdvdcss/src/dvdcss/dvdcss.h +++ b/libdvdcss/src/dvdcss/dvdcss.h @@ -106,6 +106,8 @@ LIBDVDCSS_EXPORT const char *dvdcss_error ( const dvdcss_t ); LIBDVDCSS_EXPORT int dvdcss_is_scrambled ( dvdcss_t ); +LIBDVDCSS_EXPORT char* dvdcss_get_cur_disckey(dvdcss_t dvdcss); +LIBDVDCSS_EXPORT char* dvdcss_get_cur_titlekey(dvdcss_t dvdcss); LIBDVDCSS_EXPORT int dvdcss_title(dvdcss_t dvdcss, int i_block); LIBDVDCSS_EXPORT int dvdcss_get_raw_fd(dvdcss_t); LIBDVDCSS_EXPORT int dvdcss_was_error(dvdcss_t); diff --git a/libdvdcss/src/libdvdcss.c b/libdvdcss/src/libdvdcss.c index 063d7dc..627fcc6 100644 --- a/libdvdcss/src/libdvdcss.c +++ b/libdvdcss/src/libdvdcss.c @@ -486,7 +486,16 @@ static void init_cache( dvdcss_t dvdcss ) /* If the cache is enabled, create a DVD-specific subdirectory. */ create_cache_subdir( dvdcss ); } -int dvdcss_was_error_common(dvdcss_t drive) { + +LIBDVDCSS_EXPORT char* dvdcss_get_cur_disckey(dvdcss_t dvdcss) { + return dvdcss->css.p_disc_key; +} +LIBDVDCSS_EXPORT char* dvdcss_get_cur_titlekey(dvdcss_t dvdcss) { + return dvdcss->css.p_title_key; +} + +LIBDVDCSS_EXPORT int dvdcss_was_error(dvdcss_t drive) +{ if (drive->b_errors) { drive->b_errors = FALSE; @@ -495,18 +504,9 @@ int dvdcss_was_error_common(dvdcss_t drive) { return FALSE; } -int dvdcss_get_fd_common(dvdcss_t drive) { - return drive->i_fd; -} - -LIBDVDCSS_EXPORT int dvdcss_was_error(dvdcss_t drive) -{ - return dvdcss_was_error_common(drive); -} - LIBDVDCSS_EXPORT int dvdcss_get_raw_fd(dvdcss_t drive) { - return dvdcss_get_fd_common(drive); + return drive->i_fd; } /**