unit Unit15_Classes;
interface
uses Unit3_data, Unit9_data_structures, Classes, SysUtils, StrUtils, Dialogs, ABSDecUtil, ABSMain, DB;


type
  TOniData = class
    private
      FFileName:String;
      FLevelInfo:TLevelInfo;
      FBackend:Integer;
      Fos_mac:Boolean;
    protected
    public
      property FileName:String read FFileName write FFileName; 
      property Backend:Integer read FBackend write FBackend;
      property OSisMac:Boolean read Fos_mac write Fos_mac;
      property LevelInfo:TLevelinfo read FLevelInfo write FLevelInfo;

      constructor Create(filename:String; var Result:Boolean); virtual; abstract;
      procedure Close; virtual; abstract;

      function GetFileInfo(fileid:LongWord):TFileInfo; virtual; abstract;
      function GetFilesList(ext:String; pattern:String; NoEmptyFiles:Boolean):TStringArray; virtual; abstract;
      function GetFilesCount:LongWord; virtual; abstract;
      function GetExtensionsList:TStringArray; virtual; abstract;
      function GetExtendedExtensionsList:TExtensionsMap; virtual; abstract;
      function ExtractFileID(name:String):Integer;
      function GetFileIDByName(name:String):Integer;

      function LoadDatFile(fileid:LongWord):Tdata; virtual; abstract;
      procedure UpdateDatFile(fileid:LongWord; data:Tdata); virtual; abstract;
      procedure LoadDatFilePart(fileid,offset,size:LongWord; target:Pointer); virtual; abstract;
      procedure UpdateDatFilePart(fileid,offset,size:LongWord; target:Pointer); virtual; abstract;

      function GetRawList(fileid:LongWord):TRawList; virtual; abstract;
      function GetRawInfo(fileid,dat_offset:LongWord):TRawInfo;
      procedure LoadRawFile(fileid,dat_offset:LongWord; target:Pointer); virtual; abstract;
      procedure UpdateRawFile(fileid,dat_offset:LongWord; size:LongWord; target:Pointer); virtual; abstract;
      procedure LoadRawFilePart(fileid,dat_offset:LongWord; offset,size:LongWord; target:Pointer); virtual; abstract;
      procedure UpdateRawFilePart(fileid,dat_offset:LongWord; offset,size:LongWord; target:Pointer); virtual; abstract;
      function AppendRawFile(loc_sep:Boolean; size:LongWord; target:Pointer):LongWord; virtual; abstract;//Returns new Address
    published
  end;

  TOniDataDat = class(TOniData)
    private
      Fdat_file:TFileStream;
      Fraw_file:TFileStream;
      Fsep_file:TFileStream;
      Fdat_header:THeader;
      Fdat_filesmap:TFilesMap;
      Fdat_files:TFiles;
      Fdat_namedfilesmap:TNamedFilesMap;
      Fdat_extensionsmap:TExtensionsMap;
      FUnloadWhenUnused:Boolean;
      FDatOpened:Boolean;
      FRawOpened:Boolean;
      FSepOpened:Boolean;
    protected
    public
      property UnloadWhenUnused:Boolean read FUnloadWhenUnused write FUnloadWhenUnused;

      constructor Create(DatFilename:String; var Result:Boolean); override;
      procedure Close; override;

      function GetFileInfo(fileid:LongWord):TFileInfo; override;
      function GetFilesList(ext:String; pattern:String; NoEmptyFiles:Boolean):TStringArray; override;
      function GetFilesCount:LongWord; override;
      function GetExtensionsList:TStringArray; override;
      function GetExtendedExtensionsList:TExtensionsMap; override;

      function LoadDatFile(fileid:LongWord):Tdata; override;
      procedure UpdateDatFile(fileid:LongWord; data:Tdata); override;
      procedure LoadDatFilePart(fileid,offset,size:LongWord; target:Pointer); override;
      procedure UpdateDatFilePart(fileid,offset,size:LongWord; target:Pointer); override;

      procedure LoadRawOffset(loc_sep:Boolean; raw_addr,size:LongWord; target:Pointer);
      function GetRawList(fileid:LongWord):TRawList; override;
      procedure LoadRawFile(fileid,dat_offset:LongWord; target:Pointer); override;
      procedure UpdateRawFile(fileid,dat_offset:LongWord; size:LongWord; target:Pointer); override;
      procedure LoadRawFilePart(fileid,dat_offset:LongWord; offset,size:LongWord; target:Pointer); override;
      procedure UpdateRawFilePart(fileid,dat_offset:LongWord; offset,size:LongWord; target:Pointer); override;
      function AppendRawFile(loc_sep:Boolean; size:LongWord; target:Pointer):LongWord; override;//Returns new Address
    published
  end;

  TOniDataADB = class(TOniData)
    private
      FDatabase:TABSDatabase;
      FQuery:TABSQuery;
    protected
    public
      constructor Create(OLDBFilename:String; var Result:Boolean); override;
      procedure Close; override;

//      function GetDatLinks(srcid:LongWord):TDatLinks;
      function GetFileInfo(fileid:LongWord):TFileInfo; override;
      function GetFilesList(ext:String; pattern:String; NoEmptyFiles:Boolean):TStringArray; override;
      function GetFilesCount:LongWord; override;
      function GetExtensionsList:TStringArray; override;
      function GetExtendedExtensionsList:TExtensionsMap; override;
      function GetNamedFilesMap:TNamedFilesMap;

      function LoadDatFile(fileid:LongWord):Tdata; override;
      procedure UpdateDatFile(fileid:LongWord; data:Tdata); override;
      procedure LoadDatFilePart(fileid,offset,size:LongWord; target:Pointer); override;
      procedure UpdateDatFilePart(fileid,offset,size:LongWord; target:Pointer); override;

      function GetRawList(fileid:LongWord):TRawList; override;
      procedure LoadRawFile(fileid,dat_offset:LongWord; target:Pointer); override;
      procedure UpdateRawFile(fileid,dat_offset:LongWord; size:LongWord; target:Pointer); override;
      procedure LoadRawFilePart(fileid,dat_offset:LongWord; offset,size:LongWord; target:Pointer); override;
      procedure UpdateRawFilePart(fileid,dat_offset:LongWord; offset,size:LongWord; target:Pointer); override;
    published
  end;


const
  ODB_None=-1;
  ODB_Dat=0;
  ODB_ADB=1;

var
  OniDataConnection:TOniData;

function CreateDataConnection(filename:String; backend:Integer):Boolean;
procedure CloseDataConnection;




implementation
uses Unit2_Functions;



(*
  Implementation of  TOniData
*)

function TOniData.GetFileIDByName(name:String):Integer;
  var
    files:TStringArray;
    i: Integer;
  begin
    Result:=-1;
    files:=Self.GetFilesList('',name,false);
    if Length(files)>0 then
      for i:=0 to High(files) do
        if Pos(name,files[i])=Pos('-',files[i])+1 then begin
//        if MidStr(files[i],Pos('-',files[i])+1,Length(files[i])-Pos('-',files[i])-5)=name then begin
          Result:=Self.ExtractFileID(files[i]);
          Break;
        end;
  end;

function TOniData.ExtractFileID(name:String):Integer;
  begin
    if name[5]='-' then
      Result:=HexToLong(MidStr(name,1,4))
    else
      Result:=StrToInt(MidStr(name,1,5));
  end;

function TOniData.GetRawInfo(fileid,dat_offset:LongWord):TRawInfo;
  var
    i:LongWord;
    raw_list:TRawList;
  begin
    raw_list:=Self.GetRawList(fileid);
    Result.src_id:=0;
    Result.src_offset:=0;
    Result.raw_addr:=0;
    Result.raw_size:=0;
    for i:=0 to High(raw_list) do begin
      if raw_list[i].src_offset=dat_offset then begin
        Result.src_id:=fileid;
        Result.src_offset:=raw_list[i].src_offset;
        Result.raw_addr:=raw_list[i].raw_addr;
        Result.raw_size:=raw_list[i].raw_size;
        Result.loc_sep:=raw_list[i].loc_sep;
        Break;
      end;
    end;
  end;







(*
================================================================================
                      Implementation of  TOniDataDat
*)

constructor TOniDataDat.Create(DatFilename:String; var Result:Boolean);
  const
    header_ident1_pc:Array[0..$13] of Byte=
        ($1F,$27,$DC,$33,$DF,$BC,$03,$00,$31,$33,$52,$56,$40,$00,$14,$00,$10,$00,$08,$00);
    header_ident1_mac:Array[0..$13] of Byte=
        ($61,$30,$C1,$23,$DF,$BC,$03,$00,$31,$33,$52,$56,$40,$00,$14,$00,$10,$00,$08,$00);
    header_ident2:Array[0..$F] of Byte=
        ($99,$CF,$40,$00,$90,$4F,$63,$00,$F4,$55,$5F,$00,$90,$4F,$63,$00);
  var
    i:LongWord;
    header_pc,header_mac:Boolean;
  begin
    FUnloadWhenUnused:=True;
    FDatOpened:=False;
    FRawOpened:=False;
    if not FileExists(DatFilename) then begin
      ShowMessage('File doesn''t exist!!!');
      Result:=False;
      Exit;
    end;
    FFileName:=DatFilename;
    Fdat_file:=TFileStream.Create(FFileName, fmOpenRead);
    Fdat_file.Read(Fdat_header,SizeOf(Fdat_header));
    header_pc:=True;
    header_mac:=True;
    for i:=0 to High(Fdat_header.Ident) do begin
      FLevelInfo.Ident[i]:=Fdat_header.Ident[i];
      if Fdat_header.Ident[i]<>header_ident1_pc[i] then
        header_pc:=False;
      if Fdat_header.Ident[i]<>header_ident1_mac[i] then
        header_mac:=False;
    end;
    if not (header_pc xor header_mac) then begin
      Result:=False;
      Exit;
    end else begin
      if (header_pc and not header_mac) then
        Fos_mac:=False
      else
        Fos_mac:=True;
    end;
    SetLength(Fdat_filesmap,Fdat_header.Files);
    SetLength(Fdat_files,Fdat_header.Files);
    for i:=0 to Fdat_header.Files-1 do
      Fdat_file.Read(Fdat_filesmap[i],SizeOf(Fdat_filesmap[i]));
    for i:=0 to Fdat_header.Files-1 do begin
      Fdat_files[i].Extension:=Fdat_filesmap[i].Extension;
      Fdat_files[i].Extension:=ReverseString(Fdat_files[i].Extension);
      Fdat_files[i].Size:=Fdat_filesmap[i].FileSize;
      Fdat_files[i].FileType:=Fdat_filesmap[i].FileType;
      Fdat_files[i].DatAddr:=Fdat_filesmap[i].DataAddr-8+Fdat_header.DataAddr;
      if (Fdat_filesmap[i].FileType and $01)=0 then begin
        Fdat_file.Seek(Fdat_filesmap[i].NameAddr+Fdat_header.NamesAddr,soFromBeginning);
        SetLength(Fdat_files[i].Name,100);
        Fdat_file.Read(Fdat_files[i].Name[1],100);
        Fdat_files[i].Name:=MidStr(Fdat_files[i].Name,1+4,Pos(#0,Fdat_files[i].Name)-1-4);
      end else begin
        Fdat_files[i].Name:='';
      end;
      Fdat_files[i].FileName:=FormatNumber(i,5,'0')+'-'+Fdat_files[i].Name+'.'+Fdat_files[i].Extension;
      Fdat_files[i].FileNameHex:=IntToHex(i,4)+'-'+Fdat_files[i].Name+'.'+Fdat_files[i].Extension;
    end;
    Fdat_file.Seek($40+Fdat_header.Files*$14,soFromBeginning);
    SetLength(Fdat_namedfilesmap,Fdat_header.NamedFiles);
    for i:=0 to Fdat_header.NamedFiles-1 do
      Fdat_file.Read(Fdat_namedfilesmap[i],SizeOf(Fdat_namedfilesmap[i]));

    Fdat_file.Seek($40+Fdat_header.Files*$14+Fdat_header.NamedFiles*$8,soFromBeginning);
    SetLength(Fdat_extensionsmap,Fdat_header.Extensions);
    for i:=0 to Fdat_header.Extensions-1 do
      Fdat_file.Read(Fdat_extensionsmap[i],SizeOf(Fdat_extensionsmap[i]));

    Fdat_file.Seek(Fdat_files[0].DatAddr+7,soFromBeginning);
    Fdat_file.Read(FLevelInfo.LevelNumber,1);
    FLevelInfo.LevelNumber:=FLevelInfo.LevelNumber DIV 2;

    Fdat_file.Free;

    Result:=True;
    FBackend:=ODB_Dat;
  end;

procedure TOniDataDat.Close;
  begin
    if not FUnloadWhenUnused and FDatOpened then
      Fdat_file.Free;
    if not FUnloadWhenUnused and FRawOpened then
      Fraw_file.Free;
    if not FUnloadWhenUnused and FSepOpened then
      Fsep_file.Free;
    Self.Free;
  end;



function TOniDataDat.GetFileInfo(fileid:LongWord):TFileInfo;
  begin
    if fileid<Self.GetFilesCount then
      Result:=Fdat_files[fileid]
    else
      Result.ID:=-1;
  end;

function TOniDataDat.GetFilesList(ext:String; pattern:String; NoEmptyFiles:Boolean):TStringArray;
  var
    i:LongWord;
  begin
    SetLength(Result,0);
    for i:=0 to Fdat_header.Files-1 do begin
      if ( (Length(ext)=0) or (Pos(Fdat_files[i].Extension,ext)>0) ) and
          ( (Length(pattern)=0) or (Pos(UpperCase(pattern),UpperCase(Fdat_files[i].Name))>0) ) then begin
          if (NoEmptyFiles=false) or ((Fdat_files[i].FileType and $02)=0) then begin
            SetLength(Result,Length(Result)+1);
            if AppSettings.FilenumbersAsHex then
              Result[High(Result)]:=Fdat_files[i].FileNameHex
            else
              Result[High(Result)]:=Fdat_files[i].FileName;
          end;
      end;
    end;
  end;

function TOniDataDat.GetFilesCount:LongWord;
  begin
    Result:=Fdat_header.Files;
  end;

function TOniDataDat.GetExtensionsList:TStringArray;
  var
    i:LongWord;
  begin
    SetLength(Result,Fdat_header.Extensions);
    for i:=0 to Fdat_header.Extensions-1 do begin
      with Fdat_extensionsmap[i] do begin
        Result[i]:=Extension[3]+Extension[2]+Extension[1]+Extension[0]+' ('+IntToStr(ExtCount)+')';
      end;
    end;
  end;

function TOniDataDat.GetExtendedExtensionsList:TExtensionsMap;
  var
    i:LongWord;
  begin
    SetLength(Result,Fdat_header.Extensions);
    for i:=0 to Fdat_header.Extensions-1 do begin
      Result[i]:=Fdat_extensionsmap[i];
    end;
  end;


function TOniDataDat.LoadDatFile(fileid:LongWord):Tdata;
  begin
    if fileid<Self.GetFilesCount then begin
      if FUnloadWhenUnused or not FDatOpened then
        Fdat_file:=TFileStream.Create(FFileName, fmOpenReadWrite);
      Fdat_file.Seek(Fdat_files[fileid].DatAddr,soFromBeginning);
      SetLength(Result,Fdat_files[fileid].Size);
      Fdat_file.Read(Result[0],Fdat_files[fileid].Size);
      if UnloadWhenUnused then
        Fdat_file.Free
      else
        FDatOpened:=True;
    end;
  end;

procedure TOniDataDat.UpdateDatFile(fileid:LongWord; data:Tdata);
  begin
    if fileid<Self.GetFilesCount then begin
      if FUnloadWhenUnused or not FDatOpened then
        Fdat_file:=TFileStream.Create(FFileName, fmOpenReadWrite);
      Fdat_file.Seek(Fdat_files[fileid].DatAddr,soFromBeginning);
      Fdat_file.Write(data[0],Length(data));
      if UnloadWhenUnused then
        Fdat_file.Free
      else
        FDatOpened:=True;
    end;
  end;

procedure TOniDataDat.LoadDatFilePart(fileid,offset,size:LongWord; target:Pointer);
  begin
    if fileid<Self.GetFilesCount then begin
      if FUnloadWhenUnused or not FDatOpened then
        Fdat_file:=TFileStream.Create(FFileName, fmOpenReadWrite);
      Fdat_file.Seek(Fdat_files[fileid].DatAddr+offset,soFromBeginning);
      Fdat_file.Read(target^,size);
      if UnloadWhenUnused then
        Fdat_file.Free
      else
        FDatOpened:=True;
    end;
  end;

procedure TOniDataDat.UpdateDatFilePart(fileid,offset,size:LongWord; target:Pointer);
  begin
    if fileid<Self.GetFilesCount then begin
      if FUnloadWhenUnused or not FDatOpened then
        Fdat_file:=TFileStream.Create(FFileName, fmOpenReadWrite);
      Fdat_file.Seek(Fdat_files[fileid].DatAddr+offset,soFromBeginning);
      Fdat_file.Write(target^,size);
      if UnloadWhenUnused then
        Fdat_file.Free
      else
        FDatOpened:=True;
    end;
  end;



function TOniDataDat.GetRawList(fileid:LongWord):TRawList;
  var
    i:LongWord;
  begin
    SetLength(Result,0);
    for i:=0 to High(RawListHandlers) do
      if UpperCase(RawListHandlers[i].Ext)=UpperCase(Fdat_files[fileid].extension) then
        if RawListHandlers[i].needed then begin
          Result:=RawListHandlers[i].Handler(fileid);
          Break;
        end else
          Break;
  end;

procedure TOniDataDat.LoadRawOffset(loc_sep:Boolean; raw_addr,size:LongWord; target:Pointer);
  begin
    if not loc_sep then begin
      if FUnloadWhenUnused or not FRawOpened then
        Fraw_file:=TFileStream.Create(AnsiReplaceStr(FFileName,'.dat','.raw'),fmOpenReadWrite);
      if raw_addr<=Fraw_file.Size then begin
        Fraw_file.Seek(raw_addr,soFromBeginning);
        Fraw_file.Read(target^,size);
      end;
      if UnloadWhenUnused then
        Fraw_file.Free
      else
        FRawOpened:=True
    end else begin
      if FUnloadWhenUnused or not FSepOpened then
        Fsep_file:=TFileStream.Create(AnsiReplaceStr(FFileName,'.dat','.sep'),fmOpenReadWrite);
      if raw_addr<=Fsep_file.Size then begin
        Fsep_file.Seek(raw_addr,soFromBeginning);
        Fsep_file.Read(target^,size);
      end;
      if UnloadWhenUnused then
        Fsep_file.Free
      else
        FSepOpened:=True;
    end;
  end;

procedure TOniDataDat.LoadRawFile(fileid,dat_offset:LongWord; target:Pointer);
  var
    raw_info:TRawInfo;
  begin
    if fileid<Self.GetFilesCount then begin
      raw_info:=Self.GetRawInfo(fileid,dat_offset);
      if not raw_info.loc_sep then begin
        if FUnloadWhenUnused or not FRawOpened then
          Fraw_file:=TFileStream.Create(AnsiReplaceStr(FFileName,'.dat','.raw'),fmOpenReadWrite);
        Fraw_file.Seek(raw_info.raw_addr,soFromBeginning);
        Fraw_file.Read(target^,raw_info.raw_size);
        if UnloadWhenUnused then
          Fraw_file.Free
        else
          FRawOpened:=True
      end else begin
        if FUnloadWhenUnused or not FSepOpened then
          Fsep_file:=TFileStream.Create(AnsiReplaceStr(FFileName,'.dat','.sep'),fmOpenReadWrite);
        Fsep_file.Seek(raw_info.raw_addr,soFromBeginning);
        Fsep_file.Read(target^,raw_info.raw_size);
        if UnloadWhenUnused then
          Fsep_file.Free
        else
          FSepOpened:=True;
      end;
    end;
  end;

procedure TOniDataDat.UpdateRawFile(fileid,dat_offset:LongWord; size:LongWord; target:Pointer);
  var
    raw_info:TRawInfo;
  begin
    if fileid<Self.GetFilesCount then begin
      raw_info:=Self.GetRawInfo(fileid,dat_offset);
      if not raw_info.loc_sep then begin
        if FUnloadWhenUnused or not FRawOpened then
          Fraw_file:=TFileStream.Create(AnsiReplaceStr(FFileName,'.dat','.raw'),fmOpenReadWrite);
        Fraw_file.Seek(raw_info.raw_addr,soFromBeginning);
        Fraw_file.Write(target^,raw_info.raw_size);
        if UnloadWhenUnused then
          Fraw_file.Free
        else
          FRawOpened:=True
      end else begin
        if FUnloadWhenUnused or not FSepOpened then
          Fsep_file:=TFileStream.Create(AnsiReplaceStr(FFileName,'.dat','.sep'),fmOpenReadWrite);
        Fsep_file.Seek(raw_info.raw_addr,soFromBeginning);
        Fsep_file.Write(target^,raw_info.raw_size);
        if UnloadWhenUnused then
          Fsep_file.Free
        else
          FSepOpened:=True;
      end;
    end;
  end;

procedure TOniDataDat.LoadRawFilePart(fileid,dat_offset:LongWord; offset,size:LongWord; target:Pointer);
  var
    raw_info:TRawInfo;
    data:Tdata;
    mem:TMemoryStream;
  begin
    if fileid<Self.GetFilesCount then begin
      raw_info:=Self.GetRawInfo(fileid,dat_offset);
      SetLength(data, raw_info.raw_size);
      Self.LoadRawFile(fileid,dat_offset,@data[0]);
      mem:=TMemoryStream.Create;
      mem.Write(data[offset],size);
      mem.Read(target^,size);
      mem.Free;  
    end;
  end;

procedure TOniDataDat.UpdateRawFilePart(fileid,dat_offset:LongWord; offset,size:LongWord; target:Pointer);
  var
    raw_info:TRawInfo;
  begin
    if fileid<Self.GetFilesCount then begin
      raw_info:=Self.GetRawInfo(fileid,dat_offset);
      if not raw_info.loc_sep then begin
        if FUnloadWhenUnused or not FRawOpened then
          Fraw_file:=TFileStream.Create(AnsiReplaceStr(FFileName,'.dat','.raw'),fmOpenReadWrite);
        Fraw_file.Seek(raw_info.raw_addr+offset,soFromBeginning);
        Fraw_file.Write(target^,raw_info.raw_size);
        if UnloadWhenUnused then
          Fraw_file.Free
        else
          FRawOpened:=True
      end else begin
        if FUnloadWhenUnused or not FSepOpened then
          Fsep_file:=TFileStream.Create(AnsiReplaceStr(FFileName,'.dat','.sep'),fmOpenReadWrite);
        Fsep_file.Seek(raw_info.raw_addr+offset,soFromBeginning);
        Fsep_file.Write(target^,raw_info.raw_size);
        if UnloadWhenUnused then
          Fsep_file.Free
        else
          FSepOpened:=True;
      end;
    end;
  end;

function TOniDataDat.AppendRawFile(loc_sep:Boolean; size:LongWord; target:Pointer):LongWord; //Returns new Address
  begin
    if not loc_sep then begin
      if FUnloadWhenUnused or not FRawOpened then
        Fraw_file:=TFileStream.Create(AnsiReplaceStr(FFileName,'.dat','.raw'),fmOpenReadWrite);
      Result:=Fraw_file.Size;
      Fraw_file.Seek(0,soFromEnd);
      Fraw_file.Write(target^,size);
      if UnloadWhenUnused then
        Fraw_file.Free
      else
        FRawOpened:=True
    end else begin
      if FUnloadWhenUnused or not FSepOpened then
        Fsep_file:=TFileStream.Create(AnsiReplaceStr(FFileName,'.dat','.sep'),fmOpenReadWrite);
      Result:=Fsep_file.Size;
      Fsep_file.Seek(0,soFromEnd);
      Fsep_file.Write(target^,size);
      if UnloadWhenUnused then
        Fsep_file.Free
      else
        FSepOpened:=True;
    end;
  end;









  

(*
================================================================================
                     Implementation of  TOniDataADB
*)

constructor TOniDataADB.Create(OLDBFilename:String; var Result:Boolean);
  var
    i,j:Byte;
    temps:String;
  begin
    if not FileExists(OLDBFilename) then begin
      ShowMessage('File doesn''t exist!!!');
      Result:=False;
      Exit;
    end;
    FFileName:=OLDBFilename;
    FDatabase:=TABSDatabase.Create(nil);
    FDatabase.DatabaseName:='OLDBcon';
    FDatabase.DatabaseFileName:=OLDBFilename;
    FDatabase.Open;
    FQuery:=TABSQuery.Create(FDatabase);
    FQuery.DatabaseName:='OLDBcon';
    FQuery.SQL.Text:='SELECT [name],[value] FROM globals ORDER BY [name] ASC';
    FQuery.Open;
    FQuery.First;
    repeat
      if FQuery.FieldByName('name').AsString='dbversion' then begin
        if FQuery.FieldByName('value').AsString<>DBversion then begin
          ShowMessage('Database-file '+#13+#10+
                      '"'+OLDBFilename+'"'+#13+#10+
                      'has wrong version. (Required: '+DBversion+'; found: '+
                        FQuery.FieldByName('value').AsString+')');
          FQuery.Close;
          Result:=False;
          Exit;
        end;
      end;
      if FQuery.FieldByName('name').AsString='lvl' then begin
        FLevelInfo.LevelNumber:=StrToInt(FQuery.FieldByName('value').AsString);
      end;
      if FQuery.FieldByName('name').AsString='ident' then begin
        temps:=FQuery.FieldByName('value').AsString;
        for i:=0 to High(FLevelInfo.Ident) do begin
          j:=i*2+1;
          case temps[j] of
            '0'..'9': FLevelInfo.Ident[i]:=Ord(temps[j])-48;
            'A'..'F': FLevelInfo.Ident[i]:=Ord(temps[j])-55;
          end;
          FLevelInfo.Ident[i]:=FLevelInfo.Ident[i]*16;
          case temps[j+1] of
            '0'..'9': FLevelInfo.Ident[i]:=FLevelInfo.Ident[i]+Ord(temps[j+1])-48;
            'A'..'F': FLevelInfo.Ident[i]:=FLevelInfo.Ident[i]+Ord(temps[j+1])-55;
          end;
        end;
      end;
      if FQuery.FieldByName('name').AsString='ident' then begin
        temps:=FQuery.FieldByName('value').AsString;
        Fos_mac:=temps='MAC';
      end;
      FQuery.Next;
    until FQuery.EoF;
    FQuery.Close;

    Result:=True;
    FBackend:=ODB_ADB;
  end;

procedure TOniDataADB.Close;
  begin
    FDatabase.Close;
    FDatabase.Free;
    Self.Free;
  end;



function TOniDataADB.GetFileInfo(fileid:LongWord):TFileInfo;
  begin
    if fileid<Self.GetFilesCount then begin
      FQuery.SQL.Text:='SELECT * FROM datfiles WHERE id='+IntToStr(fileid)+' ORDER BY id ASC;';
      FQuery.Open;
      if FQuery.RecordCount=1 then begin
        FQuery.First;
        Result.ID:=FQuery.FieldByName('id').AsInteger;
        Result.Name:=FQuery.FieldByName('name').AsString;
        Result.Extension:=FQuery.FieldByName('extension').AsString;
        Result.FileName:=FormatNumber(Result.ID,5,'0')+'-'+Result.Name+'.'+Result.Extension;
        Result.Size:=FQuery.FieldByName('size').AsInteger;
        Result.FileType:=HexToLong(FQuery.FieldByName('contenttype').AsString);
        Result.DatAddr:=0;
        Result.opened:=False;
      end;
      FQuery.Close;
    end else begin
      Result.ID:=-1;
    end;
  end;

function TOniDataADB.GetFilesList(ext:String; pattern:String; NoEmptyFiles:Boolean):TStringArray;
  var
    i:LongWord;
    where:String;
    where_ext:String;
  begin
    where:='';
    if Length(ext)>0 then begin
      if Length(where)>0 then
        where:=where+' AND ';
      if Pos(',',ext)>0 then begin
        i:=1;
        where_ext:='';
        while i<Length(ext) do begin
          if Length(where_ext)>0 then
            where_ext:=where_ext+' OR ';
          where_ext:=where_ext+'(extension="'+MidStr(ext,i,4)+'")';
          i:=i+5;
        end;
        where:=where+'('+where_ext+')';
      end else begin
        where:=where+'(extension="'+ext+'")';
      end;
    end;
    if Length(pattern)>0 then begin
      if Length(where)>0 then
        where:=where+' AND ';
      where:=where+'(name LIKE "%'+pattern+'%")';
    end;
    if NoEmptyFiles then begin
      if Length(where)>0 then
        where:=where+' AND ';
      where:=where+'(contenttype<>2)';
    end;
    if Length(where)>0 then
      where:=' WHERE '+where;
    FQuery.SQL.Text:='SELECT id,name,extension FROM datfiles'+where+' ORDER BY id ASC;';
    FQuery.Open;
    if FQuery.RecordCount>0 then begin
      FQuery.First;
      SetLength(Result,FQuery.RecordCount);
      i:=0;
      repeat
        Result[i]:=FormatNumber(FQuery.FieldByName('id').AsInteger,5,'0')+'-'+FQuery.FieldByName('name').AsString+'.'+FQuery.FieldByName('extension').AsString;
        Inc(i);
        FQuery.Next;
      until FQuery.EOF;
    end;
    FQuery.Close;
  end;

function TOniDataADB.GetFilesCount:LongWord;
  begin
    FQuery.SQL.Text:='SELECT Count(*) AS cnumber FROM datfiles;';
    FQuery.Open;
    if FQuery.RecordCount>0 then begin
      FQuery.First;
      Result:=FQuery.FieldByName('cnumber').AsInteger;
    end else Result:=0;
    FQuery.Close;
  end;

function TOniDataADB.GetExtensionsList:TStringArray;
  var
    i:LongWord;
  begin
    SetLength(Result,0);
    FQuery.SQL.Text:='SELECT extension,count(extension) AS x FROM datfiles GROUP BY extension ORDER BY extension ASC;';
    FQuery.Open;
    if FQuery.RecordCount>0 then begin
      SetLength(Result,FQuery.RecordCount);
      i:=0;
      repeat
        Result[i]:=FQuery.FieldByName('extension').AsString+' ('+IntToStr(FQuery.FieldByName('x').AsInteger)+')';
        Inc(i);
        FQuery.Next;
      until FQuery.EOF;
    end;
    FQuery.Close;
  end;

function TOniDataADB.GetExtendedExtensionsList:TExtensionsMap;
  var
    i,j:LongWord;
    temps:String;
    data:Tdata;
  begin
    SetLength(Result,0);
    FQuery.SQL.Text:='SELECT ext,ident FROM extlist ORDER BY ext ASC;';
    FQuery.Open;
    if FQuery.RecordCount>0 then begin
      SetLength(Result,FQuery.RecordCount);
      i:=0;
      repeat
        temps:=FQuery.FieldByName('ext').AsString;
        for j:=0 to 3 do Result[i].Extension[j]:=temps[4-j];
        data:=DecodeHexString(FQuery.FieldByName('ident').AsString);
        for j:=0 to 7 do Result[i].Ident[j]:=data[j];
        Inc(i);
        FQuery.Next;
      until FQuery.EOF;
    end;
    FQuery.Close;
  end;

function TOniDataADB.GetNamedFilesMap:TNamedFilesMap;
  var
    i:LongWord;
    temp:Integer;
    temps:String;
    temparray:Array of Record
        id:Integer;
        fullname:String[50];
      end;
  begin
    SetLength(temparray,0);
    FQuery.SQL.Text:='SELECT id,(extension+name) AS xname FROM datfiles WHERE Length(name)>0 ORDER BY extension,name ASC;';
    FQuery.Open;
    if FQuery.RecordCount>0 then begin
      repeat
        temp:=FQuery.FieldByName('id').AsInteger;
        temps:=FQuery.FieldByName('xname').AsString;

        SetLength(temparray,Length(temparray)+1);
        if Length(temparray)>1 then begin
          for i:=High(temparray)-1 downto 0 do begin
            if StringSmaller(temps,temparray[i].fullname) then begin
              temparray[i+1]:=temparray[i];
              if i=0 then begin
                temparray[i].id:=temp;
                temparray[i].fullname:=temps;
              end;
            end else begin
              temparray[i+1].id:=temp;
              temparray[i+1].fullname:=temps;
              Break;
            end;
          end;
        end else begin
          temparray[0].id:=temp;
          temparray[0].fullname:=temps;
        end;
        FQuery.Next;
      until FQuery.Eof;
    end;
    FQuery.Close;
    SetLength(Result,Length(temparray));
    for i:=0 to High(temparray) do begin
      Result[i].FileNumber:=temparray[i].id;
      Result[i].blubb:=0;
    end;
  end;



function TOniDataADB.LoadDatFile(fileid:LongWord):Tdata;
  var
    mem:TStream;
  begin
    if fileid<Self.GetFilesCount then begin
      FQuery.SQL.Text:='SELECT data FROM datfiles WHERE id='+IntToStr(fileid)+';';
      FQuery.Open;
      if FQuery.RecordCount>0 then begin
        mem:=FQuery.CreateBlobStream(FQuery.FieldByName('data'),bmRead);
        SetLength(Result,mem.Size);
        mem.Seek(0,soFromBeginning);
        mem.Read(Result[0],mem.Size);
        mem.Free;
      end;
      FQuery.Close;
    end;
  end;

procedure TOniDataADB.UpdateDatFile(fileid:LongWord; data:Tdata);
  var
    MimeCoder: TStringFormat_MIME64;
    mem:TMemoryStream;
  begin
    if fileid<Self.GetFilesCount then begin
      mimecoder:=TStringFormat_MIME64.Create;
      mem:=TMemoryStream.Create;
      mem.Write(data[0],Length(data));
      mem.Seek(0,soFromBeginning);
      FQuery.SQL.Text:='UPDATE datfiles SET data=MimeToBin("'+MimeCoder.StrTo(mem.Memory, mem.Size)+'") WHERE id='+IntToStr(fileid)+';';
      FQuery.ExecSQL;
      mem.Free;
      mimecoder.Free;
    end;
  end;

procedure TOniDataADB.LoadDatFilePart(fileid,offset,size:LongWord; target:Pointer);
  var
    mem:TStream;
  begin
    if fileid<Self.GetFilesCount then begin
      FQuery.SQL.Text:='SELECT data FROM datfiles WHERE id='+IntToStr(fileid)+';';
      FQuery.Open;
      IF FQuery.RecordCount>0 THEN BEGIN
        mem:=FQuery.CreateBlobStream(FQuery.FieldByName('data'),bmRead);
        mem.Seek(offset,soFromBeginning);
        mem.Read(target^,size);
        mem.Free;
      END;
      FQuery.Close;
    END;
  END;

procedure TOniDataADB.UpdateDatFilePart(fileid,offset,size:LongWord; target:Pointer);
  var
    MimeCoder: TStringFormat_MIME64;
    mem:TMemoryStream;
    data:Tdata;
  begin
    if fileid<Self.GetFilesCount then begin
      data:=Self.LoadDatFile(fileid);
      mimecoder:=TStringFormat_MIME64.Create;
      mem:=TMemoryStream.Create;
      mem.Write(data[0],Length(data));
      mem.Seek(offset,soFromBeginning);
      mem.Write(target^,size);
      mem.Seek(0,soFromBeginning);
      FQuery.SQL.Text:='UPDATE datfiles SET data=MimeToBin("'+MimeCoder.StrTo(mem.Memory, mem.Size)+'") WHERE id='+IntToStr(fileid)+';';
      FQuery.ExecSQL;
      mem.Free;
      mimecoder.Free;
    end;
  end;



function TOniDataADB.GetRawList(fileid:LongWord):TRawList;
  var
    i:LongWord;
  begin
    SetLength(Result,0);
    FQuery.SQL.Text:='SELECT [src_link_offset],[size],[sep] FROM rawmap WHERE [src_id]='+IntToStr(fileid)+' ORDER BY src_link_offset ASC;';
    FQuery.Open;
    if FQuery.RecordCount>0 then begin
      FQuery.First;
      SetLength(Result,FQuery.RecordCount);
      i:=0;
      repeat
        Result[i].src_id:=fileid;
        Result[i].src_offset:=FQuery.FieldByName('src_link_offset').AsInteger;
        Result[i].raw_addr:=0;
        Result[i].raw_size:=FQuery.FieldByName('size').AsInteger;
        Result[i].loc_sep:=FQuery.FieldByName('sep').AsBoolean;
        Inc(i);
        FQuery.Next;
      until FQuery.EOF;
    end;
    FQuery.Close;
  end;
  
procedure TOniDataADB.LoadRawFile(fileid,dat_offset:LongWord; target:Pointer);
  var
    mem:TStream;
  begin
    if fileid<Self.GetFilesCount then begin
      FQuery.SQL.Text:='SELECT data FROM rawmap WHERE (src_id='+IntToStr(fileid)+') AND (src_link_offset='+IntToStr(dat_offset)+');';
      FQuery.Open;
      if FQuery.RecordCount>0 then begin
        mem:=FQuery.CreateBlobStream(FQuery.FieldByName('data'),bmRead);
        mem.Seek(0,soFromBeginning);
        mem.Read(target^,mem.size);
        mem.Free;
      end;
      FQuery.Close;
    end;
  end;

procedure TOniDataADB.UpdateRawFile(fileid,dat_offset:LongWord; size:LongWord; target:Pointer);
  var
    MimeCoder: TStringFormat_MIME64;
    mem:TMemoryStream;
  begin
    if fileid<Self.GetFilesCount then begin
      mimecoder:=TStringFormat_MIME64.Create;
      mem:=TMemoryStream.Create;
      mem.Write(target^,size);
      mem.Seek(0,soFromBeginning);
      FQuery.SQL.Text:='UPDATE rawmap SET data=MimeToBin("'+MimeCoder.StrTo(mem.Memory, mem.Size)+'") WHERE (src_id='+IntToStr(fileid)+') AND (src_link_offset='+IntToStr(dat_offset)+');';
      FQuery.ExecSQL;
      mem.Free;
      mimecoder.Free;
    end;
  end;

procedure TOniDataADB.LoadRawFilePart(fileid,dat_offset:LongWord; offset,size:LongWord; target:Pointer);
  var
    data:Tdata;
    mem:TMemoryStream;
  begin
    if fileid<Self.GetFilesCount then begin
      SetLength(data, Self.GetRawInfo(fileid,dat_offset).raw_size);
      Self.LoadRawFile(fileid,dat_offset,@data[0]);
      mem:=TMemoryStream.Create;
      mem.Write(data[offset],size);
      mem.Read(target^,size);
      mem.Free;
    end;
  end;

procedure TOniDataADB.UpdateRawFilePart(fileid,dat_offset:LongWord; offset,size:LongWord; target:Pointer);
  var
    MimeCoder: TStringFormat_MIME64;
    mem:TMemoryStream;
    data:Tdata;
  begin
    if fileid<Self.GetFilesCount then begin
      SetLength(data, Self.GetRawInfo(fileid,offset).raw_size);
      Self.LoadRawFile(fileid,offset,@data[0]);
      mimecoder:=TStringFormat_MIME64.Create;
      mem:=TMemoryStream.Create;
      mem.Write(data[0],Length(data));
      mem.Seek(offset,soFromBeginning);
      mem.Write(target^,size);
      mem.Seek(0,soFromBeginning);
      FQuery.SQL.Text:='UPDATE rawmap SET data=MimeToBin("'+MimeCoder.StrTo(mem.Memory, mem.Size)+'") WHERE (src_id='+IntToStr(fileid)+') AND (src_link_offset='+IntToStr(dat_offset)+');';
      FQuery.ExecSQL;
      mem.Free;
      mimecoder.Free;
    end;
  end;











function CreateDataConnection(filename:String; backend:Integer):Boolean;
  var
    answer:Boolean;
  begin
    if Assigned(OniDataConnection) then begin
      OniDataConnection.Close;
      OniDataConnection.Free;
      OniDataConnection:=Nil;
    end;
    case backend of
      ODB_Dat: OniDataConnection:=TOniDataDat.Create(filename, answer);
      ODB_ADB: OniDataConnection:=TOniDataADB.Create(filename, answer);
    else
      ShowMessage('Unknown Backend');
      Result:=False;
      Exit;
    end;

    if answer then begin
//      ShowMessage('file loaded');
//      ShowMessage('Files: '+IntToStr(OniDataConnection.GetFilesCount));
      Result:=True;
    end else begin
      ShowMessage('File not loaded');
      OniDataConnection.Close;
      OniDataConnection.Free;
      Result:=False;
    end;
  end;

procedure CloseDataConnection;
  begin
    if Assigned(OniDataConnection) then begin
      OniDataConnection.Close;
      OniDataConnection:=Nil;
    end;
  end;

end.
