{* 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.