SA-MP Forums

Go Back   SA-MP Forums > SA-MP Scripting and Plugins > Scripting Help > Tutorials

Reply
 
Thread Tools Display Modes
Old 10/08/2010, 10:31 PM   #31
MrDeath537
High-roller
 
Join Date: Nov 2009
Location: Argentina
Posts: 2,082
Reputation: 29
Default Re: PAWN Pre-Processor

Excellent tutorials, you rocks. It's useful.
__________________
Trabajando en 3 includes, ny_INI, ny_XML y ny_Handling.
MrDeath537 is offline   Reply With Quote
Old 13/08/2010, 05:38 AM   #32
[L3th4l]
Guest
 
Posts: n/a
Default Re: PAWN Pre-Processor

@ Aztecking, what color you think you will receive? none, include a color after ...(playerid, COLOR_HERE,...

and put the macro at the top of your script
  Reply With Quote
Old 16/08/2010, 04:04 AM   #33
Backwardsman97
High-roller
 
Backwardsman97's Avatar
 
Join Date: Nov 2007
Posts: 2,229
Reputation: 36
Default Re: PAWN Pre-Processor

Typo.

Firstly - macro parameters are not the same as function parameters and should not be though of in the same way, they just match text and are quite simplistic. Function parameters are separated by commas, macro parameters are separated by whatever you tell them to be separated by and this catches a lot of people out.

Sorry, had to point that out. Very useful information explained in detail. Thanks Y_Less.
Backwardsman97 is offline   Reply With Quote
Old 17/08/2010, 08:25 PM   #34
playbox12
High-roller
 
playbox12's Avatar
 
Join Date: Feb 2010
Location: Netherlands
Posts: 1,633
Reputation: 213
Default Re: PAWN Pre-Processor

Awesome Y_Less (Like always), you spend so much time writing stuff up amazing.
playbox12 is offline   Reply With Quote
Old 01/09/2010, 10:51 AM   #35
Y_Less
Beta Tester
 
Y_Less's Avatar
 
Join Date: Jun 2008
Location: 629
Posts: 16,021
Reputation: 2280
Default PAWN Pre-Processor (Part 3)

Contents

Part 1 - Covers an introduction to the pre-processor and covers some important things when writing function-like macros.
Part 2 - Explains exactly what the compiler searches for and looks at some common macro uses.
Part 3 - Describes the other directives available (beside "#define") and looks at definitions with no replacement value.
Part 4 - How to use strings with the pre-processor.
Part 5 - Alternatives to the pre-processor, multiple symbols and recursion.
Part 6 - Case Study (y_remote).
Part 7 - Macro issues and spaces.

Additional

Utilising tagof - By g_aSlice.
Future-proof string concatenation
String bug
Macros that do multiple things
Advanced "tag" macros

Other Directives

This is just a very simple enumeration of the other available pre-processor directives. Note that this list is covered very well in the PAWN language guide (aka pawn-lang.pdf or Pawn_Langugae_Guide.pdf), so I'll only go in to detail with directives which will be used in future guides.
  • #include

This includes the text from another file in to the current file. There are two styles of include:

pawn Code:
#include "a.pwn"

This style includes a file from the same directory as the file which is doing the including. This can use directories (including ".." to go up a level).

pawn Code:
#include <a>

This style includes a file from the standard include directory (by default "pawno\include". This can again include directories in the path. The file extension can be ommitted from either include style if the extension is "pawn", "p" or "inc".

Very frequently includes have what are called "include guards". These are blocks of code looking something like the following:

pawn Code:
#if defined _MY_INC_INCLUDED
    #endinput
#endif
#define _MY_INC_INCLUDED
#pragma library "MyInc"

Every line of that code is redundant, the PAWN compiler is not the same as the C compiler on which this code is undoubtedly based. A C compiler will include a file every time it is included, this can lead to recursive inclusion if a file includes itself, or includes another file which then includes the first etc. For this reason C includes use that style of code to prevent multiple inclusions of the same file - the PAWN file only allows one copy of a file to be included ever.

The "#pragma library" line is used to automatically load PAWN native libraries - a feature which is not used in SA:MP (though does work), SA:MP has its own plugin system.

This inbuilt include guard can be avoided if you need to include a file multiple times (this is rare, but does have its uses). The PAWN compiler defines a symbol of the form "_inc_filename" for every included file, where "filename" is the name of the file excluding any directories or extensions. To remove the symbol simply add the following to your code (I add this at the very start of the file to which it applies):

pawn Code:
#undef _inc_filename

This feature is surprisingly picky given that I've never seen it as an issue in any released script ever! If two files in different directories have the same name the second will not be included as a file with the same NAME is already included. Additionally to use this feature you MUST use the native directory separator of the current operating system (for PAWNO - windows, if you are running under WINE I have NO idea what the effect will be). Assume the following two VERY simple libraries:

inc\b.pwn:

pawn Code:
#define A 10

b.pwn:

pawn Code:
#define B 10

This code will not work as the "b.pwn" file has already been included:

pawn Code:
#include <a_samp>
#include "b.pwn"
#include "inc\b.pwn"

main()
{
    printf("%d %d", A, B);
}

However this code WILL work because the wrong directory separator was used:

pawn Code:
#include <a_samp>
#include "b.pwn"
#include "inc/b.pwn"

main()
{
    printf("%d %d", A, B);
}

Additionally this code will not work as the symbol definition only works correctly for files ending in "pawn", "p" or "inc". The symbol still exists for other filetypes, it is just not available to PAWN:

pawn Code:
#include "inc\b.pwn"

#if defined _inc_b
    #error Will not get called
#endif

This code will give an error though (assuming the file is renamed "b.p" or similar):

pawn Code:
#include "inc\b"

#if defined _inc_b
    #error Will get called
#endif

Further, this will not work either as the right extension, but the wrong separator, is used:

pawn Code:
#include "inc/b"

#if defined _inc_b
    #error Will not get called
#endif

Despite these problems, this feature is useful as it allows you to see what files are and are not included:

pawn Code:
#if !defined _inc_a_samp
    #include <a_samp>
#endif

I will show some very complex uses of this in a later tutorial.
  • #tryinclude

This is similar to "#include" but will not give an error if the file does not exist. You can use this in conjunction with the file definitions explained above to get some nice compile-time configuration:

pawn Code:
#tryinclude <a_mysql>
#if !defined inc_a_mysql
    #tryinclude <mysql>
    #if !defined inc_mysql
        #error No mysql includes found
    #endif
#endif
  • #undef

This is the opposite of "#define". If you have previously defined a symbol using "#define" this will remove it again@

pawn Code:
#include <a_samp>

#undef MAX_PLAYERS
#define MAX_PLAYERS 32
  • #error

Already shown a few examples of this. This will simply halt compilation with a custom error:

pawn Code:
#error Check this error!

Gives:

Code:
C:\file.pwn(1) : fatal error 111: user error: Check this error!


Compilation aborted.Pawn compiler 3.2.3664	 	 	Copyright (c) 1997-2006, ITB CompuPhase


1 Error.
  • #endinput

Ends a file early. Used in include guards (though doesn't need to be) to finish the file before other code in the file is parsed. Note that the code after this point may not be valid syntax, but it doesn't matter as it is never read by the compiler:

pawn Code:
#endinput

This is
gibberish!!!

..:r32AEWhg43££

The above is a valid file, simply because the "#endinput" means that everything else is never read.
  • #if

"#if" includes, or excludes, code from a mode based on properties defined at compile time. Things set by "#define" are set at compile time so are known to the compiler. These can't be changed after the mode has been compiled. For example:

pawn Code:
public OnPlayerConnect(playerid)
{
    #if playerid == 0
        printf("Player %d joined", playerid);
    #endif
}

The code above will NOT work because "playerid" is not known by the compiler, it is only known when the mode is run and a player connects. That code will give an error because the compiler doesn't know the value of "playerid".

Most checks which can be used in a normal "if" statement can be used in "#if" directives - with the major exception of function calls (but not macro calls). The one stipulation is that the result MUST be constant. The code below will only print to the console when the server is run if there are more than 200 maximum players defined at compile time:

pawn Code:
main()
{
    #if MAX_PLAYERS > 200
        print("This server can support more than 200 players");
    #endif
}

There are two possibilities for this code:

pawn Code:
#define MAX_PLAYERS 100

Will compile as:

pawn Code:
main()
{
}

Notice that the code has been entirely removed - it doesn't generate an "if" statement, it generates no code at all. The other option is:

pawn Code:
#define MAX_PLAYERS 300

Will compile as:

pawn Code:
main()
{
        print("This server can support more than 200 players");
}

There are a whole range of checks you can do:

pawn Code:
#if a == b

#if a > b

#if (a == b) && (a <= 27)

#if defined a

That last one checks wether "#define" has been called for a symbol or not:

pawn Code:
#if defined SYMBOL
    // Will be false
#endif

pawn Code:
#define SYMBOL

#if defined SYMBOL
    // Will be true
#endif

pawn Code:
#define SYMBOL
#undef SYMBOL

#if defined SYMBOL
    // Will be false
#endif

The examples above used a little trick not mentioned before - definitions with no replacement. This is perfectly valid and can be used, for example, to not generate code under certain circumstances or just to mark something as done (see below).
  • #elseif

Very similar to the "else if" keyword (technically two keywords) in regular PAWN. If the associated "#if" (and all previous "#elseif"s) fails then this check is run. The checks which can be run are exactly the same as those which can be run by "#if".
  • #else

This defines the start of code to be included or directives to be evaluated if all previous "#if" and "#endif" checks failed. "#else" does not have to be the last directive in a "#if" block, but it makes sense most of the time:

pawn Code:
#if a > b
    // Run if a is greater than b.
#else
    // Run if a is less than or equal to b.
#elseif b > a
    // Run if a is less than b.
#endif

This is a valid "#if" block however if "b" is greater than "a" then both the "#else" and "#elseif" blocks will be executed. The "#if" check will fail - setting some internal variable to 0 to represent the fact that no branch has been run yet. The "#else" will see this and thus execute, but will not update the variable (this may be a bug, but could be useful). As this internal variable is still 0 when the "#elseif" is reached it will be evaluated and may be true, setting the internal variable to 1. Interestingly, the fact that "#else" doesn't update the internal settings makes the following valid:

pawn Code:
#if a > b
    // Run if a is greater than b.
#else
    // Run if a is less than or equal to b.
#elseif b > a
    // Run if a is less than b.
#else
    // Run if a is equal to b.
#endif

pawn Code:
#if a > b
    // Run if a is greater than b.
#else
    // Run if a is less than or equal to b.
#else
    // Run if a is less than or equal to b.
#else
    // Run if a is less than or equal to b.
#endif
  • #endif

Ends a pre-processor optional include block. This comes after the last block, wether this is just a "#if", a "#elseif" or a "#else". Valid complete blocks include:

pawn Code:
#if a > b
    // Do something here.
#endif

pawn Code:
#if a > b
    // Do something here.
#else
    // Do something here.
#endif

pawn Code:
#if a > b
    // Do something here.
#elseif b > a
    // Do something here.
#endif

pawn Code:
#if a > b
    // Do something here.
#elseif b > a
    // Do something here.
#else
    // Do something here.
#endif

Invalid blocks include:

pawn Code:
#if a > b
    // Do something here.

pawn Code:
#if a > b
    // Do something here.
#else
    // Do something here.

pawn Code:
#elseif b > a
    // Do something here.
#endif
  • #assert

Checks compile time constants are correct. For example:

pawn Code:
#assert MAX_PLAYERS > 32

Is equivalent to:

pawn Code:
#if MAX_PLAYERS <= 32
    #error
#endif

This can use all the checks available to "#if".
  • Others

The other directives (listed in pawn-lang.pdf, not all work) are:
  • #pragma directive - Gives instructions to the compiler.
  • #section "sname" - Restricts the scope of static variables and functions (broken).
  • #line num - Changes the current file line number.
  • #file "fname" - Changes the current file name. Does not define a new "_inc_fname" symbol.

See the main documentation for more information.

Defines With No Replace

Symbols can be defined with no replacement:

pawn Code:
#define SYMBOL

Whenever "SYMBOL" appears in code it will be replaced with nothing. This has two main uses.
  • Markers

The first main use of this system is to mark something as done. These symbols are mainly used by the preprocessor, for example:

pawn Code:
#tryinclude <my_inc>
#if defined _inc_my_inc
    // Do something
#endif

The value of "_inc_my_inc" is irrelevant - if it exists you have included the file, a value such as "0" or "42" is meaningless to wether the file is included or not, so it isn't set. You can't do:

pawn Code:
#tryinclude <my_inc>
#if _inc_my_inc == 1
    // Do something
#endif

Because if the file ISN'T included then "_inc_my_inc" won't exist at all and you will get a compiler error from trying to compare something that doesn't exist to 1.
  • Optional code

The other main use of this is for code which may or may not exist. For example:

pawn Code:
#if RETURN_VAL == 2
    #define RETURN_TAG Float:
#else
    #define RETURN_TAG
#endif

RETURN_TAG MyFunc();

If "RETURN_VAL" is "2" this will compile as:

pawn Code:
Float: MyFunc();

If it is not "2" the code will simply compile as:

pawn Code:
MyFunc();

In this way code can be configured at compile time, for example adding the "static" definition to functions if required:

pawn Code:
#if defined HIDE_FUNCTIONS
    #define STATIC static
#else
    #define STATIC
#endif

STATIC MyFunc();

Here, if "HIDE_FUNCTIONS" is defined (note that this is a "marker" replacement less macro), "STATIC" will be defined with a value, otherwise it will be defined with no value. The "STATIC" macro is placed before the "MyFunc" definition which will either define a static function or a normal function (one with nothing before it) depending on compile time options.
__________________

Last edited by Y_Less; 22/04/2013 at 02:56 PM.
Y_Less is online now   Reply With Quote
Old 01/09/2010, 12:39 PM   #36
FireCat
Banned
 
Join Date: Jul 2010
Posts: 2,472
Reputation: 596
Default Re: PAWN Pre-Processor (Updated 01\09\10)

only you Y_Less, only you...
_______________________
My jobs HCatcher
1-3$ fix warnings 1-5$fix errors
6-15$ make gamemodes(not roleplay) 3-10$ make filterscripts-scriptfiles
Dont ASK! me RolePlay questions
www.mefreeroam.tk
FireCat is offline   Reply With Quote
Old 13/09/2010, 02:08 PM   #37
Cank
Big Clucker
 
Join Date: Mar 2010
Posts: 95
Reputation: 1
Default AW: PAWN Pre-Processor (Updated 01\09\10)

why does the following not work?

pawn Code:
main()
{
      new a=3, b=4;
      #if a==b
      //bla...
      #endif
}

error 008: must be a constant expression; assumed zero
Cank is offline   Reply With Quote
Old 13/10/2010, 12:48 PM   #38
Y_Less
Beta Tester
 
Y_Less's Avatar
 
Join Date: Jun 2008
Location: 629
Posts: 16,021
Reputation: 2280
Default PAWN Pre-Processor (Part 4)

Contents

Part 1 - Covers an introduction to the pre-processor and covers some important things when writing function-like macros.
Part 2 - Explains exactly what the compiler searches for and looks at some common macro uses.
Part 3 - Describes the other directives available (beside "#define") and looks at definitions with no replacement value.
Part 4 - How to use strings with the pre-processor.
Part 5 - Alternatives to the pre-processor, multiple symbols and recursion.
Part 6 - Case Study (y_remote).
Part 7 - Macro issues and spaces.

Additional

Utilising tagof - By g_aSlice.
Future-proof string concatenation
String bug
Macros that do multiple things
Advanced "tag" macros

String Generation
  • General

The SA:MP compiler has a custom feature (since incorporated into the main PAWN compiler) for converting symbols into strings, and for concatenating constant strings. Consider the following code:

pawn Code:
#define VERSION_MAJOR 1
#define VERSION_MINOR 6
#define VERSION_BUILD 2345

main()
{
    printf("==============");
    printf(" Y_Less' Mode ");
    printf("  V %d.%d.%d  ", VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD);
    printf("==============");
}

Every time the mode is started that printf will format the string with (run-time) constant values - this is just a waste of time for the server when we can get the compiler to do it for us using hash (#):

pawn Code:
printf("  V " #VERSION_MAJOR "," #VERSION_MINOR "," #VERSION_BUILD "  ");

Let's break this down:

Code:
VERSION_MAJOR
That is a standard define, it will replace itself with "1":

pawn Code:
printf("  V " #1 "," #6 "," #2345 "  ");

Code:
#1
A hash within a string will convert anything after it into a string:

pawn Code:
printf("  V " "1" "," "6" "," "2345" "  ");

Now we have lots of strings separated by spaces. These will be IMplicitly combined to form one long string as they are not separated by commas:

pawn Code:
printf("  V 1,6,2345  ");

If this was the standard PAWN compiler you would need to EXplicitly tell it to append them:

pawn Code:
printf("  V " ... #VERSION_MAJOR ... "," ... #VERSION_MINOR ... "," ... #VERSION_BUILD ... "  ");

The implicit string concatenation will work for anything prefixed by a hash (implicit strings) and anything in double quotes (explicit strings).
  • Restrictions

There are a few restrictions. If the compiler comes across ";", ")" or "," after a hash it will stop the string:

pawn Code:
printf("hi" ", there");

Is not the same as:

pawn Code:
printf("hi" #, there);

Both will compile but the second will only print "hi" as the compiler sees the comma as the end of the implicit string. This may be more subtle, for example:

pawn Code:
#define VERSION_MAJOR 1
#define VERSION_MINOR 6
#define VERSION_BUILD 2345

#define VERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD

main()
{
    printf("==============");
    printf(" Y_Less' Mode ");
    printf("  V " #VERSION);
    printf("==============");
}

That will convert to:

pawn Code:
printf("  V " #1,6,2345);

Which will compile as:

pawn Code:
printf("  V 1", 6, 2345);

This is wrong as it will only display " V 1".

Additionally, due to the selection of string end markers you can now no longer use strings as triadic operator results:

pawn Code:
a = var ? "one" : "two";

A colon (:) is not seen as the end of a string, so the compiler complains that is was expecting a string, however there is a very simple way around this:

pawn Code:
a = var ? ("one") : ("two");

That does not change the meaning of the code in any way but compiles. This problem does not occur at all for the explicit string concatenation in the standard PAWN compiler.

Because a close bracket is seen as the end of a string you can't do:

pawn Code:
#define A (5)

printf("A = " #A);

That will try to compile with two close brackets outside the string:

pawn Code:
printf("A = (5"));

An ideal solution to this would stringify the whole of the result of the macro, regardless of context and content, but it doesn't. Note that "stringify" is the technical term for what the hash operator is doing, despite not sounding very technical!
  • Spacing

This:

pawn Code:
printf("hello"     #    there    "you");

Will print: "hellothereyou". Any spaces before or after a stringified token are ignored.
  • Printing tokens

The reason we need this operator is that:

pawn Code:
#define VERSION_MAJOR 1
#define VERSION_MINOR 6
#define VERSION_BUILD 2345

main()
{
    printf("==============");
    printf(" Y_Less' Mode ");
    printf("  V VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD");
    printf("==============");
}

Will print exactly that:

Code:
  V VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD
Pre-processor macros inside strings are not evaluated, in the same way as variables are not. There used to a bug (or feature) where "%N" macro parameters were evaluated inside strings, and this was used by the original version of dcmd, however that "bug" was "fixed" - prompting the development of this alternate compiler.

To summararise - to put the value of a compile time constant in a string use hash in front of it, to put the name of a compile time constant in a string enclose it it quotes. This also works for macro parameters:

pawn Code:
#define PRINT(%0,%1) printf(#%0,%1)

PRINT(HELLO %d, var);

This will result in:

pawn Code:
printf("HELLO %d", var);

With the first macro parameter (%0) being stringified and the second macro parameter becoming the passed "var".
__________________

Last edited by Y_Less; 22/04/2013 at 02:56 PM.
Y_Less is online now   Reply With Quote
Old 19/10/2010, 08:48 AM   #39
Slice
High-roller
 
Slice's Avatar
 
Join Date: Mar 2008
Location: Sweden
Posts: 1,825
Reputation: 1178
Spray Re: PAWN Pre-Processor (Updated 01\09\10)

Utilizing tagof

pawn Code:
#include <a_samp>

public OnFilterScriptInit( )
{
    new
        iTest = 125123,
        Float:fTest = 555.112,
        bool:bTest = false,
        Text3D:t3dTest = Text3D:4,
        Menu:mTest = Menu:62,
        Text:tTest = Text:11,
        DB:dbTest = DB:2,
        DBResult:dbrTest = DBResult:7,
        File:hTest = File:2
    ;
   
    printf( DumpVariable( iTest ) );
    printf( DumpVariable( fTest ) );
    printf( DumpVariable( bTest ) );
    printf( DumpVariable( t3dTest ) );
    printf( DumpVariable( mTest ) );
    printf( DumpVariable( tTest ) );
    printf( DumpVariable( dbTest ) );
    printf( DumpVariable( dbrTest ) );
    printf( DumpVariable( hTest ) );
}

stock DumpVariable( { _, Float, Text3D, Menu, Text, DB, DBResult, bool, File }:xVariable, iType = tagof( xVariable ) )
{
    static
        s_szReturn[ 24 ]
    ;
   
    switch ( iType )
    {
        case ( 0 ):
            format( s_szReturn, sizeof( s_szReturn ), "_:%d", xVariable );
        case ( tagof( Float: ) ):
            format( s_szReturn, sizeof( s_szReturn ), "Float:%.8f", xVariable );
        case ( tagof( Text3D: ) ):
            format( s_szReturn, sizeof( s_szReturn ), "Text3D:%d", xVariable );
        case ( tagof( Menu: ) ):
            format( s_szReturn, sizeof( s_szReturn ), "Menu:%d", xVariable );
        case ( tagof( Text: ) ):
            format( s_szReturn, sizeof( s_szReturn ), "Text:%d", xVariable );
        case ( tagof( DB: ) ):
            format( s_szReturn, sizeof( s_szReturn ), "DB:%d", xVariable );
        case ( tagof( DBResult: ) ):
            format( s_szReturn, sizeof( s_szReturn ), "DBResult:%d", xVariable );
        case ( tagof( bool: ) ):
        {
            if ( xVariable )
                memcpy( s_szReturn, "bool:true", 0, 10 * ( cellbits / 8 ) );
            else
                memcpy( s_szReturn, "bool:false", 0, 11 * ( cellbits / 8 ) );
        }
        case ( tagof( File: ) ):
            format( s_szReturn, sizeof( s_szReturn ), "File:%d", xVariable );
        default:
            format( s_szReturn, sizeof( s_szReturn ), "unknown:%d", xVariable );
    }
   
    return s_szReturn;
}

This will print:
Code:
[10:53:47] _:125123
[10:53:47] Float:555.11199951
[10:53:47] bool:false
[10:53:47] Text3D:4
[10:53:47] Menu:62
[10:53:47] Text:11
[10:53:47] DB:2
[10:53:47] DBResult:7
[10:53:47] File:2
__________________
Do you like SA-MP and DayZ? Come play both: ulclan.com:1421

Do you use any scripts I've made?
Star them on GitHub or reply in their topics so I know what I should keep working on!
Slice is offline   Reply With Quote
Old 04/11/2010, 11:09 AM   #40
Slice
High-roller
 
Slice's Avatar
 
Join Date: Mar 2008
Location: Sweden
Posts: 1,825
Reputation: 1178
Default Re: PAWN Pre-Processor (Updated 01\09\10)

Is there a way to put the value from sizeof inside a string?
__________________
Do you like SA-MP and DayZ? Come play both: ulclan.com:1421

Do you use any scripts I've made?
Star them on GitHub or reply in their topics so I know what I should keep working on!
Slice is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
[Include] [INC] zcmd 0.3.1 | Fast & Simple Command Processor (updated 30/10/2009) Zeex Includes 521 30/03/2014 10:02 AM
[Tool/Web/Other] Notepad++ PAWN syntax (UPDATED for 0.3x) Chaprnks Tutorials 14 06/03/2014 09:50 AM
Need the updated foreach.inc that works with updated YSI gychem Scripting Help 2 06/02/2013 08:36 PM
Need best processor for my PC [MG]Dimi Everything and Nothing 19 23/11/2012 03:50 PM
[INC+FS] Aero File Processor by Luby * Updated! luby Filterscripts 15 14/05/2009 01:07 PM


All times are GMT. The time now is 12:08 AM.


Powered by vBulletin® Version 3.8.6
Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.