unit ExpImpu; { Export/Import unit }
{
  (C) 2000 - 2001 Jindrich Jindrich, Pavel Pisa, PiKRON Ltd.

  Originators of the CHROMuLAN project:

  Jindrich Jindrich - http://www.jindrich.com
                      http://orgchem.natur.cuni.cz/Chromulan/
                      software developer, project coordinator
  Pavel Pisa        - http://cmp.felk.cvut.cz/~pisa
                      embeded software developer
  PiKRON Ltd.       - http://www.pikron.com
                      project initiator, sponsor, instrument developer

  The CHROMuLAN project is distributed under the GNU General Public Licence.
  See file COPYING for details.

  Originators reserve the right to use and publish sources
  under different conditions too. If third party contributors
  do not accept this condition, they can delete this statement
  and only GNU license will apply.
}

{$I define.pas}
interface
uses
  SysUtils, Classes, UlanGlob, {v0.50}UtlType, {/v0.50} WinUtl,
  {v0.45}
  ULRecTyp,
  ULAType, ULADType,
  {/v0.45}
  UlanType, ULEType, ULEObju{v0.18}, ULObju, ULFObju, Fileu{v0.18
  MS2IEEE}, BinHex{/v0.18}{v0.47}, ExeLogu{/v0.47}
  {v0.50}, MikroType, ULAObju{/v0.50};

type

  EConvert = class(Exception);

{v0.45}
function FindExpImpDef(const AExtName: string; var E: TULEObj): boolean;
{/v0.45}

procedure ULConvertFrom(E: TULEObj; Src, Dest: TStream);
  { Convert data from Src stream to Dest stream (both opened) using
    info in E object. Src is in format specified in E, Dest is
    in new standard raw data format, i.e. array of TExpPoint. }

procedure ULConvertTo(E: TULEObj; Src, Dest: TStream);
  { Convert data from Src stream to Dest stream (both opened) using
    info in E object. Src is in standard raw data format (array of TExpPoint),
    Dest is in format specified by E. }

procedure FillDatExpImp(E: TULEObj);
  { Fill the E object with information needed for export/import of
    .DAT files }

procedure FillTxtExpImp(E: TULEObj);
procedure FillULDExpImp(E: TULEObj);
procedure FillBExpImp(E: TULEObj);
{v0.45}
procedure FillUlfExpImp(E: TULEObj);
{/v0.45}
{v0.50}
procedure FillHeaExpImp(E: TULEObj);
procedure FillGExpImp(E: TULEObj);
{/v0.50}

{v0.18}
procedure FillTomExpImp(E: TULEObj);

function ConvertDataFile(const InFileName: string; const InFileFormat: string;
  const OutFileName: string; const OutFileFormat: string): integer;

function TryConvertFromFile(const AFileName: string;
  {v0.50} ULA: TULAObj;{/v0.50} Dest: TStream): boolean;

  { try to convert from AFileName (check if extension registered),
    returns false if unknown format. Raises exception upon some other
    kind of error }

function ExpImp: TULFObj;
{/v0.18}


implementation
{v0.50}
uses MikroToAAAu;
{/v0.50}

{v0.50}
procedure ConvertError(const msg: string);
begin
  ExeLog.LogErr('Convert.'+ msg);
  raise EConvert.Create(msg);
end;
{/v0.50}

{v0.18}
Type
  MKS = Array [0..3] of Byte;

Function MS2IEEE(Var MS) : Real;
{ Converts a 4 Byte Microsoft format single precision Real Variable as
  used in earlier versions of QuickBASIC and GW-BASIC to IEEE 6 Byte Real }
Var
  m    : MKS Absolute MS;
  r    : Real;
  ieee : Array [0..5] of Byte Absolute r;
begin
  FillChar(r, sizeof(r), 0);
  ieee[0] := m[3];
  ieee[3] := m[0];
  ieee[4] := m[1];
  ieee[5] := m[2];
  MS2ieee := r;
end;  { MStoIEEE }


Function IEEE2MS(ie : Real) : LongInt;
{ LongInt Type used only For convenience of Typecasting. Note that this will
  only be effective where the accuracy required can be obtained in the 23
  bits that are available With the MKS Type. }
Var
  ms    : MKS;
  ieee  : Array [0..5] of Byte Absolute ie;
begin
  ms[3] := ieee[0];
  ms[0] := ieee[3];
  ms[1] := ieee[4];
  ms[2] := ieee[5];
  IEEE2MS := LongInt(ms);
end; { IEEEtoMS }

function ValDelimToStr(s:string):string;
var
  ch: char;
{  code:integer;}
  i:integer;
  {binhex}
begin
  Result := '';
  i := 1;
  while i <= length(s) do begin
    ch := s[i];
    if s[i] = '$' then begin
      ch := chr(hextobyte(copy(s, i + 1, 2)));
      delete(s, i, 3);
    end else begin
      inc(i);
    end;
    Result := Result + ch;
  end;
end;
{/v0.18}

procedure ULConvertFrom(E: TULEObj; Src, Dest: TStream);
var
  B: Byte;
  W: Word;
  DW: LongWord;
  I8: ShortInt;
  I16: SmallInt;
  I32: Longint;
  S: Single;
  {v0.18}
  msf4: mks{bfloat4};
  xs, ys: string;
  i: integer;
  xint: single;
  nx: TXValue;
  valdelim: string;
  code1, code2: integer;
  {/v0.18}
  P: TExpPoint;
  ok: boolean;
  line: shortstring;
  X: TXValue;
  {v0.47}
  dotOrComma: char;
  lineNr: integer;
  msg: string;
  {/v0.47}

  {v0.50 support for text files without cr/lf separated values (no lines)}
  { are in text file (pair of) values each on separate line (linedelimiter separated)?}
(*  lineDelim: string;
  ss: TStringStream;
  ds: string;
  len, i, j: integer;
  numChars
type
  TReadState = (rsX, rsY);

var
  rs: TReadState;

  function HasX: boolean;   { are X values specified? }
  begin
    Result := xint = 0;
  end;

  function HasLines: boolean; { <> '' if hasLines and set to the linedelimitor(s) used in the file }
  begin
    Result := lineDelim <> '';
  end;*)
  {/v0.50}

begin
  if (E = nil) or (Src = nil) or (Dest = nil) then begin
    raise EInvalidParams.Create('ULConvertFrom nil params');
  end;
  {v0.50}
  if E.FileFormatKind = fkBinary then
  {/v0.50
  if not E.TextFile then}
  begin
    Src.Position := E.HeadLen;
    Dest.Size := 0;
    X := 0;
    repeat
      ok := true;
      case E.XValueType of
        xtNone: begin p.X := X; X := X + e.XInterval; end;
        xtByte: begin ok := src.Read(B, sizeof(B)) = sizeof(B); p.X := B; end;
        xtWord: begin ok := src.Read(W, sizeof(W)) = sizeof(W); p.X := W; end;
        xtDWord: begin ok := src.Read(DW, sizeof(DW)) = sizeof(DW); p.X := DW; end;
        xtInt8: begin ok := src.Read(I8, sizeof(I8)) = sizeof(I8); p.X := I8; end;
        xtInt16: begin ok := src.Read(I16, sizeof(I16)) = sizeof(I16); p.X := I16; end;
        xtInt32: begin ok := src.Read(I32, sizeof(I32)) = sizeof(I32); p.X := I32; end;
        xtSingle: begin ok := src.Read(S, sizeof(S)) = sizeof(S); p.X := S; end;
        xtMSFloat4: begin ok := src.Read(msf4, sizeof(msf4)) = sizeof(msf4);
          p.X := ms2ieee{Bfloat4toExtended}(msf4); end;
      else
        raise ERangeError.Create('ULConvertFrom E.XValueType');
      end;
      if ok then begin
        case E.YValueType of
          ytNone: raise EInvalidParams.Create('ULConvertFrom E.YValueType');
          ytByte: begin ok := src.Read(B, sizeof(B)) = sizeof(B); p.Y := B; end;
          ytWord: begin ok := src.Read(W, sizeof(W)) = sizeof(W); p.Y := W; end;
          ytDWord: begin ok := src.Read(DW, sizeof(DW)) = sizeof(DW); p.Y := DW; end;
          ytInt8: begin ok := src.Read(I8, sizeof(I8)) = sizeof(I8); p.Y := I8; end;
          ytInt16: begin ok := src.Read(I16, sizeof(I16)) = sizeof(I16); p.Y := I16 end;
          ytInt32: begin ok := src.Read(I32, sizeof(I32)) = sizeof(I32); p.Y := I32 end;
          ytSingle: begin ok := src.Read(S, sizeof(S)) = sizeof(S); p.Y := S end;
          ytMSFloat4: begin
            ok := src.Read(msf4, sizeof(msf4)) = sizeof(msf4);
            p.y := ms2ieee{Bfloat4toExtended}(msf4)
          end;
        else
          raise ERangeError.Create('ULConvertFrom E.YValueType');
        end;
        if ok then begin
          if E.XCoef <> 0 then begin
            p.X := p.X * E.XCoef;
          end;
          {v0.73}
          if E.XOffs <> 0 then
            p.X := p.X + E.XOffs;
          {/v0.73}
          if E.YCoef <> 0 then begin
            p.Y := p.Y * E.YCoef;
          end;
          Dest.WriteBuffer(p, sizeof(p));
        end;
      end;
      {v0.73}
      if ok and (E.TailLen > 0) then begin
        if Src.Position >= (Src.Size - E.TailLen) then
          ok := false;
      end;
      {/v0.73}
    until not ok;
  end else {v0.50} if E.FileFormatKind = fkText then {/v0.50} begin

    (* v0.50
    ss := TStringStream.Create('');
    try
      Src.Position := E.HeadLen;
      ss.CopyFrom(Src, Src.Size - Src.Position);
      ds := ss.DataString;
      len := length(ds);

      Dest.Size := 0;
      X := 0;
      xint := E.XInterval;

      valdelim := ValDelimToStr(E.ValueDelimiter);

      lineDelim := ValDelimToStr(E.LineDelimiter);
      if lineDelim = '' then begin
        if pos(#13#10, ds) > 0 then
          lineDelim := #13#10
        else if pos(#13,ds) > 0 then
          lineDelim := #13;
      end;

      if DecimalSeparator = '.' then
        dotOrComma := ','
      else
        dotOrComma := '.';

      lineNr := 0;
      i := 1;

      if HasX then
        rs := rsX
      else
        rs := rsY;


      while i <= len do begin
        xs := '';
        ys := '';
        while (i <= len) and (ds[i] = ' ') do inc(i);
        if i > len then
          break;
        if not (ds[i] in NumberChars) then
          raise EConvert.Create('Number expected at ' + IntToStr(i));

        j := i + 1;
        while (j <= len) and (ds[j] in NumberChars) do inc(j);

        if rs = rsX then begin
          xs := copy(ds, i, j - i)
          rs := rsY;
        end else begin
          ys := copy(ds, i, j - i);
          if hasX then
            rs := rsX;
        end;

        i := j;
        while (i <= len) and (ds[i] = ' ') do inc(i);
        if i > len then
          break;

        if valdelim <> '' then begin
          if copy(ds, p1, length(valdelim)) = valdelim then begin
            xs := copy(line, 1, i - 1);
            ys := copy(line, i + length(valdelim), 255);
              break;
          end;
        end else begin
          if line[i] in [' ', {v0.47}dotOrComma {/v0.47','},#9,';'] then begin
            xs := copy(line, 1, i - 1);{ulantype}
            ys := copy(line, i + 1, 255);
            break;
          end;
        end;
        inc(i);
      end;

        if ys = '' then begin
          ys := xs;
          xs := '';
          if ys = '' then
            ys := line;
        end;

        val(ys, p.y, code1);
        val(xs, nx, code2);
        if (code1 <> 0) or ((xs <> '') and (code2 <> 0)) then begin
          {v0.47}
          msg := 'Invalid TXT line format at row ' + IntToStr(lineNr) + ': ' + line;
          ExeLog.LogErr(msg);
          raise EConvert.Create(msg);
          {/v0.47
          raise EConvert.Create('Invalid TXT line format at row ' + line); }
        end;

        if (xs = '') then begin
          p.X := X;
          X := X + xint;
        end else begin
          p.X := nx;
        end;

        if E.XCoef <> 0 then begin
          p.X := p.X * E.XCoef;
        end;

        if E.YCoef <> 0 then begin
          p.Y := p.Y * E.YCoef;
        end;

        Dest.WriteBuffer(p, sizeof(p));
      end;
    finally
      ss.Free;
    end;

    /v0.50*)

    Src.Position := E.HeadLen;
    Dest.Size := 0;
    X := 0;
    xint := E.XInterval;
    if xint = 0 then
      xint := 0.001;
    valdelim := E.ValueDelimiter;
    {v0.47}
    if DecimalSeparator = '.' then
      dotOrComma := ','
    else
      dotOrComma := '.';
    lineNr := 0;
    {/v0.47}
    while StreamReadln(Src, line) do begin
      {v0.47}
      inc(lineNr);
      {/v0.47}
      xs := '';
      ys := '';
      line := trim(line);
      {v0.47}
      if line = '' then
        continue;
      {/v0.47}

      i := 1;
      while i <= length(line) do begin
        if valdelim <> '' then begin
          if copy(line, i, length(valdelim)) = valdelim then begin
            xs := copy(line, 1, i - 1);
            ys := copy(line, i + length(valdelim), 255);
            break;
          end;
        end else begin
          if line[i] in [' ', {v0.47}dotOrComma {/v0.47','},#9,';'] then begin
            xs := copy(line, 1, i - 1);{ulantype}
            ys := copy(line, i + 1, 255);
            break;
          end;
        end;
        inc(i);
      end;

      if ys = '' then begin
        ys := xs;
        xs := '';
        if ys = '' then
          ys := line;
      end;

      val(ys, p.y, code1);
      val(xs, nx, code2);
      if (code1 <> 0) or ((xs <> '') and (code2 <> 0)) then begin
        {v0.47}
        msg := 'Invalid TXT line format at row ' + IntToStr(lineNr) + ': ' + line;
        ExeLog.LogErr(msg);
        {v0.73 let the conversion continue...}
        continue;
        {/v0.73
        raise EConvert.Create(msg);}
        {/v0.47
        raise EConvert.Create('Invalid TXT line format at row ' + line); }
      end;

      if (xs = '') then begin
        p.X := X;
        X := X + xint;
      end else begin
        p.X := nx;
      end;

      if E.XCoef <> 0 then begin
        p.X := p.X * E.XCoef;
      end;

      if E.YCoef <> 0 then begin
        p.Y := p.Y * E.YCoef;
      end;

      Dest.WriteBuffer(p, sizeof(p));
    end;

  end {v0.50} else {if E.FileFormatKind = fkDOSMikro then} begin
    ConvertError('ULConvertFrom Invalid FileFormatKind');
  end{/v0.50} ;
end;

procedure ULConvertTo(E: TULEObj; Src, Dest: TStream);
var
  p: TExpPoint;
  i: integer;
  xs, ys: shortstring;
  {v0.18}
  msf4:mks{bfloat4};
  msfl:longint absolute msf4;
  valdelim: string;
  {/v0.18}
  B: Byte;
  W: Word;
  DW: LongWord;
  I8: ShortInt;
  I16: SmallInt;
  I32: Longint;

begin
  if (E = nil) or (Src = nil) or (Dest = nil) then
    raise EInvalidParams.Create('ULConverTo nil params');
  if {v0.50} E.FileFormatKind = fkText {/v0.50 E.TextFile} then begin
    Src.Position := 0;
    Dest.Size := 0;
    {v0.18}
    valdelim := ValDelimToStr(E.ValueDelimiter);
    if valdelim = '' then
      valdelim := ',';
    {/v0.18}

    repeat
      i := Src.Read(p, sizeof(p));
      if i <> sizeof(p) then
        break;
      if E.XCoef <> 0 then
        p.X := p.X / E.XCoef;
      if E.YCoef <> 0 then
        p.Y := p.Y / E.YCoef;
      str(p.x, xs);
      str(p.y, ys);
      StreamWriteln(Dest, xs + {v0.18}valdelim{/v0.18 ,} + ys);
    until false;
  end else {v0.50} if E.FileFormatKind = fkBinary then {/v0.50} begin
    Src.Position := 0;{E.HeadLen;}
    Dest.Size := 0;
    b := 0;
    i := 0;
    while i < E.HeadLen do begin
      Dest.WriteBuffer(b, sizeof(b));
      inc(i);
    end;
    repeat
      i := Src.Read(p, sizeof(p));
      if i <> sizeof(p) then
        break;
      if E.XCoef <> 0 then
        p.X := p.X / E.XCoef;
      if E.YCoef <> 0 then
        p.Y := p.Y / E.YCoef;
      case E.XValueType of
        xtNone:;
        xtByte: begin B := byte(round(p.X)); Dest.WriteBuffer(B, sizeof(B)); end;
        xtWord: begin W := word(round(p.X)); Dest.WriteBuffer(W, sizeof(W)); end;
        xtDWord: begin DW := LongWord(round(p.X)); Dest.WriteBuffer(DW, sizeof(DW)); end;
        xtInt8: begin I8 := shortint(round(p.X)); Dest.WriteBuffer(I8, sizeof(I8)); end;
        xtInt16: begin I16 := smallint(round(p.X)); Dest.WriteBuffer(I16, sizeof(I16)); end;
        xtInt32: begin I32 := longint(round(p.X)); Dest.WriteBuffer(I32, sizeof(I32)); end;
        xtSingle: begin Dest.WriteBuffer(p.X, sizeof(p.X)); end;
        xtMSFloat4: begin
          msfl{msf4}:= ieee2ms(p.x);{RealtoBFloat4(p.X, msf4); }
          Dest.WriteBuffer(msf4, sizeof(msf4));
        end;
      else
        raise ERangeError.Create('ULConvertTo E.XValueType');
      end;

      case E.YValueType of
        ytNone: raise EInvalidParams.Create('ULConvertTo E.YValueType');
        ytByte: begin B := byte(round(p.Y)); Dest.WriteBuffer(B, sizeof(B)); end;
        ytWord: begin W := word(round(p.Y)); Dest.WriteBuffer(W, sizeof(W)); end;
        ytDWord: begin DW := LongWord(round(p.Y)); Dest.WriteBuffer(DW, sizeof(DW)); end;
        ytInt8: begin I8 := shortint(round(p.Y)); Dest.WriteBuffer(I8, sizeof(I8)); end;
        ytInt16: begin I16 := smallint(round(p.Y)); Dest.WriteBuffer(I16, sizeof(I16)); end;
        ytInt32: begin I32 := longint(round(p.Y)); Dest.WriteBuffer(I32, sizeof(I32)); end;
        ytSingle: begin Dest.WriteBuffer(p.Y, sizeof(p.Y)); end;
        ytMSFloat4: begin
          msfl{msf4 }:= ieee2ms(p.x);{RealtoBFloat4(p.X, msf4);}
          Dest.WriteBuffer(msf4, sizeof(msf4));
        end;
      else
        raise ERangeError.Create('ULConvertTo E.YValueType');
      end;
    until false;
  end{v0.50} else begin
    ConvertError('ULConvertTo Invalid FileFormatKind');
  end{/v0.50};
end;

{v0.45}
procedure FillUlfExpImp(E: TULEObj);
begin
  {v0.50}
  e.FileFormatKind := fkBinary;
  {v0.50
  e.TextFile := false;}
  e.XValueType := xtSingle;
  e.YValueType := ytSingle;
  e.XCoef := 0;
  e.HeadLen := 0;
  e.DefName := UlfExt;
  e.YCoef := 0;
  e.ExtName := UlfExt;
end;
{/v0.45}

{v0.50}
procedure FillHeaExpImp(E: TULEObj);
begin
  e.FileFormatKind := fkDOSMikro;
  e.XValueType := xtSingle;
  e.YValueType := ytSingle;
  e.XCoef := 0;
  e.HeadLen := 0;
  e.DefName := HeaExt;
  e.YCoef := 0;
  e.ExtName := HeaExt;
end;
{/v0.50}


procedure FillDatExpImp(E: TULEObj);
  { Fill the E object with information needed for export/import of
   .DAT files }
begin
  {v0.50}
  e.FileFormatKind := fkBinary;
  {v0.50
  e.TextFile := false;}
  e.XValueType := xtInt32;
  e.YValueType := ytSingle;
  e.XCoef := 0.001;
  e.HeadLen := 0;
  e.DefName := DatExt;
  e.YCoef := 0;
  {v0.18}
  e.ExtName := DatExt;
  {/v0.18}
end;

procedure FillTxtExpImp(E: TULEObj);
begin
  {v0.50}
  e.FileFormatKind := fkText;
  {v0.50
  e.TextFile := true;}
  {
  e.XValueType := xtInt32;
  e.YValueType := ytSingle;
  e.XCoef := 0.001;
  e.HeadLen := 0;
  e.DefName := DatExt;
  e.YCoef := 0;
  }
  {v0.18}
  e.DefName := TxtExt;
  e.ExtName := TxtExt;
  {/v0.18}
end;

procedure FillULDExpImp(E: TULEObj);
begin
  {v0.50}
  e.FileFormatKind := fkBinary;
  {v0.50
  e.TextFile := false;}
  e.XValueType := xtInt32;
  e.YValueType := ytSingle;
  e.XCoef := 0.001;
  e.YCoef := 0;
  e.HeadLen := $2C4;
  e.DefName := ULDExt;
  {v0.18}
  e.ExtName := ULDExt;
  {/v0.18}
end;

procedure FillBExpImp(E: TULEObj);
begin
  {v0.50}
  e.FileFormatKind := fkBinary;
  {v0.50
  e.TextFile := false;}
  e.DefName := BExt;
  e.XValueType := xtNone;
  e.YValueType := ytInt16;
  e.XCoef := 0;
  e.YCoef := 0.0001;
  e.HeadLen := 0;
  e.XInterval := 1;
  {v0.18}
  e.ExtName := BExt;
  {/v0.18}
end;

{v0.18}
procedure FillTomExpImp(E: TULEObj);
begin
  {v0.50}
  e.FileFormatKind := fkBinary;
  {v0.50
  e.TextFile := false;}
  e.DefName := TomExt;
  e.XValueType := xtNone;
  e.YValueType := ytInt32;
  e.XCoef := 0;
  e.YCoef := 125E-9;
  e.HeadLen := 116;
  e.XInterval := 0.02;
  e.ExtName := TomExt;
end;

procedure CheckKnownDefs(ef: TULFObj);
var
  e: TULEObj;
begin
  if not ef.HasChildWithFieldUsrValue(fnExtName, ULDExt, TULObj(e)) then begin
    e := TULEObj(ef.Add(ULEID));
    FillULDExpImp(e);
  end;
  if not ef.HasChildWithFieldUsrValue(fnExtName, TxtExt, TULObj(e)) then begin
    e := TULEObj(ef.Add(ULEID));
    FillTxtExpImp(e);
  end;
  if not ef.HasChildWithFieldUsrValue(fnExtName, DatExt, TULObj(e)) then begin
    e := TULEObj(ef.Add(ULEID));
    FillDatExpImp(e);
  end;
  if not ef.HasChildWithFieldUsrValue(fnExtName, BExt, TULObj(e)) then begin
    e := TULEObj(ef.Add(ULEID));
    FillBExpImp(e);
  end;
  {v0.50}
  if not ef.HasChildWithFieldUsrValue(fnExtName, GExt, TULObj(e)) then begin
    e := TULEObj(ef.Add(ULEID));
    FillGExpImp(e);
  end;
  {/v0.50}

  if not ef.HasChildWithFieldUsrValue(fnExtName, TomExt, TULObj(e)) then begin
    e := TULEObj(ef.Add(ULEID));
    FillTomExpImp(e);
  end;
  {v0.45}
  if not ef.HasChildWithFieldUsrValue(fnExtName, UlfExt, TULObj(e)) then begin
    e := TULEObj(ef.Add(ULEID));
    FillUlfExpImp(e);
  end;
  {/v0.45}
  {v0.50}
  if not ef.HasChildWithFieldUsrValue(fnExtName, HeaExt, TULObj(e)) then begin
    e := TULEObj(ef.Add(ULEID));
    FillHeaExpImp(e);
  end;
  {/v0.50}

end;

{v0.50}
procedure FillGExpImp(E: TULEObj);
begin
  e.FileFormatKind := fkBinary;
  e.DefName := GExt;
  e.XValueType := xtNone;
  e.YValueType := ytInt16;
  e.XCoef := 0;
  e.YCoef := 0.0001;
  e.HeadLen := 0;
  e.XInterval := 1;
  e.ExtName := GExt;
end;
{/v0.50}

{v0.45}
function ULStreamOpenRead(const AFileName: string; e: TULEObj): TStream;
var
  f: TULFObj;
  o: TULObj;
begin
  if e.DefName = UlfExt then begin
    Result := TMemoryStream.Create;
    f := TULFObj.Create(nil);
    try
      f.LoadFromFile(AFileName);
      o := f.FindOrAdd(ULAID, '');
      o := o.FindOrAdd(ULADID, '');
      o.Data.Position := 0;
      Result.CopyFrom(o.Data, o.Data.Size);
    finally
      f.Free;
    end;
  end else begin {uletype}
    Result := TFileStream.Create(AFileName, fmOpenRead);
  end;
end;

function ConvertDataFile(const InFileName: string; const InFileFormat: string;
  const OutFileName: string; const OutFileFormat: string): integer;
var
  ef: TULFObj;
  f: array[1..2] of string;
  e: array[1..2] of TULEObj;
  i: integer;
  fldn: string;
  ifn, tfn, ofn: string;
  {v0.45}
  src: TStream;
  {/v0.45
  src,}
  tmp, dest: TFileStream;
begin
  Result := -1;
  ef := ExpImp;
  if InFileFormat = '' then
    f[1] := ExtractFileExt(InFileName)
  else
    f[1] := InFileFormat;

  if OutFileFormat = '' then
    f[2] := ExtractFileExt(OutFileName)
  else
    f[2] := OutFileFormat;
  if f[2] = '' then
    f[2] := DatExt;

  for i := 1 to 2 do begin

    if (f[i] <> '') and (f[i][1] = '.') then
      f[i] := copy(f[i], 2, 255);
    if length(f[i]) > 3 then begin
      fldn := fnDefName;
    end else begin
      fldn := fnExtName;
      f[i] := UpperCase(f[i]);
      if f[i] = '' then
        raise EConvert.Create('No data format specified.');
      f[i] := '.' + f[i];
    end;
    if not ef.HasChildWithFieldUsrValue(fldn, f[i], TULObj(e[i])) then begin
      raise EConvert.Create('Dat format definition ' + f[i] + ' not found.');
    end;
  end;

  ifn := InFileName;
  ofn := OutFileName;

  if ofn = '' then begin
    ofn := ChangeFileExt(ifn, e[2].ExtName);
  end else begin
    if ExtractFileExt(ofn) = '' then
      ofn := ChangeFileExt(ofn, e[2].ExtName);
  end;

  if FileExists(ofn) then begin
    raise EConvert.Create(ofn + ' aready exists.');
  end;
  src := nil;
  tmp := nil;
  dest := nil;
  tfn := GetTempFileName;
  try
    {v0.45}
    Src := ULStreamOpenRead(ifn, e[1]);
    {/v0.45
    Src := TFileStream.Create(ifn, fmOpenRead);}
    Tmp := TFileStream.Create(tfn, fmCreate);
    ULConvertFrom(e[1], Src, Tmp);
    { Convert data from Src stream to Dest stream (both opened) using
      info in E object. Src is in format specified in E, Dest is
      in new standard raw data format, i.e. array of TExpPoint. }
    Src.Free;
    Src := nil;
    Tmp.Free;
    Tmp := nil;
    if e[2].ExtName = DatExt then begin
      if not RenameFile(tfn, ofn) then begin
        raise EConvert.Create('Can not rename ' + tfn + ' to ' + ofn);
      end;
    end else begin
      Src := TFileStream.Create(tfn, fmOpenRead);
      Dest := TFileStream.Create(ofn, fmCreate);
      ULConvertTo(e[2], Src, Dest);
      { Convert data from Src stream to Dest stream (both opened) using
        info in E object. Src is in standard raw data format (array of TExpPoint),
        Dest is in format specified by E. }
      Src.Free;
      Src := nil;
      Dest.Free;
      Dest := nil;
    end;
    Result := 0;
  finally
    src.Free;
    tmp.Free;
    dest.Free;
    if FileExists(tfn) then
      DeleteFile(tfn);
  end;
end;

function TryConvertFromFile(const AFileName: string;
  {v0.50} ULA: TULAObj;{/v0.50} Dest: TStream): boolean;
var
  ef: TULFObj;
  e: TULEObj;
  {v0.45}
  s: TStream;
  {/v0.45
  s: TFileStream;}
begin
  Result := false;
  {ef := nil;}
  s := nil;
  try {uletype}
    ef := ExpImp;{ULEFInit(ef);}
    if not ef.HasChildWithFieldUsrValue(fnExtName, UpperCase(ExtractFileExt(AFileName)),
      TULObj(e)) then
    begin
      exit;
    end;

    {v0.50}
    if e.FileFormatKind = fkDOSMikro then begin
      MikroToULA(AFileName, ULA);
      Result := true;
      exit;
    end;
    {/v0.50}
    {if e.ExtName = TxtExt then begin
      if e.EditModal <> mrOK then
        raise EImportAborted.Create('Import aborted ' + AFileName);
    end;}

    {v0.45}
    s := ULStreamOpenRead(AFileName, e);
    {/v0.45
    s := TFileStream.Create(AFileName, fmOpenRead);}
    ULConvertFrom(e, s, Dest);
    s.Free;
    s := nil;
    Result := true;
  finally
    {ULEFDone(ef);}
    s.Free;
  end;
end;

procedure ULEFInit(var ef:TULFObj);
var
  efn, efna: string;
begin
  ef := TULFObj.Create(nil);
  efn := ExtractFilePath(paramstr(0)) + 'ULE.ULE';
  efna := ExtractFilePath(paramstr(0)) + 'ULE.ASC';
  if FileExists(efna) then
    ef.LoadFromFile(efna)
  else if FileExists(efn) then
    ef.LoadFromFile(efn);
  CheckKnownDefs(ef);
end;

procedure ULEFDone(var ef:TULFObj);
var
  efn, efna: string;
begin
  if ef <> nil then begin
    efn := ExtractFilePath(paramstr(0)) + 'ULE.ULE';
    efna := ExtractFilePath(paramstr(0)) + 'ULE.ASC';
    if ef.Modified then begin
      ef.SaveToFile(efn);
      ef.SaveToFile(efna);
    end;
    ef.Free;
    ef := nil;
  end;
end;

const
  FExpImp: TULFObj = nil;

function ExpImp: TULFObj;
begin
  if FExpImp = nil then
    ULEFInit(FExpImp);
  Result := FExpImp;
end;

function FindExpImpDef(const AExtName: string; var E: TULEObj): boolean;
begin
  Result := ExpImp.HasChildWithFieldUsrValue(fnExtName, AExtName, TULObj(E));
end;

initialization

finalization
  ULEFDone(FExpImp);
{/v0.18}
end.
