// license:BSD-3-Clause // copyright-holders:Aaron Giles,R. Belmont /*************************************************************************** cdrom.h Generic MAME cd-rom implementation ***************************************************************************/ #ifndef MAME_LIB_UTIL_CDROM_H #define MAME_LIB_UTIL_CDROM_H #pragma once #include "chd.h" #include "ioprocs.h" #include "osdcore.h" #include #include #include #include class cdrom_file { public: // tracks are padded to a multiple of this many frames static constexpr uint32_t TRACK_PADDING = 4; static constexpr uint32_t MAX_TRACKS = 99; /* AFAIK the theoretical limit */ static constexpr uint32_t MAX_SECTOR_DATA = 2352; static constexpr uint32_t MAX_SUBCODE_DATA = 96; static constexpr uint32_t MAX_INDEX = 99; static constexpr uint32_t FRAME_SIZE = MAX_SECTOR_DATA + MAX_SUBCODE_DATA; static constexpr uint32_t FRAMES_PER_HUNK = 8; static constexpr uint32_t METADATA_WORDS = 1 + MAX_TRACKS * 6; enum { CD_TRACK_MODE1 = 0, /* mode 1 2048 bytes/sector */ CD_TRACK_MODE1_RAW, /* mode 1 2352 bytes/sector */ CD_TRACK_MODE2, /* mode 2 2336 bytes/sector */ CD_TRACK_MODE2_FORM1, /* mode 2 2048 bytes/sector */ CD_TRACK_MODE2_FORM2, /* mode 2 2324 bytes/sector */ CD_TRACK_MODE2_FORM_MIX, /* mode 2 2336 bytes/sector */ CD_TRACK_MODE2_RAW, /* mode 2 2352 bytes/sector */ CD_TRACK_AUDIO, /* redbook audio track 2352 bytes/sector (588 samples) */ CD_TRACK_RAW_DONTCARE /* special flag for cdrom_read_data: just return me whatever is there */ }; enum { CD_SUB_NORMAL = 0, /* "cooked" 96 bytes per sector */ CD_SUB_RAW, /* raw uninterleaved 96 bytes per sector */ CD_SUB_NONE /* no subcode data stored */ }; enum { CD_FLAG_GDROM = 0x00000001, // disc is a GD-ROM, all tracks should be stored with GD-ROM metadata CD_FLAG_GDROMLE = 0x00000002, // legacy GD-ROM, with little-endian CDDA data CD_FLAG_MULTISESSION = 0x00000004, // multisession CD-ROM }; enum { CD_FLAG_CONTROL_PREEMPHASIS = 1, CD_FLAG_CONTROL_DIGITAL_COPY_PERMITTED = 2, CD_FLAG_CONTROL_DATA_TRACK = 4, CD_FLAG_CONTROL_4CH = 8, }; enum { CD_FLAG_ADR_START_TIME = 1, CD_FLAG_ADR_CATALOG_CODE, CD_FLAG_ADR_ISRC_CODE, }; struct track_info { /* fields used by CHDMAN and in MAME */ uint32_t trktype; /* track type */ uint32_t subtype; /* subcode data type */ uint32_t datasize; /* size of data in each sector of this track */ uint32_t subsize; /* size of subchannel data in each sector of this track */ uint32_t frames; /* number of frames in this track */ uint32_t extraframes; /* number of "spillage" frames in this track */ uint32_t pregap; /* number of pregap frames */ uint32_t postgap; /* number of postgap frames */ uint32_t pgtype; /* type of sectors in pregap */ uint32_t pgsub; /* type of subchannel data in pregap */ uint32_t pgdatasize; /* size of data in each sector of the pregap */ uint32_t pgsubsize; /* size of subchannel data in each sector of the pregap */ uint32_t control_flags; /* metadata flags associated with each track */ uint32_t session; /* session number */ /* fields used in CHDMAN only */ uint32_t padframes; /* number of frames of padding to add to the end of the track; needed for GDI */ uint32_t splitframes; /* number of frames from the next file to add to the end of the current track after padding; needed for Redump split-bin GDI */ /* fields used in MAME/MESS only */ uint32_t logframeofs; /* logical frame of actual track data - offset by pregap size if pregap not physically present */ uint32_t physframeofs; /* physical frame of actual track data in CHD data */ uint32_t chdframeofs; /* frame number this track starts at on the CHD */ uint32_t logframes; /* number of frames from logframeofs until end of track data */ /* fields used in multi-cue GDI */ uint32_t multicuearea; }; struct toc { uint32_t numtrks; /* number of tracks */ uint32_t numsessions; /* number of sessions */ uint32_t flags; /* see FLAG_ above */ track_info tracks[MAX_TRACKS + 1]; }; struct track_input_entry { track_input_entry() { reset(); } void reset() { fname.clear(); offset = 0; leadin = leadout = -1; swap = false; std::fill(std::begin(idx), std::end(idx), -1); } std::string fname; // filename for each track uint32_t offset; // offset in the data file for each track bool swap; // data needs to be byte swapped int32_t idx[MAX_INDEX + 1]; int32_t leadin, leadout; // TODO: these should probably be their own tracks entirely }; struct track_input_info { void reset() { for (auto & elem : track) elem.reset(); } track_input_entry track[MAX_TRACKS]; }; cdrom_file(chd_file *chd); cdrom_file(std::string_view inputfile); ~cdrom_file(); /* core read access */ bool read_data(uint32_t lbasector, void *buffer, uint32_t datatype, bool phys=false); bool read_subcode(uint32_t lbasector, void *buffer, bool phys=false); /* handy utilities */ uint32_t get_track(uint32_t frame) const; uint32_t get_track_start(uint32_t track) const {return cdtoc.tracks[track == 0xaa ? cdtoc.numtrks : track].logframeofs; } uint32_t get_track_start_phys(uint32_t track) const { return cdtoc.tracks[track == 0xaa ? cdtoc.numtrks : track].physframeofs; } uint32_t get_track_index(uint32_t frame) const; /* TOC utilities */ static std::error_condition parse_nero(std::string_view tocfname, toc &outtoc, track_input_info &outinfo); static std::error_condition parse_iso(std::string_view tocfname, toc &outtoc, track_input_info &outinfo); static std::error_condition parse_gdi(std::string_view tocfname, toc &outtoc, track_input_info &outinfo); static std::error_condition parse_cue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo); static bool is_gdicue(std::string_view tocfname); static std::error_condition parse_toc(std::string_view tocfname, toc &outtoc, track_input_info &outinfo); int get_last_session() const { return cdtoc.numsessions ? cdtoc.numsessions : 1; } int get_last_track() const { return cdtoc.numtrks; } int get_adr_control(int track) const { if (track == 0xaa) track = get_last_track() - 1; // use last track's flags int adrctl = (CD_FLAG_ADR_START_TIME << 4) | (cdtoc.tracks[track].control_flags & 0x0f); if (cdtoc.tracks[track].trktype != CD_TRACK_AUDIO) adrctl |= CD_FLAG_CONTROL_DATA_TRACK; return adrctl; } int get_track_type(int track) const { return cdtoc.tracks[track].trktype; } const toc &get_toc() const { return cdtoc; } /* extra utilities */ static void convert_type_string_to_track_info(const char *typestring, track_info *info); static void convert_type_string_to_pregap_info(const char *typestring, track_info *info); static void convert_subtype_string_to_track_info(const char *typestring, track_info *info); static void convert_subtype_string_to_pregap_info(const char *typestring, track_info *info); static const char *get_type_string(uint32_t trktype); static const char *get_subtype_string(uint32_t subtype); static std::error_condition parse_metadata(chd_file *chd, toc &toc); static std::error_condition write_metadata(chd_file *chd, const toc &toc); bool is_gdrom() const { return cdtoc.flags & (CD_FLAG_GDROM|CD_FLAG_GDROMLE); } // ECC utilities static bool ecc_verify(const uint8_t *sector); static void ecc_generate(uint8_t *sector); static void ecc_clear(uint8_t *sector); static inline uint32_t msf_to_lba(uint32_t msf) { return ( ((msf&0x00ff0000)>>16) * 60 * 75) + (((msf&0x0000ff00)>>8) * 75) + ((msf&0x000000ff)>>0); } static inline uint32_t lba_to_msf(uint32_t lba) { uint8_t m, s, f; m = lba / (60 * 75); lba -= m * (60 * 75); s = lba / 75; f = lba % 75; return ((m / 10) << 20) | ((m % 10) << 16) | ((s / 10) << 12) | ((s % 10) << 8) | ((f / 10) << 4) | ((f % 10) << 0); } // segacd needs it like this.. investigate // Angelo also says PCE tracks often start playing at the // wrong address.. related? static inline uint32_t lba_to_msf_alt(int lba) { uint32_t ret = 0; ret |= ((lba / (60 * 75))&0xff)<<16; ret |= (((lba / 75) % 60)&0xff)<<8; ret |= ((lba % 75)&0xff)<<0; return ret; } private: enum gdi_area { SINGLE_DENSITY, HIGH_DENSITY }; enum gdi_pattern { TYPE_UNKNOWN = 0, TYPE_I, TYPE_II, TYPE_III, TYPE_III_SPLIT }; /** @brief offset within sector. */ static constexpr int SYNC_OFFSET = 0x000; /** @brief 12 bytes. */ static constexpr int SYNC_NUM_BYTES = 12; /** @brief offset within sector. */ static constexpr int MODE_OFFSET = 0x00f; /** @brief offset within sector. */ static constexpr int ECC_P_OFFSET = 0x81c; /** @brief 2 lots of 86. */ static constexpr int ECC_P_NUM_BYTES = 86; /** @brief 24 bytes each. */ static constexpr int ECC_P_COMP = 24; /** @brief The ECC q offset. */ static constexpr int ECC_Q_OFFSET = ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES; /** @brief 2 lots of 52. */ static constexpr int ECC_Q_NUM_BYTES = 52; /** @brief 43 bytes each. */ static constexpr int ECC_Q_COMP = 43; // ECC tables static const uint8_t ecclow[256]; static const uint8_t ecchigh[256]; static const uint16_t poffsets[ECC_P_NUM_BYTES][ECC_P_COMP]; static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP]; /** @brief The chd. */ chd_file * chd; /* CHD file */ /** @brief The cdtoc. */ toc cdtoc; /* TOC for the CD */ /** @brief Information describing the track. */ track_input_info cdtrack_info; /* track info */ /** @brief The fhandle[ CD maximum tracks]. */ util::random_read::ptr fhandle[MAX_TRACKS];/* file handle */ inline uint32_t physical_to_chd_lba(uint32_t physlba, uint32_t &tracknum) const; inline uint32_t logical_to_chd_lba(uint32_t physlba, uint32_t &tracknum) const; static void get_info_from_type_string(const char *typestring, uint32_t *trktype, uint32_t *datasize); static uint8_t ecc_source_byte(const uint8_t *sector, uint32_t offset); static void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t &val1, uint8_t &val2); 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); static std::string get_file_path(std::string &path); static uint64_t get_file_size(std::string_view filename); static int tokenize(const char *linebuffer, int i, int linebuffersize, char *token, int tokensize); static int msf_to_frames(const char *token); static uint32_t parse_wav_sample(std::string_view filename, uint32_t *dataoffs); static uint16_t read_uint16(FILE *infile); static uint32_t read_uint32(FILE *infile); static uint64_t read_uint64(FILE *infile); }; #endif // MAME_LIB_UTIL_CDROM_H