mirror of
https://github.com/SubtitleEdit/subtitleedit.git
synced 2024-11-22 11:12:36 +01:00
1010 lines
41 KiB
C#
1010 lines
41 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Globalization;
|
||
using System.IO;
|
||
using System.Text;
|
||
|
||
namespace Nikse.SubtitleEdit.Core.SubtitleFormats
|
||
{
|
||
public class Cavena890 : SubtitleFormat
|
||
{
|
||
public const int LanguageIdDanish = 0x07;
|
||
public const int LanguageIdEnglish = 0x09;
|
||
public const int LanguageIdRussian = 0x56;
|
||
public const int LanguageIdArabic = 0x80;
|
||
public const int LanguageIdHebrew = 0x8f;
|
||
public const int LanguageIdChineseTraditional = 0x90;
|
||
public const int LanguageIdChineseSimplified = 0x91;
|
||
|
||
private static readonly List<int> HebrewCodes = new List<int> {
|
||
0x40, // א
|
||
0x41, // ב
|
||
0x42, // ג
|
||
0x43, // ד
|
||
0x44, // ה
|
||
0x45, // ו
|
||
0x46, // ז
|
||
0x47, // ח
|
||
0x49, // י
|
||
0x4c, // ל
|
||
0x4d, // ם
|
||
0x4e, // מ
|
||
0x4f, // ן
|
||
0x50, // נ
|
||
0x51, // ס
|
||
0x52, // ע
|
||
0x54, // פ
|
||
0x56, // צ
|
||
0x57, // ק
|
||
0x58, // ר
|
||
0x59, // ש
|
||
0x5A, // ת
|
||
0x4b, // כ
|
||
0x4a, // ך
|
||
0x48, // ט
|
||
0x53, // ף
|
||
0x55, // ץ
|
||
};
|
||
|
||
private static readonly List<string> HebrewLetters = new List<string> {
|
||
"א",
|
||
"ב",
|
||
"ג",
|
||
"ד",
|
||
"ה",
|
||
"ו",
|
||
"ז",
|
||
"ח",
|
||
"י",
|
||
"ל",
|
||
"ם",
|
||
"מ",
|
||
"ן",
|
||
"נ",
|
||
"ס",
|
||
"ע",
|
||
"פ",
|
||
"צ",
|
||
"ק",
|
||
"ר",
|
||
"ש",
|
||
"ת",
|
||
"כ",
|
||
"ך",
|
||
"ט",
|
||
"ף",
|
||
"ץ",
|
||
};
|
||
|
||
private static readonly List<int> RussianCodes = new List<int> {
|
||
0x42, // Б
|
||
0x45, // Е
|
||
0x5A, // З
|
||
0x56, // В
|
||
0x49, // И
|
||
0x4E, // Н
|
||
0x58, // Ы
|
||
0x51, // Я
|
||
0x56, // V
|
||
0x53, // С
|
||
0x72, // р
|
||
0x69, // и
|
||
0x71, // я
|
||
0x6E, // н
|
||
0x74, // т
|
||
0x5C, // Э
|
||
0x77, // ю
|
||
0x46, // Ф
|
||
0x5E, // Ч
|
||
0x44, // Д
|
||
0x62, // б
|
||
0x73, // с
|
||
0x75, // у
|
||
0x64, // д
|
||
0x60, // ж
|
||
0x6A, // й
|
||
0x6C, // л
|
||
0x47, // Г
|
||
0x78, // ы
|
||
0x7A, // з
|
||
0x7E, // ч
|
||
0x6D, // м
|
||
0x67, // г
|
||
0x79, // ь
|
||
0x70, // п
|
||
0x76, // в
|
||
0x55, // У
|
||
0x7D, // щ
|
||
0x66, // ф
|
||
0x7C, // э
|
||
0x7B, // ш
|
||
0x50, // П
|
||
0x52, // П
|
||
0x68, // П
|
||
};
|
||
|
||
private static readonly List<string> RussianLetters = new List<string> {
|
||
"Б",
|
||
"Е",
|
||
"З",
|
||
"В",
|
||
"И",
|
||
"Н",
|
||
"Ы",
|
||
"Я",
|
||
"V",
|
||
"С",
|
||
"р",
|
||
"и",
|
||
"я",
|
||
"н",
|
||
"т",
|
||
"Э",
|
||
"ю",
|
||
"Ф",
|
||
"Ч",
|
||
"Д",
|
||
"б",
|
||
"с",
|
||
"у",
|
||
"д",
|
||
"ж",
|
||
"й",
|
||
"л",
|
||
"Г",
|
||
"ы",
|
||
"з",
|
||
"ч",
|
||
"м",
|
||
"г",
|
||
"ь",
|
||
"п",
|
||
"в",
|
||
"У",
|
||
"щ",
|
||
"ф",
|
||
"э",
|
||
"ш",
|
||
"П",
|
||
"Р",
|
||
"х",
|
||
};
|
||
|
||
public override string Extension
|
||
{
|
||
get { return ".890"; }
|
||
}
|
||
|
||
public const string NameOfFormat = "Cavena 890";
|
||
|
||
public override string Name
|
||
{
|
||
get { return NameOfFormat; }
|
||
}
|
||
|
||
public override bool IsTimeBased
|
||
{
|
||
get { return false; }
|
||
}
|
||
|
||
private int _languageIdLine1 = LanguageIdEnglish;
|
||
private int _languageIdLine2 = LanguageIdEnglish;
|
||
|
||
public void Save(string fileName, Subtitle subtitle)
|
||
{
|
||
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
|
||
{
|
||
int russianCount = 0;
|
||
char[] logoGrams = { '的', '是', '啊', '吧', '好', '吧', '亲', '爱', '的', '早', '上' };
|
||
char[] russianChars = { 'я', 'д', 'й', 'л', 'щ', 'ж', 'ц', 'ф', 'ы' };
|
||
foreach (Paragraph p in subtitle.Paragraphs)
|
||
{
|
||
if (p.Text.Contains(logoGrams))
|
||
{
|
||
_languageIdLine1 = LanguageIdChineseSimplified;
|
||
_languageIdLine2 = LanguageIdChineseSimplified;
|
||
break;
|
||
}
|
||
if (p.Text.Contains(russianChars))
|
||
{
|
||
russianCount++;
|
||
if (russianCount > 10)
|
||
{
|
||
_languageIdLine1 = LanguageIdRussian;
|
||
_languageIdLine2 = LanguageIdRussian; // or 0x09?
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (Configuration.Settings.SubtitleSettings.CurrentCavena89LanguageId > 0)
|
||
{
|
||
_languageIdLine1 = Configuration.Settings.SubtitleSettings.CurrentCavena89LanguageId;
|
||
_languageIdLine2 = Configuration.Settings.SubtitleSettings.CurrentCavena89LanguageId;
|
||
}
|
||
else
|
||
{
|
||
var language = LanguageAutoDetect.AutoDetectGoogleLanguage(subtitle);
|
||
switch (language)
|
||
{
|
||
case "he":
|
||
_languageIdLine1 = LanguageIdHebrew;
|
||
_languageIdLine2 = LanguageIdHebrew; // or 0x09
|
||
break;
|
||
case "ru":
|
||
_languageIdLine1 = LanguageIdRussian;
|
||
_languageIdLine2 = LanguageIdRussian; // or 0x09?
|
||
break;
|
||
case "zh":
|
||
_languageIdLine1 = LanguageIdChineseSimplified;
|
||
_languageIdLine2 = LanguageIdChineseSimplified;
|
||
break;
|
||
case "da":
|
||
_languageIdLine1 = LanguageIdDanish;
|
||
_languageIdLine2 = LanguageIdDanish;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// prompt???
|
||
//if (Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1 >= 0)
|
||
// _languageIdLine1 = Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1;
|
||
//if (Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2 >= 0)
|
||
// _languageIdLine2 = Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2;
|
||
|
||
// write file header (some fields are known, some are not...)
|
||
|
||
fs.WriteByte(0); // ?
|
||
fs.WriteByte(0); // ?
|
||
|
||
// tape number (20 bytes)
|
||
for (int i = 0; i < 20; i++)
|
||
fs.WriteByte(0);
|
||
|
||
// ?
|
||
for (int i = 0; i < 18; i++)
|
||
fs.WriteByte(0);
|
||
|
||
// translated programme title (28 bytes)
|
||
string title = Path.GetFileNameWithoutExtension(fileName) ?? string.Empty;
|
||
if (title.Length > 28)
|
||
title = title.Substring(0, 28);
|
||
if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena89Title) && Configuration.Settings.SubtitleSettings.CurrentCavena89Title.Length <= 28)
|
||
title = Configuration.Settings.SubtitleSettings.CurrentCavena89Title;
|
||
var buffer = Encoding.ASCII.GetBytes(title);
|
||
fs.Write(buffer, 0, buffer.Length);
|
||
for (int i = 0; i < 28 - buffer.Length; i++)
|
||
fs.WriteByte(0);
|
||
|
||
// translator (28 bytes)
|
||
if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena890Translator) && Configuration.Settings.SubtitleSettings.CurrentCavena890Translator.Length <= 28)
|
||
{
|
||
buffer = Encoding.ASCII.GetBytes(Configuration.Settings.SubtitleSettings.CurrentCavena890Translator);
|
||
fs.Write(buffer, 0, buffer.Length);
|
||
for (int i = 0; i < 28 - buffer.Length; i++)
|
||
fs.WriteByte(0);
|
||
}
|
||
else
|
||
{
|
||
for (int i = 0; i < 28; i++)
|
||
fs.WriteByte(0);
|
||
}
|
||
|
||
// ?
|
||
for (int i = 0; i < 9; i++)
|
||
fs.WriteByte(0);
|
||
|
||
// translated episode title (11 bytes)
|
||
for (int i = 0; i < 11; i++)
|
||
fs.WriteByte(0);
|
||
|
||
// ?
|
||
for (int i = 0; i < 18; i++)
|
||
fs.WriteByte(0);
|
||
|
||
// ? + language codes
|
||
buffer = new byte[] { 0xA0, 0x05, 0x04, 0x03, 0x06, 0x06, 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, (byte)_languageIdLine1, (byte)_languageIdLine2 };
|
||
fs.Write(buffer, 0, buffer.Length);
|
||
|
||
// comments (24 bytes)
|
||
buffer = Encoding.ASCII.GetBytes("");
|
||
if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena89Comment) && Configuration.Settings.SubtitleSettings.CurrentCavena89Comment.Length <= 24)
|
||
buffer = Encoding.ASCII.GetBytes(Configuration.Settings.SubtitleSettings.CurrentCavena89Comment);
|
||
|
||
fs.Write(buffer, 0, buffer.Length);
|
||
for (int i = 0; i < 24 - buffer.Length; i++)
|
||
fs.WriteByte(0);
|
||
|
||
// ??
|
||
buffer = new byte[] { 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||
fs.Write(buffer, 0, buffer.Length);
|
||
|
||
// number of subtitles
|
||
fs.WriteByte((byte)(subtitle.Paragraphs.Count % 256));
|
||
fs.WriteByte((byte)(subtitle.Paragraphs.Count / 256));
|
||
|
||
// write font - prefix with binary zeroes
|
||
buffer = GetFontBytesFromLanguageId(_languageIdLine1); // also TBX308VFONTL.V for english...
|
||
for (int i = 0; i < 14 - buffer.Length; i++)
|
||
fs.WriteByte(0);
|
||
fs.Write(buffer, 0, buffer.Length);
|
||
|
||
// ?
|
||
for (int i = 0; i < 13; i++)
|
||
fs.WriteByte(0);
|
||
|
||
// number of subtitles again
|
||
fs.WriteByte((byte)(subtitle.Paragraphs.Count % 256));
|
||
fs.WriteByte((byte)(subtitle.Paragraphs.Count / 256));
|
||
|
||
|
||
// number of subtitles again again
|
||
fs.WriteByte((byte)(subtitle.Paragraphs.Count % 256));
|
||
fs.WriteByte((byte)(subtitle.Paragraphs.Count / 256));
|
||
|
||
// ?
|
||
for (int i = 0; i < 6; i++)
|
||
fs.WriteByte(0);
|
||
|
||
// original programme title (28 chars)
|
||
if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena890riginalTitle) && Configuration.Settings.SubtitleSettings.CurrentCavena890riginalTitle.Length <= 28)
|
||
{
|
||
buffer = Encoding.ASCII.GetBytes(Configuration.Settings.SubtitleSettings.CurrentCavena890riginalTitle);
|
||
fs.Write(buffer, 0, buffer.Length);
|
||
for (int i = 0; i < 28 - buffer.Length; i++)
|
||
fs.WriteByte(0);
|
||
}
|
||
else
|
||
{
|
||
for (int i = 0; i < 28; i++)
|
||
fs.WriteByte(0);
|
||
}
|
||
|
||
// write font (use same font id from line 1)
|
||
buffer = GetFontBytesFromLanguageId(_languageIdLine1);
|
||
fs.Write(buffer, 0, buffer.Length);
|
||
|
||
// ?
|
||
fs.WriteByte(0x3d);
|
||
fs.WriteByte(0x8d);
|
||
|
||
// start of message time
|
||
string startOfMessage = "10:00:00:00";
|
||
if (Configuration.Settings.SubtitleSettings.Cavena890StartOfMessage != null &&
|
||
Configuration.Settings.SubtitleSettings.Cavena890StartOfMessage.Length == startOfMessage.Length)
|
||
startOfMessage = Configuration.Settings.SubtitleSettings.Cavena890StartOfMessage;
|
||
buffer = Encoding.ASCII.GetBytes(startOfMessage);
|
||
fs.Write(buffer, 0, buffer.Length);
|
||
|
||
buffer = new byte[]
|
||
{
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x54, 0x44
|
||
};
|
||
fs.Write(buffer, 0, buffer.Length);
|
||
|
||
for (int i = 0; i < 92; i++)
|
||
fs.WriteByte(0);
|
||
|
||
// paragraphs
|
||
int number = 16;
|
||
foreach (Paragraph p in subtitle.Paragraphs)
|
||
{
|
||
// number
|
||
fs.WriteByte((byte)(number / 256));
|
||
fs.WriteByte((byte)(number % 256));
|
||
|
||
WriteTime(fs, p.StartTime);
|
||
WriteTime(fs, p.EndTime);
|
||
|
||
if (p.Text.StartsWith("{\\an1}"))
|
||
fs.WriteByte(0x50); // left
|
||
else if (p.Text.StartsWith("{\\an3}"))
|
||
fs.WriteByte(0x52); // left
|
||
else
|
||
fs.WriteByte(0x54); // center
|
||
|
||
buffer = new byte[] { 0, 0, 0, 0, 0, 0, 0 }; // 0x16 }; -- the last two bytes might be something with vertical alignment...
|
||
fs.Write(buffer, 0, buffer.Length);
|
||
|
||
bool hasBox = Utilities.RemoveSsaTags(p.Text).StartsWith("<box>");
|
||
var text = p.Text.Replace("<box>", string.Empty).Replace("</box>", string.Empty);
|
||
text = HtmlUtil.RemoveOpenCloseTags(Utilities.RemoveSsaTags(text), HtmlUtil.TagBold, HtmlUtil.TagFont, HtmlUtil.TagBold);
|
||
WriteText(fs, text, p == subtitle.Paragraphs[subtitle.Paragraphs.Count - 1], _languageIdLine1, hasBox);
|
||
|
||
number += 16;
|
||
}
|
||
}
|
||
}
|
||
|
||
private static byte[] GetFontBytesFromLanguageId(int languageId)
|
||
{
|
||
var buffer = Encoding.ASCII.GetBytes("HLV23N.V");
|
||
if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified)
|
||
buffer = Encoding.ASCII.GetBytes("CCKM44.V");
|
||
else if (languageId == LanguageIdArabic)
|
||
buffer = Encoding.ASCII.GetBytes("ARA19N.V");
|
||
else if (languageId == LanguageIdRussian)
|
||
buffer = Encoding.ASCII.GetBytes("KYRIL4.V");
|
||
else if (languageId == LanguageIdHebrew)
|
||
buffer = Encoding.ASCII.GetBytes("HEBNOA.V");
|
||
else if (languageId == LanguageIdDanish)
|
||
buffer = Encoding.ASCII.GetBytes("VFONTL.V");
|
||
return buffer;
|
||
}
|
||
|
||
private static void WriteText(Stream fs, string text, bool isLast, int languageIdLine, bool useBox)
|
||
{
|
||
string line1 = string.Empty;
|
||
string line2 = string.Empty;
|
||
var lines = text.SplitToLines();
|
||
if (lines.Length > 2)
|
||
lines = Utilities.AutoBreakLine(text).SplitToLines();
|
||
if (lines.Length > 1)
|
||
{
|
||
line1 = lines[0];
|
||
line2 = lines[1];
|
||
}
|
||
else if (lines.Length == 1)
|
||
{
|
||
line2 = lines[0];
|
||
}
|
||
|
||
var buffer = GetTextAsBytes(line1, languageIdLine);
|
||
fs.Write(buffer, 0, buffer.Length);
|
||
|
||
buffer = new byte[] { 00, 00, 00, 00, 00, 00 };
|
||
if (useBox)
|
||
buffer[3] = 0xa0;
|
||
fs.Write(buffer, 0, buffer.Length);
|
||
|
||
buffer = GetTextAsBytes(line2, languageIdLine);
|
||
fs.Write(buffer, 0, buffer.Length);
|
||
|
||
buffer = new byte[] { 00, 00, 00, 00 };
|
||
if (!isLast)
|
||
fs.Write(buffer, 0, buffer.Length);
|
||
}
|
||
|
||
private static byte[] GetTextAsBytes(string text, int languageId)
|
||
{
|
||
var buffer = new byte[51];
|
||
int skipCount = 0;
|
||
for (int i = 0; i < buffer.Length; i++)
|
||
buffer[i] = 0x7F;
|
||
|
||
if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified)
|
||
{
|
||
for (int i = 0; i < buffer.Length; i++)
|
||
buffer[i] = 0;
|
||
}
|
||
else if (languageId == LanguageIdHebrew)
|
||
{
|
||
text = Utilities.ReverseNumbers(text);
|
||
if (!Configuration.Settings.General.RightToLeftMode)
|
||
text = Utilities.ReverseStartAndEndingForRightToLeft(text);
|
||
}
|
||
|
||
var encoding = Encoding.Default;
|
||
int index = 0;
|
||
for (int i = 0; i < text.Length; i++)
|
||
{
|
||
var current = text[i];
|
||
if (skipCount > 0)
|
||
{
|
||
skipCount--;
|
||
}
|
||
else if (languageId == LanguageIdHebrew)
|
||
{
|
||
int letterIndex = HebrewLetters.IndexOf(current.ToString(CultureInfo.InvariantCulture));
|
||
if (letterIndex >= 0)
|
||
{
|
||
buffer[index] = (byte)HebrewCodes[letterIndex];
|
||
}
|
||
else if (i + 3 < text.Length && text.Substring(i, 3) == "<i>")
|
||
{
|
||
buffer[index] = 0x88;
|
||
skipCount = 2;
|
||
}
|
||
else if (i + 4 <= text.Length && text.Substring(i, 4) == "</i>")
|
||
{
|
||
buffer[index] = 0x98;
|
||
skipCount = 2;
|
||
}
|
||
else
|
||
{
|
||
buffer[index] = encoding.GetBytes(new[] { current })[0];
|
||
}
|
||
index++;
|
||
}
|
||
else if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified)
|
||
{
|
||
encoding = Encoding.GetEncoding(1201);
|
||
if (index < 49)
|
||
{
|
||
if (i + 3 < text.Length && text.Substring(i, 3) == "<i>")
|
||
{
|
||
buffer[index] = 0x88;
|
||
skipCount = 2;
|
||
}
|
||
else if (i + 4 <= text.Length && text.Substring(i, 4) == "</i>")
|
||
{
|
||
buffer[index] = 0x98;
|
||
skipCount = 3;
|
||
}
|
||
else
|
||
{
|
||
buffer[index] = encoding.GetBytes(new[] { current })[0];
|
||
index++;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (index < 50)
|
||
{
|
||
if (current == 'æ')
|
||
buffer[index] = 0x1B;
|
||
else if (current == 'ø')
|
||
buffer[index] = 0x1C;
|
||
else if (current == 'å')
|
||
buffer[index] = 0x1D;
|
||
else if (current == 'Æ')
|
||
buffer[index] = 0x5B;
|
||
else if (current == 'Ø')
|
||
buffer[index] = 0x5C;
|
||
else if (current == 'Å')
|
||
buffer[index] = 0x5D;
|
||
else if (current == 'Ä')
|
||
AddTwo(buffer, ref index, 0x86, 0x41);
|
||
else if (current == 'ä')
|
||
AddTwo(buffer, ref index, 0x86, 0x61);
|
||
else if (current == 'Ö')
|
||
AddTwo(buffer, ref index, 0x86, 0x4F);
|
||
else if (current == 'ö')
|
||
AddTwo(buffer, ref index, 0x86, 0x6F);
|
||
else if (current == 'Ë')
|
||
AddTwo(buffer, ref index, 0x86, 0x45);
|
||
else if (current == 'ë')
|
||
AddTwo(buffer, ref index, 0x86, 0x65);
|
||
else if (current == 'Ï')
|
||
AddTwo(buffer, ref index, 0x86, 0x49);
|
||
else if (current == 'ï')
|
||
AddTwo(buffer, ref index, 0x86, 0x69);
|
||
else if (current == 'Ü')
|
||
AddTwo(buffer, ref index, 0x86, 0x55);
|
||
else if (current == 'ü')
|
||
AddTwo(buffer, ref index, 0x86, 0x75);
|
||
|
||
// different language setting?
|
||
//else if (current == 'å')
|
||
//{
|
||
// buffer[index] = 0x8C;
|
||
// index++;
|
||
// buffer[index] = 0x61;
|
||
//}
|
||
//else if (current == 'Å')
|
||
//{
|
||
// buffer[index] = 0x8C;
|
||
// index++;
|
||
// buffer[index] = 0x41;
|
||
//}
|
||
|
||
// ăĂ îÎ şŞ ţŢ â (romanian)
|
||
else if (current == 'ă')
|
||
AddTwo(buffer, ref index, 0x89, 0x61);
|
||
else if (current == 'Ă')
|
||
AddTwo(buffer, ref index, 0x89, 0x41);
|
||
else if (current == 'î')
|
||
AddTwo(buffer, ref index, 0x83, 0x69);
|
||
else if (current == 'Î')
|
||
AddTwo(buffer, ref index, 0x83, 0x49);
|
||
else if (current == 'ş')
|
||
AddTwo(buffer, ref index, 0x87, 0x73);
|
||
else if (current == 'Ş')
|
||
AddTwo(buffer, ref index, 0x87, 0x53);
|
||
else if (current == 'ţ')
|
||
AddTwo(buffer, ref index, 0x87, 0x74);
|
||
else if (current == 'Ţ')
|
||
AddTwo(buffer, ref index, 0x87, 0x54);
|
||
else if (current == 'â')
|
||
AddTwo(buffer, ref index, 0x83, 0x61);
|
||
else if (current == 'Â')
|
||
AddTwo(buffer, ref index, 0x83, 0x41);
|
||
else if (current == 'è')
|
||
AddTwo(buffer, ref index, 0x81, 0x65);
|
||
else if (current == 'é')
|
||
AddTwo(buffer, ref index, 0x82, 0x65);
|
||
else if (current == 'É')
|
||
AddTwo(buffer, ref index, 0x82, 0x45);
|
||
else if (current == 'È')
|
||
AddTwo(buffer, ref index, 0x81, 0x45);
|
||
else if (i + 3 < text.Length && text.Substring(i, 3) == "<i>")
|
||
{
|
||
buffer[index] = 0x88;
|
||
skipCount = 2;
|
||
}
|
||
else if (i + 4 <= text.Length && text.Substring(i, 4) == "</i>")
|
||
{
|
||
buffer[index] = 0x98;
|
||
skipCount = 3;
|
||
}
|
||
else
|
||
{
|
||
buffer[index] = encoding.GetBytes(new[] { current })[0];
|
||
}
|
||
index++;
|
||
}
|
||
}
|
||
}
|
||
|
||
return buffer;
|
||
}
|
||
|
||
private static void AddTwo(byte[] buffer, ref int index, byte b1, byte b2)
|
||
{
|
||
buffer[index] = b1;
|
||
index++;
|
||
buffer[index] = b2;
|
||
}
|
||
|
||
private static void WriteTime(FileStream fs, TimeCode timeCode)
|
||
{
|
||
double totalMilliseconds = timeCode.TotalMilliseconds;
|
||
int frames = (int)Math.Round(totalMilliseconds / (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate));
|
||
fs.WriteByte((byte)(frames / 256 / 256));
|
||
fs.WriteByte((byte)(frames / 256));
|
||
fs.WriteByte((byte)(frames % 256));
|
||
}
|
||
|
||
public override bool IsMine(List<string> lines, string fileName)
|
||
{
|
||
if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
|
||
{
|
||
var fi = new FileInfo(fileName);
|
||
if (fi.Length >= 640 && fi.Length < 1024000) // not too small or too big
|
||
{
|
||
if (!fileName.EndsWith(".890", StringComparison.Ordinal))
|
||
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!";
|
||
}
|
||
|
||
public override void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName)
|
||
{
|
||
const int textLength = 51;
|
||
|
||
subtitle.Paragraphs.Clear();
|
||
subtitle.Header = null;
|
||
byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
|
||
|
||
_languageIdLine1 = buffer[146];
|
||
if (_languageIdLine1 == 0)
|
||
_languageIdLine1 = LanguageIdEnglish;
|
||
Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1 = _languageIdLine1;
|
||
|
||
_languageIdLine2 = buffer[147];
|
||
if (_languageIdLine2 == 0)
|
||
_languageIdLine2 = LanguageIdEnglish;
|
||
Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2 = _languageIdLine2;
|
||
|
||
var fontNameLine1 = Encoding.ASCII.GetString(buffer, 187, 6);
|
||
var fontNameLine2 = Encoding.ASCII.GetString(buffer, 246, 6);
|
||
|
||
// Hebrew
|
||
if (_languageIdLine1 == LanguageIdHebrew || fontNameLine1 == "HEBNOA" || fontNameLine2 == "HEBNOA")
|
||
{
|
||
_languageIdLine1 = LanguageIdHebrew;
|
||
_languageIdLine2 = LanguageIdHebrew;
|
||
}
|
||
|
||
// Russian
|
||
else if (_languageIdLine1 == LanguageIdRussian || fontNameLine1.StartsWith("KYRIL", StringComparison.Ordinal) || fontNameLine2.StartsWith("KYRIL", StringComparison.Ordinal))
|
||
{
|
||
_languageIdLine1 = LanguageIdRussian;
|
||
_languageIdLine2 = LanguageIdRussian;
|
||
}
|
||
|
||
// Chinese
|
||
else if (_languageIdLine1 == LanguageIdChineseSimplified)
|
||
{
|
||
_languageIdLine1 = LanguageIdChineseSimplified;
|
||
_languageIdLine2 = LanguageIdChineseSimplified;
|
||
}
|
||
else if (_languageIdLine1 == LanguageIdChineseTraditional || fontNameLine1 == "CCKM44" || fontNameLine2 == "CCKM44")
|
||
{
|
||
_languageIdLine1 = LanguageIdChineseTraditional;
|
||
_languageIdLine2 = LanguageIdChineseTraditional;
|
||
}
|
||
|
||
int i = 455;
|
||
int lastNumber = -1;
|
||
while (i < buffer.Length - 20)
|
||
{
|
||
int start = i - textLength;
|
||
|
||
int number = buffer[start - 16] * 256 + buffer[start - 15];
|
||
|
||
var p = new Paragraph();
|
||
double startFrame = buffer[start - 14] * 256 * 256 + buffer[start - 13] * 256 + buffer[start - 12];
|
||
double endFrame = buffer[start - 11] * 256 * 256 + buffer[start - 10] * 256 + buffer[start - 9];
|
||
|
||
byte boxType = buffer[start + textLength + 3];
|
||
|
||
string line1 = FixText(buffer, start, textLength, _languageIdLine1);
|
||
string line2 = FixText(buffer, start + textLength + 6, textLength, _languageIdLine2);
|
||
|
||
if (lastNumber == number)
|
||
{
|
||
p = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1];
|
||
string temp = (line1.TrimEnd() + Environment.NewLine + line2).TrimEnd();
|
||
if (temp.Length > 0)
|
||
p.Text = temp;
|
||
}
|
||
else
|
||
{
|
||
subtitle.Paragraphs.Add(p);
|
||
p.StartTime.TotalMilliseconds = (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate) * startFrame;
|
||
p.EndTime.TotalMilliseconds = (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate) * endFrame;
|
||
p.Text = (line1.TrimEnd() + Environment.NewLine + line2).TrimEnd();
|
||
}
|
||
if (boxType >= 0xa0 && boxType <= 0xa9 && !string.IsNullOrEmpty(p.Text)) // box
|
||
{
|
||
if (p.Text.StartsWith("{\\") && p.Text.Contains("}"))
|
||
{
|
||
p.Text = p.Text.Insert(p.Text.IndexOf('}', 3) + 1, "<box>") + "</box>";
|
||
}
|
||
else
|
||
{
|
||
p.Text = "<box>" + p.Text + "</box>";
|
||
}
|
||
}
|
||
|
||
lastNumber = number;
|
||
|
||
i += 128;
|
||
}
|
||
|
||
subtitle.Renumber();
|
||
}
|
||
|
||
private static string FixText(byte[] buffer, int start, int textLength, int languageId)
|
||
{
|
||
string text;
|
||
|
||
if (languageId == LanguageIdRussian)
|
||
{
|
||
var encoding = Encoding.GetEncoding(1252);
|
||
var sb = new StringBuilder();
|
||
for (int i = 0; i < textLength; i++)
|
||
{
|
||
int b = buffer[start + i];
|
||
int idx = RussianCodes.IndexOf(b);
|
||
if (idx >= 0)
|
||
sb.Append(RussianLetters[idx]);
|
||
else
|
||
sb.Append(encoding.GetString(buffer, start + i, 1));
|
||
}
|
||
|
||
text = sb.ToString();
|
||
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes
|
||
text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown?
|
||
text = FixColors(text);
|
||
|
||
if (text.Contains("<i></i>"))
|
||
text = text.Replace("<i></i>", "<i>");
|
||
if (text.Contains("<i>") && !text.Contains("</i>"))
|
||
text += "</i>";
|
||
}
|
||
else if (languageId == LanguageIdHebrew) // (_language == "HEBNOA")
|
||
{
|
||
var encoding = Encoding.GetEncoding(1252);
|
||
var sb = new StringBuilder();
|
||
for (int i = 0; i < textLength; i++)
|
||
{
|
||
int b = buffer[start + i];
|
||
int idx = HebrewCodes.IndexOf(b);
|
||
if (idx >= 0)
|
||
sb.Append(HebrewLetters[idx]);
|
||
else
|
||
sb.Append(encoding.GetString(buffer, start + i, 1));
|
||
}
|
||
|
||
text = sb.ToString();
|
||
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes
|
||
text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown?
|
||
text = FixColors(text);
|
||
if (!Configuration.Settings.General.RightToLeftMode)
|
||
text = Utilities.ReverseStartAndEndingForRightToLeft(text);
|
||
text = Utilities.ReverseNumbers(text);
|
||
}
|
||
else if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified) // (_language == "CCKM44" || _language == "TVB000")
|
||
{
|
||
int index = start;
|
||
|
||
while (textLength >= 1 && index + textLength < buffer.Length && (buffer[index + textLength - 1] == 0))
|
||
textLength--;
|
||
if (textLength > 0)
|
||
{
|
||
text = Encoding.GetEncoding(1201).GetString(buffer, index, textLength).Replace("\0", string.Empty);
|
||
}
|
||
else
|
||
{
|
||
text = string.Empty;
|
||
}
|
||
|
||
var encoding = Encoding.Default; // which encoding?? Encoding.GetEncoding("ISO-8859-5")
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes
|
||
text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown?
|
||
text = FixColors(text);
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x88 }), "<i>");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x98 }), "</i>");
|
||
|
||
if (text.Contains("<i></i>"))
|
||
text = text.Replace("<i></i>", "<i>");
|
||
if (text.Contains("<i>") && !text.Contains("</i>"))
|
||
text += "</i>";
|
||
}
|
||
else
|
||
{
|
||
var encoding = Encoding.GetEncoding(1252);
|
||
text = encoding.GetString(buffer, start, textLength).Replace("\0", string.Empty);
|
||
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes
|
||
text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown?
|
||
text = FixColors(text);
|
||
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x1B }), "æ");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x1C }), "ø");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x1D }), "å");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x1E }), "Æ");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x1F }), "Ø");
|
||
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x5B }), "Æ");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x5C }), "Ø");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x5D }), "Å");
|
||
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x41 }), "Ä");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x61 }), "ä");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x4F }), "Ö");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x6F }), "ö");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x45 }), "Ë");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x65 }), "ë");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x49 }), "Ï");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x69 }), "ï");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x55 }), "Ü");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x75 }), "ü");
|
||
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x8C, 0x61 }), "å");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x8C, 0x41 }), "Å");
|
||
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x65 }), "è");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x65 }), "é");
|
||
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x45 }), "É");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x65 }), "È");
|
||
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x88 }), "<i>");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x98 }), "</i>");
|
||
|
||
//ăĂ îÎ şŞ ţŢ â (romanian)
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x89, 0x61 }), "ă");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x89, 0x41 }), "Ă");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x69 }), "î");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x49 }), "Î");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x73 }), "ş");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x53 }), "Ş");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x74 }), "ţ");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x54 }), "Ţ");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x61 }), "â");
|
||
text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x41 }), "Â");
|
||
|
||
if (text.Contains("<i></i>"))
|
||
text = text.Replace("<i></i>", "<i>");
|
||
if (text.Contains("<i>") && !text.Contains("</i>"))
|
||
text += "</i>";
|
||
}
|
||
return text;
|
||
}
|
||
|
||
private static string FixColors(string text)
|
||
{
|
||
Encoding encoding = Encoding.GetEncoding(1252);
|
||
bool fontColorOn = false;
|
||
var sb = new StringBuilder();
|
||
for (int i = 0; i < text.Length; i++)
|
||
{
|
||
var s = text.Substring(i, 1);
|
||
if (s == encoding.GetString(new byte[] { 0xf1 }))
|
||
{
|
||
if (fontColorOn)
|
||
{
|
||
sb.Append("</font>"); // white
|
||
}
|
||
sb.Append("<font color=\"#FF797D\">"); // red
|
||
fontColorOn = true;
|
||
}
|
||
else if (s == encoding.GetString(new byte[] { 0xf2 }))
|
||
{
|
||
if (fontColorOn)
|
||
{
|
||
sb.Append("</font>"); // white
|
||
}
|
||
sb.Append("<font color=\"#AAEF9E\">"); // green
|
||
fontColorOn = true;
|
||
}
|
||
else if (s == encoding.GetString(new byte[] { 0xf3 }))
|
||
{
|
||
if (fontColorOn)
|
||
{
|
||
sb.Append("</font>"); // white
|
||
}
|
||
sb.Append("<font color=\"#FAFAA8\">"); // yellow
|
||
fontColorOn = true;
|
||
}
|
||
else if (s == encoding.GetString(new byte[] { 0xf4 }))
|
||
{
|
||
if (fontColorOn)
|
||
{
|
||
sb.Append("</font>"); // white
|
||
}
|
||
sb.Append("<font color=\"#9999FF\">"); // purple
|
||
fontColorOn = true;
|
||
}
|
||
else if (s == encoding.GetString(new byte[] { 0xf5 }))
|
||
{
|
||
if (fontColorOn)
|
||
{
|
||
sb.Append("</font>"); // white
|
||
}
|
||
sb.Append("<font color=\"#FFABFB\">"); // magenta
|
||
fontColorOn = true;
|
||
}
|
||
else if (s == encoding.GetString(new byte[] { 0xf6 }))
|
||
{
|
||
if (fontColorOn)
|
||
{
|
||
sb.Append("</font>"); // white
|
||
}
|
||
sb.Append("<font color=\"#A2FEFE\">"); // cyan
|
||
fontColorOn = true;
|
||
}
|
||
else if (s == encoding.GetString(new byte[] { 0xf7 }))
|
||
{
|
||
if (fontColorOn)
|
||
{
|
||
sb.Append("</font>"); // white
|
||
fontColorOn = false;
|
||
}
|
||
}
|
||
else if (s == encoding.GetString(new byte[] { 0xf8 }))
|
||
{
|
||
sb.Append("<font color=\"#FCC786\">"); // orange
|
||
fontColorOn = true;
|
||
}
|
||
else
|
||
{
|
||
sb.Append(s);
|
||
}
|
||
}
|
||
if (fontColorOn)
|
||
{
|
||
sb.Append("</font>"); // white
|
||
}
|
||
return sb.ToString();
|
||
}
|
||
|
||
}
|
||
}
|