// =====================================================
//
//   miniLA_win - File Routines
//
//   (c) miniLA Team
//
// =====================================================
//
// This 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, or (at your option)
// any later version.
//
// This software 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 package; see the file COPYING.  If not, write to
// the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.

unit uFileUtils;
interface

uses StdCtrls, SysUtils, Classes, Dialogs, IniFiles, uLATypes;

type
   TFileUtils = class(TObject)
    protected
    public
       filename	      : string;

       constructor Create; overload;
       function    WriteDataToFile (FileName_l:string; binfile: boolean):integer;
       function    WriteINIFile(FileName_l:string):integer;
       function    WriteVCDFile(FileName_l:string):integer;
       function    ReadDataFromFile(FileName_l:string):integer;
       function    ReadBINFile(FileName_l:string):integer;
    end;


//******************************************************************************
//******************************************************************************
//******************************************************************************

implementation

uses dlgMain, dlgTrigger, uDataHold, uDisplay, uUtils;

type  TFileSig=array[0..7] of char;
const FileSig:TFileSig='LASTMF0'#0;
const FileSig2:TFileSig='MINILA0'#0;


{ TFileUtils }

//=================================================================
// Create dataholder
//=================================================================
constructor TFileUtils.Create;
begin
   inherited;
   filename := '';
end;



//=================================================================
function TFileUtils.WriteDataToFile(FileName_l:string; binfile: boolean):integer;
//=================================================================
var F:TFileStream;
begin
  Result:=0;
  try
    F:=TFileStream.Create(FileName_l,fmCreate);
    if not(binfile) then
     begin
       F.Write(FileSig2      		, SizeOf(FileSig2));
       F.Write(frmMain.LogPanel.Display.DataHold.DataLength, SizeOf(frmMain.LogPanel.Display.DataHold.Datalength));
       F.Write(frmMain.LogPanel.Display.DataHold.StateAnalysis , SizeOf(frmMain.LogPanel.Display.DataHold.StateAnalysis));
       F.Write(frmMain.LogPanel.Timebase, SizeOf(frmMain.LogPanel.Timebase));
       F.Write(frmMain.LogPanel.Cursors[2].Position, SizeOf(frmMain.LogPanel.Cursors[2].Position));
       F.Write(frmTrigger.TrigSize   	, SizeOf(frmTrigger.TrigSize));
       F.Write(frmTrigger.TrigDelay  	, SizeOf(frmTrigger.TrigDelay));
       F.Write(frmTrigger.TrigLen 	, SizeOf(frmTrigger.TrigLen));
       F.Write(frmTrigger.TrigMask   	, SizeOf(frmTrigger.TrigMask));
       F.Write(frmTrigger.TrigValue  	, SizeOf(frmTrigger.TrigValue));
       F.Write(frmTrigger.TrigEdge   	, SizeOf(frmTrigger.TrigEdge));
       F.Write(frmTrigger.ExtTrig_en    , SizeOf(frmTrigger.ExtTrig_en));
       F.Write(frmTrigger.ExtTrig_val   , SizeOf(frmTrigger.ExtTrig_val));
       F.Write(frmTrigger.InvIntTrig    , SizeOf(frmTrigger.InvIntTrig));
     end;
    F.Write(frmMain.LogPanel.Display.DataHold.DataArray[0], SizeOf(frmMain.LogPanel.Display.DataHold.DataArray[0])*frmMain.LogPanel.Display.DataHold.Datalength);
    F.Free;
  except
    Result:=-1;
    try
      F.Free;
    except
    end;
    exit;
  end;
  filename := FileName_l;
end;

//=================================================================
function TFileUtils.WriteINIFile(FileName_l:string):integer;
//=================================================================
var
   F	: TIniFile;
   i,j	: integer;
begin
   Result:=0;
   try
      F := TIniFile.Create(Filename_l);
      F.WriteInteger('BASE', 'FORMAT', 1);
      F.WriteInteger('BASE', 'TRIGGER', frmMain.LogPanel.Cursors[2].Position);
      F.WriteString('BASE', 'TIMESTEP', TmToStr(frmMain.LogPanel.Timebase,4)+'Hz');
      with frmMain.LogPanel.Display do
       begin
	 for i:=0 to DataSet.NumberOfLines-1 do
	    if DataSet.Data[i].Group=false then
	       F.WriteString('NAMES', IntToStr(DataSet.Data[i].Signals[0]), DataSet.Data[i].Name)
	    else
	       for j:=0 to DataSet.Data[i].NumberOfSignals-1 do
		  F.WriteString('NAMES', IntToStr(DataSet.Data[i].Signals[j]), DataSet.Data[i].Name+'.'+IntToStr(j))
       end;
      F.Free;
   except
     Result:=-1;
     try
       F.Free;
     except
     end;
     exit;
   end;
end;


//=================================================================
// Export data into VCD file
//
// note: miscellaneous data from uDisplay and uComm. units are used
//       be careful when doing changes
//=================================================================
function TFileUtils.WriteVCDFile(FileName_l:string):integer;
var
   vcdfile     		: TextFile;
   b_digit		: byte;
   timescale_units   	: byte;
   timescale_mult	: byte;
   time_mult   		: double;
   s			: longword;
   j			: integer;
   time			: double;
   bus			: byte;
   val			: longword;

const
   TIME_UNITS	: array [0..5] of string = ('s', 'ms', 'us', 'ns', 'ps', 'fs');
   TIME_MULTS	: array [0..2] of string = ('100', '10', '1');

begin
   try
      b_digit := 0;
      time_mult := 1/frmMain.LogPanel.TimeBase;
      while( ((time_mult - trunc(time_mult)) <> 0) and (b_digit<15) and (time_mult<1e4)) do
       begin
	 time_mult := time_mult * 10;
	 inc(b_digit);
       end;
      timescale_mult := (b_digit+2) mod 3;
      timescale_units := (b_digit+2) div 3;

      time := 0; 					// for stateanalysis

      // save header
      AssignFile(vcdfile,FileName_l);
      Rewrite(vcdfile);
      WriteLn(vcdfile, '$comment Created in miniLA_win $end');
      WriteLn(vcdfile, '$date ',DateTimeToStr(Now),' $end');
      WriteLn(vcdfile, '$timescale '+TIME_MULTS[timescale_mult]+' '+TIME_UNITS[timescale_units]+' $end');
      WriteLn(vcdfile, '$scope module minila $end');
      WriteLn(vcdfile, '$var real 1 * sample $end');

      // write definitions of signals/buses
      with frmMain.LogPanel.Display do
       begin
	 for bus:=0 to DataSet.NumberOfLines-1 do
	    if DataSet.Data[bus].Group=false then
	       WriteLn(vcdfile, '$var reg 1 ', chr(65+bus), ' ', DataSet.Data[bus].name, ' $end')
	    else
	     begin
	       WriteLn(vcdfile, '$var reg ', IntToStr(DataSet.Data[bus].NumberOfSignals), ' ',
				chr(65+bus), ' ',DataSet.Data[bus].Name , '[',
				IntToStr(DataSet.Data[bus].NumberOfSignals-1), ':0] $end');
	     end;

	 WriteLn(vcdfile, '$upscope $end');
	 WriteLn(vcdfile, '$enddefinitions $end');

	 // save sampled data
	 for s:=0 to frmMain.LogPanel.Display.DataHold.Datalength-1 do
	  begin
	    if not(frmMain.LogPanel.Display.DataHold.StateAnalysis) then
	     begin
	       WriteLn(vcdfile, format('#%.0f',[s*time_mult]));	// timestamp
	     end
	    else
	     begin
	       if s <> 0 then
		  time := time + time_mult*((frmMain.LogPanel.Display.DataHold.DataArray[s] shr 16) + 1);
	       WriteLn(vcdfile, format('#%.0f',[time]));	// timestamp
	     end;

	    WriteLn(vcdfile,'r', s-frmMain.LogPanel.Cursors[2].Position, ' *');	// sample number

	    for bus:= 0 to DataSet.NumberOfLines-1 do
	     begin
	       if Dataset.Data[bus].Group then	       		// channel value
		begin
		  val:=0;
		  for j:=0 to DataSet.Data[bus].NumberOfSignals-1 do
		    if (frmMain.LogPanel.Display.DataHold.DataArray[s] and (1 shl DataSet.Data[bus].Signals[j])) <> 0 then
		       val:=val or (1 shl j);
		end
	       else
		begin						// signal value
		  if (frmMain.LogPanel.Display.DataHold.DataArray[s] and Dataset.Data[bus].Mask) <> 0 then
		     val:= 1
		  else
		     val:= 0;
		end;

	       if (s=0) or ((frmMain.LogPanel.Display.DataHold.DataArray[s] and DataSet.Data[bus].Mask) <> (frmMain.LogPanel.Display.DataHold.DataArray[s-1] and DataSet.Data[bus].Mask)) then
		  if Dataset.Data[bus].Group = false then
		     WriteLn(vcdfile, IntToBin(val,1,false), chr(65+bus))
		  else
		     WriteLn(vcdfile,'b', IntToBin(val,DataSet.Data[bus].NumberOfSignals,true), ' ', chr(65+bus));
	     end;
	  end;
       end;
      CloseFile(vcdfile);
      Result:=0;
   except
     Result:=-1;
     try
       CloseFile(vcdfile);
     except
     end;
   end;
end;



//=================================================================
function TFileUtils.ReadDataFromFile(FileName_l:string):integer;
//=================================================================
var F   : TFileStream;
    Sig : TFileSig;
    Size: integer;

    // copy of variables, keep the same types!
    TimeBase	   : double;
    Timebase_sel   : byte;
    Freq	   : real;
    TriggerDelay   : byte;
    TriggerLength  : byte;
    TriggerMask    : cardinal;
    TriggerValue   : cardinal;
    TriggerEdge    : cardinal;
    TriggerSize    : byte;
    TriggerPos     : int64;
    InvIntTrig     : boolean;
    ExtTrig_en     : boolean;
    ExtTrig_val    : integer;
    Datalength     : longint;
    StateAnalysis  : boolean;

    
begin
  try
    F:=TFileStream.Create(FileName_l, fmOpenRead);
  except
    Result:=-1;
    exit;
  end;

  try

    // read signature
    F.Read(Sig,SizeOf(Sig));

    if Sig=FileSig then
     begin
       // read version 0 of the file
       MessageDlg('Importing file from previous version.'+#10+
	          'This version will be no longer supported.',
		   mtWarning, [mbOk], 0);

       Size:= SizeOf(FileSig)+
              SizeOf(DataLength) +
              SizeOf(Freq)+
              SizeOf(Timebase_sel)+
              SizeOf(TriggerSize)+
              SizeOf(TriggerDelay)+
              SizeOf(TriggerLength)+
              SizeOf(TriggerMask)+
              SizeOf(TriggerValue)+
              SizeOf(ExtTrig_en)+
              SizeOf(ExtTrig_val)+
              SizeOf(InvIntTrig);

       // check header length
       if F.Size<Size then
        begin
           Result:=-2;
           F.Free;
           exit;
        end;


       // read header
       F.Read(Datalength   , SizeOf(Datalength));
       F.Read(Freq         , SizeOf(Freq));
       F.Read(Timebase_sel , SizeOf(Timebase_sel));
       F.Read(TriggerSize  , SizeOf(TriggerSize));
       F.Read(TriggerDelay , SizeOf(TriggerDelay));
       F.Read(TriggerLength, SizeOf(TriggerLength));
       F.Read(TriggerMask  , SizeOf(TriggerMask));
       F.Read(TriggerValue , SizeOf(TriggerValue));
       F.Read(ExtTrig_en   , SizeOf(ExtTrig_en));
       F.Read(ExtTrig_val  , SizeOf(ExtTrig_val));
       F.Read(InvIntTrig   , SizeOf(InvIntTrig));
       
       TriggerEdge := 0;
       
       // fixed for FW1.6
       if TriggerSize < 15 then
 	 TriggerPos := TriggerStep * (TriggerSize+1)
       else
	 TriggerPos := 0;
        
       // note: freq. might be incorrect
       Timebase := frmMain.Analyzer.Timebase_freq[Timebase_sel];
       StateAnalysis := false;
     end

    else if Sig=FileSig2 then
     begin
       // read version 1 of the file

       Size:= SizeOf(FileSig2)+
              SizeOf(DataLength) +
              SizeOf(StateAnalysis) +
              SizeOf(Timebase)+
              SizeOf(TriggerPos)+
              SizeOf(TriggerSize)+
              SizeOf(TriggerDelay)+
              SizeOf(TriggerLength)+
              SizeOf(TriggerMask)+
              SizeOf(TriggerValue)+
              SizeOf(TriggerEdge)+
              SizeOf(ExtTrig_en)+
              SizeOf(ExtTrig_val)+
              SizeOf(InvIntTrig);

       // check header length
       if F.Size<Size then
        begin
           Result:=-2;
           F.Free;
           exit;
        end;


       // read header
       F.Read(Datalength    , SizeOf(Datalength));
       F.Read(StateAnalysis , SizeOf(StateAnalysis));
       F.Read(Timebase      , SizeOf(Timebase));
       F.Read(TriggerPos    , SizeOf(TriggerPos));
       F.Read(TriggerSize   , SizeOf(TriggerSize));
       F.Read(TriggerDelay  , SizeOf(TriggerDelay));
       F.Read(TriggerLength , SizeOf(TriggerLength));
       F.Read(TriggerMask   , SizeOf(TriggerMask));
       F.Read(TriggerValue  , SizeOf(TriggerValue));
       F.Read(TriggerEdge   , SizeOf(TriggerEdge));
       F.Read(ExtTrig_en    , SizeOf(ExtTrig_en));
       F.Read(ExtTrig_val   , SizeOf(ExtTrig_val));
       F.Read(InvIntTrig    , SizeOf(InvIntTrig));

     end
    else
     begin
       Result:=-3;
       F.Free;
       exit;
     end;

    if F.Size <> (Size+(SizeOf(frmMain.LogPanel.Display.DataHold.DataArray[0])*Datalength)) then
     begin
	Result:=-2;
	F.Free;
	exit;
     end;

    if DataLength>MaxDataLength then
       DataLength := MaxDataLength;

    F.Read(frmMain.LogPanel.Display.DataHold.DataArray[0], SizeOf(frmMain.LogPanel.Display.DataHold.DataArray[0])*Datalength);

  except
    Result:=-4;
    F.Free;
    exit;
  end;
  F.Free;
  filename:=FileName_l;

  // update trigger form
  frmTrigger.TrigLen   := TriggerLength;
  frmTrigger.TrigDelay := TriggerDelay;
  frmTrigger.TrigSize  := TriggerSize;
  frmTrigger.TrigMask  := TriggerMask;
  frmTrigger.TrigValue := TriggerValue;
  frmTrigger.TrigEdge  := TriggerEdge;

  
  // other
  frmMain.LogPanel.Timebase := Timebase;

  frmMain.LogPanel.Display.DataHold.InitData(Datalength, StateAnalysis);
  frmMain.LogPanel.SetCursorPosition(2,TriggerPos);
  frmMain.ScrollBar2.SetCursorPosition(2,TriggerPos);

  result := 0;
end;


//=================================================================
function TFileUtils.ReadBINFile(FileName_l:string):integer;
//=================================================================
var F   : TFileStream;
    DataLength: integer;

begin
  try
    F:=TFileStream.Create(FileName_l, fmOpenRead);
  except
    Result:=-1;
    exit;
  end;

  try
    Datalength := F.Size div SizeOf(frmMain.LogPanel.Display.DataHold.DataArray[0]);
    if DataLength>MaxDataLength then
     begin
       MessageDlg('File too large, only'+IntToStr(MaxDataLength)+' samples will be loaded.',
		   mtWarning, [mbOk], 0);
       DataLength := MaxDataLength;
     end;
    F.Read(frmMain.LogPanel.Display.DataHold.DataArray[0], SizeOf(frmMain.LogPanel.Display.DataHold.DataArray[0])*Datalength);
  except
    Result:=-4;
    F.Free;
    exit;
  end;
  F.Free;
  filename:=FileName_l;

  frmMain.LogPanel.Display.DataHold.InitData(Datalength, false);
  frmMain.LogPanel.SetCursorPosition(2,0);
  frmMain.ScrollBar2.SetCursorPosition(2,0);

  Result := 0;
end;


end.
