From 47a732d0fe5078a922a8217294929e371f38e13e Mon Sep 17 00:00:00 2001 From: Ty Lamontagne Date: Mon, 25 Nov 2024 18:58:54 -0500 Subject: [PATCH 1/3] CDVD: CHD cue support for audio CDs experimental and will blow out your ears --- pcsx2/CDVD/CDVDcommon.h | 8 ++ pcsx2/CDVD/CDVDdiscReader.h | 8 -- pcsx2/CDVD/CDVDisoReader.cpp | 119 +++++++++++++++++++++++------- pcsx2/CDVD/ChdFileReader.cpp | 42 +++++++++-- pcsx2/CDVD/ChdFileReader.h | 4 +- pcsx2/CDVD/InputIsoFile.cpp | 13 +++- pcsx2/CDVD/IsoFileFormats.h | 3 + pcsx2/CDVD/ThreadedFileReader.cpp | 6 ++ pcsx2/CDVD/ThreadedFileReader.h | 2 + 9 files changed, 162 insertions(+), 43 deletions(-) diff --git a/pcsx2/CDVD/CDVDcommon.h b/pcsx2/CDVD/CDVDcommon.h index 4ac864d0fde19..fb2d46d017445 100644 --- a/pcsx2/CDVD/CDVDcommon.h +++ b/pcsx2/CDVD/CDVDcommon.h @@ -66,6 +66,14 @@ struct cdvdTN u8 etrack; //number of the last track }; +struct toc_entry +{ + u32 lba; + u8 track; + u8 adr : 4; + u8 control : 4; +}; + // SpindleCtrl Masks #define CDVD_SPINDLE_SPEED 0x7 // Speed ranges from 0-3 (1, 2, 3, 4x for DVD) and 0-5 (1, 2, 4, 12, 24x for CD) #define CDVD_SPINDLE_NOMINAL 0x40 // Changes the speed to be constant (CLV) based on current speed diff --git a/pcsx2/CDVD/CDVDdiscReader.h b/pcsx2/CDVD/CDVDdiscReader.h index 9b717357164c9..7b341f37a8ba6 100644 --- a/pcsx2/CDVD/CDVDdiscReader.h +++ b/pcsx2/CDVD/CDVDdiscReader.h @@ -21,14 +21,6 @@ class Error; extern int curDiskType; extern int curTrayStatus; -struct toc_entry -{ - u32 lba; - u8 track; - u8 adr : 4; - u8 control : 4; -}; - class IOCtlSrc { IOCtlSrc(const IOCtlSrc&) = delete; diff --git a/pcsx2/CDVD/CDVDisoReader.cpp b/pcsx2/CDVD/CDVDisoReader.cpp index f15064f5b5b5e..e7bd0570c5d3a 100644 --- a/pcsx2/CDVD/CDVDisoReader.cpp +++ b/pcsx2/CDVD/CDVDisoReader.cpp @@ -18,6 +18,44 @@ static int pmode, cdtype; static s32 layer1start = -1; static bool layer1searched = false; +static void ISOParseTOC() +{ + tracks.fill(cdvdTrack{}); + if (iso.GetType() != ISOTYPE_AUDIO) + { + strack = 1; + etrack = 1; + return; + } + + strack = 0xFF; + etrack = 0; + // Audio CD + for (const auto& entry : iso.ReadTOC()) + { + const u8 track = entry.track; + if (track < 1 || track >= tracks.size()) + { + Console.Warning("CDVD: Invalid track index %u, ignoring\n", track); + continue; + } + strack = std::min(strack, track); + etrack = std::max(etrack, track); + tracks[track].start_lba = entry.lba; + + if ((entry.control & 0x0C) == 0x04) + { + Console.Warning("CDVD: Unsupported data track reading. Assuming MODE1?\n"); + tracks[track].type = CDVD_MODE1_TRACK; + } + else + { + tracks[track].type = CDVD_AUDIO_TRACK; + } + } + +} + static void ISOclose() { iso.Close(); @@ -49,6 +87,8 @@ static bool ISOopen(std::string filename, Error* error) break; } + ISOParseTOC(); + layer1start = -1; layer1searched = false; @@ -60,34 +100,56 @@ static bool ISOprecache(ProgressCallback* progress, Error* error) return iso.Precache(progress, error); } +static void lsn_to_msf(u8* minute, u8* second, u8* frame, u32 lsn) +{ + *frame = itob(lsn % 75); + lsn /= 75; + *second = itob(lsn % 60); + lsn /= 60; + *minute = itob(lsn % 100); +} + static s32 ISOreadSubQ(u32 lsn, cdvdSubQ* subq) { // fake it - u8 min, sec, frm; - subq->ctrl = 4; - subq->adr = 1; - subq->trackNum = itob(1); - subq->trackIndex = itob(1); + - lba_to_msf(lsn, &min, &sec, &frm); - subq->trackM = itob(min); - subq->trackS = itob(sec); - subq->trackF = itob(frm); + memset(subq, 0, sizeof(cdvdSubQ)); - subq->pad = 0; + lsn_to_msf(&subq->discM, &subq->discS, &subq->discF, lsn + 150); - lba_to_msf(lsn + (2 * 75), &min, &sec, &frm); - subq->discM = itob(min); - subq->discS = itob(sec); - subq->discF = itob(frm); + // FIXME: Verify this is correct for ISOTYPE_CD :S + if (iso.GetType() != ISOTYPE_AUDIO && iso.GetType() != ISOTYPE_CD) + { + subq->ctrl = 4; + subq->adr = 1; + subq->trackNum = itob(1); + subq->trackIndex = itob(1); + } + else + { + u8 i = strack; + while (i < etrack && lsn >= tracks[i + 1].start_lba) + ++i; + lsn -= tracks[i].start_lba; + + subq->ctrl = 1; + subq->adr = 1; + subq->trackNum = i; + subq->trackIndex = 1; // FIXME ??? + } + + lsn_to_msf(&subq->trackM, &subq->trackS, &subq->trackF, lsn); + + Console.Warning("CDVD: SubQ M %02x S %02x F %02x\n", subq->trackM, subq->trackS, subq->trackF); return 0; } static s32 ISOgetTN(cdvdTN* Buffer) { - Buffer->strack = 1; - Buffer->etrack = 1; + Buffer->strack = strack; + Buffer->etrack = etrack; return 0; } @@ -97,13 +159,15 @@ static s32 ISOgetTD(u8 Track, cdvdTD* Buffer) if (Track == 0) { Buffer->lsn = iso.GetBlockCount(); + Buffer->type = 0; + return 0; } - else - { - Buffer->type = CDVD_MODE1_TRACK; - Buffer->lsn = 0; - } - + + if (Track < strack || Track > etrack) + return -1; + + Buffer->lsn = tracks[Track].start_lba; + Buffer->type = tracks[Track].type; return 0; } @@ -299,11 +363,12 @@ static s32 ISOgetTOC(void* toc) { err = ISOgetTD(i, &trackInfo); lba_to_msf(trackInfo.lsn, &min, &sec, &frm); - tocBuff[i * 10 + 30] = trackInfo.type; - tocBuff[i * 10 + 32] = err == -1 ? 0 : itob(i); //number - tocBuff[i * 10 + 37] = itob(min); - tocBuff[i * 10 + 38] = itob(sec); - tocBuff[i * 10 + 39] = itob(frm); + const u8 tocIndex = i - diskInfo.strack; + tocBuff[tocIndex * 10 + 30] = trackInfo.type; + tocBuff[tocIndex * 10 + 32] = err == -1 ? 0 : itob(i); //number + tocBuff[tocIndex * 10 + 37] = itob(min); + tocBuff[tocIndex * 10 + 38] = itob(sec); + tocBuff[tocIndex * 10 + 39] = itob(frm); } } else diff --git a/pcsx2/CDVD/ChdFileReader.cpp b/pcsx2/CDVD/ChdFileReader.cpp index 8cdb7e4193ad0..112542631ab17 100644 --- a/pcsx2/CDVD/ChdFileReader.cpp +++ b/pcsx2/CDVD/ChdFileReader.cpp @@ -176,8 +176,9 @@ bool ChdFileReader::Open2(std::string filename, Error* error) // The file size in the header is incorrect, each track gets padded to a multiple of 4 frames. // (see chdman.cpp from MAME). Instead, we pull the real frame count from the TOC. + std::vector entries; u64 total_frames; - if (ParseTOC(&total_frames)) + if (ParseTOC(&total_frames, entries)) { file_size = total_frames * static_cast(chd_header->unitbytes); } @@ -216,6 +217,21 @@ bool ChdFileReader::Precache2(ProgressCallback* progress, Error* error) return true; } +std::vector ChdFileReader::ReadTOC() +{ + u64 total_frames; + std::vector entries; + if (ParseTOC(&total_frames, entries)) + { + return entries; + } + else + { + Console.Warning("Failed to parse CHD TOC, file size may be incorrect."); + return {}; + } +} + ThreadedFileReader::Chunk ChdFileReader::ChunkForOffset(u64 offset) { Chunk chunk = {0}; @@ -261,7 +277,7 @@ u32 ChdFileReader::GetBlockCount() const return (file_size - m_dataoffset) / m_internalBlockSize; } -bool ChdFileReader::ParseTOC(u64* out_frame_count) +bool ChdFileReader::ParseTOC(u64* out_frame_count, std::vector& entries) { u64 total_frames = 0; int max_found_track = -1; @@ -305,15 +321,29 @@ bool ChdFileReader::ParseTOC(u64* out_frame_count) } } - DevCon.WriteLn(fmt::format("CHD Track {}: frames:{} pregap:{} postgap:{} type:{} sub:{} pgtype:{} pgsub:{}", + Console.WriteLn(fmt::format("CHD Track {}: frames:{} pregap:{} postgap:{} type:{} sub:{} pgtype:{} pgsub:{}", track_num, frames, pregap_frames, postgap_frames, type_str, subtype_str, pgtype_str, pgsub_str)); - // PCSX2 doesn't currently support multiple tracks for CDs. - if (track_num != 1) + if (track_num != 0) + { + toc_entry entry{}; + entry.lba = static_cast(total_frames); + entry.track = static_cast(track_num); + entry.adr = 1; + entry.control = 0; + + //FIXME: DATA track? + if (strncmp(type_str, "AUDIO", 5) != 0) + entry.control |= 0x04; + + entries.push_back(entry); + } + // PCSX2 doesn't currently support multiple tracks for CDs. + /* if (track_num != 1) { Console.Warning(fmt::format(" Ignoring track {} in CHD.", track_num, frames)); continue; - } + }*/ total_frames += static_cast(pregap_frames) + static_cast(frames) + static_cast(postgap_frames); max_found_track = std::max(max_found_track, track_num); diff --git a/pcsx2/CDVD/ChdFileReader.h b/pcsx2/CDVD/ChdFileReader.h index 063a9aff74887..88706cf26e0b4 100644 --- a/pcsx2/CDVD/ChdFileReader.h +++ b/pcsx2/CDVD/ChdFileReader.h @@ -19,6 +19,8 @@ class ChdFileReader final : public ThreadedFileReader bool Precache2(ProgressCallback* progress, Error* error) override; + std::vector ReadTOC() override; + Chunk ChunkForOffset(u64 offset) override; int ReadChunk(void* dst, s64 blockID) override; @@ -26,7 +28,7 @@ class ChdFileReader final : public ThreadedFileReader uint GetBlockCount(void) const override; private: - bool ParseTOC(u64* out_frame_count); + bool ParseTOC(u64* out_frame_count, std::vector& entries); chd_file* ChdFile = nullptr; u64 file_size = 0; diff --git a/pcsx2/CDVD/InputIsoFile.cpp b/pcsx2/CDVD/InputIsoFile.cpp index 4e1fa5490a3c7..5240925bb17b6 100644 --- a/pcsx2/CDVD/InputIsoFile.cpp +++ b/pcsx2/CDVD/InputIsoFile.cpp @@ -163,6 +163,17 @@ int InputIsoFile::FinishRead3(u8* dst, uint mode) return 0; } +std::vector InputIsoFile::ReadTOC() const +{ + std::vector toc; + + if (m_type == ISOTYPE_ILLEGAL) + return toc; + + toc = m_reader->ReadTOC(); + return toc; +} + InputIsoFile::InputIsoFile() { _init(); @@ -271,7 +282,7 @@ bool InputIsoFile::tryIsoType(u32 size, u32 offset, u32 blockofs) // Returns true if the image is valid/known/supported, or false if not (type == ISOTYPE_ILLEGAL). bool InputIsoFile::Detect(bool readType) { - m_type = ISOTYPE_ILLEGAL; + m_type = ISOTYPE_ILLEGAL; // First sanity check: no sane CD image has less than 16 sectors, since that's what // we need simply to contain a TOC. So if the file size is not large enough to diff --git a/pcsx2/CDVD/IsoFileFormats.h b/pcsx2/CDVD/IsoFileFormats.h index 01d7672a66507..0ba751ae61c5d 100644 --- a/pcsx2/CDVD/IsoFileFormats.h +++ b/pcsx2/CDVD/IsoFileFormats.h @@ -3,6 +3,7 @@ #pragma once +#include "CDVDcommon.h" #include "CDVD/CDVD.h" #include "CDVD/ThreadedFileReader.h" #include @@ -75,6 +76,8 @@ class InputIsoFile final void BeginRead2(uint lsn); int FinishRead3(u8* dest, uint mode); + std::vector ReadTOC() const; + protected: void _init(); diff --git a/pcsx2/CDVD/ThreadedFileReader.cpp b/pcsx2/CDVD/ThreadedFileReader.cpp index 95f21ba2127af..c4a6baa6d634d 100644 --- a/pcsx2/CDVD/ThreadedFileReader.cpp +++ b/pcsx2/CDVD/ThreadedFileReader.cpp @@ -264,6 +264,12 @@ bool ThreadedFileReader::Precache2(ProgressCallback* progress, Error* error) return false; } +std::vector ThreadedFileReader::ReadTOC() +{ + return {}; +} + + bool ThreadedFileReader::CheckAvailableMemoryForPrecaching(u64 required_size, Error* error) { // Don't allow precaching to use more than 50% of system memory. diff --git a/pcsx2/CDVD/ThreadedFileReader.h b/pcsx2/CDVD/ThreadedFileReader.h index 75945cc7ac6cd..76c68378d28cc 100644 --- a/pcsx2/CDVD/ThreadedFileReader.h +++ b/pcsx2/CDVD/ThreadedFileReader.h @@ -4,6 +4,7 @@ #pragma once #include "common/Pcsx2Defs.h" +#include "CDVDcommon.h" #include #include @@ -117,6 +118,7 @@ class ThreadedFileReader bool Open(std::string filename, Error* error); bool Precache(ProgressCallback* progress, Error* error); + virtual std::vector ReadTOC(); int ReadSync(void* pBuffer, u32 sector, u32 count); void BeginRead(void* pBuffer, u32 sector, u32 count); int FinishRead(); From d8f18a3318d7801633b4205bbe49f39e9a6d52e5 Mon Sep 17 00:00:00 2001 From: Ty Lamontagne Date: Tue, 26 Nov 2024 16:53:12 -0500 Subject: [PATCH 2/3] CDVD: Byte swap hack. CHD CD audio with OSDSYS is working --- pcsx2/CDVD/InputIsoFile.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pcsx2/CDVD/InputIsoFile.cpp b/pcsx2/CDVD/InputIsoFile.cpp index 5240925bb17b6..e16f0b2170e15 100644 --- a/pcsx2/CDVD/InputIsoFile.cpp +++ b/pcsx2/CDVD/InputIsoFile.cpp @@ -160,6 +160,17 @@ int InputIsoFile::FinishRead3(u8* dst, uint mode) dst[diff - 9] = 2; } + // Seems like CHD data ends up being the wrong endianess for audio + // Confidence is about 50% on this one, but it seems to work + // (CHD is the only file with a TOC anyways, so who cares about the other formats) + if (m_type == ISOTYPE_AUDIO && mode == CDVD_MODE_2352) + { + for (int i = 0; i < 2352; i += 2) + { + std::swap(dst[diff + i], dst[diff + i + 1]); + } + } + return 0; } From 37555e304803813d03341071092586cf3c140ef5 Mon Sep 17 00:00:00 2001 From: Ty Lamontagne Date: Wed, 27 Nov 2024 14:54:44 -0500 Subject: [PATCH 3/3] CDVD | CHD: Account for pre/post gap offsets when filling TOC This will fix discs with tracks with both an INDEX 00 and INDEX 01 --- pcsx2/CDVD/ChdFileReader.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/pcsx2/CDVD/ChdFileReader.cpp b/pcsx2/CDVD/ChdFileReader.cpp index 112542631ab17..60d60af2867a0 100644 --- a/pcsx2/CDVD/ChdFileReader.cpp +++ b/pcsx2/CDVD/ChdFileReader.cpp @@ -281,7 +281,7 @@ bool ChdFileReader::ParseTOC(u64* out_frame_count, std::vector& entri { u64 total_frames = 0; int max_found_track = -1; - + u64 total_gap_frames = 0; for (int search_index = 0;; search_index++) { char metadata_str[256]; @@ -327,7 +327,7 @@ bool ChdFileReader::ParseTOC(u64* out_frame_count, std::vector& entri if (track_num != 0) { toc_entry entry{}; - entry.lba = static_cast(total_frames); + entry.lba = static_cast(total_frames) - total_gap_frames; entry.track = static_cast(track_num); entry.adr = 1; entry.control = 0; @@ -338,14 +338,11 @@ bool ChdFileReader::ParseTOC(u64* out_frame_count, std::vector& entri entries.push_back(entry); } - // PCSX2 doesn't currently support multiple tracks for CDs. - /* if (track_num != 1) - { - Console.Warning(fmt::format(" Ignoring track {} in CHD.", track_num, frames)); - continue; - }*/ - total_frames += static_cast(pregap_frames) + static_cast(frames) + static_cast(postgap_frames); + // I have not found a CHD with an audio track with a postgap, consider that untested + total_gap_frames += static_cast(pregap_frames) + static_cast(postgap_frames); + total_frames += total_gap_frames + static_cast(frames); + max_found_track = std::max(max_found_track, track_num); }