{
    This file is part of Chentrah,
    Copyright (C) 2004-2008 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 is contains the basic functions exported from
    the mother module to the game module DLL.

 **********************************************************************}

{$include cl_ctimer_func.inc}

{$include cl_vampyre_func.inc}

{$include cl_cursor_func.inc}

  procedure CgeNewSoundData(begins: TDateTime; samples, rate: integer; data: pointer); cdecl;
  begin
   try
    if Assigned(SoundManager)
      then SoundManager.NewData(begins, samples, rate, data);
   except
    ProcessGuardedException();
   end;
  end;

  procedure CgeSetSoundVolume(volume: float); cdecl;
  begin
   try
     if Assigned(SoundManager)
      then SoundManager.SetVolume(volume);
   except
    ProcessGuardedException();
   end;
  end;

  function UnmangleFileName (m: PAnsiChar): WideString;
  begin
    Result:=AnsiToWide(PCharToString(_UnmangleFileName(m)));
    CheckForGuardedException;
  end;

{$include cl_hash.inc}
{
  procedure CgeTryWrap (backwrapperproc, calledproc: pointer); cdecl;
  var U: WideString;
  begin
    try
      TTryWrapperProcedure(backwrapperproc)(calledproc);
    except
      if not MotherState.NowDying then begin
        CheckForGenericDyingYells(U);
        U:= PervertedFormat(RuEn('При вызове %0', 'In call to %0'),
          [ExpExpAddress(calledproc)]) + U;
        AddYell(U);
      end;
    end;
  end;
 }

  var umfn: ansistring;
  function _UnmangleFileName (m: PAnsiChar): pAnsiChar; cdecl;
  var
    a: AnsiString;
  begin
    try
    a:=PCharToString(m);
    if a[1]<>'*' then umfn:=a
    else begin
      case a[2] of
        'M': begin
               if Assigned(Module) then umfn:=Module._Basedir
               else Die(MI_ERROR_PROGRAMMER_NO_BAKA, ['Module = NIL when called UnmangleFileName(''' + a + ''')']);
             end;
        'C': umfn:=MotherState.CacheDir;
        'S': umfn:=MotherState.SessionDir;
        'H': umfn:=MotherState.HomePath;
        'I': umfn:=MotherState.InstallPath;
      else
        Die(MI_ERROR_PROGRAMMER_NO_BAKA, ['Unknown symbol "' + a[2] +'" in the mangled file name "'+a+'".']);
      end;
      umfn:=umfn + Copy(a, 4, length(a) - 3)
    end;
//addlog('--"%0"->"%1"', [a , umfn]);
    Result:=PAnsiChar(umfn);
   except
    ProcessGuardedException();
   end;
  end;
  

  procedure r_MakeSurePathExists (path: AnsiString);
  var
    sl: TStringList;
    j: integer;
    p: string = '';
  begin
    if DirectoryExists(path) then Exit;
    sl:=TStringList.Create;
    sl.Text:=(StrReplace(StrReplace(ExtractFilepath(CgePath(path)), '\', #13), '/', #13));
    if sl.Count > 1 then begin //the file system root is  not yet reached
      p:= sl[0];
      for j:= 1 to sl.Count - 1 do begin
       if not DirectoryExists(IncludeTrailingPathDelimiter(p) + sl[j]) then begin
         AddLog(RuEn('Создаю папку "%0" ...','Creating folder "%0" ...'), [IncludeTrailingPathDelimiter(p) + sl[j]]);
         MkDir(IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(p) + sl[j]));
         AddLogOk;
       end;
       p:= IncludeTrailingPathDelimiter(p) + sl[j];
      end;
    end;
    sl.Free;
  end;


  procedure _MakeSurePathExists (path: PAnsiChar); cdecl;
  var
    sl: TStringList;
    j: integer;
    p: string;
  begin
    try
      r_MakeSurepathExists(CgePath(PCharToString(path)));
    except
      ProcessGuardedException();
    end;
  end;
  
  function ValidateWritePath (path: AnsiString): boolean;
  var
    sl: TStringList;
    j: integer;
    p: string;
    f: file;
    IsNetworkFolder: boolean = false;
  begin
    sl:=TStringList.Create;
    try
     {$ifdef windows}
      { Drekk. >_< A network folder on windows can start with "\\".
        Moreover, some folders in its path might not be accessible.}
      if copy(path, 1, 2) = '\\' then begin
        p:= IncludeTrailingPathDelimiter(ExtractFilePath(path));
        if not DirectoryExists(p) then Exit(false);
      end
      else begin
     {$endif}
        sl.Text:=(StrReplace(StrReplace(ExtractFilepath(CgePath(path)), '\', #13), '/', #13));
        { On Windows, sl[0] will be drive "c:", "d:", etc.
           On Unix it's always an empty string, as any path begins with "/"}
        p:=sl[0] + DirectorySeparator;
        For j:= 1 to sl.Count - 1 do begin
          p:=p + sl[j] + DirectorySeparator;
          if not DirectoryExists(p) then MkDir(p);
        end;
     {$ifdef windows}
      end;
     {$endif}
      p:=IncludeTrailingPathDelimiter(p) + 'delete-me-im-a-temporary-file-' + AppNick;
      AssignFile(f, p);
      Rewrite(f, 10);
      BlockWrite(f, p[1], 1);
      Close(f);
      Erase(f);
      Result:=True;
    except
      Result:=False;
    end;
    sl.Free;
  end;


  function _GetCGM(): integer; cdecl;
  begin
  end;
  
{
  function GetIsRussian(): boolean;cdecl;
  begin
    Result:= MessageContainer.CurrentLanguage = 0;
  end;
}
  function GetHigherPowerOf2 (i: integer): integer; cdecl;
  begin
    if (cardinal(i) > (1 shl 31)) then Exit(0);
    Result:=1;
    repeat
      Result:=Result shl 1
    until Result >= i;
  end;
  
  function GetLowerPowerOf2 (i: integer): integer; cdecl;
  var p: integer = 0;
  begin
    repeat inc(p) until (i shr p) = 0;
    Result:=1 shl (p - 1);
  end;
  
  procedure _cgeSetBackground(FileName: PAnsiChar); cdecl;
  begin
    try
    if not GetWindowExists or not Assigned(Console) then Exit;
    Console.SetBg(PCharToString(FileName));
   except
    ProcessGuardedException();
   end;
  end;
  
  procedure _cgeSetBackgroundPtr(p: pointer; size: integer); cdecl;
  begin
    try
    if not GetWindowExists or not Assigned(Console) then Exit;
    Console.SetBgPtr(p, size);
   except
    ProcessGuardedException();
   end;
  end;

  
  var cgehbPrevNow: TDateTime = 0;
  procedure _cgeHeartBeat(); cdecl;
  var n: TDateTime;
  begin
    try
 //   if CurrentOwner <> NOT_A_MODULE then Exit;
    if not GetWindowExists then Exit;
    n:=(Now() - cgehbPrevNow) * 86400000.0;
    if n < 100 then Exit;
    Console.Draw(false, true);
    _cgeSwapBuffers;
    cgehbPrevNow:=Now;
   except
    ProcessGuardedException();
   end;
  end;
  
  function GetWindowExists(): boolean; cdecl;
  begin
    Result:=Assigned(WindowManager) and (MotherState.WindowExists);
  end;

   var _std2e: WideString;
  function _exp_StopDying(): PWideChar; cdecl;
  begin
    _std2e:=StopDying();
    Result:=@_std2e[1];
  end;

  procedure _CgeSwapBuffers(); cdecl;
  begin
    try
    WindowManager.SwapBuffers;
   except
    ProcessGuardedException();
   end;
  end;
  

{  function GetCurrentSessionID(): int64; cdecl;
  begin
    Result:=TimeStamp;
  end;
}
  
  var
    PrevTimeStamp: qword = 0;
    
  function GetTimeStamp: qword;
  begin
    asm
      pushf
      {$ifdef cpu64}
        push rdx
        push rax
      {$else}
        push edx
        push eax
      {$endif}
      rdtsc
      mov dword[Result], eax
      mov dword[Result + 4], edx
      {$ifdef cpu64}
        pop rax
        pop rdx
      {$else}
        pop eax
        pop edx
      {$endif}
      popf
    end;
    if Result <= PrevTimeStamp then begin
      Result:=PrevTimeStamp + 1;
      inc(PrevTimeStamp);
    end
    else
      PrevTimeStamp:=Result;
  end;

{  procedure NewSessionID(); cdecl;
  begin
    TimeStamp:=GetTimeStamp();
  End;}

 procedure _SetConfigInt (section, id: PAnsiChar; Value: integer); cdecl;
 begin
   try
   Config[section, id]:=value;
   except
    ProcessGuardedException();
   end;
 end;
 
 function _GetConfigInt (section, id: PAnsiChar): integer; cdecl;
 begin
   try
   Result:=Config[section, id];
   except
    ProcessGuardedException();
   end;
 end;

procedure _SetConfigStr (section, id, Value: PAnsiChar); cdecl;
begin
  try
   Config.Str[section, id]:= Value;
  except
   ProcessGuardedException();
  end;
end;

var sGetConfigStrResult: AnsiString;
function _GetConfigStr (section, id: PAnsiChar): PAnsiChar; cdecl;
begin
  try
   sGetConfigStrResult:= Config.Str[section, id];
   Result:= PAnsiChar(sGetConfigStrResult);
  except
   ProcessGuardedException();
  end;
end;

 function _GetConfigIntCh (section, id: PAnsiChar; min, max:integer): integer; cdecl;
 begin
   try
   Result:=Config.IntChk[section, id, min, max];
   except
    ProcessGuardedException();
   end;
 end;

 procedure _PlaySound(Wavfilename: PAnsiChar); cdecl;
  begin
    try
      if Assigned(SoundManager) then SoundManager.PlayWav(Wavfilename);
    except
     ProcessGuardedException();
    end;
  end;





{  Procedure HostDieW(txt: PWideChar); cdecl;
  begin
    Die(PWideCharToWideString(txt));
  end;
}
{  procedure HostRaiseException; cdecl;
  begin
    Raise Exception.Create('');
  end;
}
  
  Procedure _HostDyellW(txt: PWideChar); cdecl;
  var u: WideString;
  begin
//    try
    u:=PWideCharToWideString(txt);
    if not MotherState.NowDying then begin
      AddLog(MI_SHIT_HAPPENS_SEE_BELOW, []);
      MotherState.NowDying:=Yes;
    end;
//addlog('_HostDyellW: %0', [u]);
    WarningQueue.Add(u);
//    LeaveGuardedSection;
  end;
  

{   var _hoe: WideString;
  Function HostException: PWideChar; cdecl;
  begin
    _hoe:='';
    if Assigned(ExceptObject)
    
    
    Result:=PWideChar(_hoe);
  end;}

  Procedure _HostLogW(txt: PWideChar); cdecl;
  begin
//    try
    AddLog(PWideCharToWideString(txt));
//    LeaveGuardedSection
  end;
  
  Procedure DbgSayA (Yell: PAnsiChar); cdecl;
  begin
    DbgSay(PCharToString(Yell));
  end;

  procedure _ValidateTypesetsEq(n: PAnsiChar; v1, v2: integer); cdecl;
  begin
    try
    if v1 <> v2 then Die(MI_ERROR_TYPESETS_MISMATCH,[string(n), v1, v2]);
   except
    ProcessGuardedException();
   end;
  end;
  
  procedure _ValidateTypesetsLe(n: PAnsiChar; v1, v2: integer); cdecl;
  begin
    try
    if v1 > v2 then Die(MI_ERROR_TYPESETS_MISMATCH,[string(n), v1, v2]);
   except
    ProcessGuardedException();
   end;
  end;

  
  procedure _ValidateTypesets(i, n: integer); cdecl;
  begin
    try
    Case n of
      0: if i <> ord (MI_CORE_MIDs_END) then Die(MI_ERROR_TYPESETS_MISMATCH,['TMessageId', i, ord (MI_CORE_MIDs_END)]);
      1: if i <> ord (Re_OnUnload) then Die(MI_ERROR_TYPESETS_MISMATCH,['TCbMessage', i, ord (Re_OnUnload)]);
//      2: if i <> sizeof(TiCharArray) then Die(MI_ERROR_TYPESETS_MISMATCH,['TiCharArray', i, sizeof(TiCharArray)]);
    else
      Die(MI_ERROR_TYPESETS_MISMATCH,['ValidateTypesets()']);
    end;
   except
    ProcessGuardedException();
   end;
  end;

  procedure _RequestExit; cdecl;
  begin
    try
    if not(not GetWindowExists or not Assigned(Console))
      then Console.SetDefaultBg(dbShutdown);
    MotherState.ExitRequested:=Yes;
   except
    ProcessGuardedException();
   end;
  end;

{    function GetContainerObject (Name: AnsiString): TContainer;
    var
      i: integer;
    begin
      i:=Containers.IndexOf(Name);
      if i < 0 then Result:=nil
      else Result:=Containers.Objects[i] as TContainer;
    end;
}

  procedure _ValidateVersion (m, n, b: integer); cdecl;
  begin
    try
    if (m > VersionMajor) or (n > VersionMinor) then
      DIE(MI_VERSIONS_MISMATCH, [VersionToStr(m, n, b), VersionToStr(VersionMajor, VersionMinor, BuildNumber)]);
   except
    ProcessGuardedException();
   end;
  end;

    var _bd2e, _lsd: AnsiString;

{  function _CreateContainer(Name: PAnsiChar; Size: integer): pointer; cdecl;
  var
    i: integer;
    C: TContainer;
    n: Ansistring;
  begin
    try
    n:=PCharToStr(Name);
    i:=Containers.IndexOf(n);
    if i < 0
      then C:=TContainer.Create(size)
      else begin
        C:=Containers.Objects[i] as TContainer;
        C.Size:=Size;
      end;
    Result:=C.MemoryPtr;
    if i < 0
      then Containers.AddObject(n, C);
   except
    ProcessGuardedException();
   end;
  end;
  
  function _GetContainer (Name: PAnsiChar): pointer; cdecl;
  var
    i: integer;
    n: Ansistring;
  begin
   try
    n:=PCharToStr(Name);
    i:=Containers.IndexOf(n);
    if i < 0 then Result:=nil
    else Result:=(Containers.Objects[i] as TContainer).MemoryPtr;
   except
    ProcessGuardedException();
   end;
  end;
  
  function _GetContainerSize (name: PAnsiChar): integer;   cdecl;
  var
    i: integer;
    n: Ansistring;
  begin
   try
    n:=PCharToStr(Name);
    i:=Containers.IndexOf(n);
    if i < 0 then Result:=0
    else Result:=(Containers.Objects[i] as TContainer).Size;
   except
    ProcessGuardedException();
   end;
  end;
  
  procedure _DeleteContainer (Name: PAnsiChar); cdecl;
  var
    i: integer;
    n: Ansistring;
  begin
   try
    n:=PCharToStr(Name);
    i:=Containers.IndexOf(n);
    if i < 0 then Exit;
    Containers.Objects[i].Free;
    Containers.Delete(i);
   except
    ProcessGuardedException();
   end;
  end;
  
  procedure _StoreContainerToFile (Name, FileName: PAnsiChar); cdecl;
  var
    F: TCGEStream;
    N, FN: AnsiString;
    p:pointer;
  begin
   try
    N:=PCharToStr(name);
    FN:=PCharToStr(filename);
    p:=_GetContainer(name);
    CheckForGuardedException;
    if not Assigned(p) then Die (MI_ERROR_ATTEMPT_TO_STORE_NONEXISTENT_CONTAINER,
      [N, FN]);
    F:=TCGEStream.CreateForWrite(FN);
    F.Write(p^, _GetContainerSize(Name));
    CheckForGuardedException;
    F.Free;
   except
    ProcessGuardedException();
   end;
  end;
  

  function _CreateContainerFromFile (Name, FileName: PAnsiChar; var Size: integer): pointer; cdecl;
  var
    i: integer;
    C: TContainer;
    N, FN: AnsiString;
    FS: TCGEStream;
  begin
   try
    N:=PCharToStr(name);
    FN:=PCharToStr(filename);
    FS:=TCGEStream.CreateForRead(FN);
    i:=Containers.IndexOf(N);
    if i < 0
      then begin
        C:=TContainer.Create(0);//(ModuleManager.ActiveModule.Num)
        C.FileName:=FN;
        C.LoadFromStream(FS, 0);
      end
      else begin
        C:=Containers.Objects[i] as TContainer;
        C.Clear;
        C.FileName:=FN;
        C.LoadFromStream(FS, 0);
      end;
    FS.Free;
    Result:=C.MemoryPtr;
    Size:=C.Size;
    if i < 0
      then Containers.AddObject(N, C);
   except
    ProcessGuardedException();
   end;
  end;
  
  }
  
  procedure _cgeDrawBackground(); cdecl;
  begin
    try
     if Assigned(Console) then Console.DrawBackground();
   except
    ProcessGuardedException();
   end;
  end;
  
    var _Mi2e: WideString;
  function _HostMsg (M: TMessageId): PWideChar; cdecl;
  begin
   try
     _Mi2e:=MessageContainer[M];
    Result:=@_Mi2e[1];
   except
    ProcessGuardedException();
   end;
  end;

{
  procedure SwitchToModule(n: integer); cdecl;
  begin
    Module.Reload(n);
  end;
}

{
  function _exp_Tick(): integer; cdecl;
  begin
    result:=Tick();
  end;
}
  procedure SetDying(d: boolean); cdecl;
  begin
    MotherState.NowDying:=d;
  end;


// *********************  TEXTURE MANAGEMENT ************************

  {$include cl_resource_func.inc}

//*******************************************************************
  
  //These include implementation of
  //  procedure CgeBuildAlphaMipmaps(Width, Height: integer; Data: pointer); cdecl;
  {$ifdef ASSEMBLER_ALLOWED}
    {$include cle_alphamip_asm.inc}
  {$else}
    {$include cle_alphamip.inc}
  {$endif}
  
  procedure _ExportHostProc(i: integer; var p: pointer); cdecl;
  begin
   try
    case i of
      {$include cl_exported_func.inc}
    else
      Die(MI_ERROR_TYPESETS_MISMATCH,[
        RuEn('списка экспортирумых функций (в нём нет элемента №'
             , 'exported functions list (there is no entry #')
        + IntToStr(i) + ')']);
    end;
   except
    ProcessGuardedException();
   end;
  end;


//******************************************************************  
