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
                      // 12    : dat-file-ID
                      // 13..16: Signed Integer[1..4]
                      // 1000..9999: Unused data[0-8999]
                      // 10000+: string[0+]
      description:String;
    END;
  TStructDefSub=RECORD
      SubName:String;
      Entries:Array OF TStructure_entry;
    END;
  TStructDef=RECORD
      Data:Boolean;
      Global:Array OF TStructure_entry;
      Subs:Array OF TStructDefSub;
    END;
  THandler=FUNCTION(fileid:LongWord):TRawList;
  TRawListHandlers=RECORD
    Ext:String[4];
    needed:Boolean;
    Handler:THandler;
  END;

VAR
  RawListHandlers:Array OF TRawListHandlers;


FUNCTION LoadStructureDefinition(fileid:LongWord):TStructDef;
FUNCTION GetDataType(typeid:Word):String;
FUNCTION GetTypeDataLength(datatype:Word):Word;
FUNCTION GetRawInfo(fileid,dat_offset:LongWord):TRawInfo;
FUNCTION GetRawList(fileid:LongWord):TRawList;


IMPLEMENTATION
USES Unit2_functions, Forms;

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;
      12: Result:=4;
      13..16: Result:=datatype-12;
      1000..9999: Result:=datatype-1000;
      10000..65535: Result:=datatype-10000;
    END;
  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';
      12: Result:='.dat-file-ID';
      13..16: Result:='SignedInt'+IntToStr((typeid-12)*8);
      1000..9999: Result:='Unused('+IntToStr(typeid-1000)+')';
      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
    IF NOT dat_os_mac THEN BEGIN
      LoadDatFilePart(fileid,$40,4,@datasize);
      LoadDatFilePart(fileid,$44,4,@link);
      Result[0].src_offset:=$44;
    END ELSE BEGIN
      LoadDatFilePart(fileid,$10,4,@datasize);
      LoadDatFilePart(fileid,$14,4,@link);
      Result[0].src_offset:=$14;
    END;
    SetLength(Result,1);
    Result[0].raw_addr:=link;
    Result[0].raw_size:=datasize;
    Result[0].loc_sep:=False;
  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 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;



FUNCTION LoadStructureDefinition(fileid:LongWord):TStructDef;
  VAR
    i:LongWord;
    current_type:Byte; //0: Global, 1: Undynamic, 2: Dynamic
    current_base,current_package,current_package_size:LongWord;
    packages:LongWord;
    deffile:Text;
    structentry:TStructure_Entry;
    fields:TStringList;
    filename:String;
    ext:String[4];
    temps:String;
    data:TData;
  BEGIN
    SetLength(Result.Global,0);
    SetLength(Result.Subs,0);
    Result.Data:=False;
    ext:=GetFileInfo(fileid).Extension;
    filename:=ExtractFilePath(Application.ExeName)+'\StructDefs\'+ext+'.txt';
    IF FileExists(filename) THEN BEGIN
      data:=LoadDatFile(fileid);
      AssignFile(deffile,filename);
      Reset(deffile);
      current_type:=0;
      Result.Data:=True;
      IF NOT EoF(deffile) THEN BEGIN
        ReadLn(deffile,temps);
        WHILE NOT EoF(deffile) DO BEGIN
          ReadLn(deffile,temps);
          IF Length(temps)>0 THEN BEGIN
            IF temps[1]='*' THEN BEGIN
              fields:=Explode(temps,'#');
              CASE Length(fields) OF
                1: BEGIN
                     current_type:=1;
                     current_base:=0;
                     SetLength(Result.Subs, Length(Result.Subs)+1);
                     Result.Subs[High(Result.Subs)].SubName:=MidStr(fields[0],2,Length(fields[0])-1);
                   END;
                2: BEGIN
                     current_type:=1;
                     current_base:=HexToLong(fields[1]);
                     SetLength(Result.Subs, Length(Result.Subs)+1);
                     Result.Subs[High(Result.Subs)].SubName:=MidStr(fields[0],2,Length(fields[0])-1);
                   END;
                5: BEGIN
                     current_type:=2;
                     current_base:=HexToLong(fields[1]);
                     current_package:=0;
                     current_package_size:=StrToInt(fields[4]);
                     CASE StrToInt(fields[3]) OF
                       1: packages:=data[HexToLong(fields[2])];
                       2: packages:=data[HexToLong(fields[2])]+data[HexToLong(fields[2])+1]*256;
                       4: packages:=data[HexToLong(fields[2])]+data[HexToLong(fields[2])+1]*256+data[HexToLong(fields[2])+2]*256*256+data[HexToLong(fields[2])+3]*256*256*256;
                     END;
                     SetLength(Result.Subs, Length(Result.Subs)+packages);
                     FOR current_package:=0 TO packages-1 DO BEGIN
                       Result.Subs[High(Result.Subs)-packages+current_package+1].SubName:=
                           MidStr(fields[0],2,Length(fields[0])-1)+'['+IntToStr(current_package)+']'+
                           '#'+IntToHex(current_base+current_package*current_package_size,8)+
                           '#'+IntToHex(current_package_size,8);
                     END;
                   END;
              END;
            END ELSE BEGIN
              fields:=Explode(temps,#9);
              IF (Length(fields)=3) OR (Length(fields)=4) THEN BEGIN
                structentry.name:=fields[0];
                structentry.datatype:=StrToInt(fields[2]);
                IF Length(fields)=4 THEN
                  structentry.description:=fields[3]
                ELSE
                  structentry.description:='';
                IF current_type IN [0,1] THEN BEGIN
                  structentry.offset:=HexToLong(fields[1])+current_base;
                  IF Length(Result.Subs)=0 THEN BEGIN
                    SetLength(Result.Global,Length(Result.Global)+1);
                    Result.Global[High(Result.Global)]:=structentry;
                  END ELSE BEGIN
                    SetLength(Result.Subs[High(Result.Subs)].Entries,Length(Result.Subs[High(Result.Subs)].Entries)+1);
                    Result.Subs[High(Result.Subs)].Entries[High(Result.Subs[High(Result.Subs)].Entries)]:=structentry;
                  END;
                END ELSE BEGIN
                  FOR current_package:=0 TO packages-1 DO BEGIN
                    structentry.offset:=current_base+current_package*current_package_size+HexToLong(fields[1]);
                    WITH Result.Subs[High(Result.Subs)-packages+current_package+1] DO BEGIN
                      SetLength(Entries,Length(Entries)+1);
                      Entries[High(Entries)]:=structentry;
                    END;
                  END;
                END;
              END;
            END;
          END;
        END;
      END;
      CloseFile(deffile);
    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.