Dvd.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. namespace DvdLib.Ifo
  6. {
  7. public class Dvd
  8. {
  9. private readonly ushort _titleSetCount;
  10. public readonly List<Title> Titles;
  11. private ushort _titleCount;
  12. public readonly Dictionary<ushort, string> VTSPaths = new Dictionary<ushort, string>();
  13. public Dvd(string path)
  14. {
  15. Titles = new List<Title>();
  16. var allFiles = new DirectoryInfo(path).GetFiles(path, SearchOption.AllDirectories);
  17. var vmgPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.IFO", StringComparison.OrdinalIgnoreCase)) ??
  18. allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.BUP", StringComparison.OrdinalIgnoreCase));
  19. if (vmgPath == null)
  20. {
  21. foreach (var ifo in allFiles)
  22. {
  23. if (!string.Equals(ifo.Extension, ".ifo", StringComparison.OrdinalIgnoreCase))
  24. {
  25. continue;
  26. }
  27. var nums = ifo.Name.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries);
  28. if (nums.Length >= 2 && ushort.TryParse(nums[1], out var ifoNumber))
  29. {
  30. ReadVTS(ifoNumber, ifo.FullName);
  31. }
  32. }
  33. }
  34. else
  35. {
  36. using (var vmgFs = new FileStream(vmgPath.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
  37. {
  38. using (var vmgRead = new BigEndianBinaryReader(vmgFs))
  39. {
  40. vmgFs.Seek(0x3E, SeekOrigin.Begin);
  41. _titleSetCount = vmgRead.ReadUInt16();
  42. // read address of TT_SRPT
  43. vmgFs.Seek(0xC4, SeekOrigin.Begin);
  44. uint ttSectorPtr = vmgRead.ReadUInt32();
  45. vmgFs.Seek(ttSectorPtr * 2048, SeekOrigin.Begin);
  46. ReadTT_SRPT(vmgRead);
  47. }
  48. }
  49. for (ushort titleSetNum = 1; titleSetNum <= _titleSetCount; titleSetNum++)
  50. {
  51. ReadVTS(titleSetNum, allFiles);
  52. }
  53. }
  54. }
  55. private void ReadTT_SRPT(BinaryReader read)
  56. {
  57. _titleCount = read.ReadUInt16();
  58. read.BaseStream.Seek(6, SeekOrigin.Current);
  59. for (uint titleNum = 1; titleNum <= _titleCount; titleNum++)
  60. {
  61. var t = new Title(titleNum);
  62. t.ParseTT_SRPT(read);
  63. Titles.Add(t);
  64. }
  65. }
  66. private void ReadVTS(ushort vtsNum, IReadOnlyList<FileInfo> allFiles)
  67. {
  68. var filename = string.Format("VTS_{0:00}_0.IFO", vtsNum);
  69. var vtsPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, filename, StringComparison.OrdinalIgnoreCase)) ??
  70. allFiles.FirstOrDefault(i => string.Equals(i.Name, Path.ChangeExtension(filename, ".bup"), StringComparison.OrdinalIgnoreCase));
  71. if (vtsPath == null)
  72. {
  73. throw new FileNotFoundException("Unable to find VTS IFO file");
  74. }
  75. ReadVTS(vtsNum, vtsPath.FullName);
  76. }
  77. private void ReadVTS(ushort vtsNum, string vtsPath)
  78. {
  79. VTSPaths[vtsNum] = vtsPath;
  80. using (var vtsFs = new FileStream(vtsPath, FileMode.Open, FileAccess.Read, FileShare.Read))
  81. {
  82. using (var vtsRead = new BigEndianBinaryReader(vtsFs))
  83. {
  84. // Read VTS_PTT_SRPT
  85. vtsFs.Seek(0xC8, SeekOrigin.Begin);
  86. uint vtsPttSrptSecPtr = vtsRead.ReadUInt32();
  87. uint baseAddr = (vtsPttSrptSecPtr * 2048);
  88. vtsFs.Seek(baseAddr, SeekOrigin.Begin);
  89. ushort numTitles = vtsRead.ReadUInt16();
  90. vtsRead.ReadUInt16();
  91. uint endaddr = vtsRead.ReadUInt32();
  92. uint[] offsets = new uint[numTitles];
  93. for (ushort titleNum = 0; titleNum < numTitles; titleNum++)
  94. {
  95. offsets[titleNum] = vtsRead.ReadUInt32();
  96. }
  97. for (uint titleNum = 0; titleNum < numTitles; titleNum++)
  98. {
  99. uint chapNum = 1;
  100. vtsFs.Seek(baseAddr + offsets[titleNum], SeekOrigin.Begin);
  101. var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum + 1));
  102. if (t == null) continue;
  103. do
  104. {
  105. t.Chapters.Add(new Chapter(vtsRead.ReadUInt16(), vtsRead.ReadUInt16(), chapNum));
  106. if (titleNum + 1 < numTitles && vtsFs.Position == (baseAddr + offsets[titleNum + 1])) break;
  107. chapNum++;
  108. }
  109. while (vtsFs.Position < (baseAddr + endaddr));
  110. }
  111. // Read VTS_PGCI
  112. vtsFs.Seek(0xCC, SeekOrigin.Begin);
  113. uint vtsPgciSecPtr = vtsRead.ReadUInt32();
  114. vtsFs.Seek(vtsPgciSecPtr * 2048, SeekOrigin.Begin);
  115. long startByte = vtsFs.Position;
  116. ushort numPgcs = vtsRead.ReadUInt16();
  117. vtsFs.Seek(6, SeekOrigin.Current);
  118. for (ushort pgcNum = 1; pgcNum <= numPgcs; pgcNum++)
  119. {
  120. byte pgcCat = vtsRead.ReadByte();
  121. bool entryPgc = (pgcCat & 0x80) != 0;
  122. uint titleNum = (uint)(pgcCat & 0x7F);
  123. vtsFs.Seek(3, SeekOrigin.Current);
  124. uint vtsPgcOffset = vtsRead.ReadUInt32();
  125. var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum));
  126. if (t != null) t.AddPgc(vtsRead, startByte + vtsPgcOffset, entryPgc, pgcNum);
  127. }
  128. }
  129. }
  130. }
  131. }
  132. }