SA-MP Forums

Go Back   SA-MP Forums > SA-MP Scripting and Plugins > Plugin Development

Reply
 
Thread Tools Display Modes
Old 11/01/2010, 12:45 AM   #1
Y_Less
Beta Tester
 
Y_Less's Avatar
 
Join Date: Jun 2008
Location: 629
Posts: 16,045
Reputation: 2280
Default sscanf 2.8.1 - Updated 12/03/13: Now supports npcmodes!

sscanf 2.8.1

NPC modes

To use sscanf in an NPC mode, download this file:

http://dl.dropbox.com/u/21683085/npcdll.rar

And extract it to your root server directory (so "amxsscanf.dll" is in the same directory as "samp-npc.exe"). Then use as normal. The only tiny difference is that "u", "r", and "q" don't know if a user is a bot or not, so just assume they are all players - use accordingly.

Contents
  • NPC modes - A very quick guide to using sscanf in NPC modes.
  • Contents - This list of post sections.
  • Introduction - What this plugin is.
  • Download - Where to get it.
  • Use - Basic plugin usage instructions.
  • Specifiers - List of all the unformatting options.
    • Basic specifiers - Single-letter specifiers.
    • Strings - Code to read in text.
    • Arrays - Code to read in multiple similar values.
    • Enums - Code to read in complex data.
    • Minus - Skip enum slots.
    • Quiet - Loaded and discarded data.
    • Searches - Explicit search text.
    • Delimiters - Symbols between data.
    • Optional specifiers - Specifiers with default values.
    • Users - Detect whole or partial names.
    • Custom (kustom) specifiers - Custom specifiers.
  • Options - Control how sscanf works.
  • All specifiers - A handy reference list.
  • "extract" - Simplified use macro.
  • Kustomisation - Third-party "k" specifiers.
    • player_name - Alternate "u" by Slice.
  • Errors/Warnings - How to fix common problems.
  • "fixes2" Plugin - Additional error messages.
  • Conclusion - A brief challenge.
  • Changelog - History of changes.

Introduction

I have been hinting at this plugin for quite a while now (to some people anyway, and it was posted about on twitter) and it's finally complete - I really don't have much spare time and there's quite a lot of code to this (it puts the old sscanf to shame). I was initially trying to fix a few problems with the old sscanf in PAWN - things like the lack of optional integers and buffer overflows on strings, it was going OK but the code was getting quite large - it got over 1000 lines for a single function before I decided to move it out to a plugin. As I was at it there was also a large number of new specifiers I wanted to add.

This is possibly my most thoroughly tested code ever - I've got a huge file of unit tests, but given the complexity I have no doubt there will still be bugs about. See the bottom of the post for a history of the bugs and various fixes and versions.

Download

Source, Windows .dll, and include:

Mirror from Y_Less

CentOS .so:

Mirror from Mark™
Mirror from h02

Linux .so:

Mirror from h02
Mirror from Mellnik

sscanf2.inc:

Mirror from Pastebin

Use

This behaves exactly as the old sscanf did, just MUCH faster and much more flexibly. To use it add:

pawn Code:
#include <sscanf2>

To your modes and remove the old sscanf (the new include will detect the old version and throw an error if it is detected). On windows add:

Code:
plugins sscanf
To server.cfg. On Linux add:

Code:
plugins sscanf.so
The basic code looks like:

pawn Code:
if (sscanf(params, "ui", giveplayerid, amount))
{
    return SendClientMessage(playerid, 0xFF0000AA, "Usage: /givecash <playerid/name> <amount>");
}

However it should be noted that sscanf can be used for any text processing you like. For example an ini processor could look like (don't worry about what the bits mean at this stage):

pawn Code:
if (sscanf(szFileLine, "p<=>s[8]s[32]", szIniName, szIniValue))
{
    printf("Invalid INI format line");
}

There is also an alternate function name to avoid confusion with the C standard sscanf:

pawn Code:
if (unformat(params, "ui", giveplayerid, amount))
{
    return SendClientMessage(playerid, 0xFF0000AA, "Usage: /givecash <playerid/name> <amount>");
}

Specifiers

The available specifiers (the letters "u", "i" and "s" in the codes above) are below.
  • Basic specifiers

Specifier(s) Name Example values
b Binary 01001, 0b1100
c Character a, o, *
f Float 0.7, -99.5
g IEEE Float 0.7, -99.5, INFINITY, -INFINITY, NAN, NAN_E
h, x Hex 1A, 0x23
i, d Integer 1, 42, -10
l Logical true, false
n Number 42, 0b010, 0xAC, 045
o Octal 045 12
q Bot name/id ShopBot, 27
r Player name/id Y_Less, 42
u User name/id (bots and players) Y_Less, 0
  • Strings

The specifier "s" is used, as before, for strings - but they are now more advanced. As before they support collection, so doing:

pawn Code:
sscanf("hello 27", "si", str, val);

Will give:

Code:
hello
27
Doing:

pawn Code:
sscanf("hello there 27", "si", str, val);

Will fail as "there" is not a number. However doing:

pawn Code:
sscanf("hello there", "s", str);

Will give:

Code:
hello there
Because there is nothing after "s" in the specifier, the string gets everything. To stop this simply add a space:

pawn Code:
sscanf("hello there", "s ", str);

Will give:

Code:
hello
You can also escape parts of strings with "\\" - note that it is two backslashes as 1 is used by the compiler:

pawn Code:
sscanf("hello\\ there 27", "si", str, val);

Will give:

Code:
hello there
27
All these examples however will give warnings in the server as the new version has array sizes. The above code should be:

pawn Code:
new
    str[32],
    val;
sscanf("hello\\ there 27", "s[32]i", str, val);

As you can see - the format specifier now contains the length of the target string, ensuring that you can never have your strings overflow and cause problems. This can be combined with the SA:MP compiler's stringizing:

pawn Code:
#define STR_SIZE 32
new
    str[STR_SIZE],
    val;
sscanf("hello\\ there 27", "s[" #STR_SIZE "]i", str, val);

So when you change your string size you don't need to change your specifiers.

What happened to "z", the optional string? z has been removed (you can still use it but will get a server warning) to make way for the new optional parameter system described later on.
  • Arrays

One of the advanced new specifiers is "a", which creates an array, obviously. The syntax is similar to that of strings and, as you will see later, the delimiter code:

pawn Code:
new
    arr[5];
sscanf("1 2 3 4 5", "a<i>[5]", arr);

The "a" specifier is immediately followed by a single type enclosed in angle brackets - this type can be any of the basic types listed above. It is the followed, as with strings now, by an array size. The code above will put the numbers 1 to 5 into the 5 indexes of the "arr" array variable.

Arrays can now also be combined with strings (see below), specifying the string size in the array type:

Code:
a<s[10]>[12]
This will produce an array of 12 strings, each up to 10 characters long (9 + NULL). Optional string arrays still follow the optional array syntax:

Code:
A<s[10]>(hello)[12]
However, unlike numbers you can't specify a progression and have it fill up. This code:

Code:
A<i>(0, 1)[4]
Will by default produce:

Code:
0, 1, 2, 3
However, this code:

Code:
A<s[10]>(hi, there)[4]
Will by default produce:

Code:
"hi, there", "hi, there", "hi, there", "hi, there"
As normal, you can add brackets in to the default string value with "\)":

Code:
A<s[10]>(hi (code\))[4]
It should also be noted that there is NO length checking on default strings. If you do:

Code:
A<s[10]>(This is longer than 10 characters)[4]
You will probably just corrupt the PAWN stack. The length checking is to ensure no users enter malicious data; however, in this case it is up to the scripter to ensure that the data is correct as they are the only one affecting it and shouldn't be trying to crash their own server. Interestingly, arrays of strings actually also work with jagged arrays and arrays that have been shuffled by Slice's quicksort function (this isn't a side-effect, I specifically wrote them to do so).
  • Enums

This is possibly the most powerful addition to sscanf ever. This gives you the ability to define the structure of an enum within your specifier string and read any data straight into it. The format takes after that of arrays, but with more types - and you can include strings in enums (but not other enums or arrays):

pawn Code:
enum
    E_DATA
{
    E_DATA_C,
    Float:E_DATA_X,
    E_DATA_NAME[32],
    E_DATA_Z
}

main
{
    new
        var[E_DATA];
    sscanf("1 12.0 Bob c", "e<ifs[32]c>", var);
}

Now I'll be impressed if you can read that code straight off, so I'll explain it slowly:

Code:
e - Start of the "enum" type
< - Starts the specification of the structure of the enum
i - An integer, corresponds with E_DATA_C
f - A float, corresponds with E_DATA_X
s[32] - A 32 cell string, corresponds with E_DATA_NAME
c - A character, corresponds with E_DATA_Z
> - End of the enum specification
Note that an enum doesn't require a size like arrays and strings - it's size is determined by the number and size of the types. Most, but not all, specifiers can be used inside enums (notably arrays and other enums can't be).
  • Quiet

The two new specifiers "{" and "}" are used for what are known as "quiet" strings. These are strings which are read and checked, but not saved. For example:

pawn Code:
sscanf("42 -100", "{i}i", var);

Clearly there are two numbers and two "i", but only one return variable. This is because the first "i" is quiet so is not saved, but affects the return value. The code above makes "var" "-100". The code below will fail in an if check:

pawn Code:
sscanf("hi -100", "{i}i", var);

Although the first integer is not saved it is still read - and "hi" is not an integer. Quiet zones can be as long as you like, even for the whole string if you only want to check values are right, not save them:

pawn Code:
sscanf("1 2 3", "i{ii}", var);
sscanf("1 2 3", "{iii}");
sscanf("1 2 3", "i{a<i>[2]}", var);

You can also embed quiet sections inside enum specifications:

pawn Code:
sscanf("1 12.0 Bob 42 INFINITY c", "e<ifs[32]{ig}c>", var);

Quiet sections cannot contain other quiet sections, however they can include enums which contain quiet sections.
  • Searches

Searches were in the last version of sscanf too, but I'm explaining them again anyway. Strings enclosed in single quotes (') are scanned for in the main string and the position moved on. Note that to search for a single quote you escape it as above using "\\":

pawn Code:
sscanf("10 11 woo 12", "i'woo'i", var0, var1);

Gives:

Code:
10
12
You could achieve the same effect with:

pawn Code:
sscanf("10 11 woo 12", "i{is[1000]}i", var0, var1);

But that wouldn't check that the string was "woo". Also note the use of "1000" for the string size. Quiet strings must still have a length, but as they aren't saved anywhere you can make this number as large as you like to cover any eventuality. Enum specifications can include search strings.
  • Enums

This is a feature similar to quiet sections, which allows you to skip overwriting certain parts of an enum:

Code:
e<ii-i-ii>
Here the "-" is a "minus", and tells sscanf that there is an enum element there, but not to do anything, so if you had:

pawn Code:
enum E
{
    E_A,
    E_B,
    E_C,
    E_D,
    E_E
}

And you only wanted to update the first two and the last fields and leave all others untouched you could use that specifier above. This way sscanf knows how to skip over the memory, and how much memory to skip. Note that this doesn't read anything, so you could also combine this with quiet sections:

Code:
e<ii-i-i{ii}i>
That will read two values and save them, skip over two memory locations, read two values and NOT save them, then read and save a last value. In this way you can have written down all the values for every slot in the enum, but have only used 3 of them. Note that this is the same with "E" - if you do:

Code:
E<ii-i-ii>
You should ONLY specify THREE defaults, not all five:

Code:
E<ii-i-ii>(11, 22, 55)
  • Delimiters

The previous version of sscanf had "p" to change the symbol used to separate tokens. This specifier still exists but it has been formalised to match the array and enum syntax. What was previously:

pawn Code:
sscanf("1,2,3", "p,iii", var0, var1, var2);

Is now:

pawn Code:
sscanf("1,2,3", "p<,>iii", var0, var1, var2);

The old version will still work, but it will give a warning. Enum specifications can include delimiters, and is the only time "<>"s are contained in other "<>"s:

pawn Code:
sscanf("1 12.0 Bob,c", "e<ifp<,>s[32]c>", var);

Note that the delimiter will remain in effect after the enum is complete. You can even use ">" as a specifier by doing "p<\>>" (or the older "p>").

When used with strings, the collection behaviour is overruled. Most specifiers are still space delimited, so for example this will work:

pawn Code:
sscanf("1 2 3", "p<;>iii", var0, var1, var2);

Despite the fact that there are no ";"s. However, strings will ONLY use the specified delimiters, so:

pawn Code:
sscanf("hello 1", "p<->s[32]i", str, var);

Will NOT work - the variable "str" will contain "hello 1". On the other hand, the example from earlier, slightly modified:

pawn Code:
sscanf("hello there>27", "p<>>s[32]i", str, var);

WILL work and will give an output of:

Code:
hello there
27
You can now have optional delimiters using "P" (upper case "p" to match other "optional" specifiers). These are optional in the sense that you specify multiple delimiters and any one of them can be used to end the next symbol:

pawn Code:
sscanf("(4, 5, 6, 7)", "P<(),>{s[2]}iiii", a, b, c, d);

This uses a "quiet section" to ignore anything before the first "(", and then uses multiple delimiters to end all the text. Example:

pawn Code:
sscanf("42, 43; 44@", "P<,;@>a<i>[3]", arr);
  • Optional specifiers

EVERY format specifier (that is, everything except '', {} and p) now has an optional equivalent - this is just their letter capitalised, so for example the old "z" optional string specifier is now "S" (there is still "z" and, for completeness, "Z", but both give warnings). In addition to optional specifiers, there are also now default values:

pawn Code:
sscanf("", "I(12)", var);

The "()"s (round brackets) contain the default value for the optional integer and, as the main string has no data, the value of "var" becomes "12". Default values come before array sizes and after specifications, so an optional array would look like:

pawn Code:
sscanf("1 2", "A<i>(3)[4]", arr);

Note that the size of the array is "4" and the default value is "3". There are also two values which are defined, so the final value of "arr" is:

Code:
1, 2, 3, 3
Array default values are clever, the final value of:

pawn Code:
sscanf("", "A<i>(3,6)[4]", arr);

Will be:

Code:
3, 6, 9, 12
The difference between "3" and "6" is "3", so the values increase by that every index. Note that it is not very clever, so:

pawn Code:
sscanf("", "A<i>(1,2,2)[4]", arr);

Will produce:

Code:
1, 2, 2, 2
The difference between "2" and "2" (the last 2 numbers in the default) is 0, so there will be no further increase. For "l" (logical) arrays, the value is always the same as the last value, as it is with "g" if the last value is one of the special values (INFINITY, NEG_INFINITY (same as -INFINITY), NAN or NAN_E). Note that:

pawn Code:
sscanf("", "a<I>(1,2,2)[4]", arr);

Is invalid syntax, the "A" must be the capital part.

Enums can also be optional:

pawn Code:
sscanf("4", "E<ifs[32]c>(1, 12.0, Bob, c)", var);

In that code all values except "4" will be default. Also, again, you can escape commas with "\\" in default enum strings. Some final examples:

pawn Code:
sscanf("1", "I(2)I(3)I(4)", var0, var1, var2);
sscanf("", "O(045)H(0xF4)B(0b0100)U(Y_Less)", octnum, hexnum, binnum, user);
sscanf("0xFF", "N(0b101)");

That last example is of a specifier not too well described yet - the "number" specifier, which will work out the format of the number from the leading characters (0x, 0b, 0 or nothing). Also note that the second example has changed - see the next section.
  • Users

The "u", "q", and "r" specifiers search for a user by name or ID. The method of this search has changed in the latest versions of "sscanf".

Additionally "U", "Q", and "R" used to take a name or ID as their default value - this has since been changed to JUST a number, and sscanf will not try and determine if this number is online:

Previous:

pawn Code:
sscanf(params, "U(Y_Less)", id);
if (id == INVALID_PLAYER_ID)
{
    // Y_Less or the entered player is not connected.
}

New:

pawn Code:
sscanf(params, "U(-1)", id);
if (id == -1)
{
    // No player was entered.
}
else if (id == INVALID_PLAYER_ID)
    // Entered player is not connected.
}

See the section on options for more details.

Users can now optionally return an ARRAY of users instead of just one. This array is just a list of matched IDs, followed by "INVALID_PLAYER_ID". Given the following players:

Code:
0) Y_Less
1) [CLAN]Y_Less
2) Jake
3) Alex
4) Hass
This code:

pawn Code:
new ids[3], i;
if (sscanf("Le", "?<MATCH_NAME_PARTIAL=1>u[3]", ids)) printf("Error in input");
for (i = 0; ids[i] != INVALID_PLAYER_ID; ++i)
{
    if (ids[i] == cellmin)
    {
        printf("Too many matches");
        break;
    }
    printf("id = %d", ids[i]);
}
if (i == 0) printf("No matching players found.");

Will output:

Code:
id = 0
id = 1
Too many matches
Searching "Les" instead will give:

Code:
id = 0
id = 1
And searching without "MATCH_NAME_PARTIAL" will give:

Code:
No matching players found.
Basically, if an array of size "N" is passed, this code will return the first N-1 results. If there are less than "N" players whose name matches the given name then that many players will be returned and the next slot will be "INVALID_PLAYER_ID" to indicate the end of the list. On the other hand if there are MORE than "N - 1" players whose name matches the given pattern, then the last slot will be "cellmin" to indicate this fact.

When combined with "U" and returning the default, the first slot is always exactly the default value (even if that's not a valid connected player) and the next slot is always "INVALID_PLAYER_ID".

Note also that user arrays can't be combined with normal arrays or enums, but normal single-return user specifiers still can be.
  • Custom (kustom) specifiers

The latest version of sscanf adds a new "k" specifier to allow you to define your own specifers in PAWN:

pawn Code:
SSCANF:playerstate(string[])
{
    if ('0' <= string[0] <= '9')
    {
        new
            ret = strval(string);
        if (0 <= ret <= 9)
        {
            return ret;
        }
    }
    else if (!strcmp(string, "PLAYER_STATE_NONE")) return 0;
    else if (!strcmp(string, "PLAYER_STATE_ONFOOT")) return 1;
    else if (!strcmp(string, "PLAYER_STATE_DRIVER")) return 2;
    else if (!strcmp(string, "PLAYER_STATE_PASSENGER")) return 3;
    else if (!strcmp(string, "PLAYER_STATE_WASTED")) return 7;
    else if (!strcmp(string, "PLAYER_STATE_SPAWNED")) return 8;
    else if (!strcmp(string, "PLAYER_STATE_SPECTATING")) return 9;
}

The code above, when added to the top level of your mode, will add the "playerstate" specifier, allowing you to do:

pawn Code:
sscanf(params, "uk<playerstate>", playerid, state);

This system supports optional custom specifiers with no additional PAWN code:

pawn Code:
sscanf(params, "uK<playerstate>(PLAYER_STATE_NONE)", playerid, state);

The new version of "sscanf2.inc" includes functions for "k<weapon>" and "k<vehicle>" allowing you to enter either the ID or name and get the ID back, but both are VERY basic at the moment and I expect other people will improve on them.

Note that custom specifiers are not supported in either arrays or enumerations.

Note also that custom specifiers always take a string input and always return a number, but this can be a Float, bool, or any other single cell tag type.

The optional kustom specifier "K" takes a default value that is NOT (as of sscanf 2.8) parsed by the given callback:

Code:
K<vehicle>(999)
"999" is NOT a valid vehicle model, but if no other value is supplied then 999 will be returned, allowing you to differentiate between the user entering an invalid vehicle and not entering anything at all.

Also as of sscanf 2.8, "k" can be used in both arrays and enums.

Options

The latest version of sscanf introduces several options that can be used to customise the way in which sscanf operates. There are two ways of setting these options - globally and locally:

pawn Code:
SSCANF_Option(SSCANF_QUIET, 1);

This sets the "SSCANF_QUIET" option globally. Every time "sscanf" is called the option (see below) will be in effect. Note that the use of:

Code:
SSCANF_QUIET
Instead of a string as:

Code:
SSCANF_QUIET
Is entirely valid here - all the options are defined in the sscanf2 include already.

Alternatively you can use "?" to specify an option locally - i.e. only for the current sscanf call:

pawn Code:
sscanf(params, "si", str, num);
sscanf(params, "?<SSCANF_QUIET=1>si", str, num);
sscanf(params, "si", str, num);

Obviously "s" without a length is deprecated, and the first and last "sscanf" calls will give a warning in the console, but the second one won't as for just that one call prints have been disabled. The following code disables prints globally then enables them locally:

pawn Code:
SSCANF_Option(SSCANF_QUIET, 1);
sscanf(params, "si", str, num);
sscanf(params, "?<SSCANF_QUIET=0>si", str, num);
sscanf(params, "si", str, num);

Note that disabling prints is a VERY bad idea when developing code as you open yourself up to unreported buffer overflows when no length is specified on strings less than 32 cells (the default length).

To specify multiple options requires multiple calls:

pawn Code:
SSCANF_Option(SSCANF_QUIET, 1);
SSCANF_Option(MATCH_NAME_PARTIAL, 0);
sscanf(params, "?<SSCANF_QUIET=1>?<MATCH_NAME_PARTIAL=0>s[10]i", str, num);

The options are:
  • OLD_DEFAULT_NAME:

    The behaviour of "U", "Q", and "R" have been changed to take any number as a default, instead of a connected player. Setting "OLD_DEFAULT_NAME" to "1" will revert to the old version.

  • MATCH_NAME_PARTIAL:

    Currently sscanf will search for players by name, and will ALWAYS search for player whose name STARTS with the specified string. If you have, say "[CLAN]Y_Less" connected and someone types "Y_Less", sscanf will not find "[CLAN]Y_Less" because there name doesn't start with the specified name. This option, when set to 1, will search ANYWHERE in the player's name for the given string.

  • CELLMIN_ON_MATCHES:

    Whatever the value of "MATCH_NAME_PARTIAL", the first found player will always be returned, so if you do a search for "_" on an RP server, you could get almost anyone. To detect this case, if more than one player will match the specified string then sscanf will return an ID of "cellmin" instead. This can be combined with "U" for a lot more power:

    pawn Code:
    sscanf(params, "?<CELLMIN_ON_MATCHES=1>U(-1)", id);
    if (id == -1)
    {
        // No player was entered.
    }
    else if (id == cellmin)
    {
        // Multiple matches found
    }
    else if (id == INVALID_PLAYER_ID)
    {
        // Entered player is not connected.
    }
    else
    {
        // Found just one player.
    }

  • SSCANF_QUIET:

    Don't print any errors to the console. REALLY not recommended unless you KNOW your code is stable and in production.

  • OLD_DEFAULT_KUSTOM:

    As with "U", "K" used to require a valid identifier as the default and would parse it using the specified callback, so this would NOT work:

    Code:
    K<vehicle>(Veyron)
    Because that is not a valid vehicle name in GTA. The new version now JUST takes a number and returns that regardless:

    Code:
    K<vehicle>(9999)
    This setting reverts to the old behaviour.


All specifiers

For quick reference, here is a list of ALL the specifiers and their use:

Specifier Format Use
A A<type>(default)[length] Optional array of given type
B B(binary) Optional binary number
C C(character) Optional character
D D(integer) Optional integer
E E<specification>(default) Optional enumeration of given layout
F F(float) Optional floating point number
G G(float/INFINITY/-INFINITY/NAN/NAN_E) Optional float with IEEE definitions
H H(hex value) Optional hex number
I I(integer) Optional integer
K K<callback>(any format number) Optional custom operator
L L(true/false) Optional logical truthity
N N(any format number) Optional number
O O(octal value) Optional octal value
P P<delimiters> Multiple delimiters change
Q Q(any format number) Optional bot (bot)
R R(any format number) Optional player (player)
S S(string)[length] Optional string
U U(any format number) Optional user (bot/player)
Z Z(string)[length] Invalid optional string
a a<type>[length] Array of given type
b b Binary number
c c Character
d d Integer
e e<specification> Enumeration of given layout
f f Floating point number
g g Float with IEEE definitions
h h Hex number
i i Integer
k k<callback> Custom operator
l l Logical truthity
n n Number
o o Octal value
p p<delimiter> Delimiter change
q q Bot (bot)
s s[length] String
r r Player (player)
u u User (bot/player)
z z(string)[length] Deprecated optional string
{ { Open quiet section
} } Close quiet section
' 'string' Search string
% % Deprecated optional specifier prefix
? ? Local options specifier

"extract"

I've written some (extendable) macros so you can do:

pawn Code:
extract params -> new a, string:b[32], Float:c; else
{
    return SendClientMessage(playerid, COLOUR_RED, "FAIL!");
}

This will compile as:

pawn Code:
new a, string:b[32], Float:c;
if (unformat(params, "is[32]f", a, b, c))
{
    return SendClientMessage(playerid, COLOUR_RED, "FAIL!");
}

Note that "unformat" is the same as "sscanf", also note that the "SendClientMessage" part is optional:

pawn Code:
extract params -> new a, string:b[32], Float:c;

Will simply compile as:

pawn Code:
new a, string:b[32], Float:c;
unformat(params, "is[32]f", a, b, c);

Basically it just simplifies sscanf a little bit (IMHO). I like new operators and syntax, hence this, examples:

pawn Code:
// An int and a float.
extract params -> new a, Float:b;
// An int and an OPTIONAL float.
extract params -> new a, Float:b = 7.0;
// An int and a string.
extract params -> new a, string:s[32];
// An int and a playerid.
extract params -> new a, player:b;

As I say, the syntax is extendable, so to add hex numbers you would do:

pawn Code:
#define hex_EXTRO:%0##%1,%2|||%3=%9|||%4,%5) EXTRY:%0##%1H"("#%9")"#,%2,%3|||%4|||%5)
#define hex_EXTRN:%0##%1,%2|||%3|||%4,%5) EXTRY:%0##%1h,%2,%3|||%4|||%5)
#define hex_EXTRW:%0##%1,%2|||%3[%7]|||%4,%5) EXTRY:%0##%1a<h>[%7],%2,%3|||%4|||%5)

That will add the tag "hex" to the system. Yes, the lines look complicated (because they are), but the ONLY things you need to change are the name before the underscore and the letter near the middle ("H", "h" and "a<h>" in the examples above for "optional", "required" and "required array" (no optional arrays yet besides strings)).

New examples (with "hex" added):

pawn Code:
// A hex number and a player.
extract params -> new hex:a, player:b;
// 32 numbers then 32 players.
extract params -> new a[32], player:b[32];
// 11 floats, an optional string, then an optional hex number.
extract params -> new Float:f[11], string:s[12] = "optional", hex:end = 0xFF;

The code is actually surprisingly simple (I developed another new technique to simplify my "tag" macros and it paid off big style here). By default "Float", "string", "player" and "_" (i.e. no tag) are supported, and their individual letter definitions take up the majority of the code as demonstrated with the "hex" addition above. Note that "string:" is now used extensively in my code to differentiate from tagless arrays in cases like this, it is removed automatically but "player:" and "hex:" are not so you may wish to add:

pawn Code:
#define player:
#define hex:

To avoid tag mismatch warnings (to remove them AFTER the compiler has used them to determine the correct specifier).

The very first example had an "else", this will turn:

pawn Code:
unformat(params, "ii", a, b);

In to:

pawn Code:
if (unformat(params, "ii", a, b))

You MUST put the "else" on the same line as "extract" for it to be detected, but then you can use normal single or multi-line statements. This is to cover common command use cases, you can even leave things on the same line:

pawn Code:
else return SendClientMessage(playerid, 0xFF0000AA, "Usage: /cmd <whatever>");

There is now the ability to split by things other than space (i.e. adds "P<?>" to the syntax - updated from using "p" to "P"):

pawn Code:
extract params<|> -> new a, string:b[32], Float:c;

Will simply compile as:

pawn Code:
new a, string:b[32], Float:c;
unformat(params, "P<|>is[32]f", a, b, c);

Note that for technical reasons you can use "<->" (because it looks like the arrow after the "extract" keyword). You also can't use "<;>", "<,>", or "<)>" because of a bug with "#", but you can use any other character (most notably "<|>", as is popular with SQL scripts). I'm thinking of adding enums and existing variables (currently you HAVE to declare new variables), but not right now.

Kustomisation

This is a list of all the know-to-me third-party "k" specifiers (custom specifiers written by other people). Usage is either "k<name>" or "K<name>(default)". The names are the section headers below.
  • player_name
  • Author: Slice
  • Code: http://forum.sa-mp.com/showpost.php?...postcount=2249
  • Usage: The default "u" specifier does not detect names such as "[ABC]Def" when only "Def" is typed. This is an implementation of "u" providing alternate behaviour in this case and returns "INVALID_PLAYER_ID" when multiple matching names are found (could be changed to another value to differentiate between no found players and multiple players).

Errors/Warnings
  • MSVRC100.dll Not Found

If you get this error, DO NOT just download the dll from a random website (click here for why). This is part of the "Microsoft Visual Studio Redistributable Package". This is required for many programs, but they often come with it. Download it here:

http://www.microsoft.com/download/en...s.aspx?id=5555
  • sscanf error: System not initialised.

If you get this error, you need to make sure that you have recompiled ALL your scripts using the LATEST version of "sscanf2.inc". Older versions didn't really require this as they only had two natives - "sscanf" and "unformat", the new version has some other functions - you don't need to worry about them, but you must use "sscanf2.inc" so that they are correctly called. If you think you have done this and STILL get the error then try again - make sure you are using the correct version of PAWNO for example.
  • sscanf warning: String buffer overflow.

This error comes up when people try and put too much data in to a string. For example:

pawn Code:
new str[10];
sscanf("Hello there, how are you?", "s[10]", str);

That code will try and put the string "Hello there, how are you?" in to the variable called "str". However, "str" is only 10 cells big and can thus only hold the string "Hello ther" (with a NULL terminator). In this case, the rest of the data is ignored - which could be good or bad:

pawn Code:
new str[10], num;
sscanf("Hello there you|42", "p<|>s[10]i", str, num);

In this case "num" is still correctly set to "42", but the warning is given for lost data ("e you").

Currently there is nothing you can do about this from a programming side (you can't even detect it - that is a problem I intend to address), as long as you specify how much data a user should enter this will simply discard the excess, or make the destination variable large enough to handle all cases.
  • sscanf warning: Optional types invalid in array specifiers, consider using 'A'.

A specifier such as:

Code:
a<I(5)>[10]
Has been written - here indicating an array of optional integers all with the default value "5". Instead you should use:

Code:
A<i>(5)[10]
This is an optional array of integers all with the default value "5", the advantage of this is that arrays can have multiple defaults:

Code:
A<i>(5, 6)[10]
That will set the array to "5, 6, 7, 8, 9, 10, 11, 12, 13, 14" by default, incrementing by the found difference each time.
  • sscanf warning: Optional types invalid in enum specifiers, consider using 'E'.

Similar to the previous warning, A specifier such as:

Code:
e<I(5)f>
Is invalid, instead use:

Code:
E<if>(42, 11.0)
This forces ALL the parts of an enum to be optional - anything less is not possible.
  • sscanf error: Multi-dimensional arrays are not supported.

This is not allowed:

pawn Code:
sscanf(params, "a<a<i>[5]>[10]", arr);

A work-around can be done using:

pawn Code:
sscanf(params, "a<i>[50]", arr[0]);

That will correctly set up the pointers for the system.
  • sscanf error: Search strings are not supported in arrays.

This is not allowed (see the section on search strings):

Code:
a<'hello'i>[10]
  • sscanf error: Delimiters are not supported in arrays.

This is not allowed:

Code:
a<p<,>i>[10]
Instead use:

Code:
p<,>a<i>[10]
  • sscanf error: Quiet sections are not supported in arrays.

This is not allowed:

Code:
a<{i}>[10]
Instead use:

Code:
{a<i>[10]}
  • sscanf error: Unknown format specifier '?'.

The given specifier is not known (this post contains a full list of all the specifiers near the bottom).
  • sscanf warning: 'Z' doesn't exist - that would be an optional, deprecated optional string!.

You used "Z", don't; instead use "S".
  • sscanf warning: 'z' is deprecated, consider using 'S' instead.

You used "z", don't; instead use "S".
  • sscanf warning: Empty default values.

An optional specifier has been set as (for example):

Code:
I()
Instead of:

Code:
I(42)
This does not apply to strings as they can be legitimately empty.
  • sscanf warning: Unclosed default value.

You have a default value on an optional specifier that looks like:

Code:
I(42
Instead of:

Code:
I(42)
  • sscanf warning: No default value found.

You have no default value on an optional specifier:

Code:
I
Instead of:

Code:
I(42)
  • sscanf warning: Unclosed specifier parameter, assuming '<', consider using something like p<<>.

A custom delimiter of:

Code:
p<
Was found with no matching ">" after one character. In this case the system assumes you are using the old (deprecated) style of delimiters and sets it to just "<". Instead use:

Code:
p<,>
Or, if you really do want a delimiter of "<" then use:

Code:
p<<>
Note that this does not need to be escaped; however, a delimiter of ">" does:

Code:
p<\>>
The "\" may also need to be escaped when writing actual PAWN strings, leading to:

Code:
p<\\>>
This also applies to array types ("a<" vs "a<i>"), note that this will result in an invalid array type.
  • sscanf warning: Unenclosed specifier parameters are deprecated, consider using something like p<<>.

You are using the old style:

Code:
p,
Instead of:

Code:
p<,>
This also applies to array types ("ai" vs "a<i>").
  • sscanf warning: No specified parameter found.

The format specifier just ends with:

Code:
p
This also applies to array types ("a" vs "a<i>").
  • sscanf warning: Missing string length end.
  • sscanf warning: Missing length end.

A string has been written as:

Code:
s[10
Instead of:

Code:
s[10]
I.e. the length has not been closed.
  • sscanf warning: Arrays without a length are deprecated, please add a destination size.

A string has been written as:

Code:
s
Instead of:

Code:
s[10]
I.e. the length has not been included.
  • sscanf error: Invalid data length.

An invalid array or string size has been specified (0, negative, or not a number).
  • sscanf warning: Invalid character in data length.

A string or array has been given a length that is not a number.
  • sscanf warning: Strings without a length are deprecated, please add a destination size.

In the old system, strings were not required to have lengths but this introduced security problems with overflows. Now you must add a length or get the default of "32".
  • sscanf error: String/array must include a length, please add a destination size.

Arrays are newer than strings, so never had an implementation not requiring a length, so there is no compatability problems in REQUIRING a length to be given.
  • sscanf warning: Can't have nestled quiet sections.

You have tried writing something like this:

Code:
{i{x}}
This has a quiet section ("{}") inside another one, which makes no sense.
  • sscanf warning: Not in a quiet section.

"}" was found with no corresponding "{":

Code:
i}
  • sscanf warning: Can't remove quiet in enum.

This is caused by specifiers such as:

Code:
{fe<i}x>
Where the quiet section is started before the enum, but finishes part way through it rather than after it. This can be emulated by:

Code:
{f}e<{i}x>
  • sscanf error: Arrays are not supported in enums.

Basically, you can't do:

Code:
e<fa<i>[5]f>
You can, however, still do:

Code:
e<fiiiiif>
This is a little more awkward, but is actually more technically correct given how enums are compiled.
  • sscanf warning: Unclosed string literal.

A specifier starts a string with "'", but doesn't close it:

Code:
i'hello
  • sscanf warning: sscanf specifiers do not require '%' before them.

"format" uses code such as "%d", sscanf only needs "d", and confusingly the C equivalent function (also called "sscanf") DOES require "%". Sorry.
  • sscanf error: Insufficient default values.

Default values for arrays can be partially specified and the remainder will be inferred from the pattern of the last two:

Code:
A<i>(0, 1)[10]
That specifier will default to the numbers "0" to "9". However, because enums have a mixture of types, all the default values for "E" must ALWAYS be specified:

Code:
E<iiff>(0, 1, 0.0, 1.0)
This will not do:

Code:
E<iiff>(0, 1)
  • sscanf error: Options are not supported in enums.
  • sscanf error: Options are not supported in arrays.

The "?" specifier for local options must appear outside any other specifier.
  • sscanf error: No option value.

An option was specified with no value:

Code:
?<OLD_DEFAULT_NAME>
  • sscanf error: Unknown option name.

The given option was not recognised. Check spelling and case:

Code:
?<NOT_A_VALID_NAME=1>
  • sscanf warning: Could not find function SSCANF:?.

A "k" specifier has been used, but the corresponding function could not be found. If you think it is there check the spelling matches exactly - including the case.
  • sscanf error: SSCANF_Init has incorrect parameters.
  • sscanf error: SSCANF_Join has incorrect parameters.
  • sscanf error: SSCANF_Leave has incorrect parameters.
  • sscanf error: SSCANF_SetPlayerName has incorrect parameters.

You edited something in the sscanf2 include - undo it or redownload it.

"fixes2" Plugin

The "fixes2" plugin can detect and parse server error messages - exactly the same error messages that "sscanf2" uses to alert scripters to potential problems such as attempted buffer overflows. This means that if you have that plugin too you can catch and use these error messages like so:

pawn Code:
public OnServerMessage(const msg[])
{
    if (!strcmp(msg, "sscanf warning: ", false, 16))
    {
        if (!strcmp(msg[16], "String buffer overflow."))
        {
            SendClientMessage(gLastPlayer, X11_RED, "Please type something shorter");
        }
    }
}

The "fixes2" plugin can be obtained here:

http://forum.sa-mp.com/showthread.ph...75#post2106275

Conclusion

See if you can figure out what, with this new version, this does (note that this is an extreme example):

pawn Code:
"{S("f<5>")if}jE<np<l>{u}ns[" #LEN "]g>(0777, 5, 0xF2, hi\\, there, NAN_E)A<n>(0b1010, 012, 10, 0xA)[11]"

Changelog
  • Update 13

Added multiple name returns.

Changed "K" to match "U" in default values.

Added "OLD_DEFAULT_KUSTOM" option.
  • Update 12

Added partial name matching.

Changed "U", "Q", and "R" to take default values that aren't valid players.

Added options.

Switched to using ALS 2 in the include.

Added all error messages to this post.

Added arrays of strings.

Added optional delimiters to have more than one at once.
  • Update 11

Fixed the problem with "OnPlayerUpdate" not being called AGAIN!
  • Update 10

OK, I've compiled the plugin for Linux (on Ubuntu), and blank. has provided an additional .so for CentOS if the Ubuntu version doesn't work (should do in most Linux cases). The new sscanf package is available here:

http://dl.dropbox.com/u/21683085/sscanf(2).rar

The CentOS .so is available here:

http://www.sendspace.com/file/qgdgnc
  • Update 9

Finally fixed the incorrect IDs problem thanks to leong124. Currently only compiled for Windows as my Linux box is broken - any help in this regard would be vastly appreciated:

http://dl.dropbox.com/u/21683085/sscanf.rar
  • Update 8

Fixed a small bug. New download:

http://dl.dropbox.com/u/21683085/sscanf%281%29.rar
  • Update 7

I have FINALLY written an entirely future-compatible, memory-hack-free, global version of sscanf. This will work for all versions of the server, past and present, and can be downloaded from here:

http://dl.dropbox.com/u/21683085/sscanf.rar

This wasn't done before as I was hesitant about using the GDK which has bugs, and didn't want to use other methods to call "GetPlayerName" for speed reasons, but this version does it a different way entirely (hooks "OnPlayerConnect").

This version also adds the new "k" specifier, described above.
  • Update 6

sscanf for 0.3dR2 (500 and 800 player versions):

http://www.mediafire.com/?bfp2h0d4231jjdr

Thanks to dnee`THA.
  • Update 5

Quote:
Originally Posted by Thomas. View Post
For those of you having the "missing MSVCR100.dll" error; install the x86 version of the C++ 2010 redistributable package, no matter if your system is 64-bit or not.
Or just recompile the plugin. The old download contains a solution for Visual Studio 2008, the new download contains a solution for Visual Studio 2010. Make sure you use the latest files, just replace the old ones if you're using the old solution - everything else is the same.
  • Update 4

Full final 0.3d version. I'm getting fairly smooth at updating things now, which is good! I've also fixed the "Format specifier does not match parameter count" bug. Download source, Linux and Windows plugins here:

http://dl.dropbox.com/u/21683085/sscanf-0.3d.rar

Note that this version is for 0.3d ONLY. I removed the other code as maintaining the code to detect which was the current version was getting inefficient.
  • Update 3

Thanks to Scott there is a new temporary fix for for 0.3d on Windows using ZeeK's GDK plugin. The download also includes a new version of the streamer plugin for those of you who use it to make the two work together better:

http://dl.dropbox.com/u/44207623/source.rar
  • Update 2

Fixed another major bug. Everyone using sscanf please update now! Note however that the .so file hasn't been updated yet.

Edit: Now it has been thanks to Calg00ne.
  • Update 1

Fixed a major bug (thanks pyrodave).

The name checks are now case insensitive after lots of requests.

They also now refuse names which are too long. The old version had a bug where "Y_Lessmoo" would have matched a player called "Y_Less".
__________________

Last edited by Y_Less; 18/07/2013 at 06:30 PM.
Y_Less is offline   Reply With Quote
Old 11/01/2010, 12:49 AM   #2
pliva_sb
Big Clucker
 
pliva_sb's Avatar
 
Join Date: Mar 2009
Location: Croatia
Posts: 127
Reputation: 1
Default Re: [REL] sscanf 2.0

Again Nice work Y_less,Well done.
pliva_sb is offline   Reply With Quote
Old 11/01/2010, 01:02 AM   #3
Zeex
Guest
 
Posts: n/a
Default Re: [REL] sscanf 2.0

Wow!

----

I have some questions... What is the defference between boolean and usual integer?
Also what is "l" for? It can perceive values like "yes/no", "true/false", "on/off", etc? Or it is something other?


----

Oh I found what you wrote about boolean above (but I still have no clue about the second thing...)

----

Oh god I really should read better

----

Hm, won't this name (the same as the scripted one has) cause a confusion?
  Reply With Quote
Old 11/01/2010, 03:06 AM   #4
Kalcor
SA-MP Developer
 
Join Date: Apr 2005
Posts: 612
Reputation: 1800
Default Re: [REL] sscanf 2.0

Nice work.

I agree with the above poster though. This function should not be called sscanf because its format syntax doesn't conform to the C standards like printf() and format() do.
Kalcor is offline   Reply With Quote
Old 11/01/2010, 03:31 AM   #5
Kyosaur
High-roller
 
Kyosaur's Avatar
 
Join Date: Mar 2008
Location: USA - California
Posts: 1,058
Reputation: 301
Default Re: [REL] sscanf 2.0

Amazing work! arrays/enums *drool*
__________________
[Tutorial]: Plugin development guide.
[Tutorial]: An indepth look at binary.


Follow me on twitter for updates on all my projects.
Kyosaur is offline   Reply With Quote
Old 11/01/2010, 04:14 AM   #6
Zamaroht
High-roller
 
Zamaroht's Avatar
 
Join Date: Jan 2007
Location: Argentina
Posts: 1,209
Reputation: 41
Default Re: [REL] sscanf 2.0

Amazing work Y_Less.

Regarding that last example that you posted, you wrote a 'j' as the 14th character, and you aren't explaining what it is, so I'll just remove it and give it a try :P:

pawn Code:
#define LEN 10

enum E_DATA
{
  E_NUMBER,
  E_USER,
  E_NUMBER2,
  E_STRING[LEN],
  Float:E_FLOATIEEE
}

new EnumVar[E_DATA],
  Array[11];

sscanf("hi 1 2.0 34 Zamarohtl29lwordlINFINITY 0xAC", "{S(\"f<5>\")if}E<np<l>{u}ns[" #LEN "]g>(0777, 5, 0xF2, hi\\, there, NAN_E)A<n>(0b1010, 012, 10, 0xA)[11]", EnumVar, Array);

printf("%d, %d, %d, %s, %f", EnumVar[E_NUMBER], EnumVar[E_USER], EnumVar[E_NUMBER2], EnumVar[E_STRING], EnumVar[E_FLOATIEEE]);

for (new i, j = sizeof(Array); i < j; i ++)
{
  printf("%d", Array[i]);
}

My output would be:

pawn Code:
34, 0, 29, wordere, 0x7F800000
2832
12
10
10
10
10
10
10
10
10
10

Suppusing that the user named Zamaroht was id 0, right?
Zamaroht is offline   Reply With Quote
Old 11/01/2010, 06:55 AM   #7
Sergei
High-roller
 
Sergei's Avatar
 
Join Date: Mar 2008
Location: Slovenia
Posts: 2,662
Reputation: 112
Default Re: [REL] sscanf 2.0

Awesome. I saw post about it on Twitter about it and now it's finally here
__________________
MySQL plugin R7 by BlueG
Documentation located on SA:MP wiki
Sergei is offline   Reply With Quote
Old 11/01/2010, 07:00 AM   #8
Correlli
Godfather
 
Correlli's Avatar
 
Join Date: May 2009
Posts: 7,035
Reputation: 102
Default Re: [REL] sscanf 2.0

Awesome work Y_Less.
Correlli is offline   Reply With Quote
Old 15/11/2010, 07:13 PM   #9
HfV.pT
Big Clucker
 
Join Date: Oct 2010
Posts: 59
Reputation: 0
Default Re: [REL] sscanf 2.0

Good work!
HfV.pT is offline   Reply With Quote
Old 11/01/2010, 08:27 AM   #10
Y_Less
Beta Tester
 
Y_Less's Avatar
 
Join Date: Jun 2008
Location: 629
Posts: 16,045
Reputation: 2280
Default Re: [REL] sscanf 2.0

Quote:
Originally Posted by Kye
Nice work.

I agree with the above poster though. This function should not be called sscanf because its format syntax doesn't conform to the C standards like printf() and format() do.
Wow, thanks. And possibly not, but it's fairly easy to change the name, I guess you could just do:

pawn Code:
native unformat(const sting[], const pattern[], {Float,_}:...) = sscanf;

I may include that later and deprecate the old name.

Quote:
Originally Posted by Zamaroht
Suppusing that the user named Zamaroht was id 0, right?
If you ran the code, then I think you've found a small bug, if not then you were so nearly right - "012" is an octal number and it's value is also 10 (hence the leading 0), 0xAC is only 172, not 2832 and although I mentioned that "INFINITY 0xAC" is valid even after you change the string delimiter, I should just mention again that delimters introduced within enums still apply afterwards.

Also, minor side note:

pawn Code:
for (new i, j = sizeof(Array); i < j; i ++)
{
  printf("%d", Array[i]);
}

sizeof is an operator, not a function, so is evaluated at compile time, meaning you code is actually SLOWER than doing:

pawn Code:
for (new i; i < sizeof(Array); i ++)
{
  printf("%d", Array[i]);
}

Soryy if that was just a quick example, but I don't think many people realise that - your code was good had sizeof been a regular function.

Quote:
Originally Posted by Seif_
Great job. How much faster is it from the old sscanf?
It's very hard to tell given that there are so many possible execution paths. I did some tests and I think it came out over 10 times faster, but I would have to check again.

Quote:
Originally Posted by ZeeX
Hm, won't this name (the same as the scripted one has) cause a confusion?
The idea was that it would be a direct replacement, but again you can use the code I posted above to rename it (or just a regular define, but those are less compiler checkable). As for the documentation, I wouldn't be surprised if I needed to update it quite a lot.
__________________
Y_Less 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
Macros (?) for enums/arrays MP2 Scripting Help 16 17/01/2012 08:37 PM
Enums - string arrays SuperViper Scripting Help 3 31/10/2011 11:00 AM
Arrays and enums... [MWR]Blood Scripting Help 9 23/09/2011 11:29 AM
Enums and Arrays Th3Angel Help Archive 6 15/02/2011 12:46 AM


All times are GMT. The time now is 09:18 AM.


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