(*************************************************************************
  vStrip_gui: Graphical User Interface for vStrip-DLL
  Copyright (C) 2001 [maven] (maven@maven.de)

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  0.8f:
  - added dvd2avi support to gui

  0.8e:
  - fixed SetStart/-End LBA not always working on Cell-Level

  0.8d:
  - rearranged output screen to show dependency of outputs
  - added chapters
  - added video info (IFO)
  - changed flags/type
  - fixed incorrect fio_BUFFERSIZE
  - better popup menu in ifo selection
  - ask before adding menu vobs
  - eta/throughput

  0.8c:
  - not much new on this side really... :)

  0.8b:
  - incorporated changed prototype/defines from command-line 0.8b
    (maybe 1gb size is now detected correctly)
  - fixed Key conversion (hex) when giving a Key

  0.8a:
  - udf file-selecter, new fileexit and filesize routines w/ udf support

  0.8:
  - incorporated console changes (different structures, udf, aspi)

  0.7c:
  - added audio & subp lang-support
  - fixed swapped cell & vob id when saving settings (.vsc)

  0.7b:
  - added CELL LBA display
  - renamed # of Sectors to End LBA (because that's what it was)
  - added drag & drop support for files (ifo, vsc, lst, everything else -> added)
  - added popup-menu to ifo-tree (to set start & end lba)
  - improved Percentage/ProgressBar display when using start- and/or end-lba

  0.7:
  - added support for multiple outputs
  - revised .vsc format (and loading & saving)
  - reorganised things a bit to make room
  - removed maximise button (hi alex!)

  0.6ß:
  - command-line support for files (.vsc loaded, others added)
  - icon (could someone draw me a nice one, please?)

*************************************************************************)

unit MainForm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls, StdCtrls, ExtCtrls, CheckLst, Math, vStrip, vStrip_Thread,
  Registry, IniFiles, ShellApi, Menus, Grids;

const
  vStripRegistryPath = '\Software\[maven]\vStrip_GUI';
type
  TStreamInfo = record
    save      : Byte;
    remap_to  : Byte;
  end;
  TAStreamInfo = array[0..511] of TStreamInfo; // 0..255 streams, 256..511 substreams
  TIFOProgramChain = record
    NumCells  : Integer;
    Length    : t_vs_time;
    Cells     : TPAVobCellID;
  end;
  TOutputInfo = record
    FileName  : string;
    FileSize  : Cardinal; // split
    Flags     : Cardinal;
  end;
  TvStripForm = class(TForm)
    Input: TTabSheet;
    IFO: TTabSheet;
    Output: TTabSheet;
    ButtonOpenVOB: TButton;
    OpenVOBDialog: TOpenDialog;
    ButtonRemoveVOB: TButton;
    ButtonSortVOB: TButton;
    ButtonDownVOB: TButton;
    ButtonUpVOB: TButton;
    OpenIFODialog: TOpenDialog;
    ListViewFiles: TListView;
    TreeViewIFO: TTreeView;
    CheckListBoxStreams: TCheckListBox;
    ListBoxRemap: TListBox;
    ButtonClearVOB: TButton;
    CheckBoxSubstreams: TCheckBox;
    GroupBoxStreams: TGroupBox;
    ButtonAllStreams: TButton;
    ButtonNoStreams: TButton;
    LabelRemap: TLabel;
    GroupBoxOutputOptions: TGroupBox;
    CheckBoxDemux: TCheckBox;
    CheckBoxKeepGOPs: TCheckBox;
    RadioGroupSplit: TRadioGroup;
    CheckBoxAppend: TCheckBox;
    CheckBoxAC3Header: TCheckBox;
    CheckBoxPCMHeader: TCheckBox;
    GroupBoxSplitSize: TGroupBox;
    GroupBoxFileName: TGroupBox;
    ButtonOutput: TButton;
    EditFileName: TEdit;
    SaveOutputDialog: TSaveDialog;
    ButtonRun: TButton;
    ProgressBar1: TProgressBar;
    GroupBoxInputOptions: TGroupBox;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    EditStartLBA: TEdit;
    EditEndLBA: TEdit;
    EditMaxSync: TEdit;
    GroupBoxDecrypt: TGroupBox;
    EditPadGuess: TEdit;
    EditSameGuess: TEdit;
    EditPercentageGuess: TEdit;
    EditKey: TEdit;
    Label10: TLabel;
    Label12: TLabel;
    GroupBox1: TGroupBox;
    EditIFOName: TEdit;
    ButtonOpenIFO: TButton;
    RadioGroupAngles: TRadioGroup;
    GroupBoxCells: TGroupBox;
    ListBoxVOBID: TListBox;
    CheckListBoxCELLID: TCheckListBox;
    ButtonIFOReset: TButton;
    Label1: TLabel;
    Label6: TLabel;
    ListViewInfo: TListView;
    SaveSettingsDialog: TSaveDialog;
    OpenSettingsDialog: TOpenDialog;
    GroupBox2: TGroupBox;
    ButtonLoadSettings: TButton;
    ButtonSaveSettings: TButton;
    ComboBoxKeySearch: TComboBox;
    Label7: TLabel;
    GroupBox3: TGroupBox;
    ComboBoxFrameRate: TComboBox;
    Label5: TLabel;
    Label8: TLabel;
    ComboBoxAspectRatio: TComboBox;
    RadioGroupOutputs: TRadioGroup;
    ComboBoxFileSize: TComboBox;
    PopupMenuIFO: TPopupMenu;
    SetStartLBA: TMenuItem;
    SetEndLBA: TMenuItem;
    GroupBoxIFOAudio: TGroupBox;
    ListBoxIFOAudio: TListBox;
    GroupBoxIFOSub: TGroupBox;
    ListBoxIFOSub: TListBox;
    CheckBoxIsVOB: TCheckBox;
    ComboBoxASPI: TComboBox;
    ButtonUDF: TButton;
    CheckBoxDemacro: TCheckBox;
    GroupBoxIFOVideo: TGroupBox;
    EditIFOVideo: TEdit;
    SetStartEndLBA: TMenuItem;
    ETA: TTabSheet;
    GroupBoxETA: TGroupBox;
    EditETA: TEdit;
    GroupBoxSpeed: TGroupBox;
    EditKS: TEdit;
    Sheets: TPageControl;
    CheckBoxDVD2AVI: TCheckBox;
    procedure ButtonOpenVOBClick(Sender: TObject);
    procedure ButtonRemoveVOBClick(Sender: TObject);
    procedure ButtonSortVOBClick(Sender: TObject);
    procedure ButtonUpVOBClick(Sender: TObject);
    procedure ButtonDownVOBClick(Sender: TObject);
    procedure ButtonOpenIFOClick(Sender: TObject);
    procedure ListViewFilesCompare(Sender: TObject; Item1,
      Item2: TListItem; Data: Integer; var Compare: Integer);
    procedure FormCreate(Sender: TObject);
    procedure ButtonRunClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ButtonClearVOBClick(Sender: TObject);
    procedure ListBoxRemapClick(Sender: TObject);
    procedure CheckBoxSubstreamsClick(Sender: TObject);
    procedure ButtonAllStreamsClick(Sender: TObject);
    procedure ButtonNoStreamsClick(Sender: TObject);
    procedure CheckBoxDemuxClick(Sender: TObject);
    procedure EditFileSizeKeyPress(Sender: TObject; var Key: Char);
    procedure ButtonOutputClick(Sender: TObject);
    procedure EditKeyKeyPress(Sender: TObject; var Key: Char);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure ButtonIFOResetClick(Sender: TObject);
    procedure ListBoxVOBIDClick(Sender: TObject);
    procedure CheckListBoxCELLIDClickCheck(Sender: TObject);
    procedure TreeViewIFOChange(Sender: TObject; Node: TTreeNode);
    procedure CheckListBoxStreamsClick(Sender: TObject);
    procedure ButtonSaveSettingsClick(Sender: TObject);
    procedure ButtonLoadSettingsClick(Sender: TObject);
    procedure EditFileNameChange(Sender: TObject);
    procedure RadioGroupOutputsClick(Sender: TObject);
    procedure TreeViewIFOContextPopup(Sender: TObject; MousePos: TPoint;
      var Handled: Boolean);
    procedure SetStartLBAClick(Sender: TObject);
    procedure SetEndLBAClick(Sender: TObject);
    procedure ButtonUDFClick(Sender: TObject);
    procedure SetStartEndLBAClick(Sender: TObject);
    procedure CheckBoxKeepGOPsClick(Sender: TObject);
  private
    data: t_vs_data;
    streams, substreams: ta_vs_streamflags;
    stream_ofs: Integer;
    output_mask: Byte;
    outputs: array[0..inb_MAX_OUTPUT - 1] of TOutputInfo;
    num_output, cur_output: Integer;
    stream_info: TAStreamInfo;
    cell_list: TPAVobCellID;
    num_cell: Integer;
    KeepCells: array[0..255, 0..255, 0..1] of Boolean; // 0 display, 1 keep
    ResetKeepCells: Boolean;
    NumIFOChains: Integer;
    IFOChainIdx: Integer;
    IFOChains: array of TIFOProgramChain;
    PopupMenuItem: TTreeNode;
    VSThread: TVSThread;
    VSThreadActive: Boolean;
    procedure ParseCardinal(s: String; initial_value: Cardinal; var target: Cardinal);
    procedure LoadInitialDirectory(dlg: TOpenDialog; regname: String);
    procedure SaveInitialDirectory(dlg: TOpenDialog; regname: String; FileName: String);
    procedure LoadSettings(FName: String);
    procedure SaveSettings(FName: String);
    procedure AddTime(var sum: array of Byte; b: array of Byte);
    procedure ParseIFO(FileName: String);
    procedure UpdateFileName(Name: String; OP: TRadioGroup; cur: Integer; var num: Integer);
    procedure GetOutputSettings(var op: TOutputInfo);
    procedure PutOutputSettings(op: TOutputInfo);
    function AddFile(Name: String): Boolean;
    procedure OnVSThreadTerminate(Sender: TObject);
    procedure WMDropFiles(var m: TMessage); message WM_DROPFILES;
  public
    { Public-Deklarationen }
    function GetStreamDescription(Idx: Byte; SubStream: Boolean): String;
  end;
var
  vStripForm: TvStripForm;

implementation

uses UDFSel;

{$R *.DFM}

procedure TvStripForm.ParseCardinal(s: String; initial_value: Cardinal; var target: Cardinal);
var
  i: Cardinal;
  j: Integer;
begin
  target := initial_value;
  if (s <> '') then begin
    Val(s, i, j);
    if (j = 0) then
      target := i;
  end;
end;

procedure TvStripForm.LoadInitialDirectory(dlg: TOpenDialog; regname: String);
var
  reg: TRegistry;
begin
  reg := TRegistry.Create(KEY_READ);
  try
    if (reg.OpenKey(regname, False)) then begin
      if (reg.ValueExists(dlg.Name)) then
        dlg.InitialDir := reg.ReadString(dlg.Name);
      reg.CloseKey();
    end;
  finally
    reg.Free();
  end;
end;

procedure TvStripForm.SaveInitialDirectory(dlg: TOpenDialog; regname: String; FileName: String);
var
  reg: TRegistry;
begin
  reg := TRegistry.Create(KEY_WRITE);
  try
    if (reg.OpenKey(regname, True)) then begin
      reg.WriteString(dlg.Name, ExtractFileDir(FileName));
      reg.CloseKey();
    end;
  finally
    reg.Free();
  end;
end;

procedure TvStripForm.LoadSettings(FName: String);
var
  ini: TMemIniFile;
  i, j, k, l, m: Integer;
  SaveSaveStreams: Boolean;
  OrMask, AndMask: Byte;
begin
  ini := TMemIniFile.Create(FName);

  // input
  ListViewFiles.Items.Clear();
  k := ini.ReadInteger('Input', 'NumFiles', 0);
  for i := 0 to k - 1 do
    AddFile(ini.ReadString('Input', 'F' + IntToStr(i), ''));

  EditStartLBA.Text := ini.ReadString('Input', 'OfsPackets', '');
  EditEndLBA.Text := ini.ReadString('Input', 'NumPackets', '');
  EditMaxSync.Text := ini.ReadString('Input', 'MaxSync', '2048');

  {$IFDEF vs_DECRYPT}
  EditPadGuess.Text := ini.ReadString('Input', 'PadGuess', '2');
  EditSameGuess.Text := ini.ReadString('Input', 'SameGuess', '2');
  EditPercentageGuess.Text := ini.ReadString('Input', 'PercentageGuess', '75');
  EditKey.Text := ini.ReadString('Input', 'Key', '');
  ComboBoxKeySearch.ItemIndex := ini.ReadInteger('Input', 'KeySearch', 0);
  {$ENDIF}

  ComboBoxASPI.ItemIndex := ini.ReadInteger('Input', 'ASPIMode', 0);
  CheckBoxIsVOB.Checked := ini.ReadBool('Input', 'IsVOB', true);

  // ifo
  ButtonIFOResetClick(nil);

  EditIFOName.Text := ini.ReadString('IFO', 'IFOName', '');
  if (EditIFOName.Text <> '') then
    ParseIFO(EditIFOName.Text);

  k := ini.ReadInteger('IFO', 'NumKeepCells', 0);
  if (k = 65536) then begin
    for i := 0 to 255 do
      for j := 0 to 255 do
        KeepCells[i, j, 1] := True
  end else begin
    for i := 0 to 255 do
       for j := 0 to 255 do
         KeepCells[i, j, 1] := False;
    for i := 0 to k - 1 do begin
      j := ini.ReadInteger('IFO', 'K' + IntToStr(i), -1);
      if (j <> -1) then
        KeepCells[j and $ff, j shr 8, 1] := True;
    end;
  end;

  k := ini.ReadInteger('IFO', 'IFOChainIdx', -1);
  if (k <> -1) then begin // select IFOChainIdx
    if (k < NumIFOChains) then begin
      for i := 0 to TreeViewIFO.Items.Count - 1 do with TreeViewIFO.Items[i] do
        if ((Parent = nil) and (Integer(Data) = k)) then begin
          IFOChainIdx := -1;
          ResetKeepCells := False;
          TreeViewIFO.Selected := TreeViewIFO.Items[i];
          ResetKeepCells := True;
          break;
        end;
    end;
  end;

  if (RadioGroupAngles.Enabled) then begin
    i := ini.ReadInteger('IFO', 'Angles', -1);
    if ((i <> -1) and (i < RadioGroupAngles.Items.Count)) then
      RadioGroupAngles.ItemIndex := i;
  end;

  // output
  CheckBoxDemacro.Checked := ini.ReadBool('Output', 'Demacro', True);
  ComboBoxFrameRate.ItemIndex := ini.ReadInteger('Output', 'FrameRate', 0);
  ComboBoxAspectRatio.ItemIndex := ini.ReadInteger('Output', 'AspectRatio', 0);

  RadioGroupOutputs.Items.Clear();
  num_output := ini.ReadInteger('Output', 'NumOutputs', 0);
  k := 0;
  for i := 0 to num_output do begin
    RadioGroupOutputs.Items.Append('Output ' + IntToStr(k));
    outputs[i].FileName := ini.ReadString('Output' + IntToStr(i), 'FileName', '');
    j := ini.ReadInteger('Output' + IntToStr(i), 'FileSize', 0);
    outputs[i].FileSize := j * ((1 shl 20) div fio_BUFFER_SIZE);
    with outputs[i] do begin
      flags := 0;
      if (ini.ReadBool('Output' + IntToStr(i), 'Append', False)) then
        flags := flags or vso_APPEND;
      if (ini.ReadBool('Output' + IntToStr(i), 'Demux', False)) then
        flags := flags or vso_DEMUX;
      if (ini.ReadBool('Output' + IntToStr(i), 'AC3Header', False)) then
        flags := flags or vso_KEEP_AC3_IDENT_BYTES;
      if (ini.ReadBool('Output' + IntToStr(i), 'PCMHeader', False)) then
        flags := flags or vso_KEEP_PCM_IDENT_BYTES;
      if (ini.ReadBool('Output' + IntToStr(i), 'KeepGOPs', False)) then
        flags := flags or vso_ONLY_KEEP_GOPS;
      if (ini.ReadBool('Output' + IntToStr(i), 'DVD2AVI', False)) then
        flags := flags or vso_DVD2AVI;
      j := ini.ReadInteger('Output' + IntToStr(i), 'Split', 0);
      case j of
        1: flags := flags or vso_SPLIT_VOBID;
        2: flags := flags or vso_SPLIT_CELLID;
      end;
    end;

    SaveSaveStreams := ini.ReadBool('Output' + IntToStr(i), 'SaveSaveStreams', False);
    // init masks
    if (SaveSaveStreams) then begin
      OrMask := $00;
      AndMask := Byte(not (1 shl i));
    end else begin
      OrMask := 1 shl i;
      AndMask := $ff;
    end;
    for l := 0 to 511 do
      stream_info[l].save := (stream_info[l].save or OrMask) and AndMask;
    // saved masks
    if (not SaveSaveStreams) then begin
      OrMask := $00;
      AndMask := Byte(not (1 shl i));
    end else begin
      OrMask := 1 shl i;
      AndMask := $ff;
    end;

    j := ini.ReadInteger('Output' + IntToStr(i), 'NumSaveStreams', 0);
    for l := 0 to j - 1 do begin
      m := ini.ReadInteger('Output' + IntToStr(i), 'S' + IntToStr(l), 0);
      stream_info[m].save := (stream_info[m].save or OrMask) and AndMask;
    end;

    Inc(k);
  end;

  cur_output := 0;
  PutOutputSettings(outputs[0]); // these will be copied on the next line
  RadioGroupOutputs.ItemIndex := 0;
  UpdateFileName(outputs[0].FileName, RadioGroupOutputs, 0, num_output); // empty one to RadioGroup if neccessary

  j := ini.ReadInteger('Output', 'NumRemaps', 0);
  for i := 0 to j - 1 do begin
    k := ini.ReadInteger('Output', 'R' + IntToStr(i), 0);
    stream_info[k and $1ff].remap_to := k shr 9;
  end;

  CheckBoxSubStreams.Checked := False;
  for i := 0 to 255 do begin
    CheckListBoxStreams.Checked[i] := (stream_info[i].save and output_mask) <> 0;
    CheckListBoxStreams.Items.Strings[i] := GetStreamDescription(i, False);
  end;
  CheckListBoxStreams.ItemIndex := -1;
  ListBoxRemap.ItemIndex := -1;
  stream_ofs := 0;

  RadioGroupOutputsClick(nil);

  ini.Free();
end;

procedure TvStripForm.SaveSettings(FName: String);
var
  ini: TMemIniFile;
  i, j, k, l: Integer;
  SaveSaveStreams: Boolean;
  Mask, MaxMask: Byte;
begin
  ini := TMemIniFile.Create(FName);
  ini.Clear();

  // input
  ini.WriteInteger('Input', 'NumFiles', ListViewFiles.Items.Count);
  for i := 0 to ListViewFiles.Items.Count - 1 do
    ini.WriteString('Input', 'F' + IntToStr(i), ListViewFiles.Items[i].Caption);

  if (EditStartLBA.Text <> '') then
    ini.WriteString('Input', 'OfsPackets', EditStartLBA.Text);
  if (EditEndLBA.Text <> '') then
    ini.WriteString('Input', 'NumPackets', EditEndLBA.Text);
  if (EditMaxSync.Text <> '') then
    ini.WriteString('Input', 'MaxSync', EditMaxSync.Text);

  {$IFDEF vs_DECRYPT}
  if (EditPadGuess.Text <> '') then
    ini.WriteString('Input', 'PadGuess', EditPadGuess.Text);
  if (EditSameGuess.Text <> '') then
    ini.WriteString('Input', 'SameGuess', EditSameGuess.Text);
  if (EditPercentageGuess.Text <> '') then
    ini.WriteString('Input', 'PercentageGuess', EditPercentageGuess.Text);
  if (EditKey.Text <> '') then
    ini.WriteString('Input', 'Key', EditKey.Text);
  ini.WriteInteger('Input', 'KeySearch', ComboBoxKeySearch.ItemIndex);
  {$ENDIF}

  ini.WriteInteger('Input', 'ASPIMode', ComboBoxASPI.ItemIndex);
  ini.WriteBool('Input', 'IsVOB', CheckBoxIsVOB.Checked);

  // ifo
  ini.WriteString('IFO', 'IFOName', EditIFOName.Text);
  if (IFOChainIdx <> -1) then
    ini.WriteInteger('IFO', 'IFOChainIdx', IFOChainIdx);

  k := 0;
  for i := 0 to 255 do
    for j := 0 to 255 do
      if (KeepCells[i, j, 1]) then
        Inc(k);
  ini.WriteInteger('IFO', 'NumKeepCells', k);

  if ((k <> 0) and (k <> 256 * 256)) then begin
    k := 0;
    for i := 0 to 255 do
      for j := 0 to 255 do
        if (KeepCells[i, j, 1]) then begin
           ini.WriteInteger('IFO', 'K' + IntToStr(k), i or (j shl 8)); // vob_id upper byte
           Inc(k);
        end;
  end;

  if (RadioGroupAngles.Enabled) then
    ini.WriteInteger('IFO', 'Angles', RadioGroupAngles.ItemIndex);

  // output
  ini.WriteBool('Output', 'Demacro', CheckBoxDemacro.Checked);
  ini.WriteInteger('Output', 'FrameRate', ComboBoxFrameRate.ItemIndex);
  ini.WriteInteger('Output', 'AspectRatio', ComboBoxAspectRatio.ItemIndex);

  GetOutputSettings(outputs[cur_output]);
  j := 0;
  for i := 0 to num_output do
    if (outputs[i].FileName <> '') then
      Inc(j);
  ini.WriteInteger('Output', 'NumOutputs', j);

  j := 0;
  MaxMask := 0;
  for i := 0 to num_output do
    if (outputs[i].FileName <> '') then with outputs[i] do begin
      MaxMask := MaxMask or (1 shl i);
      ini.WriteString('Output' + IntToStr(j), 'FileName', FileName);
      ini.WriteInteger('Output' + IntToStr(j), 'FileSize', FileSize div ((1 shl 20) div fio_BUFFER_SIZE));
      k := 0;
      if ((flags and vso_SPLIT_VOBID) <> 0) then
        k := 1
      else if ((flags and vso_SPLIT_CELLID) <> 0) then
        k := 2;
      ini.WriteInteger('Output' + IntToStr(j), 'Split', k);
      ini.WriteBool('Output' + IntToStr(j), 'Append', (flags and vso_APPEND) <> 0);
      ini.WriteBool('Output' + IntToStr(j), 'Demux', (flags and vso_DEMUX) <> 0);
      ini.WriteBool('Output' + IntToStr(j), 'AC3Header', (flags and vso_KEEP_AC3_IDENT_BYTES) <> 0);
      ini.WriteBool('Output' + IntToStr(j), 'PCMHeader', (flags and vso_KEEP_PCM_IDENT_BYTES) <> 0);
      ini.WriteBool('Output' + IntToStr(j), 'KeepGOPs', (flags and vso_ONLY_KEEP_GOPS) <> 0);
      ini.WriteBool('Output' + IntToStr(j), 'DVD2AVI', (flags and vso_DVD2AVI) <> 0);

      Mask := 1 shl i;
      k := 0;
      for l := 0 to 511 do
        if ((stream_info[l].save and Mask) <> 0) then
          Inc(k);
      SaveSaveStreams := k < 256;
      ini.WriteBool('Output' + IntToStr(j), 'SaveSaveStreams', SaveSaveStreams);
      if (not SaveSaveStreams) then begin
        ini.WriteInteger('Output' + IntToStr(j), 'NumSaveStreams', 512 - k);
        k := 0;
        for l := 0 to 511 do with stream_info[l] do
          if ((save and Mask) = 0) then begin
             ini.WriteInteger('Output' + IntToStr(j), 'S' + IntToStr(k), l);
             Inc(k);
          end;
      end else begin
        ini.WriteInteger('Output' + IntToStr(j), 'NumSaveStreams', k);
        k := 0;
        for l := 0 to 511 do with stream_info[l] do
          if ((save and Mask) <> 0) then begin
             ini.WriteInteger('Output' + IntToStr(j), 'S' + IntToStr(k), l);
             Inc(k);
          end;
      end;
      Inc(j);
    end;

  j := 0;
  for i := 0 to 511 do with stream_info[i] do
    if (((save and MaxMask) <> 0) and (remap_to <> (i and 255))) then
      Inc(j);
  ini.WriteInteger('Output', 'NumRemaps', j);

  j := 0;
  for i := 0 to 511 do with stream_info[i] do
    if (((save and MaxMask) <> 0) and (remap_to <> (i and 255))) then begin
       ini.WriteInteger('Output', 'R' + IntToStr(j), i + (Ord(remap_to) shl 9));
       Inc(j);
    end;

  ini.UpdateFile();
  ini.Free();
end;

procedure TvStripForm.AddTime(var sum: array of Byte; b: array of Byte);
var
  i: Integer;
  max_f: Integer;
begin
  for i := Low(sum) to High(sum) do
    Inc(sum[i], (b[i] and DVD_TIME_AND));
  max_f := 25;
  if ((b[3] and (not DVD_TIME_AND)) = 192) then
    max_f := 30;

  if (sum[3] >= max_f) then begin
    Dec(sum[3], max_f);
    Inc(sum[2]);
  end;
  if (sum[2] >= 60) then begin // seconds
    Dec(sum[2], 60);
    Inc(sum[1]);
  end;
  if (sum[1] >= 60) then begin // minutes
    Dec(sum[1], 60);
    Inc(sum[0]);
  end;
end;

procedure TvStripForm.ParseIFO(FileName: String);
var
  i, j, k: Integer;
  LastChapter: Integer;
  ifo_handle, ifo_flags: Cardinal;
  it, ch: TTreeNode;
  ctime: array[0..3] of Byte;
  s: String;
begin
  TreeViewIFO.Items.Clear();
  IFOChainIdx := -1;
  RadioGroupAngles.Items.Clear();
  RadioGroupAngles.Enabled := False;
  ListBoxIFOAudio.Items.Clear();
  ListBoxIFOSub.Items.Clear();

  for i := 0 to NumIFOChains - 1 do
    if (IFOChains[i].Cells <> Nil) then begin
      FreeMem(IFOChains[i].Cells);
      IFOChains[i].Cells := Nil;
    end;
  SetLength(IFOChains, 0);
  NumIFOChains := 0;

  it := nil;
  ifo_flags := 0;
  case ComboBoxASPI.ItemIndex of
    0: ifo_flags := ifo_flags + fio_USE_ASPI;
    1: ifo_flags := ifo_flags + fio_USE_ASPI + vs_PREFER_ASPI;
  end;

  ifo_handle := ifoOpen(PChar(EditIFOName.Text), ifo_flags);
  if (ifo_handle <> 0) then begin
    EditIFOVideo.Text := ifoGetVideoDesc(ifo_handle);

    j := ifoGetNumAudio(ifo_handle);
    for i := 0 to j - 1 do
      ListBoxIFOAudio.Items.Add(Format('%d. %s', [i, ifoGetAudioDesc(ifo_handle, i)]));

    j := ifoGetNumSubPic(ifo_handle);
    for i := 0 to j - 1 do
      ListBoxIFOSub.Items.Add(Format('%d. %s', [i, ifoGetSubPicDesc(ifo_handle, i)]));

    with TreeViewIFO.Items do begin
      BeginUpdate();
      NumIFOChains := ifoGetNumPGCI(ifo_handle);
      SetLength(IFOChains, NumIFOChains);
      for i := 0 to NumIFOChains - 1 do with IFOChains[i] do begin
        NumCells := ifoGetPGCIInfo(ifo_handle, i, Length);
        it := AddObject(nil, Format('%u. Length: %u:%.2u:%.2u:%.2u in %d cell(s)', [i, Length[0], Length[1], Length[2], Length[3], NumCells]), Pointer(i));
        GetMem(Cells, NumCells * SizeOf(t_vs_vobcellid));
        ifoGetPGCICells(ifo_handle, i, @Cells^[0]);
        ch := it;
        LastChapter := -1;
        for j := 0 to NumCells - 1 do with Cells[j] do begin
          if (chapter <> LastChapter) then begin
            ctime[0] := 0; ctime[1] := 0; ctime[2] := 0; ctime[3] := 0;
            k := j;
            while ((k < NumCells) and (Cells[k].chapter = chapter)) do begin
              AddTime(ctime, Cells[k].time);
              Inc(k);
            end;
            ch := AddChildObject(it, Format('%u. Chapter (%u:%.2u:%.2u:%.2u)',
              [chapter, ctime[0], ctime[1], ctime[2], ctime[3]]), Pointer(j));
            LastChapter := chapter;
          end;
          s := Format('%u. V: %.3u/C: %.3u', [j, vob_id, cell_id]);
          if (angle <> $11) then
            s := s + Format(' (%u/%u)', [angle and $f, angle shr 4]);
          s := s + Format(' [@LBA %u - %u]', [start_lba, end_lba]);
          AddChildObject(ch, s, Pointer(j));
        end;
      end;
      if ((it <> nil) and (NumIFOChains = 1)) then
        TreeViewIFO.Selected := it;
      EndUpdate();
    end;
    ifoClose(ifo_handle);
  end else
    MessageDlg('vStrip could not read/parse the IFO-File', mtError, [mbOK], 0);
end;

procedure TvStripForm.UpdateFileName(Name: String; OP: TRadioGroup; cur: Integer; var num: Integer);
var
  i: Integer;
  f: Boolean;
begin
  if (Name <> '') then begin // add if not one free
    outputs[cur].FileName := Name;
    f := False;
    for i := 0 to num do
      if (outputs[i].FileName = '') then
        f := True;
    if ((not f) and (num < inb_MAX_OUTPUT - 1)) then begin
      Inc(num);
      OP.Items.Append('Output ' + IntToStr(num));
    end;
  end else begin // remove if last
    if ((num > 1) and ((cur = num) or (outputs[num].FileName = ''))) then begin
      OP.Items.Delete(OP.Items.Count - 1);
      Dec(Num);
    end;
  end;
end;

procedure TvStripForm.GetOutputSettings(var op: TOutputInfo);
begin
  op.FileName := EditFileName.Text;

  if (ComboBoxFileSize.Text = '') then
    op.FileSize := 0
  else begin
    ParseCardinal(ComboBoxFileSize.Text, 0, op.FileSize);
    op.FileSize := op.FileSize * ((1 shl 20) div fio_BUFFER_SIZE);
  end;

  op.flags := 0;

  case RadioGroupSplit.ItemIndex of
    1: op.flags := op.flags + vso_SPLIT_VOBID;
    2: op.flags := op.flags + vso_SPLIT_CELLID;
  end;

  if (CheckBoxDemux.Checked) then begin
    op.flags := op.flags + vso_DEMUX;
    if (CheckBoxAC3Header.Checked) then
      op.flags := op.flags + vso_KEEP_AC3_IDENT_BYTES;
    if (CheckBoxPCMHeader.Checked) then
      op.flags := op.flags + vso_KEEP_PCM_IDENT_BYTES;
  end;

  if (CheckBoxKeepGOPs.Checked) then
    op.flags := op.flags + vso_ONLY_KEEP_GOPS;

  if (CheckBoxDVD2AVI.Checked) then
    op.flags := op.flags + vso_DVD2AVI;

  if (CheckBoxAppend.Checked) then
    op.flags := op.flags + vso_APPEND;
end;

procedure TvStripForm.PutOutputSettings(op: TOutputInfo);
begin
  EditFileName.Text := op.FileName;

  if (op.FileSize = 0) then
    ComboBoxFileSize.Text := ''
  else
    ComboBoxFileSize.Text := IntToStr(op.FileSize div ((1 shl 20) div fio_BUFFER_SIZE));

  if ((op.flags and vso_SPLIT_VOBID) <> 0) then
    RadioGroupSplit.ItemIndex := 1
  else if ((op.flags and vso_SPLIT_CELLID) <> 0) then
    RadioGroupSplit.ItemIndex := 2
  else
    RadioGroupSplit.ItemIndex := 0;

  CheckBoxDemux.Checked := (op.flags and vso_DEMUX) <> 0;
  CheckBoxAC3Header.Checked := (op.flags and vso_KEEP_AC3_IDENT_BYTES) <> 0;
  CheckBoxAC3Header.Enabled := CheckBoxDemux.Checked;
  CheckBoxPCMHeader.Checked := (op.flags and vso_KEEP_PCM_IDENT_BYTES) <> 0;
  CheckBoxPCMHeader.Enabled := CheckBoxDemux.Checked;

  CheckBoxKeepGOPs.Checked := (op.flags and vso_ONLY_KEEP_GOPS) <> 0;
  CheckBoxDVD2AVI.Checked := (op.flags and vso_DVD2AVI) <> 0;

  CheckBoxAppend.Checked := (op.flags and vso_APPEND) <> 0;
end;

function TvStripForm.AddFile(Name: String): Boolean;
var
  it: TListItem;
  s: String;
  sl: TextFile;
  ignore: Boolean;
begin
  Result := False;
  if ((Name <> '') and uFileExists(Name)) then begin
    if (AnsiCompareFileName(ExtractFileExt(Name), '.VSC') = 0) then begin
      LoadSettings(Name);
    end else if (AnsiCompareFileName(ExtractFileExt(Name), '.IFO') = 0) then begin
      EditIFOName.Text := Name;
      ParseIFO(Name);
    end else if (AnsiCompareFileName(ExtractFileExt(Name), '.LST') = 0) then begin
      AssignFile(sl, Name);
      Reset(sl);
      while (not EoF(sl)) do begin
        ReadLn(sl, s);
        if ((s <> '') and (s <> Name) and uFileExists(s)) then
          AddFile(s);
      end;
      CloseFile(sl);
      Result := True;
    end else begin
      ignore := False;
      s := Name;
      while (Pos('\', s) > 0) do
        Delete(s, 1, Pos('\', s));

      if (Length(s) = 12) then begin
        s[5] := 'X'; s[6] := 'X';
        if (AnsiCompareFileName(s, 'VTS_XX_0.VOB') = 0) then
          ignore := MessageDlg(Format('%s seems to be a Menu VOB...' + #13 + #13 + 'Really add it to the List?', [Name]), mtConfirmation, [mbYes, mbNo], 0) <> mrYes;
      end;

      if (not ignore) then begin
        it := ListViewFiles.Items.Add();
        it.Caption := Name;
        it.SubItems.Add(IntToStr(((uGetSize(it.Caption) + (1 shl 19)) shr 20)));
      end;
    end;
  end;
end;

procedure TvStripForm.OnVSThreadTerminate(Sender: TObject);
begin
  // these are *not* needed anymore, as vs_done was definitely called!
  if (data.infile <> nil) then begin
    FreeMem(data.infile);
    data.infile := nil;
  end;
  if (cell_list <> nil) then begin
    FreeMem(cell_list);
    cell_list := nil;
    num_cell := 0;
  end;
  VSThread := nil;
  VSThreadActive := False;
  ETA.TabVisible := False;
  ButtonRun.Caption := '&Run';
  ButtonRun.Hint := 'Start Processing';
end;

procedure TvStripForm.WMDropFiles(var m: TMessage);
var
  i, j: Cardinal;
  buf: array[0..511] of Char;
  was_empty, did_list : Boolean;
begin
  was_empty := ListViewFiles.Items.Count <= 0;
  did_list := False;

  j := DragQueryFile(m.WParam, $ffffffff, Nil, 0);
  for i := 1 to j do begin
    DragQueryFile(m.WParam, i - 1, @buf, SizeOf(buf));
    if (AddFile(buf)) then
      did_list := True;
  end;

  if (was_empty and not did_list) then
    ListViewFiles.CustomSort(nil, 0);

  DragFinish(m.WParam);
end;

function TvStripForm.GetStreamDescription(Idx: Byte; SubStream: Boolean): String;
var
  s: String;
begin
  s := Format('0x%.2x', [Idx]);
  if (SubStream) then
    case Idx of
      $20..$3f:  s := s + ' (Subtitle Stream ' + IntToStr(Idx - $20) + ')';
      $80..$87:  s := s + ' (AC3 Audio Stream ' + IntToStr(Idx - $80) + ')';
      $a0..$a7:  s := s + ' (PCM Audio Stream ' + IntToStr(Idx - $a0) + ')';
    end
  else
    case Idx of
      $bd:       s := s + ' (Private Stream 1)';
      $be:       s := s + ' (Padding Stream)';
      $bf:       s := s + ' (Private Stream 2)';
      $c0..$c7:  s := s + ' (Audio Stream ' + IntToStr(Idx - $c0) + ')';
      $e0..$e7:  s := s + ' (Video Stream ' + IntToStr(Idx - $e0) + ')';
    end;
  Result := s;
end;

procedure TvStripForm.ButtonOpenVOBClick(Sender: TObject);
var
  i: Integer;
  was_empty, did_list: Boolean;
begin
  OpenVOBDialog.FileName := '';
  if (OpenVOBDialog.Execute()) then with ListViewFiles.Items do begin
    SaveInitialDirectory(OpenVOBDialog, vStripRegistryPath, OpenVOBDialog.FileName);
    was_empty := Count = 0;
    did_list := False;
    BeginUpdate();
    for i := 0 to OpenVOBDialog.Files.Count - 1 do
      if (AddFile(OpenVOBDialog.Files[i])) then
        did_list := True;
    if (was_empty and not did_list) then
      ListViewFiles.CustomSort(nil, 0);
    EndUpdate();
  end;
end;

procedure TvStripForm.ButtonRemoveVOBClick(Sender: TObject);
var
  i: Integer;
begin
  ListViewFiles.Items.BeginUpdate();
  while (ListViewFiles.SelCount > 0) do
    for i := 0 to ListViewFiles.Items.Count - 1 do
      if (ListViewFiles.Items[i].Selected) then begin
        ListViewFiles.Items.Delete(i);
        break;
      end;
  ListViewFiles.Items.EndUpdate();
end;

procedure TvStripForm.ButtonSortVOBClick(Sender: TObject);
begin
  ListViewFiles.CustomSort(nil, 0);
end;

procedure TvStripForm.ButtonUpVOBClick(Sender: TObject);
var
  i: Integer;
  it: TListItem;
begin
  if ((ListViewFiles.SelCount > 0) and not ListViewFiles.Items[0].Selected) then with ListViewFiles.Items do begin
    BeginUpdate();
    for i := 1 to Count - 1 do
      if (ListViewFiles.Items[i].Selected) then begin
        it := Insert(i - 1);
        it.Caption := ListViewFiles.Items[i + 1].Caption;
        it.Selected := ListViewFiles.Items[i + 1].Selected;
        it.SubItems.Add(ListViewFiles.Items[i + 1].SubItems[0]);
        Delete(i + 1);
      end;
    EndUpdate();
  end;
end;

procedure TvStripForm.ButtonDownVOBClick(Sender: TObject);
var
  i: Integer;
  it: TListItem;
begin
  if ((ListViewFiles.SelCount > 0) and not ListViewFiles.Items[ListViewFiles.Items.Count - 1].Selected) then with ListViewFiles.Items do begin
    BeginUpdate();
    for i := Count - 2 downto 0 do
      if (ListViewFiles.Items[i].Selected) then begin
        it := Insert(i + 2);
        it.Caption := ListViewFiles.Items[i].Caption;
        it.Selected := ListViewFiles.Items[i].Selected;
        it.SubItems.Add(ListViewFiles.Items[i].SubItems[0]);
        Delete(i);
      end;
    EndUpdate();
  end;
end;

procedure TvStripForm.ButtonOpenIFOClick(Sender: TObject);
begin
  OpenIFODialog.FileName := EditIFOName.Text;
  if (OpenIFODialog.Execute()) then begin
    SaveInitialDirectory(OpenIFODialog, vStripRegistryPath, OpenIFODialog.FileName);
    EditIFOName.Text := OpenIFODialog.FileName;
    ParseIFO(OpenIFODialog.FileName);
  end;
end;

procedure TvStripForm.ListViewFilesCompare(Sender: TObject; Item1,
  Item2: TListItem; Data: Integer; var Compare: Integer);
begin
  Compare := AnsiCompareFileName(Item1.Caption, Item2.Caption);
end;

procedure TvStripForm.FormCreate(Sender: TObject);
var
  i: Integer;
  vv, fl: Cardinal;
  vv_str: String;
  dec_mismatch: Boolean;
begin
  // Parse Version Info From DLL
  vv := vs_get_version(fl);
  vv_str := Format('%u.%u%s', [(vv shr 24) and $ff, (vv shr 16) and $ff, Chr((vv shr 8) and $ff)]);
  if ((vv and $ff) > 1) then
    vv_str := vv_str + IntToStr(vv and $ff);

  if ((fl and vsv_DECRYPT) <> 0) then
    vv_str := vv_str + '_css';

  vv_str := vv_str + ' by ' + vs_get_author();

  Caption := Caption + ' ' + vv_str;

{$IFDEF vs_DECRYPT}
  dec_mismatch := True;
{$ELSE}
  dec_mismatch := False;
{$ENDIF}
  if (dec_mismatch <> ((fl and vsv_DECRYPT) <> 0)) then begin
    MessageDlg('Feature/Version Mismatch in ' + vs_DllName + ' and GUI!', mtError, [mbOK], 0);
    Application.Terminate();
  end;

  // Fill the streams in
  for i := 0 to 255 do begin
    stream_info[i].save := $ff;
    stream_info[i].remap_to := i;
    stream_info[i + 256].save := $ff;
    stream_info[i + 256].remap_to := i;
    CheckListBoxStreams.Checked[CheckListBoxStreams.Items.Add(GetStreamDescription(i, False))] := True;
    ListBoxRemap.Items.Add(Format('0x%.2x', [i]));
  end;
  NumIFOChains := 0;
  IFOChainIdx := -1;
  stream_ofs := 0;
  output_mask := $01;
  num_cell := 0;
  cell_list := nil;

  ResetKeepCells := True;
  VSThread := nil;
  VSThreadActive := False;
  ButtonIFOResetClick(nil); // init the cell-list to everything

  LoadInitialDirectory(OpenVOBDialog, vStripRegistryPath);
  LoadInitialDirectory(OpenIFODialog, vStripRegistryPath);
  LoadInitialDirectory(OpenSettingsDialog, vStripRegistryPath);
  LoadInitialDirectory(SaveOutputDialog, vStripRegistryPath);
  LoadInitialDirectory(SaveSettingsDialog, vStripRegistryPath);

  ComboBoxFrameRate.ItemIndex := 0;
  ComboBoxAspectRatio.ItemIndex := 0;
  ComboBoxASPI.ItemIndex := 0;

  {$IFDEF vs_DECRYPT}
  ComboBoxKeySearch.ItemIndex := 0;
  GroupBoxDecrypt.Visible := True;
  {$ENDIF}

  num_output := 0;
  cur_output := 0;
  for i := 0 to inb_MAX_OUTPUT - 1 do begin
    outputs[i].FileName := '';
    outputs[i].FileSize := 0;
    outputs[i].Flags := 0;
  end;

  PopupMenuItem := Nil;

  if (FileExists('vStrip_gui.vsc')) then
    LoadSettings('vStrip_gui.vsc');

  ButtonUDF.Enabled := aspi_ok;

  // parse command-line parameters
  for i := 1 to ParamCount() do
    AddFile(ParamStr(i));

  DragAcceptFiles(Handle, True);
end;

procedure TvStripForm.ButtonRunClick(Sender: TObject);
var
  i, j, k, ai: Integer;
  total_lba: Int64;
{$IFDEF vs_DECRYPT}
  key: Int64;
{$ENDIF}
begin
  if ((not VSThreadActive) and (VSThread = nil) and (ListViewFiles.Items.Count > 0)) then begin
    VSThreadActive := True;

    ETA.TabVisible := True;

    data.infile := CreateStreamList(ListViewFiles, total_lba);

    GetOutputSettings(outputs[cur_output]);
    j := 0;
    for i := 0 to num_output do begin
      StrLCopy(data.outputs[i].outfile, PChar(outputs[i].FileName), SizeOf(data.outputs[i].outfile));
      data.outputs[i].split_output := outputs[i].FileSize;
      data.outputs[i].flags := outputs[i].flags;
      if (outputs[i].FileName <> '') then
        Inc(j);
    end;
    data.num_outputs := j;

    data.flags := vs_SUPPORT_1GB;

    data.framerate := $ffffffff;
    if (ComboBoxFrameRate.ItemIndex > 0) then
      data.framerate := ComboBoxFrameRate.ItemIndex;

    data.aspectratio := $ffffffff;
    if (ComboBoxAspectRatio.ItemIndex > 0) then
      data.aspectratio := ComboBoxAspectRatio.ItemIndex;

    if (not CheckBoxISVOB.Checked) then
      data.flags := data.flags + vs_NO_VOB;

    if (CheckBoxDemacro.Checked) then
      data.flags := data.flags + vs_DEMACRO;

    case ComboBoxASPI.ItemIndex of
      0: data.flags := data.flags + vs_USE_ASPI;
      1: data.flags := data.flags + vs_USE_ASPI + vs_PREFER_ASPI;
    end;

    ParseCardinal(EditMaxSync.Text, 2048, data.max_sync_bytes);
    ParseCardinal(EditStartLBA.Text, 0, data.start_lba);
    ParseCardinal(EditEndLBA.Text, $ffffffff, data.end_lba);

    {$IFDEF vs_DECRYPT}
    ParseCardinal(EditSameGuess.Text, 2, data.same_guess);
    ParseCardinal(EditPadGuess.Text, 2, data.pad_guess);
    ParseCardinal(EditPercentageGuess.Text, 75, data.pc_guess);

    data.key[0] := 0;
    data.key[1] := 0;
    data.key[2] := 0;
    data.key[3] := 0;
    data.key[4] := 0;
    if ((EditKey.Text <> '') and (Length(EditKey.Text) = 10)) then begin
      key := StrToInt64('$' + EditKey.Text);
      data.key[0] := key shr 32;
      data.key[1] := (key shr 24) and $ff;
      data.key[2] := (key shr 16) and $ff;
      data.key[3] := (key shr 8) and $ff;
      data.key[4] := key and $ff;
    end;

    case ComboBoxKeySearch.ItemIndex of
      1: data.flags := data.flags + vs_CRACK_EACH_VOB_ID;
      2: data.flags := data.flags + vs_CRACK_EACH_CELL_ID;
      3: data.flags := data.flags + vs_CRACK_EACH_VOB_ID + vs_CRACK_EACH_CELL_ID;
      4: data.flags := data.flags + vs_DONT_CRACK;
    end;
    {$ENDIF}

    ai := 1;
    if (IFOChainIdx <> -1) then begin
      if (RadioGroupAngles.Enabled) then begin
        if (RadioGroupAngles.ItemIndex = RadioGroupAngles.Items.Count - 1) then
          ai := 0 // all angles
        else
          ai := 1 + RadioGroupAngles.ItemIndex;
      end;
    end;

    k := 0;
    for i := 0 to 255 do
      for j := 0 to 255 do
        if (KeepCells[i, j, 0] and KeepCells[i, j, 1]) then
          Inc(k);

    if ((k <> 256 * 256) and (k <> 0)) then begin
      num_cell := k;
      GetMem(cell_list, num_cell * SizeOf(t_vs_vobcellid));
      k := 0;
      if (IFOChainIdx <> -1) then begin
        for i := 0 to IFOChains[IFOChainIdx].NumCells - 1 do with IFOChains[IFOChainIdx].Cells[i] do
          if (KeepCells[vob_id and $ff, cell_id, 0] and KeepCells[vob_id and $ff, cell_id, 1]) then begin
            cell_list^[k].vob_id := vob_id;
            cell_list^[k].cell_id := cell_id;
            cell_list^[k].angle := angle;
            Inc(k);
          end;
        num_cell := k;
        if (ai <> 0) then
          for i := 0 to num_cell - 1 do
            if (((cell_list^[i].angle shr 4) >= ai) and ((cell_list^[i].angle and $f) <> ai)) then begin
              cell_list^[i].vob_id := 0;
              cell_list^[i].cell_id := 0;
            end;
      end else begin
        for i := 0 to 255 do
          for j := 0 to 255 do
            if (KeepCells[i, j, 0] and KeepCells[i, j, 1]) then begin
              cell_list^[k].vob_id := i;
              cell_list^[k].cell_id := j;
              Inc(k);
            end;
      end;
    end;

    for i := 0 to 255 do begin
      streams[i].remap_to := stream_info[i].remap_to;
      streams[i].user_func := 0;
      streams[i].save := stream_info[i].save;
      substreams[i].remap_to := stream_info[i + 256].remap_to;
      substreams[i].user_func := 0;
      substreams[i].save := stream_info[i + 256].save;
    end;
    ButtonRun.Caption := '&Abort';
    ButtonRun.Hint := 'Abort Processing';
    VSThread := TVSThread.CreateVS(@data, @streams, @substreams, total_lba, num_cell, cell_list, ProgressBar1, ListViewInfo, ListViewFiles);
    VSThread.OnTerminate := OnVSThreadTerminate;
    VSThread.Suspended := False;
  end else if ((VSThread <> nil) and VSThreadActive) then
    VSThread.Terminate();
end;

procedure TvStripForm.FormDestroy(Sender: TObject);
var
  i: Integer;
begin
  if ((VSThread <> nil) and VSThreadActive) then begin
    VSThread.Terminate();
    VSThread.WaitFor();
  end;
  for i := 0 to NumIFOChains - 1 do
    if (IFOChains[i].Cells <> NiL) then begin
      FreeMem(IFOChains[i].Cells);
      IFOChains[i].Cells := Nil;
    end;
  SetLength(IFOChains, 0);
  NumIFOChains := 0;
end;

procedure TvStripForm.ButtonClearVOBClick(Sender: TObject);
begin
  ListViewFiles.Items.Clear();
  EditStartLBA.Text := '';
  EditEndLBA.Text := '';
  EditKey.Text := '';
end;

procedure TvStripForm.ListBoxRemapClick(Sender: TObject);
begin
  if ((ListBoxRemap.ItemIndex <> -1) and (CheckListBoxStreams.ItemIndex <> -1)) then
    stream_info[stream_ofs + CheckListBoxStreams.ItemIndex].remap_to := ListBoxRemap.ItemIndex;
end;

procedure TvStripForm.CheckBoxSubstreamsClick(Sender: TObject);
var
  i: Integer;
  new_ofs: Integer;
begin
  if (CheckBoxSubstreams.Checked) then begin
    new_ofs := 256;
    GroupBoxStreams.Caption := 'SubStreams:'
  end else begin
    new_ofs := 0;
    GroupBoxStreams.Caption := 'Streams:'
  end;
  if (new_ofs <> stream_ofs) then begin
    for i := 0 to 255 do begin
      CheckListBoxStreams.Checked[i] := (stream_info[i + new_ofs].save and output_mask) <> 0;
      CheckListBoxStreams.Items.Strings[i] := GetStreamDescription(i, CheckBoxSubstreams.Checked);
    end;
    CheckListBoxStreams.ItemIndex := -1;
    ListBoxRemap.ItemIndex := -1;
    stream_ofs := new_ofs;
  end;
end;

procedure TvStripForm.ButtonAllStreamsClick(Sender: TObject);
var
  i: Integer;
begin
  for i := 0 to 255 do begin
    stream_info[i].save := stream_info[i].save or output_mask;
    stream_info[i + 256].save := stream_info[i + 256].save or output_mask;
    CheckListBoxStreams.Checked[i] := True;
  end;
end;

procedure TvStripForm.ButtonNoStreamsClick(Sender: TObject);
var
  i: Integer;
begin
  for i := 0 to 255 do begin
    stream_info[i].save := stream_info[i].save and (not output_mask);
    stream_info[i + 256].save := stream_info[i + 256].save and (not output_mask);
    CheckListBoxStreams.Checked[i] := False;
  end;
end;

procedure TvStripForm.CheckBoxDemuxClick(Sender: TObject);
var
  i, j: Integer;
begin
  CheckBoxAC3Header.Enabled := CheckBoxDemux.Checked;
  CheckBoxPCMHeader.Enabled := CheckBoxDemux.Checked;
  ListBoxRemap.Enabled := not CheckBoxDemux.Checked and (RadioGroupOutputs.ItemIndex = 0);
  LabelRemap.Enabled := not CheckBoxDemux.Checked and (RadioGroupOutputs.ItemIndex = 0);
  ButtonAllStreams.Enabled := not CheckBoxDemux.Checked and (RadioGroupOutputs.ItemIndex = 0);

  j := 0;
  if (CheckBoxDemux.Checked) then begin
    for i := 0 to 511 do
      if ((stream_info[i].save and output_mask) <> 0) then
        Inc(j);
    if (j > 1) then
      ButtonNoStreamsClick(Sender); // select none, if more than 1 selected
  end;
end;

procedure TvStripForm.EditFileSizeKeyPress(Sender: TObject; var Key: Char);
begin
  if ((Key > #31) and not (Key in ['0'..'9'])) then
    Key := #0;
end;

procedure TvStripForm.ButtonOutputClick(Sender: TObject);
begin
  SaveOutputDialog.FileName := EditFileName.Text;
  if (SaveOutputDialog.Execute()) then begin
    EditFileName.Text := SaveOutputDialog.FileName;
    UpdateFileName(EditFileName.Text, RadioGroupOutputs, cur_output, num_output);
    SaveInitialDirectory(SaveOutputDialog, vStripRegistryPath, SaveOutputDialog.FileName);
  end;
end;

procedure TvStripForm.EditKeyKeyPress(Sender: TObject; var Key: Char);
begin
  if ((Key > #31) and not (UpCase(Key) in ['0'..'9','A'..'F'])) then
    Key := #0;
end;

procedure TvStripForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if ((VSThread <> nil) and VSThreadActive) then begin
    if (MessageDlg('Operation in Progress...' + #13 + #13 + 'Quit?', mtConfirmation, [mbYes, mbNo], 0) <> mrYes) then
      CanClose := False;
  end;
end;

procedure TvStripForm.ButtonIFOResetClick(Sender: TObject);
var
  i, j: Integer;
begin
  for i := 0 to NumIFOChains - 1 do
    if (IFOChains[i].Cells <> Nil) then begin
      FreeMem(IFOChains[i].Cells);
      IFOChains[i].Cells := Nil;
    end;
  SetLength(IFOChains, 0);
  IFOChainIdx := -1;
  NumIFOChains := 0;
  RadioGroupAngles.Items.Clear();
  TreeViewIFO.Items.Clear();
  ListBoxIFOAudio.Items.Clear();
  ListBoxIFOSub.Items.Clear();
  ListBoxVOBID.Items.Clear();
  CheckListBoxCellID.Items.Clear();
  for i := 0 to 255 do begin
    for j := 0 to 255 do begin
      KeepCells[i, j, 0] := True;
      KeepCells[i, j, 1] := True;
    end;
    ListBoxVOBID.Items.Add(Format('%.3u', [i]));
    CheckListBoxCellID.Items.Add(Format('%.3u', [i]));
    CheckListBoxCellID.Checked[i] := True;
  end;
  ListBoxVOBID.ItemIndex := 0;
end;

procedure TvStripForm.ListBoxVOBIDClick(Sender: TObject);
var
  idx, i, j: Integer;
begin
  if (ListBoxVobID.ItemIndex <> -1) then begin
    Val(ListBoxVobID.Items[ListBoxVobID.ItemIndex], idx, i);
    if (i = 0) then begin
      CheckListBoxCellID.Items.Clear();
      j := 0;
      for i := 0 to 255 do
        if (KeepCells[idx, i, 0]) then begin
          CheckListBoxCellID.Items.Add(Format('%.3u', [i]));
          CheckListBoxCellID.Checked[j] := KeepCells[idx, i, 1];
          Inc(j);
        end;
    end;
  end;
end;

procedure TvStripForm.CheckListBoxCELLIDClickCheck(Sender: TObject);
var
  i, j, k: Integer;
begin
  if ((CheckListBoxCellID.ItemIndex <> -1) and (ListBoxVobID.ItemIndex <> -1)) then begin
    Val(ListBoxVobID.Items[ListBoxVobID.ItemIndex], i, k);
    if (k = 0) then begin
      Val(CheckListBoxCellID.Items[CheckListBoxCellID.ItemIndex], j, k);
      if (k = 0) then
        KeepCells[i, j, 1] := CheckListBoxCellID.Checked[CheckListBoxCellID.ItemIndex];
    end;
  end;
end;

procedure TvStripForm.TreeViewIFOChange(Sender: TObject; Node: TTreeNode);
var
  it: TTreeNode;
  i, j, k, max_a: Integer;
  arr: array[0..255] of Boolean;
begin
  if (NumIFOChains > 0) then begin
    it := Node;
    while (it.Parent <> nil) do
      it := it.Parent;
    if (IFOChainIdx <> Integer(it.Data)) then begin
      IFOChainIdx := Integer(it.Data);
      max_a := 0;
      for i := 0 to IFOChains[IFOChainIdx].NumCells - 1 do with IFOChains[IFOChainIdx].Cells[i] do
        max_a := Max(max_a, angle and $f);
      RadioGroupAngles.Enabled := max_a > 1;
      RadioGroupAngles.Items.Clear();
      for i := 0 to max_a - 1 do
        RadioGroupAngles.Items.Add('Angle ' + IntToStr(i + 1));
      RadioGroupAngles.ItemIndex := 0;
      RadioGroupAngles.Items.Add('All');

      // Update VOB/CELL-Lists
      ListBoxVOBID.Items.Clear();
      for i := 0 to 255 do
        arr[i] := False;
      for i := 0 to IFOChains[IFOChainIdx].NumCells - 1 do with IFOChains[IFOChainIdx].Cells[i] do
        arr[vob_id and $ff] := True;
      for i := 0 to 255 do
        if (arr[i]) then
          ListBoxVobID.Items.Add(Format('%.3u', [i]));

      for i := 0 to 255 do
        for j := 0 to 255 do begin
          KeepCells[i, j, 0] := False;
          if (ResetKeepCells) then
            KeepCells[i, j, 1] := False;
        end;
      for i := 0 to IFOChains[IFOChainIdx].NumCells - 1 do with IFOChains[IFOChainIdx].Cells[i] do begin
        KeepCells[vob_id and $ff, cell_id, 0] := True;
        if (ResetKeepCells) then
          KeepCells[vob_id and $ff, cell_id, 1] := True;
      end;

      if (Node.Parent = nil) then begin
        ListBoxVobID.ItemIndex := 0;                          
        ListBoxVOBIDClick(nil);
      end;
    end;
    if (Node.Parent <> nil) then begin // follow idx
      i := Integer(Node.Data);
      with IFOChains[IFOChainIdx].Cells[i] do begin
        for j := 0 to ListBoxVobID.Items.Count - 1 do begin
          Val(ListBoxVobID.Items[j], k, max_a);
          if ((max_a = 0) and (vob_id = k)) then
            if (ListBoxVobID.ItemIndex <> j) then begin
              ListBoxVobID.ItemIndex := j;
              ListBoxVOBIDClick(nil);
              break;
            end;
        end;
        for j := 0 to CheckListBoxCellID.Items.Count - 1 do begin
          Val(CheckListBoxCellID.Items[j], k, max_a);
          if ((max_a = 0) and (cell_id = k)) then begin
            CheckListBoxCellID.ItemIndex := j;
            break;
          end;
        end;
      end;
    end;
  end;
end;

procedure TvStripForm.CheckListBoxStreamsClick(Sender: TObject);
begin
  if (CheckListBoxStreams.ItemIndex <> -1) then begin
    ListBoxRemap.ItemIndex := stream_info[stream_ofs + CheckListBoxStreams.ItemIndex].remap_to;
    if (CheckListBoxStreams.Checked[CheckListBoxStreams.ItemIndex]) then
       stream_info[stream_ofs + CheckListBoxStreams.ItemIndex].save := stream_info[stream_ofs + CheckListBoxStreams.ItemIndex].save or output_mask
    else
       stream_info[stream_ofs + CheckListBoxStreams.ItemIndex].save := stream_info[stream_ofs + CheckListBoxStreams.ItemIndex].save and (not output_mask);
  end;
end;

procedure TvStripForm.ButtonSaveSettingsClick(Sender: TObject);
begin
  if (SaveSettingsDialog.Execute()) then begin
    SaveInitialDirectory(SaveSettingsDialog, vStripRegistryPath, SaveSettingsDialog.FileName);
    SaveSettings(SaveSettingsDialog.FileName);
  end;
end;

procedure TvStripForm.ButtonLoadSettingsClick(Sender: TObject);
begin
  if (OpenSettingsDialog.Execute()) then begin
    SaveInitialDirectory(OpenSettingsDialog, vStripRegistryPath, OpenSettingsDialog.FileName);
    LoadSettings(OpenSettingsDialog.FileName);
  end;
end;

procedure TvStripForm.EditFileNameChange(Sender: TObject);
begin
  if (EditFileName.Modified) then
    UpdateFileName(EditFileName.Text, RadioGroupOutputs, cur_output, num_output);
end;

procedure TvStripForm.RadioGroupOutputsClick(Sender: TObject);
var
  i: Integer;
begin
  GetOutputSettings(outputs[cur_output]);
  cur_output := RadioGroupOutputs.ItemIndex;
  PutOutputSettings(outputs[cur_output]);
  output_mask := 1 shl RadioGroupOutputs.ItemIndex;
  if (output_mask = $01) then begin
    LabelRemap.Enabled := not CheckBoxDemux.Checked;
    ListBoxRemap.Enabled := not CheckBoxDemux.Checked;
  end else begin
    LabelRemap.Enabled := False;
    ListBoxRemap.Enabled := False;
  end;
  for i := 0 to 255 do
    CheckListBoxStreams.Checked[i] := (stream_info[i + stream_ofs].save and output_mask) <> 0;
end;

procedure TvStripForm.TreeViewIFOContextPopup(Sender: TObject; MousePos: TPoint;
  var Handled: Boolean);
var
  it: TTreeNode;
begin
  if ((MousePos.x <> -1) and (MousePos.y <> - 1)) then
    it := TreeViewIFO.GetNodeAt(MousePos.x, MousePos.y)
  else
    it := TreeViewIFO.Selected;

  if (it <> Nil) then begin
    PopupMenuIFO.Items[0].Enabled := True;
    PopupMenuIFO.Items[1].Enabled := True;
    PopupMenuIFO.Items[2].Enabled := True;
    PopupMenuItem := it;
  end else begin
    PopupMenuIFO.Items[0].Enabled := False;
    PopupMenuIFO.Items[1].Enabled := False;
    PopupMenuIFO.Items[2].Enabled := False;
    PopupMenuItem := Nil;
  end;
end;

procedure TvStripForm.SetStartLBAClick(Sender: TObject);
var
  i, j: Integer;
  Top: TTreeNode;
begin
  if (PopupMenuItem <> Nil) then begin
    if (PopupMenuItem.Parent <> Nil) then begin
      Top := PopupMenuItem.Parent;
      while (Top.Parent <> Nil) do
        Top := Top.Parent;
      i := Integer(Top.Data);
      j := Integer(PopupMenuItem.Data);
      if ((i < NumIFOChains) and (j < IFOChains[i].NumCells)) then
        EditStartLBA.Text := IntToStr(IFOChains[i].Cells[j].start_lba);
    end else begin
      i := Integer(PopupMenuItem.Data);
      if ((i < NumIFOChains) and (IFOChains[i].NumCells > 0)) then
        EditStartLBA.Text := IntToStr(IFOChains[i].Cells[0].start_lba);
    end;
  end;
end;

procedure TvStripForm.SetEndLBAClick(Sender: TObject);
var
  i, j, k: Integer;
  Top: TTreeNode;
begin
  if (PopupMenuItem <> Nil) then begin
    if (PopupMenuItem.Parent <> Nil) then begin
      Top := PopupMenuItem.Parent;
      while (Top.Parent <> Nil) do
        Top := Top.Parent;
      i := Integer(Top.Data);
      j := Integer(PopupMenuItem.Data);
      if ((i < NumIFOChains) and (j < IFOChains[i].NumCells)) then begin
        if (PopupMenuItem.Parent.Parent = Nil) then begin // Chapter
          k := j;
          while ((k < IFOChains[i].NumCells) and (IFOChains[i].Cells[j].chapter = IFOChains[i].Cells[k].chapter)) do
            Inc(k);
          EditEndLBA.Text := IntToStr(IFOChains[i].Cells[k - 1].end_lba); // Chapter End
        end else
          EditEndLBA.Text := IntToStr(IFOChains[i].Cells[j].end_lba); // Cell
      end;
    end else begin
      i := Integer(PopupMenuItem.Data);
      if ((i < NumIFOChains) and (IFOChains[i].NumCells > 0)) then
        EditEndLBA.Text := IntToStr(IFOChains[i].Cells[IFOChains[i].NumCells - 1].end_lba);
    end;
  end;
end;

procedure TvStripForm.ButtonUDFClick(Sender: TObject);
var
  udf_sel: TUDFSelector;
  i: Integer;
begin
  udf_sel := TUDFSelector.Create(vStripForm);
  udf_sel.ShowModal();
  if (udf_sel.ModalResult > 0) then begin
    for i := 0 to udf_sel.ListBoxFiles.Items.Count - 1 do
      AddFile(udf_sel.ListBoxFiles.Items[i]);
  end;
  udf_sel.Free();
end;

procedure TvStripForm.SetStartEndLBAClick(Sender: TObject);
begin
  SetStartLBAClick(Sender);
  SetEndLBAClick(Sender);
end;

procedure TvStripForm.CheckBoxKeepGOPsClick(Sender: TObject);
begin
  CheckBoxDVD2AVI.Enabled := not CheckBoxKeepGOPs.Checked;
end;

end.


syntax highlighted by Code2HTML, v. 0.9.1