(************************************************************************* vStrip_Thread: Unit for vStrip Thread (that's actually doing the work) 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 *************************************************************************) unit vStrip_Thread; interface uses Windows, Classes, ComCtrls, StdCtrls, Forms, Dialogs, vStrip; const UpdateCounterAnd = 255; ETAInterval = 4000; // ms type TVSUpdateRecord = record lba: Cardinal; vob_id: Cardinal; cell_id: Cardinal; // stream-id (in low word) if vob_id = -1 key: Cardinal; // 0..3 of key if vob_id = $fffffffe end; TVSUpdate = record ProgressBar: TProgressBar; ListView: TListView; EditETA, EditKS: TEdit; OldForm1Caption: String; Events: array[0..UpdateCounterAnd] of TVSUpdateRecord; StreamsFound: array[0..511] of Boolean; NumEvents: Integer; old_vob_id, old_cell_id: Cardinal; UpdateCounter: Cardinal; TotalLBA: Cardinal; LastLBA, LastTick: Cardinal; OldPercent: Byte; was_in_mainloop: Boolean; {$IFDEF vs_DECRYPT} old_key: array[0..4] of Byte; {$ENDIF} end; TVSThread = class(TThread) private ec: t_vs_errorcode; data: tp_vs_data; streams, substreams: tpa_vs_streamflags; cell_list: TPAVobCellID; num_cell: Integer; Update: TVSUpdate; procedure InitAll(); procedure UpdateAll(); procedure CloseAll(); published constructor CreateVS(vdata: tp_vs_data; vstreams, vsubstreams: tpa_vs_streamflags; total_lba: Cardinal; vnum_cell: Integer; vcell_list: TPAVobCellID; ProgBarFile: TProgressBar; ListViewInfo, ListViewFiles: TListView); destructor Destroy; override; protected procedure Execute; override; end; implementation uses MainForm, SysUtils, Math; procedure TVSThread.InitAll(); var i: Integer; sw: Cardinal; begin Update.OldForm1Caption := vStripForm.Caption; Update.EditETA := vStripForm.EditETA; Update.EditKS := vStripForm.EditKS; Update.ProgressBar.Min := 0; Update.ProgressBar.Max := 10000; Update.ProgressBar.Position := 0; Update.ListView.Items.Clear(); Update.UpdateCounter := 0; Update.NumEvents := 0; Update.old_vob_id := $ffffffff; Update.old_cell_id := $ffffffff; Update.OldPercent := 255; Update.was_in_mainloop := False; for i := 0 to 511 do Update.StreamsFound[i] := False; {$IFDEF vs_DECRYPT} for i := 0 to 4 do Update.old_key[i] := data^.key[i]; {$ENDIF} if (data^.end_lba <> $ffffffff) then begin if (data^.end_lba < data^.start_lba) then begin sw := data^.end_lba; data^.end_lba := data^.start_lba; data^.start_lba := sw; end; if (data^.end_lba > Update.TotalLBA) then data^.end_lba := Update.TotalLBA - 1; Update.TotalLBA := (data^.end_lba - data^.start_lba) + 1; end; if ((data^.flags and vs_NO_VOB) <> 0) then Update.TotalLBA := Update.TotalLBA div 8; Update.LastLBA := data^.start_lba; Update.LastTick := GetTickCount(); ec := vs_init(data^, streams^, substreams^); end; procedure TVSThread.UpdateAll(); var time, eta: Cardinal; pc: Single; b: Byte; i: Integer; itime, ilba: Int64; s: String; it: TListItem; pts: double; begin time := GetTickCount(); if ((ec = vse_OK) and (Update.TotalLBA > 0) and (data^._in.buffer <> nil)) then begin pc := (100.0 * (data^._in.buffer^.lba - data^.start_lba)) / Update.TotalLBA; Update.ProgressBar.Position := Trunc(pc * 100.0); b := Trunc(pc); if (b <> Update.OldPercent) then begin // set form1.caption to % vStripForm.Caption := '[' + Format('%.2d', [Trunc(pc)]) + '%] ' + Update.OldForm1Caption; Application.Title := vStripForm.Caption; Update.OldPercent := b; end; if (time - Update.LastTick >= ETAInterval) then begin itime := time - Update.LastTick; ilba := data^._in.buffer^.lba - Update.LastLBA; Update.EditKS.Text := IntToStr((ilba * Int64(2 * 1000)) div itime) + ' k/s'; eta := (itime * (Update.TotalLBA - Int64(data^._in.buffer^.lba - data^.start_lba))) div ilba; DateTimeToString(s, 'hh:mm:ss', EncodeTime(Min(23, eta div (1000 * 60 * 60)), (eta div (1000 * 60)) mod 60, (eta div 1000) mod 60, eta mod 1000)); Update.EditETA.Text := s; Update.LastLBA := data^._in.buffer^.lba; Update.LastTick := time; end; end; if ((streams <> nil) and (substreams <> nil)) then begin for i := 0 to Update.NumEvents - 1 do with Update.Events[i] do begin case vob_id of $ffffffff: begin if (Boolean(cell_id and $ff)) then begin s := '0xBD ' + vStripForm.GetStreamDescription(cell_id shr 8, True); pts := substreams^[cell_id shr 8].pts; if (pts > 0.0) then begin s := s + ' PTS ' + vs_get_time(pts); if (streams^[$e0].pts > 0.0) then s := s + ' (-> delay ' + vs_get_time(pts - streams^[$e0].pts) + ')'; end; end else begin s := vStripForm.GetStreamDescription(cell_id shr 8, False); pts := streams^[cell_id shr 8].pts; if (pts > 0.0) then s := s + ' PTS ' + vs_get_time(pts); end; end; $fffffffe: s := Format('Key: 0x%.2x%.2x%.2x%.2x%.2x', [cell_id, key shr 24, (key shr 16) and $ff, (key shr 8) and $ff, key and $ff]); else s := Format('VOB-ID: %.2u/CELL-ID: %.2u', [vob_id, cell_id]); end; it := Update.ListView.Items.Add(); it.Caption := s; it.SubItems.Add(Format('[@ LBA %u]', [lba])); end; end; Update.NumEvents := 0; end; procedure TVSThread.CloseAll(); var lba_string: String; begin lba_string := Format(' [@LBA %u]', [data^._in.sti.lba]); case ec of vse_DONE: if (Update.was_in_mainloop) then Update.ProgressBar.Position := Update.ProgressBar.Max; vse_INIT_FAILED: MessageDlg('vStrip initialisation failed!', mtError, [mbOK], 0); vse_CANT_OPEN_INPUT: MessageDlg('vStrip could not open input!', mtError, [mbOK], 0); vse_CANT_CREATE_OUTPUT: MessageDlg('vStrip could not open output!', mtError, [mbOK], 0); vse_CANT_WRITE_OUTPUT: MessageDlg('vStrip could not write to output (disk full?)' + lba_string, mtError, [mbOK], 0); vse_CANT_CRACK: MessageDlg('vStrip could not decrypt' + lba_string, mtError, [mbOK], 0); vse_LOST_SYNC: MessageDlg('vStrip lost sync (not authenticated?)' + lba_string, mtError, [mbOK], 0); end; vStripForm.Caption := Update.OldForm1Caption; Application.Title := Update.OldForm1Caption; end; constructor TVSThread.CreateVS(vdata: tp_vs_data; vstreams, vsubstreams: tpa_vs_streamflags; total_lba: Cardinal; vnum_cell: Integer; vcell_list: TPAVobCellID; ProgBarFile: TProgressBar; ListViewInfo, ListViewFiles: TListView); begin inherited Create(true); Update.ProgressBar := ProgBarFile; Update.ListView := ListViewInfo; Update.TotalLBA := total_lba; data := vdata; streams := vstreams; substreams := vsubstreams; num_cell := vnum_cell; cell_list := vcell_list; Synchronize(InitAll); FreeOnTerminate := True; end; destructor TVSThread.Destroy(); begin Synchronize(CloseAll); inherited destroy; end; procedure TVSThread.Execute(); begin while ((ec = vse_OK) and not Terminated) do begin Update.was_in_mainloop := True; ec := vs_strip_one_block(data^, streams^, substreams^, num_cell, tp_vs_vobcellid(cell_list)); if ((ec = vse_OK) or (ec = vse_DONE)) then begin // look what's happened if (data^._in.sti.stream_id <> 0) then begin if (not Update.StreamsFound[data^._in.sti.stream_id and $ff]) then begin Update.StreamsFound[data^._in.sti.stream_id and $ff] := True; Update.Events[Update.NumEvents].lba := data^._in.sti.lba; Update.Events[Update.NumEvents].vob_id := $ffffffff; Update.Events[Update.NumEvents].cell_id := (data^._in.sti.stream_id and $ff) shl 8; // not substream Inc(Update.NumEvents); end; if ((data^._in.sti.stream_id = $bd) and (data^._in.sti.substream_id <> $ffffffff) and (not Update.StreamsFound[256 + (data^._in.sti.substream_id and $ff)])) then begin Update.StreamsFound[256 + (data^._in.sti.substream_id and $ff)] := True; Update.Events[Update.NumEvents].lba := data^._in.sti.lba; Update.Events[Update.NumEvents].vob_id := $ffffffff; Update.Events[Update.NumEvents].cell_id := ((data^._in.sti.substream_id and $ff) shl 8) + 1; // substream Inc(Update.NumEvents); end; end; if ((data^._in.sti.vob_id <> Update.old_vob_id) or (data^._in.sti.cell_id <> Update.old_cell_id)) then begin Update.old_vob_id := data^._in.sti.vob_id; Update.old_cell_id := data^._in.sti.cell_id; Update.Events[Update.NumEvents].lba := data^._in.sti.lba; Update.Events[Update.NumEvents].vob_id := data^._in.sti.vob_id; Update.Events[Update.NumEvents].cell_id := data^._in.sti.cell_id; Inc(Update.NumEvents); end; {$IFDEF vs_DECRYPT} if ((data^.key[0] <> Update.old_key[0]) or (data^.key[1] <> Update.old_key[1]) or (data^.key[2] <> Update.old_key[2]) or (data^.key[3] <> Update.old_key[3]) or (data^.key[4] <> Update.old_key[4])) then begin Update.old_key[0] := data^.key[0]; Update.old_key[1] := data^.key[1]; Update.old_key[2] := data^.key[2]; Update.old_key[3] := data^.key[3]; Update.old_key[4] := data^.key[4]; Update.Events[Update.NumEvents].lba := data^._in.sti.lba; Update.Events[Update.NumEvents].vob_id := $fffffffe; Update.Events[Update.NumEvents].cell_id := data^.key[0]; Update.Events[Update.NumEvents].key := (data^.key[1] shl 24) + (data^.key[2] shl 16) + (data^.key[3] shl 8) + data^.key[4]; Inc(Update.NumEvents); end; {$ENDIF} end; if ((Update.UpdateCounter and UpdateCounterAnd) = 0) then Synchronize(UpdateAll); Inc(Update.UpdateCounter); end; // end main loop if (Update.NumEvents > 0) then Synchronize(UpdateAll); if (ec = vse_OK) then ec := vs_done(data^, streams^, substreams^); end; end.