// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** cdrom.c Generic MAME CD-ROM utilties - build IDE and SCSI CD-ROMs on top of this **************************************************************************** IMPORTANT: "physical" block addresses are the actual addresses on the emulated CD. "chd" block addresses are the block addresses in the CHD file. Because we pad each track to a 4-frame boundry, these addressing schemes will differ after track 1! ***************************************************************************/ #include #include "cdrom.h" #include #include "chdcd.h" /*************************************************************************** DEBUGGING ***************************************************************************/ /** @brief The verbose. */ #define VERBOSE (0) #if VERBOSE /** * @def LOG(x) do * * @brief A macro that defines log. * * @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 */ }; /*************************************************************************** INLINE FUNCTIONS ***************************************************************************/ /*------------------------------------------------- physical_to_chd_lba - find the CHD LBA and the track number -------------------------------------------------*/ /** * @fn static inline UINT32 physical_to_chd_lba(cdrom_file *file, UINT32 physlba, UINT32 &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. */ static inline UINT32 physical_to_chd_lba(cdrom_file *file, UINT32 physlba, UINT32 &tracknum) { UINT32 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) { chdlba = physlba - file->cdtoc.tracks[track].physframeofs + file->cdtoc.tracks[track].chdframeofs; tracknum = track; return chdlba; } return physlba; } /*------------------------------------------------- logical_to_chd_lba - find the CHD LBA and the track number -------------------------------------------------*/ /** * @fn static inline UINT32 logical_to_chd_lba(cdrom_file *file, UINT32 loglba, UINT32 &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. */ static inline UINT32 logical_to_chd_lba(cdrom_file *file, UINT32 loglba, UINT32 &tracknum) { UINT32 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++) { if (loglba < file->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; tracknum = track; return chdlba; } } return loglba; } /*************************************************************************** BASE FUNCTIONALITY ***************************************************************************/ /** * @fn cdrom_file *cdrom_open(const char *inputfile) * * @brief Queries if a given cdrom open. * * @param inputfile The inputfile. * * @return null if it fails, else a cdrom_file*. */ cdrom_file *cdrom_open(const char *inputfile) { int i; cdrom_file *file; UINT32 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) { fprintf(stderr, "Error reading input file: %s\n", chd_file::error_string(err)); delete file; return nullptr; } /* fill in the data */ file->chd = nullptr; LOG(("CD has %d tracks\n", file->cdtoc.numtrks)); for (i = 0; i < file->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) { fprintf(stderr, "Unable to open file: %s\n", file->track_info.track[i].fname.c_str()); cdrom_close(file); return 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++) { file->cdtoc.tracks[i].physframeofs = physofs; file->cdtoc.tracks[i].chdframeofs = 0; file->cdtoc.tracks[i].logframeofs = logofs; // if the pregap sectors aren't in the track, add them to the track's logical length if (file->cdtoc.tracks[i].pgdatasize == 0) { logofs += file->cdtoc.tracks[i].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; } /*------------------------------------------------- cdrom_open - "open" a CD-ROM file from an already-opened CHD file -------------------------------------------------*/ /** * @fn cdrom_file *cdrom_open(chd_file *chd) * * @brief Queries if a given cdrom open. * * @param [in,out] chd If non-null, the chd. * * @return null if it fails, else a cdrom_file*. */ cdrom_file *cdrom_open(chd_file *chd) { int i; cdrom_file *file; UINT32 physofs, chdofs, logofs; chd_error err; /* punt if no CHD */ if (!chd) return nullptr; /* 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; /* read the CD-ROM metadata */ err = cdrom_parse_metadata(chd, &file->cdtoc); if (err != CHDERR_NONE) { delete file; return nullptr; } LOG(("CD has %d tracks\n", file->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++) { file->cdtoc.tracks[i].physframeofs = physofs; file->cdtoc.tracks[i].chdframeofs = chdofs; file->cdtoc.tracks[i].logframeofs = logofs; // if the pregap sectors aren't in the track, add them to the track's logical length if (file->cdtoc.tracks[i].pgdatasize == 0) { logofs += file->cdtoc.tracks[i].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; } /*------------------------------------------------- cdrom_close - "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) { if (file == nullptr) return; if (file->chd == nullptr) { for (int i = 0; i < file->cdtoc.numtrks; i++) { file->fhandle[i].reset(); } } delete file; } /*************************************************************************** CORE READ ACCESS ***************************************************************************/ /** * @fn chd_error read_partial_sector(cdrom_file *file, void *dest, UINT32 lbasector, UINT32 chdsector, UINT32 tracknum, UINT32 startoffs, UINT32 length) * * @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. * * @return The partial sector. */ chd_error read_partial_sector(cdrom_file *file, void *dest, UINT32 lbasector, UINT32 chdsector, UINT32 tracknum, UINT32 startoffs, UINT32 length, bool phys=false) { chd_error result = CHDERR_NONE; 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))) { //printf("PG missing sector: LBA %d, trklog %d\n", lbasector, file->cdtoc.tracks[tracknum].logframeofs); memset(dest, 0, length); return result; } } // if a CHD, just read if (file->chd != nullptr) { result = file->chd->read_bytes(UINT64(chdsector) * UINT64(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)) needswap = true; } else { // else read from the appropriate file util::core_file &srcfile = *file->fhandle[tracknum]; UINT64 sourcefileoffset = file->track_info.track[tracknum].offset; int bytespersector = file->cdtoc.tracks[tracknum].datasize + file->cdtoc.tracks[tracknum].subsize; sourcefileoffset += chdsector * bytespersector + startoffs; // printf("Reading sector %d from track %d at offset %lld\n", chdsector, tracknum, sourcefileoffset); srcfile.seek(sourcefileoffset, SEEK_SET); srcfile.read(dest, length); needswap = file->track_info.track[tracknum].swap; } if (needswap) { UINT8 *buffer = (UINT8 *)dest - startoffs; for (int swapindex = startoffs; swapindex < 2352; swapindex += 2 ) { int swaptemp = buffer[ swapindex ]; buffer[ swapindex ] = buffer[ swapindex + 1 ]; buffer[ swapindex + 1 ] = swaptemp; } } return result; } /*------------------------------------------------- cdrom_read_data - read one or more sectors from a CD-ROM -------------------------------------------------*/ /** * @fn UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 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. * @param phys true to physical. * * @return An UINT32. */ UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 datatype, bool phys) { if (file == nullptr) return 0; // compute CHD sector and tracknumber UINT32 tracknum = 0; UINT32 chdsector; if (phys) { chdsector = physical_to_chd_lba(file, lbasector, tracknum); } else { chdsector = logical_to_chd_lba(file, lbasector, tracknum); } /* copy out the requested sector */ UINT32 tracktype = file->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); } else { /* 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 2352 byte mode 1 raw sector from 2048 bytes of mode 1 data */ if ((datatype == CD_TRACK_MODE1_RAW) && (tracktype == CD_TRACK_MODE1)) { UINT8 *bufptr = (UINT8 *)buffer; UINT32 msf = lba_to_msf(lbasector); static const UINT8 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; 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 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 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); } LOG(("CDROM: Conversion from type %d to type %d not supported!\n", tracktype, datatype)); return 0; } } /*------------------------------------------------- cdrom_read_subcode - read subcode data for a sector -------------------------------------------------*/ /** * @fn UINT32 cdrom_read_subcode(cdrom_file *file, UINT32 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. */ UINT32 cdrom_read_subcode(cdrom_file *file, UINT32 lbasector, void *buffer, bool phys) { if (file == nullptr) return ~0; // compute CHD sector and tracknumber UINT32 tracknum = 0; UINT32 chdsector; if (phys) { chdsector = physical_to_chd_lba(file, lbasector, tracknum); } else { chdsector = logical_to_chd_lba(file, lbasector, tracknum); } if (file->cdtoc.tracks[tracknum].subsize == 0) return 0; // 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); } /*************************************************************************** HANDY UTILITIES ***************************************************************************/ /*------------------------------------------------- cdrom_get_track - get the track number for a physical frame number -------------------------------------------------*/ /** * @fn UINT32 cdrom_get_track(cdrom_file *file, UINT32 frame) * * @brief Cdrom get track. * * @param [in,out] file If non-null, the file. * @param frame The frame. * * @return An UINT32. */ UINT32 cdrom_get_track(cdrom_file *file, UINT32 frame) { UINT32 track = 0; if (file == nullptr) return ~0; /* convert to a CHD sector offset and get track information */ logical_to_chd_lba(file, frame, track); return track; } /*------------------------------------------------- cdrom_get_track_start - get the frame number that a track starts at -------------------------------------------------*/ /** * @fn UINT32 cdrom_get_track_start(cdrom_file *file, UINT32 track) * * @brief Cdrom get track start. * * @param [in,out] file If non-null, the file. * @param track The track. * * @return An UINT32. */ UINT32 cdrom_get_track_start(cdrom_file *file, UINT32 track) { 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 -------------------------------------------------*/ /** * @fn UINT32 cdrom_get_track_start_phys(cdrom_file *file, UINT32 track) * * @brief Cdrom get track start physical. * * @param [in,out] file If non-null, the file. * @param track The track. * * @return An UINT32. */ UINT32 cdrom_get_track_start_phys(cdrom_file *file, UINT32 track) { if (file == nullptr) return ~0; /* handle lead-out specially */ if (track == 0xaa) track = file->cdtoc.numtrks; return file->cdtoc.tracks[track].physframeofs; } /*************************************************************************** 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) { return 0x10; // audio track, subchannel is position } 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; return file->cdtoc.tracks[track].trktype; } /*------------------------------------------------- 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 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 *trktype, UINT32 *datasize) * * @brief Cdrom get information from type string. * * @param typestring The typestring. * @param [in,out] trktype If non-null, the trktype. * @param [in,out] datasize If non-null, the datasize. */ static void cdrom_get_info_from_type_string(const char *typestring, UINT32 *trktype, UINT32 *datasize) { if (!strcmp(typestring, "MODE1")) { *trktype = CD_TRACK_MODE1; *datasize = 2048; } else if (!strcmp(typestring, "MODE1/2048")) { *trktype = CD_TRACK_MODE1; *datasize = 2048; } else if (!strcmp(typestring, "MODE1_RAW")) { *trktype = CD_TRACK_MODE1_RAW; *datasize = 2352; } else if (!strcmp(typestring, "MODE1/2352")) { *trktype = CD_TRACK_MODE1_RAW; *datasize = 2352; } else if (!strcmp(typestring, "MODE2")) { *trktype = CD_TRACK_MODE2; *datasize = 2336; } else if (!strcmp(typestring, "MODE2/2336")) { *trktype = CD_TRACK_MODE2; *datasize = 2336; } else if (!strcmp(typestring, "MODE2_FORM1")) { *trktype = CD_TRACK_MODE2_FORM1; *datasize = 2048; } else if (!strcmp(typestring, "MODE2/2048")) { *trktype = CD_TRACK_MODE2_FORM1; *datasize = 2048; } else if (!strcmp(typestring, "MODE2_FORM2")) { *trktype = CD_TRACK_MODE2_FORM2; *datasize = 2324; } else if (!strcmp(typestring, "MODE2/2324")) { *trktype = CD_TRACK_MODE2_FORM2; *datasize = 2324; } else if (!strcmp(typestring, "MODE2_FORM_MIX")) { *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; *datasize = 2352; } else if (!strcmp(typestring, "MODE2/2352")) { *trktype = CD_TRACK_MODE2_RAW; *datasize = 2352; } else if (!strcmp(typestring, "AUDIO")) { *trktype = CD_TRACK_AUDIO; *datasize = 2352; } } /*------------------------------------------------- cdrom_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) * * @brief Cdrom 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) { cdrom_get_info_from_type_string(typestring, &info->trktype, &info->datasize); } /*------------------------------------------------- cdrom_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) * * @brief Cdrom 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) { cdrom_get_info_from_type_string(typestring, &info->pgtype, &info->pgdatasize); } /*------------------------------------------------- cdrom_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) * * @brief Cdrom 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) { if (!strcmp(typestring, "RW")) { info->subtype = CD_SUB_NORMAL; info->subsize = 96; } else if (!strcmp(typestring, "RW_RAW")) { info->subtype = CD_SUB_RAW; info->subsize = 96; } } /*------------------------------------------------- cdrom_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) * * @brief Cdrom 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) { if (!strcmp(typestring, "RW")) { info->pgsub = CD_SUB_NORMAL; info->pgsubsize = 96; } else if (!strcmp(typestring, "RW_RAW")) { info->pgsub = CD_SUB_RAW; info->pgsubsize = 96; } } /*------------------------------------------------- cdrom_get_type_string - get the string associated with the given type -------------------------------------------------*/ /** * @fn const char *cdrom_get_type_string(UINT32 trktype) * * @brief Cdrom get type string. * * @param trktype The trktype. * * @return null if it fails, else a char*. */ const char *cdrom_get_type_string(UINT32 trktype) { switch (trktype) { case CD_TRACK_MODE1: return "MODE1"; case CD_TRACK_MODE1_RAW: return "MODE1_RAW"; case CD_TRACK_MODE2: return "MODE2"; case CD_TRACK_MODE2_FORM1: return "MODE2_FORM1"; case CD_TRACK_MODE2_FORM2: return "MODE2_FORM2"; case CD_TRACK_MODE2_FORM_MIX: return "MODE2_FORM_MIX"; case CD_TRACK_MODE2_RAW: return "MODE2_RAW"; case CD_TRACK_AUDIO: return "AUDIO"; default: return "UNKNOWN"; } } /*------------------------------------------------- cdrom_get_subtype_string - get the string associated with the given subcode type -------------------------------------------------*/ /** * @fn const char *cdrom_get_subtype_string(UINT32 subtype) * * @brief Cdrom get subtype string. * * @param subtype The subtype. * * @retu
#!/usr/bin/python
##
## license:BSD-3-Clause
## copyright-holders:Vas Crabb

import os
import os.path
import re
import sys
import xml.sax
import xml.sax.saxutils
import zlib


# workaround for version incompatibility
if sys.version_info > (3, ):
    long = int


class ErrorHandler(object):
    def __init__(self, **kwargs):
        super(ErrorHandler, self).__init__(**kwargs)
        self.errors = 0
        self.warnings = 0

    def error(self, exception):
        self.errors += 1
        sys.stderr.write('error: %s' % (exception))

    def fatalError(self, exception):
        raise exception

    def warning(self, exception):
        self.warnings += 1
        sys.stderr.write('warning: %s' % (exception))


class Minifyer(object):
    def __init__(self, output, **kwargs):
        super(Minifyer, self).__init__(**kwargs)

        self.output = output
        self.incomplete_tag = False
        self.element_content = ''

    def setDocumentLocator(self, locator):
        pass

    def startDocument(self):
        self.output('<?xml version="1.0"?>')

    def endDocument(self):
        self.output('\n')

    def startElement(self, name, attrs):
        self.flushElementContent()
        if self.incomplete_tag:
            self.output('>')
        self.output('<%s' % (name))
        for name in attrs.getNames():
            self.output(' %s=%s' % (name, xml.sax.saxutils.quoteattr(attrs[name])))
        self.incomplete_tag = True

    def endElement(self, name):
        self.flushElementContent()
        if self.incomplete_tag:
            self.output('/>')
        else:
            self.output('</%s>' % (name))
        self.incomplete_tag = False

    def characters(self, content):
        self.element_content += content

    def ignorableWhitespace(self, whitespace):
        pass

    def processingInstruction(self, target, data):
        pass

    def flushElementContent(self):
        self.element_content = self.element_content.strip()
        if self.element_content:
            if self.incomplete_tag:
                self.output('>')
                self.incomplete_tag = False
            self.output(xml.sax.saxutils.escape(self.element_content))
            self.element_content = ''


class XmlError(Exception):
    pass


class LayoutChecker(Minifyer):
    BADTAGPATTERN = re.compile('[^abcdefghijklmnopqrstuvwxyz0123456789_.:^$]')
    VARPATTERN = re.compile('^.*~[0-9A-Za-z_]+~.*$')
    FLOATCHARS = re.compile('^.*[.eE].*$')
    SHAPES = frozenset(('disk', 'dotmatrix', 'dotmatrix5dot', 'dotmatrixdot', 'led14seg', 'led14segsc', 'led16seg', 'led16segsc', 'led7seg', 'led8seg_gts1', 'rect'))
    ORIENTATIONS = frozenset((0, 90, 180, 270))
    YESNO = frozenset(('yes', 'no'))
    BLENDMODES = frozenset(('none', 'alpha', 'multiply', 'add'))

    def __init__(self, output, **kwargs):
        super(LayoutChecker, self).__init__(output=output, **kwargs)
        self.locator = None
        self.errors = 0
        self.elements = { }
        self.groups = { }
        self.views = { }
        self.referenced_elements = { }
        self.referenced_groups = { }
        self.group_collections = { }
        self.current_collections = None

    def formatLocation(self):
        return '%s:%d:%d' % (self.locator.getSystemId(), self.locator.getLineNumber(), self.locator.getColumnNumber())

    def handleError(self, msg):
        self.errors += 1
        sys.stderr.write('error: %s: %s\n' % (self.formatLocation(), msg))

    def checkIntAttribute(self, name, attrs, key, default):
        if key not in attrs:
            return default
        val = attrs[key]
        if self.VARPATTERN.match(val):
            return None
        base = 10
        offs = 0
        if (len(val) >= 1) and ('$' == val[0]):
            base = 16
            offs = 1
        elif (len(val) >= 2) and ('0' == val[0]) and (('x' == val[1]) or ('X' == val[1])):
            base = 16
            offs = 2
        elif (len(val) >= 1) and ('#' == val[0]):
            offs = 1
        try:
            return int(val[offs:], base)
        except:
            self.handleError('Element %s attribute %s "%s" is not an integer' % (name, key, val))
            return None

    def checkFloatAttribute(self, name, attrs, key, default):
        if key not in attrs:
            return default
        val = attrs[key]
        if self.VARPATTERN.match(val):
            return None
        try:
            return float(val)
        except:
            self.handleError('Element %s attribute %s "%s" is not a floating point number' % (name, key, val))
            return None

    def checkNumericAttribute(self, name, attrs, key, default):
        if key not in attrs:
            return default
        val = attrs[key]
        if self.VARPATTERN.match(val):
            return None
        base = 0
        offs = 0
        try:
            if (len(val) >= 1) and ('$' == val[0]):
                base = 16
                offs = 1
            elif (len(val) >= 2) and ('0' == val[0]) and (('x' == val[1]) or ('X' == val[1])):
                base = 16
                offs = 2
            elif (len(val) >= 1) and ('#' == val[0]):
                base = 10
                offs = 1
            elif self.FLOATCHARS.match(val):
                return float(val)
            return int(val[offs:], base)
        except:
            self.handleError('Element %s attribute %s "%s" is not a number' % (name, key, val))
            return None

    def checkParameter(self, attrs):
        if 'name' not in attrs:
            self.handleError('Element param missing attribute name')
        else:
            name = attrs['name']
        self.checkNumericAttribute('param', attrs, 'increment', None)
        lshift = self.checkIntAttribute('param', attrs, 'lshift', None)
        if (lshift is not None) and (0 > lshift):
            self.handleError('Element param attribute lshift "%s" is negative' % (attrs['lshift'], ))
        rshift = self.checkIntAttribute('param', attrs, 'rshift', None)
        if (rshift is not None) and (0 > rshift):
            self.handleError('Element param attribute rshift "%s" is negative' % (attrs['rshift'], ))
        if self.repeat_depth and self.repeat_depth[-1]:
            if 'start' in attrs:
                if 'value' in attrs:
                    self.handleError('Element param has both start and value attributes')
                if 'name' in attrs:
                    if name not in self.variable_scopes[-1]:
                        self.variable_scopes[-1][name] = True
                    elif not self.VARPATTERN.match(name):
                        self.handleError('Generator parameter "%s" redefined'