vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2  
3 Sea Hooks (mostly Sea.util)
4  
5 A Mini-Library for standardized function and frame script element hooks.
6  
7 Compiler:
8 AnduinLothar (karlkfi@cosmosui.org)
9  
10 Contributors:
11 Thott (thott@thottbot.com)
12 Legorol (legorol@cosmosui.org)
13 Mugendai (mugekun@gmail.com)
14  
15  
16 ==Installation/Utilization==
17  
18 Embedding:
19 - Drop the SeaHooks folder into your Interface\AddOns\YourAddon\ folder
20 - Add Sea and SeaHooks as optional dependancies
21 - Add the following line to the end of your TOC file, before your addon files:
22 SeaHooks\SeaHooks.lua
23  
24 Standard:
25 - Drop the SeaHooks folder into your Interface\AddOns\ directory
26 - Add SeaHooks a required dependancy
27  
28  
29 Change Log:
30 v0.6
31 - Fixed bug preventing propper unhooking
32 - Synced with Sea v1.11
33 v0.5
34 - Only one replace hook is now called for each hook call, unless it returns true as arg1
35 - Old style table passing of return arguments as a table is again honored, but not encouraged. All other return value must be nil except fro arg1 (non-nil) and arg2 (argument table)
36 v0.4 - Optimized split, getValue and setValue for speed and GC (thanks Iriel and krka) - Removed cleanArgs. Slow and pointless. - Changed internal SeaHooks_debugErrorPrint syntax to match Sea.io.dprintfc syntax for easy conversion to Sea - Code Synced with Sea 1.07 v0.3 - Fixed Bug: causing no return from hooked functions where no return replacement was supplied - Fixed Oversight: causing a return to Frame Script Elements (which don't take any) - Fixed Oversight: sloppy Sea.util.unhook mirroring - Hook definitions now use an object refrence rather than a name to save on indexing (Thanks Iriel for suggestion) - Added Feature: You can now modify return values from either a 'replace' that hides the orig func or an 'after' hook - Added Feature: Added Error Debug and Vebose Debug by hook function name /script Sea.util.debugHooks(enabled, verboseHookName) enabled - boolean, verboseHookName - string equal to 'orig' passed to hook function (nil to disable) Note: enabling debug usually incurs a heavier proc load and can cause slow down when used with OnUpdate hooks. - Added Feature: current return values from a hooked function are availible to 'after' hooks via global return variables: Sea.util.returnArgs[1-20] Note: These arguments are only availible for the durration of the 'after' hook call. Also, if you call any function within the 'after' hook that would lead to the call of another 'after' hook call then the global Sea.util.returnArgs would most likely change. Thus it is highly recommend you grab whatever return arguments you need at the beginning of the function call and assign them to local variables. This method preserves reverse compatibility as well as avoids table creation and thus does not effect GC. You can also use Sea.util.getReturnArgs() to unpack them for you. Ex: local arg1, arg2 = Sea.util.getReturnArgs(); - Cleaned up comments and code readability - Added Feature: Sea.util.cleanArgs eliminates trailing nils in a list of arguments. Used in the hook functions to mask the 20 arguments utilized to avoid GC. Ex: Sea.util.cleanArgs("a", nil, "b", nil, nil) == "a", nil, "b" - Now stores a refrence of the hooked function handler in the database to facilitate future overhook recognition
37 v0.2 - Added input argument modification using a 'before' hook. If you return true as the the first arg from a before hook function the subsequent arguments will be the arguments fed to each hook function called afterwards as well as the orig function. EX: if your before hook returned (true, nil, 10) then the next hook would be called as func(nil, 10), as would the orig(nil, 10) This allows you to modify a function output without destroying or hiding the orig function, allowing additional hooks to be called without conflicting functionality.
38 v0.1 (Alpha) - SeaHooks Forked into Mini-Library from the main Sea. Still backwards compatible.
39
40
41 $LastChangedBy: karlkfi $
42 $Rev: 2577 $
43 $Date: 2005-10-10 14:44:01 -0700 (Mon, 10 Oct 2005) $
44 ]]--
45
46 local SEA_HOOKS_VERSION = 0.60;
47 local loadThisEmbeddedInstance;
48 local SeaHooks_hookInit, SeaHooks_getDynamicHookHandler, SeaHooks_hookHandler, SeaHooks_hookHandlerQuick, SeaHooks_hookHandlerDebug, SeaHooks_assignReturnArgs, SeaHooks_debugErrorPrint;
49 SEA_HOOKS_DEBUG = nil;
50 SEA_HOOKS_DEBUG_VERBOSE = nil;
51
52 ------------------------------------------------------------------------------
53 --[[ Embedded Sub-Library Load Algorithm ]]--
54 ------------------------------------------------------------------------------
55
56 if(not Sea) then
57 Sea = {};
58 Sea.versions = {};
59 Sea.versions.SeaHooks = SEA_HOOKS_VERSION;
60 loadThisEmbeddedInstance = true;
61 Sea.util = {};
62 Sea.util.Hooks = {};
63 Sea.util.valueTable = {};
64 Sea.util.returnArgs = {};
65 else
66 if(not Sea.versions) then
67 Sea.versions = {};
68 end
69  
70  
71 loadThisEmbeddedInstance = true;
72 end
73 if(not Sea.util) then
74 Sea.util = {};
75  
76 if(not Sea.util.Hooks) then
77 --Is preserved from any previously loaded embedded SeaHook to so that OnLoad triggered hooks from already loaded addons are not lost.
78 Sea.util.Hooks = {};
79 end
80 if ( not Sea.util.valueTable ) then
81  
82 end
83 if ( not Sea.util.returnArgs ) then
84 Sea.util.returnArgs = {};
85  
86 end
87
88 if (loadThisEmbeddedInstance) then
89 loadThisEmbeddedInstance = nil;
90
91
92 ------------------------------------------------------------------------------
93 --[[ Function and Frame Script Element Hooking - User Functions ]]--
94 ------------------------------------------------------------------------------
95
96 --
97 -- Sea.util.hook( string originalFunctionNameOrFrameName, string newFunction, string hooktype, string scriptElementName )
98 --
99 -- Hooks a function.
100 --
101 -- Example:
102 -- Sea.util.hook("some_function","my_function","hide|before|replace|after");
103 -- Sea.util.hook("some_frame_name","my_function","hide|before|replace|after", "some_script_element_name");
104 --
105 -- Hook types : -- "hide" - call "my_function" instead of "some_function". If you return true, subsequent hooks will be called afterwards, otherwise no further hooks nor the orig function will be called. -- "before" - call "my_function" before "some_function". If you return true, the subsequent args will be fed into the calls of the orig function as well as any other functions that hook "some_function". -- "replace" - call instead of "some_function". If you return true, the orig function will be called afterwards, otherwise subsequent args will be returned by the hooked function call. -- "after" - called after "some_function". If you return true subsequent args will be returned by the hooked function call.
106 --
107 --
108 -- Written by Thott (thott@thottbot.com)
109 -- Rewritten by AnduinLothar (karlkfi@cosmosui.org)
110 Sea.util.hook = function ( orig, new, hooktype, scriptElementName )
111 if(not hooktype) then
112 hooktype = "before";
113 end
114 local compoundOrig = orig;
115 if (scriptElementName) then
116 compoundOrig = orig.."."..scriptElementName;
117  
118 SeaHooks_debugErrorPrint((SEA_HOOKS_DEBUG and (SEA_HOOKS_DEBUG_VERBOSE==compoundOrig)), nil, NORMAL_FONT_COLOR, "SeaHooks Progress: Hooking ", orig, " to ", new, ", hooktype ", hooktype, ", scriptElementName ", scriptElementName);
119 local newFunc = new;
120  
121  
122 end
123
124 local hookObj = Sea.util.Hooks[compoundOrig];
125  
126 hookObj = SeaHooks_hookInit(orig, compoundOrig, scriptElementName);
127 else
128 for key,value in hookObj[hooktype] do
129 -- NOTE THIS SHOULD BE VALUE! VALUE! *NOT* KEY! (checking if the functions are the same, even if the names are different)
130 -- If the function is found previously inserted, it will not rehook
131 -- If the function is not found, the new function will be inserted at the end
132 if(value == newFunc) then
133 SeaHooks_debugErrorPrint((SEA_HOOKS_DEBUG and (SEA_HOOKS_DEBUG_VERBOSE==compoundOrig)), nil, NORMAL_FONT_COLOR, "SeaHooks Progress: Already hooked '", compoundOrig, "' with '", new, "' skipping.");
134 return;
135 end
136 end
137 end
138 -- intentionally will error if bad hooktype is passed
139 local currKey = table.getn(hookObj[hooktype])+1;
140 table.insert(hookObj[hooktype], currKey, newFunc);
141 hookObj.count = hookObj.count + 1; --increment hook counter
142
143 -- Adds a ["#Info"] table for preserving reverse compatibility while passing the frame name of the frame that called the hook, for debug.
144 local infoKey = currKey.."Info";
145 local embeddedTable = hookObj[hooktype][infoKey];
146 if (type(embeddedTable) ~= "table") then
147 embeddedTable = {};
148 end
149 if (this) and (this:GetName()) then
150 embeddedTable.parent = this:GetName();
151 else
152 embeddedTable.parent = 'unknown';
153 end
154 embeddedTable.name = new;
155 hookObj[hooktype][infoKey] = embeddedTable; --repackage current hook info into hookObj
156
157  
158 end
159
160 --
161 -- Sea.util.unhook( string originalFunctionNameOrFrameName, string newFunction, string hooktype, string scriptElementName )
162 --
163 -- Unhooks a function
164 --
165 -- Example:
166 -- Sea.util.unhook("some_blizzard_function","my_function","before|after|hide|replace");
167 -- Sea.util.unhook("some_frame_name","my_function","before|after|hide|replace", "some_script_element_name");
168 --
169 -- This will remove a function hooked by Sea.util.hook.
170 --
171 -- Written by Thott (thott@thottbot.com)
172 -- Rewritten by AnduinLothar (karlkfi@cosmosui.org)
173 Sea.util.unhook = function ( orig, new, hooktype, scriptElementName )
174 if(not hooktype) then
175 hooktype = "before";
176  
177 local compoundOrig = orig;
178 if (scriptElementName) then
179 compoundOrig = orig.."."..scriptElementName;
180 end
181 SeaHooks_debugErrorPrint((SEA_HOOKS_DEBUG and (SEA_HOOKS_DEBUG_VERBOSE==compoundOrig)), nil, NORMAL_FONT_COLOR, "SeaHooks Progress: Unhooking ", orig, " to ", new, ", hooktype ", hooktype, ", scriptElementName ", scriptElementName);
182 local newFunc = new;
183 if ( type(new) ~= "function" ) then
184 newFunc = Sea.util.getValue(new);
185 end
186 local hookObj = Sea.util.Hooks[compoundOrig];
187 if(not hookObj) then
188 hookObj = SeaHooks_hookInit(orig, compoundOrig, scriptElementName);
189 else
190  
191 for key,value in hookObj[hooktype] do
192 -- NOTE THIS SHOULD BE VALUE! VALUE! *NOT* KEY! (checking if the functions are the same, even if the names are different)
193  
194 if(value == newFunc) then
195 foundIt = true;
196 break;
197 --exit loop since found hook
198 end
199 end
200 if (not foundIt) then
201 SeaHooks_debugErrorPrint((SEA_HOOKS_DEBUG and (SEA_HOOKS_DEBUG_VERBOSE==compoundOrig)), nil, NORMAL_FONT_COLOR, "SeaHooks Progress: '", compoundOrig, "' not hooked with '", new, "' skipping.");
202 --hooked function not found so nothing to do
203 return;
204 end
205 end
206 local info = hookObj[hooktype]; --Sea.util.Hooks[compoundOrig][hooktype]
207 for key,value in info do
208 if (type(value) == "function") and (value == newFunc) then
209 info[key] = nil;
210 local embeddedTable = info[key.."Info"];
211 if (type(embeddedTable) == "table") then
212 embeddedTable.parent = nil;
213 embeddedTable.name = nil;
214 end
215 info[key.."Info"] = embeddedTable;
216 hookObj[hooktype] = info;
217 hookObj.count = hookObj.count - 1; --decrement hook counter
218 Sea.util.Hooks[compoundOrig] = hookObj --repackage all hook types
219 SeaHooks_debugErrorPrint((SEA_HOOKS_DEBUG and (SEA_HOOKS_DEBUG_VERBOSE==compoundOrig)), nil, NORMAL_FONT_COLOR, "SeaHooks Progress: Found and unhooked '", new, "' from '", compoundOrig, "'.");
220 return;
221 end
222 end
223 -- No Complete Unhooking - Incompatible with Frame Script Element Hooks - Also liable to erase function hooks loaded after the first hook.
224
225 Sea.util.Hooks[compoundOrig] = hookObj --repackage all hook types
226 end
227
228 --
229 -- Sea.util.getReturnArgs()
230 --
231 -- Get the current return values of a hooked function from within an 'after' hook.
232 --
233 -- Example:
234 -- local arg1, arg2 = Sea.util.getReturnArgs();
235 --
236 -- This will return nil if called outside of an 'after' hook.
237 -- Also, if you call any function within the 'after' hook that would lead to the call of another 'after' hook call then the global Sea.util.returnArgs would most likely change.
238 -- Thus it is highly recommend you grab whatever return arguments you need at the beginning of the function call and assign them to local variables.
239 -- This method preserves reverse compatibility as well as avoids table creation and thus does not effect GC.
240 --
241 -- Written by AnduinLothar (karlkfi@cosmosui.org)
242 Sea.util.getReturnArgs = function()
243 return unpack(Sea.util.returnArgs);
244 end
245
246 --
247 -- Sea.util.debugHooks( boolean enable , string verboseFunctionName )
248 --
249 -- Enable standard or verbose error logging. (Prints to the default chat frame)
250 --
251 -- Examples:
252 -- On: Sea.util.debugHooks(1);
253 -- Verbose: Sea.util.debugHooks(1, "ChatFrame_OnLoad");
254 -- Off: Sea.util.debugHooks();
255 --
256 -- Args:
257 -- (boolean) enable - true/false
258  
259 --
260 -- /script Sea.util.debugHooks(enabled, verboseHookName)
261  
262 -- Note: enabling debug usually incurs a heavier proc load and can cause slow down when used with OnUpdate hooks.
263 --
264 -- Written by AnduinLothar (karlkfi@cosmosui.org),
265 Sea.util.debugHooks = function ( enable, verboseFunctionName )
266 if (enable) then
267 SEA_HOOKS_DEBUG = true;
268 if (verboseFunctionName) then
269 SEA_HOOKS_DEBUG_VERBOSE = verboseFunctionName;
270 else
271 SEA_HOOKS_DEBUG_VERBOSE = nil;
272 end
273 else
274 SEA_HOOKS_DEBUG = nil;
275 SEA_HOOKS_DEBUG_VERBOSE = nil;
276 end
277 end
278
279  
280 --[[ String Parsing - User Functions ]]--
281 ------------------------------------------------------------------------------
282
283 --
284 -- Sea.util.split(string text, string separator [, table oldTable [, boolean noPurge] ] )
285 --
286 -- Efficiently splits a string into a table by separators
287 --
288 -- Args:
289 -- (string text, string separator, table oldTable, boolean noPurge)
290 -- text - string containing input
291 -- separator - separators
292 -- oldTable (optional) - table to fill with the results
293 -- noPurge (optional) - do not clear extraneous entries in oldTable
294 --
295 -- Returns:
296 -- (table)
297 -- table - the table containing the exploded strings, which is freshly
298 -- created if oldTable wasn't passed
299 --
300 -- Aliases:
301 -- Sea.string.split
302 --
303 -- Notes:
304 -- In the interests of avoiding garbage generation, whenever possible pass
305 -- a table for split to reuse. Also, dont use [ or % in the separator as it
306 -- will conflict with the regex.
307 --
308 -- Written by Thott (thott@thottbot.com)
309 -- Modified by Legorol (legorol@cosmosui.org)
310 -- Optimized by AnduinLothar (with suggestions from Iriel and krka)
311 Sea.util.split = function ( text, separator, t, noPurge )
312  
313 local mstart, mend = 1;
314 local oldn, numMatches = 0, 0;
315 local regexKey = "([^"..separator.."]+)";
316  
317
318 if ( not t ) then
319 t = {};
320 else
321 oldn = table.getn(t);
322 end
323
324 -- Using string.find instead of string.gfind to avoid garbage generation
325 mstart, mend, value = sfind(text, regexKey, mstart);
326 while (value) do
327 numMatches = numMatches + 1;
328 t[numMatches] = value
329 mstart = mend + 1;
330 mstart, mend, value = sfind(text, regexKey, mstart);
331 end
332
333 if ( not noPurge ) then
334 for i = numMatches+1, oldn do
335 t[i] = nil;
336 end
337 end
338
339 table.setn(t, numMatches);
340
341 return t;
342 end
343
344 -- Aliasing
345 if (not Sea.string) then
346 Sea.string = {};
347 end
348 Sea.string.split = Sea.util.split;
349 Sea.string.explode = Sea.util.split;
350
351  
352 ------------------------------------------------------------------------------
353 --[[ Indexed Variable Referencing - User Functions ]]--
354 ------------------------------------------------------------------------------
355
356 --
357  
358 --
359 -- Obtains the value of a variable given its name.
360 --
361 -- Examples:
362 -- Sea.util.getValue("ChatFrame_OnLoad");
363 -- Sea.util.getValue("Class.subclass.element");
364 --
365 -- Args:
366  
367 --
368 -- Returns:
369 -- value - the value that variable has
370 --
371 -- This function obtains the value that variableName contains.
372  
373 -- the element of a table. If variableName doesn't exist, it returns nil.
374  
375 -- Concept by Mugendai
376 -- Written by Legorol (legorol@cosmosui.org)
377  
378 Sea.util.getValue = function ( variableName )
379 if ( type(variableName) ~= "string" ) then
380 return;
381 end
382
383 local sfind = strfind;
384  
385  
386 local sstart = 2;
387 local value;
388 -- Split the variable name at ".", first field is a global name
389  
390 if ( match ) then
391 value = getglobal(strsub(variableName, 0, match-1));
392 else
393 return getglobal(variableName);
394 end
395
396 while true do
397 if (type(value) ~= "table") then
398 -- Returns nil rather than trying to index a non-table
399 return;
400 end
401 sstart = match + 1;
402 match = sfind(variableName, '.', sstart, true);
403
404 if ( match ) then
405 -- next one (there are more)
406 value = value[strsub(variableName, sstart, match-1)];
407 else
408 -- last one
409 return value[strsub(variableName, sstart)];
410 end
411 end
412 end
413
414 --
415 -- Sea.util.setValue( string variableName, value )
416  
417 -- Sets the value of a variable given its name.
418 --
419  
420 -- Sea.util.setValue("ChatFrame_OnLoad", MyChatFrame_OnLoad);
421 -- Sea.util.setValue("Class.subclass.element", 5);
422 -- Sea.util.setValue("Class.subclass.function", function() dostuff; end);
423 -- Args:
424 -- (string) variableName - the name of the variable to change
425 -- value - the new value of the variable
426 --
427 -- Returns:
428 -- (boolean) success - true if the operation succeeded
429  
430 -- This function sets the value of variableName.
431 -- It is able to set the value for both a global variable or for
432 -- the element of a table, including functions. If variableName
433 -- already exists, it is overwritten.
434 --
435 -- Concept by Mugendai
436 -- Written by Legorol (legorol@cosmosui.org)
437  
438 Sea.util.setValue = function ( variableName, newValue )
439 if ( type(variableName) ~= "string" ) then
440 return;
441 end
442
443 local sfind = strfind;
444 local strsub = strsub;
445
446 local sstart = 2;
447  
448 -- Split the variable name at ".", first field is a global name
449 local match = sfind(variableName, '.', sstart, true);
450 if ( match ) then
451 value = getglobal(strsub(variableName, 0, match-1));
452 else
453 setglobal(variableName, newValue);
454 return true;
455 end
456
457 while true do
458 if (type(value) ~= "table") then
459 -- Returns nil rather than trying to index a non-table
460 return false;
461 end
462 sstart = match + 1;
463 match = sfind(variableName, '.', sstart, true);
464
465 if ( match ) then
466 -- next one (there are more)
467 value = value[strsub(variableName, sstart, match-1)];
468 else
469 -- last one
470 value[strsub(variableName, sstart)] = newValue;
471 return true;
472 end
473 end
474
475 -- Error occured, subtable is not a table
476  
477 end
478
479  
480 ------------------------------------------------------------------------------
481 --[[ Function and Frame Script Element Hooking - Internal Functions ]]--
482 ------------------------------------------------------------------------------
483
484 --
485 -- Hook Initialization
486 --
487 -- Create a database instantiation the first time a hook is registered for a function. Stores the original function and establishes hook tables.
488 --
489 -- Written by AnduinLothar (karlkfi@cosmosui.org)
490  
491 SeaHooks_debugErrorPrint((SEA_HOOKS_DEBUG and (SEA_HOOKS_DEBUG_VERBOSE==compoundOrig)), nil, NORMAL_FONT_COLOR, "SeaHooks Progress: Hook Init - storing '", compoundOrig, "' orig and replacing with hookHandler.");
492 local hookObj = {
493 name = compoundOrig;
494 count = 0;
495 before = {};
496 after = {};
497 hide = {};
498  
499 };
500 -- Set up the hook the first time
501 if (scriptElementName) then
502 local origFrame = Sea.util.getValue(orig);
503 hookObj.orig = origFrame:GetScript(scriptElementName);
504 hookObj.hookFunction = SeaHooks_getDynamicHookHandler(compoundOrig);
505 origFrame:SetScript(scriptElementName, hookObj.hookFunction);
506 Sea.util.setValue(orig, origFrame); --Reasign refrenced and modified origFrame
507 else
508  
509 hookObj.hookFunction = SeaHooks_getDynamicHookHandler(compoundOrig);
510 Sea.util.setValue(orig, hookObj.hookFunction);
511 end
512  
513  
514
515 SeaHooks_getDynamicHookHandler = function ( databaseID )
516 return function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) return SeaHooks_hookHandler(databaseID,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) end;
517  
518
519 --
520 -- Hook Handler
521 --
522 -- Handles the name and the argument table.
523 -- An instantiated copy of this function is set to all hooked functions, passing the name of the original function staticly and any arguments dynamicly.
524 --
525 -- Written by Thott (thott@thottbot.com)
526 -- Rewritten by AnduinLothar (karlkfi@cosmosui.org)
527 SeaHooks_hookHandler = function (hookInfo,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
528 hookInfo = Sea.util.Hooks[hookInfo] --hookInfo passed in as string, exported as table on demand.
529 if (SEA_HOOKS_DEBUG) then
530 return SeaHooks_hookHandlerDebug(hookInfo,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
531 end
532 return SeaHooks_hookHandlerQuick(hookInfo,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
533 end
534
535 --Quick (non-debug) Hook Handler. Called for each hook function to iterate over hook functions and original.
536 SeaHooks_hookHandlerQuick = function (hookObj,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
537 if (type(hookObj) ~= "table") then
538 hookObj = Sea.util.Hooks[hookObj];
539 if (not hookObj) then
540 return;
541 end;
542 end
543 if (hookObj.count == 0) then
544 if (hookObj.orig) then
545 return hookObj.orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
546 end
547 return;
548  
549 local ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20; --return args
550 local toggle;
551 for key, value in hookObj.hide do
552  
553 toggle,ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
554 if(not toggle) then
555 return ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20;
556 end
557 end
558 end
559 local ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20; --temp args
560 for key, value in hookObj.before do
561 if(type(value) == "function") then
562 toggle,ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
563 if(toggle) then
564 a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20 = ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20;
565 end
566 end
567 end
568  
569 for key, value in hookObj.replace do
570 if(type(value) == "function") then
571 toggle,ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
572 if(not toggle) then
573 ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20;
574 break;
575 end
576 end
577 end
578 if (toggle) and (hookObj.orig) then
579 ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = hookObj.orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
580 end
581 for key,value in hookObj.after do
582 if(type(value) == "function") then
583 SeaHooks_assignReturnArgs(true,ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20);
584 toggle,ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
585 SeaHooks_assignReturnArgs();
586 if(toggle) then
587 ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20;
588 end
589 end
590 end
591 if ( (type(ra1) == "table") and ( ( ra2 and ra3 and ra4 and ra5 and ra6 and ra7 and ra8 and ra9 and ra10 and ra11 and ra12 and ra13 and ra14 and ra15 and ra16 and ra17 and ra18 and ra19 and ra20 ) == nil ) ) then
592 return unpack(ra1);
593 end
594 return ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20;
595 end
596
597 --Debug Hook Handler. Called for each hook function to iterate over hook functions and original.
598 SeaHooks_hookHandlerDebug = function (hookObj,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
599 --assumes SEA_HOOKS_DEBUG
600 local name, parent, hookFuncName;
601 if (type(hookObj) ~= "table") then
602 name = hookObj;
603 hookObj = Sea.util.Hooks[hookObj];
604 -- Quick exit since there's nothing to do!
605 if (not hookObj) then
606 SeaHooks_debugErrorPrint(true, nil, RED_FONT_COLOR, "SeaHooks Error: SeaHooks_hookHandler called with no defined hook parameters for '", name, "'.");
607 return;
608 end;
609 else
610 name = hookObj.name or 'unknown';
611 end
612 local debugVerbose = (SEA_HOOKS_DEBUG_VERBOSE) and (SEA_HOOKS_DEBUG_VERBOSE == name);
613 if (hookObj.count == 0) then
614 --Quickly Exit if no hooks exist
615 if (hookObj.orig) then
616 SeaHooks_debugErrorPrint(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: No known hooks for '", name, "'. Calling orig.");
617 return hookObj.orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
618 end
619 SeaHooks_debugErrorPrint(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: No known hooks or orig for '", name, "'. Exiting.");
620 return;
621 end
622 local ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20; --return args
623 local toggle; -- used for first arg returns from hooks
624 -- Itterate over and call 'hide' hooks. If there are none the for loop is skipped.
625 for key, value in hookObj.hide do
626 if(type(value) == "function") then
627 if (type(hookObj.hide[key.."Info"]) == "function") then
628 parent = hookObj.hide[key.."Info"].parent;
629 hookFuncName = hookObj.hide[key.."Info"].name;
630  
631 SeaHooks_debugErrorPrint(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: calling 'hide' hook #", key, ": '", hookFuncName,"' for '", name, "', registered by '", parent,"'.");
632 --toggle (true) used to call orig function
633 toggle,ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
634 if(not toggle) then
635 if (SEA_HOOKS_DEBUG) then
636 SeaHooks_debugErrorPrint(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: 'hide' hook #", key, " for '", name, "' has hidden all subsequent hook and original function calls.");
637 local numHideHooks = table.getn(hookObj.hide);
638 SeaHooks_debugErrorPrint((numHideHooks > key), nil, RED_FONT_COLOR, "SeaHooks Error: detected ", numHideHooks, " 'hide' hooks for '", name, "', one of which has hidden another. This will most likely cause addon conflicts.");
639 end
640 --exit after first hide unless it says to call orig
641 return ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20;
642 end
643 end
644 end
645 local ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20; --temp args
646 -- Itterate over and call 'before' hooks. If there are none the for loop is skipped.
647 for key, value in hookObj.before do
648 if(type(value) == "function") then
649 SeaHooks_debugErrorPrint(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: calling a 'before' hook for '", name, "'.");
650 --toggle (true) used to override the input args
651 toggle,ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
652 if(toggle) then
653 -- Last 'before' hook that modifies input overrides all previous 'before' hooks for input values.
654 -- Keep in mind any previous input modification will modify the input of subsequent 'before' hooks as well as all the subsequent hook and orig function calls
655 a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20 = ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20;
656 SeaHooks_debugErrorPrint(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: 'before' hook #", key, " for '", name, "' has returned argument(s) to be passed to subsequent hook and original function calls.");
657 end
658 end
659 end
660 toggle = true; -- if no 'replace' hooks are called, calls the orig
661 -- Itterate over and call 'replace' hooks. If there are none the for loop is skipped.
662 for key, value in hookObj.replace do
663 if(type(value) == "function") then
664 SeaHooks_debugErrorPrint(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: calling a 'replace' hook for '", name, "'.");
665 --toggle (true) used to call the orig
666 toggle,ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
667 if(not toggle) then
668 -- Last 'replace' hook that modifies output (not toggle) overrides all previous 'replace' hooks for return values.
669 -- Theorhetically it would be nice if 'replace' hooks that fequently returned true to continue were called before ones that didn't.
670 -- That could be done by reordering the 'replace' list. Is it worth it? It might still conflict on first call. It might be better to just inform the debugger and let the programmer unhook and rehook in the order he wants.
671 ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20;
672 --We should only call one replace hook that doesn't request the origional be called. Otherwise we will perform multiple versions of the main function, which would likely be worse, than not calling the extras at all.
673 break;
674 end
675 end
676 end
677 if (toggle) and (hookObj.orig) then --Frame Script Elements do not necissarily have an orig function
678 SeaHooks_debugErrorPrint(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: calling the 'orig' function for '", name, "'.");
679 -- If the 'orig' is called use its return values, overrides any 'replace' return values defined before a final continuing one.
680 ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = hookObj.orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
681 end
682 -- Itterate over and call 'after' hooks. If there are none the for loop is skipped.
683 for key,value in hookObj.after do
684 if(type(value) == "function") then
685 SeaHooks_debugErrorPrint(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: calling an 'after' hook for '", name, "'.");
686 --toggle (true) used to override the return args
687 --Current function return args availible via global pass vars
688 SeaHooks_assignReturnArgs(true,ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20);
689 toggle,ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20 = value(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
690 SeaHooks_assignReturnArgs(); --nil pass vars
691 if(toggle) then
692 -- Last 'after' hook that modifies output overrides all other hooks for return values.
693 ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20 = ta1,ta2,ta3,ta4,ta5,ta6,ta7,ta8,ta9,ta10,ta11,ta12,ta13,ta14,ta15,ta16,ta17,ta18,ta19,ta20;
694 SeaHooks_debugErrorPrint(debugVerbose, nil, NORMAL_FONT_COLOR, "SeaHooks Progress: 'after' hook for '", name, "' has returned argument(s) to be passed to subsequent hook and original function calls.");
695 end
696 end
697 end
698 --Only unpack ra1 if it is the only return argument passed.
699 if ( (type(ra1) == "table") and ( ( ra2 and ra3 and ra4 and ra5 and ra6 and ra7 and ra8 and ra9 and ra10 and ra11 and ra12 and ra13 and ra14 and ra15 and ra16 and ra17 and ra18 and ra19 and ra20 ) == nil ) ) then
700 SeaHooks_debugErrorPrint(SEA_HOOKS_DEBUG, nil, RED_FONT_COLOR, "SeaHooks Error: Return argument #1 is a table for '", name, "'. This can be used to pass return arguments from a replace/after hook. If that is not the intention of your hook then pass an additional non-nil argument to avoid unpacking.");
701 return unpack(ra1);
702 end
703 return ra1,ra2,ra3,ra4,ra5,ra6,ra7,ra8,ra9,ra10,ra11,ra12,ra13,ra14,ra15,ra16,ra17,ra18,ra19,ra20;
704 end
705
706 -- Assigns up to 20 arguments to a global table so as to save on table creation and still allow return argument passing to 'after' hooks
707 -- Written by AnduinLothar (karlkfi@cosmosui.org)
708 SeaHooks_assignReturnArgs = function( toggle,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20 )
709 local temp = Sea.util.returnArgs;
710 if (toggle) then
711 temp[1]=a1;temp[2]=a2;temp[3]=a3;temp[4]=a4;temp[5]=a5;temp[6]=a6;temp[7]=a7;temp[8]=a8;temp[9]=a9;temp[10]=a10;
712 temp[11]=a11;temp[12]=a12;temp[13]=a13;temp[14]=a14;temp[15]=a15;temp[16]=a16;temp[17]=a17;temp[18]=a18;temp[19]=a19;temp[20]=a20;
713 local n=0;
714 for i=1, 20 do
715 if temp[i] then
716 n=i;
717 end
718 end
719 table.setn(temp,n);
720 else
721 for i=1, 20 do
722 temp[i]=nil;
723 end
724 table.setn(temp,0);
725 end
726 Sea.util.returnArgs = temp;
727 end
728
729 -- Quick Sea print bypass for speed. (only makes 1 table) Won't cause as much slow down on debug. But don't try to vebose debug any OnUpdate functions.
730 -- Written by AnduinLothar (karlkfi@cosmosui.org)
731 SeaHooks_debugErrorPrint = function( toggle, frameUnused, color, ... )
732 --frameUnused is a placeholder to match Sea.io.dprintfc syntax
733 if (toggle) then
734 local msg = "";
735 for key, value in arg do
736 if (type(key) == "number") then
737 if ( value == nil ) then
738 msg = msg .. "(nil)";
739  
740 local currType = type(value);
741 if (currType == "boolean" ) then
742 msg = msg .. "(";
743 if (value) then
744 msg = msg .. "true";
745 else
746 msg = msg .. "false";
747 end
748 msg = msg .. ")";
749 elseif (currType ~= "string" and currType ~= "number") then
750 msg = msg .. "(" .. currType .. ")";
751 else
752 msg = msg .. value;
753 end
754 end
755 end
756 end
757 DEFAULT_CHAT_FRAME:AddMessage(msg, color.r, color.g, color.b);
758 end
759 end
760
761
762  
763 --Update old style hook database (Immediate Execution)
764 for origName, info in Sea.util.Hooks do
765 if (type(info) == "table") then
766 Sea.util.Hooks[origName].count = 0;
767 for hookType, hookTable in info do
768 if (type(hookTable) == "table") then
769 if (type(hookTable.n) == "number") then
770 table.setn(Sea.util.Hooks[origName][hookType], hookTable.n);
771 SeaHooks_debugErrorPrint((SEA_HOOKS_DEBUG and SEA_HOOKS_DEBUG_VERBOSE), nil, NORMAL_FONT_COLOR, "SeaHooks Progress: Updating Hook Registry for '", origName, "' - '", hookType, "' hooks, ", hookTable.n, " found. If possible, these hooks might benifit if their addons optionally required SeaHooks.");
772 Sea.util.Hooks[origName][hookType].n = nil;
773 end
774 local i=1;
775 while (type(hookTable[i]) == "function") do
776 Sea.util.Hooks[origName].count = Sea.util.Hooks[origName].count + 1;
777 i=i+1;
778 end
779 end
780 end
781 end
782 end
783
784 end
785
786
787