/* * sscanf 2.13.8 * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the sscanf 2.0 SA:MP plugin. * * The Initial Developer of the Original Code is Alex "Y_Less" Cole. * Portions created by the Initial Developer are Copyright (c) 2022 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Cheaterman * DEntisT * Emmet_ * karimcambridge * kalacsparty * Kirima * leHeix * maddinat0r * Southclaws * Y_Less * ziggi * * Special Thanks to: * * SA:MP Team past, present, and future. * maddinat0r, for hosting the repo for a very long time. * Emmet_, for his efforts in maintaining it for almost a year. */ #if defined _INC_SSCANF #endinput #endif #define _INC_SSCANF #if !defined _samp_included #error Please include or first. #endif /** * * * This library uses the enhanced pawndoc.xsl from * pawn-lang/pawndoc. * This XSL has features such as library and markdown support, and will not * render this message when used. * * * This is the sscanf plugin, which provides the sscanf * function to extract basic structured data from strings. This is slightly * different to regular expressions, but both have their place. A regular * expression gives you total control over the exact structure of data down * to the character level; however, extracting structured data like numbers * using it is tricky. Conversely this gives slightly higher-level * specifiers which can easily extract data types, at the expense * of fine-grained control. To convert a string in to two numbers would * look like: * * * new num1, num2;
* sscanf("45 100", "ii", num1, num2); *
* * ii is the specifier string, which here means "integer integer"; * stating that the input string should be two whole numbers in a row (which * is - "45 100"). num1 and num2 are the destination * variables to store the found numbers in (after conversion from strings). * You can check if the conversion failed by looking for a non-naught return * value: * * * new num1, num2;
* if (sscanf("hello 100", "ii", num1, num2))
* {
* printf("The input was not two numbers.");
* } *
* * This will fail because "hello" is not a whole number (or indeed * any type of number at all). For more information on using the function * refer to the tutorials or the reference documentation in * the * attached readme. *
*
*/ ///

#define SSCANF_STATIC__ #if defined __PawnBuild #if __PawnBuild == 11 // `const static` support. #undef SSCANF_STATIC__ #define SSCANF_STATIC__ static #endif #else #if !defined SSCANF_NO_NICE_FEATURES #error sscanf utilises community compiler features. Use `#define SSCANF_NO_NICE_FEATURES` to live without (if you can call that living) or better yet download it here: github.com/pawn-lang/compiler/releases #endif #endif /** * sscanf * * Was sscanf built for an NPC mode or a normal mode? * */ #if defined GetDistanceFromMeToPoint // NPC script. static stock SSCANF_NPC = 1; #define SSCANF_NPC (1) #pragma library sscanf #else static stock SSCANF_NPC = 0; #define SSCANF_NPC (0) #endif /** * sscanf * * The sscanf major version number. * */ static stock SSCANF_VERSION_MAJOR = 2; #define SSCANF_VERSION_MAJOR 2 /** * sscanf * * The sscanf minor version number. * */ static stock SSCANF_VERSION_MINOR = 13; #define SSCANF_VERSION_MINOR 13 /** * sscanf * * The sscanf build number. * */ static stock SSCANF_VERSION_BUILD = 8; #define SSCANF_VERSION_BUILD 8 /** * sscanf * * The sscanf version as a string. E.g. "2.8.1". * */ static stock SSCANF_VERSION_STRING[] = #SSCANF_VERSION_MAJOR "." #SSCANF_VERSION_MINOR "." #SSCANF_VERSION_BUILD; #define SSCANF_VERSION_STRING #SSCANF_VERSION_MAJOR "." #SSCANF_VERSION_MINOR "." #SSCANF_VERSION_BUILD /** * sscanf * * Converts a version string to * BCD. For example: * * * BCD(5.6.17); * * * Returns: * * * 0x050617 * * * Each section (between dots) is assigned a single byte and the last section is * always in the lowest byte. This implies a maximum of four sections and two * digits per section. * * */ forward BCD(number); #define BCD(%0) (_:MORE_BCD:NO_MORE_BCD:%0.$0) #define MORE_BCD:NO_MORE_BCD:%0.%1$%2) MORE_BCD:NO_MORE_BCD:%1$(%2) << 8 | DO_BCD(%0)) #define NO_MORE_BCD:$ #define DO_BCD(%0) _:(%0) / 10 << 4 | _:(%0) % 10 /** * sscanf * * The sscanf version in BCD as a proper constant. Example: * * * 2.10.3
* => 02 10 03
* => 0x021003 *
*
*/ const SSCANF_VERSION = BCD(SSCANF_VERSION_MAJOR.SSCANF_VERSION_MINOR.SSCANF_VERSION_BUILD); /** * sscanf * * The sscanf version in BCD. Example: * * * 2.10.3
* => 02 10 03
* => 0x021003 *
*
*/ stock const SSCANF_VERSION_BCD = SSCANF_VERSION; #define sscanf_%0\32; sscanf_ #define SSCANF:%0(%1) forward sscanf_%0(%1);public sscanf_%0(%1) #define @kustom()%0(%1) forward sscanf_%0(%1);public sscanf_%0(%1) // The check for `SSCANF_GetClosestString` ensures that this is the first // compiler pass and thus the check for `sscanf` only finds earlier definitions // not our later fake definition made purely for documentation purposes. #if defined sscanf && !defined SSCANF_GetClosestString #error sscanf already defined, or used before inclusion. #endif /** * sscanf * The file in which this call is found. * The line at which this call is found. * The input string containing the data to parse out. * The format description of what the input data should contain. * * The current true implementation of sscanf in the plugin. This is * wrapped by macros to provide sscanf enhanced with filenames and line * numbers so that errors have more information. The plugin also contains a * native function called sscanf which is only for backwards- * compatibility with older versions of this include. * */ /* */ native SSCANF__(const file[], const line, const data[], const format[], {T_WEAPON, Float, _}:...); /** * sscanf * The file in which this call is found. * The line at which this call is found. * The input string containing the data to parse out. * The format description of what the input data should contain. * * An alternative name for SSCANF__, used by extract so that the * name can be used as a macro. * */ /* */ native UNFORMAT__(const file[], const line, const data[], const format[], {T_WEAPON, Float, _}:...) = SSCANF__; /** * sscanf * The maximum players on the server. * The invalid player ID. * MAX_PLAYER_NAME. * * Initialise the plugin with real server information. * */ /* */ native SSCANF_Init(players, invalid, len); /** * sscanf * The ID of the player. * The name of the player. * Is this player an NPC? * * Called when a player joins to inform the plugin of the connection. * */ /* */ native SSCANF_Join(playerid, const name[], bool:npc); /** * sscanf * The ID of the player. * * Called when a player leaves to inform the plugin of the disconnection. * */ /* */ native SSCANF_Leave(playerid); /** * sscanf * The ID of the player. * * Checks if the plugin knows about a given player ID. Used when modes restart * to re-add players. * */ /* */ native bool:SSCANF_IsConnected(playerid); /** * sscanf * The first string to compare. * The second string to compare. * * Computes the * Levenshtein Distance between two strings. This is simlar to * strcmp in usage, but is slightly more "fuzzy". Distances are used to * work out which string is the most similar to another one, though they may not * be identical. Useful in k callback functions to determine if the * entered string is close to a possible string. * */ native SSCANF_Levenshtein(const string1[], const string2[]); /** * sscanf * The first string to compare. * The second string to compare. * * This works out the similarity between two strings. The Levenshtein distance * often produces results that seem weird to people, for example by that measure * NRG is closer to TUG than NRG-500. Instead this * function compares all pairs of letters between the two strings to work out * what percentage of each string is in the other string, then multiplies the * results to get the final similarity. This algorithm produces much more human * sane results, and can handle things like ls police matching * Police Car (LSPD). It ignores all punctuation and case as well. * */ native Float:SSCANF_TextSimilarity(const string1[], const string2[]); /** * sscanf * The sscanf option to look up. For example SSCANF_COLOUR_FORMS. * * The old API used SSCANF_Option to both get and set parse options, * with an optional parameter for setting. This was problematic if you wanted * to actually set an option to the default value - there was no way to * differentiate between getting an option and setting an option to the default * value. Instead the new API has explicit Get and Set functions. * */ native SSCANF_GetOption(const name[]) = SSCANF_Option; /** * sscanf * The sscanf option to look up. For example MATCH_NAME_PARTIAL. * * The old API used SSCANF_Option to both get and set parse options, * with an optional parameter for setting. This was problematic if you wanted * to actually set an option to the default value - there was no way to * differentiate between getting an option and setting an option to the default * value. Instead the new API uses true parameter counts to differentiate in * the legacy SSCANF_Option function, which are resolved through macros * to Get__ and Set__ functions. * */ /* */ native SSCANF_GetOption__(const name[]) = SSCANF_Option; /** * sscanf * The sscanf option to set. For example CELLMIN_ON_MATCHES. * The value to set the option to. * * The old API used SSCANF_Option to both get and set parse options, * with an optional parameter for setting. This was problematic if you wanted * to actually set an option to the default value - there was no way to * differentiate between getting an option and setting an option to the default * value. Instead the new API has explicit Get and Set functions. * */ native SSCANF_SetOption(const name[], {_, Float}:value) = SSCANF_Option; /** * sscanf * The sscanf option to set. For example OLD_DEFAULT_CUSTOM. * The value to set the option to. * * The old API used SSCANF_Option to both get and set parse options, * with an optional parameter for setting. This was problematic if you wanted * to actually set an option to the default value - there was no way to * differentiate between getting an option and setting an option to the default * value. Instead the new API uses true parameter counts to differentiate in * the legacy SSCANF_Option function, which are resolved through macros * to Get__ and Set__ functions. * */ /* */ native SSCANF_SetOption__(const name[], {_, Float}:value) = SSCANF_Option; /** * sscanf * The sscanf option to get or set. For example SSCANF_ARGB. * The optional value to use when setting the option. * * Backwards-compatibility with SSCANF_Option. Usage: * * * SSCANF_Option(SSCANF_QUIET, 1);
* new quiet = SSCANF_Option(SSCANF_QUIET); *
* * This doesn't actually use a default parameter because a user may want to set * the option to whatever that parameter is. Instead this is a macro that calls * SSCANF_GetOption__ when called with one parameter (just a name), or * instead calls SSCANF_SetOption__ when called with two - a name and a * value. *
*/ forward SSCANF_Option(const name[], {_, Float}:value = -1); #define SSCANF_Option SSCANF_GetOption__ #define SSCANF_GetOption__(%0,%1) SSCANF_SetOption__(%0,%1) #define _ALS_SSCANF_Option #define _ALS_SSCANF_GetOption__ /** * sscanf * Pass-by-reference return value. * The size of the destination array. * * Get the SSCANF plugin version as a string (e.g. "2.11.2"). Compare * this value to the constant SSCANF_VERSION_STRING. * */ native SSCANF_VersionString(version[], size = sizeof (version)) = SSCANF_Version; /** * sscanf * Pass-by-reference return value. * The size of the destination array. * * Get the SSCANF plugin version as a string (e.g. "2.11.2"). Compare * this value to the constant SSCANF_VERSION_STRING. This internal * alternate spelling is used by the SSCANF_Version macro, which will * return a string when called with parameters, or a BCD value when not. * */ /* */ native SSCANF_VersionString__(version[], size = sizeof (version)) = SSCANF_Version; /** * sscanf * * Get the SSCANF plugin version as a BCD-encoded value (e.g. 0x021102). * Compare this value to the constant SSCANF_VERSION_BCD. * */ native SSCANF_VersionBCD() = SSCANF_Version; /** * sscanf * * Get the SSCANF plugin version as a BCD-encoded value (e.g. 0x021102). * Compare this value to the constant SSCANF_VERSION_BCD. This internal * alternate spelling is used by the SSCANF_Version macro, which will * return a string when called with parameters, or a BCD value when not. * */ /* */ native SSCANF_VersionBCD__() = SSCANF_Version; // The `SSCANF_Version` function is overloaded to take 0 or 2 parameters (or 1 plus a default). /** * sscanf * Pass-by-reference return value. * The size of the destination array. * * Get the SSCANF plugin version as a string (e.g. "2.11.2") or * a BCD-encoded value (e.g. 0x021102). This function returns a string * (by reference) when called with parameters, or a number (directly) when not. * */ static stock SSCANF_Version(version[] = "", size = sizeof (version)) { return SSCANF_VERSION_BCD; } #define SSCANF_Version SSCANF_VersionString__ #define SSCANF_VersionString__() SSCANF_VersionBCD__() #define _ALS_SSCANF_Version #define _ALS_SSCANF_VersionString__ /** * sscanf * The input string containing the data to parse out. * The format description of what the input data should contain. * * The main entry point. See the readme for vast amounts of information on how * to call this function and all the details on what it does. This is a macro * that calls SSCANF__ and passes the current file and line number as * well for improved error messages. * */ #if defined __PawnBuild // On old compilers, only issue the warning at the call site. #pragma warning push #pragma warning disable 234 #endif #pragma deprecated - include first. forward sscanf(const data[], const format[], {T_WEAPON, Float, _}:...); #if defined __PawnBuild #pragma warning pop #endif // For pawno native detection. /* native sscanf(const data[], const format[], {T_WEAPON, Float, _}:...); */ #if defined __PawnBuild #define sscanf( SSCANF__(__file,__line, #else // `-1` because the old compiler doesn't have `__line` so we can't know // where this function was used. Tell the plugin this. Actually, we *can* // know, if we look at the CIP and try extract the file/line information // from any included debug information. `SSCANF_UNK_` tells the plugin that // this was called via `sscanf` not `unformat`, because they're actually the // same function once compiled. #define sscanf( SSCANF__(SSCANF_UNK_,-1, #endif /** * - include first. * sscanf * The input string containing the data to parse out. * The format description of what the input data should contain. * * An alternative spelling of sscanf, requested by Kalcor because the * original doesn't match the C specification for the format descriptor. This * is a macro that calls UNFORMAT__ and passes the current file and line * number as well for improved error messages. * */ #if defined __PawnBuild // On old compilers, only issue the warning at the call site. #pragma warning push #pragma warning disable 234 #endif #pragma deprecated - include first. forward unformat(const data[], const format[], {T_WEAPON, Float, _}:...); #if defined __PawnBuild #pragma warning pop #endif // For pawno native detection. /* native unformat(const data[], const format[], {T_WEAPON, Float, _}:...); */ #if defined __PawnBuild #define unformat( SSCANF__(__file,__line, #else // `-1` because the old compiler doesn't have `__line` so we can't know // where this function was used. Tell the plugin this. Actually, we *can* // know, if we look at the CIP and try extract the file/line information // from any included debug information. `SSCANF_FOM_` tells the plugin that // this was called via `unformat` not `sscanf`, because they're actually the // same function once compiled. #define unformat( SSCANF__(SSCANF_FOM_,-1, #endif /** * sscanf * * The fallback for the filename in sscanf on the old compiler, which * doesn't have the inbuilt __file macro. This is the "feature" enabled * by SSCANF_NO_NICE_FEATURES. Appends "unknown file" in the * plugin when line number < 0. * */ stock const SSCANF_UNK_[] = "sscanf"; /** * sscanf * * The fallback for the filename in unformat on the old compiler, which * doesn't have the inbuilt __file macro. This is the "feature" enabled * by SSCANF_NO_NICE_FEATURES. Appends "unknown file" in the * plugin when line number < 0. * */ stock const SSCANF_FOM_[] = "unformat"; /** * sscanf * * The fallback for the filename in extract on the old compiler, which * doesn't have the inbuilt __file macro. This is the "feature" enabled * by SSCANF_NO_NICE_FEATURES. Appends "unknown file" in the * plugin when line number < 0. * */ stock const SSCANF_EXT_[] = "extract"; /** * sscanf * * The SSCANF_QUIET option as a constant string so you can get compile- * time spell checking on the name. Don't print any errors to the console. * Really not recommended unless you know your code is stable * and in production. * */ stock const SSCANF_QUIET[] = "SSCANF_QUIET"; /** * sscanf * * The OLD_DEFAULT_NAME option as a constant string so you can get * compile-time spell checking on the 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. * */ stock const OLD_DEFAULT_NAME[] = "OLD_DEFAULT_NAME"; /** * sscanf * * The MATCH_NAME_PARTIAL option as a constant string so you can get * compile-time spell checking on the name. Currently sscanf will search for * players by name, and will always search for player whose name * starts with the specified string. If someone types Y_Less, * sscanf will not find say [CLAN]Y_Less because there name doesn't start * with the specified text. This option, when set to 1, will search * anywhere in the player's name for the given string. * */ stock const MATCH_NAME_PARTIAL[] = "MATCH_NAME_PARTIAL"; /** * sscanf * * The CELLMIN_ON_MATCHES option as a constant string so you can get * compile-time spell checking on the name. 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: * * * 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.
* } *
*
*/ stock const CELLMIN_ON_MATCHES[] = "CELLMIN_ON_MATCHES"; /** * sscanf * * The OLD_DEFAULT_KUSTOM option as a constant string so you can get * compile-time spell checking on the name. 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: * * * 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: * * * K<vehicle>(999) * * * This setting reverts to the old behaviour. * */ stock const OLD_DEFAULT_KUSTOM[] = "OLD_DEFAULT_KUSTOM"; /** * sscanf * * The OLD_DEFAULT_CUSTOM option as a constant string so you can get * compile-time spell checking on the name. This is the same as * OLD_DEFAULT_KUSTOM, but with an alternate spelling. * */ stock const OLD_DEFAULT_CUSTOM[] = "OLD_DEFAULT_CUSTOM"; /** * sscanf * * The SSCANF_COLOUR_FORMS option as a constant string so you can get * compile-time spell checking on the name. There are multiple valid colour * input formats, which you can enable or disable here. The parameter is a * bit map (flags) for all the following values: * *

* * So to ONLY accept SA:MP `SendClientMessage` colours use: * * * SSCANF_Option(SSCANF_COLOUR_FORMS, 16); * * * To only accept 8-digit values use: * * * SSCANF_Option(SSCANF_COLOUR_FORMS, 96); * * * Default values (those specified between ()s for M) ignore this * setting - they can always use any form. * */ stock const SSCANF_COLOUR_FORMS[] = "SSCANF_COLOUR_FORMS"; /** * sscanf * * The SSCANF_ALPHA option as a constant string so you can get compile- * time spell checking on the name. Specify the default alpha value for colours * (m) which don't manually specify an alpha channel. The alpha values * are specified as a decimal number, not a hex * number, so setting an alpha of 0x80 would be: * * * SSCANF_Option(SSCANF_ALPHA, 128); * * */ stock const SSCANF_ALPHA[] = "SSCANF_ALPHA"; /** * sscanf * * The SSCANF_ARGB option as a constant string so you can get compile- * time spell checking on the name. Specify whether the returned colour is * ARGB or RGBA: * * * SSCANF_Option(SSCANF_ARGB, 1); // Set 3- and 6-digit colour outputs to `AARRGGBB`.
* SSCANF_Option(SSCANF_ARGB, 0); // Set 3- and 6-digit colour outputs to `RRGGBBAA` (default). *
*
*/ stock const SSCANF_ARGB[] = "SSCANF_ARGB"; /** * sscanf * * The MATCH_NAME_FIRST option as a constant string so you can get compile- * time spell checking on the name. Specify whether u etc keep searching * for better matching player names after finding one: * * * SSCANF_Option(MATCH_NAME_FIRST, 0); // Enable scanning for the best name match not the first (default).
* SSCANF_Option(MATCH_NAME_FIRST, 1); // Enable scanning for the first name match not the best (old behaviour). *
*
*/ stock const MATCH_NAME_FIRST[] = "MATCH_NAME_FIRST"; /** * sscanf * * The MATCH_NAME_SIMILARITY option as a constant string so you can get * compile-time spell checking on the name. Specify whether u etc should * use the ngrams-based similarity algorithm when searching for players matching * a given name. I.e. enable or disable fuzzy name matching. It will return * the best match found, but depending on the threshold that may still be very * different to what was typed. * * * SSCANF_Option(MATCH_NAME_SIMILARITY, 0.0); // Will find all names, regardless of input.
* SSCANF_Option(MATCH_NAME_SIMILARITY, 1.0); // Will find exact matches only.
* SSCANF_Option(MATCH_NAME_SIMILARITY, 0.5); // Will find names somewhat similar to the input.
* SSCANF_Option(MATCH_NAME_SIMILARITY, -1.0); // Disable fuzzy name matching. *
*
*/ stock const MATCH_NAME_SIMILARITY[] = "MATCH_NAME_SIMILARITY"; /** * sscanf * * Has sscanf been initialised at least once already in this script? Avoids the * need to define FILTERSCRIPT by including OnFilterScriptInit * and OnGameModeInit (plus OnNPCModeInit) and seeing which one * gets called first. * */ static stock SSCANF_gInit = 0; // Pre-hook data. #if !defined CHAIN_ORDER #define CHAIN_ORDER() 0 #endif #define CHAIN_HOOK(%0) forward @CO_%0();public @CO_%0(){return CHAIN_ORDER()+1;} #define CHAIN_NEXT(%0) @CO_%0 #define CHAIN_FORWARD:%0_%2(%1)=%3; \ forward %0_%2(%1); \ public %0_%2(%1) <_ALS : _ALS_x0, _ALS : _ALS_x1> { return (%3); } \ public %0_%2(%1) <> { return (%3); } #define CHAIN_PUBLIC:%0(%1) %0(%1) <_ALS : _ALS_go> CHAIN_HOOK(SSCANF) #undef CHAIN_ORDER #define CHAIN_ORDER CHAIN_NEXT(SSCANF) /** * sscanf * * Common ALS boilerplate to ensure that all the <_ALS : > states * are known to the compiler. * */ static stock _SSCANF_IncludeStates() <_ALS : _ALS_x0, _ALS : _ALS_x1, _ALS : _ALS_x2, _ALS : _ALS_x3> {} static stock _SSCANF_IncludeStates() <_ALS : _ALS_go> {} #if SSCANF_NPC && !defined SSCANF_NO_PLAYERS forward SSCANF_PlayerCheck(); #endif /** * sscanf * * const-correct wrapper for SetTimer, mainly for NPC modes. * */ /* */ native SSCANF_SetTimer(const funcname[], interval, bool:repeating) = SetTimer; /// sscanf /// /// Generic initialisation code called from a range of different init publics. /// /// static stock SSCANF_RunInit0() { } #endif static stock SSCANF_RunInit() { #if SSCANF_NPC state _ALS : _ALS_go; SSCANF_Init(MAX_PLAYERS, INVALID_PLAYER_ID, MAX_PLAYER_NAME); #if !defined SSCANF_NO_PLAYERS // Initialise the system. SSCANF_PlayerCheck(); // Will be run once per frame. SSCANF_SetTimer("SSCANF_PlayerCheck", 0, true); #endif #else state _ALS : _ALS_go; if ((SSCANF_gInit = SSCANF_Init(GetMaxPlayers(), INVALID_PLAYER_ID, MAX_PLAYER_NAME)) == 1) { // SA:MP plugin. new name[MAX_PLAYER_NAME + 1]; // Check if there are any players that aren't initialized. for (new i = 0; i != MAX_PLAYERS; ++i) { if (IsPlayerConnected(i) && !SSCANF_IsConnected(i)) { GetPlayerName(i, name, sizeof (name)); SSCANF_Join(i, name, bool:IsPlayerNPC(i)); } } } // If the return is `0` we can try again later. If it is `-1` the // native code is an open.mp component. #endif } /** * sscanf * * Called when the script starts if it is a NPC mode, sets up the system, then * calls the "real" OnNPCModeInit (using the new ALS 2 hook method). * */ #if SSCANF_NPC public OnNPCModeInit() { SSCANF_RunInit(); SSCANF_OnNPCModeInit(); return 1; } #if defined _ALS_OnNPCModeInit #undef OnNPCModeInit #else #define _ALS_OnNPCModeInit #endif #define OnNPCModeInit(%0) CHAIN_PUBLIC:SSCANF_OnNPCModeInit(%0) CHAIN_FORWARD:SSCANF_OnNPCModeInit() = 1; #else const SSCANF_STATIC__ SSCANF_OnNPCModeInit = 0; #define SSCANF_OnNPCModeInit #endif /** * sscanf * * NPC modes have no OnPlayerConnect callback, so we need to simulate one. * */ #if SSCANF_NPC && !defined SSCANF_NO_PLAYERS public SSCANF_PlayerCheck() { new name[MAX_PLAYER_NAME + 1]; for (new i = 0; i != MAX_PLAYERS; ++i) { if (IsPlayerConnected(i)) { if (!SSCANF_IsConnected(i)) { GetPlayerName(i, name, sizeof (name)); // We have no way to know if they are an NPC or not! SSCANF_Join(i, name, false); } } else { if (SSCANF_IsConnected(i)) { SSCANF_Leave(i); } } } } #else const SSCANF_STATIC__ SSCANF_PlayerCheck = 0; #define SSCANF_PlayerCheck #endif // `SSCANF_BRACKETS` to avoid macro replacements. #define SSCANF_BRACKETS () forward OnScriptInit SSCANF_BRACKETS; #undef SSCANF_BRACKETS /** * sscanf * * Called for earlier initialisation by YSI. * */ #if SSCANF_NPC const SSCANF_STATIC__ SSCANF_OnScriptInit = 0; #define SSCANF_OnScriptInit #else public OnScriptInit() { if (!SSCANF_gInit) { SSCANF_RunInit(); } SSCANF_OnScriptInit(); return 1; } #if defined _ALS_OnScriptInit #undef OnScriptInit #else #define _ALS_OnScriptInit #endif #define OnScriptInit(%0) CHAIN_PUBLIC:SSCANF_OnScriptInit(%0) CHAIN_FORWARD:SSCANF_OnScriptInit() = 1; #endif /** * sscanf * * Called when the script starts if it is a filterscript, sets up the system, * then calls the "real" OnFilterScriptInit (using the new ALS 2 hook method). * */ #if SSCANF_NPC const SSCANF_STATIC__ SSCANF_OnFilterScriptInit = 0; #define SSCANF_OnFilterScriptInit #else public OnFilterScriptInit() { if (!SSCANF_gInit) { SSCANF_RunInit(); } SSCANF_OnFilterScriptInit(); return 1; } #if defined _ALS_OnFilterScriptInit #undef OnFilterScriptInit #else #define _ALS_OnFilterScriptInit #endif #define OnFilterScriptInit(%0) CHAIN_PUBLIC:SSCANF_OnFilterScriptInit(%0) CHAIN_FORWARD:SSCANF_OnFilterScriptInit() = 1; #endif /** * sscanf * * Called when the script starts if it is a gamemode. This callback is also * called in filterscripts so we don't want to reinitialise the system in that * case. * */ #if SSCANF_NPC const SSCANF_STATIC__ SSCANF_OnGameModeInit = 0; #define SSCANF_OnGameModeInit #else public OnGameModeInit() { if (!SSCANF_gInit) { SSCANF_RunInit(); } SSCANF_OnGameModeInit(); return 1; } #if defined _ALS_OnGameModeInit #undef OnGameModeInit #else #define _ALS_OnGameModeInit #endif #define OnGameModeInit(%0) CHAIN_PUBLIC:SSCANF_OnGameModeInit(%0) CHAIN_FORWARD:SSCANF_OnGameModeInit() = 1; #endif forward OnCachedInit(); /** * sscanf * * Called when the script starts if it is (legacy) YSI cached mode. * */ #if SSCANF_NPC const SSCANF_STATIC__ SSCANF_OnCachedInit = 0; #define SSCANF_OnCachedInit #else public OnCachedInit() { SSCANF_RunInit(); SSCANF_OnCachedInit(); return 1; } #if defined _ALS_OnCachedInit #undef OnCachedInit #else #define _ALS_OnCachedInit #endif #define OnCachedInit(%0) CHAIN_PUBLIC:SSCANF_OnCachedInit(%0) CHAIN_FORWARD:SSCANF_OnCachedInit() = 1; #endif /** * sscanf * * Called when a player connects. Actually increments an internal count so that * if a script ends and OnPlayerDisconnect is called then sscanf * still knows that the player is really connected. Also stores their name * internally. * */ #if SSCANF_NPC const SSCANF_STATIC__ SSCANF_OnPlayerConnect = 0; #define SSCANF_OnPlayerConnect #else public OnPlayerConnect(playerid) { if (SSCANF_gInit == 1) { new name[MAX_PLAYER_NAME + 1]; GetPlayerName(playerid, name, sizeof(name)); SSCANF_Join(playerid, name, bool:IsPlayerNPC(playerid)); } SSCANF_OnPlayerConnect(playerid); return 1; } #if defined _ALS_OnPlayerConnect #undef OnPlayerConnect #else #define _ALS_OnPlayerConnect #endif #define OnPlayerConnect(%0) CHAIN_PUBLIC:SSCANF_OnPlayerConnect(%0) CHAIN_FORWARD:SSCANF_OnPlayerConnect(playerid) = 1; #endif /** * sscanf * * Called when a player disconnects, or when a script is ended. * */ #if SSCANF_NPC const SSCANF_STATIC__ SSCANF_OnPlayerDisconnect = 0; #define SSCANF_OnPlayerDisconnect #else public OnPlayerDisconnect(playerid, reason) { SSCANF_OnPlayerDisconnect(playerid, reason); if (SSCANF_gInit == 1) { SSCANF_Leave(playerid); } return 1; } #if defined _ALS_OnPlayerDisconnect #undef OnPlayerDisconnect #else #define _ALS_OnPlayerDisconnect #endif #define OnPlayerDisconnect(%0) CHAIN_PUBLIC:SSCANF_OnPlayerDisconnect(%0) CHAIN_FORWARD:SSCANF_OnPlayerDisconnect(playerid, reason) = 1; #endif // Ensure that these purely internal natives can't be called from outside this // include. #define SSCANF_Init #define SSCANF_Join #define SSCANF_Leave #define SSCANF_IsConnected #if defined __PawnBuild #define extract%0->%1; EXTRN%1;UNFORMAT__(__file,__line,_:EXTRV:EXTRX:%0,""#,%1,,); #else #define extract%0->%1; EXTRN%1;UNFORMAT__(SSCANF_EXT_,-1,_:EXTRV:EXTRX:%0,""#,%1,,); #endif #define UNFORMAT__(%7,%8,_:EXTRV:EXTRX:%0,""#,%1);%2else if (UNFORMAT__(%7,%8,_:EXTRV:EXTRX:%0,""#,%1)) #define EXTRV:EXTRX:%0<%3>%4#,%9new%1,%2) EXTRZ:EXTRY:%0%4#P<%3>,|||%1|||%2) #define EXTRZ:EXTRY:%0#P<,> EXTRY:%0"P<,>"# #define EXTRX:%0#,%9new%1,%2) EXTRY:%0#,|||%1|||%2) #define EXTRY: EXTR8:EXTR9:EXTR0:EXTR1:EXTR2:EXTR3:EXTR4: #define EXTR8:EXTR9:EXTR0:EXTR1:EXTR2:EXTR3:EXTR4:%0#%1,%2|||%6:%3=%9|||%4) %6_EXTRO:%0#%1,%2|||%3=%9|||%4) #define EXTR9:EXTR0:EXTR1:EXTR2:EXTR3:EXTR4:%0#%1,%2|||%3=%9|||%4) __EXTRO:%0#%1,%2|||%3=%9|||%4) #define EXTR0:EXTR1:EXTR2:EXTR3:EXTR4:%0#%1,%2|||%6:%3[%7]|||%4) %6_EXTRW:%0#%1,%2|||%3[%7]|||%4) #define EXTR1:EXTR2:EXTR3:EXTR4:%0#%1,%2|||%3[%7]|||%4) __EXTRW:%0#%1,%2|||%3[%7]|||%4) #define EXTR2:EXTR3:EXTR4:%0#%1,%2|||%6:%3|||%4) %6_EXTRN:%0#%1,%2|||%3|||%4) #define EXTR3:EXTR4:%0#%1,,%2||||||%4) %0#%1,%2) #define EXTR4:%0#%1,%2|||%3|||%4) __EXTRN:%0#%1,%2|||%3|||%4) // Optional specifiers. #define __EXTRO:%0#%1,%2|||%3=%9|||%4,%5) EXTRY:%0#%1I"("#%9")"#,%2,%3|||%4|||%5) #define Float_EXTRO:%0#%1,%2|||%3=%9|||%4,%5) EXTRY:%0#%1F"("#%9")"#,%2,%3|||%4|||%5) #define player_EXTRO:%0#%1,%2|||%3=%9|||%4,%5) EXTRY:%0#%1U"("#%9")"#,%2,%3|||%4|||%5) #define string_EXTRO:%0#%1,%2|||%3[%7]=%9|||%4,%5) EXTRY:%0#%1S"("#%9")[*]",%2,(%7),%3|||%4|||%5) // Normal specifiers (the double underscore is to work for "_:". #define __EXTRN:%0#%1,%2|||%3|||%4,%5) EXTRY:%0#%1i,%2,%3|||%4|||%5) #define Float_EXTRN:%0#%1,%2|||%3|||%4,%5) EXTRY:%0#%1f,%2,%3|||%4|||%5) #define player_EXTRN:%0#%1,%2|||%3|||%4,%5) EXTRY:%0#%1u,%2,%3|||%4|||%5) //#define string_EXTRW:%0#%1,%2|||%3[%7]|||%4,%5) EXTRY:%0#%1s[%7],%2,%3|||%4|||%5) // Array versions of normal specifiers. #define __EXTRW:%0#%1,%2|||%3[%7]|||%4,%5) EXTRY:%0#%1a[*],%2,(%7),%3|||%4|||%5) #define Float_EXTRW:%0#%1,%2|||%3[%7]|||%4,%5) EXTRY:%0#%1a[*],%2,(%7),%3|||%4|||%5) #define player_EXTRW:%0#%1,%2|||%3[%7]|||%4,%5) EXTRY:%0#%1a[*],%2,(%7),%3|||%4|||%5) #define string_EXTRW:%0#%1,%2|||%3[%7]|||%4,%5) EXTRY:%0#%1s[*],%2,(%7),%3|||%4|||%5) // Get rid of excess leading space which causes warnings. #define EXTRN%0new%1; new%1; #if !defined string #define string: #endif #define player:%0;UNFORMAT__(%1) %0;UNFORMAT__(%1) #define hex:%0;UNFORMAT__(%1) %0;UNFORMAT__(%1) #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[*],%2,(%7),%3|||%4|||%5) #define bin:%0;UNFORMAT__(%1) %0;UNFORMAT__(%1) #define bin_EXTRO:%0#%1,%2|||%3=%9|||%4,%5) EXTRY:%0#%1B"("#%9")"#,%2,%3|||%4|||%5) #define bin_EXTRN:%0#%1,%2|||%3|||%4,%5) EXTRY:%0#%1b,%2,%3|||%4|||%5) #define bin_EXTRW:%0#%1,%2|||%3[%7]|||%4,%5) EXTRY:%0#%1a[*],%2,(%7),%3|||%4|||%5) #define colour:%0;UNFORMAT__(%1) %0;UNFORMAT__(%1) #define colour_EXTRO:%0#%1,%2|||%3=%9|||%4,%5) EXTRY:%0#%1M"("#%9")"#,%2,%3|||%4|||%5) #define colour_EXTRN:%0#%1,%2|||%3|||%4,%5) EXTRY:%0#%1m,%2,%3|||%4|||%5) #define colour_EXTRW:%0#%1,%2|||%3[%7]|||%4,%5) EXTRY:%0#%1a[*],%2,(%7),%3|||%4|||%5) #define kustom:%0<%1> %0 #define kustom_EXTRO:%0#%1,%2|||%3<%8>=%9|||%4,%5) EXTRY:%0#%1K<%8>"("#%9")"#,%2,%3|||%4|||%5) #define kustom_EXTRN:%0#%1,%2|||%3<%8>|||%4,%5) EXTRY:%0#%1k<%8>,%2,%3|||%4|||%5) //#define bin_EXTRW:%0#%1,%2|||%3[%7]|||%4,%5) EXTRY:%0#%1a[*],%2,(%7),%3|||%4|||%5) /** * sscanf * The first string to compare. * A list of other strings to compare against. * How similar the strings must be to be considered a match. * The number of candidates. * * Takes an input string and an array of string possibilities (candidates) and * returns the index of the string closest to the input string. If no valid * match is found, -1 is returned. Note that this will always return the * closest, even if the closest is not that close; which is why an optional * threshold parameter is available. When this parameter is provided the * closest match must be closer in Levenshtein distance than the threshold, * otherwise again -1 is returned. * */ stock SSCANF_GetSimilarString(const input[], const candidates[][], Float:threshold = 0.111111, count = sizeof (candidates)) { new closest = cellmin, Float:distance; while (count--) { distance = SSCANF_TextSimilarity(input, candidates[count]); if (distance >= threshold) { closest = count, threshold = distance; } } return closest; } /** * sscanf * The first string to compare. * A list of other strings to compare against. * The values to return when the corresponding candidate (by index) is the closest match. * The value to return when there is no good match. * How similar the strings must be to be considered a match. * The number of candidates (must match check). * The number of results (must match count). * * Similar to SSCANF_GetClosestString in that it searches the * candidates array for the string most closely matching the input * and bounded by threshold. But instead of returning the index this * function returns the value in the second results array at that index; * and instead of returning -1 on failure it returns the value of * fail. The two arrays must match in size and an assert in the * function checks for this. * */ stock SSCANF_GetSimilarValue(const input[], const candidates[][], const results[], fail = cellmin, Float:threshold = 0.111111, count = sizeof (candidates), check = sizeof (results)) { assert(count == check); new closest = SSCANF_GetSimilarString(input, candidates, threshold, count); if (closest == cellmin) { return fail; } return results[closest]; } /** * sscanf * The first string to compare. * A list of other strings to compare against. * How similar the strings must be to be considered a match. * The number of candidates. * * Takes an input string and an array of string possibilities (candidates) and * returns the index of the string closest to the input string. If no valid * match is found, -1 is returned. Note that this will always return the * closest, even if the closest is not that close; which is why an optional * threshold parameter is available. When this parameter is provided the * closest match must be closer in Levenshtein distance than the threshold, * otherwise again -1 is returned. * */ #if defined __PawnBuild // On old compilers, only issue the warning at the call site. #pragma warning push #pragma warning disable 234 #endif #pragma deprecated Use `SSCANF_GetSimilarString` for more human results. stock SSCANF_GetClosestString(const input[], const candidates[][], threshold = cellmax, count = sizeof (candidates)) { new closest = cellmin, distance; while (count--) { distance = SSCANF_Levenshtein(input, candidates[count]); if (distance < threshold) { closest = count, threshold = distance; } } return closest; } #if defined __PawnBuild #pragma warning pop #endif /** * sscanf * The first string to compare. * A list of other strings to compare against. * The values to return when the corresponding candidate (by index) is the closest match. * The value to return when there is no good match. * How similar the strings must be to be considered a match. * The number of candidates (must match check). * The number of results (must match count). * * Similar to SSCANF_GetClosestString in that it searches the * candidates array for the string most closely matching the input * and bounded by threshold. But instead of returning the index this * function returns the value in the second results array at that index; * and instead of returning -1 on failure it returns the value of * fail. The two arrays must match in size and an assert in the * function checks for this. * */ #if defined __PawnBuild // On old compilers, only issue the warning at the call site. #pragma warning push #pragma warning disable 234 #endif #pragma deprecated Use `SSCANF_GetSimilarValue` for more human results. stock SSCANF_GetClosestValue(const input[], const candidates[][], const results[], fail = cellmin, threshold = cellmax, count = sizeof (candidates), check = sizeof (results)) { assert(count == check); new closest = SSCANF_GetClosestString(input, candidates, threshold, count); if (closest == cellmin) { return fail; } return results[closest]; } #if defined __PawnBuild #pragma warning pop #endif /** * sscanf * The current word being parsed out of the sscanf input. * * The default implementation of k<weapon>. Finds the closest * weapon by Levenshtein distance to the input. * */ #if defined SSCANF_NO_K_WEAPON /* */ native SSCANF_no_k_weapon(); #define SSCANF_no_k_weapon() #else SSCANF:weapon(const string[]) { static const results[] = { 0, 0, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 22, 23, 24, 25, 26, 27, 28, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46 }; static const candidates[][] = { "Fists", "Unarmed", "Knuckles", "Knuckledusters", "Brass Knuckles", "Golf Club", "Night Stick", "Knife", "Baseball Bat", "Shovel", "Pool cue", "Katana", "Chainsaw", "Purple Dildo", "White Dildo", "Long White Dildo", "White Dildo 2", "Flowers", "Cane", "Grenades", "Tear Gas", "Molotovs", "Pistol", "Silenced Pistol", "Desert Eagle", "Shotgun", "Sawn Off Shotgun", "Combat Shotgun", "Micro Uzi", "Mac 10", "MP5", "AK47", "M4", "Tec9", "Rifle", "Sniper Rifle", "RPG", "Missile Launcher", "Flame Thrower", "Minigun", "Sachel Charges", "Detonator", "Spray Paint", "Fire Extinguisher", "Camera", "Nightvision Goggles", "Thermal Goggles", "Parachute" }; // This function is VERY basic, needs VASTLY improving to detect variations. if ('0' <= string[0] <= '9') { new ret = strval(string); if (0 <= ret <= 18 || 22 <= ret <= 46) { return ret; } } else { return SSCANF_GetSimilarValue(string, candidates, results, -1); } return -1; } #endif /** * sscanf * The current word being parsed out of the sscanf input. * * The default implementation of k<vehicle>. Finds the closest * vehicle by Levenshtein distance to the input. * */ #if defined SSCANF_NO_K_VEHICLE /* */ native SSCANF_no_k_vehicle(); #define SSCANF_no_k_vehicle() #else SSCANF:vehicle(const string[]) { static const results[] = { 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611 }; static const candidates[][] = { "Landstalker", "Bravura", "Buffalo", "Linerunner", "Perennial", "Sentinel", "Dumper", "Firetruck", "Trashmaster", "Stretch", "Manana", "Infernus", "Voodoo", "Pony", "Mule", "Cheetah", "Ambulance", "Leviathan", "Moonbeam", "Esperanto", "Taxi", "Washington", "Bobcat", "Mr Whoopee", "BF Injection", "Hunter", "Premier", "Enforcer", "Securicar", "Banshee", "Predator", "Bus", "Rhino", "Barracks", "Hotknife", "Article Trailer", "Previon", "Coach", "Cabbie", "Stallion", "Rumpo", "RC Bandit", "Romero", "Packer", "Monster", "Admiral", "Squalo", "Seasparrow", "Pizzaboy", "Tram", "Article Trailer 2", "Turismo", "Speeder", "Reefer", "Tropic", "Flatbed", "Yankee", "Caddy", "Solair", "Topfun Van (Berkley's RC)", "Skimmer", "PCJ-600", "Faggio", "Freeway", "RC Baron", "RC Raider", "Glendale", "Oceanic", "Sanchez", "Sparrow", "Patriot", "Quad", "Coastguard", "Dinghy", "Hermes", "Sabre", "Rustler", "ZR-350", "Walton", "Regina", "Comet", "BMX", "Burrito", "Camper", "Marquis", "Baggage", "Dozer", "Maverick", "SAN News Maverick", "Rancher", "FBI Rancher", "Virgo", "Greenwood", "Jetmax", "Hotring Racer", "Sandking", "Blista Compact", "Police Maverick", "Boxville", "Benson", "Mesa", "RC Goblin", "Hotring Racer \"A\"", "Hotring Racer \"B\"", "Bloodring Banger", "Rancher Lure", "Super GT", "Elegant", "Journey", "Bike", "Mountain Bike", "Beagle", "Cropduster", "Stuntplane", "Tanker", "Roadtrain", "Nebula", "Majestic", "Buccaneer", "Shamal", "Hydra", "FCR-900", "NRG-500", "HPV1000", "Cement Truck", "Towtruck", "Fortune", "Cadrona", "FBI Truck", "Willard", "Forklift", "Tractor", "Combine Harvester", "Feltzer", "Remington", "Slamvan", "Blade", "Freight (Train)", "Brownstreak (Train)", "Vortex", "Vincent", "Bullet", "Clover", "Sadler", "Firetruck LA", "Hustler", "Intruder", "Primo", "Cargobob", "Tampa", "Sunrise", "Merit", "Utility Van", "Nevada", "Yosemite", "Windsor", "Monster \"A\"", "Monster \"B\"", "Uranus", "Jester", "Sultan", "Stratum", "Elegy", "Raindance", "RC Tiger", "Flash", "Tahoma", "Savanna", "Bandito", "Freight Flat Trailer (Train)", "Streak Trailer (Train)", "Kart", "Mower", "Dune", "Sweeper", "Broadway", "Tornado", "AT400", "DFT-30", "Huntley", "Stafford", "BF-400", "Newsvan", "Tug", "Petrol Trailer", "Emperor", "Wayfarer", "Euros", "Hotdog", "Club", "Freight Box Trailer (Train)", "Article Trailer 3", "Andromada", "Dodo", "RC Cam", "Launch", "Police Car (LSPD)", "Police Car (SFPD)", "Police Car (LVPD)", "Police Ranger", "Picador", "S.W.A.T.", "Alpha", "Phoenix", "Glendale Shit", "Sadler Shit", "Baggage Trailer \"A\"", "Baggage Trailer \"B\"", "Tug Stairs Trailer", "Boxville", "Farm Trailer", "Utility Trailer" }; // This function is VERY basic, needs VASTLY improving to detect variations. if ('0' <= string[0] <= '9') { new ret = strval(string); if (400 <= ret <= 611) { return ret; } } else { return SSCANF_GetSimilarValue(string, candidates, results, -1); } return -1; } #endif // Fix the compiler crash when both the PAWN and Plugin versions of sscanf are // found by renaming the old version at declaration. (fixes.inc compatible // naming scheme: "BAD_Function()"). #define SSCANF__(%0:...) BAD_sscanf(%0:...)