aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authork-m_schindler <k-m_schindler@b956fd51-792f-4845-bead-9b4dfca2ff2c>2014-11-22 13:50:25 +0000
committerk-m_schindler <k-m_schindler@b956fd51-792f-4845-bead-9b4dfca2ff2c>2014-11-22 13:50:25 +0000
commitac1f8196a723e587f12ad355669d366f10e06f51 (patch)
treed2965a556a2ca9001f7b0da8580f3b7e97d5a8af
parente727ebce99fef8cd4d4d304702d6d4109e0143f3 (diff)
downloadusdx-ac1f8196a723e587f12ad355669d366f10e06f51.tar.gz
usdx-ac1f8196a723e587f12ad355669d366f10e06f51.tar.xz
usdx-ac1f8196a723e587f12ad355669d366f10e06f51.zip
adjust eol and set svn property svn:eol-style native for all in lua
git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@3087 b956fd51-792f-4845-bead-9b4dfca2ff2c
-rw-r--r--src/lua/UHookableEvent.pas758
-rw-r--r--src/lua/ULuaCore.pas1970
-rw-r--r--src/lua/ULuaParty.pas780
-rw-r--r--src/lua/ULuaScreenSing.pas976
-rw-r--r--src/lua/ULuaUsdx.pas288
-rw-r--r--src/lua/ULuaUtils.pas370
6 files changed, 2571 insertions, 2571 deletions
diff --git a/src/lua/UHookableEvent.pas b/src/lua/UHookableEvent.pas
index 8ad7ea9c..7193654a 100644
--- a/src/lua/UHookableEvent.pas
+++ b/src/lua/UHookableEvent.pas
@@ -1,380 +1,380 @@
-{* 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 UHookableEvent;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses ULua;
-
-type
- { Record holding information about a hook of an event }
- PHook = ^THook;
- THook = record
- Handle: Integer; //< Handle to identify the hook, e.g. for unhooking by plugin
- Parent: Integer; //< Lua Core Handle this hook belongs to
-
- Func: String; //< Name of the global that holds the function
-
- Next: PHook; //< Next Hook in list (nil for the first)
- end;
-
- { procedure is called before each call to the hooking lua functions, to push values on stack
- returns the number of pushed arguments}
- PrepareStackProc = Function(L: PLua_State): Integer;
-
- { class representing a hookable event }
- THookableEvent = class
- private
- iHandle: Integer; //< used to unregister at lua core
- LastHook: PHook; //< last hook in hook list, first to be called
- NextHookHandle: Integer; //< handle to identify next hook
-
- sName: String; //< the events name
-
- PrepareStack: PrepareStackProc; //< prepare stack procedure passed to constructor
- CallinProcess: boolean; //< true if a chain call is in process, to prepare unhooking during calls
- HooksToRemove: array of PHook; // hooks to delete after chaincall
-
- procedure RemoveWaitingHooks;
- public
- constructor Create(Name: String; const Proc: PrepareStackProc = nil);
-
- property Name: String read sName; //< returns the events name
- property Handle: Integer read iHandle; //< returns the events name
-
- procedure Hook(L: Plua_State; Parent: Integer; Func: String); //< pushes hook object/table to the lua stack
- procedure UnHook(L: Plua_State; hHook: Integer); //< unhook by plugin. push true or error string to lua stack
-
- procedure UnHookByParent(Parent: Integer); //< deletes all hooks by a specified parent (unhook by core)
-
- function CallHookChain(Breakable: Boolean): PLua_State; //< calls the events hookchain. if breakable, plugin can breake the chain by returning a value != 0 or false or nil
-
- destructor Destroy; override;
- end;
-
-{ the default function for THookableEvent.PrepareStack it don't pass any arguments }
-function PrepareStack_Dummy(L: PLua_State): Integer;
-
-{ function in resulting hook table. it calls the unhook command of the event on plugins demand }
-function LuaHook_UnHook(L: Plua_State): Integer; cdecl;
-
-implementation
-uses ULuaCore;
-
-constructor THookableEvent.Create(Name: String; const Proc: PrepareStackProc);
-begin
- inherited Create;
-
- Self.sName := Name;
-
- if (@Proc = nil) then
- Self.PrepareStack := @PrepareStack_Dummy
- else
- Self.PrepareStack := Proc;
-
- //init LastHook pointer w/ nil
- LastHook := nil;
- NextHookHandle := 1;
-
- iHandle := LuaCore.RegisterEvent(Self);
-end;
-
-destructor THookableEvent.Destroy;
-var
- Prev: PHook;
- Cur: PHook;
-begin
- //delete all hooks
- Cur := LastHook;
- While (Cur <> nil) do
- begin
- Prev := Cur;
- Cur := Prev.Next;
-
- Dispose(Prev);
- end;
-
- //remove from luacores list
- LuaCore.UnRegisterEvent(iHandle);
-
- inherited;
-end;
-
-{ adds hook to events list and pushes hook object/table to the lua stack }
-procedure THookableEvent.Hook(L: PLua_State; Parent: Integer; Func: String);
- var
- Item: PHook;
- P: TLuaPlugin;
-begin
- P := LuaCore.GetPluginById(Parent);
- if (P <> nil) then
- begin
- // get mem and fill it w/ data
- New(Item);
- Item.Handle := NextHookHandle;
- Inc(NextHookHandle);
-
- Item.Parent := Parent;
- Item.Func := Func;
-
- // add at front of the hook chain
- Item.Next := LastHook;
- LastHook := Item;
-
- //we need 2 free stack slots
- lua_checkstack(L, 2);
-
- //create the hook table, we need 2 elements (event name and unhook function)
- lua_createtable(L, 0, 2);
-
- //push events name
- lua_pushstring(L, PAnsiChar(Name));
-
- //add the name to the table
- lua_setfield(L, -2, 'Event');
-
- //push hook id to the stack
- lua_pushinteger(L, Item.Handle);
-
- //create a c closure, append one value from stack(the id)
- //this will pop both, the function and the id
- lua_pushcclosure(L, LuaHook_UnHook, 1);
-
- //add the function to our table
- lua_setfield(L, -2, 'Unhook');
-
- //the table is left on the stack, it is our result
- end;
-end;
-
-{ removes hooks in HookstoRemove array from chain }
-procedure THookableEvent.RemoveWaitingHooks;
- function IsInArray(Cur: PHook): boolean;
- var I: Integer;
- begin
- Result := false;
- for I := 0 to high(HooksToRemove) do
- if (HooksToRemove[I] = Cur) then
- begin
- Result := true;
- Break;
- end;
- end;
-
- var
- Cur, Prev: PHook;
-begin
- Prev := nil;
- Cur := LastHook;
-
- while (Cur <> nil) do
- begin
- if (IsInArray(Cur)) then
- begin //we found the hook
- if (prev <> nil) then
- Prev.Next := Cur.Next
- else //last hook found
- LastHook := Cur.Next;
-
- //free hooks memory
- Dispose(Cur);
-
- if (prev <> nil) then
- Cur := Prev.Next
- else
- Cur := LastHook;
- end
- else
- begin
- Prev := Cur;
- Cur := Prev.Next;
- end;
- end;
-
- SetLength(HooksToRemove, 0);
-end;
-
-{ unhook by plugin. push true or error string to lua stack }
-procedure THookableEvent.UnHook(L: Plua_State; hHook: Integer);
- var
- Cur, Prev: PHook;
- Len: integer;
-begin
- if (hHook < NextHookHandle) and (hHook > 0) then
- begin
- //Search for the Hook
- Prev := nil;
- Cur := LastHook;
-
- while (Cur <> nil) do
- begin
- if (Cur.Handle = hHook) then
- begin //we found the hook
- if not CallinProcess then
- begin // => remove it
- if (prev <> nil) then
- Prev.Next := Cur.Next
- else //last hook found
- LastHook := Cur.Next;
-
- //free hooks memory
- Dispose(Cur);
- end
- else
- begin // add to list of hooks to remove
- Len := Length(HooksToRemove);
- SetLength(HooksToRemove, Len + 1);
- HooksToRemove[Len] := Cur;
- end;
-
- //indicate success
- lua_pushboolean(L, True);
- exit; //break the chain and exit the function
- end;
- Prev := Cur;
- Cur := Prev.Next;
- end;
-
- lua_pushstring(L, PAnsiChar('handle already unhooked')); //the error description
- end
- else
- lua_pushstring(L, PAnsiChar('undefined hook handle')); //the error description
-end;
-
-{ deletes all hooks by a specified parent (unhook by core) }
-procedure THookableEvent.UnHookByParent(Parent: Integer);
- var
- Cur, Prev: PHook;
-begin
- Prev := nil;
- Cur := LastHook;
-
- While (Cur <> nil) do
- begin
- if (Cur.Parent = Parent) then
- begin //found a hook from parent => remove it
- if (Prev <> nil) then
- Prev.Next := Cur.Next
- Else
- LastHook := Cur.Next;
-
- Dispose(Cur);
-
- if (Prev <> nil) then
- Cur := Prev.Next
- else
- Cur := LastHook;
- end
- else //move through the chain
- begin
- Prev := Cur;
- Cur := Prev.Next;
- end;
- end;
-end;
-
-{ calls the events hookchain. if breakable, plugin can breake the chain
- by returning a value
- breakable is pushed as the first parameter to the hooking functions
- if chain is broken the LuaStack is returned, with all results left
- you may call lua_clearstack }
-function THookableEvent.CallHookChain(Breakable: Boolean): Plua_State;
- var
- Cur: PHook;
- P: TLuaPlugin;
-begin
- Result := nil;
-
- CallinProcess := true;
-
- Cur := LastHook;
- While (Cur <> nil) do
- begin
- P := LuaCore.GetPluginById(Cur.Parent);
- lua_pushboolean(P.LuaState, Breakable);
-
- if (P.CallFunctionByName(Cur.Func, 1 + PrepareStack(P.LuaState), LUA_MULTRET))
- and Breakable
- and (lua_gettop(P.LuaState) > 0) then
- begin //Chain Broken
- Result := P.LuaState;
- Break;
- end;
-
- Cur := Cur.Next;
- end;
-
- RemoveWaitingHooks;
- CallinProcess := false;
-end;
-
-{ the default function for THookableEvent.PrepareStack it don't pass any arguments }
-function PrepareStack_Dummy(L: PLua_State): Integer;
-begin
- Result := 0;
-end;
-
-{ function in resulting hook table. it calls the unhook command of the event on plugins demand }
-function LuaHook_UnHook(L: Plua_State): Integer; cdecl;
- var
- Name: string;
- Event: THookableEvent;
- hHook: integer;
-begin
- Result := 0;
-
- if not lua_isTable(L, 1) then
- LuaL_Error(L, 'Can''t find hook table in LuaHook_Unhook. Please call Unhook with method seperator (colon) instead of a point.');
-
- // get event name
- Lua_GetField(L, 1, 'Event');
- if not lua_isString(L, -1) then
- LuaL_Error(L, 'Can''t get event name in LuaHook_Unhook');
-
- Name := Lua_ToString(L, -1);
-
- // get event by name
- Event := LuaCore.GetEventbyName(Name);
-
- // free stack slots
- Lua_pop(L, Lua_GetTop(L));
-
- if (Event = nil) then
- LuaL_Error(L, PAnsiChar('event ' + Name + ' does not exist (anymore?) in LuaHook_Unhook'));
-
- // get the hookid
- hHook := lua_ToInteger(L, lua_upvalueindex(1));
-
- Event.UnHook(L, hHook);
-end;
-
+{* 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 UHookableEvent;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses ULua;
+
+type
+ { Record holding information about a hook of an event }
+ PHook = ^THook;
+ THook = record
+ Handle: Integer; //< Handle to identify the hook, e.g. for unhooking by plugin
+ Parent: Integer; //< Lua Core Handle this hook belongs to
+
+ Func: String; //< Name of the global that holds the function
+
+ Next: PHook; //< Next Hook in list (nil for the first)
+ end;
+
+ { procedure is called before each call to the hooking lua functions, to push values on stack
+ returns the number of pushed arguments}
+ PrepareStackProc = Function(L: PLua_State): Integer;
+
+ { class representing a hookable event }
+ THookableEvent = class
+ private
+ iHandle: Integer; //< used to unregister at lua core
+ LastHook: PHook; //< last hook in hook list, first to be called
+ NextHookHandle: Integer; //< handle to identify next hook
+
+ sName: String; //< the events name
+
+ PrepareStack: PrepareStackProc; //< prepare stack procedure passed to constructor
+ CallinProcess: boolean; //< true if a chain call is in process, to prepare unhooking during calls
+ HooksToRemove: array of PHook; // hooks to delete after chaincall
+
+ procedure RemoveWaitingHooks;
+ public
+ constructor Create(Name: String; const Proc: PrepareStackProc = nil);
+
+ property Name: String read sName; //< returns the events name
+ property Handle: Integer read iHandle; //< returns the events name
+
+ procedure Hook(L: Plua_State; Parent: Integer; Func: String); //< pushes hook object/table to the lua stack
+ procedure UnHook(L: Plua_State; hHook: Integer); //< unhook by plugin. push true or error string to lua stack
+
+ procedure UnHookByParent(Parent: Integer); //< deletes all hooks by a specified parent (unhook by core)
+
+ function CallHookChain(Breakable: Boolean): PLua_State; //< calls the events hookchain. if breakable, plugin can breake the chain by returning a value != 0 or false or nil
+
+ destructor Destroy; override;
+ end;
+
+{ the default function for THookableEvent.PrepareStack it don't pass any arguments }
+function PrepareStack_Dummy(L: PLua_State): Integer;
+
+{ function in resulting hook table. it calls the unhook command of the event on plugins demand }
+function LuaHook_UnHook(L: Plua_State): Integer; cdecl;
+
+implementation
+uses ULuaCore;
+
+constructor THookableEvent.Create(Name: String; const Proc: PrepareStackProc);
+begin
+ inherited Create;
+
+ Self.sName := Name;
+
+ if (@Proc = nil) then
+ Self.PrepareStack := @PrepareStack_Dummy
+ else
+ Self.PrepareStack := Proc;
+
+ //init LastHook pointer w/ nil
+ LastHook := nil;
+ NextHookHandle := 1;
+
+ iHandle := LuaCore.RegisterEvent(Self);
+end;
+
+destructor THookableEvent.Destroy;
+var
+ Prev: PHook;
+ Cur: PHook;
+begin
+ //delete all hooks
+ Cur := LastHook;
+ While (Cur <> nil) do
+ begin
+ Prev := Cur;
+ Cur := Prev.Next;
+
+ Dispose(Prev);
+ end;
+
+ //remove from luacores list
+ LuaCore.UnRegisterEvent(iHandle);
+
+ inherited;
+end;
+
+{ adds hook to events list and pushes hook object/table to the lua stack }
+procedure THookableEvent.Hook(L: PLua_State; Parent: Integer; Func: String);
+ var
+ Item: PHook;
+ P: TLuaPlugin;
+begin
+ P := LuaCore.GetPluginById(Parent);
+ if (P <> nil) then
+ begin
+ // get mem and fill it w/ data
+ New(Item);
+ Item.Handle := NextHookHandle;
+ Inc(NextHookHandle);
+
+ Item.Parent := Parent;
+ Item.Func := Func;
+
+ // add at front of the hook chain
+ Item.Next := LastHook;
+ LastHook := Item;
+
+ //we need 2 free stack slots
+ lua_checkstack(L, 2);
+
+ //create the hook table, we need 2 elements (event name and unhook function)
+ lua_createtable(L, 0, 2);
+
+ //push events name
+ lua_pushstring(L, PAnsiChar(Name));
+
+ //add the name to the table
+ lua_setfield(L, -2, 'Event');
+
+ //push hook id to the stack
+ lua_pushinteger(L, Item.Handle);
+
+ //create a c closure, append one value from stack(the id)
+ //this will pop both, the function and the id
+ lua_pushcclosure(L, LuaHook_UnHook, 1);
+
+ //add the function to our table
+ lua_setfield(L, -2, 'Unhook');
+
+ //the table is left on the stack, it is our result
+ end;
+end;
+
+{ removes hooks in HookstoRemove array from chain }
+procedure THookableEvent.RemoveWaitingHooks;
+ function IsInArray(Cur: PHook): boolean;
+ var I: Integer;
+ begin
+ Result := false;
+ for I := 0 to high(HooksToRemove) do
+ if (HooksToRemove[I] = Cur) then
+ begin
+ Result := true;
+ Break;
+ end;
+ end;
+
+ var
+ Cur, Prev: PHook;
+begin
+ Prev := nil;
+ Cur := LastHook;
+
+ while (Cur <> nil) do
+ begin
+ if (IsInArray(Cur)) then
+ begin //we found the hook
+ if (prev <> nil) then
+ Prev.Next := Cur.Next
+ else //last hook found
+ LastHook := Cur.Next;
+
+ //free hooks memory
+ Dispose(Cur);
+
+ if (prev <> nil) then
+ Cur := Prev.Next
+ else
+ Cur := LastHook;
+ end
+ else
+ begin
+ Prev := Cur;
+ Cur := Prev.Next;
+ end;
+ end;
+
+ SetLength(HooksToRemove, 0);
+end;
+
+{ unhook by plugin. push true or error string to lua stack }
+procedure THookableEvent.UnHook(L: Plua_State; hHook: Integer);
+ var
+ Cur, Prev: PHook;
+ Len: integer;
+begin
+ if (hHook < NextHookHandle) and (hHook > 0) then
+ begin
+ //Search for the Hook
+ Prev := nil;
+ Cur := LastHook;
+
+ while (Cur <> nil) do
+ begin
+ if (Cur.Handle = hHook) then
+ begin //we found the hook
+ if not CallinProcess then
+ begin // => remove it
+ if (prev <> nil) then
+ Prev.Next := Cur.Next
+ else //last hook found
+ LastHook := Cur.Next;
+
+ //free hooks memory
+ Dispose(Cur);
+ end
+ else
+ begin // add to list of hooks to remove
+ Len := Length(HooksToRemove);
+ SetLength(HooksToRemove, Len + 1);
+ HooksToRemove[Len] := Cur;
+ end;
+
+ //indicate success
+ lua_pushboolean(L, True);
+ exit; //break the chain and exit the function
+ end;
+ Prev := Cur;
+ Cur := Prev.Next;
+ end;
+
+ lua_pushstring(L, PAnsiChar('handle already unhooked')); //the error description
+ end
+ else
+ lua_pushstring(L, PAnsiChar('undefined hook handle')); //the error description
+end;
+
+{ deletes all hooks by a specified parent (unhook by core) }
+procedure THookableEvent.UnHookByParent(Parent: Integer);
+ var
+ Cur, Prev: PHook;
+begin
+ Prev := nil;
+ Cur := LastHook;
+
+ While (Cur <> nil) do
+ begin
+ if (Cur.Parent = Parent) then
+ begin //found a hook from parent => remove it
+ if (Prev <> nil) then
+ Prev.Next := Cur.Next
+ Else
+ LastHook := Cur.Next;
+
+ Dispose(Cur);
+
+ if (Prev <> nil) then
+ Cur := Prev.Next
+ else
+ Cur := LastHook;
+ end
+ else //move through the chain
+ begin
+ Prev := Cur;
+ Cur := Prev.Next;
+ end;
+ end;
+end;
+
+{ calls the events hookchain. if breakable, plugin can breake the chain
+ by returning a value
+ breakable is pushed as the first parameter to the hooking functions
+ if chain is broken the LuaStack is returned, with all results left
+ you may call lua_clearstack }
+function THookableEvent.CallHookChain(Breakable: Boolean): Plua_State;
+ var
+ Cur: PHook;
+ P: TLuaPlugin;
+begin
+ Result := nil;
+
+ CallinProcess := true;
+
+ Cur := LastHook;
+ While (Cur <> nil) do
+ begin
+ P := LuaCore.GetPluginById(Cur.Parent);
+ lua_pushboolean(P.LuaState, Breakable);
+
+ if (P.CallFunctionByName(Cur.Func, 1 + PrepareStack(P.LuaState), LUA_MULTRET))
+ and Breakable
+ and (lua_gettop(P.LuaState) > 0) then
+ begin //Chain Broken
+ Result := P.LuaState;
+ Break;
+ end;
+
+ Cur := Cur.Next;
+ end;
+
+ RemoveWaitingHooks;
+ CallinProcess := false;
+end;
+
+{ the default function for THookableEvent.PrepareStack it don't pass any arguments }
+function PrepareStack_Dummy(L: PLua_State): Integer;
+begin
+ Result := 0;
+end;
+
+{ function in resulting hook table. it calls the unhook command of the event on plugins demand }
+function LuaHook_UnHook(L: Plua_State): Integer; cdecl;
+ var
+ Name: string;
+ Event: THookableEvent;
+ hHook: integer;
+begin
+ Result := 0;
+
+ if not lua_isTable(L, 1) then
+ LuaL_Error(L, 'Can''t find hook table in LuaHook_Unhook. Please call Unhook with method seperator (colon) instead of a point.');
+
+ // get event name
+ Lua_GetField(L, 1, 'Event');
+ if not lua_isString(L, -1) then
+ LuaL_Error(L, 'Can''t get event name in LuaHook_Unhook');
+
+ Name := Lua_ToString(L, -1);
+
+ // get event by name
+ Event := LuaCore.GetEventbyName(Name);
+
+ // free stack slots
+ Lua_pop(L, Lua_GetTop(L));
+
+ if (Event = nil) then
+ LuaL_Error(L, PAnsiChar('event ' + Name + ' does not exist (anymore?) in LuaHook_Unhook'));
+
+ // get the hookid
+ hHook := lua_ToInteger(L, lua_upvalueindex(1));
+
+ Event.UnHook(L, hHook);
+end;
+
end. \ No newline at end of file
diff --git a/src/lua/ULuaCore.pas b/src/lua/ULuaCore.pas
index ea57b68b..e08dc488 100644
--- a/src/lua/ULuaCore.pas
+++ b/src/lua/ULuaCore.pas
@@ -1,985 +1,985 @@
-{* 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 ULuaCore;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses
- SysUtils,
- UHookableEvent,
- ULua,
- UPath;
-
-type
- { this exception is raised when the lua panic function
- is called. Only in case we use call instead of pcall.
- it has the lua error string in its message attribute }
- ELuaException = class(Exception);
-
- { record represents item of Eventlist of TLuaCore }
- PEventListItem = ^TEventListItem;
- TEventListItem = record
- Event: THookableEvent;
- Next: PEventListItem;
- end;
-
- { record represents a module }
- TLuaModule = record
- Name: string;
- Functions: array of luaL_reg; // modules functions, with trailing nils this time
- end;
-
- TLuaPlugin_Status = (psNone, psRunning, psClosed, psErrorOnLoad, psErrorOnCall, psErrorInInit, psErrorOnRun);
-
- { class represents a loaded plugin }
- TLuaPlugin = class
- private
- iId: integer;
- Filename: IPath;
- State: Plua_State; //< all functions of this plugin are called with this Lua state
- bPaused: boolean; //< If true no lua functions from this state are called
- ErrorCount: integer; //< counts the errors that occured during function calls of this plugin
- ShutDown: boolean; //< for self shutdown by plugin. true if plugin wants to be unloaded after execution of current function
-
- sName: string;
- sVersion: string;
- sAuthor: string;
- sURL: string;
-
- sStatus: TLuaPlugin_Status;
- public
- constructor Create(Filename: IPath; Id: integer);
-
- property Id: integer read iId;
- property Name: string read sName;
- property Version: string read sVersion;
- property Author: string read sAuthor;
- property Url: string read sUrl;
-
- property Status: TLuaPlugin_Status read sStatus;
- property CountErrors: integer read ErrorCount;
-
- property LuaState: Plua_State read State;
-
- procedure Load;
-
- procedure Register(Name, Version, Author, Url: string);
- function HasRegistered: boolean;
-
- procedure PausePlugin(doPause: boolean);
- property Paused: boolean read bPaused write PausePlugin;
-
- procedure ShutMeDown;
-
- { calls the lua function in the global with the given name.
- the arguments to the function have to be pushed to the stack
- before calling this function.
- the arguments and the function will be removed from stack
- results will not be removed.
- if result is false there was an error calling the function
- if ReportErrors is true the errorstring is popped from stack
- and written to error.log otherwise it is left on stack}
- function CallFunctionByName(Name: string;
- const nArgs: integer = 0;
- const nResults: integer = 0;
- const ReportErrors: boolean = true): boolean;
- procedure ClearStack;
-
- procedure Unload; //< Destroys the Luastate, and frees as much mem as possible, without destroying the class and important information
-
- destructor Destroy; override;
- end;
-
- { class managing the plugins with their LuaStates, the events and modules
- it also offers the usdx table to the plugins with some basic functionality
- like self unload or hook getting}
- TLuaCore = class
- private
- EventList: PEventListItem; //< pointer to first registred Event, ordered by name
- EventHandles: array of string; //< Index is Events handle, value is events name. if length(value) is 0 handle is considered unregistred
-
- Plugins: array of TLuaPlugin;
-
- eLoadingFinished: THookableEvent;
- protected
- Modules: array of TLuaModule; //< modules that has been registred, has to be proctected because fucntions of this unit need to get access
-
- function GetModuleIdByName(Name: string): integer; // returns id of given module, or -1 if module is not found
- public
- constructor Create;
- destructor Destroy; override;
-
- procedure LoadPlugins; //< calls LoadPlugin with Plugindir and LoadingFinished Eventchain
-
- procedure BrowseDir(Dir: IPath); //< searches for files with extension .usdx in the specified dir and tries to load them with lua
- procedure LoadPlugin(Filename: IPath); //< tries to load filename with lua and creates the default usdx lua environment for the plugins state
-
- function GetPluginByName(Name: string): TLuaPlugin;
- function GetPluginById(Id: integer): TLuaPlugin;
-
- { this function adds a module loader for your functions
- name is the name the script needs to write in its require()
- Functions is an array of lua calling compatible functions
- without trailing nils! }
- procedure RegisterModule(Name: string; const Functions: array of luaL_reg);
-
- function RegisterEvent(Event: THookableEvent): integer; //< adds the event to eventlist and returns its handle
- procedure UnRegisterEvent(hEvent: integer); //< removes the event from eventlist by handle
-
- function GetEventbyName(Name: string): THookableEvent; //< tries to find the event with the given name in the list
- function GetEventbyHandle(hEvent: integer): THookableEvent; //< tries to find the event with the given handle
-
- procedure UnHookByParent(Parent: integer); //< remove all hooks by given parent id from all events
-
- procedure PrepareState(L: Plua_State);
-
- procedure DumpPlugins; //< prints plugin runtime information with Log.LogStatus
- end;
-
-// some luastyle functions to call from lua scripts
-{ register global, used by plugins to identify
- register(plugin name, plugin version, [plugin author], [plugin homepage])
- can only be called once since the global "register" is niled by the function
- returns true on success. (name does not exist)}
-function TLuaPlugin_Register (L: Plua_State): integer; cdecl;
-
-{ moduleloader for usdx.* modules
- stored in package.loaders[3]
- package.loaders[3] (module name)
- returns a function to load the requested module or a error
- description(string) when the module is not found }
-function TLuaCore_ModuleLoader (L: Plua_State): integer; cdecl;
-
-{ loads module specified by a cfunction upvalue to
- usdx.modulename and returns it.
- loadmodule(module name) }
-function TLuaCore_LoadModule (L: Plua_State): integer; cdecl;
-
-{ custom lua panic function
- it writes error string to error.log and raises an ELuaException
- that may be caught }
-function TLua_CustomPanic (L: Plua_State): integer; cdecl;
-
-{ replacement for luas require function
- can be called with more than one parameter to require
- some modules at once. e.g.: require('math', 'Usdx.Log')
- modules are loaded from right to left
- unlike standard require the module tables are not returned
- the standard require function in _require is called by
- this function }
-function TLua_CustomRequire(L: PLua_State): integer; cdecl;
-
-var
- LuaCore: TLuaCore;
-
-implementation
-
-uses
- StrUtils,
- ULog,
- UFilesystem,
- ULuaUsdx,
- UPathUtils,
- ULuaUtils;
-
-constructor TLuaCore.Create;
-begin
- inherited;
-
- // init EventList with nil
- EventList := nil;
-
- eLoadingFinished := nil;
-end;
-
-destructor TLuaCore.Destroy;
-var
- Cur: PEventListItem;
- Prev: PEventListItem;
-begin
- SetLength(EventHandles, 0);
-
- // delete event list
- Cur := EventList;
-
- while(Cur <> nil) do
- begin
- Prev := Cur;
- Cur := Prev.Next;
-
- Dispose(Prev);
- end;
-
- inherited;
-end;
-
-{ calls BrowseDir with plugin dir and LoadingFinished eventchain }
-procedure TLuaCore.LoadPlugins;
-begin
- // we have to create event here, because in create it can
- // not be registred, because LuaCore is no assigned
- if (not Assigned(eLoadingFinished)) then
- eLoadingFinished := THookableEvent.Create('Usdx.LoadingFinished');
-
- BrowseDir(PluginPath);
- eLoadingFinished.CallHookChain(false);
-end;
-
-{ searches for files with extension .usdx in the specified
- dir and tries to load them with lua }
-procedure TLuaCore.BrowseDir(Dir: IPath);
- var
- Iter: IFileIterator;
- FileInfo: TFileInfo;
- FileName: IPath;
- Ext: IPath;
-begin
- Ext := Path('.usdx');
-
- // search for all files and directories
- Iter := FileSystem.FileFind(Dir.Append('*'), faAnyFile);
- while (Iter.HasNext) do
- begin
- FileInfo := Iter.Next;
- FileName := FileInfo.Name;
- if ((FileInfo.Attr and faDirectory) <> 0) then
- begin
- if (not FileName.Equals('.')) and (not FileName.Equals('..')) then
- BrowseDir(Dir.Append(FileName));
- end
- else
- begin
- if (Ext.Equals(FileName.GetExtension(), true)) then
- begin
- LoadPlugin(Dir.Append(FileName));
- end;
- end;
- end;
-end;
-
-{ tries to load filename with lua and creates the default
- usdx lua environment for the plugins state }
-procedure TLuaCore.LoadPlugin(Filename: IPath);
- var
- Len: integer;
-begin
- Len := Length(Plugins);
- SetLength(Plugins, Len + 1);
- Plugins[Len] := TLuaPlugin.Create(Filename, Len);
- Plugins[Len].Load;
-end;
-
-{ returns Plugin on success nil on failure }
-function TLuaCore.GetPluginByName(Name: string): TLuaPlugin;
- var
- I: integer;
-begin
- Result := nil;
- Name := lowercase(Name);
-
- for I := 0 to High(Plugins) do
- if (lowercase(Plugins[I].Name) = Name) then
- begin
- Result := GetPluginById(I);
- Exit;
- end;
-end;
-
-{ returns Plugin on success nil on failure }
-function TLuaCore.GetPluginById(Id: integer): TLuaPlugin;
-begin
- if (Id >= 0) and (Id <= High(Plugins)) then
- Result := Plugins[Id]
- else
- Result := nil;
-end;
-
-{ this function adds a module loader for your functions
- name is the name the script needs to write in its require()
- Functions is an array of lua calling compatible functions
- without trailing nils! }
-procedure TLuaCore.RegisterModule(Name: string; const Functions: array of luaL_reg);
- var
- Len: integer;
- FuncLen: integer;
- I: integer;
-begin
- Len := Length(Modules);
- SetLength(Modules, Len + 1);
- Modules[Len].Name := Name;
-
- FuncLen := Length(Functions);
- SetLength(Modules[Len].Functions, FuncLen + 1);
-
- for I := 0 to FuncLen-1 do
- Modules[Len].Functions[I] := Functions[I];
-
- Modules[Len].Functions[FuncLen].name := nil;
- Modules[Len].Functions[FuncLen].func := nil;
-end;
-
-{ adds the event to eventlist and returns its handle
- called by THookableEvent on creation }
-function TLuaCore.RegisterEvent(Event: THookableEvent): integer;
-var
- Cur, Prev, Item: PEventListItem;
-begin
- if (Event <> nil) and (Length(Event.Name) > 0) then
- begin
- Result := Length(EventHandles);
- SetLength(EventHandles, Result + 1); // get Handle and copy it to result
-
- EventHandles[Result] := Event.Name;
-
- // create eventlist item
- New(Item);
- Item.Event := Event;
-
- // search for a place for this event in alphabetical order
- Prev := nil;
- Cur := EventList;
-
- while (Cur <> nil) and (CompareStr(Cur.Event.Name, EventHandles[Result]) < 0) do
- begin
- Prev := Cur;
- Cur := Prev.Next;
- end;
-
- // found the place => add new item
- if (Prev <> nil) then
- Prev.Next := Item
- else // first item
- EventList := Item;
-
- Item.Next := Cur;
- end
- else
- Result := -1;
-end;
-
-{ removes the event from eventlist by handle }
-procedure TLuaCore.UnRegisterEvent(hEvent: integer);
- var
- Cur, Prev: PEventListItem;
-begin
- if (hEvent >= 0) and (hEvent <= High(EventHandles)) and (Length(EventHandles[hEvent]) > 0) then
- begin // hEvent in bounds and not already deleted
- // delete from eventlist
- Prev := nil;
- Cur := EventList;
-
- while (Cur <> nil) and (CompareStr(Cur.Event.Name, EventHandles[hEvent]) < 0) do
- begin
- Prev := Cur;
- Cur := Prev.Next;
- end;
-
- if (Cur <> nil) and (Cur.Event.Name = EventHandles[hEvent]) then
- begin // delete if found
- Prev.Next := Cur.Next; // remove from list
- Dispose(Cur); // free memory
- end;
-
- // delete from handle array
- EventHandles[hEvent] := '';
- end;
-end;
-
-{ tries to find the event with the given name in the list
- to-do : use binary search algorithm instead of linear search here
- check whether this is possible (events are saved in a pointer list) }
-function TLuaCore.GetEventbyName(Name: string): THookableEvent;
- var
- Cur: PEventListItem;
-begin
- Result := nil;
-
- if (Length(Name) > 0) then
- begin
- // search in eventlist
- Cur := EventList;
-
- while (Cur <> nil) and (CompareStr(Cur.Event.Name, Name) < 0) do
- begin
- Cur := Cur.Next;
- end;
-
- if (Cur <> nil) and (Cur.Event.Name = Name) then
- begin // we found what we want to find
- Result := Cur.Event;
- end;
- end;
-end;
-
-{ tries to find the event with the given handle }
-function TLuaCore.GetEventbyHandle(hEvent: integer): THookableEvent;
-begin
- if (hEvent >= 0) and (hEvent <= High(EventHandles)) and (Length(EventHandles[hEvent]) > 0) then
- begin // hEvent in bounds and not already deleted
- Result := GetEventByName(EventHandles[hEvent]);
- end
- else
- Result := nil;
-end;
-
-{ remove all hooks by given parent id from all events }
-procedure TLuaCore.UnHookByParent(Parent: integer);
- var
- Cur: PEventListItem;
-begin
- if (Parent >= 0) and (Parent <= High(Plugins)) then
- begin
- // go through event list
- Cur := EventList;
-
- while (Cur <> nil) do
- begin
- Cur.Event.UnHookByParent(Parent);
- Cur := Cur.Next;
- end;
- end;
-end;
-
-{ prepares the given already opened Lua state with the
- basic usdx environment, e.g.: base and package Modules,
- usdx moduleloader and usdx table }
-procedure TLuaCore.PrepareState(L: Plua_State);
-begin
- // load basic lib functionality
- lual_openLibs(L);
- lua_pop(L,1);
-
- // get package.searchers (former package.loaders) table
- lua_getGlobal (L, PChar('package'));
-{$IF LUA_VERSION_NUM >= 502}
- lua_getfield(L,-1,PChar('searchers'));
-{$ELSE}
- lua_getfield(L,-1,PChar('loaders'));
-{$IFEND}
-
- {**** Move C-Library and all-in-one module loader backwards,
- slot 3 is free now }
- // get package.loaders[4] function
- lua_pushinteger(L, 5); // push new index
- lua_pushinteger(L, 4); // push old index
- lua_gettable (L, -3);
-
- // and move it to package.loaders[5]
- lua_settable (L, -3);
-
- // get package.loaders[3] function
- lua_pushinteger(L, 4); // push new index
- lua_pushinteger(L, 3); // push old index
- lua_gettable (L, -3);
-
- // and move it to package.loaders[4]
- lua_settable (L, -3);
-
- {**** now we add the core module to package.loaders[3] }
- lua_pushinteger(L, 3); // push new loaders index
- lua_pushcfunction(L, TLuaCore_ModuleLoader);
-
- // and move it to package.loaders[3]
- lua_settable (L, -3);
-
- // pop both package and package.loaders tables from stack
- lua_pop(L, 2);
-
- {**** replace the standard require with our custom require function }
- // first move standard require function to _require
- lua_getglobal(L, PChar('require'));
-
- lua_setglobal(L, PChar('_require'));
-
- // then save custom require function to require
- lua_pushcfunction(L, TLua_CustomRequire);
-
- lua_setglobal(L, PChar('require'));
-
- {**** now we create the usdx table }
- // at first functions from ULuaUsdx
- luaL_register(L, 'Usdx', @ULuaUsdx_Lib_f[0]);
-end;
-
-{ returns id of given module, or -1 if module is not found }
-function TLuaCore.GetModuleIdByName(Name: string): integer;
- var
- I: integer;
-begin
- Result := -1;
-
- for I := 0 to High(Modules) do
- if (Modules[I].Name = Name) then
- begin
- Result := I;
- Exit;
- end;
-end;
-
-{ moduleloader for usdx.* modules
- stored in package.loaders[3]
- package.loaders[3] (module name)
- returns a function to load the requested module or an error
- description(string) when the module is not found }
-function TLuaCore_ModuleLoader (L: Plua_State): integer; cdecl;
- var
- Name: string;
- ID: integer;
-begin
- Result := 1; // we will return one value in any case (or never return in case of an error)
-
- if (lua_gettop(L) >= 1) then
- begin
- // pop all arguments but the first
- if (lua_gettop(L) > 1) then
- lua_pop(L, lua_gettop(L)-1);
-
-
- if (lua_IsString(L, 1)) then
- begin // we got the name => go get it
- Name := lua_toString(L, 1);
-
- // we need at least 6 letters
- // and first 5 letters have to be usdx.
- if (Length(Name) > 5) and (lowercase(copy(Name, 1, 5))='usdx.') then
- begin
- ID := LuaCore.GetModuleIdByName(copy(Name, 6, Length(Name) - 5));
- if (ID >= 0) then
- begin // found the module -> return loader function
- lua_pushinteger(L, Id);
- lua_pushcclosure(L, TLuaCore_LoadModule, 1);
- // the function is the result, so we leave it on stack
- end
- else
- lua_pushString(L, PChar('usdx module "' + Name + '" couldn''t be found'));
- end
- else
- lua_pushString(L, PChar('module doesn''t have "Usdx." prefix'));
-
- end
- else
- luaL_argerror(L, 1, PChar('string expected'));
- end
- else
- luaL_error(L, PChar('no modulename specified in usdx moduleloader'));
-end;
-
-{ loads module specified by a cfunction upvalue to
- usdx.modulename and returns it.
- loadmodule(module name) }
-function TLuaCore_LoadModule (L: Plua_State): integer; cdecl;
- var
- Id: integer;
-begin
- if (not lua_isnoneornil(L, lua_upvalueindex(1))) then
- begin
- Id := lua_ToInteger(L, lua_upvalueindex(1));
-{$IF LUA_VERSION_NUM >= 502}
- // set module table as a field of the global Usdx
- lua_getglobal(L,Pchar('Usdx'));
- luaL_register(L, PChar(LuaCore.Modules[Id].Name), @LuaCore.Modules[Id].Functions[0]);
- lua_setfield(L, -2, PChar(LuaCore.Modules[Id].Name));
-{$ELSE}
- luaL_register(L, PChar('Usdx.' + LuaCore.Modules[Id].Name), @LuaCore.Modules[Id].Functions[0]);
-
- // set the modules table as global "modulename"
- // so it can be accessed either by Usdx.modulename.x() or
- // by modulename.x()
- lua_setglobal(L, PChar(LuaCore.Modules[Id].Name));
-
- // no we net to push the table again to return it
- lua_getglobal(L, PChar(LuaCore.Modules[Id].Name));
-{$IFEND}
- Result := 1; // return table
- end
- else
- luaL_error(L, PChar('no upvalue found in LuaCore_LoadModule'));
-end;
-
-{ prints plugin runtime information with Log.LogStatus }
-procedure TLuaCore.DumpPlugins;
- function PluginStatusToString(Status: TLuaPlugin_Status): string;
- begin
- case Status of
- psNone: Result := 'not loaded';
- psRunning: Result := 'running';
- psClosed: Result := 'closed';
- psErrorOnLoad: Result := 'error during load';
- psErrorOnCall: Result := 'error during call';
- psErrorInInit: Result := 'error in plugin_init()';
- psErrorOnRun: Result := 'error on function call';
- else Result := 'unknown';
- end;
- end;
-
-var
- I: integer;
-begin
- // print table header
- Log.LogStatus(Format('%3s %-30s %-8s %-10s %-7s %-6s', [
- '#', 'Name', 'Version', 'Status', 'Paused', '#Errors'
- ]), 'LuaCore Plugins');
-
- for I := 0 to High(Plugins) do
- Log.LogStatus(Format('%3d %-30s %-8s %-10s %-7s %-6d', [
- Plugins[I].Id, Plugins[I].Name, Plugins[I].Version,
- PluginStatusToString(Plugins[I].Status),
- BoolToStr(Plugins[I].Paused, true),
- Plugins[I].CountErrors
- ]), 'LuaCore Plugins');
- if (High(Plugins) < 0) then
- Log.LogError(' no plugins loaded ', 'LuaCore Plugins');
-end;
-
-// Implementation of TLuaPlugin
-//--------
-constructor TLuaPlugin.Create(Filename: IPath; Id: integer);
-begin
- inherited Create;
- Self.iId := Id;
- Self.Filename := Filename;
-
- // set some default attributes
- Self.bPaused := false;
- Self.ErrorCount := 0;
- Self.sName := 'not registred';
- Self.sStatus := psNone;
- Self.ShutDown := false;
-
- State := nil; //< to prevent calls to unopened state
-end;
-
-destructor TLuaPlugin.Destroy;
-begin
- Unload;
- inherited;
-end;
-
-{ does the main loading part
- can not be called by create, because Plugins[Id] isn't defined there }
-procedure TLuaPlugin.Load;
-begin
- // create Lua state for this plugin
- State := luaL_newstate;
-
- // set our custom panic function if s/t went wrong along the init
- // we don't expect
- lua_atPanic(State, TLua_CustomPanic);
-
- if (LuaL_LoadFile(State, PChar(Filename.ToNative)) = 0) then
- begin // file loaded successful
- { note: we run the file here, but the environment isn't
- set up now. it just causes the functions to
- register in globals and runs the code in the file
- body. At least there should be no code, it could
- neither use functions from baselibs nor load libs
- with require, this code would be useless. }
- if (lua_pcall(State, 0, 0, 0) = 0) then
- begin // file called successful
-
- // let the core prepare our state
- LuaCore.PrepareState(State);
-
- // set register function
- lua_checkstack(State, 2);
- lua_pushinteger(State, Id);
- lua_pushcclosure(State, TLuaPlugin_Register, 1);
- lua_setglobal(State, PChar('register'));
-
- // write plugin id to registry
- lua_pushinteger(State, iId);
- lua_setfield (State, LUA_REGISTRYINDEX, '_USDX_STATE_ID');
- lua_pop(State, Lua_GetTop(State));
-
- // now run the plugin_init function
- // plugin_init() if false or nothing is returned plugin init is aborted
- if (CallFunctionByName('plugin_init', 0, 1)) then
- begin
- if (HasRegistered) and (sStatus = psNone) and (lua_toBoolean(State, 1)) then
- begin
- sStatus := psRunning;
- ClearStack;
- end
- else
- Unload;
- end
- else
- begin
- sStatus := psErrorInInit;
- Log.LogError('error in plugin_init: ' + Self.Filename.ToNative, 'lua');
- Unload;
- end;
- end
- else
- begin
- sStatus := psErrorOnLoad;
- Log.LogError(String(lua_toString(State, 1)), 'lua');
- Log.LogError('unable to call file: ' + Self.Filename.ToNative, 'lua');
- Unload;
- end;
-
- end
- else
- begin
- sStatus := psErrorOnLoad;
- Log.LogError(String(lua_toString(State, 1)), 'lua');
- Log.LogError('unable to load file: ' + Self.Filename.ToNative, 'lua');
- Unload;
- end;
-end;
-
-procedure TLuaPlugin.Register(Name, Version, Author, Url: string);
-begin
- sName := Name;
- sVersion := Version;
- sAuthor := Author;
- sURL := Url;
-end;
-
-{ returns true if plugin has called register }
-function TLuaPlugin.HasRegistered: boolean;
-begin
- Result := (Self.sName <> 'not registred');
-end;
-
-procedure TLuaPlugin.PausePlugin(doPause: boolean);
-begin
- bPaused := doPause;
-end;
-
-{ unload plugin after execution of the current function }
-procedure TLuaPlugin.ShutMeDown;
-begin
- ShutDown := true;
-end;
-
-{ calls the lua function in the global with the given name.
- the arguments to the function have to be pushed to the stack
- before calling this function.
- the arguments and the function will be removed from stack
- results will not be removed.
- if result is false there was an error calling the function,
- if ReportErrors is true the errorstring is popped from stack
- and written to error.log otherwise it is left on stack}
-function TLuaPlugin.CallFunctionByName(Name: string;
- const nArgs: integer;
- const nResults: integer;
- const ReportErrors: boolean): boolean;
-begin
- Result := false;
- if (State <> nil) then
- begin
- if (not bPaused) then
- begin
- // we need at least one stack slot free
- lua_checkstack(State, 1);
-
- lua_getglobal(State, PChar(Name));
-
- if (lua_isfunction(State, -1)) then
- begin // we got a function
- // move function in front of the arguments (if any)
- if (nArgs > 0) then
- lua_insert(State, -(nArgs + 1));
-
- // call it!
- if (lua_pcall(State, nArgs, nResults, 0) = 0) then
- Result := true // called without errors
- else // increase error counter
- Inc (ErrorCount);
- end
- else
- begin // we have to pop the args and the field we pushed from stack
- lua_pop(State, nArgs + 1);
- // leave an errormessage on stack
- lua_pushstring(State, Pchar('could not find function named ' + Name));
- end;
- end
- else
- begin // we have to pop the args from stack
- lua_pop(State, nArgs);
- // leave an errormessage on stack
- lua_pushstring(State, PChar('plugin paused'));
- end;
-
- if (not Result) and (ReportErrors) then
- Log.LogError(lua_toString(State, -1), 'lua/' + sName);
-
- if ShutDown then
- begin // plugin indicates self shutdown
- ShutDown := false;
- Unload;
- Result := false;
- end
- end
- else
- begin
- Log.LogError('trying to call function of closed or not opened lua state', IfThen(HasRegistered, Name, Filename.ToUTF8));
- end;
-end;
-
-{ removes all values from stack }
-procedure TLuaPlugin.ClearStack;
-begin
- if (State <> nil) and (lua_gettop(State) > 0) then
- lua_pop(State, lua_gettop(State));
-end;
-
-{ destroys the lua state, and frees as much mem as possible,
- without destroying the class and important information }
-procedure TLuaPlugin.Unload;
-begin
- if (State <> nil) then
- begin
- if (Status in [psRunning, psErrorOnRun]) then
- CallFunctionByName('plugin_unload');
-
- ClearStack;
- lua_close(State);
- State := nil; // don't forget to nil it ;)
-
- LuaCore.UnHookByParent(iId);
-
- if (sStatus = psRunning) then
- sStatus := psClosed;
- end;
-end;
-
-function TLuaPlugin_Register (L: Plua_State): integer; cdecl;
- var
- Id: integer;
- P: TLuaPlugin;
- Name, Version, Author, Url: string;
-begin
- if (lua_gettop(L) >= 2) then
- begin // we got at least name and version
- if (not lua_isNumber(L, lua_upvalueindex(1))) then
- luaL_Error(L, PChar('upvalue missing'));
-
- if (not lua_isString(L, 1)) then
- luaL_ArgError(L, 1, 'string expected');
-
- if (not lua_isString(L, 2)) then
- luaL_ArgError(L, 1, 'string expected');
-
- Id := lua_ToInteger(L, lua_upvalueindex(1));
-
- // get version and name
- Name := lua_tostring(L, 1);
- Version := lua_tostring(L, 2);
-
- // get optional parameters
- if (lua_isString(L, 3)) then // author
- Author := lua_toString(L, 3)
- else
- begin
- Author := 'unknown';
- end;
-
- // homepage
- if (lua_isString(L, 4)) then
- Url := lua_toString(L, 4)
- else
- begin
- Url := '';
- end;
-
- // clear stack
- if (lua_gettop(L) > 0) then
- lua_pop(L, lua_gettop(L));
-
- // call register
- P := LuaCore.GetPluginById(Id);
- if (P <> nil) then
- P.Register(Name, Version, Author, Url)
- else
- luaL_error(L, PChar('wrong id in upstream'));
-
- // remove function from global register
- lua_pushnil(L);
- lua_setglobal(L, PChar('register'));
-
- // return true
- Result := 1;
- lua_pushboolean(L, true);
- end
- else
- luaL_error(L, PChar('not enough arguments, at least 2 expected. in TLuaPlugin_Register'));
-end;
-
-{ custom lua panic function
- it writes error string to error.log and raises an ELuaException
- that may be caught }
-function TLua_CustomPanic (L: Plua_State): integer; cdecl;
- var
- Msg: string;
-begin
- if (lua_isString(L, -1)) then
- Msg := lua_toString(L, -1)
- else
- Msg := 'undefined lua panic';
-
- Log.LogError(Msg, 'lua');
-
- raise ELuaException.Create(Msg);;
-
- Result := 0;
-end;
-
-{ replacement for luas require function
- can be called with more than one parameter to require
- some modules at once. e.g.: require('math', 'Usdx.Log')
- modules are loaded from right to left
- unlike standard require the module tables are not returned
- the standard require function in _require is called by
- this function }
-function TLua_CustomRequire(L: PLua_State): integer; cdecl;
-begin
- // no results
- Result := 0;
-
- // move through parameters
- while (lua_getTop(L) >= 1) do
- begin
- lua_checkstack(L,1);
- // get luas require function
- lua_getglobal(L, PChar('_require'));
-
- // move it under the top param
- lua_insert(L, -2);
-
- // call it with next param (function + param are poped from stack)
- lua_call(L, 1, 0);
- end;
-end;
-
-end. \ No newline at end of file
+{* 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 ULuaCore;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SysUtils,
+ UHookableEvent,
+ ULua,
+ UPath;
+
+type
+ { this exception is raised when the lua panic function
+ is called. Only in case we use call instead of pcall.
+ it has the lua error string in its message attribute }
+ ELuaException = class(Exception);
+
+ { record represents item of Eventlist of TLuaCore }
+ PEventListItem = ^TEventListItem;
+ TEventListItem = record
+ Event: THookableEvent;
+ Next: PEventListItem;
+ end;
+
+ { record represents a module }
+ TLuaModule = record
+ Name: string;
+ Functions: array of luaL_reg; // modules functions, with trailing nils this time
+ end;
+
+ TLuaPlugin_Status = (psNone, psRunning, psClosed, psErrorOnLoad, psErrorOnCall, psErrorInInit, psErrorOnRun);
+
+ { class represents a loaded plugin }
+ TLuaPlugin = class
+ private
+ iId: integer;
+ Filename: IPath;
+ State: Plua_State; //< all functions of this plugin are called with this Lua state
+ bPaused: boolean; //< If true no lua functions from this state are called
+ ErrorCount: integer; //< counts the errors that occured during function calls of this plugin
+ ShutDown: boolean; //< for self shutdown by plugin. true if plugin wants to be unloaded after execution of current function
+
+ sName: string;
+ sVersion: string;
+ sAuthor: string;
+ sURL: string;
+
+ sStatus: TLuaPlugin_Status;
+ public
+ constructor Create(Filename: IPath; Id: integer);
+
+ property Id: integer read iId;
+ property Name: string read sName;
+ property Version: string read sVersion;
+ property Author: string read sAuthor;
+ property Url: string read sUrl;
+
+ property Status: TLuaPlugin_Status read sStatus;
+ property CountErrors: integer read ErrorCount;
+
+ property LuaState: Plua_State read State;
+
+ procedure Load;
+
+ procedure Register(Name, Version, Author, Url: string);
+ function HasRegistered: boolean;
+
+ procedure PausePlugin(doPause: boolean);
+ property Paused: boolean read bPaused write PausePlugin;
+
+ procedure ShutMeDown;
+
+ { calls the lua function in the global with the given name.
+ the arguments to the function have to be pushed to the stack
+ before calling this function.
+ the arguments and the function will be removed from stack
+ results will not be removed.
+ if result is false there was an error calling the function
+ if ReportErrors is true the errorstring is popped from stack
+ and written to error.log otherwise it is left on stack}
+ function CallFunctionByName(Name: string;
+ const nArgs: integer = 0;
+ const nResults: integer = 0;
+ const ReportErrors: boolean = true): boolean;
+ procedure ClearStack;
+
+ procedure Unload; //< Destroys the Luastate, and frees as much mem as possible, without destroying the class and important information
+
+ destructor Destroy; override;
+ end;
+
+ { class managing the plugins with their LuaStates, the events and modules
+ it also offers the usdx table to the plugins with some basic functionality
+ like self unload or hook getting}
+ TLuaCore = class
+ private
+ EventList: PEventListItem; //< pointer to first registred Event, ordered by name
+ EventHandles: array of string; //< Index is Events handle, value is events name. if length(value) is 0 handle is considered unregistred
+
+ Plugins: array of TLuaPlugin;
+
+ eLoadingFinished: THookableEvent;
+ protected
+ Modules: array of TLuaModule; //< modules that has been registred, has to be proctected because fucntions of this unit need to get access
+
+ function GetModuleIdByName(Name: string): integer; // returns id of given module, or -1 if module is not found
+ public
+ constructor Create;
+ destructor Destroy; override;
+
+ procedure LoadPlugins; //< calls LoadPlugin with Plugindir and LoadingFinished Eventchain
+
+ procedure BrowseDir(Dir: IPath); //< searches for files with extension .usdx in the specified dir and tries to load them with lua
+ procedure LoadPlugin(Filename: IPath); //< tries to load filename with lua and creates the default usdx lua environment for the plugins state
+
+ function GetPluginByName(Name: string): TLuaPlugin;
+ function GetPluginById(Id: integer): TLuaPlugin;
+
+ { this function adds a module loader for your functions
+ name is the name the script needs to write in its require()
+ Functions is an array of lua calling compatible functions
+ without trailing nils! }
+ procedure RegisterModule(Name: string; const Functions: array of luaL_reg);
+
+ function RegisterEvent(Event: THookableEvent): integer; //< adds the event to eventlist and returns its handle
+ procedure UnRegisterEvent(hEvent: integer); //< removes the event from eventlist by handle
+
+ function GetEventbyName(Name: string): THookableEvent; //< tries to find the event with the given name in the list
+ function GetEventbyHandle(hEvent: integer): THookableEvent; //< tries to find the event with the given handle
+
+ procedure UnHookByParent(Parent: integer); //< remove all hooks by given parent id from all events
+
+ procedure PrepareState(L: Plua_State);
+
+ procedure DumpPlugins; //< prints plugin runtime information with Log.LogStatus
+ end;
+
+// some luastyle functions to call from lua scripts
+{ register global, used by plugins to identify
+ register(plugin name, plugin version, [plugin author], [plugin homepage])
+ can only be called once since the global "register" is niled by the function
+ returns true on success. (name does not exist)}
+function TLuaPlugin_Register (L: Plua_State): integer; cdecl;
+
+{ moduleloader for usdx.* modules
+ stored in package.loaders[3]
+ package.loaders[3] (module name)
+ returns a function to load the requested module or a error
+ description(string) when the module is not found }
+function TLuaCore_ModuleLoader (L: Plua_State): integer; cdecl;
+
+{ loads module specified by a cfunction upvalue to
+ usdx.modulename and returns it.
+ loadmodule(module name) }
+function TLuaCore_LoadModule (L: Plua_State): integer; cdecl;
+
+{ custom lua panic function
+ it writes error string to error.log and raises an ELuaException
+ that may be caught }
+function TLua_CustomPanic (L: Plua_State): integer; cdecl;
+
+{ replacement for luas require function
+ can be called with more than one parameter to require
+ some modules at once. e.g.: require('math', 'Usdx.Log')
+ modules are loaded from right to left
+ unlike standard require the module tables are not returned
+ the standard require function in _require is called by
+ this function }
+function TLua_CustomRequire(L: PLua_State): integer; cdecl;
+
+var
+ LuaCore: TLuaCore;
+
+implementation
+
+uses
+ StrUtils,
+ ULog,
+ UFilesystem,
+ ULuaUsdx,
+ UPathUtils,
+ ULuaUtils;
+
+constructor TLuaCore.Create;
+begin
+ inherited;
+
+ // init EventList with nil
+ EventList := nil;
+
+ eLoadingFinished := nil;
+end;
+
+destructor TLuaCore.Destroy;
+var
+ Cur: PEventListItem;
+ Prev: PEventListItem;
+begin
+ SetLength(EventHandles, 0);
+
+ // delete event list
+ Cur := EventList;
+
+ while(Cur <> nil) do
+ begin
+ Prev := Cur;
+ Cur := Prev.Next;
+
+ Dispose(Prev);
+ end;
+
+ inherited;
+end;
+
+{ calls BrowseDir with plugin dir and LoadingFinished eventchain }
+procedure TLuaCore.LoadPlugins;
+begin
+ // we have to create event here, because in create it can
+ // not be registred, because LuaCore is no assigned
+ if (not Assigned(eLoadingFinished)) then
+ eLoadingFinished := THookableEvent.Create('Usdx.LoadingFinished');
+
+ BrowseDir(PluginPath);
+ eLoadingFinished.CallHookChain(false);
+end;
+
+{ searches for files with extension .usdx in the specified
+ dir and tries to load them with lua }
+procedure TLuaCore.BrowseDir(Dir: IPath);
+ var
+ Iter: IFileIterator;
+ FileInfo: TFileInfo;
+ FileName: IPath;
+ Ext: IPath;
+begin
+ Ext := Path('.usdx');
+
+ // search for all files and directories
+ Iter := FileSystem.FileFind(Dir.Append('*'), faAnyFile);
+ while (Iter.HasNext) do
+ begin
+ FileInfo := Iter.Next;
+ FileName := FileInfo.Name;
+ if ((FileInfo.Attr and faDirectory) <> 0) then
+ begin
+ if (not FileName.Equals('.')) and (not FileName.Equals('..')) then
+ BrowseDir(Dir.Append(FileName));
+ end
+ else
+ begin
+ if (Ext.Equals(FileName.GetExtension(), true)) then
+ begin
+ LoadPlugin(Dir.Append(FileName));
+ end;
+ end;
+ end;
+end;
+
+{ tries to load filename with lua and creates the default
+ usdx lua environment for the plugins state }
+procedure TLuaCore.LoadPlugin(Filename: IPath);
+ var
+ Len: integer;
+begin
+ Len := Length(Plugins);
+ SetLength(Plugins, Len + 1);
+ Plugins[Len] := TLuaPlugin.Create(Filename, Len);
+ Plugins[Len].Load;
+end;
+
+{ returns Plugin on success nil on failure }
+function TLuaCore.GetPluginByName(Name: string): TLuaPlugin;
+ var
+ I: integer;
+begin
+ Result := nil;
+ Name := lowercase(Name);
+
+ for I := 0 to High(Plugins) do
+ if (lowercase(Plugins[I].Name) = Name) then
+ begin
+ Result := GetPluginById(I);
+ Exit;
+ end;
+end;
+
+{ returns Plugin on success nil on failure }
+function TLuaCore.GetPluginById(Id: integer): TLuaPlugin;
+begin
+ if (Id >= 0) and (Id <= High(Plugins)) then
+ Result := Plugins[Id]
+ else
+ Result := nil;
+end;
+
+{ this function adds a module loader for your functions
+ name is the name the script needs to write in its require()
+ Functions is an array of lua calling compatible functions
+ without trailing nils! }
+procedure TLuaCore.RegisterModule(Name: string; const Functions: array of luaL_reg);
+ var
+ Len: integer;
+ FuncLen: integer;
+ I: integer;
+begin
+ Len := Length(Modules);
+ SetLength(Modules, Len + 1);
+ Modules[Len].Name := Name;
+
+ FuncLen := Length(Functions);
+ SetLength(Modules[Len].Functions, FuncLen + 1);
+
+ for I := 0 to FuncLen-1 do
+ Modules[Len].Functions[I] := Functions[I];
+
+ Modules[Len].Functions[FuncLen].name := nil;
+ Modules[Len].Functions[FuncLen].func := nil;
+end;
+
+{ adds the event to eventlist and returns its handle
+ called by THookableEvent on creation }
+function TLuaCore.RegisterEvent(Event: THookableEvent): integer;
+var
+ Cur, Prev, Item: PEventListItem;
+begin
+ if (Event <> nil) and (Length(Event.Name) > 0) then
+ begin
+ Result := Length(EventHandles);
+ SetLength(EventHandles, Result + 1); // get Handle and copy it to result
+
+ EventHandles[Result] := Event.Name;
+
+ // create eventlist item
+ New(Item);
+ Item.Event := Event;
+
+ // search for a place for this event in alphabetical order
+ Prev := nil;
+ Cur := EventList;
+
+ while (Cur <> nil) and (CompareStr(Cur.Event.Name, EventHandles[Result]) < 0) do
+ begin
+ Prev := Cur;
+ Cur := Prev.Next;
+ end;
+
+ // found the place => add new item
+ if (Prev <> nil) then
+ Prev.Next := Item
+ else // first item
+ EventList := Item;
+
+ Item.Next := Cur;
+ end
+ else
+ Result := -1;
+end;
+
+{ removes the event from eventlist by handle }
+procedure TLuaCore.UnRegisterEvent(hEvent: integer);
+ var
+ Cur, Prev: PEventListItem;
+begin
+ if (hEvent >= 0) and (hEvent <= High(EventHandles)) and (Length(EventHandles[hEvent]) > 0) then
+ begin // hEvent in bounds and not already deleted
+ // delete from eventlist
+ Prev := nil;
+ Cur := EventList;
+
+ while (Cur <> nil) and (CompareStr(Cur.Event.Name, EventHandles[hEvent]) < 0) do
+ begin
+ Prev := Cur;
+ Cur := Prev.Next;
+ end;
+
+ if (Cur <> nil) and (Cur.Event.Name = EventHandles[hEvent]) then
+ begin // delete if found
+ Prev.Next := Cur.Next; // remove from list
+ Dispose(Cur); // free memory
+ end;
+
+ // delete from handle array
+ EventHandles[hEvent] := '';
+ end;
+end;
+
+{ tries to find the event with the given name in the list
+ to-do : use binary search algorithm instead of linear search here
+ check whether this is possible (events are saved in a pointer list) }
+function TLuaCore.GetEventbyName(Name: string): THookableEvent;
+ var
+ Cur: PEventListItem;
+begin
+ Result := nil;
+
+ if (Length(Name) > 0) then
+ begin
+ // search in eventlist
+ Cur := EventList;
+
+ while (Cur <> nil) and (CompareStr(Cur.Event.Name, Name) < 0) do
+ begin
+ Cur := Cur.Next;
+ end;
+
+ if (Cur <> nil) and (Cur.Event.Name = Name) then
+ begin // we found what we want to find
+ Result := Cur.Event;
+ end;
+ end;
+end;
+
+{ tries to find the event with the given handle }
+function TLuaCore.GetEventbyHandle(hEvent: integer): THookableEvent;
+begin
+ if (hEvent >= 0) and (hEvent <= High(EventHandles)) and (Length(EventHandles[hEvent]) > 0) then
+ begin // hEvent in bounds and not already deleted
+ Result := GetEventByName(EventHandles[hEvent]);
+ end
+ else
+ Result := nil;
+end;
+
+{ remove all hooks by given parent id from all events }
+procedure TLuaCore.UnHookByParent(Parent: integer);
+ var
+ Cur: PEventListItem;
+begin
+ if (Parent >= 0) and (Parent <= High(Plugins)) then
+ begin
+ // go through event list
+ Cur := EventList;
+
+ while (Cur <> nil) do
+ begin
+ Cur.Event.UnHookByParent(Parent);
+ Cur := Cur.Next;
+ end;
+ end;
+end;
+
+{ prepares the given already opened Lua state with the
+ basic usdx environment, e.g.: base and package Modules,
+ usdx moduleloader and usdx table }
+procedure TLuaCore.PrepareState(L: Plua_State);
+begin
+ // load basic lib functionality
+ lual_openLibs(L);
+ lua_pop(L,1);
+
+ // get package.searchers (former package.loaders) table
+ lua_getGlobal (L, PChar('package'));
+{$IF LUA_VERSION_NUM >= 502}
+ lua_getfield(L,-1,PChar('searchers'));
+{$ELSE}
+ lua_getfield(L,-1,PChar('loaders'));
+{$IFEND}
+
+ {**** Move C-Library and all-in-one module loader backwards,
+ slot 3 is free now }
+ // get package.loaders[4] function
+ lua_pushinteger(L, 5); // push new index
+ lua_pushinteger(L, 4); // push old index
+ lua_gettable (L, -3);
+
+ // and move it to package.loaders[5]
+ lua_settable (L, -3);
+
+ // get package.loaders[3] function
+ lua_pushinteger(L, 4); // push new index
+ lua_pushinteger(L, 3); // push old index
+ lua_gettable (L, -3);
+
+ // and move it to package.loaders[4]
+ lua_settable (L, -3);
+
+ {**** now we add the core module to package.loaders[3] }
+ lua_pushinteger(L, 3); // push new loaders index
+ lua_pushcfunction(L, TLuaCore_ModuleLoader);
+
+ // and move it to package.loaders[3]
+ lua_settable (L, -3);
+
+ // pop both package and package.loaders tables from stack
+ lua_pop(L, 2);
+
+ {**** replace the standard require with our custom require function }
+ // first move standard require function to _require
+ lua_getglobal(L, PChar('require'));
+
+ lua_setglobal(L, PChar('_require'));
+
+ // then save custom require function to require
+ lua_pushcfunction(L, TLua_CustomRequire);
+
+ lua_setglobal(L, PChar('require'));
+
+ {**** now we create the usdx table }
+ // at first functions from ULuaUsdx
+ luaL_register(L, 'Usdx', @ULuaUsdx_Lib_f[0]);
+end;
+
+{ returns id of given module, or -1 if module is not found }
+function TLuaCore.GetModuleIdByName(Name: string): integer;
+ var
+ I: integer;
+begin
+ Result := -1;
+
+ for I := 0 to High(Modules) do
+ if (Modules[I].Name = Name) then
+ begin
+ Result := I;
+ Exit;
+ end;
+end;
+
+{ moduleloader for usdx.* modules
+ stored in package.loaders[3]
+ package.loaders[3] (module name)
+ returns a function to load the requested module or an error
+ description(string) when the module is not found }
+function TLuaCore_ModuleLoader (L: Plua_State): integer; cdecl;
+ var
+ Name: string;
+ ID: integer;
+begin
+ Result := 1; // we will return one value in any case (or never return in case of an error)
+
+ if (lua_gettop(L) >= 1) then
+ begin
+ // pop all arguments but the first
+ if (lua_gettop(L) > 1) then
+ lua_pop(L, lua_gettop(L)-1);
+
+
+ if (lua_IsString(L, 1)) then
+ begin // we got the name => go get it
+ Name := lua_toString(L, 1);
+
+ // we need at least 6 letters
+ // and first 5 letters have to be usdx.
+ if (Length(Name) > 5) and (lowercase(copy(Name, 1, 5))='usdx.') then
+ begin
+ ID := LuaCore.GetModuleIdByName(copy(Name, 6, Length(Name) - 5));
+ if (ID >= 0) then
+ begin // found the module -> return loader function
+ lua_pushinteger(L, Id);
+ lua_pushcclosure(L, TLuaCore_LoadModule, 1);
+ // the function is the result, so we leave it on stack
+ end
+ else
+ lua_pushString(L, PChar('usdx module "' + Name + '" couldn''t be found'));
+ end
+ else
+ lua_pushString(L, PChar('module doesn''t have "Usdx." prefix'));
+
+ end
+ else
+ luaL_argerror(L, 1, PChar('string expected'));
+ end
+ else
+ luaL_error(L, PChar('no modulename specified in usdx moduleloader'));
+end;
+
+{ loads module specified by a cfunction upvalue to
+ usdx.modulename and returns it.
+ loadmodule(module name) }
+function TLuaCore_LoadModule (L: Plua_State): integer; cdecl;
+ var
+ Id: integer;
+begin
+ if (not lua_isnoneornil(L, lua_upvalueindex(1))) then
+ begin
+ Id := lua_ToInteger(L, lua_upvalueindex(1));
+{$IF LUA_VERSION_NUM >= 502}
+ // set module table as a field of the global Usdx
+ lua_getglobal(L,Pchar('Usdx'));
+ luaL_register(L, PChar(LuaCore.Modules[Id].Name), @LuaCore.Modules[Id].Functions[0]);
+ lua_setfield(L, -2, PChar(LuaCore.Modules[Id].Name));
+{$ELSE}
+ luaL_register(L, PChar('Usdx.' + LuaCore.Modules[Id].Name), @LuaCore.Modules[Id].Functions[0]);
+
+ // set the modules table as global "modulename"
+ // so it can be accessed either by Usdx.modulename.x() or
+ // by modulename.x()
+ lua_setglobal(L, PChar(LuaCore.Modules[Id].Name));
+
+ // no we net to push the table again to return it
+ lua_getglobal(L, PChar(LuaCore.Modules[Id].Name));
+{$IFEND}
+ Result := 1; // return table
+ end
+ else
+ luaL_error(L, PChar('no upvalue found in LuaCore_LoadModule'));
+end;
+
+{ prints plugin runtime information with Log.LogStatus }
+procedure TLuaCore.DumpPlugins;
+ function PluginStatusToString(Status: TLuaPlugin_Status): string;
+ begin
+ case Status of
+ psNone: Result := 'not loaded';
+ psRunning: Result := 'running';
+ psClosed: Result := 'closed';
+ psErrorOnLoad: Result := 'error during load';
+ psErrorOnCall: Result := 'error during call';
+ psErrorInInit: Result := 'error in plugin_init()';
+ psErrorOnRun: Result := 'error on function call';
+ else Result := 'unknown';
+ end;
+ end;
+
+var
+ I: integer;
+begin
+ // print table header
+ Log.LogStatus(Format('%3s %-30s %-8s %-10s %-7s %-6s', [
+ '#', 'Name', 'Version', 'Status', 'Paused', '#Errors'
+ ]), 'LuaCore Plugins');
+
+ for I := 0 to High(Plugins) do
+ Log.LogStatus(Format('%3d %-30s %-8s %-10s %-7s %-6d', [
+ Plugins[I].Id, Plugins[I].Name, Plugins[I].Version,
+ PluginStatusToString(Plugins[I].Status),
+ BoolToStr(Plugins[I].Paused, true),
+ Plugins[I].CountErrors
+ ]), 'LuaCore Plugins');
+ if (High(Plugins) < 0) then
+ Log.LogError(' no plugins loaded ', 'LuaCore Plugins');
+end;
+
+// Implementation of TLuaPlugin
+//--------
+constructor TLuaPlugin.Create(Filename: IPath; Id: integer);
+begin
+ inherited Create;
+ Self.iId := Id;
+ Self.Filename := Filename;
+
+ // set some default attributes
+ Self.bPaused := false;
+ Self.ErrorCount := 0;
+ Self.sName := 'not registred';
+ Self.sStatus := psNone;
+ Self.ShutDown := false;
+
+ State := nil; //< to prevent calls to unopened state
+end;
+
+destructor TLuaPlugin.Destroy;
+begin
+ Unload;
+ inherited;
+end;
+
+{ does the main loading part
+ can not be called by create, because Plugins[Id] isn't defined there }
+procedure TLuaPlugin.Load;
+begin
+ // create Lua state for this plugin
+ State := luaL_newstate;
+
+ // set our custom panic function if s/t went wrong along the init
+ // we don't expect
+ lua_atPanic(State, TLua_CustomPanic);
+
+ if (LuaL_LoadFile(State, PChar(Filename.ToNative)) = 0) then
+ begin // file loaded successful
+ { note: we run the file here, but the environment isn't
+ set up now. it just causes the functions to
+ register in globals and runs the code in the file
+ body. At least there should be no code, it could
+ neither use functions from baselibs nor load libs
+ with require, this code would be useless. }
+ if (lua_pcall(State, 0, 0, 0) = 0) then
+ begin // file called successful
+
+ // let the core prepare our state
+ LuaCore.PrepareState(State);
+
+ // set register function
+ lua_checkstack(State, 2);
+ lua_pushinteger(State, Id);
+ lua_pushcclosure(State, TLuaPlugin_Register, 1);
+ lua_setglobal(State, PChar('register'));
+
+ // write plugin id to registry
+ lua_pushinteger(State, iId);
+ lua_setfield (State, LUA_REGISTRYINDEX, '_USDX_STATE_ID');
+ lua_pop(State, Lua_GetTop(State));
+
+ // now run the plugin_init function
+ // plugin_init() if false or nothing is returned plugin init is aborted
+ if (CallFunctionByName('plugin_init', 0, 1)) then
+ begin
+ if (HasRegistered) and (sStatus = psNone) and (lua_toBoolean(State, 1)) then
+ begin
+ sStatus := psRunning;
+ ClearStack;
+ end
+ else
+ Unload;
+ end
+ else
+ begin
+ sStatus := psErrorInInit;
+ Log.LogError('error in plugin_init: ' + Self.Filename.ToNative, 'lua');
+ Unload;
+ end;
+ end
+ else
+ begin
+ sStatus := psErrorOnLoad;
+ Log.LogError(String(lua_toString(State, 1)), 'lua');
+ Log.LogError('unable to call file: ' + Self.Filename.ToNative, 'lua');
+ Unload;
+ end;
+
+ end
+ else
+ begin
+ sStatus := psErrorOnLoad;
+ Log.LogError(String(lua_toString(State, 1)), 'lua');
+ Log.LogError('unable to load file: ' + Self.Filename.ToNative, 'lua');
+ Unload;
+ end;
+end;
+
+procedure TLuaPlugin.Register(Name, Version, Author, Url: string);
+begin
+ sName := Name;
+ sVersion := Version;
+ sAuthor := Author;
+ sURL := Url;
+end;
+
+{ returns true if plugin has called register }
+function TLuaPlugin.HasRegistered: boolean;
+begin
+ Result := (Self.sName <> 'not registred');
+end;
+
+procedure TLuaPlugin.PausePlugin(doPause: boolean);
+begin
+ bPaused := doPause;
+end;
+
+{ unload plugin after execution of the current function }
+procedure TLuaPlugin.ShutMeDown;
+begin
+ ShutDown := true;
+end;
+
+{ calls the lua function in the global with the given name.
+ the arguments to the function have to be pushed to the stack
+ before calling this function.
+ the arguments and the function will be removed from stack
+ results will not be removed.
+ if result is false there was an error calling the function,
+ if ReportErrors is true the errorstring is popped from stack
+ and written to error.log otherwise it is left on stack}
+function TLuaPlugin.CallFunctionByName(Name: string;
+ const nArgs: integer;
+ const nResults: integer;
+ const ReportErrors: boolean): boolean;
+begin
+ Result := false;
+ if (State <> nil) then
+ begin
+ if (not bPaused) then
+ begin
+ // we need at least one stack slot free
+ lua_checkstack(State, 1);
+
+ lua_getglobal(State, PChar(Name));
+
+ if (lua_isfunction(State, -1)) then
+ begin // we got a function
+ // move function in front of the arguments (if any)
+ if (nArgs > 0) then
+ lua_insert(State, -(nArgs + 1));
+
+ // call it!
+ if (lua_pcall(State, nArgs, nResults, 0) = 0) then
+ Result := true // called without errors
+ else // increase error counter
+ Inc (ErrorCount);
+ end
+ else
+ begin // we have to pop the args and the field we pushed from stack
+ lua_pop(State, nArgs + 1);
+ // leave an errormessage on stack
+ lua_pushstring(State, Pchar('could not find function named ' + Name));
+ end;
+ end
+ else
+ begin // we have to pop the args from stack
+ lua_pop(State, nArgs);
+ // leave an errormessage on stack
+ lua_pushstring(State, PChar('plugin paused'));
+ end;
+
+ if (not Result) and (ReportErrors) then
+ Log.LogError(lua_toString(State, -1), 'lua/' + sName);
+
+ if ShutDown then
+ begin // plugin indicates self shutdown
+ ShutDown := false;
+ Unload;
+ Result := false;
+ end
+ end
+ else
+ begin
+ Log.LogError('trying to call function of closed or not opened lua state', IfThen(HasRegistered, Name, Filename.ToUTF8));
+ end;
+end;
+
+{ removes all values from stack }
+procedure TLuaPlugin.ClearStack;
+begin
+ if (State <> nil) and (lua_gettop(State) > 0) then
+ lua_pop(State, lua_gettop(State));
+end;
+
+{ destroys the lua state, and frees as much mem as possible,
+ without destroying the class and important information }
+procedure TLuaPlugin.Unload;
+begin
+ if (State <> nil) then
+ begin
+ if (Status in [psRunning, psErrorOnRun]) then
+ CallFunctionByName('plugin_unload');
+
+ ClearStack;
+ lua_close(State);
+ State := nil; // don't forget to nil it ;)
+
+ LuaCore.UnHookByParent(iId);
+
+ if (sStatus = psRunning) then
+ sStatus := psClosed;
+ end;
+end;
+
+function TLuaPlugin_Register (L: Plua_State): integer; cdecl;
+ var
+ Id: integer;
+ P: TLuaPlugin;
+ Name, Version, Author, Url: string;
+begin
+ if (lua_gettop(L) >= 2) then
+ begin // we got at least name and version
+ if (not lua_isNumber(L, lua_upvalueindex(1))) then
+ luaL_Error(L, PChar('upvalue missing'));
+
+ if (not lua_isString(L, 1)) then
+ luaL_ArgError(L, 1, 'string expected');
+
+ if (not lua_isString(L, 2)) then
+ luaL_ArgError(L, 1, 'string expected');
+
+ Id := lua_ToInteger(L, lua_upvalueindex(1));
+
+ // get version and name
+ Name := lua_tostring(L, 1);
+ Version := lua_tostring(L, 2);
+
+ // get optional parameters
+ if (lua_isString(L, 3)) then // author
+ Author := lua_toString(L, 3)
+ else
+ begin
+ Author := 'unknown';
+ end;
+
+ // homepage
+ if (lua_isString(L, 4)) then
+ Url := lua_toString(L, 4)
+ else
+ begin
+ Url := '';
+ end;
+
+ // clear stack
+ if (lua_gettop(L) > 0) then
+ lua_pop(L, lua_gettop(L));
+
+ // call register
+ P := LuaCore.GetPluginById(Id);
+ if (P <> nil) then
+ P.Register(Name, Version, Author, Url)
+ else
+ luaL_error(L, PChar('wrong id in upstream'));
+
+ // remove function from global register
+ lua_pushnil(L);
+ lua_setglobal(L, PChar('register'));
+
+ // return true
+ Result := 1;
+ lua_pushboolean(L, true);
+ end
+ else
+ luaL_error(L, PChar('not enough arguments, at least 2 expected. in TLuaPlugin_Register'));
+end;
+
+{ custom lua panic function
+ it writes error string to error.log and raises an ELuaException
+ that may be caught }
+function TLua_CustomPanic (L: Plua_State): integer; cdecl;
+ var
+ Msg: string;
+begin
+ if (lua_isString(L, -1)) then
+ Msg := lua_toString(L, -1)
+ else
+ Msg := 'undefined lua panic';
+
+ Log.LogError(Msg, 'lua');
+
+ raise ELuaException.Create(Msg);;
+
+ Result := 0;
+end;
+
+{ replacement for luas require function
+ can be called with more than one parameter to require
+ some modules at once. e.g.: require('math', 'Usdx.Log')
+ modules are loaded from right to left
+ unlike standard require the module tables are not returned
+ the standard require function in _require is called by
+ this function }
+function TLua_CustomRequire(L: PLua_State): integer; cdecl;
+begin
+ // no results
+ Result := 0;
+
+ // move through parameters
+ while (lua_getTop(L) >= 1) do
+ begin
+ lua_checkstack(L,1);
+ // get luas require function
+ lua_getglobal(L, PChar('_require'));
+
+ // move it under the top param
+ lua_insert(L, -2);
+
+ // call it with next param (function + param are poped from stack)
+ lua_call(L, 1, 0);
+ end;
+end;
+
+end.
diff --git a/src/lua/ULuaParty.pas b/src/lua/ULuaParty.pas
index 69096e97..5ee35513 100644
--- a/src/lua/ULuaParty.pas
+++ b/src/lua/ULuaParty.pas
@@ -1,391 +1,391 @@
-{* 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 ULuaParty;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses ULua;
-
-{ lua c functions from Party table. Enables creating of party modes w/ lua scripts }
-
-{ Party.Register - register party mode at party manager
- arguments: info: table
- Name: String; //< Name used as identifier (language strings, etc.). Has to be set.
- CanNonParty: Boolean //< mode is playable when not in party mode. defaulted to false if not set
- CanParty: Boolean //< mode is playable in party mode. defaulted to false if not set
- PlayerCount: Table //< playable with one, two, three etc. players per team. defaulted to no restrictions if not set. (use table constructor e.g. {1, 2, 3) means playable w/ 1, 2 or three players)
- TeamCount: Table //< playable with one, two, three etc. different teams. defaulted to no restrictions if not set. (use table constructor e.g. {1, 2, 3) means playable w/ 1, 2 or three players)
-
- BeforeSongSelect: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
- AfterSongSelect: String //< name of global that will be called after song is selected (if nil, not callable or returns true, default action will be executed)
-
- BeforeSing: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
- OnSing: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
- AfterSing: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)}
-function ULuaParty_Register(L: Plua_State): Integer; cdecl;
-
-{ Party.GameFinished - returns true if no party game is running or all rounds
- of current game were played }
-function ULuaParty_GameFinished(L: Plua_State): Integer; cdecl;
-
-(* Party.SetRoundRanking - sets ranking of current party round,
- arguments: Ranking: table
- ranking of team i is the value (integer from 1 to number of teams) of the
- table with index [i: number].
- you may call this function in the following way:
- Party.SetRoundRanking({3, 1, 2});
- this means: team 1 is ranked third, team 2 is ranked first and team 3 is
- ranked second.
- if no party game is started or party game is finished
- it will raise an error *)
-function ULuaParty_SetRoundRanking(L: Plua_State): Integer; cdecl;
-
-{ Party.GetTeams - returns a table with all information and structure as
- in the TPartyGame.Teams array }
-function ULuaParty_GetTeams(L: Plua_State): Integer; cdecl;
-
-{ Party.SetTeams - changes all fields from TPartyGame.Teams that have been
- set in the table given as first argument}
-function ULuaParty_SetTeams(L: Plua_State): Integer; cdecl;
-
-const
- ULuaParty_Lib_f: array [0..4] of lual_reg = (
- (name:'Register'; func:ULuaParty_Register),
- (name:'GameFinished'; func:ULuaParty_GameFinished),
- (name:'SetRoundRanking'; func:ULuaParty_SetRoundRanking),
- (name:'GetTeams'; func:ULuaParty_GetTeams),
- (name:'SetTeams'; func:ULuaParty_SetTeams)
- );
-
-implementation
-uses ULuaCore, ULuaUtils, UParty, SysUtils;
-
-
-{ Party.Register - register party mode at party manager
- arguments: info: table
- Name: String; //< Name used as identifier (language strings, etc.). Has to be set.
- CanNonParty: Boolean //< mode is playable when not in party mode. defaulted to false if not set
- CanParty: Boolean //< mode is playable in party mode. defaulted to false if not set
- PlayerCount: Table //< playable with one, two, three etc. players per team. defaulted to no restrictions if not set. (use table constructor e.g. {1, 2, 3) means playable w/ 1, 2 or three players)
- TeamCount: Table //< playable with one, two, three etc. different teams. defaulted to no restrictions if not set. (use table constructor e.g. {1, 2, 3) means playable w/ 1, 2 or three players)
-
- BeforeSongSelect: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
- AfterSongSelect: String //< name of global that will be called after song is selected (if nil, not callable or returns true, default action will be executed)
-
- BeforeSing: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
- OnSing: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
- AfterSing: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)}
-function ULuaParty_Register(L: Plua_State): Integer; cdecl;
- var
- Info: TParty_ModeInfo;
- Key: String;
- P: TLuaPlugin;
-begin
- Result := 0;
-
- // check for table on stack
- luaL_checkType(L, 1, LUA_TTABLE);
-
- // get parent id
- P := Lua_GetOwner(L);
-
-
- // set mode info to default
- Party.DefaultModeInfo(Info);
-
-
- // set parent in info rec and pop it from stack
- Info.Parent := P.Id;
-
- // go through table elements
- lua_pushNil(L);
- while (lua_Next(L, 1) <> 0) do
- begin
- Key := lowercase(lua_ToString(L, -2));
-
- if (Key = 'name') and lua_isString(L, -1) then
- Info.Name := lua_toString(L, -1)
- else if (Key = 'cannonparty') and lua_isBoolean(L, -1) then
- Info.CanNonParty := lua_toBoolean(L, -1)
- else if (Key = 'canparty') and lua_isBoolean(L, -1) then
- Info.CanParty := lua_toBoolean(L, -1)
- else if (Key = 'playercount') and lua_isTable(L, -1) then
- Info.PlayerCount := lua_toBinInt(L, -1)
- else if (Key = 'teamcount') and lua_isTable(L, -1) then
- Info.TeamCount := lua_toBinInt(L, -1)
- else if (Key = 'beforesongselect') and lua_isString(L, -1) then
- Info.Functions.BeforeSongSelect := lua_toString(L, -1)
- else if (Key = 'aftersongselect') and lua_isString(L, -1) then
- Info.Functions.AfterSongSelect := lua_toString(L, -1)
- else if (Key = 'beforesing') and lua_isString(L, -1) then
- Info.Functions.BeforeSing := lua_toString(L, -1)
- else if (Key = 'onsing') and lua_isString(L, -1) then
- Info.Functions.OnSing := lua_toString(L, -1)
- else if (Key = 'aftersing') and lua_isString(L, -1) then
- Info.Functions.AfterSing := lua_toString(L, -1);
-
- // pop value from stack so key is on top
- lua_pop(L, 1);
- end;
-
- // clear stack from table
- lua_pop(L, lua_gettop(L));
-
- if not Party.RegisterMode(Info) then
- luaL_error(L, PChar('can''t register party mode at party manager in Party.Register. Is Info.Name defined or is there another mode with this name?'));
-end;
-
-{ Party.GameFinished - returns true if no party game is running or all rounds
- of current game were played }
-function ULuaParty_GameFinished(L: Plua_State): Integer; cdecl;
-begin
- // clear stack
- lua_pop(L, lua_gettop(L));
-
- // push result
- lua_pushBoolean(L, Party.GameFinished);
-
- //we return one value
- Result := 1;
-end;
-
-{ Party.SetRoundRanking - sets ranking of current party round,
- if no party game is started or party game is finished
- it will raise an error }
-function ULuaParty_SetRoundRanking(L: Plua_State): Integer; cdecl;
-var
- R: AParty_TeamRanking;
- I: Integer;
- Rank: Integer;
-begin
- Result := 0;
-
- luaL_checktype(L, 1, LUA_TTABLE);
-
- lua_checkstack(L, 1);
-
- SetLength(R, Length(Party.Teams));
-
- for I := 0 to High(R) do
- begin
- lua_pushInteger(L, (I+1));
- lua_gettable(L, 1);
-
- R[I].Rank := Length(R);
- R[I].Team := I;
- if (lua_isnumber(L, -1)) then
- begin
- Rank := lua_toInteger(L, -1);
- if (Rank >= 1) and (Rank <= Length(R)) then
- R[I].Rank := Rank
- end;
-
- lua_pop(L, 1);
-
- end;
-
- // pop table
- lua_pop(L, 1);
-
- if (not Party.SetRanking(R)) then
- luaL_error(L, PChar('cann''t set party round ranking. Is party started and not finished yet?'));
-end;
-
-{ Party.GetTeams - returns a table with all information and structure as
- in the TPartyGame.Teams array }
-function ULuaParty_GetTeams(L: Plua_State): Integer; cdecl;
- var
- Team: Integer;
- Player: Integer;
-begin
- // clear stack
- lua_pop(L, lua_gettop(L));
-
- // ensure we have enough stack slots left
- lua_checkstack(L, 7);
-
- // create the table we want to return
- lua_createtable(L, Length(Party.Teams), 0);
-
- // add the teams
- for Team := 0 to High(Party.Teams) do
- begin
- // push key for current teams value. lua array beggins at 1
- lua_pushInteger(L, Team + 1);
-
- // push table containing team info and players table
- lua_createtable(L, 0, 5);
-
- // team name
- lua_pushString(L, PChar(Party.Teams[Team].Name));
- lua_setField(L, -2, 'Name');
-
- // team score
- lua_pushInteger(L, Party.Teams[Team].Score);
- lua_setField(L, -2, 'Score');
-
- // team jokers left
- lua_pushInteger(L, Party.Teams[Team].JokersLeft);
- lua_setField(L, -2, 'JokersLeft');
-
- // team nextPlayer
- lua_pushInteger(L, Party.Teams[Team].NextPlayer);
- lua_setField(L, -2, 'NextPlayer');
-
- // team players table
- lua_createtable(L, Length(Party.Teams[Team].Players), 0);
-
- //add players
- for Player := 0 to High(Party.Teams[Team].Players) do
- begin
- // push key for current players value. lua array beggins at 1
- lua_pushInteger(L, Player + 1);
-
- // push table containing player info
- lua_createTable(L, 0, 2);
-
- // player name
- lua_PushString(L, PChar(Party.Teams[Team].Players[Player].Name));
- lua_SetField(L, -2, 'Name');
-
- // players times played
- lua_PushInteger(L, Party.Teams[Team].Players[Player].TimesPlayed);
- lua_SetField(L, -2, 'TimesPlayed');
-
- // add value - key - pair to teams player table
- lua_setTable(L, -3);
- end;
-
- lua_setField(L, -2, 'Players');
-
- // add value - key - pair to returned table
- lua_setTable(L, -3);
- end;
-
- // we return 1 value (the first table)
- Result := 1;
-end;
-
-{ Party.SetTeams - changes all fields from TPartyGame.Teams that have been
- set in the table given as first argument}
-function ULuaParty_SetTeams(L: Plua_State): Integer; cdecl;
-
- procedure Do_Player(Team, Player: Integer);
- var
- Key: String;
- begin
- if (Player >= 0) and (Player <= High(Party.Teams[Team].Players)) then
- begin
- // go through table elements
- lua_pushNil(L);
- while (lua_Next(L, -2) <> 0) do
- begin
- Key := lowercase(lua_ToString(L, -2));
-
- if (Key = 'name') and lua_isString(L, -1) then
- Party.Teams[Team].Players[Player].Name := lua_toString(L, -1)
- else if (Key = 'timesplayed') and lua_isNumber(L, -1) then
- Party.Teams[Team].Players[Player].TimesPlayed := lua_toInteger(L, -1);
-
- // pop value from stack so key is on top
- lua_pop(L, 1);
- end;
- end;
- end;
-
- procedure Do_Players(Team: Integer);
- begin
- // go through table elements
- lua_pushNil(L);
- while (lua_Next(L, -2) <> 0) do
- begin
- // check if key is a number and value is a table
- if (lua_isNumber(L, -2)) and (lua_isTable(L, -1)) then
- Do_Player(Team, lua_toInteger(L, -2));
-
- // pop value from stack so key is on top
- lua_pop(L, 1);
- end;
- end;
-
- procedure Do_Team(Team: Integer);
- var
- Key: String;
- begin
- if (Team >= 0) and (Team <= High(Party.Teams)) then
- begin
- // go through table elements
- lua_pushNil(L);
- while (lua_Next(L, -2) <> 0) do
- begin
- Key := lowercase(lua_ToString(L, -2));
-
- if (Key = 'name') and lua_isString(L, -1) then
- Party.Teams[Team].Name := lua_toString(L, -1)
- else if (Key = 'score') and lua_isNumber(L, -1) then
- Party.Teams[Team].Score := lua_toInteger(L, -1)
- else if (Key = 'jokersleft') and lua_isNumber(L, -1) then
- Party.Teams[Team].JokersLeft := lua_toInteger(L, -1)
- else if (Key = 'currentplayer') and lua_isNumber(L, -1) then
- Party.Teams[Team].NextPlayer := lua_toInteger(L, -1)
- else if (Key = 'players') and lua_isTable(L, -1) then
- Do_Players(Team);
-
- // pop value from stack so key is on top
- lua_pop(L, 1);
- end;
- end;
- end;
-begin
- Result := 0;
-
- // check for table on stack
- luaL_checkType(L, 1, LUA_TTABLE);
-
-
- // go through table elements
- lua_pushNil(L);
- while (lua_Next(L, 1) <> 0) do
- begin
- // check if key is a number and value is a table
- if (lua_isNumber(L, -2)) and (lua_isTable(L, -1)) then
- Do_Team(lua_toInteger(L, -2));
-
- // pop value from stack so key is on top
- lua_pop(L, 1);
- end;
-
- // clear stack from table
- lua_pop(L, lua_gettop(L));
-end;
-
+{* 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 ULuaParty;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses ULua;
+
+{ lua c functions from Party table. Enables creating of party modes w/ lua scripts }
+
+{ Party.Register - register party mode at party manager
+ arguments: info: table
+ Name: String; //< Name used as identifier (language strings, etc.). Has to be set.
+ CanNonParty: Boolean //< mode is playable when not in party mode. defaulted to false if not set
+ CanParty: Boolean //< mode is playable in party mode. defaulted to false if not set
+ PlayerCount: Table //< playable with one, two, three etc. players per team. defaulted to no restrictions if not set. (use table constructor e.g. {1, 2, 3) means playable w/ 1, 2 or three players)
+ TeamCount: Table //< playable with one, two, three etc. different teams. defaulted to no restrictions if not set. (use table constructor e.g. {1, 2, 3) means playable w/ 1, 2 or three players)
+
+ BeforeSongSelect: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
+ AfterSongSelect: String //< name of global that will be called after song is selected (if nil, not callable or returns true, default action will be executed)
+
+ BeforeSing: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
+ OnSing: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
+ AfterSing: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)}
+function ULuaParty_Register(L: Plua_State): Integer; cdecl;
+
+{ Party.GameFinished - returns true if no party game is running or all rounds
+ of current game were played }
+function ULuaParty_GameFinished(L: Plua_State): Integer; cdecl;
+
+(* Party.SetRoundRanking - sets ranking of current party round,
+ arguments: Ranking: table
+ ranking of team i is the value (integer from 1 to number of teams) of the
+ table with index [i: number].
+ you may call this function in the following way:
+ Party.SetRoundRanking({3, 1, 2});
+ this means: team 1 is ranked third, team 2 is ranked first and team 3 is
+ ranked second.
+ if no party game is started or party game is finished
+ it will raise an error *)
+function ULuaParty_SetRoundRanking(L: Plua_State): Integer; cdecl;
+
+{ Party.GetTeams - returns a table with all information and structure as
+ in the TPartyGame.Teams array }
+function ULuaParty_GetTeams(L: Plua_State): Integer; cdecl;
+
+{ Party.SetTeams - changes all fields from TPartyGame.Teams that have been
+ set in the table given as first argument}
+function ULuaParty_SetTeams(L: Plua_State): Integer; cdecl;
+
+const
+ ULuaParty_Lib_f: array [0..4] of lual_reg = (
+ (name:'Register'; func:ULuaParty_Register),
+ (name:'GameFinished'; func:ULuaParty_GameFinished),
+ (name:'SetRoundRanking'; func:ULuaParty_SetRoundRanking),
+ (name:'GetTeams'; func:ULuaParty_GetTeams),
+ (name:'SetTeams'; func:ULuaParty_SetTeams)
+ );
+
+implementation
+uses ULuaCore, ULuaUtils, UParty, SysUtils;
+
+
+{ Party.Register - register party mode at party manager
+ arguments: info: table
+ Name: String; //< Name used as identifier (language strings, etc.). Has to be set.
+ CanNonParty: Boolean //< mode is playable when not in party mode. defaulted to false if not set
+ CanParty: Boolean //< mode is playable in party mode. defaulted to false if not set
+ PlayerCount: Table //< playable with one, two, three etc. players per team. defaulted to no restrictions if not set. (use table constructor e.g. {1, 2, 3) means playable w/ 1, 2 or three players)
+ TeamCount: Table //< playable with one, two, three etc. different teams. defaulted to no restrictions if not set. (use table constructor e.g. {1, 2, 3) means playable w/ 1, 2 or three players)
+
+ BeforeSongSelect: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
+ AfterSongSelect: String //< name of global that will be called after song is selected (if nil, not callable or returns true, default action will be executed)
+
+ BeforeSing: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
+ OnSing: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
+ AfterSing: String //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)}
+function ULuaParty_Register(L: Plua_State): Integer; cdecl;
+ var
+ Info: TParty_ModeInfo;
+ Key: String;
+ P: TLuaPlugin;
+begin
+ Result := 0;
+
+ // check for table on stack
+ luaL_checkType(L, 1, LUA_TTABLE);
+
+ // get parent id
+ P := Lua_GetOwner(L);
+
+
+ // set mode info to default
+ Party.DefaultModeInfo(Info);
+
+
+ // set parent in info rec and pop it from stack
+ Info.Parent := P.Id;
+
+ // go through table elements
+ lua_pushNil(L);
+ while (lua_Next(L, 1) <> 0) do
+ begin
+ Key := lowercase(lua_ToString(L, -2));
+
+ if (Key = 'name') and lua_isString(L, -1) then
+ Info.Name := lua_toString(L, -1)
+ else if (Key = 'cannonparty') and lua_isBoolean(L, -1) then
+ Info.CanNonParty := lua_toBoolean(L, -1)
+ else if (Key = 'canparty') and lua_isBoolean(L, -1) then
+ Info.CanParty := lua_toBoolean(L, -1)
+ else if (Key = 'playercount') and lua_isTable(L, -1) then
+ Info.PlayerCount := lua_toBinInt(L, -1)
+ else if (Key = 'teamcount') and lua_isTable(L, -1) then
+ Info.TeamCount := lua_toBinInt(L, -1)
+ else if (Key = 'beforesongselect') and lua_isString(L, -1) then
+ Info.Functions.BeforeSongSelect := lua_toString(L, -1)
+ else if (Key = 'aftersongselect') and lua_isString(L, -1) then
+ Info.Functions.AfterSongSelect := lua_toString(L, -1)
+ else if (Key = 'beforesing') and lua_isString(L, -1) then
+ Info.Functions.BeforeSing := lua_toString(L, -1)
+ else if (Key = 'onsing') and lua_isString(L, -1) then
+ Info.Functions.OnSing := lua_toString(L, -1)
+ else if (Key = 'aftersing') and lua_isString(L, -1) then
+ Info.Functions.AfterSing := lua_toString(L, -1);
+
+ // pop value from stack so key is on top
+ lua_pop(L, 1);
+ end;
+
+ // clear stack from table
+ lua_pop(L, lua_gettop(L));
+
+ if not Party.RegisterMode(Info) then
+ luaL_error(L, PChar('can''t register party mode at party manager in Party.Register. Is Info.Name defined or is there another mode with this name?'));
+end;
+
+{ Party.GameFinished - returns true if no party game is running or all rounds
+ of current game were played }
+function ULuaParty_GameFinished(L: Plua_State): Integer; cdecl;
+begin
+ // clear stack
+ lua_pop(L, lua_gettop(L));
+
+ // push result
+ lua_pushBoolean(L, Party.GameFinished);
+
+ //we return one value
+ Result := 1;
+end;
+
+{ Party.SetRoundRanking - sets ranking of current party round,
+ if no party game is started or party game is finished
+ it will raise an error }
+function ULuaParty_SetRoundRanking(L: Plua_State): Integer; cdecl;
+var
+ R: AParty_TeamRanking;
+ I: Integer;
+ Rank: Integer;
+begin
+ Result := 0;
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ lua_checkstack(L, 1);
+
+ SetLength(R, Length(Party.Teams));
+
+ for I := 0 to High(R) do
+ begin
+ lua_pushInteger(L, (I+1));
+ lua_gettable(L, 1);
+
+ R[I].Rank := Length(R);
+ R[I].Team := I;
+ if (lua_isnumber(L, -1)) then
+ begin
+ Rank := lua_toInteger(L, -1);
+ if (Rank >= 1) and (Rank <= Length(R)) then
+ R[I].Rank := Rank
+ end;
+
+ lua_pop(L, 1);
+
+ end;
+
+ // pop table
+ lua_pop(L, 1);
+
+ if (not Party.SetRanking(R)) then
+ luaL_error(L, PChar('cann''t set party round ranking. Is party started and not finished yet?'));
+end;
+
+{ Party.GetTeams - returns a table with all information and structure as
+ in the TPartyGame.Teams array }
+function ULuaParty_GetTeams(L: Plua_State): Integer; cdecl;
+ var
+ Team: Integer;
+ Player: Integer;
+begin
+ // clear stack
+ lua_pop(L, lua_gettop(L));
+
+ // ensure we have enough stack slots left
+ lua_checkstack(L, 7);
+
+ // create the table we want to return
+ lua_createtable(L, Length(Party.Teams), 0);
+
+ // add the teams
+ for Team := 0 to High(Party.Teams) do
+ begin
+ // push key for current teams value. lua array beggins at 1
+ lua_pushInteger(L, Team + 1);
+
+ // push table containing team info and players table
+ lua_createtable(L, 0, 5);
+
+ // team name
+ lua_pushString(L, PChar(Party.Teams[Team].Name));
+ lua_setField(L, -2, 'Name');
+
+ // team score
+ lua_pushInteger(L, Party.Teams[Team].Score);
+ lua_setField(L, -2, 'Score');
+
+ // team jokers left
+ lua_pushInteger(L, Party.Teams[Team].JokersLeft);
+ lua_setField(L, -2, 'JokersLeft');
+
+ // team nextPlayer
+ lua_pushInteger(L, Party.Teams[Team].NextPlayer);
+ lua_setField(L, -2, 'NextPlayer');
+
+ // team players table
+ lua_createtable(L, Length(Party.Teams[Team].Players), 0);
+
+ //add players
+ for Player := 0 to High(Party.Teams[Team].Players) do
+ begin
+ // push key for current players value. lua array beggins at 1
+ lua_pushInteger(L, Player + 1);
+
+ // push table containing player info
+ lua_createTable(L, 0, 2);
+
+ // player name
+ lua_PushString(L, PChar(Party.Teams[Team].Players[Player].Name));
+ lua_SetField(L, -2, 'Name');
+
+ // players times played
+ lua_PushInteger(L, Party.Teams[Team].Players[Player].TimesPlayed);
+ lua_SetField(L, -2, 'TimesPlayed');
+
+ // add value - key - pair to teams player table
+ lua_setTable(L, -3);
+ end;
+
+ lua_setField(L, -2, 'Players');
+
+ // add value - key - pair to returned table
+ lua_setTable(L, -3);
+ end;
+
+ // we return 1 value (the first table)
+ Result := 1;
+end;
+
+{ Party.SetTeams - changes all fields from TPartyGame.Teams that have been
+ set in the table given as first argument}
+function ULuaParty_SetTeams(L: Plua_State): Integer; cdecl;
+
+ procedure Do_Player(Team, Player: Integer);
+ var
+ Key: String;
+ begin
+ if (Player >= 0) and (Player <= High(Party.Teams[Team].Players)) then
+ begin
+ // go through table elements
+ lua_pushNil(L);
+ while (lua_Next(L, -2) <> 0) do
+ begin
+ Key := lowercase(lua_ToString(L, -2));
+
+ if (Key = 'name') and lua_isString(L, -1) then
+ Party.Teams[Team].Players[Player].Name := lua_toString(L, -1)
+ else if (Key = 'timesplayed') and lua_isNumber(L, -1) then
+ Party.Teams[Team].Players[Player].TimesPlayed := lua_toInteger(L, -1);
+
+ // pop value from stack so key is on top
+ lua_pop(L, 1);
+ end;
+ end;
+ end;
+
+ procedure Do_Players(Team: Integer);
+ begin
+ // go through table elements
+ lua_pushNil(L);
+ while (lua_Next(L, -2) <> 0) do
+ begin
+ // check if key is a number and value is a table
+ if (lua_isNumber(L, -2)) and (lua_isTable(L, -1)) then
+ Do_Player(Team, lua_toInteger(L, -2));
+
+ // pop value from stack so key is on top
+ lua_pop(L, 1);
+ end;
+ end;
+
+ procedure Do_Team(Team: Integer);
+ var
+ Key: String;
+ begin
+ if (Team >= 0) and (Team <= High(Party.Teams)) then
+ begin
+ // go through table elements
+ lua_pushNil(L);
+ while (lua_Next(L, -2) <> 0) do
+ begin
+ Key := lowercase(lua_ToString(L, -2));
+
+ if (Key = 'name') and lua_isString(L, -1) then
+ Party.Teams[Team].Name := lua_toString(L, -1)
+ else if (Key = 'score') and lua_isNumber(L, -1) then
+ Party.Teams[Team].Score := lua_toInteger(L, -1)
+ else if (Key = 'jokersleft') and lua_isNumber(L, -1) then
+ Party.Teams[Team].JokersLeft := lua_toInteger(L, -1)
+ else if (Key = 'currentplayer') and lua_isNumber(L, -1) then
+ Party.Teams[Team].NextPlayer := lua_toInteger(L, -1)
+ else if (Key = 'players') and lua_isTable(L, -1) then
+ Do_Players(Team);
+
+ // pop value from stack so key is on top
+ lua_pop(L, 1);
+ end;
+ end;
+ end;
+begin
+ Result := 0;
+
+ // check for table on stack
+ luaL_checkType(L, 1, LUA_TTABLE);
+
+
+ // go through table elements
+ lua_pushNil(L);
+ while (lua_Next(L, 1) <> 0) do
+ begin
+ // check if key is a number and value is a table
+ if (lua_isNumber(L, -2)) and (lua_isTable(L, -1)) then
+ Do_Team(lua_toInteger(L, -2));
+
+ // pop value from stack so key is on top
+ lua_pop(L, 1);
+ end;
+
+ // clear stack from table
+ lua_pop(L, lua_gettop(L));
+end;
+
end. \ No newline at end of file
diff --git a/src/lua/ULuaScreenSing.pas b/src/lua/ULuaScreenSing.pas
index 7e17224c..8968eb36 100644
--- a/src/lua/ULuaScreenSing.pas
+++ b/src/lua/ULuaScreenSing.pas
@@ -1,489 +1,489 @@
-{* 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/branches/experimental/Lua/src/lua/ULuaTexture.pas $
- * $Id: ULuaTexture.pas 1551 2009-01-04 14:08:33Z Hawkear $
- *}
-
-unit ULuaScreenSing;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses
- ULua;
-
-{ returns a table with following structure:
- t[1..playercount] = score of player i }
-function ULuaScreenSing_GetScores(L: Plua_State): Integer; cdecl;
-
-{ returns a table with following structure:
- t[1..playercount] = rating of player i range: [0..1] }
-function ULuaScreenSing_GetRating(L: Plua_State): Integer; cdecl;
-
-{ returns a table with following structure:
- t[1..playercount] = rect of players score background: table(x, y, w, h) }
-function ULuaScreenSing_GetScoreBGRect(L: Plua_State): Integer; cdecl;
-
-{ returns a table with following structure:
- t[1..playercount] = rect of players rating bar: table(x, y, w, h) }
-function ULuaScreenSing_GetRBRect(L: Plua_State): Integer; cdecl;
-
-{ ScreenSing.GetBPM - no arguments
- returns the beats per minutes of the current song in quarts }
-function ULuaScreenSing_GetBPM(L: Plua_State): Integer; cdecl;
-
-{ ScreenSing.BeatsToSeconds(Beats: float)
- returns the time in seconds that the given number of beats (in quarts) last }
-function ULuaScreenSing_BeatsToSeconds(L: Plua_State): Integer; cdecl;
-
-{ ScreenSing.SecondsToBeats(Seconds: float)
- returns the Beats in quarts that the given seconds last }
-function ULuaScreenSing_SecondsToBeats(L: Plua_State): Integer; cdecl;
-
-{ ScreenSing.GetBeat() - returns current beat of lyricstate (in quarts) }
-function ULuaScreenSing_GetBeat(L: Plua_State): Integer; cdecl;
-
-{ finishes current song, if sing screen is not shown it will raise
- an error }
-function ULuaScreenSing_Finish(L: Plua_State): Integer; cdecl;
-
-{ ScreenSing.GetSettings - no arguments
- returns a table filled with the data of TScreenSing.Settings }
-function ULuaScreenSing_GetSettings(L: Plua_State): Integer; cdecl;
-
-{ ScreenSing.SetSettings - arguments: Table
- sets all attributes of TScreenSing.Settings that are
- unequal to nil in Table }
-function ULuaScreenSing_SetSettings(L: Plua_State): Integer; cdecl;
-
-{ ScreenSing.GetSongLines - no arguments
- returns a table filled with lines of the loaded song or
- nil if no song is loaded (singscreen is not displayed)
- structure of returned table:
- array [1.."count of lines"]
- \
- | Start: integer - beat the line is displayed at (on top of lyrics display)
- | Lyric: string - full lyric of the line
- | Notes: array [1.."count notes of this line"]
- \
- | Start: integer - beat the note starts at
- | Length: integer - length in beats
- | Tone: integer - pitch that has to be sung, full range
- | NoteType: integer - 0 for freestyle, 1 for normal, 2 for golden
- | Text: string - text of this fragment }
-function ULuaScreenSing_GetSongLines(L: Plua_State): Integer; cdecl;
-
-const
- ULuaScreenSing_Lib_f: array [0..11] of lual_reg = (
- (name:'GetScores';func:ULuaScreenSing_GetScores),
- (name:'GetRating';func:ULuaScreenSing_GetRating),
- (name:'GetBPM';func:ULuaScreenSing_GetBPM),
- (name:'BeatsToSeconds';func:ULuaScreenSing_BeatsToSeconds),
- (name:'SecondsToBeats';func:ULuaScreenSing_SecondsToBeats),
- (name:'GetBeat';func:ULuaScreenSing_GetBeat),
- (name:'GetScoreBGRect';func:ULuaScreenSing_GetScoreBGRect),
- (name:'GetRBRect';func:ULuaScreenSing_GetRBRect),
- (name:'Finish';func:ULuaScreenSing_Finish),
- (name:'GetSettings';func:ULuaScreenSing_GetSettings),
- (name:'SetSettings';func:ULuaScreenSing_SetSettings),
- (name:'GetSongLines';func:ULuaScreenSing_GetSongLines)
- );
-
-implementation
-uses UScreenSing, UNote, UDisplay, UGraphic, UMusic, ULuaUtils, SysUtils;
-
-{ returns a table with following structure:
- t[1..playercount] = score of player i }
-function ULuaScreenSing_GetScores(L: Plua_State): Integer; cdecl;
- var
- Top: Integer;
- I: Integer;
-begin
- Result := 1;
-
- // pop arguments
- Top := lua_getTop(L);
- if (Top > 0) then
- lua_pop(L, Top);
-
- // create table
- lua_createtable(L, Length(Player), 0);
-
- // fill w/ values
- for I := 0 to High(Player) do
- begin
- lua_pushInteger(L, I + 1);
- lua_pushInteger(L, Player[I].ScoreTotalInt);
-
- lua_settable(L, -3);
- end;
-
- // leave table on stack, it is our result
-end;
-
-{ returns a table with following structure:
- t[1..playercount] = rating of player i range: [0..1] }
-function ULuaScreenSing_GetRating(L: Plua_State): Integer; cdecl;
- var
- Top: Integer;
- I: Integer;
-begin
- Result := 1;
-
- // pop arguments
- Top := lua_getTop(L);
- if (Top > 0) then
- lua_pop(L, Top);
-
- // create table
- lua_createtable(L, Length(Player), 0);
-
- // fill w/ values
- for I := 0 to High(ScreenSing.Scores.Players) do
- begin
- lua_pushInteger(L, I + 1);
- lua_pushNumber(L, ScreenSing.Scores.Players[I].RBPos);
-
- lua_settable(L, -3);
- end;
-
- // leave table on stack, it is our result
-end;
-
-{ ScreenSing.GetBPM - no arguments
- returns the beats per minutes of the current song in quarts }
-function ULuaScreenSing_GetBPM(L: Plua_State): Integer; cdecl;
-begin
- lua_ClearStack(L);
- Result := 1;
-
- if (CurrentSong = nil) or (Length(CurrentSong.BPM) = 0) or (Display.CurrentScreen <> @ScreenSing) then
- lua_PushNumber(L, 0) // in case of error
- else if (Length(CurrentSong.BPM) = 1) then
- lua_PushNumber(L, CurrentSong.BPM[0].BPM)
- else
- begin
- // to-do: do this for songs w/ BPM changes
- // or drop support for BPM changes?!
- end;
-end;
-
-{ ScreenSing.BeatsToSeconds(Beats: float)
- returns the time in seconds that the given number of beats (in quarts) last }
-function ULuaScreenSing_BeatsToSeconds(L: Plua_State): Integer; cdecl;
-begin
- Result := 1;
-
- if (CurrentSong = nil) or (Length(CurrentSong.BPM) = 0) or (Display.CurrentScreen <> @ScreenSing) then
- lua_PushNumber(L, 0) // in case of error
- else if (Length(CurrentSong.BPM) = 1) then
- lua_PushNumber(L, luaL_CheckNumber(L, 1) * 60 / CurrentSong.BPM[0].BPM)
- else
- begin
- // to-do: do this for songs w/ BPM changes
- // or drop support for BPM changes?!
- end;
-end;
-
-{ ScreenSing.BeatsToSeconds(Seconds: float)
- returns the Beats in quarts that the given seconds last }
-function ULuaScreenSing_SecondsToBeats(L: Plua_State): Integer; cdecl;
-begin
- Result := 1;
-
- if (CurrentSong = nil) or (Length(CurrentSong.BPM) = 0) or (Display.CurrentScreen <> @ScreenSing) then
- lua_PushNumber(L, 0)
- else if (Length(CurrentSong.BPM) = 1) then
- lua_PushNumber(L, luaL_CheckNumber(L, 1) * CurrentSong.BPM[0].BPM / 60)
- else
- begin
- // to-do: do this for songs w/ BPM changes
- // or drop support for BPM changes?!
- end;
-end;
-
-{ ScreenSing.GetBeat() - returns current beat of lyricstate (in quarts) }
-function ULuaScreenSing_GetBeat(L: Plua_State): Integer; cdecl;
-var top: Integer;
-begin
- //remove arguments (if any)
- top := lua_gettop(L);
-
- if (top > 0) then
- lua_pop(L, top);
-
- //push result
- lua_pushnumber(L, LyricsState.MidBeat);
- Result := 1; //one result
-end;
-
-{ returns a table with following structure:
- t[1..playercount] = rect of players ScoreBG: table(x, y, w, h) }
-function ULuaScreenSing_GetScoreBGRect(L: Plua_State): Integer; cdecl;
- var
- Top: Integer;
- I: Integer;
-begin
- Result := 1;
-
- // pop arguments
- Top := lua_getTop(L);
- if (Top > 0) then
- lua_pop(L, Top);
-
- // create table
- lua_createtable(L, Length(ScreenSing.Scores.Players), 0);
-
- // fill w/ values
- for I := 0 to High(ScreenSing.Scores.Players) do
- begin
- lua_pushInteger(L, I + 1);
-
- if (ScreenSing.Scores.Players[I].Position = High(Byte)) then
- // player has no position, prevent crash by pushing nil
- lua_pushNil(L)
- else
- with ScreenSing.Scores.Positions[ScreenSing.Scores.Players[I].Position] do
- lua_PushRect(L, BGX, BGY, BGW, BGH);
-
-
- lua_settable(L, -3);
- end;
-
- // leave table on stack, it is our result
-end;
-
-{ returns a table with following structure:
- t[1..playercount] = rect of players rating bar: table(x, y, w, h) }
-function ULuaScreenSing_GetRBRect(L: Plua_State): Integer; cdecl;
- var
- Top: Integer;
- I: Integer;
-begin
- Result := 1;
-
- // pop arguments
- Top := lua_getTop(L);
- if (Top > 0) then
- lua_pop(L, Top);
-
- // create table
- lua_createtable(L, Length(ScreenSing.Scores.Players), 0);
-
- // fill w/ values
- for I := 0 to High(ScreenSing.Scores.Players) do
- begin
- lua_pushInteger(L, I + 1);
-
- if (ScreenSing.Scores.Players[I].Position = High(Byte)) then
- // player has no position, prevent crash by pushing nil
- lua_pushNil(L)
- else
- with ScreenSing.Scores.Positions[ScreenSing.Scores.Players[I].Position] do
- lua_PushRect(L, RBX, RBY, RBW, RBH);
-
-
- lua_settable(L, -3);
- end;
-
- // leave table on stack, it is our result
-end;
-
-{ finishes current song, if sing screen is not shown it will raise
- an error }
-function ULuaScreenSing_Finish(L: Plua_State): Integer; cdecl;
- var Top: Integer;
-begin
- Result := 0;
-
- // pop arguments
- Top := lua_getTop(L);
- if (Top > 0) then
- lua_pop(L, Top);
-
- if (Display.CurrentScreen^ = ScreenSing) then
- begin
- ScreenSing.EndSong;
- end
- else
- LuaL_error(L, 'Usdx.ScreenSing.Finish is called, but sing screen is not shown.');
-end;
-
-{ ScreenSing.GetSettings - no arguments
- returns a table filled with the data of TScreenSing }
-function ULuaScreenSing_GetSettings(L: Plua_State): Integer; cdecl;
- var Top: Integer;
-begin
- // pop arguments
- Top := lua_getTop(L);
- if (Top > 0) then
- lua_pop(L, Top);
-
- lua_createtable(L, 0, 3);
-
- //fill table w/ info
- lua_pushBoolean(L, ScreenSing.Settings.LyricsVisible);
- lua_setField(L, -2, 'LyricsVisible');
-
- lua_pushBinInt(L, ScreenSing.Settings.NotesVisible);
- lua_setField(L, -2, 'NotesVisible');
-
- lua_pushBinInt(L, ScreenSing.Settings.PlayerEnabled);
- lua_setField(L, -2, 'PlayerEnabled');
-
-
- Result := 1;
-end;
-
-{ ScreenSing.SetSettings - arguments: Table
- sets all attributes of TScreenSing.Settings that are
- unequal to nil in Table }
-function ULuaScreenSing_SetSettings(L: Plua_State): Integer; cdecl;
- var
- Key: String;
-begin
- Result := 0;
-
- // check for table on stack
- luaL_checkType(L, 1, LUA_TTABLE);
-
- // go through table elements
- lua_pushNil(L);
- while (lua_Next(L, 1) <> 0) do
- begin
- Key := lowercase(lua_ToString(L, -2));
-
- if (Key = 'lyricsvisible') and (lua_isBoolean(L, -1)) then
- ScreenSing.settings.LyricsVisible := lua_toBoolean(L, -1)
- else if (Key = 'notesvisible') and (lua_isTable(L, -1)) then
- ScreenSing.settings.NotesVisible := lua_toBinInt(L, -1)
- else if (Key = 'playerenabled') and (lua_isTable(L, -1)) then
- ScreenSing.settings.PlayerEnabled := lua_toBinInt(L, -1);
-
- // pop value from stack so key is on top
- lua_pop(L, 1);
- end;
-
- // clear stack from table
- lua_pop(L, lua_gettop(L));
-
- ScreenSing.ApplySettings;
-end;
-
-{ ScreenSing.GetSongLines - no arguments
- returns a table filled with lines of the loaded song or
- nil if no song is loaded (singscreen is not displayed)
- structure of returned table:
- array [1.."count of lines"]
- \
- | Start: integer - beat the line is displayed at (on top of lyrics display)
- | Lyric: string - full lyric of the line
- | Notes: array [1.."count notes of this line"]
- \
- | Start: integer - beat the note starts at
- | Length: integer - length in beats
- | Tone: integer - pitch that has to be sung, full range
- | NoteType: integer - 0 for freestyle, 1 for normal, 2 for golden
- | Text: string - text of this fragment }
-function ULuaScreenSing_GetSongLines(L: Plua_State): Integer; cdecl;
- var
- I, J: Integer;
-begin
- Result := 1;
- if (Length(Lines) >= 1) then
- begin
- lua_ClearStack(L);
-
- if not lua_CheckStack(L, 7) then
- luaL_Error(L, PChar('can''t allocate enough stack space in ULuaScreenSing_GetSongLines'));
-
- // lines array table
- lua_CreateTable(L, Length(Lines[0].Line), 0);
-
- for I := 0 to High(Lines[0].Line) do
- with Lines[0].Line[I] do
- begin
- lua_pushInteger(L, I+1);
-
- // line struct table
- lua_CreateTable(L, 0, 3);
-
- // line start
- lua_PushInteger(L, Start);
- lua_SetField(L, -2, PChar('Start'));
-
- // line lyric
- lua_PushString(L, PChar(Lyric));
- lua_SetField(L, -2, PChar('Lyric'));
-
- //line notes array table
- lua_CreateTable(L, Length(Note), 0);
-
- for J := 0 to High(Note) do
- begin
- lua_PushInteger(L, J + 1);
-
- // note struct table
- lua_CreateTable(L, 0, 5);
-
- // Notes[J+1].Start
- lua_PushInteger(L, Note[J].Start);
- lua_SetField(L, -2, PChar('Start'));
-
- // Notes[J+1].Length
- lua_PushInteger(L, Note[J].Length);
- lua_SetField(L, -2, PChar('Length'));
-
- // Notes[J+1].Tone
- lua_PushInteger(L, Note[J].Tone);
- lua_SetField(L, -2, PChar('Tone'));
-
- // Notes[J+1].NoteType
- lua_PushInteger(L, Integer(Note[J].NoteType));
- lua_SetField(L, -2, PChar('NoteType'));
-
- // Notes[J+1].Text
- lua_PushString(L, PChar(Note[J].Text));
- lua_SetField(L, -2, PChar('Text'));
-
- lua_SetTable(L, -3);
- end;
-
- lua_SetField(L, -2, PChar('Notes'));
-
- // save line to array table
- lua_setTable(L, -3);
- end;
- end
- else
- begin
- lua_ClearStack(L);
- lua_pushNil(L);
- end;
-end;
-
+{* 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/branches/experimental/Lua/src/lua/ULuaTexture.pas $
+ * $Id: ULuaTexture.pas 1551 2009-01-04 14:08:33Z Hawkear $
+ *}
+
+unit ULuaScreenSing;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ ULua;
+
+{ returns a table with following structure:
+ t[1..playercount] = score of player i }
+function ULuaScreenSing_GetScores(L: Plua_State): Integer; cdecl;
+
+{ returns a table with following structure:
+ t[1..playercount] = rating of player i range: [0..1] }
+function ULuaScreenSing_GetRating(L: Plua_State): Integer; cdecl;
+
+{ returns a table with following structure:
+ t[1..playercount] = rect of players score background: table(x, y, w, h) }
+function ULuaScreenSing_GetScoreBGRect(L: Plua_State): Integer; cdecl;
+
+{ returns a table with following structure:
+ t[1..playercount] = rect of players rating bar: table(x, y, w, h) }
+function ULuaScreenSing_GetRBRect(L: Plua_State): Integer; cdecl;
+
+{ ScreenSing.GetBPM - no arguments
+ returns the beats per minutes of the current song in quarts }
+function ULuaScreenSing_GetBPM(L: Plua_State): Integer; cdecl;
+
+{ ScreenSing.BeatsToSeconds(Beats: float)
+ returns the time in seconds that the given number of beats (in quarts) last }
+function ULuaScreenSing_BeatsToSeconds(L: Plua_State): Integer; cdecl;
+
+{ ScreenSing.SecondsToBeats(Seconds: float)
+ returns the Beats in quarts that the given seconds last }
+function ULuaScreenSing_SecondsToBeats(L: Plua_State): Integer; cdecl;
+
+{ ScreenSing.GetBeat() - returns current beat of lyricstate (in quarts) }
+function ULuaScreenSing_GetBeat(L: Plua_State): Integer; cdecl;
+
+{ finishes current song, if sing screen is not shown it will raise
+ an error }
+function ULuaScreenSing_Finish(L: Plua_State): Integer; cdecl;
+
+{ ScreenSing.GetSettings - no arguments
+ returns a table filled with the data of TScreenSing.Settings }
+function ULuaScreenSing_GetSettings(L: Plua_State): Integer; cdecl;
+
+{ ScreenSing.SetSettings - arguments: Table
+ sets all attributes of TScreenSing.Settings that are
+ unequal to nil in Table }
+function ULuaScreenSing_SetSettings(L: Plua_State): Integer; cdecl;
+
+{ ScreenSing.GetSongLines - no arguments
+ returns a table filled with lines of the loaded song or
+ nil if no song is loaded (singscreen is not displayed)
+ structure of returned table:
+ array [1.."count of lines"]
+ \
+ | Start: integer - beat the line is displayed at (on top of lyrics display)
+ | Lyric: string - full lyric of the line
+ | Notes: array [1.."count notes of this line"]
+ \
+ | Start: integer - beat the note starts at
+ | Length: integer - length in beats
+ | Tone: integer - pitch that has to be sung, full range
+ | NoteType: integer - 0 for freestyle, 1 for normal, 2 for golden
+ | Text: string - text of this fragment }
+function ULuaScreenSing_GetSongLines(L: Plua_State): Integer; cdecl;
+
+const
+ ULuaScreenSing_Lib_f: array [0..11] of lual_reg = (
+ (name:'GetScores';func:ULuaScreenSing_GetScores),
+ (name:'GetRating';func:ULuaScreenSing_GetRating),
+ (name:'GetBPM';func:ULuaScreenSing_GetBPM),
+ (name:'BeatsToSeconds';func:ULuaScreenSing_BeatsToSeconds),
+ (name:'SecondsToBeats';func:ULuaScreenSing_SecondsToBeats),
+ (name:'GetBeat';func:ULuaScreenSing_GetBeat),
+ (name:'GetScoreBGRect';func:ULuaScreenSing_GetScoreBGRect),
+ (name:'GetRBRect';func:ULuaScreenSing_GetRBRect),
+ (name:'Finish';func:ULuaScreenSing_Finish),
+ (name:'GetSettings';func:ULuaScreenSing_GetSettings),
+ (name:'SetSettings';func:ULuaScreenSing_SetSettings),
+ (name:'GetSongLines';func:ULuaScreenSing_GetSongLines)
+ );
+
+implementation
+uses UScreenSing, UNote, UDisplay, UGraphic, UMusic, ULuaUtils, SysUtils;
+
+{ returns a table with following structure:
+ t[1..playercount] = score of player i }
+function ULuaScreenSing_GetScores(L: Plua_State): Integer; cdecl;
+ var
+ Top: Integer;
+ I: Integer;
+begin
+ Result := 1;
+
+ // pop arguments
+ Top := lua_getTop(L);
+ if (Top > 0) then
+ lua_pop(L, Top);
+
+ // create table
+ lua_createtable(L, Length(Player), 0);
+
+ // fill w/ values
+ for I := 0 to High(Player) do
+ begin
+ lua_pushInteger(L, I + 1);
+ lua_pushInteger(L, Player[I].ScoreTotalInt);
+
+ lua_settable(L, -3);
+ end;
+
+ // leave table on stack, it is our result
+end;
+
+{ returns a table with following structure:
+ t[1..playercount] = rating of player i range: [0..1] }
+function ULuaScreenSing_GetRating(L: Plua_State): Integer; cdecl;
+ var
+ Top: Integer;
+ I: Integer;
+begin
+ Result := 1;
+
+ // pop arguments
+ Top := lua_getTop(L);
+ if (Top > 0) then
+ lua_pop(L, Top);
+
+ // create table
+ lua_createtable(L, Length(Player), 0);
+
+ // fill w/ values
+ for I := 0 to High(ScreenSing.Scores.Players) do
+ begin
+ lua_pushInteger(L, I + 1);
+ lua_pushNumber(L, ScreenSing.Scores.Players[I].RBPos);
+
+ lua_settable(L, -3);
+ end;
+
+ // leave table on stack, it is our result
+end;
+
+{ ScreenSing.GetBPM - no arguments
+ returns the beats per minutes of the current song in quarts }
+function ULuaScreenSing_GetBPM(L: Plua_State): Integer; cdecl;
+begin
+ lua_ClearStack(L);
+ Result := 1;
+
+ if (CurrentSong = nil) or (Length(CurrentSong.BPM) = 0) or (Display.CurrentScreen <> @ScreenSing) then
+ lua_PushNumber(L, 0) // in case of error
+ else if (Length(CurrentSong.BPM) = 1) then
+ lua_PushNumber(L, CurrentSong.BPM[0].BPM)
+ else
+ begin
+ // to-do: do this for songs w/ BPM changes
+ // or drop support for BPM changes?!
+ end;
+end;
+
+{ ScreenSing.BeatsToSeconds(Beats: float)
+ returns the time in seconds that the given number of beats (in quarts) last }
+function ULuaScreenSing_BeatsToSeconds(L: Plua_State): Integer; cdecl;
+begin
+ Result := 1;
+
+ if (CurrentSong = nil) or (Length(CurrentSong.BPM) = 0) or (Display.CurrentScreen <> @ScreenSing) then
+ lua_PushNumber(L, 0) // in case of error
+ else if (Length(CurrentSong.BPM) = 1) then
+ lua_PushNumber(L, luaL_CheckNumber(L, 1) * 60 / CurrentSong.BPM[0].BPM)
+ else
+ begin
+ // to-do: do this for songs w/ BPM changes
+ // or drop support for BPM changes?!
+ end;
+end;
+
+{ ScreenSing.BeatsToSeconds(Seconds: float)
+ returns the Beats in quarts that the given seconds last }
+function ULuaScreenSing_SecondsToBeats(L: Plua_State): Integer; cdecl;
+begin
+ Result := 1;
+
+ if (CurrentSong = nil) or (Length(CurrentSong.BPM) = 0) or (Display.CurrentScreen <> @ScreenSing) then
+ lua_PushNumber(L, 0)
+ else if (Length(CurrentSong.BPM) = 1) then
+ lua_PushNumber(L, luaL_CheckNumber(L, 1) * CurrentSong.BPM[0].BPM / 60)
+ else
+ begin
+ // to-do: do this for songs w/ BPM changes
+ // or drop support for BPM changes?!
+ end;
+end;
+
+{ ScreenSing.GetBeat() - returns current beat of lyricstate (in quarts) }
+function ULuaScreenSing_GetBeat(L: Plua_State): Integer; cdecl;
+var top: Integer;
+begin
+ //remove arguments (if any)
+ top := lua_gettop(L);
+
+ if (top > 0) then
+ lua_pop(L, top);
+
+ //push result
+ lua_pushnumber(L, LyricsState.MidBeat);
+ Result := 1; //one result
+end;
+
+{ returns a table with following structure:
+ t[1..playercount] = rect of players ScoreBG: table(x, y, w, h) }
+function ULuaScreenSing_GetScoreBGRect(L: Plua_State): Integer; cdecl;
+ var
+ Top: Integer;
+ I: Integer;
+begin
+ Result := 1;
+
+ // pop arguments
+ Top := lua_getTop(L);
+ if (Top > 0) then
+ lua_pop(L, Top);
+
+ // create table
+ lua_createtable(L, Length(ScreenSing.Scores.Players), 0);
+
+ // fill w/ values
+ for I := 0 to High(ScreenSing.Scores.Players) do
+ begin
+ lua_pushInteger(L, I + 1);
+
+ if (ScreenSing.Scores.Players[I].Position = High(Byte)) then
+ // player has no position, prevent crash by pushing nil
+ lua_pushNil(L)
+ else
+ with ScreenSing.Scores.Positions[ScreenSing.Scores.Players[I].Position] do
+ lua_PushRect(L, BGX, BGY, BGW, BGH);
+
+
+ lua_settable(L, -3);
+ end;
+
+ // leave table on stack, it is our result
+end;
+
+{ returns a table with following structure:
+ t[1..playercount] = rect of players rating bar: table(x, y, w, h) }
+function ULuaScreenSing_GetRBRect(L: Plua_State): Integer; cdecl;
+ var
+ Top: Integer;
+ I: Integer;
+begin
+ Result := 1;
+
+ // pop arguments
+ Top := lua_getTop(L);
+ if (Top > 0) then
+ lua_pop(L, Top);
+
+ // create table
+ lua_createtable(L, Length(ScreenSing.Scores.Players), 0);
+
+ // fill w/ values
+ for I := 0 to High(ScreenSing.Scores.Players) do
+ begin
+ lua_pushInteger(L, I + 1);
+
+ if (ScreenSing.Scores.Players[I].Position = High(Byte)) then
+ // player has no position, prevent crash by pushing nil
+ lua_pushNil(L)
+ else
+ with ScreenSing.Scores.Positions[ScreenSing.Scores.Players[I].Position] do
+ lua_PushRect(L, RBX, RBY, RBW, RBH);
+
+
+ lua_settable(L, -3);
+ end;
+
+ // leave table on stack, it is our result
+end;
+
+{ finishes current song, if sing screen is not shown it will raise
+ an error }
+function ULuaScreenSing_Finish(L: Plua_State): Integer; cdecl;
+ var Top: Integer;
+begin
+ Result := 0;
+
+ // pop arguments
+ Top := lua_getTop(L);
+ if (Top > 0) then
+ lua_pop(L, Top);
+
+ if (Display.CurrentScreen^ = ScreenSing) then
+ begin
+ ScreenSing.EndSong;
+ end
+ else
+ LuaL_error(L, 'Usdx.ScreenSing.Finish is called, but sing screen is not shown.');
+end;
+
+{ ScreenSing.GetSettings - no arguments
+ returns a table filled with the data of TScreenSing }
+function ULuaScreenSing_GetSettings(L: Plua_State): Integer; cdecl;
+ var Top: Integer;
+begin
+ // pop arguments
+ Top := lua_getTop(L);
+ if (Top > 0) then
+ lua_pop(L, Top);
+
+ lua_createtable(L, 0, 3);
+
+ //fill table w/ info
+ lua_pushBoolean(L, ScreenSing.Settings.LyricsVisible);
+ lua_setField(L, -2, 'LyricsVisible');
+
+ lua_pushBinInt(L, ScreenSing.Settings.NotesVisible);
+ lua_setField(L, -2, 'NotesVisible');
+
+ lua_pushBinInt(L, ScreenSing.Settings.PlayerEnabled);
+ lua_setField(L, -2, 'PlayerEnabled');
+
+
+ Result := 1;
+end;
+
+{ ScreenSing.SetSettings - arguments: Table
+ sets all attributes of TScreenSing.Settings that are
+ unequal to nil in Table }
+function ULuaScreenSing_SetSettings(L: Plua_State): Integer; cdecl;
+ var
+ Key: String;
+begin
+ Result := 0;
+
+ // check for table on stack
+ luaL_checkType(L, 1, LUA_TTABLE);
+
+ // go through table elements
+ lua_pushNil(L);
+ while (lua_Next(L, 1) <> 0) do
+ begin
+ Key := lowercase(lua_ToString(L, -2));
+
+ if (Key = 'lyricsvisible') and (lua_isBoolean(L, -1)) then
+ ScreenSing.settings.LyricsVisible := lua_toBoolean(L, -1)
+ else if (Key = 'notesvisible') and (lua_isTable(L, -1)) then
+ ScreenSing.settings.NotesVisible := lua_toBinInt(L, -1)
+ else if (Key = 'playerenabled') and (lua_isTable(L, -1)) then
+ ScreenSing.settings.PlayerEnabled := lua_toBinInt(L, -1);
+
+ // pop value from stack so key is on top
+ lua_pop(L, 1);
+ end;
+
+ // clear stack from table
+ lua_pop(L, lua_gettop(L));
+
+ ScreenSing.ApplySettings;
+end;
+
+{ ScreenSing.GetSongLines - no arguments
+ returns a table filled with lines of the loaded song or
+ nil if no song is loaded (singscreen is not displayed)
+ structure of returned table:
+ array [1.."count of lines"]
+ \
+ | Start: integer - beat the line is displayed at (on top of lyrics display)
+ | Lyric: string - full lyric of the line
+ | Notes: array [1.."count notes of this line"]
+ \
+ | Start: integer - beat the note starts at
+ | Length: integer - length in beats
+ | Tone: integer - pitch that has to be sung, full range
+ | NoteType: integer - 0 for freestyle, 1 for normal, 2 for golden
+ | Text: string - text of this fragment }
+function ULuaScreenSing_GetSongLines(L: Plua_State): Integer; cdecl;
+ var
+ I, J: Integer;
+begin
+ Result := 1;
+ if (Length(Lines) >= 1) then
+ begin
+ lua_ClearStack(L);
+
+ if not lua_CheckStack(L, 7) then
+ luaL_Error(L, PChar('can''t allocate enough stack space in ULuaScreenSing_GetSongLines'));
+
+ // lines array table
+ lua_CreateTable(L, Length(Lines[0].Line), 0);
+
+ for I := 0 to High(Lines[0].Line) do
+ with Lines[0].Line[I] do
+ begin
+ lua_pushInteger(L, I+1);
+
+ // line struct table
+ lua_CreateTable(L, 0, 3);
+
+ // line start
+ lua_PushInteger(L, Start);
+ lua_SetField(L, -2, PChar('Start'));
+
+ // line lyric
+ lua_PushString(L, PChar(Lyric));
+ lua_SetField(L, -2, PChar('Lyric'));
+
+ //line notes array table
+ lua_CreateTable(L, Length(Note), 0);
+
+ for J := 0 to High(Note) do
+ begin
+ lua_PushInteger(L, J + 1);
+
+ // note struct table
+ lua_CreateTable(L, 0, 5);
+
+ // Notes[J+1].Start
+ lua_PushInteger(L, Note[J].Start);
+ lua_SetField(L, -2, PChar('Start'));
+
+ // Notes[J+1].Length
+ lua_PushInteger(L, Note[J].Length);
+ lua_SetField(L, -2, PChar('Length'));
+
+ // Notes[J+1].Tone
+ lua_PushInteger(L, Note[J].Tone);
+ lua_SetField(L, -2, PChar('Tone'));
+
+ // Notes[J+1].NoteType
+ lua_PushInteger(L, Integer(Note[J].NoteType));
+ lua_SetField(L, -2, PChar('NoteType'));
+
+ // Notes[J+1].Text
+ lua_PushString(L, PChar(Note[J].Text));
+ lua_SetField(L, -2, PChar('Text'));
+
+ lua_SetTable(L, -3);
+ end;
+
+ lua_SetField(L, -2, PChar('Notes'));
+
+ // save line to array table
+ lua_setTable(L, -3);
+ end;
+ end
+ else
+ begin
+ lua_ClearStack(L);
+ lua_pushNil(L);
+ end;
+end;
+
end. \ No newline at end of file
diff --git a/src/lua/ULuaUsdx.pas b/src/lua/ULuaUsdx.pas
index d92289b1..ac40f180 100644
--- a/src/lua/ULuaUsdx.pas
+++ b/src/lua/ULuaUsdx.pas
@@ -1,145 +1,145 @@
-{* 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 ULuaUsdx;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses ULua;
-
-{ some basic lua c functions from usdx table }
-
-{ Usdx.Time - returns sdl_time to have time numbers comparable with
- ultrastar deluxe ones. no arguments }
-function ULuaUsdx_Time(L: Plua_State): Integer; cdecl;
-
-{ Usdx.Version - returns Usdx version string (the same that US_Version
- language-constant does). no arguments }
-function ULuaUsdx_Version(L: Plua_State): Integer; cdecl;
-
-{ Usdx.Hook - returns an hook table with name and Unhook function
- arguments: event_name: string }
-function ULuaUsdx_Hook(L: Plua_State): Integer; cdecl;
-
-{ Usdx.ShutMeDown - no results, no arguments
- unloads the calling plugin }
-function ULuaUsdx_ShutMeDown(L: Plua_State): Integer; cdecl;
-
-const
- ULuaUsdx_Lib_f: array [0..4] of lual_reg = (
- (name:'Version'; func:ULuaUsdx_Version),
- (name:'Time'; func:ULuaUsdx_Time),
- (name:'Hook'; func:ULuaUsdx_Hook),
- (name:'ShutMeDown'; func:ULuaUsdx_ShutMeDown),
- (name:nil;func:nil)
- );
-
-implementation
-uses SDL, ULuaCore, ULuaUtils, UHookableEvent, UConfig;
-
-{ Usdx.Time - returns sdl_time to have time numbers comparable with
- ultrastar deluxe ones. no arguments }
-function ULuaUsdx_Time(L: Plua_State): Integer; cdecl;
- var top: Integer;
-begin
- //remove arguments (if any)
- top := lua_gettop(L);
-
- if (top > 0) then
- lua_pop(L, top);
-
- //push result
- lua_pushinteger(L, SDL_GetTicks);
- Result := 1; //one result
-end;
-
-{ Usdx.Version - returns Usdx version string (the same that US_Version
- language-constant does). no arguments }
-function ULuaUsdx_Version(L: Plua_State): Integer; cdecl;
- var top: Integer;
-begin
- //remove arguments (if any)
- top := lua_gettop(L);
-
- if (top > 0) then
- lua_pop(L, top);
-
- //push result
- lua_pushstring(L, PChar(USDXVersionStr()));
- Result := 1; //one result
-end;
-
-{ Usdx.Hook - returns an hook table with name and Unhook function
- arguments: event_name: string; function_name: string }
-function ULuaUsdx_Hook(L: Plua_State): Integer; cdecl;
-var
- EventName: String;
- FunctionName: String;
- P: TLuaPlugin;
- Event: THookableEvent;
-begin
- EventName := luaL_checkstring(L, 1);
- FunctionName := luaL_checkstring(L, 2);
-
- P := Lua_GetOwner(L);
-
- lua_pop(L, lua_gettop(L)); //clear stack
-
- Result := 1;
-
- Event := LuaCore.GetEventByName(EventName);
- if (Event <> nil) then
- begin
- Event.Hook(L, P.Id, FunctionName);
- end
- else
- luaL_error(L, PChar('event does not exist: ' + EventName));
-end;
-
-function ULuaUsdx_ShutMeDown(L: Plua_State): Integer; cdecl;
- var
- top: Integer;
- P: TLuaPlugin;
-begin
- Result := 0;
-
- //remove arguments (if any)
- top := lua_gettop(L);
-
- if (top > 0) then
- lua_pop(L, top);
-
- P := Lua_GetOwner(L);
-
- P.ShutMeDown;
-end;
-
+{* 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 ULuaUsdx;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses ULua;
+
+{ some basic lua c functions from usdx table }
+
+{ Usdx.Time - returns sdl_time to have time numbers comparable with
+ ultrastar deluxe ones. no arguments }
+function ULuaUsdx_Time(L: Plua_State): Integer; cdecl;
+
+{ Usdx.Version - returns Usdx version string (the same that US_Version
+ language-constant does). no arguments }
+function ULuaUsdx_Version(L: Plua_State): Integer; cdecl;
+
+{ Usdx.Hook - returns an hook table with name and Unhook function
+ arguments: event_name: string }
+function ULuaUsdx_Hook(L: Plua_State): Integer; cdecl;
+
+{ Usdx.ShutMeDown - no results, no arguments
+ unloads the calling plugin }
+function ULuaUsdx_ShutMeDown(L: Plua_State): Integer; cdecl;
+
+const
+ ULuaUsdx_Lib_f: array [0..4] of lual_reg = (
+ (name:'Version'; func:ULuaUsdx_Version),
+ (name:'Time'; func:ULuaUsdx_Time),
+ (name:'Hook'; func:ULuaUsdx_Hook),
+ (name:'ShutMeDown'; func:ULuaUsdx_ShutMeDown),
+ (name:nil;func:nil)
+ );
+
+implementation
+uses SDL, ULuaCore, ULuaUtils, UHookableEvent, UConfig;
+
+{ Usdx.Time - returns sdl_time to have time numbers comparable with
+ ultrastar deluxe ones. no arguments }
+function ULuaUsdx_Time(L: Plua_State): Integer; cdecl;
+ var top: Integer;
+begin
+ //remove arguments (if any)
+ top := lua_gettop(L);
+
+ if (top > 0) then
+ lua_pop(L, top);
+
+ //push result
+ lua_pushinteger(L, SDL_GetTicks);
+ Result := 1; //one result
+end;
+
+{ Usdx.Version - returns Usdx version string (the same that US_Version
+ language-constant does). no arguments }
+function ULuaUsdx_Version(L: Plua_State): Integer; cdecl;
+ var top: Integer;
+begin
+ //remove arguments (if any)
+ top := lua_gettop(L);
+
+ if (top > 0) then
+ lua_pop(L, top);
+
+ //push result
+ lua_pushstring(L, PChar(USDXVersionStr()));
+ Result := 1; //one result
+end;
+
+{ Usdx.Hook - returns an hook table with name and Unhook function
+ arguments: event_name: string; function_name: string }
+function ULuaUsdx_Hook(L: Plua_State): Integer; cdecl;
+var
+ EventName: String;
+ FunctionName: String;
+ P: TLuaPlugin;
+ Event: THookableEvent;
+begin
+ EventName := luaL_checkstring(L, 1);
+ FunctionName := luaL_checkstring(L, 2);
+
+ P := Lua_GetOwner(L);
+
+ lua_pop(L, lua_gettop(L)); //clear stack
+
+ Result := 1;
+
+ Event := LuaCore.GetEventByName(EventName);
+ if (Event <> nil) then
+ begin
+ Event.Hook(L, P.Id, FunctionName);
+ end
+ else
+ luaL_error(L, PChar('event does not exist: ' + EventName));
+end;
+
+function ULuaUsdx_ShutMeDown(L: Plua_State): Integer; cdecl;
+ var
+ top: Integer;
+ P: TLuaPlugin;
+begin
+ Result := 0;
+
+ //remove arguments (if any)
+ top := lua_gettop(L);
+
+ if (top > 0) then
+ lua_pop(L, top);
+
+ P := Lua_GetOwner(L);
+
+ P.ShutMeDown;
+end;
+
end. \ No newline at end of file
diff --git a/src/lua/ULuaUtils.pas b/src/lua/ULuaUtils.pas
index 143b34d4..92122152 100644
--- a/src/lua/ULuaUtils.pas
+++ b/src/lua/ULuaUtils.pas
@@ -1,186 +1,186 @@
-{* 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 ULuaUtils;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses ULua, ULuaCore;
-
-{ converts a lua table with a structure like:
- * = 1 , * = 4 , * = 5
- to an integer with the value:
- 0b11001
- does not pop anything }
-function Lua_ToBinInt(L: PLua_State; idx: Integer): Integer;
-
-{ converts an integer with the value:
- 0b11001
- to a lua table with a structure like:
- * = 1 , * = 4 , * = 5
- and pushed the table onto the stack }
-procedure Lua_PushBinInt(L: PLua_State; BinInt: Integer);
-
-{ pushes a table with position and size of a rectangle
- t.x => position of the rectangle in pixels at x-axis
- t.y => position of the rectangle in pixels at y-axis
- t.w => width of the rectangle
- t.h => height of the rectangle }
-procedure Lua_PushRect(L: PLua_State; X, Y, W, H: Double);
-
-{ returns plugin that is the owner of the given state
- may raise a lua error if the parent id is not found
- in states registry, if state owner does not exists
- or is not loaded. So a check for a nil value is not
- necessary }
-function Lua_GetOwner(L: PLua_State): TLuaPlugin;
-
-{ this is a helper in case an evenet owner don't has no use for the results
- returns number of popped elements }
-function Lua_ClearStack(L: Plua_State): Integer;
-
-
-implementation
-
-{ converts a lua table with a structure like:
- * = 1 , * = 4 , * = 5
- to an integer with the value:
- 0b11001
- does not pop anything }
-function Lua_ToBinInt(L: PLua_State; idx: Integer): Integer;
- var
- I: Integer;
-begin
- // default: no bits set
- Result := 0;
-
- lua_checkstack(L, 2);
-
- if (idx < 0) then
- dec(idx); // we will push one value before using this
-
- lua_PushNil(L);
- while (lua_next(L, idx) <> 0) do
- begin
- if (lua_isNumber(L, -1)) then
- begin //check if we got an integer value from 1 to 32
- I := lua_toInteger(L, -1);
- if (I >= 1) and (I <= 32) then
- Result := Result or 1 shl (I - 1);
- end;
-
- // pop value, so key is on top
- lua_pop(L, 1);
- end;
-end;
-
-{ converts an integer with the value:
- 0b11001
- to a lua table with a structure like:
- * = 1 , * = 4 , * = 5
- and pushed the table onto the stack }
-procedure Lua_PushBinInt(L: PLua_State; BinInt: Integer);
-var
- I, Index: Integer;
-begin
- lua_newTable(L);
-
-
- Index := 1; //< lua starts w/ index 1
- for I := 0 to 31 do
- if (BinInt and (1 shl I) <> 0) then
- begin
- lua_pushInteger(L, Index);
- lua_pushInteger(L, I);
- lua_settable(L, -3);
-
- Inc(Index);
- end;
-end;
-
-{ pushes a table with position and size of a rectangle
- t.x => position of the rectangle in pixels at x-axis
- t.y => position of the rectangle in pixels at y-axis
- t.w => width of the rectangle
- t.h => height of the rectangle }
-procedure Lua_PushRect(L: PLua_State; X, Y, W, H: Double);
-begin
- lua_createtable(L, 0, 4); // table w/ 4 record fields
-
- // x pos
- lua_pushNumber(L, X);
- lua_setField(L, -2, 'x');
-
- // y pos
- lua_pushNumber(L, Y);
- lua_setField(L, -2, 'y');
-
- // width
- lua_pushNumber(L, W);
- lua_setField(L, -2, 'w');
-
- // height
- lua_pushNumber(L, H);
- lua_setField(L, -2, 'h');
-end;
-
-{ returns plugin that is the owner of the given state
- may raise a lua error if the parent id is not found
- in states registry, if state owner does not exists
- or is not loaded. So a check for a nil value is not
- necessary }
-function Lua_GetOwner(L: PLua_State): TLuaPlugin;
-begin
- lua_checkstack(L, 1);
-
- lua_getfield (L, LUA_REGISTRYINDEX, '_USDX_STATE_ID');
- if (not lua_isNumber(L, -1)) then
- luaL_error(L, 'unable to get _USDX_STATE_ID');
-
- Result := LuaCore.GetPluginById(lua_toInteger(L, -1));
-
- lua_pop(L, 1); //< remove state id from stack
-
- if (Result = nil) then
- luaL_error(L, '_USDX_STATE_ID has invalid value')
- else if (Result.Status > psRunning) then
- luaL_error(L, 'owning plugin is not loaded or already unloaded in Lua_GetOwner');
-end;
-
-{ this is a helper in case an evenet owner don't has no use for the results
- returns number of popped elements }
-function Lua_ClearStack(L: Plua_State): Integer;
-begin
- Result := lua_gettop(L);
- lua_pop(L, Result);
-end;
-
+{* 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 ULuaUtils;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses ULua, ULuaCore;
+
+{ converts a lua table with a structure like:
+ * = 1 , * = 4 , * = 5
+ to an integer with the value:
+ 0b11001
+ does not pop anything }
+function Lua_ToBinInt(L: PLua_State; idx: Integer): Integer;
+
+{ converts an integer with the value:
+ 0b11001
+ to a lua table with a structure like:
+ * = 1 , * = 4 , * = 5
+ and pushed the table onto the stack }
+procedure Lua_PushBinInt(L: PLua_State; BinInt: Integer);
+
+{ pushes a table with position and size of a rectangle
+ t.x => position of the rectangle in pixels at x-axis
+ t.y => position of the rectangle in pixels at y-axis
+ t.w => width of the rectangle
+ t.h => height of the rectangle }
+procedure Lua_PushRect(L: PLua_State; X, Y, W, H: Double);
+
+{ returns plugin that is the owner of the given state
+ may raise a lua error if the parent id is not found
+ in states registry, if state owner does not exists
+ or is not loaded. So a check for a nil value is not
+ necessary }
+function Lua_GetOwner(L: PLua_State): TLuaPlugin;
+
+{ this is a helper in case an evenet owner don't has no use for the results
+ returns number of popped elements }
+function Lua_ClearStack(L: Plua_State): Integer;
+
+
+implementation
+
+{ converts a lua table with a structure like:
+ * = 1 , * = 4 , * = 5
+ to an integer with the value:
+ 0b11001
+ does not pop anything }
+function Lua_ToBinInt(L: PLua_State; idx: Integer): Integer;
+ var
+ I: Integer;
+begin
+ // default: no bits set
+ Result := 0;
+
+ lua_checkstack(L, 2);
+
+ if (idx < 0) then
+ dec(idx); // we will push one value before using this
+
+ lua_PushNil(L);
+ while (lua_next(L, idx) <> 0) do
+ begin
+ if (lua_isNumber(L, -1)) then
+ begin //check if we got an integer value from 1 to 32
+ I := lua_toInteger(L, -1);
+ if (I >= 1) and (I <= 32) then
+ Result := Result or 1 shl (I - 1);
+ end;
+
+ // pop value, so key is on top
+ lua_pop(L, 1);
+ end;
+end;
+
+{ converts an integer with the value:
+ 0b11001
+ to a lua table with a structure like:
+ * = 1 , * = 4 , * = 5
+ and pushed the table onto the stack }
+procedure Lua_PushBinInt(L: PLua_State; BinInt: Integer);
+var
+ I, Index: Integer;
+begin
+ lua_newTable(L);
+
+
+ Index := 1; //< lua starts w/ index 1
+ for I := 0 to 31 do
+ if (BinInt and (1 shl I) <> 0) then
+ begin
+ lua_pushInteger(L, Index);
+ lua_pushInteger(L, I);
+ lua_settable(L, -3);
+
+ Inc(Index);
+ end;
+end;
+
+{ pushes a table with position and size of a rectangle
+ t.x => position of the rectangle in pixels at x-axis
+ t.y => position of the rectangle in pixels at y-axis
+ t.w => width of the rectangle
+ t.h => height of the rectangle }
+procedure Lua_PushRect(L: PLua_State; X, Y, W, H: Double);
+begin
+ lua_createtable(L, 0, 4); // table w/ 4 record fields
+
+ // x pos
+ lua_pushNumber(L, X);
+ lua_setField(L, -2, 'x');
+
+ // y pos
+ lua_pushNumber(L, Y);
+ lua_setField(L, -2, 'y');
+
+ // width
+ lua_pushNumber(L, W);
+ lua_setField(L, -2, 'w');
+
+ // height
+ lua_pushNumber(L, H);
+ lua_setField(L, -2, 'h');
+end;
+
+{ returns plugin that is the owner of the given state
+ may raise a lua error if the parent id is not found
+ in states registry, if state owner does not exists
+ or is not loaded. So a check for a nil value is not
+ necessary }
+function Lua_GetOwner(L: PLua_State): TLuaPlugin;
+begin
+ lua_checkstack(L, 1);
+
+ lua_getfield (L, LUA_REGISTRYINDEX, '_USDX_STATE_ID');
+ if (not lua_isNumber(L, -1)) then
+ luaL_error(L, 'unable to get _USDX_STATE_ID');
+
+ Result := LuaCore.GetPluginById(lua_toInteger(L, -1));
+
+ lua_pop(L, 1); //< remove state id from stack
+
+ if (Result = nil) then
+ luaL_error(L, '_USDX_STATE_ID has invalid value')
+ else if (Result.Status > psRunning) then
+ luaL_error(L, 'owning plugin is not loaded or already unloaded in Lua_GetOwner');
+end;
+
+{ this is a helper in case an evenet owner don't has no use for the results
+ returns number of popped elements }
+function Lua_ClearStack(L: Plua_State): Integer;
+begin
+ Result := lua_gettop(L);
+ lua_pop(L, Result);
+end;
+
end. \ No newline at end of file