aboutsummaryrefslogblamecommitdiffstats
path: root/unicode/src/base/UIni.pas
blob: e90ae31caeea0bf48d9f061d17c52bc0835f9bc6 (plain) (tree)






































                                                                        
                














































                                                                                                                          
                                                 

                                

                                                 

































































                                                     


                                                            













































































































                                                                                                                                         

                































































































































































































                                                                                                            
                                                           








































































































































































































































































                                                                                                                              



                                                                                            



















































































































































                                                                                                                                  


                                                                            




















































                                                                                  





                                                                                   




















                                                                       
{* UltraStar Deluxe - Karaoke Game
 *
 * UltraStar Deluxe is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 * $URL$
 * $Id$
 *}

unit UIni;

interface

{$IFDEF FPC}
  {$MODE Delphi}
{$ENDIF}

{$I switches.inc}

uses
  Classes,
  IniFiles,
  ULog,
  UTextEncoding,
  SysUtils;

type
  // TInputDeviceConfig stores the configuration for an input device.
  // Configurations will be stored in the InputDeviceConfig array.
  // Note that not all devices listed in InputDeviceConfig are active devices.
  // Some might be unplugged and hence unavailable.
  // Available devices are held in TAudioInputProcessor.DeviceList. Each
  // TAudioInputDevice listed there has a CfgIndex field which is the index to
  // its configuration in the InputDeviceConfig array.
  // Name:
  //   the name of the input device
  // Input:
  //   the index of the input source to use for recording
  // ChannelToPlayerMap:
  //   mapping of recording channels to players, e.g. ChannelToPlayerMap[0] = 2
  //   maps the channel 0 (left) to player 2. A player index of 0 means that
  //   the channel is not assigned to a player.
  PInputDeviceConfig = ^TInputDeviceConfig;
  TInputDeviceConfig = record
    Name:               string;
    Input:              integer;
    ChannelToPlayerMap: array of integer;
  end;

type

//Options

  TVisualizerOption      = (voOff, voWhenNoVideo, voOn);
  TBackgroundMusicOption = (bmoOff, bmoOn);
  TIni = class
    private
      function RemoveFileExt(FullName: string): string;
      function ExtractKeyIndex(const Key, Prefix, Suffix: string): integer;
      function GetMaxKeyIndex(Keys: TStringList; const Prefix, Suffix: string): integer;
      function GetArrayIndex(const SearchArray: array of string; Value: string; CaseInsensitiv: Boolean = False): integer;
      function ReadArrayIndex(const SearchArray: array of string; IniFile: TCustomIniFile;
          IniSection: string; IniProperty: string; Default: integer): integer;

      procedure LoadInputDeviceCfg(IniFile: TMemIniFile);
      procedure SaveInputDeviceCfg(IniFile: TIniFile);
      procedure LoadThemes(IniFile: TCustomIniFile);
      procedure LoadPaths(IniFile: TCustomIniFile);
      procedure LoadScreenModes(IniFile: TCustomIniFile);

    public
      Name:           array[0..11] of UTF8String;

      // Templates for Names Mod
      NameTeam:       array[0..2] of UTF8String;
      NameTemplate:   array[0..11] of UTF8String;

      //Filename of the opened iniFile
      Filename:       string;

      // Game
      Players:        integer;
      Difficulty:     integer;
      Language:       integer;
      Tabs:           integer;
      Tabs_at_startup:integer; //Tabs at Startup fix
      Sorting:        integer;
      Debug:          integer;

      // Graphics
      Screens:        integer;
      Resolution:     integer;
      Depth:          integer;
      VisualizerOption:integer; 
      FullScreen:     integer;
      TextureSize:    integer;
      SingWindow:     integer;
      Oscilloscope:   integer;
      Spectrum:       integer;
      Spectrograph:   integer;
      MovieSize:      integer;

      // Sound
      MicBoost:       integer;
      ClickAssist:    integer;
      BeatClick:      integer;
      SavePlayback:   integer;
      ThresholdIndex: integer;
      AudioOutputBufferSizeIndex:integer;
      VoicePassthrough:integer;

      //Song Preview
      PreviewVolume:  integer;
      PreviewFading:  integer;

      // Lyrics
      LyricsFont:     integer;
      LyricsEffect:   integer;
      Solmization:    integer;
      NoteLines:      integer;

      // Themes
      Theme:          integer;
      SkinNo:         integer;
      Color:          integer;
      BackgroundMusicOption:integer;
      
      // Record
      InputDeviceConfig: array of TInputDeviceConfig;

      // Advanced
      LoadAnimation:  integer;
      EffectSing:     integer;
      ScreenFade:     integer;
      AskBeforeDel:   integer;
      OnSongClick:    integer;
      LineBonus:      integer;
      PartyPopup:     integer;

      // Controller
      Joypad:         integer;

      // default encoding for texts (lyrics, song-name, ...)
      EncodingDefault: TEncoding;

      procedure Load();
      procedure Save();
      procedure SaveNames;
      procedure SaveLevel;
  end;

var
  Ini:            TIni;
  IResolution:    array of string;
  ILanguage:      array of string;
  ITheme:         array of string;
  ISkin:          array of string;



const
  IPlayers:       array[0..4] of string  = ('1', '2', '3', '4', '6');
  IPlayersVals:   array[0..4] of integer = ( 1 ,  2 ,  3 ,  4 ,  6 );

  IDifficulty:    array[0..2] of string = ('Easy', 'Medium', 'Hard');
  ITabs:          array[0..1] of string = ('Off', 'On');

  ISorting:       array[0..7] of string = ('Edition', 'Genre', 'Language', 'Folder', 'Title', 'Artist', 'Title2', 'Artist2');
  sEdition  = 0;
  sGenre    = 1;
  sLanguage = 2;
  sFolder   = 3;
  sTitle    = 4;
  sArtist   = 5;
  sTitle2   = 6;
  sArtist2  = 7;

  IDebug:         array[0..1] of string = ('Off', 'On');

  IScreens:       array[0..1] of string = ('1', '2');
  IFullScreen:    array[0..1] of string = ('Off', 'On');
  IDepth:         array[0..1] of string = ('16 bit', '32 bit');
  IVisualizer:    array[0..2] of string = ('Off', 'WhenNoVideo','On');

  IBackgroundMusic: array[0..1] of string = ('Off', 'On');


  ITextureSize:     array[0..2] of string  = ('128', '256', '512');
  ITextureSizeVals: array[0..2] of integer = ( 128,   256,   512);

  ISingWindow:    array[0..1] of string = ('Small', 'Big');

  //SingBar Mod
  IOscilloscope:  array[0..2] of string = ('Off', 'Osci', 'Bar');
//IOscilloscope:  array[0..1] of string = ('Off', 'On');

  ISpectrum:      array[0..1] of string = ('Off', 'On');
  ISpectrograph:  array[0..1] of string = ('Off', 'On');
  IMovieSize:     array[0..2] of string = ('Half', 'Full [Vid]', 'Full [BG+Vid]');

  IClickAssist:   array[0..1] of string = ('Off', 'On');
  IBeatClick:     array[0..1] of string = ('Off', 'On');
  ISavePlayback:  array[0..1] of string = ('Off', 'On');

  IThreshold:     array[0..3] of string = ('5%', '10%', '15%', '20%');
  IThresholdVals: array[0..3] of single = (0.05, 0.10,  0.15,  0.20);

  IVoicePassthrough:  array[0..1] of string = ('Off', 'On');

  IAudioOutputBufferSize:     array[0..9] of string  = ('Auto', '256', '512', '1024', '2048', '4096', '8192', '16384', '32768', '65536');
  IAudioOutputBufferSizeVals: array[0..9] of integer = ( 0,      256,   512 ,  1024 ,  2048 ,  4096 ,  8192 ,  16384 ,  32768 ,  65536 );

  IAudioInputBufferSize:     array[0..9] of string  = ('Auto', '256', '512', '1024', '2048', '4096', '8192', '16384', '32768', '65536');
  IAudioInputBufferSizeVals: array[0..9] of integer = ( 0,      256,   512 ,  1024 ,  2048 ,  4096 ,  8192 ,  16384 ,  32768 ,  65536 );

  //Song Preview
  IPreviewVolume:     array[0..10] of string = ('Off', '10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '100%');
  IPreviewVolumeVals: array[0..10] of single = ( 0,   0.10,  0.20,  0.30,  0.40,  0.50,  0.60,  0.70,  0.80,  0.90,   1.00  );

  IPreviewFading:     array[0..5] of string  = ('Off', '1 Sec', '2 Secs', '3 Secs', '4 Secs', '5 Secs');
  IPreviewFadingVals: array[0..5] of integer = ( 0,     1,       2,        3,        4,        5      );


  ILyricsFont:    array[0..2] of string = ('Plain', 'OLine1', 'OLine2');
  ILyricsEffect:  array[0..4] of string = ('Simple', 'Zoom', 'Slide', 'Ball', 'Shift');
  ISolmization:   array[0..3] of string = ('Off', 'Euro', 'Jap', 'American');
  INoteLines:     array[0..1] of string = ('Off', 'On');

  IColor:         array[0..8] of string = ('Blue', 'Green', 'Pink', 'Red', 'Violet', 'Orange', 'Yellow', 'Brown', 'Black');

  // Advanced
  ILoadAnimation: array[0..1] of string = ('Off', 'On');
  IEffectSing:    array[0..1] of string = ('Off', 'On');
  IScreenFade:    array[0..1] of string =('Off', 'On');
  IAskbeforeDel:  array[0..1] of string = ('Off', 'On');
  IOnSongClick:   array[0..2] of string = ('Sing', 'Select Players', 'Open Menu');
  ILineBonus:     array[0..2] of string = ('Off', 'At Score', 'At Notes');
  IPartyPopup:    array[0..1] of string = ('Off', 'On');

  IJoypad:        array[0..1] of string = ('Off', 'On');

  // Recording options
  IChannelPlayer: array[0..6] of string = ('Off', '1', '2', '3', '4', '5', '6');
  IMicBoost:      array[0..3] of string = ('Off', '+6dB', '+12dB', '+18dB');

implementation

uses
  StrUtils,
  UMain,
  SDL,
  ULanguage,
  UPlatform,
  USkins,
  URecord,
  UCommandLine,
  UUnicodeUtils;

(**
 * Returns the filename without its fileextension
 *)
function TIni.RemoveFileExt(FullName: string): string;
begin
  Result := ChangeFileExt(FullName, '');
end;

(**
 * Extracts an index of a key that is surrounded by a Prefix/Suffix pair.
 * Example: ExtractKeyIndex('MyKey[1]', '[', ']') will return 1.
 *)
function TIni.ExtractKeyIndex(const Key, Prefix, Suffix: string): integer;
var
  Value: string;
  Start: integer;
begin
  Result := -1;

  if Pos(Prefix, Key) > -1 then
  begin
    Start  := Pos(Prefix, Key) + Length(Prefix);

    // copy all between prefix and suffix
    Value  := Copy(Key, Start, Pos(Suffix, Key)-1 - Start);
    Result := StrToIntDef(Value, -1);
  end;
end;

(**
 * Finds the maximum key-index in a key-list.
 * The indexes of the list are surrounded by Prefix/Suffix,
 * e.g. MyKey[1] (Prefix='[', Suffix=']')
 *)
function TIni.GetMaxKeyIndex(Keys: TStringList; const Prefix, Suffix: string): integer;
var
  i: integer;
  KeyIndex: integer;
begin
  Result := -1;

  for i := 0 to Keys.Count-1 do
  begin
    KeyIndex := ExtractKeyIndex(Keys[i], Prefix, Suffix);
    if (KeyIndex > Result) then
      Result := KeyIndex;
  end;
end;

(**
 * Returns the index of Value in SearchArray
 * or -1 if Value is not in SearchArray.
 *)
function TIni.GetArrayIndex(const SearchArray: array of string; Value: string;
    CaseInsensitiv: Boolean = False): integer;
var
  i: integer;
begin
  Result := -1;

  for i := 0 to High(SearchArray) do
  begin
    if (SearchArray[i] = Value) or
       (CaseInsensitiv and (UpperCase(SearchArray[i]) = UpperCase(Value))) then
    begin
      Result := i;
      Break;
    end;
  end;
end;

(**
 * Reads the property IniSeaction:IniProperty from IniFile and
 * finds its corresponding index in SearchArray.
 * If SearchArray does not contain the property value, the default value is
 * returned.
 *)
function TIni.ReadArrayIndex(const SearchArray: array of string; IniFile: TCustomIniFile;
    IniSection: string; IniProperty: string; Default: integer): integer;
var
  StrValue: string;
begin
  StrValue := IniFile.ReadString(IniSection, IniProperty, SearchArray[Default]);
  Result := GetArrayIndex(SearchArray, StrValue);
  if (Result = -1) then
  begin
    Result := Default;
  end;
end;


procedure TIni.LoadInputDeviceCfg(IniFile: TMemIniFile);
var
  DeviceCfg: PInputDeviceConfig;
  DeviceIndex: integer;
  ChannelCount: integer;
  ChannelIndex: integer;
  RecordKeys: TStringList;
  i: integer;
begin
  RecordKeys := TStringList.Create();

  // read all record-keys for filtering
  IniFile.ReadSection('Record', RecordKeys);

  SetLength(InputDeviceConfig, 0);

  for i := 0 to RecordKeys.Count-1 do
  begin
    // find next device-name
    DeviceIndex := ExtractKeyIndex(RecordKeys[i], 'DeviceName[', ']');
    if (DeviceIndex >= 0) then
    begin
      if not IniFile.ValueExists('Record', Format('DeviceName[%d]', [DeviceIndex])) then
        break;

      // resize list
      SetLength(InputDeviceConfig, Length(InputDeviceConfig)+1);

      // read an input device's config.
      // Note: All devices are appended to the list whether they exist or not.
      //   Otherwise an external device's config will be lost if it is not
      //   connected (e.g. singstar mics or USB-Audio devices).
      DeviceCfg := @InputDeviceConfig[High(InputDeviceConfig)];
      DeviceCfg.Name := IniFile.ReadString('Record', Format('DeviceName[%d]', [DeviceIndex]), '');
      DeviceCfg.Input := IniFile.ReadInteger('Record', Format('Input[%d]', [DeviceIndex]), 0);

      // find the largest channel-number of the current device in the ini-file
      ChannelCount := GetMaxKeyIndex(RecordKeys, 'Channel', Format('[%d]', [DeviceIndex]));
      if (ChannelCount < 0) then
        ChannelCount := 0;

      SetLength(DeviceCfg.ChannelToPlayerMap, ChannelCount);

      // read channel-to-player mapping for every channel of the current device
      // or set non-configured channels to no player (=0).
      for ChannelIndex := 0 to High(DeviceCfg.ChannelToPlayerMap) do
      begin
        DeviceCfg.ChannelToPlayerMap[ChannelIndex] :=
          IniFile.ReadInteger('Record', Format('Channel%d[%d]', [ChannelIndex+1, DeviceIndex]), 0);
      end;
    end;
  end;

  RecordKeys.Free();

  // MicBoost
  //MicBoost := GetArrayIndex(IMicBoost, IniFile.ReadString('Record', 'MicBoost', 'Off'));
  // Threshold
  //  ThresholdIndex := GetArrayIndex(IThreshold, IniFile.ReadString('Record', 'Threshold', IThreshold[1]));
end;

procedure TIni.SaveInputDeviceCfg(IniFile: TIniFile);
var
  DeviceIndex:    integer;
  ChannelIndex:   integer;
begin
  for DeviceIndex := 0 to High(InputDeviceConfig) do
  begin
    // DeviceName and DeviceInput
    IniFile.WriteString('Record', Format('DeviceName[%d]', [DeviceIndex+1]),
                        InputDeviceConfig[DeviceIndex].Name);
    IniFile.WriteInteger('Record', Format('Input[%d]', [DeviceIndex+1]),
                        InputDeviceConfig[DeviceIndex].Input);
                        
    // Channel-to-Player Mapping
    for ChannelIndex := 0 to High(InputDeviceConfig[DeviceIndex].ChannelToPlayerMap) do
    begin
      IniFile.WriteInteger('Record',
                          Format('Channel%d[%d]', [ChannelIndex+1, DeviceIndex+1]),
                          InputDeviceConfig[DeviceIndex].ChannelToPlayerMap[ChannelIndex]);
    end;
  end;

  // MicBoost
  //IniFile.WriteString('Record', 'MicBoost', IMicBoost[MicBoost]);
  // Threshold
  //IniFile.WriteString('Record', 'Threshold', IThreshold[ThresholdIndex]);
end;

procedure TIni.LoadPaths(IniFile: TCustomIniFile);
var
  PathStrings: TStringList;
  I: integer;
begin
  PathStrings := TStringList.Create;
  IniFile.ReadSection('Directories', PathStrings);

  // Load song-paths
  for I := 0 to PathStrings.Count-1 do
  begin
    if (Pos('SONGDIR', UpperCase(PathStrings[I])) = 1) then
    begin
      AddSongPath(IniFile.ReadString('Directories', PathStrings[I], ''));
    end;
  end;

  PathStrings.Free;
end;

procedure TIni.LoadThemes(IniFile: TCustomIniFile);
var
  SearchResult: TSearchRec;
  ThemeIni:     TMemIniFile;
  ThemeName:    string;
  I: integer;
begin
  // Theme
  SetLength(ITheme, 0);
  Log.LogStatus('Searching for Theme : ' + ThemePath + '*.ini', 'Theme');

  FindFirst(ThemePath + '*.ini',faAnyFile, SearchResult);
  Repeat
    Log.LogStatus('Found Theme: ' + SearchResult.Name, 'Theme');

    //Read Themename from Theme
    ThemeIni := TMemIniFile.Create(SearchResult.Name);
    ThemeName := UpperCase(ThemeIni.ReadString('Theme','Name', RemoveFileExt(SearchResult.Name)));
    ThemeIni.Free;

    //Search for Skins for this Theme
    for I := Low(Skin.Skin) to High(Skin.Skin) do
    begin
      if UpperCase(Skin.Skin[I].Theme) = ThemeName then
      begin
        SetLength(ITheme, Length(ITheme)+1);
        ITheme[High(ITheme)] := RemoveFileExt(SearchResult.Name);
        break;
      end;
    end;
  until FindNext(SearchResult) <> 0;
  FindClose(SearchResult);

  // No Theme Found
  if (Length(ITheme) = 0) then
  begin
    Log.CriticalError('Could not find any valid Themes.');
  end;

  Theme := GetArrayIndex(ITheme, IniFile.ReadString('Themes', 'Theme', 'DELUXE'), true);
  if (Theme = -1) then
       Theme := 0;

  // Skin
  Skin.onThemeChange;

  SkinNo := GetArrayIndex(ISkin, IniFile.ReadString('Themes',    'Skin',   ISkin[0]));
end;

procedure TIni.LoadScreenModes(IniFile: TCustomIniFile);

  // swap two strings
  procedure swap(var s1, s2: string);
  var
    s3: string;
  begin
    s3 := s1;
    s1 := s2;
    s2 := s3;
  end;

var
  Modes: PPSDL_Rect;
  I: integer;
begin
  // Screens
  Screens := GetArrayIndex(IScreens, IniFile.ReadString('Graphics', 'Screens', IScreens[0]));
  
  // FullScreen
  FullScreen := GetArrayIndex(IFullScreen, IniFile.ReadString('Graphics', 'FullScreen', 'On'));

  // Resolution
  SetLength(IResolution, 0);
  
  // Check if there are any modes available
  // TODO: we should seperate windowed and fullscreen modes. Otherwise it is not
  // possible to select a reasonable fullscreen mode when in windowed mode
  if IFullScreen[FullScreen] = 'On' then
    Modes  := SDL_ListModes(nil, SDL_OPENGL or SDL_FULLSCREEN)
  else 
    Modes  := SDL_ListModes(nil, SDL_OPENGL or SDL_RESIZABLE) ;
  
  if (Modes = nil) then
  begin
    Log.LogStatus( 'No resolutions Found' , 'Video');
  end
  else if (Modes = PPSDL_Rect(-1)) then
  begin
    // Fallback to some standard resolutions
    SetLength(IResolution, 10);
    IResolution[0] := '640x480';
    IResolution[1] := '800x600';
    IResolution[2] := '1024x768';
    IResolution[3] := '1152x864';
    IResolution[4] := '1280x800';
    IResolution[5] := '1280x960';
    IResolution[6] := '1400x1050';
    IResolution[7] := '1440x900';
    IResolution[8] := '1600x1200';
    IResolution[9] := '1680x1050';
      
    Resolution := GetArrayIndex(IResolution, IniFile.ReadString('Graphics', 'Resolution', '800x600'));
    if Resolution = -1 then
    begin
      SetLength(IResolution, Length(IResolution) + 1);
      IResolution[High(IResolution)] := IniFile.ReadString('Graphics', 'Resolution', '800x600');
      Resolution := High(IResolution);
    end;
  end
  else
  begin
    while assigned( Modes^ ) do //this should solve the biggest wine problem | THANKS Linnex (11.11.07)
    begin
      Log.LogStatus( 'Found Video Mode : ' + IntToStr(Modes^.w) + 'x' + IntToStr(Modes^.h) , 'Video');
      SetLength(IResolution, Length(IResolution) + 1);
      IResolution[High(IResolution)] := IntToStr(Modes^.w) + 'x' + IntToStr(Modes^.h);
      Inc(Modes);
    end;

    // reverse order
    for I := 0 to (Length(IResolution) div 2) - 1 do
    begin
      swap(IResolution[I], IResolution[High(IResolution)-I]);
    end;
    Resolution := GetArrayIndex(IResolution, IniFile.ReadString('Graphics', 'Resolution', '800x600'));

    if Resolution = -1 then
    begin
      Resolution := GetArrayIndex(IResolution, '800x600');
      if Resolution = -1 then
        Resolution := 0;
    end;
  end;

  // if no modes were set, then failback to 800x600
  // as per http://sourceforge.net/forum/message.php?msg_id=4544965
  // THANKS : linnex at users.sourceforge.net
  if Length(IResolution) < 1 then
  begin
    Log.LogStatus( 'Found Video Mode : NONE !!! ( Defaulted to 800 x 600 )', 'Video');
    SetLength(IResolution, 1);
    IResolution[0] := '800x600';
    Resolution := 0;
    Log.LogStatus('SDL_ListModes Defaulted Res To : ' + IResolution[0] , 'Graphics - Resolutions');

    // Default to fullscreen OFF, in this case !
    FullScreen := 0;
  end;

  // Depth
  Depth := GetArrayIndex(IDepth, IniFile.ReadString('Graphics', 'Depth', '32 bit'));
end;

procedure TIni.Load();
var
  IniFile: TMemIniFile;
  I: integer;
begin
  GamePath := Platform.GetGameUserPath;

  Log.LogStatus( 'GamePath : ' +GamePath , '' );

  if (Params.ConfigFile <> '') then
    try
      FileName := Params.ConfigFile;
    except
      FileName := GamePath + 'config.ini';
    end
  else
    FileName := GamePath + 'config.ini';

  Log.LogStatus( 'Using config : ' + FileName , 'Ini');
  IniFile := TMemIniFile.Create( FileName );

  // Name
  for I := 0 to 11 do
    Name[I] := IniFile.ReadString('Name', 'P'+IntToStr(I+1), 'Player'+IntToStr(I+1));

  // Templates for Names Mod
  for I := 0 to 2 do
    NameTeam[I] := IniFile.ReadString('NameTeam', 'T'+IntToStr(I+1), 'Team'+IntToStr(I+1));
  for I := 0 to 11 do
    NameTemplate[I] := IniFile.ReadString('NameTemplate', 'Name'+IntToStr(I+1), 'Template'+IntToStr(I+1));
  
  // Players
  Players := GetArrayIndex(IPlayers, IniFile.ReadString('Game', 'Players', IPlayers[0]));
  
  // Difficulty
  Difficulty := GetArrayIndex(IDifficulty, IniFile.ReadString('Game', 'Difficulty', 'Easy'));
  
  // Language
  Language := GetArrayIndex(ILanguage, IniFile.ReadString('Game', 'Language', 'English'));
  //Language.ChangeLanguage(ILanguage[Language]);
  
  // Tabs
  Tabs := GetArrayIndex(ITabs, IniFile.ReadString('Game', 'Tabs', ITabs[0]));
  Tabs_at_startup := Tabs;	//Tabs at Startup fix
  
  // Song Sorting
  Sorting := GetArrayIndex(ISorting, IniFile.ReadString('Game', 'Sorting', ISorting[0]));
  
  // Debug
  Debug := GetArrayIndex(IDebug, IniFile.ReadString('Game', 'Debug', IDebug[0]));

  LoadScreenModes(IniFile);

  // TextureSize
  TextureSize := GetArrayIndex(ITextureSize, IniFile.ReadString('Graphics', 'TextureSize', ITextureSize[1]));

  // SingWindow
  SingWindow := GetArrayIndex(ISingWindow, IniFile.ReadString('Graphics', 'SingWindow', 'Big'));

  // Oscilloscope
  Oscilloscope := GetArrayIndex(IOscilloscope, IniFile.ReadString('Graphics', 'Oscilloscope', 'Bar'));

  // Spectrum
  Spectrum := GetArrayIndex(ISpectrum, IniFile.ReadString('Graphics', 'Spectrum', 'Off'));

  // Spectrograph
  Spectrograph := GetArrayIndex(ISpectrograph, IniFile.ReadString('Graphics', 'Spectrograph', 'Off'));

  // MovieSize
  MovieSize := GetArrayIndex(IMovieSize, IniFile.ReadString('Graphics', 'MovieSize', IMovieSize[2]));

  // ClickAssist
  ClickAssist := GetArrayIndex(IClickAssist, IniFile.ReadString('Sound', 'ClickAssist', 'Off'));

  // BeatClick
  BeatClick := GetArrayIndex(IBeatClick, IniFile.ReadString('Sound', 'BeatClick', IBeatClick[0]));

  // SavePlayback
  SavePlayback := GetArrayIndex(ISavePlayback, IniFile.ReadString('Sound', 'SavePlayback', ISavePlayback[0]));

  // AudioOutputBufferSize
  AudioOutputBufferSizeIndex := ReadArrayIndex(IAudioOutputBufferSize, IniFile, 'Sound', 'AudioOutputBufferSize', 0);

  //Preview Volume
  PreviewVolume := GetArrayIndex(IPreviewVolume, IniFile.ReadString('Sound', 'PreviewVolume', IPreviewVolume[7]));
  
  //Preview Fading
  PreviewFading := GetArrayIndex(IPreviewFading, IniFile.ReadString('Sound', 'PreviewFading', IPreviewFading[1]));

  //AudioRepeat aka VoicePassthrough
  VoicePassthrough := GetArrayIndex(IVoicePassthrough, IniFile.ReadString('Sound', 'VoicePassthrough', IVoicePassthrough[0]));

  // Lyrics Font
  LyricsFont := GetArrayIndex(ILyricsFont, IniFile.ReadString('Lyrics', 'LyricsFont', ILyricsFont[1]));

  // Lyrics Effect
  LyricsEffect := GetArrayIndex(ILyricsEffect, IniFile.ReadString('Lyrics', 'LyricsEffect', ILyricsEffect[1]));

  // Solmization
  Solmization := GetArrayIndex(ISolmization, IniFile.ReadString('Lyrics', 'Solmization', ISolmization[0]));

  // NoteLines
  NoteLines := GetArrayIndex(INoteLines, IniFile.ReadString('Lyrics', 'NoteLines', INoteLines[1]));

  //Encoding default
  // CP1252 is the USDX <1.1 default encoding
  EncodingDefault := ParseEncoding(IniFile.ReadString('Lyrics', 'Encoding', ''), encCP1252);

  LoadThemes(IniFile);

  // Color
  Color := GetArrayIndex(IColor, IniFile.ReadString('Themes',    'Color',   IColor[0]));

  LoadInputDeviceCfg(IniFile);

  // LoadAnimation
  LoadAnimation := GetArrayIndex(ILoadAnimation, IniFile.ReadString('Advanced', 'LoadAnimation', 'On'));

  // ScreenFade
  ScreenFade := GetArrayIndex(IScreenFade, IniFile.ReadString('Advanced', 'ScreenFade', 'On'));

  // Visualizations
  // <mog> this could be of use later..
  //  VisualizerOption :=
  //    TVisualizerOption(GetEnumValue(TypeInfo(TVisualizerOption),
  //            IniFile.ReadString('Graphics', 'Visualization', 'Off')));
  // || VisualizerOption := TVisualizerOption(GetArrayIndex(IVisualizer, IniFile.ReadString('Graphics', 'Visualization', 'Off')));
  VisualizerOption := GetArrayIndex(IVisualizer, IniFile.ReadString('Graphics', 'Visualization', 'Off'));

{**
 * Background music
 *}
  BackgroundMusicOption := GetArrayIndex(IBackgroundMusic, IniFile.ReadString('Sound', 'BackgroundMusic', 'Off'));

  // EffectSing
  EffectSing := GetArrayIndex(IEffectSing, IniFile.ReadString('Advanced', 'EffectSing', 'On'));

  // AskbeforeDel
  AskBeforeDel := GetArrayIndex(IAskbeforeDel, IniFile.ReadString('Advanced', 'AskbeforeDel', 'On'));

  // OnSongClick
  OnSongClick := GetArrayIndex(IOnSongClick, IniFile.ReadString('Advanced', 'OnSongClick', 'Sing'));

  // Linebonus
  LineBonus := GetArrayIndex(ILineBonus, IniFile.ReadString('Advanced', 'LineBonus', 'At Score'));

  // PartyPopup
  PartyPopup := GetArrayIndex(IPartyPopup, IniFile.ReadString('Advanced', 'PartyPopup', 'On'));

  // Joypad
  Joypad := GetArrayIndex(IJoypad, IniFile.ReadString('Controller',    'Joypad',   IJoypad[0]));

  LoadPaths(IniFile);
  
  IniFile.Free;
end;

procedure TIni.Save;
var
  IniFile:    TIniFile;
begin
  if (FileExists(Filename) and FileIsReadOnly(Filename)) then
  begin
    Log.LogError('Config-file is read-only', 'TIni.Save');
    Exit;
  end;

  IniFile := TIniFile.Create(Filename);

  // Players
  IniFile.WriteString('Game', 'Players', IPlayers[Players]);

  // Difficulty
  IniFile.WriteString('Game', 'Difficulty', IDifficulty[Difficulty]);

  // Language
  IniFile.WriteString('Game', 'Language', ILanguage[Language]);

  // Tabs
  IniFile.WriteString('Game', 'Tabs', ITabs[Tabs]);

  // Sorting
  IniFile.WriteString('Game', 'Sorting', ISorting[Sorting]);

  // Debug
  IniFile.WriteString('Game', 'Debug', IDebug[Debug]);

  // Screens
  IniFile.WriteString('Graphics', 'Screens', IScreens[Screens]);

  // FullScreen
  IniFile.WriteString('Graphics', 'FullScreen', IFullScreen[FullScreen]);

  // Visualization
  IniFile.WriteString('Graphics', 'Visualization', IVisualizer[VisualizerOption]);

  // Resolution
  IniFile.WriteString('Graphics', 'Resolution', IResolution[Resolution]);

  // Depth
  IniFile.WriteString('Graphics', 'Depth', IDepth[Depth]);

  // TextureSize
  IniFile.WriteString('Graphics', 'TextureSize', ITextureSize[TextureSize]);

  // Sing Window
  IniFile.WriteString('Graphics', 'SingWindow', ISingWindow[SingWindow]);

  // Oscilloscope
  IniFile.WriteString('Graphics', 'Oscilloscope', IOscilloscope[Oscilloscope]);

  // Spectrum
  IniFile.WriteString('Graphics', 'Spectrum', ISpectrum[Spectrum]);

  // Spectrograph
  IniFile.WriteString('Graphics', 'Spectrograph', ISpectrograph[Spectrograph]);

  // Movie Size
  IniFile.WriteString('Graphics', 'MovieSize', IMovieSize[MovieSize]);

  // ClickAssist
  IniFile.WriteString('Sound', 'ClickAssist', IClickAssist[ClickAssist]);

  // BeatClick
  IniFile.WriteString('Sound', 'BeatClick', IBeatClick[BeatClick]);

  // AudioOutputBufferSize
  IniFile.WriteString('Sound', 'AudioOutputBufferSize', IAudioOutputBufferSize[AudioOutputBufferSizeIndex]);

  // Background music
  IniFile.WriteString('Sound', 'BackgroundMusic', IBackgroundMusic[BackgroundMusicOption]);

  // Song Preview
  IniFile.WriteString('Sound', 'PreviewVolume', IPreviewVolume[PreviewVolume]);
    
  // PreviewFading
  IniFile.WriteString('Sound', 'PreviewFading', IPreviewFading[PreviewFading]);

  // SavePlayback
  IniFile.WriteString('Sound', 'SavePlayback', ISavePlayback[SavePlayback]);

  // VoicePasstrough
  IniFile.WriteString('Sound', 'VoicePassthrough', IVoicePassthrough[VoicePassthrough]);

  // Lyrics Font
  IniFile.WriteString('Lyrics', 'LyricsFont', ILyricsFont[LyricsFont]);

  // Lyrics Effect
  IniFile.WriteString('Lyrics', 'LyricsEffect', ILyricsEffect[LyricsEffect]);

  // Solmization
  IniFile.WriteString('Lyrics', 'Solmization', ISolmization[Solmization]);

  // NoteLines
  IniFile.WriteString('Lyrics', 'NoteLines', INoteLines[NoteLines]);

  //Encoding default
  IniFile.WriteString('Lyrics', 'Encoding', EncodingNames[EncodingDefault]);

  // Theme
  IniFile.WriteString('Themes', 'Theme', ITheme[Theme]);

  // Skin
  IniFile.WriteString('Themes', 'Skin', ISkin[SkinNo]);

  // Color
  IniFile.WriteString('Themes', 'Color', IColor[Color]);

  SaveInputDeviceCfg(IniFile);

  //LoadAnimation
  IniFile.WriteString('Advanced', 'LoadAnimation', ILoadAnimation[LoadAnimation]);

  //EffectSing
  IniFile.WriteString('Advanced', 'EffectSing', IEffectSing[EffectSing]);

  //ScreenFade
  IniFile.WriteString('Advanced', 'ScreenFade', IScreenFade[ScreenFade]);

  //AskbeforeDel
  IniFile.WriteString('Advanced', 'AskbeforeDel', IAskbeforeDel[AskBeforeDel]);

  //OnSongClick
  IniFile.WriteString('Advanced', 'OnSongClick', IOnSongClick[OnSongClick]);

  //Line Bonus
  IniFile.WriteString('Advanced', 'LineBonus', ILineBonus[LineBonus]);

  //Party Popup
  IniFile.WriteString('Advanced', 'PartyPopup', IPartyPopup[PartyPopup]);

  // Joypad
  IniFile.WriteString('Controller', 'Joypad', IJoypad[Joypad]);

  // Directories (add a template if section is missing)
  // Note: Value must be ' ' and not '', otherwise no key is generated on Linux
  if (not IniFile.SectionExists('Directories')) then
    IniFile.WriteString('Directories', 'SongDir1', ' ');

  IniFile.Free;
end;

procedure TIni.SaveNames;
var
  IniFile: TIniFile;
  I:       integer;
begin
  if not FileIsReadOnly(Filename) then
  begin
    IniFile := TIniFile.Create(Filename);

    //Name Templates for Names Mod
    for I := 0 to High(Name) do
      IniFile.WriteString('Name', 'P' + IntToStr(I+1), Name[I]);
    for I := 0 to High(NameTeam) do
      IniFile.WriteString('NameTeam', 'T' + IntToStr(I+1), NameTeam[I]);
    for I := 0 to High(NameTemplate) do
      IniFile.WriteString('NameTemplate', 'Name' + IntToStr(I+1), NameTemplate[I]);

    IniFile.Free;
  end;
end;

procedure TIni.SaveLevel;
var
  IniFile: TIniFile;
begin
  if not FileIsReadOnly(Filename) then
  begin
    IniFile := TIniFile.Create(Filename);

    // Difficulty
    IniFile.WriteString('Game', 'Difficulty', IDifficulty[Difficulty]);

    IniFile.Free;
  end;
end;

end.