diff options
Diffstat (limited to 'DvdLib/Ifo')
| -rw-r--r-- | DvdLib/Ifo/AudioAttributes.cs | 41 | ||||
| -rw-r--r-- | DvdLib/Ifo/Cell.cs | 24 | ||||
| -rw-r--r-- | DvdLib/Ifo/CellPlaybackInfo.cs | 54 | ||||
| -rw-r--r-- | DvdLib/Ifo/CellPositionInfo.cs | 21 | ||||
| -rw-r--r-- | DvdLib/Ifo/Chapter.cs | 21 | ||||
| -rw-r--r-- | DvdLib/Ifo/Dvd.cs | 161 | ||||
| -rw-r--r-- | DvdLib/Ifo/DvdTime.cs | 34 | ||||
| -rw-r--r-- | DvdLib/Ifo/PgcCommandTable.cs | 20 | ||||
| -rw-r--r-- | DvdLib/Ifo/Program.cs | 17 | ||||
| -rw-r--r-- | DvdLib/Ifo/ProgramChain.cs | 117 | ||||
| -rw-r--r-- | DvdLib/Ifo/Title.cs | 64 | ||||
| -rw-r--r-- | DvdLib/Ifo/UserOperation.cs | 38 | ||||
| -rw-r--r-- | DvdLib/Ifo/VideoAttributes.cs | 51 |
13 files changed, 663 insertions, 0 deletions
diff --git a/DvdLib/Ifo/AudioAttributes.cs b/DvdLib/Ifo/AudioAttributes.cs new file mode 100644 index 000000000..5b3b9fd9a --- /dev/null +++ b/DvdLib/Ifo/AudioAttributes.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DvdLib.Ifo +{ + public enum AudioCodec + { + AC3 = 0, + MPEG1 = 2, + MPEG2ext = 3, + LPCM = 4, + DTS = 6, + } + + public enum ApplicationMode + { + Unspecified = 0, + Karaoke = 1, + Surround = 2, + } + + public class AudioAttributes + { + public readonly AudioCodec Codec; + public readonly bool MultichannelExtensionPresent; + public readonly ApplicationMode Mode; + public readonly byte QuantDRC; + public readonly byte SampleRate; + public readonly byte Channels; + public readonly ushort LanguageCode; + public readonly byte LanguageExtension; + public readonly byte CodeExtension; + } + + public class MultiChannelExtension + { + + } +} diff --git a/DvdLib/Ifo/Cell.cs b/DvdLib/Ifo/Cell.cs new file mode 100644 index 000000000..d0f442e36 --- /dev/null +++ b/DvdLib/Ifo/Cell.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace DvdLib.Ifo +{ + public class Cell + { + public CellPlaybackInfo PlaybackInfo { get; private set; } + public CellPositionInfo PositionInfo { get; private set; } + + internal void ParsePlayback(BinaryReader br) + { + PlaybackInfo = new CellPlaybackInfo(br); + } + + internal void ParsePosition(BinaryReader br) + { + PositionInfo = new CellPositionInfo(br); + } + } +} diff --git a/DvdLib/Ifo/CellPlaybackInfo.cs b/DvdLib/Ifo/CellPlaybackInfo.cs new file mode 100644 index 000000000..ae3883eaa --- /dev/null +++ b/DvdLib/Ifo/CellPlaybackInfo.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace DvdLib.Ifo +{ + public enum BlockMode + { + NotInBlock = 0, + FirstCell = 1, + InBlock = 2, + LastCell = 3, + } + + public enum BlockType + { + Normal = 0, + Angle = 1, + } + + public enum PlaybackMode + { + Normal = 0, + StillAfterEachVOBU = 1, + } + + public class CellPlaybackInfo + { + public readonly BlockMode Mode; + public readonly BlockType Type; + public readonly bool SeamlessPlay; + public readonly bool Interleaved; + public readonly bool STCDiscontinuity; + public readonly bool SeamlessAngle; + public readonly PlaybackMode PlaybackMode; + public readonly bool Restricted; + public readonly byte StillTime; + public readonly byte CommandNumber; + public readonly DvdTime PlaybackTime; + public readonly uint FirstSector; + public readonly uint FirstILVUEndSector; + public readonly uint LastVOBUStartSector; + public readonly uint LastSector; + + internal CellPlaybackInfo(BinaryReader br) + { + br.BaseStream.Seek(0x4, SeekOrigin.Current); + PlaybackTime = new DvdTime(br.ReadBytes(4)); + br.BaseStream.Seek(0x10, SeekOrigin.Current); + } + } +} diff --git a/DvdLib/Ifo/CellPositionInfo.cs b/DvdLib/Ifo/CellPositionInfo.cs new file mode 100644 index 000000000..2e0715940 --- /dev/null +++ b/DvdLib/Ifo/CellPositionInfo.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace DvdLib.Ifo +{ + public class CellPositionInfo + { + public readonly ushort VOBId; + public readonly byte CellId; + + internal CellPositionInfo(BinaryReader br) + { + VOBId = br.ReadUInt16(); + br.ReadByte(); + CellId = br.ReadByte(); + } + } +} diff --git a/DvdLib/Ifo/Chapter.cs b/DvdLib/Ifo/Chapter.cs new file mode 100644 index 000000000..802c6ce62 --- /dev/null +++ b/DvdLib/Ifo/Chapter.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DvdLib.Ifo +{ + public class Chapter + { + public ushort ProgramChainNumber { get; private set; } + public ushort ProgramNumber { get; private set; } + public uint ChapterNumber { get; private set; } + + public Chapter(ushort pgcNum, ushort programNum, uint chapterNum) + { + ProgramChainNumber = pgcNum; + ProgramNumber = programNum; + ChapterNumber = chapterNum; + } + } +} diff --git a/DvdLib/Ifo/Dvd.cs b/DvdLib/Ifo/Dvd.cs new file mode 100644 index 000000000..508c23db4 --- /dev/null +++ b/DvdLib/Ifo/Dvd.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Diagnostics; +using MediaBrowser.Model.IO; + +namespace DvdLib.Ifo +{ + public class Dvd + { + private readonly ushort _titleSetCount; + public readonly List<Title> Titles; + + private ushort _titleCount; + public readonly Dictionary<ushort, string> VTSPaths = new Dictionary<ushort, string>(); + private readonly IFileSystem _fileSystem; + + public Dvd(string path, IFileSystem fileSystem) + { + _fileSystem = fileSystem; + Titles = new List<Title>(); + var allFiles = _fileSystem.GetFiles(path, true).ToList(); + + var vmgPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.IFO", StringComparison.OrdinalIgnoreCase)) ?? + allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.BUP", StringComparison.OrdinalIgnoreCase)); + + if (vmgPath == null) + { + var allIfos = allFiles.Where(i => string.Equals(i.Extension, ".ifo", StringComparison.OrdinalIgnoreCase)); + + foreach (var ifo in allIfos) + { + var num = ifo.Name.Split('_').ElementAtOrDefault(1); + ushort ifoNumber; + var numbersRead = new List<ushort>(); + + if (!string.IsNullOrEmpty(num) && ushort.TryParse(num, out ifoNumber) && !numbersRead.Contains(ifoNumber)) + { + ReadVTS(ifoNumber, ifo.FullName); + numbersRead.Add(ifoNumber); + } + } + } + else + { + using (var vmgFs = _fileSystem.GetFileStream(vmgPath.FullName, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) + { + using (BigEndianBinaryReader vmgRead = new BigEndianBinaryReader(vmgFs)) + { + vmgFs.Seek(0x3E, SeekOrigin.Begin); + _titleSetCount = vmgRead.ReadUInt16(); + + // read address of TT_SRPT + vmgFs.Seek(0xC4, SeekOrigin.Begin); + uint ttSectorPtr = vmgRead.ReadUInt32(); + vmgFs.Seek(ttSectorPtr * 2048, SeekOrigin.Begin); + ReadTT_SRPT(vmgRead); + } + } + + for (ushort titleSetNum = 1; titleSetNum <= _titleSetCount; titleSetNum++) + { + ReadVTS(titleSetNum, allFiles); + } + } + } + + private void ReadTT_SRPT(BinaryReader read) + { + _titleCount = read.ReadUInt16(); + read.BaseStream.Seek(6, SeekOrigin.Current); + for (uint titleNum = 1; titleNum <= _titleCount; titleNum++) + { + Title t = new Title(titleNum); + t.ParseTT_SRPT(read); + Titles.Add(t); + } + } + + private void ReadVTS(ushort vtsNum, List<FileSystemMetadata> allFiles) + { + var filename = String.Format("VTS_{0:00}_0.IFO", vtsNum); + + var vtsPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, filename, StringComparison.OrdinalIgnoreCase)) ?? + allFiles.FirstOrDefault(i => string.Equals(i.Name, Path.ChangeExtension(filename, ".bup"), StringComparison.OrdinalIgnoreCase)); + + if (vtsPath == null) + { + throw new FileNotFoundException("Unable to find VTS IFO file"); + } + + ReadVTS(vtsNum, vtsPath.FullName); + } + + private void ReadVTS(ushort vtsNum, string vtsPath) + { + VTSPaths[vtsNum] = vtsPath; + + using (var vtsFs = _fileSystem.GetFileStream(vtsPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) + { + using (BigEndianBinaryReader vtsRead = new BigEndianBinaryReader(vtsFs)) + { + // Read VTS_PTT_SRPT + vtsFs.Seek(0xC8, SeekOrigin.Begin); + uint vtsPttSrptSecPtr = vtsRead.ReadUInt32(); + uint baseAddr = (vtsPttSrptSecPtr * 2048); + vtsFs.Seek(baseAddr, SeekOrigin.Begin); + + ushort numTitles = vtsRead.ReadUInt16(); + vtsRead.ReadUInt16(); + uint endaddr = vtsRead.ReadUInt32(); + uint[] offsets = new uint[numTitles]; + for (ushort titleNum = 0; titleNum < numTitles; titleNum++) + { + offsets[titleNum] = vtsRead.ReadUInt32(); + } + + for (uint titleNum = 0; titleNum < numTitles; titleNum++) + { + uint chapNum = 1; + vtsFs.Seek(baseAddr + offsets[titleNum], SeekOrigin.Begin); + Title t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum + 1)); + if (t == null) continue; + + do + { + t.Chapters.Add(new Chapter(vtsRead.ReadUInt16(), vtsRead.ReadUInt16(), chapNum)); + if (titleNum + 1 < numTitles && vtsFs.Position == (baseAddr + offsets[titleNum + 1])) break; + chapNum++; + } + while (vtsFs.Position < (baseAddr + endaddr)); + } + + // Read VTS_PGCI + vtsFs.Seek(0xCC, SeekOrigin.Begin); + uint vtsPgciSecPtr = vtsRead.ReadUInt32(); + vtsFs.Seek(vtsPgciSecPtr * 2048, SeekOrigin.Begin); + + long startByte = vtsFs.Position; + + ushort numPgcs = vtsRead.ReadUInt16(); + vtsFs.Seek(6, SeekOrigin.Current); + for (ushort pgcNum = 1; pgcNum <= numPgcs; pgcNum++) + { + byte pgcCat = vtsRead.ReadByte(); + bool entryPgc = (pgcCat & 0x80) != 0; + uint titleNum = (uint)(pgcCat & 0x7F); + + vtsFs.Seek(3, SeekOrigin.Current); + uint vtsPgcOffset = vtsRead.ReadUInt32(); + + Title t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum)); + if (t != null) t.AddPgc(vtsRead, startByte + vtsPgcOffset, entryPgc, pgcNum); + } + } + } + } + } +}
\ No newline at end of file diff --git a/DvdLib/Ifo/DvdTime.cs b/DvdLib/Ifo/DvdTime.cs new file mode 100644 index 000000000..f565f5fdf --- /dev/null +++ b/DvdLib/Ifo/DvdTime.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DvdLib.Ifo +{ + public class DvdTime + { + public readonly byte Hour, Minute, Second, Frames, FrameRate; + + public DvdTime(byte[] data) + { + Hour = GetBCDValue(data[0]); + Minute = GetBCDValue(data[1]); + Second = GetBCDValue(data[2]); + Frames = GetBCDValue((byte)(data[3] & 0x3F)); + + if ((data[3] & 0x80) != 0) FrameRate = 30; + else if ((data[3] & 0x40) != 0) FrameRate = 25; + } + + private byte GetBCDValue(byte data) + { + return (byte)((((data & 0xF0) >> 4) * 10) + (data & 0x0F)); + } + + public static explicit operator TimeSpan(DvdTime time) + { + int ms = (int)(((1.0 / (double)time.FrameRate) * time.Frames) * 1000.0); + return new TimeSpan(0, time.Hour, time.Minute, time.Second, ms); + } + } +} diff --git a/DvdLib/Ifo/PgcCommandTable.cs b/DvdLib/Ifo/PgcCommandTable.cs new file mode 100644 index 000000000..2ead78cef --- /dev/null +++ b/DvdLib/Ifo/PgcCommandTable.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DvdLib.Ifo +{ + public class ProgramChainCommandTable + { + public readonly ushort LastByteAddress; + public readonly List<VirtualMachineCommand> PreCommands; + public readonly List<VirtualMachineCommand> PostCommands; + public readonly List<VirtualMachineCommand> CellCommands; + } + + public class VirtualMachineCommand + { + public readonly byte[] Command; + } +} diff --git a/DvdLib/Ifo/Program.cs b/DvdLib/Ifo/Program.cs new file mode 100644 index 000000000..48870d9dd --- /dev/null +++ b/DvdLib/Ifo/Program.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DvdLib.Ifo +{ + public class Program + { + public readonly List<Cell> Cells; + + public Program(List<Cell> cells) + { + Cells = cells; + } + } +} diff --git a/DvdLib/Ifo/ProgramChain.cs b/DvdLib/Ifo/ProgramChain.cs new file mode 100644 index 000000000..3179f73cd --- /dev/null +++ b/DvdLib/Ifo/ProgramChain.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace DvdLib.Ifo +{ + public enum ProgramPlaybackMode + { + Sequential, + Random, + Shuffle + } + + public class ProgramChain + { + private ushort _unknown1; + + private byte _programCount; + public readonly List<Program> Programs; + + private byte _cellCount; + public readonly List<Cell> Cells; + + public DvdTime PlaybackTime { get; private set; } + public UserOperation ProhibitedUserOperations { get; private set; } + public byte[] AudioStreamControl { get; private set; } // 8*2 entries + public byte[] SubpictureStreamControl { get; private set; } // 32*4 entries + + private ushort _nextProgramNumber; + public readonly ProgramChain Next; + + private ushort _prevProgramNumber; + public readonly ProgramChain Previous; + + private ushort _goupProgramNumber; + public readonly ProgramChain Goup; // ?? maybe Group + + private byte _playbackMode; + public ProgramPlaybackMode PlaybackMode { get; private set; } + public uint ProgramCount { get; private set; } + + public byte StillTime { get; private set; } + public byte[] Palette { get; private set; } // 16*4 entries + + private ushort _commandTableOffset; + public readonly ProgramChainCommandTable CommandTable; + + private ushort _programMapOffset; + private ushort _cellPlaybackOffset; + private ushort _cellPositionOffset; + + public readonly uint VideoTitleSetIndex; + + internal ProgramChain(uint vtsPgcNum) + { + VideoTitleSetIndex = vtsPgcNum; + Cells = new List<Cell>(); + Programs = new List<Program>(); + } + + internal void ParseHeader(BinaryReader br) + { + long startPos = br.BaseStream.Position; + + br.ReadUInt16(); + _programCount = br.ReadByte(); + _cellCount = br.ReadByte(); + PlaybackTime = new DvdTime(br.ReadBytes(4)); + ProhibitedUserOperations = (UserOperation)br.ReadUInt32(); + AudioStreamControl = br.ReadBytes(16); + SubpictureStreamControl = br.ReadBytes(128); + + _nextProgramNumber = br.ReadUInt16(); + _prevProgramNumber = br.ReadUInt16(); + _goupProgramNumber = br.ReadUInt16(); + + StillTime = br.ReadByte(); + byte pbMode = br.ReadByte(); + if (pbMode == 0) PlaybackMode = ProgramPlaybackMode.Sequential; + else PlaybackMode = ((pbMode & 0x80) == 0) ? ProgramPlaybackMode.Random : ProgramPlaybackMode.Shuffle; + ProgramCount = (uint)(pbMode & 0x7F); + + Palette = br.ReadBytes(64); + _commandTableOffset = br.ReadUInt16(); + _programMapOffset = br.ReadUInt16(); + _cellPlaybackOffset = br.ReadUInt16(); + _cellPositionOffset = br.ReadUInt16(); + + // read position info + br.BaseStream.Seek(startPos + _cellPositionOffset, SeekOrigin.Begin); + for (int cellNum = 0; cellNum < _cellCount; cellNum++) + { + Cell c = new Cell(); + c.ParsePosition(br); + Cells.Add(c); + } + + br.BaseStream.Seek(startPos + _cellPlaybackOffset, SeekOrigin.Begin); + for (int cellNum = 0; cellNum < _cellCount; cellNum++) + { + Cells[cellNum].ParsePlayback(br); + } + + br.BaseStream.Seek(startPos + _programMapOffset, SeekOrigin.Begin); + List<int> cellNumbers = new List<int>(); + for (int progNum = 0; progNum < _programCount; progNum++) cellNumbers.Add(br.ReadByte() - 1); + + for (int i = 0; i < cellNumbers.Count; i++) + { + int max = (i + 1 == cellNumbers.Count) ? _cellCount : cellNumbers[i+1]; + Programs.Add(new Program(Cells.Where((c, idx) => idx >= cellNumbers[i] && idx < max).ToList())); + } + } + } +} diff --git a/DvdLib/Ifo/Title.cs b/DvdLib/Ifo/Title.cs new file mode 100644 index 000000000..70deb45bf --- /dev/null +++ b/DvdLib/Ifo/Title.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace DvdLib.Ifo +{ + public class Title + { + public uint TitleNumber { get; private set; } + public uint AngleCount { get; private set; } + public ushort ChapterCount { get; private set; } + public byte VideoTitleSetNumber { get; private set; } + + private ushort _parentalManagementMask; + private byte _titleNumberInVTS; + private uint _vtsStartSector; // relative to start of entire disk + + public ProgramChain EntryProgramChain { get; private set; } + public readonly List<ProgramChain> ProgramChains; + + public readonly List<Chapter> Chapters; + + public Title(uint titleNum) + { + ProgramChains = new List<ProgramChain>(); + Chapters = new List<Chapter>(); + Chapters = new List<Chapter>(); + TitleNumber = titleNum; + } + + public bool IsVTSTitle(uint vtsNum, uint vtsTitleNum) + { + return (vtsNum == VideoTitleSetNumber && vtsTitleNum == _titleNumberInVTS); + } + + internal void ParseTT_SRPT(BinaryReader br) + { + byte titleType = br.ReadByte(); + // TODO parse Title Type + + AngleCount = br.ReadByte(); + ChapterCount = br.ReadUInt16(); + _parentalManagementMask = br.ReadUInt16(); + VideoTitleSetNumber = br.ReadByte(); + _titleNumberInVTS = br.ReadByte(); + _vtsStartSector = br.ReadUInt32(); + } + + internal void AddPgc(BinaryReader br, long startByte, bool entryPgc, uint pgcNum) + { + long curPos = br.BaseStream.Position; + br.BaseStream.Seek(startByte, SeekOrigin.Begin); + + ProgramChain pgc = new ProgramChain(pgcNum); + pgc.ParseHeader(br); + ProgramChains.Add(pgc); + if (entryPgc) EntryProgramChain = pgc; + + br.BaseStream.Seek(curPos, SeekOrigin.Begin); + } + } +} diff --git a/DvdLib/Ifo/UserOperation.cs b/DvdLib/Ifo/UserOperation.cs new file mode 100644 index 000000000..c3cffd487 --- /dev/null +++ b/DvdLib/Ifo/UserOperation.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DvdLib.Ifo +{ + [Flags] + public enum UserOperation + { + None = 0, + TitleOrTimePlay = 1, + ChapterSearchOrPlay = 2, + TitlePlay = 4, + Stop = 8, + GoUp = 16, + TimeOrChapterSearch = 32, + PrevOrTopProgramSearch = 64, + NextProgramSearch = 128, + ForwardScan = 256, + BackwardScan = 512, + TitleMenuCall = 1024, + RootMenuCall = 2048, + SubpictureMenuCall = 4096, + AudioMenuCall = 8192, + AngleMenuCall = 16384, + ChapterMenuCall = 32768, + Resume = 65536, + ButtonSelectOrActive = 131072, + StillOff = 262144, + PauseOn = 524288, + AudioStreamChange = 1048576, + SubpictureStreamChange = 2097152, + AngleChange = 4194304, + KaraokeAudioPresentationModeChange = 8388608, + VideoPresentationModeChange = 16777216, + } +} diff --git a/DvdLib/Ifo/VideoAttributes.cs b/DvdLib/Ifo/VideoAttributes.cs new file mode 100644 index 000000000..b2d375942 --- /dev/null +++ b/DvdLib/Ifo/VideoAttributes.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DvdLib.Ifo +{ + public enum VideoCodec + { + MPEG1 = 0, + MPEG2 = 1, + } + + public enum VideoFormat + { + NTSC = 0, + PAL = 1, + } + + public enum AspectRatio + { + ar4to3 = 0, + ar16to9 = 3 + } + + public enum FilmMode + { + None = -1, + Camera = 0, + Film = 1, + } + + public class VideoAttributes + { + public readonly VideoCodec Codec; + public readonly VideoFormat Format; + public readonly AspectRatio Aspect; + public readonly bool AutomaticPanScan; + public readonly bool AutomaticLetterBox; + public readonly bool Line21CCField1; + public readonly bool Line21CCField2; + public readonly int Width; + public readonly int Height; + public readonly bool Letterboxed; + public readonly FilmMode FilmMode; + + public VideoAttributes() + { + } + } +} |
