using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; namespace Nikse.SubtitleEdit.Core.SubtitleFormats { public class UnknownSubtitle70 : SubtitleFormat { private static readonly Regex RegexMicroDvdLine = new Regex(@"^\[-?\d+\]\[-?\d+\].*$", RegexOptions.Compiled); public string Errors { get; private set; } private StringBuilder _errors; private int _lineNumber; public override string Extension => ".txt"; public override string Name => "Unknown 70"; public override bool IsTimeBased => false; public override bool IsMine(List lines, string fileName) { int totalRead = 0; int errors = 0; foreach (string line in lines) { if (string.IsNullOrWhiteSpace(line)) { continue; } if (line.Contains('[')) { string s = RemoveIllegalSpacesAndFixEmptyCodes(line); if (RegexMicroDvdLine.IsMatch(s)) { totalRead++; } else { errors++; } } else { errors++; } } Errors = null; return totalRead > errors; } private static string RemoveIllegalSpacesAndFixEmptyCodes(string line) { int index = line.IndexOf(']'); if (index >= 0 && index < line.Length) { index = line.IndexOf(']', index + 1); if (index >= 0 && index + 1 < line.Length) { var indexOfBrackets = line.IndexOf("[]", StringComparison.Ordinal); if (indexOfBrackets >= 0 && indexOfBrackets < index) { line = line.Insert(indexOfBrackets + 1, "0"); // set empty time codes to zero index++; } while (line.Contains(' ') && line.IndexOf(' ') < index) { line = line.Remove(line.IndexOf(' '), 1); index--; } } } return line; } public override string ToText(Subtitle subtitle, string title) { var sb = new StringBuilder(); foreach (Paragraph p in subtitle.Paragraphs) { sb.Append('['); sb.Append(p.StartFrame); sb.Append("]["); sb.Append(p.EndFrame); sb.Append(']'); //{y:b} is italics for single line //{Y:b} is italics for both lines var parts = p.Text.SplitToLines(); int count = 0; bool italicOn = false; bool boldOn = false; bool underlineOn = false; var lineSb = new StringBuilder(); foreach (string line in parts) { if (count > 0) { lineSb.Append('|'); } bool alreadyItalic = italicOn; bool alreadyBold = boldOn; bool alreadyUnderline = underlineOn; var pre = new StringBuilder(); string s = line; for (int i = 0; i < 5; i++) { if (alreadyItalic && i == 0) { italicOn = true; boldOn = false; underlineOn = false; pre.Append("{y:i}"); // italic single line alreadyItalic = false; } else if (s.StartsWith("", StringComparison.Ordinal)) { italicOn = true; boldOn = false; underlineOn = false; if (pre.ToString() != "{y:i}") { pre.Append("{y:i}"); // italic single line } s = s.Remove(0, 3); } else if (alreadyBold && i == 0) { italicOn = false; boldOn = true; underlineOn = false; pre.Append("{y:b}"); // bold single line } else if (s.StartsWith("", StringComparison.Ordinal)) { italicOn = false; boldOn = true; underlineOn = false; if (pre.ToString() != "{y:b}") { pre.Append("{y:b}"); // bold single line } s = s.Remove(0, 3); } else if (alreadyUnderline && i == 0) { italicOn = false; boldOn = false; underlineOn = true; pre.Append("{y:u}"); // underline single line } else if (s.StartsWith("", StringComparison.Ordinal)) { italicOn = false; boldOn = false; underlineOn = true; if (pre.ToString() != "{y:u}") { pre.Append("{y:u}"); // underline single line } s = s.Remove(0, 3); } if (s.StartsWith("'); if (end > 0) { string tag = s.Substring(0, end); if (tag.Contains(" color=", StringComparison.OrdinalIgnoreCase)) { int colorStart = tag.IndexOf(" color=", StringComparison.OrdinalIgnoreCase); int colorEnd = tag.IndexOf('"', colorStart + " color=".Length + 1); if (colorEnd > 0) { string color = tag.Substring(colorStart, colorEnd - colorStart); color = color.Remove(0, " color=".Length); color = color.Trim('"'); color = color.Trim('\''); color = color.TrimStart('#'); if (color.Length == 6) { if (s.Contains(Environment.NewLine) && s.Contains("" + Environment.NewLine, StringComparison.OrdinalIgnoreCase)) { pre.Append("{c:$" + color.Substring(4, 2) + color.Substring(2, 2) + color.Substring(0, 2) + "}"); } else { pre.Append("{C:$" + color.Substring(4, 2) + color.Substring(2, 2) + color.Substring(0, 2) + "}"); } } } } if (tag.Contains(" face=", StringComparison.OrdinalIgnoreCase)) { var faceStart = tag.IndexOf(" face=", StringComparison.OrdinalIgnoreCase); var faceEnd = tag.IndexOf('"', faceStart + " face=".Length + 1); if (faceEnd > 0) { string fontName = tag.Substring(faceStart, faceEnd - faceStart); fontName = fontName.Remove(0, " face=".Length).Trim(); fontName = fontName.Trim('"'); fontName = fontName.Trim('\''); if (fontName.Length > 0) { if (s.Contains(Environment.NewLine) && s.Contains("" + Environment.NewLine, StringComparison.OrdinalIgnoreCase)) { pre.Append("{f:" + fontName + "}"); } else { pre.Append("{F:" + fontName + "}"); } } } } if (tag.Contains(" size=", StringComparison.OrdinalIgnoreCase)) { var sizeStart = tag.IndexOf(" size=", StringComparison.OrdinalIgnoreCase); var sizeEnd = tag.IndexOf('"', sizeStart + " size=".Length + 1); if (sizeEnd > 0) { string fontSize = tag.Substring(sizeStart, sizeEnd - sizeStart); fontSize = fontSize.Remove(0, " size=".Length).Trim(); fontSize = fontSize.Trim('"'); fontSize = fontSize.Trim('\''); if (fontSize.Length > 0) { if (s.Contains(Environment.NewLine) && s.Contains("" + Environment.NewLine, StringComparison.OrdinalIgnoreCase)) { pre.Append("{s:" + fontSize + "}"); } else { pre.Append("{S:" + fontSize + "}"); } } } } s = s.Remove(0, end + 1); } } } if (s.Contains("", StringComparison.OrdinalIgnoreCase)) { italicOn = false; } if (s.Contains("", StringComparison.OrdinalIgnoreCase)) { boldOn = false; } if (s.Contains("", StringComparison.OrdinalIgnoreCase)) { underlineOn = false; } lineSb.Append(HtmlUtil.RemoveHtmlTags(pre + line)); count++; } string text = lineSb.ToString(); int noOfLines = Utilities.CountTagInText(text, '|') + 1; if (Utilities.CountTagInText(text, "{y:i}") == noOfLines && text.StartsWith("{y:i}", StringComparison.Ordinal)) { text = "{Y:i}" + text.Replace("{y:i}", string.Empty); } else if (Utilities.CountTagInText(text, "{y:b}") == noOfLines && text.StartsWith("{y:b}", StringComparison.Ordinal)) { text = "{Y:b}" + text.Replace("{y:b}", string.Empty); } else if (Utilities.CountTagInText(text, "{y:u}") == noOfLines && text.StartsWith("{y:u}", StringComparison.Ordinal)) { text = "{Y:u}" + text.Replace("{y:u}", string.Empty); } else if (Utilities.CountTagInText(text, "{y:u}{y:i}") == noOfLines && text.StartsWith("{y:u}{y:i}", StringComparison.Ordinal)) { text = "{Y:u}{Y:i}" + text.Replace("{y:u}", string.Empty).Replace("{y:i}", string.Empty); } else if (Utilities.CountTagInText(text, "{y:i}{y:u}") == noOfLines && text.StartsWith("{y:i}{y:u}", StringComparison.Ordinal)) { text = "{Y:i}{Y:u}" + text.Replace("{y:i}", string.Empty).Replace("{y:u}", string.Empty); } else if (Utilities.CountTagInText(text, "{y:i}{y:b}") == noOfLines && text.StartsWith("{y:i}{y:b}", StringComparison.Ordinal)) { text = "{Y:i}{Y:b}" + text.Replace("{y:i}", string.Empty).Replace("{y:b}", string.Empty); } else if (Utilities.CountTagInText(text, "{y:b}{y:i}") == noOfLines && text.StartsWith("{y:b}{y:i}", StringComparison.Ordinal)) { text = "{Y:b}{Y:i}" + text.Replace("{y:i}", string.Empty).Replace("{y:b}", string.Empty); } else if (Utilities.CountTagInText(text, "{y:b}{y:u}") == noOfLines && text.StartsWith("{y:b}{y:u}", StringComparison.Ordinal)) { text = "{Y:b}{Y:u}" + text.Replace("{y:b}", string.Empty).Replace("{y:u}", string.Empty); } else if (Utilities.CountTagInText(text, "{y:u}{y:b}") == noOfLines && text.StartsWith("{y:u}{y:b}", StringComparison.Ordinal)) { text = "{Y:u}{Y:b}" + text.Replace("{y:u}", string.Empty).Replace("{y:b}", string.Empty); } if (Utilities.CountTagInText(text, "{y:i}") == 1 && noOfLines == 1) { text = text.Replace("{y:i}", "{Y:i}"); } if (Utilities.CountTagInText(text, "{y:b}") == 1 && noOfLines == 1) { text = text.Replace("{y:b}", "{Y:b}"); } if (Utilities.CountTagInText(text, "{y:u}") == 1 && noOfLines == 1) { text = text.Replace("{y:u}", "{Y:u}"); } sb.AppendLine(HtmlUtil.RemoveHtmlTags(text)); } return sb.ToString().Trim(); } public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) { _errorCount = 0; _errors = new StringBuilder(); Errors = null; _lineNumber = 0; foreach (string line in lines) { _lineNumber++; string s = RemoveIllegalSpacesAndFixEmptyCodes(line); if (RegexMicroDvdLine.IsMatch(s)) { try { int textIndex = GetTextStartIndex(s); if (textIndex < s.Length) { string text = s.Substring(textIndex); string temp = s.Substring(0, textIndex - 1); string[] frames = temp.Replace("][", ":").Replace("[", string.Empty).Replace("]", string.Empty).Split(':'); int startFrame = int.Parse(frames[0]); int endFrame = int.Parse(frames[1]); string post = string.Empty; string[] parts = text.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); int count = 0; var lineSb = new StringBuilder(); foreach (string s2 in parts) { if (count > 0) { lineSb.AppendLine(); } s = s2.Trim(); var pre = new StringBuilder(); string singlePost = string.Empty; for (int i = 0; i < 5; i++) { if (s.StartsWith("{Y:i}", StringComparison.Ordinal)) { s = s.Remove(0, 5); pre.Append(""); post = "" + post; } else if (s.StartsWith("{Y:b}", StringComparison.Ordinal)) { s = s.Remove(0, 5); pre.Append(""); post = "" + post; } else if (s.StartsWith("{Y:u}", StringComparison.Ordinal)) { s = s.Remove(0, 5); pre.Append(""); post = "" + post; } else if (s.StartsWith("{y:i}", StringComparison.Ordinal)) { s = s.Remove(0, 5); singlePost = "" + singlePost; pre.Append(""); } else if (s.StartsWith("{y:b}", StringComparison.Ordinal)) { s = s.Remove(0, 5); singlePost = "" + singlePost; pre.Append(""); } else if (s.StartsWith("{y:u}", StringComparison.Ordinal)) { s = s.Remove(0, 5); singlePost = "" + singlePost; pre.Append(""); } else if (s.StartsWith("{y:b,u}", StringComparison.Ordinal)) { s = s.Remove(0, 7); singlePost = "" + singlePost; pre.Append(""); } else if (s.StartsWith("{y:u,b}", StringComparison.Ordinal)) { s = s.Remove(0, 7); singlePost = "" + singlePost; pre.Append(""); } else if (s.StartsWith("{y:b,i}", StringComparison.Ordinal)) { s = s.Remove(0, 7); singlePost = "" + singlePost; pre.Append(""); } else if (s.StartsWith("{y:i,b}", StringComparison.Ordinal)) { s = s.Remove(0, 7); singlePost = "" + singlePost; pre.Append(""); } else if (s.StartsWith("{y:i,u}", StringComparison.Ordinal)) { s = s.Remove(0, 7); singlePost = "" + singlePost; pre.Append(""); } else if (s.StartsWith("{y:u,i}", StringComparison.Ordinal)) { s = s.Remove(0, 7); singlePost = "" + singlePost; pre.Append("<>"); } else if (s.StartsWith("{Y:b,u}", StringComparison.Ordinal) || s.StartsWith("{Y:u,b}", StringComparison.Ordinal)) { s = s.Remove(0, 7); pre.Append(""); post = "" + post; } else if (s.StartsWith("{Y:b,i}", StringComparison.Ordinal) || s.StartsWith("{Y:i,b}", StringComparison.Ordinal)) { s = s.Remove(0, 7); pre.Append(""); post = "" + post; } else if (s.StartsWith("{Y:i,u}", StringComparison.Ordinal) || s.StartsWith("{Y:u,i}", StringComparison.Ordinal)) { s = s.Remove(0, 7); pre.Append(""); post = "" + post; } else if (s.Contains("{c:$")) { int start = s.IndexOf("{c:$", StringComparison.Ordinal); int end = s.IndexOf('}', start); if (end > start) { string tag = s.Substring(start, end - start); tag = tag.Remove(0, 4); if (tag.Length == 6) { pre.Append(""); s = s.Remove(start, end - start + 1); singlePost = "" + singlePost; } } } else if (s.Contains("{C:$")) // uppercase=all lines { int start = s.IndexOf("{C:$", StringComparison.Ordinal); int end = s.IndexOf('}', start); if (end > start) { string tag = s.Substring(start, end - start); tag = tag.Remove(0, 4); if (tag.Length == 6) { pre.Append(""); s = s.Remove(start, end - start + 1); post = "" + post; } } } else if (s.Contains("{f:")) { int start = s.IndexOf("{f:", StringComparison.Ordinal); int end = s.IndexOf('}', start); if (end > start) { string tag = s.Substring(start, end - start); tag = tag.Remove(0, 3).Trim(); if (tag.Length > 0) { pre.Append(""); s = s.Remove(start, end - start + 1); singlePost = "" + singlePost; } } } else if (s.Contains("{F:")) // uppercase=all lines { int start = s.IndexOf("{F:", StringComparison.Ordinal); int end = s.IndexOf('}', start); if (end > start) { string tag = s.Substring(start, end - start); tag = tag.Remove(0, 3).Trim(); if (tag.Length > 0) { pre.Append(""); s = s.Remove(start, end - start + 1); post = "" + post; } } } else if (s.Contains("{s:")) { int start = s.IndexOf("{s:", StringComparison.Ordinal); int end = s.IndexOf('}', start); if (end > start) { string tag = s.Substring(start, end - start); tag = tag.Remove(0, 3).Trim(); if (tag.Length > 0) { pre.Append(""); s = s.Remove(start, end - start + 1); singlePost = "" + singlePost; } } } else if (s.Contains("{S:")) // uppercase=all lines { int start = s.IndexOf("{S:", StringComparison.Ordinal); int end = s.IndexOf('}', start); if (end > start) { string tag = s.Substring(start, end - start); tag = tag.Remove(0, 3).Trim(); if (tag.Length > 0) { pre.Append(""); s = s.Remove(start, end - start + 1); post = "" + post; } } } } s = s.Replace("{Y:i}", string.Empty).Replace("{y:i}", string.Empty); s = s.Replace("{Y:b}", string.Empty).Replace("{y:b}", string.Empty); s = s.Replace("{Y:u}", string.Empty).Replace("{y:u}", string.Empty); lineSb.Append(pre + s + singlePost); count++; } text = lineSb + post; subtitle.Paragraphs.Add(new Paragraph(startFrame, endFrame, text)); } } catch { _errorCount++; if (_errors.Length < 2000) { _errors.AppendLine(string.Format(Configuration.Settings.Language.Main.LineNumberXErrorReadingFromSourceLineY, _lineNumber, line)); } } } else { _errorCount++; if (_errors.Length < 2000) { _errors.AppendLine(string.Format(Configuration.Settings.Language.Main.LineNumberXErrorReadingFromSourceLineY, _lineNumber, line)); } } } int j = 0; foreach (Paragraph p in subtitle.Paragraphs) { Paragraph previous = subtitle.GetParagraphOrDefault(j - 1); if (p.StartFrame == 0 && previous != null) { p.StartFrame = previous.EndFrame + 1; } if (p.EndFrame == 0) { p.EndFrame = p.StartFrame; } j++; } subtitle.Renumber(); Errors = _errors.ToString(); } private static int GetTextStartIndex(string line) { int i = 0; int tagCount = 0; while (i < line.Length && tagCount < 4) { if (line[i] == '[' || line[i] == ']') { tagCount++; } i++; } return i; } } }