/*************************************************************************** The Disc Image Chef ---------------------------------------------------------------------------- Filename : CDRWin.cs Version : 1.0 Author(s) : Natalia Portillo Component : Disc image plugins Revision : $Revision$ Last change by : $Author$ Date : $Date$ --[ Description ] ---------------------------------------------------------- Manages CDRWin cuesheets (cue/bin). --[ License ] -------------------------------------------------------------- This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ---------------------------------------------------------------------------- Copyright (C) 2011-2014 Claunia.com ****************************************************************************/ //$Id$ // TODO: Implement track flags using System; using System.IO; using System.Text; using System.Text.RegularExpressions; using System.Collections.Generic; using DiscImageChef; namespace DiscImageChef.ImagePlugins { class CDRWin : ImagePlugin { #region Internal structures struct CDRWinTrackFile { public UInt32 sequence; // Track # public string datafile; // Path of file containing track public UInt64 offset; // Offset of track start in file public string filetype; // Type of file } struct CDRWinTrack { public UInt32 sequence; // Track # public string title; // Track title (from CD-Text) public string genre; // Track genre (from CD-Text) public string arranger; // Track arranger (from CD-Text) public string composer; // Track composer (from CD-Text) public string performer; // Track performer (from CD-Text) public string songwriter; // Track song writer (from CD-Text) public string isrc; // Track ISRC public CDRWinTrackFile trackfile; // File struct for this track public Dictionary indexes; // Indexes on this track public UInt64 pregap; // Track pre-gap in sectors public UInt64 postgap; // Track post-gap in sectors public bool flag_dcp; // Digical Copy Permitted public bool flag_4ch; // Track is quadraphonic public bool flag_pre; // Track has preemphasis public bool flag_scms; // Track has SCMS public UInt16 bps; // Bytes per sector public UInt64 sectors; // Sectors in track public string tracktype; // Track type public UInt16 session; // Track session } #endregion struct CDRWinDisc { public string title; // Disk title (from CD-Text) public string genre; // Disk genre (from CD-Text) public string arranger; // Disk arranger (from CD-Text) public string composer; // Disk composer (from CD-Text) public string performer; // Disk performer (from CD-Text) public string songwriter; // Disk song writer (from CD-Text) public string mcn; // Media catalog number public DiskType disktype; // Disk type public string disktypestr; // Disk type string public string disk_id; // Disk CDDB ID public string barcode; // Disk UPC/EAN public List sessions; // Sessions public List tracks; // Tracks public string comment; // Disk comment public string cdtextfile; // File containing CD-Text } #region Internal consts // Type for FILE entity const string CDRWinDiskTypeLittleEndian = "BINARY"; // Data as-is in little-endian const string CDRWinDiskTypeBigEndian = "MOTOROLA"; // Data as-is in big-endian const string CDRWinDiskTypeAIFF = "AIFF"; // Audio in Apple AIF file const string CDRWinDiskTypeRIFF = "WAVE"; // Audio in Microsoft WAV file const string CDRWinDiskTypeMP3 = "MP3"; // Audio in MP3 file // Type for TRACK entity const string CDRWinTrackTypeAudio = "AUDIO"; // Audio track, 2352 bytes/sector const string CDRWinTrackTypeCDG = "CDG"; // CD+G track, 2448 bytes/sector (audio+subchannel) const string CDRWinTrackTypeMode1 = "MODE1/2048"; // Mode 1 track, cooked, 2048 bytes/sector const string CDRWinTrackTypeMode1Raw = "MODE1/2352"; // Mode 1 track, raw, 2352 bytes/sector const string CDRWinTrackTypeMode2Form1 = "MODE2/2048"; // Mode 2 form 1 track, cooked, 2048 bytes/sector const string CDRWinTrackTypeMode2Form2 = "MODE2/2324"; // Mode 2 form 2 track, cooked, 2324 bytes/sector const string CDRWinTrackTypeMode2Formless = "MODE2/2336"; // Mode 2 formless track, cooked, 2336 bytes/sector const string CDRWinTrackTypeMode2Raw = "MODE2/2352"; // Mode 2 track, raw, 2352 bytes/sector const string CDRWinTrackTypeCDI = "CDI/2336"; // CD-i track, cooked, 2336 bytes/sector const string CDRWinTrackTypeCDIRaw = "CDI/2352"; // CD-i track, raw, 2352 bytes/sector // Type for REM ORIGINAL MEDIA-TYPE entity const string CDRWinDiskTypeCD = "CD"; // DiskType.CD const string CDRWinDiskTypeCDRW = "CD-RW"; // DiskType.CDRW const string CDRWinDiskTypeCDMRW = "CD-MRW"; // DiskType.CDMRW const string CDRWinDiskTypeCDMRW2 = "CD-(MRW)"; // DiskType.CDMRW const string CDRWinDiskTypeDVD = "DVD"; // DiskType.DVDROM const string CDRWinDiskTypeDVDPMRW = "DVD+MRW"; // DiskType.DVDPRW const string CDRWinDiskTypeDVDPMRW2 = "DVD+(MRW)"; // DiskType.DVDPRW const string CDRWinDiskTypeDVDPMRWDL = "DVD+MRW DL"; // DiskType.DVDPRWDL const string CDRWinDiskTypeDVDPMRWDL2 = "DVD+(MRW) DL"; // DiskType.DVDPRWDL const string CDRWinDiskTypeDVDPR = "DVD+R"; // DiskType.DVDPR const string CDRWinDiskTypeDVDPRDL = "DVD+R DL"; // DiskType.DVDPRDL const string CDRWinDiskTypeDVDPRW = "DVD+RW"; // DiskType.DVDPRW const string CDRWinDiskTypeDVDPRWDL = "DVD+RW DL"; // DiskType.DVDPRWDL const string CDRWinDiskTypeDVDPVR = "DVD+VR"; // DiskType.DVDPR const string CDRWinDiskTypeDVDRAM = "DVD-RAM"; // DiskType.DVDRAM const string CDRWinDiskTypeDVDR = "DVD-R"; // DiskType.DVDR const string CDRWinDiskTypeDVDRDL = "DVD-R DL"; // DiskType.DVDRDL const string CDRWinDiskTypeDVDRW = "DVD-RW"; // DiskType.DVDRW const string CDRWinDiskTypeDVDRWDL = "DVD-RW DL"; // DiskType.DVDRWDL const string CDRWinDiskTypeDVDVR = "DVD-VR"; // DiskType.DVDR const string CDRWinDiskTypeDVDRW2 = "DVDRW"; // DiskType.DVDRW const string CDRWinDiskTypeHDDVD = "HD DVD"; // DiskType.HDDVDROM const string CDRWinDiskTypeHDDVDRAM = "HD DVD-RAM"; // DiskType.HDDVDRAM const string CDRWinDiskTypeHDDVDR = "HD DVD-R"; // DiskType.HDDVDR const string CDRWinDiskTypeHDDVDRDL = "HD DVD-R DL"; // DiskType.HDDVDR const string CDRWinDiskTypeHDDVDRW = "HD DVD-RW"; // DiskType.HDDVDRW const string CDRWinDiskTypeHDDVDRWDL = "HD DVD-RW DL"; // DiskType.HDDVDRW const string CDRWinDiskTypeBD = "BD"; // DiskType.BDROM const string CDRWinDiskTypeBDR = "BD-R"; // DiskType.BDR const string CDRWinDiskTypeBDRE = "BD-RE"; // DiskType.BDRE const string CDRWinDiskTypeBDRDL = "BD-R DL"; // DiskType.BDR const string CDRWinDiskTypeBDREDL = "BD-RE DL"; // DiskType.BDRE #endregion #region Internal variables string imagePath; StreamReader cueStream; FileStream imageStream; Dictionary offsetmap; // Dictionary, index is track #, value is TrackFile CDRWinDisc discimage; List partitions; #endregion #region Parsing regexs const string SessionRegEx = "REM\\s+SESSION\\s+(?\\d+).*$"; const string DiskTypeRegEx = "REM\\s+ORIGINAL MEDIA-TYPE:\\s+(?.+)$"; const string LeadOutRegEx = "REM\\s+LEAD-OUT\\s+(?[\\d]+:[\\d]+:[\\d]+)$"; const string LBARegEx = "REM MSF:\\s+(?[\\d]+:[\\d]+:[\\d]+)\\s+=\\s+LBA:\\s+(?[\\d]+)$"; // Not checked const string DiskIDRegEx = "DISC_ID\\s+(?[\\da-f]{8})$"; const string BarCodeRegEx = "UPC_EAN\\s+(?[\\d]{12,13})$"; const string CommentRegEx = "REM\\s+(?.+)$"; const string CDTextRegEx = "CDTEXTFILE\\s+(?.+)$"; const string MCNRegEx = "CATALOG\\s+(?\\d{13})$"; const string TitleRegEx = "TITLE\\s+(?.+)$"; const string GenreRegEx = "GENRE\\s+(?<genre>.+)$"; const string ArrangerRegEx = "ARRANGER\\s+(?<arranger>.+)$"; const string ComposerRegEx = "COMPOSER\\s+(?<composer>.+)$"; const string PerformerRegEx = "PERFORMER\\s+(?<performer>.+)$"; const string SongWriterRegEx = "SONGWRITER\\s+(?<songwriter>.+)$"; const string FileRegEx = "FILE\\s+(?<filename>.+)\\s+(?<type>\\S+)$"; const string TrackRegEx = "TRACK\\s+(?<number>\\d+)\\s+(?<type>\\S+)$"; const string ISRCRegEx = "ISRC\\s+(?<isrc>\\w{12})$"; const string IndexRegEx = "INDEX\\s+(?<index>\\d+)\\s+(?<msf>[\\d]+:[\\d]+:[\\d]+)$"; const string PregapRegEx = "PREGAP\\s+(?<msf>[\\d]+:[\\d]+:[\\d]+)$"; const string PostgapRegex = "POSTGAP\\s+(?<msf>[\\d]+:[\\d]+:[\\d]+)$"; const string FlagsRegEx = "FLAGS\\s+(((?<dcp>DCP)|(?<quad>4CH)|(?<pre>PRE)|(?<scms>SCMS))\\s*)+$"; #endregion #region Methods public CDRWin(PluginBase Core) { Name = "CDRWin cuesheet"; PluginUUID = new Guid("664568B2-15D4-4E64-8A7A-20BDA8B8386F"); imagePath = ""; ImageInfo = new ImageInfo(); ImageInfo.readableSectorTags = new List<SectorTagType>(); ImageInfo.readableDiskTags = new List<DiskTagType>(); ImageInfo.imageHasPartitions = true; ImageInfo.imageHasSessions = true; ImageInfo.imageVersion = null; ImageInfo.imageApplicationVersion = null; ImageInfo.imageName = null; ImageInfo.imageCreator = null; ImageInfo.diskManufacturer = null; ImageInfo.diskModel = null; ImageInfo.diskPartNumber = null; ImageInfo.diskSequence = 0; ImageInfo.lastDiskSequence = 0; ImageInfo.driveManufacturer = null; ImageInfo.driveModel = null; ImageInfo.driveSerialNumber = null; } // Due to .cue format, this method must parse whole file, ignoring errors (those will be thrown by OpenImage()). public override bool IdentifyImage(string imagePath) { this.imagePath = imagePath; try { cueStream = new StreamReader(this.imagePath); int line = 0; while (cueStream.Peek() >= 0) { line++; string _line = cueStream.ReadLine(); Regex Sr = new Regex(SessionRegEx); Regex Rr = new Regex(CommentRegEx); Regex Cr = new Regex(MCNRegEx); Regex Fr = new Regex(FileRegEx); Match Sm; Match Rm; Match Cm; Match Fm; // First line must be SESSION, REM, CATALOG or FILE. Sm = Sr.Match(_line); Rm = Rr.Match(_line); Cm = Cr.Match(_line); Fm = Fr.Match(_line); if (!Sm.Success && !Rm.Success && !Cm.Success && !Fm.Success) return false; return true; } return false; } catch (Exception ex) { Console.WriteLine("Exception trying to identify image file {0}", this.imagePath); Console.WriteLine("Exception: {0}", ex.Message); Console.WriteLine("Stack trace: {0}", ex.StackTrace); return false; } } public override bool OpenImage(string imagePath) { if (imagePath == null) return false; if (imagePath == "") return false; this.imagePath = imagePath; try { cueStream = new StreamReader(imagePath); int line = 0; bool intrack = false; byte currentsession = 1; // Initialize all RegExs Regex RegexSession = new Regex(SessionRegEx); Regex RegexDiskType = new Regex(DiskTypeRegEx); Regex RegexLeadOut = new Regex(LeadOutRegEx); Regex RegexLBA = new Regex(LBARegEx); Regex RegexDiskID = new Regex(DiskIDRegEx); Regex RegexBarCode = new Regex(BarCodeRegEx); Regex RegexComment = new Regex(CommentRegEx); Regex RegexCDText = new Regex(CDTextRegEx); Regex RegexMCN = new Regex(MCNRegEx); Regex RegexTitle = new Regex(TitleRegEx); Regex RegexGenre = new Regex(GenreRegEx); Regex RegexArranger = new Regex(ArrangerRegEx); Regex RegexComposer = new Regex(ComposerRegEx); Regex RegexPerformer = new Regex(PerformerRegEx); Regex RegexSongWriter = new Regex(SongWriterRegEx); Regex RegexFile = new Regex(FileRegEx); Regex RegexTrack = new Regex(TrackRegEx); Regex RegexISRC = new Regex(ISRCRegEx); Regex RegexIndex = new Regex(IndexRegEx); Regex RegexPregap = new Regex(PregapRegEx); Regex RegexPostgap = new Regex(PostgapRegex); Regex RegexFlags = new Regex(FlagsRegEx); // Initialize all RegEx matches Match MatchSession; Match MatchDiskType; Match MatchLeadOut; Match MatchLBA; Match MatchDiskID; Match MatchBarCode; Match MatchComment; Match MatchCDText; Match MatchMCN; Match MatchTitle; Match MatchGenre; Match MatchArranger; Match MatchComposer; Match MatchPerformer; Match MatchSongWriter; Match MatchFile; Match MatchTrack; Match MatchISRC; Match MatchIndex; Match MatchPregap; Match MatchPostgap; Match MatchFlags; // Initialize disc discimage = new CDRWinDisc(); discimage.sessions = new List<Session>(); discimage.tracks = new List<CDRWinTrack>(); discimage.comment = ""; CDRWinTrack currenttrack = new CDRWinTrack(); currenttrack.indexes = new Dictionary<int, ulong>(); CDRWinTrackFile currentfile = new CDRWinTrackFile(); ulong currentfileoffsetsector = 0; CDRWinTrack[] cuetracks; int track_count = 0; while (cueStream.Peek() >= 0) { line++; string _line = cueStream.ReadLine(); MatchTrack = RegexTrack.Match(_line); if (MatchTrack.Success) { uint track_seq = uint.Parse(MatchTrack.Groups[1].Value); if (track_count + 1 != track_seq) throw new FeatureUnsupportedImageException(String.Format("Found TRACK {0} out of order in line {1}", track_seq, line)); track_count++; } } if (track_count == 0) throw new FeatureUnsupportedImageException("No tracks found"); cuetracks = new CDRWinTrack[track_count]; line = 0; // Mono <= 3.5 allowed this to work, with .Peek() NOT returning EOF. // However .NET framework has always returned EOF even with this rewind. // Mono 4.0 copied their bug (feature?) //cueStream.BaseStream.Seek(0, SeekOrigin.Begin); // Forcing me to do cueStream.Close(); cueStream = new StreamReader(imagePath); while (cueStream.Peek() >= 0) { line++; string _line = cueStream.ReadLine(); MatchSession = RegexSession.Match(_line); MatchDiskType = RegexDiskType.Match(_line); MatchComment = RegexComment.Match(_line); MatchLBA = RegexLBA.Match(_line); // Unhandled, just ignored MatchLeadOut = RegexLeadOut.Match(_line); // Unhandled, just ignored if (MatchDiskType.Success && !intrack) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found REM ORIGINAL MEDIA TYPE at line {0}", line); discimage.disktypestr = MatchDiskType.Groups[1].Value; } else if (MatchDiskType.Success && intrack) { throw new FeatureUnsupportedImageException(String.Format("Found REM ORIGINAL MEDIA TYPE field after a track in line {0}", line)); } else if (MatchSession.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found REM SESSION at line {0}", line); currentsession = Byte.Parse(MatchSession.Groups[1].Value); // What happens between sessions } else if (MatchLBA.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found REM MSF at line {0}", line); // Just ignored } else if (MatchLeadOut.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found REM LEAD-OUT at line {0}", line); // Just ignored } else if (MatchComment.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found REM at line {0}", line); if (discimage.comment == "") discimage.comment = MatchComment.Groups[1].Value; // First comment else discimage.comment += Environment.NewLine + MatchComment.Groups[1].Value; // Append new comments as new lines } else { MatchTrack = RegexTrack.Match(_line); MatchTitle = RegexTitle.Match(_line); MatchSongWriter = RegexSongWriter.Match(_line); MatchPregap = RegexPregap.Match(_line); MatchPostgap = RegexPostgap.Match(_line); MatchPerformer = RegexPerformer.Match(_line); MatchMCN = RegexMCN.Match(_line); MatchISRC = RegexISRC.Match(_line); MatchIndex = RegexIndex.Match(_line); MatchGenre = RegexGenre.Match(_line); MatchFlags = RegexFlags.Match(_line); MatchFile = RegexFile.Match(_line); MatchDiskID = RegexDiskID.Match(_line); MatchComposer = RegexComposer.Match(_line); MatchCDText = RegexCDText.Match(_line); MatchBarCode = RegexBarCode.Match(_line); MatchArranger = RegexArranger.Match(_line); if (MatchArranger.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found ARRANGER at line {0}", line); if (intrack) currenttrack.arranger = MatchArranger.Groups[1].Value; else discimage.arranger = MatchArranger.Groups[1].Value; } else if (MatchBarCode.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found UPC_EAN at line {0}", line); if (!intrack) discimage.barcode = MatchBarCode.Groups[1].Value; else throw new FeatureUnsupportedImageException(String.Format("Found barcode field in incorrect place at line {0}", line)); } else if (MatchCDText.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found CDTEXTFILE at line {0}", line); if (!intrack) discimage.cdtextfile = MatchCDText.Groups[1].Value; else throw new FeatureUnsupportedImageException(String.Format("Found CD-Text file field in incorrect place at line {0}", line)); } else if (MatchComposer.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found COMPOSER at line {0}", line); if (intrack) currenttrack.arranger = MatchComposer.Groups[1].Value; else discimage.arranger = MatchComposer.Groups[1].Value; } else if (MatchDiskID.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found DISC_ID at line {0}", line); if (!intrack) discimage.disk_id = MatchDiskID.Groups[1].Value; else throw new FeatureUnsupportedImageException(String.Format("Found CDDB ID field in incorrect place at line {0}", line)); } else if (MatchFile.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found FILE at line {0}", line); if (currenttrack.sequence != 0) { currentfile.sequence = currenttrack.sequence; currenttrack.trackfile = currentfile; FileInfo finfo = new FileInfo(currentfile.datafile); currenttrack.sectors = ((ulong)finfo.Length - currentfile.offset) / CDRWinTrackTypeToBytesPerSector(currenttrack.tracktype); cuetracks[currenttrack.sequence - 1] = currenttrack; intrack = false; currenttrack = new CDRWinTrack(); } currentfile.datafile = MatchFile.Groups[1].Value; currentfile.filetype = MatchFile.Groups[2].Value; // Check if file path is quoted if (currentfile.datafile[0] == '"' && currentfile.datafile[currentfile.datafile.Length - 1] == '"') { currentfile.datafile = currentfile.datafile.Substring(1, currentfile.datafile.Length - 2); // Unquote it } // Check if file exists if (!File.Exists(currentfile.datafile)) { if (currentfile.datafile[0] == '/' || (currentfile.datafile[0] == '/' && currentfile.datafile[1] == '.')) // UNIX absolute path { Regex unixpath = new Regex("^(.+)/([^/]+)$"); Match unixpathmatch = unixpath.Match(currentfile.datafile); if (unixpathmatch.Success) { currentfile.datafile = unixpathmatch.Groups[1].Value; if (!File.Exists(currentfile.datafile)) { string path = Path.GetPathRoot(imagePath); currentfile.datafile = path + Path.PathSeparator + currentfile.datafile; if (!File.Exists(currentfile.datafile)) throw new FeatureUnsupportedImageException(String.Format("File \"{0}\" not found.", MatchFile.Groups[1].Value)); } } else throw new FeatureUnsupportedImageException(String.Format("File \"{0}\" not found.", MatchFile.Groups[1].Value)); } else if ((currentfile.datafile[1] == ':' && currentfile.datafile[2] == '\\') || (currentfile.datafile[0] == '\\' && currentfile.datafile[1] == '\\') || ((currentfile.datafile[0] == '.' && currentfile.datafile[1] == '\\'))) // Windows absolute path { Regex winpath = new Regex("^(?:[a-zA-Z]\\:(\\\\|\\/)|file\\:\\/\\/|\\\\\\\\|\\.(\\/|\\\\))([^\\\\\\/\\:\\*\\?\\<\\>\\\"\\|]+(\\\\|\\/){0,1})+$"); Match winpathmatch = winpath.Match(currentfile.datafile); if (winpathmatch.Success) { currentfile.datafile = winpathmatch.Groups[1].Value; if (!File.Exists(currentfile.datafile)) { string path = Path.GetPathRoot(imagePath); currentfile.datafile = path + Path.PathSeparator + currentfile.datafile; if (!File.Exists(currentfile.datafile)) throw new FeatureUnsupportedImageException(String.Format("File \"{0}\" not found.", MatchFile.Groups[1].Value)); } } else throw new FeatureUnsupportedImageException(String.Format("File \"{0}\" not found.", MatchFile.Groups[1].Value)); } else { string path = Path.GetDirectoryName(imagePath); currentfile.datafile = path + Path.DirectorySeparatorChar + currentfile.datafile; if (!File.Exists(currentfile.datafile)) throw new FeatureUnsupportedImageException(String.Format("File \"{0}\" not found.", MatchFile.Groups[1].Value)); } } // File does exist, process it if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): File \"{0}\" found", currentfile.datafile); switch (currentfile.filetype) { case CDRWinDiskTypeLittleEndian: break; case CDRWinDiskTypeBigEndian: case CDRWinDiskTypeAIFF: case CDRWinDiskTypeRIFF: case CDRWinDiskTypeMP3: throw new FeatureSupportedButNotImplementedImageException(String.Format("Unsupported file type {0}", currentfile.filetype)); default: throw new FeatureUnsupportedImageException(String.Format("Unknown file type {0}", currentfile.filetype)); } currentfile.offset = 0; currentfile.sequence = 0; } else if (MatchFlags.Success) { // TODO: Implement FLAGS support. if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found FLAGS at line {0}", line); if (!intrack) throw new FeatureUnsupportedImageException(String.Format("Found FLAGS field in incorrect place at line {0}", line)); } else if (MatchGenre.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found GENRE at line {0}", line); if (intrack) currenttrack.genre = MatchGenre.Groups[1].Value; else discimage.genre = MatchGenre.Groups[1].Value; } else if (MatchIndex.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found INDEX at line {0}", line); if (!intrack) throw new FeatureUnsupportedImageException(String.Format("Found INDEX before a track {0}", line)); else { int index = int.Parse(MatchIndex.Groups[1].Value); ulong offset = CDRWinMSFToLBA(MatchIndex.Groups[2].Value); if ((index != 0 && index != 1) && currenttrack.indexes.Count == 0) throw new FeatureUnsupportedImageException(String.Format("Found INDEX {0} before INDEX 00 or INDEX 01", index)); if ((index == 0 || (index == 1 && !currenttrack.indexes.ContainsKey(0)))) { if ((int)(currenttrack.sequence - 2) >= 0 && offset > 1) { cuetracks[currenttrack.sequence - 2].sectors = offset - currentfileoffsetsector; currentfile.offset += cuetracks[currenttrack.sequence - 2].sectors * cuetracks[currenttrack.sequence - 2].bps; if (MainClass.isDebug) { Console.WriteLine("DEBUG (CDRWin plugin): Sets currentfile.offset to {0} at line 553", currentfile.offset); Console.WriteLine("DEBUG (CDRWin plugin): cuetracks[currenttrack.sequence-2].sectors = {0}", cuetracks[currenttrack.sequence - 2].sectors); Console.WriteLine("DEBUG (CDRWin plugin): cuetracks[currenttrack.sequence-2].bps = {0}", cuetracks[currenttrack.sequence - 2].bps); } } } if ((index == 0 || (index == 1 && !currenttrack.indexes.ContainsKey(0))) && currenttrack.sequence == 1) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Sets currentfile.offset to {0} at line 559", offset * currenttrack.bps); currentfile.offset = offset * currenttrack.bps; } currentfileoffsetsector = offset; currenttrack.indexes.Add(index, offset); } } else if (MatchISRC.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found ISRC at line {0}", line); if (!intrack) throw new FeatureUnsupportedImageException(String.Format("Found ISRC before a track {0}", line)); currenttrack.isrc = MatchISRC.Groups[1].Value; } else if (MatchMCN.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found CATALOG at line {0}", line); if (!intrack) discimage.mcn = MatchMCN.Groups[1].Value; else throw new FeatureUnsupportedImageException(String.Format("Found CATALOG field in incorrect place at line {0}", line)); } else if (MatchPerformer.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found PERFORMER at line {0}", line); if (intrack) currenttrack.performer = MatchPerformer.Groups[1].Value; else discimage.performer = MatchPerformer.Groups[1].Value; } else if (MatchPostgap.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found POSTGAP at line {0}", line); if (intrack) { currenttrack.postgap = CDRWinMSFToLBA(MatchPostgap.Groups[1].Value); } else throw new FeatureUnsupportedImageException(String.Format("Found POSTGAP field before a track at line {0}", line)); } else if (MatchPregap.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found PREGAP at line {0}", line); if (intrack) { currenttrack.pregap = CDRWinMSFToLBA(MatchPregap.Groups[1].Value); } else throw new FeatureUnsupportedImageException(String.Format("Found PREGAP field before a track at line {0}", line)); } else if (MatchSongWriter.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found SONGWRITER at line {0}", line); if (intrack) currenttrack.songwriter = MatchSongWriter.Groups[1].Value; else discimage.songwriter = MatchSongWriter.Groups[1].Value; } else if (MatchTitle.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found TITLE at line {0}", line); if (intrack) currenttrack.title = MatchTitle.Groups[1].Value; else discimage.title = MatchTitle.Groups[1].Value; } else if (MatchTrack.Success) { if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Found TRACK at line {0}", line); if (currentfile.datafile == "") throw new FeatureUnsupportedImageException(String.Format("Found TRACK field before a file is defined at line {0}", line)); if (intrack) { if (currenttrack.indexes.ContainsKey(0) && currenttrack.pregap == 0) { currenttrack.indexes.TryGetValue(0, out currenttrack.pregap); } currentfile.sequence = currenttrack.sequence; currenttrack.trackfile = currentfile; cuetracks[currenttrack.sequence - 1] = currenttrack; } currenttrack = new CDRWinTrack(); currenttrack.indexes = new Dictionary<int, ulong>(); currenttrack.sequence = uint.Parse(MatchTrack.Groups[1].Value); if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Setting currenttrack.sequence to {0}", currenttrack.sequence); currentfile.sequence = currenttrack.sequence; currenttrack.bps = CDRWinTrackTypeToBytesPerSector(MatchTrack.Groups[2].Value); currenttrack.tracktype = MatchTrack.Groups[2].Value; currenttrack.session = currentsession; intrack = true; } else if (_line == "") // Empty line, ignore it { } else // Non-empty unknown field { throw new FeatureUnsupportedImageException(String.Format("Found unknown field defined at line {0}: \"{1}\"", line, _line)); } } } if (currenttrack.sequence != 0) { currentfile.sequence = currenttrack.sequence; currenttrack.trackfile = currentfile; FileInfo finfo = new FileInfo(currentfile.datafile); currenttrack.sectors = ((ulong)finfo.Length - currentfile.offset) / CDRWinTrackTypeToBytesPerSector(currenttrack.tracktype); cuetracks[currenttrack.sequence - 1] = currenttrack; } Session[] _sessions = new Session[currentsession]; for (int s = 1; s <= _sessions.Length; s++) { _sessions[s - 1].SessionSequence = 1; if (s > 1) _sessions[s - 1].StartSector = _sessions[s - 2].EndSector + 1; else _sessions[s - 1].StartSector = 0; ulong session_sectors = 0; int last_session_track = 0; for (int i = 0; i < cuetracks.Length; i++) { if (cuetracks[i].session == s) { session_sectors += cuetracks[i].sectors; if (i > last_session_track) last_session_track = i; } } _sessions[s - 1].EndTrack = cuetracks[last_session_track].sequence; _sessions[s - 1].EndSector = session_sectors - 1; } for (int s = 1; s <= _sessions.Length; s++) discimage.sessions.Add(_sessions[s - 1]); for (int t = 1; t <= cuetracks.Length; t++) discimage.tracks.Add(cuetracks[t - 1]); discimage.disktype = CDRWinIsoBusterDiscTypeToDiskType(discimage.disktypestr); if (discimage.disktype == DiskType.Unknown || discimage.disktype == DiskType.CD) { bool data = false; bool cdg = false; bool cdi = false; bool mode2 = false; bool firstaudio = false; bool firstdata = false; bool audio = false; for (int i = 0; i < discimage.tracks.Count; i++) { // First track is audio firstaudio |= i == 0 && discimage.tracks[i].tracktype == CDRWinTrackTypeAudio; // First track is data firstdata |= i == 0 && discimage.tracks[i].tracktype != CDRWinTrackTypeAudio; // Any non first track is data data |= i != 0 && discimage.tracks[i].tracktype != CDRWinTrackTypeAudio; // Any non first track is audio audio |= i != 0 && discimage.tracks[i].tracktype == CDRWinTrackTypeAudio; switch (discimage.tracks[i].tracktype) { case CDRWinTrackTypeCDG: cdg = true; break; case CDRWinTrackTypeCDI: case CDRWinTrackTypeCDIRaw: cdi = true; break; case CDRWinTrackTypeMode2Form1: case CDRWinTrackTypeMode2Form2: case CDRWinTrackTypeMode2Formless: case CDRWinTrackTypeMode2Raw: mode2 = true; break; } } if (!data && !firstdata) discimage.disktype = DiskType.CDDA; else if (cdg) discimage.disktype = DiskType.CDG; else if (cdi) discimage.disktype = DiskType.CDI; else if (firstaudio && data && discimage.sessions.Count > 1 && mode2) discimage.disktype = DiskType.CDPLUS; else if ((firstdata && !data) || mode2) discimage.disktype = DiskType.CDROMXA; else if (!audio) discimage.disktype = DiskType.CDROM; else discimage.disktype = DiskType.CD; } if (MainClass.isDebug) { // DEBUG information Console.WriteLine("DEBUG (CDRWin plugin): Disc image parsing results"); Console.WriteLine("DEBUG (CDRWin plugin): Disc CD-TEXT:"); if (discimage.arranger == null) Console.WriteLine("DEBUG (CDRWin plugin): \tArranger is not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \tArranger: {0}", discimage.arranger); if (discimage.composer == null) Console.WriteLine("DEBUG (CDRWin plugin): \tComposer is not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \tComposer: {0}", discimage.composer); if (discimage.genre == null) Console.WriteLine("DEBUG (CDRWin plugin): \tGenre is not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \tGenre: {0}", discimage.genre); if (discimage.performer == null) Console.WriteLine("DEBUG (CDRWin plugin): \tPerformer is not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \tPerformer: {0}", discimage.performer); if (discimage.songwriter == null) Console.WriteLine("DEBUG (CDRWin plugin): \tSongwriter is not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \tSongwriter: {0}", discimage.songwriter); if (discimage.title == null) Console.WriteLine("DEBUG (CDRWin plugin): \tTitle is not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \tTitle: {0}", discimage.title); if (discimage.cdtextfile == null) Console.WriteLine("DEBUG (CDRWin plugin): \tCD-TEXT binary file not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \tCD-TEXT binary file: {0}", discimage.cdtextfile); Console.WriteLine("DEBUG (CDRWin plugin): Disc information:"); if (discimage.disktypestr == null) Console.WriteLine("DEBUG (CDRWin plugin): \tISOBuster disc type not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \tISOBuster disc type: {0}", discimage.disktypestr); Console.WriteLine("DEBUG (CDRWin plugin): \tGuessed disk type: {0}", discimage.disktype); if (discimage.barcode == null) Console.WriteLine("DEBUG (CDRWin plugin): \tBarcode not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \tBarcode: {0}", discimage.barcode); if (discimage.disk_id == null) Console.WriteLine("DEBUG (CDRWin plugin): \tDisc ID not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \tDisc ID: {0}", discimage.disk_id); if (discimage.mcn == null) Console.WriteLine("DEBUG (CDRWin plugin): \tMCN not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \tMCN: {0}", discimage.mcn); if (discimage.comment == null) Console.WriteLine("DEBUG (CDRWin plugin): \tComment not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \tComment: \"{0}\"", discimage.comment); Console.WriteLine("DEBUG (CDRWin plugin): Session information:"); Console.WriteLine("DEBUG (CDRWin plugin): \tDisc contains {0} sessions", discimage.sessions.Count); for (int i = 0; i < discimage.sessions.Count; i++) { Console.WriteLine("DEBUG (CDRWin plugin): \tSession {0} information:", i + 1); Console.WriteLine("DEBUG (CDRWin plugin): \t\tStarting track: {0}", discimage.sessions[i].StartTrack); Console.WriteLine("DEBUG (CDRWin plugin): \t\tStarting sector: {0}", discimage.sessions[i].StartSector); Console.WriteLine("DEBUG (CDRWin plugin): \t\tEnding track: {0}", discimage.sessions[i].EndTrack); Console.WriteLine("DEBUG (CDRWin plugin): \t\tEnding sector: {0}", discimage.sessions[i].EndSector); } Console.WriteLine("DEBUG (CDRWin plugin): Track information:"); Console.WriteLine("DEBUG (CDRWin plugin): \tDisc contains {0} tracks", discimage.tracks.Count); for (int i = 0; i < discimage.tracks.Count; i++) { Console.WriteLine("DEBUG (CDRWin plugin): \tTrack {0} information:", discimage.tracks[i].sequence); Console.WriteLine("DEBUG (CDRWin plugin): \t\t{0} bytes per sector", discimage.tracks[i].bps); Console.WriteLine("DEBUG (CDRWin plugin): \t\tPregap: {0} sectors", discimage.tracks[i].pregap); Console.WriteLine("DEBUG (CDRWin plugin): \t\tData: {0} sectors", discimage.tracks[i].sectors); Console.WriteLine("DEBUG (CDRWin plugin): \t\tPostgap: {0} sectors", discimage.tracks[i].postgap); if (discimage.tracks[i].flag_4ch) Console.WriteLine("DEBUG (CDRWin plugin): \t\tTrack is flagged as quadraphonic"); if (discimage.tracks[i].flag_dcp) Console.WriteLine("DEBUG (CDRWin plugin): \t\tTrack allows digital copy"); if (discimage.tracks[i].flag_pre) Console.WriteLine("DEBUG (CDRWin plugin): \t\tTrack has pre-emphasis applied"); if (discimage.tracks[i].flag_scms) Console.WriteLine("DEBUG (CDRWin plugin): \t\tTrack has SCMS"); Console.WriteLine("DEBUG (CDRWin plugin): \t\tTrack resides in file {0}, type defined as {1}, starting at byte {2}", discimage.tracks[i].trackfile.datafile, discimage.tracks[i].trackfile.filetype, discimage.tracks[i].trackfile.offset); Console.WriteLine("DEBUG (CDRWin plugin): \t\tIndexes:"); foreach (KeyValuePair<int, ulong> kvp in discimage.tracks[i].indexes) Console.WriteLine("DEBUG (CDRWin plugin): \t\t\tIndex {0} starts at sector {1}", kvp.Key, kvp.Value); if (discimage.tracks[i].isrc == null) Console.WriteLine("DEBUG (CDRWin plugin): \t\tISRC is not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \t\tISRC: {0}", discimage.tracks[i].isrc); if (discimage.tracks[i].arranger == null) Console.WriteLine("DEBUG (CDRWin plugin): \t\tArranger is not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \t\tArranger: {0}", discimage.tracks[i].arranger); if (discimage.tracks[i].composer == null) Console.WriteLine("DEBUG (CDRWin plugin): \t\tComposer is not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \t\tComposer: {0}", discimage.tracks[i].composer); if (discimage.tracks[i].genre == null) Console.WriteLine("DEBUG (CDRWin plugin): \t\tGenre is not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \t\tGenre: {0}", discimage.tracks[i].genre); if (discimage.tracks[i].performer == null) Console.WriteLine("DEBUG (CDRWin plugin): \t\tPerformer is not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \t\tPerformer: {0}", discimage.tracks[i].performer); if (discimage.tracks[i].songwriter == null) Console.WriteLine("DEBUG (CDRWin plugin): \t\tSongwriter is not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \t\tSongwriter: {0}", discimage.tracks[i].songwriter); if (discimage.tracks[i].title == null) Console.WriteLine("DEBUG (CDRWin plugin): \t\tTitle is not set."); else Console.WriteLine("DEBUG (CDRWin plugin): \t\tTitle: {0}", discimage.tracks[i].title); } } if (MainClass.isDebug) Console.WriteLine("DEBUG (CDRWin plugin): Building offset map"); partitions = new List<DiscImageChef.PartPlugins.Partition>(); ulong byte_offset = 0; ulong sector_offset = 0; ulong partitionSequence = 0; ulong index_zero_offset = 0; ulong index_one_offset = 0; bool index_zero = false; offsetmap = new Dictionary<uint, ulong>(); for (int i = 0; i < discimage.tracks.Count; i++) { if (discimage.tracks[i].sequence == 1 && i != 0) throw new ImageNotSupportedException("Unordered tracks"); PartPlugins.Partition partition = new DiscImageChef.PartPlugins.Partition(); if (discimage.tracks[i].pregap > 0) { partition.PartitionDescription = String.Format("Track {0} pregap.", discimage.tracks[i].sequence); partition.PartitionName = discimage.tracks[i].title; partition.PartitionStartSector = sector_offset; partition.PartitionLength = discimage.tracks[i].pregap * discimage.tracks[i].bps; partition.PartitionSectors = discimage.tracks[i].pregap; partition.PartitionSequence = partitionSequence; partition.PartitionStart = byte_offset; partition.PartitionType = discimage.tracks[i].tracktype; sector_offset += partition.PartitionSectors; byte_offset += partition.PartitionLength; partitionSequence++; if (!offsetmap.ContainsKey(discimage.tracks[i].sequence)) offsetmap.Add(discimage.tracks[i].sequence, partition.PartitionStartSector); else { ulong old_start; offsetmap.TryGetValue(discimage.tracks[i].sequence, out old_start); if (partition.PartitionStartSector < old_start) { offsetmap.Remove(discimage.tracks[i].sequence); offsetmap.Add(discimage.tracks[i].sequence, partition.PartitionStartSector); } } partitions.Add(partition); partition = new DiscImageChef.PartPlugins.Partition(); } index_zero |= discimage.tracks[i].indexes.TryGetValue(0, out index_zero_offset); if (!discimage.tracks[i].indexes.TryGetValue(1, out index_one_offset)) throw new ImageNotSupportedException(String.Format("Track {0} lacks index 01", discimage.tracks[i].sequence)); if (index_zero && index_one_offset > index_zero_offset) { partition.PartitionDescription = String.Format("Track {0} index 00.", discimage.tracks[i].sequence); partition.PartitionName = discimage.tracks[i].title; partition.PartitionStartSector = sector_offset; partition.PartitionLength = (index_one_offset - index_zero_offset) * discimage.tracks[i].bps; partition.PartitionSectors = index_one_offset - index_zero_offset; partition.PartitionSequence = partitionSequence; partition.PartitionStart = byte_offset; partition.PartitionType = discimage.tracks[i].tracktype; sector_offset += partition.PartitionSectors; byte_offset += partition.PartitionLength; partitionSequence++; if (!offsetmap.ContainsKey(discimage.tracks[i].sequence)) offsetmap.Add(discimage.tracks[i].sequence, partition.PartitionStartSector); else { ulong old_start; offsetmap.TryGetValue(discimage.tracks[i].sequence, out old_start); if (partition.PartitionStartSector < old_start) { offsetmap.Remove(discimage.tracks[i].sequence); offsetmap.Add(discimage.tracks[i].sequence, partition.PartitionStartSector); } } partitions.Add(partition); partition = new DiscImageChef.PartPlugins.Partition(); } // Index 01 partition.PartitionDescription = String.Format("Track {0}.", discimage.tracks[i].sequence); partition.PartitionName = discimage.tracks[i].title; partition.PartitionStartSector = sector_offset; partition.PartitionLength = (discimage.tracks[i].sectors - (index_one_offset - index_zero_offset)) * discimage.tracks[i].bps; partition.PartitionSectors = (discimage.tracks[i].sectors - (index_one_offset - index_zero_offset)); partition.PartitionSequence = partitionSequence; partition.PartitionStart = byte_offset; partition.PartitionType = discimage.tracks[i].tracktype; sector_offset += partition.PartitionSectors; byte_offset += partition.PartitionLength; partitionSequence++; if (!offsetmap.ContainsKey(discimage.tracks[i].sequence)) offsetmap.Add(discimage.tracks[i].sequence, partition.PartitionStartSector); else { ulong old_start; offsetmap.TryGetValue(discimage.tracks[i].sequence, out old_start); if (partition.PartitionStartSector < old_start) { offsetmap.Remove(discimage.tracks[i].sequence); offsetmap.Add(discimage.tracks[i].sequence, partition.PartitionStartSector); } } partitions.Add(partition); partition = new DiscImageChef.PartPlugins.Partition(); } // Print offset map if (MainClass.isDebug) { Console.WriteLine("DEBUG (CDRWin plugin) printing partition map"); foreach (DiscImageChef.PartPlugins.Partition partition in partitions) { Console.WriteLine("DEBUG (CDRWin plugin): Partition sequence: {0}", partition.PartitionSequence); Console.WriteLine("DEBUG (CDRWin plugin): \tPartition name: {0}", partition.PartitionName); Console.WriteLine("DEBUG (CDRWin plugin): \tPartition description: {0}", partition.PartitionDescription); Console.WriteLine("DEBUG (CDRWin plugin): \tPartition type: {0}", partition.PartitionType); Console.WriteLine("DEBUG (CDRWin plugin): \tPartition starting sector: {0}", partition.PartitionStartSector); Console.WriteLine("DEBUG (CDRWin plugin): \tPartition sectors: {0}", partition.PartitionSectors); Console.WriteLine("DEBUG (CDRWin plugin): \tPartition starting offset: {0}", partition.PartitionStart); Console.WriteLine("DEBUG (CDRWin plugin): \tPartition size in bytes: {0}", partition.PartitionLength); } } foreach (CDRWinTrack track in discimage.tracks) ImageInfo.imageSize += track.bps * track.sectors; foreach (CDRWinTrack track in discimage.tracks) ImageInfo.sectors += track.sectors; if (discimage.disktype == DiskType.CDG || discimage.disktype == DiskType.CDEG || discimage.disktype == DiskType.CDMIDI) ImageInfo.sectorSize = 2448; // CD+G subchannels ARE user data, as CD+G are useless without them else if (discimage.disktype != DiskType.CDROMXA && discimage.disktype != DiskType.CDDA && discimage.disktype != DiskType.CDI && discimage.disktype != DiskType.CDPLUS) ImageInfo.sectorSize = 2048; // Only data tracks else ImageInfo.sectorSize = 2352; // All others if (discimage.mcn != null) ImageInfo.readableDiskTags.Add(DiskTagType.CD_MCN); if (discimage.cdtextfile != null) ImageInfo.readableDiskTags.Add(DiskTagType.CD_TEXT); // Detect ISOBuster extensions if (discimage.disktypestr != null || discimage.comment.ToLower().Contains("isobuster") || discimage.sessions.Count > 1) ImageInfo.imageApplication = "ISOBuster"; else ImageInfo.imageApplication = "CDRWin"; FileInfo fi = new FileInfo(discimage.tracks[0].trackfile.datafile); ImageInfo.imageCreationTime = fi.CreationTimeUtc; ImageInfo.imageLastModificationTime = fi.LastWriteTimeUtc; ImageInfo.imageComments = discimage.comment; ImageInfo.diskSerialNumber = discimage.mcn; ImageInfo.diskBarcode = discimage.barcode; ImageInfo.diskType = discimage.disktype; foreach (CDRWinTrack track in discimage.tracks) { switch (track.tracktype) { case CDRWinTrackTypeAudio: { if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDTrackISRC)) ImageInfo.readableSectorTags.Add(SectorTagType.CDTrackISRC); if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDTrackFlags)) ImageInfo.readableSectorTags.Add(SectorTagType.CDTrackFlags); break; } case CDRWinTrackTypeCDG: { if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDTrackISRC)) ImageInfo.readableSectorTags.Add(SectorTagType.CDTrackISRC); if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDTrackFlags)) ImageInfo.readableSectorTags.Add(SectorTagType.CDTrackFlags); if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSubchannel)) ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorSubchannel); break; } case CDRWinTrackTypeMode2Formless: case CDRWinTrackTypeCDI: { if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSubHeader)) ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorSubHeader); if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorEDC)) ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorEDC); break; } case CDRWinTrackTypeMode2Raw: case CDRWinTrackTypeCDIRaw: { if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSync)) ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorSync); if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorHeader)) ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorHeader); if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSubHeader)) ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorSubHeader); if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorEDC)) ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorEDC); break; } case CDRWinTrackTypeMode1Raw: { if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSync)) ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorSync); if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorHeader)) ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorHeader); if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSubHeader)) ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorSubHeader); if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorECC)) ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorECC); if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorECC_P)) ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorECC_P); if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorECC_Q)) ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorECC_Q); if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorEDC)) ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorEDC); break; } } } return true; } catch (Exception ex) { Console.WriteLine("Exception trying to identify image file {0}", imagePath); Console.WriteLine("Exception: {0}", ex.Message); Console.WriteLine("Stack trace: {0}", ex.StackTrace); return false; } } public override bool ImageHasPartitions() { return ImageInfo.imageHasPartitions; } public override UInt64 GetImageSize() { return ImageInfo.imageSize; } public override UInt64 GetSectors() { return ImageInfo.sectors; } public override UInt32 GetSectorSize() { return ImageInfo.sectorSize; } public override byte[] ReadDiskTag(DiskTagType tag) { switch (tag) { case DiskTagType.CD_MCN: { if (discimage.mcn != null) { return Encoding.ASCII.GetBytes(discimage.mcn); } throw new FeatureNotPresentImageException("Image does not contain MCN information."); } case DiskTagType.CD_TEXT: { if (discimage.cdtextfile != null) // TODO: Check that binary text file exists, open it, read it, send it to caller. throw new FeatureSupportedButNotImplementedImageException("Feature not yet implemented"); throw new FeatureNotPresentImageException("Image does not contain CD-TEXT information."); } default: throw new FeatureSupportedButNotImplementedImageException("Feature not supported by image format"); } } public override byte[] ReadSector(UInt64 sectorAddress) { return ReadSectors(sectorAddress, 1); } public override byte[] ReadSectorTag(UInt64 sectorAddress, SectorTagType tag) { return ReadSectorsTag(sectorAddress, 1, tag); } public override byte[] ReadSector(UInt64 sectorAddress, UInt32 track) { return ReadSectors(sectorAddress, 1, track); } public override byte[] ReadSectorTag(UInt64 sectorAddress, UInt32 track, SectorTagType tag) { return ReadSectorsTag(sectorAddress, 1, track, tag); } public override byte[] ReadSectors(UInt64 sectorAddress, UInt32 length) { foreach (KeyValuePair<uint, ulong> kvp in offsetmap) { if (sectorAddress >= kvp.Value) { foreach (CDRWinTrack cdrwin_track in discimage.tracks) { if (cdrwin_track.sequence == kvp.Key) { if ((sectorAddress - kvp.Value) < cdrwin_track.sectors) return ReadSectors((sectorAddress - kvp.Value), length, kvp.Key); } } } } throw new ArgumentOutOfRangeException("sectorAddress", "Sector address not found"); } public override byte[] ReadSectorsTag(UInt64 sectorAddress, UInt32 length, SectorTagType tag) { foreach (KeyValuePair<uint, ulong> kvp in offsetmap) { if (sectorAddress >= kvp.Value) { foreach (CDRWinTrack cdrwin_track in discimage.tracks) { if (cdrwin_track.sequence == kvp.Key) { if ((sectorAddress - kvp.Value) < cdrwin_track.sectors) return ReadSectorsTag((sectorAddress - kvp.Value), length, kvp.Key, tag); } } } } throw new ArgumentOutOfRangeException("sectorAddress", "Sector address not found"); } public override byte[] ReadSectors(UInt64 sectorAddress, UInt32 length, UInt32 track) { CDRWinTrack _track = new CDRWinTrack(); _track.sequence = 0; foreach (CDRWinTrack cdrwin_track in discimage.tracks) { if (cdrwin_track.sequence == track) { _track = cdrwin_track; break; } } if (_track.sequence == 0) throw new ArgumentOutOfRangeException("track", "Track does not exist in disc image"); if (length > _track.sectors) throw new ArgumentOutOfRangeException("length", "Requested more sectors than present in track, won't cross tracks"); uint sector_offset; uint sector_size; uint sector_skip; switch (_track.tracktype) { case CDRWinTrackTypeMode1: case CDRWinTrackTypeMode2Form1: { sector_offset = 0; sector_size = 2048; sector_skip = 0; break; } case CDRWinTrackTypeMode2Form2: { sector_offset = 0; sector_size = 2324; sector_skip = 0; break; } case CDRWinTrackTypeMode2Formless: case CDRWinTrackTypeCDI: { sector_offset = 0; sector_size = 2336; sector_skip = 0; break; } case CDRWinTrackTypeAudio: { sector_offset = 0; sector_size = 2352; sector_skip = 0; break; } case CDRWinTrackTypeMode1Raw: { sector_offset = 16; sector_size = 2048; sector_skip = 288; break; } case CDRWinTrackTypeMode2Raw: { sector_offset = 16; sector_size = 2336; sector_skip = 0; break; } case CDRWinTrackTypeCDIRaw: { sector_offset = 16; sector_size = 2336; sector_skip = 0; break; } case CDRWinTrackTypeCDG: { sector_offset = 0; sector_size = 2352; sector_skip = 96; break; } default: throw new FeatureSupportedButNotImplementedImageException("Unsupported track type"); } byte[] buffer = new byte[sector_size * length]; imageStream = new FileStream(_track.trackfile.datafile, FileMode.Open, FileAccess.Read); using (BinaryReader br = new BinaryReader(imageStream)) { br.BaseStream.Seek((long)_track.trackfile.offset + (long)(sectorAddress * (sector_offset + sector_size + sector_skip)), SeekOrigin.Begin); if (sector_offset == 0 && sector_skip == 0) buffer = br.ReadBytes((int)(sector_size * length)); else { for (int i = 0; i < length; i++) { byte[] sector; br.BaseStream.Seek(sector_offset, SeekOrigin.Current); sector = br.ReadBytes((int)sector_size); br.BaseStream.Seek(sector_skip, SeekOrigin.Current); Array.Copy(sector, 0, buffer, i * sector_size, sector_size); } } } return buffer; } public override byte[] ReadSectorsTag(UInt64 sectorAddress, UInt32 length, UInt32 track, SectorTagType tag) { CDRWinTrack _track = new CDRWinTrack(); _track.sequence = 0; foreach (CDRWinTrack cdrwin_track in discimage.tracks) { if (cdrwin_track.sequence == track) { _track = cdrwin_track; break; } } if (_track.sequence == 0) throw new ArgumentOutOfRangeException("track", "Track does not exist in disc image"); if (length > _track.sectors) throw new ArgumentOutOfRangeException("length", "Requested more sectors than present in track, won't cross tracks"); uint sector_offset; uint sector_size; uint sector_skip; switch (tag) { case SectorTagType.CDSectorECC: case SectorTagType.CDSectorECC_P: case SectorTagType.CDSectorECC_Q: case SectorTagType.CDSectorEDC: case SectorTagType.CDSectorHeader: case SectorTagType.CDSectorSubchannel: case SectorTagType.CDSectorSubHeader: case SectorTagType.CDSectorSync: break; case SectorTagType.CDTrackFlags: { byte[] flags = new byte[1]; if (_track.tracktype != CDRWinTrackTypeAudio && _track.tracktype != CDRWinTrackTypeCDG) flags[0] += 0x40; if (_track.flag_dcp) flags[0] += 0x20; if (_track.flag_pre) flags[0] += 0x10; return flags; } case SectorTagType.CDTrackISRC: return Encoding.UTF8.GetBytes(_track.isrc); case SectorTagType.CDTrackText: throw new FeatureSupportedButNotImplementedImageException("Feature not yet implemented"); default: throw new ArgumentException("Unsupported tag requested", "tag"); } switch (_track.tracktype) { case CDRWinTrackTypeMode1: case CDRWinTrackTypeMode2Form1: case CDRWinTrackTypeMode2Form2: throw new ArgumentException("No tags in image for requested track", "tag"); case CDRWinTrackTypeMode2Formless: case CDRWinTrackTypeCDI: { switch (tag) { case SectorTagType.CDSectorSync: case SectorTagType.CDSectorHeader: case SectorTagType.CDSectorSubchannel: case SectorTagType.CDSectorECC: case SectorTagType.CDSectorECC_P: case SectorTagType.CDSectorECC_Q: throw new ArgumentException("Unsupported tag requested for this track", "tag"); case SectorTagType.CDSectorSubHeader: { sector_offset = 0; sector_size = 8; sector_skip = 2328; break; } case SectorTagType.CDSectorEDC: { sector_offset = 2332; sector_size = 4; sector_skip = 0; break; } default: throw new ArgumentException("Unsupported tag requested", "tag"); } break; } case CDRWinTrackTypeAudio: throw new ArgumentException("There are no tags on audio tracks", "tag"); case CDRWinTrackTypeMode1Raw: { switch (tag) { case SectorTagType.CDSectorSync: { sector_offset = 0; sector_size = 12; sector_skip = 2340; break; } case SectorTagType.CDSectorHeader: { sector_offset = 12; sector_size = 4; sector_skip = 2336; break; } case SectorTagType.CDSectorSubchannel: case SectorTagType.CDSectorSubHeader: throw new ArgumentException("Unsupported tag requested for this track", "tag"); case SectorTagType.CDSectorECC: { sector_offset = 2076; sector_size = 276; sector_skip = 0; break; } case SectorTagType.CDSectorECC_P: { sector_offset = 2076; sector_size = 172; sector_skip = 104; break; } case SectorTagType.CDSectorECC_Q: { sector_offset = 2248; sector_size = 104; sector_skip = 0; break; } case SectorTagType.CDSectorEDC: { sector_offset = 2064; sector_size = 4; sector_skip = 284; break; } default: throw new ArgumentException("Unsupported tag requested", "tag"); } break; } case CDRWinTrackTypeMode2Raw: // Requires reading sector case CDRWinTrackTypeCDIRaw: throw new FeatureSupportedButNotImplementedImageException("Feature not yet implemented"); case CDRWinTrackTypeCDG: { if (tag != SectorTagType.CDSectorSubchannel) throw new ArgumentException("Unsupported tag requested for this track", "tag"); sector_offset = 2352; sector_size = 96; sector_skip = 0; break; } default: throw new FeatureSupportedButNotImplementedImageException("Unsupported track type"); } byte[] buffer = new byte[sector_size * length]; imageStream = new FileStream(_track.trackfile.datafile, FileMode.Open, FileAccess.Read); using (BinaryReader br = new BinaryReader(imageStream)) { br.BaseStream.Seek((long)_track.trackfile.offset + (long)(sectorAddress * (sector_offset + sector_size + sector_skip)), SeekOrigin.Begin); if (sector_offset == 0 && sector_skip == 0) buffer = br.ReadBytes((int)(sector_size * length)); else { for (int i = 0; i < length; i++) { byte[] sector; br.BaseStream.Seek(sector_offset, SeekOrigin.Current); sector = br.ReadBytes((int)sector_size); br.BaseStream.Seek(sector_skip, SeekOrigin.Current); Array.Copy(sector, 0, buffer, i * sector_size, sector_size); } } } return buffer; } public override byte[] ReadSectorLong(UInt64 sectorAddress) { return ReadSectorsLong(sectorAddress, 1); } public override byte[] ReadSectorLong(UInt64 sectorAddress, UInt32 track) { return ReadSectorsLong(sectorAddress, 1, track); } public override byte[] ReadSectorsLong(UInt64 sectorAddress, UInt32 length) { foreach (KeyValuePair<uint, ulong> kvp in offsetmap) { if (sectorAddress >= kvp.Value) { foreach (CDRWinTrack cdrwin_track in discimage.tracks) { if (cdrwin_track.sequence == kvp.Key) { if ((sectorAddress - kvp.Value) < cdrwin_track.sectors) return ReadSectorsLong((sectorAddress - kvp.Value), length, kvp.Key); } } } } throw new ArgumentOutOfRangeException("sectorAddress", "Sector address not found"); } public override byte[] ReadSectorsLong(UInt64 sectorAddress, UInt32 length, UInt32 track) { CDRWinTrack _track = new CDRWinTrack(); _track.sequence = 0; foreach (CDRWinTrack cdrwin_track in discimage.tracks) { if (cdrwin_track.sequence == track) { _track = cdrwin_track; break; } } if (_track.sequence == 0) throw new ArgumentOutOfRangeException("track", "Track does not exist in disc image"); if (length > _track.sectors) throw new ArgumentOutOfRangeException("length", "Requested more sectors than present in track, won't cross tracks"); uint sector_offset; uint sector_size; uint sector_skip; switch (_track.tracktype) { case CDRWinTrackTypeMode1: case CDRWinTrackTypeMode2Form1: { sector_offset = 0; sector_size = 2048; sector_skip = 0; break; } case CDRWinTrackTypeMode2Form2: { sector_offset = 0; sector_size = 2324; sector_skip = 0; break; } case CDRWinTrackTypeMode2Formless: case CDRWinTrackTypeCDI: { sector_offset = 0; sector_size = 2336; sector_skip = 0; break; } case CDRWinTrackTypeMode1Raw: case CDRWinTrackTypeMode2Raw: case CDRWinTrackTypeCDIRaw: case CDRWinTrackTypeAudio: { sector_offset = 0; sector_size = 2352; sector_skip = 0; break; } case CDRWinTrackTypeCDG: { sector_offset = 0; sector_size = 2448; sector_skip = 0; break; } default: throw new FeatureSupportedButNotImplementedImageException("Unsupported track type"); } byte[] buffer = new byte[sector_size * length]; imageStream = new FileStream(_track.trackfile.datafile, FileMode.Open, FileAccess.Read); BinaryReader br = new BinaryReader(imageStream); br.BaseStream.Seek((long)_track.trackfile.offset + (long)(sectorAddress * (sector_offset + sector_size + sector_skip)), SeekOrigin.Begin); if (sector_offset == 0 && sector_skip == 0) buffer = br.ReadBytes((int)(sector_size * length)); else { for (int i = 0; i < length; i++) { byte[] sector; br.BaseStream.Seek(sector_offset, SeekOrigin.Current); sector = br.ReadBytes((int)sector_size); br.BaseStream.Seek(sector_skip, SeekOrigin.Current); Array.Copy(sector, 0, buffer, i * sector_size, sector_size); } } return buffer; } public override string GetImageFormat() { return "CDRWin CUESheet"; } public override string GetImageVersion() { return ImageInfo.imageVersion; } public override string GetImageApplication() { return ImageInfo.imageApplication; } public override string GetImageApplicationVersion() { return ImageInfo.imageApplicationVersion; } public override DateTime GetImageCreationTime() { return ImageInfo.imageCreationTime; } public override DateTime GetImageLastModificationTime() { return ImageInfo.imageLastModificationTime; } public override string GetImageComments() { return ImageInfo.imageComments; } public override string GetDiskSerialNumber() { return ImageInfo.diskSerialNumber; } public override string GetDiskBarcode() { return ImageInfo.diskBarcode; } public override DiskType GetDiskType() { return ImageInfo.diskType; } public override List<PartPlugins.Partition> GetPartitions() { return partitions; } public override List<Track> GetTracks() { List<Track> tracks = new List<Track>(); UInt64 previousStartSector = 0; foreach (CDRWinTrack cdr_track in discimage.tracks) { Track _track = new Track(); _track.Indexes = cdr_track.indexes; _track.TrackDescription = cdr_track.title; if (!cdr_track.indexes.TryGetValue(0, out _track.TrackStartSector)) cdr_track.indexes.TryGetValue(1, out _track.TrackStartSector); _track.TrackStartSector += previousStartSector; _track.TrackEndSector = _track.TrackStartSector + cdr_track.sectors - 1; _track.TrackPregap = cdr_track.pregap; _track.TrackSession = cdr_track.session; _track.TrackSequence = cdr_track.sequence; _track.TrackType = CDRWinTrackTypeToTrackType(cdr_track.tracktype); tracks.Add(_track); previousStartSector = _track.TrackEndSector + 1; } return tracks; } public override List<Track> GetSessionTracks(Session session) { if (discimage.sessions.Contains(session)) { return GetSessionTracks(session.SessionSequence); } throw new ImageNotSupportedException("Session does not exist in disc image"); } public override List<Track> GetSessionTracks(UInt16 session) { List<Track> tracks = new List<Track>(); foreach (CDRWinTrack cdr_track in discimage.tracks) { if (cdr_track.session == session) { Track _track = new Track(); _track.Indexes = cdr_track.indexes; _track.TrackDescription = cdr_track.title; if (!cdr_track.indexes.TryGetValue(0, out _track.TrackStartSector)) cdr_track.indexes.TryGetValue(1, out _track.TrackStartSector); _track.TrackEndSector = _track.TrackStartSector + cdr_track.sectors - 1; _track.TrackPregap = cdr_track.pregap; _track.TrackSession = cdr_track.session; _track.TrackSequence = cdr_track.sequence; _track.TrackType = CDRWinTrackTypeToTrackType(cdr_track.tracktype); tracks.Add(_track); } } return tracks; } public override List<Session> GetSessions() { return discimage.sessions; } public override bool? VerifySector(UInt64 sectorAddress) { byte[] buffer = ReadSectorLong(sectorAddress); return Checksums.CDChecksums.CheckCDSector(buffer); } public override bool? VerifySector(UInt64 sectorAddress, UInt32 track) { byte[] buffer = ReadSectorLong(sectorAddress, track); return Checksums.CDChecksums.CheckCDSector(buffer); } public override bool? VerifySectors(UInt64 sectorAddress, UInt32 length, out List<UInt64> FailingLBAs, out List<UInt64> UnknownLBAs) { byte[] buffer = ReadSectorsLong(sectorAddress, length); int bps = (int)(buffer.Length / length); byte[] sector = new byte[bps]; FailingLBAs = new List<UInt64>(); UnknownLBAs = new List<UInt64>(); for (int i = 0; i < length; i++) { Array.Copy(buffer, i * bps, sector, 0, bps); bool? sectorStatus = Checksums.CDChecksums.CheckCDSector(sector); switch (sectorStatus) { case null: UnknownLBAs.Add((ulong)i + sectorAddress); break; case false: FailingLBAs.Add((ulong)i + sectorAddress); break; } } if (UnknownLBAs.Count > 0) return null; if (FailingLBAs.Count > 0) return false; return true; } public override bool? VerifySectors(UInt64 sectorAddress, UInt32 length, UInt32 track, out List<UInt64> FailingLBAs, out List<UInt64> UnknownLBAs) { byte[] buffer = ReadSectorsLong(sectorAddress, length, track); int bps = (int)(buffer.Length / length); byte[] sector = new byte[bps]; FailingLBAs = new List<UInt64>(); UnknownLBAs = new List<UInt64>(); for (int i = 0; i < length; i++) { Array.Copy(buffer, i * bps, sector, 0, bps); bool? sectorStatus = Checksums.CDChecksums.CheckCDSector(sector); switch (sectorStatus) { case null: UnknownLBAs.Add((ulong)i + sectorAddress); break; case false: FailingLBAs.Add((ulong)i + sectorAddress); break; } } if (UnknownLBAs.Count > 0) return null; if (FailingLBAs.Count > 0) return false; return true; } public override bool? VerifyDiskImage() { return null; } #endregion #region Private methods static UInt64 CDRWinMSFToLBA(string MSF) { string[] MSFElements; UInt64 minute, second, frame, sectors; MSFElements = MSF.Split(':'); minute = UInt64.Parse(MSFElements[0]); second = UInt64.Parse(MSFElements[1]); frame = UInt64.Parse(MSFElements[2]); sectors = (minute * 60 * 75) + (second * 75) + frame; return sectors; } static UInt16 CDRWinTrackTypeToBytesPerSector(string trackType) { switch (trackType) { case CDRWinTrackTypeMode1: case CDRWinTrackTypeMode2Form1: return 2048; case CDRWinTrackTypeMode2Form2: return 2324; case CDRWinTrackTypeMode2Formless: case CDRWinTrackTypeCDI: return 2336; case CDRWinTrackTypeAudio: case CDRWinTrackTypeMode1Raw: case CDRWinTrackTypeMode2Raw: case CDRWinTrackTypeCDIRaw: return 2352; case CDRWinTrackTypeCDG: return 2448; default: return 0; } } static TrackType CDRWinTrackTypeToTrackType(string trackType) { switch (trackType) { case CDRWinTrackTypeMode1: case CDRWinTrackTypeMode1Raw: return TrackType.CDMode1; case CDRWinTrackTypeMode2Form1: return TrackType.CDMode2Form1; case CDRWinTrackTypeMode2Form2: return TrackType.CDMode2Form2; case CDRWinTrackTypeCDIRaw: case CDRWinTrackTypeCDI: case CDRWinTrackTypeMode2Raw: case CDRWinTrackTypeMode2Formless: return TrackType.CDMode2Formless; case CDRWinTrackTypeAudio: case CDRWinTrackTypeCDG: return TrackType.Audio; default: return TrackType.Data; } } static DiskType CDRWinIsoBusterDiscTypeToDiskType(string discType) { switch (discType) { case CDRWinDiskTypeCD: return DiskType.CD; case CDRWinDiskTypeCDRW: case CDRWinDiskTypeCDMRW: case CDRWinDiskTypeCDMRW2: return DiskType.CDRW; case CDRWinDiskTypeDVD: return DiskType.DVDROM; case CDRWinDiskTypeDVDPRW: case CDRWinDiskTypeDVDPMRW: case CDRWinDiskTypeDVDPMRW2: return DiskType.DVDPRW; case CDRWinDiskTypeDVDPRWDL: case CDRWinDiskTypeDVDPMRWDL: case CDRWinDiskTypeDVDPMRWDL2: return DiskType.DVDPRWDL; case CDRWinDiskTypeDVDPR: case CDRWinDiskTypeDVDPVR: return DiskType.DVDPR; case CDRWinDiskTypeDVDPRDL: return DiskType.DVDPRDL; case CDRWinDiskTypeDVDRAM: return DiskType.DVDRAM; case CDRWinDiskTypeDVDVR: case CDRWinDiskTypeDVDR: return DiskType.DVDR; case CDRWinDiskTypeDVDRDL: return DiskType.DVDRDL; case CDRWinDiskTypeDVDRW: case CDRWinDiskTypeDVDRWDL: case CDRWinDiskTypeDVDRW2: return DiskType.DVDRW; case CDRWinDiskTypeHDDVD: return DiskType.HDDVDROM; case CDRWinDiskTypeHDDVDRAM: return DiskType.HDDVDRAM; case CDRWinDiskTypeHDDVDR: case CDRWinDiskTypeHDDVDRDL: return DiskType.HDDVDR; case CDRWinDiskTypeHDDVDRW: case CDRWinDiskTypeHDDVDRWDL: return DiskType.HDDVDRW; case CDRWinDiskTypeBD: return DiskType.BDROM; case CDRWinDiskTypeBDR: case CDRWinDiskTypeBDRDL: return DiskType.BDR; case CDRWinDiskTypeBDRE: case CDRWinDiskTypeBDREDL: return DiskType.BDRE; default: return DiskType.Unknown; } } #endregion #region Unsupported features public override int GetDiskSequence() { return ImageInfo.diskSequence; } public override int GetLastDiskSequence() { return ImageInfo.lastDiskSequence; } public override string GetDriveManufacturer() { return ImageInfo.driveManufacturer; } public override string GetDriveModel() { return ImageInfo.driveModel; } public override string GetDriveSerialNumber() { return ImageInfo.driveSerialNumber; } public override string GetDiskPartNumber() { return ImageInfo.diskPartNumber; } public override string GetDiskManufacturer() { return ImageInfo.diskManufacturer; } public override string GetDiskModel() { return ImageInfo.diskModel; } public override string GetImageName() { return ImageInfo.imageName; } public override string GetImageCreator() { return ImageInfo.imageCreator; } #endregion } }