diff --git a/DumpDVD/Dvd/DvdRipper.cpp b/DumpDVD/Dvd/DvdRipper.cpp index fc6dfa5..a651577 100644 --- a/DumpDVD/Dvd/DvdRipper.cpp +++ b/DumpDVD/Dvd/DvdRipper.cpp @@ -33,13 +33,17 @@ namespace Li::Dvd { } std::byte* DvdRipper::GetDiscKey() { - return this->discKey; + return this->drive->DiscKey(); } std::byte* DvdRipper::GetTitleKey() { return this->titleKey; } + bool DvdRipper::Decrypt() { + return this->decrypt; + } + bool DvdRipper::Done() { return this->done; } @@ -54,12 +58,12 @@ namespace Li::Dvd { for (int i = 0; i < toRead; i++) { for (int retry = 0; retry < 10; retry++) { memset(this->tmpBuffer, 0x00, DVDCSS_BLOCK_SIZE); - int dataRd = dvdcss_read(driveIoCtl->GetDvdCssHandle(), this->tmpBuffer, 1, DVDCSS_READ_DECRYPT); + int dataRd = dvdcss_read(driveIoCtl->GetDvdCssHandle(), this->tmpBuffer, 1, (this->decrypt ? DVDCSS_READ_DECRYPT : DVDCSS_NOFLAGS)); if (dataRd < 0) { - dvdcss_seek(driveIoCtl->GetDvdCssHandle(), this->sectorsReadSoFar, DVDCSS_SEEK_KEY); + dvdcss_seek(driveIoCtl->GetDvdCssHandle(), this->sectorsReadSoFar, (this->decrypt ? DVDCSS_SEEK_KEY : DVDCSS_NOFLAGS) ); if (retry+1 >= 10) - dvdcss_seek(driveIoCtl->GetDvdCssHandle(), this->sectorsReadSoFar + 1, DVDCSS_SEEK_KEY); + dvdcss_seek(driveIoCtl->GetDvdCssHandle(), this->sectorsReadSoFar + 1, (this->decrypt ? DVDCSS_SEEK_KEY : DVDCSS_NOFLAGS)); continue; // retry } @@ -80,7 +84,7 @@ namespace Li::Dvd { } - void DvdRipper::readSectors() { + void DvdRipper::readCssSectors() { do { int toRead = this->sectorsAtOnce; if ((this->fileReadSoFar + toRead) > this->fileRemain) { @@ -120,7 +124,7 @@ namespace Li::Dvd { this->fileReadSoFar = 0; - this->readSectors(); + this->readCssSectors(); } void DvdRipper::readFileSectors() { @@ -135,21 +139,63 @@ namespace Li::Dvd { this->fileReadSoFar = 0; - this->readSectors(); + this->readCssSectors(); + } + + void DvdRipper::readUnprotectedSectors() { + do { + int toRead = this->sectorsAtOnce; + if ((this->sectorsReadSoFar + toRead) > this->drive->Sectors()) { + toRead = this->drive->Sectors() - this->sectorsReadSoFar; + } + + int numRead = dvdcss_read(this->driveIoCtl->GetDvdCssHandle(), this->tmpBuffer, toRead, DVDCSS_NOFLAGS); + + if (dvdcss_was_error(driveIoCtl->GetDvdCssHandle())) { + this->errorMsg = std::string(dvdcss_error(driveIoCtl->GetDvdCssHandle())); + } + if (numRead < 0) { // Handle bad sectors + dvdcss_seek(driveIoCtl->GetDvdCssHandle(), this->sectorsReadSoFar, DVDCSS_NOFLAGS); + read1SectorATimeSkippingBadSectors(toRead); + } + else { + this->iso->write((const char*)this->tmpBuffer, DVDCSS_BLOCK_SIZE * numRead); + this->sectorsReadSoFar += numRead; + this->fileReadSoFar += numRead; + } + + } while (this->sectorsReadSoFar < this->drive->Sectors() && this->keepRunning); } 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); + // due to how libdvdcss works, you can only actually decrypt a dvd sector + // if you seek directly at it or call dvdcss_title on the sector containing the start of the encrypted title file + // so, i have to actually parse the disc file system, in order to find all those + // + if (this->decrypt) { + Li::Dvd::TitleKey::FindTitleKeys(driveIoCtl->GetDvdCssHandle()); + dvdcss_seek(driveIoCtl->GetDvdCssHandle(), 0, DVDCSS_SEEK_KEY); + this->inFile = Li::Dvd::TitleKey::IsSectorInFile(this->sectorsReadSoFar); + } do { - if (this->inFile) - this->readFileSectors(); // read & decrypt all files (including those associated w the filesystem) - else - this->readNonFileSectors(); // read the data not associated with any file on the filesystem. + if (this->decrypt) { + if (this->inFile) { + // read & decrypt all files (including those associated w the filesystem) + this->readFileSectors(); + } + else { + // read the data not associated with any file on the filesystem. + this->readNonFileSectors(); + } + } + else { + // for non scrambled dvds, just read every sector one after another. + this->readUnprotectedSectors(); + } + } while (this->sectorsReadSoFar < this->drive->Sectors() && this->keepRunning); @@ -190,16 +236,22 @@ namespace Li::Dvd { } } - void DvdRipper::StartRip(uint32_t driveSpeed) { + void DvdRipper::StartRip(uint32_t driveSpeed, bool cssDecrypt) { this->driveIoCtl = new Li::Scsi::IoCtl(this->drive->DrivePath()); this->driveIoCtl->AllowReadingPastDisc(); this->driveIoCtl->SetDriveSpeed(driveSpeed, driveSpeed); this->driveIoCtl->ExclusiveLockDrive(); - this->discKey = (std::byte*)dvdcss_get_cur_disckey(this->driveIoCtl->GetDvdCssHandle()); - dvdcss_title(this->driveIoCtl->GetDvdCssHandle(), 0); - this->titleKey = (std::byte*)dvdcss_get_cur_titlekey(this->driveIoCtl->GetDvdCssHandle()); + this->decrypt = cssDecrypt; + if (!dvdcss_is_scrambled(this->driveIoCtl->GetDvdCssHandle())) { + this->decrypt = false; + } + + if (this->decrypt) { + dvdcss_title(this->driveIoCtl->GetDvdCssHandle(), 0); + this->titleKey = (std::byte*)dvdcss_get_cur_titlekey(this->driveIoCtl->GetDvdCssHandle()); + } this->iso = new std::ofstream(this->outputPath, std::ios::binary); this->keepRunning = true; diff --git a/DumpDVD/Dvd/DvdRipper.hpp b/DumpDVD/Dvd/DvdRipper.hpp index 88f5378..1abda7e 100644 --- a/DumpDVD/Dvd/DvdRipper.hpp +++ b/DumpDVD/Dvd/DvdRipper.hpp @@ -27,12 +27,12 @@ namespace Li::Dvd { std::thread* ripinThread; bool keepRunning; + bool decrypt; std::ofstream* iso; std::byte* tmpBuffer; std::byte* titleKey; - std::byte* discKey; Li::Scsi::IoCtl* driveIoCtl; Li::Scsi::OpticalDrive* drive; @@ -40,7 +40,9 @@ namespace Li::Dvd { void readNonFileSectors(); void readFileSectors(); - void readSectors(); + void readUnprotectedSectors(); + void readCssSectors(); + int read1SectorATimeSkippingBadSectors(int toRead); public: DvdRipper(Li::Scsi::OpticalDrive* opticalDrive, std::string outputISO, uint32_t sectorsAtOnce); @@ -53,6 +55,7 @@ namespace Li::Dvd { uint32_t FileSoFar(); uint32_t FileLen(); + bool Decrypt(); std::byte* GetTitleKey(); std::byte* GetDiscKey(); @@ -60,7 +63,7 @@ namespace Li::Dvd { bool Error(); std::string ErrorMessage(); - void StartRip(uint32_t driveSpeed); + void StartRip(uint32_t driveSpeed, bool decryptCss); void EndRip(); }; } diff --git a/DumpDVD/Dvd/TitleKey.cpp b/DumpDVD/Dvd/TitleKey.cpp index b6860c2..a1d6343 100644 --- a/DumpDVD/Dvd/TitleKey.cpp +++ b/DumpDVD/Dvd/TitleKey.cpp @@ -11,6 +11,8 @@ namespace Li::Dvd { static std::vector sectorsList; static dvdcss_t drv; + static int depth = 0; + bool TitleKey::readSector(l9660_fs* fs, void* buf, uint32_t sector) { dvdcss_seek(drv, sector, DVDCSS_SEEK_KEY); @@ -58,10 +60,16 @@ namespace Li::Dvd { } - void readDir(l9660_dir* dir) { - while (true) { + bool TitleKey::readDir(l9660_dir* dir, int depth=0) { + + if (depth > 10) return false; + + bool success = true; + for(int cnt = 0; cnt < 90000; ++cnt) { + if (cnt >= 90000) return false; + l9660_dirent* dent; - l9660_status status = l9660_readdir(dir, &dent); + if (l9660_readdir(dir, &dent) != L9660_OK) return false; if (dent == nullptr) break; @@ -69,13 +77,12 @@ namespace Li::Dvd { if (dent->name_len < 2) continue; - uint32_t cSector = *(uint32_t*)dent->sector.le; if ((dent->flags & DENT_ISDIR) != 0) { l9660_dir ndir; - l9660_opendirat(&ndir, dir, dent->name); - readDir(&ndir); + if (l9660_opendirat(&ndir, dir, dent->name) != L9660_OK) return false; + success = readDir(&ndir, depth + 1); } else if (TitleKey::GetSectorInfo(cSector) == nullptr) { sectorInfo* file = new sectorInfo(); @@ -98,11 +105,14 @@ namespace Li::Dvd { delete file; } } - + } + + + return success; } - std::vector TitleKey::GetTitleKeys(dvdcss_t drive) { + bool TitleKey::FindTitleKeys(dvdcss_t drive) { drv = drive; sectorsList = std::vector(); @@ -110,11 +120,13 @@ namespace Li::Dvd { l9660_fs isoFs; l9660_status status; - status = l9660_openfs(&isoFs, TitleKey::readSector); - status = l9660_fs_open_root(&rootDir, &isoFs); - readDir(&rootDir); + if (l9660_openfs(&isoFs, TitleKey::readSector) == L9660_OK) { + if (l9660_fs_open_root(&rootDir, &isoFs) == L9660_OK) { + return TitleKey::readDir(&rootDir); + } + } - return sectorsList; + return false; } void TitleKey::FreeMemory() { diff --git a/DumpDVD/Dvd/TitleKey.hpp b/DumpDVD/Dvd/TitleKey.hpp index 4788c78..994809b 100644 --- a/DumpDVD/Dvd/TitleKey.hpp +++ b/DumpDVD/Dvd/TitleKey.hpp @@ -13,8 +13,9 @@ namespace Li::Dvd { class TitleKey { private: static bool readSector(l9660_fs* fs, void* buf, uint32_t sector); + static bool readDir(l9660_dir* dir, int depth); public: - static std::vector GetTitleKeys(dvdcss_t drive); + static bool FindTitleKeys(dvdcss_t drive); static uint32_t GetDistanceToNextFile(uint32_t currentSector); static bool IsSectorInFile(uint32_t currentSector); static sectorInfo* GetSectorInfo(uint32_t currentSector); diff --git a/DumpDVD/Gui/DumpDVD.cpp b/DumpDVD/Gui/DumpDVD.cpp index fd7154c..99f25f0 100644 --- a/DumpDVD/Gui/DumpDVD.cpp +++ b/DumpDVD/Gui/DumpDVD.cpp @@ -1,14 +1,7 @@ #include "DumpDVD.hpp" #include #include - -#ifdef _WIN32 -#include -#include -#endif - #include - #include #include #include @@ -63,6 +56,10 @@ namespace Li::Gui { this->lock->lock(); this->freeOldDriveList(); this->drivesList = pollDrives; + + if (this->selectedDrive >= this->drivesList->size()) + this->reset(); + this->lock->unlock(); } @@ -84,21 +81,17 @@ namespace Li::Gui { } void DumpDVD::reset() { - if (this->GetCurrentSelectedDrive()->DiscInDrive()) + if (this->GetCurrentSelectedDrive()->DiscInDrive()) { this->selectedDriveSpeed = this->GetCurrentSelectedDrive()->SupportedSpeeds()->size(); - else + if (this->GetCurrentSelectedDrive()->HasCss()) { + this->decrypt = true; + } + } + 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); + 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); } @@ -120,7 +113,7 @@ namespace Li::Gui { } this->dvdRipper = new Li::Dvd::DvdRipper(this->GetCurrentSelectedDrive(), std::string(this->outputFile), this->sectorsAtOnce); - this->dvdRipper->StartRip(speed); + this->dvdRipper->StartRip(speed, this->decrypt); this->imRippinIt = true; } @@ -147,24 +140,32 @@ namespace Li::Gui { ImGui::ProgressBar(this->dvdRipper->PercentDone()); ImGui::Text("Sector %i / %i", this->dvdRipper->SectorsReadSoFar(), this->GetCurrentSelectedDrive()->Sectors()); - ImGui::Text("File: %i / %i", this->dvdRipper->FileSoFar(), this->dvdRipper->FileLen()); - 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()); - ImGui::SeparatorText("Content Scrambling System (CSS)"); + ImGui::SeparatorText("Copy Protection"); - 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]); + 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); @@ -209,7 +210,6 @@ namespace Li::Gui { this->reset(); } - if (!GetCurrentSelectedDrive()->DiscInDrive()) { ImGui::Text("Put a disc in the drive to continue ..."); this->discInserted = false; @@ -248,6 +248,15 @@ namespace Li::Gui { 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: "); diff --git a/DumpDVD/Gui/DumpDVD.hpp b/DumpDVD/Gui/DumpDVD.hpp index 9c21ffe..a92e6ba 100644 --- a/DumpDVD/Gui/DumpDVD.hpp +++ b/DumpDVD/Gui/DumpDVD.hpp @@ -20,6 +20,7 @@ namespace Li::Gui { std::mutex* lock; int sectorsAtOnce; + bool decrypt; bool discInserted; int selectedDrive; int selectedDriveSpeed; diff --git a/DumpDVD/Gui/SDL.cpp b/DumpDVD/Gui/SDL.cpp index 1ca386f..9fda9b0 100644 --- a/DumpDVD/Gui/SDL.cpp +++ b/DumpDVD/Gui/SDL.cpp @@ -1,9 +1,12 @@ #include "SDL.hpp" + #ifdef _WIN32 #include "D3D.hpp" #include #include #endif + +#include #include #include @@ -14,7 +17,7 @@ namespace Li::Gui { SDL::SDL(std::string windowTitle, int windowWidth, int windowHeight) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) { - std::cout << "Error: " << SDL_GetError() << std::endl; + std::cerr << "Error: " << SDL_GetError() << std::endl; return; } @@ -29,6 +32,8 @@ namespace Li::Gui { #ifdef _WIN32 HWND hwnd = (HWND)this->wmInfo.info.win.window; this->renderer = new D3D(hwnd); +#else +#error no renderer for this platform #endif IMGUI_CHECKVERSION(); @@ -42,6 +47,8 @@ namespace Li::Gui { #ifdef _WIN32 ImGui_ImplSDL2_InitForD3D(this->window); this->renderer->InitImgui(); +#else +#error No imgui renderer backend provided for this platform #endif isExiting = false; } @@ -63,6 +70,8 @@ namespace Li::Gui { #ifdef _WIN32 D3D* d3d = (D3D*)this->renderer; d3d->Resize(); +#else +#error No resize method provided for this platform #endif } } diff --git a/DumpDVD/Scsi/IoCtl.cpp b/DumpDVD/Scsi/IoCtl.cpp index 62894aa..9f0d65e 100644 --- a/DumpDVD/Scsi/IoCtl.cpp +++ b/DumpDVD/Scsi/IoCtl.cpp @@ -83,6 +83,9 @@ namespace Li::Scsi { } delete buffer; + +#else +#error no way to get all supported read speeds on this platform #endif return speeds; } @@ -98,6 +101,8 @@ namespace Li::Scsi { std::cerr << "Error getting geometry: " << std::to_string(GetLastError()) << std::endl; } return (uint64_t)(geometry.DiskSize.QuadPart / DVDCSS_BLOCK_SIZE); +#else +#error no way to get total sector count of disc on this platform .. #endif } bool IoCtl::AllowReadingPastDisc() { @@ -110,6 +115,8 @@ namespace Li::Scsi { std::cerr << "Error enabling DASD I/O: " << std::to_string(GetLastError()) << std::endl; } return success; +#else +#error no way to disable bounds checks (DASHD) i/o on this platform (maybe not required?) #endif } @@ -121,6 +128,8 @@ namespace Li::Scsi { std::cerr << "Error unmounting drive: " << std::to_string(GetLastError()) << std::endl; } return success; +#else +#error no way to unmount drive on this platform #endif } @@ -132,6 +141,8 @@ namespace Li::Scsi { std::cerr << "Error mounting drive: " << std::to_string(GetLastError()) << std::endl; } return success; +#else +#error no way to mount drive on this platform #endif } @@ -154,6 +165,8 @@ namespace Li::Scsi { std::cerr << "Error Locking the drive: " << std::to_string(GetLastError()) << std::endl; } return success; +#else +#error no way to exclusive lock drive on this platform. #endif } @@ -171,6 +184,8 @@ namespace Li::Scsi { this->MountVolume(); return success; +#else +#error no way to exclusive unlock drive on this platform. #endif } @@ -191,6 +206,8 @@ namespace Li::Scsi { std::cerr << "Error setting speed: " << std::to_string(GetLastError()) << std::endl; } return success; +#else +#error no way to set drive speed folder on this platform. #endif } diff --git a/DumpDVD/Scsi/OpticalDrive.cpp b/DumpDVD/Scsi/OpticalDrive.cpp index 480ede1..dd9e9fc 100644 --- a/DumpDVD/Scsi/OpticalDrive.cpp +++ b/DumpDVD/Scsi/OpticalDrive.cpp @@ -1,5 +1,6 @@ #include "OpticalDrive.hpp" #include "IoCtl.hpp" +#include "SDL.h" #ifdef _WIN32 #include @@ -30,6 +31,8 @@ namespace Li::Scsi { } delete wChrVolumeName; +#else +#error no way to get optical drive information on this platform #endif } @@ -40,6 +43,15 @@ namespace Li::Scsi { IoCtl* ctl = new IoCtl(this->drivePath); this->supportedSpeeds = ctl->GetSupportedReadSpeeds(); this->sectors = ctl->GetTotalSectors(); + + this->hasCss = dvdcss_is_scrambled(ctl->GetDvdCssHandle()); + if (this->hasCss) { + this->discKey = (std::byte*)dvdcss_get_cur_disckey(ctl->GetDvdCssHandle()); + } + else { + this->discKey = nullptr; + } + delete ctl; } } @@ -60,6 +72,14 @@ namespace Li::Scsi { return this->drivePath; } + bool OpticalDrive::HasCss() { + return this->hasCss; + } + + std::byte* OpticalDrive::DiscKey() { + return this->discKey; + } + uint32_t OpticalDrive::Sectors() { return this->sectors; } @@ -88,6 +108,20 @@ namespace Li::Scsi { } delete drivesList; +#else + + /* + * Looks like SDL cdrom support got removed at some point .. oh well + * + // Use SDL + int numCdDrives = SDL_CDNumDrives(); + for (int i = 0; i < numCdDrives; i++) { + OpticalDrive* opticalDrive = new OpticalDrive(SDL_CDName(i)); + drives->push_back(opticalDrive); + } + */ + +#error No way to list optical drives on this platform! #endif return drives; } diff --git a/DumpDVD/Scsi/OpticalDrive.hpp b/DumpDVD/Scsi/OpticalDrive.hpp index 510aa43..ab50f4b 100644 --- a/DumpDVD/Scsi/OpticalDrive.hpp +++ b/DumpDVD/Scsi/OpticalDrive.hpp @@ -8,6 +8,9 @@ namespace Li::Scsi { std::string drivePath; std::string volumeName; + bool hasCss; + std::byte* discKey; + bool discInDrive; std::vector* supportedSpeeds; uint32_t sectors; @@ -21,6 +24,8 @@ namespace Li::Scsi { std::string DrivePath(); std::string VolumeName(); uint32_t Sectors(); + std::byte* DiscKey(); + bool HasCss(); bool DiscInDrive(); static std::vector* ListOpticalDrives(); diff --git a/DumpDVD/Utils.cpp b/DumpDVD/Utils.cpp index ff847de..0564f5d 100644 --- a/DumpDVD/Utils.cpp +++ b/DumpDVD/Utils.cpp @@ -2,7 +2,28 @@ #include #include +#ifdef _WIN32 +#include +#include +#endif + namespace Li { + + std::string Utils::GetDocumentsFolder() { + 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()); +#else +#error no way to find documents folder on this platform. +#endif + + return documentsFolder; + } + bool Utils::CompareStringCaseInsensitive(std::string input, std::string compare) { unsigned int sz = input.size(); if (compare.size() != sz) diff --git a/DumpDVD/Utils.hpp b/DumpDVD/Utils.hpp index 9bd6870..55f2fc1 100644 --- a/DumpDVD/Utils.hpp +++ b/DumpDVD/Utils.hpp @@ -3,6 +3,7 @@ namespace Li { class Utils { public: + static std::string GetDocumentsFolder(); static std::string HumanReadableByteStr(uint64_t bytes); static bool CompareStringCaseInsensitive(std::string input, std::string compare); };