{*************************** DRFL 'Pascal' Source File **************************
 *                                                                              *
 * File    :    FILEVIEW.PAS                                                    *
 * Project :    DEB Viewer (Basic) + DRFWS 2006 Challenge (Alpha) File Carver   *
 *                                                                              *
 * Copyright : Phil Turner 2006                                                 *
 *                                                                              *
 * Initial version : Alpha                                                      *
 * File viewer                                                                  *
 * See procedure TFileView_fm.bitbtnAnalyseClick for the file carving stuff!!!                                                                 *
 *************************** DRFL 'Pascal' Source File **************************}

unit FileView;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Grids, ExtCtrls, Buttons, StrUtils;

const
  SCROLL_SIZE = 256;
  READ_BLOCK_SIZE = 4096;
  SECTOR_SIZE = 512;
  TEXT_THRESHOLD = 350;

type
  TFileView_fm = class(TForm)
    HexView: TStringGrid;
    btnNativeView: TButton;
    gridSectorView: TStringGrid;
    memoStatus: TMemo;
    bitbtnAnalyse: TBitBtn;
    memoIndex: TMemo;
    procedure ExecuteCmd(cmd: String);
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FlagSlack(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
      State: TGridDrawState);
    procedure ViewNative(Sender: TObject);
    procedure gridSectorViewSelectCell(Sender: TObject; ACol,
      ARow: Integer; var CanSelect: Boolean);
    procedure FormShow(Sender: TObject);
    procedure bitbtnNextClick(Sender: TObject);
    procedure bitbtnAnalyseClick(Sender: TObject);
    procedure Dice(filetype: String; sc: Integer; header: Boolean);
    procedure Carve(filename: String; StartSec, Length: Integer; NewFileFlag: Boolean);
    procedure ByteCarve(filename: String; StartSec, StartSecOffset, Length: Integer; NewFileFlag: Boolean);
  private
    { Private declarations }
    BagFileName: String;
    BagFileHandle : file of Byte;
    Bytes_Read : Integer;
    BytesPerRow : Integer;
    SectorsPerRow : Integer;
    BufX : Array[0..(READ_BLOCK_SIZE + 4)] of Byte;
    FileScrollOffset : Integer;
    File1_Open : String;
    File1_Start : Integer;
    File1_Stop : Integer;
    Text_File_Start : Integer;
    Text_File_Stop : Integer;
    procedure ShowData;

  public
    { Public declarations }
  end;

var
  FileView_fm: TFileView_fm;

implementation
uses Main;
{$R *.dfm}

//------------------------------------------------------------------------------
procedure TFileView_fm.FormCreate(Sender: TObject);
var w, rr : Integer;
begin

  // Set cell parameters
  HexView.ColWidths[0] := 60;
  HexView.Cells[0, 0] := 'OFFSET';

  BytesPerRow := 16;
  SectorsPerRow := 16;

  // Open selected file
  BagFileName := DEB_Main_fm.DEBFileName + '.B01';
  AssignFile(BagFileHandle, BagFileName);
  Reset(BagFileHandle);

  // Set title
  Caption := Caption + '  >  ' + DEB_Main_fm.TargetFileName;
  WindowState := wsNormal;

end;

//------------------------------------------------------------------------------
procedure TFileView_fm.ExecuteCmd(cmd: String);
var si : TSTARTUPINFO;
    FPiInfo : TProcessInformation;
    eow : LongWord;
begin
  // Set-up the StartupInfo
  ZeroMemory(@si, sizeof(si));
  si.cb := sizeof(si);
  si.wShowWindow := SW_NORMAL;

  // Now execute the command
  if CreateProcess(nil,                     // lpApplication
                   pChar(cmd),              // lpCommandLine
                   nil,                     // lpProcessAttributes
                   nil,                     // lpThreadAttributes
                   False,                   // bInheritHandles
                   IDLE_PRIORITY_CLASS
                     or CREATE_NEW_CONSOLE, // dwCreateFlags
                   nil,                     // lpEnvironment
                   nil,                     // lpCurrentDirectory
                   si,                      // lpStartupInfo
                   FPiInfo) then            // lpProcessInformation
  begin
    repeat
      // Check if the process is still running
      eow := WaitForSingleObject(FPiInfo.hProcess, 750);
    until eow <> WAIT_TIMEOUT;
    CloseHandle(FPiInfo.hThread);
    CloseHandle(FPiInfo.hProcess);
  end;
end;

//------------------------------------------------------------------------------
procedure TFileView_fm.FlagSlack(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  iFilePos : Integer;

begin
  begin
    with TStringGrid(Sender), Canvas do
    begin
      Brush.Color := clSkyBlue; // default to normal colour
      if ((ACol <> 0) and (ACol <> (BytesPerRow + 1)) and (ARow <> 0)) then
      begin
        iFilePos := StrToInt('$' + HexView.Cells[0,  ARow]) + ACol;
        if (iFilePos > DEB_Main_fm.TargetFileSizeL) then // use Slack colour
          Brush.Color := clBackground;
        if (iFilePos > DEB_Main_fm.TargetFileSizeP) then // use Out Of File Bounds colour
        begin
          Brush.Color := clRed;
          Pen.Color := clRed;
        end;
      end;
      FillRect(Rect);
      TextRect(Rect, Rect.Left + 2, Rect.Top + 2, Cells[ACol, ARow]);
    end;
  end;
end;


//------------------------------------------------------------------------------
procedure TFileView_fm.ShowData;
var i, cc, rr : Integer;
    h, t, txt : String;
begin
  // Compute & show row offsets
  for rr := 0 to HexView.RowCount - 1 do
    HexView.Cells[0,  rr + 1] := IntToHex(FileScrollOffset + (rr * BytesPerRow), 6);

  // Show data
  for rr := 1 to HexView.RowCount - 1 do
  begin
    txt := '';
    for cc := 1 to BytesPerRow do
    begin

      // Compute offset
      i := (rr - 1) * BytesPerRow + (cc - 1);

      // Compute text (if still in the file)
      h := '';
      t := ' ';
      if i < Bytes_Read then
      begin
        h := IntToHex(BufX[i], 2);
        if BufX[i] > 32 then // Alphanumeric ?
          t := chr(BufX[i]);
      end;
      txt := txt + t;

      // Show
      HexView.Cells[cc, rr] := h;
    end;
    HexView.Cells[BytesPerRow + 1, rr] := txt;
  end;
end;

//------------------------------------------------------------------------------
procedure TFileView_fm.ViewNative(Sender: TObject);
var
  hNative : File of Byte;
  BufferX : Array[0..16400] of Byte;
  BytesToRead, BytesRemaining, BytesRead, BytesWritten : Integer;

begin

  //Export file ready for viewer
  AssignFile(hNative, DEB_Main_fm.TargetFileName);
  Rewrite(hNative);

  // Extract target file data
  Seek(BagFileHandle, DEB_Main_fm.TargetFileOffset);
  BytesRemaining := DEB_Main_fm.TargetFileSizeP;
  while BytesRemaining > 0 do
  begin

    // Set byte count to read
    BytesToRead := 16384;
    if BytesToRead > BytesRemaining then
      BytesToRead := BytesRemaining;

    // Transfer from bag to export file
    BlockRead(BagFileHandle, BufferX[0], BytesToRead, BytesRead);
    BlockWrite(hNative, BufferX[0], BytesRead, BytesWritten);
    BytesRemaining := BytesRemaining - BytesToRead;
  end;
  CloseFile(hNative);

  // Display it
  //if (DEB_Main_fm.TargetFileExt = 'TXT') then
  //  ExecuteCmd('notepad.exe' + ' ' + DEB_Main_fm.TargetFileName)
    //ExecuteCmd('dir c: > c:\temp\dir.txt')
  if (DEB_Main_fm.TargetFileExt = 'BMP') then
    //ImageType.Picture.LoadFromFile(DEB_Main_fm.TargetFileName);
    ExecuteCmd('mspaint.exe' + ' ' + DEB_Main_fm.TargetFileName)
  else
    ExecuteCmd('notepad.exe' + ' ' + DEB_Main_fm.TargetFileName);

  //ExecuteCmd('c:\program files\quick view plus\qvp32.exe ' + DEB_Main_fm.TargetFileName + ' -m');
end;

//------------------------------------------------------------------------------
procedure TFileView_fm.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin

  CloseFile(BagFileHandle);
end;

//------------------------------------------------------------------------------
procedure TFileView_fm.gridSectorViewSelectCell(Sender: TObject; ACol,
  ARow: Integer; var CanSelect: Boolean);
var
  intSelectedSector :Integer;
  FileOffset : Integer;
begin
  memoStatus.Lines.Add('Row : ' + IntToStr(ARow) + ' Col : ' + IntToStr(ACol) );

  intSelectedSector := (ARow * gridSectorView.ColCount ) + ACol;

  memoStatus.Lines.Add('Sector : ' + IntToStr(intSelectedSector) );

    // Compute & display offsets
  FileOffset := intSelectedSector * SECTOR_SIZE;

  // Set file position
  Seek(BagFileHandle, DEB_Main_fm.TargetFileOffset + FileOffset);

  // Read data
  BlockRead(BagFileHandle, BufX, SECTOR_SIZE, Bytes_Read);

  // Show data
  ShowData;

end;

//------------------------------------------------------------------------------
procedure TFileView_fm.FormShow(Sender: TObject);
var w, cc : Integer;
begin

  // Tweak text display column
  HexView.ColWidths[BytesPerRow + 1] := 160;
  HexView.Cells[BytesPerRow + 1, 0] := 'TEXT';
  w := BytesPerRow;

  // Show column offsets
  for cc := 0 to BytesPerRow - 1 do
  begin
    HexView.Cells[cc + 1, 0] := IntToHex(cc, 1);
    HexView.ColWidths[cc + 1] := w;
  end;

  // Now go and show the data
  ShowData;

end;

//------------------------------------------------------------------------------
procedure TFileView_fm.bitbtnNextClick(Sender: TObject);
var rr : Integer;
begin
  // Compute & show row offsets
  for rr := 40 to 80 - 1 do
    gridSectorView.Cells[0,  rr + 1] := IntToHex((rr * SectorsPerRow), 6);
end;

//------------------------------------------------------------------------------
procedure TFileView_fm.bitbtnAnalyseClick(Sender: TObject);
var sc, i, ARow, ACol : Integer;
  hdr : String;
  MS_Off : String;
  MS_Off2 : String;
  JPEG : String;
  JPEG_Ftr : String;
  ZIP_lfh : String; // local file header
  ZIP_aeds : String; // archive extra data signature
  ZIP_cd : String; // central directory
  ZIP_ecd : String; // end of central directory
  ZIP_xxx : String; // unknown zip record
  Rect : TRect;
  FirstChr : Byte;
  FirstChrCount : Integer;
  AscChrCount : Integer;
  Found_Count : Integer;
  Offset : Integer;
  ZIP_CompFileLen : Integer; // Compressed ZIP File Length
  ZIP_FNLen : Integer; // ZIP Filename Length
  ZIP_EFLen : Integer; // ZIP Extra Field Length
  ZIP_FCLen : Integer; // ZIP File Comment Length
  ZIP_CDLen : Integer; // ZIP Central Directory Length
  ZIP_FN : String;
  ZipTableEntry : Integer;
  Entry_Found_Flag : Boolean;
  ZipCount : Integer;
  CurrentZipID : Integer;
  Zip_Flag : Boolean;  // Flag used to denote when Zip File Details are being carved
  Zip_Filelen : Integer;
  h_Zip_FN : Integer;
  type
  ZipFileEntry = record
    ZIP_ID : Integer;
    ZIP_FN : String;
    ZIP_CompFileLen : Integer;
    StartSector : Integer;
    StartSectorOffset : Integer;
    BytesAlreadyAlloc : Integer;
  end;
  var ZipTable : array[0..40] of ZipFileEntry ;


begin
  MS_Off := '';
  MS_Off2 := '';
  JPEG := '';
  JPEG_Ftr := '';
  ZIP_lfh := '';
  ZIP_aeds := '';
  ZIP_cd := '';
  ZIP_ecd := '';
  ZIP_xxx := '';

  MS_Off := MS_Off + Chr($d0)+ Chr($cf)+ Chr($11)+ Chr($e0)+ Chr($a1)+ Chr($b1);
  MS_Off2 := MS_Off2 + Chr($ec)+ Chr($a5);
  JPEG := JPEG + Chr($ff)+ Chr($d8)+ Chr($ff)+ Chr($e0); // + Chr($00)+ Chr($01);
  JPEG_Ftr := JPEG_Ftr + Chr($ff) + Chr($d9);
  ZIP_lfh := ZIP_lfh + Chr($50) + Chr($4b) + Chr($03) + Chr($04);  //PKx03x04
  ZIP_aeds := ZIP_aeds + Chr($50) + Chr($4b) + Chr($06) + Chr($08);  //PKx06x08
  ZIP_cd := ZIP_cd + Chr($50) + Chr($4b) + Chr($01) + Chr($02);  //PKx01x02
  ZIP_ecd := ZIP_ecd + Chr($50) + Chr($4b) + Chr($05) + Chr($06);  //PKx05x06
  ZIP_xxx := ZIP_xxx + Chr($50) + Chr($4b) + Chr($07) + Chr($08);  //PKx07x08

  Found_Count := 0;
  File1_Open := '';
  File1_Start := 0;
  File1_Stop := 0;
  Text_File_Start := -1;
  Text_File_Stop := -1;
  ZipTableEntry := 0;
  Entry_Found_Flag := FALSE;
  ZipCount := 0;
  Zip_Flag := FALSE;

  // scan file
  for sc := 0 to (DEB_Main_fm.TargetFileSizeL div SECTOR_SIZE) do
  begin
    // Set file position
    Seek(BagFileHandle, DEB_Main_fm.TargetFileOffset + (sc * SECTOR_SIZE));

    // Read data
    BlockRead(BagFileHandle, BufX, 1024, Bytes_Read);
    hdr := '';
    FirstChr := BufX[0];
    FirstChrCount := 0;
    AscChrCount := 0;

    for i := 0 to 511 do
    begin
        hdr := hdr + Chr(BufX[i]);
        if (FirstChr = BufX[i]) then
          FirstChrCount := FirstChrCount + 1;
        if ( (BufX[i] >= 20) and (BufX[i] <= 127) )then
          AscChrCount := AscChrCount + 1;
    end;

    ARow := sc div gridSectorView.ColCount;
    ACol := sc mod gridSectorView.ColCount;

//// OK Lets do stuff
//// first  characterise sector content text/binary/blank!!
    if(FirstChrCount = SECTOR_SIZE) then
    begin
      //memoStatus.Lines.Add('Blank sector : ' + IntToStr(sc) );
      Rect := gridSectorView.CellRect(ACol,ARow);
      gridSectorView.Canvas.Brush.Color := clBlack;
      gridSectorView.Canvas.FillRect(Rect);
      if (Text_File_Start <> -1) then
      begin
        Carve(IntToStr(Text_File_Start) + '.txt', Text_File_Start, sc - Text_File_Start - 1, TRUE);
        memoIndex.Lines.Add(IntToStr(Text_File_Start) + '.txt' + #09 + #09 + IntToStr((sc - Text_File_Start)*512) + #09 + #09 + DEB_Main_fm.HashFile(IntToStr(Text_File_Start) + '.txt') );
        Text_File_Start := -1;
      end;
    end
    else if(AscChrCount > TEXT_THRESHOLD) then
    begin
      //memoStatus.Lines.Add('Text sector : ' + IntToStr(sc) );
      Rect := gridSectorView.CellRect(ACol,ARow);
      gridSectorView.Canvas.Brush.Color := clYellow;
      gridSectorView.Canvas.FillRect(Rect);
      if (Text_File_Start = -1) then Text_File_Start := sc;
    end
    else
    begin
      //memoStatus.Lines.Add('Binary sector : ' + IntToStr(sc) );
      Rect := gridSectorView.CellRect(ACol,ARow);
      gridSectorView.Canvas.Brush.Color := clAqua;
      gridSectorView.Canvas.FillRect(Rect);
      if (Text_File_Start <> -1) then
      begin
        Carve(IntToStr(Text_File_Start) + '.txt', Text_File_Start, sc - Text_File_Start - 1, TRUE);
        memoIndex.Lines.Add(IntToStr(Text_File_Start) + '.txt' + #09 + #09 + IntToStr((sc - Text_File_Start)*512) + #09 + #09 + DEB_Main_fm.HashFile(IntToStr(Text_File_Start) + '.txt') );
        Text_File_Start := -1;
      end;
    end;

//// HTML Header
    Offset := Pos('<!DOCTYPE', hdr);
    while ((Offset > 0) and (Offset < SECTOR_SIZE)) do
    begin
        memoStatus.Lines.Add('HTML Hdr found at sector : ' + IntToStr(sc) + ' Offset : ' +  IntToStr(Offset));
        Rect := gridSectorView.CellRect(ACol,ARow);
        gridSectorView.Canvas.Brush.Color := clRed;
        gridSectorView.Canvas.FillRect(Rect);
        Found_Count := Found_Count + 1;
        if(Offset = 1) then
          Dice('.htm', sc, TRUE);

        if Zip_Flag = TRUE then // do some magical stuff to Zip Table because we have a fragged Zip file
        begin

          ZipTable[ZipTableEntry].BytesAlreadyAlloc := ZipTable[ZipTableEntry-1].ZIP_CompFileLen;
          ZipTable[ZipTableEntry-1].ZIP_CompFileLen := (sc - ZipTable[ZipTableEntry-1].StartSector) * SECTOR_SIZE;
          ZipTable[ZipTableEntry].ZIP_FN := ZipTable[ZipTableEntry-1].ZIP_FN;
          ZipTable[ZipTableEntry].StartSector := 0;  // this get update on a file ftr
          ZipTable[ZipTableEntry].StartSectorOffset := 1;
          ZipTable[ZipTableEntry].ZIP_ID := ZipCount;

          Inc(ZipTableEntry);
        end;
        Offset := PosEx('<!DOCTYPE', hdr, Offset + 9);
    end;

//// HTML Footer
    if (Pos('</html>', hdr) > 0) or (Pos('</HTML>', hdr) > 0) then
    begin
        memoStatus.Lines.Add('HTML Ftr found at sector : ' + IntToStr(sc) );
        Rect := gridSectorView.CellRect(ACol,ARow);
        gridSectorView.Canvas.Brush.Color := clRed;
        gridSectorView.Canvas.FillRect(Rect);

        Dice('.htm', sc, FALSE);

        if Zip_Flag = TRUE then // do some magical stuff to Zip Table because we have a fragged Zip file
        begin
            ZipTable[ZipTableEntry-1].StartSector := sc + 1;
        end;
    end;

//// MS Office Header - really  an OLE container header
    Offset :=  Pos(MS_Off, hdr);
    if  Offset > 0 then
    begin
        memoStatus.Lines.Add('MS Office Hdr found at sector : ' + IntToStr(sc)+ ' Offset : ' +  IntToStr(Offset) );
        Rect := gridSectorView.CellRect(ACol,ARow);
        gridSectorView.Canvas.Brush.Color := clRed;
        gridSectorView.Canvas.FillRect(Rect);
        Dice('.doc', sc, TRUE);
    end;

//// MS Office Header2 - only found in MS Word docs - need to do more work on this... if only I had time...
    Offset :=  Pos(MS_Off2, hdr);
    if  Offset = 1 then
    begin
        memoStatus.Lines.Add('MS Office 2 Hdr found at sector : ' + IntToStr(sc)+ ' Offset : ' +  IntToStr(Offset) );
        Rect := gridSectorView.CellRect(ACol,ARow);
        gridSectorView.Canvas.Brush.Color := clRed;
        gridSectorView.Canvas.FillRect(Rect);
    end;

//// JPeg Header
    Offset :=  Pos(JPEG, hdr);
    if  Offset > 0 then
    begin
        memoStatus.Lines.Add('JPEG Hdr found at sector : ' + IntToStr(sc) + ' Offset : ' +  IntToStr(Offset));
        Rect := gridSectorView.CellRect(ACol,ARow);
        gridSectorView.Canvas.Brush.Color := clRed;
        gridSectorView.Canvas.FillRect(Rect);
        if Offset = 1 then
          Dice('.jpg', sc, TRUE);
    end;

//// ZIP Local File Header processing stuff
    Offset := Pos(ZIP_lfh, hdr);
    while ((Offset > 0) and (Offset < SECTOR_SIZE)) do
    begin
        if Offset = 1 then
        begin
          Inc(ZipCount);
          Zip_Flag := TRUE;
        end;
        ZipTable[ZipTableEntry].ZIP_ID := ZipCount;
        ZipTable[ZipTableEntry].BytesAlreadyAlloc := 0;
        ZIP_FNLen := (BufX[(Offset-1) + 26]) + (BufX[(Offset-1) + 27] * $100);
        ZIP_EFLen := (BufX[(Offset-1) + 28]) + (BufX[(Offset-1) + 29] * $100);
        ZipTable[ZipTableEntry].ZIP_FN := '';
        ZipTable[ZipTableEntry].ZIP_CompFileLen := 0;
        for i := ((Offset-1) + 30) to ((Offset-1) + 30 + ZIP_FNLen)-1 do
        begin
          ZipTable[ZipTableEntry].ZIP_FN := ZipTable[ZipTableEntry].ZIP_FN + Chr(BufX[i]);
        end;
        ZipTable[ZipTableEntry].StartSector := sc;
        ZipTable[ZipTableEntry].StartSectorOffset := Offset;
        ZipTable[ZipTableEntry].ZIP_CompFileLen := ZipTable[ZipTableEntry].ZIP_CompFileLen + 30 + ZIP_FNLen + ZIP_EFLen;
        memoStatus.Lines.Add('ZIP Local File Hdr found at sector : ' + IntToStr(sc) + ' Offset : ' +  IntToStr(Offset) );
        Rect := gridSectorView.CellRect(ACol,ARow);
        gridSectorView.Canvas.Brush.Color := clRed;
        gridSectorView.Canvas.FillRect(Rect);
        Inc(ZipTableEntry);
        Offset := PosEx(ZIP_lfh, hdr, Offset + 30 + ZIP_FNLen + ZIP_EFLen);
    end;

//// ZIP Archive Extra Data Signature processing stuff
    if Pos(ZIP_aeds, hdr) > 0 then
    begin
        memoStatus.Lines.Add('ZIP Archive Extra Data Sig found at sector : ' + IntToStr(sc) );
        Rect := gridSectorView.CellRect(ACol,ARow);
        gridSectorView.Canvas.Brush.Color := clRed;
        gridSectorView.Canvas.FillRect(Rect);
    end;

//// ZIP Central Directory processing stuff
    Offset := Pos(ZIP_cd, hdr);
    while ((Offset > 0) and (Offset < SECTOR_SIZE)) do
    begin
        ZIP_CompFileLen := (BufX[(Offset-1) + 20]) + (BufX[(Offset-1) + 21] * $100) + (BufX[(Offset-1) + 22] * $10000)+ (BufX[(Offset-1) + 23] * $1000000);
        ZIP_FNLen := (BufX[(Offset-1) + 28]) + (BufX[(Offset-1) + 29] * $100);
        ZIP_EFLen := (BufX[(Offset-1) + 30]) + (BufX[(Offset-1) + 31] * $100);
        ZIP_FCLen := (BufX[(Offset-1) + 32]) + (BufX[(Offset-1) + 33] * $100);
        ZIP_FN := '';
        for i := ((Offset-1) + 46) to ((Offset-1) + 46 + ZIP_FNLen)-1 do
        begin
          ZIP_FN := ZIP_FN + Chr(BufX[i]);
        end;

        // find entry in ZipTable
        Entry_Found_Flag := FALSE;
        i := (ZipTableEntry-1);
        while(Entry_Found_Flag = FALSE) and (i >= 0) do
        begin
          if ZIP_FN = ZipTable[i].ZIP_FN then // found file in ZipTable
          begin
            Entry_Found_Flag := TRUE;
            if ZIP_CompFileLen = 0 then  // If  ZIP_CompFileLen = 0 then it is a directory entry
               ZipTable[i].ZIP_CompFileLen := ZipTable[i].ZIP_CompFileLen + ZIP_CompFileLen
            else
            begin
              if ZipTable[i].BytesAlreadyAlloc <> 0 then
               ZipTable[i].ZIP_CompFileLen := ZipTable[i].BytesAlreadyAlloc + (ZIP_CompFileLen + 16 - ZipTable[i-1].ZIP_CompFileLen)
              else
               ZipTable[i].ZIP_CompFileLen := ZipTable[i].ZIP_CompFileLen + ZIP_CompFileLen + 16;
            end;

          end;
          Dec(i);
        end;

        memoStatus.Lines.Add('ZIP Central Directory found at sector : ' + IntToStr(sc) + ' ' + ZIP_FN + ' ' +  IntToStr(ZIP_CompFileLen) + ' Offset: ' + IntToStr(Offset));

        Rect := gridSectorView.CellRect(ACol,ARow);
        gridSectorView.Canvas.Brush.Color := clRed;
        gridSectorView.Canvas.FillRect(Rect);

       Offset := PosEx(ZIP_cd, hdr, Offset + 46 + ZIP_FNLen + ZIP_EFLen + ZIP_FCLen);
    end;

//// ZIP End of Central Directory processing stuff
    Offset := Pos(ZIP_ecd, hdr);
    if  Offset > 0 then
    begin
        ZIP_CDLen := (BufX[(Offset-1) + 12]) + (BufX[(Offset-1) + 13] * $100) + (BufX[(Offset-1) + 14] * $10000)+ (BufX[(Offset-1) + 15] * $1000000);
        // Calculate Central Directory start sector and offset
        if ( ((Offset - ZIP_CDLen) >= 0) and ((Offset - ZIP_CDLen) < SECTOR_SIZE) ) then // start sector is within this sector
        begin
           ZipTable[ZipTableEntry].StartSector := sc;
           ZipTable[ZipTableEntry].StartSectorOffset := Offset - ZIP_CDLen;
        end
        else   // start sector is within a previous sector
        begin
          ZipTable[ZipTableEntry].StartSector := sc - ((ZIP_CDLen - Offset) div SECTOR_SIZE) - 1;
          ZipTable[ZipTableEntry].StartSectorOffset := SECTOR_SIZE - (ZIP_CDLen - Offset);
        end;

        ZipTable[ZipTableEntry].ZIP_FN := 'zipcd';
        ZipTable[ZipTableEntry].ZIP_CompFileLen := ZIP_CDLen; // Central Directory Length
        ZipTable[ZipTableEntry].ZIP_ID := ZipCount;

        Inc(ZipTableEntry);
        ZipTable[ZipTableEntry].ZIP_FN := 'zipecd';
        ZipTable[ZipTableEntry].ZIP_CompFileLen := 22; // End Central Directory record Length
        ZipTable[ZipTableEntry].StartSector := sc;
        ZipTable[ZipTableEntry].StartSectorOffset := Offset;
        ZipTable[ZipTableEntry].ZIP_ID := ZipCount;

        memoStatus.Lines.Add('ZIP Local End Central Directory found at sector : ' + IntToStr(sc) + ' Offset: ' +  IntToStr(Offset) + ' ' + IntToStr(ZIP_CDLen) );
        Rect := gridSectorView.CellRect(ACol,ARow);
        gridSectorView.Canvas.Brush.Color := clRed;
        gridSectorView.Canvas.FillRect(Rect);
        Inc(ZipTableEntry);
        Zip_Flag := FALSE;
    end;

  end; // end scan file loop

  // display Zip Table - This allows basic fragmented zips to be carved... well on a good day!
  memoStatus.Lines.Add('Zip Table');
  CurrentZipID := 0;
  for i := 0 to ZipTableEntry -1 do
  begin
     memoStatus.Lines.Add(IntToStr(i) + '    ' + ZipTable[i].ZIP_FN  + ' ' + IntToStr(ZipTable[i].StartSector) + ' '+ IntToStr(ZipTable[i].StartSectorOffset) + ' ' + IntToStr(ZipTable[i].ZIP_CompFileLen) + ' ' + IntToStr(ZipTable[i].BytesAlreadyAlloc) );
     if ZipTable[i].ZIP_ID <> CurrentZipID then
     begin
      ByteCarve(IntToStr(ZipTable[i].ZIP_ID)+ '.zip', ZipTable[i].StartSector, ZipTable[i].StartSectorOffset, ZipTable[i].ZIP_CompFileLen, TRUE);
      Inc(CurrentZipID);
     end
     else
     begin
      ByteCarve(IntToStr(ZipTable[i].ZIP_ID)+ '.zip', ZipTable[i].StartSector, ZipTable[i].StartSectorOffset, ZipTable[i].ZIP_CompFileLen, FALSE);
     end;
  end;

  // now calculate length and hash of zip files
  CurrentZipID := 0;
  for i := 0 to ZipTableEntry -1 do
  begin
     if ZipTable[i].ZIP_ID <> CurrentZipID then
     begin
        // Open file
        h_Zip_FN := FileOpen(IntToStr(ZipTable[i].ZIP_ID)+ '.zip', fmOpenRead + fmShareDenyNone);
        // Determine file size
        Zip_Filelen := FileSeek(h_Zip_FN, 0, 2);
        memoIndex.Lines.Add(IntToStr(ZipTable[i].ZIP_ID)+ '.zip' + #09 + IntToStr(ZipTable[i].StartSector) + #09 + IntToStr(Zip_Filelen) + #09 + DEB_Main_fm.HashFile(IntToStr(ZipTable[i].ZIP_ID)+ '.zip') );
        FileClose(h_Zip_FN);
        Inc(CurrentZipID);
     end;
  end;

end;

procedure TFileView_fm.Dice(filetype: String; sc: Integer; header: Boolean);
begin
        if((File1_Open = '') and (header = FALSE) ) then // no file open and footer encountered
        begin
            File1_Open := filetype;
            File1_Stop := sc;
            Carve(IntToStr(File1_Start) + File1_Open, File1_Start, File1_Stop - File1_Start, TRUE);
            memoIndex.Lines.Add(IntToStr(File1_Start) + File1_Open + #09 + #09 + IntToStr( (File1_Stop - File1_Start + 1) * 512) + #09 +  #09+ DEB_Main_fm.HashFile(IntToStr(File1_Start) + File1_Open) );
            File1_Start := sc + 1;
            File1_Open := '';
        end
        else if((File1_Open = '') and (header = TRUE) ) then // no file open and header encountered
        begin
            File1_Open := filetype;
            File1_Start := sc;
        end
        else if((File1_Open = filetype)and (header = TRUE) ) then // file already open, same file type
        begin
            File1_Stop := sc - 1;
            Carve(IntToStr(File1_Start) + File1_Open, File1_Start, File1_Stop - File1_Start, TRUE);
            memoIndex.Lines.Add(IntToStr(File1_Start) + File1_Open + #09 + #09 + IntToStr( (File1_Stop - File1_Start + 1) * 512) + #09 + #09 + DEB_Main_fm.HashFile(IntToStr(File1_Start) + File1_Open) );
            File1_Start := sc;
        end
        else if((File1_Open = filetype)and (header = FALSE) ) then // file already open, same file type
        begin
            File1_Stop := sc;
            Carve(IntToStr(File1_Start) + File1_Open, File1_Start, File1_Stop - File1_Start, TRUE);
            memoIndex.Lines.Add(IntToStr(File1_Start) + File1_Open + #09 + #09 + IntToStr( (File1_Stop - File1_Start + 1) * 512) + #09 + #09 + DEB_Main_fm.HashFile(IntToStr(File1_Start) + File1_Open) );
            File1_Start := sc + 1;
            File1_Open := '';
        end
        else
        begin   // file already open, different file type
            File1_Stop := sc - 1;
            Carve(IntToStr(File1_Start) + File1_Open, File1_Start, File1_Stop - File1_Start, TRUE);
            memoIndex.Lines.Add(IntToStr(File1_Start) + File1_Open + #09 + #09 + IntToStr( (File1_Stop - File1_Start + 1) * 512) + #09 + #09 + DEB_Main_fm.HashFile(IntToStr(File1_Start) + File1_Open) );
            File1_Start := sc;
            File1_Open := filetype;
        end;
end;

// This carves data based on sector size granularity
procedure TFileView_fm.Carve(filename: String; StartSec, Length: Integer; NewFileFlag: Boolean);
var
    Offset : Longint;
    OutputFileHandle : Integer;
    i, Bytes_Written : Integer;
begin
  if NewFileFlag = TRUE then
      OutputFileHandle := FileCreate(filename)
  else
  begin
      OutputFileHandle := FileOpen(filename, fmOpenWrite);
      FileSeek(OutputFileHandle,0,2);
  end;

  for i := 0 to Length do
  begin
    // Set file position
    Offset := DEB_Main_fm.TargetFileOffset + ((StartSec + i) * SECTOR_SIZE );
    Seek(BagFileHandle, Offset );

    // Read data
    BlockRead(BagFileHandle, BufX,  SECTOR_SIZE, Bytes_Read);

    FileWrite(OutputFileHandle, BufX, Bytes_Read);
  end;
  FileClose(OutputFileHandle);
end;

// This carves data based on byte size granularity
procedure TFileView_fm.ByteCarve(filename: String; StartSec, StartSecOffset, Length: Integer; NewFileFlag: Boolean);
var
    Offset : Longint;
    OutputFileHandle : Integer;
    i, Bytes_Written : Integer;
begin
  i := 0;
  if NewFileFlag = TRUE then
      OutputFileHandle := FileCreate(filename)
  else
  begin
      OutputFileHandle := FileOpen(filename, fmOpenWrite);
      FileSeek(OutputFileHandle,0,2);
  end;

  while(Length > SECTOR_SIZE) do
  begin
    // Set file position
    Offset := DEB_Main_fm.TargetFileOffset + ((StartSec + i) * SECTOR_SIZE ) + (StartSecOffset - 1);
    Seek(BagFileHandle, Offset );

    // Read data
    BlockRead(BagFileHandle, BufX,  SECTOR_SIZE, Bytes_Read);

    FileWrite(OutputFileHandle, BufX, Bytes_Read);
    Length := Length - SECTOR_SIZE;
    Inc(i);
  end;

  // Set file position
  Offset := DEB_Main_fm.TargetFileOffset + ((StartSec + i) * SECTOR_SIZE ) + (StartSecOffset - 1);
  Seek(BagFileHandle, Offset );
  // Read data
  BlockRead(BagFileHandle, BufX,  Length, Bytes_Read);
  FileWrite(OutputFileHandle, BufX, Bytes_Read);

  FileClose(OutputFileHandle);
end;

end.
