mirror of
https://github.com/SubtitleEdit/subtitleedit.git
synced 2024-11-25 04:33:04 +01:00
Initial version
git-svn-id: https://subtitleedit.googlecode.com/svn/trunk@13 99eadd0c-20b8-1223-b5c4-2a2b2df33de2
This commit is contained in:
parent
f73c7479e4
commit
976be2c6e6
36
src/Logic/Configuration.cs
Normal file
36
src/Logic/Configuration.cs
Normal 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
86
src/Logic/FastBitmap.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
164
src/Logic/FindReplaceDialogHelper.cs
Normal file
164
src/Logic/FindReplaceDialogHelper.cs
Normal 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
26
src/Logic/HistoryItem.cs
Normal 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
408
src/Logic/IfoParser.cs
Normal 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
361
src/Logic/ImageSplitter.cs
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
28
src/Logic/ImageSplitterItem.cs
Normal file
28
src/Logic/ImageSplitterItem.cs
Normal 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
1362
src/Logic/Language.cs
Normal file
File diff suppressed because it is too large
Load Diff
1160
src/Logic/LanguageStructure.cs
Normal file
1160
src/Logic/LanguageStructure.cs
Normal file
File diff suppressed because it is too large
Load Diff
105
src/Logic/Paragraph.cs
Normal file
105
src/Logic/Paragraph.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
src/Logic/PositionAndSize.cs
Normal file
15
src/Logic/PositionAndSize.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
56
src/Logic/PositionsAndSizes.cs
Normal file
56
src/Logic/PositionsAndSizes.cs
Normal 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
902
src/Logic/Settings.cs
Normal 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
241
src/Logic/StripableText.cs
Normal 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
397
src/Logic/Subtitle.cs
Normal 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
125
src/Logic/TimeCode.cs
Normal 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
1335
src/Logic/Utilities.cs
Normal file
File diff suppressed because it is too large
Load Diff
22
src/Logic/Wave.cs
Normal file
22
src/Logic/Wave.cs
Normal 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
353
src/Logic/WavePeak.cs
Normal 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
433
src/Logic/ZipExtractor.cs
Normal 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 (1–31)
|
||||||
|
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 (0–59)
|
||||||
|
11-15 Hour (0–23 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
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user