mirror of
https://github.com/SubtitleEdit/subtitleedit.git
synced 2024-10-28 15:02:35 +01:00
9ea30501dc
git-svn-id: https://subtitleedit.googlecode.com/svn/trunk@740 99eadd0c-20b8-1223-b5c4-2a2b2df33de2
522 lines
21 KiB
C#
522 lines
21 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using Nikse.SubtitleEdit.Logic;
|
|
using Nikse.SubtitleEdit.Logic.VobSub;
|
|
|
|
namespace Nikse.SubtitleEdit.Forms
|
|
{
|
|
public sealed partial class DvdSubRip : Form
|
|
{
|
|
volatile bool _abort;
|
|
public List<VobSubMergedPack> MergedVobSubPacks;
|
|
public List<Color> Palette;
|
|
public List<string> Languages;
|
|
LanguageStructure.DvdSubRip _language;
|
|
long _lastPresentationTimeStamp = 0;
|
|
long _lastVobPresentationTimeStamp = 0;
|
|
long _lastNavEndPts = 0;
|
|
long _accumulatedPresentationTimeStamp;
|
|
public TimeSpan LastPresentationTimeStamp
|
|
{
|
|
get
|
|
{
|
|
if (radioButtonPal.Checked)
|
|
{
|
|
float ticksPerMillisecond = 90.000F;
|
|
return TimeSpan.FromMilliseconds(Convert.ToDouble(_lastPresentationTimeStamp / ticksPerMillisecond));
|
|
}
|
|
else
|
|
{
|
|
float ticksPerMillisecond = 90.090F;
|
|
return TimeSpan.FromMilliseconds(Convert.ToDouble(_lastPresentationTimeStamp / ticksPerMillisecond));
|
|
}
|
|
}
|
|
}
|
|
|
|
public double FrameRate
|
|
{
|
|
get
|
|
{
|
|
if (radioButtonPal.Checked)
|
|
return 25.0;
|
|
else
|
|
return 30.0;
|
|
}
|
|
}
|
|
|
|
public string SelectedLanguage
|
|
{
|
|
get
|
|
{
|
|
if (comboBoxLanguages.SelectedIndex >= 0)
|
|
return string.Format("{0} (0x{1:x})", comboBoxLanguages.Items[comboBoxLanguages.SelectedIndex], comboBoxLanguages.SelectedIndex + 32);
|
|
return string.Empty;
|
|
}
|
|
}
|
|
|
|
public DvdSubRip()
|
|
{
|
|
InitializeComponent();
|
|
labelStatus.Text = string.Empty;
|
|
buttonStartRipping.Enabled = false;
|
|
|
|
_language = Configuration.Settings.Language.DvdSubrip;
|
|
Text = _language.Title;
|
|
groupBoxDvd.Text = _language.DvdGroupTitle;
|
|
labelIfoFile.Text = _language.IfoFile;
|
|
labelVobFiles.Text = _language.VobFiles;
|
|
groupBoxLanguages.Text = _language.Languages;
|
|
groupBoxPalNtsc.Text = _language.PalNtsc;
|
|
radioButtonPal.Text = _language.Pal;
|
|
radioButtonNtsc.Text = _language.Ntsc;
|
|
buttonStartRipping.Text = _language.StartRipping;
|
|
buttonAddVobFile.Text = _language.Add;
|
|
ButtonRemoveVob.Text = _language.Remove;
|
|
buttonClear.Text = _language.Clear;
|
|
ButtonMoveVobDown.Text = _language.MoveDown;
|
|
ButtonMoveVobUp.Text = _language.MoveUp;
|
|
|
|
if (System.Threading.Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName == "en")
|
|
radioButtonNtsc.Checked = true;
|
|
else
|
|
radioButtonPal.Checked = true;
|
|
|
|
FixLargeFonts();
|
|
}
|
|
|
|
private void FixLargeFonts()
|
|
{
|
|
Graphics graphics = this.CreateGraphics();
|
|
SizeF textSize = graphics.MeasureString(buttonAddVobFile.Text, this.Font);
|
|
if (textSize.Height > buttonAddVobFile.Height - 4)
|
|
{
|
|
int newButtonHeight = (int)(textSize.Height + 7 + 0.5);
|
|
Utilities.SetButtonHeight(this, newButtonHeight, 1);
|
|
}
|
|
}
|
|
|
|
private void ButtonOpenIfoClick(object sender, EventArgs e)
|
|
{
|
|
openFileDialog1.Multiselect = false;
|
|
openFileDialog1.Filter = _language.IfoFiles + "|*.IFO";
|
|
openFileDialog1.FileName = string.Empty;
|
|
if (openFileDialog1.ShowDialog() == DialogResult.OK && File.Exists(openFileDialog1.FileName))
|
|
{
|
|
OpenIfoFile(openFileDialog1.FileName);
|
|
}
|
|
}
|
|
|
|
private void OpenIfoFile(string fileName)
|
|
{
|
|
Clear();
|
|
textBoxIfoFileName.Text = fileName;
|
|
|
|
// List vob files
|
|
string path = Path.GetDirectoryName(fileName);
|
|
string onlyFileName = Path.GetFileNameWithoutExtension(fileName);
|
|
string searchPattern = onlyFileName.Substring(0, onlyFileName.Length - 1) + "?.VOB";
|
|
listBoxVobFiles.Items.Clear();
|
|
for (int i = 1; i < 30; i++)
|
|
{
|
|
string vobFileName = searchPattern.Replace("?", i.ToString());
|
|
if (File.Exists(Path.Combine(path, vobFileName)))
|
|
listBoxVobFiles.Items.Add(Path.Combine(path, vobFileName));
|
|
}
|
|
|
|
if (listBoxVobFiles.Items.Count == 0)
|
|
{
|
|
searchPattern = onlyFileName.Substring(0, onlyFileName.Length - 1) + "PGC_01_?.VOB";
|
|
for (int i = 1; i < 30; i++)
|
|
{
|
|
string vobFileName = searchPattern.Replace("?", i.ToString());
|
|
if (File.Exists(Path.Combine(path, vobFileName)))
|
|
listBoxVobFiles.Items.Add(Path.Combine(path, vobFileName));
|
|
}
|
|
}
|
|
|
|
|
|
var ifoParser = new IfoParser(fileName);
|
|
if (!string.IsNullOrEmpty(ifoParser.ErrorMessage))
|
|
{
|
|
Clear();
|
|
MessageBox.Show(ifoParser.ErrorMessage);
|
|
return;
|
|
}
|
|
|
|
// List info
|
|
labelIfoFile.Text = _language.IfoFile + ": " + ifoParser.VideoTitleSetVobs.VideoStream.Resolution;
|
|
bool isPal = string.Compare(ifoParser.VideoTitleSetVobs.VideoStream.Standard, "PAL", true) == 0;
|
|
if (isPal)
|
|
radioButtonPal.Checked = true;
|
|
else
|
|
radioButtonNtsc.Checked = true;
|
|
|
|
// List languages
|
|
comboBoxLanguages.Items.Clear();
|
|
foreach (string s in ifoParser.VideoTitleSetVobs.Subtitles)
|
|
comboBoxLanguages.Items.Add(s);
|
|
if (comboBoxLanguages.Items.Count > 0)
|
|
comboBoxLanguages.SelectedIndex = 0;
|
|
|
|
// Save palette (Color LookUp Table)
|
|
if (ifoParser.VideoTitleSetProgramChainTable.ProgramChains.Count > 0)
|
|
Palette = ifoParser.VideoTitleSetProgramChainTable.ProgramChains[0].ColorLookupTable;
|
|
|
|
buttonStartRipping.Enabled = listBoxVobFiles.Items.Count > 0;
|
|
}
|
|
|
|
private void ButtonStartRippingClick(object sender, EventArgs e)
|
|
{
|
|
if (buttonStartRipping.Text == _language.Abort)
|
|
{
|
|
_abort = true;
|
|
buttonStartRipping.Text = _language.StartRipping;
|
|
return;
|
|
}
|
|
_abort = false;
|
|
buttonStartRipping.Text = _language.Abort;
|
|
_lastPresentationTimeStamp = 0;
|
|
_lastVobPresentationTimeStamp = 0;
|
|
_lastNavEndPts = 0;
|
|
_accumulatedPresentationTimeStamp = 0;
|
|
|
|
progressBarRip.Visible = true;
|
|
var ms = new MemoryStream();
|
|
int i = 0;
|
|
foreach (string vobFileName in listBoxVobFiles.Items)
|
|
{
|
|
i++;
|
|
labelStatus.Text = string.Format(_language.RippingVobFileXofYZ, Path.GetFileName(vobFileName), i, listBoxVobFiles.Items.Count);
|
|
Refresh();
|
|
Application.DoEvents();
|
|
|
|
if (!_abort)
|
|
RipSubtitles(vobFileName, ms, i-1); // Rip/demux subtitle vob packs
|
|
}
|
|
progressBarRip.Visible = false;
|
|
buttonStartRipping.Enabled = false;
|
|
if (_abort)
|
|
{
|
|
labelStatus.Text = _language.AbortedByUser;
|
|
buttonStartRipping.Text = _language.StartRipping;
|
|
buttonStartRipping.Enabled = true;
|
|
return;
|
|
}
|
|
|
|
labelStatus.Text = string.Format(_language.ReadingSubtitleData);
|
|
Refresh();
|
|
Application.DoEvents();
|
|
var vobSub = new VobSubParser(radioButtonPal.Checked);
|
|
vobSub.Open(ms);
|
|
ms.Close();
|
|
labelStatus.Text = string.Empty;
|
|
|
|
MergedVobSubPacks = vobSub.MergeVobSubPacks(); // Merge splitted-packs to whole-packs
|
|
Languages = new List<string>();
|
|
for (int k = 0; k < comboBoxLanguages.Items.Count; k++)
|
|
Languages.Add(string.Format("{0} (0x{1:x})", comboBoxLanguages.Items[k], k + 32));
|
|
|
|
buttonStartRipping.Text = _language.StartRipping;
|
|
buttonStartRipping.Enabled = true;
|
|
DialogResult = DialogResult.OK;
|
|
}
|
|
|
|
private void RipSubtitles(string vobFileName, MemoryStream stream, int vobNumber)
|
|
{
|
|
long firstNavStartPTS = 0;
|
|
|
|
FileStream fs = null;
|
|
bool tryAgain = true;
|
|
while (tryAgain)
|
|
{
|
|
try
|
|
{
|
|
fs = new FileStream(vobFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
|
tryAgain = false;
|
|
}
|
|
catch (IOException exception)
|
|
{
|
|
var result = MessageBox.Show(string.Format("An error occured while opening file: {0}", exception.Message), "", MessageBoxButtons.RetryCancel);
|
|
if (result == DialogResult.Cancel)
|
|
return;
|
|
if (result == DialogResult.Retry)
|
|
tryAgain = true;
|
|
}
|
|
}
|
|
|
|
byte[] buffer = new byte[0x800];
|
|
long position = 0;
|
|
progressBarRip.Maximum = 100;
|
|
progressBarRip.Value = 0;
|
|
int lba = 0;
|
|
while (position < fs.Length && !_abort)
|
|
{
|
|
int bytesRead = 0;
|
|
|
|
// Reading and test for IO errors... and allow abort/retry/ignore
|
|
tryAgain = true;
|
|
while (tryAgain && position < fs.Length)
|
|
{
|
|
tryAgain = false;
|
|
try
|
|
{
|
|
fs.Seek(position, SeekOrigin.Begin);
|
|
bytesRead = fs.Read(buffer, 0, 0x0800);
|
|
}
|
|
catch (IOException exception)
|
|
{
|
|
var result = MessageBox.Show(string.Format("An error occured while reading file: {0}", exception.Message), "", MessageBoxButtons.AbortRetryIgnore);
|
|
if (result == DialogResult.Abort)
|
|
return;
|
|
if (result == DialogResult.Retry)
|
|
tryAgain = true;
|
|
if (result == DialogResult.Ignore)
|
|
{
|
|
position += 0x800;
|
|
tryAgain = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (VobSubParser.IsMpeg2PackHeader(buffer))
|
|
{
|
|
VobSubPack vsp = new VobSubPack(buffer, null);
|
|
if (IsSubtitlePack(buffer))
|
|
{
|
|
if (vsp.PacketizedElementaryStream.PresentationTimeStamp.HasValue && _accumulatedPresentationTimeStamp != 0)
|
|
UpdatePresentationTimeStamp(buffer, _accumulatedPresentationTimeStamp, vsp);
|
|
|
|
stream.Write(buffer, 0, 0x800);
|
|
if (bytesRead < 0x800)
|
|
stream.Write(Encoding.ASCII.GetBytes(new string(' ', 0x800 - bytesRead)), 0, 0x800 - bytesRead);
|
|
}
|
|
else if (IsPrivateStream2(buffer, 0x26))
|
|
{
|
|
if (Helper.GetEndian(buffer, 0x0026, 4) == 0x1bf && Helper.GetEndian(buffer, 0x400, 4) == 0x1bf)
|
|
{
|
|
uint vobu_s_ptm = Helper.GetEndian(buffer, 0x0039, 4);
|
|
uint vobu_e_ptm = Helper.GetEndian(buffer, 0x003d, 4);
|
|
|
|
_lastPresentationTimeStamp = vobu_e_ptm;
|
|
|
|
if (firstNavStartPTS == 0)
|
|
{
|
|
firstNavStartPTS = vobu_s_ptm;
|
|
if (vobNumber == 0)
|
|
_accumulatedPresentationTimeStamp = -vobu_s_ptm;
|
|
}
|
|
if (vobu_s_ptm + firstNavStartPTS + _accumulatedPresentationTimeStamp < _lastVobPresentationTimeStamp)
|
|
{
|
|
_accumulatedPresentationTimeStamp += _lastNavEndPts - vobu_s_ptm;
|
|
}
|
|
else if (_lastNavEndPts > vobu_e_ptm)
|
|
{
|
|
_accumulatedPresentationTimeStamp += _lastNavEndPts - vobu_s_ptm;
|
|
}
|
|
_lastNavEndPts = vobu_e_ptm;
|
|
}
|
|
}
|
|
|
|
}
|
|
position += 0x800;
|
|
|
|
progressBarRip.Value = (int)((position * 100) / fs.Length);
|
|
Application.DoEvents();
|
|
lba++;
|
|
}
|
|
fs.Close();
|
|
_lastVobPresentationTimeStamp = _lastPresentationTimeStamp;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write the 5 PTS bytes to buffer
|
|
/// </summary>
|
|
private void UpdatePresentationTimeStamp(byte[] buffer, long addPresentationTimeStamp, VobSubPack vsp)
|
|
{
|
|
const int presentationTimeStampIndex = 23;
|
|
|
|
string pre = "0011";
|
|
if (vsp.PacketizedElementaryStream.PresentationTimeStampDecodeTimeStampFlags == Helper.B00000010)
|
|
pre = "0010";
|
|
|
|
long newPts = addPresentationTimeStamp + ((long)vsp.PacketizedElementaryStream.PresentationTimeStamp.Value);
|
|
string bString = Convert.ToString(newPts, 2).PadLeft(33, '0');
|
|
|
|
string fiveBytesString = pre + bString.Substring(0, 3) + "1" + bString.Substring(3, 15) + "1" + bString.Substring(18, 15) + "1";
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
byte b = Convert.ToByte(fiveBytesString.Substring((i * 8), 8), 2);
|
|
buffer[presentationTimeStampIndex + i] = b;
|
|
}
|
|
}
|
|
|
|
internal static bool IsPrivateStream2(byte[] buffer, int index)
|
|
{
|
|
return buffer.Length >= index + 3 &&
|
|
buffer[index + 0] == 0 &&
|
|
buffer[index + 1] == 0 &&
|
|
buffer[index + 2] == 1 &&
|
|
buffer[index + 3] == 0xbf;
|
|
}
|
|
|
|
private static bool IsSubtitlePack(byte[] buffer)
|
|
{
|
|
const int mpeg2HeaderLength = 14;
|
|
if (buffer[0] == 0 &&
|
|
buffer[1] == 0 &&
|
|
buffer[2] == 1 &&
|
|
buffer[3] == 0xba) // 186 - MPEG-2 Pack Header
|
|
{
|
|
if (buffer[mpeg2HeaderLength + 0] == 0 &&
|
|
buffer[mpeg2HeaderLength + 1] == 0 &&
|
|
buffer[mpeg2HeaderLength + 2] == 1 &&
|
|
buffer[mpeg2HeaderLength + 3] == 0xbd) // 189 - Private stream 1 (non MPEG audio, subpictures)
|
|
{
|
|
int pesHeaderDataLength = buffer[mpeg2HeaderLength + 8];
|
|
int streamId = buffer[mpeg2HeaderLength + 8 + 1 + pesHeaderDataLength];
|
|
if (streamId >= 0x20 && streamId <= 0x3f) // Subtitle IDs allowed (or x3f to x40?)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void ButtonAddVobFileClick(object sender, EventArgs e)
|
|
{
|
|
openFileDialog1.Filter = _language.VobFiles + "|*.VOB";
|
|
openFileDialog1.FileName = string.Empty;
|
|
openFileDialog1.Multiselect = true;
|
|
if (openFileDialog1.ShowDialog() == DialogResult.OK && File.Exists(openFileDialog1.FileName))
|
|
{
|
|
foreach (var fileName in openFileDialog1.FileNames)
|
|
{
|
|
listBoxVobFiles.Items.Add(fileName);
|
|
}
|
|
}
|
|
buttonStartRipping.Enabled = listBoxVobFiles.Items.Count > 0;
|
|
}
|
|
|
|
private void ButtonMoveVobUp_Click(object sender, EventArgs e)
|
|
{
|
|
if (listBoxVobFiles.SelectedIndex > -1 && listBoxVobFiles.SelectedIndex > 0)
|
|
{
|
|
int index = listBoxVobFiles.SelectedIndex;
|
|
string old = listBoxVobFiles.Items[index].ToString();
|
|
listBoxVobFiles.Items.RemoveAt(index);
|
|
listBoxVobFiles.Items.Insert(index - 1, old);
|
|
listBoxVobFiles.SelectedIndex = index - 1;
|
|
}
|
|
}
|
|
|
|
private void ButtonMoveVobDown_Click(object sender, EventArgs e)
|
|
{
|
|
if (listBoxVobFiles.SelectedIndex > -1 && listBoxVobFiles.SelectedIndex < listBoxVobFiles.Items.Count -1)
|
|
{
|
|
int index = listBoxVobFiles.SelectedIndex;
|
|
string old = listBoxVobFiles.Items[index].ToString();
|
|
listBoxVobFiles.Items.RemoveAt(index);
|
|
listBoxVobFiles.Items.Insert(index + 1, old);
|
|
listBoxVobFiles.SelectedIndex = index + 1;
|
|
}
|
|
}
|
|
|
|
private void ButtonRemoveVob_Click(object sender, EventArgs e)
|
|
{
|
|
if (listBoxVobFiles.SelectedIndex > -1)
|
|
{
|
|
int index = listBoxVobFiles.SelectedIndex;
|
|
listBoxVobFiles.Items.RemoveAt(index);
|
|
if (index < listBoxVobFiles.Items.Count)
|
|
listBoxVobFiles.SelectedIndex = index;
|
|
else if (index > 0)
|
|
listBoxVobFiles.SelectedIndex = index-1;
|
|
|
|
buttonStartRipping.Enabled = listBoxVobFiles.Items.Count > 0;
|
|
|
|
if (listBoxVobFiles.Items.Count == 0)
|
|
Clear();
|
|
}
|
|
}
|
|
|
|
private void TextBoxIfoFileNameDragEnter(object sender, DragEventArgs e)
|
|
{
|
|
if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
|
|
e.Effect = DragDropEffects.All;
|
|
}
|
|
|
|
private void TextBoxIfoFileNameDragDrop(object sender, DragEventArgs e)
|
|
{
|
|
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
|
|
if (files.Length >= 1)
|
|
{
|
|
string fileName = files[0];
|
|
|
|
var fi = new FileInfo(fileName);
|
|
string ext = Path.GetExtension(fileName).ToLower();
|
|
if (fi.Length < 1024 * 1024 * 2) // max 2 mb
|
|
{
|
|
if (ext == ".ifo")
|
|
OpenIfoFile(fileName);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ListBoxVobFilesDragEnter(object sender, DragEventArgs e)
|
|
{
|
|
if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
|
|
e.Effect = DragDropEffects.All;
|
|
}
|
|
|
|
private void ListBoxVobFilesDragDrop(object sender, DragEventArgs e)
|
|
{
|
|
if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
|
|
e.Effect = DragDropEffects.All;
|
|
|
|
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
|
|
foreach (string fileName in files)
|
|
{
|
|
string ext = Path.GetExtension(fileName).ToLower();
|
|
if (ext == ".vob")
|
|
listBoxVobFiles.Items.Add(fileName);
|
|
}
|
|
buttonStartRipping.Enabled = listBoxVobFiles.Items.Count > 0;
|
|
}
|
|
|
|
private void ButtonClearClick(object sender, EventArgs e)
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
private void Clear()
|
|
{
|
|
textBoxIfoFileName.Text = string.Empty;
|
|
listBoxVobFiles.Items.Clear();
|
|
buttonStartRipping.Enabled = false;
|
|
comboBoxLanguages.Items.Clear();
|
|
labelIfoFile.Text = _language.IfoFile;
|
|
}
|
|
|
|
private void DvdSubRip_KeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
if (e.KeyCode == Keys.Escape)
|
|
{
|
|
if (buttonStartRipping.Text == _language.Abort)
|
|
{
|
|
ButtonStartRippingClick(sender, e);
|
|
}
|
|
else
|
|
{
|
|
e.SuppressKeyPress = true;
|
|
DialogResult = DialogResult.Cancel;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|