示例#1
0
        public AFile Parse(Stream stream)
        {
            EndianBitConverter bc    = EndianBitConverter.CreateForLittleEndian();
            EndianBitConverter bcBig = EndianBitConverter.CreateForBigEndian();
            bool isDvd = false;

            AFile aFile = new AFile();

            aFile.MDSPath = (stream as FileStream).Name;

            stream.Seek(0, SeekOrigin.Begin);

            // check whether the header in the mds file is long enough
            if (stream.Length < 88)
            {
                throw new MDSParseException("Malformed MDS format: The descriptor file does not appear to be long enough.");
            }

            // parse header
            aFile.Header = aFile.Header.Parse(stream);

            // check version to make sure this is only v1.x
            // currently NO support for version 2.x

            if (aFile.Header.Version[0] > 1)
            {
                throw new MDSParseException($"MDS Parse Error: Only MDS version 1.x is supported!\nDetected version: {aFile.Header.Version[0]}.{aFile.Header.Version[1]}");
            }

            // parse sessions
            Dictionary <int, ASession> aSessions = new Dictionary <int, ASession>();

            stream.Seek(aFile.Header.SessionOffset, SeekOrigin.Begin);
            for (int se = 0; se < aFile.Header.SessionCount; se++)
            {
                byte[] sessionHeader = new byte[24];
                stream.Read(sessionHeader, 0, 24);
                //sessionHeader.Reverse().ToArray();

                ASession session = new ASession();

                session.SessionStart   = bc.ToInt32(sessionHeader.Take(4).ToArray());
                session.SessionEnd     = bc.ToInt32(sessionHeader.Skip(4).Take(4).ToArray());
                session.SessionNumber  = bc.ToInt16(sessionHeader.Skip(8).Take(2).ToArray());
                session.AllBlocks      = sessionHeader[10];
                session.NonTrackBlocks = sessionHeader[11];
                session.FirstTrack     = bc.ToInt16(sessionHeader.Skip(12).Take(2).ToArray());
                session.LastTrack      = bc.ToInt16(sessionHeader.Skip(14).Take(2).ToArray());
                session.TrackOffset    = bc.ToInt32(sessionHeader.Skip(20).Take(4).ToArray());


                //mdsf.Sessions.Add(session);
                aSessions.Add(session.SessionNumber, session);
            }

            long footerOffset = 0;

            // parse track blocks
            Dictionary <int, ATrack> aTracks = new Dictionary <int, ATrack>();

            // iterate through each session block
            foreach (ASession session in aSessions.Values)
            {
                stream.Seek(session.TrackOffset, SeekOrigin.Begin);
                //Dictionary<int, ATrack> sessionToc = new Dictionary<int, ATrack>();

                // iterate through every block specified in each session
                for (int bl = 0; bl < session.AllBlocks; bl++)
                {
                    byte[] trackHeader;
                    ATrack track = new ATrack();

                    trackHeader = new byte[80];

                    stream.Read(trackHeader, 0, 80);

                    track.Mode         = trackHeader[0];
                    track.SubMode      = trackHeader[1];
                    track.ADR_Control  = trackHeader[2];
                    track.TrackNo      = trackHeader[3];
                    track.Point        = trackHeader[4];
                    track.AMin         = trackHeader[5];
                    track.ASec         = trackHeader[6];
                    track.AFrame       = trackHeader[7];
                    track.Zero         = trackHeader[8];
                    track.PMin         = trackHeader[9];
                    track.PSec         = trackHeader[10];
                    track.PFrame       = trackHeader[11];
                    track.ExtraOffset  = bc.ToInt32(trackHeader.Skip(12).Take(4).ToArray());
                    track.SectorSize   = bc.ToInt16(trackHeader.Skip(16).Take(2).ToArray());
                    track.PLBA         = bc.ToInt32(trackHeader.Skip(36).Take(4).ToArray());
                    track.StartOffset  = BitConverter.ToUInt64(trackHeader.Skip(40).Take(8).ToArray(), 0);
                    track.Files        = bc.ToInt32(trackHeader.Skip(48).Take(4).ToArray());
                    track.FooterOffset = bc.ToInt32(trackHeader.Skip(52).Take(4).ToArray());

                    if (track.Mode == 0x02)
                    {
                        isDvd = true;
                        throw new MDSParseException("DVD Detected. Not currently supported!");
                    }


                    // check for track extra block - this can probably be handled in a separate loop,
                    // but I'll just store the current stream position then seek forward to the extra block for this track
                    Int64 currPos = stream.Position;

                    // Only CDs have extra blocks - for DVDs ExtraOffset = track length
                    if (track.ExtraOffset > 0 && !isDvd)
                    {
                        byte[] extHeader = new byte[8];
                        stream.Seek(track.ExtraOffset, SeekOrigin.Begin);
                        stream.Read(extHeader, 0, 8);
                        track.ExtraBlock.Pregap  = bc.ToInt32(extHeader.Take(4).ToArray());
                        track.ExtraBlock.Sectors = bc.ToInt32(extHeader.Skip(4).Take(4).ToArray());
                        stream.Seek(currPos, SeekOrigin.Begin);
                    }
                    else if (isDvd == true)
                    {
                        track.ExtraBlock.Sectors = track.ExtraOffset;
                    }

                    // read the footer/filename block for this track
                    currPos = stream.Position;
                    long numOfFilenames = track.Files;
                    for (long fi = 1; fi <= numOfFilenames; fi++)
                    {
                        // skip leadin/out info tracks
                        if (track.FooterOffset == 0)
                        {
                            continue;
                        }

                        byte[] foot = new byte[16];
                        stream.Seek(track.FooterOffset, SeekOrigin.Begin);
                        stream.Read(foot, 0, 16);

                        AFooter f = new AFooter();
                        f.FilenameOffset = bc.ToInt32(foot.Take(4).ToArray());
                        f.WideChar       = bc.ToInt32(foot.Skip(4).Take(4).ToArray());
                        track.FooterBlocks.Add(f);
                        track.FooterBlocks = track.FooterBlocks.Distinct().ToList();

                        // parse the filename string
                        string fileName = "*.mdf";
                        if (f.FilenameOffset > 0)
                        {
                            // filename offset is present
                            stream.Seek(f.FilenameOffset, SeekOrigin.Begin);
                            byte[] fname;

                            if (numOfFilenames == 1)
                            {
                                if (aFile.Header.DPMOffset == 0)
                                {
                                    // filename is in the remaining space to EOF
                                    fname = new byte[stream.Length - stream.Position];
                                }
                                else
                                {
                                    // filename is in the remaining space to EOF + dpm offset
                                    fname = new byte[aFile.Header.DPMOffset - stream.Position];
                                }
                            }

                            else
                            {
                                // looks like each filename string is 6 bytes with a trailing \0
                                fname = new byte[6];
                            }


                            // read the filename
                            stream.Read(fname, 0, fname.Length);

                            // if widechar is 1 filename is stored using 16-bit, otherwise 8-bit is used
                            if (f.WideChar == 1)
                            {
                                fileName = Encoding.Unicode.GetString(fname).TrimEnd('\0');
                            }
                            else
                            {
                                fileName = Encoding.Default.GetString(fname).TrimEnd('\0');
                            }
                        }

                        else
                        {
                            // assume an MDF file with the same name as the MDS
                        }

                        string dir = Path.GetDirectoryName(aFile.MDSPath);

                        if (f.FilenameOffset == 0 ||
                            string.Compare(fileName, "*.mdf", StringComparison.InvariantCultureIgnoreCase) == 0)
                        {
                            fileName = $@"{dir}\{Path.GetFileNameWithoutExtension(aFile.MDSPath)}.mdf";
                        }
                        else
                        {
                            fileName = $@"{dir}\{fileName}";
                        }

                        track.ImageFileNamePaths.Add(fileName);
                        track.ImageFileNamePaths = track.ImageFileNamePaths.Distinct().ToList();
                    }

                    stream.Position = currPos;


                    aTracks.Add(track.Point, track);
                    aFile.Tracks.Add(track);

                    if (footerOffset == 0)
                    {
                        footerOffset = track.FooterOffset;
                    }
                }
            }


            // build custom session object
            aFile.ParsedSession = new List <Session>();
            foreach (var s in aSessions.Values)
            {
                Session session = new Session();
                ATrack  startTrack;
                ATrack  endTrack;

                if (!aTracks.TryGetValue(s.FirstTrack, out startTrack))
                {
                    break;
                }

                if (!aTracks.TryGetValue(s.LastTrack, out endTrack))
                {
                    break;
                }

                session.StartSector     = startTrack.PLBA;
                session.StartTrack      = s.FirstTrack;
                session.SessionSequence = s.SessionNumber;
                session.EndSector       = endTrack.PLBA + endTrack.ExtraBlock.Sectors - 1;
                session.EndTrack        = s.LastTrack;

                aFile.ParsedSession.Add(session);
            }

            // now build the TOC object
            foreach (var se in aFile.ParsedSession)
            {
                foreach (var t in aTracks.Values
                         .Where(a => se.StartTrack <= a.TrackNo && a.TrackNo <= se.EndTrack)
                         .OrderBy(a => a.TrackNo))
                {
                    aFile.TOCEntries.Add(new ATOCEntry(t.Point)
                    {
                        ADR_Control        = t.ADR_Control,
                        AFrame             = t.AFrame,
                        AMin               = t.AMin,
                        ASec               = t.ASec,
                        BlobIndex          = t.BlobIndex,
                        EntryNum           = t.TrackNo,
                        ExtraBlock         = t.ExtraBlock,
                        ImageFileNamePaths = t.ImageFileNamePaths,
                        PFrame             = t.PFrame,
                        PLBA               = Convert.ToInt32(t.PLBA),
                        PMin               = t.PMin,
                        Point              = t.Point,
                        PSec               = t.PSec,
                        SectorSize         = t.SectorSize,
                        Session            = se.SessionSequence,
                        TrackOffset        = Convert.ToInt64(t.StartOffset),
                        Zero               = t.Zero
                    });
                }
            }

            return(aFile);
        }
示例#2
0
        /// <summary>
        /// Parse the node record from the given ISO9660 stream.
        /// </summary>
        /// <param name="s">The stream to parse from.</param>
        public void ParseISO9660(Stream s)
        {
            EndianBitConverter bc = EndianBitConverter.CreateForLittleEndian();
            long startPosition    = s.Position;

            byte[] buffer = new byte[ISOFile.SECTOR_SIZE];

            // Get the length
            s.Read(buffer, 0, 1);
            this.Length = buffer[0];

            //the number of sectors in the attribute record
            s.Read(buffer, 0, 1);

            // Read Data Offset
            s.Read(buffer, 0, 8);
            this.OffsetOfData = (long)bc.ToInt32(buffer);

            // Read Data Length
            s.Read(buffer, 0, 8);
            this.LengthOfData = (long)bc.ToInt32(buffer);

            // Read the time and flags
            s.Read(buffer, 0, 8);
            this.Year           = buffer[0];
            this.Month          = buffer[1];
            this.Day            = buffer[2];
            this.Hour           = buffer[3];
            this.Minute         = buffer[4];
            this.Second         = buffer[5];
            this.TimeZoneOffset = buffer[6];

            this.Flags = buffer[7];

            s.Read(buffer, 0, 6);

            // Read the name length
            s.Read(buffer, 0, 1);
            this.NameLength = buffer[0];

            // Read the directory name
            s.Read(buffer, 0, this.NameLength);
            if (this.NameLength == 1 && (buffer[0] == 0 || buffer[0] == 1))
            {
                if (buffer[0] == 0)
                {
                    this.Name = ISONodeRecord.CURRENT_DIRECTORY;
                }
                else
                {
                    this.Name = ISONodeRecord.PARENT_DIRECTORY;
                }
            }
            else
            {
                this.Name = ASCIIEncoding.ASCII.GetString(buffer, 0, this.NameLength);
            }

            // Seek to end
            s.Seek(startPosition + this.Length, SeekOrigin.Begin);
        }
示例#3
0
        /// <summary>
        /// Parse the node record from the given CD-I stream.
        /// </summary>
        /// <param name="s">The stream to parse from.</param>
        public void ParseCDInteractive(Stream s)
        {
            /*
             * BP      Size in bytes   Description
             * 1       1               Record length
             * 2       1               Extended Attribute record length
             * 3       4               Reserved
             * 7       4               File beginning LBN
             * 11      4               Reserved
             * 15      4               File size
             * 19      6               Creation date
             * 25      1               Reserved
             * 26      1               File flags
             * 27      2               Interleave
             * 29      2               Reserved
             * 31      2               Album Set Sequence number
             * 33      1               File name size
             * 34      (n)             File name
             * 34+n    4               Owner ID
             * 38+n    2               Attributes
             * 40+n    2               Reserved
             * 42+n    1               File number
             * 43+n    1               Reserved
             *      43+n            Total
             */

            EndianBitConverter bc    = EndianBitConverter.CreateForLittleEndian();
            EndianBitConverter bcBig = EndianBitConverter.CreateForBigEndian();
            long startPosition       = s.Position;

            byte[] buffer = new byte[ISOFile.SECTOR_SIZE];

            // Read the entire structure
            s.Read(buffer, 0, ISOFile.SECTOR_SIZE);
            s.Position -= ISOFile.SECTOR_SIZE;

            // Get the record length
            this.Length = buffer[0];

            // extended attribute record length
            this.ExtendedAttribRecordLength = buffer[1];

            // Read Data Offset
            this.OffsetOfData = bcBig.ReadIntValue(buffer, 6, 4);

            // Read Data Length
            this.LengthOfData = bcBig.ReadIntValue(buffer, 14, 4);

            // Read the time
            var ti = bc.ReadBytes(buffer, 18, 6);

            this.Year   = ti[0];
            this.Month  = ti[1];
            this.Day    = ti[2];
            this.Hour   = ti[3];
            this.Minute = ti[4];
            this.Second = ti[5];

            // read interleave - still to do

            // read album (volume) set sequence number (we are ignoring this)

            // Read the name length
            this.NameLength = buffer[32];

            // Read the file/directory name
            var name = bc.ReadBytes(buffer, 33, this.NameLength);

            if (this.NameLength == 1 && (name[0] == 0 || name[0] == 1))
            {
                if (name[0] == 0)
                {
                    this.Name = ISONodeRecord.CURRENT_DIRECTORY;
                }
                else
                {
                    this.Name = ISONodeRecord.PARENT_DIRECTORY;
                }
            }
            else
            {
                this.Name = ASCIIEncoding.ASCII.GetString(name, 0, this.NameLength);
            }

            // skip ownerID for now

            // read the flags - only really interested in the directory attribute (bit 15)
            // (confusingly these are called 'attributes' in CD-I. the CD-I 'File Flags' entry is something else entirely)
            this.Flags = buffer[37 + this.NameLength];

            // skip filenumber
            //this.FileNumber = buffer[41 + this.NameLength];

            // Seek to end
            s.Seek(startPosition + this.Length, SeekOrigin.Begin);
        }