示例#1
0
        public bool Identify(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

            stream.Seek(0, SeekOrigin.Begin);
            if (stream.Length < 88)
            {
                return(false);
            }

            byte[] hdr = new byte[88];
            stream.Read(hdr, 0, 88);
            AlcoholHeader header = Marshal.ByteArrayToStructureLittleEndian <AlcoholHeader>(hdr);

            return(header.signature.SequenceEqual(alcoholSignature));
        }
示例#2
0
        public bool Identify(IFilter imageFilter)
        {
            Stream stream = imageFilter.GetDataForkStream();

            stream.Seek(0, SeekOrigin.Begin);
            if (stream.Length < 88)
            {
                return(false);
            }

            byte[] hdr = new byte[88];
            stream.Read(hdr, 0, 88);
            IntPtr hdrPtr = Marshal.AllocHGlobal(88);

            Marshal.Copy(hdr, 0, hdrPtr, 88);
            AlcoholHeader header = (AlcoholHeader)Marshal.PtrToStructure(hdrPtr, typeof(AlcoholHeader));

            Marshal.FreeHGlobal(hdrPtr);

            return(header.signature.SequenceEqual(alcoholSignature));
        }
示例#3
0
        public bool Close()
        {
            if (!IsWriting)
            {
                ErrorMessage = "Image is not opened for writing";
                return(false);
            }

            byte sessions = byte.MinValue;

            foreach (Track t in writingTracks)
            {
                if (t.TrackSession > byte.MinValue)
                {
                    sessions = (byte)t.TrackSession;
                }
            }

            AlcoholHeader header = new AlcoholHeader
            {
                signature        = alcoholSignature,
                version          = new byte[] { 1, 5 },
                type             = MediaTypeToAlcohol(imageInfo.MediaType),
                sessions         = sessions,
                structuresOffset = (uint)(pfi == null ? 0 : 96),
                sessionOffset    = (uint)(pfi == null ? 96 : 4196),
                unknown1         = new ushort[2],
                unknown2         = new uint[2],
                unknown3         = new uint[6],
                unknown4         = new uint[3]
            };

            // Alcohol sets this always, Daemon Tool expects this
            header.unknown1[0] = 2;

            alcSessions    = new Dictionary <int, AlcoholSession>();
            alcTracks      = new Dictionary <int, AlcoholTrack>();
            alcToc         = new Dictionary <int, Dictionary <int, AlcoholTrack> >();
            writingTracks  = writingTracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence).ToList();
            alcTrackExtras = new Dictionary <int, AlcoholTrackExtra>();
            long currentTrackOffset = header.sessionOffset + Marshal.SizeOf <AlcoholSession>() * sessions;

            FullTOC.CDFullTOC?decodedToc = FullTOC.Decode(fullToc);

            long currentExtraOffset = currentTrackOffset;

            for (int i = 1; i <= sessions; i++)
            {
                if (decodedToc.HasValue)
                {
                    currentExtraOffset += Marshal.SizeOf <AlcoholTrack>() *
                                          decodedToc.Value.TrackDescriptors.Count(t => t.SessionNumber == i);
                }
                else
                {
                    currentExtraOffset += Marshal.SizeOf <AlcoholTrack>() * 3;
                    currentExtraOffset += Marshal.SizeOf <AlcoholTrack>() *
                                          writingTracks.Count(t => t.TrackSession == i);
                    if (i < sessions)
                    {
                        currentExtraOffset += Marshal.SizeOf <AlcoholTrack>() * 2;
                    }
                }
            }

            long footerOffset = currentExtraOffset + Marshal.SizeOf <AlcoholTrackExtra>() * writingTracks.Count;

            if (bca != null)
            {
                header.bcaOffset = (uint)footerOffset;
                footerOffset    += bca.Length;
            }

            if (isDvd)
            {
                alcSessions.Add(1,
                                new AlcoholSession
                {
                    sessionEnd =
                        (int)(writingTracks[0].TrackEndSector - writingTracks[0].TrackStartSector +
                              1),
                    sessionSequence = 1,
                    allBlocks       = 1,
                    nonTrackBlocks  = 3,
                    firstTrack      = 1,
                    lastTrack       = 1,
                    trackOffset     = 4220
                });

                footerOffset = 4300;
                if (bca != null)
                {
                    footerOffset += bca.Length;
                }

                alcTracks.Add(1,
                              new AlcoholTrack
                {
                    mode        = AlcoholTrackMode.DVD,
                    adrCtl      = 20,
                    point       = 1,
                    extraOffset =
                        (uint)(writingTracks[0].TrackEndSector - writingTracks[0].TrackStartSector +
                               1),
                    sectorSize   = 2048,
                    files        = 1,
                    footerOffset = (uint)footerOffset,
                    unknown      = new byte[18],
                    unknown2     = new byte[24]
                });

                alcToc.Add(1, alcTracks);
            }
            else
            {
                for (int i = 1; i <= sessions; i++)
                {
                    Track firstTrack = writingTracks.First(t => t.TrackSession == i);
                    Track lastTrack  = writingTracks.Last(t => t.TrackSession == i);

                    alcSessions.Add(i,
                                    new AlcoholSession
                    {
                        sessionStart    = (int)firstTrack.TrackStartSector - 150,
                        sessionEnd      = (int)lastTrack.TrackEndSector + 1,
                        sessionSequence = (ushort)i,
                        allBlocks       =
                            (byte)(decodedToc?.TrackDescriptors.Count(t => t.SessionNumber == i) ??
                                   writingTracks.Count(t => t.TrackSession == i) + 3),
                        nonTrackBlocks =
                            (byte)(decodedToc?.TrackDescriptors.Count(t => t.SessionNumber == i &&
                                                                      t.POINT >= 0xA0 &&
                                                                      t.POINT <= 0xAF) ??
                                   3),
                        firstTrack  = (ushort)firstTrack.TrackSequence,
                        lastTrack   = (ushort)lastTrack.TrackSequence,
                        trackOffset = (uint)currentTrackOffset
                    });

                    Dictionary <int, AlcoholTrack> thisSessionTracks = new Dictionary <int, AlcoholTrack>();
                    trackFlags.TryGetValue((byte)firstTrack.TrackSequence, out byte firstTrackControl);
                    trackFlags.TryGetValue((byte)lastTrack.TrackSequence, out byte lastTrackControl);
                    if (firstTrackControl == 0 && firstTrack.TrackType != TrackType.Audio)
                    {
                        firstTrackControl = (byte)CdFlags.DataTrack;
                    }
                    if (lastTrackControl == 0 && lastTrack.TrackType != TrackType.Audio)
                    {
                        lastTrackControl = (byte)CdFlags.DataTrack;
                    }
                    (byte minute, byte second, byte frame)leadinPmsf = LbaToMsf(lastTrack.TrackEndSector + 1);

                    if (decodedToc.HasValue &&
                        decodedToc.Value.TrackDescriptors.Any(t => t.SessionNumber == i && t.POINT >= 0xA0 &&
                                                              t.POINT <= 0xAF))
                    {
                        foreach (FullTOC.TrackDataDescriptor tocTrk in
                                 decodedToc.Value.TrackDescriptors.Where(t => t.SessionNumber == i && t.POINT >= 0xA0 &&
                                                                         t.POINT <= 0xAF))
                        {
                            thisSessionTracks.Add(tocTrk.POINT,
                                                  new AlcoholTrack
                            {
                                adrCtl   = (byte)((tocTrk.ADR << 4) + tocTrk.CONTROL),
                                tno      = tocTrk.TNO,
                                point    = tocTrk.POINT,
                                min      = tocTrk.Min,
                                sec      = tocTrk.Sec,
                                frame    = tocTrk.Frame,
                                zero     = tocTrk.Zero,
                                pmin     = tocTrk.PMIN,
                                psec     = tocTrk.PSEC,
                                pframe   = tocTrk.PFRAME,
                                mode     = AlcoholTrackMode.NoData,
                                unknown  = new byte[18],
                                unknown2 = new byte[24]
                            });
                            currentTrackOffset += Marshal.SizeOf <AlcoholTrack>();
                        }
                    }
                    else
                    {
                        thisSessionTracks.Add(0xA0, new AlcoholTrack
                        {
                            adrCtl   = (byte)((1 << 4) + firstTrackControl),
                            pmin     = (byte)firstTrack.TrackSequence,
                            mode     = AlcoholTrackMode.NoData,
                            point    = 0xA0,
                            unknown  = new byte[18],
                            unknown2 = new byte[24],
                            psec     = (byte)(imageInfo.MediaType == MediaType.CDI
                                              ? 0x10
                                              : writingTracks.Any(t => t.TrackType == TrackType.CdMode2Form1 ||
                                                                  t.TrackType == TrackType.CdMode2Form2 ||
                                                                  t.TrackType == TrackType.CdMode2Formless)
                                                  ? 0x20
                                                  : 0)
                        });

                        thisSessionTracks.Add(0xA1,
                                              new AlcoholTrack
                        {
                            adrCtl   = (byte)((1 << 4) + lastTrackControl),
                            pmin     = (byte)lastTrack.TrackSequence,
                            mode     = AlcoholTrackMode.NoData,
                            point    = 0xA1,
                            unknown  = new byte[18],
                            unknown2 = new byte[24]
                        });

                        thisSessionTracks.Add(0xA2,
                                              new AlcoholTrack
                        {
                            adrCtl   = (byte)((1 << 4) + firstTrackControl),
                            zero     = 0,
                            pmin     = leadinPmsf.minute,
                            psec     = leadinPmsf.second,
                            pframe   = leadinPmsf.frame,
                            mode     = AlcoholTrackMode.NoData,
                            point    = 0xA2,
                            unknown  = new byte[18],
                            unknown2 = new byte[24]
                        });
                        currentTrackOffset += Marshal.SizeOf <AlcoholTrack>() * 3;
                    }

                    foreach (Track track in writingTracks.Where(t => t.TrackSession == i).OrderBy(t => t.TrackSequence))
                    {
                        AlcoholTrack alcTrk = new AlcoholTrack();
                        if (decodedToc.HasValue &&
                            decodedToc.Value.TrackDescriptors.Any(t => t.SessionNumber == i &&
                                                                  t.POINT == track.TrackSequence))
                        {
                            FullTOC.TrackDataDescriptor tocTrk =
                                decodedToc.Value.TrackDescriptors.First(t => t.SessionNumber == i &&
                                                                        t.POINT == track.TrackSequence);

                            alcTrk.adrCtl = (byte)((tocTrk.ADR << 4) + tocTrk.CONTROL);
                            alcTrk.tno    = tocTrk.TNO;
                            alcTrk.point  = tocTrk.POINT;
                            alcTrk.min    = tocTrk.Min;
                            alcTrk.sec    = tocTrk.Sec;
                            alcTrk.frame  = tocTrk.Frame;
                            alcTrk.zero   = tocTrk.Zero;
                            alcTrk.pmin   = tocTrk.PMIN;
                            alcTrk.psec   = tocTrk.PSEC;
                            alcTrk.pframe = tocTrk.PFRAME;
                        }
                        else
                        {
                            (byte minute, byte second, byte frame)msf = LbaToMsf(track.TrackStartSector);
                            trackFlags.TryGetValue((byte)track.TrackSequence, out byte trackControl);
                            if (trackControl == 0 && track.TrackType != TrackType.Audio)
                            {
                                trackControl = (byte)CdFlags.DataTrack;
                            }

                            alcTrk.adrCtl = (byte)((1 << 4) + trackControl);
                            alcTrk.point  = (byte)track.TrackSequence;
                            alcTrk.zero   = 0;
                            alcTrk.pmin   = msf.minute;
                            alcTrk.psec   = msf.second;
                            alcTrk.pframe = msf.frame;
                        }

                        alcTrk.mode    = TrackTypeToAlcohol(track.TrackType);
                        alcTrk.subMode = track.TrackSubchannelType != TrackSubchannelType.None
                                             ? AlcoholSubchannelMode.Interleaved
                                             : AlcoholSubchannelMode.None;
                        alcTrk.sectorSize = (ushort)(track.TrackRawBytesPerSector +
                                                     (track.TrackSubchannelType != TrackSubchannelType.None ? 96 : 0));
                        alcTrk.startLba     = (uint)track.TrackStartSector;
                        alcTrk.startOffset  = track.TrackFileOffset;
                        alcTrk.files        = 1;
                        alcTrk.extraOffset  = (uint)currentExtraOffset;
                        alcTrk.footerOffset = (uint)footerOffset;
                        // Alcohol seems to set that for all CD tracks
                        // Daemon Tools expect it to be like this
                        alcTrk.unknown = new byte[]
                        {
                            0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                            0x00, 0x00, 0x00, 0x00
                        };
                        alcTrk.unknown2 = new byte[24];

                        thisSessionTracks.Add((int)track.TrackSequence, alcTrk);

                        currentTrackOffset += Marshal.SizeOf <AlcoholTrack>();
                        currentExtraOffset += Marshal.SizeOf <AlcoholTrackExtra>();

                        AlcoholTrackExtra trkExtra = new AlcoholTrackExtra
                        {
                            sectors = (uint)(track.TrackEndSector - track.TrackStartSector + 1)
                        };

                        // When track mode changes there's a mandatory gap, Alcohol needs it
                        if (track.TrackSequence == firstTrack.TrackSequence)
                        {
                            trkExtra.pregap = 150;
                        }
                        else if (thisSessionTracks.TryGetValue((int)(track.TrackSequence - 1),
                                                               out AlcoholTrack previousTrack) &&
                                 alcTrackExtras.TryGetValue((int)(track.TrackSequence - 1),
                                                            out AlcoholTrackExtra previousExtra) &&
                                 previousTrack.mode != alcTrk.mode)
                        {
                            previousExtra.sectors -= 150;
                            trkExtra.pregap        = 150;
                            alcTrackExtras.Remove((int)(track.TrackSequence - 1));
                            alcTrackExtras.Add((int)(track.TrackSequence - 1), previousExtra);
                        }
                        else
                        {
                            trkExtra.pregap = 0;
                        }

                        alcTrackExtras.Add((int)track.TrackSequence, trkExtra);
                    }

                    if (decodedToc.HasValue &&
                        decodedToc.Value.TrackDescriptors.Any(t => t.SessionNumber == i && t.POINT >= 0xB0))
                    {
                        foreach (FullTOC.TrackDataDescriptor tocTrk in
                                 decodedToc.Value.TrackDescriptors.Where(t => t.SessionNumber == i && t.POINT >= 0xB0))
                        {
                            thisSessionTracks.Add(tocTrk.POINT,
                                                  new AlcoholTrack
                            {
                                adrCtl   = (byte)((tocTrk.ADR << 4) + tocTrk.CONTROL),
                                tno      = tocTrk.TNO,
                                point    = tocTrk.POINT,
                                min      = tocTrk.Min,
                                sec      = tocTrk.Sec,
                                frame    = tocTrk.Frame,
                                zero     = tocTrk.Zero,
                                pmin     = tocTrk.PMIN,
                                psec     = tocTrk.PSEC,
                                pframe   = tocTrk.PFRAME,
                                mode     = AlcoholTrackMode.NoData,
                                unknown  = new byte[18],
                                unknown2 = new byte[24]
                            });
                            currentTrackOffset += Marshal.SizeOf <AlcoholTrack>();
                        }
                    }
                    else if (i < sessions)
                    {
                        (byte minute, byte second, byte frame)leadoutAmsf =
                            LbaToMsf(writingTracks.First(t => t.TrackSession == i + 1).TrackStartSector - 150);
                        (byte minute, byte second, byte frame)leadoutPmsf =
                            LbaToMsf(writingTracks.OrderBy(t => t.TrackSession).ThenBy(t => t.TrackSequence).Last()
                                     .TrackStartSector);

                        thisSessionTracks.Add(0xB0,
                                              new AlcoholTrack
                        {
                            point    = 0xB0,
                            adrCtl   = 0x50,
                            zero     = 0,
                            min      = leadoutAmsf.minute,
                            sec      = leadoutAmsf.second,
                            frame    = leadoutAmsf.frame,
                            pmin     = leadoutPmsf.minute,
                            psec     = leadoutPmsf.second,
                            pframe   = leadoutPmsf.frame,
                            unknown  = new byte[18],
                            unknown2 = new byte[24]
                        });

                        thisSessionTracks.Add(0xC0, new AlcoholTrack
                        {
                            point    = 0xC0,
                            adrCtl   = 0x50,
                            min      = 128,
                            pmin     = 97,
                            psec     = 25,
                            unknown  = new byte[18],
                            unknown2 = new byte[24]
                        });

                        currentTrackOffset += Marshal.SizeOf <AlcoholTrack>() * 2;
                    }

                    alcToc.Add(i, thisSessionTracks);
                }
            }

            alcFooter = new AlcoholFooter
            {
                filenameOffset = (uint)(footerOffset + Marshal.SizeOf <AlcoholFooter>()), widechar = 1
            };

            byte[] filename = Encoding.Unicode.GetBytes("*.mdf"); // Yup, Alcohol stores no filename but a wildcard.

            IntPtr blockPtr;

            // Write header
            descriptorStream.Seek(0, SeekOrigin.Begin);
            byte[] block = new byte[Marshal.SizeOf <AlcoholHeader>()];
            blockPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf <AlcoholHeader>());
            System.Runtime.InteropServices.Marshal.StructureToPtr(header, blockPtr, true);
            System.Runtime.InteropServices.Marshal.Copy(blockPtr, block, 0, block.Length);
            System.Runtime.InteropServices.Marshal.FreeHGlobal(blockPtr);
            descriptorStream.Write(block, 0, block.Length);

            // Write DVD structures if pressent
            if (header.structuresOffset != 0)
            {
                if (dmi != null)
                {
                    descriptorStream.Seek(header.structuresOffset, SeekOrigin.Begin);
                    if (dmi.Length == 2052)
                    {
                        descriptorStream.Write(dmi, 0, 2052);
                    }
                    else if (dmi.Length == 2048)
                    {
                        descriptorStream.Write(new byte[] { 0x08, 0x02, 0x00, 0x00 }, 0, 4);
                        descriptorStream.Write(dmi, 0, 2048);
                    }
                }

                // TODO: Create fake PFI if none present
                if (pfi != null)
                {
                    descriptorStream.Seek(header.structuresOffset + 2052, SeekOrigin.Begin);
                    descriptorStream.Write(pfi, pfi.Length - 2048, 2048);
                }
            }

            // Write sessions
            descriptorStream.Seek(header.sessionOffset, SeekOrigin.Begin);
            foreach (AlcoholSession session in alcSessions.Values)
            {
                block    = new byte[Marshal.SizeOf <AlcoholSession>()];
                blockPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf <AlcoholSession>());
                System.Runtime.InteropServices.Marshal.StructureToPtr(session, blockPtr, true);
                System.Runtime.InteropServices.Marshal.Copy(blockPtr, block, 0, block.Length);
                System.Runtime.InteropServices.Marshal.FreeHGlobal(blockPtr);
                descriptorStream.Write(block, 0, block.Length);
            }

            // Write tracks
            foreach (KeyValuePair <int, Dictionary <int, AlcoholTrack> > kvp in alcToc)
            {
                descriptorStream.Seek(alcSessions.First(t => t.Key == kvp.Key).Value.trackOffset, SeekOrigin.Begin);
                foreach (AlcoholTrack track in kvp.Value.Values)
                {
                    block    = new byte[Marshal.SizeOf <AlcoholTrack>()];
                    blockPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf <AlcoholTrack>());
                    System.Runtime.InteropServices.Marshal.StructureToPtr(track, blockPtr, true);
                    System.Runtime.InteropServices.Marshal.Copy(blockPtr, block, 0, block.Length);
                    System.Runtime.InteropServices.Marshal.FreeHGlobal(blockPtr);
                    descriptorStream.Write(block, 0, block.Length);

                    if (isDvd)
                    {
                        continue;
                    }

                    // Write extra
                    long position = descriptorStream.Position;
                    descriptorStream.Seek(track.extraOffset, SeekOrigin.Begin);
                    if (alcTrackExtras.TryGetValue(track.point, out AlcoholTrackExtra extra))
                    {
                        block    = new byte[Marshal.SizeOf <AlcoholTrackExtra>()];
                        blockPtr =
                            System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf <AlcoholTrackExtra>());
                        System.Runtime.InteropServices.Marshal.StructureToPtr(extra, blockPtr, true);
                        System.Runtime.InteropServices.Marshal.Copy(blockPtr, block, 0, block.Length);
                        System.Runtime.InteropServices.Marshal.FreeHGlobal(blockPtr);
                        descriptorStream.Write(block, 0, block.Length);
                    }

                    descriptorStream.Seek(position, SeekOrigin.Begin);
                }
            }

            // Write BCA
            if (bca != null)
            {
                descriptorStream.Seek(header.bcaOffset, SeekOrigin.Begin);
                descriptorStream.Write(bca, 0, bca.Length);
            }

            // Write footer
            descriptorStream.Seek(footerOffset, SeekOrigin.Begin);
            block    = new byte[Marshal.SizeOf <AlcoholFooter>()];
            blockPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf <AlcoholFooter>());
            System.Runtime.InteropServices.Marshal.StructureToPtr(alcFooter, blockPtr, true);
            System.Runtime.InteropServices.Marshal.Copy(blockPtr, block, 0, block.Length);
            System.Runtime.InteropServices.Marshal.FreeHGlobal(blockPtr);
            descriptorStream.Write(block, 0, block.Length);

            // Write filename
            descriptorStream.Write(filename, 0, filename.Length);
            // Write filename null termination
            descriptorStream.Write(new byte[] { 0, 0 }, 0, 2);

            descriptorStream.Flush();
            descriptorStream.Close();
            imageStream.Flush();
            imageStream.Close();

            IsWriting    = false;
            ErrorMessage = "";
            return(true);
        }