summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util/cdrom.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/cdrom.cpp')
-rw-r--r--src/lib/util/cdrom.cpp2791
1 files changed, 2132 insertions, 659 deletions
diff --git a/src/lib/util/cdrom.cpp b/src/lib/util/cdrom.cpp
index c2cded9f9aa..ab227dce1eb 100644
--- a/src/lib/util/cdrom.cpp
+++ b/src/lib/util/cdrom.cpp
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles,R. Belmont
/***************************************************************************
cdrom.c
@@ -16,12 +16,17 @@
***************************************************************************/
-#include <assert.h>
-
#include "cdrom.h"
-#include <stdlib.h>
-#include "chdcd.h"
+#include "corestr.h"
+#include "multibyte.h"
+#include "osdfile.h"
+#include "path.h"
+#include "strformat.h"
+
+#include <cassert>
+#include <cstdlib>
+#include <tuple>
/***************************************************************************
@@ -30,7 +35,7 @@
/** @brief The verbose. */
#define VERBOSE (0)
-#if VERBOSE
+#define EXTRA_VERBOSE (0)
/**
* @def LOG(x) do
@@ -40,83 +45,7 @@
* @param x The void to process.
*/
-#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
-
-/**
- * @fn void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
- *
- * @brief Logerrors the given text.
- *
- * @param text The text.
- *
- * @return A CLIB_DECL.
- */
-
-void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
-#else
-
-/**
- * @def LOG(x);
- *
- * @brief A macro that defines log.
- *
- * @param x The void to process.
- */
-
-#define LOG(x)
-#endif
-
-
-
-/***************************************************************************
- CONSTANTS
-***************************************************************************/
-
-/** @brief offset within sector. */
-const int SYNC_OFFSET = 0x000;
-/** @brief 12 bytes. */
-const int SYNC_NUM_BYTES = 12;
-
-/** @brief offset within sector. */
-const int MODE_OFFSET = 0x00f;
-
-/** @brief offset within sector. */
-const int ECC_P_OFFSET = 0x81c;
-/** @brief 2 lots of 86. */
-const int ECC_P_NUM_BYTES = 86;
-/** @brief 24 bytes each. */
-const int ECC_P_COMP = 24;
-
-/** @brief The ECC q offset. */
-const int ECC_Q_OFFSET = ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES;
-/** @brief 2 lots of 52. */
-const int ECC_Q_NUM_BYTES = 52;
-/** @brief 43 bytes each. */
-const int ECC_Q_COMP = 43;
-
-
-
-/***************************************************************************
- TYPE DEFINITIONS
-***************************************************************************/
-
-/**
- * @struct cdrom_file
- *
- * @brief A cdrom file.
- */
-
-struct cdrom_file
-{
- /** @brief The chd. */
- chd_file * chd; /* CHD file */
- /** @brief The cdtoc. */
- cdrom_toc cdtoc; /* TOC for the CD */
- /** @brief Information describing the track. */
- chdcd_track_input_info track_info; /* track info */
- /** @brief The fhandle[ CD maximum tracks]. */
- util::core_file::ptr fhandle[CD_MAX_TRACKS];/* file handle */
-};
+#define LOG(x) do { if (VERBOSE) { osd_printf_info x; } } while (0)
@@ -130,27 +59,23 @@ struct cdrom_file
-------------------------------------------------*/
/**
- * @fn static inline uint32_t physical_to_chd_lba(cdrom_file *file, uint32_t physlba, uint32_t &tracknum)
+ * @fn static inline uint32_t physical_to_chd_lba(uint32_t physlba, uint32_t &tracknum)
*
* @brief Physical to chd lba.
*
- * @param [in,out] file If non-null, the file.
* @param physlba The physlba.
* @param [in,out] tracknum The tracknum.
*
* @return An uint32_t.
*/
-static inline uint32_t physical_to_chd_lba(cdrom_file *file, uint32_t physlba, uint32_t &tracknum)
+uint32_t cdrom_file::physical_to_chd_lba(uint32_t physlba, uint32_t &tracknum) const
{
- uint32_t chdlba;
- int track;
-
- /* loop until our current LBA is less than the start LBA of the next track */
- for (track = 0; track < file->cdtoc.numtrks; track++)
- if (physlba < file->cdtoc.tracks[track + 1].physframeofs)
+ // loop until our current LBA is less than the start LBA of the next track
+ for (int track = 0; track < cdtoc.numtrks; track++)
+ if (physlba < cdtoc.tracks[track + 1].physframeofs)
{
- chdlba = physlba - file->cdtoc.tracks[track].physframeofs + file->cdtoc.tracks[track].chdframeofs;
+ uint32_t chdlba = physlba - cdtoc.tracks[track].physframeofs + cdtoc.tracks[track].chdframeofs;
tracknum = track;
return chdlba;
}
@@ -164,36 +89,26 @@ static inline uint32_t physical_to_chd_lba(cdrom_file *file, uint32_t physlba, u
-------------------------------------------------*/
/**
- * @fn static inline uint32_t logical_to_chd_lba(cdrom_file *file, uint32_t loglba, uint32_t &tracknum)
+ * @fn uint32_t logical_to_chd_lba(uint32_t loglba, uint32_t &tracknum)
*
* @brief Logical to chd lba.
*
- * @param [in,out] file If non-null, the file.
* @param loglba The loglba.
* @param [in,out] tracknum The tracknum.
*
* @return An uint32_t.
*/
-static inline uint32_t logical_to_chd_lba(cdrom_file *file, uint32_t loglba, uint32_t &tracknum)
+uint32_t cdrom_file::logical_to_chd_lba(uint32_t loglba, uint32_t &tracknum) const
{
- uint32_t chdlba, physlba;
- int track;
-
- /* loop until our current LBA is less than the start LBA of the next track */
- for (track = 0; track < file->cdtoc.numtrks; track++)
+ // loop until our current LBA is less than the start LBA of the next track
+ for (int track = 0; track < cdtoc.numtrks; track++)
{
- if (loglba < file->cdtoc.tracks[track + 1].logframeofs)
+ if (loglba < cdtoc.tracks[track + 1].logframeofs)
{
- // is this a no-pregap-data track? compensate for the logical offset pointing to the "wrong" sector.
- if ((file->cdtoc.tracks[track].pgdatasize == 0) && (loglba > file->cdtoc.tracks[track].pregap))
- {
- loglba -= file->cdtoc.tracks[track].pregap;
- }
-
// convert to physical and proceed
- physlba = file->cdtoc.tracks[track].physframeofs + (loglba - file->cdtoc.tracks[track].logframeofs);
- chdlba = physlba - file->cdtoc.tracks[track].physframeofs + file->cdtoc.tracks[track].chdframeofs;
+ uint32_t physlba = cdtoc.tracks[track].physframeofs + (loglba - cdtoc.tracks[track].logframeofs);
+ uint32_t chdlba = physlba - cdtoc.tracks[track].physframeofs + cdtoc.tracks[track].chdframeofs;
tracknum = track;
return chdlba;
}
@@ -208,98 +123,114 @@ static inline uint32_t logical_to_chd_lba(cdrom_file *file, uint32_t loglba, uin
***************************************************************************/
/**
- * @fn cdrom_file *cdrom_open(const char *inputfile)
+ * @fn constructor
*
- * @brief Queries if a given cdrom open.
+ * @brief Open a cdrom for a file.
*
* @param inputfile The inputfile.
- *
- * @return null if it fails, else a cdrom_file*.
*/
-cdrom_file *cdrom_open(const char *inputfile)
+cdrom_file::cdrom_file(std::string_view inputfile)
{
- int i;
- cdrom_file *file;
- uint32_t physofs, logofs;
-
- /* allocate memory for the CD-ROM file */
- file = new (std::nothrow) cdrom_file();
- if (file == nullptr)
- return nullptr;
-
- /* setup the CDROM module and get the disc info */
- chd_error err = chdcd_parse_toc(inputfile, file->cdtoc, file->track_info);
- if (err != CHDERR_NONE)
+ // set up the CD-ROM module and get the disc info
+ std::error_condition err = parse_toc(inputfile, cdtoc, cdtrack_info);
+ if (err)
{
- fprintf(stderr, "Error reading input file: %s\n", chd_file::error_string(err));
- delete file;
- return nullptr;
+ osd_printf_error("Error reading input file: %s\n", err.message());
+ throw nullptr;
}
- /* fill in the data */
- file->chd = nullptr;
+ // fill in the data
+ chd = nullptr;
- LOG(("CD has %d tracks\n", file->cdtoc.numtrks));
+ LOG(("CD has %d tracks\n", cdtoc.numtrks));
- for (i = 0; i < file->cdtoc.numtrks; i++)
+ for (int i = 0; i < cdtoc.numtrks; i++)
{
- osd_file::error filerr = util::core_file::open(file->track_info.track[i].fname, OPEN_FLAG_READ, file->fhandle[i]);
- if (filerr != osd_file::error::NONE)
+ osd_file::ptr file;
+ std::uint64_t length;
+ std::error_condition const filerr = osd_file::open(cdtrack_info.track[i].fname, OPEN_FLAG_READ, file, length);
+ if (filerr)
{
- fprintf(stderr, "Unable to open file: %s\n", file->track_info.track[i].fname.c_str());
- cdrom_close(file);
- return nullptr;
+ osd_printf_error("Unable to open file: %s\n", cdtrack_info.track[i].fname);
+ throw nullptr;
+ }
+ fhandle[i] = util::osd_file_read(std::move(file));
+ if (!fhandle[i])
+ {
+ osd_printf_error("Unable to open file: %s\n", cdtrack_info.track[i].fname);
+ throw nullptr;
}
}
+
/* calculate the starting frame for each track, keeping in mind that CHDMAN
pads tracks out with extra frames to fit 4-frame size boundries
*/
- physofs = logofs = 0;
- for (i = 0; i < file->cdtoc.numtrks; i++)
+ uint32_t physofs = 0, logofs = 0;
+ for (int i = 0; i < cdtoc.numtrks; i++)
{
- file->cdtoc.tracks[i].physframeofs = physofs;
- file->cdtoc.tracks[i].chdframeofs = 0;
- file->cdtoc.tracks[i].logframeofs = logofs;
+ track_info &track = cdtoc.tracks[i];
+ track.logframeofs = 0;
- // if the pregap sectors aren't in the track, add them to the track's logical length
- if (file->cdtoc.tracks[i].pgdatasize == 0)
+ if (track.pgdatasize == 0)
+ {
+ logofs += track.pregap;
+ }
+ else
{
- logofs += file->cdtoc.tracks[i].pregap;
+ 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;
+ track.logframes = track.frames - track.pregap;
+
// postgap adds to the track length
- logofs += file->cdtoc.tracks[i].postgap;
-
- physofs += file->cdtoc.tracks[i].frames;
- logofs += file->cdtoc.tracks[i].frames;
-
-/* 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\n", i+1,
- file->cdtoc.tracks[i].trktype,
- file->cdtoc.tracks[i].subtype,
- file->cdtoc.tracks[i].datasize,
- file->cdtoc.tracks[i].subsize,
- file->cdtoc.tracks[i].frames,
- file->cdtoc.tracks[i].extraframes,
- file->cdtoc.tracks[i].pregap,
- file->cdtoc.tracks[i].pgtype,
- file->cdtoc.tracks[i].pgdatasize,
- file->cdtoc.tracks[i].postgap,
- file->cdtoc.tracks[i].logframeofs,
- file->cdtoc.tracks[i].physframeofs,
- file->cdtoc.tracks[i].chdframeofs);*/
- }
-
- /* fill out dummy entries for the last track to help our search */
- file->cdtoc.tracks[i].physframeofs = physofs;
- file->cdtoc.tracks[i].logframeofs = logofs;
- file->cdtoc.tracks[i].chdframeofs = 0;
-
- return file;
+ logofs += track.postgap;
+
+ 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)
+ {
+ osd_printf_verbose("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,
+ track.subsize,
+ track.frames,
+ track.extraframes,
+ track.pregap,
+ track.pgtype,
+ track.pgdatasize,
+ track.postgap,
+ track.logframeofs,
+ track.physframeofs,
+ track.chdframeofs,
+ track.logframes,
+ track.padframes);
+ }
+ }
+
+ // fill out dummy entries for the last track to help our search
+ track_info &track = cdtoc.tracks[cdtoc.numtrks];
+ track.physframeofs = physofs;
+ track.logframeofs = logofs;
+ track.chdframeofs = 0;
+ track.logframes = 0;
}
/*-------------------------------------------------
- cdrom_open - "open" a CD-ROM file from an
+ constructor - "open" a CD-ROM file from an
already-opened CHD file
-------------------------------------------------*/
@@ -313,116 +244,107 @@ cdrom_file *cdrom_open(const char *inputfile)
* @return null if it fails, else a cdrom_file*.
*/
-cdrom_file *cdrom_open(chd_file *chd)
+cdrom_file::cdrom_file(chd_file *_chd)
{
- int i;
- cdrom_file *file;
- uint32_t physofs, chdofs, logofs;
- chd_error err;
-
- /* punt if no CHD */
- if (!chd)
- return nullptr;
+ chd = _chd;
/* validate the CHD information */
- if (chd->hunk_bytes() % CD_FRAME_SIZE != 0)
- return nullptr;
- if (chd->unit_bytes() != CD_FRAME_SIZE)
- return nullptr;
-
- /* allocate memory for the CD-ROM file */
- file = new (std::nothrow) cdrom_file();
- if (file == nullptr)
- return nullptr;
-
- /* fill in the data */
- file->chd = chd;
+ if (chd->hunk_bytes() % FRAME_SIZE != 0)
+ throw nullptr;
+ if (chd->unit_bytes() != FRAME_SIZE)
+ throw nullptr;
/* read the CD-ROM metadata */
- err = cdrom_parse_metadata(chd, &file->cdtoc);
- if (err != CHDERR_NONE)
- {
- delete file;
- return nullptr;
- }
+ std::error_condition err = parse_metadata(chd, cdtoc);
+ if (err)
+ throw nullptr;
- LOG(("CD has %d tracks\n", file->cdtoc.numtrks));
+ LOG(("CD has %d tracks\n", cdtoc.numtrks));
/* calculate the starting frame for each track, keeping in mind that CHDMAN
pads tracks out with extra frames to fit 4-frame size boundries
*/
- physofs = chdofs = logofs = 0;
- for (i = 0; i < file->cdtoc.numtrks; i++)
+ uint32_t physofs = 0, chdofs = 0, logofs = 0;
+ for (int i = 0; i < cdtoc.numtrks; i++)
{
- file->cdtoc.tracks[i].physframeofs = physofs;
- file->cdtoc.tracks[i].chdframeofs = chdofs;
- file->cdtoc.tracks[i].logframeofs = logofs;
+ track_info &track = cdtoc.tracks[i];
+ track.logframeofs = 0;
- // if the pregap sectors aren't in the track, add them to the track's logical length
- if (file->cdtoc.tracks[i].pgdatasize == 0)
+ if (track.pgdatasize == 0)
+ {
+ // Anything that isn't cue.
+ // toc (cdrdao): Pregap data seems to be included at the end of previous track.
+ // START/PREGAP is only issued in special cases, for instance alongside ZERO commands.
+ // ZERO and SILENCE commands are supposed to generate additional data that's not included
+ // in the image directly, so the total logofs value must be offset to point to index 1.
+ logofs += track.pregap;
+ }
+ else
{
- logofs += file->cdtoc.tracks[i].pregap;
+ // cues: Pregap is the difference between index 0 and index 1 unless PREGAP is specified.
+ // The data is assumed to be in the bin and not generated separately, so the pregap should
+ // only be added to the current track's lba to offset it to index 1.
+ track.logframeofs = track.pregap;
}
+ track.physframeofs = physofs;
+ track.chdframeofs = chdofs;
+ track.logframeofs += logofs;
+ track.logframes = track.frames - track.pregap;
+
// postgap counts against the next track
- logofs += file->cdtoc.tracks[i].postgap;
-
- physofs += file->cdtoc.tracks[i].frames;
- chdofs += file->cdtoc.tracks[i].frames;
- chdofs += file->cdtoc.tracks[i].extraframes;
- logofs += file->cdtoc.tracks[i].frames;
-/*
- 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\n", i+1,
- file->cdtoc.tracks[i].trktype,
- file->cdtoc.tracks[i].subtype,
- file->cdtoc.tracks[i].datasize,
- file->cdtoc.tracks[i].subsize,
- file->cdtoc.tracks[i].frames,
- file->cdtoc.tracks[i].extraframes,
- file->cdtoc.tracks[i].pregap,
- file->cdtoc.tracks[i].pgtype,
- file->cdtoc.tracks[i].pgdatasize,
- file->cdtoc.tracks[i].postgap,
- file->cdtoc.tracks[i].logframeofs,
- file->cdtoc.tracks[i].physframeofs,
- file->cdtoc.tracks[i].chdframeofs);*/
- }
-
- /* fill out dummy entries for the last track to help our search */
- file->cdtoc.tracks[i].physframeofs = physofs;
- file->cdtoc.tracks[i].logframeofs = logofs;
- file->cdtoc.tracks[i].chdframeofs = chdofs;
-
- return file;
+ logofs += track.postgap;
+
+ physofs += track.frames;
+ chdofs += track.frames;
+ chdofs += track.extraframes;
+ logofs += track.frames;
+
+ if (EXTRA_VERBOSE)
+ {
+ osd_printf_verbose("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,
+ track.subsize,
+ track.frames,
+ track.extraframes,
+ track.pregap,
+ track.pgtype,
+ track.pgdatasize,
+ track.postgap,
+ track.logframeofs,
+ track.physframeofs,
+ track.chdframeofs,
+ track.logframes,
+ track.padframes);
+ }
+ }
+
+ // fill out dummy entries for the last track to help our search
+ track_info &track = cdtoc.tracks[cdtoc.numtrks];
+ track.physframeofs = physofs;
+ track.logframeofs = logofs;
+ track.chdframeofs = chdofs;
+ track.logframes = 0;
}
/*-------------------------------------------------
- cdrom_close - "close" a CD-ROM file
+ destructor - "close" a CD-ROM file
-------------------------------------------------*/
-/**
- * @fn void cdrom_close(cdrom_file *file)
- *
- * @brief Cdrom close.
- *
- * @param [in,out] file If non-null, the file.
- */
-
-void cdrom_close(cdrom_file *file)
+cdrom_file::~cdrom_file()
{
- if (file == nullptr)
- return;
-
- if (file->chd == nullptr)
+ if (chd == nullptr)
{
- for (int i = 0; i < file->cdtoc.numtrks; i++)
+ for (int i = 0; i < cdtoc.numtrks; i++)
{
- file->fhandle[i].reset();
+ fhandle[i].reset();
}
}
-
- delete file;
}
@@ -432,71 +354,86 @@ void cdrom_close(cdrom_file *file)
***************************************************************************/
/**
- * @fn chd_error read_partial_sector(cdrom_file *file, void *dest, uint32_t lbasector, uint32_t chdsector, uint32_t tracknum, uint32_t startoffs, uint32_t length)
+ * @fn std::error_condition read_partial_sector(void *dest, uint32_t lbasector, uint32_t chdsector, uint32_t tracknum, uint32_t startoffs, uint32_t length, bool phys)
*
* @brief Reads partial sector.
*
- * @param [in,out] file If non-null, the file.
* @param [in,out] dest If non-null, destination for the.
* @param lbasector The lbasector.
* @param chdsector The chdsector.
* @param tracknum The tracknum.
* @param startoffs The startoffs.
* @param length The length.
+ * @param phys true to physical.
*
* @return The partial sector.
*/
-chd_error read_partial_sector(cdrom_file *file, void *dest, uint32_t lbasector, uint32_t chdsector, uint32_t tracknum, uint32_t startoffs, uint32_t length, bool phys=false)
+std::error_condition cdrom_file::read_partial_sector(void *dest, uint32_t lbasector, uint32_t chdsector, uint32_t tracknum, uint32_t startoffs, uint32_t length, bool phys)
{
- chd_error result = CHDERR_NONE;
+ std::error_condition result;
bool needswap = false;
// if this is pregap info that isn't actually in the file, just return blank data
if (!phys)
{
- if ((file->cdtoc.tracks[tracknum].pgdatasize == 0) && (lbasector < (file->cdtoc.tracks[tracknum].logframeofs + file->cdtoc.tracks[tracknum].pregap)))
+ if ((cdtoc.tracks[tracknum].pgdatasize == 0) && (lbasector < cdtoc.tracks[tracknum].logframeofs))
{
- //printf("PG missing sector: LBA %d, trklog %d\n", lbasector, file->cdtoc.tracks[tracknum].logframeofs);
+ if (EXTRA_VERBOSE)
+ osd_printf_verbose("PG missing sector: LBA %d, trklog %d\n", lbasector, cdtoc.tracks[tracknum].logframeofs);
memset(dest, 0, length);
return result;
}
}
// if a CHD, just read
- if (file->chd != nullptr)
+ if (chd != nullptr)
{
- result = file->chd->read_bytes(uint64_t(chdsector) * uint64_t(CD_FRAME_SIZE) + startoffs, dest, length);
- /* swap CDDA in the case of LE GDROMs */
- if ((file->cdtoc.flags & CD_FLAG_GDROMLE) && (file->cdtoc.tracks[tracknum].trktype == CD_TRACK_AUDIO))
+ if (!phys && cdtoc.tracks[tracknum].pgdatasize != 0)
+ {
+ // chdman (phys=true) relies on chdframeofs to point to index 0 instead of index 1 for extractcd.
+ // Actually playing CDs requires it to point to index 1 instead of index 0, so adjust the offset when phys=false.
+ chdsector += cdtoc.tracks[tracknum].pregap;
+ }
+
+ result = chd->read_bytes(uint64_t(chdsector) * uint64_t(FRAME_SIZE) + startoffs, dest, length);
+
+ // swap CDDA in the case of LE GDROMs
+ if ((cdtoc.flags & CD_FLAG_GDROMLE) && (cdtoc.tracks[tracknum].trktype == CD_TRACK_AUDIO))
needswap = true;
}
else
{
// else read from the appropriate file
- util::core_file &srcfile = *file->fhandle[tracknum];
+ util::random_read &srcfile = *fhandle[tracknum];
- uint64_t sourcefileoffset = file->track_info.track[tracknum].offset;
- int bytespersector = file->cdtoc.tracks[tracknum].datasize + file->cdtoc.tracks[tracknum].subsize;
+ int bytespersector = cdtoc.tracks[tracknum].datasize + cdtoc.tracks[tracknum].subsize;
+ uint64_t sourcefileoffset = cdtrack_info.track[tracknum].offset;
+
+ if (cdtoc.tracks[tracknum].pgdatasize != 0)
+ chdsector += cdtoc.tracks[tracknum].pregap;
sourcefileoffset += chdsector * bytespersector + startoffs;
- // printf("Reading sector %d from track %d at offset %lld\n", chdsector, tracknum, sourcefileoffset);
+ if (EXTRA_VERBOSE)
+ osd_printf_verbose("Reading %u bytes from sector %d from track %d at offset %lu\n", (unsigned)length, chdsector, tracknum + 1, (unsigned long)sourcefileoffset);
- srcfile.seek(sourcefileoffset, SEEK_SET);
- srcfile.read(dest, length);
+ result = srcfile.seek(sourcefileoffset, SEEK_SET);
+ size_t actual;
+ if (!result)
+ std::tie(result, actual) = read(srcfile, dest, length);
+ // FIXME: if (!result && (actual < length)) report error
- needswap = file->track_info.track[tracknum].swap;
+ needswap = cdtrack_info.track[tracknum].swap;
}
if (needswap)
{
uint8_t *buffer = (uint8_t *)dest - startoffs;
- for (int swapindex = startoffs; swapindex < 2352; swapindex += 2 )
+ for (int swapindex = startoffs; swapindex < 2352; swapindex += 2)
{
- int swaptemp = buffer[ swapindex ];
- buffer[ swapindex ] = buffer[ swapindex + 1 ];
- buffer[ swapindex + 1 ] = swaptemp;
+ using std::swap;
+ swap(buffer[ swapindex ], buffer[ swapindex + 1 ]);
}
}
return result;
@@ -509,11 +446,10 @@ chd_error read_partial_sector(cdrom_file *file, void *dest, uint32_t lbasector,
-------------------------------------------------*/
/**
- * @fn uint32_t cdrom_read_data(cdrom_file *file, uint32_t lbasector, void *buffer, uint32_t datatype, bool phys)
+ * @fn bool read_data(uint32_t lbasector, void *buffer, uint32_t datatype, bool phys)
*
* @brief Cdrom read data.
*
- * @param [in,out] file If non-null, the file.
* @param lbasector The lbasector.
* @param [in,out] buffer If non-null, the buffer.
* @param datatype The datatype.
@@ -522,71 +458,67 @@ chd_error read_partial_sector(cdrom_file *file, void *dest, uint32_t lbasector,
* @return An uint32_t.
*/
-uint32_t cdrom_read_data(cdrom_file *file, uint32_t lbasector, void *buffer, uint32_t datatype, bool phys)
+bool cdrom_file::read_data(uint32_t lbasector, void *buffer, uint32_t datatype, bool phys)
{
- if (file == nullptr)
- return 0;
-
// compute CHD sector and tracknumber
uint32_t tracknum = 0;
uint32_t chdsector;
if (phys)
{
- chdsector = physical_to_chd_lba(file, lbasector, tracknum);
+ chdsector = physical_to_chd_lba(lbasector, tracknum);
}
else
{
- chdsector = logical_to_chd_lba(file, lbasector, tracknum);
+ chdsector = logical_to_chd_lba(lbasector, tracknum);
}
- /* copy out the requested sector */
- uint32_t tracktype = file->cdtoc.tracks[tracknum].trktype;
+ // copy out the requested sector
+ uint32_t tracktype = cdtoc.tracks[tracknum].trktype;
if ((datatype == tracktype) || (datatype == CD_TRACK_RAW_DONTCARE))
{
- return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 0, file->cdtoc.tracks[tracknum].datasize, phys) == CHDERR_NONE);
+ assert(cdtoc.tracks[tracknum].datasize != 0);
+ return !read_partial_sector(buffer, lbasector, chdsector, tracknum, 0, cdtoc.tracks[tracknum].datasize, phys);
}
else
{
- /* return 2048 bytes of mode 1 data from a 2352 byte mode 1 raw sector */
+ // return 2048 bytes of mode 1 data from a 2352 byte mode 1 raw sector
if ((datatype == CD_TRACK_MODE1) && (tracktype == CD_TRACK_MODE1_RAW))
{
- return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 16, 2048, phys) == CHDERR_NONE);
+ return !read_partial_sector(buffer, lbasector, chdsector, tracknum, 16, 2048, phys);
}
- /* return 2352 byte mode 1 raw sector from 2048 bytes of mode 1 data */
+ // return 2352 byte mode 1 raw sector from 2048 bytes of mode 1 data
if ((datatype == CD_TRACK_MODE1_RAW) && (tracktype == CD_TRACK_MODE1))
{
- uint8_t *bufptr = (uint8_t *)buffer;
+ auto *bufptr = (uint8_t *)buffer;
uint32_t msf = lba_to_msf(lbasector);
static const uint8_t syncbytes[12] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
memcpy(bufptr, syncbytes, 12);
- bufptr[12] = msf>>16;
- bufptr[13] = msf>>8;
- bufptr[14] = msf&0xff;
+ put_u24be(&bufptr[12], msf);
bufptr[15] = 1; // mode 1
LOG(("CDROM: promotion of mode1/form1 sector to mode1 raw is not complete!\n"));
- return (read_partial_sector(file, bufptr+16, lbasector, chdsector, tracknum, 0, 2048, phys) == CHDERR_NONE);
+ return !read_partial_sector(bufptr+16, lbasector, chdsector, tracknum, 0, 2048, phys);
}
- /* return 2048 bytes of mode 1 data from a mode2 form1 or raw sector */
+ // return 2048 bytes of mode 1 data from a mode2 form1 or raw sector
if ((datatype == CD_TRACK_MODE1) && ((tracktype == CD_TRACK_MODE2_FORM1)||(tracktype == CD_TRACK_MODE2_RAW)))
{
- return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 24, 2048, phys) == CHDERR_NONE);
+ return !read_partial_sector(buffer, lbasector, chdsector, tracknum, 24, 2048, phys);
}
- /* return 2048 bytes of mode 1 data from a mode2 form2 or XA sector */
+ // return 2048 bytes of mode 1 data from a mode2 form2 or XA sector
if ((datatype == CD_TRACK_MODE1) && (tracktype == CD_TRACK_MODE2_FORM_MIX))
{
- return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 8, 2048, phys) == CHDERR_NONE);
+ return !read_partial_sector(buffer, lbasector, chdsector, tracknum, 8, 2048, phys);
}
- /* return mode 2 2336 byte data from a 2352 byte mode 1 or 2 raw sector (skip the header) */
+ // return mode 2 2336 byte data from a 2352 byte mode 1 or 2 raw sector (skip the header)
if ((datatype == CD_TRACK_MODE2) && ((tracktype == CD_TRACK_MODE1_RAW) || (tracktype == CD_TRACK_MODE2_RAW)))
{
- return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 16, 2336, phys) == CHDERR_NONE);
+ return !read_partial_sector(buffer, lbasector, chdsector, tracknum, 16, 2336, phys);
}
LOG(("CDROM: Conversion from type %d to type %d not supported!\n", tracktype, datatype));
@@ -596,47 +528,43 @@ uint32_t cdrom_read_data(cdrom_file *file, uint32_t lbasector, void *buffer, uin
/*-------------------------------------------------
- cdrom_read_subcode - read subcode data for
+ read_subcode - read subcode data for
a sector
-------------------------------------------------*/
/**
- * @fn uint32_t cdrom_read_subcode(cdrom_file *file, uint32_t lbasector, void *buffer, bool phys)
+ * @fn bool read_subcode(uint32_t lbasector, void *buffer, bool phys)
*
* @brief Cdrom read subcode.
*
- * @param [in,out] file If non-null, the file.
* @param lbasector The lbasector.
* @param [in,out] buffer If non-null, the buffer.
* @param phys true to physical.
*
- * @return An uint32_t.
+ * @return false on failure.
*/
-uint32_t cdrom_read_subcode(cdrom_file *file, uint32_t lbasector, void *buffer, bool phys)
+bool cdrom_file::read_subcode(uint32_t lbasector, void *buffer, bool phys)
{
- if (file == nullptr)
- return ~0;
-
// compute CHD sector and tracknumber
uint32_t tracknum = 0;
uint32_t chdsector;
if (phys)
{
- chdsector = physical_to_chd_lba(file, lbasector, tracknum);
+ chdsector = physical_to_chd_lba(lbasector, tracknum);
}
else
{
- chdsector = logical_to_chd_lba(file, lbasector, tracknum);
+ chdsector = logical_to_chd_lba(lbasector, tracknum);
}
- if (file->cdtoc.tracks[tracknum].subsize == 0)
- return 0;
+ if (cdtoc.tracks[tracknum].subsize == 0)
+ return false;
// read the data
- chd_error err = read_partial_sector(file, buffer, lbasector, chdsector, tracknum, file->cdtoc.tracks[tracknum].datasize, file->cdtoc.tracks[tracknum].subsize);
- return (err == CHDERR_NONE);
+ std::error_condition err = read_partial_sector(buffer, lbasector, chdsector, tracknum, cdtoc.tracks[tracknum].datasize, cdtoc.tracks[tracknum].subsize, phys);
+ return !err;
}
@@ -646,220 +574,64 @@ uint32_t cdrom_read_subcode(cdrom_file *file, uint32_t lbasector, void *buffer,
***************************************************************************/
/*-------------------------------------------------
- cdrom_get_track - get the track number
+ get_track - get the track number
for a physical frame number
-------------------------------------------------*/
/**
- * @fn uint32_t cdrom_get_track(cdrom_file *file, uint32_t frame)
+ * @fn uint32_t get_track(uint32_t frame)
*
* @brief Cdrom get track.
*
- * @param [in,out] file If non-null, the file.
* @param frame The frame.
*
* @return An uint32_t.
*/
-uint32_t cdrom_get_track(cdrom_file *file, uint32_t frame)
+uint32_t cdrom_file::get_track(uint32_t frame) const
{
uint32_t track = 0;
- if (file == nullptr)
- return ~0;
-
/* convert to a CHD sector offset and get track information */
- logical_to_chd_lba(file, frame, track);
+ logical_to_chd_lba(frame, track);
return track;
}
-
-/*-------------------------------------------------
- cdrom_get_track_start - get the frame number
- that a track starts at
--------------------------------------------------*/
-
-/**
- * @fn uint32_t cdrom_get_track_start(cdrom_file *file, uint32_t track)
- *
- * @brief Cdrom get track start.
- *
- * @param [in,out] file If non-null, the file.
- * @param track The track.
- *
- * @return An uint32_t.
- */
-
-uint32_t cdrom_get_track_start(cdrom_file *file, uint32_t track)
+uint32_t cdrom_file::get_track_index(uint32_t frame) const
{
- if (file == nullptr)
- return ~0;
-
- /* handle lead-out specially */
- if (track == 0xaa)
- track = file->cdtoc.numtrks;
-
- return file->cdtoc.tracks[track].logframeofs;
-}
-
-/*-------------------------------------------------
- cdrom_get_track_start_phys - get the
- physical frame number that a track starts at
--------------------------------------------------*/
+ 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;
-/**
- * @fn uint32_t cdrom_get_track_start_phys(cdrom_file *file, uint32_t track)
- *
- * @brief Cdrom get track start physical.
- *
- * @param [in,out] file If non-null, the file.
- * @param track The track.
- *
- * @return An uint32_t.
- */
-
-uint32_t cdrom_get_track_start_phys(cdrom_file *file, uint32_t track)
-{
- if (file == nullptr)
- return ~0;
-
- /* handle lead-out specially */
- if (track == 0xaa)
- track = file->cdtoc.numtrks;
-
- return file->cdtoc.tracks[track].physframeofs;
-}
-
-/*-------------------------------------------------
- cdrom_get_chd - get a handle to a CHD
- from a cdrom
--------------------------------------------------*/
-
-chd_file *cdrom_get_chd(cdrom_file *file)
-{
- return file->chd;
-}
-
-/***************************************************************************
- TOC UTILITIES
-***************************************************************************/
-
-/*-------------------------------------------------
- cdrom_get_last_track - returns the last track
- number
--------------------------------------------------*/
-
-/**
- * @fn int cdrom_get_last_track(cdrom_file *file)
- *
- * @brief Cdrom get last track.
- *
- * @param [in,out] file If non-null, the file.
- *
- * @return An int.
- */
-
-int cdrom_get_last_track(cdrom_file *file)
-{
- if (file == nullptr)
- return -1;
-
- return file->cdtoc.numtrks;
-}
-
-
-/*-------------------------------------------------
- cdrom_get_adr_control - get the ADR | CONTROL
- for a track
--------------------------------------------------*/
-
-/**
- * @fn int cdrom_get_adr_control(cdrom_file *file, int track)
- *
- * @brief Cdrom get address control.
- *
- * @param [in,out] file If non-null, the file.
- * @param track The track.
- *
- * @return An int.
- */
-
-int cdrom_get_adr_control(cdrom_file *file, int track)
-{
- if (file == nullptr)
- return -1;
-
- if (track == 0xaa || file->cdtoc.tracks[track].trktype == CD_TRACK_AUDIO)
+ for (int i = 0; i < std::size(cdtrack_info.track[track].idx); i++)
{
- return 0x10; // audio track, subchannel is position
+ if (index_offset >= cdtrack_info.track[track].idx[i])
+ index = i;
+ else
+ break;
}
- return 0x14; // data track, subchannel is position
-}
-
-
-/*-------------------------------------------------
- cdrom_get_track_type - return the track type
--------------------------------------------------*/
-
-/**
- * @fn int cdrom_get_track_type(cdrom_file *file, int track)
- *
- * @brief Cdrom get track type.
- *
- * @param [in,out] file If non-null, the file.
- * @param track The track.
- *
- * @return An int.
- */
-
-int cdrom_get_track_type(cdrom_file *file, int track)
-{
- if (file == nullptr)
- return -1;
+ if (cdtrack_info.track[track].idx[index] == -1)
+ index = 1; // valid index not found, default to index 1
- return file->cdtoc.tracks[track].trktype;
+ return index;
}
-/*-------------------------------------------------
- cdrom_get_toc - return the TOC data for a
- CD-ROM
--------------------------------------------------*/
-
-/**
- * @fn const cdrom_toc *cdrom_get_toc(cdrom_file *file)
- *
- * @brief Cdrom get TOC.
- *
- * @param [in,out] file If non-null, the file.
- *
- * @return null if it fails, else a cdrom_toc*.
- */
-
-const cdrom_toc *cdrom_get_toc(cdrom_file *file)
-{
- if (file == nullptr)
- return nullptr;
-
- return &file->cdtoc;
-}
-
-
-
/***************************************************************************
EXTRA UTILITIES
***************************************************************************/
/*-------------------------------------------------
- cdrom_get_info_from_type_string
+ get_info_from_type_string
take a string and convert it into track type
and track data size
-------------------------------------------------*/
/**
- * @fn static void cdrom_get_info_from_type_string(const char *typestring, uint32_t *trktype, uint32_t *datasize)
+ * @fn static void get_info_from_type_string(const char *typestring, uint32_t *trktype, uint32_t *datasize)
*
* @brief Cdrom get information from type string.
*
@@ -868,7 +640,7 @@ const cdrom_toc *cdrom_get_toc(cdrom_file *file)
* @param [in,out] datasize If non-null, the datasize.
*/
-static void cdrom_get_info_from_type_string(const char *typestring, uint32_t *trktype, uint32_t *datasize)
+void cdrom_file::get_info_from_type_string(const char *typestring, uint32_t *trktype, uint32_t *datasize)
{
if (!strcmp(typestring, "MODE1"))
{
@@ -925,11 +697,6 @@ static void cdrom_get_info_from_type_string(const char *typestring, uint32_t *tr
*trktype = CD_TRACK_MODE2_FORM_MIX;
*datasize = 2336;
}
- else if (!strcmp(typestring, "MODE2/2336"))
- {
- *trktype = CD_TRACK_MODE2_FORM_MIX;
- *datasize = 2336;
- }
else if (!strcmp(typestring, "MODE2_RAW"))
{
*trktype = CD_TRACK_MODE2_RAW;
@@ -940,6 +707,11 @@ static void cdrom_get_info_from_type_string(const char *typestring, uint32_t *tr
*trktype = CD_TRACK_MODE2_RAW;
*datasize = 2352;
}
+ else if (!strcmp(typestring, "CDI/2352"))
+ {
+ *trktype = CD_TRACK_MODE2_RAW;
+ *datasize = 2352;
+ }
else if (!strcmp(typestring, "AUDIO"))
{
*trktype = CD_TRACK_AUDIO;
@@ -948,61 +720,61 @@ static void cdrom_get_info_from_type_string(const char *typestring, uint32_t *tr
}
/*-------------------------------------------------
- cdrom_convert_type_string_to_track_info -
+ convert_type_string_to_track_info -
take a string and convert it into track type
and track data size
-------------------------------------------------*/
/**
- * @fn void cdrom_convert_type_string_to_track_info(const char *typestring, cdrom_track_info *info)
+ * @fn void convert_type_string_to_track_info(const char *typestring, track_info *info)
*
- * @brief Cdrom convert type string to track information.
+ * @brief Convert type string to track information.
*
* @param typestring The typestring.
* @param [in,out] info If non-null, the information.
*/
-void cdrom_convert_type_string_to_track_info(const char *typestring, cdrom_track_info *info)
+void cdrom_file::convert_type_string_to_track_info(const char *typestring, track_info *info)
{
- cdrom_get_info_from_type_string(typestring, &info->trktype, &info->datasize);
+ get_info_from_type_string(typestring, &info->trktype, &info->datasize);
}
/*-------------------------------------------------
- cdrom_convert_type_string_to_pregap_info -
+ convert_type_string_to_pregap_info -
take a string and convert it into pregap type
and pregap data size
-------------------------------------------------*/
/**
- * @fn void cdrom_convert_type_string_to_pregap_info(const char *typestring, cdrom_track_info *info)
+ * @fn void convert_type_string_to_pregap_info(const char *typestring, track_info *info)
*
- * @brief Cdrom convert type string to pregap information.
+ * @brief Convert type string to pregap information.
*
* @param typestring The typestring.
* @param [in,out] info If non-null, the information.
*/
-void cdrom_convert_type_string_to_pregap_info(const char *typestring, cdrom_track_info *info)
+void cdrom_file::convert_type_string_to_pregap_info(const char *typestring, track_info *info)
{
- cdrom_get_info_from_type_string(typestring, &info->pgtype, &info->pgdatasize);
+ get_info_from_type_string(typestring, &info->pgtype, &info->pgdatasize);
}
/*-------------------------------------------------
- cdrom_convert_subtype_string_to_track_info -
+ convert_subtype_string_to_track_info -
take a string and convert it into track subtype
and track subcode data size
-------------------------------------------------*/
/**
- * @fn void cdrom_convert_subtype_string_to_track_info(const char *typestring, cdrom_track_info *info)
+ * @fn void convert_subtype_string_to_track_info(const char *typestring, track_info *info)
*
- * @brief Cdrom convert subtype string to track information.
+ * @brief Convert subtype string to track information.
*
* @param typestring The typestring.
* @param [in,out] info If non-null, the information.
*/
-void cdrom_convert_subtype_string_to_track_info(const char *typestring, cdrom_track_info *info)
+void cdrom_file::convert_subtype_string_to_track_info(const char *typestring, track_info *info)
{
if (!strcmp(typestring, "RW"))
{
@@ -1017,21 +789,21 @@ void cdrom_convert_subtype_string_to_track_info(const char *typestring, cdrom_tr
}
/*-------------------------------------------------
- cdrom_convert_subtype_string_to_pregap_info -
+ convert_subtype_string_to_pregap_info -
take a string and convert it into track subtype
and track subcode data size
-------------------------------------------------*/
/**
- * @fn void cdrom_convert_subtype_string_to_pregap_info(const char *typestring, cdrom_track_info *info)
+ * @fn void convert_subtype_string_to_pregap_info(const char *typestring, track_info *info)
*
- * @brief Cdrom convert subtype string to pregap information.
+ * @brief Convert subtype string to pregap information.
*
* @param typestring The typestring.
* @param [in,out] info If non-null, the information.
*/
-void cdrom_convert_subtype_string_to_pregap_info(const char *typestring, cdrom_track_info *info)
+void cdrom_file::convert_subtype_string_to_pregap_info(const char *typestring, track_info *info)
{
if (!strcmp(typestring, "RW"))
{
@@ -1046,21 +818,21 @@ void cdrom_convert_subtype_string_to_pregap_info(const char *typestring, cdrom_t
}
/*-------------------------------------------------
- cdrom_get_type_string - get the string
+ get_type_string - get the string
associated with the given type
-------------------------------------------------*/
/**
- * @fn const char *cdrom_get_type_string(uint32_t trktype)
+ * @fn const char *get_type_string(uint32_t trktype)
*
- * @brief Cdrom get type string.
+ * @brief Get type string.
*
* @param trktype The trktype.
*
* @return null if it fails, else a char*.
*/
-const char *cdrom_get_type_string(uint32_t trktype)
+const char *cdrom_file::get_type_string(uint32_t trktype)
{
switch (trktype)
{
@@ -1078,21 +850,21 @@ const char *cdrom_get_type_string(uint32_t trktype)
/*-------------------------------------------------
- cdrom_get_subtype_string - get the string
+ get_subtype_string - get the string
associated with the given subcode type
-------------------------------------------------*/
/**
- * @fn const char *cdrom_get_subtype_string(uint32_t subtype)
+ * @fn const char *get_subtype_string(uint32_t subtype)
*
- * @brief Cdrom get subtype string.
+ * @brief Get subtype string.
*
* @param subtype The subtype.
*
* @return null if it fails, else a char*.
*/
-const char *cdrom_get_subtype_string(uint32_t subtype)
+const char *cdrom_file::get_subtype_string(uint32_t subtype)
{
switch (subtype)
{
@@ -1109,112 +881,98 @@ const char *cdrom_get_subtype_string(uint32_t subtype)
***************************************************************************/
/*-------------------------------------------------
- cdrom_parse_metadata - parse metadata into the
+ parse_metadata - parse metadata into the
TOC structure
-------------------------------------------------*/
/**
- * @fn chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
+ * @fn std::error_condition cdrom_parse_metadata(chd_file *chd, toc *toc)
*
- * @brief Cdrom parse metadata.
+ * @brief Parse metadata.
*
* @param [in,out] chd If non-null, the chd.
* @param [in,out] toc If non-null, the TOC.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
+std::error_condition cdrom_file::parse_metadata(chd_file *chd, toc &toc)
{
std::string metadata;
- chd_error err;
- int i;
+ std::error_condition err;
- toc->flags = 0;
+ /* clear structures */
+ memset(&toc, 0, sizeof(toc));
+
+ toc.numsessions = 1;
/* start with no tracks */
- for (toc->numtrks = 0; toc->numtrks < CD_MAX_TRACKS; toc->numtrks++)
+ 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];
- cdrom_track_info *track;
+ 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 == CHDERR_NONE)
+ // fetch the metadata for this track
+ 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 CHDERR_INVALID_DATA;
- if (tracknum == 0 || tracknum > CD_MAX_TRACKS)
- return CHDERR_INVALID_DATA;
- track = &toc->tracks[tracknum - 1];
+ return chd_file::error::INVALID_DATA;
+ }
+ 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;
}
else
{
- err = chd->read_metadata(CDROM_TRACK_METADATA2_TAG, toc->numtrks, metadata);
- if (err == CHDERR_NONE)
- {
- /* 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 CHDERR_INVALID_DATA;
- if (tracknum == 0 || tracknum > CD_MAX_TRACKS)
- return CHDERR_INVALID_DATA;
- track = &toc->tracks[tracknum - 1];
- }
+ // fall through to GD-ROM detection
+ err = chd->read_metadata(GDROM_OLD_METADATA_TAG, toc.numtrks, metadata);
+ if (!err)
+ toc.flags |= CD_FLAG_GDROMLE; // legacy GDROM track was detected
else
- {
- err = chd->read_metadata(GDROM_OLD_METADATA_TAG, toc->numtrks, metadata);
- if (err == CHDERR_NONE)
- /* 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 == CHDERR_NONE)
- {
- /* 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 CHDERR_INVALID_DATA;
- if (tracknum == 0 || tracknum > CD_MAX_TRACKS)
- return CHDERR_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;
}
- /* extract the track type and determine the data size */
+ 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;
- cdrom_convert_type_string_to_track_info(type, track);
+ convert_type_string_to_track_info(type, track);
if (track->datasize == 0)
- return CHDERR_INVALID_DATA;
+ return chd_file::error::INVALID_DATA;
- /* extract the subtype and determine the subcode data size */
+ // extract the subtype and determine the subcode data size
track->subtype = CD_SUB_NONE;
track->subsize = 0;
- cdrom_convert_subtype_string_to_track_info(subtype, track);
+ convert_subtype_string_to_track_info(subtype, track);
- /* set the frames and extra frames data */
+ // set the frames and extra frames data
track->frames = frames;
track->padframes = padframes;
- int padded = (frames + CD_TRACK_PADDING - 1) / CD_TRACK_PADDING;
- track->extraframes = padded * CD_TRACK_PADDING - frames;
+ int padded = (frames + TRACK_PADDING - 1) / TRACK_PADDING;
+ track->extraframes = padded * TRACK_PADDING - frames;
- /* set the pregap info */
+ // set the pregap info
track->pregap = pregap;
track->pgtype = CD_TRACK_MODE1;
track->pgsub = CD_SUB_NONE;
@@ -1224,10 +982,10 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
{
if (pgtype[0] == 'V')
{
- cdrom_convert_type_string_to_pregap_info(&pgtype[1], track);
+ convert_type_string_to_pregap_info(&pgtype[1], track);
}
- cdrom_convert_subtype_string_to_pregap_info(pgsub, track);
+ convert_subtype_string_to_pregap_info(pgsub, track);
}
/* set the postgap info */
@@ -1235,113 +993,116 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
}
/* if we got any tracks this way, we're done */
- if (toc->numtrks > 0)
- return CHDERR_NONE;
+ if (toc.numtrks > 0)
+ return std::error_condition();
- printf("toc->numtrks = %u?!\n", toc->numtrks);
+ osd_printf_info("toc.numtrks = %u?!\n", toc.numtrks);
/* look for old-style metadata */
std::vector<uint8_t> oldmetadata;
err = chd->read_metadata(CDROM_OLD_METADATA_TAG, 0, oldmetadata);
- if (err != CHDERR_NONE)
+ if (err)
return err;
/* reconstruct the TOC from it */
- uint32_t *mrp = reinterpret_cast<uint32_t *>(&oldmetadata[0]);
- toc->numtrks = *mrp++;
+ auto *mrp = reinterpret_cast<uint32_t *>(&oldmetadata[0]);
+ toc.numtrks = *mrp++;
- for (i = 0; i < CD_MAX_TRACKS; i++)
+ toc.numsessions = 1;
+
+ for (int i = 0; i < MAX_TRACKS; i++)
{
- toc->tracks[i].trktype = *mrp++;
- toc->tracks[i].subtype = *mrp++;
- toc->tracks[i].datasize = *mrp++;
- toc->tracks[i].subsize = *mrp++;
- toc->tracks[i].frames = *mrp++;
- toc->tracks[i].extraframes = *mrp++;
- toc->tracks[i].pregap = 0;
- toc->tracks[i].postgap = 0;
- toc->tracks[i].pgtype = 0;
- toc->tracks[i].pgsub = 0;
- toc->tracks[i].pgdatasize = 0;
- toc->tracks[i].pgsubsize = 0;
+ toc.tracks[i].session = 0;
+ toc.tracks[i].trktype = *mrp++;
+ toc.tracks[i].subtype = *mrp++;
+ toc.tracks[i].datasize = *mrp++;
+ toc.tracks[i].subsize = *mrp++;
+ toc.tracks[i].frames = *mrp++;
+ toc.tracks[i].extraframes = *mrp++;
+ toc.tracks[i].pregap = 0;
+ toc.tracks[i].postgap = 0;
+ toc.tracks[i].pgtype = 0;
+ toc.tracks[i].pgsub = 0;
+ toc.tracks[i].pgdatasize = 0;
+ toc.tracks[i].pgsubsize = 0;
}
/* TODO: I don't know why sometimes the data is one endian and sometimes another */
- if (toc->numtrks > CD_MAX_TRACKS)
+ if (toc.numtrks > MAX_TRACKS)
{
- toc->numtrks = swapendian_int32(toc->numtrks);
- for (i = 0; i < CD_MAX_TRACKS; i++)
+ toc.numtrks = swapendian_int32(toc.numtrks);
+ for (int i = 0; i < MAX_TRACKS; i++)
{
- toc->tracks[i].trktype = swapendian_int32(toc->tracks[i].trktype);
- toc->tracks[i].subtype = swapendian_int32(toc->tracks[i].subtype);
- toc->tracks[i].datasize = swapendian_int32(toc->tracks[i].datasize);
- toc->tracks[i].subsize = swapendian_int32(toc->tracks[i].subsize);
- toc->tracks[i].frames = swapendian_int32(toc->tracks[i].frames);
- toc->tracks[i].padframes = swapendian_int32(toc->tracks[i].padframes);
- toc->tracks[i].extraframes = swapendian_int32(toc->tracks[i].extraframes);
+ toc.tracks[i].trktype = swapendian_int32(toc.tracks[i].trktype);
+ toc.tracks[i].subtype = swapendian_int32(toc.tracks[i].subtype);
+ toc.tracks[i].datasize = swapendian_int32(toc.tracks[i].datasize);
+ toc.tracks[i].subsize = swapendian_int32(toc.tracks[i].subsize);
+ toc.tracks[i].frames = swapendian_int32(toc.tracks[i].frames);
+ toc.tracks[i].padframes = swapendian_int32(toc.tracks[i].padframes);
+ toc.tracks[i].extraframes = swapendian_int32(toc.tracks[i].extraframes);
}
}
- return CHDERR_NONE;
+ return std::error_condition();
}
/*-------------------------------------------------
- cdrom_write_metadata - write metadata
+ write_metadata - write metadata
-------------------------------------------------*/
/**
- * @fn chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc)
+ * @fn std::error_condition write_metadata(chd_file *chd, const toc *toc)
*
- * @brief Cdrom write metadata.
+ * @brief Write metadata.
*
* @param [in,out] chd If non-null, the chd.
* @param toc The TOC.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc)
+std::error_condition cdrom_file::write_metadata(chd_file *chd, const toc &toc)
{
- chd_error err;
+ std::error_condition err;
/* write the metadata */
- for (int i = 0; i < toc->numtrks; i++)
+ 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];
- if (toc->tracks[i].pgdatasize > 0)
+ if (toc.tracks[i].pgdatasize > 0)
{
- strcpy(&submode[1], cdrom_get_type_string(toc->tracks[i].pgtype));
+ strcpy(&submode[1], get_type_string(toc.tracks[i].pgtype));
submode[0] = 'V'; // indicate valid submode
}
else
{
- strcpy(submode, cdrom_get_type_string(toc->tracks[i].pgtype));
+ strcpy(submode, get_type_string(toc.tracks[i].pgtype));
}
- metadata = string_format(CDROM_TRACK_METADATA2_FORMAT, i + 1, cdrom_get_type_string(toc->tracks[i].trktype),
- cdrom_get_subtype_string(toc->tracks[i].subtype), toc->tracks[i].frames, toc->tracks[i].pregap,
- submode, cdrom_get_subtype_string(toc->tracks[i].pgsub),
- toc->tracks[i].postgap);
+ metadata = util::string_format(CDROM_TRACK_METADATA2_FORMAT, i + 1, get_type_string(toc.tracks[i].trktype),
+ get_subtype_string(toc.tracks[i].subtype), toc.tracks[i].frames, toc.tracks[i].pregap,
+ submode, get_subtype_string(toc.tracks[i].pgsub),
+ toc.tracks[i].postgap);
err = chd->write_metadata(CDROM_TRACK_METADATA2_TAG, i, metadata);
}
- else
- {
- metadata = string_format(GDROM_TRACK_METADATA_FORMAT, i + 1, cdrom_get_type_string(toc->tracks[i].trktype),
- cdrom_get_subtype_string(toc->tracks[i].subtype), toc->tracks[i].frames, toc->tracks[i].padframes,
- toc->tracks[i].pregap, cdrom_get_type_string(toc->tracks[i].pgtype),
- cdrom_get_subtype_string(toc->tracks[i].pgsub), toc->tracks[i].postgap);
-
- err = chd->write_metadata(GDROM_TRACK_METADATA_TAG, i, metadata);
- }
- if (err != CHDERR_NONE)
+ if (err)
return err;
}
- return CHDERR_NONE;
+ return std::error_condition();
}
/**
@@ -1350,7 +1111,7 @@ chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc)
* -------------------------------------------------.
*/
-static const uint8_t ecclow[256] =
+const uint8_t cdrom_file::ecclow[256] =
{
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
@@ -1371,7 +1132,7 @@ static const uint8_t ecclow[256] =
};
/** @brief The ecchigh[ 256]. */
-static const uint8_t ecchigh[256] =
+const uint8_t cdrom_file::ecchigh[256] =
{
0x00, 0xf4, 0xf5, 0x01, 0xf7, 0x03, 0x02, 0xf6, 0xf3, 0x07, 0x06, 0xf2, 0x04, 0xf0, 0xf1, 0x05,
0xfb, 0x0f, 0x0e, 0xfa, 0x0c, 0xf8, 0xf9, 0x0d, 0x08, 0xfc, 0xfd, 0x09, 0xff, 0x0b, 0x0a, 0xfe,
@@ -1398,7 +1159,7 @@ static const uint8_t ecchigh[256] =
* -------------------------------------------------.
*/
-static const uint16_t poffsets[ECC_P_NUM_BYTES][ECC_P_COMP] =
+const uint16_t cdrom_file::poffsets[cdrom_file::ECC_P_NUM_BYTES][cdrom_file::ECC_P_COMP] =
{
{ 0x000,0x056,0x0ac,0x102,0x158,0x1ae,0x204,0x25a,0x2b0,0x306,0x35c,0x3b2,0x408,0x45e,0x4b4,0x50a,0x560,0x5b6,0x60c,0x662,0x6b8,0x70e,0x764,0x7ba },
{ 0x001,0x057,0x0ad,0x103,0x159,0x1af,0x205,0x25b,0x2b1,0x307,0x35d,0x3b3,0x409,0x45f,0x4b5,0x50b,0x561,0x5b7,0x60d,0x663,0x6b9,0x70f,0x765,0x7bb },
@@ -1495,7 +1256,7 @@ static const uint16_t poffsets[ECC_P_NUM_BYTES][ECC_P_COMP] =
* -------------------------------------------------.
*/
-static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] =
+const uint16_t cdrom_file::qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] =
{
{ 0x000,0x058,0x0b0,0x108,0x160,0x1b8,0x210,0x268,0x2c0,0x318,0x370,0x3c8,0x420,0x478,0x4d0,0x528,0x580,0x5d8,0x630,0x688,0x6e0,0x738,0x790,0x7e8,0x840,0x898,0x034,0x08c,0x0e4,0x13c,0x194,0x1ec,0x244,0x29c,0x2f4,0x34c,0x3a4,0x3fc,0x454,0x4ac,0x504,0x55c,0x5b4 },
{ 0x001,0x059,0x0b1,0x109,0x161,0x1b9,0x211,0x269,0x2c1,0x319,0x371,0x3c9,0x421,0x479,0x4d1,0x529,0x581,0x5d9,0x631,0x689,0x6e1,0x739,0x791,0x7e9,0x841,0x899,0x035,0x08d,0x0e5,0x13d,0x195,0x1ed,0x245,0x29d,0x2f5,0x34d,0x3a5,0x3fd,0x455,0x4ad,0x505,0x55d,0x5b5 },
@@ -1558,7 +1319,7 @@ static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] =
// particular to a mode
//-------------------------------------------------
-inline uint8_t ecc_source_byte(const uint8_t *sector, uint32_t offset)
+uint8_t cdrom_file::ecc_source_byte(const uint8_t *sector, uint32_t offset)
{
// in mode 2 always treat these as 0 bytes
return (sector[MODE_OFFSET] == 2 && offset < 4) ? 0x00 : sector[SYNC_OFFSET + SYNC_NUM_BYTES + offset];
@@ -1578,7 +1339,7 @@ inline uint8_t ecc_source_byte(const uint8_t *sector, uint32_t offset)
* @param [in,out] val2 The second value.
*/
-void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t &val1, uint8_t &val2)
+void cdrom_file::ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t &val1, uint8_t &val2)
{
val1 = val2 = 0;
for (int component = 0; component < rowlen; component++)
@@ -1603,7 +1364,7 @@ void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, u
* @return true if it succeeds, false if it fails.
*/
-bool ecc_verify(const uint8_t *sector)
+bool cdrom_file::ecc_verify(const uint8_t *sector)
{
// first verify P bytes
for (int byte = 0; byte < ECC_P_NUM_BYTES; byte++)
@@ -1636,7 +1397,7 @@ bool ecc_verify(const uint8_t *sector)
* @param [in,out] sector If non-null, the sector.
*/
-void ecc_generate(uint8_t *sector)
+void cdrom_file::ecc_generate(uint8_t *sector)
{
// first verify P bytes
for (int byte = 0; byte < ECC_P_NUM_BYTES; byte++)
@@ -1657,8 +1418,1720 @@ void ecc_generate(uint8_t *sector)
* @param [in,out] sector If non-null, the sector.
*/
-void ecc_clear(uint8_t *sector)
+void cdrom_file::ecc_clear(uint8_t *sector)
{
memset(&sector[ECC_P_OFFSET], 0, 2 * ECC_P_NUM_BYTES);
memset(&sector[ECC_Q_OFFSET], 0, 2 * ECC_Q_NUM_BYTES);
}
+
+/***************************************************************************
+
+ TOC parser
+ Handles CDRDAO .toc, CDRWIN .cue, Nero .nrg, and Sega GDROM .gdi
+
+***************************************************************************/
+
+/***************************************************************************
+ CONSTANTS & DEFINES
+***************************************************************************/
+
+/**
+ * @def TOKENIZE();
+ *
+ * @brief A macro that defines tokenize.
+ *
+ * @param linebuffer The linebuffer.
+ * @param i Zero-based index of the.
+ * @param std::size(linebuffer) The std::size(linebuffer)
+ * @param token The token.
+ * @param std::size(token) The std::size(token)
+ */
+
+#define TOKENIZE i = tokenize( linebuffer, i, std::size(linebuffer), token, std::size(token) );
+
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/**
+ * @fn std::string get_file_path(std::string &path)
+ *
+ * @brief Gets file path.
+ *
+ * @param [in,out] path Full pathname of the file.
+ *
+ * @return The file path.
+ */
+
+std::string cdrom_file::get_file_path(std::string &path)
+{
+ int pos = path.find_last_of('\\');
+ if (pos!=-1)
+ {
+ path = path.substr(0,pos+1);
+ }
+ else
+ {
+ pos = path.find_last_of('/');
+ path = path.substr(0,pos+1);
+ }
+ return path;
+}
+/*-------------------------------------------------
+ get_file_size - get the size of a file
+-------------------------------------------------*/
+
+/**
+ * @fn static uint64_t get_file_size(std::string_view filename)
+ *
+ * @brief Gets file size.
+ *
+ * @param filename Filename of the file.
+ *
+ * @return The file size.
+ */
+
+uint64_t cdrom_file::get_file_size(std::string_view filename)
+{
+ osd_file::ptr file;
+ std::uint64_t filesize = 0;
+
+ osd_file::open(std::string(filename), OPEN_FLAG_READ, file, filesize); // FIXME: allow osd_file to accept std::string_view
+
+ return filesize;
+}
+
+
+/*-------------------------------------------------
+ tokenize - get a token from the line buffer
+-------------------------------------------------*/
+
+/**
+ * @fn static int tokenize( const char *linebuffer, int i, int linebuffersize, char *token, int tokensize )
+ *
+ * @brief Tokenizes.
+ *
+ * @param linebuffer The linebuffer.
+ * @param i Zero-based index of the.
+ * @param linebuffersize The linebuffersize.
+ * @param [in,out] token If non-null, the token.
+ * @param tokensize The tokensize.
+ *
+ * @return An int.
+ */
+
+int cdrom_file::tokenize(const char *linebuffer, int i, int linebuffersize, char *token, int tokensize)
+{
+ int j = 0;
+ bool singlequote = false;
+ bool doublequote = false;
+
+ while ((i < linebuffersize) && isspace((uint8_t)linebuffer[i]))
+ {
+ i++;
+ }
+
+ while ((i < linebuffersize) && (j < tokensize) && (linebuffer[i] != '\0'))
+ {
+ if (!singlequote && linebuffer[i] == '"')
+ {
+ doublequote = !doublequote;
+ }
+ else if (!doublequote && linebuffer[i] == '\'')
+ {
+ singlequote = !singlequote;
+ }
+ else if (!singlequote && !doublequote && isspace((uint8_t)linebuffer[i]))
+ {
+ break;
+ }
+ else
+ {
+ token[j] = linebuffer[i];
+ j++;
+ }
+
+ i++;
+ }
+
+ token[j] = '\0';
+
+ return i;
+}
+
+
+/*-------------------------------------------------
+ msf_to_frames - convert m:s:f into a number of frames
+-------------------------------------------------*/
+
+/**
+ * @fn static int msf_to_frames( char *token )
+ *
+ * @brief Msf to frames.
+ *
+ * @param [in,out] token If non-null, the token.
+ *
+ * @return An int.
+ */
+
+int cdrom_file::msf_to_frames(const char *token)
+{
+ int m = 0;
+ int s = 0;
+ int f = 0;
+
+ if (sscanf(token, "%d:%d:%d", &m, &s, &f) == 1)
+ {
+ f = m;
+ }
+ else
+ {
+ // convert to just frames
+ s += (m * 60);
+ f += (s * 75);
+ }
+
+ return f;
+}
+
+/*-------------------------------------------------
+ parse_wav_sample - takes a .WAV file, verifies
+ that the file is 16 bits, and returns the
+ length in bytes of the data and the offset in
+ bytes to where the data starts in the file.
+-------------------------------------------------*/
+
+/**
+ * @fn static uint32_t parse_wav_sample(std::string_view filename, uint32_t *dataoffs)
+ *
+ * @brief Parse WAV sample.
+ *
+ * @param filename Filename of the file.
+ * @param [in,out] dataoffs If non-null, the dataoffs.
+ *
+ * @return An uint32_t.
+ */
+
+uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *dataoffs)
+{
+ unsigned long offset = 0;
+ uint32_t length, rate, filesize;
+ uint16_t bits, temp16;
+ char buf[32];
+ osd_file::ptr file;
+ uint64_t fsize = 0;
+ std::uint32_t actual;
+
+ std::string fname = std::string(filename);
+ std::error_condition const filerr = osd_file::open(fname, OPEN_FLAG_READ, file, fsize);
+ if (filerr)
+ {
+ osd_printf_error("ERROR: could not open (%s)\n", fname);
+ return 0;
+ }
+
+ /* read the core header and make sure it's a WAVE file */
+ file->read(buf, 0, 4, actual);
+ offset += actual;
+ if (offset < 4)
+ {
+ osd_printf_error("ERROR: unexpected RIFF offset %lu (%s)\n", offset, fname);
+ return 0;
+ }
+ if (memcmp(&buf[0], "RIFF", 4) != 0)
+ {
+ osd_printf_error("ERROR: could not find RIFF header (%s)\n", fname);
+ return 0;
+ }
+
+ /* get the total size */
+ file->read(&filesize, offset, 4, actual);
+ offset += actual;
+ if (offset < 8)
+ {
+ osd_printf_error("ERROR: unexpected size offset %lu (%s)\n", offset, fname);
+ return 0;
+ }
+ filesize = little_endianize_int32(filesize);
+
+ /* read the RIFF file type and make sure it's a WAVE file */
+ file->read(buf, offset, 4, actual);
+ offset += actual;
+ if (offset < 12)
+ {
+ osd_printf_error("ERROR: unexpected WAVE offset %lu (%s)\n", offset, fname);
+ return 0;
+ }
+ if (memcmp(&buf[0], "WAVE", 4) != 0)
+ {
+ osd_printf_error("ERROR: could not find WAVE header (%s)\n", fname);
+ return 0;
+ }
+
+ /* seek until we find a format tag */
+ while (true)
+ {
+ file->read(buf, offset, 4, actual);
+ offset += actual;
+ file->read(&length, offset, 4, actual);
+ offset += actual;
+ length = little_endianize_int32(length);
+ if (memcmp(&buf[0], "fmt ", 4) == 0)
+ break;
+
+ /* seek to the next block */
+ offset += length;
+ if (offset >= filesize)
+ {
+ osd_printf_error("ERROR: could not find fmt tag (%s)\n", fname);
+ return 0;
+ }
+ }
+
+ /* read the format -- make sure it is PCM */
+ file->read(&temp16, offset, 2, actual);
+ offset += actual;
+ temp16 = little_endianize_int16(temp16);
+ if (temp16 != 1)
+ {
+ osd_printf_error("ERROR: unsupported format %u - only PCM is supported (%s)\n", temp16, fname);
+ return 0;
+ }
+
+ /* number of channels -- only stereo is supported */
+ file->read(&temp16, offset, 2, actual);
+ offset += actual;
+ temp16 = little_endianize_int16(temp16);
+ if (temp16 != 2)
+ {
+ osd_printf_error("ERROR: unsupported number of channels %u - only stereo is supported (%s)\n", temp16, fname);
+ return 0;
+ }
+
+ /* sample rate */
+ file->read(&rate, offset, 4, actual);
+ offset += actual;
+ rate = little_endianize_int32(rate);
+ if (rate != 44100)
+ {
+ osd_printf_error("ERROR: unsupported samplerate %u - only 44100 is supported (%s)\n", rate, fname);
+ return 0;
+ }
+
+ /* bytes/second and block alignment are ignored */
+ file->read(buf, offset, 6, actual);
+ offset += actual;
+
+ /* bits/sample */
+ file->read(&bits, offset, 2, actual);
+ offset += actual;
+ bits = little_endianize_int16(bits);
+ if (bits != 16)
+ {
+ osd_printf_error("ERROR: unsupported bits/sample %u - only 16 is supported (%s)\n", bits, fname);
+ return 0;
+ }
+
+ /* seek past any extra data */
+ offset += length - 16;
+
+ /* seek until we find a data tag */
+ while (true)
+ {
+ file->read(buf, offset, 4, actual);
+ offset += actual;
+ file->read(&length, offset, 4, actual);
+ offset += actual;
+ length = little_endianize_int32(length);
+ if (memcmp(&buf[0], "data", 4) == 0)
+ break;
+
+ /* seek to the next block */
+ offset += length;
+ if (offset >= filesize)
+ {
+ osd_printf_error("ERROR: could not find data tag (%s)\n", fname);
+ return 0;
+ }
+ }
+
+ /* if there was a 0 length data block, we're done */
+ if (length == 0)
+ {
+ osd_printf_error("ERROR: empty data block (%s)\n", fname);
+ return 0;
+ }
+
+ *dataoffs = offset;
+
+ return length;
+}
+
+/**
+ * @fn uint16_t read_uint16(FILE *infile)
+ *
+ * @brief Reads uint 16.
+ *
+ * @param [in,out] infile If non-null, the infile.
+ *
+ * @return The uint 16.
+ */
+
+uint16_t cdrom_file::read_uint16(FILE *infile)
+{
+ unsigned char buffer[2];
+
+ fread(buffer, 2, 1, infile);
+
+ return get_u16be(buffer);
+}
+
+/**
+ * @fn uint32_t read_uint32(FILE *infile)
+ *
+ * @brief Reads uint 32.
+ *
+ * @param [in,out] infile If non-null, the infile.
+ *
+ * @return The uint 32.
+ */
+
+uint32_t cdrom_file::read_uint32(FILE *infile)
+{
+ unsigned char buffer[4];
+
+ fread(buffer, 4, 1, infile);
+
+ return get_u32be(buffer);
+}
+
+/**
+ * @fn uint64_t read_uint64(FILE *infile)
+ *
+ * @brief Reads uint 64.
+ *
+ * @param [in,out] infile If non-null, the infile.
+ *
+ * @return The uint 64.
+ */
+
+uint64_t cdrom_file::read_uint64(FILE *infile)
+{
+ unsigned char buffer[8];
+
+ fread(buffer, 8, 1, infile);
+
+ return get_u64be(buffer);
+}
+
+/*-------------------------------------------------
+ parse_nero - parse a Nero .NRG file
+-------------------------------------------------*/
+
+/**
+ * @fn std::error_condition parse_nero(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+ *
+ * @brief Chdcd parse nero.
+ *
+ * @param tocfname The tocfname.
+ * @param [in,out] outtoc The outtoc.
+ * @param [in,out] outinfo The outinfo.
+ *
+ * @return A std::error_condition.
+ */
+
+std::error_condition cdrom_file::parse_nero(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+{
+ unsigned char buffer[12];
+ uint32_t chain_offs, chunk_size;
+ int done = 0;
+
+ std::string path = std::string(tocfname);
+
+ FILE *infile = fopen(path.c_str(), "rb");
+ if (!infile)
+ {
+ return std::error_condition(errno, std::generic_category());
+ }
+
+ path = get_file_path(path);
+
+ /* clear structures */
+ 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);
+
+ if (memcmp(buffer, "NER5", 4))
+ {
+ osd_printf_error("ERROR: Not a Nero 5.5 or later image!\n");
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
+
+ chain_offs = get_u32be(&buffer[8]);
+
+ if ((buffer[7] != 0) || (buffer[6] != 0) || (buffer[5] != 0) || (buffer[4] != 0))
+ {
+ osd_printf_error("ERROR: File size is > 4GB, this version of CHDMAN cannot handle it.");
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
+
+// printf("NER5 detected, chain offset: %x\n", chain_offs);
+
+ while (!done)
+ {
+ fseek(infile, chain_offs, SEEK_SET);
+ fread(buffer, 8, 1, infile);
+
+ chunk_size = get_u32be(&buffer[4]);
+
+// printf("Chunk type: %c%c%c%c, size %x\n", buffer[0], buffer[1], buffer[2], buffer[3], chunk_size);
+
+ // we want the DAOX chunk, which has the TOC information
+ if (!memcmp(buffer, "DAOX", 4))
+ {
+ // skip second chunk size and UPC code
+ fseek(infile, 20, SEEK_CUR);
+
+ uint8_t start, end;
+ fread(&start, 1, 1, infile);
+ fread(&end, 1, 1, infile);
+
+// printf("Start track %d End track: %d\n", start, end);
+
+ outtoc.numtrks = (end-start) + 1;
+
+ uint32_t offset = 0;
+ for (int track = start; track <= end; track++)
+ {
+ uint32_t size, mode;
+ uint64_t index0, index1, track_end;
+
+ fseek(infile, 12, SEEK_CUR); // skip ISRC code
+ size = read_uint16(infile);
+ mode = read_uint16(infile);
+ fseek(infile, 2, SEEK_CUR);
+ index0 = read_uint64(infile);
+ index1 = read_uint64(infile);
+ track_end = read_uint64(infile);
+
+// 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].idx[0] = outinfo.track[track-1].idx[1] = 0;
+
+ switch (mode)
+ {
+ case 0x0000: // 2048 byte data
+ outtoc.tracks[track-1].trktype = CD_TRACK_MODE1;
+ outinfo.track[track-1].swap = false;
+ break;
+
+ case 0x0300: // Mode 2 Form 1
+ osd_printf_error("ERROR: Mode 2 Form 1 tracks not supported\n");
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+
+ case 0x0500: // raw data
+ osd_printf_error("ERROR: Raw data tracks not supported\n");
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+
+ case 0x0600: // 2352 byte mode 2 raw
+ outtoc.tracks[track-1].trktype = CD_TRACK_MODE2_RAW;
+ outinfo.track[track-1].swap = false;
+ break;
+
+ case 0x0700: // 2352 byte audio
+ outtoc.tracks[track-1].trktype = CD_TRACK_AUDIO;
+ outinfo.track[track-1].swap = true;
+ break;
+
+ case 0x0f00: // raw data with sub-channel
+ osd_printf_error("ERROR: Raw data tracks with sub-channel not supported\n");
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+
+ case 0x1000: // audio with sub-channel
+ osd_printf_error("ERROR: Audio tracks with sub-channel not supported\n");
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+
+ case 0x1100: // raw Mode 2 Form 1 with sub-channel
+ osd_printf_error("ERROR: Raw Mode 2 Form 1 tracks with sub-channel not supported\n");
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+
+ default:
+ osd_printf_error("ERROR: Unknown track type %x, contact MAMEDEV!\n", mode);
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
+
+ outtoc.tracks[track-1].datasize = size;
+
+ outtoc.tracks[track-1].subtype = CD_SUB_NONE;
+ outtoc.tracks[track-1].subsize = 0;
+
+ outtoc.tracks[track-1].pregap = (uint32_t)(index1-index0)/size;
+ outtoc.tracks[track-1].frames = (uint32_t)(track_end-index1)/size;
+ outtoc.tracks[track-1].postgap = 0;
+ outtoc.tracks[track-1].pgtype = 0;
+ outtoc.tracks[track-1].pgsub = CD_SUB_NONE;
+ outtoc.tracks[track-1].pgdatasize = 0;
+ outtoc.tracks[track-1].pgsubsize = 0;
+ outtoc.tracks[track-1].padframes = 0;
+
+ offset += (uint32_t)track_end-index1;
+ }
+ }
+
+ if (!memcmp(buffer, "END!", 4))
+ {
+ done = 1;
+ }
+ else
+ {
+ chain_offs += chunk_size + 8;
+ }
+ }
+
+ fclose(infile);
+
+ return std::error_condition();
+}
+
+/*-------------------------------------------------
+ parse_iso - parse a .ISO file
+-------------------------------------------------*/
+
+/**
+ * @fn std::error_condition parse_iso(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+ *
+ * @brief Parse ISO.
+ *
+ * @param tocfname The tocfname.
+ * @param [in,out] outtoc The outtoc.
+ * @param [in,out] outinfo The outinfo.
+ *
+ * @return A std::error_condition.
+ */
+
+std::error_condition cdrom_file::parse_iso(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+{
+ std::string path = std::string(tocfname);
+
+ FILE *infile = fopen(path.c_str(), "rb");
+ if (!infile)
+ {
+ return std::error_condition(errno, std::generic_category());
+ }
+
+ path = get_file_path(path);
+
+ /* clear structures */
+ memset(&outtoc, 0, sizeof(outtoc));
+ outinfo.reset();
+
+ uint64_t size = get_file_size(tocfname);
+ fclose(infile);
+
+
+ outtoc.numtrks = 1;
+ outtoc.numsessions = 1;
+
+ outinfo.track[0].fname = tocfname;
+ outinfo.track[0].offset = 0;
+ outinfo.track[0].idx[0] = outinfo.track[0].idx[1] = 0;
+
+ if ((size % 2048) == 0)
+ {
+ outtoc.tracks[0].trktype = CD_TRACK_MODE1;
+ outtoc.tracks[0].frames = size / 2048;
+ outtoc.tracks[0].datasize = 2048;
+ outinfo.track[0].swap = false;
+ }
+ else if ((size % 2336) == 0)
+ {
+ // 2352 byte mode 2
+ outtoc.tracks[0].trktype = CD_TRACK_MODE2;
+ outtoc.tracks[0].frames = size / 2336;
+ outtoc.tracks[0].datasize = 2336;
+ outinfo.track[0].swap = false;
+ }
+ else if ((size % 2352) == 0)
+ {
+ // 2352 byte mode 2 raw
+ outtoc.tracks[0].trktype = CD_TRACK_MODE2_RAW;
+ outtoc.tracks[0].frames = size / 2352;
+ outtoc.tracks[0].datasize = 2352;
+ outinfo.track[0].swap = false;
+ }
+ else
+ {
+ osd_printf_error("ERROR: Unrecognized track type\n");
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
+
+ outtoc.tracks[0].subtype = CD_SUB_NONE;
+ outtoc.tracks[0].subsize = 0;
+
+ outtoc.tracks[0].pregap = 0;
+
+ outtoc.tracks[0].postgap = 0;
+ outtoc.tracks[0].pgtype = 0;
+ outtoc.tracks[0].pgsub = CD_SUB_NONE;
+ outtoc.tracks[0].pgdatasize = 0;
+ outtoc.tracks[0].pgsubsize = 0;
+ outtoc.tracks[0].padframes = 0;
+
+
+ return std::error_condition();
+}
+
+/*-------------------------------------------------
+ parse_gdi - parse a Sega GD-ROM rip
+-------------------------------------------------*/
+
+/**
+ * @fn static std::error_condition parse_gdi(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+ *
+ * @brief Chdcd parse GDI.
+ *
+ * @param tocfname The tocfname.
+ * @param [in,out] outtoc The outtoc.
+ * @param [in,out] outinfo The outinfo.
+ *
+ * @return A std::error_condition.
+ */
+
+std::error_condition cdrom_file::parse_gdi(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+{
+ char token[512];
+ int i = 0;
+ int trackcnt = 0;
+
+ std::string path = std::string(tocfname);
+
+ FILE *infile = fopen(path.c_str(), "rt");
+ if (!infile)
+ {
+ return std::error_condition(errno, std::generic_category());
+ }
+
+ path = get_file_path(path);
+
+ /* clear structures */
+ memset(&outtoc, 0, sizeof(outtoc));
+ outinfo.reset();
+
+ outtoc.flags = CD_FLAG_GDROM;
+
+ char linebuffer[512];
+ memset(linebuffer, 0, sizeof(linebuffer));
+
+ if (!fgets(linebuffer,511,infile))
+ {
+ osd_printf_error("GDI doesn't have track count (blank file?)\n");
+ return chd_file::error::INVALID_DATA;
+ }
+
+ i = 0;
+ TOKENIZE
+
+ const int numtracks = atoi(token);
+
+ if (numtracks > 0 && numtracks - 1 > std::size(outinfo.track))
+ {
+ osd_printf_error("GDI expects too many tracks. Expected %d tracks but only up to %zu tracks allowed\n", numtracks, std::size(outinfo.track) + 1);
+ return chd_file::error::INVALID_DATA;
+ }
+
+ if (numtracks == 0)
+ {
+ osd_printf_error("GDI header specifies no tracks\n");
+ return chd_file::error::INVALID_DATA;
+ }
+
+ while (!feof(infile))
+ {
+ int paramcnt = 0;
+
+ if (!fgets(linebuffer,511,infile))
+ break;
+
+ i = 0;
+ TOKENIZE
+
+ // Ignore empty lines so they're not countered toward the total track count
+ if (!token[0])
+ continue;
+
+ paramcnt++;
+ const int trknum = atoi(token) - 1;
+
+ if (trknum < 0 || trknum > std::size(outinfo.track) || trknum + 1 > numtracks)
+ {
+ osd_printf_error("Track %d is out of expected range of 1 to %d\n", trknum + 1, numtracks);
+ return chd_file::error::INVALID_DATA;
+ }
+
+ if (outtoc.tracks[trknum].datasize != 0)
+ osd_printf_warning("Track %d defined multiple times?\n", trknum + 1);
+ else
+ trackcnt++;
+
+ outinfo.track[trknum].swap = false;
+ outinfo.track[trknum].offset = 0;
+
+ outtoc.tracks[trknum].datasize = 0;
+ outtoc.tracks[trknum].subtype = CD_SUB_NONE;
+ outtoc.tracks[trknum].subsize = 0;
+ outtoc.tracks[trknum].pgsub = CD_SUB_NONE;
+
+ TOKENIZE
+ if (token[0])
+ paramcnt++;
+ outtoc.tracks[trknum].physframeofs = atoi(token);
+
+ TOKENIZE
+ if (token[0])
+ paramcnt++;
+ const int trktype = atoi(token);
+
+ TOKENIZE
+ if (token[0])
+ paramcnt++;
+ const int trksize = atoi(token);
+
+ if (trktype == 4 && trksize == 2352)
+ {
+ outtoc.tracks[trknum].trktype = CD_TRACK_MODE1_RAW;
+ outtoc.tracks[trknum].datasize = 2352;
+ }
+ else if (trktype == 4 && trksize == 2048)
+ {
+ outtoc.tracks[trknum].trktype = CD_TRACK_MODE1;
+ outtoc.tracks[trknum].datasize = 2048;
+ }
+ else if (trktype == 0)
+ {
+ outtoc.tracks[trknum].trktype = CD_TRACK_AUDIO;
+ outtoc.tracks[trknum].datasize = 2352;
+ outinfo.track[trknum].swap = true;
+ }
+ else
+ {
+ osd_printf_error("Unknown track type %d and track size %d combination encountered\n", trktype, trksize);
+ return chd_file::error::INVALID_DATA;
+ }
+
+ // skip to start of next token
+ int pi = i;
+ while (pi < std::size(linebuffer) && isspace((uint8_t)linebuffer[pi]))
+ pi++;
+
+ if (linebuffer[pi] == '"' && strchr(linebuffer + pi + 1, '"') == nullptr)
+ {
+ osd_printf_error("Track %d filename does not having closing quotation mark: '%s'\n", trknum + 1, linebuffer + pi);
+ return chd_file::error::INVALID_DATA;
+ }
+
+ TOKENIZE
+ if (token[0])
+ paramcnt++;
+
+ outinfo.track[trknum].fname.assign(path).append(token);
+
+ const uint64_t sz = get_file_size(outinfo.track[trknum].fname);
+ outtoc.tracks[trknum].frames = sz / trksize;
+ outtoc.tracks[trknum].padframes = 0;
+
+ if (trknum != 0)
+ {
+ const int dif = outtoc.tracks[trknum].physframeofs - (outtoc.tracks[trknum-1].frames + outtoc.tracks[trknum-1].physframeofs);
+ outtoc.tracks[trknum-1].frames += dif;
+ outtoc.tracks[trknum-1].padframes = dif;
+ }
+
+ TOKENIZE
+ // offset parameter, not used
+ if (token[0])
+ paramcnt++;
+
+ // check if there are any extra parameters that shouldn't be there
+ while (token[0])
+ {
+ TOKENIZE
+ if (token[0])
+ paramcnt++;
+ }
+
+ if (paramcnt != 6)
+ {
+ osd_printf_error("GDI track entry should have 6 parameters, found %d\n", paramcnt);
+ return chd_file::error::INVALID_DATA;
+ }
+ }
+
+ bool missing_tracks = trackcnt != numtracks;
+ for (int i = 0; i < numtracks; i++)
+ {
+ if (outtoc.tracks[i].datasize == 0)
+ {
+ osd_printf_warning("Could not find track %d\n", i + 1);
+ missing_tracks = true;
+ }
+ }
+
+ if (missing_tracks)
+ {
+ osd_printf_error("GDI is missing tracks\n");
+ return chd_file::error::INVALID_DATA;
+ }
+
+ if (EXTRA_VERBOSE)
+ for (int i = 0; i < numtracks; i++)
+ {
+ osd_printf_verbose("'%s' %d %d %d (true %d)\n", outinfo.track[i].fname, outtoc.tracks[i].frames, outtoc.tracks[i].padframes, outtoc.tracks[i].physframeofs, outtoc.tracks[i].frames - outtoc.tracks[i].padframes);
+ }
+
+ /* close the input TOC */
+ fclose(infile);
+
+ /* store the number of tracks found */
+ outtoc.numtrks = numtracks;
+ outtoc.numsessions = 1;
+
+ return std::error_condition();
+}
+
+/*-------------------------------------------------
+ parse_cue - parse a .CUE file
+-------------------------------------------------*/
+
+/**
+ * @fn std::error_condition parse_cue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+ *
+ * @brief Chdcd parse cue.
+ *
+ * @param tocfname The tocfname.
+ * @param [in,out] outtoc The outtoc.
+ * @param [in,out] outinfo The outinfo.
+ *
+ * @return A std::error_condition.
+ *
+ * Redump multi-CUE for Dreamcast GDI:
+ * Dreamcast discs have two images on a single disc. The first image is SINGLE-DENSITY and the second image
+ * is HIGH-DENSITY. The SINGLE-DENSITY area starts 0 LBA and HIGH-DENSITY area starts 45000 LBA.
+ */
+
+std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+{
+ 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)
+ {
+ return std::error_condition(errno, std::generic_category());
+ }
+
+ path = get_file_path(path);
+
+ /* clear structures */
+ memset(&outtoc, 0, sizeof(outtoc));
+ outinfo.reset();
+
+ trknum = -1;
+ wavoffs = wavlen = 0;
+ sessionnum = 0;
+ session_pregap = 0;
+
+ if (is_gdrom)
+ {
+ outtoc.flags = CD_FLAG_GDROM;
+ }
+
+ char linebuffer[512];
+ memset(linebuffer, 0, sizeof(linebuffer));
+
+ while (!feof(infile))
+ {
+ /* get the next line */
+ if (!fgets(linebuffer, 511, infile))
+ break;
+
+ i = 0;
+
+ TOKENIZE
+
+ if (!strcmp(token, "REM"))
+ {
+ /* skip to actual data of REM command */
+ while (i < std::size(linebuffer) && isspace((uint8_t)linebuffer[i]))
+ i++;
+
+ 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;
+ }
+ else if (is_gdrom && !strncmp(linebuffer+i, "HIGH-DENSITY AREA", 17))
+ {
+ /* high-density area starts LBA = 45000 */
+ current_area = HIGH_DENSITY;
+ }
+ }
+ else if (!strcmp(token, "FILE"))
+ {
+ /* found the data file for a track */
+ TOKENIZE
+
+ /* keep the filename */
+ if (!is_multibin)
+ {
+ std::string prevfname(std::move(lastfname));
+ lastfname.assign(path).append(token);
+ is_multibin = !prevfname.empty() && lastfname != prevfname;
+ }
+ else
+ {
+ lastfname.assign(path).append(token);
+ }
+
+ /* get the file type */
+ TOKENIZE
+
+ if (!strcmp(token, "BINARY"))
+ {
+ outinfo.track[trknum+1].swap = false;
+ }
+ else if (!strcmp(token, "MOTOROLA"))
+ {
+ outinfo.track[trknum+1].swap = true;
+ }
+ else if (!strcmp(token, "WAVE"))
+ {
+ wavlen = parse_wav_sample(lastfname, &wavoffs);
+ if (!wavlen)
+ {
+ fclose(infile);
+ osd_printf_error("ERROR: couldn't read [%s] or not a valid .WAV\n", lastfname);
+ return chd_file::error::INVALID_DATA;
+ }
+ }
+ else
+ {
+ fclose(infile);
+ osd_printf_error("ERROR: Unhandled track type %s\n", token);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
+ }
+ else if (!strcmp(token, "TRACK"))
+ {
+ /* get the track number */
+ TOKENIZE
+ trknum = strtoul(token, nullptr, 10) - 1;
+
+ /* 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;
+ outtoc.tracks[trknum].pregap = 0;
+ outtoc.tracks[trknum].padframes = 0;
+ outtoc.tracks[trknum].datasize = 0;
+ outtoc.tracks[trknum].multicuearea = is_gdrom ? current_area : 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)
+ {
+ outtoc.tracks[trknum].frames = wavlen/2352;
+ outinfo.track[trknum].offset = wavoffs;
+ wavoffs = wavlen = 0;
+ }
+
+ outinfo.track[trknum].fname.assign(lastfname); /* default filename to the last one */
+
+ if (EXTRA_VERBOSE)
+ {
+ if (is_gdrom)
+ {
+ osd_printf_verbose("trk %d: fname %s offset %d area %d\n", trknum, outinfo.track[trknum].fname, outinfo.track[trknum].offset, outtoc.tracks[trknum].multicuearea);
+ }
+ else
+ {
+ osd_printf_verbose("trk %d: fname %s offset %d\n", trknum, outinfo.track[trknum].fname, outinfo.track[trknum].offset);
+ }
+ }
+
+ convert_type_string_to_track_info(token, &outtoc.tracks[trknum]);
+ if (outtoc.tracks[trknum].datasize == 0)
+ {
+ fclose(infile);
+ osd_printf_error("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
+
+ /* next (optional) token on the line is the subcode type */
+ TOKENIZE
+
+ convert_subtype_string_to_track_info(token, &outtoc.tracks[trknum]);
+ }
+ else if (!strcmp(token, "INDEX"))
+ {
+ int idx, frames;
+
+ /* get index number */
+ TOKENIZE
+ idx = strtoul(token, nullptr, 10);
+
+ /* get index */
+ TOKENIZE
+ frames = msf_to_frames(token);
+
+ if (idx < 0 || idx > MAX_INDEX)
+ {
+ osd_printf_error("ERROR: encountered invalid index %d\n", idx);
+ return chd_file::error::INVALID_DATA;
+ }
+
+ outinfo.track[trknum].idx[idx] = frames;
+
+ if (idx == 1)
+ {
+ if (outtoc.tracks[trknum].pregap == 0 && outinfo.track[trknum].idx[0] != -1)
+ {
+ 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 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].idx[0] = frames;
+ }
+ }
+ }
+ else if (!strcmp(token, "PREGAP"))
+ {
+ int frames;
+
+ /* get index */
+ TOKENIZE
+ frames = msf_to_frames(token);
+
+ outtoc.tracks[trknum].pregap = frames;
+ }
+ else if (!strcmp(token, "POSTGAP"))
+ {
+ int frames;
+
+ /* get index */
+ TOKENIZE
+ frames = msf_to_frames(token);
+
+ 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;
+ }
+ }
+ }
+
+ /* close the input CUE */
+ fclose(infile);
+
+ /* 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;
+
+ if (outinfo.track[trknum].idx[1] == -1)
+ {
+ /* index 1 should always be set */
+ osd_printf_error("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)
+ continue;
+
+ if (trknum+1 >= outtoc.numtrks && trknum > 0 && (outinfo.track[trknum].fname.compare(outinfo.track[trknum-1].fname) == 0))
+ {
+ /* if the last track's filename is the same as the previous track */
+ tlen = get_file_size(outinfo.track[trknum].fname);
+ if (tlen == 0)
+ {
+ osd_printf_error("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum].fname);
+ 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)
+ {
+ osd_printf_error("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)
+ {
+ osd_printf_error("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum].fname);
+ 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)
+ {
+ /* 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) */
+ }
+
+ if (outinfo.track[trknum].leadin == -1 && trknum > 0 && outtoc.tracks[trknum].session != outtoc.tracks[trknum-1].session)
+ {
+ /* add a standard lead-in to the first track of a new session */
+ outinfo.track[trknum].leadin = 4500; /* lead-in (1m0s0f) */
+ }
+ }
+ else
+ {
+ if (outinfo.track[trknum].leadout != -1)
+ {
+ /*
+ 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)
+ {
+ outtoc.tracks[trknum].frames = endframes; /* trim track length */
+
+ 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;
+ }
+ }
+ }
+
+ if (trknum > 0 && outinfo.track[trknum-1].leadout != -1)
+ {
+ /*
+ 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"))
+ {
+ 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);
+ }
+ }
+ }
+ }
+ }
+
+ if (is_gdrom)
+ {
+ /*
+ * Strip pregaps from Redump tracks and adjust the LBA offset to match TOSEC layout
+ */
+ for (trknum = 1; trknum < outtoc.numtrks; trknum++)
+ {
+ uint32_t this_pregap = outtoc.tracks[trknum].pregap;
+ uint32_t this_offset = this_pregap * (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+
+ outtoc.tracks[trknum-1].frames += this_pregap;
+ outtoc.tracks[trknum-1].splitframes += this_pregap;
+
+ outinfo.track[trknum].offset += this_offset;
+ outtoc.tracks[trknum].frames -= this_pregap;
+ outinfo.track[trknum].idx[1] -= this_pregap;
+
+ outtoc.tracks[trknum].pregap = 0;
+ outtoc.tracks[trknum].pgtype = 0;
+ }
+
+ /*
+ * TOC now matches TOSEC layout, set LBA for every track with HIGH-DENSITY area @ LBA 45000
+ */
+ for (trknum = 1; trknum < outtoc.numtrks; trknum++)
+ {
+ if (outtoc.tracks[trknum].multicuearea == HIGH_DENSITY && outtoc.tracks[trknum-1].multicuearea == SINGLE_DENSITY)
+ {
+ outtoc.tracks[trknum].physframeofs = 45000;
+ int dif=outtoc.tracks[trknum].physframeofs-(outtoc.tracks[trknum-1].frames+outtoc.tracks[trknum-1].physframeofs);
+ outtoc.tracks[trknum-1].frames += dif;
+ outtoc.tracks[trknum-1].padframes = dif;
+ }
+ else
+ {
+ outtoc.tracks[trknum].physframeofs = outtoc.tracks[trknum-1].physframeofs + outtoc.tracks[trknum-1].frames;
+ }
+ }
+ }
+
+ if (EXTRA_VERBOSE)
+ {
+ for (trknum = 0; trknum < outtoc.numtrks; trknum++)
+ {
+ osd_printf_verbose("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,
+ outtoc.tracks[trknum].padframes,
+ outtoc.tracks[trknum].splitframes,
+ outtoc.tracks[trknum].multicuearea,
+ outtoc.tracks[trknum].physframeofs,
+ outtoc.tracks[trknum].pregap,
+ outtoc.tracks[trknum].pgtype,
+ outtoc.tracks[trknum].pgdatasize,
+ outinfo.track[trknum].idx[0],
+ outinfo.track[trknum].idx[1],
+ outtoc.tracks[trknum].frames - outtoc.tracks[trknum].padframes);
+ }
+ }
+
+ return std::error_condition();
+}
+
+/*---------------------------------------------------------------------------------------
+ is_gdicue - determine if CUE contains Redump multi-CUE format for Dreamcast GDI
+----------------------------------------------------------------------------------------*/
+
+/**
+ * Dreamcast GDI has two images on one disc, SINGLE-DENSITY and HIGH-DENSITY.
+ *
+ * Redump stores both images in a single .cue with a REM comment separating the images.
+ * This multi-cue format replaces the old flawed .gdi format.
+ *
+ * http://forum.redump.org/topic/19969/done-sega-dreamcast-multicue-gdi/
+ *
+ * This function looks for strings "REM SINGLE-DENSITY AREA" & "REM HIGH-DENSITY AREA"
+ * indicating the Redump multi-cue format and therefore a Dreamcast GDI disc.
+ */
+
+bool cdrom_file::is_gdicue(std::string_view tocfname)
+{
+ char token[512];
+ bool has_rem_singledensity = false;
+ bool has_rem_highdensity = false;
+ std::string path = std::string(tocfname);
+
+ FILE *infile = fopen(path.c_str(), "rt");
+ if (!infile)
+ {
+ return false;
+ }
+
+ path = get_file_path(path);
+
+ char linebuffer[512];
+ memset(linebuffer, 0, sizeof(linebuffer));
+
+ while (!feof(infile))
+ {
+ if (!fgets(linebuffer, 511, infile))
+ break;
+
+ int i = 0;
+
+ TOKENIZE
+
+ if (!strcmp(token, "REM"))
+ {
+ /* skip to actual data of REM command */
+ while (i < std::size(linebuffer) && isspace((uint8_t)linebuffer[i]))
+ i++;
+
+ if (!strncmp(linebuffer+i, "SINGLE-DENSITY AREA", 19))
+ has_rem_singledensity = true;
+ else if (!strncmp(linebuffer+i, "HIGH-DENSITY AREA", 17))
+ has_rem_highdensity = true;
+ }
+ }
+
+ fclose(infile);
+
+ return has_rem_singledensity && has_rem_highdensity;
+}
+
+/*-------------------------------------------------
+ parse_toc - parse a CDRDAO format TOC file
+-------------------------------------------------*/
+
+/**
+ * @fn std::error_condition parse_toc(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+ *
+ * @brief Chdcd parse TOC.
+ *
+ * @param tocfname The tocfname.
+ * @param [in,out] outtoc The outtoc.
+ * @param [in,out] outinfo The outinfo.
+ *
+ * @return A std::error_condition.
+ */
+
+std::error_condition cdrom_file::parse_toc(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+{
+ char token[512];
+
+ auto pos = tocfname.rfind('.');
+ std::string tocfext = pos == std::string_view::npos ? std::string() : strmakelower(tocfname.substr(pos + 1));
+
+ if (tocfext == "gdi")
+ {
+ return parse_gdi(tocfname, outtoc, outinfo);
+ }
+
+ if (tocfext == "cue")
+ {
+ return parse_cue(tocfname, outtoc, outinfo);
+ }
+
+ if (tocfext == "nrg")
+ {
+ return parse_nero(tocfname, outtoc, outinfo);
+ }
+
+ if (tocfext == "iso" || tocfext == "cdr" || tocfext == "toast")
+ {
+ return parse_iso(tocfname, outtoc, outinfo);
+ }
+
+ std::string path = std::string(tocfname);
+
+ FILE *infile = fopen(path.c_str(), "rt");
+ if (!infile)
+ {
+ return std::error_condition(errno, std::generic_category());
+ }
+
+ path = get_file_path(path);
+
+ /* clear structures */
+ memset(&outtoc, 0, sizeof(outtoc));
+ outinfo.reset();
+
+ int trknum = -1;
+
+ char linebuffer[512];
+ memset(linebuffer, 0, sizeof(linebuffer));
+
+ while (!feof(infile))
+ {
+ /* get the next line */
+ if (!fgets(linebuffer, 511, infile))
+ break;
+
+ int i = 0;
+
+ TOKENIZE
+
+ /*
+ Samples: https://github.com/cdrdao/cdrdao/tree/master/testtocs
+
+ Unimplemented:
+ CD_TEXT
+ SILENCE
+ ZERO
+ FIFO
+ PREGAP
+ CATALOG
+ ISRC
+ */
+ if (!strcmp(token, "NO"))
+ {
+ TOKENIZE
+ if (!strcmp(token, "COPY"))
+ outtoc.tracks[trknum].control_flags &= ~CD_FLAG_CONTROL_DIGITAL_COPY_PERMITTED;
+ else if (!strcmp(token, "PRE_EMPHASIS"))
+ outtoc.tracks[trknum].control_flags &= ~CD_FLAG_CONTROL_PREEMPHASIS;
+ }
+ else if (!strcmp(token, "COPY"))
+ {
+ outtoc.tracks[trknum].control_flags |= CD_FLAG_CONTROL_DIGITAL_COPY_PERMITTED;
+ }
+ else if (!strcmp(token, "PRE_EMPHASIS"))
+ {
+ outtoc.tracks[trknum].control_flags |= CD_FLAG_CONTROL_PREEMPHASIS;
+ }
+ else if (!strcmp(token, "TWO_CHANNEL_AUDIO"))
+ {
+ outtoc.tracks[trknum].control_flags &= ~CD_FLAG_CONTROL_4CH;
+ }
+ else if (!strcmp(token, "FOUR_CHANNEL_AUDIO"))
+ {
+ outtoc.tracks[trknum].control_flags |= CD_FLAG_CONTROL_4CH;
+ }
+ else if ((!strcmp(token, "DATAFILE")) || (!strcmp(token, "AUDIOFILE")) || (!strcmp(token, "FILE")))
+ {
+ int f;
+
+ /* found the data file for a track */
+ TOKENIZE
+
+ /* keep the filename */
+ outinfo.track[trknum].fname.assign(path).append(token);
+
+ /* get either the offset or the length */
+ TOKENIZE
+
+ if (!strcmp(token, "SWAP"))
+ {
+ TOKENIZE
+
+ outinfo.track[trknum].swap = true;
+ }
+ else
+ {
+ outinfo.track[trknum].swap = false;
+ }
+
+ if (token[0] == '#')
+ {
+ /* it's a decimal offset, use it */
+ f = strtoul(&token[1], nullptr, 10);
+ }
+ else if (isdigit((uint8_t)token[0]))
+ {
+ /* convert the time to an offset */
+ f = msf_to_frames(token);
+
+ f *= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+ }
+ else
+ {
+ f = 0;
+ }
+
+ outinfo.track[trknum].offset = f;
+
+ TOKENIZE
+
+ if (isdigit((uint8_t)token[0]))
+ {
+ // this could be the length or an offset from the previous field.
+ f = msf_to_frames(token);
+
+ TOKENIZE
+
+ if (isdigit((uint8_t)token[0]))
+ {
+ // it was an offset.
+ f *= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+
+ outinfo.track[trknum].offset += f;
+
+ // this is the length.
+ f = msf_to_frames(token);
+ }
+ }
+ else if (trknum == 0 && outinfo.track[trknum].offset != 0)
+ {
+ /* the 1st track might have a length with no offset */
+ f = outinfo.track[trknum].offset / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+ outinfo.track[trknum].offset = 0;
+ }
+ else
+ {
+ /* guesstimate the track length? */
+ f = 0;
+ }
+
+ outtoc.tracks[trknum].frames = f;
+ }
+ else if (!strcmp(token, "TRACK"))
+ {
+ trknum++;
+
+ /* next token on the line is the track type */
+ TOKENIZE
+
+ outtoc.tracks[trknum].trktype = CD_TRACK_MODE1;
+ outtoc.tracks[trknum].datasize = 0;
+ outtoc.tracks[trknum].subtype = CD_SUB_NONE;
+ outtoc.tracks[trknum].subsize = 0;
+ outtoc.tracks[trknum].pgsub = CD_SUB_NONE;
+ outtoc.tracks[trknum].padframes = 0;
+
+ convert_type_string_to_track_info(token, &outtoc.tracks[trknum]);
+ if (outtoc.tracks[trknum].datasize == 0)
+ {
+ fclose(infile);
+ osd_printf_error("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
+
+ /* next (optional) token on the line is the subcode type */
+ TOKENIZE
+
+ convert_subtype_string_to_track_info(token, &outtoc.tracks[trknum]);
+ }
+ else if (!strcmp(token, "START"))
+ {
+ int frames;
+
+ /* get index */
+ TOKENIZE
+ frames = msf_to_frames(token);
+
+ outtoc.tracks[trknum].pregap = frames;
+ }
+ }
+
+ /* close the input TOC */
+ fclose(infile);
+
+ /* store the number of tracks found */
+ outtoc.numtrks = trknum + 1;
+ outtoc.numsessions = 1;
+
+ return std::error_condition();
+}