unit BinEdit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, VirtualTrees, Grids, Wrapgrid, MPHexEditor,
  StdCtrls, Menus, ExtCtrls, Buttons,
  Data, TypeDefs, ConnectionManager,
  _TemplateFileList, VTHeaderPopup, ComCtrls;

type
  TForm_BinEdit = class(TForm_TemplateFileList)
    panel_imexport: TPanel;
    btn_export: TButton;
    btn_import: TButton;
    hex: TMPHexEditor;
    Splitter2: TSplitter;
    value_viewer: TWrapGrid;
    Splitter3: TSplitter;
    VST: TVirtualStringTree;
    VTHPopup: TVTHeaderPopupMenu;
    value_viewer_context: TPopupMenu;
    value_viewer_context_copy: TMenuItem;
    value_viewer_context_copyasdec: TMenuItem;
    value_viewer_context_copyasfloat: TMenuItem;
    value_viewer_context_copyasbitset: TMenuItem;
    value_viewer_context_copyasstring: TMenuItem;
    value_viewer_context_copyashex: TMenuItem;
    procedure NewFile(fileinfo: TFileInfo);

    function Save: Boolean;
    function GetValue(datatype: Word; offset: Integer): String;
    procedure SetNewValue(datatype: Word; offset: Integer; Value: String);

    procedure WriteStructureInfos;
    procedure ClearStructViewer;

    procedure ClearValues;
    procedure WriteValues;

    procedure FormCreate(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure hexChange(Sender: TObject);
    procedure hexKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure hexSelectionChanged(Sender: TObject);
    procedure value_viewerDblClick(Sender: TObject);
    procedure FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure value_viewer_contextPopup(Sender: TObject);
    procedure value_viewerMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure value_viewer_context_copyClick(Sender: TObject);
    procedure VSTDblClick(Sender: TObject);
    procedure VSTFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex);
    procedure VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
    procedure VSTHeaderDragged(Sender: TVTHeader; Column: TColumnIndex;
      OldPosition: Integer);
    procedure VTHPopupColumnChange(const Sender: TBaseVirtualTree;
      const Column: TColumnIndex; Visible: Boolean);
  private
    fileid: Integer;
    ConID: Integer;
    rawlist: TRawDataList;
  public
  end;

implementation
{$R *.dfm}
uses ValueEdit, Main, Functions, DatStructureLoader, RawEdit, RawList, 
  StrUtils, Clipbrd, _TemplateFile;


type
  PNodeData = ^TNodeData;

  TNodeData = record
    Caption:  String;
    Offset:   LongInt;
    DataType: Word;
    Value:    String;
    Description: String;
  end;



procedure TForm_BinEdit.FormCreate(Sender: TObject);
begin
  inherited;
  Self.OnNewFileSelected := NewFile;

  Self.Caption := '';
  fileid     := 0;
  VST.NodeDataSize := SizeOf(TNodeData);
  value_viewer.ColCount := 2;
  value_viewer.RowCount := 8;
  value_viewer.FixedRows := 1;
  value_viewer.FixedCols := 1;
  value_viewer.Cells[0, 0] := 'Type';
  value_viewer.Cells[1, 0] := 'Value';
  value_viewer.Cells[0, 1] := '1 byte, unsigned';
  value_viewer.Cells[0, 2] := '2 bytes, unsigned';
  value_viewer.Cells[0, 3] := '4 bytes, unsigned';
  value_viewer.Cells[0, 4] := 'Bitset';
  value_viewer.Cells[0, 5] := 'Float';
  value_viewer.Cells[0, 6] := 'String';
  value_viewer.Cells[0, 7] := 'Selected length';
  value_viewer.ColWidths[0] := 120;
  value_viewer.ColWidths[1] := 1000;
//  hex.Height := content.Height - 215;
  //
  value_viewer.Font.Charset := AppSettings.CharSet;
  VST.Font.Charset := AppSettings.CharSet;
  hex.Translation := tkAsIs;
  hex.Font.Charset := AppSettings.CharSet;
  //
end;


procedure TForm_BinEdit.NewFile(fileinfo: TFileInfo);
var
  mem:  TMemoryStream;
begin
  if ConID <> -1 then
  begin
    if hex.Modified then
    begin
      if not Save then
      begin
        Self.SelectFileID(ConnectionID, FileID);
        Exit;
      end;
    end;
  end;
  if fileinfo.ID >= 0 then
  begin
    fileid := fileinfo.ID;
    ConID := ConnectionID;
//    if ConManager.Connection[ConID].ExtractFileIDOfName(filelist.Items.Strings[filelist.ItemIndex]) <> fileid then
//      Self.SelectFileID(ConnectionID, fileid);
    Self.ClearStructViewer;
    ClearValues;
    mem := nil;
    hex.DataSize := 0;
    if fileinfo.Size > 0 then
    begin
      ConManager.Connection[ConID].LoadDatFile(fileid, TStream(mem));
      rawlist := ConManager.Connection[ConID].GetRawList(fileid);
      hex.LoadFromStream(mem);
      mem.Free;
      WriteStructureInfos;
    end;
  end
  else
  begin
    fileid := -1;
    ConID := -1;
    Self.ClearStructViewer;
    ClearValues;
    hex.DataSize := 0;
    SetLength(rawlist, 0);
  end;
end;




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;





function TForm_BinEdit.GetValue(datatype: Word; offset: Integer): String;
var
  Data: TByteData;
  i:    Integer;
  floatformat: TFormatSettings;
begin
  floatformat.DecimalSeparator := '.';
  case datatype of
    1:
      Result := IntToStr(hex.Data[offset]);
    2:
      Result := IntToStr(hex.Data[offset] + hex.Data[offset + 1] * 256);
    3:
      Result := IntToStr(hex.Data[offset] + hex.Data[offset + 1] * 256 + hex.Data[offset + 2] * 256 * 256);
    4:
      Result := IntToStr(hex.Data[offset] + hex.Data[offset + 1] * 256 + hex.Data[offset + 2] *
        256 * 256 + hex.Data[offset + 3] * 256 * 256 * 256);
    5:
      Result := '0x' + IntToHex(hex.Data[offset], 2);
    6:
      Result := '0x' + IntToHex(hex.Data[offset] + hex.Data[offset + 1] * 256, 4);
    7:
      Result := '0x' + IntToHex(hex.Data[offset] + hex.Data[offset + 1] * 256 +
        hex.Data[offset + 2] * 256 * 256, 6);
    8:
      Result := '0x' + IntToHex(hex.Data[offset] + hex.Data[offset + 1] * 256 +
        hex.Data[offset + 2] * 256 * 256 + hex.Data[offset + 3] * 256 * 256 * 256, 8);
    9:
    begin
      SetLength(Data, 4);
      Data[0] := hex.Data[offset];
      Data[1] := hex.Data[offset + 1];
      Data[2] := hex.Data[offset + 2];
      Data[3] := hex.Data[offset + 3];
      Result  := FloatToStr(Decode_Float(Data), floatformat);
    end;
    10:
      Result := IntToBin(hex.Data[offset]);
    11:
    begin
      if Length(rawlist) > 0 then
      begin
        for i := 0 to High(rawlist) do
          if rawlist[i].SrcOffset = offset then
          begin
            if rawlist[i].RawAddr > 0 then
              Result := '0x' + IntToHex(rawlist[i].RawAddr, 8)
            else
              Result := 'unused';
            Break;
          end;
        if i > High(rawlist) then
          Result := 'unused';
      end;
    end;
    12:
      if hex.Data[offset] = 1 then
        Result := FormatNumber(hex.Data[offset + 1] + hex.Data[offset + 2] * 256 +
          hex.Data[offset + 3] * 256 * 256, 5, '0')
      else
        Result := 'no link';
    13:
      Result := IntToStr(hex.Data[offset]);
    14:
      Result := IntToStr(hex.Data[offset] + hex.Data[offset + 1] * 256);
    15:
      Result := IntToStr(hex.Data[offset] + hex.Data[offset + 1] * 256 + hex.Data[offset + 2] * 256 * 256);
    16:
      Result := IntToStr(hex.Data[offset] + hex.Data[offset + 1] * 256 + hex.Data[offset + 2] *
        256 * 256 + hex.Data[offset + 3] * 256 * 256 * 256);
    17:
      Result := IntToStr((hex.Data[offset + 3]) div 2);
    100..300:
    begin
      Result := '';
      for i := 1 to datatype - 100 do
      begin
        if hex.Data[offset + i - 1] >= 32 then
          Result := Result + Chr(hex.Data[offset + i - 1])
        else
          Break;
      end;
    end;
    1000..9999:
    begin
      Result := '';
      for i := 1 to datatype - 1000 do
      begin
        if hex.Data[offset + i - 1] >= 32 then
          Result := Result + Chr(hex.Data[offset + i - 1])
        else
          Result := Result + '.';
      end;
    end;
    10000..65535:
    begin
      Result := '';
      for i := 1 to datatype - 10000 do
      begin
        if hex.Data[offset + i - 1] >= 32 then
          Result := Result + Chr(hex.Data[offset + i - 1])
        else
          Result := Result + '.';
      end;
    end;
  end;
end;




procedure TForm_BinEdit.WriteStructureInfos;
var
  i, j:    Integer;
  pdata:   PNodeData;
  Data:    TNodeData;
  node:    PVirtualNode;
  structs: TStructDef;
begin
  VST.BeginUpdate;
  if VST.RootNodeCount = 0 then
  begin
    structs := LoadStructureDefinition(ConID, fileid);
    if structs.Data then
    begin
      if Length(structs.Global) > 0 then
      begin
        for i := 0 to High(structs.Global) do
        begin
          Data.Caption  := structs.Global[i].Name;
          Data.Offset   := structs.Global[i].offset;
          Data.DataType := structs.Global[i].datatype;
          Data.Value    := GetValue(structs.Global[i].datatype, structs.Global[i].offset);
          Data.Description := structs.Global[i].description;
          AddVSTEntry(VST, nil, Data);
        end;
      end;
      if Length(structs.Subs) > 0 then
      begin
        for i := 0 to High(structs.Subs) do
        begin
          with structs.Subs[i] do
          begin
            if Length(Entries) > 0 then
            begin
              if Pos('#', SubName) > 0 then
              begin
                Data.Offset  := StrToInt('$'+MidStr(SubName, Pos('#', SubName) + 1, 8));
                Data.Value   := '$' +
                  MidStr(SubName, PosEx('#', SubName, Pos('#', SubName) + 1) + 1, 8);
                Data.Caption := MidStr(SubName, 1, Pos('#', SubName) - 1);
                Data.Description := SubDesc;
              end
              else
              begin
                Data.Caption := SubName;
                Data.Description := SubDesc;
                Data.Offset := 0;
                Data.Value := '';
              end;
              Data.DataType := 0;
              node := AddVSTEntry(VST, nil, Data);
              Data.Description := '';
              for j := 0 to High(Entries) do
              begin
                Data.Caption  := Entries[j].Name;
                Data.Offset   := Entries[j].offset;
                Data.DataType := Entries[j].datatype;
                Data.Value    := GetValue(Entries[j].datatype, Entries[j].offset);
                Data.Description := Entries[j].description;
                AddVSTEntry(VST, node, Data);
              end;
            end;
          end;
        end;
      end;
    end;
    if VST.RootNodeCount > 0 then
      VST.FocusedNode := VST.GetFirst;
  end
  else
  begin
    Node := VST.GetFirst;
    while Assigned(Node) do
    begin
      pdata := VST.GetNodeData(Node);
      if pdata.DataType > 0 then
        pdata.Value := GetValue(pdata.Datatype, pdata.Offset);
      Node := VST.GetNext(Node);
    end;
  end;
  VST.EndUpdate;
end;




procedure TForm_BinEdit.ClearValues;
var
  i: Byte;
begin
  for i := 1 to value_viewer.RowCount - 1 do
  begin
    value_viewer.Cells[1, i] := '';
  end;
end;




procedure TForm_BinEdit.WriteValues;
var
  i, j:  Integer;
  Data:  TByteData;
  str:   String;
  Value: Integer;
  floatformat: TFormatSettings;
begin
  floatformat.DecimalSeparator := '.';
  for i := 1 to value_viewer.RowCount - 1 do
  begin
    if value_viewer.Cells[0, i] = '1 byte, unsigned' then
    begin
      if ((hex.SelCount = 1) or (hex.SelCount = 0)) and not
        ((hex.SelStart + 1) > hex.DataSize) then
      begin
        Value := hex.Data[hex.SelStart];
        value_viewer.Cells[1, i] := IntToStr(Value) + ' / 0x' + IntToHex(Value, 2);
      end
      else
        value_viewer.Cells[1, i] := '';
    end;
    if value_viewer.Cells[0, i] = '2 bytes, unsigned' then
    begin
      if ((hex.SelCount = 2) or (hex.SelCount = 0)) and not
        ((hex.SelStart + 2) > hex.DataSize) then
      begin
        Value := hex.Data[hex.SelStart] + hex.Data[hex.SelStart + 1] * 256;
        value_viewer.Cells[1, i] := IntToStr(Value) + ' / 0x' + IntToHex(Value, 4);
      end
      else
        value_viewer.Cells[1, i] := '';
    end;
    if value_viewer.Cells[0, i] = '4 bytes, unsigned' then
    begin
      if ((hex.SelCount = 4) or (hex.SelCount = 0)) and not
        ((hex.SelStart + 4) > hex.DataSize) then
      begin
        Value := hex.Data[hex.SelStart] + hex.Data[hex.SelStart + 1] * 256 +
          hex.Data[hex.SelStart + 2] * 256 * 256 + hex.Data[hex.SelStart + 3] * 256 * 256 * 256;
        value_viewer.Cells[1, i] := IntToStr(Value) + ' / 0x' + IntToHex(Value, 8);
      end
      else
        value_viewer.Cells[1, i] := '';
    end;
    if value_viewer.Cells[0, i] = 'Bitset' then
    begin
      if (hex.SelCount <= 8) then
      begin
        if hex.SelCount = 0 then
        begin
          SetLength(Data, 1);
          Data[0] := hex.Data[hex.SelStart];
        end
        else
        begin
          SetLength(Data, hex.SelCount);
          for j := 0 to hex.SelCount - 1 do
            Data[j] := hex.Data[hex.SelStart + j];
        end;
        value_viewer.Cells[1, i] := DataToBin(Data);
      end
      else
        value_viewer.Cells[1, i] := '';
    end;
    if value_viewer.Cells[0, i] = 'Float' then
    begin
      if ((hex.SelCount = 4) or (hex.SelCount = 0)) and not
        ((hex.SelStart + 4) > hex.DataSize) then
      begin
        SetLength(Data, 4);
        for j := 0 to 3 do
          Data[j] := hex.Data[hex.SelStart + j];
        value_viewer.Cells[1, i] := FloatToStr(Decode_Float(Data), floatformat);
      end
      else
        value_viewer.Cells[1, i] := '';
    end;
    if value_viewer.Cells[0, i] = 'Selected length' then
    begin
      value_viewer.Cells[1, i] := IntToStr(hex.SelCount) + ' bytes';
    end;
    if value_viewer.Cells[0, i] = 'String' then
    begin
      j   := 0;
      str := '';
      if hex.SelCount = 0 then
      begin
        while (hex.SelStart + j) < hex.DataSize do
        begin
          if hex.Data[hex.SelStart + j] = 0 then
            Break;
          if hex.Data[hex.selstart + j] >= 32 then
            str := str + Char(hex.Data[hex.SelStart + j])
          else
            str := str + '.';
          Inc(j);
        end;
      end
      else
      begin
        for j := 0 to hex.SelCount - 1 do
          if hex.Data[hex.selstart + j] >= 32 then
            str := str + Char(hex.Data[hex.SelStart + j])
          else if hex.Data[hex.selstart + j] > 0 then
            str := str + '.'
          else
            Break;
      end;
      value_viewer.Cells[1, i] := str;
    end;
  end;
end;




function TForm_BinEdit.Save: Boolean;
var
  mem:  TMemoryStream;
  i:    Integer;
begin
  case MessageBox(Self.Handle, PChar('Save changes to file ' +
      ConManager.Connection[ConID].GetFileInfo(fileid).Name + '?'), PChar('Data changed...'),
      MB_YESNOCANCEL + MB_ICONQUESTION) of
    idYes:
    begin
      mem := TMemoryStream.Create;
      hex.SaveToStream(mem);
      mem.Seek(0, soFromBeginning);
      ConManager.Connection[ConID].UpdateDatFile(fileid, mem);
      mem.Free;
      hex.Modified := False;
      for i := 0 to hex.Datasize - 1 do
        hex.ByteChanged[i] := False;
      Result := True;
    end;
    idNo:
      Result := True;
    idCancel:
    begin
      Result := False;
    end;
  end;
end;




procedure TForm_BinEdit.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if hex.Modified then
  begin
    if not Save then
      CanClose := False;
  end;
end;




procedure TForm_BinEdit.ClearStructViewer;
begin
  VST.Clear;
end;






procedure TForm_BinEdit.hexChange(Sender: TObject);
begin
  ClearValues;
  if hex.DataSize > 0 then
  begin
    WriteStructureInfos;
    WriteValues;
  end;
end;




procedure TForm_BinEdit.hexKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
//var
//  temps: String;
begin
  if (Shift = [ssCtrl]) and (Key = Ord('C')) then
  begin
    if hex.SelCount > 0 then
    begin
      if hex.InCharField then
        Clipboard.AsText := hex.SelectionAsText
      else
        Clipboard.AsText := hex.SelectionAsHex;
    end;
  end;
  if (Shift = [ssCtrl]) and (Key = Ord('V')) then
  begin
{      temps:=Clipboard.AsText;
      IF hex.SelStart+Length(temps)>hex.DataSize THEN
        SetLength(temps, hex.DataSize-hex.SelStart);
      hex.Sel
      hex.SelCount:=Length(temps);
      hex.ReplaceSelection(temps,Length(temps));
}    end;
end;




procedure TForm_BinEdit.hexSelectionChanged(Sender: TObject);
var
  selstart: Integer;
  node:     PVirtualNode;
  pdata:    PNodeData;
begin
  if hex.DataSize > 0 then
  begin
    WriteValues;
    selstart := hex.SelStart;
    if VST.RootNodeCount > 0 then
    begin
      Node := VST.GetFirst;
      while Assigned(Node) do
      begin
        pdata := VST.GetNodeData(Node);
        if pdata.DataType > 0 then
        begin
          if ((selstart - pdata.Offset) < GetDataTypeLength(pdata.DataType)) and
            ((selstart - pdata.Offset) >= 0) then
          begin
            VST.FocusedNode    := Node;
            VST.Selected[Node] := True;
            Break;
          end;
        end;
        Node := VST.GetNext(Node);
      end;
    end;
  end;
end;




procedure TForm_BinEdit.value_viewer_contextPopup(Sender: TObject);
var
  i: Byte;
begin
  for i := 0 to value_viewer_context.Items.Count - 1 do
    value_viewer_context.Items.Items[i].Visible := False;
  with value_viewer do
  begin
    if (Col = 1) and (Row > 0) and (Length(Cells[Col, Row]) > 0) then
    begin
      if Pos(' byte', Cells[0, Row]) = 2 then
      begin
        value_viewer_context.Items.Find('Copy to &clipboard').Visible := True;
        value_viewer_context.Items.Find('Copy to clipboard (as &dec)').Visible := True;
        value_viewer_context.Items.Find('Copy to clipboard (as &hex)').Visible := True;
      end;
      if Pos('Float', Cells[0, Row]) = 1 then
        value_viewer_context.Items.Find('Copy to clipboard (as &float)').Visible := True;
      if Pos('Bitset', Cells[0, Row]) = 1 then
        value_viewer_context.Items.Find(
          'Copy to clipboard (as &bitset)').Visible := True;
      if Pos('String', Cells[0, Row]) = 1 then
        value_viewer_context.Items.Find(
          'Copy to clipboard (as &string)').Visible := True;
      if Pos('Selected length', Cells[0, Row]) = 1 then
        value_viewer_context.Items.Find('Copy to &clipboard').Visible := True;
    end;
  end;
end;




procedure TForm_BinEdit.value_viewerMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  ACol, ARow: Integer;
begin
  if Button = mbRight then
  begin
    value_viewer.MouseToCell(x, y, ACol, ARow);
    if ARow > 0 then
    begin
      value_viewer.Col := ACol;
      value_viewer.Row := ARow;
    end;
  end;
end;




procedure TForm_BinEdit.value_viewer_context_copyClick(Sender: TObject);
var
  Name:  String;
  Value: Integer;
begin
  Name := TMenuItem(Sender).Name;
  if Pos('asstring', Name) > 0 then
  begin
    Clipboard.AsText := value_viewer.Cells[value_viewer.Col, value_viewer.Row];
  end
  else if Pos('asfloat', Name) > 0 then
  begin
    Clipboard.AsText := value_viewer.Cells[value_viewer.Col, value_viewer.Row];
  end
  else if Pos('asbitset', Name) > 0 then
  begin
    Clipboard.AsText := value_viewer.Cells[value_viewer.Col, value_viewer.Row];
  end
  else if (Pos('ashex', Name) > 0) or (Pos('asdec', Name) > 0) then
  begin
    if value_viewer.Cells[0, value_viewer.Row] = '1 byte, unsigned' then
    begin
      if ((hex.SelCount = 1) or (hex.SelCount = 0)) and not
        ((hex.SelStart + 1) > hex.DataSize) then
        Value := hex.Data[hex.SelStart];
    end;
    if value_viewer.Cells[0, value_viewer.Row] = '2 bytes, unsigned' then
    begin
      if ((hex.SelCount = 2) or (hex.SelCount = 0)) and not
        ((hex.SelStart + 2) > hex.DataSize) then
        Value := hex.Data[hex.SelStart] + hex.Data[hex.SelStart + 1] * 256;
    end;
    if value_viewer.Cells[0, value_viewer.Row] = '4 bytes, unsigned' then
    begin
      if ((hex.SelCount = 4) or (hex.SelCount = 0)) and not
        ((hex.SelStart + 4) > hex.DataSize) then
        Value := hex.Data[hex.SelStart] + hex.Data[hex.SelStart + 1] * 256 +
          hex.Data[hex.SelStart + 2] * 256 * 256 + hex.Data[hex.SelStart + 3] * 256 * 256 * 256;
    end;
    if Pos('asdec', Name) > 0 then
    begin
      Clipboard.AsText := IntToStr(Value);
    end
    else
    begin
      if value_viewer.Cells[0, value_viewer.Row] = '1 byte, unsigned' then
        Clipboard.AsText := '0x' + IntToHex(Value, 2);
      if value_viewer.Cells[0, value_viewer.Row] = '2 bytes, unsigned' then
        Clipboard.AsText := '0x' + IntToHex(Value, 4);
      if value_viewer.Cells[0, value_viewer.Row] = '4 bytes, unsigned' then
        Clipboard.AsText := '0x' + IntToHex(Value, 8);
    end;
  end
  else
  begin
    Clipboard.AsText := value_viewer.Cells[value_viewer.Col, value_viewer.Row];
  end;
end;




procedure TForm_BinEdit.VSTDblClick(Sender: TObject);
var
  node: PVirtualNode;
  nodedata: PNodeData;
  rawinfo: TRawDataInfo;
  form: TForm_TemplateFileList;
begin
  if VST.FocusedColumn = 3 then
  begin
    node     := VST.FocusedNode;
    nodedata := VST.GetNodeData(node);

    if not (nodedata.datatype in [11, 12]) and
      ((nodedata.DataType < 100) or (nodedata.DataType > 300)) then
    begin
      Form_ValueEdit.MakeVarInput(nodedata.Caption, nodedata.offset,
        nodedata.datatype, nodedata.Value, Self);
    end
    else
    begin
      if (nodedata.DataType = 11) and (nodedata.Value <> 'unused') then
      begin
        rawinfo := ConManager.Connection[ConID].GetRawInfo(fileid, nodedata.offset);
        if rawinfo.RawSize > 0 then
        begin
          form := nil;
          form := TForm_TemplateFileList(Form_Main.open_child('rawedit', ConID, fileid));
          if Assigned(form) then
            TForm_RawEdit(form).LoadRaw(rawinfo);
        end;
      end;
      if (nodedata.DataType = 12) and (nodedata.Value <> 'no link') then
      begin
        if (StrToInt(nodedata.Value) < ConManager.Connection[ConID].GetFileCount) and
          (StrToInt(nodedata.Value) > 0) and
          (StrToInt(nodedata.Value) <> fileid) then
        begin
          if ConManager.Connection[ConID].GetFileInfo(StrToInt(nodedata.Value)).Size > 0 then
            Form_Main.open_child('binedit', ConID, StrToInt(nodedata.Value))
          else
            ShowMessage('Linked filed is a zero-byte-file');
        end;
      end;
      if (nodedata.DataType >= 100) and (nodedata.DataType <= 300) then
      begin
        form := TForm_TemplateFileList(Form_Main.open_child('binedit', ConID));
        if Assigned(form) then
          form.SetFileFilters(nodedata.Value, '', False);
      end;
    end;

  end;
end;




procedure TForm_BinEdit.VSTFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex);
var
  Data: PNodeData;
begin
  Data := VST.GetNodeData(node);
  if Data.DataType > 0 then
  begin
    hex.SelStart := Data.Offset;
    hex.SelEnd   := Data.Offset + GetDataTypeLength(Data.DataType) - 1;
  end
  else
  begin
    hex.SelStart := Data.Offset;
    hex.SelEnd   := Data.Offset + StrToInt(Data.Value) - 1;
  end;
end;




procedure TForm_BinEdit.VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
var
  Data: PNodeData;
begin
  Data     := Sender.GetNodeData(Node);
  CellText := '';
  if TextType = ttNormal then
  begin
    case Column of
      0:
        CellText := Data.Caption;
      1:
        if Data.DataType > 0 then
          CellText := '0x' + IntToHex(Data.Offset, 8)
        else if Data.Offset > 0 then
          CellText := '0x' + IntToHex(Data.Offset, 8);
      2:
        if Data.DataType > 0 then
          CellText := GetDataType(Data.DataType);
      3:
        if Data.DataType > 0 then
          CellText := Data.Value //GetValue(data.DataType, data.Offset)
        else if Length(Data.Value) > 0 then
          CellText := IntToStr(StrToInt(Data.Value)) + ' Bytes';
      4:
        CellText := Data.Description;
    end;
  end;
end;




procedure TForm_BinEdit.VSTHeaderDragged(Sender: TVTHeader; Column: TColumnIndex;
  OldPosition: Integer);
begin
  if Sender.Columns.Items[column].Position < 1 then
    Sender.Columns.Items[column].Position := OldPosition;
end;




procedure TForm_BinEdit.VTHPopupColumnChange(const Sender: TBaseVirtualTree;
  const Column: TColumnIndex; Visible: Boolean);
begin
  if column = 0 then
    TVirtualStringTree(Sender).Header.Columns.Items[column].Options :=
      TVirtualStringTree(Sender).Header.Columns.Items[column].Options + [coVisible];
end;




procedure TForm_BinEdit.SetNewValue(datatype: Word; offset: Integer; Value: String);
var
  Data: TByteData;
  value_int: LongWord;
  value_float: Single;
  i: Word;
begin
  case datatype of
    1..4:
    begin
      value_int := StrToInt(Value);
      SetLength(Data, datatype);
      for i := 0 to datatype - 1 do
      begin
        Data[i]   := value_int mod 256;
        value_int := value_int div 256;
      end;
    end;
    5..8:
    begin
      value_int := StrToInt('$' + Value);
      SetLength(Data, datatype - 4);
      for i := 0 to datatype - 5 do
      begin
        Data[i]   := value_int mod 256;
        value_int := value_int div 256;
      end;
    end;
    9:
    begin
      value_float := StrToFloat(Value);
      Data := Encode_Float(value_float);
    end;
    10:
    begin
      value_int := BinToInt(Value);
      SetLength(Data, 1);
      Data[0] := value_int;
    end;
    10000..65535:
    begin
      SetLength(Data, datatype - 10000);
      for i := 1 to datatype - 10000 do
      begin
        if i <= Length(Value) then
          Data[i - 1] := Ord(Value[i])
        else
          Data[i - 1] := 0;
      end;
    end;
  end;
  for i := 0 to High(Data) do
  begin
    if hex.Data[offset + i] <> Data[i] then
      hex.ByteChanged[offset + i] := True;
    hex.Data[offset + i] := Data[i];
  end;
  hex.Modified := True;
  hexChange(Self);
  hex.Repaint;
end;




procedure TForm_BinEdit.value_viewerDblClick(Sender: TObject);
var
  offset:     Integer;
  datatype:   Word;
  objectname: String;
  Value:      String;
begin
  if (value_viewer.Col = 1) and (Length(value_viewer.Cells[1, value_viewer.Row]) > 0) then
  begin
    offset := hex.SelStart;
    if value_viewer.Cells[0, value_viewer.Row] = '1 byte, unsigned' then
      datatype := 1;
    if value_viewer.Cells[0, value_viewer.Row] = '2 bytes, unsigned' then
      datatype := 2;
    if value_viewer.Cells[0, value_viewer.Row] = '4 bytes, unsigned' then
      datatype := 4;
    if value_viewer.Cells[0, value_viewer.Row] = 'Bitset' then
      datatype := 10;
    if value_viewer.Cells[0, value_viewer.Row] = 'Float' then
      datatype := 9;
    if value_viewer.Cells[0, value_viewer.Row] = 'Selected length' then
      Exit;
    if value_viewer.Cells[0, value_viewer.Row] = 'String' then
    begin
      if hex.SelCount > 0 then
        datatype := 10000 + hex.SelCount
      else
        datatype := 10000 + Length(value_viewer.Cells[1, value_viewer.Row]);
    end;
    objectname := '';
    Value      := GetValue(datatype, offset);
    Form_ValueEdit.MakeVarInput(objectname, offset, datatype, Value, Self);
  end;
end;



procedure TForm_BinEdit.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if (Shift = [ssCtrl]) and (Key = 83) then
    if hex.Modified then
      if not Save then
        Exit;
end;


begin
  AddToolListEntry('binedit', 'Binary .dat-Editor', '');
end.