unit RawList;
interface
uses TypeDefs;

type
  THandler = function(ConnectionID, FileID: Integer): TRawDataList;
  TRawListHandler = record
    Ext:     String[4];
    needed:  Boolean;
    Handler: THandler;
  end;
  TRawListHandlers = array of TRawListHandler;

  TRawLists = class
    private
      FRawListHandlers: TRawListHandlers;
    public
      property RawListHandlers: TRawListHandlers read FRawListHandlers;
      procedure InsertRawListHandler(ext: String; needed: Boolean; handler: THandler);
      function GetRawList(ConnectionID, FileID: Integer): TRawDataList;
      function GetRawInfo(ConnectionID, FileID, DatOffset: Integer): TRawDataInfo;
  end;


var
  RawLists: TRawLists;


implementation

uses
  Template, ConnectionManager, Access_OniArchive, Classes, SysUtils, Math;


function AGDB(ConnectionID, FileID: Integer): TRawDataList;
var
  link:  Integer;
  links: Integer;
  i:     Integer;
begin
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $1C, 4, @links);
  links := links * 2;
  SetLength(Result, links);
  for i := 0 to links - 1 do
  begin
    Result[i].SrcOffset := $20 + i * 4;
    ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $20 + i * 4, 4, @link);
    Result[i].RawAddr := link;
    Result[i].RawSize := 32;
    Result[i].LocSep  := False;
  end;
end;




function AKVA(ConnectionID, FileID: Integer): TRawDataList;
var
  link:  Integer;
  links: Integer;
  i:     Integer;
begin
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $1C, 4, @links);
  SetLength(Result, links);
  for i := 0 to links - 1 do
  begin
    Result[i].SrcOffset := $20 + i * $74 + $24;
    ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $20 + i * $74 + $24, 4, @link);
    Result[i].RawAddr := link;
    ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $20 + i * $74 + $28, 4, @link);
    Result[i].RawSize := link;
    Result[i].LocSep  := False;
  end;
end;




function BINA(ConnectionID, FileID: Integer): TRawDataList;
var
  link:     Integer;
  datasize: Integer;
begin
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $0C, 4, @link);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $08, 4, @datasize);
  SetLength(Result, 1);
  Result[0].SrcOffset := $0C;
  Result[0].RawAddr   := link;
  Result[0].RawSize   := datasize;
  Result[0].LocSep    := not (ConManager.Connection[ConnectionID].DataOS = DOS_WIN);
end;




function OSBD(ConnectionID, FileID: Integer): TRawDataList;
var
  link:     Integer;
  datasize: Integer;
begin
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $08, 4, @datasize);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $0C, 4, @link);
  SetLength(Result, 1);
  Result[0].SrcOffset := $0C;
  Result[0].RawAddr   := link;
  Result[0].RawSize   := datasize;
  Result[0].LocSep     := not (ConManager.Connection[ConnectionID].DataOS = DOS_WIN);
end;




function SNDD(ConnectionID, FileID: Integer): TRawDataList;
var
  link:     Integer;
  datasize: Integer;
begin
  SetLength(Result, 1);
  if ConManager.Connection[ConnectionID].DataOS = DOS_MAC then
  begin
    ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $10, 4, @datasize);
    ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $14, 4, @link);
    Result[0].SrcOffset := $14;
  end
  else
  begin
    ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $40, 4, @datasize);
    ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $44, 4, @link);
    Result[0].SrcOffset := $44;
  end;
  Result[0].RawAddr := link;
  Result[0].RawSize := datasize;
  Result[0].LocSep  := False;
end;




function SUBT(ConnectionID, FileID: Integer): TRawDataList;
var
  baselink, lastlink: Integer;
  links: Integer;
  Data:  TStream;
  read:  Integer;
  char:  Byte;
  foundzeros: Byte;
begin
  SetLength(Result, 0);

  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $18, 4, @baselink);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $1C, 4, @links);
  if links > 0 then
  begin
    ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $20 + (links - 1) * 4, 4, @lastlink);
    Data := nil;
    TAccess_OniArchive(ConManager.Connection[ConnectionID]).LoadRawOffset(
          False, baselink, lastlink + 1024, Data);
    Data.Seek(lastlink, soFromBeginning);

    foundzeros := 0;
    repeat
      read := Data.Read(char, 1);
      if (read > 0) and (char = 0) then
      begin
        Inc(foundzeros);
      end;
    until (read = 0) or (foundzeros = 2);

    if foundzeros = 2 then
    begin
      SetLength(Result, 1);
      Result[0].SrcID     := FileID;
      Result[0].SrcOffset := $18;
      Result[0].RawAddr   := baselink;
      Result[0].RawSize   := Data.Position;
      Result[0].LocSep    := False;
    end;
  end;
end;




function TRAM(ConnectionID, FileID: Integer): TRawDataList;
var
  i:      Integer;
  link:   Integer;
  frames: Word;
  tempb:  Byte;
  tempw:  Word;
  templ:  Integer;
  Data:   TByteData;
  offset: Word;
  frame_count: Integer;
begin
  SetLength(Result, 13);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $16C, 2, @frames);
  {x-z-pos}
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $0C, 4, @link);
  Result[0].SrcOffset := $0C;
  Result[0].RawAddr   := link;
  if link > 0 then
    Result[0].RawSize := frames * 4
  else
    Result[0].RawSize := 0;
  {y-pos}
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $10, 4, @link);
  Result[1].SrcOffset := $10;
  Result[1].RawAddr   := link;
  if link > 0 then
    Result[1].RawSize := frames * 8
  else
    Result[1].RawSize := 0;
  {attacks}
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $182, 1, @tempb);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $14, 4, @link);
  Result[2].SrcOffset := $14;
  Result[2].RawAddr   := link;
  Result[2].RawSize   := tempb * 32;
  {damage}
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $183, 1, @tempb);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $18, 4, @link);
  Result[3].SrcOffset := $18;
  Result[3].RawAddr   := link;
  Result[3].RawSize   := tempb * 8;
  {motionblur}
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $184, 1, @tempb);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $1C, 4, @link);
  Result[4].SrcOffset := $1C;
  Result[4].RawAddr   := link;
  Result[4].RawSize   := tempb * 12;
  {shortcut}
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $185, 1, @tempb);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $20, 4, @link);
  Result[5].SrcOffset := $20;
  Result[5].RawAddr   := link;
  Result[5].RawSize   := tempb * 8;
  {throw}
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $24, 4, @link);
  Result[6].SrcOffset := $24;
  Result[6].RawAddr   := link;
  if link > 0 then
    Result[6].RawSize := 24
  else
    Result[6].RawSize := 0;
  {footstep}
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $186, 1, @tempb);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $28, 4, @link);
  Result[7].SrcOffset := $28;
  Result[7].RawAddr   := link;
  Result[7].RawSize   := tempb * 4;
  {particle}
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $187, 1, @tempb);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $2C, 4, @link);
  Result[8].SrcOffset := $2C;
  Result[8].RawAddr   := link;
  Result[8].RawSize   := tempb * 24;
  {position}
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $30, 4, @link);
  Result[9].SrcOffset := $30;
  Result[9].RawAddr   := link;
  Result[9].RawSize   := frames * 8;
  {particle}
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $154, 2, @tempw);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $38, 4, @link);
  Result[11].SrcOffset := $38;
  Result[11].RawAddr   := link;
  Result[11].RawSize   := tempw * 34;
  {extent}
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $138, 4, @templ);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $13C, 4, @link);
  Result[12].SrcOffset := $13C;
  Result[12].RawAddr   := link;
  Result[12].RawSize   := templ * 12;

  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $34, 4, @link);
  if link > 0 then
  begin
    ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $160, 2, @tempw);
    frame_count := 0;
    i := 0;
    SetLength(Data, $FFFF);
    TAccess_OniArchive(ConManager.Connection[ConnectionID]).LoadRawOffset(
          False, link, $FFFF, Data);
    offset := Data[$24] + Data[$25] * 256;
    while (offset + i < Length(Data)) and (frame_count < frames - 1) do
    begin
      Inc(i, tempw);
      frame_count := frame_count + Data[offset + i];
      Inc(i);
    end;
    if offset + i < Length(Data) then
    begin
      Inc(i, tempw);
      Result[10].RawSize := offset + i;
    end
    else
    begin
      Result[10].RawSize := 0;
    end;
  end;
  Result[10].SrcOffset := $34;
  Result[10].RawAddr   := link;
end;




function TXMP(ConnectionID, FileID: Integer): TRawDataList;
var
  link_pc:   Integer;
  link_mac:  Integer;
  x, y:      Word;
  storetype: Byte;
  datasize:  Integer;
  mipmap:    Byte;

  function GetImgSize(w,h, storetype: Integer): Integer;
  begin
    case storetype of
      0, 1, 2:
        Result := w*h*2;
      8:
        Result := w*h*4;
      9:
        Result :=  Max(1, w div 4) * Max(1, h div 4) * 8;
    else
      Result := -1;
    end;
  end;

begin
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $88, SizeOf(mipmap), @mipmap);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $8C, SizeOf(x), @x);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $8E, SizeOf(y), @y);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $90, SizeOf(storetype), @storetype);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $9C, 4, @link_pc);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $A0, 4, @link_mac);


  datasize := GetImgSize(x, y, storetype);
  if (mipmap and $01) > 0 then
  begin
    repeat
      x    := Max(x div 2, 1);
      y    := Max(y div 2, 1);
      datasize := datasize + GetImgSize(x, y, storetype);
    until (x = 1) and (y = 1);
  end;

  SetLength(Result, 1);
  if ConManager.Connection[ConnectionID].DataOS = DOS_WIN then
  begin
    Result[0].SrcOffset := $9C;
    Result[0].RawAddr   := link_pc;
  end
  else
  begin
    Result[0].SrcOffset := $A0;
    Result[0].RawAddr   := link_mac;
  end;
  Result[0].RawSize := datasize;
  Result[0].LocSep  := not (ConManager.Connection[ConnectionID].DataOS = DOS_WIN);
end;




function TRawLists.GetRawInfo(ConnectionID, FileID,
  DatOffset: Integer): TRawDataInfo;
var
  i: Integer;
  RawList: TRawDataList;
begin
  RawList          := GetRawList(ConnectionID, FileID);
  Result.SrcID     := -1;
  Result.SrcOffset := -1;
  Result.RawAddr   := -1;
  Result.RawSize   := -1;
  if Length(RawList) > 0 then
  begin
    for i := 0 to High(RawList) do
    begin
      if RawList[i].SrcOffset = DatOffset then
      begin
        Result.SrcID     := FileID;
        Result.SrcOffset := RawList[i].SrcOffset;
        Result.RawAddr   := RawList[i].RawAddr;
        Result.RawSize   := RawList[i].RawSize;
        Result.LocSep    := RawList[i].LocSep;
        Break;
      end;
    end;
  end;
end;


function TRawLists.GetRawList(ConnectionID, FileID: Integer): TRawDataList;
var
  i: Integer;
  fileinfo: TFileInfo;
begin
  SetLength(Result, 0);
  fileinfo := ConManager.Connection[ConnectionID].GetFileInfo(FileID);
  for i := 0 to High(FRawListHandlers) do
    if UpperCase(FRawListHandlers[i].Ext) = UpperCase(fileinfo.extension) then
    begin
      if FRawListHandlers[i].needed then
        Result := FRawListHandlers[i].Handler(ConnectionID, FileID);
      Break;
    end;
end;

procedure TRawLists.InsertRawListHandler(ext: String; needed: Boolean; handler: THandler);
begin
  SetLength(FRawListHandlers, Length(FRawListHandlers) + 1);
  FRawListHandlers[High(FRawListHandlers)].Ext := ext;
  FRawListHandlers[High(FRawListHandlers)].needed := needed;
  FRawListHandlers[High(FRawListHandlers)].handler := handler;
//  Raws := Raws + ext;
  AddToolListEntry('rawedit', '', ext);
end;




initialization
  RawLists := TRawLists.Create;
  RawLists.InsertRawListHandler('AGDB', False, AGDB);
  RawLists.InsertRawListHandler('AKVA', True, AKVA);
  RawLists.InsertRawListHandler('BINA', True, BINA);
  RawLists.InsertRawListHandler('OSBD', True, OSBD);
  RawLists.InsertRawListHandler('SNDD', True, SNDD);
  RawLists.InsertRawListHandler('SUBT', True, SUBT);
  RawLists.InsertRawListHandler('TRAM', True, TRAM);
  RawLists.InsertRawListHandler('TXMP', True, TXMP);
end.