From bf7b206aac69a390a65a6c50717f226bb737a40d Mon Sep 17 00:00:00 2001 From: k-m_schindler Date: Tue, 23 Sep 2008 21:58:57 +0000 Subject: base/uPluginLoader.pas renamed to UPluginLoader.pas. no code change git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1408 b956fd51-792f-4845-bead-9b4dfca2ff2c --- src/base/UPluginLoader.pas | 798 +++++++++++++++++++++++++++++++++++++++++++++ src/base/uPluginLoader.pas | 798 --------------------------------------------- src/ultrastardx.dpr | 2 +- 3 files changed, 799 insertions(+), 799 deletions(-) create mode 100644 src/base/UPluginLoader.pas delete mode 100644 src/base/uPluginLoader.pas diff --git a/src/base/UPluginLoader.pas b/src/base/UPluginLoader.pas new file mode 100644 index 00000000..5e581c23 --- /dev/null +++ b/src/base/UPluginLoader.pas @@ -0,0 +1,798 @@ +{* 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/uPluginLoader.pas $ + * $Id: uPluginLoader.pas 1403 2008-09-23 21:17:22Z k-m_schindler $ + *} + +unit UPluginLoader; +{********************* + UPluginLoader + Unit contains two classes + TPluginLoader: Class searching for and loading the plugins + TtehPlugins: Class representing the plugins in modules chain +*********************} + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +uses + UPluginDefs, + UCoreModule; + +type + TPluginListItem = record + Info: TUS_PluginInfo; + State: Byte; // State of this plugin: 0 - undefined; 1 - loaded; 2 - inited / running; 4 - unloaded; 254 - loading aborted by plugin; 255 - unloaded because of error + Path: String; // path to this plugin + NeedsDeInit: Boolean; // if this is inited correctly this should be true + hLib: THandle; // handle of loaded libary + Procs: record // procs offered by plugin. Don't call this directly use wrappers of TPluginLoader + Load: Func_Load; + Init: Func_Init; + DeInit: Proc_DeInit; + end; + end; + {********************* + TPluginLoader + Class searches for plugins and manages loading and unloading + *********************} + PPluginLoader = ^TPluginLoader; + TPluginLoader = class (TCoreModule) + private + LoadingProcessFinished: Boolean; + sUnloadPlugin: THandle; + sLoadPlugin: THandle; + sGetPluginInfo: THandle; + sGetPluginState: THandle; + + procedure FreePlugin(Index: Cardinal); + public + PluginInterface: TUS_PluginInterface; + Plugins: array of TPluginListItem; + + // TCoreModule methods to inherit + constructor Create; override; + procedure Info(const pInfo: PModuleInfo); override; + function Load: Boolean; override; + function Init: Boolean; override; + procedure DeInit; override; + Destructor Destroy; override; + + // New methods + procedure BrowseDir(Path: String); // browses the path at _Path_ for plugins + function PluginExists(Name: String): integer; // if plugin exists: Index of plugin, else -1 + procedure AddPlugin(Filename: String); // adds plugin to the array + + function CallLoad(Index: Cardinal): integer; + function CallInit(Index: Cardinal): integer; + procedure CallDeInit(Index: Cardinal); + + //Services offered + function LoadPlugin(wParam: TwParam; lParam: TlParam): integer; //wParam PChar(PluginName/PluginPath) | lParam (if wParam = nil) ID of the Plugin + function UnloadPlugin(wParam: TwParam; lParam: TlParam): integer; //wParam PChar(PluginName/PluginPath) | lParam (if wParam = nil) ID of the Plugin + function GetPluginInfo(wParam: TwParam; lParam: TlParam): integer; //If wParam = -1 then (If lParam = nil then get length of Moduleinfo Array. If lparam <> nil then write array of TUS_PluginInfo to address at lparam) else (Get PluginInfo of Plugin with Index(wParam) to Address at lParam) + function GetPluginState(wParam: TwParam; lParam: TlParam): integer; //If wParam = -1 then (If lParam = nil then get length of Moduleinfo Array. If lparam <> nil then write array of TUS_PluginInfo to address at lparam) else (Return PluginInfo of Plugin with Index(wParam)) + + end; + + {********************* + TtehPlugins + Class represents the plugins in module chain. + It calls the plugins procs and funcs + *********************} + TtehPlugins = class (TCoreModule) + private + PluginLoader: PPluginLoader; + public + // TCoreModule methods to inherit + constructor Create; override; + + procedure Info(const pInfo: PModuleInfo); override; + function Load: Boolean; override; + function Init: Boolean; override; + procedure DeInit; override; + end; + +const +{$IF Defined(MSWINDOWS)} + PluginFileExtension = '.dll'; +{$ELSEIF Defined(DARWIN)} + PluginFileExtension = '.dylib'; +{$ELSEIF Defined(UNIX)} + PluginFileExtension = '.so'; +{$IFEND} + +implementation + +uses + UCore, + UPluginInterface, +{$IFDEF MSWINDOWS} + windows, +{$ELSE} + dynlibs, +{$ENDIF} + UMain, + SysUtils; + +{********************* + TPluginLoader + Implementation +*********************} + +//------------- +// function that gives some infos about the module to the core +//------------- +procedure TPluginLoader.Info(const pInfo: PModuleInfo); +begin + pInfo^.Name := 'TPluginLoader'; + pInfo^.Version := MakeVersion(1,0,0,chr(0)); + pInfo^.Description := 'Searches for plugins, loads and unloads them'; +end; + +//------------- +// Just the constructor +//------------- +constructor TPluginLoader.Create; +begin + inherited; + + // Init PluginInterface + // Using methods from UPluginInterface + PluginInterface.CreateHookableEvent := CreateHookableEvent; + PluginInterface.DestroyHookableEvent := DestroyHookableEvent; + PluginInterface.NotivyEventHooks := NotivyEventHooks; + PluginInterface.HookEvent := HookEvent; + PluginInterface.UnHookEvent := UnHookEvent; + PluginInterface.EventExists := EventExists; + + PluginInterface.CreateService := @CreateService; + PluginInterface.DestroyService := DestroyService; + PluginInterface.CallService := CallService; + PluginInterface.ServiceExists := ServiceExists; + + // UnSet private var + LoadingProcessFinished := False; +end; + +//------------- +// Is called on loading. +// In this method only events and services should be created +// to offer them to other modules or plugins during the init process +// if false is returned this will cause a forced exit +//------------- +function TPluginLoader.Load: Boolean; +begin + Result := True; + + try + // Start searching for plugins + BrowseDir(PluginPath); + except + Result := False; + Core.ReportError(integer(PChar('Error browsing and loading.')), PChar('TPluginLoader')); + end; +end; + +//------------- +// Is called on init process +// In this method you can hook some events and create + init +// your classes, variables etc. +// If false is returned this will cause a forced exit +//------------- +function TPluginLoader.Init: Boolean; +begin + // Just set private var to true. + LoadingProcessFinished := True; + Result := True; +end; + +//------------- +// Is called if this module has been inited and there is a exit. +// Deinit is in backwards initing order +//------------- +procedure TPluginLoader.DeInit; +var + I: integer; +begin + // Force deinit + // if some plugins aren't deinited for some reason o0 + for I := 0 to High(Plugins) do + begin + if (Plugins[I].State < 4) then + FreePlugin(I); + end; + + // Nothing to do here. Core will remove the hooks +end; + +//------------- +// Is called if this module will be unloaded and has been created +// Should be used to free memory +//------------- +Destructor TPluginLoader.Destroy; +begin + // Just save some memory if it wasn't done now.. + SetLength(Plugins, 0); + inherited; +end; + +//-------------- +// Browses the path at _Path_ for plugins +//-------------- +procedure TPluginLoader.BrowseDir(Path: String); +var + SR: TSearchRec; +begin + // Search for other dirs to browse + if FindFirst(Path + '*', faDirectory, SR) = 0 then begin + repeat + if (SR.Name <> '.') and (SR.Name <> '..') then + BrowseDir(Path + Sr.Name + PathDelim); + until FindNext(SR) <> 0; + end; + FindClose(SR); + + // Search for plugins at path + if FindFirst(Path + '*' + PluginFileExtension, 0, SR) = 0 then + begin + repeat + AddPlugin(Path + SR.Name); + until FindNext(SR) <> 0; + end; + FindClose(SR); +end; + +//-------------- +// If plugin exists: Index of plugin, else -1 +//-------------- +function TPluginLoader.PluginExists(Name: String): integer; +var + I: integer; +begin + Result := -1; + + if (Length(Name) <= 32 { =>Length(TUS_PluginInfo.Name)}) then + begin + for I := 0 to High(Plugins) do + if (Plugins[I].Info.Name = Name) then + begin //Found the Plugin + Result := I; + Break; + end; + end; +end; + +//-------------- +// Adds plugin to the array +//-------------- +procedure TPluginLoader.AddPlugin(Filename: String); +var + hLib: THandle; + PInfo: Proc_PluginInfo; + Info: TUS_PluginInfo; + PluginID: integer; +begin + if (FileExists(Filename)) then + begin //Load Libary + hLib := LoadLibrary(PChar(Filename)); + if (hLib <> 0) then + begin // Try to get address of the info proc + PInfo := GetProcAddress (hLib, PChar('USPlugin_Info')); + if (@PInfo <> nil) then + begin + Info.cbSize := SizeOf(TUS_PluginInfo); + + try // Call info proc + PInfo(@Info); + except + Info.Name := ''; + Core.ReportError(integer(PChar('Error getting plugin info: ' + Filename)), PChar('TPluginLoader')); + end; + + // Is name set ? + if (Trim(Info.Name) <> '') then + begin + PluginID := PluginExists(Info.Name); + + if (PluginID > 0) and (Plugins[PluginID].State >=4) then + PluginID := -1; + + if (PluginID = -1) then + begin + // Add new item to array + PluginID := Length(Plugins); + SetLength(Plugins, PluginID + 1); + + // Fill with info: + Plugins[PluginID].Info := Info; + Plugins[PluginID].State := 0; + Plugins[PluginID].Path := Filename; + Plugins[PluginID].NeedsDeInit := False; + Plugins[PluginID].hLib := hLib; + + // Try to get procs + Plugins[PluginID].Procs.Load := GetProcAddress (hLib, PChar('USPlugin_Load')); + Plugins[PluginID].Procs.Init := GetProcAddress (hLib, PChar('USPlugin_Init')); + Plugins[PluginID].Procs.DeInit := GetProcAddress (hLib, PChar('USPlugin_DeInit')); + + if (@Plugins[PluginID].Procs.Load = nil) OR (@Plugins[PluginID].Procs.Init = nil) OR (@Plugins[PluginID].Procs.DeInit = nil) then + begin + Plugins[PluginID].State := 255; + FreeLibrary(hLib); + Core.ReportError(integer(PChar('Can''t get plugin procs from libary: "' + Info.Name + '" ' + Filename)), PChar('TPluginLoader')); + end; + + // Emulate loading process if this plugin is loaded too late + if (LoadingProcessFinished) then + begin + CallLoad(PluginID); + CallInit(PluginID); + end; + end + else if (LoadingProcessFinished = False) then + begin + if (Plugins[PluginID].Info.Version < Info.Version) then + begin // Found newer version of this plugin + Core.ReportDebug(integer(PChar('Found a newer version of plugin: ' + String(Info.Name))), PChar('TPluginLoader')); + + // Unload old plugin + UnloadPlugin(PluginID, nil); + + // Fill with new info + Plugins[PluginID].Info := Info; + Plugins[PluginID].State := 0; + Plugins[PluginID].Path := Filename; + Plugins[PluginID].NeedsDeInit := False; + Plugins[PluginID].hLib := hLib; + + // Try to get procs + Plugins[PluginID].Procs.Load := GetProcAddress (hLib, PChar('USPlugin_Load')); + Plugins[PluginID].Procs.Init := GetProcAddress (hLib, PChar('USPlugin_Init')); + Plugins[PluginID].Procs.DeInit := GetProcAddress (hLib, PChar('USPlugin_DeInit')); + + if (@Plugins[PluginID].Procs.Load = nil) OR (@Plugins[PluginID].Procs.Init = nil) OR (@Plugins[PluginID].Procs.DeInit = nil) then + begin + FreeLibrary(hLib); + Plugins[PluginID].State := 255; + Core.ReportError(integer(PChar('Can''t get plugin procs from libary: "' + Info.Name + '" ' + Filename)), PChar('TPluginLoader')); + end; + end + else + begin // Newer Version already loaded + FreeLibrary(hLib); + end; + end + else + begin + FreeLibrary(hLib); + Core.ReportError(integer(PChar('Plugin with this name already exists: ' + String(Info.Name))), PChar('TPluginLoader')); + end; + end + else + begin + FreeLibrary(hLib); + Core.ReportError(integer(PChar('No name reported: ' + Filename)), PChar('TPluginLoader')); + end; + end + else + begin + FreeLibrary(hLib); + Core.ReportError(integer(PChar('Can''t find info procedure: ' + Filename)), PChar('TPluginLoader')); + end; + end + else + Core.ReportError(integer(PChar('Can''t load plugin libary: ' + Filename)), PChar('TPluginLoader')); + end; +end; + +//-------------- +// Calls load func of plugin with the given index +//-------------- +function TPluginLoader.CallLoad(Index: Cardinal): integer; +begin + Result := -2; + if(Index < Length(Plugins)) then + begin + if (@Plugins[Index].Procs.Load <> nil) and (Plugins[Index].State = 0) then + begin + try + Result := Plugins[Index].Procs.Load(@PluginInterface); + except + Result := -3; + End; + + if (Result = 0) then + Plugins[Index].State := 1 + else + begin + FreePlugin(Index); + Plugins[Index].State := 255; + Core.ReportError(integer(PChar('Error calling load function from plugin: ' + String(Plugins[Index].Info.Name))), PChar('TPluginLoader')); + end; + end; + end; +end; + +//-------------- +// Calls init func of plugin with the given index +//-------------- +function TPluginLoader.CallInit(Index: Cardinal): integer; +begin + Result := -2; + if(Index < Length(Plugins)) then + begin + if (@Plugins[Index].Procs.Init <> nil) and (Plugins[Index].State = 1) then + begin + try + Result := Plugins[Index].Procs.Init(@PluginInterface); + except + Result := -3; + End; + + if (Result = 0) then + begin + Plugins[Index].State := 2; + Plugins[Index].NeedsDeInit := True; + end + else + begin + FreePlugin(Index); + Plugins[Index].State := 255; + Core.ReportError(integer(PChar('Error calling init function from plugin: ' + String(Plugins[Index].Info.Name))), PChar('TPluginLoader')); + end; + end; + end; +end; + +//-------------- +// Calls deinit proc of plugin with the given index +//-------------- +procedure TPluginLoader.CallDeInit(Index: Cardinal); +begin + if(Index < Length(Plugins)) then + begin + if (Plugins[Index].State < 4) then + begin + if (@Plugins[Index].Procs.DeInit <> nil) and (Plugins[Index].NeedsDeInit) then + try + Plugins[Index].Procs.DeInit(@PluginInterface); + except + + End; + + // Don't forget to remove services and subscriptions by this plugin + Core.Hooks.DelbyOwner(-1 - Index); + + FreePlugin(Index); + end; + end; +end; + +//-------------- +// Frees all plugin sources (procs and handles) - helper for deiniting functions +//-------------- +procedure TPluginLoader.FreePlugin(Index: Cardinal); +begin + Plugins[Index].State := 4; + Plugins[Index].Procs.Load := nil; + Plugins[Index].Procs.Init := nil; + Plugins[Index].Procs.DeInit := nil; + + if (Plugins[Index].hLib <> 0) then + FreeLibrary(Plugins[Index].hLib); +end; + + + +//-------------- +// wParam PChar(PluginName/PluginPath) | wParam (if lParam = nil) ID of the plugin +//-------------- +function TPluginLoader.LoadPlugin(wParam: TwParam; lParam: TlParam): integer; +var + Index: integer; + sFile: String; +begin + Result := -1; + sFile := ''; + // lParam is ID + if (lParam = nil) then + begin + Index := wParam; + end + else + begin //lParam is PChar + try + sFile := String(PChar(lParam)); + Index := PluginExists(sFile); + if (Index < 0) And FileExists(sFile) then + begin // Is filename + AddPlugin(sFile); + Result := Plugins[High(Plugins)].State; + end; + except + Index := -2; + end; + end; + + + if (Index >= 0) and (Index < Length(Plugins)) then + begin + AddPlugin(Plugins[Index].Path); + Result := Plugins[Index].State; + end; +end; + +//-------------- +// wParam PChar(PluginName/PluginPath) | wParam (if lParam = nil) ID of the plugin +//-------------- +function TPluginLoader.UnloadPlugin(wParam: TwParam; lParam: TlParam): integer; +var + Index: integer; + sName: String; +begin + Result := -1; + // lParam is ID + if (lParam = nil) then + begin + Index := wParam; + end + else + begin // wParam is PChar + try + sName := String(PChar(lParam)); + Index := PluginExists(sName); + except + Index := -2; + end; + end; + + + if (Index >= 0) and (Index < Length(Plugins)) then + CallDeInit(Index) +end; + +//-------------- +// if wParam = -1 then (if lParam = nil then get length of moduleinfo array. if lparam <> nil then write array of TUS_PluginInfo to address at lparam) else (Get PluginInfo of plugin with Index(wParam) to address at lParam) +//-------------- +function TPluginLoader.GetPluginInfo(wParam: TwParam; lParam: TlParam): integer; +var I: integer; +begin + Result := 0; + if (wParam > 0) then + begin // Get info of 1 plugin + if (lParam <> nil) and (wParam < Length(Plugins)) then + begin + try + Result := 1; + PUS_PluginInfo(lParam)^ := Plugins[wParam].Info; + except + + End; + end; + end + else if (lParam = nil) then + begin // Get length of plugin (info) array + Result := Length(Plugins); + end + else //Write PluginInfo Array to Address in lParam + begin + try + for I := 0 to high(Plugins) do + PAUS_PluginInfo(lParam)^[I] := Plugins[I].Info; + Result := Length(Plugins); + except + Core.ReportError(integer(PChar('Could not write PluginInfo Array')), PChar('TPluginLoader')); + End; + end; + +end; + +//-------------- +// if wParam = -1 then (if lParam = nil then get length of plugin state array. if lparam <> nil then write array of Byte to address at lparam) else (return state of plugin with index(wParam)) +//-------------- +function TPluginLoader.GetPluginState(wParam: TwParam; lParam: TlParam): integer; +var I: integer; +begin + Result := -1; + if (wParam > 0) then + begin // Get state of 1 plugin + if (wParam < Length(Plugins)) then + begin + Result := Plugins[wParam].State; + end; + end + else if (lParam = nil) then + begin // Get length of plugin (info) array + Result := Length(Plugins); + end + else // Write plugininfo array to address in lParam + begin + try + for I := 0 to high(Plugins) do + Byte(Pointer(integer(lParam) + I)^) := Plugins[I].State; + Result := Length(Plugins); + except + Core.ReportError(integer(PChar('Could not write pluginstate array')), PChar('TPluginLoader')); + End; + end; +end; + + +{********************* + TtehPlugins + Implementation +*********************} + +//------------- +// function that gives some infos about the module to the core +//------------- +procedure TtehPlugins.Info(const pInfo: PModuleInfo); +begin + pInfo^.Name := 'TtehPlugins'; + pInfo^.Version := MakeVersion(1,0,0,chr(0)); + pInfo^.Description := 'Module executing the Plugins!'; +end; + +//------------- +// Just the constructor +//------------- +constructor TtehPlugins.Create; +begin + inherited; + PluginLoader := nil; +end; + +//------------- +// Is called on loading. +// In this method only events and services should be created +// to offer them to other modules or plugins during the init process +// if false is returned this will cause a forced exit +//------------- +function TtehPlugins.Load: Boolean; +var + i: integer; // Counter + CurExecutedBackup: integer; //backup of Core.CurExecuted Attribute +begin + // Get pointer to pluginloader + PluginLoader := PPluginLoader(Core.GetModulebyName('TPluginLoader')); + if (PluginLoader = nil) then + begin + Result := false; + Core.ReportError(integer(PChar('Could not get pointer to pluginLoader')), PChar('TtehPlugins')); + end + else + begin + Result := true; + + // Backup curexecuted + CurExecutedBackup := Core.CurExecuted; + + // Start loading the plugins + for i := 0 to High(PluginLoader.Plugins) do + begin + Core.CurExecuted := -1 - i; + + try + // Unload plugin if not correctly executed + if (PluginLoader.CallLoad(i) <> 0) then + begin + PluginLoader.CallDeInit(i); + PluginLoader.Plugins[i].State := 254; // Plugin asks for unload + Core.ReportDebug(integer(PChar('Plugin selfabort during loading process: ' + String(PluginLoader.Plugins[i].Info.Name))), PChar('TtehPlugins')); + end + else + begin + Core.ReportDebug(integer(PChar('Plugin loaded succesfully: ' + String(PluginLoader.Plugins[i].Info.Name))), PChar('TtehPlugins')); + end; + except + // Plugin could not be loaded. + // => Show error message, then shutdown plugin + on E: Exception do + begin + PluginLoader.CallDeInit(i); + PluginLoader.Plugins[i].State := 255; // Plugin causes error + Core.ReportError(integer(PChar('Plugin causes error during loading process: ' + PluginLoader.Plugins[i].Info.Name + ', ErrorMsg: "' + E.Message + '"')), PChar('TtehPlugins')); + end; + end; + end; + + // Reset CurExecuted + Core.CurExecuted := CurExecutedBackup; + end; +end; + +//------------- +// Is called on init process +// in this method you can hook some events and create + init +// your classes, variables etc. +// if false is returned this will cause a forced exit +//------------- +function TtehPlugins.Init: Boolean; +var + i: integer; // Counter + CurExecutedBackup: integer; // backup of Core.CurExecuted attribute +begin + Result := true; + + // Backup CurExecuted + CurExecutedBackup := Core.CurExecuted; + + // Start loading the plugins + for i := 0 to High(PluginLoader.Plugins) do + try + Core.CurExecuted := -1 - i; + + // Unload plugin if not correctly executed + if (PluginLoader.CallInit(i) <> 0) then + begin + PluginLoader.CallDeInit(i); + PluginLoader.Plugins[i].State := 254; //Plugin asks for unload + Core.ReportDebug(integer(PChar('Plugin selfabort during init process: ' + String(PluginLoader.Plugins[i].Info.Name))), PChar('TtehPlugins')); + end + else + Core.ReportDebug(integer(PChar('Plugin inited succesfully: ' + String(PluginLoader.Plugins[i].Info.Name))), PChar('TtehPlugins')); + except + // Plugin could not be loaded. + // => Show error message, then shut down plugin + PluginLoader.CallDeInit(i); + PluginLoader.Plugins[i].State := 255; //Plugin causes Error + Core.ReportError(integer(PChar('Plugin causes error during init process: ' + String(PluginLoader.Plugins[i].Info.Name))), PChar('TtehPlugins')); + end; + + // Reset CurExecuted + Core.CurExecuted := CurExecutedBackup; +end; + +//------------- +// Is called if this module has been inited and there is a exit. +// Deinit is in backwards initing order +//------------- +procedure TtehPlugins.DeInit; +var + i: integer; // Counter + CurExecutedBackup: integer; // backup of Core.CurExecuted attribute +begin + // Backup CurExecuted + CurExecutedBackup := Core.CurExecuted; + + // Start loop + + for i := 0 to High(PluginLoader.Plugins) do + begin + try + // DeInit plugin + PluginLoader.CallDeInit(i); + except + end; + end; + + // Reset CurExecuted + Core.CurExecuted := CurExecutedBackup; +end; + +end. diff --git a/src/base/uPluginLoader.pas b/src/base/uPluginLoader.pas deleted file mode 100644 index 876f23c6..00000000 --- a/src/base/uPluginLoader.pas +++ /dev/null @@ -1,798 +0,0 @@ -{* 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$ - * $Id$ - *} - -unit UPluginLoader; -{********************* - UPluginLoader - Unit contains two classes - TPluginLoader: Class searching for and loading the plugins - TtehPlugins: Class representing the plugins in modules chain -*********************} - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - -uses - UPluginDefs, - UCoreModule; - -type - TPluginListItem = record - Info: TUS_PluginInfo; - State: Byte; // State of this plugin: 0 - undefined; 1 - loaded; 2 - inited / running; 4 - unloaded; 254 - loading aborted by plugin; 255 - unloaded because of error - Path: String; // path to this plugin - NeedsDeInit: Boolean; // if this is inited correctly this should be true - hLib: THandle; // handle of loaded libary - Procs: record // procs offered by plugin. Don't call this directly use wrappers of TPluginLoader - Load: Func_Load; - Init: Func_Init; - DeInit: Proc_DeInit; - end; - end; - {********************* - TPluginLoader - Class searches for plugins and manages loading and unloading - *********************} - PPluginLoader = ^TPluginLoader; - TPluginLoader = class (TCoreModule) - private - LoadingProcessFinished: Boolean; - sUnloadPlugin: THandle; - sLoadPlugin: THandle; - sGetPluginInfo: THandle; - sGetPluginState: THandle; - - procedure FreePlugin(Index: Cardinal); - public - PluginInterface: TUS_PluginInterface; - Plugins: array of TPluginListItem; - - // TCoreModule methods to inherit - constructor Create; override; - procedure Info(const pInfo: PModuleInfo); override; - function Load: Boolean; override; - function Init: Boolean; override; - procedure DeInit; override; - Destructor Destroy; override; - - // New methods - procedure BrowseDir(Path: String); // browses the path at _Path_ for plugins - function PluginExists(Name: String): integer; // if plugin exists: Index of plugin, else -1 - procedure AddPlugin(Filename: String); // adds plugin to the array - - function CallLoad(Index: Cardinal): integer; - function CallInit(Index: Cardinal): integer; - procedure CallDeInit(Index: Cardinal); - - //Services offered - function LoadPlugin(wParam: TwParam; lParam: TlParam): integer; //wParam PChar(PluginName/PluginPath) | lParam (if wParam = nil) ID of the Plugin - function UnloadPlugin(wParam: TwParam; lParam: TlParam): integer; //wParam PChar(PluginName/PluginPath) | lParam (if wParam = nil) ID of the Plugin - function GetPluginInfo(wParam: TwParam; lParam: TlParam): integer; //If wParam = -1 then (If lParam = nil then get length of Moduleinfo Array. If lparam <> nil then write array of TUS_PluginInfo to address at lparam) else (Get PluginInfo of Plugin with Index(wParam) to Address at lParam) - function GetPluginState(wParam: TwParam; lParam: TlParam): integer; //If wParam = -1 then (If lParam = nil then get length of Moduleinfo Array. If lparam <> nil then write array of TUS_PluginInfo to address at lparam) else (Return PluginInfo of Plugin with Index(wParam)) - - end; - - {********************* - TtehPlugins - Class represents the plugins in module chain. - It calls the plugins procs and funcs - *********************} - TtehPlugins = class (TCoreModule) - private - PluginLoader: PPluginLoader; - public - // TCoreModule methods to inherit - constructor Create; override; - - procedure Info(const pInfo: PModuleInfo); override; - function Load: Boolean; override; - function Init: Boolean; override; - procedure DeInit; override; - end; - -const -{$IF Defined(MSWINDOWS)} - PluginFileExtension = '.dll'; -{$ELSEIF Defined(DARWIN)} - PluginFileExtension = '.dylib'; -{$ELSEIF Defined(UNIX)} - PluginFileExtension = '.so'; -{$IFEND} - -implementation - -uses - UCore, - UPluginInterface, -{$IFDEF MSWINDOWS} - windows, -{$ELSE} - dynlibs, -{$ENDIF} - UMain, - SysUtils; - -{********************* - TPluginLoader - Implementation -*********************} - -//------------- -// function that gives some infos about the module to the core -//------------- -procedure TPluginLoader.Info(const pInfo: PModuleInfo); -begin - pInfo^.Name := 'TPluginLoader'; - pInfo^.Version := MakeVersion(1,0,0,chr(0)); - pInfo^.Description := 'Searches for plugins, loads and unloads them'; -end; - -//------------- -// Just the constructor -//------------- -constructor TPluginLoader.Create; -begin - inherited; - - // Init PluginInterface - // Using methods from UPluginInterface - PluginInterface.CreateHookableEvent := CreateHookableEvent; - PluginInterface.DestroyHookableEvent := DestroyHookableEvent; - PluginInterface.NotivyEventHooks := NotivyEventHooks; - PluginInterface.HookEvent := HookEvent; - PluginInterface.UnHookEvent := UnHookEvent; - PluginInterface.EventExists := EventExists; - - PluginInterface.CreateService := @CreateService; - PluginInterface.DestroyService := DestroyService; - PluginInterface.CallService := CallService; - PluginInterface.ServiceExists := ServiceExists; - - // UnSet private var - LoadingProcessFinished := False; -end; - -//------------- -// Is called on loading. -// In this method only events and services should be created -// to offer them to other modules or plugins during the init process -// if false is returned this will cause a forced exit -//------------- -function TPluginLoader.Load: Boolean; -begin - Result := True; - - try - // Start searching for plugins - BrowseDir(PluginPath); - except - Result := False; - Core.ReportError(integer(PChar('Error browsing and loading.')), PChar('TPluginLoader')); - end; -end; - -//------------- -// Is called on init process -// In this method you can hook some events and create + init -// your classes, variables etc. -// If false is returned this will cause a forced exit -//------------- -function TPluginLoader.Init: Boolean; -begin - // Just set private var to true. - LoadingProcessFinished := True; - Result := True; -end; - -//------------- -// Is called if this module has been inited and there is a exit. -// Deinit is in backwards initing order -//------------- -procedure TPluginLoader.DeInit; -var - I: integer; -begin - // Force deinit - // if some plugins aren't deinited for some reason o0 - for I := 0 to High(Plugins) do - begin - if (Plugins[I].State < 4) then - FreePlugin(I); - end; - - // Nothing to do here. Core will remove the hooks -end; - -//------------- -// Is called if this module will be unloaded and has been created -// Should be used to free memory -//------------- -Destructor TPluginLoader.Destroy; -begin - // Just save some memory if it wasn't done now.. - SetLength(Plugins, 0); - inherited; -end; - -//-------------- -// Browses the path at _Path_ for plugins -//-------------- -procedure TPluginLoader.BrowseDir(Path: String); -var - SR: TSearchRec; -begin - // Search for other dirs to browse - if FindFirst(Path + '*', faDirectory, SR) = 0 then begin - repeat - if (SR.Name <> '.') and (SR.Name <> '..') then - BrowseDir(Path + Sr.Name + PathDelim); - until FindNext(SR) <> 0; - end; - FindClose(SR); - - // Search for plugins at path - if FindFirst(Path + '*' + PluginFileExtension, 0, SR) = 0 then - begin - repeat - AddPlugin(Path + SR.Name); - until FindNext(SR) <> 0; - end; - FindClose(SR); -end; - -//-------------- -// If plugin exists: Index of plugin, else -1 -//-------------- -function TPluginLoader.PluginExists(Name: String): integer; -var - I: integer; -begin - Result := -1; - - if (Length(Name) <= 32 { =>Length(TUS_PluginInfo.Name)}) then - begin - for I := 0 to High(Plugins) do - if (Plugins[I].Info.Name = Name) then - begin //Found the Plugin - Result := I; - Break; - end; - end; -end; - -//-------------- -// Adds plugin to the array -//-------------- -procedure TPluginLoader.AddPlugin(Filename: String); -var - hLib: THandle; - PInfo: Proc_PluginInfo; - Info: TUS_PluginInfo; - PluginID: integer; -begin - if (FileExists(Filename)) then - begin //Load Libary - hLib := LoadLibrary(PChar(Filename)); - if (hLib <> 0) then - begin // Try to get address of the info proc - PInfo := GetProcAddress (hLib, PChar('USPlugin_Info')); - if (@PInfo <> nil) then - begin - Info.cbSize := SizeOf(TUS_PluginInfo); - - try // Call info proc - PInfo(@Info); - except - Info.Name := ''; - Core.ReportError(integer(PChar('Error getting plugin info: ' + Filename)), PChar('TPluginLoader')); - end; - - // Is name set ? - if (Trim(Info.Name) <> '') then - begin - PluginID := PluginExists(Info.Name); - - if (PluginID > 0) and (Plugins[PluginID].State >=4) then - PluginID := -1; - - if (PluginID = -1) then - begin - // Add new item to array - PluginID := Length(Plugins); - SetLength(Plugins, PluginID + 1); - - // Fill with info: - Plugins[PluginID].Info := Info; - Plugins[PluginID].State := 0; - Plugins[PluginID].Path := Filename; - Plugins[PluginID].NeedsDeInit := False; - Plugins[PluginID].hLib := hLib; - - // Try to get procs - Plugins[PluginID].Procs.Load := GetProcAddress (hLib, PChar('USPlugin_Load')); - Plugins[PluginID].Procs.Init := GetProcAddress (hLib, PChar('USPlugin_Init')); - Plugins[PluginID].Procs.DeInit := GetProcAddress (hLib, PChar('USPlugin_DeInit')); - - if (@Plugins[PluginID].Procs.Load = nil) OR (@Plugins[PluginID].Procs.Init = nil) OR (@Plugins[PluginID].Procs.DeInit = nil) then - begin - Plugins[PluginID].State := 255; - FreeLibrary(hLib); - Core.ReportError(integer(PChar('Can''t get plugin procs from libary: "' + Info.Name + '" ' + Filename)), PChar('TPluginLoader')); - end; - - // Emulate loading process if this plugin is loaded too late - if (LoadingProcessFinished) then - begin - CallLoad(PluginID); - CallInit(PluginID); - end; - end - else if (LoadingProcessFinished = False) then - begin - if (Plugins[PluginID].Info.Version < Info.Version) then - begin // Found newer version of this plugin - Core.ReportDebug(integer(PChar('Found a newer version of plugin: ' + String(Info.Name))), PChar('TPluginLoader')); - - // Unload old plugin - UnloadPlugin(PluginID, nil); - - // Fill with new info - Plugins[PluginID].Info := Info; - Plugins[PluginID].State := 0; - Plugins[PluginID].Path := Filename; - Plugins[PluginID].NeedsDeInit := False; - Plugins[PluginID].hLib := hLib; - - // Try to get procs - Plugins[PluginID].Procs.Load := GetProcAddress (hLib, PChar('USPlugin_Load')); - Plugins[PluginID].Procs.Init := GetProcAddress (hLib, PChar('USPlugin_Init')); - Plugins[PluginID].Procs.DeInit := GetProcAddress (hLib, PChar('USPlugin_DeInit')); - - if (@Plugins[PluginID].Procs.Load = nil) OR (@Plugins[PluginID].Procs.Init = nil) OR (@Plugins[PluginID].Procs.DeInit = nil) then - begin - FreeLibrary(hLib); - Plugins[PluginID].State := 255; - Core.ReportError(integer(PChar('Can''t get plugin procs from libary: "' + Info.Name + '" ' + Filename)), PChar('TPluginLoader')); - end; - end - else - begin // Newer Version already loaded - FreeLibrary(hLib); - end; - end - else - begin - FreeLibrary(hLib); - Core.ReportError(integer(PChar('Plugin with this name already exists: ' + String(Info.Name))), PChar('TPluginLoader')); - end; - end - else - begin - FreeLibrary(hLib); - Core.ReportError(integer(PChar('No name reported: ' + Filename)), PChar('TPluginLoader')); - end; - end - else - begin - FreeLibrary(hLib); - Core.ReportError(integer(PChar('Can''t find info procedure: ' + Filename)), PChar('TPluginLoader')); - end; - end - else - Core.ReportError(integer(PChar('Can''t load plugin libary: ' + Filename)), PChar('TPluginLoader')); - end; -end; - -//-------------- -// Calls load func of plugin with the given index -//-------------- -function TPluginLoader.CallLoad(Index: Cardinal): integer; -begin - Result := -2; - if(Index < Length(Plugins)) then - begin - if (@Plugins[Index].Procs.Load <> nil) and (Plugins[Index].State = 0) then - begin - try - Result := Plugins[Index].Procs.Load(@PluginInterface); - except - Result := -3; - End; - - if (Result = 0) then - Plugins[Index].State := 1 - else - begin - FreePlugin(Index); - Plugins[Index].State := 255; - Core.ReportError(integer(PChar('Error calling load function from plugin: ' + String(Plugins[Index].Info.Name))), PChar('TPluginLoader')); - end; - end; - end; -end; - -//-------------- -// Calls init func of plugin with the given index -//-------------- -function TPluginLoader.CallInit(Index: Cardinal): integer; -begin - Result := -2; - if(Index < Length(Plugins)) then - begin - if (@Plugins[Index].Procs.Init <> nil) and (Plugins[Index].State = 1) then - begin - try - Result := Plugins[Index].Procs.Init(@PluginInterface); - except - Result := -3; - End; - - if (Result = 0) then - begin - Plugins[Index].State := 2; - Plugins[Index].NeedsDeInit := True; - end - else - begin - FreePlugin(Index); - Plugins[Index].State := 255; - Core.ReportError(integer(PChar('Error calling init function from plugin: ' + String(Plugins[Index].Info.Name))), PChar('TPluginLoader')); - end; - end; - end; -end; - -//-------------- -// Calls deinit proc of plugin with the given index -//-------------- -procedure TPluginLoader.CallDeInit(Index: Cardinal); -begin - if(Index < Length(Plugins)) then - begin - if (Plugins[Index].State < 4) then - begin - if (@Plugins[Index].Procs.DeInit <> nil) and (Plugins[Index].NeedsDeInit) then - try - Plugins[Index].Procs.DeInit(@PluginInterface); - except - - End; - - // Don't forget to remove services and subscriptions by this plugin - Core.Hooks.DelbyOwner(-1 - Index); - - FreePlugin(Index); - end; - end; -end; - -//-------------- -// Frees all plugin sources (procs and handles) - helper for deiniting functions -//-------------- -procedure TPluginLoader.FreePlugin(Index: Cardinal); -begin - Plugins[Index].State := 4; - Plugins[Index].Procs.Load := nil; - Plugins[Index].Procs.Init := nil; - Plugins[Index].Procs.DeInit := nil; - - if (Plugins[Index].hLib <> 0) then - FreeLibrary(Plugins[Index].hLib); -end; - - - -//-------------- -// wParam PChar(PluginName/PluginPath) | wParam (if lParam = nil) ID of the plugin -//-------------- -function TPluginLoader.LoadPlugin(wParam: TwParam; lParam: TlParam): integer; -var - Index: integer; - sFile: String; -begin - Result := -1; - sFile := ''; - // lParam is ID - if (lParam = nil) then - begin - Index := wParam; - end - else - begin //lParam is PChar - try - sFile := String(PChar(lParam)); - Index := PluginExists(sFile); - if (Index < 0) And FileExists(sFile) then - begin // Is filename - AddPlugin(sFile); - Result := Plugins[High(Plugins)].State; - end; - except - Index := -2; - end; - end; - - - if (Index >= 0) and (Index < Length(Plugins)) then - begin - AddPlugin(Plugins[Index].Path); - Result := Plugins[Index].State; - end; -end; - -//-------------- -// wParam PChar(PluginName/PluginPath) | wParam (if lParam = nil) ID of the plugin -//-------------- -function TPluginLoader.UnloadPlugin(wParam: TwParam; lParam: TlParam): integer; -var - Index: integer; - sName: String; -begin - Result := -1; - // lParam is ID - if (lParam = nil) then - begin - Index := wParam; - end - else - begin // wParam is PChar - try - sName := String(PChar(lParam)); - Index := PluginExists(sName); - except - Index := -2; - end; - end; - - - if (Index >= 0) and (Index < Length(Plugins)) then - CallDeInit(Index) -end; - -//-------------- -// if wParam = -1 then (if lParam = nil then get length of moduleinfo array. if lparam <> nil then write array of TUS_PluginInfo to address at lparam) else (Get PluginInfo of plugin with Index(wParam) to address at lParam) -//-------------- -function TPluginLoader.GetPluginInfo(wParam: TwParam; lParam: TlParam): integer; -var I: integer; -begin - Result := 0; - if (wParam > 0) then - begin // Get info of 1 plugin - if (lParam <> nil) and (wParam < Length(Plugins)) then - begin - try - Result := 1; - PUS_PluginInfo(lParam)^ := Plugins[wParam].Info; - except - - End; - end; - end - else if (lParam = nil) then - begin // Get length of plugin (info) array - Result := Length(Plugins); - end - else //Write PluginInfo Array to Address in lParam - begin - try - for I := 0 to high(Plugins) do - PAUS_PluginInfo(lParam)^[I] := Plugins[I].Info; - Result := Length(Plugins); - except - Core.ReportError(integer(PChar('Could not write PluginInfo Array')), PChar('TPluginLoader')); - End; - end; - -end; - -//-------------- -// if wParam = -1 then (if lParam = nil then get length of plugin state array. if lparam <> nil then write array of Byte to address at lparam) else (return state of plugin with index(wParam)) -//-------------- -function TPluginLoader.GetPluginState(wParam: TwParam; lParam: TlParam): integer; -var I: integer; -begin - Result := -1; - if (wParam > 0) then - begin // Get state of 1 plugin - if (wParam < Length(Plugins)) then - begin - Result := Plugins[wParam].State; - end; - end - else if (lParam = nil) then - begin // Get length of plugin (info) array - Result := Length(Plugins); - end - else // Write plugininfo array to address in lParam - begin - try - for I := 0 to high(Plugins) do - Byte(Pointer(integer(lParam) + I)^) := Plugins[I].State; - Result := Length(Plugins); - except - Core.ReportError(integer(PChar('Could not write pluginstate array')), PChar('TPluginLoader')); - End; - end; -end; - - -{********************* - TtehPlugins - Implementation -*********************} - -//------------- -// function that gives some infos about the module to the core -//------------- -procedure TtehPlugins.Info(const pInfo: PModuleInfo); -begin - pInfo^.Name := 'TtehPlugins'; - pInfo^.Version := MakeVersion(1,0,0,chr(0)); - pInfo^.Description := 'Module executing the Plugins!'; -end; - -//------------- -// Just the constructor -//------------- -constructor TtehPlugins.Create; -begin - inherited; - PluginLoader := nil; -end; - -//------------- -// Is called on loading. -// In this method only events and services should be created -// to offer them to other modules or plugins during the init process -// if false is returned this will cause a forced exit -//------------- -function TtehPlugins.Load: Boolean; -var - i: integer; // Counter - CurExecutedBackup: integer; //backup of Core.CurExecuted Attribute -begin - // Get pointer to pluginloader - PluginLoader := PPluginLoader(Core.GetModulebyName('TPluginLoader')); - if (PluginLoader = nil) then - begin - Result := false; - Core.ReportError(integer(PChar('Could not get pointer to pluginLoader')), PChar('TtehPlugins')); - end - else - begin - Result := true; - - // Backup curexecuted - CurExecutedBackup := Core.CurExecuted; - - // Start loading the plugins - for i := 0 to High(PluginLoader.Plugins) do - begin - Core.CurExecuted := -1 - i; - - try - // Unload plugin if not correctly executed - if (PluginLoader.CallLoad(i) <> 0) then - begin - PluginLoader.CallDeInit(i); - PluginLoader.Plugins[i].State := 254; // Plugin asks for unload - Core.ReportDebug(integer(PChar('Plugin selfabort during loading process: ' + String(PluginLoader.Plugins[i].Info.Name))), PChar('TtehPlugins')); - end - else - begin - Core.ReportDebug(integer(PChar('Plugin loaded succesfully: ' + String(PluginLoader.Plugins[i].Info.Name))), PChar('TtehPlugins')); - end; - except - // Plugin could not be loaded. - // => Show error message, then shutdown plugin - on E: Exception do - begin - PluginLoader.CallDeInit(i); - PluginLoader.Plugins[i].State := 255; // Plugin causes error - Core.ReportError(integer(PChar('Plugin causes error during loading process: ' + PluginLoader.Plugins[i].Info.Name + ', ErrorMsg: "' + E.Message + '"')), PChar('TtehPlugins')); - end; - end; - end; - - // Reset CurExecuted - Core.CurExecuted := CurExecutedBackup; - end; -end; - -//------------- -// Is called on init process -// in this method you can hook some events and create + init -// your classes, variables etc. -// if false is returned this will cause a forced exit -//------------- -function TtehPlugins.Init: Boolean; -var - i: integer; // Counter - CurExecutedBackup: integer; // backup of Core.CurExecuted attribute -begin - Result := true; - - // Backup CurExecuted - CurExecutedBackup := Core.CurExecuted; - - // Start loading the plugins - for i := 0 to High(PluginLoader.Plugins) do - try - Core.CurExecuted := -1 - i; - - // Unload plugin if not correctly executed - if (PluginLoader.CallInit(i) <> 0) then - begin - PluginLoader.CallDeInit(i); - PluginLoader.Plugins[i].State := 254; //Plugin asks for unload - Core.ReportDebug(integer(PChar('Plugin selfabort during init process: ' + String(PluginLoader.Plugins[i].Info.Name))), PChar('TtehPlugins')); - end - else - Core.ReportDebug(integer(PChar('Plugin inited succesfully: ' + String(PluginLoader.Plugins[i].Info.Name))), PChar('TtehPlugins')); - except - // Plugin could not be loaded. - // => Show error message, then shut down plugin - PluginLoader.CallDeInit(i); - PluginLoader.Plugins[i].State := 255; //Plugin causes Error - Core.ReportError(integer(PChar('Plugin causes error during init process: ' + String(PluginLoader.Plugins[i].Info.Name))), PChar('TtehPlugins')); - end; - - // Reset CurExecuted - Core.CurExecuted := CurExecutedBackup; -end; - -//------------- -// Is called if this module has been inited and there is a exit. -// Deinit is in backwards initing order -//------------- -procedure TtehPlugins.DeInit; -var - i: integer; // Counter - CurExecutedBackup: integer; // backup of Core.CurExecuted attribute -begin - // Backup CurExecuted - CurExecutedBackup := Core.CurExecuted; - - // Start loop - - for i := 0 to High(PluginLoader.Plugins) do - begin - try - // DeInit plugin - PluginLoader.CallDeInit(i); - except - end; - end; - - // Reset CurExecuted - Core.CurExecuted := CurExecutedBackup; -end; - -end. diff --git a/src/ultrastardx.dpr b/src/ultrastardx.dpr index 36dc7ee8..c35cb620 100644 --- a/src/ultrastardx.dpr +++ b/src/ultrastardx.dpr @@ -195,7 +195,7 @@ uses UCore in 'base\UCore.pas', //Core, Maybe remove this UCoreModule in 'base\UCoreModule.pas', //^ UPluginInterface in 'base\UPluginInterface.pas', //Interface offered by Core to Plugins - uPluginLoader in 'base\uPluginLoader.pas', //New Plugin Loader Module + UPluginLoader in 'base\UPluginLoader.pas', //New Plugin Loader Module UParty in 'base\UParty.pas', // TODO: rewrite Party Manager as Module, reomplent ability to offer party Mody by Plugin -- cgit v1.2.3