diff --git a/.gitignore b/.gitignore index 7c1d329..73a369b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,10 @@ libdvdcss/msvc/Debug/* libdvdcss/msvc/Release/* libdvdcss/msvc/.vs/* +Lib9660/Release/* +Lib9660/Debug/* + + ImGui/Release/* ImGui/Debug/* diff --git a/DumpDVD.sln b/DumpDVD.sln index 0eaebce..62d0753 100644 --- a/DumpDVD.sln +++ b/DumpDVD.sln @@ -4,29 +4,58 @@ Microsoft Visual Studio Solution File, Format Version 12.00 VisualStudioVersion = 17.4.33205.214 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DumpDVD", "DumpDVD\DumpDVD.vcxproj", "{A9A5F041-541F-47EA-B583-DE84B59D9B62}" + ProjectSection(ProjectDependencies) = postProject + {50F2BF62-8520-4B37-97DD-578EA282EC04} = {50F2BF62-8520-4B37-97DD-578EA282EC04} + {7F404DED-08FF-4092-97EF-0F63C5788DD2} = {7F404DED-08FF-4092-97EF-0F63C5788DD2} + {B2BCCCDA-BDB0-4FBE-84A3-EA1E8429F732} = {B2BCCCDA-BDB0-4FBE-84A3-EA1E8429F732} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libdvdcss", "libdvdcss\msvc\libdvdcss.vcxproj", "{50F2BF62-8520-4B37-97DD-578EA282EC04}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImGui", "ImGui\ImGui.vcxproj", "{B2BCCCDA-BDB0-4FBE-84A3-EA1E8429F732}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Lib9660", "Lib9660\Lib9660.vcxproj", "{7F404DED-08FF-4092-97EF-0F63C5788DD2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A9A5F041-541F-47EA-B583-DE84B59D9B62}.Debug|x64.ActiveCfg = Debug|x64 + {A9A5F041-541F-47EA-B583-DE84B59D9B62}.Debug|x64.Build.0 = Debug|x64 {A9A5F041-541F-47EA-B583-DE84B59D9B62}.Debug|x86.ActiveCfg = Debug|Win32 {A9A5F041-541F-47EA-B583-DE84B59D9B62}.Debug|x86.Build.0 = Debug|Win32 + {A9A5F041-541F-47EA-B583-DE84B59D9B62}.Release|x64.ActiveCfg = Release|x64 + {A9A5F041-541F-47EA-B583-DE84B59D9B62}.Release|x64.Build.0 = Release|x64 {A9A5F041-541F-47EA-B583-DE84B59D9B62}.Release|x86.ActiveCfg = Release|Win32 {A9A5F041-541F-47EA-B583-DE84B59D9B62}.Release|x86.Build.0 = Release|Win32 + {50F2BF62-8520-4B37-97DD-578EA282EC04}.Debug|x64.ActiveCfg = Debug|x64 + {50F2BF62-8520-4B37-97DD-578EA282EC04}.Debug|x64.Build.0 = Debug|x64 {50F2BF62-8520-4B37-97DD-578EA282EC04}.Debug|x86.ActiveCfg = Release|Win32 {50F2BF62-8520-4B37-97DD-578EA282EC04}.Debug|x86.Build.0 = Release|Win32 + {50F2BF62-8520-4B37-97DD-578EA282EC04}.Release|x64.ActiveCfg = Release|x64 + {50F2BF62-8520-4B37-97DD-578EA282EC04}.Release|x64.Build.0 = Release|x64 {50F2BF62-8520-4B37-97DD-578EA282EC04}.Release|x86.ActiveCfg = Release|Win32 {50F2BF62-8520-4B37-97DD-578EA282EC04}.Release|x86.Build.0 = Release|Win32 + {B2BCCCDA-BDB0-4FBE-84A3-EA1E8429F732}.Debug|x64.ActiveCfg = Debug|x64 + {B2BCCCDA-BDB0-4FBE-84A3-EA1E8429F732}.Debug|x64.Build.0 = Debug|x64 {B2BCCCDA-BDB0-4FBE-84A3-EA1E8429F732}.Debug|x86.ActiveCfg = Release|Win32 {B2BCCCDA-BDB0-4FBE-84A3-EA1E8429F732}.Debug|x86.Build.0 = Release|Win32 + {B2BCCCDA-BDB0-4FBE-84A3-EA1E8429F732}.Release|x64.ActiveCfg = Release|x64 + {B2BCCCDA-BDB0-4FBE-84A3-EA1E8429F732}.Release|x64.Build.0 = Release|x64 {B2BCCCDA-BDB0-4FBE-84A3-EA1E8429F732}.Release|x86.ActiveCfg = Release|Win32 {B2BCCCDA-BDB0-4FBE-84A3-EA1E8429F732}.Release|x86.Build.0 = Release|Win32 + {7F404DED-08FF-4092-97EF-0F63C5788DD2}.Debug|x64.ActiveCfg = Debug|x64 + {7F404DED-08FF-4092-97EF-0F63C5788DD2}.Debug|x64.Build.0 = Debug|x64 + {7F404DED-08FF-4092-97EF-0F63C5788DD2}.Debug|x86.ActiveCfg = Release|Win32 + {7F404DED-08FF-4092-97EF-0F63C5788DD2}.Debug|x86.Build.0 = Release|Win32 + {7F404DED-08FF-4092-97EF-0F63C5788DD2}.Release|x64.ActiveCfg = Release|x64 + {7F404DED-08FF-4092-97EF-0F63C5788DD2}.Release|x64.Build.0 = Release|x64 + {7F404DED-08FF-4092-97EF-0F63C5788DD2}.Release|x86.ActiveCfg = Release|Win32 + {7F404DED-08FF-4092-97EF-0F63C5788DD2}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/DumpDVD/DumpDVD.vcxproj b/DumpDVD/DumpDVD.vcxproj index ec08c29..414bc86 100644 --- a/DumpDVD/DumpDVD.vcxproj +++ b/DumpDVD/DumpDVD.vcxproj @@ -19,6 +19,8 @@ + + @@ -29,6 +31,8 @@ + + @@ -96,7 +100,7 @@ true _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - $(SolutionDir)include\PFD;$(SolutionDir)include\SDL;$(SolutionDir)ImGui;$(SolutionDir)libdvdcss\src\dvdcss;%(AdditionalIncludeDirectories) + $(SolutionDir)include\PFD;$(SolutionDir)include\SDL;$(SolutionDir)Lib9660;$(SolutionDir)ImGui;$(SolutionDir)libdvdcss\src\dvdcss;%(AdditionalIncludeDirectories) stdcpp20 stdc17 @@ -104,7 +108,7 @@ Windows true $(SolutionDir)\lib;%(AdditionalLibraryDirectories) - d3d11.lib;winmm.lib;version.lib;Imm32.lib;Setupapi.lib;SDL2.lib;ImGui.lib;libdvdcss.lib;%(AdditionalDependencies) + d3d11.lib;winmm.lib;version.lib;Imm32.lib;Setupapi.lib;SDL2.lib;ImGui.lib;Lib9660.lib;libdvdcss.lib;%(AdditionalDependencies) @@ -117,7 +121,7 @@ false _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - $(SolutionDir)include\PFD;$(SolutionDir)include\SDL;$(SolutionDir)ImGui;$(SolutionDir)libdvdcss\src\dvdcss;%(AdditionalIncludeDirectories) + $(SolutionDir)include\PFD;$(SolutionDir)include\SDL;$(SolutionDir)Lib9660;$(SolutionDir)ImGui;$(SolutionDir)libdvdcss\src\dvdcss;%(AdditionalIncludeDirectories) MultiThreaded stdcpp20 stdc17 @@ -128,7 +132,7 @@ true false $(SolutionDir)\lib;%(AdditionalLibraryDirectories) - d3d11.lib;winmm.lib;version.lib;Imm32.lib;Setupapi.lib;SDL2.lib;ImGui.lib;libdvdcss.lib;%(AdditionalDependencies) + d3d11.lib;winmm.lib;version.lib;Imm32.lib;Setupapi.lib;SDL2.lib;ImGui.lib;Lib9660.lib;libdvdcss.lib;%(AdditionalDependencies) diff --git a/DumpDVD/DumpDVD.vcxproj.filters b/DumpDVD/DumpDVD.vcxproj.filters index 893fe00..f6aee24 100644 --- a/DumpDVD/DumpDVD.vcxproj.filters +++ b/DumpDVD/DumpDVD.vcxproj.filters @@ -39,6 +39,12 @@ Source Files + + Source Files + + + Source Files + @@ -65,5 +71,11 @@ Header Files + + Header Files + + + Header Files + \ No newline at end of file diff --git a/DumpDVD/Dvd/DvdRipper.cpp b/DumpDVD/Dvd/DvdRipper.cpp new file mode 100644 index 0000000..ebc0ad1 --- /dev/null +++ b/DumpDVD/Dvd/DvdRipper.cpp @@ -0,0 +1,233 @@ +#include "DvdRipper.hpp" + +#include +#include +#include +#include +#include + +#include "../Scsi/IoCtl.hpp" +#include "../Scsi/OpticalDrive.hpp" +#include "../Dvd/TitleKey.hpp" + +namespace Li::Dvd { + + + + uint32_t DvdRipper::FileSoFar() { + if (this->inFile) + return this->fileReadSoFar; + else + return this->nonFileReadSoFar; + } + uint32_t DvdRipper::FileLen() { + if (this->inFile) + return this->fileRemain; + else + return this->nonFileRemain; + } + + float DvdRipper::PercentDone() { + return (((float)this->sectorsReadSoFar / (float)this->drive->Sectors())); + } + uint32_t DvdRipper::SectorsReadSoFar() { + return this->sectorsReadSoFar; + } + uint32_t DvdRipper::FileReadSoFar() { + return this->fileReadSoFar; + } + std::string DvdRipper::OutputPath() { + return this->outputPath; + } + + bool DvdRipper::Done() { + return this->done; + } + bool DvdRipper::Error() { + return this->error; + } + std::string DvdRipper::ErrorMessage() { + return this->errorMsg; + } + int DvdRipper::read1SectorATimeSkippingBadSectors(int toRead) { + memset(this->tmpBuffer, 0x00, this->sectorsAtOnce * DVDCSS_BLOCK_SIZE); + int nmRd = 0; + for (int i = 0; i < toRead; i++) { + for (int retry = 0; retry < 10; retry++) { + int dataRd = dvdcss_read(driveIoCtl->GetDvdCssHandle(), this->tmpBuffer + (i * DVDCSS_BLOCK_SIZE), 1, DVDCSS_READ_DECRYPT); + if (dataRd < 0) { + continue; // retry + } + } + nmRd += 1; + } + + return nmRd; + + } + void DvdRipper::readNonFileSectors() { + dvdcss_title(this->driveIoCtl->GetDvdCssHandle(), this->sectorsReadSoFar); + + this->nonFileRemain = Li::Dvd::TitleKey::GetDistanceToNextFile(this->sectorsReadSoFar); + if (this->nonFileRemain == (uint32_t)-1) { + this->nonFileRemain = (this->drive->Sectors() - this->sectorsReadSoFar); + } + if ((this->sectorsReadSoFar + this->nonFileRemain) > this->drive->Sectors()) { + this->nonFileRemain = (this->drive->Sectors() - this->sectorsReadSoFar); + } + this->nonFileReadSoFar = 0; + + do { + int toRead = this->sectorsAtOnce; + if ((this->nonFileReadSoFar + toRead) > this->nonFileRemain) { + toRead = this->nonFileRemain - this->nonFileReadSoFar; + } + int numRead = dvdcss_read(driveIoCtl->GetDvdCssHandle(), this->tmpBuffer, toRead, DVDCSS_READ_DECRYPT); + + if (dvdcss_was_error(driveIoCtl->GetDvdCssHandle())) { + this->errorMsg = std::string(dvdcss_error(driveIoCtl->GetDvdCssHandle())); + } + if (numRead < 0) { + dvdcss_seek(driveIoCtl->GetDvdCssHandle(), this->sectorsAtOnce, DVDCSS_SEEK_MPEG); + numRead = read1SectorATimeSkippingBadSectors(toRead); + } + + this->iso->write((const char*)this->tmpBuffer, DVDCSS_BLOCK_SIZE * numRead); + + this->sectorsReadSoFar += numRead; + this->nonFileReadSoFar += numRead; + + } while (this->nonFileReadSoFar < this->nonFileRemain && this->keepRunning); + + this->inFile = Li::Dvd::TitleKey::IsSectorInFile(this->sectorsReadSoFar); + this->fileReadSoFar = 0; + this->nonFileReadSoFar = 0; + } + + void DvdRipper::readFileSectors() { + dvdcss_title(this->driveIoCtl->GetDvdCssHandle(), this->sectorsReadSoFar); + + sectorInfo* sectorInf = Li::Dvd::TitleKey::GetSectorInfo(this->sectorsReadSoFar); + this->fileRemain = (sectorInf->endSector - sectorInf->startSector); + + if ((this->sectorsReadSoFar + this->fileRemain) > this->drive->Sectors()) { + this->fileRemain = (this->drive->Sectors() - this->sectorsReadSoFar); + } + + this->fileReadSoFar = 0; + + do { + int toRead = this->sectorsAtOnce; + if ((this->fileReadSoFar + toRead) > this->fileRemain) { + toRead = this->fileRemain - this->fileReadSoFar; + } + int numRead = dvdcss_read(driveIoCtl->GetDvdCssHandle(), this->tmpBuffer, toRead, DVDCSS_READ_DECRYPT); + + if (dvdcss_was_error(driveIoCtl->GetDvdCssHandle())) { + this->errorMsg = std::string(dvdcss_error(driveIoCtl->GetDvdCssHandle())); + } + if (numRead <= 0) { + numRead = read1SectorATimeSkippingBadSectors(toRead); + } + + iso->write((const char*)this->tmpBuffer, DVDCSS_BLOCK_SIZE * numRead); + + this->sectorsReadSoFar += numRead; + this->fileReadSoFar += numRead; + + } while ((this->fileReadSoFar < this->fileRemain) && this->keepRunning); + + this->inFile = Li::Dvd::TitleKey::IsSectorInFile(this->sectorsReadSoFar); + this->fileReadSoFar = 0; + this->nonFileReadSoFar = 0; + } + + 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); + + do { + if (this->inFile) + this->readFileSectors(); + else + this->readNonFileSectors(); + + } while (this->sectorsReadSoFar < this->drive->Sectors() && this->keepRunning); + + this->done = true; + } + + DvdRipper::DvdRipper(Li::Scsi::OpticalDrive* opticalDrive, std::string outputISO, uint32_t sectorsAtOnce) { + this->drive = opticalDrive; + this->outputPath = outputISO; + this->sectorsAtOnce = sectorsAtOnce; + + this->sectorsReadSoFar = 0; + + this->fileReadSoFar = 0; + this->fileRemain = 0; + + this->nonFileReadSoFar = 0; + this->nonFileRemain = 0; + + this->inFile = false; + this->done = false; + this->error = false; + this->errorMsg = ""; + this->keepRunning = false; + + this->ripinThread = nullptr; + this->tmpBuffer = new uint8_t[DVDCSS_BLOCK_SIZE * this->sectorsAtOnce]; + } + + DvdRipper::~DvdRipper() { + delete this->tmpBuffer; + + if (this->ripinThread != nullptr) { + this->EndRip(); + } + if (this->iso != nullptr) { + if (this->iso->is_open()) + this->iso->close(); + delete this->iso; + } + } + + void DvdRipper::StartRip(uint32_t driveSpeed) { + + this->driveIoCtl = new Li::Scsi::IoCtl(this->drive->DrivePath()); + this->driveIoCtl->AllowReadingPastDisc(); + this->driveIoCtl->SetDriveSpeed(driveSpeed, driveSpeed); + this->driveIoCtl->ExclusiveLockDrive(); + this->iso = new std::ofstream(this->outputPath, std::ios::binary); + this->keepRunning = true; + + this->ripinThread = new std::thread(&DvdRipper::ripThread, this); + + } + + void DvdRipper::EndRip() { + this->done = false; + this->error = false; + this->errorMsg = ""; + + this->sectorsReadSoFar = 0; + this->fileReadSoFar = 0; + this->keepRunning = false; + this->ripinThread->join(); + + delete this->driveIoCtl; + delete this->ripinThread; + this->ripinThread = nullptr; + iso->close(); + delete iso; + + this->iso = nullptr; + this->driveIoCtl->ExclusiveUnlockDrive(); + } +} \ No newline at end of file diff --git a/DumpDVD/Dvd/DvdRipper.hpp b/DumpDVD/Dvd/DvdRipper.hpp new file mode 100644 index 0000000..32b23ec --- /dev/null +++ b/DumpDVD/Dvd/DvdRipper.hpp @@ -0,0 +1,64 @@ +#ifndef _LI_DVDRIPPER +#define _LI_DVDRIPPER 1 +#include +#include +#include +#include +#include + +#include "../Scsi/IoCtl.hpp" +#include "../Scsi/OpticalDrive.hpp" + +namespace Li::Dvd { + class DvdRipper { + private: + bool error; + bool done; + std::string errorMsg; + + bool inFile; + uint32_t fileReadSoFar; + uint32_t fileRemain; + + uint32_t nonFileReadSoFar; + uint32_t nonFileRemain; + + + uint32_t sectorsAtOnce; + uint32_t sectorsReadSoFar; + + std::string outputPath; + std::thread* ripinThread; + + bool keepRunning; + + std::ofstream* iso; + uint8_t* tmpBuffer; + + Li::Scsi::IoCtl* driveIoCtl; + Li::Scsi::OpticalDrive* drive; + void ripThread(); + void readNonFileSectors(); + void readFileSectors(); + int read1SectorATimeSkippingBadSectors(int toRead); + public: + DvdRipper(Li::Scsi::OpticalDrive* opticalDrive, std::string outputISO, uint32_t sectorsAtOnce); + ~DvdRipper(); + + uint32_t SectorsReadSoFar(); + uint32_t FileReadSoFar(); + float PercentDone(); + std::string OutputPath(); + + uint32_t FileSoFar(); + uint32_t FileLen(); + + bool Done(); + bool Error(); + std::string ErrorMessage(); + + void StartRip(uint32_t driveSpeed); + void EndRip(); + }; +} +#endif \ No newline at end of file diff --git a/DumpDVD/Dvd/TitleKey.cpp b/DumpDVD/Dvd/TitleKey.cpp new file mode 100644 index 0000000..77c4721 --- /dev/null +++ b/DumpDVD/Dvd/TitleKey.cpp @@ -0,0 +1,112 @@ +#include "TitleKey.hpp" +#include +#include +#include +#include + +#include "../Utils.hpp" + +namespace Li::Dvd { + + + static std::vector sectorsList; + static dvdcss_t drv; + + bool TitleKey::readSector(l9660_fs* fs, void* buf, uint32_t sector) { + dvdcss_seek(drv, sector, DVDCSS_SEEK_KEY); + dvdcss_read(drv, buf, 1, DVDCSS_READ_DECRYPT); + + return !dvdcss_was_error(drv); + } + + bool TitleKey::IsSectorInFile(uint32_t currentSector) { + for (sectorInfo* inf : sectorsList) { + if (currentSector >= inf->startSector && currentSector < inf->endSector) + return true; + } + return false; + } + + uint32_t TitleKey::GetDistanceToNextFile(uint32_t currentSector) { + + uint32_t lowestDistance = -1; + for (sectorInfo* inf : sectorsList) { + if (currentSector >= inf->startSector && currentSector < inf->endSector) + return 0; + + uint32_t distance = inf->startSector - currentSector; + if (distance < 0) continue; + + if (distance < lowestDistance) { + lowestDistance = distance; + } + } + + return lowestDistance; + + } + + sectorInfo* TitleKey::GetSectorInfo(uint32_t currentSector) { + + for (sectorInfo* inf : sectorsList) { + if (currentSector >= inf->startSector && currentSector < inf->endSector) { + return inf; + } + } + + return nullptr; + + } + + void readDir(l9660_dir* dir) { + while (true) { + l9660_dirent* dent; + l9660_status status = l9660_readdir(dir, &dent); + + if (dent == nullptr) + break; + + if (dent->name_len < 2) + continue; + + if ((dent->flags & DENT_ISDIR) != 0) { + l9660_dir ndir; + l9660_opendirat(&ndir, dir, dent->name); + readDir(&ndir); + } + else if (TitleKey::GetSectorInfo(*(uint32_t*)dent->size.le) == nullptr) { + sectorInfo* file = new sectorInfo(); + + file->startSector = *(uint32_t*)dent->sector.le; + file->endSector = file->startSector + (*(uint32_t*)dent->size.le / DVDCSS_BLOCK_SIZE); + + sectorsList.push_back(file); + } + + } + } + + std::vector TitleKey::GetTitleKeys(dvdcss_t drive) { + drv = drive; + sectorsList = std::vector(); + + l9660_dir rootDir; + l9660_fs isoFs; + l9660_status status; + + status = l9660_openfs(&isoFs, TitleKey::readSector); + status = l9660_fs_open_root(&rootDir, &isoFs); + readDir(&rootDir); + + return sectorsList; + } + + void TitleKey::FreeMemory() { + for (sectorInfo* inf : sectorsList) { + delete inf; + } + + sectorsList.clear(); + + } +} \ No newline at end of file diff --git a/DumpDVD/Dvd/TitleKey.hpp b/DumpDVD/Dvd/TitleKey.hpp new file mode 100644 index 0000000..4788c78 --- /dev/null +++ b/DumpDVD/Dvd/TitleKey.hpp @@ -0,0 +1,25 @@ +#ifndef _LI_TITLEKEY +#define _LI_TITLEKEY 1 +#include +#include +#include + +typedef struct sectorInfo { + uint32_t startSector; + uint32_t endSector; +} sectorInfo; + +namespace Li::Dvd { + class TitleKey { + private: + static bool readSector(l9660_fs* fs, void* buf, uint32_t sector); + public: + static std::vector GetTitleKeys(dvdcss_t drive); + static uint32_t GetDistanceToNextFile(uint32_t currentSector); + static bool IsSectorInFile(uint32_t currentSector); + static sectorInfo* GetSectorInfo(uint32_t currentSector); + static void FreeMemory(); + }; +} + +#endif \ No newline at end of file diff --git a/DumpDVD/Gui/DumpDVD.cpp b/DumpDVD/Gui/DumpDVD.cpp index 2d9382a..a6e264b 100644 --- a/DumpDVD/Gui/DumpDVD.cpp +++ b/DumpDVD/Gui/DumpDVD.cpp @@ -1,3 +1,4 @@ +#include "DumpDVD.hpp" #include #include @@ -8,18 +9,17 @@ #include -#include -#include #include #include -#include #include -#include "DumpDVD.hpp" + #include "../Utils.hpp" #include "../Scsi/IoCtl.hpp" #include "../Scsi/OpticalDrive.hpp" +#include "../Dvd/DvdRipper.hpp" +#include "../Dvd/TitleKey.hpp" namespace Li::Gui { @@ -29,20 +29,14 @@ namespace Li::Gui { 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(); @@ -56,54 +50,6 @@ namespace Li::Gui { 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); - drive->ExclusiveLockDrive(); - 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; - this->errorMsg = std::string(dvdcss_error(drive->GetDvdCssHandle())); - break; - } - - iso->write((const char*)buffer, DVDCSS_BLOCK_SIZE * numRead); - this->sectorsReadSoFar += numRead; - - } while (this->sectorsReadSoFar < totalSectors && this->imRippinIt); - - - drive->ExclusiveUnlockDrive(); - iso->close(); - this->done = true; - delete buffer; - delete drive; - delete iso; - } - void DumpDVD::pollDrivesThread() { while (this->keepPolling) { if ( (this->counter % (60 * 10)) == 0) { @@ -167,18 +113,25 @@ namespace Li::Gui { this->pollDrives->join(); delete this->pollDrives; - this->pollDrives = new std::thread(&DumpDVD::ripDiscThread, this); + 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->imRippinIt = true; } } void DumpDVD::endRipAndGoBackToMenu() { - this->done = false; + this->dvdRipper->EndRip(); + delete this->dvdRipper; + this->imRippinIt = false; this->reset(); - this->sectorsReadSoFar = 0; - this->counter = 0; - this->pollDrives->join(); this->keepPolling = true; this->drivesList = Li::Scsi::OpticalDrive::ListOpticalDrives(); @@ -189,28 +142,29 @@ namespace Li::Gui { void DumpDVD::showRippingStatus() { ImGui::SeparatorText("Status"); - float percentage = (((float)this->sectorsReadSoFar / (float)this->GetCurrentSelectedDrive()->Sectors())); - ImGui::Text("Copying Disc ... ", (int)percentage); + ImGui::Text("Copying Disc ... "); ImGui::SameLine(); - ImGui::ProgressBar(percentage); + ImGui::ProgressBar(this->dvdRipper->PercentDone()); - 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("Sector %i / %i", this->dvdRipper->SectorsReadSoFar(), this->GetCurrentSelectedDrive()->Sectors()); + ImGui::Text("File: %i / %i", this->dvdRipper->FileReadSoFar(), 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("Output"); ImGui::Text("Output file: \"%s\"", this->outputFile); if (ImGui::Button("Cancel")) { this->endRipAndGoBackToMenu(); std::filesystem::remove(std::string(this->outputFile)); + return; } - if (this->done) { - if (!this->error) { - ImGui::Begin("Complete", &done, this->windowFlags); + if (this->dvdRipper->Done()) { + if (!this->dvdRipper->Error()) { + ImGui::Begin("Complete", &this->imRippinIt, ImGuiWindowFlags_NoCollapse); ImGui::Text("ISO file was created successfully."); if (ImGui::Button("OK")) { this->endRipAndGoBackToMenu(); @@ -218,8 +172,8 @@ namespace Li::Gui { ImGui::End(); } else { - ImGui::Begin("Error", &done, this->windowFlags); - ImGui::Text("Error occured when creating an ISO\n\"%s\"", this->errorMsg.c_str()); + ImGui::Begin("Error", &this->imRippinIt, ImGuiWindowFlags_NoCollapse); + ImGui::Text("Error occured when creating an ISO\n\"%s\"", this->dvdRipper->ErrorMessage().c_str()); if (ImGui::Button("OK")) { this->endRipAndGoBackToMenu(); std::filesystem::remove(std::string(this->outputFile)); @@ -309,7 +263,7 @@ namespace Li::Gui { ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Appearing); ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize, ImGuiCond_Appearing); - ImGui::Begin("DVD Dumper", NULL, windowFlags); + ImGui::Begin("DVD Dumper", NULL, ImGuiWindowFlags_NoCollapse); if (this->imRippinIt) { diff --git a/DumpDVD/Gui/DumpDVD.hpp b/DumpDVD/Gui/DumpDVD.hpp index cc84fbe..9c21ffe 100644 --- a/DumpDVD/Gui/DumpDVD.hpp +++ b/DumpDVD/Gui/DumpDVD.hpp @@ -2,11 +2,12 @@ #define _LI_DUMP_DVD_H 1 #include #include +#include #include #include -#include "../Scsi/IoCtl.hpp" #include "../Scsi/OpticalDrive.hpp" +#include "../Dvd/DvdRipper.hpp" namespace Li::Gui { @@ -18,22 +19,17 @@ namespace Li::Gui { std::mutex* lock; - bool error; - bool done; - std::string errorMsg; - uint64_t sectorsReadSoFar; int sectorsAtOnce; - bool discInserted; int selectedDrive; - ImGuiWindowFlags windowFlags; int selectedDriveSpeed; uint32_t counter; char outputFile[0x8000]; - std::vector* drivesList; std::thread* pollDrives; + + Li::Dvd::DvdRipper* dvdRipper; void ripDiscThread(); void pollDrivesThread(); diff --git a/DumpDVD/Scsi/OpticalDrive.cpp b/DumpDVD/Scsi/OpticalDrive.cpp index d18a63a..480ede1 100644 --- a/DumpDVD/Scsi/OpticalDrive.cpp +++ b/DumpDVD/Scsi/OpticalDrive.cpp @@ -60,7 +60,7 @@ namespace Li::Scsi { return this->drivePath; } - uint64_t OpticalDrive::Sectors() { + uint32_t OpticalDrive::Sectors() { return this->sectors; } diff --git a/DumpDVD/Scsi/OpticalDrive.hpp b/DumpDVD/Scsi/OpticalDrive.hpp index e81983d..510aa43 100644 --- a/DumpDVD/Scsi/OpticalDrive.hpp +++ b/DumpDVD/Scsi/OpticalDrive.hpp @@ -10,7 +10,7 @@ namespace Li::Scsi { bool discInDrive; std::vector* supportedSpeeds; - uint64_t sectors; + uint32_t sectors; void getDriveInformation(std::string drv); public: @@ -20,7 +20,7 @@ namespace Li::Scsi { std::vector* SupportedSpeeds(); std::string DrivePath(); std::string VolumeName(); - uint64_t Sectors(); + uint32_t Sectors(); bool DiscInDrive(); static std::vector* ListOpticalDrives(); diff --git a/DumpDVD/Utils.cpp b/DumpDVD/Utils.cpp index 9621b31..ff847de 100644 --- a/DumpDVD/Utils.cpp +++ b/DumpDVD/Utils.cpp @@ -3,6 +3,15 @@ #include namespace Li { + bool Utils::CompareStringCaseInsensitive(std::string input, std::string compare) { + unsigned int sz = input.size(); + if (compare.size() != sz) + return false; + for (unsigned int i = 0; i < sz; ++i) + if (tolower(input[i]) != tolower(compare[i])) + return false; + return true; + } std::string Utils::HumanReadableByteStr(uint64_t bytes) { const char* suffix[5] = { "B", "KB", "MB", "GB", "TB" }; diff --git a/DumpDVD/Utils.hpp b/DumpDVD/Utils.hpp index d899781..9bd6870 100644 --- a/DumpDVD/Utils.hpp +++ b/DumpDVD/Utils.hpp @@ -4,6 +4,7 @@ namespace Li { class Utils { public: static std::string HumanReadableByteStr(uint64_t bytes); + static bool CompareStringCaseInsensitive(std::string input, std::string compare); }; } #endif \ No newline at end of file diff --git a/Lib9660/Jamfile b/Lib9660/Jamfile new file mode 100644 index 0000000..68ae755 --- /dev/null +++ b/Lib9660/Jamfile @@ -0,0 +1,4 @@ +SubDir LIB9660_TOP ; +SubDirCcFlags -std=c99 $(LIB9660_OPTS) ; + +Library lib9660 : lib9660.c ; diff --git a/Lib9660/Lib9660.vcxproj b/Lib9660/Lib9660.vcxproj new file mode 100644 index 0000000..a185e12 --- /dev/null +++ b/Lib9660/Lib9660.vcxproj @@ -0,0 +1,158 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + 16.0 + Win32Proj + {7f404ded-08ff-4092-97ef-0f63c5788dd2} + Lib9660 + 10.0 + + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + true + Unicode + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)lib\ + + + + Level3 + true + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + MultiThreadedDebug + + + + + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + MultiThreaded + + + + + true + true + true + + + $(SolutionDir)lib\$(TargetName)$(TargetExt) + + + + + Level3 + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + Use + pch.h + + + + + true + + + + + Level3 + true + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + true + Use + pch.h + + + + + true + true + true + + + + + + \ No newline at end of file diff --git a/Lib9660/Lib9660.vcxproj.filters b/Lib9660/Lib9660.vcxproj.filters new file mode 100644 index 0000000..6950d0c --- /dev/null +++ b/Lib9660/Lib9660.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + \ No newline at end of file diff --git a/Lib9660/Lib9660.vcxproj.user b/Lib9660/Lib9660.vcxproj.user new file mode 100644 index 0000000..966b4ff --- /dev/null +++ b/Lib9660/Lib9660.vcxproj.user @@ -0,0 +1,6 @@ + + + + true + + \ No newline at end of file diff --git a/Lib9660/Makefile b/Lib9660/Makefile new file mode 100644 index 0000000..06cde20 --- /dev/null +++ b/Lib9660/Makefile @@ -0,0 +1,28 @@ +.POSIX: +.SUFFIXES: +.SUFFIXES: .o .c +.PHONY: all clean install + +PREFIX = /usr/local +CFLAGS = -DL9660_HAVE_STDIO -g -DDEBUG + +OBJS := lib9660.o +ALL := lib9660.a tb9660 + +all: $(ALL) + +.c.o: + $(CC) -c -o $@ $< $(CFLAGS) + +lib9660.a: $(OBJS) + ar cru lib9660.a $(OBJS) + +tb9660: tb9660.c lib9660.a + $(CC) -g -o tb9660 tb9660.c lib9660.a $(CFLAGS) + +clean: + rm $(ALL) $(OBJS) + +install: + install -m644 lib9660.a $(DESTDIR)$(PREFIX)/lib + install -m644 lib9660.h $(DESTDIR)$(PREFIX)/include diff --git a/Lib9660/README.md b/Lib9660/README.md new file mode 100644 index 0000000..1a81b26 --- /dev/null +++ b/Lib9660/README.md @@ -0,0 +1,53 @@ +# lib9660 - a simple implementation of the ISO 9660 file system + +lib9660 is a simple implementation of the ISO 9660 file system, as used on CD. +It does not perform any dynamic memory allocations or depend upon any +complicated standard library routines (actually all it requires is memcmp and +memcpy). It provides a reasonable facsimilie of the standard POSIX file +descriptor I/O API. + +lib9660 is re-entrant; it is safe to use it from multiple threads, provided you +do not access the same file from multiple threads (note that for these purposes +the openat and opendirat functions count as accessing the passed parent file) + +## Usage notes +l9660_dir is simply a wrapper for l9660_file. You may use them interchangably if +necessary to save memory (note that this will require casting). The distinct +types are provided purely for type checking. + +You can pass the same file/directory pointer for both child and parent +parameters of l9660_open(dir)at. This can be useful in memory constrained +environments. + +l9660_read will not perform a read from the underlying device as long as the +request can be answered from its' internal buffer. Be prepared for short reads. + +lib9660_open(dir)at use lib9660_readdir internally. Remember to save and +restore your seek position if that will be necessary. + +## Tuning parameters +### L9660_SINGLEBUFFER +By default, lib9660 maintains a buffer per open file. This means each file (and +directory) consumes just over 2kB of RAM for the buffer and state information. + +If you are memory constrained, you can build with L9660_SINGLEBUFFER (make sure +it is also defined in any files which include lib9660.h), which causes lib9660 +to instead use a single common buffer. lib9660 will now consume a constant +2kB of RAM for this buffer, instead of 2kB per file. + +The cost of this is slightly reduced performance; if you alternate between +reading multiple files, then each time you change file that file's buffer must +be reloaded. Additionally, this makes lib9660 no longer thread safe. + +### L9660_HAVE_STDIO +If your system has stdio.h and it defines SEEK_[SET,CUR,END], then lib9660 will +use those definitions for l9660_seek. Otherwise, it will use its' own +definitions of L9660_SEEK_[SET,CUR,END]. + +### DEBUG +If you build l9660.c with DEBUG defined, then this will enable some library +debugging features. Most notably, if L9660_DEBUG is set in the environment, +then all directory entries processed (by the open[dir]at functions) will be +dumped to stdout. + +This requires a functioning printf and getenv. You probably don't need it \ No newline at end of file diff --git a/Lib9660/lib9660.c b/Lib9660/lib9660.c new file mode 100644 index 0000000..1d4b7e3 --- /dev/null +++ b/Lib9660/lib9660.c @@ -0,0 +1,336 @@ +/* lib9660: a simple ISO9660 reader library especially suited to embedded + * systems + * + * Copyright © 2014, Owen Shepherd + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice appears in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lib9660.h" +#include + +#ifdef DEBUG +#include +#endif + +#ifdef L9660_HAVE_STDIO +#include +#else +#define SEEK_END L9660_SEEK_END +#define SEEK_SET L9660_SEEK_SET +#define SEEK_CUR L9660_SEEK_CUR +#endif + +#define DENT_EXISTS (1 << 0) +#define DENT_ISDIR (1 << 1) +#define DENT_ASSOCIATED (1 << 2) +#define DENT_RECORD (1 << 3) +#define DENT_PROTECTION (1 << 4) +#define DENT_MULTIEXTENT (1 << 5) + +#define PVD(vdesc) ((l9660_vdesc_primary*)(vdesc)) + +#ifdef L9660_BIG_ENDIAN +#define READ16(v) (((v).be[1]) | ((v).be[0] << 8)) +#define READ32(v) (((v).be[3]) | ((v).be[2] << 8) | ((v).be[1]) << 16 | ((v).be[0] << 24)) +#else +#define READ16(v) (((v).le[0]) | ((v).le[1] << 8)) +#define READ32(v) (((v).le[0]) | ((v).le[1] << 8) | ((v).le[2]) << 16 | ((v).le[3] << 24)) +#endif + +#ifndef L9660_SINGLEBUFFER +#define HAVEBUFFER(f) (true) +#define BUF(f) ((f)->buf) +#else +#define HAVEBUFFER(f) ((f) == last_file) +#define BUF(f) (gbuf) +static l9660_file *last_file; +static char gbuf[2048]; +#endif + +static char *strchrnul(const char *s, int c) +{ + while (*s) { + if((*s++) == c) + break; + } + return (char *) s; +} + +static inline uint16_t fsectoff(l9660_file *f) +{ + return f->position % 2048; +} + +static inline uint32_t fsector(l9660_file *f) +{ + return f->position / 2048; +} + +static inline uint32_t fnextsectpos(l9660_file *f) +{ + return (f->position + 2047) & ~2047; +} + +l9660_status l9660_openfs( + l9660_fs *fs, + bool (*read_sector)(l9660_fs *fs, void *buf, uint32_t sector)) +{ + fs->read_sector = read_sector; + +#ifndef L9660_SINGLEBUFFER + l9660_vdesc_primary *pvd = PVD(&fs->pvd); +#else + last_file = NULL; + l9660_vdesc_primary *pvd = PVD(gbuf); +#endif + uint32_t idx = 0x10; + for (;;) { + // Read next sector + if (!read_sector(fs, pvd, idx)) + return L9660_EIO; + + // Validate magic + if (memcmp(pvd->hdr.magic, "CD001", 5) != 0) + return L9660_EBADFS; + + if (pvd->hdr.type == 1) + break; // Found PVD + else if(pvd->hdr.type == 255) + return L9660_EBADFS; + } + +#ifdef L9660_SINGLEBUFFER + memcpy(&fs->root_dir_ent, &pvd->root_dir_ent, pvd->root_dir_ent.length); +#endif + + return L9660_OK; +} + +l9660_status l9660_fs_open_root(l9660_dir *dir, l9660_fs *fs) +{ + l9660_file *f = &dir->file; +#ifndef L9660_SINGLEBUFFER + l9660_dirent *dirent = &PVD(&fs->pvd)->root_dir_ent; +#else + l9660_dirent *dirent = &fs->root_dir_ent; +#endif + + f->fs = fs; + f->first_sector = READ32(dirent->sector); + f->length = READ32(dirent->size); + f->position = 0; + + return L9660_OK; +} + +static l9660_status buffer(l9660_file *f) +{ +#ifdef L9660_SINGLEBUFFER + last_file = f; +#endif + if (!f->fs->read_sector(f->fs, BUF(f), f->first_sector + f->position / 2048)) + return L9660_EIO; + else + return L9660_OK; +} + +static l9660_status prebuffer(l9660_file *f) +{ + if (!HAVEBUFFER(f) || (f->position % 2048) == 0) + return buffer(f); + else return L9660_OK; +} + +#if defined(DEBUG) +static void print_dirent(l9660_dirent *dent) +{ + if (!getenv("L9660_DEBUG")) + return; + + printf("| ---- dirent\n"); + printf("| length %d\n", dent->length); + printf("| xattr_length %d\n", dent->xattr_length); + printf("| sector %u\n", READ32(dent->sector)); + printf("| size %u\n", READ32(dent->size)); + printf("| name \"%.*s\"\n", dent->name_len, dent->name); + printf("| ---- end dirent\n"); +} +#endif + +static l9660_status openat_raw(l9660_file *child, l9660_dir *parent, const char *name, bool isdir) +{ + l9660_status rv; + l9660_dirent *dent = NULL; + if ((rv = l9660_seekdir(parent, 0))) return rv; + + do { + const char *seg = name; + name = strchrnul(name, '/'); + size_t seglen = name - seg; + + /* ISO9660 stores '.' as '\0' */ + if (seglen == 1 && *seg == '.') + seg = "\0"; + + /* ISO9660 stores ".." as '\1' */ + if (seglen == 2 && seg[0] == '.' && seg[1] == '.') { + seg = "\1"; + seglen = 1; + } + + for(;;) { + if ((rv = l9660_readdir(parent, &dent))) + return rv; + + /* EOD */ + if (!dent) + return L9660_ENOENT; + +#ifdef DEBUG + print_dirent(dent); +#endif + + /* wrong length */ + if (seglen > dent->name_len) + continue; + + /* check name */ + if (memcmp(seg, dent->name, seglen) != 0) + continue; + + /* check for a revision tag */ + if (dent->name_len > seglen && dent->name[seglen] != ';') + continue; + + /* all tests pass */ + break; + } + + child->fs = parent->file.fs; + child->first_sector = READ32(dent->sector) + dent->xattr_length; + child->length = READ32(dent->size); + child->position = 0; + + if (*name && (dent->flags & DENT_ISDIR) != 0) + return L9660_ENOTDIR; + + parent = (l9660_dir*) child; + } while(*name); + + if (isdir) { + if ((dent->flags & DENT_ISDIR) == 0) + return L9660_ENOTDIR; + } else { + if ((dent->flags & DENT_ISDIR) != 0) + return L9660_ENOTFILE; + } + + return L9660_OK; +} + +l9660_status l9660_opendirat(l9660_dir *dir, l9660_dir *parent, const char *path) +{ + return openat_raw(&dir->file, parent, path, true); +} + +static inline unsigned aligneven(unsigned v) { + return v + (v & 1); +} + +l9660_status l9660_readdir(l9660_dir *dir, l9660_dirent **pdirent) +{ + l9660_status rv; + l9660_file *f = &dir->file; + +rebuffer: + if(f->position >= f->length) { + *pdirent = NULL; + return L9660_OK; + } + + if ((rv = prebuffer(f))) + return rv; + + char *off = BUF(f) + fsectoff(f); + if (*off == 0) { + // Padded end of sector + f->position = fnextsectpos(f); + goto rebuffer; + } + + l9660_dirent *dirent = (l9660_dirent*) off; + f->position += aligneven(dirent->length); + + *pdirent = dirent; + return L9660_OK; +} + +l9660_status l9660_openat(l9660_file *child, l9660_dir *parent, const char * name) +{ + return openat_raw(child, parent, name, false); +} + +/*! Seek the file to \p offset from \p whence */ +l9660_status l9660_seek(l9660_file *f, int whence, int32_t offset) +{ + l9660_status rv; + uint32_t cursect = fsector(f); + + switch (whence) { + case SEEK_SET: + f->position = offset; + break; + + case SEEK_CUR: + f->position = f->position + offset; + break; + + case SEEK_END: + f->position = f->length - offset; + break; + } + + if (fsector(f) != cursect && fsectoff(f) != 0) { + if ((rv = buffer(f))) + return rv; + } + + return L9660_OK; +} + +uint32_t l9660_tell(l9660_file *f) +{ + return f->position; +} + +l9660_status l9660_read(l9660_file *f, void* buf, size_t size, size_t *read) +{ + l9660_status rv; + + if ((rv = prebuffer(f))) + return rv; + + uint16_t rem = 2048 - fsectoff(f); + if (rem > f->length - f->position) + rem = f->length - f->position; + if (rem < size) + size = rem; + + memcpy(buf, BUF(f) + fsectoff(f), size); + + *read = size; + f->position += size; + + return L9660_OK; +} diff --git a/Lib9660/lib9660.h b/Lib9660/lib9660.h new file mode 100644 index 0000000..6162a7b --- /dev/null +++ b/Lib9660/lib9660.h @@ -0,0 +1,219 @@ +/* lib9660: a simple ISO9660 reader library especially suited to embedded + * systems + * + * Copyright © 2014, Owen Shepherd + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice appears in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef LIB9660_H +#define LIB9660_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#ifdef L9660_HAVE_STDIO +#include +#define L9660_SEEK_END SEEK_END +#define L9660_SEEK_SET SEEK_SET +#define L9660_SEEK_CUR SEEK_CUR +#else +#define L9660_SEEK_END -1 +#define L9660_SEEK_SET 0 +#define L9660_SEEK_CUR +1 +#endif + +#define DENT_EXISTS (1 << 0) +#define DENT_ISDIR (1 << 1) +#define DENT_ASSOCIATED (1 << 2) +#define DENT_RECORD (1 << 3) +#define DENT_PROTECTION (1 << 4) +#define DENT_MULTIEXTENT (1 << 5) + + /* Our error return format */ + typedef enum { + /*! Success! */ + L9660_OK = 0, + /*! read_sector callback returned false */ + L9660_EIO, + /*! file system is bad */ + L9660_EBADFS, + /*! specified name does not exist */ + L9660_ENOENT, + /*! attempted to open a non-file (e.g. a directory) as a file */ + L9660_ENOTFILE, + /*! attempted to open a non-directory (e.g. a file) as a directory + * may be returned by l9660_openat if e.g. you pass path "a/b" and + * "a" is a file + */ + L9660_ENOTDIR, + } l9660_status; + + /* ISO9660 uses big/little/dual endian integers */ + typedef struct { uint8_t le[2]; } l9660_luint16; + typedef struct { uint8_t be[2]; } l9660_buint16; + typedef struct { uint8_t le[2], be[2]; } l9660_duint16; + typedef struct { uint8_t le[4]; } l9660_luint32; + typedef struct { uint8_t be[4]; } l9660_buint32; + typedef struct { uint8_t le[4], be[4]; } l9660_duint32; + + /* Descriptor time format */ + typedef struct { + char d[17]; + } l9660_desctime; + + /* File time format */ + typedef struct { + char d[7]; + } l9660_filetime; + + /* Directory entry */ + typedef struct { + uint8_t length; + uint8_t xattr_length; + l9660_duint32 sector; + l9660_duint32 size; + l9660_filetime time; + uint8_t flags; + uint8_t unit_size; + uint8_t gap_size; + l9660_duint16 vol_seq_number; + uint8_t name_len; + char name[/*name_len*/]; + } l9660_dirent; + + /* Volume descriptor header */ + typedef struct { + uint8_t type; + char magic[5]; + uint8_t version; + } l9660_vdesc_header; + + /* Primary volume descriptor */ + typedef struct { + l9660_vdesc_header hdr; + char pad0[1]; + char system_id[32]; + char volume_id[32]; + char pad1[8]; + l9660_duint32 volume_space_size; + char pad2[32]; + l9660_duint16 volume_set_size; + l9660_duint16 volume_seq_number; + l9660_duint16 logical_block_size; + l9660_duint32 path_table_size; + l9660_luint32 path_table_le; + l9660_luint32 path_table_opt_le; + l9660_buint32 path_table_be; + l9660_buint32 path_table_opt_be; + union { + l9660_dirent root_dir_ent; + char pad3[34]; + }; + char volume_set_id[128]; + char data_preparer_id[128]; + char app_id[128]; + char copyright_file[38]; + char abstract_file[36]; + char bibliography_file[37]; + l9660_desctime volume_created, + volume_modified, + volume_expires, + volume_effective; + uint8_t file_structure_version; + char pad4[1]; + char app_reserved[512]; + char reserved[653]; + } l9660_vdesc_primary; + + /* A generic volume descriptor (i.e. 2048 bytes) */ + typedef union { + l9660_vdesc_header hdr; + char _bits[2048]; + } l9660_vdesc; + + /* File system structure. + * Stick this inside your own structure and cast/offset as appropriate to store + * private data + */ + typedef struct l9660_fs { +#ifdef L9660_SINGLEBUFFER + union { + l9660_dirent root_dir_ent; + char root_dir_pad[34]; + }; +#else + /* Sector buffer to hold the PVD */ + l9660_vdesc pvd; +#endif + + /* read_sector func */ + bool (*read_sector)(struct l9660_fs* fs, void* buf, uint32_t sector); + } l9660_fs; + + typedef struct { +#ifndef L9660_SINGLEBUFFER + /* single sector buffer */ + char buf[2048]; +#endif + l9660_fs* fs; + uint32_t first_sector; + uint32_t position; + uint32_t length; + } l9660_file; + + typedef struct { + /* directories are mostly just files with special accessors, but we like type safetey */ + l9660_file file; + } l9660_dir; + + /* Open a file system, initialising *fs. */ + l9660_status l9660_openfs( + l9660_fs* fs, + bool (*read_sector)(l9660_fs* fs, void* buf, uint32_t sector)); + + /*void l9660_closefs(l9660_fs *fs); (nop) */ + + /*! Open the root directory */ + l9660_status l9660_fs_open_root(l9660_dir* dir, l9660_fs* fs); + + /*! Open the subdirectory given by \p path */ + l9660_status l9660_opendirat(l9660_dir* dir, l9660_dir* parent, const char* path); + /*! Returns the next directory entry. If end-of-directory is reached, *dirent is + * set to NULL. */ + l9660_status l9660_readdir(l9660_dir* dir, l9660_dirent** dirent); + +#define l9660_seekdir(dir, pos) (l9660_seek(&(dir)->file, L9660_SEEK_SET, (pos))) +#define l9660_telldir(dir) (l9660_tell(&(dir)->file)) + + /*! Open the file given by \p path in \p parent */ + l9660_status l9660_openat(l9660_file* file, l9660_dir* parent, const char* path); + + /*! Read \p size bytes into \p buf. The number of bytes read will be returned in + * \p *read. May be less than \p size (but only 0 on EOF) + */ + l9660_status l9660_read(l9660_file* file, void* buf, size_t size, size_t* read); + /*! Seek the file to \p offset from \p whence */ + l9660_status l9660_seek(l9660_file* file, int whence, int32_t offset); + /*! Return the current position (suitable for passing to l9660_seek(file, SEEK_SET, ...)) */ + uint32_t l9660_tell(l9660_file* file); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/ImGui.lib b/lib/ImGui.lib index aa1d1fd..80485ec 100644 Binary files a/lib/ImGui.lib and b/lib/ImGui.lib differ diff --git a/lib/Lib9660.lib b/lib/Lib9660.lib new file mode 100644 index 0000000..c94c524 Binary files /dev/null and b/lib/Lib9660.lib differ diff --git a/lib/libdvdcss.lib b/lib/libdvdcss.lib index d61b7c0..0af46a1 100644 Binary files a/lib/libdvdcss.lib and b/lib/libdvdcss.lib differ diff --git a/libdvdcss/msvc/libdvdcss.vcxproj b/libdvdcss/msvc/libdvdcss.vcxproj index c6c0111..6fe6715 100644 --- a/libdvdcss/msvc/libdvdcss.vcxproj +++ b/libdvdcss/msvc/libdvdcss.vcxproj @@ -112,7 +112,7 @@ true - $(SolutionDir)lib\ + $(SolutionDir)lib\$(TargetName)$(TargetExt) @@ -148,7 +148,7 @@ MultiThreadedDebug Default - false + true Disabled true Level3 @@ -172,7 +172,7 @@ true - $(SolutionDir)lib\ + $(SolutionDir)lib\$(TargetName)$(TargetExt) diff --git a/libdvdcss/src/dvdcss/dvdcss.h b/libdvdcss/src/dvdcss/dvdcss.h index 2d86043..a38b1ad 100644 --- a/libdvdcss/src/dvdcss/dvdcss.h +++ b/libdvdcss/src/dvdcss/dvdcss.h @@ -106,7 +106,9 @@ LIBDVDCSS_EXPORT const char *dvdcss_error ( const dvdcss_t ); LIBDVDCSS_EXPORT int dvdcss_is_scrambled ( dvdcss_t ); +LIBDVDCSS_EXPORT int dvdcss_title(dvdcss_t dvdcss, int i_block); LIBDVDCSS_EXPORT int dvdcss_get_raw_fd(dvdcss_t); +LIBDVDCSS_EXPORT int dvdcss_was_error(dvdcss_t); #ifdef __cplusplus } #endif diff --git a/libdvdcss/src/libdvdcss.c b/libdvdcss/src/libdvdcss.c index 54e5d38..063d7dc 100644 --- a/libdvdcss/src/libdvdcss.c +++ b/libdvdcss/src/libdvdcss.c @@ -486,11 +486,24 @@ static void init_cache( dvdcss_t dvdcss ) /* If the cache is enabled, create a DVD-specific subdirectory. */ create_cache_subdir( dvdcss ); } +int dvdcss_was_error_common(dvdcss_t drive) { + if (drive->b_errors) + { + drive->b_errors = FALSE; + return TRUE; + } + return FALSE; +} int dvdcss_get_fd_common(dvdcss_t drive) { return drive->i_fd; } +LIBDVDCSS_EXPORT int dvdcss_was_error(dvdcss_t drive) +{ + return dvdcss_was_error_common(drive); +} + LIBDVDCSS_EXPORT int dvdcss_get_raw_fd(dvdcss_t drive) { return dvdcss_get_fd_common(drive);