Example #1
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();
            }
        }
Example #2
-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();
            }
        }