program cdejecter;
{$mode delphi}
{$codepage UTF8}
uses Interfaces, {$IFDEF UNIX}{$IFDEF UseCThreads}cthreads,{$ENDIF}{$ENDIF} Classes, SysUtils, CustApp,
     ANBFormatString, dynlibs, LazUTF8Classes, cde_dir, cde_lang, cde_types, ParamsMngr, cde_kernel, LazUTF8;
type
  { TCDEjecter }
  TCDEjecter = class(TCustomApplication)
  protected
   procedure DoRun; override;
  public
   constructor Create(TheOwner: TComponent); OVERRIDE;
   destructor Destroy; OVERRIDE;
   procedure WriteHelp; VIRTUAL;
   procedure ListDiscs (const Extendend: Boolean);
   function CheckIFEmpty (const ADisc: String): Boolean;
   procedure GetALLDriveState;
   procedure GetDriveState (const ADisc: String);
   function FormatDiscInfo (const AString: String; const AInfo: TDiscInfo): String;
   procedure ShowDiscInfo (const ADisc: String);
   procedure EjectDisc (const ADisc: String);
   procedure CloseDisc (const ADisc: String);
   procedure BuildTitle;
  end;
{ TCDEjecter }
procedure TCDEjecter.DoRun;
var isHandled: Boolean;
begin
BuildTitle;
isHandled:= False;
if HasParam('list') then
 begin
 isHandled:= True;
 ListDiscs(False);
 end;
if HasParam('listex') then
 begin
 isHandled:= True;
 ListDiscs(True);
 end;
if HasParam('stateall') then
 begin
 isHandled:= True;
 GetALLDriveState;
 end;
if HasParam('state') then
 begin
 isHandled:= True;
 GetDriveState(GetParamValue('state'));
 end;
if HasParam('info') then
 begin
 isHandled:= True;
 ShowDiscInfo(GetParamValue('info'));
 end;
if HasParam('eject') then
 begin
 isHandled:= True;
 EjectDisc(GetParamValue('eject'));
 end;
if HasParam('close') then
 begin
 isHandled:= True;
 CloseDisc(GetParamValue('close'));
 end;
if not isHandled then
 WriteHelp;
// stop program loop
Terminate;
end;
constructor TCDEjecter.Create(TheOwner: TComponent);
begin
inherited Create (TheOwner);
StopOnException:= True;
end;
destructor TCDEjecter.Destroy;
begin
inherited Destroy;
end;
procedure TCDEjecter.WriteHelp;
begin
writeln(FormatStr(GetLocalizedString(cde_messages_help), ['$newline$'], [#13#10]));
end;
procedure TCDEjecter.ListDiscs (const Extendend: Boolean);
var DLL: TLibHandle;
    GetCDDiscs: function: PChar;
    GetVolumeInfo: function (const ADisc: WideChar): TDiscInfo;
    SL: TStringListUTF8;
    Msg: String;
    Indx: Integer;
    DInfo: TDiscInfo;
begin
Msg:= '';
SL:= TStringListUTF8.Create;
DLL:= SafeLoadLibrary(CDELib);
GetCDDiscs:= GetProcAddress(DLL, 'GetCDDiscs');
GetVolumeInfo:= GetProcAddress(DLL, 'GetVolumeInfo');
SL.Delimiter:= ';';
SL.DelimitedText:= GetCDDiscs;
if SL.Count > 0 then
 for Indx:= 0 to SL.Count - 1 do
  begin
  DInfo:= GetVolumeInfo(WideChar(SL[Indx][1]));
  if not (Trim(Msg) = '') then
   Msg:= Msg + #13#10;
  Msg:= Msg + SL[Indx];
  if Extendend then
   if DInfo.diHasInfo then
    Msg:= Msg + ' - ' + DInfo.diVolumeName
    else
    Msg:= Msg + ' - ' + GetLocalizedString(cde_messages_nodiscorempty);
  end;
FreeLibrary(DLL);
SL.Free;
WriteLn(GetLocalizedString(cde_messages_discslist));
WriteLn(Msg);
end;
function TCDEjecter.CheckIFEmpty (const ADisc: String): Boolean;
begin
Result:= False;
if Trim(ADisc) = '' then
 WriteLn(GetLocalizedString(cde_messages_errornoparameters))
 else
 Result:= True;
end;
procedure TCDEjecter.GetALLDriveState;
 procedure WriteDiscState (const ADLL: TLibHandle; const ADisc: WideChar);
 var GetDiscState: function (const ADisc: WideChar): TDiscState;
     GetVolumeInfo: function (const ADisc: WideChar): TDiscInfo;
     Msg: String;
     DInfo: TDiscInfo;
     DS: TDiscState;
 begin
 DInfo.diDrive:= ADisc;
 GetDiscState:= GetProcAddress(ADLL, 'GetDiscState');
 DS:= GetDiscState(ADisc);
 Msg:= GetLocalizedString(cde_messages_statealltext);
 case DS of
  dsNormal: begin
            GetVolumeInfo:= GetProcAddress(ADLL, 'GetVolumeInfo');
            DInfo:= GetVolumeInfo(ADisc);
            Msg:= Msg + GetLocalizedString(cde_discstate_normal);
            end;
  dsEmpty: Msg:= Msg + GetLocalizedString(cde_discstate_empty);
  dsUnFormatted: Msg:= Msg + GetLocalizedString(cde_discstate_unformatted);
  dsNIL: Msg:= Msg + GetLocalizedString(cde_discstate_nil);
  end;
 WriteLn(FormatDiscInfo(Msg, DInfo));
 end;
var DLL: TLibHandle;
    GetCDDiscs: function: PChar;
    SL: TStringListUTF8;
    Indx: Integer;
begin
WriteLn(GetLocalizedString(cde_messages_statealltitle));
SL:= TStringListUTF8.Create;
DLL:= SafeLoadLibrary(CDELib);
GetCDDiscs:= GetProcAddress(DLL, 'GetCDDiscs');
SL.Delimiter:= ';';
SL.DelimitedText:= GetCDDiscs;
if SL.Count > 0 then
 for Indx:= 0 to SL.Count - 1 do
  WriteDiscState(DLL, WideChar(SL[Indx][1]));
FreeLibrary(DLL);
FreeAndNil(SL);
end;
procedure TCDEjecter.GetDriveState (const ADisc: String);
var DLL: TLibHandle;
    GetDiscState: function (const ADisc: WideChar): TDiscState;
    GetVolumeInfo: function (const ADisc: WideChar): TDiscInfo;
    Msg: String;
    DInfo: TDiscInfo;
    DS: TDiscState;
begin
DInfo.diDrive:= ADisc[1];
WriteLn(FormatDiscInfo(GetLocalizedString(cde_messages_statetitle), DInfo));
if CheckIFEmpty(ADisc) then
 begin
 DLL:= SafeLoadLibrary(CDELib);
 GetDiscState:= GetProcAddress(DLL, 'GetDiscState');
 DS:= GetDiscState(WideChar(ADisc[1]));
 Msg:= GetLocalizedString(cde_messages_statetext);
 case DS of
  dsNormal: begin
            GetVolumeInfo:= GetProcAddress(DLL, 'GetVolumeInfo');
            DInfo:= GetVolumeInfo(WideChar(ADisc[1]));
            Msg:= Msg + GetLocalizedString(cde_discstate_normal);
            end;
  dsEmpty: Msg:= Msg + GetLocalizedString(cde_discstate_empty);
  dsUnFormatted: Msg:= Msg + GetLocalizedString(cde_discstate_unformatted);
  dsNIL: Msg:= Msg + GetLocalizedString(cde_discstate_nil);
  end;
 FreeLibrary(DLL);
 WriteLn(FormatDiscInfo(Msg, DInfo));
 end;
end;
function TCDEjecter.FormatDiscInfo (const AString: String; const AInfo: TDiscInfo): String;
 function GetPercentFree: Byte;
 const A: Int64 = 500416512;
       B: Int64 = 1524536461;
 begin
 WriteLn('Original: a = ' + IntToStr(A) + ' b = ' + IntToStr(B));
 WriteLn('correct: ' + IntToStr(round((A / B) * 100)));
 WriteLn('tested: ' + IntToStr((A * 100) div B));
 Result:= round((AInfo.diDiscFree / AInfo.diDiscSize) * 100);
 end;
begin
Result:= FormatStr(AString,
                            ['$newline$',
                             '$letter$',
                             '$volumename$',
                             '$filesystem$',
                             '$serial$',
                             '$discsizebyte$',
                             '$discsize$',
                             '$discfreebyte$',
                             '$discfree$',
                             '$discfreepercent$'
                            ],
                            [#13#10,
                             AInfo.diDrive,
                             AInfo.diVolumeName,
                             AInfo.diFileSystem,
                             IntToStr(AInfo.diSerial),
                             CDEFileSize2Str(AInfo.diDiscSize, True),
                             CDEFileSize2Str(AInfo.diDiscSize, False),
                             CDEFileSize2Str(AInfo.diDiscFree, True),
                             CDEFileSize2Str(AInfo.diDiscFree, False),
                             IntToStr(GetPercentFree) + '%'
                            ]);
end;
procedure TCDEjecter.ShowDiscInfo (const ADisc: String);
var DLL: TLibHandle;
    GetVolumeInfo: function (const ADisc: WideChar): TDiscInfo;
    Msg: String;
    DInfo: TDiscInfo;
begin
if CheckIFEmpty(ADisc) then
 begin
 DLL:= SafeLoadLibrary(CDELib);
 GetVolumeInfo:= GetProcAddress(DLL, 'GetVolumeInfo');
 DInfo:= GetVolumeInfo(WideChar(ADisc[1]));
 FreeLibrary(DLL);
 if DInfo.diHasInfo then
  Msg:= FormatDiscInfo(GetLocalizedString(cde_messages_discdesc), DInfo)
  else
  Msg:= FormatDiscInfo(GetLocalizedString(cde_messages_discdesconerror), DInfo);
 WriteLn(GetLocalizedString(cde_messages_discinfo));
 WriteLn(Msg);
 end;
end;
procedure TCDEjecter.EjectDisc (const ADisc: String);
var DLL: TLibHandle;
    EjectCD: function (const ADisc: WideChar): Boolean;
    GetVolumeInfo: function (const ADisc: WideChar): TDiscInfo;
    Msg: String;
    DInfo: TDiscInfo;
begin
WriteLn(GetLocalizedString(cde_messages_ejecttitle));
if CheckIFEmpty(ADisc) then
 begin
 DLL:= SafeLoadLibrary(CDELib);
 GetVolumeInfo:= GetProcAddress(DLL, 'GetVolumeInfo');
 EjectCD:= GetProcAddress(DLL, 'EjectCD');
 DInfo.diDrive:= ADisc[1];
 DInfo.diVolumeName:= ADisc;
 DInfo:= GetVolumeInfo(WideChar(ADisc[1]));
 if EjectCD (WideChar(ADisc[1])) then
  Msg:= FormatDiscInfo(GetLocalizedString(cde_messages_ejectsuccess), DInfo)
  else
  Msg:= FormatDiscInfo(GetLocalizedString(cde_messages_ejecterror), DInfo);
 FreeLibrary(DLL);
 WriteLn(Msg);
 end;
end;
procedure TCDEjecter.CloseDisc (const ADisc: String);
var DLL: TLibHandle;
    CloseCD: function (const ADisc: WideChar): Boolean;
    Msg: String;
    DInfo: TDiscInfo;
begin
WriteLn(GetLocalizedString(cde_messages_closetitle));
if CheckIFEmpty(ADisc) then
 begin
 DLL:= SafeLoadLibrary(CDELib);
 CloseCD:= GetProcAddress(DLL, 'CloseCD');
 DInfo.diDrive:= ADisc[1];
 if CloseCD (WideChar(ADisc[1])) then
  Msg:= FormatDiscInfo(GetLocalizedString(cde_messages_closesuccess), DInfo)
  else
  Msg:= FormatDiscInfo(GetLocalizedString(cde_messages_closeerror), DInfo);
 FreeLibrary(DLL);
 WriteLn(Msg);
 end;
end;
procedure TCDEjecter.BuildTitle;
 procedure WriteWord (const AWord: String; const hMaxLength, AMaxLength: Byte; const ASymbol: Char);
 var S1, S2: String;
     Indx, hLen: Byte;
 begin
 hLen:= UTF8Length(AWord) div 2;
 S1:= '';
 for Indx:= 1 to (hMaxLength - hLen) - 1 do
  S1:= S1 + ASymbol;
 S1:= S1 + ' ';
 S2:= ' ';
 for Indx:= (UTF8Length(S1) + UTF8Length(AWord)) + 2 to AMaxLength do
  S2:= S2 + ASymbol;
 WriteLn(S1 + AWord + S2);
 end;
var StrT, StrV, StrC, StrABL: String;
    MaxLength, hMaxLength, Indx: Byte;
const ASymbol: Char = '#';
begin
StrT:= 'CD Ejecter';
StrV:= GetLocalizedString(cde_version) + ' ' + GetCDEVerStr('cde');
StrC:= FormatStr(GetLocalizedString(cde_copyrights), ['$DEVYEARS$'], ['2011 - 2017']);
MaxLength:= UTF8Length(StrT);
if UTF8Length(StrV) > MaxLength then
 MaxLength:= UTF8Length(StrV);
if UTF8Length(StrC) > MaxLength then
 MaxLength:= UTF8Length(StrC);
MaxLength:= MaxLength + 8;
hMaxLength:= MaxLength div 2;
StrABL:= '';
for Indx:= 1 to MaxLength do
 StrABL:= StrABL + ASymbol;
WriteLn(StrABL);
WriteWord(StrT, hMaxLength, MaxLength, ASymbol);
WriteWord(StrV, hMaxLength, MaxLength, ASymbol);
WriteWord(StrC, hMaxLength, MaxLength, ASymbol);
WriteLn(StrABL);
WriteLn('');
end;
var Application: TCDEjecter;
{$R *.res}
begin
Application:= TCDEjecter.Create(Nil);
Application.Title:= 'CD Ejecter';
Application.Run;
Application.Free;
end.