unit Main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Buttons, StdCtrls, ComCtrls, ExtCtrls, Menus, Spin, FileCtrl;

type
  TConfigItem = class(TCollectionItem)
  private
    // General options:
    GrabMouse, NoVertMouse, NoSound, WavOnly, PrimarySound, NoMusic, HiColor: Boolean;
    PixelMultiply, Width, Height: Integer;

    // Additional options:
    SfxVolume, MusicVolume, GammaCorrection, SoundChannels, ScreenBlocks: Integer;
    ShowMessages, HighDetails: Boolean;

    // Chat macros:
    Macro0, Macro1, Macro2, Macro3, Macro4, Macro5, Macro6, Macro7, Macro8, Macro9: String;

    // Mouse options:
    UseMouse: Boolean;
    MouseSensitivity, FireButton, StrafeButton, ForwardButton: Integer;

    // Joystick options:
    UseJoystick: Boolean;
    JoyFire, JoyUse, JoyStrafe, JoySpeed: Integer;

    // Keys values:
    KeyUp, KeyDown, KeyLeft, KeyRight, StrafeLeft, StrafeRight,
    KeyFire, KeyStrafe, KeySpeed, KeyUse: Integer;

    // Config name
    Name: String;

    // Alternative WAD filename:
    Wad: String;
  public
    constructor Create(Collection: TCollection); override;
    procedure AssignTo(Dest: TPersistent); override;
  end;

  TConfigCollection = class(TCollection)
    function Add: TConfigItem;
  end;

  TNTDoomFrontEnd_Atm = class(TForm)
    Pages: TPageControl;
    MainSheet: TTabSheet;
    DoomSheet: TTabSheet;
    AboutSheet: TTabSheet;
    ConfigList: TListBox;
    AddButton: TSpeedButton;
    DelButton: TSpeedButton;
    RenameButton: TSpeedButton;
    RunButton: TSpeedButton;
    Memo1: TMemo;
    SaveButton: TSpeedButton;
    Panel1: TPanel;
    Memo2: TMemo;
    Label1: TLabel;
    Link1: TLabel;
    Label2: TLabel;
    HeaderLabel: TLabel;
    Link2: TLabel;
    Link3: TLabel;
    Link4: TLabel;
    Label5: TLabel;
    Popup: TPopupMenu;
    Add1: TMenuItem;
    Delete1: TMenuItem;
    N1: TMenuItem;
    Run1: TMenuItem;
    Rename1: TMenuItem;
    N2: TMenuItem;
    Quit1: TMenuItem;
    StatusLabel: TLabel;
    Label7: TLabel;
    Savechanges1: TMenuItem;
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    TabSheet2: TTabSheet;
    TabSheet3: TTabSheet;
    chGrabMouse: TCheckBox;
    chNoVertMouse: TCheckBox;
    chHiColor: TCheckBox;
    chNoSound: TCheckBox;
    chNoMusic: TCheckBox;
    chWavOnly: TCheckBox;
    chPrimarySound: TCheckBox;
    Label4: TLabel;
    Label6: TLabel;
    Label3: TLabel;
    ScrWidth: TSpinEdit;
    ScrHeight: TSpinEdit;
    PixMultiplier: TSpinEdit;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    Label11: TLabel;
    Label12: TLabel;
    Label13: TLabel;
    Label14: TLabel;
    Label15: TLabel;
    Label16: TLabel;
    Label17: TLabel;
    Macro9: TEdit;
    Macro0: TEdit;
    Macro1: TEdit;
    Macro2: TEdit;
    Macro3: TEdit;
    Macro4: TEdit;
    Macro5: TEdit;
    Macro6: TEdit;
    Macro7: TEdit;
    Macro8: TEdit;
    SfxVolume: TTrackBar;
    MusicVolume: TTrackBar;
    Gamma: TTrackBar;
    Label19: TLabel;
    Label20: TLabel;
    Label21: TLabel;
    HighDetails: TCheckBox;
    ShowMessages: TCheckBox;
    Label22: TLabel;
    Channels: TTrackBar;
    TabSheet4: TTabSheet;
    Label18: TLabel;
    Sensitivity: TSpinEdit;
    UseMouse: TCheckBox;
    TabSheet5: TTabSheet;
    Label23: TLabel;
    Label24: TLabel;
    UseJoystick: TCheckBox;
    FireButton: TRadioGroup;
    ForwardButton: TRadioGroup;
    StrafeButton: TRadioGroup;
    Label25: TLabel;
    ScrBlocks: TSpinEdit;
    JoyFire: TRadioGroup;
    JoyUse: TRadioGroup;
    JoyStrafe: TRadioGroup;
    JoySpeed: TRadioGroup;
    TabSheet6: TTabSheet;
    Label26: TLabel;
    Label27: TLabel;
    Label28: TLabel;
    Label29: TLabel;
    Label30: TLabel;
    Label34: TLabel;
    KeyUp: TSpinEdit;
    Label37: TLabel;
    Label38: TLabel;
    Label39: TLabel;
    Label40: TLabel;
    KeyDown: TSpinEdit;
    KeyLeft: TSpinEdit;
    KeyRight: TSpinEdit;
    KeyFire: TSpinEdit;
    KeyUse: TSpinEdit;
    KeySpeed: TSpinEdit;
    KeyStrafe: TSpinEdit;
    StrafeRight: TSpinEdit;
    StrafeLeft: TSpinEdit;
    TabSheet7: TTabSheet;
    Label31: TLabel;
    Label32: TLabel;
    Label33: TLabel;
    Wad: TFileListBox;
    FireButtonAssign: TSpeedButton;
    ForwardButtonAssign: TSpeedButton;
    StrafeButtonAssign: TSpeedButton;
    KeyUpAssign: TSpeedButton;
    KeyDownAssign: TSpeedButton;
    KeyLeftAssign: TSpeedButton;
    KeyRightAssign: TSpeedButton;
    KeyFireAssign: TSpeedButton;
    StrafeLeftAssign: TSpeedButton;
    StrafeRightAssign: TSpeedButton;
    KeyStrafeAssign: TSpeedButton;
    KeySpeedAssign: TSpeedButton;
    KeyUseAssign: TSpeedButton;
    procedure ConfigListClick(Sender: TObject);
    procedure ConfigListDblClick(Sender: TObject);
    procedure AddButtonClick(Sender: TObject);
    procedure DelButtonClick(Sender: TObject);
    procedure RenameButtonClick(Sender: TObject);
    procedure RunButtonClick(Sender: TObject);
    procedure SaveButtonClick(Sender: TObject);
    procedure ConfigListKeyPress(Sender: TObject; var Key: Char);
    procedure LinkClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure PopupPopup(Sender: TObject);
    procedure Quit1Click(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure ScrWidthChange(Sender: TObject);
    procedure chHiColorKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure FireButtonAssignMouseDown(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure ForwardButtonAssignMouseDown(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure StrafeButtonAssignMouseDown(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure KeyUpAssignClick(Sender: TObject);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
  private
    { Private declarations }
    UserName: String;
    Coll: TConfigCollection;
    LastIndex: Integer;
    FModified: Boolean;

    procedure Save;
    procedure Load;
    procedure UpdateMenus;
    procedure SetModified(Value: Boolean);
    property Modified: Boolean read FModified write SetModified;
  public
    { Public declarations }
  end;

var
  NTDoomFrontEnd_Atm: TNTDoomFrontEnd_Atm;

implementation

{$R *.DFM}

uses
  ShellApi, IniFiles, kAssign;

function TempFileName(const aExt: String): String;
var
  Buffer: array[0..1023] of Char;
  aFile : String;
begin
  GetTempPath(Sizeof(Buffer)-1,Buffer);
  GetTempFileName(Buffer,'TMP',0,Buffer);
  SetString(aFile, Buffer, StrLen(Buffer));
  Result:=ChangeFileExt(aFile,aExt);
  RenameFile(aFile,Result);
end;

function OpenURL(Handle: Integer; const sURL: String): Boolean;
var
  sTemp: String;
  fTemp: Text;
begin
  try
    sTemp := TempFileName('.URL');
    Assign(fTemp, sTemp);
    Rewrite(fTemp);
    writeln(fTemp, '[InternetShortcut]');
    writeln(fTemp, 'URL='+sURL);
    Close(fTemp);
    Result := ShellExecute(Handle, PChar('open'), PChar(sTemp), NIL, NIL, SW_SHOWNORMAL) <= 32;
  except
    Result := FALSE;
  end;
end;

function iifi(Q: Boolean; IntTrue, IntFalse: Integer): Integer;
begin
  if (Q) then Result := IntTrue else Result := IntFalse;
end;

constructor TConfigItem.Create(Collection: TCollection);
begin
  inherited Create(Collection);

  // General options:
  Width := 320;
  Height := 240;
  GrabMouse := TRUE;
  NoVertMouse := TRUE;
  PixelMultiply := 2;

  // Additional options:
  SfxVolume := 15;
  MusicVolume := 8;
  GammaCorrection := 2;
  SoundChannels := 8;
  ShowMessages := TRUE;
  HighDetails := TRUE;
  ScreenBlocks := 10;

  // Chat Macros:
  Macro0 := 'No';
  Macro1 := 'I''m ready to kick butt!';
  Macro2 := 'I''m OK.';
  Macro3 := 'I''m not looking too good!';
  Macro4 := 'Help!';
  Macro5 := 'You suck!';
  Macro6 := 'Next time, scumbag...';
  Macro7 := 'Come here!';
  Macro8 := 'I''ll take care of it.';
  Macro9 := 'Yes';

  // Mouse options:
  UseMouse := TRUE;
  MouseSensitivity := 5;
  FireButton := 0;
  StrafeButton := 1;
  ForwardButton := 2;

  // Joystick options:
  UseJoystick := FALSE;
  JoyFire := 0;
  JoyUse := 3;
  JoyStrafe := 1;
  JoySpeed := 2;

  // Keys:
  KeyUp := 173;
  KeyDown := 175;
  KeyLeft := 172;
  KeyRight := 174;
  StrafeLeft := 44;
  StrafeRight := 46;
  KeyFire := 157;
  KeyStrafe := 184;
  KeySpeed := 182;
  KeyUse := 32;

  // Config name:
  Name := 'Standard';

  // WAD:
  Wad := '';
end;

procedure TConfigItem.AssignTo(Dest: TPersistent);
begin
  if Dest is TConfigItem then
    with TConfigItem(Dest) do
    begin
      // Config name:
      Name := Self.Name;

      // General options:
      GrabMouse := Self.GrabMouse;
      NoVertMouse := Self.NoVertMouse;
      NoSound := Self.NoSound;
      WavOnly := Self.WavOnly;
      PrimarySound := Self.PrimarySound;
      NoMusic := Self.NoMusic;
      HiColor := Self.HiColor;
      PixelMultiply := Self.PixelMultiply;
      Width := Self.Width;
      Height := Self.Height;

      // Additional options:
      SfxVolume := Self.SfxVolume;
      MusicVolume := Self.MusicVolume;
      GammaCorrection := Self.GammaCorrection;
      SoundChannels := Self.SoundChannels;
      ShowMessages := Self.ShowMessages;
      HighDetails := Self.HighDetails;
      ScreenBlocks := Self.ScreenBlocks;

      // Chat Macros:
      Macro0 := Self.Macro0;
      Macro1 := Self.Macro1;
      Macro2 := Self.Macro2;
      Macro3 := Self.Macro3;
      Macro4 := Self.Macro4;
      Macro5 := Self.Macro5;
      Macro6 := Self.Macro6;
      Macro7 := Self.Macro7;
      Macro8 := Self.Macro8;
      Macro9 := Self.Macro9;

      // Mouse options:
      UseMouse := Self.UseMouse;
      MouseSensitivity := Self.MouseSensitivity;
      FireButton := Self.FireButton;
      StrafeButton := Self.StrafeButton;
      ForwardButton := Self.ForwardButton;

      // Joystick options:
      UseJoystick := Self.UseJoystick;
      JoyFire := Self.JoyFire;
      JoyUse := Self.JoyUse;
      JoyStrafe := Self.JoyStrafe;
      JoySpeed := Self.JoySpeed;

      // Keys:
      KeyUp := Self.KeyUp;
      KeyDown := Self.KeyDown;
      KeyLeft := Self.KeyLeft;
      KeyRight := Self.KeyRight;
      StrafeLeft := Self.StrafeLeft;
      StrafeRight := Self.StrafeRight;
      KeyFire := Self.KeyFire;
      KeyStrafe := Self.KeyStrafe;
      KeySpeed := Self.KeySpeed;
      KeyUse := Self.KeyUse;

      // WAD:
      Wad := Self.Wad;
    end else inherited AssignTo(Dest);
end;

function TConfigCollection.Add: TConfigItem;
begin
  Result := TConfigItem(inherited Add);
end;

procedure TNTDoomFrontEnd_Atm.SetModified(Value: Boolean);
begin
  if (FModified <> Value) then
  begin
    FModified := Value;

    if (FModified) then StatusLabel.Caption := 'Modified. Press the save button to save changes.'
    else StatusLabel.Caption := 'Ready to kick some ass (press "Run" button to start The Action) ...';
    UpdateMenus;
  end;
end;

procedure TNTDoomFrontEnd_Atm.ConfigListClick(Sender: TObject);
begin
  // nacitaj konfiguraciu oznaceneho configu z pola a zobraz ju
  UpdateMenus;
  if (ConfigList.ItemIndex <> -1) then
    with TConfigItem(Coll.Items[ConfigList.ItemIndex]) do
    begin
      // Config name:
      Self.Caption := Name + ' - NT-Doom Front-End 1.0';

      // General options:
      chGrabMouse.Checked := GrabMouse;
      chNoVertMouse.Checked := NoVertMouse;
      chNoSound.Checked := NoSound;
      chNoMusic.Checked := NoMusic;
      chWavOnly.Checked := WavOnly;
      chPrimarySound.Checked := PrimarySound;
      chHiColor.Checked := HiColor;
      ScrWidth.Value := Width;
      ScrHeight.Value := Height;
      PixMultiplier.Value := PixelMultiply;

      // Additional options:
      Self.SfxVolume.Position := SfxVolume;
      Self.MusicVolume.Position := MusicVolume;
      Self.Gamma.Position := GammaCorrection;
      Self.Channels.Position := SoundChannels;
      Self.ShowMessages.Checked := ShowMessages;
      Self.HighDetails.Checked := HighDetails;
      Self.ScrBlocks.Value := ScreenBlocks;

      // Chat Macros:
      Self.Macro0.Text := Macro0;
      Self.Macro1.Text := Macro1;
      Self.Macro2.Text := Macro2;
      Self.Macro3.Text := Macro3;
      Self.Macro4.Text := Macro4;
      Self.Macro5.Text := Macro5;
      Self.Macro6.Text := Macro6;
      Self.Macro7.Text := Macro7;
      Self.Macro8.Text := Macro8;
      Self.Macro9.Text := Macro9;

      // Mouse options:
      Self.UseMouse.Checked := UseMouse;
      Self.Sensitivity.Value := MouseSensitivity;
      Self.FireButton.ItemIndex := FireButton;
      Self.StrafeButton.ItemIndex := StrafeButton;
      Self.ForwardButton.ItemIndex := ForwardButton;

      // Joystick options:
      Self.UseJoystick.Checked := UseJoystick;
      Self.JoyFire.ItemIndex := JoyFire;
      Self.JoyUse.ItemIndex := JoyUse;
      Self.JoyStrafe.ItemIndex := JoyStrafe;
      Self.JoySpeed.ItemIndex := JoySpeed;

      // Keys:
      Self.KeyUp.Value := KeyUp;
      Self.KeyDown.Value := KeyDown;
      Self.KeyLeft.Value := KeyLeft;
      Self.KeyRight.Value := KeyRight;
      Self.StrafeLeft.Value := StrafeLeft;
      Self.StrafeRight.Value := StrafeRight;
      Self.KeyFire.Value := KeyFire;
      Self.KeyStrafe.Value := KeyStrafe;
      Self.KeySpeed.Value := KeySpeed;
      Self.KeyUse.Value := KeyUse;

      // WAD:
      try
        Self.Wad.FileName := Wad;
      except
        Wad := '';
      end;
    end;
  Modified := FALSE;
end;

procedure TNTDoomFrontEnd_Atm.ConfigListDblClick(Sender: TObject);
begin
  RunButtonClick(NIL);
end;

procedure TNTDoomFrontEnd_Atm.AddButtonClick(Sender: TObject);
var
  NewName: String;
begin
  // Add new config to ConfigList
  NewName := 'Config_'+IntToStr(Coll.Count+1);
  if (InputQuery('Type name of then new config', 'Name:', NewName)) then
  begin
    Coll.Add.Name := NewName;
    ConfigList.Items.Add(NewName);
    ConfigList.ItemIndex := ConfigList.Items.Count-1;
    ConfigListClick(NIL);
  end;
end;

procedure TNTDoomFrontEnd_Atm.DelButtonClick(Sender: TObject);
var
  i: Integer;
begin
  // Delete selected config
  UpdateMenus;
  i := ConfigList.ItemIndex;
  if (i <> -1) then
    if (MessageDlg('Do you really want to delete item "'+ConfigList.Items[i]+'" ?',
                   mtConfirmation, [mbYes, mbNo], 0) = mrYes)
    then
    begin
      ConfigList.Items.Delete(i);
      Coll.Items[i].Free;
      if (i >= ConfigList.Items.Count) then Dec(i);
      if (i >= 0) then ConfigList.ItemIndex := i;
    end;
end;

procedure TNTDoomFrontEnd_Atm.RenameButtonClick(Sender: TObject);
var
  NewName: String;
  i: Integer;
begin
  // Rename selected config
  UpdateMenus;
  i := ConfigList.ItemIndex;
  if (i <> -1) then
  begin
    NewName := ConfigList.Items[i];
    if (InputQuery('Type a NEW name', 'NEW Name:', NewName)) then
    begin
      TConfigItem(Coll.Items[i]).Name := NewName;
      ConfigList.Items[i] := NewName;
      Self.Caption := Name + ' - NT-Doom Front-End 1.0';
    end;
  end;
end;

procedure TNTDoomFrontEnd_Atm.RunButtonClick(Sender: TObject);
var
  CmdLine: String;
  Config: System.Text;
  F: File;
begin
  // Run selected config
  UpdateMenus;
  if (ConfigList.ItemIndex <> -1) then
  begin
    SaveButtonClick(NIL);

    CmdLine := '';

    with TConfigItem(Coll.Items[ConfigList.ItemIndex]) do
    begin
      if (GrabMouse) then CmdLine := CmdLine + '-grabmouse ';
      if (NoVertMouse) then CmdLine := CmdLine + '-novertmouse ';
      if (NoSound) then CmdLine := CmdLine + '-nosound ';
      if (WavOnly) then CmdLine := CmdLine + '-wavonly ';
      if (PrimarySound) then CmdLine := CmdLine + '-primarysound ';
      if (NoMusic) then CmdLine := CmdLine + '-nomusic ';
      if (HiColor) then CmdLine := CmdLine + '-hicolor ';
      if (PixelMultiply > 1) then CmdLine := CmdLine + '-'+IntToStr(PixelMultiply) + ' ';

      CmdLine := CmdLine + '-width '+IntToStr(Width) + ' -height '+IntToStr(Height);

      // now, we need to generate '.doomrc' file
      System.AssignFile(Config, '.doomrc');

      try
        System.Rewrite(Config);

        writeln(Config, 'mouse_sensitivity         ', MouseSensitivity);
        writeln(Config, 'sfx_volume                ', SfxVolume);
        writeln(Config, 'music_volume              ', MusicVolume);
        writeln(Config, 'show_messages             ', iifi(ShowMessages, 1, 0));
        writeln(Config, 'key_right                 ', KeyRight);
        writeln(Config, 'key_left                  ', KeyLeft);
        writeln(Config, 'key_up                    ', KeyUp);
        writeln(Config, 'key_down                  ', KeyDown);
        writeln(Config, 'key_strafeleft            ', StrafeLeft);
        writeln(Config, 'key_straferight           ', StrafeRight);
        writeln(Config, 'key_fire                  ', KeyFire);
        writeln(Config, 'key_use                   ', KeyUse);
        writeln(Config, 'key_strafe                ', KeyStrafe);
        writeln(Config, 'key_speed                 ', KeySpeed);
        writeln(Config, 'use_mouse                 ', iifi(UseMouse, 1, 0));
        writeln(Config, 'mouseb_fire               ', FireButton);
        writeln(Config, 'mouseb_strafe             ', StrafeButton);
        writeln(Config, 'mouseb_forward            ', ForwardButton);
        writeln(Config, 'use_joystick              ', iifi(UseJoystick, 1, 0));
        writeln(Config, 'joyb_fire                 ', JoyFire);
        writeln(Config, 'joyb_strafe               ', JoyStrafe);
        writeln(Config, 'joyb_use                  ', JoyUse);
        writeln(Config, 'joyb_speed                ', JoySpeed);
        writeln(Config, 'screenblocks              ', ScreenBlocks);
        writeln(Config, 'detaillevel               ', iifi(HighDetails, 0, 1));
        writeln(Config, 'snd_channels              ', SoundChannels);
        writeln(Config, 'usegamma                  ', GammaCorrection);
        writeln(Config, 'chatmacro0                "', Macro0, '"');
        writeln(Config, 'chatmacro1                "', Macro1, '"');
        writeln(Config, 'chatmacro2                "', Macro2, '"');
        writeln(Config, 'chatmacro3                "', Macro3, '"');
        writeln(Config, 'chatmacro4                "', Macro4, '"');
        writeln(Config, 'chatmacro5                "', Macro5, '"');
        writeln(Config, 'chatmacro6                "', Macro6, '"');
        writeln(Config, 'chatmacro7                "', Macro7, '"');
        writeln(Config, 'chatmacro8                "', Macro8, '"');
        writeln(Config, 'chatmacro9                "', Macro9, '"');

        System.Close(Config);

        if (Length(Trim(Wad)) > 0) and (FileExists(Wad)) and
           (UpperCase(ExtractFileName(Wad)) <> 'DOOM1.WAD') then
        begin
          if (FileExists('doom1.wad')) then
          begin
            AssignFile(F, 'doom1.wad');
            System.Erase(F);
          end;
          RenameFile(Wad, 'doom1.wad');
        end;

        if (FileExists('ntdoom.exe')) then
          ShellExecute(Application.Handle, 'open', 'ntdoom.exe', PChar(CmdLine), '', SW_SHOW)
        else ShowMessage('"ntDoom.exe" NOT found!'#13'Please move "ntDoomFE.exe" to the directory, where "ntDoom.exe" is!');
      except on E: Exception do
        ShowMessage('Error: '+E.Message);
      end;
    end;
  end;
end;

procedure TNTDoomFrontEnd_Atm.SaveButtonClick(Sender: TObject);
var
  i: Integer;
begin
  // Save changes to config
  UpdateMenus;
  i := ConfigList.ItemIndex;
  if (i <> -1) and (Modified) then
    with TConfigItem(Coll.Items[i]) do
    begin
      // General options:
      GrabMouse := chGrabMouse.Checked;
      NoVertMouse := chNoVertMouse.Checked;
      NoSound := chNoSound.Checked;
      NoMusic := chNoMusic.Checked;
      WavOnly := chWavOnly.Checked;
      PrimarySound := chPrimarySound.Checked;
      HiColor := chHiColor.Checked;
      Width := ScrWidth.Value;
      Height := ScrHeight.Value;
      PixelMultiply := PixMultiplier.Value;

      // Additional options:
      SfxVolume := Self.SfxVolume.Position;
      MusicVolume := Self.MusicVolume.Position;
      GammaCorrection := Self.Gamma.Position;
      SoundChannels := Self.Channels.Position;
      ShowMessages := Self.ShowMessages.Checked;
      HighDetails := Self.HighDetails.Checked;
      ScreenBlocks := Self.ScrBlocks.Value;

      // Chat Macros:
      Macro0 := Self.Macro0.Text;
      Macro1 := Self.Macro1.Text;
      Macro2 := Self.Macro2.Text;
      Macro3 := Self.Macro3.Text;
      Macro4 := Self.Macro4.Text;
      Macro5 := Self.Macro5.Text;
      Macro6 := Self.Macro6.Text;
      Macro7 := Self.Macro7.Text;
      Macro8 := Self.Macro8.Text;
      Macro9 := Self.Macro9.Text;

      // Mouse options:
      UseMouse := Self.UseMouse.Checked;
      MouseSensitivity := Self.Sensitivity.Value;
      FireButton := Self.FireButton.ItemIndex;
      StrafeButton := Self.StrafeButton.ItemIndex;
      ForwardButton := Self.ForwardButton.ItemIndex;

      // Joystick options:
      UseJoystick := Self.UseJoystick.Checked;
      JoyFire := Self.JoyFire.ItemIndex;
      JoyUse := Self.JoyUse.ItemIndex;
      JoyStrafe := Self.JoyStrafe.ItemIndex;
      JoySpeed := Self.JoySpeed.ItemIndex;

      // Keys:
      KeyUp := Self.KeyUp.Value;
      KeyDown := Self.KeyDown.Value;
      KeyLeft := Self.KeyLeft.Value;
      KeyRight := Self.KeyRight.Value;
      StrafeLeft := Self.StrafeLeft.Value;
      StrafeRight := Self.StrafeRight.Value;
      KeyFire := Self.KeyFire.Value;
      KeyStrafe := Self.KeyStrafe.Value;
      KeySpeed := Self.KeySpeed.Value;
      KeyUse := Self.KeyUse.Value;

      // WAD:
      try
        Wad := Self.Wad.FileName;
      except
        Wad := '';
      end;

      Modified := FALSE;
    end;
end;

procedure TNTDoomFrontEnd_Atm.ConfigListKeyPress(Sender: TObject; var Key: Char);
begin
  ConfigListClick(NIL);
end;

procedure TNTDoomFrontEnd_Atm.LinkClick(Sender: TObject);
begin
  // spusta jednotlive URL:
  if (Sender = Link1) or (Sender = Link2) or (Sender = Link3) or (Sender = Link4) then
    OpenURL(Application.Handle, TLabel(Sender).Hint);
end;

procedure TNTDoomFrontEnd_Atm.FormCreate(Sender: TObject);
var
  UserNameBuffer: array[0..255] of Char;
  UserNameSize: Integer;
  i: Integer;
begin
  if (Windows.GetUserName(UserNameBuffer, UserNameSize)) then
  begin
    SetLength(UserName, UserNameSize-1);
    System.Move(UserNameBuffer, UserName[1], UserNameSize-1);
    UserName := '_' + UserName;
  end else UserName := '';

  Coll := TConfigCollection.Create(TConfigItem);
  Load;

  for i := 0 to Coll.Count - 1 do
    ConfigList.Items.Add(TConfigItem(Coll.Items[i]).Name);

  FModified := FALSE;
  UpdateMenus;
  if (LastIndex < 0) and (LastIndex >= Coll.Count) then LastIndex := 0;
  if (Coll.Count > 0) then
  begin
    ConfigList.ItemIndex := LastIndex;
    ConfigListClick(NIL);
  end;
end;

procedure TNTDoomFrontEnd_Atm.Load;
var
  Ini: TIniFile;
  Str, cExeName, cIniName: String;
  i, Count: Integer;
const
  EXE_EXTENSION = 'exe';
  INI_EXTENSION = 'ini';
begin
  try
    cExeName := ParamStr(0);
    cIniName := ExtractFilePath(cExeName) +
                System.Copy(ExtractFileName(cExeName), 1,
                  Length(ExtractFileName(cExeName)) - Length(EXE_EXTENSION)) +
                INI_EXTENSION;

    Ini := TIniFile.Create(cIniName);

    Str := Ini.ReadString('Description', 'App', '');
    if (Str <> 'NT-Doom Runner') then Coll.Add
    else
    begin
      Count := Ini.ReadInteger('Configs'+UserName, 'Count', -1);
      LastIndex := Ini.ReadInteger('Configs'+UserName, 'LastIndex', 0);

      if (Count < 1) then Coll.Add
      else
        for i := 0 to Count - 1 do
          with Coll.Add do
          begin
            // Config name:
            Name           := Ini.ReadString('Configs'+UserName, 'Name_'+IntToStr(i), 'New ('+IntToStr(i)+')');

            // General options:
            GrabMouse      := Ini.ReadInteger('Configs'+UserName, 'GrabMouse_'+IntToStr(i), 1) = 1;
            NoVertMouse    := Ini.ReadInteger('Configs'+UserName, 'NoVertMouse_'+IntToStr(i), 1) = 1;
            NoSound        := Ini.ReadInteger('Configs'+UserName, 'NoSound_'+IntToStr(i), 1) = 1;
            WavOnly        := Ini.ReadInteger('Configs'+UserName, 'WavOnly_'+IntToStr(i), 1) = 1;
            PrimarySound   := Ini.ReadInteger('Configs'+UserName, 'PrimarySound_'+IntToStr(i), 1) = 1;
            NoMusic        := Ini.ReadInteger('Configs'+UserName, 'NoMusic_'+IntToStr(i), 1) = 1;
            HiColor        := Ini.ReadInteger('Configs'+UserName, 'HiColor_'+IntToStr(i), 1) = 1;
            PixelMultiply  := Ini.ReadInteger('Configs'+UserName, 'PixelMultiply_'+IntToStr(i), 2);
            Width          := Ini.ReadInteger('Configs'+UserName, 'Width_'+IntToStr(i), 320);
            Height         := Ini.ReadInteger('Configs'+UserName, 'Height_'+IntToStr(i), 240);

            // Additional options:
            SfxVolume := Ini.ReadInteger('Configs'+UserName, 'SfxVolume_'+IntToStr(i), 15);
            MusicVolume := Ini.ReadInteger('Configs'+UserName, 'MusicVolume_'+IntToStr(i), 8);
            GammaCorrection := Ini.ReadInteger('Configs'+UserName, 'GammaCorrection_'+IntToStr(i), 2);
            SoundChannels := Ini.ReadInteger('Configs'+UserName, 'SoundChannels_'+IntToStr(i), 8);
            ShowMessages := Ini.ReadInteger('Configs'+UserName, 'ShowMessages_'+IntToStr(i), 1) = 1;
            HighDetails := Ini.ReadInteger('Configs'+UserName, 'HighDetails_'+IntToStr(i), 1) = 1;
            ScreenBlocks := Ini.ReadInteger('Configs'+UserName, 'ScreenBlocks_'+IntToStr(i), 10);

            // Chat Macros:
            Macro0 := Ini.ReadString('Configs'+UserName, 'Macro0_'+IntToStr(i), 'No');
            Macro1 := Ini.ReadString('Configs'+UserName, 'Macro1_'+IntToStr(i), 'I''m ready to kick butt!');
            Macro2 := Ini.ReadString('Configs'+UserName, 'Macro2_'+IntToStr(i), 'I''m OK.');
            Macro3 := Ini.ReadString('Configs'+UserName, 'Macro3_'+IntToStr(i), 'I''m not looking too good!');
            Macro4 := Ini.ReadString('Configs'+UserName, 'Macro4_'+IntToStr(i), 'Help!');
            Macro5 := Ini.ReadString('Configs'+UserName, 'Macro5_'+IntToStr(i), 'You suck!');
            Macro6 := Ini.ReadString('Configs'+UserName, 'Macro6_'+IntToStr(i), 'Next time, scumbag...');
            Macro7 := Ini.ReadString('Configs'+UserName, 'Macro7_'+IntToStr(i), 'Come here!');
            Macro8 := Ini.ReadString('Configs'+UserName, 'Macro8_'+IntToStr(i), 'I''l take care of it.');
            Macro9 := Ini.ReadString('Configs'+UserName, 'Macro9_'+IntToStr(i), 'Yes');

            // Mouse options:
            UseMouse := Ini.ReadInteger('Configs'+UserName, 'UseMouse_'+IntToStr(i), 1) = 1;
            MouseSensitivity := Ini.ReadInteger('Configs'+UserName, 'MouseSensitivity_'+IntToStr(i), 5);
            FireButton := Ini.ReadInteger('Configs'+UserName, 'FireButton_'+IntToStr(i), 0);
            StrafeButton := Ini.ReadInteger('Configs'+UserName, 'StrafeButton_'+IntToStr(i), 1);
            ForwardButton := Ini.ReadInteger('Configs'+UserName, 'ForwardButton_'+IntToStr(i), 2);

            // Joystick options:
            UseJoystick := Ini.ReadInteger('Configs'+UserName, 'UseJoystick_'+IntToStr(i), 0) = 1;
            JoyFire := Ini.ReadInteger('Configs'+UserName, 'JoyFire_'+IntToStr(i), 0);
            JoyUse := Ini.ReadInteger('Configs'+UserName, 'JoyUse_'+IntToStr(i), 3);
            JoyStrafe := Ini.ReadInteger('Configs'+UserName, 'JoyStrafe_'+IntToStr(i), 1);
            JoySpeed := Ini.ReadInteger('Configs'+UserName, 'JoySpeed_'+IntToStr(i), 2);

            // Keys:
            KeyUp := Ini.ReadInteger('Configs'+UserName, 'KeyUp_'+IntToStr(i), 173);
            KeyDown := Ini.ReadInteger('Configs'+UserName, 'KeyDown_'+IntToStr(i), 176);
            KeyLeft := Ini.ReadInteger('Configs'+UserName, 'KeyLeft_'+IntToStr(i), 44);
            KeyRight := Ini.ReadInteger('Configs'+UserName, 'KeyRight_'+IntToStr(i), 46);
            StrafeLeft := Ini.ReadInteger('Configs'+UserName, 'StrafeLeft_'+IntToStr(i), 172);
            StrafeRight := Ini.ReadInteger('Configs'+UserName, 'StrafeRight_'+IntToStr(i), 174);
            KeyFire := Ini.ReadInteger('Configs'+UserName, 'KeyFire_'+IntToStr(i), 157);
            KeyStrafe := Ini.ReadInteger('Configs'+UserName, 'KeyStrafe_'+IntToStr(i), 184);
            KeySpeed := Ini.ReadInteger('Configs'+UserName, 'KeySpeed_'+IntToStr(i), 182);
            KeyUse := Ini.ReadInteger('Configs'+UserName, 'KeyUse_'+IntToStr(i), 32);

            // WAD:
            Wad := Ini.ReadString('Configs'+UserName, 'Wad_'+IntToStr(i), '');
          end;
    end;

    Ini.Free;
  except on E: Exception do
    MessageDlg('Error by reading .INI file!'#13 + '"' + cIniName + '"'#13 +
               E.Message,mtError,[mbOk],0);
  end;
end;

procedure TNTDoomFrontEnd_Atm.Save;
var
  Ini: TIniFile;
  cExeName, cIniName: String;
  i: Integer;
const
  EXE_EXTENSION = 'exe';
  INI_EXTENSION = 'ini';
begin
  try
    cExeName := ParamStr(0);
    cIniName := ExtractFilePath(cExeName) +
                System.Copy(ExtractFileName(cExeName), 1,
                  Length(ExtractFileName(cExeName)) - Length(EXE_EXTENSION)) +
                INI_EXTENSION;

    Ini := TIniFile.Create(cIniName);

    Ini.WriteString('Description', 'App', 'NT-Doom Runner');
    Ini.WriteInteger('Configs'+UserName, 'Count', Coll.Count);
    Ini.WriteInteger('Configs'+UserName, 'LastIndex', ConfigList.ItemIndex);

    for i := 0 to Coll.Count-1 do
      with TConfigItem(Coll.Items[i]) do
      begin
        // Config name:
        Ini.WriteString('Configs'+UserName, 'Name_'+IntToStr(i), Name);

        // General options:
        Ini.WriteInteger('Configs'+UserName, 'GrabMouse_'+IntToStr(i), iifi(GrabMouse, 1, 0));
        Ini.WriteInteger('Configs'+UserName, 'NoVertMouse_'+IntToStr(i), iifi(NoVertMouse, 1, 0));
        Ini.WriteInteger('Configs'+UserName, 'NoSound_'+IntToStr(i), iifi(NoSound, 1, 0));
        Ini.WriteInteger('Configs'+UserName, 'WavOnly_'+IntToStr(i), iifi(WavOnly, 1, 0));
        Ini.WriteInteger('Configs'+UserName, 'PrimarySound_'+IntToStr(i), iifi(PrimarySound, 1, 0));
        Ini.WriteInteger('Configs'+UserName, 'NoMusic_'+IntToStr(i), iifi(NoMusic, 1, 0));
        Ini.WriteInteger('Configs'+UserName, 'HiColor_'+IntToStr(i), iifi(HiColor, 1, 0));
        Ini.WriteInteger('Configs'+UserName, 'PixelMultiply_'+IntToStr(i), PixelMultiply);
        Ini.WriteInteger('Configs'+UserName, 'Width_'+IntToStr(i), Width);
        Ini.WriteInteger('Configs'+UserName, 'Height_'+IntToStr(i), Height);

        // Additional options:
        Ini.WriteInteger('Configs'+UserName, 'SfxVolume_'+IntToStr(i), SfxVolume);
        Ini.WriteInteger('Configs'+UserName, 'MusicVolume_'+IntToStr(i), MusicVolume);
        Ini.WriteInteger('Configs'+UserName, 'GammaCorrection_'+IntToStr(i), GammaCorrection);
        Ini.WriteInteger('Configs'+UserName, 'SoundChannels_'+IntToStr(i), SoundChannels);
        Ini.WriteInteger('Configs'+UserName, 'ShowMessages_'+IntToStr(i), iifi(ShowMessages, 1, 0));
        Ini.WriteInteger('Configs'+UserName, 'HighDetails_'+IntToStr(i), iifi(HighDetails, 1, 0));
        Ini.WriteInteger('Configs'+UserName, 'ScreenBlocks_'+IntToStr(i), ScreenBlocks);

        // Chat Macros:
        Ini.WriteString('Configs'+UserName, 'Macro0_'+IntToStr(i), Macro0);
        Ini.WriteString('Configs'+UserName, 'Macro1_'+IntToStr(i), Macro1);
        Ini.WriteString('Configs'+UserName, 'Macro2_'+IntToStr(i), Macro2);
        Ini.WriteString('Configs'+UserName, 'Macro3_'+IntToStr(i), Macro3);
        Ini.WriteString('Configs'+UserName, 'Macro4_'+IntToStr(i), Macro4);
        Ini.WriteString('Configs'+UserName, 'Macro5_'+IntToStr(i), Macro5);
        Ini.WriteString('Configs'+UserName, 'Macro6_'+IntToStr(i), Macro6);
        Ini.WriteString('Configs'+UserName, 'Macro7_'+IntToStr(i), Macro7);
        Ini.WriteString('Configs'+UserName, 'Macro8_'+IntToStr(i), Macro8);
        Ini.WriteString('Configs'+UserName, 'Macro9_'+IntToStr(i), Macro9);

        // Mouse options:
        Ini.WriteInteger('Configs'+UserName, 'UseMouse_'+IntToStr(i), iifi(UseMouse, 1, 0));
        Ini.WriteInteger('Configs'+UserName, 'MouseSensitivity_'+IntToStr(i), MouseSensitivity);
        Ini.WriteInteger('Configs'+UserName, 'FireButton_'+IntToStr(i), FireButton);
        Ini.WriteInteger('Configs'+UserName, 'StrafeButton_'+IntToStr(i), StrafeButton);
        Ini.WriteInteger('Configs'+UserName, 'ForwardButton_'+IntToStr(i), ForwardButton);

        // Joystick options:
        Ini.WriteInteger('Configs'+UserName, 'UseJoystick_'+IntToStr(i), iifi(UseJoystick, 1, 0));
        Ini.WriteInteger('Configs'+UserName, 'JoyFire_'+IntToStr(i), JoyFire);
        Ini.WriteInteger('Configs'+UserName, 'JoyUse_'+IntToStr(i), JoyUse);
        Ini.WriteInteger('Configs'+UserName, 'JoyStrafe_'+IntToStr(i), JoyStrafe);
        Ini.WriteInteger('Configs'+UserName, 'JoySpeed_'+IntToStr(i), JoySpeed);

        // Keys:
        Ini.WriteInteger('Configs'+UserName, 'KeyUp_'+IntToStr(i), KeyUp);
        Ini.WriteInteger('Configs'+UserName, 'KeyDown_'+IntToStr(i), KeyDown);
        Ini.WriteInteger('Configs'+UserName, 'KeyLeft_'+IntToStr(i), KeyLeft);
        Ini.WriteInteger('Configs'+UserName, 'KeyRight_'+IntToStr(i), KeyRight);
        Ini.WriteInteger('Configs'+UserName, 'StrafeLeft_'+IntToStr(i), StrafeLeft);
        Ini.WriteInteger('Configs'+UserName, 'StrafeRight_'+IntToStr(i), StrafeRight);
        Ini.WriteInteger('Configs'+UserName, 'KeyFire_'+IntToStr(i), KeyFire);
        Ini.WriteInteger('Configs'+UserName, 'KeyStrafe_'+IntToStr(i), KeyStrafe);
        Ini.WriteInteger('Configs'+UserName, 'KeySpeed_'+IntToStr(i), KeySpeed);
        Ini.WriteInteger('Configs'+UserName, 'KeyUse_'+IntToStr(i), KeyUse);

        // WAD:
        Ini.WriteString('Configs'+UserName, 'Wad_'+IntToStr(i), Wad);
      end;

    Ini.Free;
  except on E: Exception do
    messageDlg('Error by writing .INI file!'#13 + '"' + cIniName + '"'#13 +
               E.Message,mtError,[mbOk],0);
  end;
end;

procedure TNTDoomFrontEnd_Atm.FormDestroy(Sender: TObject);
begin
  Save;
  Coll.Free;
end;

procedure TNTDoomFrontEnd_Atm.UpdateMenus;
begin
  Delete1.Enabled := ConfigList.ItemIndex <> -1;
  Rename1.Enabled := Delete1.Enabled;
  Run1.Enabled := Delete1.Enabled;

  DelButton.Enabled := Delete1.Enabled;
  RenameButton.Enabled := Rename1.Enabled;
  RunButton.Enabled := Run1.Enabled;

  SaveButton.Enabled := Modified;
  SaveChanges1.Enabled := SaveButton.Enabled;
end;

procedure TNTDoomFrontEnd_Atm.PopupPopup(Sender: TObject);
begin
  // prekontroluj platnost poloziek popup-menu
  UpdateMenus;
end;

procedure TNTDoomFrontEnd_Atm.Quit1Click(Sender: TObject);
begin
  Self.Close;
end;

procedure TNTDoomFrontEnd_Atm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose := TRUE;
end;

procedure TNTDoomFrontEnd_Atm.ScrWidthChange(Sender: TObject);
begin
  Modified := TRUE;
end;

procedure TNTDoomFrontEnd_Atm.chHiColorKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  ScrWidthChange(NIL);
end;

procedure TNTDoomFrontEnd_Atm.FireButtonAssignMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  case (Button) of
    mbLeft: FireButton.ItemIndex := 0;
    mbRight: FireButton.ItemIndex := 1;
    mbMiddle: FireButton.ItemIndex := 2;
  end;
end;

procedure TNTDoomFrontEnd_Atm.ForwardButtonAssignMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  case (Button) of
    mbLeft: ForwardButton.ItemIndex := 0;
    mbRight: ForwardButton.ItemIndex := 1;
    mbMiddle: ForwardButton.ItemIndex := 2;
  end;
end;

procedure TNTDoomFrontEnd_Atm.StrafeButtonAssignMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  case (Button) of
    mbLeft: StrafeButton.ItemIndex := 0;
    mbRight: StrafeButton.ItemIndex := 1;
    mbMiddle: StrafeButton.ItemIndex := 2;
  end;
end;

procedure TNTDoomFrontEnd_Atm.KeyUpAssignClick(Sender: TObject);
var
  Value: Integer;
begin
  Value := GetKeyCode;

  if (Value <> -1) then
  begin
    if (Sender = KeyUpAssign) then KeyUp.Value := Value
    else if (Sender = KeyDownAssign) then KeyDown.Value := Value
    else if (Sender = KeyLeftAssign) then KeyLeft.Value := Value
    else if (Sender = KeyRightAssign) then KeyRight.Value := Value
    else if (Sender = KeyUseAssign) then KeyUse.Value := Value
    else if (Sender = KeyFireAssign) then KeyFire.Value := Value
    else if (Sender = KeyStrafeAssign) then KeyStrafe.Value := Value
    else if (Sender = KeySpeedAssign) then KeySpeed.Value := Value
    else if (Sender = StrafeLeftAssign) then StrafeLeft.Value := Value
    else if (Sender = StrafeRightAssign) then StrafeRight.Value := Value;
  end;
end;

procedure TNTDoomFrontEnd_Atm.FormKeyPress(Sender: TObject; var Key: Char);
begin
  if (Key = Chr(27)) then Application.Terminate;

  if (Key = Chr(13)) then RunButtonClick(NIL);
end;

end.
