{
    This file is part of Chentrah,
    Copyright (C) 2004-2014 Anton Rzheshevski (chebmaster@mail.ru).

    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.  If not, see http://www.gnu.org/licenses/

 **********************************************************************

    This file contains the module class implementation

 **********************************************************************}
  var
    sse2fname: ansistring = '';
    nosse2fname: ansistring = '';

  procedure TModule.Load;
  begin
//dbgsays('3');
    f_Index:= f_IndexToLoad;
    ResetMotherErrorState();
    Console.SetDefaultBg(dbTitle);
    if MotherState.DebugMode then AddLog(' Loading the module %0 ...', [_FName]);
    try
     {$ifndef buildmein}
      if f_Index > 0 then begin
        _UnloadMeDll;
        if not FileExists(_Fromdir + _FName)  then begin
          if not MotherState.SSe2Available and FileExists(_Fromdir + sse2fname)
            then Die(MI_NO_NOSSE2)
            else Die(MI_ERROR_MODULE_MISSING, [_Fromdir + _FName]);
        end;
        f_dll_age:= FileAge(_Fromdir + _FName);
        {$ifdef win32}
        if MotherState.DeveloperMode {and not RunningInWine} then begin
          chCopyFile(_Fromdir + _FName, _ToDir + _FName);
          chCopyFile(_Fromdir + ChangeFileExt(_FName, '.zd2'), _ToDir + ChangeFileExt(_FName, '.zd2'));
          _dllname:= _ToDir + _Fname;
          // Windows locks executable files so that
          // they could not be changed while they are running,
          // thus making recompile-on-the-fly impossible.

           //To hack around this, we make a copy
          // of the original dll and load that copy, leaving the original file
          // alone so that the compiler can rewrite it.

          //Since Wine doesn't emulate this particular feature of msWindows,
          // we can omit this step.
          //EDIT: they've implemented the file locking in 0.9.56
        end
        else
        {$endif}
          _dllname:= _FromDir + _Fname;//in Linux we don't have to bother.
        MotherState.ModuleThreadStackSize:= 1024 * 1024 * MList[f_Index].ThreadStackSize;
        _LoadMeDll(_dllname);
      end;
     {$endif}
      _LoadAddresses;
      AttemptNum:= -1; //Stop trying to reload it. The compilation is finished
      //  (as we succeeded in loading the procedure addressses) so it's a genuine failure.
      _Initialize;
      MotherState.ModuleState:= ms_Loaded;
      SwitchedConsoleOff:=false;
//dbgsays('4');
    except
      CurrentOwner:=NOT_A_MODULE;
      Die(MI_ERROR_CANNOT_LOAD_MODULE, [_Fname]);
    end;
  end;

  procedure TModule.Unload;
  begin
    _UnloadMeDll;
  end;

  constructor TModule.Create;
  begin
    inherited Create;
    f_Index:= NOT_A_MODULE;
    f_IndexToLoad:= NOT_A_MODULE;
    _RebuildModulesList;
   {$ifdef buildmein}
    Reload(1);
   {$else}
    ModuleCheckPeriod:=Config.IntChk['modules', 'CheckIfModulesAreRecompiledPeriod', 100, 60000];
    Reload(GetDefaultNum())
   {$endif}
  end;

  Procedure TModule._Initialize;
  begin
    MotherState.ModulePureName:= PureName;
    if MotherState.DebugMode then AddLog('Initializing the module %0 ...', [ExtractFileName(_FName)]);
    Try
      CurrentOwner:=ActiveOwner;
      if not
        M_Initialize(@MotherState) then begin
          CurrentOwner:= NOT_A_MODULE;
          raise exception.Create('');
        end;
      CurrentOwner:=NOT_A_MODULE;
      VerboseLogOk;
    Except
      CurrentOwner:=NOT_A_MODULE;
      Die(MI_ERROR_MODULE_CRASHED_AT_LOADING, [_FName]);
    End;
  end;

  destructor TModule.Destroy;
  begin
    if MotherState.ModuleState = ms_Loaded
    then begin
      if MotherState.DebugMode then AddLog('Unloading the module %0 ...', [ExtractFileName(_FName)]);
      MotherState.ModuleState:=ms_Exiting;
      Pulse;
      if MotherState.DebugMode then AddLogOk;
    end;
    inherited;
  end;

  function TModule._GetProcAddress(ProcName: PChar): Pointer;
  begin
  {$IFDEF windows}
    Result:=Windows.GetProcAddress(DLL, ProcName);
  {$ELSE}
    Result:=dlsym(DLL, ProcName);
  {$ENDIF}
//addlog('   pn %0  %1  (dll %2)', [PCharToString(procname), Result, pointer(dll)]);
  end;

  Procedure TModule._LoadAddresses;
  begin
    if f_Index > 0 then begin
      {$ifdef buildmein}
        pointer(M_Initialize):= @IM_Init;
        pointer(M_Pulse):= @IM_Pulse;
        pointer(M_Save):= @IM_Save;
        pointer(M_Free):= @IM_Free;
      {$else}
        pointer(M_Initialize):=_GetProcAddress(PChar('M_Init'));
        pointer(M_Pulse):=_GetProcAddress(PChar('M_Pulse'));
        pointer(M_Save):=_GetProcAddress(PChar('M_Save'));
        pointer(M_Free):=_GetProcAddress(PChar('M_Free'));
       {$ifndef buildmein}
        pointer(PassUnhandledException):= _GetProcAddress(PChar('M_PassUnhandledException'));
       {$endif}

        if not Assigned (M_Initialize)
           or not Assigned (M_Pulse)
           or not Assigned (M_Save)
           or not Assigned (M_Free)
          {$ifndef buildmein}
           or not Assigned (PassUnhandledException)
          {$endif}
           then Die(MI_ERROR_INVALID_MODULE, [_dllname]);
     {$endif}
    end
    else begin
      M_Initialize:= nil;
      M_Pulse:= nil;
      M_Save:= nil;
      M_Free:= nil;
     {$ifndef buildmein}
      PassUnhandledException:= nil;
     {$endif}
    end;
  end;

   function TModule._GetFname: string;
   begin
     Result:=_ToDir + _FName;
   end;

   procedure TModule._LoadMeDll(n: string);
   var
     repeatcount: integer = 0;
   begin
     {$ifdef unix}
       DLL:= dlopen(PChar(n), RTLD_NOW);
       If not Assigned(DLL) then Die('dlopen() returned NULL for'#10#13'  %0', [n]);
     {$else}
       repeat
         if repeatcount > 0 then Sleep(50);
         SetLastError(0);
         DLL:= LoadLibrary(PChar(n));
         inc(repeatcount);
       until (DLL <> 0) or (repeatcount > 13);
       if DLL = 0 then Die('LoadLibrary() returned zero for'#10#13'  %0', [n]);
     {$endif}
   end;

   procedure TModule._UnloadMeDll;
   begin
    {$ifndef buildmein}
     try
      {$ifdef unix}
       if DLL = nil then Exit;
       dlClose(DLL);
       DLL:=nil;
      {$else}
       if DLL = 0 then Exit;
       FreeLibrary(DLL);
       DLL:=0;
      {$endif}
     except
       try Die('Crashed trying to unload the module DLL') except end;
     end;
    {$endif}
   end;

  procedure TModule.Checkdate;
  var fa: longint;
  begin
    if f_Index < 1 then Exit;
    if Tick() < ReloadAt then Exit;
    fa:=FileAge(_Fromdir + _FName);
    if (fa > 0) and (f_dll_age = 0) then f_dll_age:=fa;
    if (fa > 0) and (fa <> f_dll_age)
    then begin
      PrevModuleDateCheckTick:=Tick;
      f_dll_age:=fa;
      if MotherState.ModuleState = ms_Loaded then MotherState.ModuleState:=ms_Unloading;
    end
    else ReloadAt:=Tick() + ModuleCheckPeriod;
  end;
  

  procedure TModule.Reload(i: integer);
  begin
//dbgsays('1');
    f_IndexToLoad:=i;
    BimLoaded:= No;
    if MotherState.ModuleState = ms_Loaded
      then MotherState.ModuleState:=ms_Unloading
      else MotherState.ModuleState:=ms_Loading;
      
    CgeHourglassCursor;
//    MotherState.Fadein:= 1.0;
    
    if i > 0 then begin
      //an external dll
      _ToDir:=OptiPath(MotherState.CacheDir + 'bin' + PathSlash);
      _MakeSurePathExists(PChar(_ToDir));
      CheckForGuardedException;
      _FName:=OptiFileName(MotherState.InstallPath + 'modules' + PathSlash + MList[i - 1].Dll);
      _FromDir:=ExtractFilePath(_Fname);
      _BaseDir:=_FromDir;
      PureName:=ChangeFileExt(ExtractFileName(_Fname), '');
      MotherState.ModuleNameW:= MList[i - 1].Name;
      MotherState.ModuleRequiresOpenGL2:= MList[i - 1].RequiresOpenGL2;
      sse2fname:=   DllPrefix + ChangeFileExt(PureName, DllExtension);
      nosse2fname:= DllPrefix + ChangeFileExt(PureName + '_nosse2', DllExtension);
      if MotherState.SSE2Available
        then _FName:= sse2fname
        else _FName:= nosse2fname;
      MotherState.ModuleFileName:= _FromDir + _Fname;
      ReloadAt:=Tick();
//dbgsays('2');
    end
    else
      GoToModuleSelectionMenu;
{      begin
      PureName:='<ERROR>';
      MotherState.ModuleNameW:='<ERROR>';
      if MotherState.InstallPath = ''
        then BimLoaded:= true
        else begin
          if MotherState.DebugMode then AddLog('Module.Reload(0) was called, setting the index to 1.');
          Reload(1);
        end;
    end;}
  end;
  
  procedure TModule.ReactToSpace;
  begin
//    if MotherState.ModuleState = ms_Crashed then GoToModuleSelectionMenu;
    if MotherState.ModuleState = ms_Crashed then begin
      MotherState.ModuleState:=ms_Loading;
      ReloadAt:=Tick();
    end;
  end;
  
  procedure TModule.ReactToEsc;
  begin
    if MotherState.ModuleState = ms_Crashed then MotherState.ExitRequested:=Yes;
  end;
  
  function TModule.GetDefaultNum: integer;
  var
    i: integer;
    s: ansistring;
  begin
    Result:= 0;
    s:= ParamStr(1);
    if (s= '') or (copy (s, 1, 2) = '--')
    then begin
      s:= Config.Str['session', 'MostRecentUsedModule'];
      if s = '' then s:= Config.Str['Main', 'DefaultModule'];
    end;
    s:=lowercase(s);
    if s > '' then
      for i:=0 to high(MList) do
        if MList[i].PureN = s then Exit(i + 1);
    AddLog(RuEn(
      'WARNING: "%0" нет в текущем списке dll модулей. Вызываю меню выбора модуля.',
      'WARNING: "%0" is not a currently known module dll name. Invoking the module selection menu.'),
       [s]);
  end;
  

  function TModule.Pulse(): boolean;
  var
    success, pesets: boolean;
    a: pointer;
  begin
    if MotherState.ModuleState <> ms_Loaded
      then MotherState.TextInput:= false;

    if MessageContainer.CurrentLanguage >=0 then GiveWarnings();
    { A one-shot function that fills the warnings list,
      must be called only after the language is chosen.
      I'd wish I hadn't have to put this call here, but... oh, well :|    }
      
    if ((length (WarningMessage) > 0)
      or (Length(StopMessage) > 0)
      or (MessageContainer.CurrentLanguage < 0))
      and not BimLoaded
      then Module.GoToModuleSelectionMenu; //go to the mother menu

    if BimLoaded then begin
      Result:= True;
      CurrentOwner:= NOT_A_MODULE;
      if not BimPulse() then begin

        AddLog(MI_DEVMODE_MODULE_UNLOADED, [StopDying()]);
        MotherState.ModuleState:= ms_Crashed;
        BimLoaded:= No;
      end
      else begin
        if not SwitchedConsoleOff then begin
          Console.Fade;
          SwitchedConsoleOff:=Yes;
        end;
      end;
    end
    else begin
//      MotherState.Fadein:= 1.0;
      if MotherState.LogoAnimationPlaying then Exit(Yes)
      else
        case MotherState.ModuleState of
          ms_Loading: begin
  //dbgsays('ms_Loading');
            Result:=true;
            try
  //byte(nil^):=0;
              Config.Str['session', 'MostRecentUsedModule']:= MList[f_IndexToLoad - 1].PureN;
              if MotherState.StateTrashedRestartRequired then begin
                 MotherState.ModuleState:= ms_DoNothing;
                 AddLog(RuEn(
                   'Нарушена целостность структуры данных приложения. Принудительный перезапуск приложения.',
                   'Application state is trashed, forcing application restart.'));
                 MotherState.RestartRequested:= Yes;
              end
              else Load;

  //dbgsays('Loaded.');
            except
  //dbgsays('boom!');
              result:=false;
              ReloadAt:=Tick() + ModuleCheckPeriod;
              if (AttemptNum <= 0) or (AttemptNum > MAX_MODULE_LOAD_ATTEMPTS) then begin
                AttemptNum:= -1;
                if MotherState.DebugMode or MotherState.DeveloperMode
                then begin
                  AddLog(MI_DEVMODE_MODULE_UNLOADED, [StopDying()]);
                  MotherState.ModuleState:=ms_Crashed;
                  if Assigned(SoundManager) then SoundManager.PlayWav('oops.wav');
                end
                else
                  _SwitchToStopScreen(false);
                _UnloadMeDll;
                Exit;
              end
              else begin
                AddLog(RuEn(
                  'Попытка #%0 провалилась'#10#13#10#13'%1'
                 ,'Attempt #%0 failed.'#10#13#10#13'%1')
                 ,[AttemptNum, StopDying()]);
                inc(AttemptNum);
              end;
            end;
          end;
          ms_Unloading: begin
            Result:=false;
            CurrentOwner:= ActiveOwner;
            if M_Save() then begin
              if MotherState.ModuleHasThreadsRunning then begin
                MotherState.ModuleState:= ms_WaitingForThreadsToTerminate;
                MotherState.WaitingForModuleThreadsToTerminateTimeoutMoment:= Now() + MODULE_THREADS_STOP_TIMEOUT;
              end else begin
                CurrentOwner:= NOT_A_MODULE;
                Unload;
                MotherState.ModuleState:=ms_Loading;
                AttemptNum:=1;
              end;
            end
            else begin
              CurrentOwner:= NOT_A_MODULE;
              if MotherState.Fatality then _ProcessFatality; //causes immediate restart
              if MotherState.DeveloperMode or MotherState.DebugMode
              then begin
                AddLog(MI_ERROR_MODULE_CRASHED_UNLOADING, [StopDying()]);
                if MotherState.ModuleHasThreadsRunning then MotherState.StateTrashedRestartRequired:= Yes;
                MotherState.ModuleState:=ms_Crashed;
                if Assigned(SoundManager) then SoundManager.PlayWav('oops.wav');
  //              _UnloadMeDll;
              end
              else
                _SwitchToStopScreen(false);
              Result:=False;
            end;
          end;
          ms_WaitingForThreadsToTerminate: begin
            CurrentOwner:=ActiveOwner;
            try
              success:= M_Pulse(); //The module is required to stop all activity except displaying the "Waiting for background tasks to terminate" screen.
              SetLength(MotherState.InputEvents, 0);
              pesets:=false;
            except
              CurrentOwner:= NOT_A_MODULE;
              success:= false;
              pesets:= true;
             {$ifndef buildmein}
              try
                a:= ExceptAddr();
                Die(RuEn(
                  'ФАТАЛЬНО:'#10#13'  непойманное исключение пересекло границу dll''ы!',
                  'FATAL:'#10#13'  an uncaught exception crossed the dll boundary!'))
              except end;
             {$endif}
            end;
            if not success then begin
              MotherState.RollBackTheSessionOnNextLoad:= False;
              CurrentOwner:=NOT_A_MODULE;
              if MotherState.Fatality then _ProcessFatality; //causes immediate restart
              MotherState.StateTrashedRestartRequired:= Yes;
              if MotherState.DeveloperMode or MotherState.DebugMode then begin
                AddLog(MI_DEVMODE_MODULE_UNLOADED, [StopDying()]);
                MotherState.ModuleState:=ms_Crashed;
                Result:=False;
              end
              else
                _SwitchToStopScreen(pesets);
            end
            else begin
              if MotherState.ModuleHasThreadsRunning then begin
                if Now() > MotherState.WaitingForModuleThreadsToTerminateTimeoutMoment then begin
                  MotherState.ModuleState:= ms_DoNothing;
                  AddLog(RuEn(
                    'Превышено время ожидания завершения фоновых задач. Принудительный перезапуск приложения.',
                    'Timeout. Background jobs take too long to terminate. Forcing application restart.'));
                  MotherState.RestartRequested:= Yes;
                end;
              end else begin
                CurrentOwner:= NOT_A_MODULE;
                Unload;
                MotherState.ModuleState:=ms_Loading;
                AttemptNum:=1;
              end;
            end;
          end;
          ms_Exiting: begin
            {There's no need to care about the dll threads here,
              as the Chentrah process calls TerminateProcess() on itself as it finishes.}
            if {$ifdef unix} DLL <> nil {$else} DLL <> 0 {$endif} then begin
              CurrentOwner:= ActiveOwner;
              if not M_Save() then begin
                CurrentOwner:= NOT_A_MODULE;
                Die(MI_ERROR_MODULE_CRASHED_UNLOADING_SHORT, [StopDying()]);
              end;
              if not M_Free() then begin
                CurrentOwner:= NOT_A_MODULE;
                Die(MI_CRASHED_UNLOADING_MODULE, [MList[f_Index].Name, StopDying()]);
              end;
              Unload;
            end;
            Result:=True;
          end;
          ms_Loaded: begin
  //dbgsays('ms_Loaded');
  //dbgsays('1');
            CurrentOwner:=ActiveOwner;
            try
              success:= M_Pulse();
              SetLength(MotherState.InputEvents, 0);
              pesets:=false;
  //dbgsays('sehr gut!');
            except
//  WriteLn('oopsie!'); Halt(0);
              CurrentOwner:= NOT_A_MODULE;
              success:= false;
              pesets:= true;
             {$ifndef buildmein}
              try
                a:= ExceptAddr();
                Die(RuEn(
                  'ФАТАЛЬНО:'#10#13'  непойманное исключение пересекло границу dll''ы!',
                  'FATAL:'#10#13'  an uncaught exception crossed the dll boundary!')
               {$ifndef unix}
                 //call the dll internal self-debugger
               //  + #10#13'%0', [M_ReportDebugBacktrack(a)] //not necessary anymore
               {$endif}
              ) except end;
              {$endif}
            end;
            if not success then begin
//  dbgsays('a');
              MotherState.RollBackTheSessionOnNextLoad:= False;
              CurrentOwner:=NOT_A_MODULE;
              if MotherState.Fatality then _ProcessFatality; //causes immediate restart
              if MotherState.ModuleHasThreadsRunning then MotherState.StateTrashedRestartRequired:= Yes;
              if MotherState.DeveloperMode or MotherState.DebugMode then begin
                AddLog(MI_DEVMODE_MODULE_UNLOADED, [StopDying()]);
                MotherState.ModuleState:=ms_Crashed;
                Result:=False;
                if Assigned(SoundManager) then SoundManager.PlayWav('oops.wav');
              end
              else
                _SwitchToStopScreen(pesets);
            end
            else begin
  //dbgsays('b');
              CurrentOwner:=NOT_A_MODULE;
              if not SwitchedConsoleOff then begin
                Console.Fade;
                SwitchedConsoleOff:=Yes;
              end;
              Result:=True;
              AttemptNum:=0;
              CheckDate;
            end;
            if MotherState.ModuleRequestToChooseModule then begin
               MotherState.ModuleRequestToChooseModule:= No;
               GoToModuleSelectionMenu;
            end;
  //dbgsays('2');
          end;
          ms_Crashed: begin
            Result:= false;
            ReloadAt:= 0;
            f_dll_age:= 0;
          end;
        end;
    end;
  end;
  
  procedure TModule._SwitchToStopScreen(itisfatal: boolean);
  begin
//dbgsays('b');
    BimLoaded:= Yes;
    Console.Fade;
    AddMStopMessage(itisfatal);
    cl_builtinmodule.prevState:= bimsInvalid;
    if itisfatal then
      cl_builtinmodule.bimState:= bimsModuleFatalError
    else begin
      cl_builtinmodule.bimState:= bimsModuleError;
//      _UnloadMeDll;
    end;
    MotherState.ModuleState:= ms_Crashed;
    if Assigned(SoundManager) then SoundManager.PlayWav('crashed.wav');
//dbgsays('c');
  end;

  procedure TModule._ProcessFatality;
  var
    F: TextFile;
    w: WideString;
  begin
    w:= StopDying();
    AddLog(
       RuEn(
         'Обнаружена фаталити, пишу завещание и самоубиваюсь:#10#13#10#13%0',
         'Fatality detected, writing the last will and terminating myself:#10#13#10#13%0'),
       [w]
     );
    try
      _MakeSurePathExists(_UnmangleFileName('*H/logs'));
      AssignFile(F, _UnmangleFileName('*H/logs/last-last-will.txt'));
      Rewrite(F);
      WriteLn(F, Utf8Encode(w));
      CloseFile(F);
      RestartMyself('--last-will');
    except
      AddLog(RuEn(
        'Не удалось записать файл с завещанием, перезаупуск невозможен!',
        'Failed to write the last will file, restart is not possible!'));
    end;
    KillMyself;
  end;

  procedure TModule.GoToModuleSelectionMenu;
  begin
    _RebuildModulesList;
    BimLoaded:= Yes;
    Console.Fade;
    cl_builtinmodule.prevState:= bimsInvalid;
    cl_builtinmodule.bimState:= bimsModuleMenu;
  end;

  procedure TModule.RollbackSession;
  begin
    if MotherState.ModuleState <> ms_Loaded then begin
      AddLog(RuEn('Откат сессии не выполнен так как модуль не загружен.','Session rollback canceled: no module is loaded.'));
      Exit;
    end;
    MotherState.RollBackTheSessionOnNextLoad:= Yes;
    BimLoaded:= No;
    Reload(f_indextoload);
  end;
  
  procedure TModule._RebuildModulesList;
  var
    F: TFileList;
    ini: TIniFile;
    i, cl: integer;
    w: WideString = '';
    ModulesRequiringOGL2: WideString = '';
  begin
    if MotherState.InstallPath = '' then begin
      SetLength(MList, 0);
      SetLength(ModuleCiMs, 0);
      ListAlreadyBuilt:= Yes;
    end;

    if ListAlreadyBuilt then Exit;

   {$ifdef buildmein}
    SetLength(MList, 1);
    SetLength(ModuleCiMs, 1); ModuleCiMs[1]:= nil;
    FillChar(MList[0], SizeOf(MList[0]), 0);

    if MotherState.VerboseLog then AddLog(RuEn('Найдено %0 модулей', '%0 modules found'), [1]);
    with MessageContainer do begin
      MList[0].PureN:= lowercase(MyInternalModuleBasePath);
      ini:= TIniFile.Create(MotherState.InstallPath + 'modules' + MyInternalModuleBasePath + '.module');
      MList[0].Name:= RuEn('Встроенный','Default');
      MList[0].Dll:= MyInternalModuleBasePath;//shit. I forgot it was used in session files names... //:= '<default>';
      MList[0].RequiresOpenGL2:= 0 <> ini.readInteger('module', 'requires_opengl_2', 0);
      if MList[0].RequiresOpenGL2 then ModulesRequiringOGL2+= '  ' + MList[0].Name + #10#13;
      Mlist[0].ThreadStackSize:= ini.readInteger('module', 'thread_stack_size_megabytes', 1);
      ini.Free;
    end;
   {$else}

    F:= TFileList.Create(IncludeTrailingPathDelimiter(
                            MotherState.InstallPath + 'modules') + '*.module');
    if F.Count = 0 then Die('Не найдено ни одного модуля!','No modules found!', []);
    F.SortByDate;
    SetLength(MList, F.Count);
    SetLength(ModuleCiMs, F.Count);
    for i:= 0 to High(ModuleCiMs) do ModuleCiMs[i]:= nil;
    FillChar(MList[0], SizeOf(MList[0]) * Length(MList), 0);
    
    if MotherState.VerboseLog then AddLog(RuEn('Найдено %0 модулей', '%0 modules found'), [F.Count]);
    with MessageContainer do
      for i:= F.Count - 1 downto 0 do begin
        MList[i].PureN:= lowercase(ExtractFileName(ChangeFileExt(F.Names[i], '')));
        ini:= TIniFile.Create(F.Names[i]);
        if CurrentLanguage >=0 then begin
          cl:= CurrentLanguage;
          w:= Utf8Decode(ini.ReadString('module', 'name-' + LanguageId[CurrentLanguage],''));
        end
        else cl:= SubstituteLanguage;
        if w = '' then
          w:= Utf8Decode(ini.ReadString('module', 'name-' + LanguageId[SubstituteLanguage],''));
        if w = '' then Die(
          'В %0 не задано имя модуля,'#10#13'  как для языка %1, так и для языка %2.',
          'Undefined module name in %0'#10#13'  for both %1 and %2 languages.',
          [ExtractFileName(F.Names[i]),
           LanguageName(cl), LanguageName(SubstituteLanguage)]);
        MList[i].Name:= w;
        MList[i].Dll:= ini.readString('module', 'dll', '');
        if MList[i].Dll = '' then Die(
          'В %0 не задана dll модуля.',
          'Undefined module dll in %0', [ExtractFileName(F.Names[i])]);
        MList[i].RequiresOpenGL2:= 0 <> ini.readInteger('module', 'requires_opengl_2', 0);
        if MList[i].RequiresOpenGL2 then  ModulesRequiringOGL2+= '  ' + MList[i].Name + #10#13;
        Mlist[i].ThreadStackSize:= ini.readInteger('module', 'thread_stack_size_megabytes', 1);
        ini.Free;
      end;
    F.Free;
   {$endif}
    ListAlreadyBuilt:= Yes;


    if ((MotherState.OGLVersionHi < OGL_REQ_HI2)
      or ((MotherState.OGLVersionHi = OGL_REQ_HI2) and (MotherState.OGLVersionMed < OGL_REQ_MED2)))
    and (ModulesRequiringOGL2 <> '')
      then GiveWarning('ogl_too_low_' + IniCompatibleStringForm(MotherState.OGLSummary),
        MI_WARN_WARNING, MI_WARNING_OPENGL_VERSION_TOO_LOW,
      [OGL_REQ_HI2, OGL_REQ_MED2,
        ModulesRequiringOGL2,
        MotherState.OGLVersionString,
        MotherState.OGLRenderer,
        MotherState.OGLVendor]);
  end;
  

