Exemplo n.º 1
0
        private void CalculateSubtitleTypes(ProgramChain programChain)
        {
            // Additional Code to analyse stream bytes
            if (_vtsVobs.NumberOfSubtitles > 0)
            {
                // load the 'last' subpicture stream info,
                // because if we have more than one subtitle stream,
                // all subtitle positions > 0
                // lastSubtitle[0] is related to 4:3
                // lastSubtitle[1] is related to Wide
                // lastSubtitle[2] is related to letterboxed
                // lastSubtitle[3] is related to pan&scan
                byte[] lastSubtitle = programChain.SubtitlesAvailable[programChain.SubtitlesAvailable.Count - 1];

                int countSubs = 0;

                // set defaults for all possible subpicture types and positions
                programChain.Has43Subs = false;
                programChain.HasWideSubs = false;
                programChain.HasLetterSubs = false;
                programChain.HasPanSubs = false;
                programChain.HasNoSpecificSubs = true;

                int pos43Subs = -1;
                int posWideSubs = -1;
                int posLetterSubs = -1;
                int posPanSubs = -1;

                // parse different subtitle bytes
                if (lastSubtitle[0] > 0x80)
                {
                    programChain.Has43Subs = true;
                    countSubs++; // 4:3
                }
                if (lastSubtitle[1] > 0)
                {
                    programChain.HasWideSubs = true;
                    countSubs++; // wide
                }
                if (lastSubtitle[2] > 0)
                {
                    programChain.HasLetterSubs = true;
                    countSubs++; // letterboxed
                }
                if (lastSubtitle[3] > 0)
                {
                    programChain.HasPanSubs = true;
                    countSubs++; // pan&scan
                }

                if (countSubs == 0)
                {
                    // may be, only a 4:3 stream exists
                    // -> lastSubtitle[0] = 0x80
                }
                else
                {
                    if (_vtsVobs.NumberOfSubtitles == 1)
                    {
                        // only 1 stream exists, may be letterboxed
                        // if so we cound't find wide id, because lastSubtitle[1] = 0 !!
                        // corresponding wide stream byte is 0 => wide id = 0x20
                        // letterboxed = 0x21
                        if (programChain.HasLetterSubs && !programChain.HasWideSubs)
                        {
                            // repair it
                            programChain.HasWideSubs = true;
                        }
                    }
                    programChain.HasNoSpecificSubs = false;
                }

                // subpucture streams start with 0x20
                int subStream = 0x20;

                // Now we know all about available subpicture streams, including position type
                // And we can create whole complete definitions for all avalable streams
                foreach (byte[] subtitle in programChain.SubtitlesAvailable)
                {
                    if (programChain.HasNoSpecificSubs)
                    {
                        // only one unspezified subpicture stream exists
                        _vtsVobs.SubtitleIDs.Add(string.Format("0x{0:x2}", subStream++));
                        _vtsVobs.SubtitleTypes.Add("unspecific");
                    }
                    else
                    {
                        // read stream position for evey subtitle type from subtitle byte
                        if (programChain.Has43Subs)
                        {
                            pos43Subs = subtitle[0] - 0x80;
                        }
                        if (programChain.HasWideSubs)
                        {
                            posWideSubs = subtitle[1];
                        }
                        if (programChain.HasLetterSubs)
                        {
                            posLetterSubs = subtitle[2];
                        }
                        if (programChain.HasPanSubs)
                        {
                            posPanSubs = subtitle[3];
                        }

                        // Now we can create subpicture id's and types for every stream
                        // All used subpicture id's and types will beappended to string, separated by colon
                        // So it's possible to split it later
                        string sub = string.Empty;
                        string subType = string.Empty;
                        if (programChain.Has43Subs)
                        {
                            sub = string.Format("0x{0:x2}", subStream + pos43Subs);
                            subType = "4:3";
                        }
                        if (programChain.HasWideSubs)
                        {
                            if (sub.Length > 0)
                            {
                                sub += ", ";
                                subType += ", ";
                            }
                            sub += string.Format("0x{0:x2}", subStream + posWideSubs);
                            subType += "wide";
                        }
                        if (programChain.HasLetterSubs)
                        {
                            if (sub.Length > 0)
                            {
                                sub += ", ";
                                subType += ", ";
                            }
                            sub += string.Format("0x{0:x2}", subStream + posLetterSubs);
                            subType += "letterboxed";
                        }
                        if (programChain.HasPanSubs)
                        {
                            if (sub.Length > 0)
                            {
                                sub += ", ";
                                subType += ", ";
                            }
                            sub += string.Format("0x{0:x2}", subStream + posPanSubs);
                            subType += "pan&scan";
                        }

                        _vtsVobs.SubtitleIDs.Add(sub);
                        _vtsVobs.SubtitleTypes.Add(subType);
                    }
                }
            }
        }
Exemplo n.º 2
0
        private void ParseVtsPgci()
        {
            const int sectorSize = 2048;

            _fs.Position = 0xCC; //Get VTS_PGCI adress
            int tableStart = sectorSize * GetEndian(4);

            _fs.Position = tableStart;
            _vtsPgci.NumberOfProgramChains = GetEndian(2);
            _vtsPgci.ProgramChains = new List<ProgramChain>();

            for (int i = 0; i < _vtsPgci.NumberOfProgramChains; i++)
            {
                //Parse PGC Header
                var programChain = new ProgramChain();
                _fs.Position = tableStart + 4 + 8 * (i + 1);  //Get PGC adress
                int programChainAdress = GetEndian(4);
                _fs.Position = tableStart + programChainAdress + 2;  //Move to PGC
                programChain.NumberOfPgc = _fs.ReadByte();
                programChain.NumberOfCells = _fs.ReadByte();
                programChain.PlaybackTime = InterpretTime(GetEndian(4));
                _fs.Seek(4, SeekOrigin.Current);

                // check if audio streams are available for this PGC
                _fs.Position = tableStart + programChainAdress + 0xC;
                for (int j = 0; j < _vtsVobs.NumberOfAudioStreams; j++)
                {
                    string temp = IntToBin(_fs.ReadByte(), 8);
                    programChain.AudioStreamsAvailable.Add(temp[0]);
                    _fs.Seek(1, SeekOrigin.Current);
                }

                // check if subtitles are available for this PGC
                _fs.Position = tableStart + programChainAdress + 0x1C;
                for (int j = 0; j < _vtsVobs.NumberOfSubtitles; j++)
                {
                    // read and save full subpicture stream info inside program chain
                    var subtitle = new byte[4];
                    _fs.Read(subtitle, 0, 4);
                    programChain.SubtitlesAvailable.Add(subtitle);
                }

                CalculateSubtitleTypes(programChain);

                //Parse Color LookUp Table (CLUT) - offset 00A4, 16*4 (0, Y, Cr, Cb)
                _fs.Position = tableStart + programChainAdress + 0xA4;
                for (int colorNumber = 0; colorNumber < 16; colorNumber++)
                {
                    var colors = new byte[4];
                    _fs.Read(colors, 0, 4);
                    int y = colors[1] - 16;
                    int cr = colors[2] - 128;
                    int cb = colors[3] - 128;
                    int r = (int)Math.Min(Math.Max(Math.Round(1.1644F * y + 1.596F * cr), 0), 255);
                    int g = (int)Math.Min(Math.Max(Math.Round(1.1644F * y - 0.813F * cr - 0.391F * cb), 0), 255);
                    int b = (int)Math.Min(Math.Max(Math.Round(1.1644F * y + 2.018F * cb), 0), 255);

                    programChain.ColorLookupTable.Add(Color.FromArgb(r, g, b));
                }

                //Parse Program Map
                _fs.Position = tableStart + programChainAdress + 0xE6;
                _fs.Position = tableStart + programChainAdress + GetEndian(2);
                for (int j = 0; j < programChain.NumberOfPgc; j++)
                {
                    programChain.PgcEntryCells.Add((byte)_fs.ReadByte());
                }

                // Cell Playback Info Table to retrieve duration
                _fs.Position = tableStart + programChainAdress + 0xE8;
                _fs.Position = tableStart + programChainAdress + GetEndian(2);
                var timeArray = new List<int>();
                for (int k = 0; k < programChain.NumberOfPgc; k++)
                {
                    int time = 0;
                    int max;
                    if (k == programChain.NumberOfPgc - 1)
                        max = programChain.NumberOfCells;
                    else
                        max = programChain.PgcEntryCells[k + 1] - 1;
                    for (int j = programChain.PgcEntryCells[k]; j <= max; j++)
                    {
                        _fs.Seek(4, SeekOrigin.Current);
                        time += TimeToMs(GetEndian(4));
                        _fs.Seek(16, SeekOrigin.Current);
                    }
                    programChain.PgcPlaybackTimes.Add(MsToTime(time));
                    timeArray.Add(time);

                    //convert to start time
                    time = 0;
                    for (int l = 1; l <= k; l++)
                    {
                        time += timeArray[l - 1];
                    }
                    if (k == 0)
                        programChain.PgcStartTimes.Add(MsToTime(0));
                    if (k > 0)
                        programChain.PgcStartTimes.Add(MsToTime(time));
                }
                _vtsPgci.ProgramChains.Add(programChain);
            }
        }
Exemplo n.º 3
0
        public IFOParse(string filename)
        {
            m_file = new FileInfo(filename);
            FileStream fs = File.OpenRead(filename);

            try
            {
                #region Header
                // verify valid video IFO header
                byte[] id = new byte[12];
                if (fs.Read(id, 0, 12) != 12)
                {
                    throw new IOException("Error reading IFO file");
                }
                string idStr = Encoding.ASCII.GetString(id, 0, 12);
                if (idStr != "DVDVIDEO-VTS")
                {
                    throw new Exception("Invalid IFO Header");
                }
                #endregion

                #region Version
                // verify IFO version (1.0 or 1.1)
                if (fs.Seek(0x21, SeekOrigin.Begin) != 0x21)
                {
                    throw new IOException("Error reading IFO file");
                }
                byte[] version = new byte[1];
                if (fs.Read(version, 0, 1) != 1)
                {
                    throw new IOException("Error reading IFO file");
                }
                if (version[0] != 0x10 && version[0] != 0x11)
                {
                    throw new Exception("Invalid IFO Header Version");
                }
                #endregion

                #region PGC Info
                // find offset to pgc tables
                if (fs.Seek(0xcc, SeekOrigin.Begin) != 0xcc)
                {
                    throw new IOException("Error reading IFO file");
                }
                byte[] pgcOffsetData = new byte[4];
                if (fs.Read(pgcOffsetData, 0, 4) != 4)
                {
                    throw new IOException("Error reading IFO file");
                }
                int pgcOffset = (pgcOffsetData[0] << 24) + (pgcOffsetData[1] << 16) + (pgcOffsetData[2] << 8) + pgcOffsetData[3];

                // get count of pgcs
                if (fs.Seek(pgcOffset * 2048, SeekOrigin.Begin) != pgcOffset * 2048)
                {
                    throw new IOException("Error reading IFO file");
                }
                byte[] pgciData = new byte[2];
                if (fs.Read(pgciData, 0, 2) != 2)
                {
                    throw new IOException("Error reading IFO file");
                }
                int pgcCount = (pgciData[0] << 8) + pgciData[1];

                for (int i = 1; i <= pgcCount; i++)
                {
                    ProgramChain pgc = new ProgramChain(this);

                    // get pgc's info offset within pgc (length is 4 bytes into pgc info)
                    if (fs.Seek((pgcOffset * 2048) + (i * 8) + 4, SeekOrigin.Begin) != (pgcOffset * 2048) + (i * 8) + 4)
                    {
                        throw new IOException("Error reading IFO file");
                    }
                    byte[] pgcStartData = new byte[4];
                    if (fs.Read(pgcStartData, 0, 4) != 4)
                    {
                        throw new IOException("Error reading IFO file");
                    }
                    int pgcStart = (pgcStartData[0] << 24) + (pgcStartData[1] << 16) + (pgcStartData[2] << 8) + pgcStartData[3];

                    // get pgc's playback time
                    if (fs.Seek((pgcOffset * 2048) + pgcStart + 4, SeekOrigin.Begin) != (pgcOffset * 2048) + pgcStart + 4)
                    {
                        throw new IOException("Error reading IFO file");
                    }
                    byte[] playbackTimeData = new byte[4];
                    if (fs.Read(playbackTimeData, 0, 4) != 4)
                    {
                        throw new IOException("Error reading IFO file");
                    }

                    // frame rate
                    int frameRate = (playbackTimeData[3] & 0xC0) >> 6;
                    if (frameRate == 3)
                    {
                        pgc.FrameRate = 30;
                    }
                    else if (frameRate == 1)
                    {
                        pgc.FrameRate = 25;
                    }

                    // duration
                    int playbackTime = (playbackTimeData[0] << 24) + (playbackTimeData[1] << 16) + (playbackTimeData[2] << 8) + playbackTimeData[3];

                    int hour  = ((playbackTime >> 28) & 0x0f) * 10 + ((playbackTime >> 24) & 0x0f);
                    int min   = ((playbackTime >> 20) & 0x0f) * 10 + ((playbackTime >> 16) & 0x0f);
                    int sec   = ((playbackTime >> 12) & 0x0f) * 10 + ((playbackTime >> 8) & 0x0f);
                    int frame = (((playbackTime >> 4) & 0x0f) * 10 + (playbackTime & 0x0f)) - 120;

                    pgc.Duration      = hour * 3600 + min * 60 + sec;
                    pgc.PartialFrames = frame;

                    // get pgc's cell playback info table
                    if (fs.Seek((pgcOffset * 2048) + pgcStart + 0xE8, SeekOrigin.Begin) != (pgcOffset * 2048) + pgcStart + 0xE8)
                    {
                        throw new IOException("Error reading IFO file");
                    }
                    byte[] C_PBKT_buf = new byte[2];
                    if (fs.Read(C_PBKT_buf, 0, 2) != 2)
                    {
                        throw new IOException("Error reading IFO file");
                    }
                    int C_PBKT = (C_PBKT_buf[0] << 8) + C_PBKT_buf[1];
                    if (C_PBKT != 0)
                    {
                        C_PBKT += (pgcOffset * 2048) + pgcStart;
                    }

                    // get pgc's cell position info table
                    if (fs.Seek((pgcOffset * 2048) + pgcStart + 0xEA, SeekOrigin.Begin) != (pgcOffset * 2048) + pgcStart + 0xEA)
                    {
                        throw new IOException("Error reading IFO file");
                    }
                    byte[] C_POST_buf = new byte[2];
                    if (fs.Read(C_POST_buf, 0, 2) != 2)
                    {
                        throw new IOException("Error reading IFO file");
                    }
                    int C_POST = (C_POST_buf[0] << 8) + C_POST_buf[1];
                    if (C_POST != 0)
                    {
                        C_POST += (pgcOffset * 2048) + pgcStart;
                    }

                    // get pgc's number of cells
                    if (fs.Seek((pgcOffset * 2048) + pgcStart + 3, SeekOrigin.Begin) != (pgcOffset * 2048) + pgcStart + 3)
                    {
                        throw new IOException("Error reading IFO file");
                    }
                    byte[] nCells_buf = new byte[1];
                    if (fs.Read(nCells_buf, 0, 1) != 1)
                    {
                        throw new IOException("Error reading IFO file");
                    }
                    int nCells  = nCells_buf[0];
                    int nAngles = 1;
                    if (C_POST != 0 && C_PBKT != 0)
                    {
                        for (int nCell = 0; nCell < nCells; nCell++)
                        {
                            Cell c = new Cell();
                            // get info for cell

                            /*if (fs.Seek(C_PBKT + 24 * nCell, SeekOrigin.Begin) != C_PBKT + 24 * nCell)
                             *      throw new IOException("Error reading IFO file");
                             * byte[] iCat_buf = new byte[1];
                             * if (fs.Read(iCat_buf, 0, 1) != 1)
                             *      throw new IOException("Error reading IFO file");
                             *
                             * // ignore multi-angle
                             * int iCat = iCat_buf[0] & 0xF10;
                             * //			0101=First; 1001=Middle ;	1101=Last
                             * if (iCat == 0x50)
                             *      nAngles = 1;
                             * else if (iCat == 0x90)
                             *      nAngles++;
                             * else if (iCat == 0xD0)
                             * {
                             *      nAngles++;
                             *      break;
                             * }*/

                            #region read duration
                            if (fs.Seek(C_PBKT + 24 * nCell + 4, SeekOrigin.Begin) != C_PBKT + 24 * nCell + 4)
                            {
                                throw new IOException("Error reading IFO file");
                            }
                            playbackTimeData = new byte[4];
                            if (fs.Read(playbackTimeData, 0, 4) != 4)
                            {
                                throw new IOException("Error reading IFO file");
                            }
                            playbackTime = (playbackTimeData[0] << 24) + (playbackTimeData[1] << 16) + (playbackTimeData[2] << 8) + playbackTimeData[3];

                            hour  = ((playbackTime >> 28) & 0x0f) * 10 + ((playbackTime >> 24) & 0x0f);
                            min   = ((playbackTime >> 20) & 0x0f) * 10 + ((playbackTime >> 16) & 0x0f);
                            sec   = ((playbackTime >> 12) & 0x0f) * 10 + ((playbackTime >> 8) & 0x0f);
                            frame = (((playbackTime >> 4) & 0x0f) * 10 + (playbackTime & 0x0f)) - 120;

                            c.Duration      = hour * 3600 + min * 60 + sec;
                            c.PartialFrames = frame;
                            #endregion

                            #region entry point
                            if (fs.Seek(C_PBKT + 24 * nCell + 8, SeekOrigin.Begin) != C_PBKT + 24 * nCell + 8)
                            {
                                throw new IOException("Error reading IFO file");
                            }
                            byte[] entryPointBuf = new byte[4];
                            if (fs.Read(entryPointBuf, 0, 4) != 4)
                            {
                                throw new IOException("Error reading IFO file");
                            }
                            c.FirstSector = (entryPointBuf[0] << 24) + (entryPointBuf[1] << 16) + (entryPointBuf[2] << 8) + entryPointBuf[3];
                            #endregion

                            #region last sector
                            if (fs.Seek(C_PBKT + 24 * nCell + 20, SeekOrigin.Begin) != C_PBKT + 24 * nCell + 20)
                            {
                                throw new IOException("Error reading IFO file");
                            }
                            byte[] lastSectorBuf = new byte[4];
                            if (fs.Read(lastSectorBuf, 0, 4) != 4)
                            {
                                throw new IOException("Error reading IFO file");
                            }
                            c.LastSector = (lastSectorBuf[0] << 24) + (lastSectorBuf[1] << 16) + (lastSectorBuf[2] << 8) + lastSectorBuf[3];
                            #endregion

                            if (fs.Seek(C_POST + 4 * nCell, SeekOrigin.Begin) != C_POST + 4 * nCell)
                            {
                                throw new IOException("Error reading IFO file");
                            }
                            byte[] vobIDBuf = new byte[2];
                            if (fs.Read(vobIDBuf, 0, 2) != 2)
                            {
                                throw new IOException("Error reading IFO file");
                            }
                            c.VobID = (vobIDBuf[0] << 8) + vobIDBuf[1];
                            if (fs.Seek(C_POST + 4 * nCell + 3, SeekOrigin.Begin) != C_POST + 4 * nCell + 3)
                            {
                                throw new IOException("Error reading IFO file");
                            }
                            byte[] cellIDBuf = new byte[1];
                            if (fs.Read(cellIDBuf, 0, 1) != 1)
                            {
                                throw new IOException("Error reading IFO file");
                            }
                            c.CellID = cellIDBuf[0];

                            pgc.Cells.Add(c);
                        }
                    }
                    pgc.Angles = nAngles;
                    m_pgcs.Add(pgc);
                }
                #endregion

                #region Video
                if (fs.Seek(0x200, SeekOrigin.Begin) != 0x200)
                {
                    throw new IOException("Error reading IFO file");
                }
                byte[] videoAttributes = new byte[2];
                if (fs.Read(videoAttributes, 0, 2) != 2)
                {
                    throw new IOException("Error reading IFO file");
                }
                byte va1 = videoAttributes[0];
                byte va2 = videoAttributes[1];

                // video mode
                if (((va1 & 0x30) >> 4) == 0)
                {
                    m_videoMode = "NTSC";
                }
                else if (((va1 & 0x30) >> 4) == 1)
                {
                    m_videoMode = "PAL";
                }

                // aspect
                if (((va1 & 0xC) >> 2) == 0)
                {
                    m_aspectRatio = "4:3";
                }
                else if (((va1 & 0xC) >> 2) == 3)
                {
                    m_aspectRatio = "16:9";
                }

                // resolution
                int resolution = ((va2 & 0x38) >> 3);
                switch (resolution)
                {
                case 0:
                    m_resolution = "720x480";
                    break;

                case 1:
                    m_resolution = "704x480";
                    break;

                case 2:
                    m_resolution = "352x480";
                    break;

                case 3:
                    m_resolution = "352x240";
                    break;
                }
                #endregion

                #region Audio
                // http://dvd.sourceforge.net/dvdinfo/ifo.html
                // find count of audio streams
                if (fs.Seek(0x203, SeekOrigin.Begin) != 0x203)
                {
                    throw new IOException("Error reading IFO file");
                }
                byte[] numAudio = new byte[1];
                if (fs.Read(numAudio, 0, 1) != 1)
                {
                    throw new IOException("Error reading IFO file");
                }
                m_numAudio = numAudio[0];
                for (int i = 0; i < m_numAudio; i++)
                {
                    byte[] audioFormatData = new byte[8];
                    if (fs.Read(audioFormatData, 0, 8) != 8)
                    {
                        throw new IOException("Error reading IFO file");
                    }

                    // check if the language is 'specified'
                    string lang = "";
                    if (((audioFormatData[0] & 0xC) >> 2) > 0)
                    {
                        // extract the two character ASCII code for the language
                        char langID1 = (char)audioFormatData[2];
                        char langID2 = (char)audioFormatData[3];
                        lang = String.Format("{0}{1}", (char)audioFormatData[2], (char)audioFormatData[3]);
                        // translate some language codes
                        //http://www.dvd-replica.com/DVD/vtslanguage.php
                        if (lang == "en")
                        {
                            lang = "English";
                        }
                        else if (lang == "es")
                        {
                            lang = "Spanish";
                        }
                        else if (lang == "fr")
                        {
                            lang = "French";
                        }
                        else if (lang == "ja")
                        {
                            lang = "Japanese";
                        }
                        else if (lang == "ar")
                        {
                            lang = "Arabic";
                        }
                        else if (lang == "no")
                        {
                            lang = "Norwegian";
                        }
                        else if (lang == "pl")
                        {
                            lang = "Polish";
                        }
                        else if (lang == "km")
                        {
                            lang = "Cambodian";
                        }
                        else if (lang == "pt")
                        {
                            lang = "Portuguese";
                        }
                        else if (lang == "zh")
                        {
                            lang = "Chinese";
                        }
                        else if (lang == "ro")
                        {
                            lang = "Romanian";
                        }
                        else if (lang == "ru")
                        {
                            lang = "Russian";
                        }
                        else if (lang == "cs")
                        {
                            lang = "Czech";
                        }
                        else if (lang == "da")
                        {
                            lang = "Danish";
                        }
                        else if (lang == "nl")
                        {
                            lang = "Dutch";
                        }
                        else if (lang == "sa")
                        {
                            lang = "Sanskrit";
                        }
                        else if (lang == "eo")
                        {
                            lang = "Esperanto";
                        }
                        else if (lang == "fi")
                        {
                            lang = "Finnish";
                        }
                        else if (lang == "de")
                        {
                            lang = "German";
                        }
                        else if (lang == "el")
                        {
                            lang = "Greek";
                        }
                        else if (lang == "sw")
                        {
                            lang = "Swahili";
                        }
                        else if (lang == "sv")
                        {
                            lang = "Swedish";
                        }
                        else if (lang == "iw")
                        {
                            lang = "Hebrew";
                        }
                        else if (lang == "hi")
                        {
                            lang = "Hindi";
                        }
                        else if (lang == "th")
                        {
                            lang = "Thai";
                        }
                        else if (lang == "ga")
                        {
                            lang = "Irish";
                        }
                        else if (lang == "it")
                        {
                            lang = "Italian";
                        }
                        else if (lang == "vi")
                        {
                            lang = "Vietnamese";
                        }
                        else if (lang == "ko")
                        {
                            lang = "Korean";
                        }
                        else if (lang == "ji")
                        {
                            lang = "Yiddish";
                        }
                        else if (lang == "la")
                        {
                            lang = "Latin";
                        }
                        else if (audioFormatData[2] == 0)
                        {
                            lang = "";
                        }

                        int audioType = audioFormatData[5];
                        switch (audioType)
                        {
                        case 0:
                        case 1:
                        case 2:                                 //for visually impaired
                            break;

                        case 3:                                 //director's comments
                        case 4:                                 //alt. director's comments
                            lang += "/c";
                            break;
                        }
                    }

                    // display the number of channels in the audio stream
                    int channels = (audioFormatData[1] & 0x7) + 1;
                    if (lang != "")
                    {
                        lang += " ";
                    }
                    lang += String.Format("{0}ch", channels);

                    // find the format for the stream and add it to the format list if it is supported
                    int audioFormat = (audioFormatData[0] & 0xe0) >> 5;
                    switch (audioFormat)
                    {
                    case 0:
                        m_audioFormat.Add(new AudioFormatDetails("AC3", lang, 0x80 + i));
                        break;

                    case 2:
                        m_audioFormat.Add(new AudioFormatDetails("MPEG1", lang, 0xC0 + i));
                        break;

                    case 4:
                        m_audioFormat.Add(new AudioFormatDetails("LPCM", lang, 0xA0 + i));
                        break;

                    case 6:                             // DTS
                    case 3:                             // MPEG 2
                    default:
                        // ignore other stream types (don't add to the list)
                        //throw new IOException("This DVD contains an unsupported audio type");
                        //m_audioFormat.Add(new AudioFormatDetails("DTS", lang, 0x88 + i));
                        break;
                    }
                }
                #endregion

                #region VOB File info
                // VTS_##_0.IFO -> VTS_##_1.VOB
                string vobFile = filename.Replace("0.IFO", "1.VOB");
                int    idx     = vobFile.LastIndexOf("_");
                if (idx == -1)
                {
                    // got a straight vob file like "TRAILER.VOB"
                    VOB v = new VOB(filename.ToUpper().Replace("IFO", "VOB"));
                    m_vobs.Add(v);
                }
                else
                {
                    string vobBase     = "";
                    int    vobNum      = 0;
                    long   nextSectors = 0;
                    // extract the VOB index # into vobNum
                    // vobBase will contain the title's index
                    vobBase = vobFile.Substring(0, idx);                     // up to last _
                    vobNum  = Convert.ToInt32(vobFile.Substring(idx + 1, 1));
                    if (vobNum == 0)
                    {
                        vobNum++;
                    }
                    while (true)
                    {
                        vobFile = vobBase + "_" + vobNum.ToString("0") + ".VOB";
                        vobNum++;
                        if (File.Exists(vobFile) == false)
                        {
                            break;
                        }
                        VOB v = new VOB(vobFile);
                        v.FirstSector = nextSectors;
                        nextSectors  += v.Sectors;
                        m_vobs.Add(v);
                    }
                }
                #endregion
            }
            finally
            {
                fs.Close();
            }
        }
Exemplo n.º 4
0
        private void CalculateSubtitleTypes(ProgramChain programChain)
        {
            // Additional Code to analyse stream bytes
            if (_vtsVobs.NumberOfSubtitles > 0)
            {
                // load the 'last' subpicture stream info,
                // because if we have more than one subtitle stream,
                // all subtitle positions > 0
                // lastSubtitle[0] is related to 4:3
                // lastSubtitle[1] is related to Wide
                // lastSubtitle[2] is related to letterboxed
                // lastSubtitle[3] is related to pan&scan
                byte[] lastSubtitle = programChain.SubtitlesAvailable[programChain.SubtitlesAvailable.Count - 1];

                int countSubs = 0;

                // set defaults for all possible subpicture types and positions
                programChain.Has43Subs         = false;
                programChain.HasWideSubs       = false;
                programChain.HasLetterSubs     = false;
                programChain.HasPanSubs        = false;
                programChain.HasNoSpecificSubs = true;

                int pos43Subs     = -1;
                int posWideSubs   = -1;
                int posLetterSubs = -1;
                int posPanSubs    = -1;

                // parse different subtitle bytes
                if (lastSubtitle[0] > 0x80)
                {
                    programChain.Has43Subs = true;
                    countSubs++; // 4:3
                }
                if (lastSubtitle[1] > 0)
                {
                    programChain.HasWideSubs = true;
                    countSubs++; // wide
                }
                if (lastSubtitle[2] > 0)
                {
                    programChain.HasLetterSubs = true;
                    countSubs++; // letterboxed
                }
                if (lastSubtitle[3] > 0)
                {
                    programChain.HasPanSubs = true;
                    countSubs++; // pan&scan
                }

                if (countSubs == 0)
                {
                    // may be, only a 4:3 stream exists
                    // -> lastSubtitle[0] = 0x80
                }
                else
                {
                    if (_vtsVobs.NumberOfSubtitles == 1)
                    {
                        // only 1 stream exists, may be letterboxed
                        // if so we cound't find wide id, because lastSubtitle[1] = 0 !!
                        // corresponding wide stream byte is 0 => wide id = 0x20
                        // letterboxed = 0x21
                        if (programChain.HasLetterSubs && !programChain.HasWideSubs)
                        {
                            // repair it
                            programChain.HasWideSubs = true;
                        }
                    }
                    programChain.HasNoSpecificSubs = false;
                }

                // subpucture streams start with 0x20
                int subStream = 0x20;

                // Now we know all about available subpicture streams, including position type
                // And we can create whole complete definitions for all avalable streams
                foreach (byte[] subtitle in programChain.SubtitlesAvailable)
                {
                    if (programChain.HasNoSpecificSubs)
                    {
                        // only one unspezified subpicture stream exists
                        _vtsVobs.SubtitleIDs.Add(string.Format("0x{0:x2}", subStream++));
                        _vtsVobs.SubtitleTypes.Add("unspecific");
                    }
                    else
                    {
                        // read stream position for evey subtitle type from subtitle byte
                        if (programChain.Has43Subs)
                        {
                            pos43Subs = subtitle[0] - 0x80;
                        }
                        if (programChain.HasWideSubs)
                        {
                            posWideSubs = subtitle[1];
                        }
                        if (programChain.HasLetterSubs)
                        {
                            posLetterSubs = subtitle[2];
                        }
                        if (programChain.HasPanSubs)
                        {
                            posPanSubs = subtitle[3];
                        }

                        // Now we can create subpicture id's and types for every stream
                        // All used subpicture id's and types will beappended to string, separated by colon
                        // So it's possible to split it later
                        string sub     = string.Empty;
                        string subType = string.Empty;
                        if (programChain.Has43Subs)
                        {
                            sub     = string.Format("0x{0:x2}", subStream + pos43Subs);
                            subType = "4:3";
                        }
                        if (programChain.HasWideSubs)
                        {
                            if (sub.Length > 0)
                            {
                                sub     += ", ";
                                subType += ", ";
                            }
                            sub     += string.Format("0x{0:x2}", subStream + posWideSubs);
                            subType += "wide";
                        }
                        if (programChain.HasLetterSubs)
                        {
                            if (sub.Length > 0)
                            {
                                sub     += ", ";
                                subType += ", ";
                            }
                            sub     += string.Format("0x{0:x2}", subStream + posLetterSubs);
                            subType += "letterboxed";
                        }
                        if (programChain.HasPanSubs)
                        {
                            if (sub.Length > 0)
                            {
                                sub     += ", ";
                                subType += ", ";
                            }
                            sub     += string.Format("0x{0:x2}", subStream + posPanSubs);
                            subType += "pan&scan";
                        }

                        _vtsVobs.SubtitleIDs.Add(sub);
                        _vtsVobs.SubtitleTypes.Add(subType);
                    }
                }
            }
        }
Exemplo n.º 5
0
        private void ParseVtsPgci()
        {
            const int sectorSize = 2048;

            _fs.Position = 0xCC; //Get VTS_PGCI adress
            int tableStart = sectorSize * GetEndian(4);

            _fs.Position = tableStart;
            _vtsPgci.NumberOfProgramChains = GetEndian(2);
            _vtsPgci.ProgramChains         = new List <ProgramChain>();

            for (int i = 0; i < _vtsPgci.NumberOfProgramChains; i++)
            {
                //Parse PGC Header
                var programChain = new ProgramChain();
                _fs.Position = tableStart + 4 + 8 * (i + 1);                      //Get PGC adress
                int programChainAdress = GetEndian(4);
                _fs.Position               = tableStart + programChainAdress + 2; //Move to PGC
                programChain.NumberOfPgc   = _fs.ReadByte();
                programChain.NumberOfCells = _fs.ReadByte();
                programChain.PlaybackTime  = InterpretTime(GetEndian(4));
                _fs.Seek(4, SeekOrigin.Current);

                // check if audio streams are available for this PGC
                _fs.Position = tableStart + programChainAdress + 0xC;
                for (int j = 0; j < _vtsVobs.NumberOfAudioStreams; j++)
                {
                    string temp = IntToBin(_fs.ReadByte(), 8);
                    programChain.AudioStreamsAvailable.Add(temp[0]);
                    _fs.Seek(1, SeekOrigin.Current);
                }

                // check if subtitles are available for this PGC
                _fs.Position = tableStart + programChainAdress + 0x1C;
                for (int j = 0; j < _vtsVobs.NumberOfSubtitles; j++)
                {
                    // read and save full subpicture stream info inside program chain
                    var subtitle = new byte[4];
                    _fs.Read(subtitle, 0, 4);
                    programChain.SubtitlesAvailable.Add(subtitle);
                }

                CalculateSubtitleTypes(programChain);

                //Parse Color LookUp Table (CLUT) - offset 00A4, 16*4 (0, Y, Cr, Cb)
                _fs.Position = tableStart + programChainAdress + 0xA4;
                for (int colorNumber = 0; colorNumber < 16; colorNumber++)
                {
                    var colors = new byte[4];
                    _fs.Read(colors, 0, 4);
                    int y  = colors[1] - 16;
                    int cr = colors[2] - 128;
                    int cb = colors[3] - 128;
                    int r  = (int)Math.Min(Math.Max(Math.Round(1.1644F * y + 1.596F * cr), 0), 255);
                    int g  = (int)Math.Min(Math.Max(Math.Round(1.1644F * y - 0.813F * cr - 0.391F * cb), 0), 255);
                    int b  = (int)Math.Min(Math.Max(Math.Round(1.1644F * y + 2.018F * cb), 0), 255);

                    programChain.ColorLookupTable.Add(Color.FromArgb(r, g, b));
                }

                //Parse Program Map
                _fs.Position = tableStart + programChainAdress + 0xE6;
                _fs.Position = tableStart + programChainAdress + GetEndian(2);
                for (int j = 0; j < programChain.NumberOfPgc; j++)
                {
                    programChain.PgcEntryCells.Add((byte)_fs.ReadByte());
                }

                // Cell Playback Info Table to retrieve duration
                _fs.Position = tableStart + programChainAdress + 0xE8;
                _fs.Position = tableStart + programChainAdress + GetEndian(2);
                var timeArray = new List <int>();
                for (int k = 0; k < programChain.NumberOfPgc; k++)
                {
                    int time = 0;
                    int max;
                    if (k == programChain.NumberOfPgc - 1)
                    {
                        max = programChain.NumberOfCells;
                    }
                    else
                    {
                        max = programChain.PgcEntryCells[k + 1] - 1;
                    }
                    for (int j = programChain.PgcEntryCells[k]; j <= max; j++)
                    {
                        _fs.Seek(4, SeekOrigin.Current);
                        time += TimeToMs(GetEndian(4));
                        _fs.Seek(16, SeekOrigin.Current);
                    }
                    programChain.PgcPlaybackTimes.Add(MsToTime(time));
                    timeArray.Add(time);

                    //convert to start time
                    time = 0;
                    for (int l = 1; l <= k; l++)
                    {
                        time += timeArray[l - 1];
                    }
                    if (k == 0)
                    {
                        programChain.PgcStartTimes.Add(MsToTime(0));
                    }
                    if (k > 0)
                    {
                        programChain.PgcStartTimes.Add(MsToTime(time));
                    }
                }
                _vtsPgci.ProgramChains.Add(programChain);
            }
        }
Exemplo n.º 6
-2
        public IFOParse(string filename)
        {
            m_file = new FileInfo(filename);
            FileStream fs = File.OpenRead(filename);
            try
            {
                #region Header
                // verify valid video IFO header
                byte[] id = new byte[12];
                if(fs.Read(id, 0, 12) != 12)
                    throw new IOException("Error reading IFO file");
                string idStr = Encoding.ASCII.GetString(id, 0, 12);
                if(idStr != "DVDVIDEO-VTS")
                    throw new Exception("Invalid IFO Header");
                #endregion

                #region Version
                // verify IFO version (1.0 or 1.1)
                if(fs.Seek(0x21, SeekOrigin.Begin) != 0x21)
                    throw new IOException("Error reading IFO file");
                byte[] version = new byte[1];
                if(fs.Read(version, 0, 1) != 1)
                    throw new IOException("Error reading IFO file");
                if(version[0] != 0x10 && version[0] != 0x11)
                    throw new Exception("Invalid IFO Header Version");
                #endregion

                #region PGC Info
                // find offset to pgc tables
                if(fs.Seek(0xcc, SeekOrigin.Begin) != 0xcc)
                    throw new IOException("Error reading IFO file");
                byte[] pgcOffsetData = new byte[4];
                if(fs.Read(pgcOffsetData, 0, 4) != 4)
                    throw new IOException("Error reading IFO file");
                int pgcOffset = (pgcOffsetData[0] << 24) + (pgcOffsetData[1] << 16) + (pgcOffsetData[2] << 8) + pgcOffsetData[3];

                // get count of pgcs
                if(fs.Seek(pgcOffset * 2048, SeekOrigin.Begin) != pgcOffset * 2048)
                    throw new IOException("Error reading IFO file");
                byte[] pgciData = new byte[2];
                if(fs.Read(pgciData, 0, 2) != 2)
                    throw new IOException("Error reading IFO file");
                int pgcCount = (pgciData[0] << 8) + pgciData[1];

                for (int i = 1; i <= pgcCount; i++)
                {
                    ProgramChain pgc = new ProgramChain(this);

                    // get pgc's info offset within pgc (length is 4 bytes into pgc info)
                    if(fs.Seek((pgcOffset * 2048) + (i * 8) + 4, SeekOrigin.Begin) != (pgcOffset * 2048) + (i * 8) + 4)
                        throw new IOException("Error reading IFO file");
                    byte[] pgcStartData = new byte[4];
                    if(fs.Read(pgcStartData, 0, 4) != 4)
                        throw new IOException("Error reading IFO file");
                    int pgcStart = (pgcStartData[0] << 24) + (pgcStartData[1] << 16) + (pgcStartData[2] << 8) + pgcStartData[3];

                    // get pgc's playback time
                    if(fs.Seek((pgcOffset * 2048) + pgcStart + 4, SeekOrigin.Begin) != (pgcOffset * 2048) + pgcStart + 4)
                        throw new IOException("Error reading IFO file");
                    byte[] playbackTimeData = new byte[4];
                    if(fs.Read(playbackTimeData, 0, 4) != 4)
                        throw new IOException("Error reading IFO file");

                    // frame rate
                    int frameRate = (playbackTimeData[3] & 0xC0) >> 6;
                    if(frameRate == 3)
                        pgc.FrameRate = 30;
                    else if(frameRate == 1)
                        pgc.FrameRate = 25;

                    // duration
                    int playbackTime = (playbackTimeData[0] << 24) + (playbackTimeData[1] << 16) + (playbackTimeData[2] << 8) + playbackTimeData[3];

                    int hour = ((playbackTime >> 28) & 0x0f) * 10 + ((playbackTime >> 24) & 0x0f);
                    int min = ((playbackTime >> 20) & 0x0f) * 10 + ((playbackTime >> 16) & 0x0f);
                    int sec = ((playbackTime >> 12) & 0x0f) * 10 + ((playbackTime >> 8) & 0x0f);
                    int frame = (((playbackTime >> 4) & 0x0f) * 10 + (playbackTime & 0x0f)) - 120;

                    pgc.Duration = hour * 3600 + min * 60 + sec;
                    pgc.PartialFrames = frame;

                    // get pgc's cell playback info table
                    if (fs.Seek((pgcOffset * 2048) + pgcStart + 0xE8, SeekOrigin.Begin) != (pgcOffset * 2048) + pgcStart + 0xE8)
                        throw new IOException("Error reading IFO file");
                    byte[] C_PBKT_buf = new byte[2];
                    if (fs.Read(C_PBKT_buf, 0, 2) != 2)
                        throw new IOException("Error reading IFO file");
                    int C_PBKT = (C_PBKT_buf[0] << 8) + C_PBKT_buf[1];
                    if (C_PBKT != 0)
                        C_PBKT += (pgcOffset * 2048) + pgcStart;

                    // get pgc's cell position info table
                    if (fs.Seek((pgcOffset * 2048) + pgcStart + 0xEA, SeekOrigin.Begin) != (pgcOffset * 2048) + pgcStart + 0xEA)
                        throw new IOException("Error reading IFO file");
                    byte[] C_POST_buf = new byte[2];
                    if (fs.Read(C_POST_buf, 0, 2) != 2)
                        throw new IOException("Error reading IFO file");
                    int C_POST = (C_POST_buf[0] << 8) + C_POST_buf[1];
                    if (C_POST != 0)
                        C_POST += (pgcOffset * 2048) + pgcStart;

                    // get pgc's number of cells
                    if (fs.Seek((pgcOffset * 2048) + pgcStart + 3, SeekOrigin.Begin) != (pgcOffset * 2048) + pgcStart + 3)
                        throw new IOException("Error reading IFO file");
                    byte[] nCells_buf = new byte[1];
                    if (fs.Read(nCells_buf, 0, 1) != 1)
                        throw new IOException("Error reading IFO file");
                    int nCells = nCells_buf[0];
                    int nAngles = 1;
                    if (C_POST != 0 && C_PBKT != 0)
                    {
                        for (int nCell = 0; nCell < nCells; nCell++)
                        {
                            Cell c = new Cell();
                            // get info for cell
                            /*if (fs.Seek(C_PBKT + 24 * nCell, SeekOrigin.Begin) != C_PBKT + 24 * nCell)
                                throw new IOException("Error reading IFO file");
                            byte[] iCat_buf = new byte[1];
                            if (fs.Read(iCat_buf, 0, 1) != 1)
                                throw new IOException("Error reading IFO file");

                            // ignore multi-angle
                            int iCat = iCat_buf[0] & 0xF10;
                            //			0101=First; 1001=Middle ;	1101=Last
                            if (iCat == 0x50)
                                nAngles = 1;
                            else if (iCat == 0x90)
                                nAngles++;
                            else if (iCat == 0xD0)
                            {
                                nAngles++;
                                break;
                            }*/

                            #region read duration
                            if (fs.Seek(C_PBKT + 24 * nCell + 4, SeekOrigin.Begin) != C_PBKT + 24 * nCell + 4)
                                throw new IOException("Error reading IFO file");
                            playbackTimeData = new byte[4];
                            if (fs.Read(playbackTimeData, 0, 4) != 4)
                                throw new IOException("Error reading IFO file");
                            playbackTime = (playbackTimeData[0] << 24) + (playbackTimeData[1] << 16) + (playbackTimeData[2] << 8) + playbackTimeData[3];

                            hour = ((playbackTime >> 28) & 0x0f) * 10 + ((playbackTime >> 24) & 0x0f);
                            min = ((playbackTime >> 20) & 0x0f) * 10 + ((playbackTime >> 16) & 0x0f);
                            sec = ((playbackTime >> 12) & 0x0f) * 10 + ((playbackTime >> 8) & 0x0f);
                            frame = (((playbackTime >> 4) & 0x0f) * 10 + (playbackTime & 0x0f)) - 120;

                            c.Duration = hour * 3600 + min * 60 + sec;
                            c.PartialFrames = frame;
                            #endregion

                            #region entry point
                            if (fs.Seek(C_PBKT + 24 * nCell + 8, SeekOrigin.Begin) != C_PBKT + 24 * nCell + 8)
                                throw new IOException("Error reading IFO file");
                            byte[] entryPointBuf = new byte[4];
                            if (fs.Read(entryPointBuf, 0, 4) != 4)
                                throw new IOException("Error reading IFO file");
                            c.FirstSector = (entryPointBuf[0] << 24) + (entryPointBuf[1] << 16) + (entryPointBuf[2] << 8) + entryPointBuf[3];
                            #endregion

                            #region last sector
                            if (fs.Seek(C_PBKT + 24 * nCell + 20, SeekOrigin.Begin) != C_PBKT + 24 * nCell + 20)
                                throw new IOException("Error reading IFO file");
                            byte[] lastSectorBuf = new byte[4];
                            if (fs.Read(lastSectorBuf, 0, 4) != 4)
                                throw new IOException("Error reading IFO file");
                            c.LastSector = (lastSectorBuf[0] << 24) + (lastSectorBuf[1] << 16) + (lastSectorBuf[2] << 8) + lastSectorBuf[3];
                            #endregion

                            if (fs.Seek(C_POST + 4 * nCell, SeekOrigin.Begin) != C_POST + 4 * nCell)
                                throw new IOException("Error reading IFO file");
                            byte[] vobIDBuf = new byte[2];
                            if (fs.Read(vobIDBuf, 0, 2) != 2)
                                throw new IOException("Error reading IFO file");
                            c.VobID = (vobIDBuf[0] << 8) + vobIDBuf[1];
                            if (fs.Seek(C_POST + 4 * nCell + 3, SeekOrigin.Begin) != C_POST + 4 * nCell + 3)
                                throw new IOException("Error reading IFO file");
                            byte[] cellIDBuf = new byte[1];
                            if (fs.Read(cellIDBuf, 0, 1) != 1)
                                throw new IOException("Error reading IFO file");
                            c.CellID = cellIDBuf[0];

                            pgc.Cells.Add(c);
                        }
                    }
                    pgc.Angles = nAngles;
                    m_pgcs.Add(pgc);
                }
                #endregion

                #region Video
                if(fs.Seek(0x200, SeekOrigin.Begin) != 0x200)
                    throw new IOException("Error reading IFO file");
                byte[] videoAttributes = new byte[2];
                if(fs.Read(videoAttributes, 0, 2) != 2)
                    throw new IOException("Error reading IFO file");
                byte va1 = videoAttributes[0];
                byte va2 = videoAttributes[1];

                // video mode
                if(((va1 & 0x30) >> 4) == 0)
                    m_videoMode = "NTSC";
                else if(((va1 & 0x30) >> 4) == 1)
                    m_videoMode = "PAL";

                // aspect
                if(((va1 & 0xC) >> 2) == 0)
                    m_aspectRatio = "4:3";
                else if(((va1 & 0xC) >> 2) == 3)
                    m_aspectRatio = "16:9";

                // resolution
                int resolution = ((va2 & 0x38) >> 3);
                switch(resolution)
                {
                    case 0:
                        m_resolution = "720x480";
                        break;
                    case 1:
                        m_resolution = "704x480";
                        break;
                    case 2:
                        m_resolution = "352x480";
                        break;
                    case 3:
                        m_resolution = "352x240";
                        break;
                }
                #endregion

                #region Audio
                // find count of audio streams
                if(fs.Seek(0x203, SeekOrigin.Begin) != 0x203)
                    throw new IOException("Error reading IFO file");
                byte[] numAudio = new byte[1];
                if(fs.Read(numAudio, 0, 1) != 1)
                    throw new IOException("Error reading IFO file");
                m_numAudio = numAudio[0];
                byte[] audioFormatData = new byte[1];
                if(fs.Read(audioFormatData, 0, 1) != 1)
                    throw new IOException("Error reading IFO file");
                int audioFormat = (audioFormatData[0] & 0xe0) >> 5;
                switch(audioFormat)
                {
                    case 0:
                        m_audioFormat = "AC3";
                        break;
                    case 2:
                        m_audioFormat = "MPEG1";
                        break;
                    case 3:
                        m_audioFormat = "MPEG2";
                        break;
                    case 4:
                        m_audioFormat = "LPCM";
                        break;
                    case 6:
                        m_audioFormat = "DTS";
                        break;
                }
                #endregion

                #region VOB File info
                // VTS_##_0.IFO -> VTS_##_1.VOB
                string vobFile = filename.Replace("0.IFO", "1.VOB");
                int idx = vobFile.LastIndexOf("_");
                if (idx == -1)
                {
                    // got a straight vob file like "TRAILER.VOB"
                    VOB v = new VOB(filename.ToUpper().Replace("IFO", "VOB"));
                    m_vobs.Add(v);
                }
                else
                {
                    string vobBase = "";
                    int vobNum = 0;
                    long nextSectors = 0;
                    // extract the VOB index # into vobNum
                    // vobBase will contain the title's index
                    vobBase = vobFile.Substring(0, idx); // up to last _
                    vobNum = Convert.ToInt32(vobFile.Substring(idx + 1, 1));
                    if (vobNum == 0)
                        vobNum++;
                    while (true)
                    {
                        vobFile = vobBase + "_" + vobNum.ToString("0") + ".VOB";
                        vobNum++;
                        if (File.Exists(vobFile) == false)
                            break;
                        VOB v = new VOB(vobFile);
                        v.FirstSector = nextSectors;
                        nextSectors += v.Sectors;
                        m_vobs.Add(v);
                    }
                }
                #endregion
            }
            finally
            {
                fs.Close();
            }
        }