UNIT Unit2_functions;
INTERFACE
USES Classes, SysUtils, StrUtils, Math, SQLiteTable3, Unit3_data, Unit4_Exporters;

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;



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 LoadDatInfos(filename:String):Boolean;
  VAR i:LongWord;
    dat_file:TFileStream;
  BEGIN
    Result:=True;
    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
    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;


PROCEDURE SaveDatFile(fileid:LongWord; data:Tdata);
  VAR dat_file:TFileStream;
  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;


FUNCTION LoadDatFilePart(fileid,offset,size:LongWord; target:Pointer):Boolean;
  VAR dat_file:TFileStream;
  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;


FUNCTION LoadRawFilePart(address,size:LongWord; target:Pointer):Boolean;
  VAR
    filestream:TFileStream;
  BEGIN
    Result:=True;
    filestream:=TFileStream.Create(AnsiReplaceStr(dat_filename,'.dat','.raw'),fmOpenRead);
    filestream.Seek(address,soFromBeginning);
    filestream.Read(target^,size);
    filestream.Free;
  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>=1024*1024*1024 THEN BEGIN
      Result:=FloatToStrF(size/1024/1024/1024,ffFixed,5,1)+' GB';
    END ELSE BEGIN
      IF size>=1024*1024 THEN BEGIN
        Result:=FloatToStrF(size/1024/1024,ffFixed,5,1)+' MB';
      END ELSE BEGIN
        IF size>=1024 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
    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;


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;


END.
