UNIT Unit9_data_structures;
INTERFACE
USES SysUtils, ABSMain, DB, ABSDecUtil, Classes, Unit3_data, Dialogs, StrUtils;

TYPE
  Tstructure_entry=RECORD
      name:String;
      offset:LongWord;
      datatype:Word;  // 1..4: Integer[1..4] dec; 5..8: Integer[1..4] hex; 9: float; 10: bitset; 11: raw-addr; 10000+: string[0+]
      description:String;
    END;
  Tstructure_info=RECORD
      extension:String;
      typedesc:String;
      entries:Array OF Tstructure_entry;
    END;
  Tstructures=Array OF Tstructure_info;
  THandler=FUNCTION(fileid:LongWord):TRawList;
  TRawListHandlers=RECORD
    Ext:String[4];
    needed:Boolean;
    Handler:THandler;
  END;

VAR
  structure_infos:Tstructures;
  RawListHandlers:Array OF TRawListHandlers;


FUNCTION GetDataType(typeid:Word):String;
FUNCTION GetStructureInfoId(ext:String):Integer;
FUNCTION GetTypeDataLength(datatype:Word):Word;
FUNCTION GetRawInfo(fileid,dat_offset:LongWord):TRawInfo;
FUNCTION GetRawList(fileid:LongWord):TRawList;

PROCEDURE LoadStructureDefinitions(defdir:String);


IMPLEMENTATION
USES Unit2_functions;

FUNCTION GetTypeDataLength(datatype:Word):Word;
  BEGIN
    CASE datatype OF
      1..4: Result:=datatype;
      5..8: Result:=datatype-4;
      9: Result:=4;
      10: Result:=1;
      11: Result:=4;
      10000..65535: Result:=datatype-10000;
    END;
  END;


FUNCTION GetStructureInfoId(ext:String):Integer;
  VAR
    i:Integer;
  BEGIN
    FOR i:=0 TO High(structure_infos) DO BEGIN
      IF structure_infos[i].extension=ext THEN BEGIN
        Result:=i;
        Exit;
      END;
    END;
    Result:=-1;
  END;


FUNCTION GetDataType(typeid:Word):String;
  BEGIN
    CASE typeid OF
      1..4: Result:='Int'+IntToStr(typeid*8);
      5..8: Result:='Int'+IntToStr((typeid-4)*8);
      9: Result:='Float';
      10: Result:='BitSet';
      11: Result:='Raw-Address';
      10000..65535: Result:='String('+IntToStr(typeid-10000)+')';
    END;
  END;


FUNCTION GetRawInfo(fileid,dat_offset:LongWord):TRawInfo;
  VAR
    i:LongWord;
    raw_list:TRawList;
  BEGIN
    raw_list:=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;

FUNCTION GetRawList(fileid:LongWord):TRawList;
  VAR
    i:LongWord;
    Query:TABSQuery;
  BEGIN
    IF opened_state=opened_dat THEN BEGIN
      FOR i:=0 TO High(RawListHandlers) DO
        IF UpperCase(RawListHandlers[i].Ext)=UpperCase(dat_files[fileid].extension) THEN
          IF RawListHandlers[i].needed THEN BEGIN
            Result:=RawListHandlers[i].Handler(fileid);
            Break;
          END ELSE
            Break;
    END ELSE BEGIN
      SetLength(Result,0);
      Query.SQL.Text:='SELECT [src_link_offset],[size] FROM rawmap WHERE [src_id]='+IntToStr(fileid)+' ORDER BY src_link_offset ASC;';
      Query.Open;
      IF Query.RecordCount>0 THEN BEGIN
        Query.First;
        SetLength(Result,Query.RecordCount);
        i:=0;
        REPEAT
          Result[i].src_id:=fileid;
          Result[i].src_offset:=Query.FieldByName('src_link_offset').AsInteger;
          Result[i].raw_addr:=0;
          Result[i].raw_size:=Query.FieldByName('size').AsInteger;
          Inc(i);
          Query.Next;
        UNTIL Query.EOF;
      END;
      Query.Close;
    END;
  END;


FUNCTION AGDB(fileid:LongWord):TRawList;
  VAR
    link:LongWord;
    links:LongWord;
    i:LongWord;
  BEGIN
    IF NOT dat_os_mac THEN BEGIN
      LoadDatFilePart(fileid,$1C,4,@links);
      links:=links*2;
      SetLength(Result,links);
      FOR i:=0 TO links-1 DO BEGIN
        Result[i].src_offset:=$20+i*4;
        LoadDatFilePart(fileid,$20+i*4,4,@link);
        Result[i].raw_addr:=link;
        Result[i].raw_size:=0{????????????????????????????????};
        Result[i].loc_sep:=False;
      END;
    END;
  END;
FUNCTION AKVA(fileid:LongWord):TRawList;
  VAR
    link:LongWord;
    links:LongWord;
    i:LongWord;
  BEGIN
    IF NOT dat_os_mac THEN BEGIN
      LoadDatFilePart(fileid,$1C,4,@links);
      SetLength(Result,links);
      FOR i:=0 TO links-1 DO BEGIN
        Result[i].src_offset:=$20+i*$74+$24;
        LoadDatFilePart(fileid,$20+i*$74+$24,4,@link);
        Result[i].raw_addr:=link;
        LoadDatFilePart(fileid,$20+i*$74+$28,4,@link);
        Result[i].raw_size:=link;
        Result[i].loc_sep:=False;
      END;
    END;
  END;
FUNCTION BINA(fileid:LongWord):TRawList;
  VAR
    link:LongWord;
    datasize:LongWord;
  BEGIN
    LoadDatFilePart(fileid,$0C,4,@link);
    LoadDatFilePart(fileid,$08,4,@datasize);
    SetLength(Result,1);
    Result[0].src_offset:=$0C;
    Result[0].raw_addr:=link;
    Result[0].raw_size:=datasize;
    Result[0].loc_sep:=dat_os_mac;
  END;
FUNCTION OSBD(fileid:LongWord):TRawList;
  VAR
    link:LongWord;
    datasize:LongWord;
  BEGIN
    LoadDatFilePart(fileid,$08,4,@datasize);
    LoadDatFilePart(fileid,$0C,4,@link);
    SetLength(Result,1);
    Result[0].src_offset:=$0C;
    Result[0].raw_addr:=link;
    Result[0].raw_size:=datasize;
    Result[0].loc_sep:=dat_os_mac;
  END;
FUNCTION SNDD(fileid:LongWord):TRawList;
  VAR
    link:LongWord;
    datasize:LongWord;
  BEGIN
    LoadDatFilePart(fileid,$40,4,@datasize);
    LoadDatFilePart(fileid,$44,4,@link);
    SetLength(Result,1);
    Result[0].src_offset:=$44;
    Result[0].raw_addr:=link;
    Result[0].raw_size:=datasize;
  END;
FUNCTION SUBT(fileid:LongWord):TRawList;
  VAR
    baselink,link:LongWord;
    links:LongWord;
    i,j,k:LongWord;
    data:Tdata;
  BEGIN
    LoadDatFilePart(fileid,$18,4,@baselink);
    LoadDatFilePart(fileid,$1C,4,@links);
    IF links>0 THEN BEGIN
      LoadDatFilePart(fileid,$20+(links-1)*4,4,@link);
      SetLength(data,link+1024);
      LoadRawFile(fileid,$1C,baselink,link+1024,False,@data[0]);
      k:=0;
      FOR j:=0 TO 1024 DO BEGIN
        IF (data[link+j]=$00) OR (j=1024) THEN BEGIN
          IF j<1024 THEN BEGIN
            IF k=0 THEN BEGIN
              k:=1;
            END ELSE BEGIN
              SetLength(Result,1);
              Result[0].src_offset:=$18;
              Result[0].raw_addr:=baselink;
              Result[0].raw_size:=link+j;
              Break;
            END;
          END;
        END;
      END;
    END;
  END;
FUNCTION TRAM(fileid:LongWord):TRawList;
  VAR
    i:Byte;
    link:LongWord;
    frames:Word;
    tempb:Byte;
    tempw:Word;
    templ:LongWord;
    data:Tdata;
    offset:Word;
  BEGIN
    SetLength(Result,13);
    LoadDatFilePart(fileid,$16C,2,@frames);
    {y-pos}
    LoadDatFilePart(fileid,$0C,4,@link);
    Result[0].src_offset:=$0C;
    Result[0].raw_addr:=link;
    Result[0].raw_size:=frames*4;
    {x-z-pos}
    LoadDatFilePart(fileid,$10,4,@link);
    Result[1].src_offset:=$10;
    Result[1].raw_addr:=link;
    Result[1].raw_size:=frames*8;
    {attacks}
    LoadDatFilePart(fileid,$182,1,@tempb);
    LoadDatFilePart(fileid,$14,4,@link);
    Result[2].src_offset:=$14;
    Result[2].raw_addr:=link;
    Result[2].raw_size:=tempb*32;
    {damage}
    LoadDatFilePart(fileid,$183,1,@tempb);
    LoadDatFilePart(fileid,$18,4,@link);
    Result[3].src_offset:=$18;
    Result[3].raw_addr:=link;
    Result[3].raw_size:=tempb*8;
    {motionblur}
    LoadDatFilePart(fileid,$184,1,@tempb);
    LoadDatFilePart(fileid,$1C,4,@link);
    Result[4].src_offset:=$1C;
    Result[4].raw_addr:=link;
    Result[4].raw_size:=tempb*8;
    {shortcut}
    LoadDatFilePart(fileid,$185,1,@tempb);
    LoadDatFilePart(fileid,$20,4,@link);
    Result[5].src_offset:=$20;
    Result[5].raw_addr:=link;
    Result[5].raw_size:=tempb*8;
    {throw}
    LoadDatFilePart(fileid,$24,4,@link);
    Result[6].src_offset:=$24;
    Result[6].raw_addr:=link;
    IF link>0 THEN
      Result[6].raw_size:=24
    ELSE
      Result[6].raw_size:=0;
    {footstep}
    LoadDatFilePart(fileid,$186,1,@tempb);
    LoadDatFilePart(fileid,$28,4,@link);
    Result[7].src_offset:=$28;
    Result[7].raw_addr:=link;
    Result[7].raw_size:=tempb*4;
    {particle}
    LoadDatFilePart(fileid,$187,1,@tempb);
    LoadDatFilePart(fileid,$2C,4,@link);
    Result[8].src_offset:=$2C;
    Result[8].raw_addr:=link;
    Result[8].raw_size:=tempb*24;
    {position}
    LoadDatFilePart(fileid,$30,4,@link);
    Result[9].src_offset:=$30;
    Result[9].raw_addr:=link;
    Result[9].raw_size:=frames*8;
    {particle}
    LoadDatFilePart(fileid,$154,2,@tempw);
    LoadDatFilePart(fileid,$38,4,@link);
    Result[11].src_offset:=$38;
    Result[11].raw_addr:=link;
    Result[11].raw_size:=tempw*34;
    {extent}
    LoadDatFilePart(fileid,$138,4,@templ);
    LoadDatFilePart(fileid,$13C,4,@link);
    Result[12].src_offset:=$13C;
    Result[12].raw_addr:=link;
    Result[12].raw_size:=templ*12;

    LoadDatFilePart(fileid,$34,4,@link);
    tempw:=0;
    IF link>0 THEN BEGIN
      {BODY ANIMATIONS PART DECODE!!!}
      SetLength(data,$FFFF);
      LoadRawFile(fileid,$34,link,$FFFF,False,@data[0]);
      offset:=data[24]+data[25]*255;
      {}
    END;
    Result[10].src_offset:=$34;
    Result[10].raw_addr:=link;
    Result[10].raw_size:=tempw;
  END;
FUNCTION TXMP(fileid:LongWord):TRawList;
  VAR
    link_pc:LongWord;
    link_mac:LongWord;
    x,y:Word;
    storetype:Byte;
    datasize:LongWord;
  BEGIN
    LoadDatFilePart(fileid,$8C,SizeOf(x),@x);
    LoadDatFilePart(fileid,$8E,SizeOf(y),@y);
    LoadDatFilePart(fileid,$90,SizeOf(storetype),@storetype);
    LoadDatFilePart(fileid,$9C,4,@link_pc);
    LoadDatFilePart(fileid,$A0,4,@link_mac);
    CASE storetype OF
      0,1,2: datasize:=x*y*2;
      8: datasize:=x*y*4;
      9: datasize:=x*y DIV 2;
    END;
    SetLength(Result,1);
    IF NOT dat_os_mac THEN BEGIN
      Result[0].src_offset:=$9C;
      Result[0].raw_addr:=link_pc
    END ELSE BEGIN
      Result[0].src_offset:=$A0;
      Result[0].raw_addr:=link_mac;
    END;
    Result[0].raw_size:=datasize;
    Result[0].loc_sep:=dat_os_mac;
  END;



PROCEDURE AddEntry(_ext:String; _name:String; _offset:LongWord; _datatype:Word; _description:String);
  VAR
    sid:Integer;
  BEGIN
    sid:=GetStructureInfoId(_ext);
    IF sid>=0 THEN BEGIN
      WITH structure_infos[sid] DO BEGIN
        SetLength(entries,Length(entries)+1);
        WITH entries[High(entries)] DO BEGIN
          name:=_name;
          offset:=_offset;
          datatype:=_datatype;
          description:=_description;
        END;
      END;
    END;
  END;

PROCEDURE AddExtension(_ext:String; _typedesc:String);
  BEGIN
    IF GetStructureInfoId(_ext)<0 THEN BEGIN
      SetLength(structure_infos,Length(structure_infos)+1);
      WITH structure_infos[High(structure_infos)] DO BEGIN
        extension:=_ext;
        typedesc:=_typedesc;
      END;
    END;
  END;

PROCEDURE InsertRawListHandler(ext:String; needed:Boolean; handler:THandler);
  BEGIN
    SetLength(RawListHandlers,Length(RawListHandlers)+1);
    RawListHandlers[High(RawListHandlers)].Ext:=ext;
    RawListHandlers[High(RawListHandlers)].needed:=needed;
    RawListHandlers[High(RawListHandlers)].handler:=handler;
  END;

PROCEDURE LoadStructureDefinitionFile(filename:String);
  VAR
    temps:String;
    deffile:Text;
    ext:String;
    fields:TStringList;
    fieldname:String;
    fieldoffset:LongWord;
    fieldtype:Word;
    fielddesc:String;
  BEGIN
    ext:=MidStr(ExtractFileName(filename),1,4);
    AssignFile(deffile,filename);
    Reset(deffile);
    IF NOT EoF(deffile) THEN BEGIN
      ReadLn(deffile,temps);
      AddExtension(ext,temps);
      WHILE NOT EoF(deffile) DO BEGIN
        ReadLn(deffile,temps);
        fields:=Explode(temps,#9);
        IF (Length(fields)=3) OR (Length(fields)=4) THEN BEGIN
          fieldname:=fields[0];
          fieldoffset:=HexToLong(fields[1]);
          fieldtype:=StrToInt(fields[2]);
          IF Length(fields)=4 THEN
            fielddesc:=fields[3]
          ELSE
            fielddesc:='';
//          ShowMessage(fieldname+' - '+IntToHex(fieldoffset,4)+' - '+IntToStr(fieldtype)+' - '+fielddesc);
          AddEntry(ext,fieldname,fieldoffset,fieldtype,fielddesc);
        END;
      END;
    END;
    CloseFile(deffile);
  END;

PROCEDURE LoadStructureDefinitions(defdir:String);
  VAR
    SR:TSearchRec;
  BEGIN
    IF DirectoryExists(defdir) THEN BEGIN
      IF FindFirst(defdir+'\*.txt', faAnyFile, SR)=0 THEN BEGIN
        REPEAT
          IF (SR.Attr AND faDirectory) <> faDirectory THEN BEGIN
            LoadStructureDefinitionFile(defdir+'\'+SR.Name);
          END;
        UNTIL FindNext(SR)<>0;
      END;
      FindClose(SR);
    END;
  END;

BEGIN
//  InsertRawListHandler('AGDB',True,AGDB);
  InsertRawListHandler('AKVA',True,AKVA);
  InsertRawListHandler('BINA',True,BINA);
  InsertRawListHandler('OSBD',True,OSBD);
  InsertRawListHandler('SNDD',True,SNDD);
  InsertRawListHandler('SUBT',True,SUBT);
  InsertRawListHandler('TRAM',True,TRAM);
  InsertRawListHandler('TXMP',True,TXMP);
END.