#include "DumpDVD.hpp" #include #include #include #include #include #include #include "Renderer.hpp" #include "../Utils.hpp" #include "../Scsi/IoCtl.hpp" #include "../Scsi/OpticalDrive.hpp" #include "../Dvd/DvdRipper.hpp" #include "../Dvd/TitleKey.hpp" namespace Li::Gui { DumpDVD::DumpDVD(Renderer* renderer) { this->discInserted = false; this->keepPolling = true; this->showDemoWindow = true; this->imRippinIt = false; this->selectedDrive = 0; this->selectedDriveSpeed = 0; this->dvdSpin = DvdSpin::RenderDvdSpin::CreateDvdSpinner(renderer); this->drivesList = Li::Scsi::OpticalDrive::ListOpticalDrives(); this->pollDrives = new std::thread(&DumpDVD::pollDrivesThread, this); this->sectorsAtOnce = 0x400; this->lock = new std::mutex(); this->reset(); } DumpDVD::~DumpDVD() { if (this->imRippinIt) { this->endRipNow(); std::filesystem::remove(std::string(this->outputFile)); } else { this->keepPolling = false; this->lock->lock(); this->pollDrives->detach(); delete this->pollDrives; this->lock->unlock(); } DvdSpin::RenderDvdSpin::DeleteDvdSpinner(this->dvdSpin); delete this->lock; freeOldDriveList(); } void DumpDVD::pollDrivesThread() { while (this->keepPolling) { this->refreshDriveList(); std::this_thread::sleep_for(std::chrono::seconds(5)); } } void DumpDVD::updateSpinTexture() { if (this->dvdSpin != nullptr) { if (this->drivesList->size() >= 0) { this->dvdSpin->UpdateTexture(this->GetCurrentSelectedDrive()->MediaType()); } else { this->dvdSpin->UpdateTexture(DvdSpin::CdType::UNKNOWN_DISC); } } } void DumpDVD::refreshDriveList() { std::vector* pollDrives = Li::Scsi::OpticalDrive::ListOpticalDrives(); this->lock->lock(); this->freeOldDriveList(); this->drivesList = pollDrives; if (this->selectedDrive >= this->drivesList->size()) this->reset(); this->updateSpinTexture(); this->lock->unlock(); } 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->drivesList->size() > 0) { if (this->GetCurrentSelectedDrive()->DiscInDrive()) { this->selectedDriveSpeed = this->GetCurrentSelectedDrive()->SupportedSpeeds()->size(); if (this->GetCurrentSelectedDrive()->HasCss()) { this->decrypt = true; } } else { this->selectedDriveSpeed = 0; } std::filesystem::path p = std::filesystem::path(Utils::GetDocumentsFolder()); p = p.append(GetCurrentSelectedDrive()->VolumeName()).replace_extension(".iso"); strncpy(this->outputFile, p.string().c_str(), sizeof(DumpDVD::outputFile) - 1); } else { this->selectedDriveSpeed = 0; memset(this->outputFile, 0, sizeof(DumpDVD::outputFile)); } this->updateSpinTexture(); } Li::Scsi::OpticalDrive* DumpDVD::GetCurrentSelectedDrive() { if (this->drivesList->size() <= 0) return nullptr; return this->drivesList->at(this->selectedDrive); } void DumpDVD::startRip() { if (this->GetCurrentSelectedDrive()->DiscInDrive()) { this->keepPolling = false; this->pollDrives->detach(); delete this->pollDrives; std::vector* speeds = this->GetCurrentSelectedDrive()->SupportedSpeeds(); uint32_t speed = 0xFFFF; if (this->selectedDriveSpeed < speeds->size()) { speed = speeds->at(this->selectedDriveSpeed); } this->dvdRipper = new Li::Dvd::DvdRipper(this->GetCurrentSelectedDrive(), std::string(this->outputFile), this->sectorsAtOnce); this->dvdRipper->StartRip(speed, this->decrypt); this->imRippinIt = true; } } void DumpDVD::endRipNow() { this->dvdRipper->EndRip(); delete this->dvdRipper; this->imRippinIt = false; } void DumpDVD::endRipAndGoBackToMenu() { this->endRipNow(); this->reset(); this->keepPolling = true; this->drivesList = Li::Scsi::OpticalDrive::ListOpticalDrives(); this->pollDrives = new std::thread(&DumpDVD::pollDrivesThread, this); } void DumpDVD::showStarting() { ImGui::SeparatorText("Status"); ImGui::Text("Starting copy..."); } void DumpDVD::showRippingStatus() { ImGui::SeparatorText("Status"); ImGui::Text("Copying Disc ... "); ImGui::SameLine(); ImGui::ProgressBar(this->dvdRipper->PercentDone()); ImGui::Text("Sector %i / %i", this->dvdRipper->SectorsReadSoFar(), this->GetCurrentSelectedDrive()->Sectors()); 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()); if (this->dvdRipper->FailedReads() > 0) { ImGui::SeparatorText("Errors"); ImGui::Text("Retried: %i", this->dvdRipper->RetriedReads()); ImGui::Text("Failed: %i", this->dvdRipper->FailedReads()); } ImGui::SeparatorText("Copy Protection"); if (this->GetCurrentSelectedDrive()->HasCss()) { ImGui::Text("Copy Protection: \"Content Scrambling System\" (CSS)"); ImGui::Text("Decrypt Enabled? %s", this->decrypt ? "Yes" : "No"); if (this->decrypt) { 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]); } } else { ImGui::Text("Copy Protection: None Detected."); } ImGui::SeparatorText("Output"); ImGui::Text("Output file: \"%s\"", this->outputFile); if (ImGui::Button("Cancel")) { this->endRipAndGoBackToMenu(); std::filesystem::remove(std::string(this->outputFile)); return; } if (this->dvdRipper->Done()) { if (!this->dvdRipper->Error()) { ImGui::Begin("Complete", &this->imRippinIt, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize); ImGui::Text("ISO file was created successfully."); if (ImGui::Button("OK")) { this->endRipAndGoBackToMenu(); } ImGui::End(); } else { ImGui::Begin("Completed with Error", &this->imRippinIt, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize); ImGui::Text("ISO file was created with errors ...\n\"%s\"\nDo you want to keep the ISO File?", this->dvdRipper->ErrorMessage().c_str()); if (ImGui::Button("Keep")) { this->endRipAndGoBackToMenu(); } ImGui::SameLine(); if(ImGui::Button("Remove")) { this->endRipAndGoBackToMenu(); std::filesystem::remove(std::string(this->outputFile)); } ImGui::End(); } } } void DumpDVD::showRipMenu() { this->lock->lock(); ImGui::SeparatorText("Input"); if (this->drivesList == nullptr || this->drivesList->size() <= 0) { ImGui::Text("No Optical Drives were found!"); ImGui::Text("Please connect an CD, DVD or Blu Ray drive to this computer."); ImGui::Text("\n"); ImGui::Text("If you do have one connected, please make sure it is working properly"); ImGui::Text("and that is not being used by another process."); } else { 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 ..."); this->discInserted = false; } else { if (!this->discInserted) { this->discInserted = true; this->reset(); } 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("Copy Protection"); if (this->GetCurrentSelectedDrive()->HasCss()) { ImGui::Text("Copy Protection: \"Content Scrambling System\" (CSS)"); ImGui::Checkbox("Decrypt/Remove CSS", &this->decrypt); } else { ImGui::Text("Copy Protection: None Detected.\n(This doesn't completely confirm there isn't any ...)"); } 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() { this->dvdSpin->RenderDVD(); if (this->drivesList == nullptr) return; const ImGuiViewport* mainViewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always); ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize, ImGuiCond_Always); ImGui::Begin("Disc Dumper", NULL, /* ImGuiWindowFlags_NoCollapse | */ ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoFocusOnAppearing); if (this->imRippinIt) { if (this->dvdRipper->Starting()) { this->showStarting(); } else { this->showRippingStatus(); } } else { this->showRipMenu(); } ImGui::End(); #ifdef _DEBUG if (showDemoWindow) ImGui::ShowDemoWindow(&showDemoWindow); #endif } }