////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001-2023 Alexey Kuryakin daqgroup@mail.ru under MIT license //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// This file is part of the CRW-DAQ project by DaqGroup - component CRWLIB.   //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Purpose:                                                                   //
// That is library to detect WINE runtime platform - Win32 under Linux.       //
// WINE is acronym of "Wine Is Not Emulator". See http://www.winehq.org       //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// History:                                                                   //
// 2022xxxx - Created by A.K.                                                 //
// 20230801 - Modified for FPC (A.K.)                                         //
////////////////////////////////////////////////////////////////////////////////

unit _crw_wine; // WINE detecting library.

{$I _crw_sysdef}

{$IFDEF FPC}{$mode Delphi}{$ENDIF}{$H+}

interface

uses
 _crw_sharemem, // NB: THIS UNIT MUST BE FIRST !!!
 {$IFDEF UNIX} cthreads, dl, {$ENDIF}
 {$IFDEF WINDOWS} windows, {$ENDIF}
 sysutils, classes,
 _crw_alloc;

/////////////////////////////////////////////////////////////////////////
// Library based on code (wine_get_version from ntdll.dll) got from site:
// https://www.winehq.org/pipermail/wine-devel/2008-September/069387.html
/////////////////////////////////////////////////////////////////////////

const                           // detect_wine() codes:
 STATUS_NO_WINE         = 0;    // WINE is not detected.
 STATUS_NO_NTDLL        = -1;   // ntdll.dll is not found.
 STATUS_BAD_WINE        = -2;   // Invalid WINE version ID.
 STATUS_WINE_UNASSIGNED = -3;   // WINE is not assigned yet.

 /////////////////////////////////////////////
 // Detecting WINE runtime and return version.
 // Return positive value (like 520) when WINE
 // is running and return version (like 5.20).
 // Return value <=0 when WINE is not running.
 /////////////////////////////////////////////
function detect_wine(var version:String):Integer;   // Detect WINE version (>0) or error (<=0)
function detect_wine_number:Integer;                // Detect WINE version as Integer like 520
function detect_wine_string:String;                 // Detect WINE version as String  like 5.20
function detect_wine_host:Boolean;                  // Detect WINE host is running

implementation

{$IFDEF WINDOWS}
const wine_version_string : String = '';
const wine_version_number : integer= STATUS_WINE_UNASSIGNED;

function detect_wine_host:Boolean;
var dummy:String;
begin
 dummy:='';
 if (wine_version_number<>STATUS_WINE_UNASSIGNED)
 then Result:=(wine_version_number>0)
 else Result:=(detect_wine(dummy)>0);
end;

function detect_wine_number:Integer;
var dummy:String;
begin
 dummy:='';
 if (wine_version_number<>STATUS_WINE_UNASSIGNED)
 then Result:=wine_version_number
 else Result:=detect_wine(dummy);
end;

function detect_wine_string:String;
begin
 if (wine_version_number<>STATUS_WINE_UNASSIGNED)
 then Result:=wine_version_string
 else detect_wine(Result);
end;

function detect_wine(var version:String):Integer;
type  twine_get_version = function():PChar; cdecl;
const fwine_get_version:twine_get_version=nil; 
var h,l,p,ch,cl:Integer; hntdll:HMODULE; ver:PChar;
begin
 version:='';
 if (wine_version_number<>STATUS_WINE_UNASSIGNED) then begin
  version:=wine_version_string;
  Result:=wine_version_number;
  Exit;
 end;
 hntdll := GetModuleHandle('ntdll.dll');
 if (hntdll=0) then begin
  wine_version_number := STATUS_NO_NTDLL;
  Result:=wine_version_number;
  Exit;
 end;
 @fwine_get_version := GetProcAddress(hntdll, 'wine_get_version');
 if (@fwine_get_version = nil) then begin
  wine_version_number := STATUS_NO_WINE;
  Result:=wine_version_number;
  Exit;
 end;
 ver:=fwine_get_version;
 version:=SysUtils.Trim(ver);
 wine_version_string:=version;
 p:=Pos('.',wine_version_string);
 Val(Copy(wine_version_string,1,p-1),h,ch);
 Val(Copy(wine_version_string,p+1,MaxInt),l,cl);
 if (p=0) or (ch<>0) or (cl<>0) then begin
  wine_version_number := STATUS_BAD_WINE;
  Result:=wine_version_number;
  Exit;
 end;
 wine_version_number := h * 100 + (l mod 100);
 Result:=wine_version_number;
end;
{$ENDIF ~WINDOWS}

{$IFDEF UNIX}
function detect_wine_host:Boolean;
begin
 Result:=false;
end;

function detect_wine_number:Integer;
begin
 Result:=STATUS_NO_WINE;
end;

function detect_wine_string:String;
begin
 Result:='';
end;

function detect_wine(var version:String):Integer;
begin
 version:='';
 Result:=STATUS_NO_WINE;
end;
{$ENDIF ~UNIX}

///////////////////////////////////////
// Unit initialization and finalization
///////////////////////////////////////

procedure Init_crw_wine;
begin
end;

procedure Free_crw_wine;
begin
end;

initialization

 Init_crw_wine;

finalization

 Free_crw_wine;

end.

//////////////
// END OF FILE
//////////////

