#include #include #ifdef _WIN32 #include #include #endif #include #include #include #include #include #include #include #include "DumpDVD.hpp" #include "../Utils.hpp" #include "../Scsi/IoCtl.hpp" #include "../Scsi/OpticalDrive.hpp" namespace Li::Gui { DumpDVD::DumpDVD() { this->keepPolling = true; this->showDemoWindow = true; this->imRippinIt = false; this->error = false; this->done = false; this->selectedDrive = 0; this->windowFlags = ImGuiWindowFlags_NoCollapse; this->selectedDriveSpeed = 0; this->counter = 0; this->drivesList = Li::Scsi::OpticalDrive::ListOpticalDrives(); this->pollDrives = new std::thread(&DumpDVD::pollDrivesThread, this); this->sectorsReadSoFar = 0; this->sectorsAtOnce = 0x400; this->lock = new std::mutex(); this->reset(); } DumpDVD::~DumpDVD() { this->keepPolling = false; this->pollDrives->join(); delete this->pollDrives; freeOldDriveList(); } void DumpDVD::ripDiscThread() { std::ofstream* iso = new std::ofstream(std::string(this->outputFile), std::ios::binary); uint64_t totalSectors = this->GetCurrentSelectedDrive()->Sectors(); std::vector* speeds = this->GetCurrentSelectedDrive()->SupportedSpeeds(); uint32_t speed = 0xFFFF; if (this->selectedDriveSpeed < speeds->size()) { speed = speeds->at(this->selectedDriveSpeed); } Li::Scsi::IoCtl* drive = new Li::Scsi::IoCtl(this->GetCurrentSelectedDrive()->DrivePath()); drive->AllowReadingPastDisc(); drive->SetDriveSpeed(speed, speed); this->sectorsReadSoFar = 0; dvdcss_seek(drive->GetDvdCssHandle(), 0, DVDCSS_SEEK_KEY); uint8_t* buffer = new uint8_t[DVDCSS_BLOCK_SIZE * this->sectorsAtOnce]; do { int sectorsToRead = this->sectorsAtOnce; if ((this->sectorsReadSoFar + sectorsToRead) > totalSectors) { sectorsToRead = totalSectors - this->sectorsReadSoFar; } int numRead = dvdcss_read(drive->GetDvdCssHandle(), buffer, sectorsToRead, DVDCSS_READ_DECRYPT); if (numRead <= 0) { this->error = true; break; } iso->write((const char*)buffer, DVDCSS_BLOCK_SIZE * numRead); this->sectorsReadSoFar += numRead; } while (this->sectorsReadSoFar < totalSectors); this->done = true; delete buffer; delete drive; iso->close(); delete iso; } void DumpDVD::pollDrivesThread() { while (this->keepPolling) { if ( (this->counter % (60 * 10)) == 0) { this->lock->lock(); refreshDriveList(); this->lock->unlock(); } } } void DumpDVD::refreshDriveList() { std::vector* pollDrives = Li::Scsi::OpticalDrive::ListOpticalDrives(); freeOldDriveList(); this->drivesList = pollDrives; } void DumpDVD::freeOldDriveList() { std::vector* prevList = this->drivesList; this->drivesList = nullptr; for (Li::Scsi::OpticalDrive* drive : *prevList) { delete drive; } delete prevList; } std::string DumpDVD::getDrivesStr() { std::string drivesComboBox = ""; for (Li::Scsi::OpticalDrive* drive : *this->drivesList) { drivesComboBox += drive->DrivePath() + " - " + drive->VolumeName() + '\0'; } return drivesComboBox; } void DumpDVD::reset() { if (this->GetCurrentSelectedDrive()->DiscInDrive()) this->selectedDriveSpeed = this->GetCurrentSelectedDrive()->SupportedSpeeds()->size(); else this->selectedDriveSpeed = 0; std::string documentsFolder = ""; #ifdef _WIN32 WCHAR documents[MAX_PATH]; HRESULT result = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, documents); std::wstring wDocuments = std::wstring(documents); documentsFolder = std::string(wDocuments.begin(), wDocuments.end()); #endif std::filesystem::path p = std::filesystem::path(documentsFolder); p = p.append(GetCurrentSelectedDrive()->VolumeName()).replace_extension(".iso"); strncpy(this->outputFile, p.string().c_str(), sizeof(DumpDVD::outputFile)-1); } Li::Scsi::OpticalDrive* DumpDVD::GetCurrentSelectedDrive() { return this->drivesList->at(this->selectedDrive); } void DumpDVD::startRip() { refreshDriveList(); if (this->GetCurrentSelectedDrive()->DiscInDrive()) { this->keepPolling = false; this->pollDrives->join(); delete this->pollDrives; this->pollDrives = new std::thread(&DumpDVD::ripDiscThread, this); this->imRippinIt = true; } } void DumpDVD::endRipAndGoBackToMenu() { this->done = false; this->imRippinIt = false; this->reset(); this->sectorsReadSoFar = 0; this->pollDrives->join(); delete this->pollDrives; this->drivesList = Li::Scsi::OpticalDrive::ListOpticalDrives(); this->pollDrives = new std::thread(&DumpDVD::pollDrivesThread, this); } void DumpDVD::showRippingStatus() { ImGui::SeparatorText("Status"); float percentage = (((float)this->sectorsReadSoFar / (float)this->GetCurrentSelectedDrive()->Sectors())); ImGui::Text("Copying Disc ... ", (int)percentage); ImGui::SameLine(); ImGui::ProgressBar(percentage); ImGui::Text("Sector %lli / %lli", this->sectorsReadSoFar, this->GetCurrentSelectedDrive()->Sectors()); uint64_t szSoFar = this->sectorsReadSoFar * DVDCSS_BLOCK_SIZE; uint64_t szTotal = this->GetCurrentSelectedDrive()->Sectors() * DVDCSS_BLOCK_SIZE; ImGui::Text("Total %s / %s", Utils::HumanReadableByteStr(szSoFar).c_str(), Utils::HumanReadableByteStr(szTotal).c_str()); if (this->done) { if (!this->error) { ImGui::Begin("Complete", &done, this->windowFlags); ImGui::Text("ISO file was created successfully."); if (ImGui::Button("OK")) { this->endRipAndGoBackToMenu(); } ImGui::End(); } else { ImGui::Begin("Error", &done, this->windowFlags); ImGui::Text("Error occured when creating an ISO"); if (ImGui::Button("OK")) { this->endRipAndGoBackToMenu(); } ImGui::End(); } } } void DumpDVD::showRipMenu() { this->lock->lock(); ImGui::SeparatorText("Input"); ImGui::Text("Drive: "); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (ImGui::Combo("##driveList", &this->selectedDrive, this->getDrivesStr().c_str())) { this->reset(); } if (!GetCurrentSelectedDrive()->DiscInDrive()) { ImGui::Text("Put a disc in the drive to continue ..."); } else { int numDrvSpeeds = GetCurrentSelectedDrive()->SupportedSpeeds()->size() + 1; std::string driveSpeedName = "Unknown"; if (this->selectedDriveSpeed >= 0 && this->selectedDriveSpeed < GetCurrentSelectedDrive()->SupportedSpeeds()->size()) { uint32_t speed = GetCurrentSelectedDrive()->SupportedSpeeds()->at(this->selectedDriveSpeed); float spdFriendly = ((float)speed / (float)1352.0); std::ostringstream xAmt; xAmt.precision(1); xAmt << std::fixed << spdFriendly; driveSpeedName = xAmt.str() + "x (" + std::to_string(speed) + " kbps)"; } else if (this->selectedDriveSpeed == GetCurrentSelectedDrive()->SupportedSpeeds()->size()) { driveSpeedName = "As Fast as Possible"; } ImGui::Text("Read Speed: "); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SliderInt("##readSpeed", &this->selectedDriveSpeed, 0, numDrvSpeeds - 1, driveSpeedName.c_str()); ImGui::Text("Buffer Size (in sectors): "); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::InputInt("##bufferSize", &this->sectorsAtOnce, 1, 100); if (this->sectorsAtOnce <= 0) this->sectorsAtOnce = 1; ImGui::SeparatorText("Output"); ImGui::Text("Output file: "); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 60); ImGui::InputTextWithHint("##outputIso", "Output ISO file path", this->outputFile, IM_ARRAYSIZE(DumpDVD::outputFile)); ImGui::SameLine(); if (ImGui::Button("Browse")) { auto sav = pfd::save_file("Save ISO", std::string(this->outputFile), { "ISO9660 Image Files (.iso)", "*.iso" }, pfd::opt::force_overwrite); strncpy(this->outputFile, sav.result().c_str(), sizeof(DumpDVD::outputFile) - 1); } if (ImGui::Button("Create ISO", ImVec2(ImGui::GetContentRegionAvail().x, 20))) { this->startRip(); } } this->lock->unlock(); } void DumpDVD::RenderUI() { if (this->drivesList == nullptr) return; const ImGuiViewport* mainViewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Appearing); ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize, ImGuiCond_Appearing); ImGui::Begin("DVD Dumper", NULL, windowFlags); if (this->imRippinIt) { this->showRippingStatus(); } else { this->showRipMenu(); } ImGui::End(); #ifdef _DEBUG if (showDemoWindow) ImGui::ShowDemoWindow(&showDemoWindow); #endif this->counter++; } }