diff --git a/ChangeLog.md b/ChangeLog.md index 2bff983..2853263 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -352,8 +352,12 @@ - 完整功能的UHD章节读取 - 全新喷气脑袋风格图标 -## [2.33.33.3] +## [2.33.33.31] - 错误修正与效能提升 - 增加英语界面 - 修正部分高分屏的问题 - 修正DVD章节读取 +- +## [2.33.33.32] +- 改进DVD章节读取 +- 输出无BOM的QPF文件 diff --git a/Time_Shift/ChapterData/IData.cs b/Time_Shift/ChapterData/IData.cs index d1ac3ac..1257eef 100644 --- a/Time_Shift/ChapterData/IData.cs +++ b/Time_Shift/ChapterData/IData.cs @@ -1,5 +1,4 @@ -using System; -using ChapterTool.Util; +using ChapterTool.Util; namespace ChapterTool.ChapterData { diff --git a/Time_Shift/Forms/Form1.cs b/Time_Shift/Forms/Form1.cs index 2a7f2c6..48be52c 100644 --- a/Time_Shift/Forms/Form1.cs +++ b/Time_Shift/Forms/Form1.cs @@ -900,7 +900,8 @@ private void SaveFile(SaveTypeEnum saveType) _info.SaveXml(savePath, string.IsNullOrWhiteSpace(key) ? "" : LanguageSelectionContainer.Languages[key], AutoGenName); break; case SaveTypeEnum.QPF: - _info.GetQpfile().SaveAs(savePath); + // Write qpf file without bom + _info.GetQpfile().SaveAs(savePath, false); break; case SaveTypeEnum.TimeCodes: _info.GetTimecodes().SaveAs(savePath); diff --git a/Time_Shift/Properties/AssemblyInfo.cs b/Time_Shift/Properties/AssemblyInfo.cs index 44c0fe1..833cb5d 100644 --- a/Time_Shift/Properties/AssemblyInfo.cs +++ b/Time_Shift/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, // 方法是按如下所示使用“*”: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.33.33.31")] -[assembly: AssemblyFileVersion("2.33.33.31")] +[assembly: AssemblyVersion("2.33.33.32")] +[assembly: AssemblyFileVersion("2.33.33.32")] diff --git a/Time_Shift/Util/ChapterData/IfoData.cs b/Time_Shift/Util/ChapterData/IfoData.cs index de253a6..d71956b 100644 --- a/Time_Shift/Util/ChapterData/IfoData.cs +++ b/Time_Shift/Util/ChapterData/IfoData.cs @@ -56,9 +56,9 @@ private static ChapterInfo GetChapterInfo(string location, int titleSetNum) pgc.Title = pgc.SourceName = $"{fileName.Substring(0, barIndex)}_{titleSetNum}"; } - pgc.Chapters = GetChapters(location, titleSetNum, out IfoTimeSpan duration, out decimal fps); + pgc.Chapters = GetChapters(location, titleSetNum, out var duration, out var isNTSC); pgc.Duration = duration; - pgc.FramesPerSecond = fps; + pgc.FramesPerSecond = isNTSC ? 30000M / 1001 : 25; if (pgc.Duration.TotalSeconds < 10) pgc = null; @@ -66,22 +66,21 @@ private static ChapterInfo GetChapterInfo(string location, int titleSetNum) return pgc; } - private static List GetChapters(string ifoFile, int programChain, out IfoTimeSpan duration, out decimal fps) + private static List GetChapters(string ifoFile, int programChain, out IfoTimeSpan duration, out bool isNTSC) { var chapters = new List(); duration = IfoTimeSpan.Zero; - fps = 0; + isNTSC = true; var stream = new FileStream(ifoFile, FileMode.Open, FileAccess.Read, FileShare.Read); var pcgItPosition = stream.GetPCGIP_Position(); var programChainPrograms = -1; var programTime = TimeSpan.Zero; - decimal fpsLocal; if (programChain >= 0) { var chainOffset = stream.GetChainOffset(pcgItPosition, programChain); - programTime = stream.ReadTimeSpan(pcgItPosition, chainOffset, out fpsLocal) ?? TimeSpan.Zero; + //programTime = stream.ReadTimeSpan(pcgItPosition, chainOffset, out _) ?? TimeSpan.Zero; programChainPrograms = stream.GetNumberOfPrograms(pcgItPosition, chainOffset); } else @@ -90,7 +89,7 @@ private static List GetChapters(string ifoFile, int programChain, out I for (var curChain = 1; curChain <= programChains; curChain++) { var chainOffset = stream.GetChainOffset(pcgItPosition, curChain); - var time = stream.ReadTimeSpan(pcgItPosition, chainOffset, out fpsLocal); + var time = stream.ReadTimeSpan(pcgItPosition, chainOffset, out _); if (time == null) break; if (time.Value <= programTime) continue; @@ -122,7 +121,7 @@ private static List GetChapters(string ifoFile, int programChain, out I if (cellType == 0x00 || cellType == 0x01) { bytes = stream.GetFileBlock(((pcgItPosition + longestChainOffset) + cellStart) + 4, 4); - var ret = IfoParser.ReadTimeSpan(bytes, out fps) ?? IfoTimeSpan.Zero; + var ret = IfoParser.ReadTimeSpan(bytes, out isNTSC) ?? IfoTimeSpan.Zero; totalTime.IsNTSC = ret.IsNTSC; totalTime += ret; } @@ -140,72 +139,115 @@ private static List GetChapters(string ifoFile, int programChain, out I public struct IfoTimeSpan { - public int TotalSeconds { get; set; } - public int Frames { get; set; } + public long TotalFrames { get; set; } public bool IsNTSC { get; set; } public int RawFrameRate => IsNTSC ? 30 : 25; private decimal TimeFrameRate => IsNTSC ? 30000M / 1001 : 25; - public int TotalFrames => TotalSeconds * RawFrameRate + Frames; - public int Hours => (int) Math.Round((TotalSeconds + Frames / TimeFrameRate) / 3600); - public int Minutes => (int)Math.Round((TotalSeconds + Frames / TimeFrameRate) / 60) % 60; - public int Second => (int)Math.Round(TotalSeconds + Frames / TimeFrameRate) % 60; + public int Hours => (int) Math.Round(TotalFrames / TimeFrameRate / 3600); + public int Minutes => (int) Math.Round(TotalFrames / TimeFrameRate / 60) % 60; + public int Second => (int) Math.Round(TotalFrames / TimeFrameRate) % 60; public static readonly IfoTimeSpan Zero = new IfoTimeSpan(true); public IfoTimeSpan(bool isNTSC) { - TotalSeconds = 0; - Frames = 0; + TotalFrames = 0; IsNTSC = isNTSC; } + private IfoTimeSpan(long totalFrames, bool isNTSC) + { + IsNTSC = isNTSC; + TotalFrames = totalFrames; + } + public IfoTimeSpan(int seconds, int frames, bool isNTSC) { - TotalSeconds = seconds; - Frames = frames; IsNTSC = isNTSC; + TotalFrames = frames; + TotalFrames += seconds * RawFrameRate; } public IfoTimeSpan(int hour, int minute, int second, int frames, bool isNTSC) { - TotalSeconds = hour * 3600 + minute * 60 + second; - Frames = frames; IsNTSC = isNTSC; + TotalFrames = frames; + TotalFrames += (hour * 3600 + minute * 60 + second) * RawFrameRate; } public IfoTimeSpan(TimeSpan time, bool isNTSC) { IsNTSC = isNTSC; - TotalSeconds = (int) Math.Floor(time.TotalSeconds); - Frames = 0; - Frames = (int) Math.Round((decimal) (time.TotalSeconds - TotalSeconds) / TimeFrameRate); + TotalFrames = 0; + TotalFrames = (long) Math.Round((decimal) time.TotalSeconds / TimeFrameRate); } public static implicit operator TimeSpan(IfoTimeSpan time) { - return new TimeSpan( - (long) ((time.TotalSeconds*time.RawFrameRate + time.Frames) / time.TimeFrameRate * TimeSpan.TicksPerSecond)); + return new TimeSpan((long) Math.Round(time.TotalFrames / time.TimeFrameRate * TimeSpan.TicksPerSecond)); } - public static IfoTimeSpan operator +(IfoTimeSpan t1, IfoTimeSpan t2) + #region Operator + private static void FrameRateModeCheck(IfoTimeSpan t1, IfoTimeSpan t2) { if (t1.IsNTSC ^ t2.IsNTSC) throw new InvalidOperationException("Unmatch frames rate mode"); - return new IfoTimeSpan(t1.TotalSeconds + t2.TotalSeconds, t1.Frames + t2.Frames, t1.IsNTSC); + } + + public static IfoTimeSpan operator +(IfoTimeSpan t1, IfoTimeSpan t2) + { + FrameRateModeCheck(t1, t2); + return new IfoTimeSpan(t1.TotalFrames + t2.TotalFrames, t1.IsNTSC); } public static IfoTimeSpan operator -(IfoTimeSpan t1, IfoTimeSpan t2) { - if (t1.IsNTSC ^ t2.IsNTSC) - throw new InvalidOperationException("Unmatch frames rate mode"); - return new IfoTimeSpan(t1.TotalSeconds - t2.TotalSeconds, t1.Frames - t2.Frames, t1.IsNTSC); + FrameRateModeCheck(t1, t2); + return new IfoTimeSpan(t1.TotalFrames - t2.TotalFrames, t1.IsNTSC); + } + + public static bool operator <(IfoTimeSpan t1, IfoTimeSpan t2) + { + FrameRateModeCheck(t1, t2); + return t1.TotalFrames < t2.TotalFrames; + } + + public static bool operator >(IfoTimeSpan t1, IfoTimeSpan t2) + { + FrameRateModeCheck(t1, t2); + return t1.TotalFrames > t2.TotalFrames; + } + + public static bool operator <=(IfoTimeSpan t1, IfoTimeSpan t2) + { + FrameRateModeCheck(t1, t2); + return t1.TotalFrames <= t2.TotalFrames; + } + + public static bool operator >=(IfoTimeSpan t1, IfoTimeSpan t2) + { + FrameRateModeCheck(t1, t2); + return t1.TotalFrames >= t2.TotalFrames; + } + + public static bool operator ==(IfoTimeSpan t1, IfoTimeSpan t2) + { + FrameRateModeCheck(t1, t2); + return t1.TotalFrames == t2.TotalFrames; + } + + public static bool operator !=(IfoTimeSpan t1, IfoTimeSpan t2) + { + FrameRateModeCheck(t1, t2); + return t1.TotalFrames != t2.TotalFrames; } + #endregion public override int GetHashCode() { - return (((long) TotalSeconds << 32) | ((long) Frames << 1) | (IsNTSC ? 1L : 0L)).GetHashCode(); + return ((TotalFrames << 1) | (IsNTSC ? 1L : 0L)).GetHashCode(); } public override bool Equals(object obj) @@ -214,13 +256,13 @@ public override bool Equals(object obj) return false; if (obj.GetType() != GetType()) return false; - var time = (IfoTimeSpan)obj; - return TotalSeconds == time.TotalSeconds && TotalFrames == time.TotalFrames && IsNTSC == time.IsNTSC; + var time = (IfoTimeSpan) obj; + return TotalFrames == time.TotalFrames && IsNTSC == time.IsNTSC; } public override string ToString() { - return $"{Hours:D2}:{Minutes:D2}:{Second:D2}.{Frames} [{(IsNTSC?'N':'P')}]"; + return $"{Hours:D2}:{Minutes:D2}:{Second:D2}.{TotalFrames} [{(IsNTSC?'N':'P')}]"; } } } diff --git a/Time_Shift/Util/ChapterData/IfoParser.cs b/Time_Shift/Util/ChapterData/IfoParser.cs index 4b0ac37..12bb827 100644 --- a/Time_Shift/Util/ChapterData/IfoParser.cs +++ b/Time_Shift/Util/ChapterData/IfoParser.cs @@ -19,6 +19,7 @@ // **************************************************************************** using System; +using System.Diagnostics; using System.IO; namespace ChapterTool.Util.ChapterData @@ -63,9 +64,9 @@ internal static int GetNumberOfPrograms(this FileStream ifoStream, long pcgitPos return ifoStream.GetFileBlock((pcgitPosition + chainOffset) + 2, 1)[0]; } - internal static TimeSpan? ReadTimeSpan(this FileStream ifoStream, long pcgitPosition, uint chainOffset, out decimal fps) + internal static IfoTimeSpan? ReadTimeSpan(this FileStream ifoStream, long pcgitPosition, uint chainOffset, out bool isNTSC) { - return ReadTimeSpan(ifoStream.GetFileBlock((pcgitPosition + chainOffset) + 4, 4), out fps); + return ReadTimeSpan(ifoStream.GetFileBlock((pcgitPosition + chainOffset) + 4, 4), out isNTSC); } /// @@ -74,22 +75,27 @@ internal static int GetNumberOfPrograms(this FileStream ifoStream, long pcgitPos /// byte[2] seconds in bcd format
/// byte[3] milliseconds in bcd format (2 high bits are the frame rate) /// - /// fps of the chapter - internal static IfoTimeSpan? ReadTimeSpan(byte[] playbackBytes, out decimal fps) + /// frame rate mode of the chapter + internal static IfoTimeSpan? ReadTimeSpan(byte[] playbackBytes, out bool isNTSC) { var frames = GetFrames(playbackBytes[3]); var fpsMask = playbackBytes[3] >> 6; - fps = fpsMask == 0x01 ? 25M : fpsMask == 0x03 ? (30M / 1.001M) : 0; - var isNTSC = fpsMask == 0x03; + Debug.Assert(fpsMask == 0x01 || fpsMask == 0x03); + //var fps = fpsMask == 0x01 ? 25M : fpsMask == 0x03 ? (30M / 1.001M) : 0; + isNTSC = fpsMask == 0x03; if (frames == null) return null; try { - var hours = BcdToInt(playbackBytes[0]); - var minutes = BcdToInt(playbackBytes[1]); - var seconds = BcdToInt(playbackBytes[2]); + var hours = BcdToInt(playbackBytes[0]); + var minutes = BcdToInt(playbackBytes[1]); + var seconds = BcdToInt(playbackBytes[2]); return new IfoTimeSpan(hours, minutes, seconds, (int) frames, isNTSC); } - catch { return null; } + catch (Exception exception) + { + Logger.Log(exception.Message); + return null; + } } /// diff --git a/Time_Shift/Util/ChapterInfo.cs b/Time_Shift/Util/ChapterInfo.cs index 5306cfd..018ec08 100644 --- a/Time_Shift/Util/ChapterInfo.cs +++ b/Time_Shift/Util/ChapterInfo.cs @@ -214,7 +214,7 @@ public string GetText(bool autoGenName) return lines.ToString(); } - public string[] GetQpfile() => Chapters.Where(c => c.Time != TimeSpan.MinValue).Select(c => c.FramsInfo.ToString().Replace("*", "I").Replace("K", "I")).ToArray(); + public string[] GetQpfile() => Chapters.Where(c => c.Time != TimeSpan.MinValue).Select(c => c.FramsInfo.TrimEnd('K', '*') + "I").ToArray(); public static void Chapter2Qpfile(string ipath, string opath, double fps, string tcfile = "") { diff --git a/Time_Shift/Util/ToolKits.cs b/Time_Shift/Util/ToolKits.cs index 8eef853..adb76d1 100644 --- a/Time_Shift/Util/ToolKits.cs +++ b/Time_Shift/Util/ToolKits.cs @@ -166,11 +166,11 @@ public static void LoadColor(this Form1 window) window.TextFrontColor = ColorTranslator.FromHtml(matchesOfJson[5].Groups["hex"].Value); } - public static void SaveAs(this string[] chapter, string path) => File.WriteAllLines(path, chapter, Encoding.UTF8); + public static void SaveAs(this string[] chapter, string path, bool bom = true) => File.WriteAllLines(path, chapter, new UTF8Encoding(bom)); - public static void SaveAs(this string chapter, string path) => File.WriteAllText(path, chapter, Encoding.UTF8); + public static void SaveAs(this string chapter, string path, bool bom = true) => File.WriteAllText(path, chapter, new UTF8Encoding(bom)); - public static void SaveAs(this object chapter, string path) => File.WriteAllText(path, chapter.ToString(), Encoding.UTF8); + public static void SaveAs(this object chapter, string path, bool bom = true) => File.WriteAllText(path, chapter.ToString(), new UTF8Encoding(bom)); public static bool IsAdministrator() => new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); diff --git a/Time_Shift_Test/Util/IfoDataTests.cs b/Time_Shift_Test/Util/IfoDataTests.cs index 4163828..0a42dba 100644 --- a/Time_Shift_Test/Util/IfoDataTests.cs +++ b/Time_Shift_Test/Util/IfoDataTests.cs @@ -22,12 +22,12 @@ public void IfoDataTest() var expectResult = new[] { new { Name = "Chapter 01", Time = "00:00:00.000" }, - new { Name = "Chapter 02", Time = "00:17:42.500" }, - new { Name = "Chapter 03", Time = "00:37:14.767" }, - new { Name = "Chapter 04", Time = "00:56:24.167" }, - new { Name = "Chapter 05", Time = "01:12:36.701" }, - new { Name = "Chapter 06", Time = "01:32:26.268" }, - new { Name = "Chapter 07", Time = "01:49:06.136" } + new { Name = "Chapter 02", Time = "00:17:43.562" }, + new { Name = "Chapter 03", Time = "00:37:17.001" }, + new { Name = "Chapter 04", Time = "00:56:27.551" }, + new { Name = "Chapter 05", Time = "01:12:41.057" }, + new { Name = "Chapter 06", Time = "01:32:31.813" }, + new { Name = "Chapter 07", Time = "01:49:12.679" } }; Console.WriteLine(result[0]); diff --git a/Time_Shift_Test/Util/IfoTimeTests.cs b/Time_Shift_Test/Util/IfoTimeTests.cs index 932dc83..a619ebd 100644 --- a/Time_Shift_Test/Util/IfoTimeTests.cs +++ b/Time_Shift_Test/Util/IfoTimeTests.cs @@ -1,7 +1,5 @@ using System; -using System.Text; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using ChapterTool.Util.ChapterData; using FluentAssertions; @@ -12,24 +10,23 @@ namespace Time_Shift_Test.Util [TestClass] public class IfoTimeTests { - [TestMethod] public void TestMethod1() { var times = new[] { - new[] {0, 0, 5, 0}, new[] {0, 0, 15, 0}, - new[] {0, 1, 29, 28}, new[] {0, 0, 10, 0}, + new[] {0, 0, 5, 0}, new[] {0, 0, 15, 0}, + new[] {0, 1, 29, 28}, new[] {0, 0, 10, 0}, new[] {0, 7, 54, 16}, new[] {0, 6, 40, 16}, - new[] {0, 5, 8, 22}, new[] {0, 1, 19, 28}, - new[] {0, 0, 14, 28}, new[] {0, 0, 10, 2}, - new[] {0, 0, 6, 0}, new[] {0, 0, 5, 0}, + new[] {0, 5, 8, 22}, new[] {0, 1, 19, 28}, + new[] {0, 0, 14, 28}, new[] {0, 0, 10, 2}, + new[] {0, 0, 6, 0}, new[] {0, 0, 5, 0}, new[] {0, 2, 44, 26}, new[] {0, 1, 29, 26}, - new[] {0, 0, 10, 0}, new[] {0, 5, 35, 20}, + new[] {0, 0, 10, 0}, new[] {0, 5, 35, 20}, new[] {0, 5, 21, 20}, new[] {0, 6, 16, 18}, new[] {0, 1, 19, 28}, new[] {0, 0, 14, 28}, - new[] {0, 0, 10, 0}, new[] {0, 0, 6, 0} - }.Select(time => new IfoTimeSpan(time[0], time[1], time[2], time[3], true)).ToList(); + new[] {0, 0, 10, 0}, new[] {0, 0, 6, 0} + }.Select(time => new IfoTimeSpan(time[0], time[1], time[2], time[3], true)); var exceptedFrames = new[] { 150, 600, 3298, 3598, 17834, @@ -39,7 +36,7 @@ public void TestMethod1() 84696, 84876 }; var total = new IfoTimeSpan(true); - var frames = new List(); + var frames = new List(); foreach (var time in times) { total += time;