UNIT Unit2_functions;
INTERFACE
USES Classes, Dialogs, SysUtils, StrUtils, Math, SQLiteTable3,
      Unit3_data, Unit4_Exporters;

FUNCTION GetFilesList(ext:String; pattern:String):TStringList;
FUNCTION Decode_Int(buffer:Tdata):LongWord;
FUNCTION Encode_Int(input:LongWord):Tdata;
FUNCTION Decode_Float(buffer:Tdata):Single;
FUNCTION Encode_Float(input:Single):Tdata;
FUNCTION LoadDatInfos(filename:String):Boolean;
FUNCTION LoadDatFile(fileid:LongWord):Tdata;
PROCEDURE SaveDatFile(fileid:LongWord; data:Tdata);
FUNCTION LoadDatFilePart(fileid,offset,size:LongWord; target:Pointer):Boolean;
FUNCTION LoadRawFilePart(address,size:LongWord; target:Pointer):Boolean;
FUNCTION FormatNumber(value:LongWord; width:Byte; leadingzeros:Char):String;
FUNCTION FormatFileSize(size:LongWord):String;
FUNCTION CreateHexString(data:Tdata; HexOnly:Boolean):String;
FUNCTION ExportFile(fileid:LongWord; convert:Boolean):Integer;
FUNCTION GetWinFileName(fileid:LongWord; substring:String):String;
FUNCTION GetExtractPath:String;
PROCEDURE OpenDatabase(FileName:String);


IMPLEMENTATION

TYPE
  TValueSwitcher=Record
    CASE IsFloat: Boolean OF
      True: (ValueFloat:Single);
      False: (ValueInt:LongWord);
  END;


FUNCTION Decode_Int(buffer:Tdata):LongWord;
  BEGIN
    Result:=buffer[0]+buffer[1]*256+buffer[2]*256*256+buffer[3]*256*256*256;
  END;
FUNCTION Encode_Int(input:LongWord):Tdata;
  BEGIN
    Result[0]:=input MOD 256;
    input:=input DIV 256;
    Result[1]:=input MOD 256;
    input:=input DIV 256;
    Result[2]:=input MOD 256;
    input:=input DIV 256;
    Result[3]:=input MOD 256;
  END;
FUNCTION Decode_Float(buffer:Tdata):Single;
  VAR _valueswitcher:TValueSwitcher;
  BEGIN
    _valueswitcher.ValueInt:=Decode_Int(buffer);
    Result:=_valueswitcher.ValueFloat;
  END;
FUNCTION Encode_Float(input:Single):Tdata;
  VAR _valueswitcher:TValueSwitcher;
  BEGIN
    _valueswitcher.ValueFloat:=input;
    Result:=Encode_Int(_valueswitcher.ValueInt);
  END;


FUNCTION GetFilesList(ext:String; pattern:String):TStringList;
  VAR
    i:LongWord;
  BEGIN
    SetLength(Result,0);
    IF opened_state=opened_dat THEN BEGIN
      FOR i:=0 TO dat_header.Files-1 DO BEGIN
        IF (Length(ext)=0 OR dat_files[i].Extension=ext) AND
             (Length(pattern)=0 OR Pos(pattern,dat_files[i].FileName)>0) THEN BEGIN
          SetLength(Result,Length(Result)+1);
          Result[High(Result)]:=dat_files[i].FileName;
        END;
      END;
    END ELSE BEGIN
    END;
  END;

FUNCTION LoadDatInfos(filename:String):Boolean;
  VAR i:LongWord;
    dat_file:TFileStream;
  BEGIN
    Result:=True;
    opened_state:=opened_dat;
    dat_filename:=filename;
    raw_filename:=MidStr(filename,1,Length(filename)-3)+'raw';
    dat_file:=TFileStream.Create(filename, fmOpenRead);
    dat_file.Read(dat_header,SizeOf(dat_header));
    FOR i:=0 TO High(dat_header.Ident) DO
      IF dat_header.Ident[i]<>header_ident1[i] THEN BEGIN
        Result:=False;
        Exit;
      END;
{    FOR i:=0 TO High(dat_header.Ident2) DO
      IF dat_header.Ident2[i]<>header_ident2[i] THEN BEGIN
        Result:=False;
        Exit;
     END;
}
    SetLength(dat_filesmap,dat_header.Files);
    SetLength(dat_files,dat_header.Files);
    FOR i:=0 TO dat_header.Files-1 DO dat_file.Read(dat_filesmap[i],SizeOf(dat_filesmap[i]));
    FOR i:=0 TO dat_header.Files-1 DO BEGIN
      dat_files[i].Extension:=dat_filesmap[i].Extension;
      dat_files[i].Extension:=ReverseString(dat_files[i].Extension);
      dat_files[i].Size:=dat_filesmap[i].FileSize;
      dat_files[i].FileType:=dat_filesmap[i].FileType;
      dat_files[i].DatAddr:=dat_filesmap[i].DataAddr-8+dat_header.DataAddr;
      IF (dat_filesmap[i].FileType AND $01)=0 THEN BEGIN
        dat_file.Seek(dat_filesmap[i].NameAddr+dat_header.NamesAddr,soFromBeginning);
        SetLength(dat_files[i].Name,100);
        dat_file.Read(dat_files[i].Name[1],100);
        dat_files[i].Name:=MidStr(dat_files[i].Name,1+4,Pos(#0,dat_files[i].Name)-1-4);
      END ELSE BEGIN
        dat_files[i].Name:='';
      END;
      dat_files[i].FileName:=FormatNumber(i,5,'0')+'-'+dat_files[i].Name+'.'+dat_files[i].Extension;
    END;
    dat_file.Seek($40+dat_header.Files*$14,soFromBeginning);
    SetLength(dat_namedfilesmap,dat_header.NamedFiles);
    FOR i:=0 TO dat_header.NamedFiles-1 DO dat_file.Read(dat_namedfilesmap[i],SizeOf(dat_namedfilesmap[i]));

    dat_file.Seek($40+dat_header.Files*$14+dat_header.NamedFiles*$8,soFromBeginning);
    SetLength(dat_extensionsmap,dat_header.Extensions);
    FOR i:=0 TO dat_header.Extensions-1 DO dat_file.Read(dat_extensionsmap[i],SizeOf(dat_extensionsmap[i]));

    dat_file.Free;
  END;


FUNCTION LoadDatFile(fileid:LongWord):Tdata;
  VAR dat_file:TFileStream;
  BEGIN
    IF opened_state=opened_dat THEN BEGIN
      dat_file:=TFileStream.Create(dat_filename, fmOpenRead);
      dat_file.Seek(dat_files[fileid].DatAddr,soFromBeginning);
      SetLength(Result,dat_files[fileid].Size);
      dat_file.Read(Result[0],dat_files[fileid].Size);
      dat_file.Free;
    END;
  END;


PROCEDURE SaveDatFile(fileid:LongWord; data:Tdata);
  VAR dat_file:TFileStream;
  BEGIN
    IF opened_state=opened_dat THEN BEGIN
      dat_file:=TFileStream.Create(dat_filename, fmOpenReadWrite);
      dat_file.Seek(dat_files[fileid].DatAddr,soFromBeginning);
      dat_file.Write(data[0],Length(data));
      dat_file.Free;
    END;
  END;


FUNCTION LoadDatFilePart(fileid,offset,size:LongWord; target:Pointer):Boolean;
  VAR dat_file:TFileStream;
  BEGIN
    IF opened_state=opened_dat THEN BEGIN
      dat_file:=TFileStream.Create(dat_filename, fmOpenRead);
      Result:=True;
      dat_file.Seek(dat_files[fileid].DatAddr+offset,soFromBeginning);
      dat_file.Read(target^,size);
      dat_file.Free;
    END;
  END;


FUNCTION LoadRawFilePart(address,size:LongWord; target:Pointer):Boolean;
  VAR
    filestream:TFileStream;
  BEGIN
    IF opened_state=opened_dat THEN BEGIN
      Result:=True;
      filestream:=TFileStream.Create(AnsiReplaceStr(dat_filename,'.dat','.raw'),fmOpenRead);
      filestream.Seek(address,soFromBeginning);
      filestream.Read(target^,size);
      filestream.Free;
    END;
  END;


FUNCTION FormatNumber(value:LongWord; width:Byte; leadingzeros:Char):String;
  BEGIN
    Result:=AnsiReplaceStr(Format('%'+IntToStr(width)+'u',[value]),' ',leadingzeros);
  END;


FUNCTION FormatFileSize(size:LongWord):String;
  BEGIN
    IF size>=1000*1024*1024 THEN BEGIN
      Result:=FloatToStrF(size/1024/1024/1024,ffFixed,5,1)+' GB';
    END ELSE BEGIN
      IF size>=1000*1024 THEN BEGIN
        Result:=FloatToStrF(size/1024/1024,ffFixed,5,1)+' MB';
      END ELSE BEGIN
        IF size>=1000 THEN BEGIN
          Result:=FloatToStrF(size/1024,ffFixed,5,1)+' KB';
        END ELSE BEGIN
          Result:=IntToStr(size)+' B';
        END;
      END;
    END;
  END;


FUNCTION CreateHexString(data:Tdata; HexOnly:Boolean):String;
  VAR
    string_build,ascii_version:String;
    i:LongWord;
  BEGIN
    string_build:='';
    ascii_version:='';
    FOR i:=0 TO High(data) DO BEGIN
      IF NOT HexOnly THEN
        IF (i MOD 16)=0 THEN
          string_build:=string_build+'0x'+IntToHex(i,6)+'  ';
      string_build:=string_build+IntToHex(data[i],2);
      IF NOT HexOnly THEN BEGIN
        IF data[i]>=32 THEN ascii_version:=ascii_version+Chr(data[i])
        ELSE ascii_version:=ascii_version+'.';
        IF ((i+1) MOD 2)=0 THEN string_build:=string_build+#32;
        IF ((i+1) MOD 16)=0 THEN BEGIN
          string_build:=string_build+#32+ascii_version+CrLf;
          ascii_version:='';
        END;
      END;
    END;
    Result:=string_build;
  END;


FUNCTION ExportFile(fileid:LongWord; convert:Boolean):Integer;
  VAR
    i:Byte;
  BEGIN
    IF opened_state=opened_dat THEN BEGIN
      Result:=export_noerror;
      //ExportDefFileHeader(fileid);
      IF (dat_files[fileid].FileType AND $02)=0 THEN BEGIN
        //ExportDatFile(fileid);
        FOR i:=1 TO Length(ExportHandlers)+1 DO BEGIN
          IF i<=Length(ExportHandlers) THEN BEGIN
            IF ExportHandlers[i].Ext=dat_files[fileid].Extension THEN BEGIN
              IF ExportHandlers[i].needed THEN BEGIN
                CASE ExportHandlers[i].Handler(fileid,convert) OF
                  0: Result:=0;
                ELSE
                  Result:=export_handlererror;
                END;
              END;
              Break;
            END;
          END ELSE BEGIN
            Result:=export_nohandler;
          END;
        END;
      END;
    END;
  END;


FUNCTION GetWinFileName(fileid:LongWord; substring:String):String;
  VAR
    name:String;
  BEGIN
    name:=dat_files[fileid].Name;
    name:=AnsiReplaceStr(name,'\','__');
    name:=AnsiReplaceStr(name,'/','__');
    name:=AnsiReplaceStr(name,'>','__');
    name:=AnsiReplaceStr(name,'<','__');
    Result:=FormatNumber(fileid,5,'0')+'-'+name+'.'+substring+'.'+dat_files[fileid].Extension;
  END;

FUNCTION GetExtractPath:String;
  BEGIN
    Result:=ExtractFilePath(dat_filename)+'\extracted_'+ExtractFileName(dat_filename);
  END;


PROCEDURE OpenDatabase(FileName:String);
  VAR
    i:Byte;
    data:Tdata;
    temps:String;
    Tbl: TSQLiteTable;
  BEGIN
    IF NOT FileExists(FileName) THEN BEGIN
      ShowMessage('File doesn''t exist!!!');
      Exit;
    END;
    DB:=TSQLiteDatabase.Create(FileName);
    Tbl:=DB.GetTable('SELECT name,value FROM globals ORDER BY name ASC');
    REPEAT
      IF Tbl.FieldAsString('name')='dbversion' THEN BEGIN
        IF Tbl.FieldAsString('value')<>DBversion THEN BEGIN
          ShowMessage('Database-file '+CrLf+'"'+FileName+'"'+CrLf+'has wrong version. (Required: '+DBversion+'; found: '+Tbl.FieldAsString('value')+')');
          Exit;
        END;
      END;
      IF Tbl.FieldAsString('name')='lvl' THEN BEGIN
        database_level:=StrToInt(Tbl.FieldAsString('value'));
      END;
      IF Tbl.FieldAsString('name')='ident' THEN BEGIN
        temps:=Tbl.FieldAsString('value');
        FOR i:=0 TO High(database_ident) DO BEGIN
          CASE temps[(i*2)+1+0] OF
            '0'..'9': database_ident[i]:=Ord(temps[(i*2)+1+0])-48;
            'A'..'F': database_ident[i]:=Ord(temps[(i*2)+1+0])-55;
          END;
          database_ident[i]:=database_ident[i]*16;
          CASE temps[(i*2)+1+1] OF
            '0'..'9': database_ident[i]:=database_ident[i]+Ord(temps[(i*2)+1+0])-48;
            'A'..'F': database_ident[i]:=database_ident[i]+Ord(temps[(i*2)+1+0])-55;
          END;
        END;
      END;
      Tbl.Next;
    UNTIL Tbl.EOF;
    Tbl.Free;
    opened_state:=opened_db;
  END;




END.
