diff --git a/src/devices/imagedev/cdromimg.cpp b/src/devices/imagedev/cdromimg.cpp index b4deb9fc4ad9a..c9c57b75c1fad 100644 --- a/src/devices/imagedev/cdromimg.cpp +++ b/src/devices/imagedev/cdromimg.cpp @@ -228,6 +228,13 @@ uint32_t cdrom_image_device::get_track_start(uint32_t track) const return 0; } +uint32_t cdrom_image_device::get_adr_control_frame(uint32_t frame) const +{ + if (m_cdrom_handle) + return m_cdrom_handle->get_adr_control_frame(frame); + return 0; +} + bool cdrom_image_device::read_data(uint32_t lbasector, void *buffer, uint32_t datatype, bool phys) { if (m_cdrom_handle) @@ -237,10 +244,17 @@ bool cdrom_image_device::read_data(uint32_t lbasector, void *buffer, uint32_t da return 0; } -bool cdrom_image_device::read_subcode(uint32_t lbasector, void *buffer, bool phys) +bool cdrom_image_device::read_subcode(uint32_t lbasector, void *buffer, bool phys, bool uninterlaced) +{ + if (m_cdrom_handle) + return m_cdrom_handle->read_subcode(lbasector, buffer, phys, uninterlaced); + return 0; +} + +bool cdrom_image_device::read_subcode_channel_raw(uint32_t lbasector, void *buffer, uint32_t subchan) { if (m_cdrom_handle) - return m_cdrom_handle->read_subcode(lbasector, buffer, phys); + return m_cdrom_handle->read_subcode_channel_raw(lbasector, buffer, subchan); return 0; } @@ -266,6 +280,20 @@ int cdrom_image_device::get_track_type(int track) const return 0; } +uint32_t cdrom_image_device::get_absolute_msf(uint32_t frame) const +{ + if (m_cdrom_handle) + return m_cdrom_handle->get_absolute_msf(frame); + return 0; +} + +uint32_t cdrom_image_device::get_relative_msf(uint32_t frame) const +{ + if (m_cdrom_handle) + return m_cdrom_handle->get_relative_msf(frame); + return 0; +} + bool cdrom_image_device::is_cd() const { return m_cdrom_handle != nullptr; diff --git a/src/devices/imagedev/cdromimg.h b/src/devices/imagedev/cdromimg.h index 6b423c5ca5a93..22fd4cd6ef38d 100644 --- a/src/devices/imagedev/cdromimg.h +++ b/src/devices/imagedev/cdromimg.h @@ -63,12 +63,17 @@ class cdrom_image_device : public device_t, 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; + uint32_t get_adr_control_frame(uint32_t frame) 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); + bool read_subcode(uint32_t lbasector, void *buffer, bool phys=false, bool uninterlaced=false); + bool read_subcode_channel_raw(uint32_t lbasector, void *buffer, uint32_t subchan); int get_adr_control(int track) const; const cdrom_file::toc &get_toc() const; int get_track_type(int track) const; + uint32_t get_absolute_msf(uint32_t frame) const; + uint32_t get_relative_msf(uint32_t frame) const; + bool is_cd() const; bool is_gd() const; bool is_dvd() const; diff --git a/src/devices/machine/t10mmc.cpp b/src/devices/machine/t10mmc.cpp index eac2c97f6d0cd..f9787799334f5 100644 --- a/src/devices/machine/t10mmc.cpp +++ b/src/devices/machine/t10mmc.cpp @@ -1,10 +1,12 @@ // license:BSD-3-Clause // copyright-holders:smf +#include "coreutil.h" #include "emu.h" #include "t10mmc.h" #include "multibyte.h" + static int32_t to_msf_raw(int32_t frame) { const int m = (frame / (75 * 60)) % 100; @@ -1162,93 +1164,166 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) break; case T10MMC_CMD_READ_SUB_CHANNEL: - switch (command[3]) + { + enum { + FlagTime = 1 << 1, + FlagSubq = 1 << 6, + }; + + const bool msf = (command[1] & FlagTime) != 0; + m_device->logerror("T10MMC: READ SUB-CHANNEL Time = %x, SUBQ = %x, LBA = %d)\n", msf, command[2], m_last_lba); + + if (!m_image) + return; + + const int audio_active = m_cdda->audio_active(); + if (audio_active) { - case 1: // return current position + // if audio is playing, get the latest LBA from the CDROM layer + m_last_lba = m_cdda->get_audio_lba(); + if (m_cdda->audio_paused()) { - if (!m_image) - { - return; - } - - bool msf = (command[1] & 0x2) != 0; + data[1] = 0x12; // audio is paused + } + else + { + data[1] = 0x11; // audio in progress + } + } + else + { + m_last_lba = 0; + if (m_cdda->audio_ended()) + { + data[1] = 0x13; // ended successfully + } + else + { + // data[1] = 0x14; // stopped due to error + data[1] = 0x15; // No current audio status to return + } + } - data[0]= 0x00; + uint16_t datalen = 0; - const int audio_active = m_cdda->audio_active(); - if (audio_active) + if (command[2] & FlagSubq) + { + switch (command[3]) + { + case 1: // CD current position { - // if audio is playing, get the latest LBA from the CDROM layer - m_last_lba = m_cdda->get_audio_lba(); - if (m_cdda->audio_paused()) - { - data[1] = 0x12; // audio is paused - } - else - { - data[1] = 0x11; // audio in progress - } + data[4 + datalen] = 0x01; // sub-channel format code + data[5 + datalen] = m_image->get_adr_control_frame(m_last_lba); + data[6 + datalen] = m_image->get_track(m_last_lba) + 1; + data[7 + datalen] = m_image->get_track_index(m_last_lba); + datalen += 4; + + uint32_t msftime = m_image->get_absolute_msf(m_last_lba); + if (!msf) + msftime = cdrom_file::msf_to_lba(msftime); + + put_u32be(&data[4 + datalen], msftime); + datalen += 4; + + msftime = m_image->get_relative_msf(m_last_lba); + if (!msf) + msftime = cdrom_file::msf_to_lba(msftime); + + put_u32be(&data[4 + datalen], msftime); + datalen += 4; + break; } - else + + case 2: // Media Catalog number (UPC/bar code) { - m_last_lba = 0; - if (m_cdda->audio_ended()) - { - data[1] = 0x13; // ended successfully - } - else + const int start = m_image->get_track_start(m_image->get_track(m_last_lba)); + const int end = m_image->get_track_start(m_image->get_track(m_last_lba) + 1); + + data[4 + datalen] = 0x02; // sub-channel format code + data[5 + datalen] = 0; // reserved + data[6 + datalen] = 0; // reserved + data[7 + datalen] = 0; // reserved + datalen += 4; + + std::fill_n(&data[4 + datalen], 15, 0); + std::fill_n(&data[4 + datalen + 1], 13, '0'); + + for (int lba = start; lba < end; lba++) { -// data[1] = 0x14; // stopped due to error - data[1] = 0x15; // No current audio status to return + uint8_t subbuf[12]; + m_image->read_subcode_channel_raw(lba, subbuf, cdrom_file::SUBCODE_CHAN_Q); + + if ((subbuf[0] & 0xf) == 2) + { + // Found subchannel + data[4 + datalen] |= 0x80; // MCVAL, found media catalog number data + datalen++; + + cdrom_file::mcn2ascii(&subbuf[1], reinterpret_cast(&data[4 + datalen])); + datalen += 13; + + data[4 + datalen] = 0; // zero + data[5 + datalen] = subbuf[9]; // aframe + datalen += 2; + + break; + } } - } - m_device->logerror("T10MMC: READ SUB-CHANNEL Time = %x, SUBQ = %x, LBA = %d)\n", msf, command[2], m_last_lba); + break; + } - if (command[2] & 0x40) + case 3: // Track International standard recording code (ISRC) { - int track = m_image->get_track(m_last_lba); + const int track = m_image->get_track(m_last_lba); + const int adrctrl = m_image->get_adr_control_frame(m_last_lba); + const int start = m_image->get_track_start(track); + const int end = m_image->get_track_start(m_image->get_track(m_last_lba) + 1); - data[2] = 0; - data[3] = 12; // data length - data[4] = 0x01; // sub-channel format code - data[5] = m_image->get_adr_control(track); - data[6] = track + 1; // track - data[7] = m_image->get_track_index(m_last_lba); // index + data[4 + datalen] = 0x03; // sub-channel format code + data[5 + datalen] = adrctrl; + data[6 + datalen] = track + 1; + data[7 + datalen] = 0; // reserved + datalen += 4; - uint32_t frame = m_last_lba; + std::fill_n(&data[4 + datalen], 15, 0); - if (msf) + for (int lba = start; lba < end; lba++) { - frame = to_msf(frame); - } + uint8_t subbuf[12]; + m_image->read_subcode_channel_raw(lba, subbuf, cdrom_file::SUBCODE_CHAN_Q); - put_u32be(&data[8], frame); + if ((subbuf[0] & 0xf) == 3) + { + // Found subchannel + data[4 + datalen] |= 0x80; // TCVAL, found ISRC data + datalen++; - frame = m_last_lba - m_image->get_track_start(data[6] - 1); + cdrom_file::isrc2ascii(&subbuf[1], reinterpret_cast(&data[4 + datalen])); + datalen += 12; - if (msf) - { - // this is relative so don't adjust the LBA when converting to MSF - frame = to_msf_raw(frame); + data[4 + datalen] = 0; // zero + data[5 + datalen] = subbuf[9]; // aframe + data[6 + datalen] = 0; // reserved + datalen += 3; + + break; + } } - put_u32be(&data[12], frame); - } - else - { - data[2] = 0; - data[3] = 0; + break; } - break; - } - default: - m_device->logerror("T10MMC: Unknown subchannel type %d requested\n", command[3]); - std::fill_n(data, dataLength, 0); - break; + default: + m_device->logerror("T10MMC: Unknown subchannel type %d requested\n", command[3]); + break; + } } + + put_u16be(&data[2], datalen); + break; + } case T10MMC_CMD_READ_TOC_PMA_ATIP: /* @@ -1297,18 +1372,13 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) break; } + int32_t tstart = m_image->get_track_start(cdrom_track); + data[dptr++] = 0; - data[dptr++] = m_image->get_adr_control(cdrom_track); + data[dptr++] = m_image->get_adr_control_frame(tstart); data[dptr++] = track; data[dptr++] = 0; - uint32_t tstart = m_image->get_track_start(cdrom_track); - - if (msf) - { - tstart = to_msf(tstart); - } - put_u32be(&data[dptr], tstart); dptr += 4; } @@ -1336,17 +1406,14 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) data[dptr++] = 1; data[dptr++] = last_session; + uint32_t tstart = m_image->get_track_start(first_track_last_session); data[dptr++] = 0; - data[dptr++] = m_image->get_adr_control(first_track_last_session); + data[dptr++] = m_image->get_adr_control_frame(tstart); 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(first_track_last_session); - if (msf) - { tstart = to_msf(tstart); - } put_u32be(&data[dptr], tstart); // Start Address of First Track in Last Session dptr += 4; @@ -1381,7 +1448,7 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) // 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++] = m_image->get_adr_control_frame(m_image->get_track_start(cur_track)); data[dptr++] = 0; data[dptr++] = 0xa0; put_u24be(&data[dptr], 0); @@ -1393,7 +1460,7 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) // 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++] = m_image->get_adr_control_frame(m_image->get_track_start(last_track_in_session)); data[dptr++] = 0; data[dptr++] = 0xa1; put_u24be(&data[dptr], 0); @@ -1409,7 +1476,7 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) 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++] = m_image->get_adr_control_frame(m_image->get_track_start(last_track_in_session)); data[dptr++] = 0; data[dptr++] = 0xa2; put_u24be(&data[dptr], 0); @@ -1421,7 +1488,7 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) 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++] = m_image->get_adr_control_frame(m_image->get_track_start(cur_track)); data[dptr++] = 0; data[dptr++] = cur_track + 1; put_u24be(&data[dptr], 0); @@ -1440,7 +1507,7 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) // 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++] = (m_image->get_adr_control_frame(m_image->get_track_start(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 @@ -1451,7 +1518,7 @@ void t10mmc::ReadData( uint8_t *data, int dataLength ) // 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++] = (m_image->get_adr_control_frame(m_image->get_track_start(0)) & 0x0f) | 0x50; data[dptr++] = 0; data[dptr++] = 0xc0; put_u24be(&data[dptr], 0); diff --git a/src/lib/util/cdrom.cpp b/src/lib/util/cdrom.cpp index 76f3fe551555c..f39426fa9235b 100644 --- a/src/lib/util/cdrom.cpp +++ b/src/lib/util/cdrom.cpp @@ -19,6 +19,7 @@ #include "cdrom.h" #include "corestr.h" +#include "coreutil.h" #include "multibyte.h" #include "osdfile.h" #include "path.h" @@ -53,6 +54,22 @@ INLINE FUNCTIONS ***************************************************************************/ +uint16_t cdrom_file::subchan_crc16(uint8_t *data, size_t len) const +{ + uint8_t lsb = 0; + uint8_t msb = 0; + + for (size_t i = 0; i < len; i++) + { + uint8_t x = data[i] ^ msb; + x = x ^ (x >> 4); + msb = lsb ^ (x >> 3) ^ (x << 4); + lsb = x ^ (x << 5); + } + + return ((msb << 8) | lsb) ^ 0xffff; +} + /*------------------------------------------------- physical_to_chd_lba - find the CHD LBA and the track number @@ -533,38 +550,222 @@ bool cdrom_file::read_data(uint32_t lbasector, void *buffer, uint32_t datatype, -------------------------------------------------*/ /** - * @fn bool read_subcode(uint32_t lbasector, void *buffer, bool phys) + * @fn bool read_subcode(uint32_t lbasector, void *buffer, bool phys, bool uninterlaced) * * @brief Cdrom read subcode. * * @param lbasector The lbasector. * @param [in,out] buffer If non-null, the buffer. * @param phys true to physical. + * @param uninterlaced If true, returns uninterlaced subcode data. * * @return false on failure. */ -bool cdrom_file::read_subcode(uint32_t lbasector, void *buffer, bool phys) +bool cdrom_file::read_subcode(uint32_t lbasector, void *buffer, bool phys, bool uninterlaced, bool force_fake) { // compute CHD sector and tracknumber uint32_t tracknum = 0; uint32_t chdsector; + uint8_t subbuf[MAX_SUBCODE_DATA]; + + if (buffer == nullptr || !chd->check_is_dvd()) + return false; if (phys) - { chdsector = physical_to_chd_lba(lbasector, tracknum); - } else - { chdsector = logical_to_chd_lba(lbasector, tracknum); + + std::fill_n((uint8_t*)buffer, MAX_SUBCODE_DATA, 0); + + if (force_fake || cdtoc.tracks[tracknum].subsize == 0) + { + std::fill_n(subbuf, std::size(subbuf), 0); + + // TODO: test when phys = false + + uint8_t *subchan_p = &subbuf[0]; + uint8_t *subchan_q = &subbuf[12]; + uint8_t *subchan_r = &subbuf[24]; + uint8_t *subchan_s = &subbuf[36]; + uint8_t *subchan_t = &subbuf[48]; + uint8_t *subchan_u = &subbuf[60]; + uint8_t *subchan_v = &subbuf[72]; + uint8_t *subchan_w = &subbuf[84]; + + { + // Generate subchannel Q + const uint32_t track_start = get_track_start(tracknum); + const uint32_t track_offset = lbasector - track_start; + const uint32_t start_offset = get_track_type(0) == CD_TRACK_AUDIO ? 0 : 150; + + const uint32_t amsf = lba_to_msf(lbasector + start_offset); + const uint32_t aframe = util::BIT(amsf, 0, 8); + + subchan_q[0] = (get_adr_control(tracknum) & 0x0f) << 4; + + if (strlen(cdtoc.tracks[tracknum].catalog) > 0 && track_offset > 0 && (track_offset % 16) == 0) + { + // mode 2, catalog + subchan_q[0] |= CD_FLAG_ADR_CATALOG_CODE; + + for (int i = 0; i < strlen(cdtoc.tracks[tracknum].catalog); i++) + { + uint8_t c = cdtoc.tracks[tracknum].catalog[i] >= '0' && cdtoc.tracks[tracknum].catalog[i] <= '9' ? cdtoc.tracks[tracknum].catalog[i] - '0' : 0; + subchan_q[1 + i / 2] |= c << (4 * (1 - (i % 2))); + } + } + else if (strlen(cdtoc.tracks[tracknum].isrc) > 0 && track_offset > 0 && (track_offset % 31) == 0) + { + // mode 3, isrc + subchan_q[0] |= CD_FLAG_ADR_ISRC_CODE; + + // Country, Owner 6 bits packed into 8 bit bytes + constexpr int PACKED_LEN = 5; + int packed = 0, bits = 0, offs = 0; + for (int i = 0; i < PACKED_LEN; i++) + { + const char c = cdtoc.tracks[tracknum].isrc[i]; + packed <<= 6; + + if (isdigit(c)) + packed |= c - '0'; + else if (isalpha(c)) + packed |= toupper(c) - 'A' + 17; + + bits += 6; + + if (bits >= 8 || i + 1 >= PACKED_LEN) + { + const int bitofs = std::max(std::min(bits - 8, bits), 0); + const int bitlen = std::min(8, bits); + const int bitshift = std::max(8 - bits, 0); + subchan_q[1 + offs] = util::BIT(packed, bitofs, bitlen) << bitshift; + bits -= bitlen; + offs++; + } + } + + // Serial + for (int i = 0; i < 7; i++) + { + if (!isdigit(cdtoc.tracks[tracknum].isrc[5 + i])) + continue; + + subchan_q[5 + i / 2] |= cdtoc.tracks[tracknum].isrc[5 + i] - '0'; + subchan_q[5 + i / 2] <<= 4 * (1 - (i % 2)); + } + } + else + { + // mode 1, position + subchan_q[0] |= CD_FLAG_ADR_START_TIME; + + int index = 0; + + for (int i = 0; i < std::size(cdtrack_info.track[tracknum].idx); i++) + { + if (track_offset >= cdtrack_info.track[tracknum].idx[i]) + index = i; + else + break; + } + + if (cdtrack_info.track[tracknum].idx[index] == -1) + index = 1; // valid index not found, default to index 1 + + const uint32_t difflba = lbasector - track_start; + const uint32_t msf = lba_to_msf(difflba); + const uint32_t min = util::BIT(msf, 16, 8); + const uint32_t sec = util::BIT(msf, 8, 8); + const uint32_t frame = util::BIT(msf, 0, 8); + + const uint32_t amin = util::BIT(amsf, 16, 8); + const uint32_t asec = util::BIT(amsf, 8, 8); + + subchan_q[0] = util::bitswap<8>(get_adr_control(tracknum), 3, 2, 1, 0, 7, 6, 5, 4); + subchan_q[1] = dec_2_bcd(tracknum + 1); + subchan_q[2] = dec_2_bcd(index); + subchan_q[3] = min; + subchan_q[4] = sec; + subchan_q[5] = frame; + subchan_q[6] = 0; + subchan_q[7] = amin; + subchan_q[8] = asec; + } + + + subchan_q[9] = aframe; + put_u16be(&subchan_q[10], subchan_crc16(subchan_q, 10)); + } + + uint8_t *outbuf = static_cast(buffer); + std::fill_n(outbuf, MAX_SUBCODE_DATA, 0); + + if (uninterlaced) + { + std::copy_n(subbuf, std::size(subbuf), outbuf); + } + else + { + for (int i = 0; i < MAX_SUBCODE_DATA; i+=8) + { + for (int j = 0; j < 8; j++) + { + outbuf[i+j] |= util::BIT(subchan_p[i / 8], 7 - j) << 7; + outbuf[i+j] |= util::BIT(subchan_q[i / 8], 7 - j) << 6; + outbuf[i+j] |= util::BIT(subchan_r[i / 8], 7 - j) << 5; + outbuf[i+j] |= util::BIT(subchan_s[i / 8], 7 - j) << 4; + outbuf[i+j] |= util::BIT(subchan_t[i / 8], 7 - j) << 3; + outbuf[i+j] |= util::BIT(subchan_u[i / 8], 7 - j) << 2; + outbuf[i+j] |= util::BIT(subchan_v[i / 8], 7 - j) << 1; + outbuf[i+j] |= util::BIT(subchan_w[i / 8], 7 - j); + } + } + } + + return true; } - if (cdtoc.tracks[tracknum].subsize == 0) + // read the data + std::error_condition err = read_partial_sector(subbuf, lbasector, chdsector, tracknum, cdtoc.tracks[tracknum].datasize, cdtoc.tracks[tracknum].subsize, phys); + if (err) return false; - // read the data - std::error_condition err = read_partial_sector(buffer, lbasector, chdsector, tracknum, cdtoc.tracks[tracknum].datasize, cdtoc.tracks[tracknum].subsize, phys); - return !err; + uint8_t *outbuf = static_cast(buffer); + if (uninterlaced && cdtoc.tracks[tracknum].subtype == CD_SUB_RAW) + { + std::fill_n(outbuf, MAX_SUBCODE_DATA, 0); + + uint8_t *subchan_p = &outbuf[0]; + uint8_t *subchan_q = &outbuf[12]; + uint8_t *subchan_r = &outbuf[24]; + uint8_t *subchan_s = &outbuf[36]; + uint8_t *subchan_t = &outbuf[48]; + uint8_t *subchan_u = &outbuf[60]; + uint8_t *subchan_v = &outbuf[72]; + uint8_t *subchan_w = &outbuf[84]; + + for (int i = 0; i < std::size(subbuf); i++) + { + const uint8_t c = subbuf[i]; + subchan_p[i / 8] |= util::BIT(c, 7) << (7 - (i % 8)); + subchan_q[i / 8] |= util::BIT(c, 6) << (7 - (i % 8)); + subchan_r[i / 8] |= util::BIT(c, 5) << (7 - (i % 8)); + subchan_s[i / 8] |= util::BIT(c, 4) << (7 - (i % 8)); + subchan_t[i / 8] |= util::BIT(c, 3) << (7 - (i % 8)); + subchan_u[i / 8] |= util::BIT(c, 2) << (7 - (i % 8)); + subchan_v[i / 8] |= util::BIT(c, 1) << (7 - (i % 8)); + subchan_w[i / 8] |= util::BIT(c, 0) << (7 - (i % 8)); + } + } + else + { + std::copy_n(subbuf, std::size(subbuf), outbuf); + } + + return true; } @@ -578,6 +779,136 @@ bool cdrom_file::read_subcode(uint32_t lbasector, void *buffer, bool phys) for a physical frame number -------------------------------------------------*/ +bool cdrom_file::read_subcode_channel_raw(uint32_t lbasector, void *buffer, uint32_t subchan) +{ + if (buffer == nullptr) + return true; + + uint8_t subbuf[96]; + uint8_t *subchan_p = &subbuf[0]; + uint8_t *subchan_q = &subbuf[12]; + uint8_t *subchan_r = &subbuf[24]; + uint8_t *subchan_s = &subbuf[36]; + uint8_t *subchan_t = &subbuf[48]; + uint8_t *subchan_u = &subbuf[60]; + uint8_t *subchan_v = &subbuf[72]; + uint8_t *subchan_w = &subbuf[84]; + + if (!read_subcode(lbasector, subbuf, false, true)) + return false; + +#if 0 + uint8_t subbuf_fake[96]; + uint8_t *subchan_q_fake = &subbuf_fake[12]; + + if (!read_subcode(lbasector, subbuf_fake, false, true, true)) + return false; + + { + printf("real: "); + for (int i = 0; i < 12; i++) + printf("%02x ", subchan_q[i]); + printf("\n"); + + printf("fake: "); + for (int i = 0; i < 12; i++) + printf("%02x ", subchan_q_fake[i]); + printf("\n"); + + printf("real %08x %d:%d:%d %d:%d:%d crc: %d\n", + lbasector, + bcd_2_dec(subchan_q[7]), bcd_2_dec(subchan_q[8]), bcd_2_dec(subchan_q[9]), + bcd_2_dec(subchan_q[3]), bcd_2_dec(subchan_q[4]), bcd_2_dec(subchan_q[5]), + get_u16be(&subchan_q[10]) == subchan_crc16(subchan_q, 10) + ); + + printf("fake %08x %d:%d:%d %d:%d:%d crc: %d\n", + lbasector, + bcd_2_dec(subchan_q_fake[7]), bcd_2_dec(subchan_q_fake[8]), bcd_2_dec(subchan_q_fake[9]), + bcd_2_dec(subchan_q_fake[3]), bcd_2_dec(subchan_q_fake[4]), bcd_2_dec(subchan_q_fake[5]), + get_u16be(&subchan_q_fake[10]) == subchan_crc16(subchan_q_fake, 10) + ); + + printf("\n"); + } +#endif + + uint8_t *outbuf = static_cast(buffer); + switch (subchan) + { + case SUBCODE_CHAN_P: + std::copy_n(subchan_p, 12, outbuf); + break; + case SUBCODE_CHAN_Q: + std::copy_n(subchan_q, 12, outbuf); + break; + case SUBCODE_CHAN_R: + std::copy_n(subchan_r, 12, outbuf); + break; + case SUBCODE_CHAN_S: + std::copy_n(subchan_s, 12, outbuf); + break; + case SUBCODE_CHAN_T: + std::copy_n(subchan_t, 12, outbuf); + break; + case SUBCODE_CHAN_U: + std::copy_n(subchan_u, 12, outbuf); + break; + case SUBCODE_CHAN_V: + std::copy_n(subchan_v, 12, outbuf); + break; + case SUBCODE_CHAN_W: + std::copy_n(subchan_w, 12, outbuf); + break; + } + + return true; +} + +uint32_t cdrom_file::get_adr_control_frame(uint32_t frame) +{ + uint8_t subbuf[12]; + if (read_subcode_channel_raw(frame, subbuf, SUBCODE_CHAN_Q)) + return util::bitswap<8>(subbuf[0], 3, 2, 1, 0, 7, 6, 5, 4); + + uint32_t track = 0; + + /* convert to a CHD sector offset and get track information */ + logical_to_chd_lba(frame, track); + + return get_adr_control(track); +} + +uint32_t cdrom_file::get_absolute_msf(uint32_t frame) +{ + uint8_t subbuf[12]; + if (read_subcode_channel_raw(frame, subbuf, SUBCODE_CHAN_Q)) + { + const uint32_t m = bcd_2_dec(subbuf[7]); + const uint32_t s = bcd_2_dec(subbuf[8]); + const uint32_t f = bcd_2_dec(subbuf[9]); + return (m << 16) | (s << 8) | f; + } + + return lba_to_msf(frame); +} + +uint32_t cdrom_file::get_relative_msf(uint32_t frame) +{ + uint8_t subbuf[12]; + if (read_subcode_channel_raw(frame, subbuf, SUBCODE_CHAN_Q)) + { + const uint32_t m = bcd_2_dec(subbuf[3]); + const uint32_t s = bcd_2_dec(subbuf[4]); + const uint32_t f = bcd_2_dec(subbuf[5]); + return (m << 16) | (s << 8) | f; + } + + const uint32_t track = get_track(frame); + const uint32_t track_start = get_track_start(track); + return lba_to_msf(frame - track_start); +} + /** * @fn uint32_t get_track(uint32_t frame) * @@ -588,8 +919,12 @@ bool cdrom_file::read_subcode(uint32_t lbasector, void *buffer, bool phys) * @return An uint32_t. */ -uint32_t cdrom_file::get_track(uint32_t frame) const +uint32_t cdrom_file::get_track(uint32_t frame) { + uint8_t subbuf[12]; + if (read_subcode_channel_raw(frame, subbuf, SUBCODE_CHAN_Q)) + return bcd_2_dec(subbuf[1]) - 1; + uint32_t track = 0; /* convert to a CHD sector offset and get track information */ @@ -598,8 +933,12 @@ uint32_t cdrom_file::get_track(uint32_t frame) const return track; } -uint32_t cdrom_file::get_track_index(uint32_t frame) const +uint32_t cdrom_file::get_track_index(uint32_t frame) { + uint8_t subbuf[12]; + if (read_subcode_channel_raw(frame, subbuf, SUBCODE_CHAN_Q)) + return bcd_2_dec(subbuf[2]); + const uint32_t track = get_track(frame); const uint32_t track_start = get_track_start(track); const uint32_t index_offset = frame - track_start; diff --git a/src/lib/util/cdrom.h b/src/lib/util/cdrom.h index b11ecb5b3ba95..f95618bed0626 100644 --- a/src/lib/util/cdrom.h +++ b/src/lib/util/cdrom.h @@ -80,6 +80,17 @@ class cdrom_file { CD_FLAG_ADR_ISRC_CODE, }; + enum { + SUBCODE_CHAN_P = 0, + SUBCODE_CHAN_Q, + SUBCODE_CHAN_R, + SUBCODE_CHAN_S, + SUBCODE_CHAN_T, + SUBCODE_CHAN_U, + SUBCODE_CHAN_V, + SUBCODE_CHAN_W, + }; + struct track_info { /* fields used by CHDMAN and in MAME */ @@ -110,6 +121,9 @@ class cdrom_file { /* fields used in multi-cue GDI */ uint32_t multicuearea; + + char catalog[14]; + char isrc[13]; }; @@ -148,13 +162,18 @@ class cdrom_file { /* core read access */ 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); + bool read_subcode(uint32_t lbasector, void *buffer, bool phys=false, bool uninterlaced=false, bool force_fake=false); + bool read_subcode_channel_raw(uint32_t lbasector, void *buffer, uint32_t subchan); /* handy utilities */ - uint32_t get_track(uint32_t frame) const; + uint32_t get_track(uint32_t frame); 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; + uint32_t get_track_index(uint32_t frame); + uint32_t get_adr_control_frame(uint32_t frame); + + uint32_t get_absolute_msf(uint32_t frame); + uint32_t get_relative_msf(uint32_t frame); /* TOC utilities */ static std::error_condition parse_nero(std::string_view tocfname, toc &outtoc, track_input_info &outinfo); @@ -228,6 +247,40 @@ class cdrom_file { return ret; } + static inline void mcn2ascii(uint8_t *input, char *output) + { + if (input == nullptr || output == nullptr) + return; + + for (int i = 0; i < 13; i++) + output[i] = '0' + ((input[i/2] >> (4 * (1 - (i % 2)))) & 0xf); + } + + static inline void isrc2ascii(uint8_t *input, char *output) + { + if (input == NULL || output == NULL) + return; + + const int parts[] = { + input[0] >> 2, + ((input[0] & 0x03) << 4) | (input[1] >> 4), + ((input[1] & 0x0f) << 2) | (input[2] >> 6), + input[2] & 0x3f, + input[3] >> 2 + }; + + for (int i = 0; i < std::size(parts); i++) + { + if (parts[i] < 10) + output[i] = parts[i] + '0'; + else + output[i] = parts[i] - 17 + 'A'; + } + + for (int i = 0; i < 7; i++) + output[5 + i] = '0' + ((input[4 + i/2] >> (4 * (1 - (i % 2)))) & 0xf); + } + private: enum gdi_area { SINGLE_DENSITY, @@ -279,6 +332,7 @@ class cdrom_file { /** @brief The fhandle[ CD maximum tracks]. */ util::random_read::ptr fhandle[MAX_TRACKS];/* file handle */ + inline uint16_t subchan_crc16(uint8_t *data, size_t len) const; inline uint32_t physical_to_chd_lba(uint32_t physlba, uint32_t &tracknum) const; inline uint32_t logical_to_chd_lba(uint32_t physlba, uint32_t &tracknum) const;