From 3b368e0bd4369803d1422b2eec273e0b3e24af03 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 23 Dec 2015 23:46:31 +0000 Subject: [PATCH] * TODO: * README.md: * DiscImageChef.DiscImages/CDRDAO.cs: * DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj: Added support for cdrdao images. Solves #53 * DiscImageChef.DiscImages/CDRWin.cs: Implemented FLAGS field support and quadraphonic/broadcasting flag. Return CD track flags for any track type. * DiscImageChef.Filesystems/ISO9660.cs: Filter null characters from Volume Descriptors. If Joliet descriptor is null, or ISO descriptor is longer, use ISO descriptor for XML metadata. * DiscImageChef/Commands/CreateSidecar.cs: If a filesystem plugin crashes, do not abort. * DiscImageChef/DetectImageFormat.cs: If an image plugin crashes, keep trying the others. --- DiscImageChef.DiscImages/CDRDAO.cs | 1786 +++++++++++++++++ DiscImageChef.DiscImages/CDRWin.cs | 16 +- DiscImageChef.DiscImages/ChangeLog | 11 + .../DiscImageChef.DiscImages.csproj | 1 + DiscImageChef.Filesystems/ChangeLog | 7 + DiscImageChef.Filesystems/ISO9660.cs | 56 +- DiscImageChef/ChangeLog | 8 + DiscImageChef/Commands/CreateSidecar.cs | 30 +- DiscImageChef/DetectImageFormat.cs | 28 +- README.md | 1 + TODO | 1 - 11 files changed, 1906 insertions(+), 39 deletions(-) create mode 100644 DiscImageChef.DiscImages/CDRDAO.cs diff --git a/DiscImageChef.DiscImages/CDRDAO.cs b/DiscImageChef.DiscImages/CDRDAO.cs new file mode 100644 index 00000000..a08cd313 --- /dev/null +++ b/DiscImageChef.DiscImages/CDRDAO.cs @@ -0,0 +1,1786 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : CDRDAO.cs +// Version : 1.0 +// Author(s) : Natalia Portillo +// +// Component : Component +// +// Revision : $Revision$ +// Last change by : $Author$ +// Date : $Date$ +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ 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-2015 Claunia.com +// ****************************************************************************/ +// //$Id$ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using DiscImageChef.Console; +using DiscImageChef.CommonTypes; +using System.Text; + +namespace DiscImageChef.ImagePlugins +{ + // TODO: Doesn't support compositing from several files + // TODO: Doesn't support silences that are not in files + class CDRDAO : ImagePlugin + { + #region Internal structures + + struct CDRDAOTrackFile + { + /// Track # + public UInt32 sequence; + /// Path of file containing track + public string datafile; + /// Offset of track start in file + public UInt64 offset; + /// Type of file + public string filetype; + } + + struct CDRDAOTrack + { + /// Track # + public UInt32 sequence; + /// Track title (from CD-Text) + public string title; + /// Track genre (from CD-Text) + public string genre; + /// Track arranger (from CD-Text) + public string arranger; + /// Track composer (from CD-Text) + public string composer; + /// Track performer (from CD-Text) + public string performer; + /// Track song writer (from CD-Text) + public string songwriter; + /// Track ISRC + public string isrc; + /// Disk provider's message (from CD-Text) + public string message; + /// File struct for this track + public CDRDAOTrackFile trackfile; + /// Indexes on this track + public Dictionary indexes; + /// Track pre-gap in sectors + public UInt64 pregap; + /// Track post-gap in sectors + public UInt64 postgap; + /// Digical Copy Permitted + public bool flag_dcp; + /// Track is quadraphonic + public bool flag_4ch; + /// Track has preemphasis + public bool flag_pre; + /// Bytes per sector + public UInt16 bps; + /// Sectors in track + public UInt64 sectors; + /// Starting sector in track + public UInt64 startSector; + /// Track type + public string tracktype; + public bool subchannel; + public bool packedsubchannel; + } + + struct CDRDAODisc + { + /// Disk title (from CD-Text) + public string title; + /// Disk genre (from CD-Text) + public string genre; + /// Disk arranger (from CD-Text) + public string arranger; + /// Disk composer (from CD-Text) + public string composer; + /// Disk performer (from CD-Text) + public string performer; + /// Disk song writer (from CD-Text) + public string songwriter; + /// Disk provider's message (from CD-Text) + public string message; + /// Media catalog number + public string mcn; + /// Disk type + public DiskType disktype; + /// Disk type string + public string disktypestr; + /// Disk CDDB ID + public string disk_id; + /// Disk UPC/EAN + public string barcode; + /// Tracks + public List tracks; + /// Disk comment + public string comment; + } + + #endregion Internal structures + + #region Internal consts + + /// Audio track, 2352 bytes/sector + const string CDRDAOTrackTypeAudio = "AUDIO"; + /// Mode 1 track, cooked, 2048 bytes/sector + const string CDRDAOTrackTypeMode1 = "MODE1"; + /// Mode 1 track, raw, 2352 bytes/sector + const string CDRDAOTrackTypeMode1Raw = "MODE1_RAW"; + /// Mode 2 mixed formless, cooked, 2336 bytes/sector + const string CDRDAOTrackTypeMode2 = "MODE2"; + /// Mode 2 form 1 track, cooked, 2048 bytes/sector + const string CDRDAOTrackTypeMode2Form1 = "MODE2_FORM1"; + /// Mode 2 form 2 track, cooked, 2324 bytes/sector + const string CDRDAOTrackTypeMode2Form2 = "MODE2_FORM2"; + /// Mode 2 mixed forms track, cooked, 2336 bytes/sector + const string CDRDAOTrackTypeMode2Mix = "MODE2_FORM_MIX"; + /// Mode 2 track, raw, 2352 bytes/sector + const string CDRDAOTrackTypeMode2Raw = "MODE2_RAW"; + + #endregion Internal consts + + #region Internal variables + + string imagePath; + StreamReader tocStream; + FileStream imageStream; + /// Dictionary, index is track #, value is TrackFile + Dictionary offsetmap; + List partitions; + CDRDAODisc discimage; + + #endregion + + #region Parsing regexs + + const string CommentRegEx = "^\\s*\\/{2}(?.+)$"; + const string DiskTypeRegEx = "^\\s*(?(CD_DA|CD_ROM_XA|CD_ROM|CD_I))"; + const string MCNRegEx = "^\\s*CATALOG\\s*\"(?[\\d]{13,13})\""; + const string TrackRegEx = "^\\s*TRACK\\s*(?(AUDIO|MODE1_RAW|MODE1|MODE2_FORM1|MODE2_FORM2|MODE2_FORM_MIX|MODE2_RAW|MODE2))\\s*(?(RW_RAW|RW))?"; + const string CopyRegEx = "^\\s*(?NO)?\\s*COPY"; + const string EmphasisRegEx = "^\\s*(?NO)?\\s*PRE_EMPHASIS"; + const string StereoRegEx = "^\\s*(?(TWO|FOUR))_CHANNEL_AUDIO"; + const string ISRCRegEx = "^\\s*ISRC\\s*\"(?[A-Z0-9]{5,5}[0-9]{7,7})\""; + const string IndexRegEx = "^\\s*INDEX\\s*(?
\\d+:\\d+:\\d+)"; + const string PregapRegEx = "^\\s*START\\s*(?
\\d+:\\d+:\\d+)?"; + const string ZeroPregapRegEx = "^\\s*PREGAP\\s*(?\\d+:\\d+:\\d+)"; + const string ZeroDataRegEx = "^\\s*ZERO\\s*(?\\d+:\\d+:\\d+)"; + const string ZeroAudioRegEx = "^\\s*SILENCE\\s*(?\\d+:\\d+:\\d+)"; + const string AudioFileRegEx = "^\\s*(AUDIO)?FILE\\s*\"(?.+)\"\\s*(#(?\\d+))?\\s*((?[\\d]+:[\\d]+:[\\d]+)|(?\\d+))\\s*(?[\\d]+:[\\d]+:[\\d]+)?"; + const string FileRegEx = "^\\s*DATAFILE\\s*\"(?.+)\"\\s*(#(?\\d+))?\\s*(?[\\d]+:[\\d]+:[\\d]+)?"; + + // CD-Text + const string TitleRegEx = "^\\s*TITLE\\s*\"(?.+)\""; + const string PerformerRegEx = "^\\s*PERFORMER\\s*\"(?<performer>.+)\""; + const string SongwriterRegEx = "^\\s*SONGWRITER\\s*\"(?<songwriter>.+)\""; + const string ComposerRegEx = "^\\s*COMPOSER\\s*\"(?<composer>.+)\""; + const string ArrangerRegEx = "^\\s*ARRANGER\\s*\"(?<arranger>.+)\""; + const string MessageRegEx = "^\\s*MESSAGE\\s*\"(?<message>.+)\""; + const string DiscIDRegEx = "^\\s*DISC_ID\\s*\"(?<discid>.+)\""; + const string UPCRegEx = "^\\s*UPC_EAN\\s*\"(?<catalog>[\\d]{13,13})\""; + + // Unused + const string CDTextRegEx = "^\\s*CD_TEXT\\s*\\{"; + const string LanguageRegEx = "^\\s*LANGUAGE\\s*(?<code>\\d+)\\s*\\{"; + const string ClosureRegEx = "^\\s*\\}"; + const string LanguageMapRegEx = "^\\s*LANGUAGE_MAP\\s*\\{"; + const string LanguageMappingRegEx = "^\\s*(?<code>\\d+)\\s?\\:\\s?(?<language>\\d+|\\w+)"; + + #endregion + + #region Public methods + + public CDRDAO() + { + Name = "CDRDAO tocfile"; + PluginUUID = new Guid("04D7BA12-1BE8-44D4-97A4-1B48A505463E"); + 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; + } + + #endregion Public methods + + public override bool IdentifyImage(string imagePath) + { + this.imagePath = imagePath; + + try + { + tocStream = new StreamReader(this.imagePath); + string _line = tocStream.ReadLine(); + + Regex Dr = new Regex(DiskTypeRegEx); + Match Dm; + + Dm = Dr.Match(_line); + + return Dm.Success; + } + catch (Exception ex) + { + DicConsole.ErrorWriteLine("Exception trying to identify image file {0}", this.imagePath); + DicConsole.ErrorWriteLine("Exception: {0}", ex.Message); + DicConsole.ErrorWriteLine("Stack trace: {0}", ex.StackTrace); + return false; + } + } + + #region Not implemented methods + + public override bool OpenImage(string imagePath) + { + if (imagePath == null) + return false; + if (imagePath == "") + return false; + + this.imagePath = imagePath; + + try + { + tocStream = new StreamReader(imagePath); + int line = 0; + bool intrack = false; + + // Initialize all RegExs + Regex RegexComment = new Regex(CommentRegEx); + Regex RegexDiskType = new Regex(DiskTypeRegEx); + Regex RegexMCN = new Regex(MCNRegEx); + Regex RegexTrack = new Regex(TrackRegEx); + Regex RegexCopy = new Regex(CopyRegEx); + Regex RegexEmphasis = new Regex(EmphasisRegEx); + Regex RegexStereo = new Regex(StereoRegEx); + Regex RegexISRC = new Regex(ISRCRegEx); + Regex RegexIndex = new Regex(IndexRegEx); + Regex RegexPregap = new Regex(PregapRegEx); + Regex RegexZeroPregap = new Regex(ZeroPregapRegEx); + Regex RegexZeroData = new Regex(ZeroDataRegEx); + Regex RegexZeroAudio = new Regex(ZeroAudioRegEx); + Regex RegexAudioFile = new Regex(AudioFileRegEx); + Regex RegexFile = new Regex(FileRegEx); + Regex RegexTitle = new Regex(TitleRegEx); + Regex RegexPerformer = new Regex(PerformerRegEx); + Regex RegexSongwriter = new Regex(SongwriterRegEx); + Regex RegexComposer = new Regex(ComposerRegEx); + Regex RegexArranger = new Regex(ArrangerRegEx); + Regex RegexMessage = new Regex(MessageRegEx); + Regex RegexDiscID = new Regex(DiscIDRegEx); + Regex RegexUPC = new Regex(UPCRegEx); + Regex RegexCDText = new Regex(CDTextRegEx); + Regex RegexLanguage = new Regex(LanguageRegEx); + Regex RegexClosure = new Regex(ClosureRegEx); + Regex RegexLanguageMap = new Regex(LanguageMapRegEx); + Regex RegexLanguageMapping = new Regex(LanguageMappingRegEx); + + // Initialize all RegEx matches + Match MatchComment; + Match MatchDiskType; + Match MatchMCN; + Match MatchTrack; + Match MatchCopy; + Match MatchEmphasis; + Match MatchStereo; + Match MatchISRC; + Match MatchIndex; + Match MatchPregap; + Match MatchZeroPregap; + Match MatchZeroData; + Match MatchZeroAudio; + Match MatchAudioFile; + Match MatchFile; + Match MatchTitle; + Match MatchPerformer; + Match MatchSongwriter; + Match MatchComposer; + Match MatchArranger; + Match MatchMessage; + Match MatchDiscID; + Match MatchUPC; + Match MatchCDText; + Match MatchLanguage; + Match MatchClosure; + Match MatchLanguageMap; + Match MatchLanguageMapping; + + // Initialize disc + discimage = new CDRDAODisc(); + discimage.tracks = new List<CDRDAOTrack>(); + discimage.comment = ""; + + CDRDAOTrack currenttrack = new CDRDAOTrack(); + uint currentTrackNumber = 0; + currenttrack.indexes = new Dictionary<int, ulong>(); + currenttrack.pregap = 0; + ulong currentSector = 0; + int nextindex = 2; + StringBuilder commentBuilder = new StringBuilder(); + + tocStream = new StreamReader(this.imagePath); + string _line = tocStream.ReadLine(); + + MatchDiskType = RegexDiskType.Match(_line); + + if (!MatchDiskType.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Not a CDRDAO TOC or TOC type not in first line."); + return false; + } + + tocStream.Close(); + tocStream = new StreamReader(this.imagePath); + + while (tocStream.Peek() >= 0) + { + line++; + _line = tocStream.ReadLine(); + + MatchComment = RegexComment.Match(_line); + MatchDiskType = RegexDiskType.Match(_line); + MatchMCN = RegexMCN.Match(_line); + MatchTrack = RegexTrack.Match(_line); + MatchCopy = RegexCopy.Match(_line); + MatchEmphasis = RegexEmphasis.Match(_line); + MatchStereo = RegexStereo.Match(_line); + MatchISRC = RegexISRC.Match(_line); + MatchIndex = RegexIndex.Match(_line); + MatchPregap = RegexPregap.Match(_line); + MatchZeroPregap = RegexZeroPregap.Match(_line); + MatchZeroData = RegexZeroData.Match(_line); + MatchZeroAudio = RegexZeroAudio.Match(_line); + MatchAudioFile = RegexAudioFile.Match(_line); + MatchFile = RegexFile.Match(_line); + MatchTitle = RegexTitle.Match(_line); + MatchPerformer = RegexPerformer.Match(_line); + MatchSongwriter = RegexSongwriter.Match(_line); + MatchComposer = RegexComposer.Match(_line); + MatchArranger = RegexArranger.Match(_line); + MatchMessage = RegexMessage.Match(_line); + MatchDiscID = RegexDiscID.Match(_line); + MatchUPC = RegexUPC.Match(_line); + MatchCDText = RegexCDText.Match(_line); + MatchLanguage = RegexLanguage.Match(_line); + MatchClosure = RegexClosure.Match(_line); + MatchLanguageMap = RegexLanguageMap.Match(_line); + MatchLanguageMapping = RegexLanguageMapping.Match(_line); + + if (MatchComment.Success) + { + // Ignore "// Track X" comments + if (!MatchComment.Groups["comment"].Value.StartsWith(" Track ", StringComparison.Ordinal)) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found comment \"{1}\" at line {0}", line, MatchComment.Groups["comment"].Value.Trim()); + commentBuilder.AppendLine(MatchComment.Groups["comment"].Value.Trim()); + } + } + else if (MatchDiskType.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found {1} at line {0}", line, MatchDiskType.Groups["type"].Value); + discimage.disktypestr = MatchDiskType.Groups["type"].Value; + switch (MatchDiskType.Groups["type"].Value) + { + case "CD_DA": + discimage.disktype = DiskType.CDDA; + break; + case "CD_ROM": + discimage.disktype = DiskType.CDROM; + break; + case "CD_ROM_XA": + discimage.disktype = DiskType.CDROMXA; + break; + case "CD_I": + discimage.disktype = DiskType.CDI; + break; + default: + discimage.disktype = DiskType.CD; + break; + } + } + else if (MatchMCN.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found CATALOG \"{1}\" at line {0}", line, MatchMCN.Groups["catalog"].Value); + discimage.mcn = MatchMCN.Groups["catalog"].Value; + } + else if (MatchTrack.Success) + { + if (MatchTrack.Groups["subchan"].Value == "") + DicConsole.DebugWriteLine("CDRDAO plugin", "Found TRACK type \"{1}\" with no subchannel at line {0}", line, MatchTrack.Groups["type"].Value); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "Found TRACK type \"{1}\" subchannel {2} at line {0}", line, MatchTrack.Groups["type"].Value, MatchTrack.Groups["subchan"].Value); + + if (intrack) + { + currentSector += currenttrack.sectors; + if(currenttrack.pregap != currenttrack.sectors && !currenttrack.indexes.ContainsKey(1)) + currenttrack.indexes.Add(1, currenttrack.startSector + currenttrack.pregap); + discimage.tracks.Add(currenttrack); + currenttrack = new CDRDAOTrack(); + currenttrack.indexes = new Dictionary<int, ulong>(); + currenttrack.pregap = 0; + nextindex = 2; + } + currentTrackNumber++; + intrack = true; + + switch (MatchTrack.Groups["type"].Value) + { + case "AUDIO": + case "MODE1_RAW": + case "MODE2_RAW": + currenttrack.bps = 2352; + break; + case "MODE1": + case "MODE2_FORM1": + currenttrack.bps = 2048; + break; + case "MODE2_FORM2": + currenttrack.bps = 2324; + break; + case "MODE2": + case "MODE2_FORM_MIX": + currenttrack.bps = 2336; + break; + default: + throw new NotSupportedException(String.Format("Track mode {0} is unsupported", MatchTrack.Groups["type"].Value)); + } + + switch (MatchTrack.Groups["subchan"].Value) + { + case "": + break; + case "RW": + currenttrack.packedsubchannel = true; + goto case "RW_RAW"; + case "RW_RAW": + currenttrack.bps += 96; + currenttrack.subchannel = true; + break; + default: + throw new NotSupportedException(String.Format("Track subchannel mode {0} is unsupported", MatchTrack.Groups["subchan"].Value)); + } + + currenttrack.tracktype = MatchTrack.Groups["type"].Value; + + currenttrack.sequence = currentTrackNumber; + currenttrack.startSector = currentSector; + } + else if (MatchCopy.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found {1} COPY at line {0}", line, MatchCopy.Groups["no"].Value); + currenttrack.flag_dcp |= intrack && MatchCopy.Groups["no"].Value == ""; + } + else if (MatchEmphasis.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found {1} PRE_EMPHASIS at line {0}", line, MatchEmphasis.Groups["no"].Value); + currenttrack.flag_pre |= intrack && MatchCopy.Groups["no"].Value == ""; + } + else if (MatchStereo.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found {1}_CHANNEL_AUDIO at line {0}", line, MatchStereo.Groups["num"].Value); + currenttrack.flag_4ch |= intrack && MatchCopy.Groups["num"].Value == "FOUR"; + } + else if (MatchISRC.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found ISRC \"{1}\" at line {0}", line, MatchISRC.Groups["isrc"].Value); + if (intrack) + currenttrack.isrc = MatchISRC.Groups["isrc"].Value; + } + else if (MatchIndex.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found INDEX \"{1}\" at line {0}", line, MatchIndex.Groups["address"].Value); + + string[] lengthString = MatchFile.Groups["length"].Value.Split(new char[] { ':' }); + ulong nextIndexPos = ulong.Parse(lengthString[0]) * 60 * 75 + ulong.Parse(lengthString[1]) * 75 + ulong.Parse(lengthString[2]); + currenttrack.indexes.Add(nextindex, nextIndexPos + currenttrack.pregap + currenttrack.startSector); + } + else if (MatchPregap.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found START \"{1}\" at line {0}", line, MatchPregap.Groups["address"].Value); + + currenttrack.indexes.Add(0, currenttrack.startSector); + if(MatchPregap.Groups["address"].Value != "") + { + string[] lengthString = MatchPregap.Groups["address"].Value.Split(new char[] { ':' }); + currenttrack.pregap = ulong.Parse(lengthString[0]) * 60 * 75 + ulong.Parse(lengthString[1]) * 75 + ulong.Parse(lengthString[2]); + } + else + currenttrack.pregap = currenttrack.sectors; + } + else if (MatchZeroPregap.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found PREGAP \"{1}\" at line {0}", line, MatchZeroPregap.Groups["length"].Value); + currenttrack.indexes.Add(0, currenttrack.startSector); + string[] lengthString = MatchZeroPregap.Groups["length"].Value.Split(new char[] { ':' }); + currenttrack.pregap = ulong.Parse(lengthString[0]) * 60 * 75 + ulong.Parse(lengthString[1]) * 75 + ulong.Parse(lengthString[2]); + } + else if (MatchZeroData.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found ZERO \"{1}\" at line {0}", line, MatchZeroData.Groups["length"].Value); + // Seems can be ignored as the data is still in the image + } + else if (MatchZeroAudio.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found SILENCE \"{1}\" at line {0}", line, MatchZeroAudio.Groups["length"].Value); + // Seems can be ignored as the data is still in the image + } + else if (MatchAudioFile.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found AUDIOFILE \"{1}\" at line {0}", line, MatchAudioFile.Groups["filename"].Value); + + currenttrack.trackfile = new CDRDAOTrackFile(); + currenttrack.trackfile.datafile = MatchAudioFile.Groups["filename"].Value; + currenttrack.trackfile.offset = MatchAudioFile.Groups["base_offset"].Value != "" ? ulong.Parse(MatchAudioFile.Groups["base_offset"].Value) : 0; + + currenttrack.trackfile.filetype = "BINARY"; + currenttrack.trackfile.sequence = currentTrackNumber; + + ulong startSectors = 0; + + if (MatchFile.Groups["start"].Value != "") + { + string[] startString = MatchAudioFile.Groups["start"].Value.Split(new char[] { ':' }); + startSectors = ulong.Parse(startString[0]) * 60 * 75 + ulong.Parse(startString[1]) * 75 + ulong.Parse(startString[2]); + } + + currenttrack.trackfile.offset += (startSectors * currenttrack.bps); + + if (MatchFile.Groups["length"].Value != "") + { + string[] lengthString = MatchAudioFile.Groups["length"].Value.Split(new char[] { ':' }); + currenttrack.sectors = ulong.Parse(lengthString[0]) * 60 * 75 + ulong.Parse(lengthString[1]) * 75 + ulong.Parse(lengthString[2]); + } + else + { + FileInfo pfi = new FileInfo(currenttrack.trackfile.datafile); + currenttrack.sectors = ((ulong)pfi.Length - currenttrack.trackfile.offset) / currenttrack.bps; + } + } + else if (MatchFile.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found DATAFILE \"{1}\" at line {0}", line, MatchFile.Groups["filename"].Value); + + currenttrack.trackfile = new CDRDAOTrackFile(); + currenttrack.trackfile.datafile = MatchFile.Groups["filename"].Value; + currenttrack.trackfile.offset = MatchFile.Groups["base_offset"].Value != "" ? ulong.Parse(MatchFile.Groups["base_offset"].Value) : 0; + + currenttrack.trackfile.filetype = "BINARY"; + currenttrack.trackfile.sequence = currentTrackNumber; + if (MatchFile.Groups["length"].Value != "") + { + string[] lengthString = MatchFile.Groups["length"].Value.Split(new char[] { ':' }); + currenttrack.sectors = ulong.Parse(lengthString[0]) * 60 * 75 + ulong.Parse(lengthString[1]) * 75 + ulong.Parse(lengthString[2]); + } + else + { + FileInfo pfi = new FileInfo(currenttrack.trackfile.datafile); + currenttrack.sectors = ((ulong)pfi.Length - currenttrack.trackfile.offset) / currenttrack.bps; + } + } + else if (MatchTitle.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found TITLE \"{1}\" at line {0}", line, MatchTitle.Groups["title"].Value); + if (intrack) + currenttrack.title = MatchTitle.Groups["title"].Value; + else + discimage.title = MatchTitle.Groups["title"].Value; + } + else if (MatchPerformer.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found PERFORMER \"{1}\" at line {0}", line, MatchPerformer.Groups["performer"].Value); + if (intrack) + currenttrack.performer = MatchPerformer.Groups["performer"].Value; + else + discimage.performer = MatchPerformer.Groups["performer"].Value; + } + else if (MatchSongwriter.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found SONGWRITER \"{1}\" at line {0}", line, MatchSongwriter.Groups["songwriter"].Value); + if (intrack) + currenttrack.songwriter = MatchSongwriter.Groups["songwriter"].Value; + else + discimage.songwriter = MatchSongwriter.Groups["songwriter"].Value; + } + else if (MatchComposer.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found COMPOSER \"{1}\" at line {0}", line, MatchComposer.Groups["composer"].Value); + if (intrack) + currenttrack.composer = MatchComposer.Groups["composer"].Value; + else + discimage.composer = MatchComposer.Groups["composer"].Value; + } + else if (MatchArranger.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found ARRANGER \"{1}\" at line {0}", line, MatchArranger.Groups["arranger"].Value); + if (intrack) + currenttrack.arranger = MatchArranger.Groups["arranger"].Value; + else + discimage.arranger = MatchArranger.Groups["arranger"].Value; + } + else if (MatchMessage.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found MESSAGE \"{1}\" at line {0}", line, MatchMessage.Groups["message"].Value); + if (intrack) + currenttrack.message = MatchMessage.Groups["message"].Value; + else + discimage.message = MatchMessage.Groups["message"].Value; + } + else if (MatchDiscID.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found DISC_ID \"{1}\" at line {0}", line, MatchDiscID.Groups["discid"].Value); + if (!intrack) + discimage.disk_id = MatchDiscID.Groups["discid"].Value; + } + else if (MatchUPC.Success) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Found UPC_EAN \"{1}\" at line {0}", line, MatchUPC.Groups["catalog"].Value); + if (!intrack) + discimage.barcode = MatchUPC.Groups["catalog"].Value; + } + // Ignored fields + else if (MatchCDText.Success || MatchLanguage.Success || MatchClosure.Success || + MatchLanguageMap.Success || MatchLanguageMapping.Success) + { + + } + else if (_line == "") // Empty line, ignore it + { + + } + // TODO: Regex CD-TEXT SIZE_INFO + /* + else // Non-empty unknown field + { + throw new FeatureUnsupportedImageException(String.Format("Found unknown field defined at line {0}: \"{1}\"", line, _line)); + } + */ + } + + if (currenttrack.sequence != 0) + { + if(currenttrack.pregap != currenttrack.sectors && !currenttrack.indexes.ContainsKey(1)) + currenttrack.indexes.Add(1, currenttrack.startSector + currenttrack.pregap); + + discimage.tracks.Add(currenttrack); + } + + discimage.comment = commentBuilder.ToString(); + + // DEBUG information + DicConsole.DebugWriteLine("CDRDAO plugin", "Disc image parsing results"); + DicConsole.DebugWriteLine("CDRDAO plugin", "Disc CD-TEXT:"); + if (discimage.arranger == null) + DicConsole.DebugWriteLine("CDRDAO plugin", "\tArranger is not set."); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "\tArranger: {0}", discimage.arranger); + if (discimage.composer == null) + DicConsole.DebugWriteLine("CDRDAO plugin", "\tComposer is not set."); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "\tComposer: {0}", discimage.composer); + if (discimage.performer == null) + DicConsole.DebugWriteLine("CDRDAO plugin", "\tPerformer is not set."); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "\tPerformer: {0}", discimage.performer); + if (discimage.songwriter == null) + DicConsole.DebugWriteLine("CDRDAO plugin", "\tSongwriter is not set."); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "\tSongwriter: {0}", discimage.songwriter); + if (discimage.title == null) + DicConsole.DebugWriteLine("CDRDAO plugin", "\tTitle is not set."); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "\tTitle: {0}", discimage.title); + DicConsole.DebugWriteLine("CDRDAO plugin", "Disc information:"); + DicConsole.DebugWriteLine("CDRDAO plugin", "\tGuessed disk type: {0}", discimage.disktype); + if (discimage.barcode == null) + DicConsole.DebugWriteLine("CDRDAO plugin", "\tBarcode not set."); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "\tBarcode: {0}", discimage.barcode); + if (discimage.disk_id == null) + DicConsole.DebugWriteLine("CDRDAO plugin", "\tDisc ID not set."); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "\tDisc ID: {0}", discimage.disk_id); + if (discimage.mcn == null) + DicConsole.DebugWriteLine("CDRDAO plugin", "\tMCN not set."); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "\tMCN: {0}", discimage.mcn); + if (string.IsNullOrEmpty(discimage.comment)) + DicConsole.DebugWriteLine("CDRDAO plugin", "\tComment not set."); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "\tComment: \"{0}\"", discimage.comment); + + DicConsole.DebugWriteLine("CDRDAO plugin", "Track information:"); + DicConsole.DebugWriteLine("CDRDAO plugin", "\tDisc contains {0} tracks", discimage.tracks.Count); + for (int i = 0; i < discimage.tracks.Count; i++) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "\tTrack {0} information:", discimage.tracks[i].sequence); + + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\t{0} bytes per sector", discimage.tracks[i].bps); + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tPregap: {0} sectors", discimage.tracks[i].pregap); + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tData: {0} sectors starting at sector {1}", discimage.tracks[i].sectors, discimage.tracks[i].startSector); + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tPostgap: {0} sectors", discimage.tracks[i].postgap); + + if (discimage.tracks[i].flag_4ch) + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tTrack is flagged as quadraphonic"); + if (discimage.tracks[i].flag_dcp) + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tTrack allows digital copy"); + if (discimage.tracks[i].flag_pre) + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tTrack has pre-emphasis applied"); + + DicConsole.DebugWriteLine("CDRDAO 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); + + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tIndexes:"); + foreach (KeyValuePair<int, ulong> kvp in discimage.tracks[i].indexes) + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\t\tIndex {0} starts at sector {1}", kvp.Key, kvp.Value); + + if (discimage.tracks[i].isrc == null) + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tISRC is not set."); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tISRC: {0}", discimage.tracks[i].isrc); + + if (discimage.tracks[i].arranger == null) + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tArranger is not set."); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tArranger: {0}", discimage.tracks[i].arranger); + if (discimage.tracks[i].composer == null) + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tComposer is not set."); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tComposer: {0}", discimage.tracks[i].composer); + if (discimage.tracks[i].performer == null) + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tPerformer is not set."); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tPerformer: {0}", discimage.tracks[i].performer); + if (discimage.tracks[i].songwriter == null) + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tSongwriter is not set."); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tSongwriter: {0}", discimage.tracks[i].songwriter); + if (discimage.tracks[i].title == null) + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tTitle is not set."); + else + DicConsole.DebugWriteLine("CDRDAO plugin", "\t\tTitle: {0}", discimage.tracks[i].title); + } + + DicConsole.DebugWriteLine("CDRDAO plugin", "Building offset map"); + + partitions = new List<Partition>(); + offsetmap = new Dictionary<uint, ulong>(); + + ulong byte_offset = 0; + ulong partitionSequence = 0; + for (int i = 0; i < discimage.tracks.Count; i++) + { + ulong index0_len = 0; + + if (discimage.tracks[i].sequence == 1 && i != 0) + throw new ImageNotSupportedException("Unordered tracks"); + + Partition partition = new Partition(); + + // Index 01 + partition.PartitionDescription = String.Format("Track {0}.", discimage.tracks[i].sequence); + partition.PartitionName = discimage.tracks[i].title; + partition.PartitionStartSector = discimage.tracks[i].startSector; + partition.PartitionLength = (discimage.tracks[i].sectors - index0_len) * discimage.tracks[i].bps; + partition.PartitionSectors = (discimage.tracks[i].sectors - index0_len); + partition.PartitionSequence = partitionSequence; + partition.PartitionStart = byte_offset; + partition.PartitionType = discimage.tracks[i].tracktype; + + 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 Partition(); + } + + // Print partition map + DicConsole.DebugWriteLine("CDRDAO plugin", "printing partition map"); + foreach (Partition partition in partitions) + { + DicConsole.DebugWriteLine("CDRDAO plugin", "Partition sequence: {0}", partition.PartitionSequence); + DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition name: {0}", partition.PartitionName); + DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition description: {0}", partition.PartitionDescription); + DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition type: {0}", partition.PartitionType); + DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition starting sector: {0}", partition.PartitionStartSector); + DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition sectors: {0}", partition.PartitionSectors); + DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition starting offset: {0}", partition.PartitionStart); + DicConsole.DebugWriteLine("CDRDAO plugin", "\tPartition size in bytes: {0}", partition.PartitionLength); + } + + foreach (CDRDAOTrack track in discimage.tracks) + { + ImageInfo.imageSize += track.bps * track.sectors; + 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); + + ImageInfo.imageApplication = "CDRDAO"; + + 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; + + ImageInfo.readableSectorTags.Add(SectorTagType.CDTrackFlags); + + foreach (CDRDAOTrack track in discimage.tracks) + { + if (track.subchannel) + { + if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSubchannel)) + ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorSubchannel); + } + + switch (track.tracktype) + { + case CDRDAOTrackTypeAudio: + { + if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDTrackISRC)) + ImageInfo.readableSectorTags.Add(SectorTagType.CDTrackISRC); + break; + } + case CDRDAOTrackTypeMode2: + case CDRDAOTrackTypeMode2Mix: + { + if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSubHeader)) + ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorSubHeader); + if (!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorEDC)) + ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorEDC); + break; + } + case CDRDAOTrackTypeMode2Raw: + { + 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 CDRDAOTrackTypeMode1Raw: + { + 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; + } + } + } + + ImageInfo.xmlMediaType = XmlMediaType.OpticalDisc; + + return true; + } + catch (Exception ex) + { + DicConsole.ErrorWriteLine("Exception trying to identify image file {0}", imagePath); + DicConsole.ErrorWriteLine("Exception: {0}", ex.Message); + DicConsole.ErrorWriteLine("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."); + } + 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 (CDRDAOTrack cdrdao_track in discimage.tracks) + { + if (cdrdao_track.sequence == kvp.Key) + { + if ((sectorAddress - kvp.Value) < cdrdao_track.sectors) + return ReadSectors((sectorAddress - kvp.Value), length, kvp.Key); + } + } + } + } + + throw new ArgumentOutOfRangeException("sectorAddress", String.Format("Sector address {0} not found", sectorAddress)); + } + + public override byte[] ReadSectorsTag(UInt64 sectorAddress, UInt32 length, SectorTagType tag) + { + foreach (KeyValuePair<uint, ulong> kvp in offsetmap) + { + if (sectorAddress >= kvp.Value) + { + foreach (CDRDAOTrack cdrdao_track in discimage.tracks) + { + if (cdrdao_track.sequence == kvp.Key) + { + if ((sectorAddress - kvp.Value) < cdrdao_track.sectors) + return ReadSectorsTag((sectorAddress - kvp.Value), length, kvp.Key, tag); + } + } + } + } + + throw new ArgumentOutOfRangeException("sectorAddress", String.Format("Sector address {0} not found", sectorAddress)); + } + + public override byte[] ReadSectors(UInt64 sectorAddress, UInt32 length, UInt32 track) + { + CDRDAOTrack _track = new CDRDAOTrack(); + + _track.sequence = 0; + + foreach (CDRDAOTrack cdrdao_track in discimage.tracks) + { + if (cdrdao_track.sequence == track) + { + _track = cdrdao_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 CDRDAOTrackTypeMode1: + case CDRDAOTrackTypeMode2Form1: + { + sector_offset = 0; + sector_size = 2048; + sector_skip = 0; + break; + } + case CDRDAOTrackTypeMode2Form2: + { + sector_offset = 0; + sector_size = 2324; + sector_skip = 0; + break; + } + case CDRDAOTrackTypeMode2: + case CDRDAOTrackTypeMode2Mix: + { + sector_offset = 0; + sector_size = 2336; + sector_skip = 0; + break; + } + case CDRDAOTrackTypeAudio: + { + sector_offset = 0; + sector_size = 2352; + sector_skip = 0; + break; + } + case CDRDAOTrackTypeMode1Raw: + { + sector_offset = 16; + sector_size = 2048; + sector_skip = 288; + break; + } + case CDRDAOTrackTypeMode2Raw: + { + sector_offset = 16; + sector_size = 2336; + sector_skip = 0; + break; + } + default: + throw new FeatureSupportedButNotImplementedImageException("Unsupported track type"); + } + + if (_track.subchannel) + sector_skip += 96; + + 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) + { + CDRDAOTrack _track = new CDRDAOTrack(); + + _track.sequence = 0; + + foreach (CDRDAOTrack cdrdao_track in discimage.tracks) + { + if (cdrdao_track.sequence == track) + { + _track = cdrdao_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 = 0; + + if (!_track.subchannel && tag == SectorTagType.CDSectorSubchannel) + throw new ArgumentException("No tags in image for requested track", "tag"); + + 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 != CDRDAOTrackTypeAudio) + flags[0] += 0x40; + + if (_track.flag_dcp) + flags[0] += 0x20; + + if (_track.flag_pre) + flags[0] += 0x10; + + if (_track.flag_4ch) + flags[0] += 0x80; + + return flags; + } + case SectorTagType.CDTrackISRC: + return Encoding.UTF8.GetBytes(_track.isrc); + default: + throw new ArgumentException("Unsupported tag requested", "tag"); + } + + switch (_track.tracktype) + { + case CDRDAOTrackTypeMode1: + case CDRDAOTrackTypeMode2Form1: + if (tag == SectorTagType.CDSectorSubchannel) + { + sector_offset = 2048; + sector_size = 96; + break; + } + throw new ArgumentException("No tags in image for requested track", "tag"); + case CDRDAOTrackTypeMode2Form2: + case CDRDAOTrackTypeMode2Mix: + if (tag == SectorTagType.CDSectorSubchannel) + { + sector_offset = 2336; + sector_size = 96; + break; + } + throw new ArgumentException("No tags in image for requested track", "tag"); + case CDRDAOTrackTypeAudio: + if (tag == SectorTagType.CDSectorSubchannel) + { + sector_offset = 2352; + sector_size = 96; + break; + } + throw new ArgumentException("No tags in image for requested track", "tag"); + case CDRDAOTrackTypeMode1Raw: + { + 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: + { + sector_offset = 2352; + sector_size = 96; + break; + } + 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 CDRDAOTrackTypeMode2Raw: // Requires reading sector + if(tag == SectorTagType.CDSectorSubchannel) + { + sector_offset = 2352; + sector_size = 96; + break; + } + throw new FeatureSupportedButNotImplementedImageException("Feature not yet implemented"); + 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 (CDRDAOTrack cdrdao_track in discimage.tracks) + { + if (cdrdao_track.sequence == kvp.Key) + { + if ((sectorAddress - kvp.Value) < cdrdao_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) + { + CDRDAOTrack _track = new CDRDAOTrack(); + + _track.sequence = 0; + + foreach (CDRDAOTrack cdrdao_track in discimage.tracks) + { + if (cdrdao_track.sequence == track) + { + _track = cdrdao_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 CDRDAOTrackTypeMode1: + case CDRDAOTrackTypeMode2Form1: + { + sector_offset = 0; + sector_size = 2048; + sector_skip = 0; + break; + } + case CDRDAOTrackTypeMode2Form2: + { + sector_offset = 0; + sector_size = 2324; + sector_skip = 0; + break; + } + case CDRDAOTrackTypeMode2: + case CDRDAOTrackTypeMode2Mix: + { + sector_offset = 0; + sector_size = 2336; + sector_skip = 0; + break; + } + case CDRDAOTrackTypeMode1Raw: + case CDRDAOTrackTypeMode2Raw: + case CDRDAOTrackTypeAudio: + { + sector_offset = 0; + sector_size = 2352; + sector_skip = 0; + break; + } + default: + throw new FeatureSupportedButNotImplementedImageException("Unsupported track type"); + } + + if (_track.subchannel) + sector_skip += 96; + + 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 "CDRDAO tocfile"; + } + + 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<Partition> GetPartitions() + { + return partitions; + } + + public override List<Track> GetTracks() + { + List<Track> tracks = new List<Track>(); + + foreach (CDRDAOTrack 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 = cdr_track.startSector; + _track.TrackEndSector = _track.TrackStartSector + cdr_track.sectors - 1; + _track.TrackPregap = cdr_track.pregap; + _track.TrackSession = 1; + _track.TrackSequence = cdr_track.sequence; + _track.TrackType = CDRDAOTrackTypeToTrackType(cdr_track.tracktype); + _track.TrackFile = cdr_track.trackfile.datafile; + _track.TrackFileOffset = cdr_track.trackfile.offset; + _track.TrackFileType = cdr_track.trackfile.filetype; + _track.TrackRawBytesPerSector = cdr_track.bps; + _track.TrackBytesPerSector = CDRDAOTrackTypeToCookedBytesPerSector(cdr_track.tracktype); + if (cdr_track.subchannel) + { + _track.TrackSubchannelType = cdr_track.packedsubchannel ? TrackSubchannelType.PackedInterleaved : TrackSubchannelType.RawInterleaved; + + _track.TrackSubchannelFile = cdr_track.trackfile.datafile; + _track.TrackSubchannelOffset = cdr_track.trackfile.offset; + } + else + _track.TrackSubchannelType = TrackSubchannelType.None; + + tracks.Add(_track); + } + + return tracks; + } + + public override List<Track> GetSessionTracks(Session session) + { + return GetSessionTracks(session.SessionSequence); + } + + public override List<Track> GetSessionTracks(ushort session) + { + if (session == 1) + return GetTracks(); + throw new ImageNotSupportedException("Session does not exist in disc image"); + } + + public override List<Session> GetSessions() + { + // TODO + throw new NotImplementedException(); + } + + 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 UInt16 CDRDAOTrackTypeToBytesPerSector(string trackType) + { + switch (trackType) + { + case CDRDAOTrackTypeMode1: + case CDRDAOTrackTypeMode2Form1: + return 2048; + case CDRDAOTrackTypeMode2Form2: + return 2324; + case CDRDAOTrackTypeMode2: + case CDRDAOTrackTypeMode2Mix: + return 2336; + case CDRDAOTrackTypeAudio: + case CDRDAOTrackTypeMode1Raw: + case CDRDAOTrackTypeMode2Raw: + return 2352; + default: + return 0; + } + } + + static UInt16 CDRDAOTrackTypeToCookedBytesPerSector(string trackType) + { + switch (trackType) + { + case CDRDAOTrackTypeMode1: + case CDRDAOTrackTypeMode2Form1: + case CDRDAOTrackTypeMode1Raw: + return 2048; + case CDRDAOTrackTypeMode2Form2: + return 2324; + case CDRDAOTrackTypeMode2: + case CDRDAOTrackTypeMode2Mix: + case CDRDAOTrackTypeMode2Raw: + return 2336; + case CDRDAOTrackTypeAudio: + return 2352; + default: + return 0; + } + } + + static TrackType CDRDAOTrackTypeToTrackType(string trackType) + { + switch (trackType) + { + case CDRDAOTrackTypeMode1: + case CDRDAOTrackTypeMode1Raw: + return TrackType.CDMode1; + case CDRDAOTrackTypeMode2Form1: + return TrackType.CDMode2Form1; + case CDRDAOTrackTypeMode2Form2: + return TrackType.CDMode2Form2; + case CDRDAOTrackTypeMode2: + case CDRDAOTrackTypeMode2Mix: + case CDRDAOTrackTypeMode2Raw: + return TrackType.CDMode2Formless; + case CDRDAOTrackTypeAudio: + return TrackType.Audio; + default: + return TrackType.Data; + } + } + + #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 + } +} + diff --git a/DiscImageChef.DiscImages/CDRWin.cs b/DiscImageChef.DiscImages/CDRWin.cs index 69a40e93..e432c627 100644 --- a/DiscImageChef.DiscImages/CDRWin.cs +++ b/DiscImageChef.DiscImages/CDRWin.cs @@ -677,10 +677,15 @@ namespace DiscImageChef.ImagePlugins } else if (MatchFlags.Success) { - // TODO: Implement FLAGS support. DicConsole.DebugWriteLine("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)); + + const string FlagsRegEx = "FLAGS\\s+(((?<dcp>DCP)|(?<quad>4CH)|(?<pre>PRE)|(?<scms>SCMS))\\s*)+$"; + currenttrack.flag_dcp |= MatchFile.Groups["dcp"].Value == "DCP"; + currenttrack.flag_4ch |= MatchFile.Groups["quad"].Value == "4CH"; + currenttrack.flag_pre |= MatchFile.Groups["pre"].Value == "PRE"; + currenttrack.flag_scms |= MatchFile.Groups["scms"].Value == "SCMS"; } else if (MatchGenre.Success) { @@ -1216,6 +1221,8 @@ namespace DiscImageChef.ImagePlugins ImageInfo.diskBarcode = discimage.barcode; ImageInfo.diskType = discimage.disktype; + ImageInfo.readableSectorTags.Add(SectorTagType.CDTrackFlags); + foreach (CDRWinTrack track in discimage.tracks) { switch (track.tracktype) @@ -1224,16 +1231,12 @@ namespace DiscImageChef.ImagePlugins { 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; @@ -1562,6 +1565,9 @@ namespace DiscImageChef.ImagePlugins if (_track.flag_pre) flags[0] += 0x10; + if (_track.flag_4ch) + flags[0] += 0x80; + return flags; } case SectorTagType.CDTrackISRC: diff --git a/DiscImageChef.DiscImages/ChangeLog b/DiscImageChef.DiscImages/ChangeLog index bfba153b..362bdbdf 100644 --- a/DiscImageChef.DiscImages/ChangeLog +++ b/DiscImageChef.DiscImages/ChangeLog @@ -1,3 +1,14 @@ +2015-12-23 Natalia Portillo <claunia@claunia.com> + + * CDRDAO.cs: + * DiscImageChef.DiscImages.csproj: + Added support for cdrdao images. Solves #53 + + * CDRWin.cs: + Implemented FLAGS field support and + quadraphonic/broadcasting flag. + Return CD track flags for any track type. + 2015-12-06 Natalia Portillo <claunia@claunia.com> * CDRWin.cs: diff --git a/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj b/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj index 6687dbb2..b25428ef 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj +++ b/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj @@ -43,6 +43,7 @@ <Compile Include="TeleDisk.cs" /> <Compile Include="VHD.cs" /> <Compile Include="ZZZRawImage.cs" /> + <Compile Include="CDRDAO.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup> diff --git a/DiscImageChef.Filesystems/ChangeLog b/DiscImageChef.Filesystems/ChangeLog index e3eecd9a..48a921a0 100644 --- a/DiscImageChef.Filesystems/ChangeLog +++ b/DiscImageChef.Filesystems/ChangeLog @@ -1,3 +1,10 @@ +2015-12-23 Natalia Portillo <claunia@claunia.com> + + * ISO9660.cs: + Filter null characters from Volume Descriptors. + If Joliet descriptor is null, or ISO descriptor is longer, use + ISO descriptor for XML metadata. + 2015-12-06 Natalia Portillo <claunia@claunia.com> * ISO9660.cs: diff --git a/DiscImageChef.Filesystems/ISO9660.cs b/DiscImageChef.Filesystems/ISO9660.cs index a89eb928..21d7d855 100644 --- a/DiscImageChef.Filesystems/ISO9660.cs +++ b/DiscImageChef.Filesystems/ISO9660.cs @@ -902,14 +902,36 @@ namespace DiscImageChef.Plugins xmlFSType = new Schemas.FileSystemType(); xmlFSType.Type = "ISO9660"; + if (Joliet) { - xmlFSType.SystemIdentifier = decodedJolietVD.SystemIdentifier; xmlFSType.VolumeName = decodedJolietVD.VolumeIdentifier; - xmlFSType.VolumeSetIdentifier = decodedJolietVD.VolumeSetIdentifier; - xmlFSType.PublisherIdentifier = decodedJolietVD.PublisherIdentifier; - xmlFSType.DataPreparerIdentifier = decodedJolietVD.DataPreparerIdentifier; - xmlFSType.ApplicationIdentifier = decodedJolietVD.ApplicationIdentifier; + + if(decodedJolietVD.SystemIdentifier == null || decodedVD.SystemIdentifier.Length > decodedJolietVD.SystemIdentifier.Length) + xmlFSType.SystemIdentifier = decodedVD.SystemIdentifier; + else + xmlFSType.SystemIdentifier = decodedJolietVD.SystemIdentifier; + + if(decodedJolietVD.VolumeSetIdentifier == null || decodedVD.VolumeSetIdentifier.Length > decodedJolietVD.VolumeSetIdentifier.Length) + xmlFSType.VolumeSetIdentifier = decodedVD.VolumeSetIdentifier; + else + xmlFSType.VolumeSetIdentifier = decodedJolietVD.VolumeSetIdentifier; + + if(decodedJolietVD.PublisherIdentifier == null || decodedVD.PublisherIdentifier.Length > decodedJolietVD.PublisherIdentifier.Length) + xmlFSType.PublisherIdentifier = decodedVD.PublisherIdentifier; + else + xmlFSType.PublisherIdentifier = decodedJolietVD.PublisherIdentifier; + + if(decodedJolietVD.DataPreparerIdentifier == null || decodedVD.DataPreparerIdentifier.Length > decodedJolietVD.DataPreparerIdentifier.Length) + xmlFSType.DataPreparerIdentifier = decodedVD.DataPreparerIdentifier; + else + xmlFSType.DataPreparerIdentifier = decodedJolietVD.SystemIdentifier; + + if(decodedJolietVD.ApplicationIdentifier == null || decodedVD.ApplicationIdentifier.Length > decodedJolietVD.ApplicationIdentifier.Length) + xmlFSType.ApplicationIdentifier = decodedVD.ApplicationIdentifier; + else + xmlFSType.ApplicationIdentifier = decodedJolietVD.SystemIdentifier; + xmlFSType.CreationDate = decodedJolietVD.CreationTime; xmlFSType.CreationDateSpecified = true; if (decodedJolietVD.HasModificationTime) @@ -966,12 +988,12 @@ namespace DiscImageChef.Plugins { DecodedVolumeDescriptor decodedVD = new DecodedVolumeDescriptor(); - decodedVD.SystemIdentifier = Encoding.BigEndianUnicode.GetString(VDSysId).TrimEnd(); - decodedVD.VolumeIdentifier = Encoding.BigEndianUnicode.GetString(VDVolId).TrimEnd(); - decodedVD.VolumeSetIdentifier = Encoding.BigEndianUnicode.GetString(VDVolSetId).TrimEnd(); - decodedVD.PublisherIdentifier = Encoding.BigEndianUnicode.GetString(VDPubId).TrimEnd(); - decodedVD.DataPreparerIdentifier = Encoding.BigEndianUnicode.GetString(VDDataPrepId).TrimEnd(); - decodedVD.ApplicationIdentifier = Encoding.BigEndianUnicode.GetString(VDAppId).TrimEnd(); + decodedVD.SystemIdentifier = Encoding.BigEndianUnicode.GetString(VDSysId).TrimEnd().Trim(new []{'\u0000'}); + decodedVD.VolumeIdentifier = Encoding.BigEndianUnicode.GetString(VDVolId).TrimEnd().Trim(new []{'\u0000'}); + decodedVD.VolumeSetIdentifier = Encoding.BigEndianUnicode.GetString(VDVolSetId).TrimEnd().Trim(new []{'\u0000'}); + decodedVD.PublisherIdentifier = Encoding.BigEndianUnicode.GetString(VDPubId).TrimEnd().Trim(new []{'\u0000'}); + decodedVD.DataPreparerIdentifier = Encoding.BigEndianUnicode.GetString(VDDataPrepId).TrimEnd().Trim(new []{'\u0000'}); + decodedVD.ApplicationIdentifier = Encoding.BigEndianUnicode.GetString(VDAppId).TrimEnd().Trim(new []{'\u0000'}); if (VCTime[0] == '0' || VCTime[0] == 0x00) decodedVD.CreationTime = DateTime.MinValue; else @@ -1014,12 +1036,12 @@ namespace DiscImageChef.Plugins { DecodedVolumeDescriptor decodedVD = new DecodedVolumeDescriptor(); - decodedVD.SystemIdentifier = Encoding.ASCII.GetString(VDSysId).TrimEnd(); - decodedVD.VolumeIdentifier = Encoding.ASCII.GetString(VDVolId).TrimEnd(); - decodedVD.VolumeSetIdentifier = Encoding.ASCII.GetString(VDVolSetId).TrimEnd(); - decodedVD.PublisherIdentifier = Encoding.ASCII.GetString(VDPubId).TrimEnd(); - decodedVD.DataPreparerIdentifier = Encoding.ASCII.GetString(VDDataPrepId).TrimEnd(); - decodedVD.ApplicationIdentifier = Encoding.ASCII.GetString(VDAppId).TrimEnd(); + decodedVD.SystemIdentifier = Encoding.ASCII.GetString(VDSysId).TrimEnd().Trim(new []{'\u0000'}); + decodedVD.VolumeIdentifier = Encoding.ASCII.GetString(VDVolId).TrimEnd().Trim(new []{'\u0000'}); + decodedVD.VolumeSetIdentifier = Encoding.ASCII.GetString(VDVolSetId).TrimEnd().Trim(new []{'\u0000'}); + decodedVD.PublisherIdentifier = Encoding.ASCII.GetString(VDPubId).TrimEnd().Trim(new []{'\u0000'}); + decodedVD.DataPreparerIdentifier = Encoding.ASCII.GetString(VDDataPrepId).TrimEnd().Trim(new []{'\u0000'}); + decodedVD.ApplicationIdentifier = Encoding.ASCII.GetString(VDAppId).TrimEnd().Trim(new []{'\u0000'}); if (VCTime[0] == '0' || VCTime[0] == 0x00) decodedVD.CreationTime = DateTime.MinValue; else diff --git a/DiscImageChef/ChangeLog b/DiscImageChef/ChangeLog index a8132179..0a9a5de6 100644 --- a/DiscImageChef/ChangeLog +++ b/DiscImageChef/ChangeLog @@ -1,3 +1,11 @@ +2015-12-23 Natalia Portillo <claunia@claunia.com> + + * Commands/CreateSidecar.cs: + If a filesystem plugin crashes, do not abort. + + * DetectImageFormat.cs: + If an image plugin crashes, keep trying the others. + 2015-12-13 Natalia Portillo <claunia@claunia.com> * Commands/MediaInfo.cs: diff --git a/DiscImageChef/Commands/CreateSidecar.cs b/DiscImageChef/Commands/CreateSidecar.cs index 3967ea45..fc9fac1f 100644 --- a/DiscImageChef/Commands/CreateSidecar.cs +++ b/DiscImageChef/Commands/CreateSidecar.cs @@ -1010,11 +1010,18 @@ namespace DiscImageChef.Commands foreach (Plugin _plugin in plugins.PluginsList.Values) { - if (_plugin.Identify(_imageFormat, partitions[i].PartitionStartSector, partitions[i].PartitionStartSector+partitions[i].PartitionSectors-1)) + try { - string foo; - _plugin.GetInformation(_imageFormat, partitions[i].PartitionStartSector, partitions[i].PartitionStartSector+partitions[i].PartitionSectors-1, out foo); - lstFs.Add(_plugin.XmlFSType); + if (_plugin.Identify(_imageFormat, partitions[i].PartitionStartSector, partitions[i].PartitionStartSector+partitions[i].PartitionSectors-1)) + { + string foo; + _plugin.GetInformation(_imageFormat, partitions[i].PartitionStartSector, partitions[i].PartitionStartSector+partitions[i].PartitionSectors-1, out foo); + lstFs.Add(_plugin.XmlFSType); + } + } + catch + { + //DicConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name); } } @@ -1032,11 +1039,18 @@ namespace DiscImageChef.Commands foreach (Plugin _plugin in plugins.PluginsList.Values) { - if (_plugin.Identify(_imageFormat, (ulong)xmlTrk.StartSector, (ulong)xmlTrk.EndSector)) + try { - string foo; - _plugin.GetInformation(_imageFormat, (ulong)xmlTrk.StartSector, (ulong)xmlTrk.EndSector, out foo); - lstFs.Add(_plugin.XmlFSType); + if (_plugin.Identify(_imageFormat, (ulong)xmlTrk.StartSector, (ulong)xmlTrk.EndSector)) + { + string foo; + _plugin.GetInformation(_imageFormat, (ulong)xmlTrk.StartSector, (ulong)xmlTrk.EndSector, out foo); + lstFs.Add(_plugin.XmlFSType); + } + } + catch + { + //DicConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name); } } diff --git a/DiscImageChef/DetectImageFormat.cs b/DiscImageChef/DetectImageFormat.cs index 4dc1aed2..489031ac 100644 --- a/DiscImageChef/DetectImageFormat.cs +++ b/DiscImageChef/DetectImageFormat.cs @@ -55,12 +55,18 @@ namespace DiscImageChef.ImagePlugins // Check all but RAW plugin foreach (ImagePlugin _imageplugin in plugins.ImagePluginsList.Values) { - if(_imageplugin.PluginUUID != new Guid("12345678-AAAA-BBBB-CCCC-123456789000")) + if (_imageplugin.PluginUUID != new Guid("12345678-AAAA-BBBB-CCCC-123456789000")) { - if (_imageplugin.IdentifyImage(imagePath)) + try + { + if (_imageplugin.IdentifyImage(imagePath)) + { + _imageFormat = _imageplugin; + break; + } + } + catch { - _imageFormat = _imageplugin; - break; } } } @@ -70,12 +76,18 @@ namespace DiscImageChef.ImagePlugins { foreach (ImagePlugin _imageplugin in plugins.ImagePluginsList.Values) { - if(_imageplugin.PluginUUID == new Guid("12345678-AAAA-BBBB-CCCC-123456789000")) + if (_imageplugin.PluginUUID == new Guid("12345678-AAAA-BBBB-CCCC-123456789000")) { - if (_imageplugin.IdentifyImage(imagePath)) + try + { + if (_imageplugin.IdentifyImage(imagePath)) + { + _imageFormat = _imageplugin; + break; + } + } + catch { - _imageFormat = _imageplugin; - break; } } } diff --git a/README.md b/README.md index 229fb159..759de738 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Supported disk image formats * Nero Burning ROM (both image formats) * Apple 2IMG (used with Apple // emulators) * Virtual PC fixed size, dynamic size and differencing (undo) disk images +* CDRDAO TOC sheets Supported partitioning schemes ============================== diff --git a/TODO b/TODO index ad804edd..c933f3ad 100644 --- a/TODO +++ b/TODO @@ -3,7 +3,6 @@ --- Add support for CloneCD images --- Add support for DiscJuggler images --- Add support for Alcohol images ---- Add support for cdrdao images --- Add support for dump(8) images --- Add support for IMD images --- Add support for Kryoflux images