aboutsummaryrefslogblamecommitdiffstats
path: root/ServiceBasedPlugins/src/pluginsupport/UPluginLoader_DLL.pas
blob: c11fe708a71395798aaa596959aee31c25487391 (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: https://ultrastardx.svn.sourceforge.net/svnroot/ultrastardx/trunk/src/base/UMain.pas $
 * $Id: UMain.pas 1629 2009-03-07 22:30:04Z k-m_schindler $
 *}

unit UPluginLoader_DLL;

interface

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

{$I switches.inc}

uses UPluginDefines, UPlugin;

type
  TPluginLoader_DLL = class
    protected
      PluginDir: WideString;
    public
      PluginInterface: TUS_PluginInterface;

      constructor Create(PluginDir: WideString);
      procedure Browse(Dir: WideString);
  end;
  
  TPlugin_DLL = class (TPlugin)
    protected
      hLib: THandle; //< handle of loaded library
      Lib_Proc_Init: TUS_Plugin_Proc_Init;
      Lib_OnChangeStatus: TUS_Plugin_OnChangeStatus;
      
      procedure OnChangeStatus(Status: TUS_PluginStatus); override;
    public
      constructor Create(Handle: TUS_Handle; Filename: WideString); override;
      function GetLoader: LongInt; override;
      destructor Destroy; override;
  end;

const
  {$IF Defined(MSWINDOWS)}
    DLLExt  = '.dll';
  {$ELSEIF Defined(DARWIN)}
    DLLExt  = '.dylib';
  {$ELSEIF Defined(UNIX)}
    DLLExt  = '.so';
  {$IFEND}

// procedures used in the PluginInterface record
// see UPluginDefines for descriptions
function DLL_Identify (Handle: TUS_Handle; Version: TUS_Version; Buffer: PUS_PluginInfo; BufferLength: LongInt): LongInt;
  {$IFDEF MSWINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF}
function DLL_Error (Handle: TUS_Handle; Reason: PWideChar): LongInt;
  {$IFDEF MSWINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF}
function DLL_GetFilename (Handle: TUS_Handle; Buffer: PWideChar; BufferLength: LongInt): LongInt;
  {$IFDEF MSWINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF}

implementation
uses
  {$IFDEF MSWINDOWS}
  windows,
  {$ELSE}
  dynlibs,
  {$ENDIF}
  SysUtils,
  UPluginManager,
  UPlatform,
  ULog;

{ implementation of TPluginLoader_DLL }
constructor TPluginLoader_DLL.Create(PluginDir: WideString);
begin
  //start to fill the plugininterface w/ default values
  PluginInterface.Handle        := US_HANDLE_UNDEFINED;
  PluginInterface.Version       := SDK_Version;
  PluginInterface.Identify      := DLL_Identify;
  PluginInterface.Error         := DLL_Error;
  PluginInterface.GetFilename   := DLL_GetFilename;
  PluginInterface.GetInterface  := nil;
  PluginInterface.SetInterface  := nil;
  PluginInterface.GetData       := nil;
  PluginInterface.SetData       := nil;

  Self.PluginDir := PluginDir;
  Browse(PluginDir);
end;

procedure TPluginLoader_DLL.Browse(Dir: WideString);
  var
    Files: TDirectoryEntryArray;
    I: Integer;
begin
  Files := Platform.DirectoryFindFiles(Dir, DLLExt, True);

  for I := 0 to High(Files) do
  begin
    if (Files[I].IsDirectory) then
      Browse(Dir + Files[I].Name + PathDelim)
    else if (Files[I].IsFile) then
      PluginManager.AddPlugin(TPlugin_DLL.Create(PluginManager.GetHandle, Dir + Files[I].Name));
  end;
end;

{ implementation of TPlugin_DLL }
constructor TPlugin_DLL.Create(Handle: TUS_Handle; Filename: WideString);
begin
  inherited;
  
  hLib := 0;
  Lib_Proc_Init := nil;
  Lib_OnChangeStatus := nil;

  //try to load the library
  SetStatus(psWaitingInit);
end;

function TPlugin_DLL.GetLoader: LongInt;
begin
  Result := US_LOADER_LIBRARY;
end;

destructor TPlugin_DLL.Destroy;
begin

  inherited;
end;

procedure TPlugin_DLL.OnChangeStatus(Status: TUS_PluginStatus);
begin
  Case Status of
    psWaitingInit: begin
      //we have to try to load the plugin here
      hLib := LoadLibrary(PChar(AnsiString(Filename)));
      if (hlib <> 0) then
      begin
        @Lib_Proc_Init := GetProcAddress(hLib, PChar('Proc_Init'));

        if (not Assigned(Lib_Proc_Init)) then
        begin
          FreeLibrary(hLib);

          {$IFDEF MSWINDOWS}
          SetError('Can''t export Proc_Init procedure from library. Windows reports: ' + SysErrorMessage(GetLastError));
          {$ELSE}
          SetError('Can''t export Proc_Init procedure from library.');
          {$ENDIF}
          Exit;
        end;

        @Lib_OnChangeStatus := GetProcAddress(hLib, PChar('OnChangeStatus'));

        if (not Assigned(Lib_OnChangeStatus)) then
        begin
          FreeLibrary(hLib);

          {$IFDEF MSWINDOWS}
          SetError('Can''t export OnChangeStatus procedure from library. Windows reports: ' + SysErrorMessage(GetLastError));
          {$ELSE}
          SetError('Can''t export OnChangeStatus procedure from library.');
          {$ENDIF}
          Exit;
        end;  

      end
      else
      begin
        {$IFDEF MSWINDOWS}
        SetError('Error loading library. Windows reports: ' + SysErrorMessage(GetLastError));
        {$ELSE}
        SetError('Error loading library.');
        {$ENDIF}
        Exit;
      end;

      //library + procedure pointers are loaded
      //now try to do the handshake
      //call Proc_Init
      Lib_Proc_Init(Self.Handle, SDK_Version, nil);

      //check if plugin has identified itself
      if (Self.Info.Version = US_VERSION_UNDEFINED) then
      begin
        SetError('library did not identify');
        exit;
      end;    
    end;

  end;

  //call plugins OnChangeStatus procedure
  if assigned(Lib_OnChangeStatus) then
    try
      Lib_OnChangeStatus(Self.Handle, Status);
    except
      if (Status <> psError) then
        SetError('exception in librarys OnChangeStatus (from: ' + USPluginStatustoString(Self.Status) + ' to: ' + USPluginStatustoString(Status) + ')');
    end;

  Self.Status := Status;
end;

// procedures used in the PluginInterface record
// see UPluginDefines for descriptions
// --------
function DLL_Identify (Handle: TUS_Handle; Version: TUS_Version; Buffer: PUS_PluginInfo; BufferLength: LongInt): LongInt;
var
  Plugin: IUS_Plugin;
  Info: TUS_PluginInfo;
begin
  Result := US_ERROR_Unknown;
  Plugin := PluginManager.GetPluginbyHandle(Handle);

  if (Plugin <> nil) then
  begin
    if (Plugin.GetLoader = US_LOADER_LIBRARY) then
    begin
      If (Version = SDK_Version) then
      begin
        If (BufferLength = SizeOf(TUS_PluginInfo)) then
        begin
          try
            move(Buffer^, Info, BufferLength);
          except
            Plugin.SetError('error copying plugininfo from buffer in DLL_Identify');
          end;

          If Plugin.Identify(Info) then
          begin
            Result := US_ERROR_None;
          end;
        end
        else
        begin
          Result := US_ERROR_SizeMismatch;
          Plugin.SetError('reported buffersize is wrong in DLL_Identify');
        end;
      end
      else
      begin
        Result := US_ERROR_VersionMismatch;
        Plugin.SetError('SDK version is not supported in DLL_Identify');
      end;

    end
    else
    begin
      Result := US_ERROR_WrongHandle;
      Log.LogError('called w/ handle of plugin from another loader', 'DLL_Identify');
    end;
  end
  else
  begin
    Result := US_ERROR_WrongHandle;
    Log.LogError('called w/ invalid handle', 'DLL_Identify');
  end;
end;

function DLL_Error (Handle: TUS_Handle; Reason: PWideChar): LongInt;
var
  Plugin: IUS_Plugin;
  S: WideString;
begin
  Result := US_ERROR_Unknown;
  Plugin := PluginManager.GetPluginbyHandle(Handle);

  if (Plugin <> nil) then
  begin
    if (Plugin.GetLoader = US_LOADER_LIBRARY) then
    begin
      Result := US_ERROR_None;

      //try to copy the error reason
      try
        SetLength(S, Length(Reason^));
        S := copy(Reason^, 1, Length(S)); //or has this to be len*2 ?
      except
        Reason := 'couldn''t copy error reason in DLL_Error';
      end;

      Plugin.SetError(Reason);
    end
    else
    begin
      Result := US_ERROR_WrongHandle;
      Log.LogError('called w/ handle of plugin from another loader', 'DLL_Identify');
    end;
  end
  else
  begin
    Result := US_ERROR_WrongHandle;
    Log.LogError('called w/ invalid handle', 'DLL_Identify');
  end;
end;

function DLL_GetFilename (Handle: TUS_Handle; Buffer: PWideChar; BufferLength: LongInt): LongInt;
begin

end;

end.