2023-03-16 19:26:43 +00:00
|
|
|
#include "DumpDVD.hpp"
|
2023-03-15 06:59:59 +00:00
|
|
|
#include <imgui.h>
|
|
|
|
#include <SDL.h>
|
|
|
|
#include <portable-file-dialogs.h>
|
|
|
|
#include <mutex>
|
|
|
|
#include <thread>
|
|
|
|
#include <filesystem>
|
|
|
|
|
2023-03-16 19:26:43 +00:00
|
|
|
|
2023-03-15 06:59:59 +00:00
|
|
|
|
|
|
|
#include "../Utils.hpp"
|
|
|
|
#include "../Scsi/IoCtl.hpp"
|
|
|
|
#include "../Scsi/OpticalDrive.hpp"
|
2023-03-16 19:26:43 +00:00
|
|
|
#include "../Dvd/DvdRipper.hpp"
|
|
|
|
#include "../Dvd/TitleKey.hpp"
|
2023-03-15 06:59:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
namespace Li::Gui {
|
|
|
|
|
|
|
|
DumpDVD::DumpDVD() {
|
2023-03-16 08:35:31 +00:00
|
|
|
this->discInserted = false;
|
2023-03-15 06:59:59 +00:00
|
|
|
this->keepPolling = true;
|
|
|
|
this->showDemoWindow = true;
|
|
|
|
this->imRippinIt = false;
|
|
|
|
this->selectedDrive = 0;
|
|
|
|
this->selectedDriveSpeed = 0;
|
|
|
|
|
|
|
|
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() {
|
2023-03-19 02:37:35 +00:00
|
|
|
if (this->imRippinIt) {
|
|
|
|
this->endRipNow();
|
|
|
|
std::filesystem::remove(std::string(this->outputFile));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
this->keepPolling = false;
|
|
|
|
this->pollDrives->detach();
|
|
|
|
delete this->pollDrives;
|
|
|
|
}
|
|
|
|
|
2023-03-15 08:13:58 +00:00
|
|
|
delete this->lock;
|
2023-03-15 06:59:59 +00:00
|
|
|
freeOldDriveList();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DumpDVD::pollDrivesThread() {
|
|
|
|
while (this->keepPolling) {
|
2023-03-19 02:37:35 +00:00
|
|
|
this->refreshDriveList();
|
2023-03-21 10:12:28 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
2023-03-15 06:59:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DumpDVD::refreshDriveList() {
|
|
|
|
std::vector<Li::Scsi::OpticalDrive*>* pollDrives = Li::Scsi::OpticalDrive::ListOpticalDrives();
|
2023-03-16 08:35:31 +00:00
|
|
|
this->lock->lock();
|
2023-03-15 08:13:58 +00:00
|
|
|
this->freeOldDriveList();
|
2023-03-15 06:59:59 +00:00
|
|
|
this->drivesList = pollDrives;
|
2023-03-17 08:23:19 +00:00
|
|
|
|
|
|
|
if (this->selectedDrive >= this->drivesList->size())
|
|
|
|
this->reset();
|
|
|
|
|
2023-03-16 08:35:31 +00:00
|
|
|
this->lock->unlock();
|
2023-03-15 06:59:59 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
void DumpDVD::freeOldDriveList() {
|
|
|
|
std::vector<Li::Scsi::OpticalDrive*>* 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() {
|
2023-03-21 10:23:37 +00:00
|
|
|
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;
|
2023-03-17 08:23:19 +00:00
|
|
|
}
|
2023-03-21 10:23:37 +00:00
|
|
|
|
|
|
|
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);
|
2023-03-17 08:23:19 +00:00
|
|
|
}
|
|
|
|
else {
|
2023-03-15 06:59:59 +00:00
|
|
|
this->selectedDriveSpeed = 0;
|
2023-03-21 10:23:37 +00:00
|
|
|
memset(this->outputFile, 0, sizeof(DumpDVD::outputFile));
|
2023-03-17 08:23:19 +00:00
|
|
|
}
|
2023-03-15 06:59:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Li::Scsi::OpticalDrive* DumpDVD::GetCurrentSelectedDrive() {
|
|
|
|
return this->drivesList->at(this->selectedDrive);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DumpDVD::startRip() {
|
|
|
|
if (this->GetCurrentSelectedDrive()->DiscInDrive()) {
|
|
|
|
this->keepPolling = false;
|
2023-03-19 02:37:35 +00:00
|
|
|
this->pollDrives->detach();
|
2023-03-15 06:59:59 +00:00
|
|
|
delete this->pollDrives;
|
|
|
|
|
2023-03-16 19:26:43 +00:00
|
|
|
std::vector<uint32_t>* 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);
|
2023-03-17 08:23:19 +00:00
|
|
|
this->dvdRipper->StartRip(speed, this->decrypt);
|
2023-03-16 19:26:43 +00:00
|
|
|
|
2023-03-15 06:59:59 +00:00
|
|
|
this->imRippinIt = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-19 02:37:35 +00:00
|
|
|
void DumpDVD::endRipNow() {
|
2023-03-16 19:26:43 +00:00
|
|
|
this->dvdRipper->EndRip();
|
|
|
|
delete this->dvdRipper;
|
2023-03-15 06:59:59 +00:00
|
|
|
this->imRippinIt = false;
|
2023-03-19 02:37:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DumpDVD::endRipAndGoBackToMenu() {
|
|
|
|
this->endRipNow();
|
|
|
|
|
2023-03-15 06:59:59 +00:00
|
|
|
this->reset();
|
|
|
|
|
2023-03-16 08:35:31 +00:00
|
|
|
this->keepPolling = true;
|
2023-03-15 06:59:59 +00:00
|
|
|
this->drivesList = Li::Scsi::OpticalDrive::ListOpticalDrives();
|
|
|
|
this->pollDrives = new std::thread(&DumpDVD::pollDrivesThread, this);
|
|
|
|
}
|
|
|
|
|
2023-03-21 10:12:28 +00:00
|
|
|
void DumpDVD::showStarting() {
|
|
|
|
ImGui::SeparatorText("Status");
|
|
|
|
ImGui::Text("Starting copy...");
|
|
|
|
}
|
2023-03-15 06:59:59 +00:00
|
|
|
|
|
|
|
void DumpDVD::showRippingStatus() {
|
|
|
|
ImGui::SeparatorText("Status");
|
2023-03-21 10:12:28 +00:00
|
|
|
|
2023-03-16 19:26:43 +00:00
|
|
|
ImGui::Text("Copying Disc ... ");
|
2023-03-15 06:59:59 +00:00
|
|
|
ImGui::SameLine();
|
2023-03-16 19:26:43 +00:00
|
|
|
ImGui::ProgressBar(this->dvdRipper->PercentDone());
|
2023-03-15 06:59:59 +00:00
|
|
|
|
2023-03-21 10:12:28 +00:00
|
|
|
ImGui::Text("Sector %i / %i", this->dvdRipper->SectorsReadSoFar(), this->GetCurrentSelectedDrive()->Sectors());
|
2023-03-15 06:59:59 +00:00
|
|
|
|
2023-03-16 19:26:43 +00:00
|
|
|
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);
|
2023-03-15 06:59:59 +00:00
|
|
|
|
|
|
|
ImGui::Text("Total %s / %s", Utils::HumanReadableByteStr(szSoFar).c_str(), Utils::HumanReadableByteStr(szTotal).c_str());
|
2023-03-21 10:12:28 +00:00
|
|
|
|
|
|
|
if (this->dvdRipper->FailedReads() > 0) {
|
|
|
|
ImGui::SeparatorText("Errors");
|
|
|
|
ImGui::Text("Retried: %i", this->dvdRipper->RetriedReads());
|
|
|
|
ImGui::Text("Failed: %i", this->dvdRipper->FailedReads());
|
|
|
|
}
|
|
|
|
|
2023-03-17 08:23:19 +00:00
|
|
|
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();
|
2023-03-21 10:12:28 +00:00
|
|
|
|
2023-03-17 08:23:19 +00:00
|
|
|
if (discKey != nullptr)
|
|
|
|
ImGui::Text("Disc Key: %02X%02X%02X%02X%02X", discKey[0], discKey[1], discKey[2], discKey[3], discKey[4]);
|
2023-03-21 10:12:28 +00:00
|
|
|
|
2023-03-17 08:23:19 +00:00
|
|
|
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.");
|
|
|
|
}
|
2023-03-16 19:26:43 +00:00
|
|
|
ImGui::SeparatorText("Output");
|
2023-03-16 08:35:31 +00:00
|
|
|
ImGui::Text("Output file: \"%s\"", this->outputFile);
|
|
|
|
|
|
|
|
if (ImGui::Button("Cancel")) {
|
|
|
|
this->endRipAndGoBackToMenu();
|
|
|
|
std::filesystem::remove(std::string(this->outputFile));
|
2023-03-16 19:26:43 +00:00
|
|
|
return;
|
2023-03-16 08:35:31 +00:00
|
|
|
}
|
2023-03-15 06:59:59 +00:00
|
|
|
|
2023-03-16 19:26:43 +00:00
|
|
|
if (this->dvdRipper->Done()) {
|
|
|
|
if (!this->dvdRipper->Error()) {
|
2023-03-19 02:37:35 +00:00
|
|
|
ImGui::Begin("Complete", &this->imRippinIt, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize);
|
2023-03-15 06:59:59 +00:00
|
|
|
ImGui::Text("ISO file was created successfully.");
|
|
|
|
if (ImGui::Button("OK")) {
|
|
|
|
this->endRipAndGoBackToMenu();
|
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
}
|
|
|
|
else {
|
2023-03-21 10:12:28 +00:00
|
|
|
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")) {
|
2023-03-15 06:59:59 +00:00
|
|
|
this->endRipAndGoBackToMenu();
|
2023-03-16 08:35:31 +00:00
|
|
|
std::filesystem::remove(std::string(this->outputFile));
|
2023-03-15 06:59:59 +00:00
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
}
|
|
|
|
}
|
2023-03-16 08:35:31 +00:00
|
|
|
|
|
|
|
|
2023-03-15 06:59:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DumpDVD::showRipMenu() {
|
|
|
|
this->lock->lock();
|
|
|
|
|
|
|
|
ImGui::SeparatorText("Input");
|
|
|
|
|
2023-03-21 10:12:28 +00:00
|
|
|
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.");
|
2023-03-15 06:59:59 +00:00
|
|
|
}
|
|
|
|
else {
|
2023-03-21 10:12:28 +00:00
|
|
|
ImGui::Text("Drive: ");
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
|
|
|
if (ImGui::Combo("##driveList", &this->selectedDrive, this->getDrivesStr().c_str())) {
|
2023-03-16 08:35:31 +00:00
|
|
|
this->reset();
|
|
|
|
}
|
2023-03-15 06:59:59 +00:00
|
|
|
|
2023-03-21 10:12:28 +00:00
|
|
|
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;
|
2023-03-15 06:59:59 +00:00
|
|
|
|
2023-03-21 10:12:28 +00:00
|
|
|
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);
|
2023-03-15 06:59:59 +00:00
|
|
|
|
2023-03-21 10:12:28 +00:00
|
|
|
std::ostringstream xAmt;
|
|
|
|
xAmt.precision(1);
|
|
|
|
xAmt << std::fixed << spdFriendly;
|
2023-03-15 06:59:59 +00:00
|
|
|
|
2023-03-21 10:12:28 +00:00
|
|
|
driveSpeedName = xAmt.str() + "x (" + std::to_string(speed) + " kbps)";
|
|
|
|
}
|
|
|
|
else if (this->selectedDriveSpeed == GetCurrentSelectedDrive()->SupportedSpeeds()->size()) {
|
|
|
|
driveSpeedName = "As Fast as Possible";
|
|
|
|
}
|
2023-03-15 06:59:59 +00:00
|
|
|
|
|
|
|
|
2023-03-21 10:12:28 +00:00
|
|
|
ImGui::Text("Read Speed: ");
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
|
|
|
ImGui::SliderInt("##readSpeed", &this->selectedDriveSpeed, 0, numDrvSpeeds - 1, driveSpeedName.c_str());
|
2023-03-15 06:59:59 +00:00
|
|
|
|
2023-03-21 10:12:28 +00:00
|
|
|
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 ...)");
|
|
|
|
}
|
2023-03-17 08:23:19 +00:00
|
|
|
|
2023-03-21 10:12:28 +00:00
|
|
|
ImGui::SeparatorText("Output");
|
2023-03-15 06:59:59 +00:00
|
|
|
|
2023-03-21 10:12:28 +00:00
|
|
|
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);
|
|
|
|
}
|
2023-03-15 06:59:59 +00:00
|
|
|
|
2023-03-21 10:12:28 +00:00
|
|
|
if (ImGui::Button("Create ISO", ImVec2(ImGui::GetContentRegionAvail().x, 20))) {
|
|
|
|
this->startRip();
|
|
|
|
}
|
2023-03-15 06:59:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
this->lock->unlock();
|
|
|
|
}
|
|
|
|
|
2023-03-20 09:39:23 +00:00
|
|
|
|
2023-03-15 06:59:59 +00:00
|
|
|
void DumpDVD::RenderUI() {
|
|
|
|
if (this->drivesList == nullptr) return;
|
|
|
|
|
|
|
|
const ImGuiViewport* mainViewport = ImGui::GetMainViewport();
|
2023-03-17 08:25:44 +00:00
|
|
|
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
|
|
|
ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize, ImGuiCond_Always);
|
2023-03-15 06:59:59 +00:00
|
|
|
|
2023-03-19 02:37:35 +00:00
|
|
|
ImGui::Begin("DVD Dumper", NULL, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
|
2023-03-17 08:25:44 +00:00
|
|
|
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoFocusOnAppearing);
|
2023-03-15 06:59:59 +00:00
|
|
|
|
|
|
|
if (this->imRippinIt)
|
|
|
|
{
|
2023-03-21 10:12:28 +00:00
|
|
|
if (this->dvdRipper->Starting()) {
|
|
|
|
this->showStarting();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
this->showRippingStatus();
|
|
|
|
}
|
2023-03-15 06:59:59 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this->showRipMenu();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ImGui::End();
|
|
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
|
if (showDemoWindow)
|
|
|
|
ImGui::ShowDemoWindow(&showDemoWindow);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|