diff --git a/DiscImageChef.DiscImages/Alcohol120.cs b/DiscImageChef.DiscImages/Alcohol120.cs index 4375ec71..c374c0ca 100644 --- a/DiscImageChef.DiscImages/Alcohol120.cs +++ b/DiscImageChef.DiscImages/Alcohol120.cs @@ -29,14 +29,1722 @@ // ---------------------------------------------------------------------------- // Copyright © 2011-2016 Natalia Portillo // ****************************************************************************/ + using System; -namespace DiscImageChef.DiscImages +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using DiscImageChef.CommonTypes; +using DiscImageChef.Console; +using System.Text; + +namespace DiscImageChef.ImagePlugins { - public class Alcohol120 + public class Alcohol120 : ImagePlugin { + #region Internal Structures + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct AlcoholHeader + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] + public string signature; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] version; + public AlcoholMediumType type; + public ushort sessions; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public ushort[] unknown1; + public ushort bcaLength; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public uint[] unknown2; + public uint bcaOffset; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public uint[] unknown3; + public uint structuresOffset; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public uint[] unknown4; + public uint sessionOffset; + public uint dpmOffset; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct AlcoholSession + { + public int sessionStart; + public int sessionEnd; + public ushort sessionSequence; + public byte allBlocks; + public byte nonTrackBlocks; + public ushort firstTrack; + public ushort lastTrack; + public uint unknown; + public uint trackOffset; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct AlcoholTrack + { + public AlcoholTrackMode mode; + public AlcoholSubchannelMode subMode; + public byte adrCtl; + public byte tno; + public byte point; + public byte min; + public byte sec; + public byte frame; + public byte zero; + public byte pmin; + public byte psec; + public byte pframe; + public uint extraOffset; + public ushort sectorSize; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)] + public byte[] unknown; + public uint startLba; + public ulong startOffset; + public uint files; + public uint footerOffset; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] + public byte[] unknown2; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct AlcoholTrackExtra + { + public uint pregap; + public uint sectors; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct AlcoholFooter + { + public uint filenameOffset; + public uint widechar; + public uint unknown1; + public uint unknown2; + } + #endregion Internal Structures + + #region Internal enumerations + enum AlcoholMediumType : ushort + { + CD = 0x00, + CDR = 0x01, + CDRW = 0x02, + DVD = 0x10, + DVDR = 0x12 + } + + enum AlcoholTrackMode : byte + { + NoData = 0x00, + DVD = 0x02, + Audio = 0xA9, + Mode1 = 0xAA, + Mode2 = 0xAB, + Mode2F1 = 0xAC, + Mode2F2 = 0xAD + } + + enum AlcoholSubchannelMode : byte + { + None = 0x00, + Interleaved = 0x08 + } + #endregion Internal enumerations + + #region Internal variables + + Dictionary offsetmap; + List partitions; + Dictionary alcSessions; + Dictionary alcTracks; + Dictionary> alcToc; + Dictionary alcTrackExtras; + AlcoholFooter alcFooter; + string alcImage; + byte[] bca; + List sessions; + FileStream imageStream; + byte[] fullToc; + bool isDvd; + byte[] dmi; + byte[] pfi; + + #endregion + + #region Public Methods + public Alcohol120() { + Name = "Alcohol 120% Media Descriptor Structure"; + PluginUUID = new Guid("A78FBEBA-0307-4915-BDE3-B8A3B57F843F\n"); + ImageInfo = new ImageInfo(); + ImageInfo.readableSectorTags = new List(); + ImageInfo.readableMediaTags = new List(); + ImageInfo.imageHasPartitions = false; + ImageInfo.imageHasSessions = false; + ImageInfo.imageVersion = null; + ImageInfo.imageApplication = null; + ImageInfo.imageApplicationVersion = null; + ImageInfo.imageCreator = null; + ImageInfo.imageComments = null; + ImageInfo.mediaManufacturer = null; + ImageInfo.mediaModel = null; + ImageInfo.mediaSerialNumber = null; + ImageInfo.mediaBarcode = null; + ImageInfo.mediaPartNumber = null; + ImageInfo.mediaSequence = 0; + ImageInfo.lastMediaSequence = 0; + ImageInfo.driveManufacturer = null; + ImageInfo.driveModel = null; + ImageInfo.driveSerialNumber = null; } + + public override bool IdentifyImage(string imagePath) + { + FileStream stream = new FileStream(imagePath, FileMode.Open, FileAccess.Read); + stream.Seek(0, SeekOrigin.Begin); + if(stream.Length < 88) + return false; + + byte[] hdr = new byte[88]; + stream.Read(hdr, 0, 88); + AlcoholHeader header = new AlcoholHeader(); + IntPtr hdrPtr = Marshal.AllocHGlobal(88); + Marshal.Copy(hdr, 0, hdrPtr, 88); + header = (AlcoholHeader)Marshal.PtrToStructure(hdrPtr, typeof(AlcoholHeader)); + Marshal.FreeHGlobal(hdrPtr); + stream.Close(); + + return header.signature == "MEDIA DESCRIPTO"; + } + + public override bool OpenImage(string imagePath) + { + FileStream stream = new FileStream(imagePath, FileMode.Open, FileAccess.Read); + stream.Seek(0, SeekOrigin.Begin); + if(stream.Length < 88) + return false; + + isDvd = false; + byte[] hdr = new byte[88]; + stream.Read(hdr, 0, 88); + AlcoholHeader header = new AlcoholHeader(); + IntPtr hdrPtr = Marshal.AllocHGlobal(88); + Marshal.Copy(hdr, 0, hdrPtr, 88); + header = (AlcoholHeader)Marshal.PtrToStructure(hdrPtr, typeof(AlcoholHeader)); + Marshal.FreeHGlobal(hdrPtr); + + DicConsole.DebugWriteLine("Alcohol 120% plugin", "header.signature = {0}", header.signature); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "header.version = {0}.{1}", header.version[0], header.version[1]); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "header.type = {0}", header.type); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "header.sessions = {0}", header.sessions); + for(int i = 0; i < header.unknown1.Length; i++) + DicConsole.DebugWriteLine("Alcohol 120% plugin", "header.unknown1[{1}] = 0x{0:X4}", header.unknown1[i], i); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "header.bcaLength = {0}", header.bcaLength); + for(int i = 0; i < header.unknown2.Length; i++) + DicConsole.DebugWriteLine("Alcohol 120% plugin", "header.unknown2[{1}] = 0x{0:X8}", header.unknown2[i], i); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "header.bcaOffset = {0}", header.bcaOffset); + for(int i = 0; i < header.unknown3.Length; i++) + DicConsole.DebugWriteLine("Alcohol 120% plugin", "header.unknown3[{1}] = 0x{0:X8}", header.unknown3[i], i); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "header.structuresOffset = {0}", header.structuresOffset); + for(int i = 0; i < header.unknown4.Length; i++) + DicConsole.DebugWriteLine("Alcohol 120% plugin", "header.unknown4[{1}] = 0x{0:X8}", header.unknown4[i], i); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "header.sessionOffset = {0}", header.sessionOffset); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "header.dpmOffset = {0}", header.dpmOffset); + + stream.Seek(header.sessionOffset, SeekOrigin.Begin); + alcSessions = new Dictionary(); + for(int i = 0; i < header.sessions; i++) + { + byte[] sesHdr = new byte[24]; + stream.Read(sesHdr, 0, 24); + AlcoholSession session = new AlcoholSession(); + IntPtr sesPtr = Marshal.AllocHGlobal(24); + Marshal.Copy(sesHdr, 0, sesPtr, 24); + session = (AlcoholSession)Marshal.PtrToStructure(sesPtr, typeof(AlcoholSession)); + Marshal.FreeHGlobal(sesPtr); + + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].sessionStart = {0}", session.sessionStart, i); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].sessionEnd = {0}", session.sessionEnd, i); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].sessionSequence = {0}", session.sessionSequence, i); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].allBlocks = {0}", session.allBlocks, i); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].nonTrackBlocks = {0}", session.nonTrackBlocks, i); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].firstTrack = {0}", session.firstTrack, i); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].lastTrack = {0}", session.lastTrack, i); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].unknown = 0x{0:X8}", session.unknown, i); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].trackOffset = {0}", session.trackOffset, i); + + alcSessions.Add(session.sessionSequence, session); + } + + long footerOff = 0; + + alcTracks = new Dictionary(); + alcToc = new Dictionary>(); + foreach(AlcoholSession session in alcSessions.Values) + { + stream.Seek(session.trackOffset, SeekOrigin.Begin); + Dictionary sesToc = new Dictionary(); + for(int i = 0; i < session.allBlocks; i++) + { + byte[] trkHdr; + AlcoholTrack track; + IntPtr trkPtr; + + trkHdr = new byte[80]; + stream.Read(trkHdr, 0, 80); + track = new AlcoholTrack(); + trkPtr = Marshal.AllocHGlobal(80); + Marshal.Copy(trkHdr, 0, trkPtr, 80); + track = (AlcoholTrack)Marshal.PtrToStructure(trkPtr, typeof(AlcoholTrack)); + Marshal.FreeHGlobal(trkPtr); + + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].mode = {0}", track.mode, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].subMode = {0}", track.subMode, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].adrCtl = {0}", track.adrCtl, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].tno = {0}", track.tno, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].point = {0:X2}", track.point, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].min = {0}", track.min, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].sec = {0}", track.sec, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].frame = {0}", track.frame, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].zero = {0}", track.zero, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].pmin = {0}", track.pmin, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].psec = {0}", track.psec, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].pframe = {0}", track.pframe, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].extraOffset = {0}", track.extraOffset, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].sectorSize = {0}", track.sectorSize, track.point, session.sessionSequence); + //for(int j = 0; j < track.unknown.Length; j++) + // DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].unknown[{2}] = {0}", track.unknown[j], i, j, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].startLba = {0}", track.startLba, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].startOffset = {0}", track.startOffset, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].files = {0}", track.files, track.point, session.sessionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].footerOffset = {0}", track.footerOffset, track.point, session.sessionSequence); + //for(int j = 0; j < track.unknown2.Length; j++) + // DicConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].unknown2[{2}] = {0}", track.unknown2[j], i, j, session.sessionSequence); + + if(!sesToc.ContainsKey(track.point)) + sesToc.Add(track.point, track); + + if(track.point < 0xA0) + alcTracks.Add(track.point, track); + + if(footerOff == 0) + footerOff = track.footerOffset; + + isDvd |= track.mode == AlcoholTrackMode.DVD; + } + alcToc.Add(session.sessionSequence, sesToc); + } + + alcTrackExtras = new Dictionary(); + foreach(AlcoholTrack track in alcTracks.Values) + { + if(track.extraOffset > 0 && !isDvd) + { + byte[] extHdr = new byte[8]; + stream.Seek(track.extraOffset, SeekOrigin.Begin); + stream.Read(extHdr, 0, 8); + AlcoholTrackExtra extra = new AlcoholTrackExtra(); + IntPtr extPtr = Marshal.AllocHGlobal(8); + Marshal.Copy(extHdr, 0, extPtr, 8); + extra = (AlcoholTrackExtra)Marshal.PtrToStructure(extPtr, typeof(AlcoholTrackExtra)); + Marshal.FreeHGlobal(extPtr); + + DicConsole.DebugWriteLine("Alcohol 120% plugin", "track[{1}].extra.pregap = {0}", extra.pregap, track.point); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "track[{1}].extra.sectors = {0}", extra.sectors, track.point); + + alcTrackExtras.Add(track.point, extra); + } + else if(isDvd) + { + AlcoholTrackExtra extra = new AlcoholTrackExtra(); + extra.sectors = track.extraOffset; + alcTrackExtras.Add(track.point, extra); + } + } + + if(footerOff > 0) + { + byte[] footer = new byte[16]; + stream.Seek(footerOff, SeekOrigin.Begin); + stream.Read(footer, 0, 16); + alcFooter = new AlcoholFooter(); + IntPtr footPtr = Marshal.AllocHGlobal(16); + Marshal.Copy(footer, 0, footPtr, 16); + alcFooter = (AlcoholFooter)Marshal.PtrToStructure(footPtr, typeof(AlcoholFooter)); + Marshal.FreeHGlobal(footPtr); + + DicConsole.DebugWriteLine("Alcohol 120% plugin", "footer.filenameOffset = {0}", alcFooter.filenameOffset); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "footer.widechar = {0}", alcFooter.widechar); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "footer.unknown1 = 0x{0:X8}", alcFooter.unknown1); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "footer.unknown2 = 0x{0:X8}", alcFooter.unknown2); + } + + if(alcFooter.filenameOffset > 0) + { + stream.Seek(alcFooter.filenameOffset, SeekOrigin.Begin); + byte[] filename; + if(header.dpmOffset == 0) + filename = new byte[stream.Length - stream.Position]; + else + filename = new byte[header.dpmOffset - stream.Position]; + + stream.Read(filename, 0, filename.Length); + if(alcFooter.widechar == 1) + alcImage = Encoding.Unicode.GetString(filename); + else + alcImage = Encoding.Default.GetString(filename); + + DicConsole.DebugWriteLine("Alcohol 120% plugin", "footer.filename = {0}", alcImage); + } + + if(alcFooter.filenameOffset == 0 || string.Compare(alcImage, "*.mdf", StringComparison.InvariantCultureIgnoreCase) == 0) + alcImage = Path.GetFileNameWithoutExtension(imagePath) + ".mdf"; + + if(header.bcaLength > 0 && header.bcaOffset > 0 && isDvd) + { + bca = new byte[header.bcaLength]; + stream.Seek(header.bcaOffset, SeekOrigin.Begin); + int readBytes = stream.Read(bca, 0, bca.Length); + + if(readBytes == bca.Length) + { + switch(header.type) + { + case AlcoholMediumType.DVD: + case AlcoholMediumType.DVDR: + ImageInfo.readableMediaTags.Add(MediaTagType.DVD_BCA); + break; + } + } + } + + ImageInfo.mediaType = AlcoholMediumTypeToMediaType(header.type); + + if(isDvd) + { + // TODO: Second layer + if(header.structuresOffset >= 0) + { + byte[] structures = new byte[4100]; + stream.Seek(header.structuresOffset, SeekOrigin.Begin); + stream.Read(structures, 0, 4100); + dmi = new byte[2052]; + pfi = new byte[2052]; + + Array.Copy(structures, 0, dmi, 0, 2052); + Array.Copy(structures, 0x804, pfi, 4, 2048); + + pfi[0] = 0x08; + pfi[1] = 0x02; + dmi[0] = 0x08; + dmi[1] = 0x02; + + Decoders.DVD.PFI.PhysicalFormatInformation? pfi0 = Decoders.DVD.PFI.Decode(pfi); + + // All discs I tested the disk category and part version (as well as the start PSN for DVD-RAM) where modified by Alcohol + // So much for archival value + if(pfi0.HasValue) + { + switch(pfi0.Value.DiskCategory) + { + case Decoders.DVD.DiskCategory.DVDPR: + ImageInfo.mediaType = MediaType.DVDPR; + break; + case Decoders.DVD.DiskCategory.DVDPRDL: + ImageInfo.mediaType = MediaType.DVDPRDL; + break; + case Decoders.DVD.DiskCategory.DVDPRW: + ImageInfo.mediaType = MediaType.DVDPRW; + break; + case Decoders.DVD.DiskCategory.DVDPRWDL: + ImageInfo.mediaType = MediaType.DVDPRWDL; + break; + case Decoders.DVD.DiskCategory.DVDR: + if(pfi0.Value.PartVersion == 6) + ImageInfo.mediaType = MediaType.DVDRDL; + else + ImageInfo.mediaType = MediaType.DVDR; + break; + case Decoders.DVD.DiskCategory.DVDRAM: + ImageInfo.mediaType = MediaType.DVDRAM; + break; + default: + ImageInfo.mediaType = MediaType.DVDROM; + break; + case Decoders.DVD.DiskCategory.DVDRW: + if(pfi0.Value.PartVersion == 3) + ImageInfo.mediaType = MediaType.DVDRWDL; + else + ImageInfo.mediaType = MediaType.DVDRW; + break; + case Decoders.DVD.DiskCategory.HDDVDR: + ImageInfo.mediaType = MediaType.HDDVDR; + break; + case Decoders.DVD.DiskCategory.HDDVDRAM: + ImageInfo.mediaType = MediaType.HDDVDRAM; + break; + case Decoders.DVD.DiskCategory.HDDVDROM: + ImageInfo.mediaType = MediaType.HDDVDROM; + break; + case Decoders.DVD.DiskCategory.HDDVDRW: + ImageInfo.mediaType = MediaType.HDDVDRW; + break; + case Decoders.DVD.DiskCategory.Nintendo: + if(pfi0.Value.DiscSize == Decoders.DVD.DVDSize.Eighty) + ImageInfo.mediaType = MediaType.GOD; + else + ImageInfo.mediaType = MediaType.WOD; + break; + case Decoders.DVD.DiskCategory.UMD: + ImageInfo.mediaType = MediaType.UMD; + break; + } + + if(Decoders.Xbox.DMI.IsXbox360(dmi)) + ImageInfo.mediaType = MediaType.XGD2; + + ImageInfo.readableMediaTags.Add(MediaTagType.DVD_PFI); + ImageInfo.readableMediaTags.Add(MediaTagType.DVD_DMI); + } + } + } + else if(header.type == AlcoholMediumType.CD) + { + bool data = false; + bool mode2 = false; + bool firstaudio = false; + bool firstdata = false; + bool audio = false; + int i = 0; + + foreach(AlcoholTrack _track in alcTracks.Values) + { + // First track is audio + firstaudio |= i == 0 && _track.mode == AlcoholTrackMode.Audio; + + // First track is data + firstdata |= i == 0 && _track.mode != AlcoholTrackMode.Audio; + + // Any non first track is data + data |= i != 0 && _track.mode != AlcoholTrackMode.Audio; + + // Any non first track is audio + audio |= i != 0 && _track.mode == AlcoholTrackMode.Audio; + + switch(_track.mode) + { + case AlcoholTrackMode.Mode2: + case AlcoholTrackMode.Mode2F1: + case AlcoholTrackMode.Mode2F2: + mode2 = true; + break; + } + } + + if(!data && !firstdata) + ImageInfo.mediaType = MediaType.CDDA; + else if(firstaudio && data && sessions.Count > 1 && mode2) + ImageInfo.mediaType = MediaType.CDPLUS; + else if((firstdata && audio) || mode2) + ImageInfo.mediaType = MediaType.CDROMXA; + else if(!audio) + ImageInfo.mediaType = MediaType.CDROM; + else + ImageInfo.mediaType = MediaType.CD; + } + + DicConsole.DebugWriteLine("Alcohol 120% plugin", "ImageInfo.mediaType = {0}", ImageInfo.mediaType); + + stream.Close(); + + sessions = new List(); + foreach(AlcoholSession alcSes in alcSessions.Values) + { + Session session = new Session(); + AlcoholTrack stTrk; + AlcoholTrack enTrk; + AlcoholTrackExtra enTrkExt; + + if(!alcTracks.TryGetValue(alcSes.firstTrack, out stTrk)) + break; + if(!alcTracks.TryGetValue(alcSes.lastTrack, out enTrk)) + break; + if(!alcTrackExtras.TryGetValue(alcSes.lastTrack, out enTrkExt)) + break; + + session.StartSector = stTrk.startLba; + session.StartTrack = alcSes.firstTrack; + session.SessionSequence = alcSes.sessionSequence; + session.EndSector = enTrk.startLba + enTrkExt.sectors - 1; + session.EndTrack = alcSes.lastTrack; + + sessions.Add(session); + } + + partitions = new List(); + offsetmap = new Dictionary(); + ulong byte_offset = 0; + + foreach(AlcoholTrack trk in alcTracks.Values) + { + AlcoholTrackExtra extra; + if(alcTrackExtras.TryGetValue(trk.point, out extra)) + { + Partition partition = new Partition(); + + partition.PartitionDescription = string.Format("Track {0}.", trk.point); + partition.PartitionStartSector = trk.startLba; + partition.PartitionLength = extra.sectors * trk.sectorSize; + partition.PartitionSectors = extra.sectors; + partition.PartitionSequence = trk.point; + partition.PartitionStart = byte_offset; + partition.PartitionType = trk.mode.ToString(); + + partitions.Add(partition); + ImageInfo.sectors += extra.sectors; + byte_offset += partition.PartitionLength; + } + + if(!offsetmap.ContainsKey(trk.point)) + offsetmap.Add(trk.point, trk.startLba); + + switch(trk.mode) + { + case AlcoholTrackMode.Mode1: + case AlcoholTrackMode.Mode2F1: + 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); + if(ImageInfo.sectorSize < 2048) + ImageInfo.sectorSize = 2048; + break; + case AlcoholTrackMode.Mode2: + if(!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorSync)) + ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorSync); + if(!ImageInfo.readableSectorTags.Contains(SectorTagType.CDSectorHeader)) + ImageInfo.readableSectorTags.Add(SectorTagType.CDSectorHeader); + if(ImageInfo.sectorSize < 2336) + ImageInfo.sectorSize = 2336; + break; + case AlcoholTrackMode.Mode2F2: + 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); + if(ImageInfo.sectorSize < 2324) + ImageInfo.sectorSize = 2324; + break; + case AlcoholTrackMode.DVD: + ImageInfo.sectorSize = 2048; + break; + default: + ImageInfo.sectorSize = 2352; + break; + } + } + + DicConsole.DebugWriteLine("Alcohol 120% plugin", "printing partition map"); + foreach(Partition partition in partitions) + { + DicConsole.DebugWriteLine("Alcohol 120% plugin", "Partition sequence: {0}", partition.PartitionSequence); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "\tPartition name: {0}", partition.PartitionName); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "\tPartition description: {0}", partition.PartitionDescription); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "\tPartition type: {0}", partition.PartitionType); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "\tPartition starting sector: {0}", partition.PartitionStartSector); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "\tPartition sectors: {0}", partition.PartitionSectors); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "\tPartition starting offset: {0}", partition.PartitionStart); + DicConsole.DebugWriteLine("Alcohol 120% plugin", "\tPartition size in bytes: {0}", partition.PartitionLength); + } + + ImageInfo.imageApplication = "Alcohol 120%"; + + DicConsole.DebugWriteLine("Alcohol 120% plugin", "Data filename: {0}", alcImage); + + FileInfo fi = new FileInfo(alcImage); + ImageInfo.imageSize = (ulong)fi.Length; + ImageInfo.imageCreationTime = fi.CreationTimeUtc; + ImageInfo.imageLastModificationTime = fi.LastWriteTimeUtc; + ImageInfo.xmlMediaType = XmlMediaType.OpticalDisc; + ImageInfo.imageVersion = string.Format("{0}.{1}", header.version[0], header.version[1]); + + if(!isDvd) + { + DicConsole.DebugWriteLine("Alcohol 120% plugin", "Rebuilding TOC"); + byte firstSession = byte.MaxValue; + byte lastSession = 0; + MemoryStream tocMs = new MemoryStream(); + tocMs.Write(new byte[] { 0, 0, 0, 0 }, 0, 4); // Reserved for TOC response size and session numbers + foreach(KeyValuePair> sessionToc in alcToc) + { + if(sessionToc.Key < firstSession) + firstSession = (byte)sessionToc.Key; + if(sessionToc.Key > lastSession) + lastSession = (byte)sessionToc.Key; + + foreach(AlcoholTrack sessionTrack in sessionToc.Value.Values) + { + tocMs.WriteByte((byte)sessionToc.Key); + tocMs.WriteByte(sessionTrack.adrCtl); + tocMs.WriteByte(sessionTrack.tno); + tocMs.WriteByte(sessionTrack.point); + tocMs.WriteByte(sessionTrack.min); + tocMs.WriteByte(sessionTrack.sec); + tocMs.WriteByte(sessionTrack.frame); + tocMs.WriteByte(sessionTrack.zero); + tocMs.WriteByte(sessionTrack.pmin); + tocMs.WriteByte(sessionTrack.psec); + tocMs.WriteByte(sessionTrack.pframe); + } + } + fullToc = tocMs.ToArray(); + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + byte[] fullTocSize = BigEndianBitConverter.GetBytes((short)(fullToc.Length - 2)); + fullToc[0] = fullTocSize[0]; + fullToc[1] = fullTocSize[1]; + fullToc[2] = firstSession; + fullToc[3] = lastSession; + + Decoders.CD.FullTOC.CDFullTOC? decodedFullToc = Decoders.CD.FullTOC.Decode(fullToc); + + if(!decodedFullToc.HasValue) + { + DicConsole.DebugWriteLine("Alcohol 120% plugin", "TOC not correctly rebuilt"); + fullToc = null; + } + else + ImageInfo.readableMediaTags.Add(MediaTagType.CD_FullTOC); + + ImageInfo.readableSectorTags.Add(SectorTagType.CDTrackFlags); + } + + return true; + } + + public override bool ImageHasPartitions() + { + return ImageInfo.imageHasPartitions; + } + + public override ulong GetImageSize() + { + return ImageInfo.imageSize; + } + + public override ulong GetSectors() + { + return ImageInfo.sectors; + } + + public override uint GetSectorSize() + { + return ImageInfo.sectorSize; + } + + public override byte[] ReadDiskTag(MediaTagType tag) + { + switch(tag) + { + case MediaTagType.DVD_BCA: + { + if(bca != null) + { + return (byte[])bca.Clone(); + } + throw new FeatureNotPresentImageException("Image does not contain BCA information."); + } + case MediaTagType.CD_FullTOC: + { + if(fullToc != null) + { + return (byte[])fullToc.Clone(); + } + throw new FeatureNotPresentImageException("Image does not contain TOC information."); + } + default: + throw new FeatureSupportedButNotImplementedImageException("Feature not supported by image format"); + } + } + + public override byte[] ReadSector(ulong sectorAddress) + { + return ReadSectors(sectorAddress, 1); + } + + public override byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag) + { + return ReadSectorsTag(sectorAddress, 1, tag); + } + + public override byte[] ReadSector(ulong sectorAddress, uint track) + { + return ReadSectors(sectorAddress, 1, track); + } + + public override byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag) + { + return ReadSectorsTag(sectorAddress, 1, track, tag); + } + + public override byte[] ReadSectors(ulong sectorAddress, uint length) + { + foreach(KeyValuePair kvp in offsetmap) + { + if(sectorAddress >= kvp.Value) + { + foreach(AlcoholTrack track in alcTracks.Values) + { + AlcoholTrackExtra extra; + + if(track.point == kvp.Key && alcTrackExtras.TryGetValue(track.point, out extra)) + { + if((sectorAddress - kvp.Value) < extra.sectors) + return ReadSectors((sectorAddress - kvp.Value), length, kvp.Key); + } + } + } + } + + throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found"); + } + + public override byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag) + { + foreach(KeyValuePair kvp in offsetmap) + { + if(sectorAddress >= kvp.Value) + { + foreach(AlcoholTrack track in alcTracks.Values) + { + AlcoholTrackExtra extra; + + if(track.point == kvp.Key && alcTrackExtras.TryGetValue(track.point, out extra)) + { + if((sectorAddress - kvp.Value) < extra.sectors) + return ReadSectorsTag((sectorAddress - kvp.Value), length, kvp.Key, tag); + } + } + } + } + + throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found"); + } + + public override byte[] ReadSectors(ulong sectorAddress, uint length, uint track) + { + AlcoholTrack _track; + AlcoholTrackExtra _extra; + + if(!alcTracks.TryGetValue((int)track, out _track) || !alcTrackExtras.TryGetValue((int)track, out _extra)) + throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image"); + + if(length + sectorAddress > _extra.sectors) + throw new ArgumentOutOfRangeException(nameof(length), string.Format("Requested more sectors ({0}) than present in track ({1}), won't cross tracks", length + sectorAddress, _extra.sectors)); + + uint sector_offset; + uint sector_size; + uint sector_skip; + + switch(_track.mode) + { + case AlcoholTrackMode.Mode1: + { + sector_offset = 16; + sector_size = 2048; + sector_skip = 288; + break; + } + case AlcoholTrackMode.Mode2: + { + sector_offset = 16; + sector_size = 2336; + sector_skip = 0; + break; + } + case AlcoholTrackMode.Mode2F1: + { + sector_offset = 24; + sector_size = 2048; + sector_skip = 280; + break; + } + case AlcoholTrackMode.Mode2F2: + { + sector_offset = 24; + sector_size = 2324; + sector_skip = 4; + break; + } + case AlcoholTrackMode.Audio: + { + sector_offset = 0; + sector_size = 2352; + sector_skip = 0; + break; + } + case AlcoholTrackMode.DVD: + { + sector_offset = 0; + sector_size = 2048; + sector_skip = 0; + break; + } + default: + throw new FeatureSupportedButNotImplementedImageException("Unsupported track type"); + } + + switch(_track.subMode) + { + case AlcoholSubchannelMode.None: + sector_skip += 0; + break; + case AlcoholSubchannelMode.Interleaved: + sector_skip += 96; + break; + default: + throw new FeatureSupportedButNotImplementedImageException("Unsupported subchannel type"); + } + + byte[] buffer = new byte[sector_size * length]; + + imageStream = new FileStream(alcImage, FileMode.Open, FileAccess.Read); + using(BinaryReader br = new BinaryReader(imageStream)) + { + br.BaseStream.Seek((long)_track.startOffset + (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(ulong sectorAddress, uint length, uint track, SectorTagType tag) + { + AlcoholTrack _track; + AlcoholTrackExtra _extra; + + if(!alcTracks.TryGetValue((int)track, out _track) || !alcTrackExtras.TryGetValue((int)track, out _extra)) + throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image"); + + if(length + sectorAddress > _extra.sectors) + throw new ArgumentOutOfRangeException(nameof(length), string.Format("Requested more sectors ({0}) than present in track ({1}), won't cross tracks", length, _extra.sectors)); + + uint sector_offset; + uint sector_size; + uint sector_skip; + + if(_track.mode == AlcoholTrackMode.DVD) + throw new ArgumentException("Unsupported tag requested", nameof(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: + return new byte[] { ((byte)(_track.adrCtl & 0x0F)) }; + default: + throw new ArgumentException("Unsupported tag requested", nameof(tag)); + } + + switch(_track.mode) + { + case AlcoholTrackMode.Mode1: + 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.CDSectorSubHeader: + throw new ArgumentException("Unsupported tag requested for this track", nameof(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; + } + case SectorTagType.CDSectorSubchannel: + { + switch(_track.subMode) + { + case AlcoholSubchannelMode.Interleaved: + + sector_offset = 2352; + sector_size = 96; + sector_skip = 0; + break; + default: + throw new ArgumentException("Unsupported tag requested", nameof(tag)); + } + break; + } + default: + throw new ArgumentException("Unsupported tag requested", nameof(tag)); + } + break; + case AlcoholTrackMode.Mode2: + { + switch(tag) + { + case SectorTagType.CDSectorSync: + case SectorTagType.CDSectorHeader: + case SectorTagType.CDSectorECC: + case SectorTagType.CDSectorECC_P: + case SectorTagType.CDSectorECC_Q: + throw new ArgumentException("Unsupported tag requested for this track", nameof(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; + } + case SectorTagType.CDSectorSubchannel: + { + switch(_track.subMode) + { + case AlcoholSubchannelMode.Interleaved: + + sector_offset = 2352; + sector_size = 96; + sector_skip = 0; + break; + default: + throw new ArgumentException("Unsupported tag requested", nameof(tag)); + } + break; + } + default: + throw new ArgumentException("Unsupported tag requested", nameof(tag)); + } + break; + } + case AlcoholTrackMode.Mode2F1: + 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.CDSectorSubHeader: + { + sector_offset = 16; + sector_size = 8; + sector_skip = 2328; + break; + } + 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 = 2072; + sector_size = 4; + sector_skip = 276; + break; + } + case SectorTagType.CDSectorSubchannel: + { + switch(_track.subMode) + { + case AlcoholSubchannelMode.Interleaved: + + sector_offset = 2352; + sector_size = 96; + sector_skip = 0; + break; + default: + throw new ArgumentException("Unsupported tag requested", nameof(tag)); + } + break; + } + default: + throw new ArgumentException("Unsupported tag requested", nameof(tag)); + } + break; + case AlcoholTrackMode.Mode2F2: + 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.CDSectorSubHeader: + { + sector_offset = 16; + sector_size = 8; + sector_skip = 2328; + break; + } + case SectorTagType.CDSectorEDC: + { + sector_offset = 2348; + sector_size = 4; + sector_skip = 0; + break; + } + case SectorTagType.CDSectorSubchannel: + { + switch(_track.subMode) + { + case AlcoholSubchannelMode.Interleaved: + + sector_offset = 2352; + sector_size = 96; + sector_skip = 0; + break; + default: + throw new ArgumentException("Unsupported tag requested", nameof(tag)); + } + break; + } + default: + throw new ArgumentException("Unsupported tag requested", nameof(tag)); + } + break; + case AlcoholTrackMode.Audio: + { + switch(tag) + { + case SectorTagType.CDSectorSubchannel: + { + switch(_track.subMode) + { + case AlcoholSubchannelMode.Interleaved: + + sector_offset = 2352; + sector_size = 96; + sector_skip = 0; + break; + default: + throw new ArgumentException("Unsupported tag requested", nameof(tag)); + } + break; + } + default: + throw new ArgumentException("Unsupported tag requested", nameof(tag)); + } + break; + } + default: + throw new FeatureSupportedButNotImplementedImageException("Unsupported track type"); + } + + switch(_track.subMode) + { + case AlcoholSubchannelMode.None: + sector_skip += 0; + break; + case AlcoholSubchannelMode.Interleaved: + if(tag != SectorTagType.CDSectorSubchannel) + sector_skip += 96; + break; + default: + throw new FeatureSupportedButNotImplementedImageException("Unsupported subchannel type"); + } + + byte[] buffer = new byte[sector_size * length]; + + imageStream = new FileStream(alcImage, FileMode.Open, FileAccess.Read); + using(BinaryReader br = new BinaryReader(imageStream)) + { + br.BaseStream.Seek((long)_track.startOffset + (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(ulong sectorAddress) + { + return ReadSectorsLong(sectorAddress, 1); + } + + public override byte[] ReadSectorLong(ulong sectorAddress, uint track) + { + return ReadSectorsLong(sectorAddress, 1, track); + } + + public override byte[] ReadSectorsLong(ulong sectorAddress, uint length) + { + foreach(KeyValuePair kvp in offsetmap) + { + if(sectorAddress >= kvp.Value) + { + foreach(AlcoholTrack track in alcTracks.Values) + { + AlcoholTrackExtra extra; + + if(track.point == kvp.Key && alcTrackExtras.TryGetValue(track.point, out extra)) + { + if((sectorAddress - kvp.Value) < extra.sectors) + return ReadSectorsLong((sectorAddress - kvp.Value), length, kvp.Key); + } + } + } + } + + throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Sector address not found"); + } + + public override byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track) + { + AlcoholTrack _track; + AlcoholTrackExtra _extra; + + if(!alcTracks.TryGetValue((int)track, out _track) || !alcTrackExtras.TryGetValue((int)track, out _extra)) + throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image"); + + if(length + sectorAddress > _extra.sectors) + throw new ArgumentOutOfRangeException(nameof(length), string.Format("Requested more sectors ({0}) than present in track ({1}), won't cross tracks", length, _extra.sectors)); + + uint sector_offset; + uint sector_size; + uint sector_skip; + + switch(_track.mode) + { + case AlcoholTrackMode.Mode1: + case AlcoholTrackMode.Mode2: + case AlcoholTrackMode.Mode2F1: + case AlcoholTrackMode.Mode2F2: + case AlcoholTrackMode.Audio: + case AlcoholTrackMode.DVD: + { + sector_offset = 0; + sector_size = _track.sectorSize; + sector_skip = 0; + break; + } + default: + throw new FeatureSupportedButNotImplementedImageException("Unsupported track type"); + } + + byte[] buffer = new byte[sector_size * length]; + + imageStream = new FileStream(alcImage, FileMode.Open, FileAccess.Read); + using(BinaryReader br = new BinaryReader(imageStream)) + { + br.BaseStream.Seek((long)_track.startOffset + (long)(sectorAddress * (sector_offset + sector_size + sector_skip)), SeekOrigin.Begin); + buffer = br.ReadBytes((int)(sector_size * length)); + } + + return buffer; + } + + public override string GetImageFormat() + { + return "Alcohol 120% Media Descriptor Structure"; + } + + public override string GetImageVersion() + { + return ImageInfo.imageVersion; + } + + public override string GetImageApplication() + { + return ImageInfo.imageApplication; + } + + public override MediaType GetMediaType() + { + return ImageInfo.mediaType; + } + + public override List GetPartitions() + { + return partitions; + } + + public override List GetTracks() + { + List tracks = new List(); + + foreach(AlcoholTrack track in alcTracks.Values) + { + ushort sessionNo = 0; + + foreach(Session session in sessions) + { + if(track.point >= session.StartTrack || + track.point <= session.EndTrack) + { + sessionNo = session.SessionSequence; + break; + } + } + + AlcoholTrackExtra extra; + if(alcTrackExtras.TryGetValue(track.point, out extra)) + { + Track _track = new Track(); + + _track.Indexes = new Dictionary(); + _track.Indexes.Add(1, track.startLba); + _track.TrackStartSector = track.startLba; + _track.TrackEndSector = extra.sectors - 1; + _track.TrackPregap = extra.pregap; + _track.TrackSession = sessionNo; + _track.TrackSequence = track.point; + _track.TrackType = AlcoholTrackTypeToTrackType(track.mode); + _track.TrackFile = alcImage; + _track.TrackFileOffset = track.startOffset; + _track.TrackFileType = "BINARY"; + _track.TrackRawBytesPerSector = track.sectorSize; + _track.TrackBytesPerSector = AlcoholTrackModeToCookedBytesPerSector(track.mode); + switch(track.subMode) + { + case AlcoholSubchannelMode.Interleaved: + _track.TrackSubchannelFile = alcImage; + _track.TrackSubchannelOffset = track.startOffset; + _track.TrackSubchannelType = TrackSubchannelType.RawInterleaved; + _track.TrackRawBytesPerSector += 96; + break; + case AlcoholSubchannelMode.None: + _track.TrackSubchannelType = TrackSubchannelType.None; + break; + } + + tracks.Add(_track); + } + } + + return tracks; + } + + public override List GetSessionTracks(Session session) + { + if(sessions.Contains(session)) + { + return GetSessionTracks(session.SessionSequence); + } + throw new ImageNotSupportedException("Session does not exist in disc image"); + } + + public override List GetSessionTracks(ushort session) + { + List tracks = new List(); + + foreach(AlcoholTrack track in alcTracks.Values) + { + ushort sessionNo = 0; + + foreach(Session ses in sessions) + { + if(track.point >= ses.StartTrack || + track.point <= ses.EndTrack) + { + sessionNo = ses.SessionSequence; + break; + } + } + + AlcoholTrackExtra extra; + if(alcTrackExtras.TryGetValue(track.point, out extra) && session == sessionNo) + { + Track _track = new Track(); + + _track.Indexes = new Dictionary(); + _track.Indexes.Add(1, track.startLba); + _track.TrackStartSector = track.startLba; + _track.TrackEndSector = extra.sectors - 1; + _track.TrackPregap = extra.pregap; + _track.TrackSession = sessionNo; + _track.TrackSequence = track.point; + _track.TrackType = AlcoholTrackTypeToTrackType(track.mode); + _track.TrackFile = alcImage; + _track.TrackFileOffset = track.startOffset; + _track.TrackFileType = "BINARY"; + _track.TrackRawBytesPerSector = track.sectorSize; + _track.TrackBytesPerSector = AlcoholTrackModeToCookedBytesPerSector(track.mode); + switch(track.subMode) + { + case AlcoholSubchannelMode.Interleaved: + _track.TrackSubchannelFile = alcImage; + _track.TrackSubchannelOffset = track.startOffset; + _track.TrackSubchannelType = TrackSubchannelType.RawInterleaved; + _track.TrackRawBytesPerSector += 96; + break; + case AlcoholSubchannelMode.None: + _track.TrackSubchannelType = TrackSubchannelType.None; + break; + } + + tracks.Add(_track); + } + } + + return tracks; + } + + public override List GetSessions() + { + return sessions; + } + + public override bool? VerifySector(ulong sectorAddress) + { + byte[] buffer = ReadSectorLong(sectorAddress); + return Checksums.CDChecksums.CheckCDSector(buffer); + } + + public override bool? VerifySector(ulong sectorAddress, uint track) + { + byte[] buffer = ReadSectorLong(sectorAddress, track); + return Checksums.CDChecksums.CheckCDSector(buffer); + } + + public override bool? VerifySectors(ulong sectorAddress, uint length, out List FailingLBAs, out List UnknownLBAs) + { + byte[] buffer = ReadSectorsLong(sectorAddress, length); + int bps = (int)(buffer.Length / length); + byte[] sector = new byte[bps]; + FailingLBAs = new List(); + UnknownLBAs = new List(); + + 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(ulong sectorAddress, uint length, uint track, out List FailingLBAs, out List UnknownLBAs) + { + byte[] buffer = ReadSectorsLong(sectorAddress, length, track); + int bps = (int)(buffer.Length / length); + byte[] sector = new byte[bps]; + FailingLBAs = new List(); + UnknownLBAs = new List(); + + 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? VerifyMediaImage() + { + return null; + } + #endregion Public Methods + + #region Private methods + static ushort AlcoholTrackModeToBytesPerSector(AlcoholTrackMode trackMode) + { + switch(trackMode) + { + case AlcoholTrackMode.Audio: + case AlcoholTrackMode.Mode1: + case AlcoholTrackMode.Mode2: + case AlcoholTrackMode.Mode2F2: + case AlcoholTrackMode.Mode2F1: + return 2352; + case AlcoholTrackMode.DVD: + return 2048; + default: + return 0; + } + } + + static ushort AlcoholTrackModeToCookedBytesPerSector(AlcoholTrackMode trackMode) + { + switch(trackMode) + { + case AlcoholTrackMode.Mode1: + case AlcoholTrackMode.Mode2F1: + return 2048; + case AlcoholTrackMode.Mode2F2: + return 2324; + case AlcoholTrackMode.Mode2: + return 2336; + case AlcoholTrackMode.Audio: + return 2352; + case AlcoholTrackMode.DVD: + return 2048; + default: + return 0; + } + } + + static TrackType AlcoholTrackTypeToTrackType(AlcoholTrackMode trackType) + { + switch(trackType) + { + case AlcoholTrackMode.Mode1: + return TrackType.CDMode1; + case AlcoholTrackMode.Mode2F1: + return TrackType.CDMode2Form1; + case AlcoholTrackMode.Mode2F2: + return TrackType.CDMode2Form2; + case AlcoholTrackMode.Mode2: + return TrackType.CDMode2Formless; + case AlcoholTrackMode.Audio: + return TrackType.Audio; + default: + return TrackType.Data; + } + } + + static MediaType AlcoholMediumTypeToMediaType(AlcoholMediumType discType) + { + switch(discType) + { + case AlcoholMediumType.CD: + return MediaType.CD; + case AlcoholMediumType.CDR: + return MediaType.CDR; + case AlcoholMediumType.CDRW: + return MediaType.CDRW; + case AlcoholMediumType.DVD: + return MediaType.DVDROM; + case AlcoholMediumType.DVDR: + return MediaType.DVDR; + default: + return MediaType.Unknown; + } + } + #endregion Private methods + + #region Unsupported features + 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 GetMediaSerialNumber() + { + return ImageInfo.mediaSerialNumber; + } + + public override string GetMediaBarcode() + { + return ImageInfo.mediaBarcode; + } + + public override int GetMediaSequence() + { + return ImageInfo.mediaSequence; + } + + public override int GetLastDiskSequence() + { + return ImageInfo.lastMediaSequence; + } + + public override string GetDriveManufacturer() + { + return ImageInfo.driveManufacturer; + } + + public override string GetDriveModel() + { + return ImageInfo.driveModel; + } + + public override string GetDriveSerialNumber() + { + return ImageInfo.driveSerialNumber; + } + + public override string GetMediaPartNumber() + { + return ImageInfo.mediaPartNumber; + } + + public override string GetMediaManufacturer() + { + return ImageInfo.mediaManufacturer; + } + + public override string GetMediaModel() + { + return ImageInfo.mediaModel; + } + + public override string GetImageName() + { + return ImageInfo.imageName; + } + + public override string GetImageCreator() + { + return ImageInfo.imageCreator; + } + #endregion Unsupported features } } diff --git a/DiscImageChef.DiscImages/ChangeLog b/DiscImageChef.DiscImages/ChangeLog index c6d7bee8..632112e4 100644 --- a/DiscImageChef.DiscImages/ChangeLog +++ b/DiscImageChef.DiscImages/ChangeLog @@ -1,3 +1,10 @@ +2016-08-09 Natalia Portillo + + * Alcohol120.cs: + * DiscImageChef.DiscImages.csproj: Added support for Alcohol + 120% Media Descriptor Structure disk images (.mds/.mdf). + Resolves #55. + 2016-08-09 Natalia Portillo * CDRWin.cs: Corrected return of CD+G data, should return all diff --git a/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj b/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj index 27318e02..71c9ef3b 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj +++ b/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj @@ -47,6 +47,7 @@ + @@ -66,6 +67,10 @@ {CCAA7AFE-C094-4D82-A66D-630DE8A3F545} DiscImageChef.Console + + {0BEB3088-B634-4289-AE17-CDF2D25D00D5} + DiscImageChef.Decoders + diff --git a/README.md b/README.md index fdb7e348..9d139174 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Supported disk image formats * CDRDAO TOC sheets * Dreamcast GDI * CopyQM +* Alcohol 120% Media Descriptor Structure (.MDS/.MDF) Supported partitioning schemes ============================== diff --git a/TODO b/TODO index 9f67a446..f1e94cc8 100644 --- a/TODO +++ b/TODO @@ -2,7 +2,6 @@ --- Add support for BlindWrite images --- Add support for CloneCD images --- Add support for DiscJuggler images ---- Add support for Alcohol images --- Add support for dump(8) images --- Add support for IMD images --- Add support for Kryoflux images