[Tutorial] Converting to ALS 4 (Hook Method 7) Very Quickly With Regular Expressions
#1

I just improved this post a bit so I may as well post here as I'm sure someone can find it useful.

___


A while ago, I stumbled upon the ALS 7 topic and I set out to convert SIF and SS to use this method but SS has 195 files alone, a search found 230 hooks woah! I ain’t doing all those by hand!

Luckily, Sublime Text supports Regular Expression search and replace. So in this post, I will guide you through the regex queries needed to convert from the old ALS method to the new (faster) version.


Regular Expressions

If you're not familiar with Regular Expressions, they are extremely useful! Especially when you want to search for patterns in code. But regex isn't just a neat text-editor feature, you can grab a library and embed regex functionality into a piece of software (there are libraries for a huge variety of languages) even SA:MP!


Step 1. Identify the Differences

This is an old hook:

pawn Code:
public OnSomething()
{
    return CallLocalFunction("lib_OnSomething", "");
}
#if defined _ALS_OnSomething
    #undef OnSomething
#else
    #define _ALS_OnSomething
#endif
#define OnSomething lib_OnSomething
forward lib_OnSomething();
And this is a new one:

pawn Code:
public OnSomething()
{
    #if defined lib_OnSomething
        return lib_OnSomething();
    #else
        return 0;
    #endif
}
#if defined _ALS_OnSomething
    #undef OnSomething
#else
    #define _ALS_OnSomething
#endif
#define OnSomething lib_OnSomething
#if defined lib_OnSomething
    forward lib_OnSomething();
#endif
There are two differences here:
Firstly, the return line returns an actual function call instead of CallLocalFunction (read the topic about ALS7 for why):

pawn Code:
return CallLocalFunction("lib_OnSomething", "");
becomes

pawn Code:
#if defined lib_OnSomething
    return lib_OnSomething();
#else
    return 0;
#endif
Secondly, the forward for the new callback is nested under a compile-time #if check:

pawn Code:
forward lib_OnSomething();
becomes

pawn Code:
#if defined lib_OnSomething
    forward lib_OnSomething();
#endif
There is another thing to take into account here: hooked callbacks may or may not have parameters. This means we need a regex query for catching either just an empty parameter format string or a full one with the parameters too. The parameters must be in a separate capture since those are all we need for the replacement:

pawn Code:
return CallLocalFunction("lib_OnSomething", "d", arg);
becomes

pawn Code:
#if defined lib_OnSomething
    return lib_OnSomething(arg);
#else
    return 0;
#endif
and

pawn Code:
forward lib_OnSomething(arg);
becomes

pawn Code:
#if defined lib_OnSomething
    forward lib_OnSomething(arg);
#endif

Step 2. The Search Queries

The first part is the return of CallLocalFunction which is no longer needed as a simple compile time if check works.

This search regex will catch functions with or without parameters:

pawn Code:
return CallLocalFunction\("([A-Za-z_][A-Za-z_0-9]*)", (""\)|"[a-z]*", ([A-Za-z_0-9\, ]*)\));
The second part searches specifically for a 'forward' that followed a 'define' line because not all 'forward' lines are hooks.

pawn Code:
#define (On[A-Za-z_][A-Za-z_0-9]*) ([A-Za-z_]*_On[A-Za-z_0-9]*)
forward [A-Za-z_][A-Za-z_0-9]*\(([A-Za-z_0-9\, ]*)\);
You can test these out to ensure they work by just doing a simple regex search with ST2/Pawno/Notepad++


Step 3. The Replacements

RegEx has a feature called 'captures' which is used for search+replace. To define a capture field, part of the query is enclosed in brackets. Look at the above queries for example, the word after '#define' is the raw function name, this is captured using brackets and can be used in the replacement query.

Each capture is sequentially numbered and in the replacement query these captures can be inserted with a '$' followed by the number that identifies the capture you want to insert.

Note: these replacement queries were done in Sublime Text 2. In other editors, the indentation may be different.
Replacement 1: The return value
Search:

pawn Code:
return CallLocalFunction\("([A-Za-z_][A-Za-z_0-9]*)", (""\)|"[a-z]*", ([A-Za-z_0-9\, ]*)\));
Replace:

pawn Code:
#if defined $1
        return $1($3);
    #else
        return 0;
    #endif
The parameters use capture $3 since $2 actually surrounds the entire set of parameters including the format string and capture $3; pseudo-code example: 2:("dd", 3:(int1, int2)).

Replacement 2: The forwarding
Search:

pawn Code:
#define (On[A-Za-z_][A-Za-z_0-9]*) ([A-Za-z_]*_On[A-Za-z_0-9]*)
forward [A-Za-z_][A-Za-z_0-9]*\(([A-Za-z_0-9\, ]*)\);
Replace:

pawn Code:
#define $1 $2
#if defined $2
    forward $2($3);
#endif

Conclusion

I may have made some mistakes here and it may not be compatible with all coding styles or hooking styles.
RegEx is a powerful tool though and if there are incompatibilities, play around with the expressions and reply to this post with fixes.

I hope this has helped you save hours or work manually converting to ALS 7!
Reply
#2

This is great idea!


Thank you, Southclaw
Reply


Forum Jump:


Users browsing this thread: 2 Guest(s)