Beispiele: HookDaedalus

Eine Daedalusfunktion hooken

Eine Daedalusfunktion hooken zu müssen sollte recht selten vorkommen, denn man kann ja einfach die entsprechende Funktion anpassen. Allerdings kann es in manchen Kontexten notwendig werden.

Das Prinzip dabei läuft ähnlich wie beim Hooken der Engine, aber einfacher:
Wir haben eine Funktion (gehookte Funktion), vor oder nach(!) der wir gern eine andere Funktion (hookende Funktion) einklinken möchten.

Vor einer Funktion hooken

Mit folgender Zeile wird immer die Funktion bar aufgerufen, wenn foo aufgerufen wird.
HookDaedalus(foo, bar);Diese beiden Funktionen könnten so aussehen:
func void foo() {
Print("FOO");
};
func void bar() {
Print("BAR");
ContinueCall();
};
Der Aufruf von ContinueCall am Ende der hookenden Funktion setzt den Programmverlauf mit der ursprünglichen Funktion fort. Ohne, würde bar ganz einfach foo ersetzen und letztere Funktion würde nie aufgerufen.

Ein Aufruf von foo im Spiel sollte so aussehen:BAR
FOO

Nach einer Funktion hooken

Verschieben wir in obigen Beispiel den Aufruf von ContinueCall an den Anfang der hookenden Funktion, können wir Dinge direkt nach foo einschieben.
func void bar() {
ContinueCall();
Print("BAR");
};

Ein Aufruf von foo im Spiel sollte dann so aussehen:FOO
BAR

Argumente und Rückgabewerte

Erwartet eine zu hookende Funktion Parameter oder gibt einen Wert zurück, sollte unsere hookende Funktion sich danach ausrichten.
func int foo(var int i) {
Print("FOO");
return i+1;
};
func int bar(var int i) {
Print("BAR");
PassArgumentI(i);
ContinueCall();
};
Ein Return am Ende von bar können wir uns in diesem Fall sparen, da der Rückgabewert einfach auf dem Stack bleibt. Auf den Aufruf von PassArgumentI(i) sollten wir aber nicht verzichten, um Sicherzustellen, dass i tatsächlich noch oben auf dem Stack liegt, wenn das Programm mit foo fortfährt.

Manipulation von Argumenten und Rückgabewerten

Argumente und Rückgabewerte können wir mit unserem Hook auch manipulieren.
func int bar(var int i) {
Print("BAR");
PassArgumentI(i+1); // Addiere 1
ContinueCall();
i = MEM_PopIntResult();
i *= 2; // Multipliziere mit 2
return i;
};

Mehrere Hooks

Eine Funktion kann beliebig oft gehookt werden, allerdings kann jede Funktion nur eine einzige hooken. Neue Hooks werden immer nach dem vorherigen eingefügt. Das folgende Beispiel stellt das recht gut dar:
HookDaedalusFunc(a, b); // B hookt A
HookDaedalusFunc(a, c); // C hookt A nach B
HookDaedalusFunc(a, d); // D hookt A nach C

HookDaedalusFunc(c, b); // Ignoriert, weil B bereits eine Funktion hookt

var int i; i = a(1);

// Ausgabe:
// -> D: 1
// -> C: 2
// -- B: 3
// <- C: 2
// <- D: 1

// Gehookte Funktion
func int a(var int i) {
MEM_Info(ConcatStrings("--- A: ", IntToString(i)));
return i+1;
};

// Erste hookende Funktion:
// Ersetzt a, da der Programmlauf nicht mit ContinueCall fortgeführt wird
func int b(var int i) {
MEM_Info(ConcatStrings(" -- B: ", IntToString(i)));
return i;
};

// Zweite hookende Funktion:
// Erhöht das Argument vor ContinueCall und verringert den Rückgabewert anschließend
func int c(var int i) {
MEM_Info(ConcatStrings(" -> C: ", IntToString(i)));
passArgumentI(i+1);
ContinueCall();

i = MEM_PopIntResult();
i -= 1;
MEM_Info(ConcatStrings(" <- C: ", IntToString(i)));
return i;
};

// Dritte hookende Funktion:
// Erhöht das Argument vor ContinueCall und verringert den Rückgabewert anschließend
func int d(var int i) {
MEM_Info(ConcatStrings("-> D: ", IntToString(i)));
passArgumentI(i+1);
ContinueCall();

i = MEM_PopIntResult();
i -= 1;
MEM_Info(ConcatStrings("<- D: ", IntToString(i)));
return i;
};