/*
Copyright 2024 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see .
*/
#include "build.h"
#ifdef USE_LIBCDIO
#include "types.h"
#include "imgread/common.h"
#include
#include
#include
#include
namespace hostfs
{
const std::vector& getCdromDrives()
{
static std::vector cdromDevices;
static bool devicesFetched;
if (devicesFetched)
return cdromDevices;
devicesFetched = true;
// Set a custom log handler
cdio_log_set_handler([](cdio_log_level_t level, const char message[]) {
switch (level)
{
case CDIO_LOG_DEBUG:
DEBUG_LOG(GDROM, "%s", message);
break;
case CDIO_LOG_INFO:
INFO_LOG(GDROM, "%s", message);
break;
case CDIO_LOG_WARN:
WARN_LOG(GDROM, "%s", message);
break;
case CDIO_LOG_ERROR:
case CDIO_LOG_ASSERT:
ERROR_LOG(GDROM, "%s", message);
break;
}
});
// Get the list of all hardware devices
char **list = cdio_get_devices(DRIVER_DEVICE);
if (list != nullptr)
{
for (char **dev = &list[0]; *dev != nullptr; dev++)
cdromDevices.push_back(*dev);
cdio_free_device_list(list);
}
return cdromDevices;
}
}
struct CdioDrive;
struct CdioTrack : public TrackFile
{
CdioTrack(CdioDrive& disk, bool audio)
: disk(disk), audio(audio) {}
bool Read(u32 FAD, u8 *dst, SectorFormat *sector_type, u8 *subcode, SubcodeFormat *subcode_type);
CdioDrive& disk;
bool audio;
};
struct CdioDrive : public Disc
{
bool open(const char *path)
{
const std::vector& devices = hostfs::getCdromDrives();
if (!devices.empty())
{
// If the list isn't empty, check that an entry exists for the current path
std::string lpath(path);
#ifdef _WIN32
if (lpath.substr(0, 4) != "\\\\.\\")
lpath = "\\\\.\\" + lpath;
#endif
bool found = false;
for (const std::string& dev : devices)
if (dev == lpath) {
found = true;
break;
}
if (!found) {
WARN_LOG(GDROM, "%s isn't a CD device", path);
return false;
}
}
pCdio = cdio_open(path, DRIVER_DEVICE);
if (pCdio == nullptr) {
WARN_LOG(GDROM, "Can't open CD device %s", path);
return false;
}
track_t firstTrk = cdio_get_first_track_num(pCdio);
track_t lastTrk = cdio_get_last_track_num(pCdio);
if (firstTrk == CDIO_INVALID_TRACK || lastTrk == CDIO_INVALID_TRACK)
{
WARN_LOG(GDROM, "Can't find first and/or last track");
close();
return false;
}
Session session;
session.StartFAD = 150;
session.FirstTrack = firstTrk;
sessions.push_back(session);
type = CdDA; // TODO more CD types
for (int i = firstTrk; i <= lastTrk; i++)
{
lba_t lba = cdio_get_track_lba(pCdio, i);
if (lba == CDIO_INVALID_LBA)
{
WARN_LOG(GDROM, "Can't find track %d", i);
close();
return false;
}
track_format_t format = cdio_get_track_format(pCdio, i);
bool copy = true;
if (format == TRACK_FORMAT_AUDIO) {
track_flag_t copyFlag = cdio_get_track_copy_permit(pCdio, i);
copy = copyFlag == CDIO_TRACK_FLAG_TRUE;
}
else if (!tracks.empty() && !tracks.back().isDataTrack())
{
// session 1 lead-out & session 2 lead-in and pre-gap
tracks.back().EndFAD -= 11400;
type = CdRom_XA;
Session session;
session.StartFAD = lba;
session.FirstTrack = i;
sessions.push_back(session);
}
Track t;
t.ADR = 1; // FIXME correct?
t.CTRL = format == TRACK_FORMAT_AUDIO ? 0 : CDIO_CDROM_DATA_TRACK;
t.StartFAD = lba;
lsn_t last = cdio_get_track_last_lsn(pCdio, i);
if (last == CDIO_INVALID_LSN)
WARN_LOG(GDROM, "Can't get last lsn of track %d", i);
else
t.EndFAD = cdio_lsn_to_lba(last);
if (i == firstTrk)
sessions.front().StartFAD = t.StartFAD;
INFO_LOG(GDROM, "Track #%d: start %d end %d format %d copy %d", i, t.StartFAD, t.EndFAD, format, copy);
t.file = new CdioTrack(*this, format == TRACK_FORMAT_AUDIO);
tracks.push_back(t);
}
lba_t leadout = cdio_get_track_lba(pCdio, CDIO_CDROM_LEADOUT_TRACK);
if (leadout == CDIO_INVALID_LBA)
{
WARN_LOG(GDROM, "Can't find leadout track");
close();
return false;
}
LeadOut.StartFAD = leadout;
LeadOut.ADR = 1;
LeadOut.CTRL = CDIO_CDROM_DATA_TRACK;
return true;
}
void close()
{
if (pCdio != nullptr) {
cdio_destroy(pCdio);
pCdio = nullptr;
}
}
~CdioDrive() {
close();
}
CdIo_t *pCdio = nullptr;
};
bool CdioTrack::Read(u32 FAD, u8 *dst, SectorFormat *sector_type, u8 *subcode, SubcodeFormat *subcode_type)
{
lsn_t lsn = cdio_lba_to_lsn(FAD);
if (audio)
{
*sector_type = SECFMT_2352;
if (cdio_read_audio_sector(disk.pCdio, dst, lsn) != DRIVER_OP_SUCCESS) {
WARN_LOG(GDROM, "Read audio fad %d failed", FAD);
return false;
}
}
else
{
*sector_type = SECFMT_2048_MODE2_FORM1;
if (cdio_read_mode2_sector(disk.pCdio, dst, lsn, false) != DRIVER_OP_SUCCESS)
{
if (cdio_read_mode1_sector(disk.pCdio, dst, lsn, false) != DRIVER_OP_SUCCESS) {
WARN_LOG(GDROM, "Read data fad %d failed", FAD);
return false;
}
*sector_type = SECFMT_2048_MODE1;
}
}
return true;
}
Disc *cdio_parse(const char *file, std::vector *digest)
{
INFO_LOG(GDROM, "Opening CDIO device %s", file);
CdioDrive *disk = new CdioDrive();
if (disk->open(file))
{
if (digest != nullptr)
digest->clear();
return disk;
}
else {
delete disk;
return nullptr;
}
}
#endif // USE_LIBCDIO