{*************************** DRFL 'Pascal' Source File **************************
 *                                                                              *
 * File    :    MAIN.PAS                                                        *
 * Project :    DEB Viewer                                                      *
 *                                                                              *
 * Copyright : Phil Turner (c)2006                                                 *
 *                                                                              *
 * Initial version                                                              *
 * Application Main                                                             *
 *                                                                              *
 *************************** DRFL 'Pascal' Source File **************************}

unit Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, StrUtils, ComCtrls, Grids, ExtCtrls;

const
  APP_VERSION = '1.0'; // This helps you with version control
  APP_COPYRIGHT = 'Copyright (c) Phil Turner 2006';

type
  TDEB_Main_fm = class(TForm)
    Btn_Load_File: TBitBtn;
    Open_Dlg: TOpenDialog;
    File_List: TListBox;
    PageSelector: TTabControl;
    InfoWindow: TMemo;
    IndexView: TStringGrid;
    procedure FormCreate(Sender: TObject);
    procedure IndexViewMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure LoadDEB(Sender: TObject);
    procedure LoadIndex(iEUID : integer);
    procedure PageSelectorChange(Sender: TObject);
    procedure ProgramExit(Sender: TObject; var Action: TCloseAction);
    procedure UpdateTCB();
  private
    { Private declarations }
    tfDEBOpened : Boolean;
    TagFileName: String;
    IndexFileName: String;
    TagFileHandle : TextFile;
    IndexFileHandle : TextFile;
    TagTokenList : TStringList;
    IndexColumnTitles : TStringList;
    FColumn : Integer;
    FxColumn : Integer;
    FlsColumn : Integer;
    FpsColumn : Integer;
    procedure DisplayTagInfo();
  public
    DEBFileName: String;
    TargetFileName : String;
    TargetFileExt : String;
    TargetFileSizeL : Longint;
    TargetFileSizeP : Longint;
    TargetFileOffset : Longint;
    SelectedIndexRow : Longint;
    SelectedIndexCol : Longint;
    BagFileSize : Longint;
    LogicalSzFlag : Boolean;
    function HashFile(FileToHash : String) : String;
  end;

var
  DEB_Main_fm: TDEB_Main_fm;
  
implementation
uses FileView, Hashing;

{$R *.dfm}

//------------------------------------------------------------------------------
procedure TDEB_Main_fm.FormCreate(Sender: TObject);
begin
  tfDEBOpened := false; // DEB is not open yet
  LogicalSzFlag := false;
end;

//------------------------------------------------------------------------------
function TDEB_Main_fm.HashFile(FileToHash : String) : String;
var
  hFileToHash : Integer;
  md5 : MD5_Hash_obj;
  BufX : Array[0..16400] of Byte;
  BytesToRead, BytesRemaining, BytesRead : Integer;
  strMD5 : String;

begin
  Result := '';

  // Open file
  hFileToHash := FileOpen(FileToHash, fmOpenRead + fmShareDenyNone);

  // Create hashing object
  md5 := MD5_Hash_obj.Create;

  // Determine file size
  BytesRemaining := FileSeek(hFileToHash, 0, 2);
  FileSeek(hFileToHash, 0, 0);
  
  // Make a hash of it
  while BytesRemaining > 0 do
  begin

    // Set byte count to read
    BytesToRead := 16384;
    if BytesToRead > BytesRemaining then
      BytesToRead := BytesRemaining;

    // Get buffer full and munge it
    BytesRead := FileRead(hFileToHash, BufX[0], BytesToRead);
    md5.Update(@BufX, BytesRead);
    BytesRemaining := BytesRemaining - BytesToRead;
  end;

  // Get the hash
  strMD5 := md5.Final;

  // Clean up
  FileClose(hFileToHash);
  md5.Free;

  // Return hash
  Result := strMD5;
end;

//------------------------------------------------------------------------------
procedure TDEB_Main_fm.LoadDEB(Sender: TObject);
var ff : Integer;
begin
  // Open file selector dialog
  if not Open_Dlg.Execute then
    exit;

  // Scan for files and add to list
  for  ff := 0 to Open_Dlg.Files.Count - 1 do
    File_List.Items.Add(Open_Dlg.Files[ff]);

  // Open file
//????????  OI !: What happens if more than one file in folder?????????
  TagFileName := Open_Dlg.Files[ff - 1];
  DEBFileName := Copy(TagFileName, 1, Pos('.', TagFileName) - 1);
  AssignFile(TagFileHandle, TagFileName);
  tfDEBOpened := true;

  // Add Tag Continuity Block (TCB)
  Append(TagFileHandle);
  Writeln(TagFileHandle, '[TCB]');
  Writeln(TagFileHandle, 'Date & Time : ' + DateToStr(Date) + ' ' + TimeToStr(Time) );
  Writeln(TagFileHandle, 'Application ID : DEB Viewer');
  Writeln(TagFileHandle, 'Application Version : ' + APP_VERSION);
  Writeln(TagFileHandle, 'Application Function : Open and View contents of a Digital Evidence Bag (DEB)');
  Writeln(TagFileHandle, 'Application Signature : ' + HashFile('DEB_Viewer.exe'));
  Writeln(TagFileHandle, 'Tag File Hash : ' + HashFile(TagFileName));

  // Display file contents
  DEB_Main_fm.Caption := 'DEB Viewer  >  ' + TagFileName;
  DisplayTagInfo();
end;

//------------------------------------------------------------------------------
procedure TDEB_Main_fm.DisplayTagInfo();
var i : Integer;
  mbuf, strToken : String;
begin
  // Grab memory for string lists
  TagTokenList := TStringList.Create;
  IndexColumnTitles := TStringList.Create;

  // Initialise (this is only for wimps)
  Reset(TagFileHandle);
  FColumn := 0;
  FxColumn := 0;
  FlsColumn := 0;
  FpsColumn := 0;

  //Scan for index field record
  while not Eof(TagFileHandle) do
  begin
    Readln(TagFileHandle, mBuf);
    InfoWindow.Lines.Add(mbuf);
    i := 0;
    if LeftStr(mbuf, 15) = 'Index Format : ' then

    // Found tag field record - extract tag tokens
    begin
      IndexColumnTitles.Add('#');
      mbuf := Copy(mbuf, 16, Length(mbuf) - 15);
      while (Pos(' ', mbuf) > 0) do // tokens delimited by space character
      begin
        strToken := Copy(mbuf, 1, Pos(' ', mbuf) - 1);
        TagTokenList.Add(strToken);

        // Build column titles
        if(TagTokenList[i] = 'F') then
          begin
            IndexColumnTitles.Add('FileName');
            FColumn := i + 1;
          end
        else if (TagTokenList[i] = 'Fx') then
          begin
            IndexColumnTitles.Add('Ext');
            FxColumn := i + 1;
          end
        else if (TagTokenList[i] = 'Fa') then
          IndexColumnTitles.Add('Attributes')
        else if (TagTokenList[i] = 'Tmod') then
          IndexColumnTitles.Add('Modified')
        else if (TagTokenList[i] = 'Tacc') then
          IndexColumnTitles.Add('Accessed')
        else if (TagTokenList[i] = 'Tcre') then
          IndexColumnTitles.Add('Created')
        else if (TagTokenList[i] = 'Fls') then
          begin
            IndexColumnTitles.Add('Logical');
            FlsColumn := i + 1;
            LogicalSzFlag := true;
          end
        else if (TagTokenList[i] = 'Fps') then
          begin
            IndexColumnTitles.Add('Physical');
            FpsColumn := i + 1;
          end
        else if (TagTokenList[i] = 'P') then
          IndexColumnTitles.Add('Provenance')
        else if (TagTokenList[i] = 'Hmd5') then // fix - won't get this 'cos cr/lf at end?
          IndexColumnTitles.Add('MD5 Hash')
        else
          IndexColumnTitles.Add(TagTokenList[i]);
        Inc(i);

        // Snap extracted token off front of record
        mbuf := Copy(mbuf, Pos(' ', mbuf) + 1, Length(mbuf) - Length(strToken) + 1);
      end; // go get next token

      // grab remaining token which will not be delimited
      if(Length(mbuf) > 0) then
        IndexColumnTitles.Add(mbuf);
    end;
  end;

  CloseFile(TagFileHandle);
end;

//------------------------------------------------------------------------------
procedure TDEB_Main_fm.PageSelectorChange(Sender: TObject);
var i: Integer;
begin
  // Sanity check
  if not tfDEBOpened then
    exit;

  // Clear display
  InfoWindow.Clear;

  // Show info according to tab selected
  case PageSelector.TabIndex of
    0: // Summary
      begin
        InfoWindow.Visible := true; // swap to correct window
        IndexView.Visible := false;
        DisplayTagInfo();
      end; // Case 0
    1: // Index
      begin
        InfoWindow.Visible := false; // swap to correct window
        IndexView.Visible := true;
        IndexView.ColCount := IndexColumnTitles.Count;
        for i := 0 to IndexColumnTitles.Count - 1 do // show column titles
        begin
          IndexView.Cells[i, 0] := IndexColumnTitles[i];
        end;
        LoadIndex(1); // show index data
      end; // Case 1
  end;
end;

//------------------------------------------------------------------------------
procedure TDEB_Main_fm.LoadIndex(iEUID : integer);
var mbuf, strToken: String;
    r, c: Integer;
begin
  // Open index file
  IndexFileName := DEBFileName + '.I' + IntToHex(iEUID, 2);
  AssignFile(IndexFileHandle, IndexFileName);
  Reset(IndexFileHandle);
  r := 1;

  // Extract index fields
  while not Eof(IndexFileHandle) do
  begin
    Readln(IndexFileHandle, mBuf);
    IndexView.Cells[0, r] := IntToStr(r);
    c := 1;
    while(Pos(Chr(9), mbuf) > 0 ) do // entries delimited by TAB character
    begin
      strToken := Copy(mbuf, 1, Pos(Chr(9), mbuf) - 1);
      IndexView.Cells[c, r] := strToken;
      Inc(c);

      // Snap extracted token off front of record
      mbuf := Copy(mbuf, Pos(Chr(9), mbuf) + 1, Length(mbuf) - Length(strToken) + 1);
    end;

    // Snarf remaining (non-delimited) token
    if (Length(mbuf) > 0) then
      IndexView.Cells[c, r] := mbuf;
    Inc(r);
  end;
end;

//------------------------------------------------------------------------------
procedure TDEB_Main_fm.IndexViewMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var i : Integer;
begin
  SelectedIndexRow := 0;
  SelectedIndexCol := 0;

  // Convert screen co-ords to row/col
  IndexView.MouseToCell(X, Y, SelectedIndexCol, SelectedIndexRow);

  // Position file pointer
  TargetFileOffset := 0;
  for i := 1 to SelectedIndexRow - 1 do
    TargetFileOffset := TargetFileOffset + StrToInt(IndexView.Cells[FpsColumn, i]);

  // Grab stuff for file viewer
  // PROFESSIONAL coders would probably consider using a Record type here
  TargetFileName := IndexView.Cells[FColumn, SelectedIndexRow];
  TargetFileExt := IndexView.Cells[FxColumn, SelectedIndexRow];
  TargetFileSizeP := StrToInt(IndexView.Cells[FpsColumn, SelectedIndexRow]);
  if (LogicalSzFlag = true) then
    TargetFileSizeL := StrToInt(IndexView.Cells[FlsColumn, SelectedIndexRow])
  else
    TargetFileSizeL := TargetFileSizeP;

  // DEBUG: Display selected cell's row and col in that cell
  //IndexView.Cells[Column, Row] := 'Col ' + IntToStr(Column) + ',Row ' + IntToStr(Row);

  if (SelectedIndexRow > 0) then // don't kick off viewer if we're just resizing columns!
  begin
    FileView_fm := TFileView_fm.Create(Application);
    FileView_fm.ShowModal;
    FileView_fm.Free;
  end;
end;

//------------------------------------------------------------------------------
procedure TDEB_Main_fm.UpdateTCB();
//var
  //strHash : String;
begin
  //strHash := HashFile(filename);

end;

//------------------------------------------------------------------------------
procedure TDEB_Main_fm.ProgramExit(Sender: TObject; var Action: TCloseAction);
begin

  // Update Tag Continuity Block
  //UpdateTCB();

  // Leg it!
  TagTokenList.Free;
  IndexColumnTitles.Free;
  Application.Terminate;
end;
end.
