Initial version

git-svn-id: https://subtitleedit.googlecode.com/svn/trunk@13 99eadd0c-20b8-1223-b5c4-2a2b2df33de2
This commit is contained in:
niksedk 2010-10-12 11:21:51 +00:00
parent f73c7479e4
commit 976be2c6e6
20 changed files with 7615 additions and 0 deletions

View File

@ -0,0 +1,36 @@
using System.IO;
namespace Nikse.SubtitleEdit.Logic
{
public class Configuration
{
static readonly Configuration Instance = new Configuration();
string _baseDir;
Settings _settings;
public static string BaseDirectory
{
get
{
if (Instance._baseDir == null)
{
System.Reflection.Assembly a = System.Reflection.Assembly.GetEntryAssembly();
Instance._baseDir = Path.GetDirectoryName(a.Location);
if (!Instance._baseDir.EndsWith(Path.DirectorySeparatorChar.ToString()))
Instance._baseDir += Path.DirectorySeparatorChar;
}
return Instance._baseDir;
}
}
public static Settings Settings
{
get
{
if (Instance._settings == null)
Instance._settings = Settings.GetSettings();
return Instance._settings;
}
}
}
}

86
src/Logic/FastBitmap.cs Normal file
View File

@ -0,0 +1,86 @@
//Downloaded from Visual C# Kicks - http://www.vcskicks.com/
using System;
using System.Drawing;
using System.Drawing.Imaging;
namespace Nikse.SubtitleEdit.Logic
{
unsafe public class FastBitmap
{
private struct PixelData
{
public byte Blue;
public byte Green;
public byte Red;
public byte Alpha;
public override string ToString()
{
return "(" + Alpha + ", " + Red + ", " + Green + ", " + Blue + ")";
}
}
public int Width { get; set; }
public int Height { get; set; }
private readonly Bitmap _workingBitmap;
private int _width;
private BitmapData _bitmapData;
private Byte* _pBase = null;
public FastBitmap(Bitmap inputBitmap)
{
_workingBitmap = inputBitmap;
Width = inputBitmap.Width;
Height = inputBitmap.Height;
}
public void LockImage()
{
var bounds = new Rectangle(Point.Empty, _workingBitmap.Size);
_width = bounds.Width * sizeof(PixelData);
if (_width % 4 != 0) _width = 4 * (_width / 4 + 1);
//Lock Image
_bitmapData = _workingBitmap.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
_pBase = (Byte*)_bitmapData.Scan0.ToPointer();
}
private PixelData* _pixelData = null;
public Color GetPixel(int x, int y)
{
_pixelData = (PixelData*)(_pBase + y * _width + x * sizeof(PixelData));
return Color.FromArgb(_pixelData->Alpha, _pixelData->Red, _pixelData->Green, _pixelData->Blue);
}
public Color GetPixelNext()
{
_pixelData++;
return Color.FromArgb(_pixelData->Alpha, _pixelData->Red, _pixelData->Green, _pixelData->Blue);
}
public void SetPixel(int x, int y, Color color)
{
var data = (PixelData*)(_pBase + y * _width + x * sizeof(PixelData));
data->Alpha = color.A;
data->Red = color.R;
data->Green = color.G;
data->Blue = color.B;
}
public Bitmap GetBitmap()
{
return _workingBitmap;
}
public void UnlockImage()
{
_workingBitmap.UnlockBits(_bitmapData);
_bitmapData = null;
_pBase = null;
}
}
}

View File

@ -0,0 +1,164 @@
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Nikse.SubtitleEdit.Logic.Enums;
namespace Nikse.SubtitleEdit.Logic
{
public class FindReplaceDialogHelper
{
readonly string _findText = string.Empty;
readonly string _replaceText = string.Empty;
readonly Regex _regEx;
int _findTextLenght;
public FindType FindType { get; set; }
public int SelectedIndex { get; set; }
public int SelectedPosition { get; set; }
public int WindowPositionLeft { get; set; }
public int WindowPositionTop { get; set; }
public int StartLineIndex { get; set; }
public int FindTextLength
{
get
{
return _findTextLenght;
}
}
public string FindText
{
get
{
return _findText;
}
}
public string ReplaceText
{
get
{
return _replaceText;
}
}
public FindReplaceDialogHelper(FindType findType, string findText, Regex regEx, string replaceText, int left, int top, int startLineIndex)
{
FindType = findType;
_findText = findText;
_replaceText = replaceText;
_regEx = regEx;
_findTextLenght = findText.Length;
WindowPositionLeft = left;
WindowPositionTop = top;
StartLineIndex = startLineIndex;
}
public bool Find(Subtitle subtitle, int startIndex)
{
return FindNext(subtitle, startIndex, 0);
}
public bool Find(TextBox textBox, int startIndex)
{
return FindNext(textBox, startIndex);
}
private int FindPositionInText(string text, int startIndex)
{
if (startIndex >= text.Length)
return -1;
switch (FindType)
{
case FindType.Normal:
return (text.ToLower().IndexOf(_findText.ToLower(), startIndex));
case FindType.CaseSensitive:
return (text.IndexOf(_findText, startIndex));
case FindType.RegEx:
{
Match match = _regEx.Match(text, startIndex);
if (match.Success)
{
_findTextLenght = match.Length;
return match.Index;
}
return -1;
}
}
return -1;
}
public bool FindNext(Subtitle subtitle, int startIndex, int position)
{
int index = 0;
if (position < 0)
position = 0;
foreach (Paragraph p in subtitle.Paragraphs)
{
if (index >= startIndex)
{
int pos = FindPositionInText(p.Text, position);
if (pos >= 0)
{
SelectedIndex = index;
SelectedPosition = pos;
return true;
}
position = 0;
}
index++;
}
return false;
}
public static ContextMenu GetRegExContextMenu(TextBox textBox)
{
var cm = new ContextMenu();
cm.MenuItems.Add("Word boundary (\\b)", delegate { textBox.SelectedText = "\\b"; });
cm.MenuItems.Add("Non word boundary (\\B)", delegate { textBox.SelectedText = "\\B"; });
cm.MenuItems.Add("Carriage return + new line (\\r\\n)", delegate { textBox.SelectedText = "\\r\\n"; });
cm.MenuItems.Add("Any digit (\\d)", delegate { textBox.SelectedText = "\\d"; });
cm.MenuItems.Add("Any character (.)", delegate { textBox.SelectedText = "."; });
cm.MenuItems.Add("Any whitespace", delegate { textBox.SelectedText = "\\s"; });
cm.MenuItems.Add("Zero or more (*)", delegate { textBox.SelectedText = "*"; });
cm.MenuItems.Add("One or more", delegate { textBox.SelectedText = "+"; });
cm.MenuItems.Add("In character goup ([test])", delegate { textBox.SelectedText = "[test]"; });
cm.MenuItems.Add("Not in character goup ([^test])", delegate { textBox.SelectedText = "[^test]"; });
return cm;
}
public static ContextMenu GetReplaceTextContextMenu(TextBox textBox)
{
var cm = new ContextMenu();
cm.MenuItems.Add("Carriage return + new line (\\r\\n)", delegate { textBox.SelectedText = "\\r\\n"; });
return cm;
}
public bool FindNext(TextBox textBox, int startIndex)
{
startIndex++;
if (startIndex < textBox.Text.Length)
{
if (FindType == FindType.RegEx)
{
Match match = _regEx.Match(textBox.Text, startIndex);
if (match.Success)
{
_findTextLenght = match.Length;
SelectedIndex = match.Index;
}
return match.Success;
}
string searchText = textBox.Text.Substring(startIndex);
int pos = FindPositionInText(searchText, 0);
if (pos >= 0)
{
SelectedIndex = pos + startIndex;
return true;
}
}
return false;
}
}
}

26
src/Logic/HistoryItem.cs Normal file
View File

@ -0,0 +1,26 @@
using System;
namespace Nikse.SubtitleEdit.Logic
{
public class HistoryItem
{
public int Index { get; set; }
public DateTime Timestamp { get; set; }
public string Description { get; set; }
public string FileName { get; set; }
public DateTime FileModified { get; set; }
public Subtitle Subtitle { get; set; }
public string SubtitleFormatFriendlyName { get; set; }
public HistoryItem(int index, Subtitle subtitle, string description, string fileName, DateTime fileModified, string subtitleFormatFriendlyName)
{
Index = index;
Timestamp = DateTime.Now;
Subtitle = new Subtitle(subtitle);
Description = description;
FileName = fileName;
FileModified = fileModified;
SubtitleFormatFriendlyName = subtitleFormatFriendlyName;
}
}
}

408
src/Logic/IfoParser.cs Normal file
View File

@ -0,0 +1,408 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
namespace Nikse.SubtitleEdit.Logic
{
public class IfoParser
{
public struct AudioStream
{
public int LangageTypeSpecified;
public string Langage;
public string LangageCode;
public string CodingMode;
public int Channels;
public string Extension;
};
public struct VideoStream
{
public string Aspect;
public string Standard;
public string CodingMode;
public string Resolution;
}
public class VtsVobs
{
public int NumberOfAudioStreams;
public int NumberOfSubtitles;
public VideoStream VideoStream;
public List<AudioStream> AudioStreams;
public List<string> Subtitles;
public VtsVobs()
{
VideoStream = new VideoStream();
AudioStreams = new List<AudioStream>();
Subtitles = new List<string>();
}
};
public class ProgramChain
{
public int NumberOfPGC;
public int NumberOfCells;
public string PlaybackTime;
public List<byte> PGCEntryCells;
public List<string> PGCPlaybackTimes;
public List<string> PGCStartTimes;
public List<char> AudioStreamsAvailable;
public List<char> SubtitlesAvailable;
public List<Color> ColorLookupTable;
public ProgramChain()
{
PGCEntryCells = new List<byte>();
PGCPlaybackTimes = new List<string>();
PGCStartTimes = new List<string>();
AudioStreamsAvailable = new List<char>();
SubtitlesAvailable = new List<char>();
ColorLookupTable = new List<Color>();
}
};
public class VtsPgci
{
public int NumberOfProgramChains;
public List<ProgramChain> ProgramChains;
public VtsPgci()
{
ProgramChains = new List<ProgramChain>();
}
};
readonly List<string> ArrayOfAudioMode = new List<string> { "AC3", "...", "MPEG1", "MPEG2", "LPCM", "...", "DTS" };
readonly List<string> ArrayOfAudioExtension = new List<string> { "unspecified", "normal", "for visually impaired", "director's comments", "alternate director's comments" };
readonly List<string> ArrayOfAspect = new List<string> { "4:3", "...", "...", "16:9" };
readonly List<string> ArrayOfStandard = new List<string> { "NTSC", "PAL", "...", "..." };
readonly List<string> ArrayOfCodingMode = new List<string> { "MPEG1", "MPEG2" };
readonly List<string> ArrayOfNTSCResolution = new List<string> { "720x480", "704x480", "352x480", "352x240" };
readonly List<string> ArrayOfPALResolution = new List<string> { "720x576", "704x576", "352x576", "352x288" };
readonly List<string> ArrayOfLangageCode = new List<string> { " ", "aa", "ab", "af", "am", "ar", "as", "ay", "az", "ba", "be", "bg", "bh", "bi", "bn", "bo", "br", "ca", "co", "cs", "cy", "da", "de", "dz", "el",
"en", "eo", "es", "et", "eu", "fa", "fi", "fj", "fo", "fr", "fy", "ga", "gd", "gl", "gn", "gu", "ha", "he", "hi", "hr", "hu", "hy", "ia", "id", "ie", "ik",
"in", "is", "it", "iu", "iw", "ja", "ji", "jw", "ka", "kk", "kl", "km", "kn", "ko", "ks", "ku", "ky", "la", "ln", "lo", "lt", "lv", "mg", "mi", "mk", "ml",
"mn", "mo", "mr", "ms", "mt", "my", "na", "ne", "nl", "no", "oc", "om", "or", "pa", "pl", "ps", "pt", "qu", "rm", "rn", "ro", "ru", "rw", "sa", "sd", "sg",
"sh", "si", "sk", "sl", "sm", "sn", "so", "sq", "sr", "ss", "st", "su", "sv", "sw", "ta", "te", "tg", "th", "ti", "tk", "tl", "tn", "to", "tr", "ts", "tt",
"tw", "ug", "uk", "ur", "uz", "vi", "vo", "wo", "xh", "yi", "yo", "za", "zh", "zu", ""};
readonly List<string> ArrayOfLangage = new List<string> { "Not Specified", "Afar", "Abkhazian", "Afrikaans", "Amharic", "Arabic", "Assamese", "Aymara", "Azerbaijani", "Bashkir", "Byelorussian", "Bulgarian", "Bihari", "Bislama", "Bengali; Bangla", "Tibetan", "Breton", "Catalan", "Corsican", "Czech(Ceske)", "Welsh", "Dansk", "Deutsch", "Bhutani", "Greek",
"English", "Esperanto", "Espanol", "Estonian", "Basque", "Persian", "Suomi", "Fiji", "Faroese", "Français", "Frisian", "Irish", "Scots Gaelic", "Galician", "Guarani", "Gujarati", "Hausa", "Hebrew", "Hindi", "Hrvatski", "Magyar", "Armenian", "Interlingua", "Indonesian", "Interlingue", "Inupiak",
"Indonesian", "Islenska", "Italiano", "Inuktitut", "Hebrew", "Japanese", "Yiddish", "Javanese", "Georgian", "Kazakh", "Greenlandic", "Cambodian", "Kannada", "Korean", "Kashmiri", "Kurdish", "Kirghiz", "Latin", "Lingala", "Laothian", "Lithuanian", "Latvian, Lettish", "Malagasy", "Maori", "Macedonian", "Malayalam",
"Mongolian", "Moldavian", "Marathi", "Malay", "Maltese", "Burmese", "Nauru", "Nepali", "Nederlands", "Norsk", "Occitan", "(Afan) Oromo", "Oriya", "Punjabi", "Polish", "Pashto, Pushto", "Portugues", "Quechua", "Rhaeto-Romance", "Kirundi", "Romanian", "Russian", "Kinyarwanda", "Sanskrit", "Sindhi", "Sangho",
"Serbo-Croatian", "Sinhalese", "Slovak", "Slovenian", "Samoan", "Shona", "Somali", "Albanian", "Serbian", "Siswati", "Sesotho", "Sundanese", "Svenska", "Swahili", "Tamil", "Telugu", "Tajik", "Thai", "Tigrinya", "Turkmen", "Tagalog", "Setswana", "Tonga", "Turkish", "Tsonga", "Tatar",
"Twi", "Uighur", "Ukrainian", "Urdu", "Uzbek", "Vietnamese", "Volapuk", "Wolof", "Xhosa", "Yiddish", "Yoruba", "Zhuang", "Chinese", "Zulu", "???"};
public VtsPgci VideoTitleSetProgramChainTable { get { return _vtsPgci; } }
public VtsVobs VideoTitleSetVobs { get { return _vtsVobs; } }
public string ErrorMessage { get; private set; }
private VtsVobs _vtsVobs = new VtsVobs();
private VtsPgci _vtsPgci = new VtsPgci();
FileStream _fs;
public IfoParser(string fileName)
{
try
{
_fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
byte[] buffer = new byte[12];
_fs.Position = 0;
_fs.Read(buffer, 0, 12);
string id = Encoding.UTF8.GetString(buffer);
if (id != "DVDVIDEO-VTS")
{
ErrorMessage = string.Format("IFO type is '{0}' and not 'DVDVIDEO-VTS'.{1}Try another file than {2}", id, Environment.NewLine, fileName);
return;
}
ParseVtsVobs();
ParseVtsPgci();
_fs.Close();
}
catch (Exception exception)
{
ErrorMessage = exception.Message + Environment.NewLine + exception.StackTrace;
}
}
private void ParseVtsVobs()
{
string data;
byte[] buffer = new byte[16];
//retrieve video info
_fs.Position = 0x200;
data = IntToBin(GetEndian(2), 16);
_vtsVobs.VideoStream.CodingMode = ArrayOfCodingMode[BinToInt(MidStr(data, 0, 2))];
_vtsVobs.VideoStream.Standard = ArrayOfStandard[BinToInt(MidStr(data, 2, 2))];
_vtsVobs.VideoStream.Aspect = ArrayOfAspect[BinToInt(MidStr(data, 4, 2))];
if (_vtsVobs.VideoStream.Standard == "PAL")
_vtsVobs.VideoStream.Resolution = ArrayOfPALResolution[BinToInt(MidStr(data, 13, 2))];
else if (_vtsVobs.VideoStream.Standard == "NTSC")
_vtsVobs.VideoStream.Resolution = ArrayOfNTSCResolution[BinToInt(MidStr(data, 13, 2))];
//retrieve audio info
_fs.Position = 0x202; //useless but here for readability
_vtsVobs.NumberOfAudioStreams = GetEndian(2);
// _ifo.VtsVobs.AudioStreams = new List<AudioStream>();
for (int i = 0; i < _vtsVobs.NumberOfAudioStreams; i++)
{
AudioStream audioStream = new AudioStream();
data = IntToBin(GetEndian(2), 16);
audioStream.LangageTypeSpecified = Convert.ToInt32(MidStr(data, 4, 2));
audioStream.CodingMode = ArrayOfAudioMode[(BinToInt(MidStr(data, 0, 3)))];
audioStream.Channels = BinToInt(MidStr(data, 13, 3)) + 1;
_fs.Read(buffer, 0, 2);
audioStream.LangageCode = Convert.ToChar(buffer[0]).ToString() + Convert.ToChar(buffer[1]).ToString();
if (ArrayOfLangageCode.Contains(audioStream.LangageCode))
audioStream.Langage = ArrayOfLangage[ArrayOfLangageCode.IndexOf(audioStream.LangageCode)];
_fs.Seek(1, SeekOrigin.Current);
audioStream.Extension = ArrayOfAudioExtension[_fs.ReadByte()];
_fs.Seek(2, SeekOrigin.Current);
_vtsVobs.AudioStreams.Add(audioStream);
}
//retrieve subs info (only name)
_fs.Position = 0x254;
_vtsVobs.NumberOfSubtitles = GetEndian(2);
_fs.Position += 2;
for (int i = 0; i < _vtsVobs.NumberOfSubtitles; i++)
{
_fs.Read(buffer, 0, 2);
string languageTwoLetter = Convert.ToChar(buffer[0]).ToString() + Convert.ToChar(buffer[1]).ToString();
_vtsVobs.Subtitles.Add(InterpretLangageCode(languageTwoLetter));
string subtitleFormat = string.Empty;
_fs.Read(buffer, 0, 2); // reserved for language code extension + code extension
switch (buffer[0]) // 4, 8, 10-12 unused
{
// http://dvd.sourceforge.net/dvdinfo/sprm.html
case 1: subtitleFormat = "(caption/normal size char)"; break; //0 = unspecified caption
case 2: subtitleFormat = "(caption/large size char)"; break;
case 3: subtitleFormat = "(caption for children)"; break;
case 5: subtitleFormat = "(closed caption/normal size char)"; break;
case 6: subtitleFormat = "(closed caption/large size char)"; break;
case 7: subtitleFormat = "(closed caption for children)"; break;
case 9: subtitleFormat = "(forced caption)"; break;
case 13: subtitleFormat = "(director comments/normal size char)"; break;
case 14: subtitleFormat = "(director comments/large size char)"; break;
case 15: subtitleFormat = "(director comments for children)"; break;
}
//// int languageId = buffer[1] & Helper.B11111000;
// int languageId1 = buffer[0] & Helper.B11111000;
// int languageId2= buffer[1] & Helper.B11111000;
// System.Diagnostics.Debug.WriteLine(languageTwoLetter + " " + languageId1.ToString() + " " + languageId2.ToString() + " " + buffer[0].ToString() + " " + buffer[1].ToString());
_fs.Position += 2;
}
}
private int BinToInt(string p)
{
return Convert.ToInt32(p, 2);
}
private string MidStr(string data, int start, int count)
{
return data.Substring(start, count);
}
private string IntToBin(int value, int digits)
{
string result = string.Empty;
result = Convert.ToString(value, 2);
while (result.Length < digits)
result = "0" + result;
return result;
}
private int GetEndian(int count)
{
int result = 0;
for (int i = count; i > 0; i--)
{
int b = _fs.ReadByte();
result = (result << 8) + b;
}
return result;
}
private string InterpretLangageCode(string code)
{
int i = 0;
while (ArrayOfLangageCode[i] != code && i < 143)
{
i++;
}
return ArrayOfLangage[i];
}
private void ParseVtsPgci()
{
const int SectorSize = 2048;
_fs.Position = 0xCC; //Get VTS_PGCI adress
int tableStart = SectorSize * GetEndian(4);
_fs.Position = tableStart;
_vtsPgci.NumberOfProgramChains = GetEndian(2);
_vtsPgci.ProgramChains = new List<ProgramChain>();
for (int i = 0; i < _vtsPgci.NumberOfProgramChains; i++)
{
//Parse PGC Header
ProgramChain programChain = new ProgramChain();
_fs.Position = tableStart + 4 + 8 * (i + 1); //Get PGC adress
int programChainAdress = GetEndian(4);
_fs.Position = tableStart + programChainAdress + 2; //Move to PGC
programChain.NumberOfPGC = _fs.ReadByte();
programChain.NumberOfCells = _fs.ReadByte();
programChain.PlaybackTime = InterpretTime(GetEndian(4));
_fs.Seek(4, SeekOrigin.Current);
// check if audio streams are available for this PGC
_fs.Position = tableStart + programChainAdress + 0xC;
for (int j = 0; j < _vtsVobs.NumberOfAudioStreams; j++)
{
string temp = IntToBin(_fs.ReadByte(), 8);
programChain.AudioStreamsAvailable.Add(temp[0]);
_fs.Seek(1, SeekOrigin.Current);
}
// check if subtitles are available for this PGC
_fs.Position = tableStart + programChainAdress + 0x1C;
for (int j = 0; j < _vtsVobs.NumberOfSubtitles; j++)
{
string temp = IntToBin(_fs.ReadByte(), 8);
programChain.SubtitlesAvailable.Add(temp[0]);
_fs.Seek(3, SeekOrigin.Current);
}
//Parse Color LookUp Table (CLUT) - offset 00A4, 16*4 (0, Y, Cr, Cb)
_fs.Position = tableStart + programChainAdress + 0xA4;
for (int colorNumber = 0; colorNumber < 16; colorNumber++)
{
byte[] colors = new byte[4];
_fs.Read(colors, 0, 4);
int y = colors[1]-16;
int cr = colors[2]-128;
int cb = colors[3]-128;
int r = (int)Math.Min(Math.Max(Math.Round(1.1644F * y + 1.596F * cr), 0), 255);
int g = (int)Math.Min(Math.Max(Math.Round(1.1644F * y - 0.813F * cr - 0.391F * cb), 0), 255);
int b = (int)Math.Min(Math.Max(Math.Round(1.1644F * y + 2.018F * cb), 0), 255);
programChain.ColorLookupTable.Add(Color.FromArgb(r, g, b));
}
//Parse Program Map
_fs.Position = tableStart + programChainAdress + 0xE6;
_fs.Position = tableStart + programChainAdress + GetEndian(2);
for (int j = 0; j < programChain.NumberOfPGC; j++)
{
programChain.PGCEntryCells.Add((byte)_fs.ReadByte());
}
// Cell Playback Info Table to retrieve duration
_fs.Position = tableStart + programChainAdress + 0xE8;
_fs.Position = tableStart + programChainAdress + GetEndian(2);
int max = 0;
List<int> timeArray = new List<int>();
for (int k = 0; k < programChain.NumberOfPGC; k++)
{
int time = 0;
if (k == programChain.NumberOfPGC - 1)
max = programChain.NumberOfCells;
else
max = programChain.PGCEntryCells[k + 1] - 1;
for (int j = programChain.PGCEntryCells[k]; j <= max; j++)
{
_fs.Seek(4, SeekOrigin.Current);
time += TimeToMs(GetEndian(4));
_fs.Seek(16, SeekOrigin.Current);
}
programChain.PGCPlaybackTimes.Add(MsToTime(time));
timeArray.Add(time);
//convert to start time
time = 0;
for (int l = 1; l <= k; l++)
{
time += timeArray[l - 1];
}
if (k == 0)
programChain.PGCStartTimes.Add(MsToTime(0));
if (k > 0)
programChain.PGCStartTimes.Add(MsToTime(time));
}
_vtsPgci.ProgramChains.Add(programChain);
}
}
private int StrToInt(string s, int number)
{
return int.Parse(s);
}
private int TimeToMs(int time)
{
int result;
string temp;
double fps;
temp = IntToBin(time, 32);
result = StrToInt(IntToHex(BinToInt(MidStr(temp,0,8)),1))*3600000;
result = result + StrToInt(IntToHex(BinToInt(MidStr(temp,8,8)),2))*60000;
result = result + StrToInt(IntToHex(BinToInt(MidStr(temp,16,8)),2))*1000;
if (temp.Substring(24,2) == "11")
fps = 30;
else
fps = 25;
result += (int) Math.Round((double)(1000.0/fps)*StrToFloat(IntToHex(BinToInt(MidStr(temp,26,6)),3)));
return result;
}
private double StrToFloat(string p)
{
return Convert.ToDouble(p);
}
private int StrToInt(string p)
{
return int.Parse(p);
}
private string IntToHex(int value, int digits)
{
string hex = value.ToString("X");
return hex.PadLeft(digits, '0');
}
private string MsToTime(double milliseconds)
{
TimeSpan ts = TimeSpan.FromMilliseconds(milliseconds);
string s = string.Format("{0:#0}:{1:00}:{2:00}.{3:000}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
return s;
}
private string InterpretTime(int timeNumber)
{
string timeBytes = IntToBin(timeNumber, 32);
int h = StrToInt(IntToHex(BinToInt(timeBytes.Substring(0,8)),1));
int m = StrToInt(IntToHex(BinToInt(timeBytes.Substring(8,8)),2));
int s = StrToInt(IntToHex(BinToInt(timeBytes.Substring(16,8)),2));
int fps = 25;
if (timeBytes.Substring(24, 2) == "11")
fps = 30;
int milliseconds = (int)Math.Round((1000.0/fps)*StrToFloat(IntToHex(BinToInt(timeBytes.Substring(26,6)),3)));
TimeSpan ts = new TimeSpan(0, h, m, s, milliseconds);
return MsToTime(ts.TotalMilliseconds);
}
}
}

361
src/Logic/ImageSplitter.cs Normal file
View File

@ -0,0 +1,361 @@
using System;
using System.Collections.Generic;
using System.Drawing;
namespace Nikse.SubtitleEdit.Logic
{
public class ImageSplitter
{
public static bool IsColorClose(Color a, Color b, int tolerance)
{
int diff = (a.R + a.G + a.B) - (b.R + b.G + b.B);
return diff < tolerance && diff > -tolerance;
}
public static Bitmap Copy(Bitmap sourceBitmap, Rectangle section)
{
// Create the new bitmap and associated graphics object
var bmp = new Bitmap(section.Width, section.Height);
Graphics g = Graphics.FromImage(bmp);
// Draw the specified section of the source bitmap to the new one
g.DrawImage(sourceBitmap, 0, 0, section, GraphicsUnit.Pixel);
// Clean up
g.Dispose();
// Return the bitmap
return bmp;
}
private static Bitmap CropTopAndBottom(Bitmap bmp, out int topCropping)
{
int startTop = 0;
int maxTop = bmp.Height-2;
if (maxTop > bmp.Height)
maxTop = bmp.Height;
for (int y = 0; y < maxTop; y++)
{
bool allTransparent = true;
for (int x = 1; x < bmp.Width - 1; x++)
{
Color c = bmp.GetPixel(x, y);
if (c.A != 0)
{
allTransparent = false;
break;
}
}
if (!allTransparent)
break;
startTop++;
}
if (startTop > 9)
startTop -= 5; // if top space > 9, then allways leave blank 5 pixels on top (so . is not confused with ').
topCropping = startTop;
for (int y = bmp.Height-1; y > 3; y--)
{
bool allTransparent = true;
for (int x = 1; x < bmp.Width-1; x++)
{
Color c = bmp.GetPixel(x, y);
if (c.A != 0)
{
allTransparent = false;
break;
}
}
if (allTransparent == false)
return Copy(bmp, new Rectangle(0, startTop, bmp.Width - 1, y-startTop+1));
}
return bmp;
}
public static List<ImageSplitterItem> SplitVertical(Bitmap bmp)
{ // split into lines
int startY = 0;
int size = 0;
var parts = new List<ImageSplitterItem>();
for (int y = 0; y < bmp.Height; y++)
{
bool allTransparent = true;
for (int x = 0; x < bmp.Width; x++)
{
Color c = bmp.GetPixel(x, y);
if (c.A != 0)
{
allTransparent = false;
break;
}
}
if (allTransparent)
{
if (size > 2 && size < 6)
{
size++; // at least 5 pixels, like top of 'i'
}
else
{
if (size > 2)
{
Bitmap part = Copy(bmp, new Rectangle(0, startY, bmp.Width, size+1));
// part.Save("c:\\line_0_to_width.bmp");
parts.Add(new ImageSplitterItem(0, startY, part));
// bmp.Save("c:\\orginal.bmp");
}
size = 0;
startY = y;
}
}
else
{
size++;
}
}
if (size > 2)
{
Bitmap part = Copy(bmp, new Rectangle(0, startY, bmp.Width, size+1));
parts.Add(new ImageSplitterItem(0, startY, part));
}
return parts;
}
public static int IsBitmapsAlike(Bitmap bmp1, Bitmap bmp2)
{
int different = 0;
for (int x = 1; x < bmp1.Width; x++)
{
for (int y = 1; y < bmp1.Height; y++)
{
if (!IsColorClose(bmp1.GetPixel(x, y), bmp2.GetPixel(x, y), 20))
different++;
}
}
return different;
}
public static int IsBitmapsAlike(FastBitmap bmp1, Bitmap bmp2)
{
int different = 0;
for (int x = 1; x < bmp1.Width; x++)
{
for (int y = 1; y < bmp1.Height; y++)
{
Color c1 = bmp1.GetPixel(x, y);
Color c2 = bmp1.GetPixel(x, y);
if (!IsColorClose(c1, c2, 20))
different++;
}
}
return different;
}
private static List<ImageSplitterItem> SplitHorizontal(ImageSplitterItem verticalItem, int xOrMorePixelsMakesSpace)
{ // split line into letters
Bitmap bmp = verticalItem.Bitmap;
var parts = new List<ImageSplitterItem>();
int size = 0;
int startX = 0;
int lastEndX = 0;
int y = 0;
for (int x = 0; x < bmp.Width - 1; x++)
{
bool allTransparent = IsVerticalLineTransparent(bmp, ref y, x);
// check if line is transparent and cursive
bool cursiveOk = false;
int tempY = 0;
if (allTransparent == false &&
size > 5 &&
y > 3 &&
x < bmp.Width-2 &&
!IsVerticalLineTransparent(bmp, ref tempY, x + 1))
{
var cursivePoints = new List<Point>();
cursiveOk = IsCursiveVerticalLineTransparent(bmp, size, y, x, cursivePoints);
if (cursiveOk)
{
// make letter image
int end = x + 1 - startX;
if (startX > 0)
{
startX--;
end++;
}
Bitmap b1 = Copy(bmp, new Rectangle(startX, 0, end, bmp.Height));
// b1.Save("c:\\cursive.bmp");
// make non-black/transparent stuff from other letter transparent
foreach (Point p in cursivePoints)
{
for (int fixY = p.Y; fixY < bmp.Height; fixY++)
b1.SetPixel(p.X - startX, fixY, Color.Transparent);
}
RemoveBlackBarRight(b1);
// b1.Save("c:\\cursive-cleaned.bmp");
// crop and save image
int addY;
b1 = CropTopAndBottom(b1, out addY);
parts.Add(new ImageSplitterItem(startX, verticalItem.Y + addY, b1));
size = 0;
startX = x + 1;
lastEndX = x;
}
}
if (!cursiveOk)
{
if (allTransparent)
{
if (size > 0)
{
if (size > 1)
{
//Add space?
if (lastEndX > 0 && lastEndX + xOrMorePixelsMakesSpace < startX)
parts.Add(new ImageSplitterItem(" "));
if (startX > 0)
startX--;
lastEndX = x;
int end = x + 1 - startX;
Bitmap part = Copy(bmp, new Rectangle(startX, 0, end, bmp.Height));
RemoveBlackBarRight(part);
int addY;
// part.Save("c:\\before" + startX.ToString() + ".bmp");
part = CropTopAndBottom(part, out addY);
// part.Save("c:\\after" + startX.ToString() + ".bmp");
parts.Add(new ImageSplitterItem(startX, verticalItem.Y + addY, part));
}
size = 0;
}
startX = x + 1;
}
else
{
size++;
}
}
}
if (size > 0)
{
if (lastEndX > 0 && lastEndX + xOrMorePixelsMakesSpace < startX)
parts.Add(new ImageSplitterItem(" "));
if (startX > 0)
startX--;
lastEndX = bmp.Width-1;
int end = lastEndX + 1 - startX;
Bitmap part = Copy(bmp, new Rectangle(startX, 0, end, bmp.Height - 1));
int addY;
part = CropTopAndBottom(part, out addY);
parts.Add(new ImageSplitterItem(startX, verticalItem.Y + addY, part));
}
return parts;
}
private static void RemoveBlackBarRight(Bitmap bmp)
{
int xRemoveBlackBar = bmp.Width-1;
for (int yRemoveBlackBar = 0; yRemoveBlackBar < bmp.Height; yRemoveBlackBar++)
{
Color c = bmp.GetPixel(xRemoveBlackBar, yRemoveBlackBar);
if (c.A == 0 || IsColorClose(c, Color.Black, 280))
{
if (bmp.GetPixel(xRemoveBlackBar - 1, yRemoveBlackBar).A == 0)
bmp.SetPixel(xRemoveBlackBar, yRemoveBlackBar, Color.Transparent);
}
}
}
private static bool IsCursiveVerticalLineTransparent(Bitmap bmp, int size, int y, int x, List<Point> cursivePoints)
{
bool cursiveOk = true;
int newY = y;
int newX = x;
while (cursiveOk && newY < bmp.Height - 1)
{
Color c0 = bmp.GetPixel(newX, newY);
if (c0.A == 0 || IsColorClose(c0, Color.Black, 280))
{
newY++;
}
else
{
Color c1 = bmp.GetPixel(newX - 1, newY - 1);
Color c2 = bmp.GetPixel(newX - 1, newY);
if ((c1.A == 0 || IsColorClose(c1, Color.Black, 280)) && // still dark color...
(c2.A == 0 || IsColorClose(c2, Color.Black, 280)))
{
cursivePoints.Add(new Point(newX, newY));
newX--;
newY++;
}
else
{
cursiveOk = false;
}
}
if (newX < x - size)
cursiveOk = false;
}
return cursiveOk;
}
private static bool IsVerticalLineTransparent(Bitmap bmp, ref int y, int x)
{
bool allTransparent = true;
for (y = 0; y < bmp.Height - 1; y++)
{
Color c = bmp.GetPixel(x, y);
if (c.A == 0 || //c.ToArgb() == transparentColor.ToArgb() ||
IsColorClose(c, Color.Black, 280)) // still dark color...
{
}
else
{
allTransparent = false;
break;
}
}
return allTransparent;
}
public static List<ImageSplitterItem> SplitBitmapToLetters(Bitmap bmp, int xOrMorePixelsMakesSpace)
{
var list = new List<ImageSplitterItem>();
// split into seperate lines
List<ImageSplitterItem> verticalBitmaps = SplitVertical(bmp);
// split into letters
int lineCount = 0;
foreach (ImageSplitterItem b in verticalBitmaps)
{
if (lineCount > 0)
list.Add(new ImageSplitterItem(Environment.NewLine));
foreach (ImageSplitterItem item in SplitHorizontal(b, xOrMorePixelsMakesSpace))
{
list.Add(item);
}
lineCount++;
}
return list;
}
}
}

View File

@ -0,0 +1,28 @@
using System.Drawing;
namespace Nikse.SubtitleEdit.Logic
{
public class ImageSplitterItem
{
public int X { get; set; }
public int Y { get; set; }
public Bitmap Bitmap { get; set; }
public string SpecialCharacter { get; set; }
public ImageSplitterItem(int x, int y, Bitmap bitmap)
{
X = x;
Y = y;
Bitmap = bitmap;
SpecialCharacter = null;
}
public ImageSplitterItem(string specialCharacter)
{
X = 0;
Y = 0;
SpecialCharacter = specialCharacter;
Bitmap = null;
}
}
}

1362
src/Logic/Language.cs Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

105
src/Logic/Paragraph.cs Normal file
View File

@ -0,0 +1,105 @@
using System;
namespace Nikse.SubtitleEdit.Logic
{
public class Paragraph
{
public int Number { get; set; }
public string Text { get; set; }
public TimeCode StartTime { get; set; }
public TimeCode EndTime { get; set; }
public TimeCode Duration
{
get
{
var timeCode = new TimeCode(EndTime.TimeSpan);
timeCode.AddTime(- StartTime.TotalMilliseconds);
return timeCode;
}
}
public int StartFrame { get; set; }
public int EndFrame { get; set; }
public Paragraph()
{
StartTime = new TimeCode(TimeSpan.FromSeconds(0));
EndTime = new TimeCode(TimeSpan.FromSeconds(0));
Text = string.Empty;
}
public Paragraph(TimeCode startTime, TimeCode endTime, string text)
{
StartTime = startTime;
EndTime = endTime;
Text = text;
}
public Paragraph(Paragraph paragraph)
{
Number = paragraph.Number;
Text = paragraph.Text;
StartTime = new TimeCode(paragraph.StartTime.TimeSpan);
EndTime = new TimeCode(paragraph.EndTime.TimeSpan);
StartFrame = paragraph.StartFrame;
EndFrame = paragraph.EndFrame;
}
public Paragraph(int startFrame, int endFrame, string text)
{
StartTime = new TimeCode(0, 0, 0, 0);
EndTime = new TimeCode(0, 0, 0, 0);
StartFrame = startFrame;
EndFrame = endFrame;
Text = text;
}
public Paragraph(string text, double startTotalMilliseconds, double endTotalMilliseconds)
{
StartTime = new TimeCode(TimeSpan.FromMilliseconds(startTotalMilliseconds));
EndTime = new TimeCode(TimeSpan.FromMilliseconds(endTotalMilliseconds));
Text = text;
}
internal void Adjust(double factor, double adjust)
{
double seconds = StartTime.TimeSpan.TotalSeconds * factor + adjust;
StartTime.TimeSpan = TimeSpan.FromSeconds(seconds);
seconds = EndTime.TimeSpan.TotalSeconds * factor + adjust;
EndTime.TimeSpan = TimeSpan.FromSeconds(seconds);
}
public void CalculateFrameNumbersFromTimeCodes(double frameRate)
{
StartFrame = (int)(StartTime.TotalMilliseconds / 1000.0 * frameRate);
EndFrame = (int)(EndTime.TotalMilliseconds / 1000.0 * frameRate);
}
public void CalculateTimeCodesFromFrameNumbers(double frameRate)
{
StartTime.TotalMilliseconds = StartFrame * (1000.0 / frameRate);
EndTime.TotalMilliseconds = EndFrame * (1000.0 / frameRate);
}
public override string ToString()
{
return StartTime + " --> " + EndTime + " " + Text;
}
public int NumberOfLines
{
get
{
if (string.IsNullOrEmpty(Text))
return 0;
return Text.Length - Text.Replace(Environment.NewLine, string.Empty).Length;
}
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace Nikse.SubtitleEdit.Logic
{
class PositionAndSize
{
public string Name { get; set; }
public int Left { get; set; }
public int Top { get; set; }
public Size Size { get; set; }
}
}

View File

@ -0,0 +1,56 @@
using System.Collections.Generic;
using System.Windows.Forms;
namespace Nikse.SubtitleEdit.Logic
{
class PositionsAndSizes
{
List<PositionAndSize> _positionsAndSizes = new List<PositionAndSize>();
public void SetPositionAndSize(Form form)
{
if (form == null)
return;
foreach (PositionAndSize ps in _positionsAndSizes)
{
if (ps.Name == form.Name)
{
form.StartPosition = FormStartPosition.Manual;
form.Left = ps.Left;
form.Top = ps.Top;
form.Size = ps.Size;
break;
}
}
}
public void SavePositionAndSize(Form form)
{
if (form == null)
return;
bool found = false;
PositionAndSize pAndS = new PositionAndSize();
foreach (PositionAndSize ps in _positionsAndSizes)
{
if (ps.Name == form.Name)
{
found = true;
pAndS = ps;
break;
}
}
pAndS.Left = form.Left;
pAndS.Top = form.Top;
pAndS.Size = form.Size;
if (!found)
{
pAndS.Name = form.Name;
_positionsAndSizes.Add(pAndS);
}
}
}
}

902
src/Logic/Settings.cs Normal file
View File

@ -0,0 +1,902 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Drawing;
namespace Nikse.SubtitleEdit.Logic
{
// The settings classes are build for easy xml-serilization (makes save/load code simple)
// ...but the built-in serialization is too slow - so a custom (de-)serialization has been used!
public class RecentFilesSettings
{
private const int MaxRecentFiles = 25;
[XmlArrayItem("FileName")]
public List<string> FileNames { get; set; }
public RecentFilesSettings()
{
FileNames = new List<string>();
}
public void Add(string fileName)
{
var newList = new List<string> {fileName};
int index = 0;
foreach (string oldFileName in FileNames)
{
if (string.Compare(fileName, oldFileName, true) != 0 && index < MaxRecentFiles)
newList.Add(oldFileName);
index++;
}
FileNames = newList;
}
}
public class ToolsSettings
{
public int StartSceneIndex { get; set; }
public int EndSceneIndex { get; set; }
public int VerifyPlaySeconds { get; set; }
public int MergeLinesShorterThan { get; set; }
public string MusicSymbol { get; set; }
public string MusicSymbolToReplace { get; set; }
public bool SpellCheckAutoChangeNames { get; set; }
public ToolsSettings()
{
StartSceneIndex = 1;
EndSceneIndex = 1;
VerifyPlaySeconds = 2;
MergeLinesShorterThan = 33;
MusicSymbol = "♪";
MusicSymbolToReplace = "♪ ⶠ♪ âTª ã¢â™âª ?t×3 ?t¤3";
SpellCheckAutoChangeNames = true;
}
}
public class WordListSettings
{
public string LastLanguage { get; set; }
public string NamesEtcUrl { get; set; }
public bool UseOnlineNamesEtc { get; set; }
public WordListSettings()
{
LastLanguage = "en";
NamesEtcUrl = "http://www.nikse.dk/se/Names_Etc.xml";
}
}
public class SsaStyleSettings
{
public string FontName { get; set; }
public double FontSize { get; set; }
public int FontColorArgb { get; set; }
public SsaStyleSettings()
{
FontName = "Microsoft Sans Serif";
FontSize = 18;
FontColorArgb = System.Drawing.Color.FromArgb(255, 255, 255).ToArgb();
}
}
public class ProxySettings
{
public string ProxyAddress { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string Domain { get; set; }
public string DecodePassword()
{
return Encoding.UTF8.GetString(Convert.FromBase64String(Password));
}
public void EncodePassword(string unencryptedPassword)
{
Password = Convert.ToBase64String(Encoding.UTF8.GetBytes(unencryptedPassword));
}
}
public class FixCommonErrorsSettings
{
public bool EmptyLinesTicked { get; set; }
public bool OverlappingDisplayTimeTicked { get; set; }
public bool TooShortDisplayTimeTicked { get; set; }
public bool TooLongDisplayTimeTicked { get; set; }
public bool InvalidItalicTagsTicked { get; set; }
public bool BreakLongLinesTicked { get; set; }
public bool MergeShortLinesTicked { get; set; }
public bool UnneededSpacesTicked { get; set; }
public bool UnneededPeriodsTicked { get; set; }
public bool MissingSpacesTicked { get; set; }
public bool AddMissingQuotesTicked { get; set; }
public bool Fix3PlusLinesTicked { get; set; }
public bool FixHyphensTicked { get; set; }
public bool UppercaseIInsideLowercaseWordTicked { get; set; }
public bool DoubleApostropheToQuoteTicked { get; set; }
public bool AddPeriodAfterParagraphTicked { get; set; }
public bool StartWithUppercaseLetterAfterParagraphTicked { get; set; }
public bool StartWithUppercaseLetterAfterPeriodInsideParagraphTicked { get; set; }
public bool AloneLowercaseIToUppercaseIEnglishTicked { get; set; }
public bool FixOcrErrorsViaReplaceListTicked { get; set; }
public bool DanishLetterITicked { get; set; }
public bool SpanishInvertedQuestionAndExclamationMarksTicked { get; set; }
public bool FixDoubleDashTicked { get; set; }
public bool FixDoubleGreaterThanTicked { get; set; }
public bool FixEllipsesStartTicked { get; set; }
public bool FixMissingOpenBracketTicked { get; set; }
public bool FixMusicNotationTicked { get; set; }
public FixCommonErrorsSettings()
{
EmptyLinesTicked = true;
OverlappingDisplayTimeTicked = true;
TooShortDisplayTimeTicked = true;
TooLongDisplayTimeTicked = true;
InvalidItalicTagsTicked = true;
BreakLongLinesTicked = true;
MergeShortLinesTicked = true;
UnneededPeriodsTicked = true;
UnneededSpacesTicked = true;
MissingSpacesTicked = true;
UppercaseIInsideLowercaseWordTicked = true;
DoubleApostropheToQuoteTicked = true;
AddPeriodAfterParagraphTicked = false;
StartWithUppercaseLetterAfterParagraphTicked = true;
StartWithUppercaseLetterAfterPeriodInsideParagraphTicked = false;
AloneLowercaseIToUppercaseIEnglishTicked = false;
DanishLetterITicked = false;
FixDoubleDashTicked = true;
FixDoubleGreaterThanTicked = true;
FixEllipsesStartTicked = true;
FixMissingOpenBracketTicked = true;
FixMusicNotationTicked = true;
}
}
public class GeneralSettings
{
public bool ShowToolbarNew { get; set; }
public bool ShowToolbarOpen { get; set; }
public bool ShowToolbarSave { get; set; }
public bool ShowToolbarSaveAs { get; set; }
public bool ShowToolbarFind { get; set; }
public bool ShowToolbarReplace { get; set; }
public bool ShowToolbarVisualSync { get; set; }
public bool ShowToolbarSpellCheck { get; set; }
public bool ShowToolbarSettings { get; set; }
public bool ShowToolbarHelp { get; set; }
public bool ShowVideoPlayer { get; set; }
public bool ShowWaveForm { get; set; }
public bool ShowFrameRate { get; set; }
public double DefaultFrameRate { get; set; }
public string DefaultEncoding { get; set; }
public bool AutoGuessAnsiEncoding { get; set; }
public string SubtitleFontName { get; set; }
public int SubtitleFontSize { get; set; }
public bool SubtitleFontBold { get; set; }
public bool ShowRecentFiles { get; set; }
public bool StartLoadLastFile { get; set; }
public bool StartRememberPositionAndSize { get; set; }
public string StartPosition { get; set; }
public string StartSize { get; set; }
public bool StartInSourceView { get; set; }
public bool RemoveBlankLinesWhenOpening { get; set; }
public int SubtitleLineMaximumLength { get; set; }
public string SpellCheckLanguage { get; set; }
public string VideoPlayer { get; set; }
public int VideoPlayerDefaultVolume { get; set; }
public bool VideoPlayerShowStopButton { get; set; }
public string Language { get; set; }
public string ListViewLineSeparatorString { get; set; }
public int ListViewDoubleClickAction { get; set; }
public string UppercaseLetters { get; set; }
public int DefaultAdjustMilliseconds { get; set; }
public GeneralSettings()
{
ShowToolbarNew = true;
ShowToolbarOpen = true;
ShowToolbarSave = true;
ShowToolbarSaveAs = false;
ShowToolbarFind = true;
ShowToolbarReplace = true;
ShowToolbarVisualSync = true;
ShowToolbarSpellCheck = true;
ShowToolbarSettings = false;
ShowToolbarHelp = true;
ShowVideoPlayer = false;
ShowWaveForm = false;
ShowFrameRate = false;
DefaultFrameRate = 23.976;
SubtitleFontName = "Microsoft Sans Serif";
if (Environment.OSVersion.Version.Major < 6) // 6 == Vista/Win2008Server/Win7
SubtitleFontName = "Lucida Sans Unicode";
SubtitleFontSize = 8;
SubtitleFontBold = false;
DefaultEncoding = "UTF-8";
AutoGuessAnsiEncoding = false;
ShowRecentFiles = true;
StartLoadLastFile = true;
StartRememberPositionAndSize = true;
SubtitleLineMaximumLength = 65;
SpellCheckLanguage = null;
VideoPlayer = string.Empty;
VideoPlayerDefaultVolume = 50;
VideoPlayerShowStopButton = true;
ListViewLineSeparatorString = "<br />";
ListViewDoubleClickAction = 1;
UppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWZYXÆØÃÅÄÖÉÈÁÂÀÇÉÊÍÓÔÕÚŁ";
DefaultAdjustMilliseconds = 1000;
}
}
public class VideoControlsSettings
{
public string CustomSearchText { get; set; }
public string CustomSearchUrl { get; set; }
public string LastActiveTab { get; set; }
public bool WaveFormDrawGrid { get; set; }
public Color WaveFormGridColor { get; set; }
public Color WaveFormColor { get; set; }
public Color WaveFormSelectedColor { get; set; }
public Color WaveFormBackgroundColor { get; set; }
public Color WaveFormTextColor { get; set; }
public VideoControlsSettings()
{
CustomSearchText = "MS Encarta Thesaurus";
CustomSearchUrl = "http://encarta.msn.com/encnet/features/dictionary/DictionaryResults.aspx?lextype=2&search={0}";
LastActiveTab = "Translate";
WaveFormDrawGrid = true;
WaveFormGridColor = Color.FromArgb(255, 20, 20, 18);
WaveFormColor = Color.GreenYellow;
WaveFormSelectedColor = Color.Red;
WaveFormBackgroundColor = Color.Black;
WaveFormTextColor = Color.Gray;
}
}
public class VobSubOcrSettings
{
public int XOrMorePixelsMakesSpace { get; set; }
public double AllowDifferenceInPercent { get; set; }
public string LastImageCompareFolder { get; set; }
public int LastModiLanguageId { get; set; }
public string LastOcrMethod { get; set; }
public string TesseractLastLanguage { get; set; }
public VobSubOcrSettings()
{
XOrMorePixelsMakesSpace = 5;
AllowDifferenceInPercent = 1.0;
LastImageCompareFolder = "English";
LastModiLanguageId = 9;
LastOcrMethod = "Tesseract";
}
}
public class MultipleSearchAndReplaceSetting
{
public bool Enabled { get; set; }
public string FindWhat { get; set; }
public string ReplaceWith { get; set; }
public string SearchType { get; set; }
}
public class Settings
{
public RecentFilesSettings RecentFiles { get; set; }
public GeneralSettings General { get; set; }
public ToolsSettings Tools { get; set; }
public SsaStyleSettings SsaStyle { get; set; }
public ProxySettings Proxy { get; set; }
public WordListSettings WordLists { get; set; }
public FixCommonErrorsSettings CommonErrors { get; set; }
public VobSubOcrSettings VobSubOcr { get; set; }
public VideoControlsSettings VideoControls { get; set; }
[XmlArrayItem("MultipleSearchAndReplaceItem")]
public List<MultipleSearchAndReplaceSetting> MultipleSearchAndReplaceList { get; set; }
[XmlIgnore]
public Language Language { get; set; }
private Settings()
{
RecentFiles = new RecentFilesSettings();
General = new GeneralSettings();
Tools = new ToolsSettings();
WordLists = new WordListSettings();
SsaStyle = new SsaStyleSettings();
Proxy = new ProxySettings();
CommonErrors = new FixCommonErrorsSettings();
VobSubOcr = new VobSubOcrSettings();
VideoControls = new VideoControlsSettings();
MultipleSearchAndReplaceList = new List<MultipleSearchAndReplaceSetting>();
Language = new Language();
}
public void Save()
{
//slow
//Serialize(Configuration.BaseDirectory + "Settings.xml", this);
//Fast - TODO: Fix in release
CustomSerialize(Configuration.BaseDirectory + "Settings.xml", this);
}
private static void Serialize(string fileName, Settings settings)
{
var s = new XmlSerializer(typeof(Settings));
TextWriter w = new StreamWriter(fileName);
s.Serialize(w, settings);
w.Close();
}
public static Settings GetSettings()
{
Settings settings = new Settings();
if (File.Exists(Configuration.BaseDirectory + "Settings.xml"))
{
try
{
//TODO: Fix in release
settings = CustomDeserialize(Configuration.BaseDirectory + "Settings.xml"); // 15 msecs
//too slow... :(
//settings = Deserialize(Configuration.BaseDirectory + "Settings.xml"); // 688 msecs
}
catch
{
settings = new Settings();
}
if (string.IsNullOrEmpty(settings.General.ListViewLineSeparatorString))
settings.General.ListViewLineSeparatorString = Environment.NewLine;
}
return settings;
}
private static Settings Deserialize(string fileName)
{
var r = new StreamReader(fileName);
var s = new XmlSerializer(typeof(Settings));
var settings = (Settings)s.Deserialize(r);
r.Close();
if (settings.RecentFiles == null)
settings.RecentFiles = new RecentFilesSettings();
if (settings.General == null)
settings.General = new GeneralSettings();
if (settings.SsaStyle == null)
settings.SsaStyle = new SsaStyleSettings();
if (settings.CommonErrors == null)
settings.CommonErrors = new FixCommonErrorsSettings();
if (settings.VideoControls == null)
settings.VideoControls = new VideoControlsSettings();
if (settings.VobSubOcr == null)
settings.VobSubOcr = new VobSubOcrSettings();
if (settings.MultipleSearchAndReplaceList == null)
settings.MultipleSearchAndReplaceList = new List<MultipleSearchAndReplaceSetting>();
return settings;
}
/// <summary>
/// A faster serializer than xml serializer... which is insanely slow (first time)!!!!
/// This method is auto-generated with XmlSerializerGenerator
/// </summary>
/// <param name="fileName">File name of xml settings file to load</param>
/// <returns>Newly loaded settings</returns>
private static Settings CustomDeserialize(string fileName)
{
var doc = new XmlDocument();
doc.Load(fileName);
Settings settings = new Settings();
settings.RecentFiles = new Nikse.SubtitleEdit.Logic.RecentFilesSettings();
XmlNode node = doc.DocumentElement.SelectSingleNode("RecentFiles");
foreach (XmlNode listNode in node.SelectNodes("FileNames/FileName"))
settings.RecentFiles.FileNames.Add(listNode.InnerText);
settings.General = new Nikse.SubtitleEdit.Logic.GeneralSettings();
node = doc.DocumentElement.SelectSingleNode("General");
XmlNode subNode = node.SelectSingleNode("ShowToolbarNew");
if (subNode != null)
settings.General.ShowToolbarNew = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("ShowToolbarOpen");
if (subNode != null)
settings.General.ShowToolbarOpen = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("ShowToolbarSave");
if (subNode != null)
settings.General.ShowToolbarSave = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("ShowToolbarSaveAs");
if (subNode != null)
settings.General.ShowToolbarSaveAs = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("ShowToolbarFind");
if (subNode != null)
settings.General.ShowToolbarFind = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("ShowToolbarReplace");
if (subNode != null)
settings.General.ShowToolbarReplace = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("ShowToolbarVisualSync");
if (subNode != null)
settings.General.ShowToolbarVisualSync = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("ShowToolbarSpellCheck");
if (subNode != null)
settings.General.ShowToolbarSpellCheck = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("ShowToolbarSettings");
if (subNode != null)
settings.General.ShowToolbarSettings = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("ShowToolbarHelp");
if (subNode != null)
settings.General.ShowToolbarHelp = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("ShowFrameRate");
if (subNode != null)
settings.General.ShowFrameRate = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("ShowVideoPlayer");
if (subNode != null)
settings.General.ShowVideoPlayer = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("ShowWaveForm");
if (subNode != null)
settings.General.ShowWaveForm = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("DefaultFrameRate");
if (subNode != null)
settings.General.DefaultFrameRate = Convert.ToDouble(subNode.InnerText);
subNode = node.SelectSingleNode("DefaultEncoding");
if (subNode != null)
settings.General.DefaultEncoding = subNode.InnerText;
subNode = node.SelectSingleNode("AutoGuessAnsiEncoding");
if (subNode != null)
settings.General.AutoGuessAnsiEncoding = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("SubtitleFontName");
if (subNode != null)
settings.General.SubtitleFontName = subNode.InnerText;
subNode = node.SelectSingleNode("SubtitleFontSize");
if (subNode != null)
settings.General.SubtitleFontSize = Convert.ToInt32(subNode.InnerText);
subNode = node.SelectSingleNode("SubtitleFontBold");
if (subNode != null)
settings.General.SubtitleFontBold = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("ShowRecentFiles");
if (subNode != null)
settings.General.ShowRecentFiles = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("StartLoadLastFile");
if (subNode != null)
settings.General.StartLoadLastFile = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("StartRememberPositionAndSize");
if (subNode != null)
settings.General.StartRememberPositionAndSize = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("StartPosition");
if (subNode != null)
settings.General.StartPosition = subNode.InnerText;
subNode = node.SelectSingleNode("StartSize");
if (subNode != null)
settings.General.StartSize = subNode.InnerText;
subNode = node.SelectSingleNode("StartInSourceView");
if (subNode != null)
settings.General.StartInSourceView = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("RemoveBlankLinesWhenOpening");
if (subNode != null)
settings.General.RemoveBlankLinesWhenOpening = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("SubtitleLineMaximumLength");
if (subNode != null)
settings.General.SubtitleLineMaximumLength = Convert.ToInt32(subNode.InnerText);
subNode = node.SelectSingleNode("SpellCheckLanguage");
if (subNode != null)
settings.General.SpellCheckLanguage = subNode.InnerText;
subNode = node.SelectSingleNode("VideoPlayer");
if (subNode != null)
settings.General.VideoPlayer = subNode.InnerText;
subNode = node.SelectSingleNode("VideoPlayerDefaultVolume");
if (subNode != null)
settings.General.VideoPlayerDefaultVolume = Convert.ToInt32(subNode.InnerText);
subNode = node.SelectSingleNode("VideoPlayerShowStopButton");
if (subNode != null)
settings.General.VideoPlayerShowStopButton = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("Language");
if (subNode != null)
settings.General.Language = subNode.InnerText;
subNode = node.SelectSingleNode("ListViewLineSeparatorString");
if (subNode != null)
settings.General.ListViewLineSeparatorString = subNode.InnerText;
subNode = node.SelectSingleNode("ListViewDoubleClickAction");
if (subNode != null)
settings.General.ListViewDoubleClickAction = Convert.ToInt32(subNode.InnerText);
subNode = node.SelectSingleNode("UppercaseLetters");
if (subNode != null)
settings.General.UppercaseLetters = subNode.InnerText;
subNode = node.SelectSingleNode("DefaultAdjustMilliseconds");
if (subNode != null)
settings.General.DefaultAdjustMilliseconds = Convert.ToInt32(subNode.InnerText);
settings.Tools = new Nikse.SubtitleEdit.Logic.ToolsSettings();
node = doc.DocumentElement.SelectSingleNode("Tools");
subNode = node.SelectSingleNode("StartSceneIndex");
if (subNode != null)
settings.Tools.StartSceneIndex = Convert.ToInt32(subNode.InnerText);
subNode = node.SelectSingleNode("EndSceneIndex");
if (subNode != null)
settings.Tools.EndSceneIndex = Convert.ToInt32(subNode.InnerText);
subNode = node.SelectSingleNode("VerifyPlaySeconds");
if (subNode != null)
settings.Tools.VerifyPlaySeconds = Convert.ToInt32(subNode.InnerText);
subNode = node.SelectSingleNode("MergeLinesShorterThan");
if (subNode != null)
settings.Tools.MergeLinesShorterThan = Convert.ToInt32(subNode.InnerText);
subNode = node.SelectSingleNode("MusicSymbol");
if (subNode != null)
settings.Tools.MusicSymbol = subNode.InnerText;
subNode = node.SelectSingleNode("MusicSymbolToReplace");
if (subNode != null)
settings.Tools.MusicSymbolToReplace = subNode.InnerText;
subNode = node.SelectSingleNode("SpellCheckAutoChangeNames");
if (subNode != null)
settings.Tools.SpellCheckAutoChangeNames = Convert.ToBoolean(subNode.InnerText);
settings.SsaStyle = new Nikse.SubtitleEdit.Logic.SsaStyleSettings();
node = doc.DocumentElement.SelectSingleNode("SsaStyle");
subNode = node.SelectSingleNode("FontName");
if (subNode != null)
settings.SsaStyle.FontName = subNode.InnerText;
subNode = node.SelectSingleNode("FontSize");
if (subNode != null)
settings.SsaStyle.FontSize = Convert.ToDouble(subNode.InnerText);
subNode = node.SelectSingleNode("FontColorArgb");
if (subNode != null)
settings.SsaStyle.FontColorArgb = Convert.ToInt32(subNode.InnerText);
settings.Proxy = new Nikse.SubtitleEdit.Logic.ProxySettings();
node = doc.DocumentElement.SelectSingleNode("Proxy");
subNode = node.SelectSingleNode("ProxyAddress");
if (subNode != null)
settings.Proxy.ProxyAddress = subNode.InnerText;
subNode = node.SelectSingleNode("UserName");
if (subNode != null)
settings.Proxy.UserName = subNode.InnerText;
subNode = node.SelectSingleNode("Password");
if (subNode != null)
settings.Proxy.Password = subNode.InnerText;
subNode = node.SelectSingleNode("Domain");
if (subNode != null)
settings.Proxy.Domain = subNode.InnerText;
settings.WordLists = new Nikse.SubtitleEdit.Logic.WordListSettings();
node = doc.DocumentElement.SelectSingleNode("WordLists");
subNode = node.SelectSingleNode("LastLanguage");
if (subNode != null)
settings.WordLists.LastLanguage = subNode.InnerText;
subNode = node.SelectSingleNode("NamesEtcUrl");
if (subNode != null)
settings.WordLists.NamesEtcUrl = subNode.InnerText;
subNode = node.SelectSingleNode("UseOnlineNamesEtc");
if (subNode != null)
settings.WordLists.UseOnlineNamesEtc = Convert.ToBoolean(subNode.InnerText);
settings.CommonErrors = new Nikse.SubtitleEdit.Logic.FixCommonErrorsSettings();
node = doc.DocumentElement.SelectSingleNode("CommonErrors");
subNode = node.SelectSingleNode("EmptyLinesTicked");
if (subNode != null)
settings.CommonErrors.EmptyLinesTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("OverlappingDisplayTimeTicked");
if (subNode != null)
settings.CommonErrors.OverlappingDisplayTimeTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("TooShortDisplayTimeTicked");
if (subNode != null)
settings.CommonErrors.TooShortDisplayTimeTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("TooLongDisplayTimeTicked");
if (subNode != null)
settings.CommonErrors.TooLongDisplayTimeTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("InvalidItalicTagsTicked");
if (subNode != null)
settings.CommonErrors.InvalidItalicTagsTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("BreakLongLinesTicked");
if (subNode != null)
settings.CommonErrors.BreakLongLinesTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("MergeShortLinesTicked");
if (subNode != null)
settings.CommonErrors.MergeShortLinesTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("UnneededSpacesTicked");
if (subNode != null)
settings.CommonErrors.UnneededSpacesTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("UnneededPeriodsTicked");
if (subNode != null)
settings.CommonErrors.UnneededPeriodsTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("MissingSpacesTicked");
if (subNode != null)
settings.CommonErrors.MissingSpacesTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("AddMissingQuotesTicked");
if (subNode != null)
settings.CommonErrors.AddMissingQuotesTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("Fix3PlusLinesTicked");
if (subNode != null)
settings.CommonErrors.Fix3PlusLinesTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("FixHyphensTicked");
if (subNode != null)
settings.CommonErrors.FixHyphensTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("UppercaseIInsideLowercaseWordTicked");
if (subNode != null)
settings.CommonErrors.UppercaseIInsideLowercaseWordTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("DoubleApostropheToQuoteTicked");
if (subNode != null)
settings.CommonErrors.DoubleApostropheToQuoteTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("AddPeriodAfterParagraphTicked");
if (subNode != null)
settings.CommonErrors.AddPeriodAfterParagraphTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("StartWithUppercaseLetterAfterParagraphTicked");
if (subNode != null)
settings.CommonErrors.StartWithUppercaseLetterAfterParagraphTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("StartWithUppercaseLetterAfterPeriodInsideParagraphTicked");
if (subNode != null)
settings.CommonErrors.StartWithUppercaseLetterAfterPeriodInsideParagraphTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("AloneLowercaseIToUppercaseIEnglishTicked");
if (subNode != null)
settings.CommonErrors.AloneLowercaseIToUppercaseIEnglishTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("FixOcrErrorsViaReplaceListTicked");
if (subNode != null)
settings.CommonErrors.FixOcrErrorsViaReplaceListTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("DanishLetterITicked");
if (subNode != null)
settings.CommonErrors.DanishLetterITicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("SpanishInvertedQuestionAndExclamationMarksTicked");
if (subNode != null)
settings.CommonErrors.SpanishInvertedQuestionAndExclamationMarksTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("FixDoubleDashTicked");
if (subNode != null)
settings.CommonErrors.FixDoubleDashTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("FixDoubleGreaterThanTicked");
if (subNode != null)
settings.CommonErrors.FixDoubleGreaterThanTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("FixEllipsesStartTicked");
if (subNode != null)
settings.CommonErrors.FixEllipsesStartTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("FixMissingOpenBracketTicked");
if (subNode != null)
settings.CommonErrors.FixMissingOpenBracketTicked = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("FixMusicNotationTicked");
if (subNode != null)
settings.CommonErrors.FixMusicNotationTicked = Convert.ToBoolean(subNode.InnerText);
settings.VideoControls = new VideoControlsSettings();
node = doc.DocumentElement.SelectSingleNode("VideoControls");
subNode = node.SelectSingleNode("CustomSearchText");
if (subNode != null)
settings.VideoControls.CustomSearchText = subNode.InnerText;
subNode = node.SelectSingleNode("CustomSearchUrl");
if (subNode != null)
settings.VideoControls.CustomSearchUrl = subNode.InnerText;
subNode = node.SelectSingleNode("LastActiveTab");
if (subNode != null)
settings.VideoControls.LastActiveTab = subNode.InnerText;
subNode = node.SelectSingleNode("WaveFormDrawGrid");
if (subNode != null)
settings.VideoControls.WaveFormDrawGrid = Convert.ToBoolean(subNode.InnerText);
subNode = node.SelectSingleNode("WaveFormGridColor");
if (subNode != null)
settings.VideoControls.WaveFormGridColor = Color.FromArgb(int.Parse(subNode.InnerText));
subNode = node.SelectSingleNode("WaveFormColor");
if (subNode != null)
settings.VideoControls.WaveFormColor = Color.FromArgb(int.Parse(subNode.InnerText));
subNode = node.SelectSingleNode("WaveFormSelectedColor");
if (subNode != null)
settings.VideoControls.WaveFormSelectedColor = Color.FromArgb(int.Parse(subNode.InnerText));
subNode = node.SelectSingleNode("WaveFormBackgroundColor");
if (subNode != null)
settings.VideoControls.WaveFormBackgroundColor = Color.FromArgb(int.Parse(subNode.InnerText));
subNode = node.SelectSingleNode("WaveFormTextColor");
if (subNode != null)
settings.VideoControls.WaveFormTextColor = Color.FromArgb(int.Parse(subNode.InnerText));
settings.VobSubOcr = new Nikse.SubtitleEdit.Logic.VobSubOcrSettings();
node = doc.DocumentElement.SelectSingleNode("VobSubOcr");
subNode = node.SelectSingleNode("XOrMorePixelsMakesSpace");
if (subNode != null)
settings.VobSubOcr.XOrMorePixelsMakesSpace = Convert.ToInt32(subNode.InnerText);
subNode = node.SelectSingleNode("AllowDifferenceInPercent");
if (subNode != null)
settings.VobSubOcr.AllowDifferenceInPercent = Convert.ToDouble(subNode.InnerText);
subNode = node.SelectSingleNode("LastImageCompareFolder");
if (subNode != null)
settings.VobSubOcr.LastImageCompareFolder = subNode.InnerText;
subNode = node.SelectSingleNode("LastModiLanguageId");
if (subNode != null)
settings.VobSubOcr.LastModiLanguageId = Convert.ToInt32(subNode.InnerText);
subNode = node.SelectSingleNode("LastOcrMethod");
if (subNode != null)
settings.VobSubOcr.LastOcrMethod = subNode.InnerText;
subNode = node.SelectSingleNode("TesseractLastLanguage");
if (subNode != null)
settings.VobSubOcr.TesseractLastLanguage = subNode.InnerText;
foreach (XmlNode listNode in doc.DocumentElement.SelectNodes("MultipleSearchAndReplaceList/MultipleSearchAndReplaceItem"))
{
MultipleSearchAndReplaceSetting item = new MultipleSearchAndReplaceSetting();
subNode = listNode.SelectSingleNode("Enabled");
if (subNode != null)
item.Enabled = Convert.ToBoolean(subNode.InnerText);
subNode = listNode.SelectSingleNode("FindWhat");
if (subNode != null)
item.FindWhat = subNode.InnerText;
subNode = listNode.SelectSingleNode("ReplaceWith");
if (subNode != null)
item.ReplaceWith = subNode.InnerText;
subNode = listNode.SelectSingleNode("SearchType");
if (subNode != null)
item.SearchType = subNode.InnerText;
settings.MultipleSearchAndReplaceList.Add(item);
}
return settings;
}
private static void CustomSerialize(string fileName, Settings settings)
{
var textWriter = new XmlTextWriter(fileName, null) {Formatting = Formatting.Indented};
textWriter.WriteStartDocument();
textWriter.WriteStartElement("Settings", "");
textWriter.WriteStartElement("RecentFiles", "");
textWriter.WriteStartElement("FileNames", "");
foreach (var item in settings.RecentFiles.FileNames)
{
textWriter.WriteElementString("FileName", item);
}
textWriter.WriteEndElement();
textWriter.WriteEndElement();
textWriter.WriteStartElement("General", "");
textWriter.WriteElementString("ShowToolbarNew", settings.General.ShowToolbarNew.ToString());
textWriter.WriteElementString("ShowToolbarOpen", settings.General.ShowToolbarOpen.ToString());
textWriter.WriteElementString("ShowToolbarSave", settings.General.ShowToolbarSave.ToString());
textWriter.WriteElementString("ShowToolbarSaveAs", settings.General.ShowToolbarSaveAs.ToString());
textWriter.WriteElementString("ShowToolbarFind", settings.General.ShowToolbarFind.ToString());
textWriter.WriteElementString("ShowToolbarReplace", settings.General.ShowToolbarReplace.ToString());
textWriter.WriteElementString("ShowToolbarVisualSync", settings.General.ShowToolbarVisualSync.ToString());
textWriter.WriteElementString("ShowToolbarSpellCheck", settings.General.ShowToolbarSpellCheck.ToString());
textWriter.WriteElementString("ShowToolbarSettings", settings.General.ShowToolbarSettings.ToString());
textWriter.WriteElementString("ShowToolbarHelp", settings.General.ShowToolbarHelp.ToString());
textWriter.WriteElementString("ShowFrameRate", settings.General.ShowFrameRate.ToString());
textWriter.WriteElementString("ShowVideoPlayer", settings.General.ShowVideoPlayer.ToString());
textWriter.WriteElementString("ShowWaveForm", settings.General.ShowWaveForm.ToString());
textWriter.WriteElementString("DefaultFrameRate", settings.General.DefaultFrameRate.ToString());
textWriter.WriteElementString("DefaultEncoding", settings.General.DefaultEncoding);
textWriter.WriteElementString("AutoGuessAnsiEncoding", settings.General.AutoGuessAnsiEncoding.ToString());
textWriter.WriteElementString("SubtitleFontName", settings.General.SubtitleFontName);
textWriter.WriteElementString("SubtitleFontSize", settings.General.SubtitleFontSize.ToString());
textWriter.WriteElementString("SubtitleFontBold", settings.General.SubtitleFontBold.ToString());
textWriter.WriteElementString("ShowRecentFiles", settings.General.ShowRecentFiles.ToString());
textWriter.WriteElementString("StartLoadLastFile", settings.General.StartLoadLastFile.ToString());
textWriter.WriteElementString("StartRememberPositionAndSize", settings.General.StartRememberPositionAndSize.ToString());
textWriter.WriteElementString("StartPosition", settings.General.StartPosition);
textWriter.WriteElementString("StartSize", settings.General.StartSize);
textWriter.WriteElementString("StartInSourceView", settings.General.StartInSourceView.ToString());
textWriter.WriteElementString("RemoveBlankLinesWhenOpening", settings.General.RemoveBlankLinesWhenOpening.ToString());
textWriter.WriteElementString("SubtitleLineMaximumLength", settings.General.SubtitleLineMaximumLength.ToString());
textWriter.WriteElementString("SpellCheckLanguage", settings.General.SpellCheckLanguage);
textWriter.WriteElementString("VideoPlayer", settings.General.VideoPlayer);
textWriter.WriteElementString("VideoPlayerDefaultVolume", settings.General.VideoPlayerDefaultVolume.ToString());
textWriter.WriteElementString("VideoPlayerShowStopButton", settings.General.VideoPlayerShowStopButton.ToString());
textWriter.WriteElementString("Language", settings.General.Language);
textWriter.WriteElementString("ListViewLineSeparatorString", settings.General.ListViewLineSeparatorString);
textWriter.WriteElementString("ListViewDoubleClickAction", settings.General.ListViewDoubleClickAction.ToString());
textWriter.WriteElementString("UppercaseLetters", settings.General.UppercaseLetters);
textWriter.WriteElementString("DefaultAdjustMilliseconds", settings.General.DefaultAdjustMilliseconds.ToString());
textWriter.WriteEndElement();
textWriter.WriteStartElement("Tools", "");
textWriter.WriteElementString("StartSceneIndex", settings.Tools.StartSceneIndex.ToString());
textWriter.WriteElementString("EndSceneIndex", settings.Tools.EndSceneIndex.ToString());
textWriter.WriteElementString("VerifyPlaySeconds", settings.Tools.VerifyPlaySeconds.ToString());
textWriter.WriteElementString("MergeLinesShorterThan", settings.Tools.MergeLinesShorterThan.ToString());
textWriter.WriteElementString("MusicSymbol", settings.Tools.MusicSymbol);
textWriter.WriteElementString("MusicSymbolToReplace", settings.Tools.MusicSymbolToReplace);
textWriter.WriteElementString("SpellCheckAutoChangeNames", settings.Tools.SpellCheckAutoChangeNames.ToString());
textWriter.WriteEndElement();
textWriter.WriteStartElement("SsaStyle", "");
textWriter.WriteElementString("FontName", settings.SsaStyle.FontName);
textWriter.WriteElementString("FontSize", settings.SsaStyle.FontSize.ToString());
textWriter.WriteElementString("FontColorArgb", settings.SsaStyle.FontColorArgb.ToString());
textWriter.WriteEndElement();
textWriter.WriteStartElement("Proxy", "");
textWriter.WriteElementString("ProxyAddress", settings.Proxy.ProxyAddress);
textWriter.WriteElementString("UserName", settings.Proxy.UserName);
textWriter.WriteElementString("Password", settings.Proxy.Password);
textWriter.WriteElementString("Domain", settings.Proxy.Domain);
textWriter.WriteEndElement();
textWriter.WriteStartElement("WordLists", "");
textWriter.WriteElementString("LastLanguage", settings.WordLists.LastLanguage);
textWriter.WriteElementString("NamesEtcUrl", settings.WordLists.NamesEtcUrl);
textWriter.WriteElementString("UseOnlineNamesEtc", settings.WordLists.UseOnlineNamesEtc.ToString());
textWriter.WriteEndElement();
textWriter.WriteStartElement("CommonErrors", "");
textWriter.WriteElementString("EmptyLinesTicked", settings.CommonErrors.EmptyLinesTicked.ToString());
textWriter.WriteElementString("OverlappingDisplayTimeTicked", settings.CommonErrors.OverlappingDisplayTimeTicked.ToString());
textWriter.WriteElementString("TooShortDisplayTimeTicked", settings.CommonErrors.TooShortDisplayTimeTicked.ToString());
textWriter.WriteElementString("TooLongDisplayTimeTicked", settings.CommonErrors.TooLongDisplayTimeTicked.ToString());
textWriter.WriteElementString("InvalidItalicTagsTicked", settings.CommonErrors.InvalidItalicTagsTicked.ToString());
textWriter.WriteElementString("BreakLongLinesTicked", settings.CommonErrors.BreakLongLinesTicked.ToString());
textWriter.WriteElementString("MergeShortLinesTicked", settings.CommonErrors.MergeShortLinesTicked.ToString());
textWriter.WriteElementString("UnneededSpacesTicked", settings.CommonErrors.UnneededSpacesTicked.ToString());
textWriter.WriteElementString("UnneededPeriodsTicked", settings.CommonErrors.UnneededPeriodsTicked.ToString());
textWriter.WriteElementString("MissingSpacesTicked", settings.CommonErrors.MissingSpacesTicked.ToString());
textWriter.WriteElementString("AddMissingQuotesTicked", settings.CommonErrors.AddMissingQuotesTicked.ToString());
textWriter.WriteElementString("Fix3PlusLinesTicked", settings.CommonErrors.Fix3PlusLinesTicked.ToString());
textWriter.WriteElementString("FixHyphensTicked", settings.CommonErrors.FixHyphensTicked.ToString());
textWriter.WriteElementString("UppercaseIInsideLowercaseWordTicked", settings.CommonErrors.UppercaseIInsideLowercaseWordTicked.ToString());
textWriter.WriteElementString("DoubleApostropheToQuoteTicked", settings.CommonErrors.DoubleApostropheToQuoteTicked.ToString());
textWriter.WriteElementString("AddPeriodAfterParagraphTicked", settings.CommonErrors.AddPeriodAfterParagraphTicked.ToString());
textWriter.WriteElementString("StartWithUppercaseLetterAfterParagraphTicked", settings.CommonErrors.StartWithUppercaseLetterAfterParagraphTicked.ToString());
textWriter.WriteElementString("StartWithUppercaseLetterAfterPeriodInsideParagraphTicked", settings.CommonErrors.StartWithUppercaseLetterAfterPeriodInsideParagraphTicked.ToString());
textWriter.WriteElementString("AloneLowercaseIToUppercaseIEnglishTicked", settings.CommonErrors.AloneLowercaseIToUppercaseIEnglishTicked.ToString());
textWriter.WriteElementString("FixOcrErrorsViaReplaceListTicked", settings.CommonErrors.FixOcrErrorsViaReplaceListTicked.ToString());
textWriter.WriteElementString("DanishLetterITicked", settings.CommonErrors.DanishLetterITicked.ToString());
textWriter.WriteElementString("SpanishInvertedQuestionAndExclamationMarksTicked", settings.CommonErrors.SpanishInvertedQuestionAndExclamationMarksTicked.ToString());
textWriter.WriteElementString("FixDoubleDashTicked", settings.CommonErrors.FixDoubleDashTicked.ToString());
textWriter.WriteElementString("FixDoubleGreaterThanTicked", settings.CommonErrors.FixDoubleGreaterThanTicked.ToString());
textWriter.WriteElementString("FixEllipsesStartTicked", settings.CommonErrors.FixEllipsesStartTicked.ToString());
textWriter.WriteElementString("FixMissingOpenBracketTicked", settings.CommonErrors.FixMissingOpenBracketTicked.ToString());
textWriter.WriteElementString("FixMusicNotationTicked", settings.CommonErrors.FixMusicNotationTicked.ToString());
textWriter.WriteEndElement();
textWriter.WriteStartElement("VideoControls", "");
textWriter.WriteElementString("CustomSearchText", settings.VideoControls.CustomSearchText);
textWriter.WriteElementString("CustomSearchUrl", settings.VideoControls.CustomSearchUrl);
textWriter.WriteElementString("LastActiveTab", settings.VideoControls.LastActiveTab);
textWriter.WriteElementString("WaveFormDrawGrid", settings.VideoControls.WaveFormDrawGrid.ToString());
textWriter.WriteElementString("WaveFormGridColor", settings.VideoControls.WaveFormGridColor.ToArgb().ToString());
textWriter.WriteElementString("WaveFormColor", settings.VideoControls.WaveFormColor.ToArgb().ToString());
textWriter.WriteElementString("WaveFormSelectedColor", settings.VideoControls.WaveFormSelectedColor.ToArgb().ToString());
textWriter.WriteElementString("WaveFormBackgroundColor", settings.VideoControls.WaveFormBackgroundColor.ToArgb().ToString());
textWriter.WriteElementString("WaveFormTextColor", settings.VideoControls.WaveFormTextColor.ToArgb().ToString());
textWriter.WriteEndElement();
textWriter.WriteStartElement("VobSubOcr", "");
textWriter.WriteElementString("XOrMorePixelsMakesSpace", settings.VobSubOcr.XOrMorePixelsMakesSpace.ToString());
textWriter.WriteElementString("AllowDifferenceInPercent", settings.VobSubOcr.AllowDifferenceInPercent.ToString());
textWriter.WriteElementString("LastImageCompareFolder", settings.VobSubOcr.LastImageCompareFolder);
textWriter.WriteElementString("LastModiLanguageId", settings.VobSubOcr.LastModiLanguageId.ToString());
textWriter.WriteElementString("LastOcrMethod", settings.VobSubOcr.LastOcrMethod);
textWriter.WriteElementString("TesseractLastLanguage", settings.VobSubOcr.TesseractLastLanguage);
textWriter.WriteEndElement();
textWriter.WriteStartElement("MultipleSearchAndReplaceList", "");
foreach (var item in settings.MultipleSearchAndReplaceList)
{
textWriter.WriteStartElement("MultipleSearchAndReplaceItem", "");
textWriter.WriteElementString("Enabled", item.Enabled.ToString());
textWriter.WriteElementString("FindWhat", item.FindWhat);
textWriter.WriteElementString("ReplaceWith", item.ReplaceWith);
textWriter.WriteElementString("SearchType", item.SearchType);
textWriter.WriteEndElement();
}
textWriter.WriteEndElement();
textWriter.WriteEndElement();
textWriter.WriteEndDocument();
textWriter.Close();
}
}
}

241
src/Logic/StripableText.cs Normal file
View File

@ -0,0 +1,241 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Nikse.SubtitleEdit.Logic
{
public class StripableText
{
public string Pre { get; set; }
public string Post { get; set; }
public string StrippedText { get; set; }
public string OriginalText { get; private set; }
private string _stripStartCharacters;
private string _stripEndCharacters;
public string MergedString
{
get { return Pre + StrippedText + Post; }
}
public StripableText(string text)
: this(text, " >-\"['`´¶(♪¿¡.", " -\"]'`´¶)♪.!?:")
{
}
public StripableText(string text, string stripStartCharacters, string stripEndCharacters)
{
_stripStartCharacters = stripStartCharacters;
_stripEndCharacters = stripEndCharacters;
OriginalText = text;
Pre = string.Empty;
if (text.Length > 0 && !Utilities.GetLetters(true, true, true).Contains(text[0].ToString()))
{
for (int i = 0; i < 5; i++)
{
while (text.Length > 1 && _stripStartCharacters.Contains(text.Substring(0, 1)))
{
Pre += text.Substring(0, 1);
text = text.Substring(1);
}
// tags like <i> or <font color="#ff0000">
if (text.StartsWith("<") && text.IndexOf(">") <= 21)
{
int index = text.IndexOf(">") + 1;
Pre += text.Substring(0, index);
text = text.Substring(index);
}
}
}
Post = string.Empty;
if (text.Length > 0 && !Utilities.GetLetters(true, true, true).Contains(text[text.Length - 1].ToString()))
{
for (int i = 0; i < 5; i++)
{
while (text.Length > 1 && _stripEndCharacters.Contains(text.Substring(text.Length - 1, 1)))
{
Post = text.Substring(text.Length - 1, 1) + Post;
text = text.Substring(0, text.Length - 1);
}
// tags </i> </b> </u>
if (text.ToLower().EndsWith("</i>") ||
text.ToLower().EndsWith("</b>") ||
text.ToLower().EndsWith("</u>"))
{
Post = text.Substring(text.Length - 4, 4) + Post;
text = text.Substring(0, text.Length - 4);
}
// tag </font>
if (text.ToLower().EndsWith("</font>"))
{
Post = text.Substring(text.Length - 7, 7) + Post;
text = text.Substring(0, text.Length - 7);
}
}
}
StrippedText = text;
}
private static string GetAndInsertNextId(List<string> replaceIds, List<string> replaceNames, string name)
{
int i = 0;
string id = string.Format("_@{0}_", i);
while (replaceIds.Contains(id))
{
i++;
id = string.Format("_@{0}_", i);
}
replaceIds.Add(id);
replaceNames.Add(name);
return id;
}
private void ReplaceNames1Remove(List<string> namesEtc, List<string> replaceIds, List<string> replaceNames, List<string> originalNames)
{
if (Post.StartsWith("."))
{
StrippedText += ".";
Post = Post.Remove(0, 1);
}
string lower = StrippedText.ToLower();
foreach (string name in namesEtc)
{
int start = lower.IndexOf(name.ToLower());
while (start >= 0 && start < lower.Length)
{
bool startOk = (start == 0) || (lower[start - 1] == ' ') || (lower[start - 1] == '-') ||
(lower[start - 1] == '"') || (lower[start - 1] == '\'') || (lower[start - 1] == '>') ||
(Environment.NewLine.EndsWith(lower[start - 1].ToString()));
if (startOk)
{
int end = start + name.Length;
bool endOk = end <= lower.Length;
if (endOk)
endOk = (end == lower.Length) || ((" ,.!?:;')- <\"" + Environment.NewLine).Contains(lower[end].ToString()));
if (endOk && StrippedText.Length >= start + name.Length)
{
string orginalName = StrippedText.Substring(start, name.Length);
originalNames.Add(orginalName);
StrippedText = StrippedText.Remove(start, name.Length);
StrippedText = StrippedText.Insert(start, GetAndInsertNextId(replaceIds, replaceNames, name));
lower = StrippedText.ToLower();
}
}
start = lower.IndexOf(name.ToLower(), start +3);
}
}
if (StrippedText.EndsWith("."))
{
Post = "." + Post;
StrippedText = StrippedText.TrimEnd('.');
}
}
private void ReplaceNames2Fix(List<string> replaceIds, List<string> replaceNames)
{
for (int i=0; i<replaceIds.Count; i++)
{
StrippedText = StrippedText.Replace(replaceIds[i], replaceNames[i]);
}
}
public void FixCasing(List<string> namesEtc, bool changeNameCases, bool makeUppercaseAfterBreak, bool checkLastLine, string lastLine)
{
var replaceIds = new List<string>();
var replaceNames = new List<string>();
var originalNames = new List<string>();
ReplaceNames1Remove(namesEtc, replaceIds, replaceNames, originalNames);
if (checkLastLine)
{
string s = Utilities.RemoveHtmlTags(lastLine).TrimEnd().TrimEnd('\"').TrimEnd();
bool startWithUppercase = string.IsNullOrEmpty(s) ||
s.EndsWith(".") ||
s.EndsWith("!") ||
s.EndsWith("?") ||
s.EndsWith("]") ||
s.EndsWith(")") ||
s.EndsWith(":");
if (startWithUppercase && StrippedText.Length > 0)
{
StrippedText = StrippedText.Remove(0, 1).Insert(0, StrippedText[0].ToString().ToUpper());
}
}
if (makeUppercaseAfterBreak &&
(StrippedText.Contains(".") ||
StrippedText.Contains("!") ||
StrippedText.Contains("?") ||
StrippedText.Contains(":") ||
StrippedText.Contains(";") ||
StrippedText.Contains(")") ||
StrippedText.Contains("]") ||
StrippedText.Contains("}") ||
StrippedText.Contains("(") ||
StrippedText.Contains("[") ||
StrippedText.Contains("{")))
{
var sb = new StringBuilder();
bool lastWasBreak = false;
for (int i=0; i<StrippedText.Length; i++)
{
string s = StrippedText[i].ToString();
if (lastWasBreak)
{
if (("\"`´'()<>!?.- " + Environment.NewLine).Contains(s))
{
sb.Append(s);
}
else if ((sb.ToString().EndsWith("<") || sb.ToString().EndsWith("</")) && i + 1 < StrippedText.Length && StrippedText[i + 1] == '>')
{ // tags
sb.Append(s);
}
else if (sb.ToString().EndsWith("<") && s == "/" && i + 2 < StrippedText.Length && StrippedText[i + 2] == '>')
{ // tags
sb.Append(s);
}
else if (sb.ToString().EndsWith("... "))
{
sb.Append(s);
lastWasBreak = false;
}
else
{
lastWasBreak = false;
sb.Append(s.ToUpper());
}
}
else
{
sb.Append(s);
if (".!?:;)]}([{".Contains(s))
lastWasBreak = true;
}
}
StrippedText = sb.ToString();
}
if (changeNameCases)
ReplaceNames2Fix(replaceIds, replaceNames);
else
ReplaceNames2Fix(replaceIds, originalNames);
}
}
}

397
src/Logic/Subtitle.cs Normal file
View File

@ -0,0 +1,397 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Nikse.SubtitleEdit.Logic.SubtitleFormats;
using Nikse.SubtitleEdit.Logic.Enums;
namespace Nikse.SubtitleEdit.Logic
{
public class Subtitle
{
List<Paragraph> _paragraphs;
List<HistoryItem> _history;
SubtitleFormat _format;
bool _wasLoadedWithFrameNumbers;
public string Header { get; set; }
public string FileName { get; set; }
public SubtitleFormat OriginalFormat
{
get
{
return _format;
}
}
public List<HistoryItem> HistoryItems
{
get { return _history; }
}
public Subtitle()
{
_paragraphs = new List<Paragraph>();
_history = new List<HistoryItem>();
FileName = "Untitled";
}
public Subtitle(List<HistoryItem> historyItems) : this()
{
_history = historyItems;
}
/// <summary>
/// Copy constructor (only paragraphs)
/// </summary>
/// <param name="subtitle">Subtitle to copy</param>
public Subtitle(Subtitle subtitle) : this()
{
foreach (Paragraph p in subtitle.Paragraphs)
{
_paragraphs.Add(new Paragraph(p));
}
_wasLoadedWithFrameNumbers = subtitle.WasLoadedWithFrameNumbers;
}
public List<Paragraph> Paragraphs
{
get
{
return _paragraphs;
}
}
public Paragraph GetParagraphOrDefault(int index)
{
if (_paragraphs == null || _paragraphs.Count <= index || index < 0)
return null;
return _paragraphs[index];
}
public SubtitleFormat ReloadLoadSubtitle(List<string> lines, string fileName)
{
foreach (SubtitleFormat subtitleFormat in SubtitleFormat.AllSubtitleFormats)
{
if (subtitleFormat.IsMine(lines, fileName))
{
subtitleFormat.LoadSubtitle(this, lines, fileName);
_format = subtitleFormat;
return subtitleFormat;
}
}
return null;
}
public SubtitleFormat LoadSubtitle(string fileName, out Encoding encoding, Encoding useThisEncoding)
{
FileName = fileName;
_paragraphs = new List<Paragraph>();
var lines = new List<string>();
StreamReader sr;
if (useThisEncoding != null)
sr = new StreamReader(fileName, useThisEncoding);
else
sr = new StreamReader(fileName, Utilities.GetEncodingFromFile(fileName), true);
encoding = sr.CurrentEncoding;
while (!sr.EndOfStream)
lines.Add(sr.ReadLine());
sr.Close();
foreach (SubtitleFormat subtitleFormat in SubtitleFormat.AllSubtitleFormats)
{
if (subtitleFormat.IsMine(lines, fileName))
{
subtitleFormat.LoadSubtitle(this, lines, fileName);
_format = subtitleFormat;
_wasLoadedWithFrameNumbers = _format.IsFrameBased;
return subtitleFormat;
}
}
return null;
}
public void MakeHistoryForUndo(string description, SubtitleFormat subtitleFormat, DateTime fileModified)
{
// don't fill memory with history - use a max rollback points
if (_history.Count > 50)
_history.RemoveAt(0);
_history.Add(new HistoryItem(_history.Count, this, description, FileName, fileModified, subtitleFormat.FriendlyName));
}
public bool CanUndo
{
get
{
return _history.Count > 0;
}
}
public void UndoLast()
{
string subtitleFormatFriendlyName;
DateTime fileModified;
UndoHistory(_history.Count - 1, out subtitleFormatFriendlyName, out fileModified);
}
public string UndoHistory(int index, out string subtitleFormatFriendlyName, out DateTime fileModified)
{
_paragraphs.Clear();
foreach (Paragraph p in _history[index].Subtitle.Paragraphs)
_paragraphs.Add(new Paragraph(p));
subtitleFormatFriendlyName = _history[index].SubtitleFormatFriendlyName;
FileName = _history[index].FileName;
fileModified = _history[index].FileModified;
return FileName;
}
internal string ToText(SubtitleFormat format)
{
return format.ToText(this, Path.GetFileNameWithoutExtension(FileName));
}
public void AddTimeToAllParagraphs(TimeSpan time)
{
foreach (Paragraph p in Paragraphs)
{
p.StartTime.AddTime(time);
p.EndTime.AddTime(time);
}
}
/// <summary>
/// Calculate the time codes from framenumber/framerate
/// </summary>
/// <param name="frameRate">Number of frames per second</param>
/// <returns>True if times could be calculated</returns>
public bool CalculateTimeCodesFromFrameNumbers(double frameRate)
{
if (_format == null)
return false;
if (_format.IsTimeBased)
return false;
foreach (Paragraph p in Paragraphs)
{
p.CalculateTimeCodesFromFrameNumbers(frameRate);
}
return true;
}
/// <summary>
/// Calculate the frame numbers from time codes/framerate
/// </summary>
/// <param name="frameRate"></param>
/// <returns></returns>
public bool CalculateFrameNumbersFromTimeCodes(double frameRate)
{
if (_format == null)
return false;
if (_format.IsFrameBased)
return false;
foreach (Paragraph p in Paragraphs)
{
p.CalculateFrameNumbersFromTimeCodes(frameRate);
}
FixEqualOrJustOverlappingFrameNumbers();
return true;
}
public void CalculateFrameNumbersFromTimeCodesNoCheck(double frameRate)
{
foreach (Paragraph p in Paragraphs)
p.CalculateFrameNumbersFromTimeCodes(frameRate);
FixEqualOrJustOverlappingFrameNumbers();
}
private void FixEqualOrJustOverlappingFrameNumbers()
{
for (int i = 0; i < Paragraphs.Count - 1; i++)
{
Paragraph p = Paragraphs[i];
Paragraph next = Paragraphs[i + 1];
if (next != null && p.EndFrame == next.StartFrame || p.EndFrame == next.StartFrame + 1)
p.EndFrame = next.StartFrame - 1;
}
}
internal void ChangeFramerate(double oldFramerate, double newFramerate)
{
foreach (Paragraph p in Paragraphs)
{
p.CalculateFrameNumbersFromTimeCodes(oldFramerate);
p.CalculateTimeCodesFromFrameNumbers(newFramerate);
}
}
public bool WasLoadedWithFrameNumbers
{
get
{
return _wasLoadedWithFrameNumbers;
}
set
{
_wasLoadedWithFrameNumbers = value;
}
}
internal void AdjustDisplayTimeUsingPercent(double percent, System.Windows.Forms.ListView.SelectedIndexCollection selectedIndexes)
{
for (int i = 0; i < _paragraphs.Count; i++)
{
if (selectedIndexes == null || selectedIndexes.Contains(i))
{
double nextStartMilliseconds = _paragraphs[_paragraphs.Count - 1].EndTime.TotalMilliseconds + 100000;
if (i + 1 < _paragraphs.Count)
nextStartMilliseconds = _paragraphs[i + 1].StartTime.TotalMilliseconds;
double newEndMilliseconds = _paragraphs[i].EndTime.TotalMilliseconds;
newEndMilliseconds = _paragraphs[i].StartTime.TotalMilliseconds + (((newEndMilliseconds - _paragraphs[i].StartTime.TotalMilliseconds) * percent) / 100);
if (newEndMilliseconds > nextStartMilliseconds)
newEndMilliseconds = nextStartMilliseconds - 1;
_paragraphs[i].EndTime.TotalMilliseconds = newEndMilliseconds;
}
}
}
internal void AdjustDisplayTimeUsingSeconds(double seconds, System.Windows.Forms.ListView.SelectedIndexCollection selectedIndexes)
{
for (int i = 0; i < _paragraphs.Count; i++)
{
if (selectedIndexes == null || selectedIndexes.Contains(i))
{
double nextStartMilliseconds = _paragraphs[_paragraphs.Count - 1].EndTime.TotalMilliseconds + 100000;
if (i + 1 < _paragraphs.Count)
nextStartMilliseconds = _paragraphs[i + 1].StartTime.TotalMilliseconds;
double newEndMilliseconds = _paragraphs[i].EndTime.TotalMilliseconds + (seconds * 1000.0);
if (newEndMilliseconds > nextStartMilliseconds)
newEndMilliseconds = nextStartMilliseconds - 1;
_paragraphs[i].EndTime.TotalMilliseconds = newEndMilliseconds;
}
}
}
internal void Renumber(int startNumber)
{
int i = startNumber;
foreach (Paragraph p in _paragraphs)
{
p.Number = i;
i++;
}
}
internal int GetIndex(Paragraph p)
{
return _paragraphs.IndexOf(p);
}
internal Paragraph GetFirstParagraphByLineNumber(int number)
{
foreach (Paragraph p in _paragraphs)
{
if (p.Number == number)
return p;
}
return null;
}
internal int RemoveEmptyLines()
{
int count = 0;
if (_paragraphs.Count > 0)
{
int firstNumber = _paragraphs[0].Number;
for (int i = _paragraphs.Count - 1; i >= 0; i--)
{
Paragraph p = _paragraphs[i];
string s = p.Text.Trim();
if (s.Length == 0)
{
_paragraphs.RemoveAt(i);
count++;
}
}
Renumber(firstNumber);
}
return count;
}
/// <summary>
/// Sort subtitle paragraphs
/// </summary>
/// <param name="sortCriteria">Paragraph sort criteria</param>
public void Sort(SubtitleSortCriteria sortCriteria)
{
switch (sortCriteria)
{
case SubtitleSortCriteria.Number:
_paragraphs.Sort(delegate(Paragraph p1, Paragraph p2)
{
return p1.Number.CompareTo(p2.Number);
});
break;
case SubtitleSortCriteria.StartTime:
_paragraphs.Sort(delegate(Paragraph p1, Paragraph p2)
{
return p1.StartTime.TotalMilliseconds.CompareTo(p2.StartTime.TotalMilliseconds);
});
break;
case SubtitleSortCriteria.EndTime:
_paragraphs.Sort(delegate(Paragraph p1, Paragraph p2)
{
return p1.EndTime.TotalMilliseconds.CompareTo(p2.EndTime.TotalMilliseconds);
});
break;
case SubtitleSortCriteria.Duration:
_paragraphs.Sort(delegate(Paragraph p1, Paragraph p2)
{
return p1.Duration.TotalMilliseconds.CompareTo(p2.Duration.TotalMilliseconds);
});
break;
case SubtitleSortCriteria.Text:
_paragraphs.Sort(delegate(Paragraph p1, Paragraph p2)
{
return p1.Text.CompareTo(p2.Text);
});
break;
case SubtitleSortCriteria.TextMaxLineLength:
_paragraphs.Sort(delegate(Paragraph p1, Paragraph p2)
{
return Utilities.GetMaxLineLength(p1.Text).CompareTo(Utilities.GetMaxLineLength(p2.Text));
});
break;
case SubtitleSortCriteria.TextTotalLength:
_paragraphs.Sort(delegate(Paragraph p1, Paragraph p2)
{
return p1.Text.Length.CompareTo(p2.Text.Length);
});
break;
case SubtitleSortCriteria.TextNumberOfLines:
_paragraphs.Sort(delegate(Paragraph p1, Paragraph p2)
{
return p1.NumberOfLines.CompareTo(p2.NumberOfLines);
});
break;
default:
break;
}
}
}
}

125
src/Logic/TimeCode.cs Normal file
View File

@ -0,0 +1,125 @@
using System;
namespace Nikse.SubtitleEdit.Logic
{
public class TimeCode
{
TimeSpan _time;
public static double ParseToMilliseconds(string text)
{
string[] parts = text.Split(":,.".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 4)
{
int hours;
int minutes;
int seconds;
int milliseconds;
if (int.TryParse(parts[0], out hours) && int.TryParse(parts[1], out minutes) && int.TryParse(parts[2], out seconds) && int.TryParse(parts[3], out milliseconds))
{
TimeSpan ts = new TimeSpan(0, hours, minutes, seconds, milliseconds);
return ts.TotalMilliseconds;
}
}
return 0;
}
public TimeCode(TimeSpan timeSpan)
{
TimeSpan = timeSpan;
}
public TimeCode(int hour, int minute, int seconds, int milliseconds)
{
_time = new TimeSpan(0, hour, minute, seconds, milliseconds);
}
public int Hours
{
get { return _time.Hours; }
set { _time = new TimeSpan(0, value, _time.Minutes, _time.Seconds, _time.Milliseconds); }
}
public int Minutes
{
get { return _time.Minutes; }
set { _time = new TimeSpan(0, _time.Hours, value, _time.Seconds, _time.Milliseconds); }
}
public int Seconds
{
get { return _time.Seconds; }
set { _time = new TimeSpan(0, _time.Hours, _time.Minutes, value, _time.Milliseconds); }
}
public int Milliseconds
{
get { return _time.Milliseconds; }
set { _time = new TimeSpan(0, _time.Hours, _time.Minutes, _time.Seconds, value); }
}
public double TotalMilliseconds
{
get { return _time.TotalMilliseconds; }
set { _time = TimeSpan.FromMilliseconds(value); }
}
public double TotalSeconds
{
get { return _time.TotalSeconds; }
set { _time = TimeSpan.FromSeconds(value); }
}
public TimeSpan TimeSpan
{
get
{
return _time;
}
set
{
_time = value;
}
}
public void AddTime(int hour, int minutes, int seconds, int milliseconds)
{
Hours += hour;
Minutes += minutes;
Seconds += seconds;
Milliseconds += milliseconds;
}
public void AddTime(long milliseconds)
{
_time = TimeSpan.FromMilliseconds(_time.TotalMilliseconds + milliseconds);
}
public void AddTime(TimeSpan timeSpan)
{
_time = TimeSpan.FromMilliseconds(_time.TotalMilliseconds + timeSpan.TotalMilliseconds);
}
public void AddTime(double milliseconds)
{
_time = TimeSpan.FromMilliseconds(_time.TotalMilliseconds + milliseconds);
}
public override string ToString()
{
return string.Format("{0:00}:{1:00}:{2:00},{3:000}", _time.Hours, _time.Minutes, _time.Seconds, _time.Milliseconds);
}
public string ToShortString()
{
if (_time.Minutes == 0 && _time.Hours == 0)
return string.Format("{0:00},{1:000}", _time.Seconds, _time.Milliseconds);
else if (_time.Hours == 0)
return string.Format("{0:00}:{1:00},{2:000}", _time.Minutes, _time.Seconds, _time.Milliseconds);
else
return string.Format("{0:00}:{1:00}:{2:00},{3:000}", _time.Hours, _time.Minutes, _time.Seconds, _time.Milliseconds);
}
}
}

1335
src/Logic/Utilities.cs Normal file

File diff suppressed because it is too large Load Diff

22
src/Logic/Wave.cs Normal file
View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Nikse.SubtitleEdit.Logic
{
class Wave
{
public Wave(string fileName)
{
RiffParser parser = new RiffParser();
RiffDecodeHeader decoder = new RiffDecodeHeader(parser);
parser.OpenFile(fileName);
if (RiffParser.ckidAVI == parser.FileType)
{
decoder.ProcessMainAVI();
decoder.ProcessMainWAVE();
parser.CloseFile();
}
}
}
}

353
src/Logic/WavePeak.cs Normal file
View File

@ -0,0 +1,353 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace Nikse.SubtitleEdit.Logic
{
public class WaveHeader
{
const int ConstantHeaderSize = 20;
private byte[] _headerData;
public string ChunkId { get; private set; }
public int ChunkSize { get; private set; }
public string Format { get; private set; }
public string FmtId { get; private set; }
public int FmtChunkSize { get; private set; }
/// <summary>
/// 1 = PCM (uncompressed)
/// </summary>
public int AudioFormat { get; private set; }
public int NumberOfChannels { get; private set; }
/// <summary>
/// Number of samples per second
/// </summary>
public int SampleRate { get; private set; }
/// <summary>
/// Should be SampleRate * BlockAlign
/// </summary>
public int ByteRate { get; private set; }
/// <summary>
/// 8 bytes per block (32 bit); 6 bytes per block (24 bit); 4 bytes per block (16 bit)
/// </summary>
public int BlockAlign { get; private set; }
public int BitsPerSample { get; private set; }
public string DataId { get; private set; }
/// <summary>
/// Size of sound data
/// </summary>
public int DataChunkSize { get; private set; }
public int DataStartPosition { get; private set; }
public WaveHeader(Stream stream)
{
stream.Position = 0;
byte[] buffer = new byte[ConstantHeaderSize];
int bytesRead = stream.Read(buffer, 0, buffer.Length);
if (bytesRead < buffer.Length)
throw new ArgumentException("Stream is too small");
// constant header
ChunkId = Encoding.UTF8.GetString(buffer, 0, 4);
ChunkSize = BitConverter.ToInt32(buffer, 4);
Format = Encoding.UTF8.GetString(buffer, 8, 4);
FmtId = Encoding.UTF8.GetString(buffer, 12, 4);
FmtChunkSize = BitConverter.ToInt32(buffer, 16);
// fmt data
buffer = new byte[FmtChunkSize];
stream.Read(buffer, 0, buffer.Length);
AudioFormat = BitConverter.ToInt16(buffer, 0);
NumberOfChannels = BitConverter.ToInt16(buffer, 2);
SampleRate = BitConverter.ToInt32(buffer, 4);
ByteRate = BitConverter.ToInt32(buffer, 8);
BlockAlign = BitConverter.ToInt16(buffer, 12);
BitsPerSample = BitConverter.ToInt16(buffer, 14);
// data
buffer = new byte[8];
stream.Position = ConstantHeaderSize + FmtChunkSize;
stream.Read(buffer, 0, buffer.Length);
DataId = Encoding.UTF8.GetString(buffer, 0, 4);
DataChunkSize = BitConverter.ToInt32(buffer, 4);
DataStartPosition = (int)(ConstantHeaderSize + FmtChunkSize + 8);
_headerData = new byte[DataStartPosition];
stream.Position = 0;
stream.Read(_headerData, 0, _headerData.Length);
}
public long BytesPerSecond
{
get
{
return SampleRate * (BitsPerSample / 8) * NumberOfChannels;
}
}
public double LengthInSeconds
{
get
{
return DataChunkSize / BytesPerSecond;
}
}
internal void WriteHeader(Stream fromStream, Stream toStream, int sampleRate, int numberOfChannels, int bitsPerSample, int dataSize)
{
int byteRate = sampleRate * (bitsPerSample / 8) * numberOfChannels;
WriteInt32ToByteArray(_headerData, 4, dataSize + DataStartPosition - 8);
WriteInt16ToByteArray(_headerData, ConstantHeaderSize + 2, numberOfChannels);
WriteInt32ToByteArray(_headerData, ConstantHeaderSize + 4, sampleRate);
WriteInt32ToByteArray(_headerData, ConstantHeaderSize + 8, byteRate);
WriteInt16ToByteArray(_headerData, ConstantHeaderSize + 14, bitsPerSample);
WriteInt32ToByteArray(_headerData, ConstantHeaderSize + FmtChunkSize + 4, dataSize);
toStream.Write(_headerData, 0, _headerData.Length);
}
private void WriteInt16ToByteArray(byte[] _headerData, int index, int value)
{
byte[] buffer = BitConverter.GetBytes((short)value);
for (int i = 0; i < buffer.Length; i++)
_headerData[index + i] = buffer[i];
}
private void WriteInt32ToByteArray(byte[] _headerData, int index, int value)
{
byte[] buffer = BitConverter.GetBytes(value);
for (int i = 0; i < buffer.Length; i++)
_headerData[index + i] = buffer[i];
}
}
public class WavePeakGenerator
{
private Stream _stream = null;
private byte[] _data = null;
private delegate int ReadSampleDataValueDelegate(ref int index);
public WaveHeader Header { get; private set; }
/// <summary>
/// Lowest data value
/// </summary>
public int DataMinValue { get; private set; }
/// <summary>
/// Highest data value
/// </summary>
public int DataMaxValue { get; private set; }
/// <summary>
/// Number of peaks per second (should be divideable by SampleRate)
/// </summary>
public int PeaksPerSecond { get; private set; }
/// <summary>
/// List of all peak samples (channels are merged)
/// </summary>
public List<int> PeakSamples { get; private set; }
/// <summary>
/// List of all samples (channels are merged)
/// </summary>
public List<int> AllSamples { get; private set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="fileName">Wave file name</param>
public WavePeakGenerator(string fileName)
{
Initialize(new FileStream(fileName, FileMode.Open, FileAccess.Read));
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="stream">Stream of a wave file</param>
public WavePeakGenerator(Stream stream)
{
Initialize(stream);
}
/// <summary>
/// Generate peaks (samples with some interval) for an uncompressed wave file
/// </summary>
/// <param name="peaksPerSecond">Sampeles per second / sample rate</param>
public void GeneratePeakSamples(int peaksPerSecond)
{
PeaksPerSecond = peaksPerSecond;
ReadSampleDataValueDelegate readSampleDataValue = GetSampleDataRerader();
DataMinValue = int.MaxValue;
DataMaxValue = int.MinValue;
PeakSamples = new List<int>();
int bytesInterval = (int)Header.BytesPerSecond / PeaksPerSecond;
_data = new byte[Header.BytesPerSecond];
_stream.Position = Header.DataStartPosition;
int bytesRead = _stream.Read(_data, 0, _data.Length);
while (bytesRead == Header.BytesPerSecond)
{
for (int i = 0; i < Header.BytesPerSecond; i += bytesInterval)
{
int index = i;
int value = 0;
for (int channelNumber = 0; channelNumber < Header.NumberOfChannels; channelNumber++)
{
value += readSampleDataValue.Invoke(ref index);
}
value = value / Header.NumberOfChannels;
if (value < DataMinValue)
DataMinValue = value;
if (value > DataMaxValue)
DataMaxValue = value;
PeakSamples.Add(value);
}
bytesRead = _stream.Read(_data, 0, _data.Length);
}
}
public void GenerateAllSamples()
{
// determine how to read sample values
ReadSampleDataValueDelegate readSampleDataValue = GetSampleDataRerader();
// load data
_data = new byte[Header.DataChunkSize];
_stream.Position = Header.DataStartPosition;
int bytesRead = _stream.Read(_data, 0, _data.Length);
// read sample values
DataMinValue = int.MaxValue;
DataMaxValue = int.MinValue;
AllSamples = new List<int>();
int index = 0;
while (index + Header.NumberOfChannels < Header.DataChunkSize)
{
int value = 0;
for (int channelNumber = 0; channelNumber < Header.NumberOfChannels; channelNumber++)
{
value += readSampleDataValue.Invoke(ref index);
}
value = value / Header.NumberOfChannels;
if (value < DataMinValue)
DataMinValue = value;
if (value > DataMaxValue)
DataMaxValue = value;
AllSamples.Add(value);
}
}
public void WritePeakSamples(string fileName)
{
FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
WritePeakSamples(fs);
fs.Close();
}
public void WritePeakSamples(Stream stream)
{
Header.WriteHeader(_stream, stream, PeaksPerSecond, 1, 16, PeakSamples.Count * 2);
WritePeakData(stream);
stream.Flush();
}
private void WritePeakData(Stream stream)
{
foreach (var value in PeakSamples)
{
byte[] buffer = BitConverter.GetBytes((short)(value));
stream.Write(buffer, 0, buffer.Length);
}
}
private void Initialize(Stream stream)
{
_stream = stream;
Header = new WaveHeader(_stream);
}
private int ReadValue8Bit(ref int index)
{
int result = _data[index];
index += 2;
return result;
}
private int ReadValue16Bit(ref int index)
{
int result = BitConverter.ToInt16(_data, index);
index += 2;
return result;
}
private int ReadValue24Bit(ref int index)
{
byte[] buffer = new byte[4];
buffer[0] = 0;
buffer[1] = _data[index];
buffer[2] = _data[index + 1];
buffer[3] = _data[index + 2];
int result = BitConverter.ToInt32(buffer, 0);
index += 3;
return result;
}
private int ReadValue32Bit(ref int index)
{
int result = BitConverter.ToInt32(_data, index);
index += 4;
return result;
}
/// <summary>
/// Determine how to read sample values
/// </summary>
/// <returns>Sample data reader that matches bits per sample</returns>
private ReadSampleDataValueDelegate GetSampleDataRerader()
{
ReadSampleDataValueDelegate readSampleDataValue = null;
switch (Header.BitsPerSample)
{
case 8:
readSampleDataValue = ReadValue8Bit;
break;
case 16:
readSampleDataValue = ReadValue16Bit;
break;
case 24:
readSampleDataValue = ReadValue24Bit;
break;
case 32:
readSampleDataValue = ReadValue32Bit;
break;
default:
throw new InvalidDataException("Cannot read bits per sample of " + Header.BitsPerSample);
}
return readSampleDataValue;
}
public void Dispose()
{
if (_stream != null)
_stream.Close();
}
public void Close()
{
if (_stream != null)
_stream.Close();
}
}
}

433
src/Logic/ZipExtractor.cs Normal file
View File

@ -0,0 +1,433 @@
// ZipStorer, by Jaime Olivares
// Website: zipstorer.codeplex.com
// Version: 2.35 (March 14, 2010)
// Simplified to extract-only by Nikse - August 18, 2010
using System.Collections.Generic;
using System.Text;
namespace System.IO.Compression
{
/// <summary>
/// Zip archive decompression. Represents a Zip file.
/// </summary>
public class ZipExtractor : IDisposable
{
/// <summary>
/// Compression method enumeration
/// </summary>
public enum Compression : ushort
{
/// <summary>Uncompressed storage</summary>
Store = 0,
/// <summary>Deflate compression method</summary>
Deflate = 8
}
/// <summary>
/// Represents an entry in Zip file directory
/// </summary>
public struct ZipFileEntry
{
/// <summary>Compression method</summary>
public Compression Method;
/// <summary>Full path and filename as stored in Zip</summary>
public string FilenameInZip;
/// <summary>Original file size</summary>
public uint FileSize;
/// <summary>Compressed file size</summary>
public uint CompressedSize;
/// <summary>Offset of header information inside Zip storage</summary>
public uint HeaderOffset;
/// <summary>Offset of file inside Zip storage</summary>
public uint FileOffset;
/// <summary>Size of header information</summary>
public uint HeaderSize;
/// <summary>32-bit checksum of entire file</summary>
public uint Crc32;
/// <summary>Last modification time of file</summary>
public DateTime ModifyTime;
/// <summary>User comment for file</summary>
public string Comment;
/// <summary>True if UTF8 encoding for filename and comments, false if default (CP 437)</summary>
public bool EncodeUTF8;
/// <summary>Overriden method</summary>
/// <returns>Filename in Zip</returns>
public override string ToString()
{
return this.FilenameInZip;
}
}
#region Public fields
/// <summary>True if UTF8 encoding for filename and comments, false if default (CP 437)</summary>
public bool EncodeUTF8 = false;
/// <summary>Force deflate algotithm even if it inflates the stored file. Off by default.</summary>
public bool ForceDeflating = false;
#endregion
#region Private fields
// List of files to store
private List<ZipFileEntry> Files = new List<ZipFileEntry>();
// Filename of storage file
private string FileName;
// Stream object of storage file
private Stream ZipFileStream;
// Central dir image
private byte[] CentralDirImage = null;
// Existing files in zip
private ushort ExistingFiles = 0;
// Static CRC32 Table
private static UInt32[] CrcTable = null;
// Default filename encoder
private static Encoding DefaultEncoding = Encoding.GetEncoding(437);
#endregion
#region Public methods
// Static constructor. Just invoked once in order to create the CRC32 lookup table.
static ZipExtractor()
{
// Generate CRC32 table
CrcTable = new UInt32[256];
for (int i = 0; i < CrcTable.Length; i++)
{
UInt32 c = (UInt32)i;
for (int j = 0; j < 8; j++)
{
if ((c & 1) != 0)
c = 3988292384 ^ (c >> 1);
else
c >>= 1;
}
CrcTable[i] = c;
}
}
/// <summary>
/// Method to open an existing storage file
/// </summary>
/// <param name="_filename">Full path of Zip file to open</param>
/// <returns>A valid ZipStorer object</returns>
public static ZipExtractor Open(string _filename)
{
Stream stream = (Stream)new FileStream(_filename, FileMode.Open, FileAccess.Read);
ZipExtractor zip = Open(stream);
zip.FileName = _filename;
return zip;
}
/// <summary>
/// Method to open an existing storage from stream
/// </summary>
/// <param name="_stream">Already opened stream with zip contents</param>
/// <returns>A valid ZipStorer object</returns>
public static ZipExtractor Open(Stream _stream)
{
ZipExtractor zip = new ZipExtractor();
zip.ZipFileStream = _stream;
if (zip.ReadFileInfo())
return zip;
throw new System.IO.InvalidDataException();
}
/// <summary>
/// Close the Zip storage
/// </summary>
/// <remarks>This is a required step, unless automatic dispose is used</remarks>
public void Close()
{
if (this.ZipFileStream != null)
{
this.ZipFileStream.Flush();
this.ZipFileStream.Dispose();
this.ZipFileStream = null;
}
}
/// <summary>
/// Read all the file records in the central directory
/// </summary>
/// <returns>List of all entries in directory</returns>
public List<ZipFileEntry> ReadCentralDir()
{
if (this.CentralDirImage == null)
throw new InvalidOperationException("Central directory currently does not exist");
List<ZipFileEntry> result = new List<ZipFileEntry>();
for (int pointer = 0; pointer < this.CentralDirImage.Length; )
{
uint signature = BitConverter.ToUInt32(CentralDirImage, pointer);
if (signature != 0x02014b50)
break;
bool encodeUTF8 = (BitConverter.ToUInt16(CentralDirImage, pointer + 8) & 0x0800) != 0;
ushort method = BitConverter.ToUInt16(CentralDirImage, pointer + 10);
uint modifyTime = BitConverter.ToUInt32(CentralDirImage, pointer + 12);
uint crc32 = BitConverter.ToUInt32(CentralDirImage, pointer + 16);
uint comprSize = BitConverter.ToUInt32(CentralDirImage, pointer + 20);
uint fileSize = BitConverter.ToUInt32(CentralDirImage, pointer + 24);
ushort filenameSize = BitConverter.ToUInt16(CentralDirImage, pointer + 28);
ushort extraSize = BitConverter.ToUInt16(CentralDirImage, pointer + 30);
ushort commentSize = BitConverter.ToUInt16(CentralDirImage, pointer + 32);
uint headerOffset = BitConverter.ToUInt32(CentralDirImage, pointer + 42);
uint headerSize = (uint)(46 + filenameSize + extraSize + commentSize);
Encoding encoder = encodeUTF8 ? Encoding.UTF8 : DefaultEncoding;
ZipFileEntry zfe = new ZipFileEntry();
zfe.Method = (Compression)method;
zfe.FilenameInZip = encoder.GetString(CentralDirImage, pointer + 46, filenameSize);
zfe.FileOffset = GetFileOffset(headerOffset);
zfe.FileSize = fileSize;
zfe.CompressedSize = comprSize;
zfe.HeaderOffset = headerOffset;
zfe.HeaderSize = headerSize;
zfe.Crc32 = crc32;
zfe.ModifyTime = DosTimeToDateTime(modifyTime);
if (commentSize > 0)
zfe.Comment = encoder.GetString(CentralDirImage, pointer + 46 + filenameSize + extraSize, commentSize);
result.Add(zfe);
pointer += (46 + filenameSize + extraSize + commentSize);
}
return result;
}
/// <summary>
/// Copy the contents of a stored file into a physical file
/// </summary>
/// <param name="_zfe">Entry information of file to extract</param>
/// <param name="_filename">Name of file to store uncompressed data</param>
/// <returns>True if success, false if not.</returns>
/// <remarks>Unique compression methods are Store and Deflate</remarks>
public bool ExtractFile(ZipFileEntry _zfe, string _filename)
{
// Make sure the parent directory exist
string path = System.IO.Path.GetDirectoryName(_filename);
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
// Check it is directory. If so, do nothing
if (Directory.Exists(_filename))
return true;
Stream output = new FileStream(_filename, FileMode.Create, FileAccess.Write);
bool result = ExtractFile(_zfe, output);
if (result)
output.Close();
File.SetCreationTime(_filename, _zfe.ModifyTime);
File.SetLastWriteTime(_filename, _zfe.ModifyTime);
return result;
}
/// <summary>
/// Copy the contents of a stored file into an opened stream
/// </summary>
/// <param name="_zfe">Entry information of file to extract</param>
/// <param name="_stream">Stream to store the uncompressed data</param>
/// <returns>True if success, false if not.</returns>
/// <remarks>Unique compression methods are Store and Deflate</remarks>
public bool ExtractFile(ZipFileEntry _zfe, Stream _stream)
{
if (!_stream.CanWrite)
throw new InvalidOperationException("Stream cannot be written");
// check signature
byte[] signature = new byte[4];
this.ZipFileStream.Seek(_zfe.HeaderOffset, SeekOrigin.Begin);
this.ZipFileStream.Read(signature, 0, 4);
if (BitConverter.ToUInt32(signature, 0) != 0x04034b50)
return false;
// Select input stream for inflating or just reading
Stream inStream;
if (_zfe.Method == Compression.Store)
inStream = this.ZipFileStream;
else if (_zfe.Method == Compression.Deflate)
inStream = new DeflateStream(this.ZipFileStream, CompressionMode.Decompress, true);
else
return false;
// Buffered copy
byte[] buffer = new byte[16384];
this.ZipFileStream.Seek(_zfe.FileOffset, SeekOrigin.Begin);
uint bytesPending = _zfe.FileSize;
while (bytesPending > 0)
{
int bytesRead = inStream.Read(buffer, 0, (int)Math.Min(bytesPending, buffer.Length));
_stream.Write(buffer, 0, bytesRead);
bytesPending -= (uint)bytesRead;
}
_stream.Flush();
if (_zfe.Method == Compression.Deflate)
inStream.Dispose();
return true;
}
#endregion
#region Private methods
/// <summary>
/// Calculate the file offset by reading the corresponding local header
/// </summary>
private uint GetFileOffset(uint _headerOffset)
{
byte[] buffer = new byte[2];
this.ZipFileStream.Seek(_headerOffset + 26, SeekOrigin.Begin);
this.ZipFileStream.Read(buffer, 0, 2);
ushort filenameSize = BitConverter.ToUInt16(buffer, 0);
this.ZipFileStream.Read(buffer, 0, 2);
ushort extraSize = BitConverter.ToUInt16(buffer, 0);
return (uint)(30 + filenameSize + extraSize + _headerOffset);
}
/* Local file header:
local file header signature 4 bytes (0x04034b50)
version needed to extract 2 bytes
general purpose bit flag 2 bytes
compression method 2 bytes
last mod file time 2 bytes
last mod file date 2 bytes
crc-32 4 bytes
compressed size 4 bytes
uncompressed size 4 bytes
filename length 2 bytes
extra field length 2 bytes
filename (variable size)
extra field (variable size)
*/
/* Central directory's File header:
central file header signature 4 bytes (0x02014b50)
version made by 2 bytes
version needed to extract 2 bytes
general purpose bit flag 2 bytes
compression method 2 bytes
last mod file time 2 bytes
last mod file date 2 bytes
crc-32 4 bytes
compressed size 4 bytes
uncompressed size 4 bytes
filename length 2 bytes
extra field length 2 bytes
file comment length 2 bytes
disk number start 2 bytes
internal file attributes 2 bytes
external file attributes 4 bytes
relative offset of local header 4 bytes
filename (variable size)
extra field (variable size)
file comment (variable size)
*
/* End of central dir record:
end of central dir signature 4 bytes (0x06054b50)
number of this disk 2 bytes
number of the disk with the
start of the central directory 2 bytes
total number of entries in
the central dir on this disk 2 bytes
total number of entries in
the central dir 2 bytes
size of the central directory 4 bytes
offset of start of central
directory with respect to
the starting disk number 4 bytes
zipfile comment length 2 bytes
zipfile comment (variable size)
*/
/* DOS Date and time:
MS-DOS date. The date is a packed value with the following format. Bits Description
0-4 Day of the month (131)
5-8 Month (1 = January, 2 = February, and so on)
9-15 Year offset from 1980 (add 1980 to get actual year)
MS-DOS time. The time is a packed value with the following format. Bits Description
0-4 Second divided by 2
5-10 Minute (059)
11-15 Hour (023 on a 24-hour clock)
*/
private DateTime DosTimeToDateTime(uint _dt)
{
return new DateTime(
(int)(_dt >> 25) + 1980,
(int)(_dt >> 21) & 15,
(int)(_dt >> 16) & 31,
(int)(_dt >> 11) & 31,
(int)(_dt >> 5) & 63,
(int)(_dt & 31) * 2);
}
// Reads the end-of-central-directory record
private bool ReadFileInfo()
{
if (this.ZipFileStream.Length < 22)
return false;
try
{
this.ZipFileStream.Seek(-17, SeekOrigin.End);
BinaryReader br = new BinaryReader(this.ZipFileStream);
do
{
this.ZipFileStream.Seek(-5, SeekOrigin.Current);
UInt32 sig = br.ReadUInt32();
if (sig == 0x06054b50)
{
this.ZipFileStream.Seek(6, SeekOrigin.Current);
UInt16 entries = br.ReadUInt16();
Int32 centralSize = br.ReadInt32();
UInt32 centralDirOffset = br.ReadUInt32();
UInt16 commentSize = br.ReadUInt16();
// check if comment field is the very last data in file
if (this.ZipFileStream.Position + commentSize != this.ZipFileStream.Length)
return false;
// Copy entire central directory to a memory buffer
this.ExistingFiles = entries;
this.CentralDirImage = new byte[centralSize];
this.ZipFileStream.Seek(centralDirOffset, SeekOrigin.Begin);
this.ZipFileStream.Read(this.CentralDirImage, 0, centralSize);
// Leave the pointer at the begining of central dir, to append new files
this.ZipFileStream.Seek(centralDirOffset, SeekOrigin.Begin);
return true;
}
} while (this.ZipFileStream.Position > 0);
}
catch { }
return false;
}
#endregion
#region IDisposable Members
/// <summary>
/// Closes the Zip file stream
/// </summary>
public void Dispose()
{
this.Close();
}
#endregion
}
}