PageRenderTime 161ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Logic/SubtitleFormats/CheetahCaption.cs

https://gitlab.com/minaz922/subtitleedit
C# | 319 lines | 291 code | 19 blank | 9 comment | 16 complexity | 6e5ff623102a27333e1beeed0be87138 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Text;
  5. namespace Nikse.SubtitleEdit.Logic.SubtitleFormats
  6. {
  7. public class CheetahCaption : SubtitleFormat
  8. {
  9. static readonly List<int> LatinCodes = new List<int> {
  10. 0x81, // ♪
  11. 0x82, // á
  12. 0x83, // é
  13. 0x84, // í
  14. 0x85, // ó
  15. 0x86, // ú
  16. 0x87, // â
  17. 0x88, // ê
  18. 0x89, // î
  19. 0x8A, // ô
  20. 0x8B, // û
  21. 0x8C, // à
  22. 0x8D, // è
  23. 0x8E, // Ñ
  24. 0x8F, // ñ
  25. 0x90, // ç
  26. 0x91, // ¢
  27. 0x92, // £
  28. 0x93, // ¿
  29. 0x94, // ½
  30. 0x95, // ®
  31. };
  32. static readonly List<string> LatinLetters = new List<string> {
  33. "♪",
  34. "á",
  35. "é",
  36. "í",
  37. "ó",
  38. "ú",
  39. "â",
  40. "ê",
  41. "î",
  42. "ô",
  43. "û",
  44. "à",
  45. "è",
  46. "Ñ",
  47. "ñ",
  48. "ç",
  49. "¢",
  50. "£",
  51. "¿",
  52. "½",
  53. "®",
  54. };
  55. public override string Extension
  56. {
  57. get { return ".cap"; }
  58. }
  59. public override string Name
  60. {
  61. get { return "Cheetah Caption"; }
  62. }
  63. public override bool IsTimeBased
  64. {
  65. get { return true; }
  66. }
  67. public void Save(string fileName, Subtitle subtitle)
  68. {
  69. var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
  70. byte[] buffer = new byte[] { 0xEA, 0x22, 1, 0 }; // header
  71. fs.Write(buffer, 0, buffer.Length);
  72. int numberOfLines = subtitle.Paragraphs.Count;
  73. fs.WriteByte((byte)(numberOfLines % 256)); // paragraphs - low byte
  74. fs.WriteByte((byte)(numberOfLines / 256)); // paragraphs - high byte
  75. buffer = new byte[] { 9, 0xA8, 0xAF, 0x4F }; // ?
  76. fs.Write(buffer, 0, buffer.Length);
  77. for (int i = 0; i < 118; i++)
  78. fs.WriteByte(0);
  79. // paragraphs
  80. foreach (Paragraph p in subtitle.Paragraphs)
  81. {
  82. string text = p.Text;
  83. //styles + ?
  84. buffer = new byte[] {
  85. 0x12,
  86. 1,
  87. 0,
  88. 0,
  89. 0,
  90. 0,
  91. 3, // justification, 1=left, 2=right, 3=center
  92. 0xF, //horizontal position, 1=top, F=bottom
  93. 0x10, //horizontal position, 3=left, 0x10=center, 0x19=right
  94. };
  95. //Normal : 12 01 00 00 00 00 03 0F 10
  96. //Right-top : 12 01 00 00 00 00 03 01 1C
  97. //Top : 12 01 00 00 00 00 03 01 10
  98. //Left-top : 12 01 00 00 00 00 03 01 05
  99. //Left : 12 01 00 00 00 00 03 0F 0A
  100. //Right : 12 01 00 00 00 00 03 0F 1E
  101. //Left : 12 03 00 00 00 00 03 0F 07
  102. if (text.StartsWith("{\\an7}") || text.StartsWith("{\\an8}") || text.StartsWith("{\\an9}"))
  103. {
  104. buffer[7] = 1; // align top (vertial)
  105. }
  106. else if (text.StartsWith("{\\an4}") || text.StartsWith("{\\an5}") || text.StartsWith("{\\an6}"))
  107. {
  108. buffer[7] = 8; // center (vertical)
  109. }
  110. if (text.StartsWith("{\\an7}") || text.StartsWith("{\\an4}") || text.StartsWith("{\\an1}"))
  111. {
  112. buffer[8] = 2; // align left (horizontal)
  113. }
  114. else if (text.StartsWith("{\\an9}") || text.StartsWith("{\\an6}") || text.StartsWith("{\\an3}"))
  115. {
  116. buffer[8] = 0x1e; // align right (vertical)
  117. }
  118. int startTag = text.IndexOf("}");
  119. if (text.StartsWith("{\\") && startTag > 0 && startTag < 10)
  120. {
  121. text = text.Remove(0, startTag + 1);
  122. }
  123. var textBytes = new List<byte>();
  124. bool italic = false;
  125. if (p.Text.StartsWith("<i>") && p.Text.EndsWith("</i>"))
  126. italic = true;
  127. text = Utilities.RemoveHtmlTags(text);
  128. int j = 0;
  129. if (italic)
  130. textBytes.Add(0xd0);
  131. while (j < text.Length)
  132. {
  133. if (text.Substring(j).StartsWith(Environment.NewLine))
  134. {
  135. j += Environment.NewLine.Length;
  136. textBytes.Add(0);
  137. textBytes.Add(0);
  138. textBytes.Add(0);
  139. textBytes.Add(0);
  140. if (italic)
  141. textBytes.Add(0xd0);
  142. }
  143. else
  144. {
  145. int idx = LatinLetters.IndexOf(text.Substring(j, 1));
  146. if (idx >= 0)
  147. textBytes.Add((byte)LatinCodes[idx]);
  148. else
  149. textBytes.Add(Encoding.GetEncoding(1252).GetBytes(text.Substring(j, 1))[0]);
  150. j++;
  151. }
  152. }
  153. int length = textBytes.Count + 20;
  154. long end = fs.Position + length;
  155. fs.WriteByte((byte)(length));
  156. fs.WriteByte(0x62); // ?
  157. WriteTime(fs, p.StartTime);
  158. WriteTime(fs, p.EndTime);
  159. fs.Write(buffer, 0, buffer.Length); // styles
  160. foreach (byte b in textBytes) // text
  161. fs.WriteByte(b);
  162. while (end > fs.Position)
  163. fs.WriteByte(0);
  164. }
  165. fs.Close();
  166. }
  167. private void WriteTime(FileStream fs, TimeCode timeCode)
  168. {
  169. fs.WriteByte((byte)timeCode.Hours);
  170. fs.WriteByte((byte)timeCode.Minutes);
  171. fs.WriteByte((byte)timeCode.Seconds);
  172. fs.WriteByte((byte)MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds));
  173. }
  174. public override bool IsMine(List<string> lines, string fileName)
  175. {
  176. if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
  177. {
  178. var fi = new FileInfo(fileName);
  179. if (fi.Length >= 200 && fi.Length < 1024000) // not too small or too big
  180. {
  181. if (fileName.ToLower().EndsWith(".cap"))
  182. {
  183. byte[] buffer = File.ReadAllBytes(fileName);
  184. for (int i = 0; i < buffer.Length - 20; i++)
  185. {
  186. if (buffer[i + 0] == 0xEA &&
  187. buffer[i + 1] == 0x22 &&
  188. buffer[i + 2] <= 3)
  189. return true;
  190. }
  191. }
  192. }
  193. }
  194. return false;
  195. }
  196. public override string ToText(Subtitle subtitle, string title)
  197. {
  198. return "Not supported!";
  199. }
  200. private TimeCode DecodeTimeStamp(byte[] buffer, int index)
  201. {
  202. return new TimeCode(buffer[index], buffer[index + 1], buffer[index + 2], FramesToMillisecondsMax999(buffer[index + 3]));
  203. }
  204. public override void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName)
  205. {
  206. subtitle.Paragraphs.Clear();
  207. subtitle.Header = null;
  208. byte[] buffer = File.ReadAllBytes(fileName);
  209. int i = 128;
  210. Paragraph last = null;
  211. while (i < buffer.Length - 20)
  212. {
  213. var p = new Paragraph();
  214. int length = buffer[i];
  215. int textLength = length - 20;
  216. int start = 19;
  217. for (int j=0; j<4; j++)
  218. {
  219. if (buffer[i + start - 1] > 0x10)
  220. {
  221. start--;
  222. textLength++;
  223. }
  224. }
  225. if (textLength > 0 && buffer.Length >= i+textLength)
  226. {
  227. byte firstByte = buffer[i + 1];
  228. p.StartTime = DecodeTimeStamp(buffer, i + 2);
  229. if (last != null && last.EndTime.TotalMilliseconds > p.StartTime.TotalMilliseconds)
  230. last.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds - Configuration.Settings.General.MininumMillisecondsBetweenLines;
  231. p.EndTime = DecodeTimeStamp(buffer, i + 6);
  232. var sb = new StringBuilder();
  233. int j = 0;
  234. bool italics = false;
  235. while (j < textLength)
  236. {
  237. int index = i + start + j;
  238. if (buffer[index] == 0)
  239. {
  240. if (italics)
  241. sb.Append("</i>");
  242. italics = false;
  243. if (!sb.ToString().EndsWith(Environment.NewLine))
  244. sb.AppendLine();
  245. }
  246. else if (LatinCodes.Contains(buffer[index]))
  247. {
  248. sb.Append(LatinLetters[LatinCodes.IndexOf(buffer[index])]);
  249. }
  250. else if (buffer[index] >= 0xC0 || buffer[index] <= 0x14) // codes/styles?
  251. {
  252. if (buffer[index] == 0xd0) // italics
  253. {
  254. italics = true;
  255. sb.Append("<i>");
  256. }
  257. }
  258. else
  259. {
  260. sb.Append(Encoding.GetEncoding(1252).GetString(buffer, index, 1));
  261. }
  262. j++;
  263. }
  264. if (italics)
  265. sb.Append("</i>");
  266. p.Text = sb.ToString();
  267. p.Text = p.Text.Replace("</i>" + Environment.NewLine + "<i>", Environment.NewLine);
  268. subtitle.Paragraphs.Add(p);
  269. last = p;
  270. }
  271. if (length == 0)
  272. length++;
  273. i += length;
  274. }
  275. if (last != null && last.Duration.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds)
  276. last.EndTime.TotalMilliseconds = last.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(last.Text);
  277. subtitle.Renumber(1);
  278. }
  279. }
  280. }