unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, VirtualTrees, StrUtils, ExtCtrls;

type
  TForm1 = class(TForm)
    vst: TVirtualStringTree;
    list: TListBox;
    Splitter1: TSplitter;
    procedure FormCreate(Sender: TObject);
    procedure listClick(Sender: TObject);
    procedure vstGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
  private
  public
  end;

  TSwapList = class;

  TSwapItem = class
    name: String;
    size: Integer;
    parent: TSwapList;
  end;

  TSwapList = class(TSwapItem)
    childs: array of TSwapItem;
    count: Integer;
    constructor Create;
    function AddElem(name: String; size: Integer): Integer;
    function AddArray(name: String; count: Integer): Integer;
    function CloseArray: TSwapList;
    function Child(index: Integer): TSwapItem;
  end;

  TType = record
    name: String;
    SwapList: TSwapList;
  end;

  TTypes = array of TType;

  PNodeData = ^TNodeData;
  TNodeData = record
    TypeName:  String;
    Address:   Integer;
    Size:      Integer;
  end;

var
  Form1: TForm1;
  Types: TTypes;
  descfile: Text;

implementation

{$R *.dfm}

function AddVSTEntry(AVST: TCustomVirtualStringTree; ANode: PVirtualNode;
  ARecord: TNodeData): PVirtualNode;
var
  Data: PNodeData;
begin
  Result := AVST.AddChild(ANode);
  Data   := AVST.GetNodeData(Result);
  AVST.ValidateNode(Result, False);
  Data^ := ARecord;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  state: Integer;
  line, datas: String;
  i: Integer;
  current_list: TSwapList;
begin
  VST.NodeDataSize := SizeOf(TNodeData);

  state := 0;
  current_list := nil;
  AssignFile(descfile, ExtractFilePath(Application.ExeName)+'\templates.txt.gz');
  Reset(descfile);
  while not EoF(descfile) do
  begin
    ReadLn(descfile, line);
    if state = 0 then
    begin
      if Pos('gSwapCodes_', line) = 1 then
      begin
        SetLength(Types, Length(Types) + 1);
        Types[High(Types)].name := MidStr(line, Pos('_', line)+1, 4);
        Types[High(Types)].SwapList := TSwapList.Create;
        Types[High(Types)].SwapList.parent := nil;
        current_list := Types[High(Types)].SwapList;
        state := 1;
        datas := MidStr(line, Pos('db ', line) + 3, PosEx(' ', line, Pos('db ', line) + 3) - Pos('db ', line) - 3 );
        if datas = 'SWAPC_8BYTE' then
          current_list.AddElem('SWAPC_8BYTE', 8)
        else if datas = 'SWAPC_4BYTE' then
          current_list.AddElem('SWAPC_4BYTE', 4)
        else if datas = 'SWAPC_2BYTE' then
          current_list.AddElem('SWAPC_2BYTE', 2)
        else if datas = 'SWAPC_1BYTE' then
          current_list.AddElem('SWAPC_1BYTE', 1);
      end;
      if Pos('gTemplate_', line) = 1 then
      begin
        datas := MidStr(line, Pos('_', line)+1, 4);
        for i := 0 to High(Types) do
          if Types[i].name = datas then
            Break;
        if i < Length(Types) then
        begin
          if Pos('"', line) = 0 then
            ReadLn(descfile, line);
          datas := MidStr(line, Pos('"', line) + 1, PosEx('"', line, Pos('"', line) + 1) - (Pos('"', line) + 1) );
          Types[i].name := Types[i].name + ' - ' + datas;
        end;
      end;
    end else begin
      if PosEx(' ', line, Pos(' d', line) + 4) > 0 then
        datas := MidStr(line, Pos(' d', line) + 4, PosEx(' ', line, Pos(' d', line) + 4) - (Pos(' d', line) + 4) )
      else
        datas := MidStr(line, Pos(' d', line) + 4, Length(line) - Pos(' d', line) );
      if datas = 'SWAPC_8BYTE' then
        current_list.AddElem('SWAPC_8BYTE', 8)
      else if datas = 'SWAPC_4BYTE' then
        current_list.AddElem('SWAPC_4BYTE', 4)
      else if datas = 'SWAPC_2BYTE' then
        current_list.AddElem('SWAPC_2BYTE', 2)
      else if datas = 'SWAPC_1BYTE' then
        current_list.AddElem('SWAPC_1BYTE', 1)
      else if datas = 'SWAPC_FIXARR_S' then
      begin
        ReadLn(descfile, line);
        if Pos('h', line) > 0 then
          datas := MidStr(line, Pos('db ', line) + 3, Pos('h', line) - (Pos('db ', line) + 3) )
        else
          datas := MidStr(line, Pos('db ', line) + 3, Length(Line) - (Pos('db ', line) + 2) );
        i := current_list.AddArray('SWAPC_FIXARR_S', StrToInt('$'+datas));
        current_list := TSwapList(current_list.Child(i));
        Inc(State);
      end else if datas = 'SWAPC_FIXARR_E' then
      begin
        Dec(State);
        current_list := current_list.CloseArray;
      end else if datas = 'SWAPC_VARARR_S' then
      begin
        ReadLn(descfile, line);
        datas := MidStr(line, Pos(' d', line) + 4, Length(line) - (Pos(' d', line) + 3));
        if datas = 'SWAPC_8BYTE' then
          i := current_list.AddArray('SWAPC_VARARR_S', 8)
        else if datas = 'SWAPC_4BYTE' then
          i := current_list.AddArray('SWAPC_VARARR_S', 4)
        else if datas = 'SWAPC_2BYTE' then
          i := current_list.AddArray('SWAPC_VARARR_S', 2)
        else if datas = 'SWAPC_1BYTE' then
          i := current_list.AddArray('SWAPC_VARARR_S', 1);
        current_list := TSwapList(current_list.Child(i));
      end else if datas = 'SWAPC_VARARR_E' then
      begin
        Dec(State);
        current_list := current_list.CloseArray;
      end else if datas = 'SWAPC_TMPL_PTR' then
      begin
        ReadLn(descfile, line);
        datas := MidStr(line, Pos('dd ', line) + 4, 4);
        current_list.AddElem('SWAPC_TMPL_PTR: ' + datas, 4);
      end;
    end;
  end;
  CloseFile(descfile);

  list.Items.Clear;
  for i := 0 to High(Types) do
    list.Items.Add(Types[i].name);
end;


{ TSwapList }

function TSwapList.AddArray(name: String; count: Integer): Integer;
begin
  SetLength(childs, Length(childs) + 1);
  Result := High(childs);
  childs[Result] := TSwapList.Create;
  childs[Result].name := name;
  childs[Result].size := 0;
  childs[Result].parent := Self;
  TSwapList(childs[Result]).count := count;
end;

function TSwapList.AddElem(name: String; size: Integer): Integer;
begin
  SetLength(childs, Length(childs) + 1);
  Result := High(childs);
  childs[Result] := TSwapItem.Create;
  childs[Result].name := name;
  childs[Result].size := size;
  childs[Result].parent := Self;
  Self.size := Self.size + size;
end;

function TSwapList.Child(index: Integer): TSwapItem;
begin
  Result := childs[index];
end;

function TSwapList.CloseArray: TSwapList;
begin
  if Self.name = 'SWAPC_FIXARR_S' then
  begin
    Self.size := Self.size * Self.count;
    Self.parent.size := Self.parent.size + Self.size;
  end
  else
    Self.size := Self.count;
  Result := Self.parent;
end;

constructor TSwapList.Create;
begin
  SetLength(childs, 0);
  count := -1;
end;

procedure TForm1.listClick(Sender: TObject);
var
  i:    LongWord;
  Data:    TNodeData;
  node:    PVirtualNode;
  name:    String;

  procedure AddChilds(parent: PVirtualNode; SwapList: TSwapList; address: Integer);
  var
    i: Integer;
  begin
    if Length(SwapList.childs) > 0 then
    begin
      for i := 0 to High(SwapList.childs) do
      begin
        data.TypeName := SwapList.Child(i).name;
        data.Size := SwapList.Child(i).size;
        data.Address := address;
        address := address + data.Size;
        node := AddVSTEntry(VST, parent, data);
        if SwapList.Child(i) is TSwapList then
          AddChilds(node, TSwapList(SwapList.Child(i)), address);
      end;
    end;
  end;

begin
  VST.Clear;
  VST.BeginUpdate;

  name := list.Items.Strings[list.ItemIndex];
  for i := 0 to High(Types) do
    if Types[i].name = name then
      Break;

  if i < Length(Types) then
  begin
    AddChilds(nil, Types[i].SwapList, 0);
  end;

  VST.EndUpdate;
end;

procedure TForm1.vstGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
var
  data: PNodeData;
begin
  data := vst.GetNodeData(node);
  if TextType = ttNormal then
  begin
    case Column of
      0: CellText := data.TypeName;
      1: CellText := '0x' + IntToHex(data.Address, 8);
      2: CellText := IntToStr(data.Size);
    end;
  end;
end;

end.
