diff --git a/.gitmodules b/.gitmodules index b336aa2b43..52ef064f6e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "3rdparty/yaml-cpp/yaml-cpp"] path = 3rdparty/yaml-cpp/yaml-cpp url = https://github.com/jbeder/yaml-cpp.git +[submodule "3rdparty/libchdr/libchdr"] + path = 3rdparty/libchdr/libchdr + url = http://github.com/rtissera/libchdr.git diff --git a/3rdparty/libchdr/libchdr b/3rdparty/libchdr/libchdr new file mode 160000 index 0000000000..97706b2bf8 --- /dev/null +++ b/3rdparty/libchdr/libchdr @@ -0,0 +1 @@ +Subproject commit 97706b2bf84b78bbf68b231d6870cc05e9e1fe7e diff --git a/3rdparty/libchdr/libchdr.vcxproj b/3rdparty/libchdr/libchdr.vcxproj new file mode 100644 index 0000000000..37174242ae --- /dev/null +++ b/3rdparty/libchdr/libchdr.vcxproj @@ -0,0 +1,99 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Devel + Win32 + + + Devel + x64 + + + Release + Win32 + + + Release + x64 + + + + + {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0} + Win32Proj + + + + StaticLibrary + $(DefaultPlatformToolset) + MultiByte + true + true + false + + + + + + + + + + + + + + AllRules.ruleset + + + + + + + + %(PreprocessorDefinitions);_7ZIP_ST + TurnOffAllWarnings + $(ProjectDir)/libchdr/include/;$(ProjectDir)/libchdr/src/;$(ProjectDir)/libchdr/deps/lzma-19.00/include;$(ProjectDir)/libchdr/deps/zlib-1.2.11;%(AdditionalIncludeDirectories) + stdcpp17 + MaxSpeed + Speed + true + true + Default + false + false + false + false + + + + + + + + + + + + + + + false + + + + + + + + + \ No newline at end of file diff --git a/PCSX2_suite.sln b/PCSX2_suite.sln index 5656d960a3..5ae9d362bf 100644 --- a/PCSX2_suite.sln +++ b/PCSX2_suite.sln @@ -67,6 +67,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsamplerate", "3rdparty\l EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yaml", "3rdparty\yaml-cpp\yaml.vcxproj", "{48329442-E41B-4A1F-8364-36EEE1B71343}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libchdr", "3rdparty\libchdr\libchdr.vcxproj", "{A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -527,6 +529,7 @@ Global {449AD25E-424A-4714-BABC-68706CDCC33B} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} {48329442-E41B-4A1F-8364-36EEE1B71343} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} + {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0BC474EA-3628-45D3-9DBC-E22D0B7E0F77} diff --git a/cmake/SearchForStuff.cmake b/cmake/SearchForStuff.cmake index caa678f544..74259efc4b 100644 --- a/cmake/SearchForStuff.cmake +++ b/cmake/SearchForStuff.cmake @@ -288,3 +288,6 @@ if(NOT USE_SYSTEM_YAML) message(FATAL_ERROR "No bundled yaml-cpp was found") endif() endif() + +add_subdirectory(3rdparty/libchdr/libchdr EXCLUDE_FROM_ALL) +include_directories(3rdparty/libchdr/libchdr/include) diff --git a/common/src/Utilities/CMakeLists.txt b/common/src/Utilities/CMakeLists.txt index 8d633247dc..034afe6b2f 100644 --- a/common/src/Utilities/CMakeLists.txt +++ b/common/src/Utilities/CMakeLists.txt @@ -111,7 +111,7 @@ set(UtilitiesFinalSources set(UtilitiesFinalLibs ${LIBC_LIBRARIES} # Gold (new linux linker) does not get automatically dependency of dependency ${wxWidgets_LIBRARIES} - yaml-cpp + yaml-cpp chdr-static ) add_pcsx2_lib(${Output} "${UtilitiesFinalSources}" "${UtilitiesFinalLibs}" "${UtilitiesFinalFlags}") diff --git a/pcsx2/CDVD/ChdFileReader.cpp b/pcsx2/CDVD/ChdFileReader.cpp new file mode 100644 index 0000000000..22898d032c --- /dev/null +++ b/pcsx2/CDVD/ChdFileReader.cpp @@ -0,0 +1,173 @@ +#include "PrecompiledHeader.h" +#include "ChdFileReader.h" + +#include "CDVD/CompressedFileReaderUtils.h" + +#include + + +bool ChdFileReader::CanHandle(const wxString &fileName) +{ + if (!wxFileName::FileExists(fileName) || !fileName.Lower().EndsWith(L".chd")) { + return false; + } + return true; +} + +bool ChdFileReader::Open(const wxString &fileName) +{ + m_filename = fileName; + + chd_file *child = NULL; + chd_file *parent = NULL; + chd_header *header = new chd_header; + chd_header *parent_header = new chd_header; + + wxString chds[8]; + chds[0] = fileName; + int chd_depth = 0; + chd_error error; + + do { + // Console.Error(L"chd_open checking: %s", static_cast(chds[chd_depth])); + error = chd_open(static_cast(chds[chd_depth]), CHD_OPEN_READ, NULL, &child); + if (error == CHDERR_REQUIRES_PARENT) { + if (chd_read_header(static_cast(chds[chd_depth]), header) != CHDERR_NONE) { + Console.Error(L"chd_open chd_read_header error: %s: %s", chd_error_string(error), static_cast(chds[chd_depth])); + delete header; + delete parent_header; + return false; + } + bool found_parent = false; + wxFileName wxfilename(chds[chd_depth]); + wxString dir_path = wxfilename.GetPath(); + wxDir dir(dir_path); + if (dir.IsOpened()) { + wxString parent_fileName; + bool cont = dir.GetFirst(&parent_fileName, wxString("*.", wxfilename.GetExt()), wxDIR_FILES | wxDIR_HIDDEN); + while ( cont ) { + parent_fileName = wxFileName(dir_path, parent_fileName).GetFullPath(); + if (chd_read_header(static_cast(parent_fileName), parent_header) == CHDERR_NONE && + memcmp(parent_header->sha1, header->parentsha1, sizeof(parent_header->sha1)) == 0) { + found_parent = true; + chds[++chd_depth] = wxString(parent_fileName); + break; + } + cont = dir.GetNext(&parent_fileName); + } + } + if (!found_parent) { + Console.Error(L"chd_open no parent for: %s", static_cast(chds[chd_depth])); + break; + } + } + } while(error == CHDERR_REQUIRES_PARENT); + delete parent_header; + + if (error != CHDERR_NONE) { + Console.Error(L"chd_open return error: %s", chd_error_string(error)); + delete header; + return false; + } + + // Console.Error(L"chd_opened parent: %d %s", chd_depth, static_cast(chds[chd_depth])); + for (int d = chd_depth-1; d >= 0; d--) { + // parent = child; + // child = (chd_file**)malloc(sizeof(chd_file*)); + parent = child; + child = NULL; + // Console.Error(L"chd_open opening chd: %d %s", d, static_cast(chds[d])); + error = chd_open(static_cast(chds[d]), CHD_OPEN_READ, parent, &child); + if (error != CHDERR_NONE) { + Console.Error(L"chd_open return error: %s", chd_error_string(error)); + delete header; + return false; + } + } + ChdFile = child; + if (chd_read_header(static_cast(chds[0]), header) != CHDERR_NONE) { + Console.Error(L"chd_open chd_read_header error: %s: %s", chd_error_string(error), static_cast(chds[0])); + delete header; + return false; + } + + // const chd_header *header = chd_get_header(ChdFile); + sector_size = header->unitbytes; + sector_count = header->unitcount; + sectors_per_hunk = header->hunkbytes / sector_size; + hunk_buffer = new u8[header->hunkbytes]; + current_hunk = -1; + + delete header; + return true; +} + +int ChdFileReader::ReadSync(void *pBuffer, uint sector, uint count) +{ + u8 *dst = (u8 *) pBuffer; + u32 hunk = sector / sectors_per_hunk; + u32 sector_in_hunk = sector % sectors_per_hunk; + chd_error error; + + for (uint i = 0; i < count; i++) { + if (current_hunk != hunk) { + error = chd_read(ChdFile, hunk, hunk_buffer); + if (error != CHDERR_NONE) { + Console.Error(L"chd_read return error: %s", chd_error_string(error)); + // return i * m_blocksize; + } + current_hunk = hunk; + } + memcpy(dst + i * m_blocksize, hunk_buffer + sector_in_hunk * sector_size, m_blocksize); + sector_in_hunk++; + if (sector_in_hunk >= sectors_per_hunk) { + hunk++; + sector_in_hunk = 0; + } + } + return m_blocksize * count; +} + +void ChdFileReader::BeginRead(void *pBuffer, uint sector, uint count) +{ + // TODO: Check if libchdr can read asynchronously + async_read = ReadSync(pBuffer, sector, count); +} + +int ChdFileReader::FinishRead() +{ + return async_read; +} + +void ChdFileReader::Close() +{ + if (hunk_buffer != NULL) { + //free(hunk_buffer); + delete[] hunk_buffer; + hunk_buffer = NULL; + } + if (ChdFile != NULL) { + chd_close(ChdFile); + ChdFile = NULL; + } +} + +uint ChdFileReader::GetBlockSize() const +{ + return m_blocksize; +} + +void ChdFileReader::SetBlockSize(uint blocksize) +{ + m_blocksize = blocksize; +} + +u32 ChdFileReader::GetBlockCount() const +{ + return sector_count; +} +ChdFileReader::ChdFileReader(void) +{ + ChdFile = NULL; + hunk_buffer = NULL; +}; diff --git a/pcsx2/CDVD/ChdFileReader.h b/pcsx2/CDVD/ChdFileReader.h new file mode 100644 index 0000000000..6c39f00b73 --- /dev/null +++ b/pcsx2/CDVD/ChdFileReader.h @@ -0,0 +1,34 @@ +#pragma once +#include "AsyncFileReader.h" +#include "libchdr/chd.h" + +class ChdFileReader : public AsyncFileReader +{ + DeclareNoncopyableObject(ChdFileReader); +public: + virtual ~ChdFileReader(void) { Close(); }; + + static bool CanHandle(const wxString &fileName); + bool Open(const wxString &fileName) override; + + int ReadSync(void *pBuffer, uint sector, uint count) override; + + void BeginRead(void *pBuffer, uint sector, uint count) override; + int FinishRead(void) override; + void CancelRead(void) override {}; + + void Close(void) override; + void SetBlockSize(uint blocksize); + uint GetBlockSize() const; + uint GetBlockCount(void) const override; + ChdFileReader(void); + +private: + chd_file *ChdFile; + u8 *hunk_buffer; + u32 sector_size; + u32 sector_count; + u32 sectors_per_hunk; + u32 current_hunk; + u32 async_read; +}; diff --git a/pcsx2/CDVD/CompressedFileReader.cpp b/pcsx2/CDVD/CompressedFileReader.cpp index affdef483e..c7b7a56042 100644 --- a/pcsx2/CDVD/CompressedFileReader.cpp +++ b/pcsx2/CDVD/CompressedFileReader.cpp @@ -16,12 +16,16 @@ #include "PrecompiledHeader.h" #include "AsyncFileReader.h" #include "CompressedFileReader.h" +#include "ChdFileReader.h" #include "CsoFileReader.h" #include "GzippedFileReader.h" // CompressedFileReader factory. AsyncFileReader* CompressedFileReader::GetNewReader(const wxString& fileName) { + if (ChdFileReader::CanHandle(fileName)) { + return new ChdFileReader(); + } if (GzippedFileReader::CanHandle(fileName)) { return new GzippedFileReader(); diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 232f37cf4b..ebf07efcee 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -208,6 +208,7 @@ set(pcsx2CDVDSources CDVD/OutputIsoFile.cpp CDVD/ChunksCache.cpp CDVD/CompressedFileReader.cpp + CDVD/ChdFileReader.cpp CDVD/CsoFileReader.cpp CDVD/GzippedFileReader.cpp CDVD/IsoFS/IsoFile.cpp @@ -226,6 +227,7 @@ set(pcsx2CDVDHeaders CDVD/ChunksCache.h CDVD/CompressedFileReader.h CDVD/CompressedFileReaderUtils.h + CDVD/ChdFileReader.h CDVD/CsoFileReader.h CDVD/GzippedFileReader.h CDVD/IsoFileFormats.h diff --git a/pcsx2/gui/MainMenuClicks.cpp b/pcsx2/gui/MainMenuClicks.cpp index 68cbea51a4..38a6b5934c 100644 --- a/pcsx2/gui/MainMenuClicks.cpp +++ b/pcsx2/gui/MainMenuClicks.cpp @@ -374,8 +374,8 @@ bool MainEmuFrame::_DoSelectIsoBrowser(wxString& result) wxArrayString isoFilterTypes; - isoFilterTypes.Add(pxsFmt(_("All Supported (%s)"), WX_STR((isoSupportedLabel + L" .dump" + L" .gz" + L" .cso")))); - isoFilterTypes.Add(isoSupportedList + L";*.dump" + L";*.gz" + L";*.cso"); + isoFilterTypes.Add(pxsFmt(_("All Supported (%s)"), WX_STR((isoSupportedLabel + L" .dump" + L" .gz" + L" .cso" + L" .chd")))); + isoFilterTypes.Add(isoSupportedList + L";*.dump" + L";*.gz" + L";*.cso" + L";*.chd"); isoFilterTypes.Add(pxsFmt(_("Disc Images (%s)"), WX_STR(isoSupportedLabel))); isoFilterTypes.Add(isoSupportedList); @@ -383,8 +383,8 @@ bool MainEmuFrame::_DoSelectIsoBrowser(wxString& result) isoFilterTypes.Add(pxsFmt(_("Blockdumps (%s)"), L".dump")); isoFilterTypes.Add(L"*.dump"); - isoFilterTypes.Add(pxsFmt(_("Compressed (%s)"), L".gz .cso")); - isoFilterTypes.Add(L"*.gz;*.cso"); + isoFilterTypes.Add(pxsFmt(_("Compressed (%s)"), L".gz .cso .chd")); + isoFilterTypes.Add(L"*.gz;*.cso;*.chd"); isoFilterTypes.Add(_("All Files (*.*)")); isoFilterTypes.Add(L"*.*"); diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj b/pcsx2/windows/VCprojects/pcsx2.vcxproj index 789994d856..406ad4001f 100644 --- a/pcsx2/windows/VCprojects/pcsx2.vcxproj +++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj @@ -248,6 +248,7 @@ + @@ -949,6 +950,9 @@ {48329442-e41b-4a1f-8364-36eee1b71343} + + {a0d2b3ad-1f72-4ee3-8b5c-f2c358da35f0} + {2f6c0388-20cb-4242-9f6c-a6ebb6a83f47} false @@ -971,4 +975,4 @@ - + \ No newline at end of file diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters index e9d65998ba..177aef4aba 100644 --- a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters +++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters @@ -1312,6 +1312,9 @@ AppHost\Dialogs + + System\ISO +