diff --git a/DiscImageChef/ImagePlugins/Nero.cs b/DiscImageChef/ImagePlugins/Nero.cs new file mode 100644 index 00000000..547a90f9 --- /dev/null +++ b/DiscImageChef/ImagePlugins/Nero.cs @@ -0,0 +1,2462 @@ +/*************************************************************************** +The Disc Image Chef +---------------------------------------------------------------------------- + +Filename : Nero.cs +Version : 2.0 +Author(s) : Natalia Portillo + +Component : Disc image plugins + +Revision : $Revision$ +Last change by : $Author$ +Date : $Date$ + +--[ Description ] ---------------------------------------------------------- + +Manages Nero Burning ROM images. + +--[ 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$ +using System; +using System.IO; +using System.Collections.Generic; +using DiscImageChef; + +namespace DiscImageChef.ImagePlugins +{ + class Nero : ImagePlugin + { + #region Internal structures + + struct NeroV1Footer + { + /// + /// "NERO" + /// + public UInt32 ChunkID; + + /// + /// Offset of first chunk in file + /// + public UInt32 FirstChunkOffset; + } + + struct NeroV2Footer + { + /// + /// "NER5" + /// + public UInt32 ChunkID; + + /// + /// Offset of first chunk in file + /// + public UInt64 FirstChunkOffset; + } + + struct NeroV2CueEntry + { + /// + /// Track mode. 0x01 for audio, 0x21 for copy-protected audio, 0x41 for data + /// + public byte Mode; + + /// + /// Track number in BCD + /// + public byte TrackNumber; + + /// + /// Index number in BCD + /// + public byte IndexNumber; + + /// + /// Always zero + /// + public byte Dummy; + + /// + /// LBA sector start for this entry + /// + public Int32 LBAStart; + } + + struct NeroV2Cuesheet + { + /// + /// "CUEX" + /// + public UInt32 ChunkID; + + /// + /// Chunk size + /// + public UInt32 ChunkSize; + + /// + /// Cuesheet entries + /// + public List Entries; + } + + struct NeroV1CueEntry + { + /// + /// Track mode. 0x01 for audio, 0x21 for copy-protected audio, 0x41 for data + /// + public byte Mode; + + /// + /// Track number in BCD + /// + public byte TrackNumber; + + /// + /// Index number in BCD + /// + public byte IndexNumber; + + /// + /// Always zero + /// + public UInt16 Dummy; + + /// + /// MSF start sector's minute for this entry + /// + public byte Minute; + + /// + /// MSF start sector's second for this entry + /// + public byte Second; + + /// + /// MSF start sector's frame for this entry + /// + public byte Frame; + } + + struct NeroV1Cuesheet + { + /// + /// "CUES" + /// + public UInt32 ChunkID; + + /// + /// Chunk size + /// + public UInt32 ChunkSize; + + /// + /// Cuesheet entries + /// + public List Entries; + } + + struct NeroV1DAOEntry + { + /// + /// ISRC (12 bytes) + /// + public byte[] ISRC; + + /// + /// Size of sector inside image (in bytes) + /// + public UInt16 SectorSize; + + /// + /// Sector mode in image + /// + public UInt16 Mode; + + /// + /// Unknown + /// + public UInt16 Unknown; + + /// + /// Index 0 start + /// + public UInt32 Index0; + + /// + /// Index 1 start + /// + public UInt32 Index1; + + /// + /// End of track + 1 + /// + public UInt32 EndOfTrack; + } + + struct NeroV1DAO + { + /// + /// "DAOI" + /// + public UInt32 ChunkID; + + /// + /// Chunk size (big endian) + /// + public UInt32 ChunkSizeBe; + + /// + /// Chunk size (little endian) + /// + public UInt32 ChunkSizeLe; + + /// + /// UPC (14 bytes, null-padded) + /// + public byte[] UPC; + + /// + /// TOC type + /// + public UInt16 TocType; + + /// + /// First track + /// + public byte FirstTrack; + + /// + /// Last track + /// + public byte LastTrack; + + /// + /// Tracks + /// + public List Tracks; + } + + struct NeroV2DAOEntry + { + /// + /// ISRC (12 bytes) + /// + public byte[] ISRC; + + /// + /// Size of sector inside image (in bytes) + /// + public UInt16 SectorSize; + + /// + /// Sector mode in image + /// + public UInt16 Mode; + + /// + /// Seems to be always 0. + /// + public UInt16 Unknown; + + /// + /// Index 0 start + /// + public UInt64 Index0; + + /// + /// Index 1 start + /// + public UInt64 Index1; + + /// + /// End of track + 1 + /// + public UInt64 EndOfTrack; + } + + struct NeroV2DAO + { + /// + /// "DAOX" + /// + public UInt32 ChunkID; + + /// + /// Chunk size (big endian) + /// + public UInt32 ChunkSizeBe; + + /// + /// Chunk size (little endian) + /// + public UInt32 ChunkSizeLe; + + /// + /// UPC (14 bytes, null-padded) + /// + public byte[] UPC; + + /// + /// TOC type + /// + public UInt16 TocType; + + /// + /// First track + /// + public byte FirstTrack; + + /// + /// Last track + /// + public byte LastTrack; + + /// + /// Tracks + /// + public List Tracks; + } + + struct NeroCDTextPack + { + /// + /// Pack type + /// + public byte PackType; + + /// + /// Track number + /// + public byte TrackNumber; + + /// + /// Pack number in block + /// + public byte PackNumber; + + /// + /// Block number + /// + public byte BlockNumber; + + /// + /// 12 bytes of data + /// + public byte[] Text; + + /// + /// CRC + /// + public UInt16 CRC; + } + + struct NeroCDText + { + /// + /// "CDTX" + /// + public UInt32 ChunkID; + + /// + /// Chunk size + /// + public UInt32 ChunkSize; + + /// + /// CD-TEXT packs + /// + public List Packs; + } + + struct NeroV1TAOEntry + { + /// + /// Offset of track on image + /// + public UInt32 Offset; + + /// + /// Length of track in bytes + /// + public UInt32 Length; + + /// + /// Track mode + /// + public UInt32 Mode; + + /// + /// LBA track start (plus 150 lead in sectors) + /// + public UInt32 StartLBA; + + /// + /// Unknown + /// + public UInt32 Unknown; + } + + struct NeroV1TAO + { + /// + /// "ETNF" + /// + public UInt32 ChunkID; + + /// + /// Chunk size + /// + public UInt32 ChunkSize; + + /// + /// CD-TEXT packs + /// + public List Tracks; + } + + struct NeroV2TAOEntry + { + /// + /// Offset of track on image + /// + public UInt64 Offset; + + /// + /// Length of track in bytes + /// + public UInt64 Length; + + /// + /// Track mode + /// + public UInt32 Mode; + + /// + /// LBA track start (plus 150 lead in sectors) + /// + public UInt32 StartLBA; + + /// + /// Unknown + /// + public UInt32 Unknown; + + /// + /// Track length in sectors + /// + public UInt32 Sectors; + } + + struct NeroV2TAO + { + /// + /// "ETN2" + /// + public UInt32 ChunkID; + + /// + /// Chunk size + /// + public UInt32 ChunkSize; + + /// + /// CD-TEXT packs + /// + public List Tracks; + } + + struct NeroSession + { + /// + /// "SINF" + /// + public UInt32 ChunkID; + + /// + /// Chunk size + /// + public UInt32 ChunkSize; + + /// + /// Tracks in session + /// + public UInt32 Tracks; + } + + struct NeroMediaType + { + /// + /// "MTYP" + /// + public UInt32 ChunkID; + + /// + /// Chunk size + /// + public UInt32 ChunkSize; + + /// + /// Media type + /// + public UInt32 Type; + } + + struct NeroDiscInformation + { + /// + /// "DINF" + /// + public UInt32 ChunkID; + + /// + /// Chunk size + /// + public UInt32 ChunkSize; + + /// + /// Unknown + /// + public UInt32 Unknown; + } + + struct NeroTOCChunk + { + /// + /// "TOCT" + /// + public UInt32 ChunkID; + + /// + /// Chunk size + /// + public UInt32 ChunkSize; + + /// + /// Unknown + /// + public UInt16 Unknown; + } + + struct NeroRELOChunk + { + /// + /// "RELO" + /// + public UInt32 ChunkID; + + /// + /// Chunk size + /// + public UInt32 ChunkSize; + + /// + /// Unknown + /// + public UInt32 Unknown; + } + + struct NeroEndOfChunkChain + { + /// + /// "END!" + /// + public UInt32 ChunkID; + + /// + /// Chunk size + /// + public UInt32 ChunkSize; + } + + // Internal use only + struct NeroTrack + { + public byte[] ISRC; + public UInt16 SectorSize; + public UInt64 Offset; + public UInt64 Length; + public UInt64 EndOfTrack; + public UInt32 Mode; + public UInt64 StartLBA; + public UInt64 Sectors; + public UInt64 Index0; + public UInt64 Index1; + public UInt32 Sequence; + } + + #endregion + + #region Internal consts + + // "NERO" + public const UInt32 NeroV1FooterID = 0x4E45524F; + + // "NER5" + public const UInt32 NeroV2FooterID = 0x4E455235; + + // "CUES" + public const UInt32 NeroV1CUEID = 0x43554553; + + // "CUEX" + public const UInt32 NeroV2CUEID = 0x43554558; + + // "ETNF" + public const UInt32 NeroV1TAOID = 0x45544E46; + + // "ETN2" + public const UInt32 NeroV2TAOID = 0x45544E32; + + // "DAOI" + public const UInt32 NeroV1DAOID = 0x44414F49; + + // "DAOX" + public const UInt32 NeroV2DAOID = 0x44414F58; + + // "CDTX" + public const UInt32 NeroCDTextID = 0x43445458; + + // "SINF" + public const UInt32 NeroSessionID = 0x53494E46; + + // "MTYP" + public const UInt32 NeroDiskTypeID = 0x4D545950; + + // "DINF" + public const UInt32 NeroDiscInfoID = 0x44494E46; + + // "TOCT" + public const UInt32 NeroTOCID = 0x544F4354; + + // "RELO" + public const UInt32 NeroReloID = 0x52454C4F; + + // "END!" + public const UInt32 NeroEndID = 0x454E4421; + + public enum DAOMode : ushort + { + Data = 0x0000, + DataM2F1 = 0x0002, + DataM2F2 = 0x0003, + DataRaw = 0x0005, + DataM2Raw = 0x0006, + Audio = 0x0007, + DataRawSub = 0x000F, + AudioSub = 0x0010, + DataM2RawSub = 0x0011 + } + + public enum NeroMediaTypes : uint + { + /// + /// No media + /// + NERO_MTYP_NONE = 0x00000, + /// + /// CD-R/RW + /// + NERO_MTYP_CD = 0x00001, + /// + /// DDCD-R/RW + /// + NERO_MTYP_DDCD = 0x00002, + /// + /// DVD-R/RW + /// + NERO_MTYP_DVD_M = 0x00004, + /// + /// DVD+RW + /// + NERO_MTYP_DVD_P = 0x00008, + /// + /// DVD-RAM + /// + NERO_MTYP_DVD_RAM = 0x00010, + /// + /// Multi-level disc + /// + NERO_MTYP_ML = 0x00020, + /// + /// Mount Rainier + /// + NERO_MTYP_MRW = 0x00040, + /// + /// Exclude CD-R + /// + NERO_MTYP_NO_CDR = 0x00080, + /// + /// Exclude CD-RW + /// + NERO_MTYP_NO_CDRW = 0x00100, + /// + /// CD-RW + /// + NERO_MTYP_CDRW = NERO_MTYP_CD | NERO_MTYP_NO_CDR, + /// + /// CD-R + /// + NERO_MTYP_CDR = NERO_MTYP_CD | NERO_MTYP_NO_CDRW, + /// + /// DVD-ROM + /// + NERO_MTYP_DVD_ROM = 0x00200, + /// + /// CD-ROM + /// + NERO_MTYP_CDROM = 0x00400, + /// + /// Exclude DVD-RW + /// + NERO_MTYP_NO_DVD_M_RW = 0x00800, + /// + /// Exclude DVD-R + /// + NERO_MTYP_NO_DVD_M_R = 0x01000, + /// + /// Exclude DVD+RW + /// + NERO_MTYP_NO_DVD_P_RW = 0x02000, + /// + /// Exclude DVD+R + /// + NERO_MTYP_NO_DVD_P_R = 0x04000, + /// + /// DVD-R + /// + NERO_MTYP_DVD_M_R = NERO_MTYP_DVD_M | NERO_MTYP_NO_DVD_M_RW, + /// + /// DVD-RW + /// + NERO_MTYP_DVD_M_RW = NERO_MTYP_DVD_M | NERO_MTYP_NO_DVD_M_R, + /// + /// DVD+R + /// + NERO_MTYP_DVD_P_R = NERO_MTYP_DVD_P | NERO_MTYP_NO_DVD_P_RW, + /// + /// DVD+RW + /// + NERO_MTYP_DVD_P_RW = NERO_MTYP_DVD_P | NERO_MTYP_NO_DVD_P_R, + /// + /// Packet-writing (fixed) + /// + NERO_MTYP_FPACKET = 0x08000, + /// + /// Packet-writing (variable) + /// + NERO_MTYP_VPACKET = 0x10000, + /// + /// Packet-writing (any) + /// + NERO_MTYP_PACKETW = NERO_MTYP_MRW | NERO_MTYP_FPACKET | NERO_MTYP_VPACKET, + /// + /// HD-Burn + /// + NERO_MTYP_HDB = 0x20000, + /// + /// DVD+R DL + /// + NERO_MTYP_DVD_P_R9 = 0x40000, + /// + /// DVD-R DL + /// + NERO_MTYP_DVD_M_R9 = 0x80000, + /// + /// Any DVD double-layer + /// + NERO_MTYP_DVD_ANY_R9 = NERO_MTYP_DVD_P_R9 | NERO_MTYP_DVD_M_R9, + /// + /// Any DVD + /// + NERO_MTYP_DVD_ANY = NERO_MTYP_DVD_M | NERO_MTYP_DVD_P | NERO_MTYP_DVD_RAM | NERO_MTYP_DVD_ANY_R9, + /// + /// BD-ROM + /// + NERO_MTYP_BD_ROM = 0x100000, + /// + /// BD-R + /// + NERO_MTYP_BD_R = 0x200000, + /// + /// BD-RE + /// + NERO_MTYP_BD_RE = 0x400000, + /// + /// BD-R/RE + /// + NERO_MTYP_BD = NERO_MTYP_BD_R | NERO_MTYP_BD_RE, + /// + /// Any BD + /// + NERO_MTYP_BD_ANY = NERO_MTYP_BD | NERO_MTYP_BD_ROM, + /// + /// HD DVD-ROM + /// + NERO_MTYP_HD_DVD_ROM = 0x0800000, + /// + /// HD DVD-R + /// + NERO_MTYP_HD_DVD_R = 0x1000000, + /// + /// HD DVD-RW + /// + NERO_MTYP_HD_DVD_RW = 0x2000000, + /// + /// HD DVD-R/RW + /// + NERO_MTYP_HD_DVD = NERO_MTYP_HD_DVD_R | NERO_MTYP_HD_DVD_RW, + /// + /// Any HD DVD + /// + NERO_MTYP_HD_DVD_ANY = NERO_MTYP_HD_DVD | NERO_MTYP_HD_DVD_ROM, + } + + #endregion + + #region Internal variables + + string _imagePath; + FileStream imageStream; + FileInfo imageInfo; + bool imageNewFormat; + Dictionary neroSessions; + NeroV1Cuesheet neroCuesheetV1; + NeroV2Cuesheet neroCuesheetV2; + NeroV1DAO neroDAOV1; + NeroV2DAO neroDAOV2; + NeroCDText neroCDTXT; + NeroV1TAO neroTAOV1; + NeroV2TAO neroTAOV2; + NeroMediaType neroMediaTyp; + NeroDiscInformation neroDiscInfo; + NeroTOCChunk neroTOC; + NeroRELOChunk neroRELO; + + List imageTracks; + Dictionary TrackISRCs; + byte[] UPC; + Dictionary neroTracks; + Dictionary offsetmap; + List imageSessions; + List ImagePartitions; + + #endregion + + #region Accesible variables + + ImageInfo _imageInfo; + + public ImageInfo ImageInfo + { + get + { + return _imageInfo; + } + } + + #endregion + + #region Methods + + public Nero(PluginBase Core) + { + Name = "Nero Burning ROM image"; + PluginUUID = new Guid("D160F9FF-5941-43FC-B037-AD81DD141F05"); + _imagePath = ""; + imageNewFormat = false; + _imageInfo = new ImageInfo(); + _imageInfo.readableSectorTags = new List(); + _imageInfo.readableDiskTags = new List(); + neroSessions = new Dictionary(); + neroTracks = new Dictionary(); + offsetmap = new Dictionary(); + imageSessions = new List(); + ImagePartitions = new List(); + } + + // Due to .cue format, this method must parse whole file, ignoring errors (those will be thrown by OpenImage()). + public override bool IdentifyImage(string imagePath) + { + imageInfo = new FileInfo(imagePath); + imageStream = new FileStream(imagePath, FileMode.Open, FileAccess.Read); + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + + byte[] buffer; + NeroV1Footer footerV1 = new NeroV1Footer(); + NeroV2Footer footerV2 = new NeroV2Footer(); + + imageStream.Seek(-8, SeekOrigin.End); + buffer = new byte[8]; + imageStream.Read(buffer, 0, 8); + footerV1.ChunkID = BigEndianBitConverter.ToUInt32(buffer, 0); + footerV1.FirstChunkOffset = BigEndianBitConverter.ToUInt32(buffer, 4); + + imageStream.Seek(-12, SeekOrigin.End); + buffer = new byte[12]; + imageStream.Read(buffer, 0, 12); + footerV2.ChunkID = BigEndianBitConverter.ToUInt32(buffer, 0); + footerV2.FirstChunkOffset = BigEndianBitConverter.ToUInt64(buffer, 4); + + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (Nero plugin): imageInfo.Length = {0}", imageInfo.Length); + Console.WriteLine("DEBUG (Nero plugin): footerV1.ChunkID = 0x{0:X2} (\"{1}\")", footerV1.ChunkID, System.Text.Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(footerV1.ChunkID))); + Console.WriteLine("DEBUG (Nero plugin): footerV1.FirstChunkOffset = {0}", footerV1.FirstChunkOffset); + Console.WriteLine("DEBUG (Nero plugin): footerV2.ChunkID = 0x{0:X2} (\"{1}\")", footerV2.ChunkID, System.Text.Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(footerV2.ChunkID))); + Console.WriteLine("DEBUG (Nero plugin): footerV2.FirstChunkOffset = {0}", footerV2.FirstChunkOffset); + } + + if (footerV2.ChunkID == NeroV2FooterID && footerV2.FirstChunkOffset < (ulong)imageInfo.Length) + { + imageStream.Close(); + return true; + } + if (footerV1.ChunkID == NeroV1FooterID && footerV1.FirstChunkOffset < (ulong)imageInfo.Length) + { + imageStream.Close(); + return true; + } + + imageStream.Close(); + return false; + } + + public override bool OpenImage(string imagePath) + { + try + { + imageInfo = new FileInfo(imagePath); + imageStream = new FileStream(imagePath, FileMode.Open, FileAccess.Read); + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + + byte[] buffer; + NeroV1Footer footerV1 = new NeroV1Footer(); + NeroV2Footer footerV2 = new NeroV2Footer(); + + imageStream.Seek(-8, SeekOrigin.End); + buffer = new byte[8]; + imageStream.Read(buffer, 0, 8); + footerV1.ChunkID = BigEndianBitConverter.ToUInt32(buffer, 0); + footerV1.FirstChunkOffset = BigEndianBitConverter.ToUInt32(buffer, 4); + + imageStream.Seek(-12, SeekOrigin.End); + buffer = new byte[12]; + imageStream.Read(buffer, 0, 12); + footerV2.ChunkID = BigEndianBitConverter.ToUInt32(buffer, 0); + footerV2.FirstChunkOffset = BigEndianBitConverter.ToUInt64(buffer, 4); + + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (Nero plugin): imageInfo.Length = {0}", imageInfo.Length); + Console.WriteLine("DEBUG (Nero plugin): footerV1.ChunkID = 0x{0:X2} (\"{1}\")", footerV1.ChunkID, System.Text.Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(footerV1.ChunkID))); + Console.WriteLine("DEBUG (Nero plugin): footerV1.FirstChunkOffset = {0}", footerV1.FirstChunkOffset); + Console.WriteLine("DEBUG (Nero plugin): footerV2.ChunkID = 0x{0:X2} (\"{1}\")", footerV2.ChunkID, System.Text.Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(footerV2.ChunkID))); + Console.WriteLine("DEBUG (Nero plugin): footerV2.FirstChunkOffset = {0}", footerV2.FirstChunkOffset); + } + + if (footerV1.ChunkID == NeroV1FooterID && footerV1.FirstChunkOffset < (ulong)imageInfo.Length) + imageNewFormat = false; + else if (footerV2.ChunkID == NeroV2FooterID && footerV2.FirstChunkOffset < (ulong)imageInfo.Length) + imageNewFormat = true; + else + { + imageStream.Close(); + return true; + } + + if (imageNewFormat) + imageStream.Seek((long)footerV2.FirstChunkOffset, SeekOrigin.Begin); + else + imageStream.Seek(footerV1.FirstChunkOffset, SeekOrigin.Begin); + + bool parsing = true; + ushort currentsession = 1; + uint currenttrack = 1; + + imageTracks = new List(); + TrackISRCs = new Dictionary(); + + _imageInfo.diskType = DiskType.CD; + _imageInfo.sectors = 0; + _imageInfo.sectorSize = 0; + + while (parsing) + { + byte[] ChunkHeaderBuffer = new byte[8]; + UInt32 ChunkID; + UInt32 ChunkLength; + + imageStream.Read(ChunkHeaderBuffer, 0, 8); + ChunkID = BigEndianBitConverter.ToUInt32(ChunkHeaderBuffer, 0); + ChunkLength = BigEndianBitConverter.ToUInt32(ChunkHeaderBuffer, 4); + + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (Nero plugin): ChunkID = 0x{0:X2} (\"{1}\")", ChunkID, System.Text.Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(ChunkID))); + Console.WriteLine("DEBUG (Nero plugin): ChunkLength = {0}", ChunkLength); + } + + switch (ChunkID) + { + case NeroV1CUEID: + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): Found \"CUES\" chunk, parsing {0} bytes", ChunkLength); + + neroCuesheetV1 = new NeroV1Cuesheet(); + neroCuesheetV1.ChunkID = ChunkID; + neroCuesheetV1.ChunkSize = ChunkLength; + neroCuesheetV1.Entries = new List(); + + byte[] tmpbuffer = new byte[8]; + for (int i = 0; i < neroCuesheetV1.ChunkSize; i += 8) + { + NeroV1CueEntry _entry = new NeroV1CueEntry(); + imageStream.Read(tmpbuffer, 0, 8); + _entry.Mode = tmpbuffer[0]; + _entry.TrackNumber = tmpbuffer[1]; + _entry.IndexNumber = tmpbuffer[2]; + _entry.Dummy = BigEndianBitConverter.ToUInt16(tmpbuffer, 3); + _entry.Minute = tmpbuffer[5]; + _entry.Second = tmpbuffer[6]; + _entry.Frame = tmpbuffer[7]; + + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (Nero plugin): Cuesheet entry {0}", (i / 8) + 1); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Mode = {1:X2}", (i / 8) + 1, _entry.Mode); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].TrackNumber = {1:X2}", (i / 8) + 1, _entry.TrackNumber); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].IndexNumber = {1:X2}", (i / 8) + 1, _entry.IndexNumber); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Dummy = {1:X4}", (i / 8) + 1, _entry.Dummy); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Minute = {1:X2}", (i / 8) + 1, _entry.Minute); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Second = {1:X2}", (i / 8) + 1, _entry.Second); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Frame = {1:X2}", (i / 8) + 1, _entry.Frame); + } + + neroCuesheetV1.Entries.Add(_entry); + } + + break; + } + case NeroV2CUEID: + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): Found \"CUEX\" chunk, parsing {0} bytes", ChunkLength); + + neroCuesheetV2 = new NeroV2Cuesheet(); + neroCuesheetV2.ChunkID = ChunkID; + neroCuesheetV2.ChunkSize = ChunkLength; + neroCuesheetV2.Entries = new List(); + + byte[] tmpbuffer = new byte[8]; + for (int i = 0; i < neroCuesheetV2.ChunkSize; i += 8) + { + NeroV2CueEntry _entry = new NeroV2CueEntry(); + imageStream.Read(tmpbuffer, 0, 8); + _entry.Mode = tmpbuffer[0]; + _entry.TrackNumber = tmpbuffer[1]; + _entry.IndexNumber = tmpbuffer[2]; + _entry.Dummy = tmpbuffer[3]; + _entry.LBAStart = BigEndianBitConverter.ToInt32(tmpbuffer, 4); + + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (Nero plugin): Cuesheet entry {0}", (i / 8) + 1); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Mode = 0x{1:X2}", (i / 8) + 1, _entry.Mode); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].TrackNumber = {1:X2}", (i / 8) + 1, _entry.TrackNumber); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].IndexNumber = {1:X2}", (i / 8) + 1, _entry.IndexNumber); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Dummy = {1:X2}", (i / 8) + 1, _entry.Dummy); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].LBAStart = {1}", (i / 8) + 1, _entry.LBAStart); + } + + neroCuesheetV2.Entries.Add(_entry); + } + + break; + } + case NeroV1DAOID: + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): Found \"DAOI\" chunk, parsing {0} bytes", ChunkLength); + + neroDAOV1 = new NeroV1DAO(); + neroDAOV1.ChunkID = ChunkID; + neroDAOV1.ChunkSizeBe = ChunkLength; + + byte[] tmpbuffer = new byte[22]; + imageStream.Read(tmpbuffer, 0, 22); + neroDAOV1.ChunkSizeLe = BigEndianBitConverter.ToUInt32(tmpbuffer, 0); + neroDAOV1.UPC = new byte[14]; + Array.Copy(tmpbuffer, 4, neroDAOV1.UPC, 0, 14); + neroDAOV1.TocType = BigEndianBitConverter.ToUInt16(tmpbuffer, 18); + neroDAOV1.FirstTrack = tmpbuffer[20]; + neroDAOV1.LastTrack = tmpbuffer[21]; + neroDAOV1.Tracks = new List(); + + if (!_imageInfo.readableDiskTags.Contains(DiskTagType.CD_MCN)) + _imageInfo.readableDiskTags.Add(DiskTagType.CD_MCN); + + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDTrackISRC)) + _imageInfo.readableSectorTags.Add(SectorTagType.CDTrackISRC); + + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (Nero plugin): neroDAOV1.ChunkSizeLe = {0} bytes", neroDAOV1.ChunkSizeLe); + Console.WriteLine("DEBUG (Nero plugin): neroDAOV1.UPC = \"{0}\"", StringHandlers.CToString(neroDAOV1.UPC)); + Console.WriteLine("DEBUG (Nero plugin): neroDAOV1.TocType = 0x{0:X4}", neroDAOV1.TocType); + Console.WriteLine("DEBUG (Nero plugin): neroDAOV1.FirstTrack = {0}", neroDAOV1.FirstTrack); + Console.WriteLine("DEBUG (Nero plugin): neroDAOV1.LastTrack = {0}", neroDAOV1.LastTrack); + } + + UPC = neroDAOV1.UPC; + + tmpbuffer = new byte[30]; + for (int i = 0; i < (neroDAOV1.ChunkSizeBe - 22); i += 30) + { + NeroV1DAOEntry _entry = new NeroV1DAOEntry(); + imageStream.Read(tmpbuffer, 0, 30); + _entry.ISRC = new byte[12]; + Array.Copy(tmpbuffer, 4, _entry.ISRC, 0, 12); + _entry.SectorSize = BigEndianBitConverter.ToUInt16(tmpbuffer, 12); + _entry.Mode = BitConverter.ToUInt16(tmpbuffer, 14); + _entry.Unknown = BigEndianBitConverter.ToUInt16(tmpbuffer, 16); + _entry.Index0 = BigEndianBitConverter.ToUInt32(tmpbuffer, 18); + _entry.Index1 = BigEndianBitConverter.ToUInt32(tmpbuffer, 22); + _entry.EndOfTrack = BigEndianBitConverter.ToUInt32(tmpbuffer, 26); + + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (Nero plugin): Disc-At-Once entry {0}", (i / 32) + 1); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].ISRC = \"{1}\"", (i / 32) + 1, StringHandlers.CToString(_entry.ISRC)); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].SectorSize = {1}", (i / 32) + 1, _entry.SectorSize); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Mode = {1} (0x{2:X4})", (i / 32) + 1, (DAOMode)_entry.Mode, _entry.Mode); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Unknown = 0x{1:X4}", (i / 32) + 1, _entry.Unknown); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Index0 = {1}", (i / 32) + 1, _entry.Index0); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Index1 = {1}", (i / 32) + 1, _entry.Index1); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].EndOfTrack = {1}", (i / 32) + 1, _entry.EndOfTrack); + } + + neroDAOV1.Tracks.Add(_entry); + + if (_entry.SectorSize > _imageInfo.sectorSize) + _imageInfo.sectorSize = _entry.SectorSize; + + TrackISRCs.Add(currenttrack, _entry.ISRC); + + NeroTrack _neroTrack = new NeroTrack(); + _neroTrack.EndOfTrack = _entry.EndOfTrack; + _neroTrack.ISRC = _entry.ISRC; + _neroTrack.Length = _entry.EndOfTrack - _entry.Index0; + _neroTrack.Mode = _entry.Mode; + _neroTrack.Offset = _entry.Index0; + _neroTrack.Sectors = _neroTrack.Length / _entry.SectorSize; + _neroTrack.SectorSize = _entry.SectorSize; + _neroTrack.StartLBA = _imageInfo.sectors; + _neroTrack.Index0 = _entry.Index0; + _neroTrack.Index1 = _entry.Index1; + _neroTrack.Sequence = currenttrack; + neroTracks.Add(currenttrack, _neroTrack); + + _imageInfo.sectors += _neroTrack.Sectors; + + currenttrack++; + } + + break; + } + case NeroV2DAOID: + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): Found \"DAOX\" chunk, parsing {0} bytes", ChunkLength); + + neroDAOV2 = new NeroV2DAO(); + neroDAOV2.ChunkID = ChunkID; + neroDAOV2.ChunkSizeBe = ChunkLength; + + byte[] tmpbuffer = new byte[22]; + imageStream.Read(tmpbuffer, 0, 22); + neroDAOV2.ChunkSizeLe = BigEndianBitConverter.ToUInt32(tmpbuffer, 0); + neroDAOV2.UPC = new byte[14]; + Array.Copy(tmpbuffer, 4, neroDAOV2.UPC, 0, 14); + neroDAOV2.TocType = BigEndianBitConverter.ToUInt16(tmpbuffer, 18); + neroDAOV1.FirstTrack = tmpbuffer[20]; + neroDAOV2.LastTrack = tmpbuffer[21]; + neroDAOV2.Tracks = new List(); + + if (!_imageInfo.readableDiskTags.Contains(DiskTagType.CD_MCN)) + _imageInfo.readableDiskTags.Add(DiskTagType.CD_MCN); + + if (!_imageInfo.readableSectorTags.Contains(SectorTagType.CDTrackISRC)) + _imageInfo.readableSectorTags.Add(SectorTagType.CDTrackISRC); + + UPC = neroDAOV2.UPC; + + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (Nero plugin): neroDAOV2.ChunkSizeLe = {0} bytes", neroDAOV2.ChunkSizeLe); + Console.WriteLine("DEBUG (Nero plugin): neroDAOV2.UPC = \"{0}\"", StringHandlers.CToString(neroDAOV2.UPC)); + Console.WriteLine("DEBUG (Nero plugin): neroDAOV2.TocType = 0x{0:X4}", neroDAOV2.TocType); + Console.WriteLine("DEBUG (Nero plugin): neroDAOV2.FirstTrack = {0}", neroDAOV2.FirstTrack); + Console.WriteLine("DEBUG (Nero plugin): neroDAOV2.LastTrack = {0}", neroDAOV2.LastTrack); + } + + tmpbuffer = new byte[42]; + for (int i = 0; i < (neroDAOV2.ChunkSizeBe - 22); i += 42) + { + NeroV2DAOEntry _entry = new NeroV2DAOEntry(); + imageStream.Read(tmpbuffer, 0, 42); + _entry.ISRC = new byte[12]; + Array.Copy(tmpbuffer, 4, _entry.ISRC, 0, 12); + _entry.SectorSize = BigEndianBitConverter.ToUInt16(tmpbuffer, 12); + _entry.Mode = BitConverter.ToUInt16(tmpbuffer, 14); + _entry.Unknown = BigEndianBitConverter.ToUInt16(tmpbuffer, 16); + _entry.Index0 = BigEndianBitConverter.ToUInt64(tmpbuffer, 18); + _entry.Index1 = BigEndianBitConverter.ToUInt64(tmpbuffer, 26); + _entry.EndOfTrack = BigEndianBitConverter.ToUInt64(tmpbuffer, 34); + + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (Nero plugin): Disc-At-Once entry {0}", (i / 32) + 1); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].ISRC = \"{1}\"", (i / 32) + 1, StringHandlers.CToString(_entry.ISRC)); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].SectorSize = {1}", (i / 32) + 1, _entry.SectorSize); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Mode = {1} (0x{2:X4})", (i / 32) + 1, (DAOMode)_entry.Mode, _entry.Mode); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Unknown = {1:X2}", (i / 32) + 1, _entry.Unknown); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Index0 = {1}", (i / 32) + 1, _entry.Index0); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Index1 = {1}", (i / 32) + 1, _entry.Index1); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].EndOfTrack = {1}", (i / 32) + 1, _entry.EndOfTrack); + } + + neroDAOV2.Tracks.Add(_entry); + + if (_entry.SectorSize > _imageInfo.sectorSize) + _imageInfo.sectorSize = _entry.SectorSize; + + TrackISRCs.Add(currenttrack, _entry.ISRC); + + NeroTrack _neroTrack = new NeroTrack(); + _neroTrack.EndOfTrack = _entry.EndOfTrack; + _neroTrack.ISRC = _entry.ISRC; + _neroTrack.Length = _entry.EndOfTrack - _entry.Index0; + _neroTrack.Mode = _entry.Mode; + _neroTrack.Offset = _entry.Index0; + _neroTrack.Sectors = _neroTrack.Length / _entry.SectorSize; + _neroTrack.SectorSize = _entry.SectorSize; + _neroTrack.StartLBA = _imageInfo.sectors; + _neroTrack.Index0 = _entry.Index0; + _neroTrack.Index1 = _entry.Index1; + _neroTrack.Sequence = currenttrack; + neroTracks.Add(currenttrack, _neroTrack); + + _imageInfo.sectors += _neroTrack.Sectors; + + currenttrack++; + } + + break; + } + case NeroCDTextID: + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): Found \"CDTX\" chunk, parsing {0} bytes", ChunkLength); + + neroCDTXT = new NeroCDText(); + neroCDTXT.ChunkID = ChunkID; + neroCDTXT.ChunkSize = ChunkLength; + neroCDTXT.Packs = new List(); + + byte[] tmpbuffer = new byte[18]; + for (int i = 0; i < (neroCDTXT.ChunkSize); i += 18) + { + NeroCDTextPack _entry = new NeroCDTextPack(); + imageStream.Read(tmpbuffer, 0, 18); + + _entry.PackType = tmpbuffer[0]; + _entry.TrackNumber = tmpbuffer[1]; + _entry.PackNumber = tmpbuffer[2]; + _entry.BlockNumber = tmpbuffer[3]; + _entry.Text = new byte[12]; + Array.Copy(tmpbuffer, 4, _entry.Text, 0, 12); + _entry.CRC = BigEndianBitConverter.ToUInt16(tmpbuffer, 16); + + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (Nero plugin): CD-TEXT entry {0}", (i / 18) + 1); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].PackType = 0x{1:X2}", (i / 18) + 1, _entry.PackType); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].TrackNumber = 0x{1:X2}", (i / 18) + 1, _entry.TrackNumber); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].PackNumber = 0x{1:X2}", (i / 18) + 1, _entry.PackNumber); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].BlockNumber = 0x{1:X2}", (i / 18) + 1, _entry.BlockNumber); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Text = \"{1}\"", (i / 18) + 1, StringHandlers.CToString(_entry.Text)); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].CRC = 0x{1:X4}", (i / 18) + 1, _entry.CRC); + } + + neroCDTXT.Packs.Add(_entry); + } + + break; + } + case NeroV1TAOID: + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): Found \"ETNF\" chunk, parsing {0} bytes", ChunkLength); + + neroTAOV1 = new NeroV1TAO(); + neroTAOV1.ChunkID = ChunkID; + neroTAOV1.ChunkSize = ChunkLength; + neroTAOV1.Tracks = new List(); + + byte[] tmpbuffer = new byte[20]; + for (int i = 0; i < (neroTAOV1.ChunkSize); i += 20) + { + NeroV1TAOEntry _entry = new NeroV1TAOEntry(); + imageStream.Read(tmpbuffer, 0, 20); + + _entry.Offset = BigEndianBitConverter.ToUInt32(tmpbuffer, 0); + _entry.Length = BigEndianBitConverter.ToUInt32(tmpbuffer, 4); + _entry.Mode = BigEndianBitConverter.ToUInt32(tmpbuffer, 8); + _entry.StartLBA = BigEndianBitConverter.ToUInt32(tmpbuffer, 12); + _entry.Unknown = BigEndianBitConverter.ToUInt32(tmpbuffer, 16); + + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (Nero plugin): Track-at-Once entry {0}", (i / 20) + 1); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Offset = {1}", (i / 20) + 1, _entry.Offset); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Length = {1} bytes", (i / 20) + 1, _entry.Length); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Mode = {1} (0x{2:X4})", (i / 20) + 1, (DAOMode)_entry.Mode, _entry.Mode); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].StartLBA = {1}", (i / 20) + 1, _entry.StartLBA); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Unknown = 0x{1:X4}", (i / 20) + 1, _entry.Unknown); + } + + neroTAOV1.Tracks.Add(_entry); + + if (NeroTrackModeToBytesPerSector((DAOMode)_entry.Mode) > _imageInfo.sectorSize) + _imageInfo.sectorSize = NeroTrackModeToBytesPerSector((DAOMode)_entry.Mode); + + NeroTrack _neroTrack = new NeroTrack(); + _neroTrack.EndOfTrack = _entry.Offset + _entry.Length; + _neroTrack.ISRC = new byte[12]; + _neroTrack.Length = _entry.Length; + _neroTrack.Mode = _entry.Mode; + _neroTrack.Offset = _entry.Offset; + _neroTrack.Sectors = _neroTrack.Length / NeroTrackModeToBytesPerSector((DAOMode)_entry.Mode); + _neroTrack.SectorSize = NeroTrackModeToBytesPerSector((DAOMode)_entry.Mode); + _neroTrack.StartLBA = _imageInfo.sectors; + _neroTrack.Index0 = _entry.Offset; + _neroTrack.Index1 = _entry.Offset; + _neroTrack.Sequence = currenttrack; + neroTracks.Add(currenttrack, _neroTrack); + + _imageInfo.sectors += _neroTrack.Sectors; + + currenttrack++; + } + + break; + } + case NeroV2TAOID: + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): Found \"ETN2\" chunk, parsing {0} bytes", ChunkLength); + + neroTAOV2 = new NeroV2TAO(); + neroTAOV2.ChunkID = ChunkID; + neroTAOV2.ChunkSize = ChunkLength; + neroTAOV2.Tracks = new List(); + + byte[] tmpbuffer = new byte[32]; + for (int i = 0; i < (neroTAOV2.ChunkSize); i += 32) + { + NeroV2TAOEntry _entry = new NeroV2TAOEntry(); + imageStream.Read(tmpbuffer, 0, 32); + + _entry.Offset = BigEndianBitConverter.ToUInt64(tmpbuffer, 0); + _entry.Length = BigEndianBitConverter.ToUInt64(tmpbuffer, 8); + _entry.Mode = BigEndianBitConverter.ToUInt32(tmpbuffer, 16); + _entry.StartLBA = BigEndianBitConverter.ToUInt32(tmpbuffer, 20); + _entry.Unknown = BigEndianBitConverter.ToUInt32(tmpbuffer, 24); + _entry.Sectors = BigEndianBitConverter.ToUInt32(tmpbuffer, 28); + + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (Nero plugin): Track-at-Once entry {0}", (i / 32) + 1); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Offset = {1}", (i / 32) + 1, _entry.Offset); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Length = {1} bytes", (i / 32) + 1, _entry.Length); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Mode = {1} (0x{2:X4})", (i / 32) + 1, (DAOMode)_entry.Mode, _entry.Mode); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].StartLBA = {1}", (i / 32) + 1, _entry.StartLBA); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Unknown = 0x{1:X4}", (i / 32) + 1, _entry.Unknown); + Console.WriteLine("DEBUG (Nero plugin):\t _entry[{0}].Sectors = {1}", (i / 32) + 1, _entry.Sectors); + } + + neroTAOV2.Tracks.Add(_entry); + + if (NeroTrackModeToBytesPerSector((DAOMode)_entry.Mode) > _imageInfo.sectorSize) + _imageInfo.sectorSize = NeroTrackModeToBytesPerSector((DAOMode)_entry.Mode); + + NeroTrack _neroTrack = new NeroTrack(); + _neroTrack.EndOfTrack = _entry.Offset + _entry.Length; + _neroTrack.ISRC = new byte[12]; + _neroTrack.Length = _entry.Length; + _neroTrack.Mode = _entry.Mode; + _neroTrack.Offset = _entry.Offset; + _neroTrack.Sectors = _neroTrack.Length / NeroTrackModeToBytesPerSector((DAOMode)_entry.Mode); + _neroTrack.SectorSize = NeroTrackModeToBytesPerSector((DAOMode)_entry.Mode); + _neroTrack.StartLBA = _imageInfo.sectors; + _neroTrack.Index0 = _entry.Offset; + _neroTrack.Index1 = _entry.Offset; + _neroTrack.Sequence = currenttrack; + neroTracks.Add(currenttrack, _neroTrack); + + _imageInfo.sectors += _neroTrack.Sectors; + + currenttrack++; + } + + break; + } + case NeroSessionID: + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): Found \"SINF\" chunk, parsing {0} bytes", ChunkLength); + + UInt32 sessionTracks; + byte[] tmpbuffer = new byte[4]; + imageStream.Read(tmpbuffer, 0, 4); + sessionTracks = BigEndianBitConverter.ToUInt32(tmpbuffer, 0); + neroSessions.Add(currentsession, sessionTracks); + + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): \tSession {0} has {1} tracks", currentsession, sessionTracks); + + currentsession++; + break; + } + case NeroDiskTypeID: + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): Found \"MTYP\" chunk, parsing {0} bytes", ChunkLength); + + neroMediaTyp = new NeroMediaType(); + + neroMediaTyp.ChunkID = ChunkID; + neroMediaTyp.ChunkSize = ChunkLength; + + byte[] tmpbuffer = new byte[4]; + imageStream.Read(tmpbuffer, 0, 4); + neroMediaTyp.Type = BigEndianBitConverter.ToUInt32(tmpbuffer, 0); + + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): \tMedia type is {0} ({1})", (NeroMediaTypes)neroMediaTyp.Type, neroMediaTyp.Type); + + _imageInfo.diskType = NeroMediaTypeToDiskType((NeroMediaTypes)neroMediaTyp.Type); + + break; + } + case NeroDiscInfoID: + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): Found \"DINF\" chunk, parsing {0} bytes", ChunkLength); + + neroDiscInfo = new NeroDiscInformation(); + neroDiscInfo.ChunkID = ChunkID; + neroDiscInfo.ChunkSize = ChunkLength; + byte[] tmpbuffer = new byte[4]; + imageStream.Read(tmpbuffer, 0, 4); + neroDiscInfo.Unknown = BigEndianBitConverter.ToUInt32(tmpbuffer, 0); + + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): \tneroDiscInfo.Unknown = 0x{0:X4} ({0})", neroDiscInfo.Unknown); + + break; + } + case NeroReloID: + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): Found \"RELO\" chunk, parsing {0} bytes", ChunkLength); + + neroRELO = new NeroRELOChunk(); + neroRELO.ChunkID = ChunkID; + neroRELO.ChunkSize = ChunkLength; + byte[] tmpbuffer = new byte[4]; + imageStream.Read(tmpbuffer, 0, 4); + neroRELO.Unknown = BigEndianBitConverter.ToUInt32(tmpbuffer, 0); + + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): \tneroRELO.Unknown = 0x{0:X4} ({0})", neroRELO.Unknown); + + break; + } + case NeroTOCID: + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): Found \"TOCT\" chunk, parsing {0} bytes", ChunkLength); + + neroTOC = new NeroTOCChunk(); + neroTOC.ChunkID = ChunkID; + neroTOC.ChunkSize = ChunkLength; + byte[] tmpbuffer = new byte[2]; + imageStream.Read(tmpbuffer, 0, 2); + neroTOC.Unknown = BigEndianBitConverter.ToUInt16(tmpbuffer, 0); + + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): \tneroTOC.Unknown = 0x{0:X4} ({0})", neroTOC.Unknown); + + break; + } + case NeroEndID: + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): Found \"END!\" chunk, finishing parse"); + parsing = false; + break; + } + default: + { + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): Unknown chunk ID \"{0}\", skipping...", System.Text.Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(ChunkID))); + imageStream.Seek(ChunkLength, SeekOrigin.Current); + break; + } + } + } + + _imageInfo.imageHasPartitions = true; + _imageInfo.imageHasSessions = true; + _imageInfo.imageCreator = null; + _imageInfo.imageCreationTime = imageInfo.CreationTimeUtc; + _imageInfo.imageLastModificationTime = imageInfo.LastWriteTimeUtc; + _imageInfo.imageName = Path.GetFileNameWithoutExtension(imagePath); + _imageInfo.imageComments = null; + _imageInfo.diskManufacturer = null; + _imageInfo.diskModel = null; + _imageInfo.diskSerialNumber = null; + _imageInfo.diskBarcode = null; + _imageInfo.diskPartNumber = null; + _imageInfo.driveManufacturer = null; + _imageInfo.driveModel = null; + _imageInfo.driveSerialNumber = null; + _imageInfo.diskSequence = 0; + _imageInfo.lastDiskSequence = 0; + if (imageNewFormat) + { + _imageInfo.imageSize = footerV2.FirstChunkOffset; + _imageInfo.imageVersion = "Nero Burning ROM >= 5.5"; + _imageInfo.imageApplication = "Nero Burning ROM"; + _imageInfo.imageApplicationVersion = ">= 5.5"; + } + else + { + _imageInfo.imageSize = footerV1.FirstChunkOffset; + _imageInfo.imageVersion = "Nero Burning ROM <= 5.0"; + _imageInfo.imageApplication = "Nero Burning ROM"; + _imageInfo.imageApplicationVersion = "<= 5.0"; + } + + if (neroSessions.Count == 0) + neroSessions.Add(1, currenttrack); + + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): Building offset, track and session maps"); + + currentsession = 1; + uint currentsessionmaxtrack; + neroSessions.TryGetValue(1, out currentsessionmaxtrack); + uint currentsessioncurrenttrack = 1; + Session currentsessionstruct = new Session(); + ulong PartitionSequence = 0; + for (uint i = 1; i <= neroTracks.Count; i++) + { + NeroTrack _neroTrack; + if (neroTracks.TryGetValue(i, out _neroTrack)) + { + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (Nero plugin): \tcurrentsession = {0}", currentsession); + Console.WriteLine("DEBUG (Nero plugin): \tcurrentsessionmaxtrack = {0}", currentsessionmaxtrack); + Console.WriteLine("DEBUG (Nero plugin): \tcurrentsessioncurrenttrack = {0}", currentsessioncurrenttrack); + } + + Track _track = new Track(); + _track.Indexes = new Dictionary(); + if (_neroTrack.Index0 < _neroTrack.Index1) + _track.Indexes.Add(0, _neroTrack.Index0 / _neroTrack.SectorSize); + _track.Indexes.Add(1, _neroTrack.Index1 / _neroTrack.SectorSize); + _track.TrackDescription = StringHandlers.CToString(_neroTrack.ISRC); + _track.TrackEndSector = (_neroTrack.EndOfTrack / _neroTrack.SectorSize) - 1; + _track.TrackPregap = (_neroTrack.Index1 - _neroTrack.Index0) / _neroTrack.SectorSize; + _track.TrackSequence = _neroTrack.Sequence; + _track.TrackSession = currentsession; + _track.TrackStartSector = _neroTrack.StartLBA; + _track.TrackType = NeroTrackModeToTrackType((DAOMode)_neroTrack.Mode); + imageTracks.Add(_track); + + if (MainClass.isDebug) + { + Console.WriteLine("DEBUG (Nero plugin): \t\t _track.TrackDescription = {0}", _track.TrackDescription); + Console.WriteLine("DEBUG (Nero plugin): \t\t _track.TrackEndSector = {0}", _track.TrackEndSector); + Console.WriteLine("DEBUG (Nero plugin): \t\t _track.TrackPregap = {0}", _track.TrackPregap); + Console.WriteLine("DEBUG (Nero plugin): \t\t _track.TrackSequence = {0}", _track.TrackSequence); + Console.WriteLine("DEBUG (Nero plugin): \t\t _track.TrackSession = {0}", _track.TrackSession); + Console.WriteLine("DEBUG (Nero plugin): \t\t _track.TrackStartSector = {0}", _track.TrackStartSector); + Console.WriteLine("DEBUG (Nero plugin): \t\t _track.TrackType = {0}", _track.TrackType); + } + + if (currentsessioncurrenttrack == 1) + { + currentsessionstruct = new Session(); + currentsessionstruct.SessionSequence = currentsession; + currentsessionstruct.StartSector = _track.TrackStartSector; + currentsessionstruct.StartTrack = _track.TrackSequence; + } + currentsessioncurrenttrack++; + if (currentsessioncurrenttrack > currentsessionmaxtrack) + { + currentsession++; + neroSessions.TryGetValue(currentsession, out currentsessionmaxtrack); + currentsessioncurrenttrack = 1; + currentsessionstruct.EndTrack = _track.TrackSequence; + currentsessionstruct.EndSector = _track.TrackEndSector; + imageSessions.Add(currentsessionstruct); + } + + offsetmap.Add(_track.TrackSequence, _track.TrackStartSector); + if (MainClass.isDebug) + Console.WriteLine("DEBUG (Nero plugin): \t\t Offset[{0}]: {1}", _track.TrackSequence, _track.TrackStartSector); + + PartPlugins.Partition partition; + + if (_neroTrack.Index0 < _neroTrack.Index1) + { + partition = new PartPlugins.Partition(); + partition.PartitionDescription = String.Format("Track {0} Index 0", _track.TrackSequence); + partition.PartitionLength = (_neroTrack.Index1 - _neroTrack.Index0); + partition.PartitionName = StringHandlers.CToString(_neroTrack.ISRC); + partition.PartitionSectors = partition.PartitionLength / _neroTrack.SectorSize; + partition.PartitionSequence = PartitionSequence; + partition.PartitionStart = _neroTrack.Index0; + partition.PartitionStartSector = _neroTrack.StartLBA; + partition.PartitionType = NeroTrackModeToTrackType((DAOMode)_neroTrack.Mode).ToString(); + ImagePartitions.Add(partition); + PartitionSequence++; + } + + partition = new PartPlugins.Partition(); + partition.PartitionDescription = String.Format("Track {0} Index 1", _track.TrackSequence); + partition.PartitionLength = (_neroTrack.EndOfTrack - _neroTrack.Index1); + partition.PartitionName = StringHandlers.CToString(_neroTrack.ISRC); + partition.PartitionSectors = partition.PartitionLength / _neroTrack.SectorSize; + partition.PartitionSequence = PartitionSequence; + partition.PartitionStart = _neroTrack.Index1; + partition.PartitionStartSector = _neroTrack.StartLBA + ((_neroTrack.Index1 - _neroTrack.Index0) / _neroTrack.SectorSize); + partition.PartitionType = NeroTrackModeToTrackType((DAOMode)_neroTrack.Mode).ToString(); + ImagePartitions.Add(partition); + PartitionSequence++; + } + } + + _imagePath = imagePath; + imageStream.Close(); + return true; + } + catch + { + return false; + } + } + + public override bool ImageHasPartitions() + { + // Even if they only have 1 track, there is a partition (track 1) + return true; + } + + 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: + return UPC; + case DiskTagType.CD_TEXT: + throw new NotImplementedException("Not yet implemented"); + default: + throw new FeaturedNotSupportedByDiscImageException("Requested disk tag not supported by image"); + } + } + + 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 kvp in offsetmap) + { + if (sectorAddress >= kvp.Value) + { + foreach (Track _track in imageTracks) + { + if (_track.TrackSequence == kvp.Key) + { + if ((sectorAddress - kvp.Value) < (_track.TrackEndSector - _track.TrackStartSector)) + 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 kvp in offsetmap) + { + if (sectorAddress >= kvp.Value) + { + foreach (Track _track in imageTracks) + { + if (_track.TrackSequence == kvp.Key) + { + if ((sectorAddress - kvp.Value) < (_track.TrackEndSector - _track.TrackStartSector)) + 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) + { + NeroTrack _track; + + if (!neroTracks.TryGetValue(track, out _track)) + throw new ArgumentOutOfRangeException("track", "Track not found"); + + 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 ((DAOMode)_track.Mode) + { + case DAOMode.Data: + case DAOMode.DataM2F1: + { + sector_offset = 0; + sector_size = 2048; + sector_skip = 0; + break; + } + case DAOMode.DataM2F2: + { + sector_offset = 8; + sector_size = 2324; + sector_skip = 4; + break; + } + case DAOMode.Audio: + { + sector_offset = 0; + sector_size = 2352; + sector_skip = 0; + break; + } + case DAOMode.DataRaw: + { + sector_offset = 16; + sector_size = 2048; + sector_skip = 288; + break; + } + case DAOMode.DataM2Raw: + { + sector_offset = 24; + sector_size = 2324; + sector_skip = 4; + break; + } + // TODO: Supposing Nero suffixes the subchannel to the channel + case DAOMode.DataRawSub: + { + sector_offset = 16; + sector_size = 2048; + sector_skip = 288 + 96; + break; + } + case DAOMode.DataM2RawSub: + { + sector_offset = 24; + sector_size = 2324; + sector_skip = 4 + 96; + break; + } + case DAOMode.AudioSub: + { + 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(_imagePath, FileMode.Open, FileAccess.Read); + using (BinaryReader br = new BinaryReader(imageStream)) + { + br.BaseStream.Seek((long)_track.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); + } + } + } + + imageStream.Close(); + return buffer; + } + + public override byte[] ReadSectorsTag(UInt64 sectorAddress, UInt32 length, UInt32 track, SectorTagType tag) + { + NeroTrack _track; + + if (!neroTracks.TryGetValue(track, out _track)) + throw new ArgumentOutOfRangeException("track", "Track not found"); + + 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]; + flags[0] = 0x00; + + if ((DAOMode)_track.Mode != DAOMode.Audio && (DAOMode)_track.Mode != DAOMode.AudioSub) + flags[0] += 0x40; + + return flags; + } + case SectorTagType.CDTrackISRC: + return _track.ISRC; + case SectorTagType.CDTrackText: + throw new FeatureSupportedButNotImplementedImageException("Feature not yet implemented"); + default: + throw new ArgumentException("Unsupported tag requested", "tag"); + } + + switch ((DAOMode)_track.Mode) + { + case DAOMode.Data: + case DAOMode.DataM2F1: + throw new ArgumentException("No tags in image for requested track", "tag"); + case DAOMode.DataM2F2: + { + 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 DAOMode.Audio: + throw new ArgumentException("There are no tags on audio tracks", "tag"); + case DAOMode.DataRaw: + { + 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; + } + // TODO + case DAOMode.DataM2RawSub: + throw new FeatureSupportedButNotImplementedImageException("Feature not yet implemented"); + case DAOMode.DataRawSub: + { + switch (tag) + { + case SectorTagType.CDSectorSync: + { + sector_offset = 0; + sector_size = 12; + sector_skip = 2340 + 96; + break; + } + case SectorTagType.CDSectorHeader: + { + sector_offset = 12; + sector_size = 4; + sector_skip = 2336 + 96; + break; + } + case SectorTagType.CDSectorSubchannel: + { + sector_offset = 2352; + sector_size = 96; + sector_skip = 0; + 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 + 96; + break; + } + case SectorTagType.CDSectorECC_P: + { + sector_offset = 2076; + sector_size = 172; + sector_skip = 104 + 96; + break; + } + case SectorTagType.CDSectorECC_Q: + { + sector_offset = 2248; + sector_size = 104; + sector_skip = 0 + 96; + break; + } + case SectorTagType.CDSectorEDC: + { + sector_offset = 2064; + sector_size = 4; + sector_skip = 284 + 96; + break; + } + default: + throw new ArgumentException("Unsupported tag requested", "tag"); + } + break; + } + case DAOMode.AudioSub: + { + 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(_imagePath, FileMode.Open, FileAccess.Read); + using (BinaryReader br = new BinaryReader(imageStream)) + { + br.BaseStream.Seek((long)_track.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); + } + } + } + + imageStream.Close(); + 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 kvp in offsetmap) + { + if (sectorAddress >= kvp.Value) + { + foreach (Track _track in imageTracks) + { + if (_track.TrackSequence == kvp.Key) + { + if ((sectorAddress - kvp.Value) < (_track.TrackEndSector - _track.TrackStartSector)) + return ReadSectorsLong((sectorAddress - kvp.Value), length, kvp.Key); + } + } + } + } + + throw new ArgumentOutOfRangeException("sectorAddress", String.Format("Sector address {0} not found", sectorAddress)); + } + + public override byte[] ReadSectorsLong(UInt64 sectorAddress, UInt32 length, UInt32 track) + { + NeroTrack _track; + + if (!neroTracks.TryGetValue(track, out _track)) + throw new ArgumentOutOfRangeException("track", "Track not found"); + + 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 ((DAOMode)_track.Mode) + { + case DAOMode.Data: + case DAOMode.DataM2F1: + { + sector_offset = 0; + sector_size = 2048; + sector_skip = 0; + break; + } + case DAOMode.DataM2F2: + { + sector_offset = 0; + sector_size = 2336; + sector_skip = 0; + break; + } + case DAOMode.DataRaw: + case DAOMode.DataM2Raw: + case DAOMode.Audio: + { + sector_offset = 0; + sector_size = 2352; + sector_skip = 0; + break; + } + case DAOMode.DataRawSub: + case DAOMode.DataM2RawSub: + case DAOMode.AudioSub: + { + 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(_imagePath, FileMode.Open, FileAccess.Read); + BinaryReader br = new BinaryReader(imageStream); + + br.BaseStream.Seek((long)_track.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); + } + } + + imageStream.Close(); + return buffer; + } + + public override string GetImageFormat() + { + return "Nero Burning ROM"; + } + + 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 GetDiskBarcode() + { + return _imageInfo.diskBarcode; + } + + public override DiskType GetDiskType() + { + return _imageInfo.diskType; + } + + public override List GetPartitions() + { + return ImagePartitions; + } + + public override List GetTracks() + { + return imageTracks; + } + + public override List GetSessionTracks(Session session) + { + return GetSessionTracks(session.SessionSequence); + } + + public override List GetSessionTracks(UInt16 session) + { + List sessionTracks = new List(); + foreach (Track _track in imageTracks) + if (_track.TrackSession == session) + sessionTracks.Add(_track); + + return sessionTracks; + } + + public override List GetSessions() + { + return imageSessions; + } + + #endregion + + #region Private methods + + static DiskType NeroMediaTypeToDiskType(NeroMediaTypes MediaType) + { + switch (MediaType) + { + case NeroMediaTypes.NERO_MTYP_DDCD: + return DiskType.DDCD; + case NeroMediaTypes.NERO_MTYP_DVD_M: + case NeroMediaTypes.NERO_MTYP_DVD_M_R: + return DiskType.DVDR; + case NeroMediaTypes.NERO_MTYP_DVD_P: + case NeroMediaTypes.NERO_MTYP_DVD_P_R: + return DiskType.DVDPR; + case NeroMediaTypes.NERO_MTYP_DVD_RAM: + return DiskType.DVDRAM; + case NeroMediaTypes.NERO_MTYP_ML: + case NeroMediaTypes.NERO_MTYP_MRW: + case NeroMediaTypes.NERO_MTYP_CDRW: + return DiskType.CDRW; + case NeroMediaTypes.NERO_MTYP_CDR: + return DiskType.CDR; + case NeroMediaTypes.NERO_MTYP_DVD_ROM: + return DiskType.DVDROM; + case NeroMediaTypes.NERO_MTYP_CDROM: + return DiskType.CDROM; + case NeroMediaTypes.NERO_MTYP_DVD_M_RW: + return DiskType.DVDRW; + case NeroMediaTypes.NERO_MTYP_DVD_P_RW: + return DiskType.DVDPRW; + case NeroMediaTypes.NERO_MTYP_DVD_P_R9: + return DiskType.DVDPRDL; + case NeroMediaTypes.NERO_MTYP_DVD_M_R9: + return DiskType.DVDRDL; + case NeroMediaTypes.NERO_MTYP_BD: + case NeroMediaTypes.NERO_MTYP_BD_ANY: + case NeroMediaTypes.NERO_MTYP_BD_ROM: + return DiskType.BDROM; + case NeroMediaTypes.NERO_MTYP_BD_R: + return DiskType.BDR; + case NeroMediaTypes.NERO_MTYP_BD_RE: + return DiskType.BDRE; + case NeroMediaTypes.NERO_MTYP_HD_DVD: + case NeroMediaTypes.NERO_MTYP_HD_DVD_ANY: + case NeroMediaTypes.NERO_MTYP_HD_DVD_ROM: + return DiskType.HDDVDROM; + case NeroMediaTypes.NERO_MTYP_HD_DVD_R: + return DiskType.HDDVDR; + case NeroMediaTypes.NERO_MTYP_HD_DVD_RW: + return DiskType.HDDVDRW; + default: + return DiskType.CD; + } + } + + static TrackType NeroTrackModeToTrackType(DAOMode mode) + { + switch (mode) + { + case DAOMode.Data: + case DAOMode.DataRaw: + case DAOMode.DataRawSub: + return TrackType.CDMode1; + case DAOMode.DataM2F1: + return TrackType.CDMode2Form1; + case DAOMode.DataM2F2: + return TrackType.CDMode2Form2; + case DAOMode.DataM2RawSub: + case DAOMode.DataM2Raw: + return TrackType.CDMode2Formless; + case DAOMode.Audio: + case DAOMode.AudioSub: + return TrackType.Audio; + default: + return TrackType.Data; + } + } + + static UInt16 NeroTrackModeToBytesPerSector(DAOMode mode) + { + switch (mode) + { + case DAOMode.Data: + case DAOMode.DataM2F1: + return 2048; + case DAOMode.DataM2F2: + return 2336; + case DAOMode.DataRaw: + case DAOMode.DataM2Raw: + case DAOMode.Audio: + return 2352; + case DAOMode.DataM2RawSub: + case DAOMode.DataRawSub: + case DAOMode.AudioSub: + return 2448; + default: + return 2352; + } + } + + #endregion + + #region Unsupported features + + public override int GetDiskSequence() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override int GetLastDiskSequence() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override string GetDriveManufacturer() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override string GetDriveModel() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override string GetDriveSerialNumber() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override string GetDiskPartNumber() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override string GetDiskManufacturer() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override string GetDiskModel() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override string GetImageName() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override string GetImageCreator() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override string GetImageComments() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + public override string GetDiskSerialNumber() + { + throw new FeatureUnsupportedImageException("Feature not supported by image format"); + } + + #endregion + } +} \ No newline at end of file diff --git a/README.md b/README.md index 237f3010..7aade5ae 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Works under any operating system where there is Mono or .NET Framework. Tested w Features ======== -* Supports reading CDRWin cue/bin cuesheets, Apple DiskCopy 4.2 and TeleDisk disk images. +* Supports reading CDRWin cue/bin cuesheets, Apple DiskCopy 4.2, TeleDisk and Nero Burning ROM disk images. * Supports reading all raw (sector by sector copy) disk images with a multiple of 512 bytes/sector, and a few known formats that are 256, 128 and variable bytes per sector. * Supports traversing MBR, Apple and NeXT partitioning schemes. * Identifies HFS, HFS+, MFS, BeFS, ext/2/3/4, FAT12/16/32, FFS/UFS/UFS2, HPFS, ISO9660, LisaFS, MinixFS, NTFS, ODS11, Opera, PCEngine, SolarFS, System V and UnixWare boot filesystem. diff --git a/TODO b/TODO index 6062d037..4bab4fd8 100644 --- a/TODO +++ b/TODO @@ -1,10 +1,8 @@ Disc image plugins: ---- Add support for no disc image, that is, raw. --- Add support for BlindWrite images --- Add support for CloneCD images --- Add support for DiscJuggler images --- Add support for Alcohol images ---- Add support for Nero images --- Add support for cdrdao images --- Add support for dump(8) images --- Add support for IMD images @@ -66,4 +64,24 @@ Other things: Teledisk plugin: --- Add support for "advanced compression" ---- Handle variable sectors per track situation. Teledisk seems to be able to read garbage from previous formattings. \ No newline at end of file +--- Handle variable sectors per track situation. Teledisk seems to be able to read garbage from previous formattings. + +RAW plugin: +--- Finish support for reading sectors of variable bytes/sector images + +Image comparison: +--- Compare sector tags +--- Offer the option to see differing values +--- Optimize and multithread + +Image verification: +--- Implement verification on image plugins to implement this verb + +Checksums: +--- Implement SpamSum fuzzy hashing (aka ssdeep) + +Image checksum: +--- Checksum disk tags +--- Checksum sector tags +--- Checksum long sectors +--- Optimize and multithread \ No newline at end of file