DumpDVD/DumpDVD/Dvd/DvdRipper.cpp

233 lines
6.3 KiB
C++

#include "DvdRipper.hpp"
#include <iostream>
#include <fstream>
#include <thread>
#include <sstream>
#include <filesystem>
#include "../Scsi/IoCtl.hpp"
#include "../Scsi/OpticalDrive.hpp"
#include "../Dvd/TitleKey.hpp"
namespace Li::Dvd {
uint32_t DvdRipper::FileSoFar() {
if (this->inFile)
return this->fileReadSoFar;
else
return this->nonFileReadSoFar;
}
uint32_t DvdRipper::FileLen() {
if (this->inFile)
return this->fileRemain;
else
return this->nonFileRemain;
}
float DvdRipper::PercentDone() {
return (((float)this->sectorsReadSoFar / (float)this->drive->Sectors()));
}
uint32_t DvdRipper::SectorsReadSoFar() {
return this->sectorsReadSoFar;
}
uint32_t DvdRipper::FileReadSoFar() {
return this->fileReadSoFar;
}
std::string DvdRipper::OutputPath() {
return this->outputPath;
}
bool DvdRipper::Done() {
return this->done;
}
bool DvdRipper::Error() {
return this->error;
}
std::string DvdRipper::ErrorMessage() {
return this->errorMsg;
}
int DvdRipper::read1SectorATimeSkippingBadSectors(int toRead) {
memset(this->tmpBuffer, 0x00, this->sectorsAtOnce * DVDCSS_BLOCK_SIZE);
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);
if (dataRd < 0) {
continue; // retry
}
}
nmRd += 1;
}
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;
do {
int toRead = this->sectorsAtOnce;
if ((this->nonFileReadSoFar + toRead) > this->nonFileRemain) {
toRead = this->nonFileRemain - this->nonFileReadSoFar;
}
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);
}
this->iso->write((const char*)this->tmpBuffer, DVDCSS_BLOCK_SIZE * numRead);
this->sectorsReadSoFar += numRead;
this->nonFileReadSoFar += numRead;
} while (this->nonFileReadSoFar < this->nonFileRemain && this->keepRunning);
this->inFile = Li::Dvd::TitleKey::IsSectorInFile(this->sectorsReadSoFar);
this->fileReadSoFar = 0;
this->nonFileReadSoFar = 0;
}
void DvdRipper::readFileSectors() {
dvdcss_title(this->driveIoCtl->GetDvdCssHandle(), 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);
}
iso->write((const char*)this->tmpBuffer, DVDCSS_BLOCK_SIZE * numRead);
this->sectorsReadSoFar += numRead;
this->fileReadSoFar += numRead;
} while ((this->fileReadSoFar < this->fileRemain) && this->keepRunning);
this->inFile = Li::Dvd::TitleKey::IsSectorInFile(this->sectorsReadSoFar);
this->fileReadSoFar = 0;
this->nonFileReadSoFar = 0;
}
void DvdRipper::ripThread() {
this->sectorsReadSoFar = 0;
Li::Dvd::TitleKey::GetTitleKeys(driveIoCtl->GetDvdCssHandle());
dvdcss_seek(driveIoCtl->GetDvdCssHandle(), 0, DVDCSS_SEEK_KEY);
this->inFile = Li::Dvd::TitleKey::IsSectorInFile(this->sectorsReadSoFar);
do {
if (this->inFile)
this->readFileSectors();
else
this->readNonFileSectors();
} while (this->sectorsReadSoFar < this->drive->Sectors() && this->keepRunning);
this->done = true;
}
DvdRipper::DvdRipper(Li::Scsi::OpticalDrive* opticalDrive, std::string outputISO, uint32_t sectorsAtOnce) {
this->drive = opticalDrive;
this->outputPath = outputISO;
this->sectorsAtOnce = sectorsAtOnce;
this->sectorsReadSoFar = 0;
this->fileReadSoFar = 0;
this->fileRemain = 0;
this->nonFileReadSoFar = 0;
this->nonFileRemain = 0;
this->inFile = false;
this->done = false;
this->error = false;
this->errorMsg = "";
this->keepRunning = false;
this->ripinThread = nullptr;
this->tmpBuffer = new uint8_t[DVDCSS_BLOCK_SIZE * this->sectorsAtOnce];
}
DvdRipper::~DvdRipper() {
delete this->tmpBuffer;
if (this->ripinThread != nullptr) {
this->EndRip();
}
if (this->iso != nullptr) {
if (this->iso->is_open())
this->iso->close();
delete this->iso;
}
}
void DvdRipper::StartRip(uint32_t driveSpeed) {
this->driveIoCtl = new Li::Scsi::IoCtl(this->drive->DrivePath());
this->driveIoCtl->AllowReadingPastDisc();
this->driveIoCtl->SetDriveSpeed(driveSpeed, driveSpeed);
this->driveIoCtl->ExclusiveLockDrive();
this->iso = new std::ofstream(this->outputPath, std::ios::binary);
this->keepRunning = true;
this->ripinThread = new std::thread(&DvdRipper::ripThread, this);
}
void DvdRipper::EndRip() {
this->done = false;
this->error = false;
this->errorMsg = "";
this->sectorsReadSoFar = 0;
this->fileReadSoFar = 0;
this->keepRunning = false;
this->ripinThread->join();
delete this->driveIoCtl;
delete this->ripinThread;
this->ripinThread = nullptr;
iso->close();
delete iso;
this->iso = nullptr;
this->driveIoCtl->ExclusiveUnlockDrive();
}
}