Dvd.cs 6.4 KB

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