DumpDVD/DumpDVD/Gui/DumpDVD.cpp

315 lines
8.6 KiB
C++

#include <imgui.h>
#include <SDL.h>
#ifdef _WIN32
#include <Windows.h>
#include <shlobj.h>
#endif
#include <portable-file-dialogs.h>
#include <iostream>
#include <fstream>
#include <mutex>
#include <thread>
#include <sstream>
#include <filesystem>
#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<uint32_t>* 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<Li::Scsi::OpticalDrive*>* pollDrives = Li::Scsi::OpticalDrive::ListOpticalDrives();
freeOldDriveList();
this->drivesList = pollDrives;
}
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() {
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++;
}
}