|
|
|
|
#1 | |
|
Beta Tester
![]() ![]() ![]() ![]() ![]() Join Date: Jun 2008
Location: 629
Posts: 13,340
Reputation: 1911
|
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
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:
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 Code:
plugins sscanf.so pawn Code:
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:
There is also an alternate function name to avoid confusion with the C standard sscanf: pawn Code:
Specifiers The available specifiers (the letters "u", "i" and "s" in the codes above) are below.
Code:
Specifier(s) Name Example values i, d Integer 1, 42, -10 c Character a, o, * l Logical true, false b Binary 01001, 0b1100 h, x Hex 1A, 0x23 o Octal 045 12 n Number 42, 0b010, 0xAC, 045 f Float 0.7, -99.5 g IEEE Float 0.7, -99.5, INFINITY, -INFINITY, NAN, NAN_E u User name/id (bots and players) Y_Less, 0 q Bot name/id ShopBot, 27 r Player name/id Y_Less, 42
The specifier "s" is used, as before, for strings - but they are now more advanced. As before they support collection, so doing: pawn Code:
Will give: Code:
hello 27 pawn Code:
Will fail as "there" is not a number. However doing: pawn Code:
Will give: Code:
hello there pawn Code:
Will give: Code:
hello pawn Code:
Will give: Code:
hello there 27 pawn Code:
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:
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.
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:
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] Code:
A<s[10]>(hello)[12] Code:
A<i>(0, 1)[4] Code:
0, 1, 2, 3 Code:
A<s[10]>(hi, there)[4] Code:
"hi, there", "hi, there", "hi, there", "hi, there" Code:
A<s[10]>(hi (code\))[4] Code:
A<s[10]>(This is longer than 10 characters)[4]
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:
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
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:
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:
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:
You can also embed quiet sections inside enum specifications: pawn Code:
Quiet sections cannot contain other quiet sections, however they can include enums which contain quiet sections.
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:
Gives: Code:
10 12 pawn Code:
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.
This is a feature similar to quiet sections, which allows you to skip overwriting certain parts of an enum: Code:
e<ii-i-ii> pawn Code:
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>
Code:
E<ii-i-ii> Code:
E<ii-i-ii>(11, 22, 55)
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:
Is now: pawn Code:
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:
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:
Despite the fact that there are no ";"s. However, strings will ONLY use the specified delimiters, so: pawn Code:
Will NOT work - the variable "str" will contain "hello 1". On the other hand, the example from earlier, slightly modified: pawn Code:
WILL work and will give an output of: Code:
hello there 27 pawn Code:
This uses a "quiet section" to ignore anything before the first "(", and then uses multiple delimiters to end all the text. Example: pawn Code:
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:
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:
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 pawn Code:
Will be: Code:
3, 6, 9, 12 pawn Code:
Will produce: Code:
1, 2, 2, 2 pawn Code:
Is invalid syntax, the "A" must be the capital part. Enums can also be optional: pawn Code:
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:
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.
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:
New: pawn Code:
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 pawn Code:
Will output: Code:
id = 0 id = 1 Too many matches Code:
id = 0 id = 1 Code:
No matching players found. 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.
The latest version of sscanf adds a new "k" specifier to allow you to define your own specifers in PAWN: pawn Code:
The code above, when added to the top level of your mode, will add the "playerstate" specifier, allowing you to do: pawn Code:
This system supports optional custom specifiers with no additional PAWN code: pawn Code:
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) 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:
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 Code:
SSCANF_QUIET Alternatively you can use "?" to specify an option locally - i.e. only for the current sscanf call: pawn Code:
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:
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:
The options are:
All specifiers For quick reference, here is a list of ALL the specifiers and their use: Code:
Format Use
L(true/false) Optional logical truthity
l Logical truthity
K<callback>(any format number) Optional custom operator
k<callback> Custom operator
B(binary) Optional binary number
b Binary number
N(any format number) Optional number
n Number
C(character) Optional character
c Character
I(integer) Optional integer
i Integer
D(integer) Optional integer
d Integer
H(hex value) Optional hex number
h Hex number
O(octal value) Optional octal value
o Octal value
F(float) Optional floating point number
f Floating point number
G(float/INFINITY/-INFINITY/NAN/NAN_E) Optional float with IEEE definitions
g Float with IEEE definitions
{ Open quiet section
} Close quiet section
P<delimiters> Multiple delimiters change
p<delimiter> Delimiter change
Z(string)[length] Invalid optional string
z(string)[length] Deprecated optional string
S(string)[length] Optional string
s[length] String
U(any format number) Optional user (bot/player)
u User (bot/player)
Q(any format number) Optional bot (bot)
q Bot (bot)
R(any format number) Optional player (player)
r Player (player)
A<type>(default)[length] Optional array of given type
a<type>[length] Array of given type
E<specification>(default) Optional enumeration of given layout
e<specification> Enumeration of given layout
'string' Search string
% Deprecated optional specifier prefix
? Local options specifier
I've written some (extendable) macros so you can do: pawn Code:
This will compile as: pawn Code:
Note that "unformat" is the same as "sscanf", also note that the "SendClientMessage" part is optional: pawn Code:
Will simply compile as: pawn Code:
Basically it just simplifies sscanf a little bit (IMHO). I like new operators and syntax, hence this, examples: pawn Code:
As I say, the syntax is extendable, so to add hex numbers you would do: pawn Code:
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:
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:
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:
In to: pawn Code:
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:
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:
Will simply compile as: pawn Code:
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.
Errors/Warnings
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
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.
This error comes up when people try and put too much data in to a string. For example: pawn Code:
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:
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.
A specifier such as: Code:
a<I(5)>[10] Code:
A<i>(5)[10] Code:
A<i>(5, 6)[10]
Similar to the previous warning, A specifier such as: Code:
e<I(5)f> Code:
E<if>(42, 11.0)
This is not allowed: pawn Code:
A work-around can be done using: pawn Code:
That will correctly set up the pointers for the system.
This is not allowed (see the section on search strings): Code:
a<'hello'i>[10]
This is not allowed: Code:
a<p<,>i>[10] Code:
p<,>a<i>[10]
This is not allowed: Code:
a<{i}>[10]
Code:
{a<i>[10]}
The given specifier is not known (this post contains a full list of all the specifiers near the bottom).
You used "Z", don't; instead use "S".
You used "z", don't; instead use "S".
An optional specifier has been set as (for example): Code:
I() Code:
I(42)
You have a default value on an optional specifier that looks like: Code:
I(42 Code:
I(42)
You have no default value on an optional specifier: Code:
I Code:
I(42)
A custom delimiter of: Code:
p< Code:
p<,> Code:
p<<> Code:
p<\>> Code:
p<\\>>
You are using the old style: Code:
p, Code:
p<,>
The format specifier just ends with: Code:
p
A string has been written as: Code:
s[10 Code:
s[10]
A string has been written as: Code:
s Code:
s[10]
An invalid array or string size has been specified (0, negative, or not a number).
A string or array has been given a length that is not a number.
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".
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.
You have tried writing something like this: Code:
{i{x}}
"}" was found with no corresponding "{": Code:
i}
This is caused by specifiers such as: Code:
{fe<i}x>
Code:
{f}e<{i}x>
Basically, you can't do: Code:
e<fa<i>[5]f> Code:
e<fiiiiif>
A specifier starts a string with "'", but doesn't close it: Code:
i'hello
"format" uses code such as "%d", sscanf only needs "d", and confusingly the C equivalent function (also called "sscanf") DOES require "%". Sorry.
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] Code:
E<iiff>(0, 1, 0.0, 1.0) Code:
E<iiff>(0, 1)
The "?" specifier for local options must appear outside any other specifier.
An option was specified with no value: Code:
?<OLD_DEFAULT_NAME>
The given option was not recognised. Check spelling and case: Code:
?<NOT_A_VALID_NAME=1>
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.
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:
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:
Changelog
Added multiple name returns. Changed "K" to match "U" in default values. Added "OLD_DEFAULT_KUSTOM" option.
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.
Fixed the problem with "OnPlayerUpdate" not being called AGAIN!
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
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
Fixed a small bug. New download: http://dl.dropbox.com/u/21683085/sscanf%281%29.rar
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.
sscanf for 0.3dR2 (500 and 800 player versions): http://www.mediafire.com/?bfp2h0d4231jjdr Thanks to dnee`THA.
Quote:
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.
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
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.
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; 12/03/2013 at 10:19 PM. |
|
|
|
|
|
|
#2 |
|
Big Clucker
![]() ![]() Join Date: Mar 2009
Location: Croatia
Posts: 125
Reputation: 1
|
Again Nice work Y_less,Well done.
|
|
|
|
|
|
#3 |
|
Guest
Posts: n/a
|
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? |
|
|
|
#4 |
|
SA-MP Developer
![]() ![]() ![]() ![]() ![]() Join Date: Apr 2005
Posts: 574
Reputation: 1508
|
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. |
|
|
|
|
|
#5 |
|
High-roller
![]() ![]() ![]() ![]() ![]() Join Date: Mar 2008
Location: USA - California
Posts: 1,024
Reputation: 260
|
Amazing work! arrays/enums *drool*
|
|
|
|
|
|
#6 |
|
High-roller
![]() ![]() ![]() ![]() ![]() Join Date: Jan 2007
Location: Argentina
Posts: 1,209
Reputation: 40
|
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:
My output would be: pawn Code:
Suppusing that the user named Zamaroht was id 0, right? |
|
|
|
|
|
#7 |
|
High-roller
![]() ![]() ![]() ![]() ![]() Join Date: Mar 2008
Location: Slovenia
Posts: 2,611
Reputation: 57
|
Awesome. I saw post about it on Twitter about it and now it's finally here
|
|
|
|
|
|
#8 |
|
Godfather
![]() ![]() ![]() ![]() ![]() ![]() Join Date: May 2009
Posts: 7,012
Reputation: 83
|
Awesome work Y_Less.
|
|
|
|
|
|
#9 |
|
Big Clucker
![]() ![]() Join Date: Oct 2010
Location: Portugal - Braga
Posts: 59
Reputation: 0
|
Good work!
|
|
|
|
|
|
#10 | ||||
|
Beta Tester
![]() ![]() ![]() ![]() ![]() Join Date: Jun 2008
Location: 629
Posts: 13,340
Reputation: 1911
|
Quote:
pawn Code:
I may include that later and deprecate the old name. Quote:
Also, minor side note: pawn Code:
sizeof is an operator, not a function, so is evaluated at compile time, meaning you code is actually SLOWER than doing: pawn Code:
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:
Quote:
|
||||
|
|
|
![]() |
| Thread Tools | |
| Display Modes | |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| Macros (?) for enums/arrays | MP2 | Scripting Help | 16 | 17/01/2012 07:37 PM |
| Enums - string arrays | SuperViper | Scripting Help | 3 | 31/10/2011 10:00 AM |
| Arrays and enums... | [MWR]Blood | Scripting Help | 9 | 23/09/2011 11:29 AM |
| Enums and Arrays | Th3Angel | Help Archive | 6 | 14/02/2011 11:46 PM |