From 2c8de2ee29788764415c87250164f4a4bb223eac Mon Sep 17 00:00:00 2001 From: 987123879113 <63495610+987123879113@users.noreply.github.com> Date: Sat, 9 Nov 2024 01:23:07 +0900 Subject: -util/cdrom.cpp: Fixed issues with CUE, TOC and GDI parsers. (#12948) * Rewrote GDI parser to be more robust and identify more kinds of invalid input. * Don't ignore the last line in CUE and TOC files if there is no terminating newline. * Use osd_printf_ family functions for output. -tools/chdman.cpp: Added a sink for osd_printf_ family function output. --- src/lib/util/cdrom.cpp | 935 ++++++++++++++++++++++++++----------------------- src/tools/chdman.cpp | 45 +++ 2 files changed, 551 insertions(+), 429 deletions(-) diff --git a/src/lib/util/cdrom.cpp b/src/lib/util/cdrom.cpp index 8b60db1ae62..35cbe2bbf34 100644 --- a/src/lib/util/cdrom.cpp +++ b/src/lib/util/cdrom.cpp @@ -200,7 +200,7 @@ cdrom_file::cdrom_file(std::string_view inputfile) if (EXTRA_VERBOSE) { - printf("session %d track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d logframes %d pad %d\n", + 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, @@ -302,7 +302,7 @@ cdrom_file::cdrom_file(chd_file *_chd) if (EXTRA_VERBOSE) { - printf("session %d track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d logframes %d pad %d\n", + 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, @@ -380,7 +380,7 @@ std::error_condition cdrom_file::read_partial_sector(void *dest, uint32_t lbasec if ((cdtoc.tracks[tracknum].pgdatasize == 0) && (lbasector < cdtoc.tracks[tracknum].logframeofs)) { if (EXTRA_VERBOSE) - printf("PG missing sector: LBA %d, trklog %d\n", lbasector, cdtoc.tracks[tracknum].logframeofs); + osd_printf_verbose("PG missing sector: LBA %d, trklog %d\n", lbasector, cdtoc.tracks[tracknum].logframeofs); memset(dest, 0, length); return result; } @@ -416,7 +416,7 @@ std::error_condition cdrom_file::read_partial_sector(void *dest, uint32_t lbasec sourcefileoffset += chdsector * bytespersector + startoffs; if (EXTRA_VERBOSE) - printf("Reading %u bytes from sector %d from track %d at offset %lu\n", (unsigned)length, chdsector, tracknum + 1, (unsigned long)sourcefileoffset); + osd_printf_verbose("Reading %u bytes from sector %d from track %d at offset %lu\n", (unsigned)length, chdsector, tracknum + 1, (unsigned long)sourcefileoffset); result = srcfile.seek(sourcefileoffset, SEEK_SET); size_t actual; @@ -996,7 +996,7 @@ std::error_condition cdrom_file::parse_metadata(chd_file *chd, toc &toc) 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 oldmetadata; @@ -1624,7 +1624,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao std::error_condition const filerr = osd_file::open(fname, OPEN_FLAG_READ, file, fsize); if (filerr) { - printf("ERROR: could not open (%s)\n", fname.c_str()); + osd_printf_error("ERROR: could not open (%s)\n", fname); return 0; } @@ -1633,12 +1633,12 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao offset += actual; if (offset < 4) { - printf("ERROR: unexpected RIFF offset %lu (%s)\n", offset, fname.c_str()); + osd_printf_error("ERROR: unexpected RIFF offset %lu (%s)\n", offset, fname); return 0; } if (memcmp(&buf[0], "RIFF", 4) != 0) { - printf("ERROR: could not find RIFF header (%s)\n", fname.c_str()); + osd_printf_error("ERROR: could not find RIFF header (%s)\n", fname); return 0; } @@ -1647,7 +1647,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao offset += actual; if (offset < 8) { - printf("ERROR: unexpected size offset %lu (%s)\n", offset, fname.c_str()); + osd_printf_error("ERROR: unexpected size offset %lu (%s)\n", offset, fname); return 0; } filesize = little_endianize_int32(filesize); @@ -1657,12 +1657,12 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao offset += actual; if (offset < 12) { - printf("ERROR: unexpected WAVE offset %lu (%s)\n", offset, fname.c_str()); + osd_printf_error("ERROR: unexpected WAVE offset %lu (%s)\n", offset, fname); return 0; } if (memcmp(&buf[0], "WAVE", 4) != 0) { - printf("ERROR: could not find WAVE header (%s)\n", fname.c_str()); + osd_printf_error("ERROR: could not find WAVE header (%s)\n", fname); return 0; } @@ -1681,7 +1681,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao offset += length; if (offset >= filesize) { - printf("ERROR: could not find fmt tag (%s)\n", fname.c_str()); + osd_printf_error("ERROR: could not find fmt tag (%s)\n", fname); return 0; } } @@ -1692,7 +1692,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao temp16 = little_endianize_int16(temp16); if (temp16 != 1) { - printf("ERROR: unsupported format %u - only PCM is supported (%s)\n", temp16, fname.c_str()); + osd_printf_error("ERROR: unsupported format %u - only PCM is supported (%s)\n", temp16, fname); return 0; } @@ -1702,7 +1702,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao temp16 = little_endianize_int16(temp16); if (temp16 != 2) { - printf("ERROR: unsupported number of channels %u - only stereo is supported (%s)\n", temp16, fname.c_str()); + osd_printf_error("ERROR: unsupported number of channels %u - only stereo is supported (%s)\n", temp16, fname); return 0; } @@ -1712,7 +1712,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao rate = little_endianize_int32(rate); if (rate != 44100) { - printf("ERROR: unsupported samplerate %u - only 44100 is supported (%s)\n", rate, fname.c_str()); + osd_printf_error("ERROR: unsupported samplerate %u - only 44100 is supported (%s)\n", rate, fname); return 0; } @@ -1726,7 +1726,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao bits = little_endianize_int16(bits); if (bits != 16) { - printf("ERROR: unsupported bits/sample %u - only 16 is supported (%s)\n", bits, fname.c_str()); + osd_printf_error("ERROR: unsupported bits/sample %u - only 16 is supported (%s)\n", bits, fname); return 0; } @@ -1748,7 +1748,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao offset += length; if (offset >= filesize) { - printf("ERROR: could not find data tag (%s)\n", fname.c_str()); + osd_printf_error("ERROR: could not find data tag (%s)\n", fname); return 0; } } @@ -1756,7 +1756,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao /* if there was a 0 length data block, we're done */ if (length == 0) { - printf("ERROR: empty data block (%s)\n", fname.c_str()); + osd_printf_error("ERROR: empty data block (%s)\n", fname); return 0; } @@ -1866,7 +1866,7 @@ std::error_condition cdrom_file::parse_nero(std::string_view tocfname, toc &outt if (memcmp(buffer, "NER5", 4)) { - printf("ERROR: Not a Nero 5.5 or later image!\n"); + osd_printf_error("ERROR: Not a Nero 5.5 or later image!\n"); fclose(infile); return chd_file::error::UNSUPPORTED_FORMAT; } @@ -1875,7 +1875,7 @@ std::error_condition cdrom_file::parse_nero(std::string_view tocfname, toc &outt if ((buffer[7] != 0) || (buffer[6] != 0) || (buffer[5] != 0) || (buffer[4] != 0)) { - printf("ERROR: File size is > 4GB, this version of CHDMAN cannot handle it."); + osd_printf_error("ERROR: File size is > 4GB, this version of CHDMAN cannot handle it."); fclose(infile); return chd_file::error::UNSUPPORTED_FORMAT; } @@ -1932,12 +1932,12 @@ std::error_condition cdrom_file::parse_nero(std::string_view tocfname, toc &outt break; case 0x0300: // Mode 2 Form 1 - printf("ERROR: Mode 2 Form 1 tracks not supported\n"); + osd_printf_error("ERROR: Mode 2 Form 1 tracks not supported\n"); fclose(infile); return chd_file::error::UNSUPPORTED_FORMAT; case 0x0500: // raw data - printf("ERROR: Raw data tracks not supported\n"); + osd_printf_error("ERROR: Raw data tracks not supported\n"); fclose(infile); return chd_file::error::UNSUPPORTED_FORMAT; @@ -1952,22 +1952,22 @@ std::error_condition cdrom_file::parse_nero(std::string_view tocfname, toc &outt break; case 0x0f00: // raw data with sub-channel - printf("ERROR: Raw data tracks with sub-channel not supported\n"); + 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 - printf("ERROR: Audio tracks with sub-channel not supported\n"); + 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 - printf("ERROR: Raw Mode 2 Form 1 tracks with sub-channel not supported\n"); + 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: - printf("ERROR: Unknown track type %x, contact MAMEDEV!\n", mode); + osd_printf_error("ERROR: Unknown track type %x, contact MAMEDEV!\n", mode); fclose(infile); return chd_file::error::UNSUPPORTED_FORMAT; } @@ -2066,7 +2066,7 @@ std::error_condition cdrom_file::parse_iso(std::string_view tocfname, toc &outto outtoc.tracks[0].datasize = 2352; outinfo.track[0].swap = false; } else { - printf("ERROR: Unrecognized track type\n"); + osd_printf_error("ERROR: Unrecognized track type\n"); return chd_file::error::UNSUPPORTED_FORMAT; } @@ -2104,7 +2104,9 @@ std::error_condition cdrom_file::parse_iso(std::string_view tocfname, toc &outto std::error_condition cdrom_file::parse_gdi(std::string_view tocfname, toc &outtoc, track_input_info &outinfo) { - int i, numtracks; + char token[512]; + int i = 0; + int trackcnt = 0; std::string path = std::string(tocfname); @@ -2125,89 +2127,170 @@ std::error_condition cdrom_file::parse_gdi(std::string_view tocfname, toc &outto char linebuffer[512]; memset(linebuffer, 0, sizeof(linebuffer)); - fgets(linebuffer,511,infile); - numtracks=atoi(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; + } - for(i=0;i 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; + } - trknum=atoi(tok)-1; + 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; + 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; - tok=strtok(nullptr," "); - outtoc.tracks[trknum].physframeofs=atoi(tok); + TOKENIZE + if (token[0]) + paramcnt++; + outtoc.tracks[trknum].physframeofs = atoi(token); - tok=strtok(nullptr," "); - trktype=atoi(tok); + TOKENIZE + if (token[0]) + paramcnt++; + const int trktype = atoi(token); - tok=strtok(nullptr," "); - trksize=atoi(tok); + TOKENIZE + if (token[0]) + paramcnt++; + const int trksize = atoi(token); - if(trktype==4 && trksize==2352) + if (trktype == 4 && trksize == 2352) { - outtoc.tracks[trknum].trktype=CD_TRACK_MODE1_RAW; - outtoc.tracks[trknum].datasize=2352; + outtoc.tracks[trknum].trktype = CD_TRACK_MODE1_RAW; + outtoc.tracks[trknum].datasize = 2352; } - if(trktype==4 && trksize==2048) + else if (trktype == 4 && trksize == 2048) { - outtoc.tracks[trknum].trktype=CD_TRACK_MODE1; - outtoc.tracks[trknum].datasize=2048; + outtoc.tracks[trknum].trktype = CD_TRACK_MODE1; + outtoc.tracks[trknum].datasize = 2048; } - if(trktype==0) + else if (trktype == 0) { - outtoc.tracks[trknum].trktype=CD_TRACK_AUDIO; - outtoc.tracks[trknum].datasize=2352; + 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; + } - std::string name; + // skip to start of next token + int pi = i; + while (pi < std::size(linebuffer) && isspace((uint8_t)linebuffer[pi])) + pi++; - tok=strtok(nullptr," "); - name = tok; - if (tok[0]=='"') { - do { - tok=strtok(nullptr," "); - if (tok!=nullptr) { - name += " "; - name += tok; - } - } while(tok!=nullptr && (strrchr(tok,'"')-tok !=(strlen(tok)-1))); - strdelchr(name,'"'); + 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; } - outinfo.track[trknum].fname.assign(path).append(name); - sz = get_file_size(outinfo.track[trknum].fname); + TOKENIZE + if (token[0]) + paramcnt++; + + outinfo.track[trknum].fname.assign(path).append(token); - outtoc.tracks[trknum].frames = sz/trksize; + 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) { - int dif=outtoc.tracks[trknum].physframeofs-(outtoc.tracks[trknum-1].frames+outtoc.tracks[trknum-1].physframeofs); + 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(i=0; i < numtracks; i++) + for(int i = 0; i < numtracks; i++) { - printf("%s %d %d %d (true %d)\n", outinfo.track[i].fname.c_str(), outtoc.tracks[i].frames, outtoc.tracks[i].padframes, outtoc.tracks[i].physframeofs, outtoc.tracks[i].frames - outtoc.tracks[i].padframes); + 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 */ @@ -2283,270 +2366,272 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto if (!fgets(linebuffer, 511, infile)) break; - /* if EOF didn't hit, keep going */ - if (!feof(infile)) - { - i = 0; + 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++; + TOKENIZE - if (!strncmp(linebuffer+i, "SESSION", 7)) - { - /* IsoBuster extension */ - TOKENIZE + if (!strcmp(token, "REM")) + { + /* skip to actual data of REM command */ + while (i < std::size(linebuffer) && isspace((uint8_t)linebuffer[i])) + i++; - /* get the session number */ - TOKENIZE + if (!strncmp(linebuffer+i, "SESSION", 7)) + { + /* IsoBuster extension */ + TOKENIZE - sessionnum = strtoul(token, nullptr, 10) - 1; + /* get the session number */ + TOKENIZE - 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 + sessionnum = strtoul(token, nullptr, 10) - 1; - 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 + 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 - /* 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 + 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 - Redump and DiscImageCreator (multiple bins) - Lead-out time is the duration of just the lead-out - */ - 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 - /* 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 + Redump and DiscImageCreator (multiple bins) - Lead-out time is the duration of just the lead-out + */ + 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; - } + /* get lead-out time */ + TOKENIZE + int leadout_offset = msf_to_frames(token); + outinfo.track[trknum].leadout = leadout_offset; } - else if (!strcmp(token, "FILE")) + else if (!strncmp(linebuffer+i, "LEAD-IN", 7)) { - /* found the data file for a track */ + /* + 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 - /* keep the filename */ - std::string prevfname = lastfname; + /* 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); - if (prevfname.length() > 0 && lastfname.compare(prevfname) != 0) - is_multibin = true; + is_multibin = !prevfname.empty() && lastfname != prevfname; + } + else + { + lastfname.assign(path).append(token); + } - /* get the file type */ - TOKENIZE + /* 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); - printf("ERROR: couldn't read [%s] or not a valid .WAV\n", lastfname.c_str()); - return chd_file::error::INVALID_DATA; - } - } - else + 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); - printf("ERROR: Unhandled track type %s\n", token); - return chd_file::error::UNSUPPORTED_FORMAT; + osd_printf_error("ERROR: couldn't read [%s] or not a valid .WAV\n", lastfname); + return chd_file::error::INVALID_DATA; } } - else if (!strcmp(token, "TRACK")) + else { - /* get the track number */ - TOKENIZE - trknum = strtoul(token, nullptr, 10) - 1; + 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 + /* 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); + 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; + 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 (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; - } + 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 */ + outinfo.track[trknum].fname.assign(lastfname); /* default filename to the last one */ - if (EXTRA_VERBOSE) + if (EXTRA_VERBOSE) + { + if (is_gdrom) { - if (is_gdrom) - { - printf("trk %d: fname %s offset %d area %d\n", trknum, outinfo.track[trknum].fname.c_str(), outinfo.track[trknum].offset, outtoc.tracks[trknum].multicuearea); - } - else - { - printf("trk %d: fname %s offset %d\n", trknum, outinfo.track[trknum].fname.c_str(), outinfo.track[trknum].offset); - } + 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); } - - convert_type_string_to_track_info(token, &outtoc.tracks[trknum]); - if (outtoc.tracks[trknum].datasize == 0) + else { - fclose(infile); - printf("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token); - return chd_file::error::UNSUPPORTED_FORMAT; + osd_printf_verbose("trk %d: fname %s offset %d\n", trknum, outinfo.track[trknum].fname, outinfo.track[trknum].offset); } - - /* 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")) + + convert_type_string_to_track_info(token, &outtoc.tracks[trknum]); + if (outtoc.tracks[trknum].datasize == 0) { - int idx, frames; + fclose(infile); + osd_printf_error("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token); + return chd_file::error::UNSUPPORTED_FORMAT; + } - /* get index number */ - TOKENIZE - idx = strtoul(token, nullptr, 10); + /* next (optional) token on the line is the subcode type */ + TOKENIZE - /* get index */ - TOKENIZE - frames = msf_to_frames(token); + convert_subtype_string_to_track_info(token, &outtoc.tracks[trknum]); + } + else if (!strcmp(token, "INDEX")) + { + int idx, frames; - if (idx < 0 || idx > MAX_INDEX) - { - printf("ERROR: encountered invalid index %d\n", idx); - return chd_file::error::INVALID_DATA; - } + /* 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; + outinfo.track[trknum].idx[idx] = frames; - if (idx == 1) + if (idx == 1) + { + if (outtoc.tracks[trknum].pregap == 0 && outinfo.track[trknum].idx[0] != -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; - } + 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; + } + else if (!strcmp(token, "PREGAP")) + { + int frames; - /* get index */ - TOKENIZE - frames = msf_to_frames(token); + /* get index */ + TOKENIZE + frames = msf_to_frames(token); - outtoc.tracks[trknum].pregap = frames; - } - else if (!strcmp(token, "POSTGAP")) - { - int frames; + outtoc.tracks[trknum].pregap = frames; + } + else if (!strcmp(token, "POSTGAP")) + { + int frames; - /* get index */ - TOKENIZE - frames = msf_to_frames(token); + /* get index */ + TOKENIZE + frames = msf_to_frames(token); - outtoc.tracks[trknum].postgap = frames; - } - else if (!strcmp(token, "FLAGS")) - { - outtoc.tracks[trknum].control_flags = 0; + 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; + /* 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 + TOKENIZE - if (i == last_idx) - break; + 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; - } + 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; } } } @@ -2566,7 +2651,7 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto if (outinfo.track[trknum].idx[1] == -1) { /* index 1 should always be set */ - printf("ERROR: track %d is missing INDEX 01 marker\n", trknum+1); + osd_printf_error("ERROR: track %d is missing INDEX 01 marker\n", trknum+1); return chd_file::error::INVALID_DATA; } @@ -2586,7 +2671,7 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto tlen = get_file_size(outinfo.track[trknum].fname); if (tlen == 0) { - printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str()); + osd_printf_error("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname); return std::errc::no_such_file_or_directory; } @@ -2600,7 +2685,7 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto if (outtoc.tracks[trknum].frames == 0) { - printf("ERROR: unable to determine size of track %d, missing INDEX 01 markers?\n", trknum+1); + osd_printf_error("ERROR: unable to determine size of track %d, missing INDEX 01 markers?\n", trknum+1); return chd_file::error::INVALID_DATA; } @@ -2616,7 +2701,7 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto tlen = get_file_size(outinfo.track[trknum].fname); if (tlen == 0) { - printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str()); + osd_printf_error("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname); return std::errc::no_such_file_or_directory; } @@ -2725,7 +2810,7 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto { for (trknum = 0; trknum < outtoc.numtrks; trknum++) { - printf("session %d trk %d: %d frames @ offset %d, pad=%d, split=%d, area=%d, phys=%d, pregap=%d, pgtype=%d, pgdatasize=%d, idx0=%d, idx1=%d, dataframes=%d\n", + 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, @@ -2785,24 +2870,20 @@ bool cdrom_file::is_gdicue(std::string_view tocfname) if (!fgets(linebuffer, 511, infile)) break; - /* if EOF didn't hit, keep going */ - if (!feof(infile)) - { - int i = 0; + int i = 0; - TOKENIZE + 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; - } + 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; } } @@ -2879,163 +2960,159 @@ std::error_condition cdrom_file::parse_toc(std::string_view tocfname, toc &outto if (!fgets(linebuffer, 511, infile)) break; - /* if EOF didn't hit, keep going */ - if (!feof(infile)) + 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 i = 0; + int f; + /* found the data file for a track */ TOKENIZE - /* - Samples: https://github.com/cdrdao/cdrdao/tree/master/testtocs - - Unimplemented: - CD_TEXT - SILENCE - ZERO - FIFO - PREGAP - CATALOG - ISRC - */ - if (!strcmp(token, "NO")) + /* keep the filename */ + outinfo.track[trknum].fname.assign(path).append(token); + + /* get either the offset or the length */ + TOKENIZE + + if (!strcmp(token, "SWAP")) { 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; + + outinfo.track[trknum].swap = true; } - else if (!strcmp(token, "COPY")) + else { - outtoc.tracks[trknum].control_flags |= CD_FLAG_CONTROL_DIGITAL_COPY_PERMITTED; + outinfo.track[trknum].swap = false; } - else if (!strcmp(token, "PRE_EMPHASIS")) + + if (token[0] == '#') { - outtoc.tracks[trknum].control_flags |= CD_FLAG_CONTROL_PREEMPHASIS; + /* it's a decimal offset, use it */ + f = strtoul(&token[1], nullptr, 10); } - else if (!strcmp(token, "TWO_CHANNEL_AUDIO")) + else if (isdigit((uint8_t)token[0])) { - outtoc.tracks[trknum].control_flags &= ~CD_FLAG_CONTROL_4CH; + /* convert the time to an offset */ + f = msf_to_frames(token); + + f *= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize); } - else if (!strcmp(token, "FOUR_CHANNEL_AUDIO")) + else { - outtoc.tracks[trknum].control_flags |= CD_FLAG_CONTROL_4CH; + f = 0; } - 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 + outinfo.track[trknum].offset = f; - 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; - } + TOKENIZE - outinfo.track[trknum].offset = f; + 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])) { - // 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); + // it was an offset. + f *= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize); - outinfo.track[trknum].offset += f; + 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; + // this is the length. + f = msf_to_frames(token); } - - outtoc.tracks[trknum].frames = f; } - else if (!strcmp(token, "TRACK")) + else if( trknum == 0 && outinfo.track[trknum].offset != 0 ) { - trknum++; - - /* next token on the line is the track type */ - TOKENIZE + /* 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].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; + outtoc.tracks[trknum].frames = f; + } + else if (!strcmp(token, "TRACK")) + { + trknum++; - convert_type_string_to_track_info(token, &outtoc.tracks[trknum]); - if (outtoc.tracks[trknum].datasize == 0) - { - fclose(infile); - printf("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token); - return chd_file::error::UNSUPPORTED_FORMAT; - } + /* next token on the line is the track type */ + TOKENIZE - /* next (optional) token on the line is the subcode 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_subtype_string_to_track_info(token, &outtoc.tracks[trknum]); - } - else if (!strcmp(token, "START")) + convert_type_string_to_track_info(token, &outtoc.tracks[trknum]); + if (outtoc.tracks[trknum].datasize == 0) { - int frames; + fclose(infile); + osd_printf_error("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token); + return chd_file::error::UNSUPPORTED_FORMAT; + } - /* get index */ - TOKENIZE - frames = msf_to_frames(token); + /* next (optional) token on the line is the subcode type */ + TOKENIZE - outtoc.tracks[trknum].pregap = frames; - } + 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; } } diff --git a/src/tools/chdman.cpp b/src/tools/chdman.cpp index e7aca251dd7..353ac88ce6b 100644 --- a/src/tools/chdman.cpp +++ b/src/tools/chdman.cpp @@ -17,6 +17,7 @@ #include "hashing.h" #include "md5.h" #include "multibyte.h" +#include "osdcore.h" #include "path.h" #include "strformat.h" #include "vbiparse.h" @@ -71,6 +72,9 @@ constexpr int MODE_NORMAL = 0; constexpr int MODE_CUEBIN = 1; constexpr int MODE_GDI = 2; +// osd printf verbosity +constexpr bool OSD_PRINTF_VERBOSE = false; + // command modifier #define REQUIRED "~" @@ -157,6 +161,46 @@ static void do_list_templates(parameters_map ¶ms); // TYPE DEFINITIONS //************************************************************************** +// Allow chdman to show osd_printf_X messages +class chdman_osd_output : public osd_output +{ +public: + chdman_osd_output() { + osd_output::push(this); + } + + ~chdman_osd_output() { + osd_output::pop(this); + } + + void output_callback(osd_output_channel channel, const util::format_argument_pack &args); +}; + +void chdman_osd_output::output_callback(osd_output_channel channel, const util::format_argument_pack &args) +{ + switch (channel) + { + case OSD_OUTPUT_CHANNEL_ERROR: + case OSD_OUTPUT_CHANNEL_WARNING: + util::stream_format(std::cerr, args); + break; + case OSD_OUTPUT_CHANNEL_INFO: + case OSD_OUTPUT_CHANNEL_LOG: + util::stream_format(std::cout, args); + break; + case OSD_OUTPUT_CHANNEL_VERBOSE: + if (OSD_PRINTF_VERBOSE) util::stream_format(std::cout, args); + break; + case OSD_OUTPUT_CHANNEL_DEBUG: +#ifdef MAME_DEBUG + util::stream_format(std::cout, args); +#endif + break; + default: + break; + } +} + // ======================> option_description struct option_description @@ -3354,6 +3398,7 @@ static void do_list_templates(parameters_map ¶ms) int CLIB_DECL main(int argc, char *argv[]) { const std::vector args = osd_get_command_line(argc, argv); + chdman_osd_output osdoutput; // print the header extern const char build_version[]; -- cgit v1.2.3