DEADSOFTWARE

Options: update third masterserv.
[d2df-sdl.git] / src / game / g_options.pas
index 7b2a13c4050c3f25bcf271bead8596e25f62a43e..d154de12aeae754b74799e77ed84b0ea8181e862 100644 (file)
@@ -2,8 +2,7 @@
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation, version 3 of the License ONLY.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -19,232 +18,199 @@ unit g_options;
 interface
 
 uses
-  g_language, g_weapons;
+  g_language, g_weapons, utils;
 
-type
-  TPlayerControl = record
-    KeyRight:      Word;
-    KeyLeft:       Word;
-    KeyUp:         Word;
-    KeyDown:       Word;
-    KeyFire:       Word;
-    KeyJump:       Word;
-    KeyNextWeapon: Word;
-    KeyPrevWeapon: Word;
-    KeyOpen:       Word;
-    KeyStrafe:     Word;
-    KeyWeapon:     array [WP_FIRST..WP_LAST] of Word;
+function GenPlayerName (n: Integer): String;
 
-    KeyRight2:      Word;
-    KeyLeft2:       Word;
-    KeyUp2:         Word;
-    KeyDown2:       Word;
-    KeyFire2:       Word;
-    KeyJump2:       Word;
-    KeyNextWeapon2: Word;
-    KeyPrevWeapon2: Word;
-    KeyOpen2:       Word;
-    KeyStrafe2:     Word;
-    KeyWeapon2:     array [WP_FIRST..WP_LAST] of Word;
-  end;
-
-  TGameControls = record
-    TakeScreenshot: Word;
-    Stat:           Word;
-    Chat:           Word;
-    TeamChat:       Word;
-  end;
-
-  TControls = record
-    GameControls: TGameControls;
-    P1Control:    TPlayerControl;
-    P2Control:    TPlayerControl;
-  end;
-
-procedure g_Options_SetDefault();
-procedure g_Options_Read(FileName: String);
-procedure g_Options_Write(FileName: String);
-procedure g_Options_Write_Language(FileName: String);
-procedure g_Options_Write_Video(FileName: String);
-procedure g_Options_Write_Gameplay_Custom(FileName: String);
-procedure g_Options_Write_Gameplay_Net(FileName: String);
-procedure g_Options_Write_Net_Server(FileName: String);
-procedure g_Options_Write_Net_Client(FileName: String);
+procedure g_Options_SetDefault;
+procedure g_Options_SetDefaultVideo;
+procedure g_Options_ApplyGameSettings;
 
 const DF_Default_Megawad_Start = 'megawads/DOOM2D.WAD:\MAP01';
 
 var
-  gGameControls: TControls;
-  gScreenWidth: Word          = 800;
-  gScreenHeight: Word         = 600;
-  gWinRealPosX: Integer       = 0;
-  gWinRealPosY: Integer       = 0;
-  gBPP: Byte                  = 32;
-  gFreq: Byte                 = 0;
-  gFullscreen: Boolean        = False;
-  gWinMaximized: Boolean      = False;
-  gVSync: Boolean             = False;
-  glLegacyNPOT: Boolean       = False;
-  gTextureFilter: Boolean     = True;
-  gNoSound: Boolean           = False;
-  gSoundLevel: Byte           = 75;
-  gMusicLevel: Byte           = 65;
-  gMaxSimSounds: Byte         = 8;
-  gMuteWhenInactive: Boolean  = False;
-  gAdvCorpses: Boolean        = True;
-  gAdvBlood: Boolean          = True;
-  gAdvGibs: Boolean           = True;
-  gGibsCount: Integer         = 32;
-  gBloodCount: Byte           = 3;
-  gFlash: Byte                = 1;
-  gDrawBackGround: Boolean    = True;
-  gShowMessages: Boolean      = True;
-  gRevertPlayers: Boolean     = False;
-  gLanguage: String           = LANGUAGE_ENGLISH;
-  gAskLanguage: Boolean       = True;
-  gcMap: String               = '';
-  gcGameMode: String          = '';
-  gcTimeLimit: Word           = 0;
-  gcGoalLimit: Word           = 0;
-  gcMaxLives: Byte            = 0;
-  gcPlayers: Byte             = 1;
-  gcTeamDamage: Boolean       = False;
-  gcAllowExit: Boolean        = True;
-  gcWeaponStay: Boolean       = False;
-  gcMonsters: Boolean         = False;
-  gcBotsVS: String            = 'Everybody';
-  gnMap: String               = '';
-  gnGameMode: String          = '';
-  gnTimeLimit: Word           = 0;
-  gnGoalLimit: Word           = 0;
-  gnMaxLives: Byte            = 0;
-  gnPlayers: Byte             = 1;
-  gnTeamDamage: Boolean       = False;
-  gnAllowExit: Boolean        = True;
-  gnWeaponStay: Boolean       = False;
-  gnMonsters: Boolean         = False;
-  gnBotsVS: String            = 'Everybody';
-  gsSDLSampleRate: Integer    = 44100;
-  gsSDLBufferSize: Integer    = 2048;
-  gSFSDebug: Boolean          = False;
-  gSFSFastMode: Boolean       = False;
-  gDefaultMegawadStart: AnsiString = DF_Default_Megawad_Start;
-  gBerserkAutoswitch: Boolean = True;
+  gBPP: Integer;
+  gFreq: Byte;
+  gFullscreen: Boolean;
+  gWinSizeX, gWinSizeY: Integer;
+  gWinMaximized: Boolean;
+  gVSync: Boolean;
+  glLegacyNPOT: Boolean;
+  glRenderToFBO: Boolean = True;
+  gTextureFilter: Boolean;
+  gLerpActors: Boolean = True;
+  gFrameTime: Integer = 5;
+  gMaxFPS: Integer = 200;
+  gNoSound: Boolean;
+  gSoundLevel: Integer;
+  gMusicLevel: Integer;
+  gMaxSimSounds: Integer;
+  gMuteWhenInactive: Boolean;
+  gAdvCorpses: Boolean;
+  gAdvBlood: Boolean;
+  gAdvGibs: Boolean;
+  gGibsCount: Integer;
+  gBloodCount: Integer;
+  gFlash: Integer;
+  gDrawBackGround: Boolean;
+  gShowMessages: Boolean;
+  gRevertPlayers: Boolean;
+  gLanguage: String;
+  gAskLanguage: Boolean;
+  gSaveStats: Boolean = False;
+  gScreenshotStats: Boolean = False;
+  gsSDLSampleRate: Integer;
+  gsSDLBufferSize: Integer;
+  gDefaultMegawadStart: AnsiString;
+  glNPOTOverride: Boolean = false;
+
+  (* Latched game settings *)
+  gsMap: String;
+  gsGameMode: String;
+  gsTimeLimit: Word;
+  gsGoalLimit: Word;
+  gsMaxLives: Byte;
+  gsPlayers: Byte;
+  gsGameFlags: LongWord;
+  gsSpawnInvul: Integer = 0;
+  gsItemRespawnTime: Word = 60;
+  gsWarmupTime: Word = 30;
 
 implementation
 
 uses
-{$IFDEF USE_NANOGL}
-  nanoGL,
+  {$INCLUDE ../nogl/noGLuses.inc}
+  {$IFDEF USE_SDL2}
+    SDL2,
+  {$ENDIF}
+  e_log, e_input, g_console, g_window, g_sound, g_gfx, g_player, Math,
+  g_map, g_net, g_netmaster, SysUtils, CONFIG, g_game, g_main, e_texture,
+  g_items, wadreader, e_graphics, g_touch, envvars, g_system;
+
+  var
+    machine: Integer;
+
+  function GenPlayerName (n: Integer): String;
+  begin
+    ASSERT(n >= 1);
+    Result := GetUserName;
+    if Result = '' then
+      Result := 'Player' + IntToStr(machine MOD 10000);
+    if n = 1 then
+      Result := Copy(Result, 1, 12) + ' '
+    else
+      Result := Copy(Result, 1, 10) + ' ' + IntToStr(n)
+  end;
+
+{$IFDEF USE_SDL2}
+procedure g_Options_SetDefaultVideo;
+  var display: TSDL_DisplayMode;
+  {$IFNDEF ANDROID}
+    var target, closest: TSDL_DisplayMode; percentage: Integer;
+  {$ENDIF}
+begin
+  (* Display 0 = Primary display *)
+  gScreenWidth := 640;
+  gScreenHeight := 480;
+  gWinSizeX := 640;
+  gWinSizeY := 480;
+  //gBPP := SDL_BITSPERPIXEL(dispaly.format);
+  gBPP := 32;
+  {$IFDEF ANDROID}
+    gFullScreen := True; (* rotation not allowed? *)
+  {$ELSE}
+    gFullScreen := False;
+  {$ENDIF}
+  if SDL_GetDesktopDisplayMode(0, @display) = 0 then
+  begin
+  {$IFDEF ANDROID}
+    gWinSizeX := display.w;
+    gWinSizeY := display.h;
+  {$ELSE}
+    (* Window must be smaller than display *)
+    closest.w := display.w;
+    closest.h := display.h;
+    percentage := 75;
+    while (display.w - closest.w < 48) or (display.h - closest.h < 48) do
+    begin
+      if percentage < 25 then
+      begin
+        closest.w := display.w * 75 div 100;
+        closest.h := display.h * 75 div 100;
+        break;
+      end;
+      target.w := display.w * percentage div 100;
+      target.h := display.h * percentage div 100;
+      target.format := 0; (* didn't care *)
+      target.refresh_rate := 0; (* didn't care *)
+      target.driverdata := nil; (* init *)
+      SDL_GetClosestDisplayMode(0, @target, @closest);
+      Dec(percentage);
+    end;
+    gWinSizeX := closest.w;
+    gWinSizeY := closest.h;
+    //gBPP := SDL_BITSPERPIXEL(closest.format); (* Resolution list didn't work for some reason *)
+  {$ENDIF}
+  end
+  else
+  begin
+    e_LogWritefln('SDL: Failed to get desktop display mode: %s', [SDL_GetError])
+  end;
+  (* Must be positioned on primary display *)
+  gWinMaximized := False;
+  gVSync := True;
+  gTextureFilter := True;
+  glLegacyNPOT := False;
+  gRC_Width := gWinSizeX;
+  gRC_Height := gWinSizeY;
+  gRC_FullScreen := gFullScreen;
+  gRC_Maximized := gWinMaximized;
+  e_LogWriteLn('g_Options_SetDefaultVideo: w = ' + IntToStr(gWinSizeX) + ' h = ' + IntToStr(gWinSizeY));
+  g_Console_ResetBinds;
+end;
 {$ELSE}
-  GL, GLExt,
+procedure g_Options_SetDefaultVideo;
+begin
+  gWinSizeX := 640;
+  gWinSizeY := 480;
+  gBPP := 32;
+  gFullScreen := False;
+  gWinMaximized := False;
+  gVSync := True;
+  gTextureFilter := True;
+  glLegacyNPOT := False;
+  gScreenWidth := gWinSizeX;
+  gScreenHeight := gWinSizeY;
+  gRC_Width := gWinSizeX;
+  gRC_Height := gWinSizeY;
+  gRC_FullScreen := gFullScreen;
+  gRC_Maximized := gWinMaximized;
+  e_LogWriteLn('g_Options_SetDefaultVideo: w = ' + IntToStr(gWinSizeX) + ' h = ' + IntToStr(gWinSizeY));
+  g_Console_ResetBinds;
+end;
 {$ENDIF}
-  e_log, e_input, g_window, g_sound, g_gfx, g_player, Math,
-  g_map, g_net, g_netmaster, SysUtils, CONFIG, g_game, g_main, e_texture,
-  g_items, wadreader, e_graphics, g_touch;
 
 procedure g_Options_SetDefault();
 var
   i: Integer;
 begin
-  g_Sound_SetupAllVolumes(75, 65);
+  (* section Sound *)
+  gNoSound := False;
+  gSoundLevel := 75;
+  gMusicLevel := 65;
   gMaxSimSounds := 8;
   gMuteWhenInactive := False;
   gAnnouncer := ANNOUNCE_MEPLUS;
   gSoundEffectsDF := True;
   gUseChatSounds := True;
-  g_GFX_SetMax(2000);
-  g_Gibs_SetMax(150);
-  g_Corpses_SetMax(20);
-  g_Shells_SetMax(300);
-  gGibsCount := 32;
-  gBloodCount := 3;
-  gAdvBlood := True;
-  gAdvCorpses := True;
-  gAdvGibs := True;
-  gFlash := 1;
-  gDrawBackGround := True;
-  gShowMessages := True;
-  gRevertPlayers := False;
-  g_dbg_scale := 1.0;
-  g_touch_size := 1.0;
-  g_touch_fire := True;
-
-  for i := 0 to e_MaxJoys-1 do
-    e_JoystickDeadzones[i] := 8192;
-
-  with gGameControls.GameControls do
-  begin
-    TakeScreenshot := 183;
-    Stat := 15;
-    Chat := 20; // [T]
-    TeamChat := 21; // [Y]
-  end;
-
-  with gGameControls.P1Control do
-  begin
-    KeyRight := 77;
-    KeyLeft := 75;
-    KeyUp := 72;
-    KeyDown := 76;
-    KeyFire := 184;
-    KeyJump := 157;
-    KeyNextWeapon := 73;
-    KeyPrevWeapon := 71;
-    KeyOpen := 54;
-    KeyStrafe := 0;
-    for i := 0 to 9 do
-      KeyWeapon[i] := 30 + i;
-    KeyWeapon[10] := 45;
-    for i := 10 to High(KeyWeapon) do
-      KeyWeapon[i] := 0;
-
-    KeyRight2 := VK_RIGHT;
-    KeyLeft2 := VK_LEFT;
-    KeyUp2 := VK_UP;
-    KeyDown2 := VK_DOWN;
-    KeyFire2 := VK_FIRE;
-    KeyJump2 := VK_JUMP;
-    KeyNextWeapon2 := VK_NEXT;
-    KeyPrevWeapon2 := VK_PREV;
-    KeyOpen2 := VK_OPEN;
-    KeyStrafe2 := VK_STRAFE;
-    for i := 0 to High(KeyWeapon2) do
-      KeyWeapon2[i] := VK_0 + i;
-  end;
-
-  with gGameControls.P2Control do
-  begin
-    KeyRight := 33;
-    KeyLeft := 31;
-    KeyUp := 18;
-    KeyDown := 32;
-    KeyFire := 30;
-    KeyJump := 16;
-    KeyNextWeapon := 19;
-    KeyPrevWeapon := 17;
-    KeyOpen := 58;
-    KeyStrafe := 0;
-    for i := 0 to High(KeyWeapon) do
-      KeyWeapon[i] := 0;
+  gsSDLSampleRate := 44100;
+  gsSDLBufferSize := 2048;
 
-    KeyRight2 := 0;
-    KeyLeft2 := 0;
-    KeyUp2 := 0;
-    KeyDown2 := 0;
-    KeyFire2 := 0;
-    KeyJump2 := 0;
-    KeyNextWeapon2 := 0;
-    KeyPrevWeapon2 := 0;
-    KeyOpen2 := 0;
-    KeyStrafe2 := 0;
-    for i := 0 to High(KeyWeapon2) do
-      KeyWeapon2[i] := 0;
-  end;
+  g_Sound_SetupAllVolumes(gSoundLevel, gMusicLevel);
 
   with gPlayer1Settings do
   begin
-    Name := 'Player1';
+    Name := GenPlayerName(1);
     Model := STD_PLAYER_MODEL;
     Color.R := PLAYER1_DEF_COLOR.R;
     Color.G := PLAYER1_DEF_COLOR.G;
@@ -254,7 +220,7 @@ begin
 
   with gPlayer2Settings do
   begin
-    Name := 'Player2';
+    Name := GenPlayerName(2);
     Model := STD_PLAYER_MODEL;
     Color.R := PLAYER2_DEF_COLOR.R;
     Color.G := PLAYER2_DEF_COLOR.G;
@@ -262,615 +228,143 @@ begin
     Team := TEAM_BLUE;
   end;
 
-  NetUseMaster := True;
-  NetForwardPorts := False;
-  g_Net_Slist_Set('mpms.doom2d.org', 25665);
-end;
-
-procedure g_Options_Read(FileName: String);
-var
-  config: TConfig;
-  str: String;
-  i: Integer;
-begin
-  gAskLanguage := True;
-  e_WriteLog('Reading config', TMsgType.Notify);
-
-  if not FileExists(FileName) then
-  begin
-    e_WriteLog('Config file '+FileName+' not found', TMsgType.Warning);
-    g_Options_SetDefault();
-
-  // Default video options:
-    gScreenWidth := 800;
-    gScreenHeight := 600;
-    gWinRealPosX := 0;
-    gWinRealPosY := 0;
-    gWinMaximized := False;
-    gFullScreen := False;
-    gBPP := 32;
-    gVSync := False;
-    gTextureFilter := True;
-    glLegacyNPOT := False;
-
-    Exit;
-  end;
-
-  config := TConfig.CreateFile(FileName);
-
-  gScreenWidth := config.ReadInt('Video', 'ScreenWidth', 800);
-  if gScreenWidth < 640 then
-    gScreenWidth := 640;
-  gScreenHeight := config.ReadInt('Video', 'ScreenHeight', 600);
-  if gScreenHeight < 480 then
-    gScreenHeight := 480;
-  gWinRealPosX := config.ReadInt('Video', 'WinPosX', 60);
-  if gWinRealPosX < 0 then
-    gWinRealPosX := 60;
-  gWinRealPosY := config.ReadInt('Video', 'WinPosY', 60);
-  if gWinRealPosY < 0 then
-    gWinRealPosY := 60;
-  gFullScreen := config.ReadBool('Video', 'Fullscreen', False);
-  gWinMaximized := config.ReadBool('Video', 'Maximized', False);
-  gBPP := config.ReadInt('Video', 'BPP', 32);
-  gFreq := config.ReadInt('Video', 'Freq', 0);
-  gVSync := config.ReadBool('Video', 'VSync', True);
-  gTextureFilter := config.ReadBool('Video', 'TextureFilter', True);
-  glLegacyNPOT := config.ReadBool('Video', 'LegacyCompatible', False);
-
-  gNoSound := config.ReadBool('Sound', 'NoSound', False);
-  gSoundLevel := Min(config.ReadInt('Sound', 'SoundLevel', 75), 255);
-  gMusicLevel := Min(config.ReadInt('Sound', 'MusicLevel', 65), 255);
-  gMaxSimSounds := Max(Min(config.ReadInt('Sound', 'MaxSimSounds', 8), 66), 2);
-  gMuteWhenInactive := config.ReadBool('Sound', 'MuteInactive', False);
-  gAnnouncer := Min(Max(config.ReadInt('Sound', 'Announcer', ANNOUNCE_MEPLUS), ANNOUNCE_NONE), ANNOUNCE_ALL);
-  gSoundEffectsDF := config.ReadBool('Sound', 'SoundEffectsDF', True);
-  gUseChatSounds := config.ReadBool('Sound', 'ChatSounds', True);
-  gsSDLSampleRate := Min(Max(config.ReadInt('Sound', 'SDLSampleRate', 44100), 11025), 96000);
-  gsSDLBufferSize := Min(Max(config.ReadInt('Sound', 'SDLBufferSize', 2048), 64), 16384);
-
-  with gGameControls.GameControls do
-  begin
-    TakeScreenshot := config.ReadInt('GameControls', 'TakeScreenshot', 183);
-    Stat := config.ReadInt('GameControls', 'Stat', 15);
-    Chat := config.ReadInt('GameControls', 'Chat', 20);
-    TeamChat := config.ReadInt('GameControls', 'TeamChat', 21);
-  end;
-
-  with gGameControls.P1Control, config do
-  begin
-    KeyRight := ReadInt('Player1', 'KeyRight', 33);
-    KeyLeft := ReadInt('Player1', 'KeyLeft', 31);
-    KeyUp := ReadInt('Player1', 'KeyUp', 18);
-    KeyDown := ReadInt('Player1', 'KeyDown', 32);
-    KeyFire := ReadInt('Player1', 'KeyFire', 30);
-    KeyJump := ReadInt('Player1', 'KeyJump', 16);
-    KeyNextWeapon := ReadInt('Player1', 'KeyNextWeapon', 19);
-    KeyPrevWeapon := ReadInt('Player1', 'KeyPrevWeapon', 17);
-    KeyOpen := ReadInt('Player1', 'KeyOpen', 58);
-    KeyStrafe := ReadInt('Player1', 'KeyStrafe', 0);
-    for i := 0 to High(KeyWeapon) do
-      KeyWeapon[i] := ReadInt('Player1', 'KeyWeapon' + IntToStr(i), 0);
-
-    KeyRight2 := ReadInt('Player1', 'KeyRight2', 0);
-    KeyLeft2 := ReadInt('Player1', 'KeyLeft2', 0);
-    KeyUp2 := ReadInt('Player1', 'KeyUp2', 0);
-    KeyDown2 := ReadInt('Player1', 'KeyDown2', 0);
-    KeyFire2 := ReadInt('Player1', 'KeyFire2', 0);
-    KeyJump2 := ReadInt('Player1', 'KeyJump2', 0);
-    KeyNextWeapon2 := ReadInt('Player1', 'KeyNextWeapon2', 0);
-    KeyPrevWeapon2 := ReadInt('Player1', 'KeyPrevWeapon2', 0);
-    KeyOpen2 := ReadInt('Player1', 'KeyOpen2', 0);
-    KeyStrafe2 := ReadInt('Player1', 'KeyStrafe2', 0);
-    for i := 0 to High(KeyWeapon2) do
-      KeyWeapon2[i] := ReadInt('Player1', 'KeyWeapon2' + IntToStr(i), 0);
-  end;
-
-  with gPlayer1Settings, config do
-  begin
-    Name := ReadStr('Player1', 'name', 'Player1');
-    Model := ReadStr('Player1', 'model', STD_PLAYER_MODEL);
-    Color.R := Min(Abs(ReadInt('Player1', 'red', PLAYER1_DEF_COLOR.R)), 255);
-    Color.G := Min(Abs(ReadInt('Player1', 'green', PLAYER1_DEF_COLOR.G)), 255);
-    Color.B := Min(Abs(ReadInt('Player1', 'blue', PLAYER1_DEF_COLOR.B)), 255);
-    Team := ReadInt('Player1', 'team', TEAM_RED);
-    if (Team < TEAM_RED) or (Team > TEAM_BLUE) then
-      Team := TEAM_RED;
-  end;
-
-  with gGameControls.P2Control, config do
+  (* section Joysticks *)
+  for i := 0 to e_MaxJoys - 1 do
   begin
-    KeyRight := ReadInt('Player2', 'KeyRight', 205);
-    KeyLeft := ReadInt('Player2', 'KeyLeft', 203);
-    KeyUp := ReadInt('Player2', 'KeyUp', 200);
-    KeyDown := ReadInt('Player2', 'KeyDown', 208);
-    KeyFire := ReadInt('Player2', 'KeyFire', 184);
-    KeyJump := ReadInt('Player2', 'KeyJump', 157);
-    KeyNextWeapon := ReadInt('Player2', 'KeyNextWeapon', 73);
-    KeyPrevWeapon := ReadInt('Player2', 'KeyPrevWeapon', 71);
-    KeyOpen := ReadInt('Player2', 'KeyOpen', 54);
-    KeyStrafe := ReadInt('Player2', 'KeyStrafe', 0);
-    for i := 0 to High(KeyWeapon) do
-      KeyWeapon[i] := ReadInt('Player2', 'KeyWeapon' + IntToStr(i), 0);
-
-    KeyRight2 := ReadInt('Player2', 'KeyRight2', 0);
-    KeyLeft2 := ReadInt('Player2', 'KeyLeft2', 0);
-    KeyUp2 := ReadInt('Player2', 'KeyUp2', 0);
-    KeyDown2 := ReadInt('Player2', 'KeyDown2', 0);
-    KeyFire2 := ReadInt('Player2', 'KeyFire2', 0);
-    KeyJump2 := ReadInt('Player2', 'KeyJump2', 0);
-    KeyNextWeapon2 := ReadInt('Player2', 'KeyNextWeapon2', 0);
-    KeyPrevWeapon2 := ReadInt('Player2', 'KeyPrevWeapon2', 0);
-    KeyOpen2 := ReadInt('Player2', 'KeyOpen2', 0);
-    KeyStrafe2 := ReadInt('Player2', 'KeyStrafe2', 0);
-    for i := 0 to High(KeyWeapon2) do
-      KeyWeapon2[i] := ReadInt('Player2', 'KeyWeapon2' + IntToStr(i), 0);
-  end;
-
-  with gPlayer2Settings, config do
-  begin
-    Name := ReadStr('Player2', 'name', 'Player2');
-    Model := ReadStr('Player2', 'model', STD_PLAYER_MODEL);
-    Color.R := Min(Abs(ReadInt('Player2', 'red', PLAYER2_DEF_COLOR.R)), 255);
-    Color.G := Min(Abs(ReadInt('Player2', 'green', PLAYER2_DEF_COLOR.G)), 255);
-    Color.B := Min(Abs(ReadInt('Player2', 'blue', PLAYER2_DEF_COLOR.B)), 255);
-    Team := ReadInt('Player2', 'team', TEAM_BLUE);
-    if (Team < TEAM_RED) or (Team > TEAM_BLUE) then
-      Team := TEAM_RED;
+    e_JoystickDeadzones[i] := 8192
   end;
 
-  for i := 0 to e_MaxJoys-1 do
-    e_JoystickDeadzones[i] := config.ReadInt('Joysticks', 'Deadzone' + IntToStr(i), 8192);
-
-  g_touch_size := Max(config.ReadInt('Touch', 'Size', 10) / 10, 0.1);
-  g_touch_fire := config.ReadBool('Touch', 'Fire', True);
-
-  g_GFX_SetMax(Min(config.ReadInt('Game', 'MaxParticles', 1000), 50000));
-  g_Shells_SetMax(Min(config.ReadInt('Game', 'MaxShells', 300), 600));
-  g_Gibs_SetMax(Min(config.ReadInt('Game', 'MaxGibs', 150), 500));
-  g_Corpses_SetMax(Min(config.ReadInt('Game', 'MaxCorpses', 20), 100));
-
-  case config.ReadInt('Game', 'GibsCount', 3) of
-    0: gGibsCount := 0;
-    1: gGibsCount := 8;
-    2: gGibsCount := 16;
-    3: gGibsCount := 32;
-    else gGibsCount := 48;
-  end;
+  (* section Game *)
+  g_GFX_SetMax(2000);
+  g_Shells_SetMax(300);
+  g_Gibs_SetMax(150);
+  g_Corpses_SetMax(20);
+  gGibsCount := 32;
+  gBloodCount := 4;
+  gAdvBlood := True;
+  gAdvCorpses := True;
+  gAdvGibs := True;
+  gFlash := 1;
+  gDrawBackGround := True;
+  gShowMessages := True;
+  gRevertPlayers := False;
+  gChatBubble := 4;
+  wadoptDebug := False;
+  wadoptFast := False;
+  e_FastScreenshots := True;
+  gDefaultMegawadStart := DF_Default_Megawad_Start;
+  g_dbg_scale := 1.0;
+  gSaveStats := False;
 
-  ITEM_RESPAWNTIME := 36*Max(config.ReadInt('Game', 'ItemRespawnTime', 60), 0);
-  gBloodCount := Min(config.ReadInt('Game', 'BloodCount', 4), 4);
-  gAdvBlood := config.ReadBool('Game', 'AdvancesBlood', True);
-  gAdvCorpses := config.ReadBool('Game', 'AdvancesCorpses', True);
-  gAdvGibs := config.ReadBool('Game', 'AdvancesGibs', True);
-  gFlash := Min(Max(config.ReadInt('Game', 'Flash', 1), 0), 2);
-  gDrawBackGround := config.ReadBool('Game', 'BackGround', True);
-  gShowMessages := config.ReadBool('Game', 'Messages', True);
-  gRevertPlayers := config.ReadBool('Game', 'RevertPlayers', False);
-  gChatBubble := Min(Max(config.ReadInt('Game', 'ChatBubble', 4), 0), 4);
-  gSFSDebug := config.ReadBool('Game', 'SFSDebug', False);
-  wadoptDebug := gSFSDebug;
-  gSFSFastMode := config.ReadBool('Game', 'SFSFastMode', False);
-  wadoptFast := gSFSFastMode;
-  e_FastScreenshots := config.ReadBool('Game', 'FastScreenshots', True);
-  gDefaultMegawadStart := config.ReadStr('Game', 'DefaultMegawadStart', DF_Default_Megawad_Start);
-  gBerserkAutoswitch := config.ReadBool('Game', 'BerserkAutoswitching', True);
-  g_dbg_scale := Max(config.ReadInt('Game', 'Scale', 100) / 100, 1.0);
+  gAskLanguage := True;
+  gLanguage := LANGUAGE_ENGLISH;
+
+  gsMap := '';
+  gsGameMode := _lc[I_MENU_GAME_TYPE_DM];
+  gsTimeLimit := 0;
+  gsGoalLimit := 0;
+  gsMaxLives := 0;
+  gsPlayers := 1;
+  gsSpawnInvul := 0;
+  gsItemRespawnTime := 60;
+  gsGameFlags := GAME_OPTION_ALLOWEXIT or GAME_OPTION_DMKEYS or
+    GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER or
+    GAME_OPTION_TEAMHITTRACE or GAME_OPTION_TEAMHITPROJECTILE or
+    GAME_OPTION_ALLOWDROPFLAG;
+  gsPlayers := 1;
+
+  if not gGameOn then
+    g_Options_ApplyGameSettings;
+
+  (* section MasterServer *)
+  NetMasterList := 'mpms.doom2d.org:25665, deadsoftware.ru:25665, terminalcorner.ru:25665';
+  g_Net_Slist_Set(NetMasterList);
+
+  (* section Server *)
+  NetServerName := 'Unnamed Server';
+  NetPassword := '';
+  NetPort := 25666;
+  NetMaxClients := 16;
+  NetAllowRCON := False;
+  NetRCONPassword := 'default';
+  NetUseMaster := True;
+  NetUpdateRate := 0;
+  NetRelupdRate := 18;
+  NetMasterRate := 60000;
+  NetForwardPorts := False;
 
-// Ãåéìïëåé â ñâîåé èãðå
-  gcMap := config.ReadStr('GameplayCustom', 'Map', '');
-  gcGameMode := config.ReadStr('GameplayCustom', 'GameMode', _lc[I_MENU_GAME_TYPE_DM]);
-  gcTimeLimit := Min(Max(config.ReadInt('GameplayCustom', 'TimeLimit', 0), 0), 65535);
-  gcGoalLimit := Min(Max(config.ReadInt('GameplayCustom', 'GoalLimit', 0), 0), 65535);
-  gcMaxLives := Min(Max(config.ReadInt('GameplayCustom', 'MaxLives', 0), 0), 255);
-  gcPlayers := Min(Max(config.ReadInt('GameplayCustom', 'Players', 1), 0), 2);
-  gcTeamDamage := config.ReadBool('GameplayCustom', 'TeamDamage', False);
-  gcAllowExit := config.ReadBool('GameplayCustom', 'AllowExit', True);
-  gcWeaponStay := config.ReadBool('GameplayCustom', 'WeaponStay', False);
-  gcMonsters := config.ReadBool('GameplayCustom', 'Monsters', False);
-  gcBotsVS := config.ReadStr('GameplayCustom', 'BotsVS', 'Everybody');
+  (* section Client *)
+  NetInterpLevel := 2;
+  NetForcePlayerUpdate := False;
+  NetPredictSelf := True;
+  NetClientIP := '127.0.0.1';
+  NetClientPort := NetPort;
+end;
 
+procedure g_Options_ApplyGameSettings;
+begin
   with gGameSettings do
   begin
-    GameMode := g_Game_TextToMode(gcGameMode);
+    GameMode := g_Game_TextToMode(gsGameMode);
     if GameMode = GM_NONE then
       GameMode := GM_DM;
     if GameMode = GM_SINGLE then
       GameMode := GM_COOP;
-    TimeLimit := gcTimeLimit;
-    GoalLimit := gcGoalLimit;
-    MaxLives := gcMaxLives;
-
-    Options := 0;
-    if gcTeamDamage then
-      Options := Options or GAME_OPTION_TEAMDAMAGE;
-    if gcAllowExit then
-      Options := Options or GAME_OPTION_ALLOWEXIT;
-    if gcWeaponStay then
-      Options := Options or GAME_OPTION_WEAPONSTAY;
-    if gcMonsters then
-      Options := Options or GAME_OPTION_MONSTERS;
-    if gcBotsVS = 'Everybody' then
-      Options := Options or GAME_OPTION_BOTVSPLAYER or GAME_OPTION_BOTVSMONSTER;
-    if gcBotsVS = 'Players' then
-      Options := Options or GAME_OPTION_BOTVSPLAYER;
-    if gcBotsVS = 'Monsters' then
-      Options := Options or GAME_OPTION_BOTVSMONSTER;
-  end;
-
-// Ãåéìïëåé â ñåòåâîé èãðå
-  gnMap := config.ReadStr('GameplayNetwork', 'Map', '');
-  gnGameMode := config.ReadStr('GameplayNetwork', 'GameMode', _lc[I_MENU_GAME_TYPE_DM]);
-  gnTimeLimit := Min(Max(config.ReadInt('GameplayNetwork', 'TimeLimit', 0), 0), 65535);
-  gnGoalLimit := Min(Max(config.ReadInt('GameplayNetwork', 'GoalLimit', 0), 0), 65535);
-  gnMaxLives := Min(Max(config.ReadInt('GameplayNetwork', 'MaxLives', 0), 0), 255);
-  gnPlayers := Min(Max(config.ReadInt('GameplayNetwork', 'Players', 1), 0), 2);
-  gnTeamDamage := config.ReadBool('GameplayNetwork', 'TeamDamage', False);
-  gnAllowExit := config.ReadBool('GameplayNetwork', 'AllowExit', True);
-  gnWeaponStay := config.ReadBool('GameplayNetwork', 'WeaponStay', False);
-  gnMonsters := config.ReadBool('GameplayNetwork', 'Monsters', False);
-  gnBotsVS := config.ReadStr('GameplayNetwork', 'BotsVS', 'Everybody');
-
-// Îáùèå ñåòåâûå
-  NetSlistIP := config.ReadStr('MasterServer', 'IP', 'mpms.doom2d.org');
-  NetSlistPort := config.ReadInt('MasterServer', 'Port', 25665);
-
-// Ñåðâåð
-  NetServerName := config.ReadStr('Server', 'Name', 'Unnamed Server');
-  NetPassword :=  config.ReadStr('Server', 'Password', '');
-  NetPort := Min(Max(0, config.ReadInt('Server', 'Port', 25666)), 65535);
-  NetMaxClients := Min(Max(0, config.ReadInt('Server', 'MaxClients', 16)), NET_MAXCLIENTS);
-  NetAllowRCON := config.ReadBool('Server', 'RCON', False);
-  NetRCONPassword := config.ReadStr('Server', 'RCONPassword', 'default');
-  NetUseMaster := config.ReadBool('Server', 'SyncWithMaster', True);
-  NetUpdateRate := Max(0, config.ReadInt('Server', 'UpdateInterval', 0));
-  NetRelupdRate := Max(0, config.ReadInt('Server', 'ReliableUpdateInterval', 18));
-  NetMasterRate := Max(1, config.ReadInt('Server', 'MasterSyncInterval', 60000));
-  NetForwardPorts := config.ReadBool('Server', 'ForwardPorts', False);
-
-// Êëèåíò
-  NetInterpLevel := Max(0, config.ReadInt('Client', 'InterpolationSteps', 2));
-  NetForcePlayerUpdate := config.ReadBool('Client', 'ForcePlayerUpdate', False);
-  NetPredictSelf := config.ReadBool('Client', 'PredictSelf', True);
-  NetClientIP := config.ReadStr('Client', 'LastIP', '127.0.0.1');
-  NetClientPort := Max(0, config.ReadInt('Client', 'LastPort', 25666));
-
-// ßçûê:
-  str := config.ReadStr('Game', 'Language', '');
-  if (str = LANGUAGE_RUSSIAN) or
-     (str = LANGUAGE_ENGLISH) then
-  begin
-    gLanguage := str;
-    gAskLanguage := False;
-  end
-  else
-    gLanguage := LANGUAGE_ENGLISH;
-
-  config.Free();
-
-  //if gTextureFilter then TEXTUREFILTER := GL_LINEAR else TEXTUREFILTER := GL_NEAREST;
-end;
-
-procedure g_Options_Write(FileName: String);
-var
-  config: TConfig;
-  i: Integer;
-begin
-  e_WriteLog('Writing config', TMsgType.Notify);
-
-  config := TConfig.CreateFile(FileName);
-
-  config.WriteInt('Video', 'ScreenWidth', gScreenWidth);
-  config.WriteInt('Video', 'ScreenHeight', gScreenHeight);
-  config.WriteInt('Video', 'WinPosX', gWinRealPosX);
-  config.WriteInt('Video', 'WinPosY', gWinRealPosY);
-  config.WriteBool('Video', 'Fullscreen', gFullScreen);
-  config.WriteBool('Video', 'Maximized', gWinMaximized);
-  config.WriteInt('Video', 'BPP', gBPP);
-  config.WriteBool('Video', 'VSync', gVSync);
-  config.WriteBool('Video', 'TextureFilter', gTextureFilter);
-  config.WriteBool('Video', 'LegacyCompatible', glLegacyNPOT);
-
-  config.WriteBool('Sound', 'NoSound', gNoSound);
-  config.WriteInt('Sound', 'SoundLevel', gSoundLevel);
-  config.WriteInt('Sound', 'MusicLevel', gMusicLevel);
-  config.WriteInt('Sound', 'MaxSimSounds', gMaxSimSounds);
-  config.WriteBool('Sound', 'MuteInactive', gMuteWhenInactive);
-  config.WriteInt('Sound', 'Announcer', gAnnouncer);
-  config.WriteBool('Sound', 'SoundEffectsDF', gSoundEffectsDF);
-  config.WriteBool('Sound', 'ChatSounds', gUseChatSounds);
-  config.WriteInt('Sound', 'SDLSampleRate', gsSDLSampleRate);
-  config.WriteInt('Sound', 'SDLBufferSize', gsSDLBufferSize);
-
-  with config, gGameControls.GameControls do
-  begin
-    WriteInt('GameControls', 'TakeScreenshot', TakeScreenshot);
-    WriteInt('GameControls', 'Stat', Stat);
-    WriteInt('GameControls', 'Chat', Chat);
-    WriteInt('GameControls', 'TeamChat', TeamChat);
-  end;
-
-  with config, gGameControls.P1Control, gPlayer1Settings do
-  begin
-    WriteInt('Player1', 'KeyRight', KeyRight);
-    WriteInt('Player1', 'KeyLeft', KeyLeft);
-    WriteInt('Player1', 'KeyUp', KeyUp);
-    WriteInt('Player1', 'KeyDown', KeyDown);
-    WriteInt('Player1', 'KeyFire', KeyFire);
-    WriteInt('Player1', 'KeyJump', KeyJump);
-    WriteInt('Player1', 'KeyNextWeapon', KeyNextWeapon);
-    WriteInt('Player1', 'KeyPrevWeapon', KeyPrevWeapon);
-    WriteInt('Player1', 'KeyOpen', KeyOpen);
-    WriteInt('Player1', 'KeyStrafe', KeyStrafe);
-    for i := 0 to High(KeyWeapon) do
-      WriteInt('Player1', 'KeyWeapon' + IntToStr(i), KeyWeapon[i]);
-
-    WriteInt('Player1', 'KeyRight2', KeyRight2);
-    WriteInt('Player1', 'KeyLeft2', KeyLeft2);
-    WriteInt('Player1', 'KeyUp2', KeyUp2);
-    WriteInt('Player1', 'KeyDown2', KeyDown2);
-    WriteInt('Player1', 'KeyFire2', KeyFire2);
-    WriteInt('Player1', 'KeyJump2', KeyJump2);
-    WriteInt('Player1', 'KeyNextWeapon2', KeyNextWeapon2);
-    WriteInt('Player1', 'KeyPrevWeapon2', KeyPrevWeapon2);
-    WriteInt('Player1', 'KeyOpen2', KeyOpen2);
-    WriteInt('Player1', 'KeyStrafe2', KeyStrafe2);
-    for i := 0 to High(KeyWeapon2) do
-      WriteInt('Player1', 'KeyWeapon2' + IntToStr(i), KeyWeapon2[i]);
-
-    WriteStr('Player1', 'Name', Name);
-    WriteStr('Player1', 'model', Model);
-    WriteInt('Player1', 'red', Color.R);
-    WriteInt('Player1', 'green', Color.G);
-    WriteInt('Player1', 'blue', Color.B);
-    WriteInt('Player1', 'team', Team);
-  end;
-
-  with config, gGameControls.P2Control, gPlayer2Settings do
-  begin
-    WriteInt('Player2', 'KeyRight', KeyRight);
-    WriteInt('Player2', 'KeyLeft', KeyLeft);
-    WriteInt('Player2', 'KeyUp', KeyUp);
-    WriteInt('Player2', 'KeyDown', KeyDown);
-    WriteInt('Player2', 'KeyFire', KeyFire);
-    WriteInt('Player2', 'KeyJump', KeyJump);
-    WriteInt('Player2', 'KeyNextWeapon', KeyNextWeapon);
-    WriteInt('Player2', 'KeyPrevWeapon', KeyPrevWeapon);
-    WriteInt('Player2', 'KeyOpen', KeyOpen);
-    WriteInt('Player2', 'KeyStrafe', KeyStrafe);
-    for i := 0 to High(KeyWeapon) do
-      WriteInt('Player2', 'KeyWeapon' + IntToStr(i), KeyWeapon[i]);
-
-    WriteInt('Player2', 'KeyRight2', KeyRight2);
-    WriteInt('Player2', 'KeyLeft2', KeyLeft2);
-    WriteInt('Player2', 'KeyUp2', KeyUp2);
-    WriteInt('Player2', 'KeyDown2', KeyDown2);
-    WriteInt('Player2', 'KeyFire2', KeyFire2);
-    WriteInt('Player2', 'KeyJump2', KeyJump2);
-    WriteInt('Player2', 'KeyNextWeapon2', KeyNextWeapon2);
-    WriteInt('Player2', 'KeyPrevWeapon2', KeyPrevWeapon2);
-    WriteInt('Player2', 'KeyOpen2', KeyOpen2);
-    WriteInt('Player2', 'KeyStrafe2', KeyStrafe2);
-    for i := 0 to High(KeyWeapon2) do
-      WriteInt('Player2', 'KeyWeapon2' + IntToStr(i), KeyWeapon2[i]);
-
-    WriteStr('Player2', 'Name', Name);
-    WriteStr('Player2', 'model', Model);
-    WriteInt('Player2', 'red', Color.R);
-    WriteInt('Player2', 'green', Color.G);
-    WriteInt('Player2', 'blue', Color.B);
-    WriteInt('Player2', 'team', Team);
+    TimeLimit := gsTimeLimit;
+    GoalLimit := gsGoalLimit;
+    MaxLives := gsMaxLives;
+    SpawnInvul := gsSpawnInvul;
+    ItemRespawnTime := gsItemRespawnTime;
+    WarmupTime := gsWarmupTime;
+    Options := gsGameFlags;
   end;
-
-  for i := 0 to e_MaxJoys-1 do
-    config.WriteInt('Joysticks', 'Deadzone' + IntToStr(i), e_JoystickDeadzones[i]);
-
-  config.WriteInt('Touch', 'Size', Round(g_touch_size * 10));
-  config.WriteBool('Touch', 'Fire', g_touch_fire);
-
-  with config do
-    case gGibsCount of
-      0: config.WriteInt('Game', 'GibsCount', 0);
-      8: config.WriteInt('Game', 'GibsCount', 1);
-      16: config.WriteInt('Game', 'GibsCount', 2);
-      32: config.WriteInt('Game', 'GibsCount', 3);
-      else config.WriteInt('Game', 'GibsCount', 4);
-    end;
-
-  config.WriteInt('Game', 'ItemRespawnTime', ITEM_RESPAWNTIME div 36);
-  config.WriteInt('Game', 'MaxParticles', g_GFX_GetMax());
-  config.WriteInt('Game', 'MaxShells', g_Shells_GetMax());
-  config.WriteInt('Game', 'MaxGibs', g_Gibs_GetMax());
-  config.WriteInt('Game', 'MaxCorpses', g_Corpses_GetMax());
-  config.WriteInt('Game', 'BloodCount', gBloodCount);
-  config.WriteBool('Game', 'AdvancesBlood', gAdvBlood);
-  config.WriteBool('Game', 'AdvancesCorpses', gAdvCorpses);
-  config.WriteBool('Game', 'AdvancesGibs', gAdvGibs);
-  config.WriteInt('Game', 'Flash', gFlash);
-  config.WriteBool('Game', 'BackGround', gDrawBackGround);
-  config.WriteBool('Game', 'Messages', gShowMessages);
-  config.WriteBool('Game', 'RevertPlayers', gRevertPlayers);
-  config.WriteInt('Game', 'ChatBubble', gChatBubble);
-  config.WriteBool('Game', 'SFSDebug', gSFSDebug);
-  config.WriteBool('Game', 'SFSFastMode', gSFSFastMode);
-  config.WriteBool('Game', 'FastScreenshots', e_FastScreenshots);
-  config.WriteStr('Game', 'DefaultMegawadStart', gDefaultMegawadStart);
-  config.WriteBool('Game', 'BerserkAutoswitching', gBerserkAutoswitch);
-  config.WriteInt('Game', 'Scale', Round(g_dbg_scale * 100));
-
-  config.WriteStr ('GameplayCustom', 'Map', gcMap);
-  config.WriteStr ('GameplayCustom', 'GameMode', gcGameMode);
-  config.WriteInt ('GameplayCustom', 'TimeLimit', gcTimeLimit);
-  config.WriteInt ('GameplayCustom', 'GoalLimit', gcGoalLimit);
-  config.WriteInt ('GameplayCustom', 'MaxLives', gcMaxLives);
-  config.WriteInt ('GameplayCustom', 'Players', gcPlayers);
-  config.WriteBool('GameplayCustom', 'TeamDamage', gcTeamDamage);
-  config.WriteBool('GameplayCustom', 'AllowExit', gcAllowExit);
-  config.WriteBool('GameplayCustom', 'WeaponStay', gcWeaponStay);
-  config.WriteBool('GameplayCustom', 'Monsters', gcMonsters);
-  config.WriteStr ('GameplayCustom', 'BotsVS', gcBotsVS);
-
-  config.WriteStr ('GameplayNetwork', 'Map', gnMap);
-  config.WriteStr ('GameplayNetwork', 'GameMode', gnGameMode);
-  config.WriteInt ('GameplayNetwork', 'TimeLimit', gnTimeLimit);
-  config.WriteInt ('GameplayNetwork', 'GoalLimit', gnGoalLimit);
-  config.WriteInt ('GameplayNetwork', 'MaxLives', gnMaxLives);
-  config.WriteInt ('GameplayNetwork', 'Players', gnPlayers);
-  config.WriteBool('GameplayNetwork', 'TeamDamage', gnTeamDamage);
-  config.WriteBool('GameplayNetwork', 'AllowExit', gnAllowExit);
-  config.WriteBool('GameplayNetwork', 'WeaponStay', gnWeaponStay);
-  config.WriteBool('GameplayNetwork', 'Monsters', gnMonsters);
-  config.WriteStr ('GameplayNetwork', 'BotsVS', gnBotsVS);
-
-  config.WriteStr('MasterServer', 'IP', NetSlistIP);
-  config.WriteInt('MasterServer', 'Port', NetSlistPort);
-
-  config.WriteStr ('Server', 'Name', NetServerName);
-  config.WriteStr ('Server', 'Password', NetPassword);
-  config.WriteInt ('Server', 'Port', NetPort);
-  config.WriteInt ('Server', 'MaxClients', NetMaxClients);
-  config.WriteBool('Server', 'RCON', NetAllowRCON);
-  config.WriteStr ('Server', 'RCONPassword', NetRCONPassword);
-  config.WriteBool('Server', 'SyncWithMaster', NetUseMaster);
-  config.WriteBool('Server', 'ForwardPorts', NetForwardPorts);
-  config.WriteInt ('Server', 'UpdateInterval', NetUpdateRate);
-  config.WriteInt ('Server', 'ReliableUpdateInterval', NetRelupdRate);
-  config.WriteInt ('Server', 'MasterSyncInterval', NetMasterRate);
-
-  config.WriteInt  ('Client', 'InterpolationSteps', NetInterpLevel);
-  config.WriteBool ('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
-  config.WriteBool ('Client', 'PredictSelf', NetPredictSelf);
-  config.WriteStr  ('Client', 'LastIP', NetClientIP);
-  config.WriteInt  ('Client', 'LastPort', NetClientPort);
-
-  config.SaveFile(FileName);
-  config.Free();
-end;
-
-procedure g_Options_Write_Language(FileName: String);
-var
-  config: TConfig;
-begin
-  e_WriteLog('Writing language config', TMsgType.Notify);
-
-  config := TConfig.CreateFile(FileName);
-  config.WriteStr('Game', 'Language', gLanguage);
-  config.SaveFile(FileName);
-  config.Free();
-end;
-
-procedure g_Options_Write_Video(FileName: String);
-var
-  config: TConfig;
-  sW, sH: Integer;
-begin
-  e_WriteLog('Writing resolution to config', TMsgType.Notify);
-
-  config := TConfig.CreateFile(FileName);
-
-  if gWinMaximized and (not gFullscreen) then
-    begin
-      sW := gWinSizeX;
-      sH := gWinSizeY;
-    end
-  else
-    begin
-      sW := gScreenWidth;
-      sH := gScreenHeight;
-    end;
-  e_LogWritefln('  (ws=%dx%d) (ss=%dx%d)', [gWinSizeX, gWinSizeY, gScreenWidth, gScreenHeight]);
-
-  config.WriteInt('Video', 'ScreenWidth', sW);
-  config.WriteInt('Video', 'ScreenHeight', sH);
-  config.WriteInt('Video', 'WinPosX', gWinRealPosX);
-  config.WriteInt('Video', 'WinPosY', gWinRealPosY);
-  config.WriteBool('Video', 'Fullscreen', gFullscreen);
-  config.WriteBool('Video', 'Maximized', gWinMaximized);
-
-  config.SaveFile(FileName);
-  config.Free();
-end;
-
-procedure g_Options_Write_Gameplay_Custom(FileName: String);
-var
-  config: TConfig;
-begin
-  e_WriteLog('Writing custom gameplay config', TMsgType.Notify);
-
-  config := TConfig.CreateFile(FileName);
-
-  config.WriteStr ('GameplayCustom', 'Map', gcMap);
-  config.WriteStr ('GameplayCustom', 'GameMode', gcGameMode);
-  config.WriteInt ('GameplayCustom', 'TimeLimit', gcTimeLimit);
-  config.WriteInt ('GameplayCustom', 'GoalLimit', gcGoalLimit);
-  config.WriteInt ('GameplayCustom', 'MaxLives', gcMaxLives);
-  config.WriteInt ('GameplayCustom', 'Players', gcPlayers);
-  config.WriteBool('GameplayCustom', 'TeamDamage', gcTeamDamage);
-  config.WriteBool('GameplayCustom', 'AllowExit', gcAllowExit);
-  config.WriteBool('GameplayCustom', 'WeaponStay', gcWeaponStay);
-  config.WriteBool('GameplayCustom', 'Monsters', gcMonsters);
-  config.WriteStr ('GameplayCustom', 'BotsVS', gcBotsVS);
-
-  config.SaveFile(FileName);
-  config.Free();
-end;
-
-procedure g_Options_Write_Gameplay_Net(FileName: String);
-var
-  config: TConfig;
-begin
-  e_WriteLog('Writing network gameplay config', TMsgType.Notify);
-
-  config := TConfig.CreateFile(FileName);
-
-  config.WriteStr ('GameplayNetwork', 'Map', gnMap);
-  config.WriteStr ('GameplayNetwork', 'GameMode', gnGameMode);
-  config.WriteInt ('GameplayNetwork', 'TimeLimit', gnTimeLimit);
-  config.WriteInt ('GameplayNetwork', 'GoalLimit', gnGoalLimit);
-  config.WriteInt ('GameplayNetwork', 'MaxLives', gnMaxLives);
-  config.WriteInt ('GameplayNetwork', 'Players', gnPlayers);
-  config.WriteBool('GameplayNetwork', 'TeamDamage', gnTeamDamage);
-  config.WriteBool('GameplayNetwork', 'AllowExit', gnAllowExit);
-  config.WriteBool('GameplayNetwork', 'WeaponStay', gnWeaponStay);
-  config.WriteBool('GameplayNetwork', 'Monsters', gnMonsters);
-  config.WriteStr ('GameplayNetwork', 'BotsVS', gnBotsVS);
-
-  config.SaveFile(FileName);
-  config.Free();
-end;
-
-procedure g_Options_Write_Net_Server(FileName: String);
-var
-  config: TConfig;
-begin
-  e_WriteLog('Writing server config', TMsgType.Notify);
-
-  config := TConfig.CreateFile(FileName);
-
-  config.WriteStr ('Server', 'Name', NetServerName);
-  config.WriteStr ('Server', 'Password', NetPassword);
-  config.WriteInt ('Server', 'Port', NetPort);
-  config.WriteInt ('Server', 'MaxClients', NetMaxClients);
-  config.WriteBool('Server', 'SyncWithMaster', NetUseMaster);
-  config.WriteBool('Server', 'ForwardPorts', NetForwardPorts);
-
-  config.SaveFile(FileName);
-  config.Free();
-end;
-
-procedure g_Options_Write_Net_Client(FileName: String);
-var
-  config: TConfig;
-begin
-  e_WriteLog('Writing client config', TMsgType.Notify);
-
-  config := TConfig.CreateFile(FileName);
-
-  config.WriteStr('Client', 'LastIP', NetClientIP);
-  config.WriteInt('Client', 'LastPort', NetClientPort);
-
-  config.SaveFile(FileName);
-  config.Free();
 end;
 
+initialization
+  Randomize;
+  machine := Random(10000);
+
+  (* Video *)
+  conRegVar('r_width', @gRC_Width, '', '');
+  conRegVar('r_height', @gRC_Height, '', '');
+  conRegVar('r_fullscreen', @gRC_FullScreen, '', '');
+  conRegVar('r_maximized', @gRC_Maximized, '', '');
+  conRegVar('r_bpp', @gBPP, '', '');
+  conRegVar('r_vsync', @gVSync, '', '');
+  conRegVar('r_texfilter', @gTextureFilter, '', '');
+  conRegVar('r_npot', @glNPOTOverride, '', '');
+  conRegVar('r_interp', @gLerpActors, '', 'interpolate actors');
+
+  (* Sound *)
+  conRegVar('s_nosound', @gNoSound, '', '');
+  conRegVar('s_soundvolume', @gSoundLevel, '', '');
+  conRegVar('s_musicvolume', @gMusicLevel, '', '');
+  conRegVar('s_maxsim', @gMaxSimSounds, '', ''); // e_sound_fmod/sdl?
+  conRegVar('s_muteinactive', @gMuteWhenInactive, '', '');
+  conRegVar('s_announcer', @gAnnouncer, '', '');
+  conRegVar('s_sfx', @gSoundEffectsDF, '', '');
+  conRegVar('s_chatsounds', @gUseChatSounds, '', '');
+  {$IFDEF USE_SDLMIXER}
+    conRegVar('sdl_mixer_samplerate', @gsSDLSampleRate, '', '');
+    conRegVar('sdl_mixer_buffersize', @gsSDLBufferSize, '', '');
+  {$ENDIF}
+
+  (* Game *)
+  conRegVar('g_gibs_count', @gGibsCount, '', '');
+  conRegVar('g_blood_count', @gBloodCount, '', '');
+  conRegVar('g_adv_blood', @gAdvBlood, '', '');
+  conRegVar('g_adv_corpses', @gAdvCorpses, '', '');
+  conRegVar('g_adv_gibs', @gAdvGibs, '', '');
+  conRegVar('r_flash', @gFlash, '', '');
+  conRegVar('r_background', @gDrawBackGround, '', '');
+  conRegVar('g_show_messages', @gShowMessages, '', '');
+  conRegVar('r_revert_players', @gRevertPlayers, '', '');
+  conRegVar('r_chat_bubble', @gChatBubble, '', '');
+  conRegVar('sfs_debug', @wadoptDebug, '', '');
+  conRegVar('sfs_fastmode', @wadoptFast, '', '');
+  conRegVar('g_fast_screenshots', @e_FastScreenshots, '', '');
+  conRegVar('g_default_megawad', @gDefaultMegawadStart, '', '');
+  conRegVar('g_save_stats', @gSaveStats, '', '');
+  conRegVar('g_screenshot_stats', @gScreenshotStats, '', '');
+  conRegVar('g_lastmap', @gsMap, '', '');
 end.