2016-02-08 21:11:03 +01:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
|
|
|
|
namespace Nikse.SubtitleEdit.Core.SubtitleFormats
|
|
|
|
|
{
|
|
|
|
|
public class CaptionsInc : SubtitleFormat
|
|
|
|
|
{
|
|
|
|
|
public override string Extension
|
|
|
|
|
{
|
|
|
|
|
get { return ".cin"; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string Name
|
|
|
|
|
{
|
|
|
|
|
get { return "Caption Inc"; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override bool IsTimeBased
|
|
|
|
|
{
|
|
|
|
|
get { return true; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void Save(string fileName, Subtitle subtitle)
|
|
|
|
|
{
|
|
|
|
|
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
|
|
|
|
|
{
|
|
|
|
|
string name = Path.GetFileNameWithoutExtension(fileName) ?? string.Empty;
|
|
|
|
|
byte[] buffer = Encoding.ASCII.GetBytes(name);
|
|
|
|
|
for (int i = 0; i < buffer.Length && i < 8; i++)
|
|
|
|
|
fs.WriteByte(buffer[i]);
|
|
|
|
|
while (fs.Length < 8)
|
|
|
|
|
fs.WriteByte(0x20);
|
|
|
|
|
|
|
|
|
|
WriteTime(fs, subtitle.Paragraphs[0].StartTime, false); // first start time
|
|
|
|
|
WriteTime(fs, subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime, false); // last end time
|
|
|
|
|
|
|
|
|
|
buffer = Encoding.ASCII.GetBytes("Generic Unknown Unknown \"\" Unknown Unknown Unknown ");
|
|
|
|
|
fs.Write(buffer, 0, buffer.Length);
|
|
|
|
|
|
|
|
|
|
// paragraphs
|
|
|
|
|
foreach (Paragraph p in subtitle.Paragraphs)
|
|
|
|
|
{
|
|
|
|
|
buffer = new byte[] { 0x0D, 0x0A, 0xFE }; // header
|
|
|
|
|
fs.Write(buffer, 0, buffer.Length);
|
|
|
|
|
|
|
|
|
|
// styles
|
|
|
|
|
var text = new List<byte> { 0x14, 0x20, 0x14, 0x2E, 0x14, 0x54, 0x17 };
|
|
|
|
|
int noOfLines = Utilities.GetNumberOfLines(p.Text);
|
|
|
|
|
if (noOfLines == 1)
|
|
|
|
|
text.Add(0x22); // 1 line?
|
|
|
|
|
else
|
|
|
|
|
text.Add(0x21); // 2 lines?
|
|
|
|
|
|
|
|
|
|
var lines = p.Text.Split(Utilities.NewLineChars, StringSplitOptions.None);
|
|
|
|
|
foreach (string line in lines)
|
|
|
|
|
{
|
|
|
|
|
foreach (char ch in line)
|
|
|
|
|
text.Add(Encoding.GetEncoding(1252).GetBytes(new[] { ch })[0]);
|
|
|
|
|
|
|
|
|
|
// new line
|
|
|
|
|
//text.Add(0x14); // y? 0x14 was lower!? 0x17 is higher??? 12=little top 11=top, 13=most buttom?, 15=little over middle
|
|
|
|
|
//text.Add(0x72);
|
|
|
|
|
|
|
|
|
|
text.Add(0x14);
|
|
|
|
|
text.Add(0x74);
|
|
|
|
|
//text.Add(0x17);
|
|
|
|
|
//text.Add(0x21);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// codes+text length
|
|
|
|
|
buffer = Encoding.ASCII.GetBytes(string.Format("{0:000}", text.Count));
|
|
|
|
|
fs.Write(buffer, 0, buffer.Length);
|
|
|
|
|
|
|
|
|
|
WriteTime(fs, p.StartTime, true);
|
|
|
|
|
|
|
|
|
|
// write codes + text
|
|
|
|
|
foreach (byte b in text)
|
|
|
|
|
fs.WriteByte(b);
|
|
|
|
|
|
|
|
|
|
buffer = new byte[] { 0x14, 0x2F, 0x0D, 0x0A, 0xFE, 0x30, 0x30, 0x32, 0x30 };
|
|
|
|
|
fs.Write(buffer, 0, buffer.Length);
|
|
|
|
|
WriteTime(fs, p.EndTime, true);
|
|
|
|
|
//buffer = new byte[] { 0x14, 0x2C };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void WriteTime(FileStream fs, TimeCode timeCode, bool addEndBytes)
|
|
|
|
|
{
|
|
|
|
|
var time = timeCode.ToHHMMSSFF();
|
|
|
|
|
var buffer = Encoding.ASCII.GetBytes(time);
|
|
|
|
|
fs.Write(buffer, 0, buffer.Length);
|
|
|
|
|
if (addEndBytes)
|
|
|
|
|
{
|
|
|
|
|
fs.WriteByte(0xd);
|
|
|
|
|
fs.WriteByte(0xa);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override bool IsMine(List<string> lines, string fileName)
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
|
|
|
|
|
{
|
|
|
|
|
if (!fileName.EndsWith(".cin", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
var sub = new Subtitle();
|
|
|
|
|
LoadSubtitle(sub, lines, fileName);
|
|
|
|
|
return sub.Paragraphs.Count > 0;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string ToText(Subtitle subtitle, string title)
|
|
|
|
|
{
|
|
|
|
|
return "Not supported!";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static TimeCode DecodeTimestamp(string timeCode)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return new TimeCode(int.Parse(timeCode.Substring(0, 2)), int.Parse(timeCode.Substring(2, 2)), int.Parse(timeCode.Substring(4, 2)), FramesToMillisecondsMax999(int.Parse(timeCode.Substring(6, 2))));
|
|
|
|
|
}
|
|
|
|
|
catch (Exception exception)
|
|
|
|
|
{
|
|
|
|
|
System.Diagnostics.Debug.WriteLine(exception.Message);
|
2016-10-29 16:56:39 +02:00
|
|
|
|
return new TimeCode();
|
2016-02-08 21:11:03 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName)
|
|
|
|
|
{
|
|
|
|
|
subtitle.Paragraphs.Clear();
|
|
|
|
|
subtitle.Header = null;
|
|
|
|
|
byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
|
|
|
|
|
|
|
|
|
|
int i = 256;
|
|
|
|
|
Paragraph last = null;
|
|
|
|
|
while (i < buffer.Length - 20)
|
|
|
|
|
{
|
|
|
|
|
var p = new Paragraph();
|
|
|
|
|
|
|
|
|
|
while (buffer[i] != 0xfe && i < buffer.Length - 20)
|
|
|
|
|
{
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
if (buffer[i] == 0xfe)
|
|
|
|
|
{
|
|
|
|
|
i += 4;
|
|
|
|
|
string startTime = Encoding.ASCII.GetString(buffer, i, 8);
|
|
|
|
|
i += 8;
|
|
|
|
|
if (Utilities.IsInteger(startTime))
|
|
|
|
|
{
|
|
|
|
|
p.StartTime = DecodeTimestamp(startTime);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool startFound = false;
|
|
|
|
|
bool textEnd = false;
|
|
|
|
|
while (!startFound && !textEnd && i < buffer.Length - 20)
|
|
|
|
|
{
|
|
|
|
|
bool skip = false;
|
|
|
|
|
if (buffer[i] == 0x0d)
|
|
|
|
|
i++;
|
|
|
|
|
else if (buffer[i] == 0x0a)
|
|
|
|
|
skip = true;
|
|
|
|
|
else if (buffer[i] == 0x14 && buffer[i + 1] == 0x2c) // text end
|
|
|
|
|
textEnd = true;
|
|
|
|
|
else if (buffer[i] <= 0x20) // text start
|
|
|
|
|
i++;
|
|
|
|
|
else
|
|
|
|
|
startFound = true;
|
|
|
|
|
|
|
|
|
|
if (!skip)
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
if (!textEnd)
|
|
|
|
|
{
|
|
|
|
|
i -= 2;
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
while (!textEnd && i < buffer.Length - 20)
|
|
|
|
|
{
|
|
|
|
|
if (buffer[i] == 0x14 && buffer[i + 1] == 0x2c) // text end
|
|
|
|
|
textEnd = true;
|
|
|
|
|
else if (buffer[i] == 0xd && buffer[i + 1] == 0xa) // text end
|
|
|
|
|
textEnd = true;
|
|
|
|
|
else if (buffer[i] <= 0x17)
|
|
|
|
|
{
|
|
|
|
|
if (!sb.ToString().EndsWith(Environment.NewLine))
|
|
|
|
|
sb.Append(Environment.NewLine);
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
sb.Append(Encoding.GetEncoding(1252).GetString(buffer, i, 1));
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
i++;
|
|
|
|
|
if (sb.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
string text = sb.ToString().Trim();
|
|
|
|
|
p.Text = text;
|
|
|
|
|
subtitle.Paragraphs.Add(p);
|
|
|
|
|
last = p;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (buffer[i] == 0xFE)
|
|
|
|
|
{
|
|
|
|
|
string endTime = Encoding.ASCII.GetString(buffer, i + 4, 8);
|
|
|
|
|
if (Utilities.IsInteger(endTime))
|
|
|
|
|
{
|
|
|
|
|
p.EndTime = DecodeTimestamp(endTime);
|
|
|
|
|
}
|
|
|
|
|
while (i < buffer.Length && buffer[i] != 0xa)
|
|
|
|
|
i++;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
while (i < buffer.Length && buffer[i] != 0xa)
|
|
|
|
|
i++;
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
if (buffer[i] == 0xfe)
|
|
|
|
|
{
|
|
|
|
|
string endTime = Encoding.ASCII.GetString(buffer, i + 4, 8);
|
|
|
|
|
if (Utilities.IsInteger(endTime))
|
|
|
|
|
{
|
|
|
|
|
p.EndTime = DecodeTimestamp(endTime);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (last != null && last.Duration.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds)
|
|
|
|
|
last.EndTime.TotalMilliseconds = last.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(last.Text);
|
|
|
|
|
|
|
|
|
|
subtitle.Renumber();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|