summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--src/devices/bus/ata/atapicdr.cpp20
-rw-r--r--src/devices/imagedev/cdromimg.cpp14
-rw-r--r--src/devices/imagedev/cdromimg.h2
-rw-r--r--src/devices/machine/t10mmc.cpp265
-rw-r--r--src/devices/machine/t10mmc.h9
-rw-r--r--src/lib/util/cdrom.cpp428
-rw-r--r--src/lib/util/cdrom.h65
7 files changed, 601 insertions, 202 deletions
diff --git a/src/devices/bus/ata/atapicdr.cpp b/src/devices/bus/ata/atapicdr.cpp
index aa32806913f..3b035b1b503 100644
--- a/src/devices/bus/ata/atapicdr.cpp
+++ b/src/devices/bus/ata/atapicdr.cpp
@@ -199,9 +199,19 @@ void atapi_cdrom_device::ExecCommand()
}
t10mmc::ExecCommand();
- // truckk requires seek complete flag to be set after calling the SEEK command
- // so set the seek complete status flag after a successful request to emulate
- // having asked the device itself to seek
- if (command[0] == T10SBC_CMD_SEEK_10 && m_status_code == SCSI_STATUS_CODE_GOOD)
- m_status |= IDE_STATUS_DSC;
+ if (m_status_code == SCSI_STATUS_CODE_GOOD)
+ {
+ switch (command[0])
+ {
+ // Set the DSC (Drive Seek Complete) bit on commands that involve a drive seek.
+ // truckk is known to rely on this flag being set after T10SBC_CMD_SEEK_10.
+ case T10SBC_CMD_SEEK_10:
+ case T10MMC_CMD_PLAY_AUDIO_10:
+ case T10MMC_CMD_PLAY_AUDIO_12:
+ case T10MMC_CMD_PLAY_AUDIO_MSF:
+ case T10MMC_CMD_PLAY_AUDIO_TRACK_INDEX:
+ m_status |= IDE_STATUS_DSC;
+ break;
+ }
+ }
}
diff --git a/src/devices/imagedev/cdromimg.cpp b/src/devices/imagedev/cdromimg.cpp
index a5ceca8b16d..5848eb349dc 100644
--- a/src/devices/imagedev/cdromimg.cpp
+++ b/src/devices/imagedev/cdromimg.cpp
@@ -184,6 +184,13 @@ int cdrom_image_device::get_last_track() const
return 0;
}
+int cdrom_image_device::get_last_session() const
+{
+ if (m_cdrom_handle)
+ return m_cdrom_handle->get_last_session();
+ return 0;
+}
+
uint32_t cdrom_image_device::get_track(uint32_t frame) const
{
if (m_cdrom_handle)
@@ -191,6 +198,13 @@ uint32_t cdrom_image_device::get_track(uint32_t frame) const
return 0;
}
+uint32_t cdrom_image_device::get_track_index(uint32_t frame) const
+{
+ if (m_cdrom_handle)
+ return m_cdrom_handle->get_track_index(frame);
+ return 0;
+}
+
uint32_t cdrom_image_device::get_track_start(uint32_t track) const
{
if (m_cdrom_handle)
diff --git a/src/devices/imagedev/cdromimg.h b/src/devices/imagedev/cdromimg.h
index 6e222703672..d37e0d5d64d 100644
--- a/src/devices/imagedev/cdromimg.h
+++ b/src/devices/imagedev/cdromimg.h
@@ -59,7 +59,9 @@ public:
virtual const char *image_brief_type_name() const noexcept override { return "cdrm"; }
int get_last_track() const;
+ int get_last_session() const;
uint32_t get_track(uint32_t frame) const;
+ uint32_t get_track_index(uint32_t frame) const;
uint32_t get_track_start(uint32_t track) const;
bool read_data(uint32_t lbasector, void *buffer, uint32_t datatype, bool phys=false);
bool read_subcode(uint32_t lbasector, void *buffer, bool phys=false);
diff --git a/src/devices/machine/t10mmc.cpp b/src/devices/machine/t10mmc.cpp
index bc3d6d0a95c..eac2c97f6d0 100644
--- a/src/devices/machine/t10mmc.cpp
+++ b/src/devices/machine/t10mmc.cpp
@@ -5,26 +5,25 @@
#include "multibyte.h"
-static int to_msf_raw(int frame)
+static int32_t to_msf_raw(int32_t frame)
{
- int m = frame / (75 * 60);
- int s = (frame / 75) % 60;
- int f = frame % 75;
-
+ const int m = (frame / (75 * 60)) % 100;
+ const int s = (frame / 75) % 60;
+ const int f = frame % 75;
return (m << 16) | (s << 8) | f;
}
-static int to_msf(int frame)
+static int32_t to_msf(int32_t frame)
{
- int adjusted_frame = frame + 150;
- if (frame <= -151)
+ int32_t adjusted_frame = frame + 150;
+ if (adjusted_frame < 0)
adjusted_frame += 450000;
return to_msf_raw(adjusted_frame);
}
-static int to_lba(int msf)
+static int32_t to_lba(int32_t msf)
{
- int lba = cdrom_file::msf_to_lba(msf) - 150;
+ int32_t lba = cdrom_file::msf_to_lba(msf) - 150;
if (BIT(msf, 16, 8) >= 90) // 90:00:00 and later
lba -= 450000;
return lba;
@@ -256,6 +255,16 @@ void t10mmc::ExecCommand()
length = 4 + (8 * 1);
break;
+ case TOC_FORMAT_FULL_TOC:
+ {
+ const int total_tracks = m_image->get_last_track();
+ const int total_sessions = m_image->get_last_session();
+ const int total_a0s = total_sessions * 3; // every session has a set of a0/a1/a2
+ const int total_b0s = (total_sessions - 1) * 2; // every additional session starts with a b0/c0 set
+ length = 4 + (11 * (total_tracks + total_a0s + total_b0s));
+ break;
+ }
+
default:
m_device->logerror("T10MMC: Unhandled READ TOC format %d\n", toc_format());
length = 0;
@@ -288,6 +297,12 @@ void t10mmc::ExecCommand()
m_lba = get_u32be(&command[2]);
m_blocks = SCSILengthFromUINT16( &command[7] );
+ if (m_lba == 0xffffffff)
+ {
+ m_device->logerror("T10MMC: play audio from current not fully implemented!\n");
+ m_lba = m_cdda->get_audio_lba();
+ }
+
if (m_lba == 0)
{
// A request for LBA 0 will return something different depending on the type of media being played.
@@ -298,10 +313,6 @@ void t10mmc::ExecCommand()
else
m_lba = 150;
}
- else if (m_lba == 0xffffffff)
- {
- m_device->logerror("T10MMC: play audio from current not implemented!\n");
- }
//m_device->logerror("T10MMC: PLAY AUDIO(10) at LBA %x for %x blocks\n", m_lba, m_blocks);
@@ -334,23 +345,19 @@ void t10mmc::ExecCommand()
break;
}
- const uint32_t msf_start = get_u24be(&command[3]);
+ uint32_t msf_start = get_u24be(&command[3]);
const uint32_t msf_end = get_u24be(&command[6]);
- int32_t lba_start = to_lba(msf_start);
- int32_t lba_end = to_lba(msf_end);
-
- // LBA valid range is technically -45150 to 404849 but negatives are not handled anywhere
- if (lba_start < 0 || lba_end < 0)
+ if (msf_start == 0xffffff)
{
- m_device->logerror("T10MMC: tried playing audio from lba %d to %d\n", lba_start, lba_end);
- m_status_code = SCSI_STATUS_CODE_CHECK_CONDITION;
- set_sense(SCSI_SENSE_KEY_ILLEGAL_REQUEST, SCSI_SENSE_ASC_ASCQ_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE);
- break;
+ // when start MSF is set to all FFs, the starting address becomes the current optical head location
+ m_lba = m_cdda->get_audio_lba();
+ msf_start = to_msf(m_lba);
}
- m_lba = lba_start;
- m_blocks = lba_end - lba_start;
+ // note: BeOS's CD player sends the start MSF + a large end MSF (99:59:71) when a scan is ended and it wants to resume playback
+ m_lba = to_lba(msf_start);
+ m_blocks = cdrom_file::msf_to_lba(msf_end - msf_start);
if (m_lba == 0)
{
@@ -372,13 +379,6 @@ void t10mmc::ExecCommand()
m_status_code = SCSI_STATUS_CODE_GOOD;
m_audio_sense = SCSI_SENSE_ASC_ASCQ_NO_SENSE;
}
- else if (msf_start == 0xffffff)
- {
- // when start MSF is set to all FFs, the starting address becomes the current optical head location
- m_device->logerror("T10MMC: play audio from current not implemented!\n");
- m_status_code = SCSI_STATUS_CODE_CHECK_CONDITION;
- set_sense(SCSI_SENSE_KEY_ILLEGAL_REQUEST, SCSI_SENSE_ASC_ASCQ_AUDIO_PLAY_OPERATION_STOPPED_DUE_TO_ERROR);
- }
else if (msf_start > msf_end)
{
m_device->logerror("T10MMC: track starts after requested end time!\n");
@@ -479,6 +479,8 @@ void t10mmc::ExecCommand()
break;
case T10MMC_CMD_STOP_PLAY_SCAN:
+ m_last_lba = m_cdda->get_audio_lba();
+
abort_audio();
//m_device->logerror("T10MMC: STOP_PLAY_SCAN\n");
@@ -512,6 +514,12 @@ void t10mmc::ExecCommand()
m_lba = get_u32be(&command[2]);
m_blocks = get_u32be(&command[6]);
+ if (m_lba == 0xffffffff)
+ {
+ m_device->logerror("T10MMC: play audio from current not fully implemented!\n");
+ m_lba = m_cdda->get_audio_lba();
+ }
+
if (m_lba == 0)
{
if (m_image->get_track_type(0) == cdrom_file::CD_TRACK_AUDIO)
@@ -519,10 +527,6 @@ void t10mmc::ExecCommand()
else
m_lba = 150;
}
- else if (m_lba == 0xffffffff)
- {
- m_device->logerror("T10MMC: play audio from current not implemented!\n");
- }
//m_device->logerror("T10MMC: PLAY AUDIO(12) at LBA %x for %x blocks\n", m_lba, m_blocks);
@@ -582,6 +586,13 @@ void t10mmc::ExecCommand()
m_transfer_length = 0;
break;
+ case T10MMC_CMD_MECHANISM_STATUS:
+ m_phase = SCSI_PHASE_DATAIN;
+ m_status_code = SCSI_STATUS_CODE_GOOD;
+ m_transfer_length = get_u16be(&command[8]);
+ m_device->logerror("T10MMC: MECHANISM STATUS requested %d bytes\n", m_transfer_length);
+ break;
+
case T10MMC_CMD_READ_CD:
{
if (!m_image->exists())
@@ -892,6 +903,32 @@ void t10mmc::ReadData( uint8_t *data, int dataLength )
}
break;
+ case T10MMC_CMD_MECHANISM_STATUS:
+ {
+ uint8_t status_header[8];
+ size_t transfer_len = get_u16be(&command[8]);
+
+ if (transfer_len == 0)
+ break;
+
+ std::fill_n(&data[0], transfer_len, 0);
+ std::fill(std::begin(status_header), std::end(status_header), 0);
+
+ put_u24be(&status_header[2], m_last_lba);
+
+ if (m_cdda->audio_active())
+ {
+ // cdrdao checks this flag after starting an audio track to play for 1 block to
+ // determine when it can read the sub q channel data when finding tracks and indexes
+ m_last_lba = m_cdda->get_audio_lba();
+ status_header[1] |= 1 << 5; // playing (audio or data) flag
+ }
+
+ std::copy_n(std::begin(status_header), std::min(std::size(status_header), transfer_len), &data[0]);
+
+ break;
+ }
+
case T10MMC_CMD_READ_CD:
//m_device->logerror("T10MMC: read CD %x dataLength lba=%x\n", dataLength, m_lba);
if ((m_image) && (m_blocks))
@@ -1134,13 +1171,11 @@ void t10mmc::ReadData( uint8_t *data, int dataLength )
return;
}
- m_device->logerror("T10MMC: READ SUB-CHANNEL Time = %x, SUBQ = %x\n", command[1], command[2]);
-
bool msf = (command[1] & 0x2) != 0;
data[0]= 0x00;
- int audio_active = m_cdda->audio_active();
+ const int audio_active = m_cdda->audio_active();
if (audio_active)
{
// if audio is playing, get the latest LBA from the CDROM layer
@@ -1168,14 +1203,18 @@ void t10mmc::ReadData( uint8_t *data, int dataLength )
}
}
+ m_device->logerror("T10MMC: READ SUB-CHANNEL Time = %x, SUBQ = %x, LBA = %d)\n", msf, command[2], m_last_lba);
+
if (command[2] & 0x40)
{
+ int track = m_image->get_track(m_last_lba);
+
data[2] = 0;
- data[3] = 12; // data length
+ data[3] = 12; // data length
data[4] = 0x01; // sub-channel format code
- data[5] = 0x10 | (audio_active ? 0 : 4);
- data[6] = m_image->get_track(m_last_lba) + 1; // track
- data[7] = 0; // index
+ data[5] = m_image->get_adr_control(track);
+ data[6] = track + 1; // track
+ data[7] = m_image->get_track_index(m_last_lba); // index
uint32_t frame = m_last_lba;
@@ -1203,8 +1242,11 @@ void t10mmc::ReadData( uint8_t *data, int dataLength )
}
break;
}
+
default:
m_device->logerror("T10MMC: Unknown subchannel type %d requested\n", command[3]);
+ std::fill_n(data, dataLength, 0);
+ break;
}
break;
@@ -1275,33 +1317,158 @@ void t10mmc::ReadData( uint8_t *data, int dataLength )
case TOC_FORMAT_SESSIONS:
{
+ auto toc = m_image->get_toc();
+ const int tracks = toc_tracks();
+ const int last_session = m_image->get_last_session();
+ uint32_t first_track_last_session = 0;
+
+ for (int i = 0; i < tracks - 1; i++)
+ {
+ if (toc.tracks[i].session == last_session - 1)
+ first_track_last_session = i;
+ }
+
int len = 2 + (8 * 1);
int dptr = 0;
put_u16be(&data[dptr], len);
dptr += 2;
data[dptr++] = 1;
- data[dptr++] = 1;
+ data[dptr++] = last_session;
data[dptr++] = 0;
- data[dptr++] = m_image->get_adr_control(0);
- data[dptr++] = 1;
+ data[dptr++] = m_image->get_adr_control(first_track_last_session);
+ data[dptr++] = first_track_last_session + 1; // First Track Number In Last Complete Session
data[dptr++] = 0;
- uint32_t tstart = m_image->get_track_start(0);
+ uint32_t tstart = m_image->get_track_start(first_track_last_session);
if (msf)
{
tstart = to_msf(tstart);
}
- put_u32be(&data[dptr], tstart);
+ put_u32be(&data[dptr], tstart); // Start Address of First Track in Last Session
dptr += 4;
}
break;
+ case TOC_FORMAT_FULL_TOC:
+ {
+ auto toc = m_image->get_toc();
+
+ const uint32_t tracks = m_image->get_last_track();
+ const uint32_t total_sessions = m_image->get_last_session();
+ const int total_a0s = total_sessions * 3; // every session has a set of a0/a1/a2
+ const int total_b0s = (total_sessions - 1) * 2; // every additional session starts with a b0/c0 set
+ const int len = 2 + (11 * (tracks + total_a0s + total_b0s));
+
+ int dptr = 0;
+ put_u16be(&data[dptr], len);
+ dptr += 2;
+ data[dptr++] = 1;
+ data[dptr++] = total_sessions;
+
+ int cur_track = 0;
+ for (uint32_t cur_session = 0; cur_session < total_sessions; cur_session++)
+ {
+ if (toc.tracks[cur_track].session != cur_session)
+ continue;
+
+ int last_track_in_session = cur_track;
+ for (int i = cur_track; i < tracks && toc.tracks[i].session == cur_session; i++)
+ last_track_in_session = i;
+
+ // point 0xa0, first track number in session
+ data[dptr++] = toc.tracks[cur_track].session + 1;
+ data[dptr++] = m_image->get_adr_control(cur_track);
+ data[dptr++] = 0;
+ data[dptr++] = 0xa0;
+ put_u24be(&data[dptr], 0);
+ dptr += 3;
+ data[dptr++] = 0;
+ data[dptr++] = cur_track + 1; // first track number in session
+ data[dptr++] = 0;
+ data[dptr++] = 0;
+
+ // point 0xa1, last track number in session
+ data[dptr++] = toc.tracks[cur_track].session + 1;
+ data[dptr++] = m_image->get_adr_control(last_track_in_session);
+ data[dptr++] = 0;
+ data[dptr++] = 0xa1;
+ put_u24be(&data[dptr], 0);
+ dptr += 3;
+ data[dptr++] = 0;
+ data[dptr++] = last_track_in_session + 1; // last track number in session
+ data[dptr++] = 0;
+ data[dptr++] = 0;
+
+ // point 0xa2, start of lead-out
+ uint32_t leadout_addr = m_image->get_track_start(last_track_in_session) + toc.tracks[last_track_in_session].frames;
+ if (toc.tracks[last_track_in_session].pgdatasize != 0)
+ leadout_addr -= toc.tracks[last_track_in_session].pregap; // pregap is already included in frame count if pgdatasize is set
+
+ data[dptr++] = toc.tracks[cur_track].session + 1;
+ data[dptr++] = m_image->get_adr_control(last_track_in_session);
+ data[dptr++] = 0;
+ data[dptr++] = 0xa2;
+ put_u24be(&data[dptr], 0);
+ dptr += 3;
+ data[dptr++] = 0;
+ put_u24be(&data[dptr], to_msf(leadout_addr)); // Track start time
+ dptr += 3;
+
+ while (cur_track < tracks && toc.tracks[cur_track].session == cur_session)
+ {
+ data[dptr++] = toc.tracks[cur_track].session + 1;
+ data[dptr++] = m_image->get_adr_control(cur_track);
+ data[dptr++] = 0;
+ data[dptr++] = cur_track + 1;
+ put_u24be(&data[dptr], 0);
+ dptr += 3;
+ data[dptr++] = 0;
+ put_u24be(&data[dptr], to_msf(m_image->get_track_start(cur_track))); // Track start time
+ dptr += 3;
+
+ cur_track++;
+ }
+
+ if (cur_session + 1 < total_sessions)
+ {
+ const uint32_t leadout_addr = m_image->get_track_start(cur_track) + toc.tracks[cur_track].frames;
+ const uint32_t next_session_program_area = m_image->get_track_start(cur_track) - 150; // fixed 2s pregap
+
+ // point 0xb0, info about next session
+ data[dptr++] = cur_session + 1;
+ data[dptr++] = (m_image->get_adr_control(cur_track - 1) & 0x0f) | 0x50;
+ data[dptr++] = 0;
+ data[dptr++] = 0xb0;
+ put_u24be(&data[dptr], to_msf(next_session_program_area)); // start time for the next possible session's program area
+ dptr += 3;
+ data[dptr++] = (total_sessions - (cur_session + 1)) * 2; // the number of different Mode-5 pointers present from this point, inclusive
+ put_u24be(&data[dptr], to_msf(leadout_addr)); // the maximum possible start time of the outermost Lead-out
+ dptr += 3;
+
+ // point 0xc0, start time of first lead-in of the disc
+ data[dptr++] = cur_session + 1;
+ data[dptr++] = (m_image->get_adr_control(0) & 0x0f) | 0x50;
+ data[dptr++] = 0;
+ data[dptr++] = 0xc0;
+ put_u24be(&data[dptr], 0);
+ dptr += 3;
+ data[dptr++] = 0;
+ // Hardcode a value of 95:00:00 because that's the most common value I've seen on various multisession CD dumps
+ put_u24be(&data[dptr], 0x5f0000); // Start time of the first Lead-in area of the disc
+ dptr += 3;
+ }
+ }
+
+ break;
+ }
+
default:
m_device->logerror("T10MMC: Unhandled READ TOC format %d\n", toc_format());
+ std::fill_n(data, dataLength, 0);
break;
}
}
diff --git a/src/devices/machine/t10mmc.h b/src/devices/machine/t10mmc.h
index 9b84e929c29..852e21db046 100644
--- a/src/devices/machine/t10mmc.h
+++ b/src/devices/machine/t10mmc.h
@@ -45,6 +45,7 @@ protected:
T10MMC_CMD_PLAY_AUDIO_12 = 0xa5,
T10MMC_CMD_READ_DISC_STRUCTURE = 0xad,
T10MMC_CMD_SET_CD_SPEED = 0xbb,
+ T10MMC_CMD_MECHANISM_STATUS = 0xbd,
T10MMC_CMD_READ_CD = 0xbe
};
@@ -70,8 +71,12 @@ protected:
enum toc_format_t
{
- TOC_FORMAT_TRACKS = 0,
- TOC_FORMAT_SESSIONS = 1
+ TOC_FORMAT_TRACKS = 0, // legacy toc
+ TOC_FORMAT_SESSIONS = 1, // multi-session information
+ TOC_FORMAT_FULL_TOC = 2, // full TOC - includes sessions, lead-ins, lead-outs
+ TOC_FORMAT_PMA = 3, // PMA
+ TOC_FORMAT_ATIP = 4, // ATIP
+ TOC_FORMAT_CDTEXT = 5, // valid only for CD audio discs
};
void abort_audio();
diff --git a/src/lib/util/cdrom.cpp b/src/lib/util/cdrom.cpp
index 159a9c964a1..d9074bfd838 100644
--- a/src/lib/util/cdrom.cpp
+++ b/src/lib/util/cdrom.cpp
@@ -21,6 +21,7 @@
#include "corestr.h"
#include "multibyte.h"
#include "osdfile.h"
+#include "path.h"
#include "strformat.h"
#include <cassert>
@@ -180,6 +181,9 @@ cdrom_file::cdrom_file(std::string_view inputfile)
track.logframeofs = track.pregap;
}
+ if ((cdtoc.flags & CD_FLAG_MULTISESSION) && cdtrack_info.track[i].leadin != -1)
+ logofs += cdtrack_info.track[i].leadin;
+
track.physframeofs = physofs;
track.chdframeofs = 0;
track.logframeofs += logofs;
@@ -191,8 +195,13 @@ cdrom_file::cdrom_file(std::string_view inputfile)
physofs += track.frames;
logofs += track.frames;
+ if ((cdtoc.flags & CD_FLAG_MULTISESSION) && cdtrack_info.track[i].leadout != -1)
+ logofs += cdtrack_info.track[i].leadout;
+
if (EXTRA_VERBOSE)
- printf("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d logframes %d pad %d\n", i+1,
+ printf("session %d track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d logframes %d pad %d\n",
+ track.session+1,
+ i+1,
track.trktype,
track.subtype,
track.datasize,
@@ -290,7 +299,9 @@ cdrom_file::cdrom_file(chd_file *_chd)
logofs += track.frames;
if (EXTRA_VERBOSE)
- printf("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d logframes %d pad %d\n", i+1,
+ printf("session %d track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d logframes %d pad %d\n",
+ track.session+1,
+ i+1,
track.trktype,
track.subtype,
track.datasize,
@@ -583,6 +594,27 @@ uint32_t cdrom_file::get_track(uint32_t frame) const
return track;
}
+uint32_t cdrom_file::get_track_index(uint32_t frame) const
+{
+ const uint32_t track = get_track(frame);
+ const uint32_t track_start = get_track_start(track);
+ const uint32_t index_offset = frame - track_start;
+ int index = 0;
+
+ for (int i = 0; i < std::size(cdtrack_info.track[track].idx); i++)
+ {
+ if (index_offset >= cdtrack_info.track[track].idx[i])
+ index = i;
+ else
+ break;
+ }
+
+ if (cdtrack_info.track[track].idx[index] == -1)
+ index = 1; // valid index not found, default to index 1
+
+ return index;
+}
+
/***************************************************************************
EXTRA UTILITIES
@@ -868,70 +900,57 @@ std::error_condition cdrom_file::parse_metadata(chd_file *chd, toc &toc)
/* clear structures */
memset(&toc, 0, sizeof(toc));
+ toc.numsessions = 1;
+
/* start with no tracks */
for (toc.numtrks = 0; toc.numtrks < MAX_TRACKS; toc.numtrks++)
{
- int tracknum = -1, frames = 0, pregap, postgap, padframes;
+ int tracknum, frames, pregap, postgap, padframes;
char type[16], subtype[16], pgtype[16], pgsub[16];
track_info *track;
- pregap = postgap = padframes = 0;
+ tracknum = -1;
+ frames = pregap = postgap = padframes = 0;
+ std::fill(std::begin(type), std::end(type), 0);
+ std::fill(std::begin(subtype), std::end(subtype), 0);
+ std::fill(std::begin(pgtype), std::end(pgtype), 0);
+ std::fill(std::begin(pgsub), std::end(pgsub), 0);
/* fetch the metadata for this track */
- err = chd->read_metadata(CDROM_TRACK_METADATA_TAG, toc.numtrks, metadata);
- if (!err)
+ if (!chd->read_metadata(CDROM_TRACK_METADATA_TAG, toc.numtrks, metadata))
{
- /* parse the metadata */
- type[0] = subtype[0] = 0;
- pgtype[0] = pgsub[0] = 0;
if (sscanf(metadata.c_str(), CDROM_TRACK_METADATA_FORMAT, &tracknum, type, subtype, &frames) != 4)
return chd_file::error::INVALID_DATA;
- if (tracknum == 0 || tracknum > MAX_TRACKS)
+ }
+ else if (!chd->read_metadata(CDROM_TRACK_METADATA2_TAG, toc.numtrks, metadata))
+ {
+ if (sscanf(metadata.c_str(), CDROM_TRACK_METADATA2_FORMAT, &tracknum, type, subtype, &frames, &pregap, pgtype, pgsub, &postgap) != 8)
return chd_file::error::INVALID_DATA;
- track = &toc.tracks[tracknum - 1];
}
else
{
- err = chd->read_metadata(CDROM_TRACK_METADATA2_TAG, toc.numtrks, metadata);
+ /* fall through to GD-ROM detection */
+ err = chd->read_metadata(GDROM_OLD_METADATA_TAG, toc.numtrks, metadata);
if (!err)
- {
- /* parse the metadata */
- type[0] = subtype[0] = 0;
- pregap = postgap = 0;
- if (sscanf(metadata.c_str(), CDROM_TRACK_METADATA2_FORMAT, &tracknum, type, subtype, &frames, &pregap, pgtype, pgsub, &postgap) != 8)
- return chd_file::error::INVALID_DATA;
- if (tracknum == 0 || tracknum > MAX_TRACKS)
- return chd_file::error::INVALID_DATA;
- track = &toc.tracks[tracknum - 1];
- }
+ /* legacy GDROM track was detected */
+ toc.flags |= CD_FLAG_GDROMLE;
else
- {
- err = chd->read_metadata(GDROM_OLD_METADATA_TAG, toc.numtrks, metadata);
- if (!err)
- /* legacy GDROM track was detected */
- toc.flags |= CD_FLAG_GDROMLE;
- else
- err = chd->read_metadata(GDROM_TRACK_METADATA_TAG, toc.numtrks, metadata);
+ err = chd->read_metadata(GDROM_TRACK_METADATA_TAG, toc.numtrks, metadata);
- if (!err)
- {
- /* parse the metadata */
- type[0] = subtype[0] = 0;
- pregap = postgap = 0;
- if (sscanf(metadata.c_str(), GDROM_TRACK_METADATA_FORMAT, &tracknum, type, subtype, &frames, &padframes, &pregap, pgtype, pgsub, &postgap) != 9)
- return chd_file::error::INVALID_DATA;
- if (tracknum == 0 || tracknum > MAX_TRACKS)
- return chd_file::error::INVALID_DATA;
- track = &toc.tracks[tracknum - 1];
- toc.flags |= CD_FLAG_GDROM;
- }
- else
- {
- break;
- }
- }
+ if (err)
+ break;
+
+ if (sscanf(metadata.c_str(), GDROM_TRACK_METADATA_FORMAT, &tracknum, type, subtype, &frames, &padframes, &pregap, pgtype, pgsub, &postgap) != 9)
+ return chd_file::error::INVALID_DATA;
+
+ toc.flags |= CD_FLAG_GDROM;
}
+ if (tracknum == 0 || tracknum > MAX_TRACKS)
+ return chd_file::error::INVALID_DATA;
+
+ track = &toc.tracks[tracknum - 1];
+
/* extract the track type and determine the data size */
track->trktype = CD_TRACK_MODE1;
track->datasize = 0;
@@ -986,8 +1005,11 @@ std::error_condition cdrom_file::parse_metadata(chd_file *chd, toc &toc)
auto *mrp = reinterpret_cast<uint32_t *>(&oldmetadata[0]);
toc.numtrks = *mrp++;
+ toc.numsessions = 1;
+
for (int i = 0; i < MAX_TRACKS; i++)
{
+ toc.tracks[i].session = 0;
toc.tracks[i].trktype = *mrp++;
toc.tracks[i].subtype = *mrp++;
toc.tracks[i].datasize = *mrp++;
@@ -1045,7 +1067,16 @@ std::error_condition cdrom_file::write_metadata(chd_file *chd, const toc &toc)
for (int i = 0; i < toc.numtrks; i++)
{
std::string metadata;
- if (!(toc.flags & CD_FLAG_GDROM))
+ if (toc.flags & CD_FLAG_GDROM)
+ {
+ metadata = util::string_format(GDROM_TRACK_METADATA_FORMAT, i + 1, get_type_string(toc.tracks[i].trktype),
+ get_subtype_string(toc.tracks[i].subtype), toc.tracks[i].frames, toc.tracks[i].padframes,
+ toc.tracks[i].pregap, get_type_string(toc.tracks[i].pgtype),
+ get_subtype_string(toc.tracks[i].pgsub), toc.tracks[i].postgap);
+
+ err = chd->write_metadata(GDROM_TRACK_METADATA_TAG, i, metadata);
+ }
+ else
{
char submode[32];
@@ -1065,15 +1096,6 @@ std::error_condition cdrom_file::write_metadata(chd_file *chd, const toc &toc)
toc.tracks[i].postgap);
err = chd->write_metadata(CDROM_TRACK_METADATA2_TAG, i, metadata);
}
- else
- {
- metadata = util::string_format(GDROM_TRACK_METADATA_FORMAT, i + 1, get_type_string(toc.tracks[i].trktype),
- get_subtype_string(toc.tracks[i].subtype), toc.tracks[i].frames, toc.tracks[i].padframes,
- toc.tracks[i].pregap, get_type_string(toc.tracks[i].pgtype),
- get_subtype_string(toc.tracks[i].pgsub), toc.tracks[i].postgap);
-
- err = chd->write_metadata(GDROM_TRACK_METADATA_TAG, i, metadata);
- }
if (err)
return err;
}
@@ -1833,6 +1855,8 @@ std::error_condition cdrom_file::parse_nero(std::string_view tocfname, toc &outt
memset(&outtoc, 0, sizeof(outtoc));
outinfo.reset();
+ outtoc.numsessions = 1;
+
// seek to 12 bytes before the end
fseek(infile, -12, SEEK_END);
fread(buffer, 12, 1, infile);
@@ -1895,8 +1919,7 @@ std::error_condition cdrom_file::parse_nero(std::string_view tocfname, toc &outt
// printf("Track %d: sector size %d mode %x index0 %llx index1 %llx track_end %llx (pregap %d sectors, length %d sectors)\n", track, size, mode, index0, index1, track_end, (uint32_t)(index1-index0)/size, (uint32_t)(track_end-index1)/size);
outinfo.track[track-1].fname.assign(tocfname);
outinfo.track[track-1].offset = offset + (uint32_t)(index1-index0);
- outinfo.track[track-1].idx0offs = 0;
- outinfo.track[track-1].idx1offs = 0;
+ outinfo.track[track-1].idx[0] = outinfo.track[track-1].idx[1] = 0;
switch (mode)
{
@@ -2016,11 +2039,11 @@ std::error_condition cdrom_file::parse_iso(std::string_view tocfname, toc &outto
outtoc.numtrks = 1;
+ outtoc.numsessions = 1;
outinfo.track[0].fname = tocfname;
outinfo.track[0].offset = 0;
- outinfo.track[0].idx0offs = 0;
- outinfo.track[0].idx1offs = 0;
+ outinfo.track[0].idx[0] = outinfo.track[0].idx[1] = 0;
if ((size % 2048)==0 ) {
outtoc.tracks[0].trktype = CD_TRACK_MODE1;
@@ -2189,6 +2212,7 @@ std::error_condition cdrom_file::parse_gdi(std::string_view tocfname, toc &outto
/* store the number of tracks found */
outtoc.numtrks = numtracks;
+ outtoc.numsessions = 1;
return std::error_condition();
}
@@ -2215,13 +2239,15 @@ std::error_condition cdrom_file::parse_gdi(std::string_view tocfname, toc &outto
std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
{
- int i, trknum;
+ int i, trknum, sessionnum, session_pregap;
char token[512];
std::string lastfname;
uint32_t wavlen, wavoffs;
std::string path = std::string(tocfname);
const bool is_gdrom = is_gdicue(tocfname);
enum gdi_area current_area = SINGLE_DENSITY;
+ bool is_multibin = false;
+ int leadin = -1;
FILE *infile = fopen(path.c_str(), "rt");
if (!infile)
@@ -2237,6 +2263,8 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
trknum = -1;
wavoffs = wavlen = 0;
+ sessionnum = 0;
+ session_pregap = 0;
if (is_gdrom)
{
@@ -2261,13 +2289,65 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
if (!strcmp(token, "REM"))
{
- /* TODO: sessions are notated using REM commands: "REM SESSION 01" */
-
/* skip to actual data of REM command */
while (i < std::size(linebuffer) && isspace((uint8_t)linebuffer[i]))
i++;
- if (is_gdrom && !strncmp(linebuffer+i, "SINGLE-DENSITY AREA", 19))
+ if (!strncmp(linebuffer+i, "SESSION", 7))
+ {
+ /* IsoBuster extension */
+ TOKENIZE
+
+ /* get the session number */
+ TOKENIZE
+
+ sessionnum = strtoul(token, nullptr, 10) - 1;
+
+ if (sessionnum >= 1) /* don't consider it a multisession CD unless there's actually more than 1 session */
+ outtoc.flags |= CD_FLAG_MULTISESSION;
+ }
+ else if ((outtoc.flags & CD_FLAG_MULTISESSION) && !strncmp(linebuffer+i, "PREGAP", 6))
+ {
+ /*
+ Redump extension? PREGAP associated with the session instead of the track
+
+ DiscImageCreator - Older versions would write a bogus value here (and maybe session lead-in and lead-out).
+ These should be considered bad dumps and will not be supported.
+ */
+ TOKENIZE
+
+ /* get pregap time */
+ TOKENIZE
+ session_pregap = msf_to_frames( token );
+ }
+ else if (!strncmp(linebuffer+i, "LEAD-OUT", 8))
+ {
+ /*
+ IsoBuster and ImgBurn (single bin file) - Lead-out time is the start of the lead-out
+ lead-out time - MSF of last track of session = size of last track
+
+ Redump and DiscImageCreator (multiple bins) - Lead-out time is the duration of just the lead-out
+ */
+ TOKENIZE
+
+ /* get lead-out time */
+ TOKENIZE
+ int leadout_offset = msf_to_frames( token );
+ outinfo.track[trknum].leadout = leadout_offset;
+ }
+ else if (!strncmp(linebuffer+i, "LEAD-IN", 7))
+ {
+ /*
+ IsoBuster and ImgBurn (single bin file) - Not used?
+ Redump and DiscImageCreator (multiple bins) - Lead-in time is the duration of just the lead-in
+ */
+ TOKENIZE
+
+ /* get lead-in time */
+ TOKENIZE
+ leadin = msf_to_frames( token );
+ }
+ else if (is_gdrom && !strncmp(linebuffer+i, "SINGLE-DENSITY AREA", 19))
{
/* single-density area starts LBA = 0 */
current_area = SINGLE_DENSITY;
@@ -2284,7 +2364,10 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
TOKENIZE
/* keep the filename */
+ std::string prevfname = lastfname;
lastfname.assign(path).append(token);
+ if (prevfname.length() > 0 && lastfname.compare(prevfname) != 0)
+ is_multibin = true;
/* get the file type */
TOKENIZE
@@ -2323,6 +2406,7 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
/* next token on the line is the track type */
TOKENIZE
+ outtoc.tracks[trknum].session = sessionnum;
outtoc.tracks[trknum].subtype = CD_SUB_NONE;
outtoc.tracks[trknum].subsize = 0;
outtoc.tracks[trknum].pgsub = CD_SUB_NONE;
@@ -2330,9 +2414,26 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
outtoc.tracks[trknum].padframes = 0;
outtoc.tracks[trknum].datasize = 0;
outtoc.tracks[trknum].multicuearea = is_gdrom ? current_area : 0;
- outinfo.track[trknum].idx0offs = -1;
- outinfo.track[trknum].idx1offs = 0;
outinfo.track[trknum].offset = 0;
+ std::fill(std::begin(outinfo.track[trknum].idx), std::end(outinfo.track[trknum].idx), -1);
+
+ outinfo.track[trknum].leadout = -1;
+ outinfo.track[trknum].leadin = leadin; /* use previously saved lead-in value */
+ leadin = -1;
+
+ if (session_pregap != 0)
+ {
+ /*
+ associated the pregap from the session transition with the lead-in to simplify things for now.
+ setting it as the proper pregap for the track causes logframeofs of the last dummy entry in the TOC
+ to become 2s later than it should. this might be an issue with how pgdatasize = 0 pregaps are handled.
+ */
+ if (outinfo.track[trknum].leadin == -1)
+ outinfo.track[trknum].leadin = session_pregap;
+ else
+ outinfo.track[trknum].leadin += session_pregap;
+ session_pregap = 0;
+ }
if (wavlen != 0)
{
@@ -2341,7 +2442,7 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
wavoffs = wavlen = 0;
}
- outinfo.track[trknum].fname.assign(lastfname); // default filename to the last one
+ outinfo.track[trknum].fname.assign(lastfname); /* default filename to the last one */
if (EXTRA_VERBOSE)
{
@@ -2368,7 +2469,7 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
convert_subtype_string_to_track_info(token, &outtoc.tracks[trknum]);
}
- else if (!strcmp(token, "INDEX")) /* only in bin/cue files */
+ else if (!strcmp(token, "INDEX"))
{
int idx, frames;
@@ -2380,22 +2481,25 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
TOKENIZE
frames = msf_to_frames( token );
- if (idx == 0)
+ if (idx < 0 || idx > MAX_INDEX)
{
- outinfo.track[trknum].idx0offs = frames;
+ printf("ERROR: encountered invalid index %d\n", idx);
+ return chd_file::error::INVALID_DATA;
}
- else if (idx == 1)
+
+ outinfo.track[trknum].idx[idx] = frames;
+
+ if (idx == 1)
{
- outinfo.track[trknum].idx1offs = frames;
- if ((outtoc.tracks[trknum].pregap == 0) && (outinfo.track[trknum].idx0offs != -1))
+ if (outtoc.tracks[trknum].pregap == 0 && outinfo.track[trknum].idx[0] != -1)
{
- outtoc.tracks[trknum].pregap = frames - outinfo.track[trknum].idx0offs;
+ outtoc.tracks[trknum].pregap = frames - outinfo.track[trknum].idx[0];
outtoc.tracks[trknum].pgtype = outtoc.tracks[trknum].trktype;
outtoc.tracks[trknum].pgdatasize = outtoc.tracks[trknum].datasize;
}
- else // pregap sectors not in file, but we're always using idx0ofs for track length calc now
+ else if (outinfo.track[trknum].idx[0] == -1) /* pregap sectors not in file, but we're always using idx 0 for track length calc now */
{
- outinfo.track[trknum].idx0offs = frames;
+ outinfo.track[trknum].idx[0] = frames;
}
}
}
@@ -2419,6 +2523,28 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
outtoc.tracks[trknum].postgap = frames;
}
+ else if (!strcmp(token, "FLAGS"))
+ {
+ outtoc.tracks[trknum].control_flags = 0;
+
+ /* keep looping over remaining tokens in FLAGS line until there's no more to read */
+ while (i < std::size(linebuffer))
+ {
+ int last_idx = i;
+
+ TOKENIZE
+
+ if (i == last_idx)
+ break;
+
+ if (!strcmp(token, "DCP"))
+ outtoc.tracks[trknum].control_flags |= CD_FLAG_CONTROL_DIGITAL_COPY_PERMITTED;
+ else if (!strcmp(token, "4CH"))
+ outtoc.tracks[trknum].control_flags |= CD_FLAG_CONTROL_4CH;
+ else if (!strcmp(token, "PRE"))
+ outtoc.tracks[trknum].control_flags |= CD_FLAG_CONTROL_PREEMPHASIS;
+ }
+ }
}
}
@@ -2427,82 +2553,126 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
/* store the number of tracks found */
outtoc.numtrks = trknum + 1;
+ outtoc.numsessions = sessionnum + 1;
/* now go over the files again and set the lengths */
for (trknum = 0; trknum < outtoc.numtrks; trknum++)
{
uint64_t tlen = 0;
- // this is true for cue/bin and cue/iso, and we need it for cue/wav since .WAV is little-endian
+ if (outinfo.track[trknum].idx[1] == -1)
+ {
+ /* index 1 should always be set */
+ printf("ERROR: track %d is missing INDEX 01 marker\n", trknum+1);
+ return chd_file::error::INVALID_DATA;
+ }
+
+ /* this is true for cue/bin and cue/iso, and we need it for cue/wav since .WAV is little-endian */
if (outtoc.tracks[trknum].trktype == CD_TRACK_AUDIO)
{
outinfo.track[trknum].swap = true;
}
- // don't do this for .WAV tracks, we already have their length and offset filled out
- if (outinfo.track[trknum].offset == 0)
+ /* don't do this for .WAV tracks, we already have their length and offset filled out */
+ if (outinfo.track[trknum].offset != 0)
+ continue;
+
+ if (trknum+1 >= outtoc.numtrks && trknum > 0 && (outinfo.track[trknum].fname.compare(outinfo.track[trknum-1].fname)==0))
{
- // is this the last track?
- if (trknum == (outtoc.numtrks-1))
+ /* if the last track's filename is the same as the previous track */
+ tlen = get_file_size(outinfo.track[trknum].fname);
+ if (tlen == 0)
{
- /* if we have the same filename as the last track, do it that way */
- if (trknum != 0 && (outinfo.track[trknum].fname.compare(outinfo.track[trknum-1].fname)==0))
+ printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str());
+ return std::errc::no_such_file_or_directory;
+ }
+
+ outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize);
+ outtoc.tracks[trknum].frames = (tlen - outinfo.track[trknum].offset) / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+ }
+ else if (trknum+1 < outtoc.numtrks && outinfo.track[trknum].fname.compare(outinfo.track[trknum+1].fname)==0)
+ {
+ /* if the current filename is the same as the next track */
+ outtoc.tracks[trknum].frames = outinfo.track[trknum+1].idx[0] - outinfo.track[trknum].idx[0];
+
+ if (outtoc.tracks[trknum].frames == 0)
+ {
+ printf("ERROR: unable to determine size of track %d, missing INDEX 01 markers?\n", trknum+1);
+ return chd_file::error::INVALID_DATA;
+ }
+
+ if (trknum > 0)
+ {
+ const uint32_t previous_track_raw_size = outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize);
+ outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + previous_track_raw_size;
+ }
+ }
+ else if (outtoc.tracks[trknum].frames == 0)
+ {
+ /* if the filenames between tracks are different */
+ tlen = get_file_size(outinfo.track[trknum].fname);
+ if (tlen == 0)
+ {
+ printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str());
+ return std::errc::no_such_file_or_directory;
+ }
+
+ outtoc.tracks[trknum].frames = tlen / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+ outinfo.track[trknum].offset = 0;
+ }
+
+ if (outtoc.flags & CD_FLAG_MULTISESSION)
+ {
+ if (is_multibin)
+ {
+ if (outinfo.track[trknum].leadout == -1 && trknum + 1 < outtoc.numtrks && outtoc.tracks[trknum].session != outtoc.tracks[trknum+1].session)
{
- tlen = get_file_size(outinfo.track[trknum].fname);
- if (tlen == 0)
- {
- printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str());
- return std::errc::no_such_file_or_directory;
- }
- outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize);
- outtoc.tracks[trknum].frames = (tlen - outinfo.track[trknum].offset) / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+ /* add a standard lead-out to the last track before changing sessions */
+ outinfo.track[trknum].leadout = outtoc.tracks[trknum].session == 0 ? 6750 : 2250; /* first session lead-out (1m30s0f) is longer than the rest (0m30s0f) */
}
- else /* data files are different */
+
+ if (outinfo.track[trknum].leadin == -1 && trknum > 0 && outtoc.tracks[trknum].session != outtoc.tracks[trknum-1].session)
{
- tlen = get_file_size(outinfo.track[trknum].fname);
- if (tlen == 0)
- {
- printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str());
- return std::errc::no_such_file_or_directory;
- }
- tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
- outtoc.tracks[trknum].frames = tlen;
- outinfo.track[trknum].offset = 0;
+ /* add a standard lead-in to the first track of a new session */
+ outinfo.track[trknum].leadin = 4500; /* lead-in (1m0s0f) */
}
}
else
{
- /* if we have the same filename as the next track, do it that way */
- if (outinfo.track[trknum].fname.compare(outinfo.track[trknum+1].fname)==0)
+ if (outinfo.track[trknum].leadout != -1)
{
- outtoc.tracks[trknum].frames = outinfo.track[trknum+1].idx0offs - outinfo.track[trknum].idx0offs;
-
- if (trknum == 0) // track 0 offset is 0
+ /*
+ if a lead-out time is specified in a multisession CD then the size of the previous track needs to be trimmed
+ to use the lead-out time instead of the idx 0 of the next track
+ */
+ const int endframes = outinfo.track[trknum].leadout - outinfo.track[trknum].idx[0];
+ if (outtoc.tracks[trknum].frames >= endframes)
{
- outinfo.track[trknum].offset = 0;
- }
- else
- {
- outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize);
- }
+ outtoc.tracks[trknum].frames = endframes; /* trim track length */
- if (!outtoc.tracks[trknum].frames)
- {
- printf("ERROR: unable to determine size of track %d, missing INDEX 01 markers?\n", trknum+1);
- return chd_file::error::INVALID_DATA;
+ if (trknum + 1 < outtoc.numtrks)
+ {
+ /* lead-out value becomes just the duration between the lead-out to the pre-gap of the next track */
+ outinfo.track[trknum].leadout = outinfo.track[trknum+1].idx[0] - outinfo.track[trknum].leadout;
+ }
}
}
- else /* data files are different */
+
+ if (trknum > 0 && outinfo.track[trknum-1].leadout != -1)
{
- tlen = get_file_size(outinfo.track[trknum].fname);
- if (tlen == 0)
+ /*
+ ImgBurn bin/cue have dummy data to pad between the lead-out and the start of the next track.
+ DiscImageCreator img/cue does not have any data between the lead-out and the start of the next track.
+
+ Detecting by extension is an awful way to handle this but there's no other way to determine what format
+ the data will be in since we don't know the exact length of the last track just from the cue.
+ */
+ if (!core_filename_ends_with(outinfo.track[trknum-1].fname, ".img"))
{
- printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum].fname.c_str());
- return std::errc::no_such_file_or_directory;
+ outtoc.tracks[trknum-1].padframes += outinfo.track[trknum-1].leadout;
+ outtoc.tracks[trknum].frames -= outinfo.track[trknum-1].leadout;
+ outinfo.track[trknum].offset += outinfo.track[trknum-1].leadout * (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
}
- tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
- outtoc.tracks[trknum].frames = tlen;
- outinfo.track[trknum].offset = 0;
}
}
}
@@ -2523,7 +2693,7 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
outinfo.track[trknum].offset += this_offset;
outtoc.tracks[trknum].frames -= this_pregap;
- outinfo.track[trknum].idx1offs -= this_pregap;
+ outinfo.track[trknum].idx[1] -= this_pregap;
outtoc.tracks[trknum].pregap = 0;
outtoc.tracks[trknum].pgtype = 0;
@@ -2551,7 +2721,8 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
if (EXTRA_VERBOSE)
for (trknum = 0; trknum < outtoc.numtrks; trknum++)
{
- printf("trk %d: %d frames @ offset %d, pad=%d, split=%d, area=%d, phys=%d, pregap=%d, pgtype=%d, pgdatasize=%d, idx0=%d, idx1=%d, dataframes=%d\n",
+ printf("session %d trk %d: %d frames @ offset %d, pad=%d, split=%d, area=%d, phys=%d, pregap=%d, pgtype=%d, pgdatasize=%d, idx0=%d, idx1=%d, dataframes=%d\n",
+ outtoc.tracks[trknum].session+1,
trknum+1,
outtoc.tracks[trknum].frames,
outinfo.track[trknum].offset,
@@ -2562,8 +2733,8 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
outtoc.tracks[trknum].pregap,
outtoc.tracks[trknum].pgtype,
outtoc.tracks[trknum].pgdatasize,
- outinfo.track[trknum].idx0offs,
- outinfo.track[trknum].idx1offs,
+ outinfo.track[trknum].idx[0],
+ outinfo.track[trknum].idx[1],
outtoc.tracks[trknum].frames - outtoc.tracks[trknum].padframes);
}
@@ -2832,6 +3003,7 @@ std::error_condition cdrom_file::parse_toc(std::string_view tocfname, toc &outto
/* store the number of tracks found */
outtoc.numtrks = trknum + 1;
+ outtoc.numsessions = 1;
return std::error_condition();
}
diff --git a/src/lib/util/cdrom.h b/src/lib/util/cdrom.h
index 9bf80190a7c..dd62badeff1 100644
--- a/src/lib/util/cdrom.h
+++ b/src/lib/util/cdrom.h
@@ -24,6 +24,7 @@ public:
static constexpr uint32_t MAX_TRACKS = 99; /* AFAIK the theoretical limit */
static constexpr uint32_t MAX_SECTOR_DATA = 2352;
static constexpr uint32_t MAX_SUBCODE_DATA = 96;
+ static constexpr uint32_t MAX_INDEX = 99;
static constexpr uint32_t FRAME_SIZE = MAX_SECTOR_DATA + MAX_SUBCODE_DATA;
static constexpr uint32_t FRAMES_PER_HUNK = 8;
@@ -53,25 +54,43 @@ public:
enum
{
- CD_FLAG_GDROM = 0x00000001, // disc is a GD-ROM, all tracks should be stored with GD-ROM metadata
- CD_FLAG_GDROMLE = 0x00000002 // legacy GD-ROM, with little-endian CDDA data
+ CD_FLAG_GDROM = 0x00000001, // disc is a GD-ROM, all tracks should be stored with GD-ROM metadata
+ CD_FLAG_GDROMLE = 0x00000002, // legacy GD-ROM, with little-endian CDDA data
+ CD_FLAG_MULTISESSION = 0x00000004, // multisession CD-ROM
+ };
+
+ enum
+ {
+ CD_FLAG_CONTROL_PREEMPHASIS = 1,
+ CD_FLAG_CONTROL_DIGITAL_COPY_PERMITTED = 2,
+ CD_FLAG_CONTROL_DATA_TRACK = 4,
+ CD_FLAG_CONTROL_4CH = 8,
+ };
+
+ enum
+ {
+ CD_FLAG_ADR_START_TIME = 1,
+ CD_FLAG_ADR_CATALOG_CODE,
+ CD_FLAG_ADR_ISRC_CODE,
};
struct track_info
{
/* fields used by CHDMAN and in MAME */
- uint32_t trktype; /* track type */
- uint32_t subtype; /* subcode data type */
- uint32_t datasize; /* size of data in each sector of this track */
- uint32_t subsize; /* size of subchannel data in each sector of this track */
- uint32_t frames; /* number of frames in this track */
- uint32_t extraframes; /* number of "spillage" frames in this track */
- uint32_t pregap; /* number of pregap frames */
- uint32_t postgap; /* number of postgap frames */
- uint32_t pgtype; /* type of sectors in pregap */
- uint32_t pgsub; /* type of subchannel data in pregap */
- uint32_t pgdatasize; /* size of data in each sector of the pregap */
- uint32_t pgsubsize; /* size of subchannel data in each sector of the pregap */
+ uint32_t trktype; /* track type */
+ uint32_t subtype; /* subcode data type */
+ uint32_t datasize; /* size of data in each sector of this track */
+ uint32_t subsize; /* size of subchannel data in each sector of this track */
+ uint32_t frames; /* number of frames in this track */
+ uint32_t extraframes; /* number of "spillage" frames in this track */
+ uint32_t pregap; /* number of pregap frames */
+ uint32_t postgap; /* number of postgap frames */
+ uint32_t pgtype; /* type of sectors in pregap */
+ uint32_t pgsub; /* type of subchannel data in pregap */
+ uint32_t pgdatasize; /* size of data in each sector of the pregap */
+ uint32_t pgsubsize; /* size of subchannel data in each sector of the pregap */
+ uint32_t control_flags; /* metadata flags associated with each track */
+ uint32_t session; /* session number */
/* fields used in CHDMAN only */
uint32_t padframes; /* number of frames of padding to add to the end of the track; needed for GDI */
@@ -91,6 +110,7 @@ public:
struct toc
{
uint32_t numtrks; /* number of tracks */
+ uint32_t numsessions; /* number of sessions */
uint32_t flags; /* see FLAG_ above */
track_info tracks[MAX_TRACKS + 1];
};
@@ -98,13 +118,13 @@ public:
struct track_input_entry
{
track_input_entry() { reset(); }
- void reset() { fname.clear(); offset = idx0offs = idx1offs = 0; swap = false; }
+ void reset() { fname.clear(); offset = 0; leadin = leadout = -1; swap = false; std::fill(std::begin(idx), std::end(idx), -1); }
std::string fname; // filename for each track
uint32_t offset; // offset in the data file for each track
bool swap; // data needs to be byte swapped
- uint32_t idx0offs;
- uint32_t idx1offs;
+ int32_t idx[MAX_INDEX + 1];
+ int32_t leadin, leadout; // TODO: these should probably be their own tracks entirely
};
struct track_input_info
@@ -128,6 +148,7 @@ public:
uint32_t get_track(uint32_t frame) const;
uint32_t get_track_start(uint32_t track) const {return cdtoc.tracks[track == 0xaa ? cdtoc.numtrks : track].logframeofs; }
uint32_t get_track_start_phys(uint32_t track) const { return cdtoc.tracks[track == 0xaa ? cdtoc.numtrks : track].physframeofs; }
+ uint32_t get_track_index(uint32_t frame) const;
/* TOC utilities */
static std::error_condition parse_nero(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
@@ -136,8 +157,16 @@ public:
static std::error_condition parse_cue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
static bool is_gdicue(std::string_view tocfname);
static std::error_condition parse_toc(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
+ int get_last_session() const { return cdtoc.numsessions ? cdtoc.numsessions : 1; }
int get_last_track() const { return cdtoc.numtrks; }
- int get_adr_control(int track) const { return track == 0xaa || cdtoc.tracks[track].trktype == CD_TRACK_AUDIO ? 0x10 : 0x14; }
+ int get_adr_control(int track) const {
+ if (track == 0xaa)
+ track = get_last_track() - 1; // use last track's flags
+ int adrctl = (CD_FLAG_ADR_START_TIME << 4) | (cdtoc.tracks[track].control_flags & 0x0f);
+ if (cdtoc.tracks[track].trktype != CD_TRACK_AUDIO)
+ adrctl |= CD_FLAG_CONTROL_DATA_TRACK;
+ return adrctl;
+ }
int get_track_type(int track) const { return cdtoc.tracks[track].trktype; }
const toc &get_toc() const { return cdtoc; }