{"id":22283469,"url":"https://github.com/y-less/sscanf","last_synced_at":"2025-07-28T21:32:42.198Z","repository":{"id":39697260,"uuid":"53882865","full_name":"Y-Less/sscanf","owner":"Y-Less","description":"SA:MP sscanf plugin originally made by @Y-Less","archived":false,"fork":false,"pushed_at":"2024-01-18T03:51:06.000Z","size":1847,"stargazers_count":119,"open_issues_count":13,"forks_count":49,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-09-27T01:54:32.170Z","etag":null,"topics":["cpp","sa-mp","sscanf"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Y-Less.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2016-03-14T18:49:06.000Z","updated_at":"2024-09-10T06:11:04.000Z","dependencies_parsed_at":"2023-02-09T04:00:56.903Z","dependency_job_id":"dadd022c-b54d-4685-b66f-ac44231236dd","html_url":"https://github.com/Y-Less/sscanf","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Y-Less%2Fsscanf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Y-Less%2Fsscanf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Y-Less%2Fsscanf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Y-Less%2Fsscanf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Y-Less","download_url":"https://codeload.github.com/Y-Less/sscanf/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227959459,"owners_count":17847604,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","sa-mp","sscanf"],"created_at":"2024-12-03T16:40:32.765Z","updated_at":"2024-12-03T16:40:33.623Z","avatar_url":"https://github.com/Y-Less.png","language":"C","readme":"# sscanf 2.15.1\n\n## Introduction\n\nThis 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:\n\n```pawn\nnew num1, num2;\nsscanf(\"45 100\", \"ii\", num1, num2);\n```\n\n`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:\n\n```pawn\nnew num1, num2;\nif (sscanf(\"hello 100\", \"ii\", num1, num2))\n{\n\tprintf(\"The input was not two numbers.\");\n}\n```\n\nThis 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 below:\n\n## Contents\n\n* 1 [Introduction](#introduction)\n* 2 [Contents](#contents)\n* 3 [Downloads](#downloads)\n* 4 [Use](#use)\n\t* 4.1 [Scripting](#scripting)\n\t* 4.2 [open.mp](#openmp)\n\t* 4.3 [SA:MP Windows](#samp-windows)\n\t* 4.4 [SA:MP Linux](#samp-linux)\n\t* 4.5 [NPC Modes](#npc-modes)\n* 5 [Tutorials](#tutorials)\n    * 5.1 [`/sendcash` Command](#sendcash-command)\n    * 5.2 [INI Parser](#ini-parser)\n    * 5.3 [Error Detection](#error-detection)\n    * 5.4 [A Basic Shop](#a-basic-shop)\n* 6 [Specifiers](#specifiers)\n    * 6.1 [Strings](#strings)\n    * 6.2 [Packed Strings](#packed-strings)\n    * 6.3 [Arrays](#arrays)\n    * 6.4 [Enums](#enums)\n    * 6.5 [Provided Lengths](#provided-lengths)\n    * 6.6 [Quiet](#quiet)\n    * 6.7 [Searches](#searches)\n    * 6.8 [Enums](#enums)\n    * 6.9 [Delimiters](#delimiters)\n    * 6.10 [Optional specifiers](#optional-specifiers)\n    * 6.11 [Users](#users)\n    * 6.12 [Custom (kustom) specifiers](#custom-kustom-specifiers)\n    * 6.13 [Colours](#colours)\n        * 6.13.1 [3 digits](#3-digits)\n        * 6.13.2 [6 digits](#6-digits)\n        * 6.13.3 [8 digits](#8-digits)\n    * 6.14 [End Of Input](#end-of-input)\n* 7 [Options](#options)\n    * 7.1 [OLD_DEFAULT_NAME:](#old_default_name)\n    * 7.2 [MATCH_NAME_PARTIAL:](#match_name_partial)\n    * 7.3 [CELLMIN_ON_MATCHES:](#cellmin_on_matches)\n    * 7.4 [SSCANF_QUIET:](#sscanf_quiet)\n    * 7.5 [OLD_DEFAULT_KUSTOM:](#old_default_kustom)\n    * 7.6 [SSCANF_ALPHA:](#sscanf_alpha)\n    * 7.7 [SSCANF_COLOUR_FORMS:](#sscanf_colour_forms)\n    * 7.8 [SSCANF_ARGB:](#sscanf_argb)\n    * 7.9 [MATCH_NAME_FIRST:](#match_name_first)\n    * 7.10 [MATCH_NAME_SIMILARITY:](#match_name_similarity)\n    * 7.11 [ERROR_CODE_IN_RET:](#error_code_in_ret)\n    * 7.12 [WARNINGS_AS_ERRORS:](#warnings_as_errors)\n    * 7.13 [ERROR_CATEGORY_ONLY:](#error_category_only)\n* 8 [`extract`](#extract)\n* 9 [Similarity](#similarity)\n* 10 [Error Returns](#error-returns)\n    * 10.1 [Additional Codes](#additional-codes)\n    * 10.2 [Error Categories](#error-categories)\n* 11 [Alternates](#alternates)\n* 12 [All Specifiers](#all-specifiers)\n* 13 [Full API](#full-api)\n    * 13.1 [`sscanf(const data[], const format[], {Float, _}:...);`](#sscanfconst-data-const-format-float-_)\n    * 13.2 [`unformat(const data[], const format[], {Float, _}:...);`](#unformatconst-data-const-format-float-_)\n    * 13.3 [`SSCANF_Option(const name[], value);`](#sscanf_optionconst-name-value)\n    * 13.4 [`SSCANF_Option(const name[]);`](#sscanf_optionconst-name)\n    * 13.5 [`SSCANF_SetOption(const name[], value);`](#sscanf_setoptionconst-name-value)\n    * 13.6 [`SSCANF_GetOption(const name[], value);`](#sscanf_getoptionconst-name-value)\n    * 13.7 [`SSCANF_Version(version[], size = sizeof (version));`](#sscanf_versionversion-size--sizeof-version)\n    * 13.8 [`SSCANF_Version();`](#sscanf_version)\n    * 13.9 [`SSCANF_VersionString(version[], size = sizeof (version));`](#sscanf_versionstringversion-size--sizeof-version)\n    * 13.10 [`SSCANF_VersionBCD();`](#sscanf_versionbcd)\n    * 13.11 [`SSCANF_Levenshtein(const string1[], const string2[]);`](#sscanf_levenshteinconst-string1-const-string2)\n    * 13.12 [`Float:SSCANF_TextSimilarity(const string1[], const string2[]);`](#floatsscanf_textsimilarityconst-string1-const-string2)\n    * 13.13 [`SSCANF_GetClosestString(const input[], const candidates[][], threshold = cellmax, count = sizeof (candidates));`](#sscanf_getcloseststringconst-input-const-candidates-threshold--cellmax-count--sizeof-candidates)\n    * 13.14 [`SSCANF_GetClosestValue(const input[], const candidates[][], const results[], fail = cellmin, threshold = cellmax, count = sizeof (candidates), check = sizeof (results));`](#sscanf_getclosestvalueconst-input-const-candidates-const-results-fail--cellmin-threshold--cellmax-count--sizeof-candidates-check--sizeof-results)\n    * 13.15 [`SSCANF_GetSimilarString(const input[], const candidates[][], Float:threshold = 0.111111, count = sizeof (candidates));`](#sscanf_getsimilarstringconst-input-const-candidates-floatthreshold--0111111-count--sizeof-candidates)\n    * 13.16 [`SSCANF_GetSimilarValue(const input[], const candidates[][], const results[], fail = cellmin, Float:threshold = 0.111111, count = sizeof (candidates), check = sizeof (results));`](#sscanf_getsimilarvalueconst-input-const-candidates-const-results-fail--cellmin-floatthreshold--0111111-count--sizeof-candidates-check--sizeof-results)\n    * 13.17 [`SSCANF_VERSION_STRING`](#sscanf_version_string)\n    * 13.18 [`SSCANF_VERSION_BCD`](#sscanf_version_bcd)\n    * 13.19 [`SSCANF_VERSION`](#sscanf_version-1)\n    * 13.20 [`SSCANF_NO_K_VEHICLE`](#sscanf_no_k_vehicle)\n    * 13.21 [`SSCANF_NO_K_WEAPON`](#sscanf_no_k_weapon)\n    * 13.22 [`SSCANF_NO_NICE_FEATURES`](#sscanf_no_nice_features)\n    * 13.23 [`SSCANF_GetLastError();`](#sscanf_getlasterror)\n    * 13.24 [`SSCANF_ClearLastError();`](#sscanf_clearlasterror)\n    * 13.25 [`sscanf_error:SSCANF_GetErrorCategory(error);`](#sscanf_errorsscanf_geterrorcategoryerror)\n    * 13.26 [`SSCANF_GetErrorSpecifier();`](#sscanf_geterrorspecifier)\n    * 13.27 [`SSCANF_Debug`();`](#sscanf_debug)\n* 14 [Errors/Warnings](#errorswarnings)\n    * 14.1 [MSVRC100.dll not found](#msvrc100dll-not-found)\n    * 14.2 [sscanf error: System not initialised](#sscanf-error-system-not-initialised)\n    * 14.3 [sscanf warning: String buffer overflow.](#sscanf-warning-string-buffer-overflow)\n    * 14.4 [sscanf warning: Optional types invalid in array specifiers, consider using 'A'.](#sscanf-warning-optional-types-invalid-in-array-specifiers-consider-using-a)\n    * 14.5 [sscanf warning: Optional types invalid in enum specifiers, consider using 'E'.](#sscanf-warning-optional-types-invalid-in-enum-specifiers-consider-using-e)\n    * 14.6 [sscanf error: Multi-dimensional arrays are not supported.](#sscanf-error-multi-dimensional-arrays-are-not-supported)\n    * 14.7 [sscanf error: Search strings are not supported in arrays.](#sscanf-error-search-strings-are-not-supported-in-arrays)\n    * 14.8 [sscanf error: Delimiters are not supported in arrays.](#sscanf-error-delimiters-are-not-supported-in-arrays)\n    * 14.9 [sscanf error: Quiet sections are not supported in arrays.](#sscanf-error-quiet-sections-are-not-supported-in-arrays)\n    * 14.10 [sscanf error: Unknown format specifier '?'.](#sscanf-error-unknown-format-specifier-)\n    * 14.11 [sscanf warning: Empty default values.](#sscanf-warning-empty-default-values)\n    * 14.12 [sscanf warning: Unclosed default value.](#sscanf-warning-unclosed-default-value)\n    * 14.13 [sscanf warning: No default value found.](#sscanf-warning-no-default-value-found)\n    * 14.14 [sscanf warning: Unenclosed specifier parameter.](#sscanf-warning-unenclosed-specifier-parameter)\n    * 14.15 [sscanf warning: No specified parameter found.](#sscanf-warning-no-specified-parameter-found)\n    * 14.16 [sscanf warning: Missing string length end.](#sscanf-warning-missing-string-length-end)\n    * 14.17 [sscanf warning: Missing length end.](#sscanf-warning-missing-length-end)\n    * 14.18 [sscanf error: Invalid data length.](#sscanf-error-invalid-data-length)\n    * 14.19 [sscanf error: Invalid character in data length.](#sscanf-error-invalid-character-in-data-length)\n    * 14.20 [sscanf error: String/array must include a length, please add a destination size.](#sscanf-error-stringarray-must-include-a-length-please-add-a-destination-size)\n    * 14.21 [sscanf warning: Can't have nestled quiet sections.](#sscanf-warning-cant-have-nestled-quiet-sections)\n    * 14.22 [sscanf warning: Not in a quiet section.](#sscanf-warning-not-in-a-quiet-section)\n    * 14.23 [sscanf warning: Can't remove quiet in enum.](#sscanf-warning-cant-remove-quiet-in-enum)\n    * 14.24 [sscanf error: Arrays are not supported in enums.](#sscanf-error-arrays-are-not-supported-in-enums)\n    * 14.25 [sscanf warning: Unclosed string literal.](#sscanf-warning-unclosed-string-literal)\n    * 14.26 [sscanf warning: sscanf specifiers do not require '%' before them.](#sscanf-warning-sscanf-specifiers-do-not-require--before-them)\n    * 14.27 [sscanf error: Insufficient default values.](#sscanf-error-insufficient-default-values)\n    * 14.28 [sscanf error: Options are not supported in enums.](#sscanf-error-options-are-not-supported-in-enums)\n    * 14.29 [sscanf error: Options are not supported in arrays.](#sscanf-error-options-are-not-supported-in-arrays)\n    * 14.30 [sscanf error: No option value.](#sscanf-error-no-option-value)\n    * 14.31 [sscanf error: Unknown option name.](#sscanf-error-unknown-option-name)\n    * 14.32 [sscanf warning: Could not find function SSCANF:?.](#sscanf-warning-could-not-find-function-sscanf)\n    * 14.33 [sscanf error: SSCANF_Init has incorrect parameters.](#sscanf-error-sscanf_init-has-incorrect-parameters)\n    * 14.34 [sscanf error: SSCANF_Join has incorrect parameters.](#sscanf-error-sscanf_join-has-incorrect-parameters)\n    * 14.35 [sscanf error: SSCANF_Leave has incorrect parameters.](#sscanf-error-sscanf_leave-has-incorrect-parameters)\n    * 14.36 [sscanf error: SSCANF_IsConnected has incorrect parameters.](#sscanf-error-sscanf_isconnected-has-incorrect-parameters)\n    * 14.37 [sscanf error: SSCANF_Version has incorrect parameters.](#sscanf-error-sscanf_version-has-incorrect-parameters)\n    * 14.38 [sscanf error: SSCANF_Option has incorrect parameters.](#sscanf-error-sscanf_option-has-incorrect-parameters)\n    * 14.39 [sscanf error: SetPlayerName has incorrect parameters.](#sscanf-error-setplayername-has-incorrect-parameters)\n    * 14.40 [sscanf error: Missing required parameters.](#sscanf-error-missing-required-parameters)\n    * 14.41 [`fatal error 111: user error: sscanf already defined, or used before inclusion.`](#fatal-error-111-user-error-sscanf-already-defined-or-used-before-inclusion)\n    * 14.42 [`error 004: function \"sscanf\" is not implemented`](#error-004-function-sscanf-is-not-implemented)\n    * 14.43 [`error 004: function \"sscanf\" is not implemented - include \u003csscanf2\u003e first.`](#error-004-function-sscanf-is-not-implemented---include-sscanf2-first)\n    * 14.44 [sscanf error: Pawn component not loaded.](#sscanf-error-pawn-component-not-loaded)\n    * 14.45 [sscanf warning: Unknown `player-\u003esetName()` return.](#sscanf-warning-unknown-player-setname-return)\n    * 14.46 [sscanf error: This script was built with the component version of the include.](#sscanf-error-this-script-was-built-with-the-component-version-of-the-include)\n    * 14.47 [sscanf error: Unable to allocate memory.](#sscanf-error-unable-to-allocate-memory)\n    * 14.48 [sscanf warning: User arrays are not supported in arrays.](#sscanf-warning-user-arrays-are-not-supported-in-arrays)\n    * 14.49 [sscanf warning: Invalid values in array defaults.](#sscanf-warning-invalid-values-in-array-defaults)\n    * 14.50 [sscanf warning: Excess array defaults found.](#sscanf-warning-excess-array-defaults-found)\n    * 14.51 [sscanf warning: Format specifier does not match parameter count.](#sscanf-warning-format-specifier-does-not-match-parameter-count)\n    * 14.52 [sscanf warning: Unclosed quiet section.](#sscanf-warning-unclosed-quiet-section)\n    * 14.53 [sscanf warning: Include / plugin mismatch, please recompile your script for the latest features.](#sscanf-warning-include--plugin-mismatch-please-recompile-your-script-for-the-latest-features)\n    * 14.54 [sscanf warning: A minus minus makes no sense.](#sscanf-warning-a-minus-minus-makes-no-sense)\n    * 14.55 [sscanf warning: A minus option makes no sense.](#sscanf-warning-a-minus-option-makes-no-sense)\n    * 14.56 [sscanf warning: A minus delimiter makes no sense.](#sscanf-warning-a-minus-delimiter-makes-no-sense)\n    * 14.57 [sscanf warning: A minus quiet section makes no sense.](#sscanf-warning-a-minus-quiet-section-makes-no-sense)\n    * 14.58 [sscanf warning: User arrays are not supported in enums.](#sscanf-warning-user-arrays-are-not-supported-in-enums)\n    * 14.59 [sscanf error: 'U(name)[len]' is incompatible with OLD_DEFAULT_NAME.](#sscanf-error-u-name-len-is-incompatible-with-old_default_name)\n    * 14.60 [sscanf error: 'U(num)[len]' length under 2.](#sscanf-error-u-num-len-length-under-2)\n    * 14.61 [sscanf error: 'u[len]' length under 2.](#sscanf-error-u-len-length-under-2)\n    * 14.62 [sscanf error: 'Q(name)[len]' is incompatible with OLD_DEFAULT_NAME.](#sscanf-error-q-name-len-is-incompatible-with-old_default_name)\n    * 14.63 [sscanf error: 'Q(num)[len]' length under 2.](#sscanf-error-q-num-len-length-under-2)\n    * 14.64 [sscanf error: 'q[len]' length under 2.](#sscanf-error-q-len-length-under-2)\n    * 14.65 [sscanf error: 'R(name)[len]' is incompatible with OLD_DEFAULT_NAME.](#sscanf-error-r-name-len-is-incompatible-with-old_default_name)\n    * 14.66 [sscanf error: 'R(num)[len]' length under 2.](#sscanf-error-r-num-len-length-under-2)\n    * 14.67 [sscanf error: 'r[len]' length under 2.](#sscanf-error-r-len-length-under-2)\n    * 14.68 [sscanf error: (*) is not supported in strings/arrays yet.](#sscanf-error-is-not-supported-in-strings-arrays-yet)\n    * 14.69 [sscanf error: Unclosed specifier parameters.](#sscanf-error-unclosed-specifier-parameters)\n    * 14.70 [sscanf error: No specified parameters found.](#sscanf-error-no-specified-parameters-found)\n    * 14.71 [sscanf error: Enums are not supported in enums.](#sscanf-error-enums-are-not-supported-in-enums)\n    * 14.72 [sscanf error: SSCANF_TextSimilarity has incorrect parameters.](#sscanf-error-sscanf_textsimilarity-has-incorrect-parameters)\n    * 14.73 [sscanf error: SSCANF_GetErrorCategory has incorrect parameters.](#sscanf-error-sscanf_geterrorcategory-has-incorrect-parameters)\n    * 14.74 [sscanf error: End of text is not supported in arrays.](#sscanf-error-end-of-text-is-not-supported-in-arrays)\n    * 14.75 [sscanf error: End of text is not supported in enums.](#sscanf-error-end-of-text-is-not-supported-in-enums)\n    * 14.76 [sscanf warning: A minus end of text makes no sense.](#sscanf-warning-a-minus-end-of-text-makes-no-sense)\n    * 14.77 [sscanf error: No alternate destination.](#sscanf-error-no-alternate-destination)\n* 14 [Future Plans](#future-plans)\n    * 14.1 [Reserved Specifiers](#reserved-specifiers)\n    * 14.2 [Validity Ranges](#validity-ranges)\n    * 14.3 [Enums And Arrays](#enums-and-arrays)\n    * 14.4 [Compilation](#compilation)\n* 15 [Building](#building)\n    * 15.1 [Getting The Code](#getting-the-code)\n    * 15.2 [Building On Windows](#building-on-windows)\n    * 15.3 [Building On Linux](#building-on-linux)\n    * 15.4 [Building With Docker](#building-with-docker)\n* 16 [License](#license)\n    * 16.1 [Version: MPL 1.1](#version-mpl-11)\n    * 16.2 [Contributor(s):](#contributors)\n    * 16.3 [Special Thanks to:](#special-thanks-to)\n* 17 [Changelog](#changelog)\n    * 17.1 [sscanf 2.8.2 - 18/04/2015](#sscanf-282---18042015)\n    * 17.2 [sscanf 2.8.3 - 02/10/2018](#sscanf-283---02102018)\n    * 17.3 [sscanf 2.9.0 - 04/11/2019](#sscanf-290---04112019)\n    * 17.4 [sscanf 2.10.0 - 27/06/2020](#sscanf-2100---27062020)\n    * 17.5 [sscanf 2.10.1 - 27/06/2020](#sscanf-2101---27062020)\n    * 17.6 [sscanf 2.10.2 - 28/06/2020](#sscanf-2102---28062020)\n    * 17.7 [sscanf 2.10.3 - 28/04/2021](#sscanf-2103---28042021)\n    * 17.8 [sscanf 2.10.4 - 17/01/2022](#sscanf-2104---17012022)\n    * 17.9 [sscanf 2.11.1 - 25/01/2022](#sscanf-2111---25012022)\n    * 17.9 [sscanf 2.11.2 - 04/02/2022](#sscanf-2112---04022022)\n    * 17.10 [sscanf 2.11.3 - 05/02/2022](#sscanf-2113---05022022)\n    * 17.10 [sscanf 2.11.4 - 02/03/2022](#sscanf-2114---02032022)\n    * 17.11 [sscanf 2.11.5 - 31/03/2022](#sscanf-2115---31032022)\n    * 17.12 [sscanf 2.12.1 - 05/05/2022](#sscanf-2121---05052022)\n    * 17.13 [sscanf 2.12.2 - 11/05/2022](#sscanf-2122---11052022)\n    * 17.14 [sscanf 2.13.1 - 25/06/2022](#sscanf-2131---25062022)\n    * 17.15 [sscanf 2.13.2 - 07/09/2022](#sscanf-2132---07092022)\n    * 17.16 [sscanf 2.13.3 - 04/12/2022](#sscanf-2133---04122022)\n    * 17.17 [sscanf 2.13.4 - 20/12/2022](#sscanf-2134---20122022)\n    * 17.18 [sscanf 2.13.5 - 28/12/2022](#sscanf-2135---28122022)\n    * 17.19 [sscanf 2.13.6 - 28/12/2022](#sscanf-2136---28122022)\n    * 17.20 [sscanf 2.13.7 - 02/01/2023](#sscanf-2137---02012023)\n    * 17.21 [sscanf 2.13.8 - 05/01/2023](#sscanf-2138---05012023)\n    * 17.21 [sscanf 2.14.1 - 08/09/2023](#sscanf-2141---08092023)\n    * 17.22 [sscanf 2.15.1 - 10/09/2023](#sscanf-2151---10092023)\n\n## Downloads\n\nGitHub repo:\n\nhttps://github.com/Y-Less/sscanf/\n\n## Use\n\n### Scripting\n\nThis behaves exactly as the old sscanf did, just MUCH faster and much more flexibly.  To use it add:\n\n```pawn\n#include \u003csscanf2\u003e\n```\n\nTo your modes and remove the old sscanf (the new include will detect the old version and throw an error if it is detected).\n\nThe basic code looks like:\n\n```pawn\nif (sscanf(params, \"ui\", giveplayerid, amount))\n{\n    return SendClientMessage(playerid, 0xFF0000AA, \"Usage: /givecash \u003cplayerid/name\u003e \u003camount\u003e\");\n}\n```\n\nHowever 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):\n\n```pawn\nif (sscanf(szFileLine, \"p\u003c=\u003es[8]s[32]\", szIniName, szIniValue))\n{\n    printf(\"Invalid INI format line\");\n}\n```\n\nThere is also an alternate function name to avoid confusion with the C standard sscanf:\n\n```pawn\nif (unformat(params, \"ui\", giveplayerid, amount))\n{\n    return SendClientMessage(playerid, 0xFF0000AA, \"Usage: /givecash \u003cplayerid/name\u003e \u003camount\u003e\");\n}\n```\n\n### open.mp\n\nThe `sscanf` binary (`sscanf.dll` on Windows, or `sscanf.so` on Linux) works as both a legacy (SA:MP) plugin or an *open.mp* component.  The recommended method is to use it as a component - just place the file in the `components` directory in the server root and *open.mp* will load it automatically.\n\nIf you wish to use it as a legacy plugin for some reason (there is no need if you are on version `2.12.1` or higher) place it in the `plugins` directory in the open.mp server root and either follow the *SA:MP*-specific instructions for `server.cfg` on your platform or add `\"sscanf\"` to `\"pawn.legacy_plugins\"` in `config.json`:\n\n```json\n{\n\t\"pawn\":\n\t{\n\t\t\"legacy_plugins\":\n\t\t[\n\t\t\t\"sscanf\"\n\t\t]\n\t}\n}\n```\n\n### SA:MP Windows\n\nAdd `sscanf` to the start of the `plugins` line in `server.cfg`.  For example:\n\n```\nplugins sscanf streamer crashdetect\n```\n\nIf there isn't a `plugins` line already, add one:\n\n```pawn\nplugins sscanf\n```\n\nYou must also place `sscanf.dll` in the `plugins` subdirectory of the server.  If there isn't a `plugins` directory, create one.\n\n### SA:MP Linux\n\nAdd `sscanf.so` to the start of the `plugins` line in `server.cfg`.  For example:\n\n```\nplugins sscanf.so streamer.so crashdetect.so\n```\n\nIf there isn't a `plugins` line already, add one:\n\n```pawn\nplugins sscanf.so\n```\n\nYou must also place `sscanf.so` in the `plugins` subdirectory of the server.  If there isn't a `plugins` directory, create one.\n\n### NPC modes\n\nTo use sscanf in an NPC mode save the plugin as `amxsscanf.dll` (on Windows) or `amxsscanf.so` (on Linux) in the same directory as `samp-npc(.exe)` (i.e. the server root).  This allows NPC modes to automatically find and load the library.  The only tiny differences between this sscanf and the normal sscanf are that there are no prints; and `u`, `r`, and `q` don't know if a user is a bot or not thus just assume they are all players.\n\n## Tutorials\n\n### `/sendcash` Command\n\nSend some of your money to another player.  This command allows you to specify the target player using their name or ID so you can type `/sendcash Y_Less 500` to send me $500 (*hint hint...*), or `/sendcash 27 12` to send $12 to whoever player ID 27 is.  Note that if a player has a numeric name such as `69` you will have to use their ID only, typing that name will always select the player with ID 69, not the player with name \"69\":\n\n```pawn\n@cmd() sendcash(playerid, params[], help)\n{\n\tif (help)\n\t{\n\t\tSendClientMessage(playerid, COLOUR_HELP, \"Send money to another player.  Usage: /sendcash \u003cname/id\u003e \u003camount\u003e\");\n\t\treturn 1;\n\t}\n\tnew targetid, money;\n\tif (sscanf(params, \"ui\", targetid, money) != 0)\n\t{\n\t\tSendClientMessage(playerid, COLOUR_HELP, \"Missing parameters.  Usage: /sendcash \u003cname/id\u003e \u003camount\u003e\");\n\t\treturn 1;\n\t}\n\tif (targetid == INVALID_PLAYER_ID)\n\t{\n\t\tSendClientMessage(playerid, COLOUR_HELP, \"Unknown target player.\");\n\t\treturn 1;\n\t}\n\tif (money \u003e GetPlayerMoney(playerid))\n\t{\n\t\tSendClientMessage(playerid, COLOUR_HELP, \"You don't have enough money.\");\n\t\treturn 1;\n\t}\n\tGivePlayerMoney(playerid, -money);\n\tGivePlayerMoney(targetid, money);\n\tSendClientMessage(playerid, COLOUR_HELP, \"You sent money.\");\n\tSendClientMessage(targetid, COLOUR_HELP, \"You receieved money.\");\n\treturn 1;\n}\n```\n\n### INI Parser\n\nThis very basic file reader uses *sscanf* to split the keys and values in an INI file by `=`.\n\n```pawn\nbool:ReadINIString(const filename[], const key[], \u0026value)\n{\n\tnew File:f = fopen(filename, io_read);\n\tif (!f)\n\t{\n\t\treturn false;\n\t}\n\tnew line[128];\n\tnew k[32], v[96];\n\twhile ((fread(f, line)))\n\t{\n\t\tif (sscanf(line, \"p\u003c=\u003es[32]s[96]\", k, v) == 0)\n\t\t{\n\t\t\tif (strcmp(key, k, true) == 0)\n\t\t\t{\n\t\t\t\tvalue = strval(v);\n\t\t\t\tfclose(f);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\tfclose(f);\n\treturn false;\n}\n```\n\n### Error Detection\n\nWe can write a command and use error returns to know exactly what happened when the user entered something wrong.  See the section on [Error Returns](#error-returns) for more information on what all the values mean.  This will expand the earlier `/sendcash` command with even more checks:\n\n```pawn\n@cmd() sendcash(playerid, params[], help)\n{\n\tif (help)\n\t{\n\t\tSendClientMessage(playerid, COLOUR_HELP, \"Send money to another player.  Usage: /sendcash \u003cname/id\u003e \u003camount\u003e [optional reason]\");\n\t\treturn 1;\n\t}\n\tnew targetid, money, reason;\n\tSSCANF_Option(ERROR_CATEGORY_ONLY, 1);\n\tswitch (sscanf(params, \"?\u003cERROR_CODE_IN_RET=1\u003e?\u003cWARNINGS_AS_ERRORS=1\u003euiS[16]()\", targetid, money, reason))\n\t{\n\t\tcase SSCANF_ERROR(3, MISSING):\n\t\t{\n\t\t\tSendClientMessage(playerid, COLOUR_HELP, \"No player specified.\");\n\t\t\treturn 1;\n\t\t}\n\t\tcase SSCANF_ERROR(3, INVALID):\n\t\t{\n\t\t\tSendClientMessage(playerid, COLOUR_HELP, \"Invalid player specified.\");\n\t\t\treturn 1;\n\t\t}\n\t\tcase SSCANF_ERROR(4, MISSING):\n\t\t{\n\t\t\tSendClientMessage(playerid, COLOUR_HELP, \"No amount specified.\");\n\t\t\treturn 1;\n\t\t}\n\t\tcase SSCANF_ERROR(4, INVALID):\n\t\t{\n\t\t\tSendClientMessage(playerid, COLOUR_HELP, \"Invalid amount specified.\");\n\t\t\treturn 1;\n\t\t}\n\t\tcase SSCANF_ERROR(5, OVERFLOW):\n\t\t{\n\t\t\tSendClientMessage(playerid, COLOUR_HELP, \"Reason too long, please shorten it.\");\n\t\t\treturn 1;\n\t\t}\n\t}\n\tif (targetid == INVALID_PLAYER_ID)\n\t{\n\t\tSendClientMessage(playerid, COLOUR_HELP, \"Unknown target player.\");\n\t\treturn 1;\n\t}\n\tif (money \u003c 0)\n\t{\n\t\tSendClientMessage(playerid, COLOUR_HELP, \"You can't steal money.\");\n\t\treturn 1;\n\t}\n\tif (money \u003e GetPlayerMoney(playerid))\n\t{\n\t\tSendClientMessage(playerid, COLOUR_HELP, \"You don't have enough money.\");\n\t\treturn 1;\n\t}\n\tGivePlayerMoney(playerid, -money);\n\tGivePlayerMoney(targetid, money);\n\tSendClientMessage(playerid, COLOUR_HELP, \"You sent money.\");\n\tif (IsNull(reason))\n\t{\n\t\tSendClientMessage(targetid, COLOUR_HELP, \"You receieved money.\");\n\t}\n\telse\n\t{\n\t\tSendClientMessage(targetid, COLOUR_HELP, \"You receieved money because: %s.\", reason);\n\t}\n\treturn 1;\n}\n```\n\n### A Basic Shop\n\nThis example will use alternates to make a `/buy` command in a simple manner:\n\n```pawn\n@cmd() buy(playerid, params[], help)\n{\n\tnew\n\t\talt,\n\t\tweapon,\n\t\tammo,\n\t\tvehicle,\n\t\tcolour1,\n\t\tcolour2;\n\tif (help || sscanf(params, \"'weapon'ii|'armour'|'health'|'vehicle'k\u003cvehicle\u003eI(-1)I(-1)\", alt, weapon, ammo, vehicle, colour1, colour2))\n\t{\n\t\tSendClientMessage(playerid, COLOUR_ERROR, \"Usage:\");\n\t\tSendClientMessage(playerid, COLOUR_ERROR, \" \");\n\t\tSendClientMessage(playerid, COLOUR_ERROR, \"\t /buy weapon \u003cid\u003e \u003cammo\u003e\");\n\t\tSendClientMessage(playerid, COLOUR_ERROR, \"\t /buy armour\");\n\t\tSendClientMessage(playerid, COLOUR_ERROR, \"\t /buy health\");\n\t\tSendClientMessage(playerid, COLOUR_ERROR, \"\t /buy vehicle \u003ctype\u003e \u003ccolour1\u003e \u003ccolour2\u003e\");\n\t\tSendClientMessage(playerid, COLOUR_ERROR, \" \");\n\t\treturn 1;\n\t}\n\telse switch (alt)\n\t{\n\t\tcase 1:\n\t\t{\n\t\t\tSendClientMessage(playerid, COLOUR_OK, \"You bought weapon %d with %d ammo\", weapon, ammo);\n\t\t}\n\t\tcase 2:\n\t\t{\n\t\t\tSendClientMessage(playerid, COLOUR_OK, \"You bought armour\");\n\t\t}\n\t\tcase 3:\n\t\t{\n\t\t\tSendClientMessage(playerid, COLOUR_OK, \"You bought health\");\n\t\t}\n\t\tcase 4:\n\t\t{\n\t\t\tSendClientMessage(playerid, COLOUR_OK, \"You bought vehicle %d with colours %d, %d\", vehicle, colour1, colour2);\n\t\t}\n\t}\n\treturn 1;\n}\n```\n\nThese command variants all now work:\n\n```\n/weapon 4 5\n/armour\n/buy health\n/buy vehicle 500 9 10\n```\n\nThis also works thanks to using `k\u003cvehicle\u003e` instead of `i` for the vehicle model ID:\n\n```\n/buy vehicle Infernus 5 6\n```\n\nAs does this, using default random colours:\n\n```\n/buy vehicle PCJ-600\n```\n\nThis one doesn't work, and displays the help message instead:\n\n```\n/buy food\n```\n\nNote that the actual task of giving the player the items is left as an exercise for the reader.\n\nWe can do one better for the vehicle colours.  Currently they are optional so you can provide naught, one or two colours.  But what if we want the player to provide both or neither.  Using `I` won't work for that because it makes both optional.  Instead we can use alternatives with two or no integer parameters:\n\n```pawn\n@cmd() buy(playerid, params[], help)\n{\n\tnew\n\t\talt,\n\t\tweapon,\n\t\tammo,\n\t\tvehicle,\n\t\tcolour1,\n\t\tcolour2;\n\tif (help || sscanf(params, \"'weapon'ii|'armour'|'health'|'vehicle'k\u003cvehicle\u003eii|'vehicle'k\u003cvehicle\u003e\", alt, weapon, ammo, vehicle, colour1, colour2, vehicle))\n\t{\n\t\t// ...\n\t\treturn 1;\n\t}\n\telse switch (alt)\n\t{\n\t\t// ...\n\t\tcase 4:\n\t\t{\n\t\t\tSendClientMessage(playerid, COLOUR_OK, \"You bought vehicle %d with colours %d, %d\", vehicle, colour1, colour2);\n\t\t}\n\t\tcase 5:\n\t\t{\n\t\t\tcolour1 = -1;\n\t\t\tcolour2 = -1;\n\t\t\tSendClientMessage(playerid, COLOUR_OK, \"You bought vehicle %d with random colours\", vehicle);\n\t\t}\n\t}\n\treturn 1;\n}\n```\n\nThe alternative with two integer parameters must come first as the one with no integers could also match that and simply ignore the data (unless you use `!` for strict text ending, i.e. `'vehicle'k\u003cvehicle\u003e!`).  The `vehicle` parameter is also used twice in the `sscanf` call so that we only need a single variable for both alternatives.\n\n## Specifiers\n\nThe basic specifiers (the letters `u`, `i`, `s` etc. in the codes above) here.  There are more advanced ones in a later table.\n\n|  Specifier(s)  |               Name                |                      Example values                       |\n| -------------- | --------------------------------- | --------------------------------------------------------- |\n|  `i`, `d`      |  Integer                          |  `1`, `42`, `-10`                                         |\n|  `c`           |  Character                        |  `a`, `o`, `*`                                            |\n|  `l`           |  Logical                          |  `true`, `false`                                          |\n|  `b`           |  Binary                           |  `01001`, `0b1100`                                        |\n|  `h`, `x`      |  Hex                              |  `1A`, `0x23`                                             |\n|  `o`           |  Octal                            |  `045`, `12`                                              |\n|  `n`           |  Number                           |  `42`, `0b010`, `0xAC`, `045`                             |\n|  `f`           |  Float                            |  `0.7`, `-99.5`                                           |\n|  `g`           |  IEEE Float                       |  `0.7`, `-99.5`, `INFINITY`, `-INFINITY`, `NAN`, `NAN_E`  |\n|  `u`           |  User name/id (bots and players)  |  `Y_Less`, `0`                                            |\n|  `q`           |  Bot name/id                      |  `ShopBot`, `27`                                          |\n|  `r`           |  Player name/id                   |  `Y_Less`, `42`                                           |\n|  `m`           |  Colour                           |  `{FF00AA}`, `0xFFFFFFFF`, `444`                          |\n\n### Strings\n\nThe specifier `s` is used, as before, for strings - but they are now more advanced.  As before they support collection, so doing:\n\n```pawn\nsscanf(\"hello 27\", \"si\", str, val);\n```\n\nWill give:\n\n```\nhello\n27\n```\n\nDoing:\n\n```pawn\nsscanf(\"hello there 27\", \"si\", str, val);\n```\n\nWill fail as `there` is not a number.  However doing:\n\n```pawn\nsscanf(\"hello there\", \"s\", str);\n```\n\nWill give:\n\n```\nhello there\n```\n\nBecause there is nothing after `s` in the specifier, the string gets everything.  To stop this simply add a space:\n\n```pawn\nsscanf(\"hello there\", \"s \", str);\n```\n\nWill give:\n\n```\nhello\n```\n\nYou can also escape parts of strings with `\\\\` - note that this is two backslashes as 1 is used by the compiler:\n\n```pawn\nsscanf(\"hello\\\\ there 27\", \"si\", str, val);\n```\n\nWill give:\n\n```\nhello there\n27\n```\n\nAll these examples however will give warnings in the server as the new version has array sizes.  The above code should be:\n\n```pawn\nnew\n    str[32],\n    val;\nsscanf(\"hello\\\\ there 27\", \"s[32]i\", str, val);\n```\n\nAs 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:\n\n```pawn\n#define STR_SIZE 32\nnew\n    str[STR_SIZE],\n    val;\nsscanf(\"hello\\\\ there 27\", \"s\n#STR_SIZE \"]i\", str, val);\n```\n\nOr better yet, you can now use `[*]` to pass a string length as an additional parameter (see \"Provided Lengths\" below).\n\nSo when you change your string size you don't need to change your specifiers.\n\n### Packed Strings\n\n`z` and `Z` return packed strings.  They are otherwise identical to `s` and `S`, so see the `Strings` documentation above for more details.\n\n### Arrays\n\nOne 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:\n\n```pawn\nnew\n    arr[5];\nsscanf(\"1 2 3 4 5\", \"a\u003ci\u003e[5]\", arr);\n```\n\nThe `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.\n\nArrays can now also be combined with strings (see below), specifying the string size in the array type:\n\n```\na\u003cs[10]\u003e[12]\n```\n\nThis will produce an array of 12 strings, each up to 10 characters long (9 + NULL).  Optional string arrays still follow the optional array syntax:\n\n```\nA\u003cs[10]\u003e(hello)[12]\n```\n\nHowever, unlike numbers you can't specify a progression and have it fill up.  This code:\n\n```\nA\u003ci\u003e(0, 1)[4]\n```\n\nWill by default produce:\n\n```\n0, 1, 2, 3\n```\n\nHowever, this code:\n\n```\nA\u003cs[10]\u003e(hi, there)[4]\n```\n\nWill by default produce:\n\n```\n\"hi, there\", \"hi, there\", \"hi, there\", \"hi, there\"\n```\n\nAs normal, you can add brackets in to the default string value with `\\)`:\n\n```\nA\u003cs[10]\u003e(hi (code\\))[4]\n```\n\nIt should also be noted that there is NO length checking on default strings.  If you do:\n\n```\nA\u003cs[10]\u003e(This is longer than 10 characters)[4]\n```\n\nYou 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).\n\n### Enums\n\nThis 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):\n\n```pawn\nenum\n    E_DATA\n{\n    E_DATA_C,\n    Float:E_DATA_X,\n    E_DATA_NAME[32],\n    E_DATA_Z\n}\n\nmain\n{\n    new\n        var[E_DATA];\n    sscanf(\"1 12.0 Bob c\", \"e\u003cifs[32]c\u003e\", var);\n}\n```\n\nNow I'll be impressed if you can read that code straight off, so I'll explain it slowly:\n\n```pawn\ne - Start of the `enum` type\n\u003c - Starts the specification of the structure of the enum\ni - An integer, corresponds with E_DATA_C\nf - A float, corresponds with E_DATA_X\ns[32] - A 32 cell string, corresponds with E_DATA_NAME\nc - A character, corresponds with E_DATA_Z\n\u003e - End of the enum specification\n```\n\nNote 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).\n\n### Provided Lengths\n\nBoth strings and arrays take a length, normally specified in the string with (say) `s[32]`.  However, this system has some extreme limitations - most notably macros.  This code will not work:\n\n```pawn\n#define LEN 32\nsscanf(params, \"s[LEN]\", str);\n```\n\nThis code will work:\n\n```pawn\n#define LEN 32\nsscanf(params, \"s[\"#LEN\"]\", str);\n```\n\nBut this code won't even compile due to a compiler issue stringifying brackets before version 3.10.11.\n\n```pawn\n#define LEN (32)\nsscanf(params, \"s[\"#LEN\"]\", str);\n```\n\nThis code will work, but is a bit awkward:\n\n```pawn\nsscanf(params, \"s[(32)]\", str);\n```\n\nThis code will compile, but then also won't work:\n\n```pawn\n#define LEN 8*4\nsscanf(params, \"s[\"#LEN\"]\", str);\n```\n\nFor this reason you can pass string and array lengths as additional parameters using `*` for the length:\n\n```pawn\n#define LEN 8*4\nsscanf(params, \"s[*]\", LEN, str);\n```\n\nThe lengths appear BEFORE the destination, arrays first then strings:\n\n```pawn\nnew int, arr[5][10], str[32];\nsscanf(params, \"ia\u003cs[*]\u003e[*]s[*]\", int, sizeof (arr), sizeof (arr[]), arr, 32, str);\n```\n\nThere are three main specifiers here - `i`, `a\u003cs[*]\u003e[*]`, and `s[*]`; and each is handled entirely independently.  So the first parameter (`int`) is for `i`, the next three (`sizeof (arr), sizeof (arr[]), arr`) are for `a\u003cs[*]\u003e[*]`, and the final two (`32, str`) are for `s[*]`.  The first and last are easy to understand, the second one not so much.  The sizes are in reverse order, outer to inner, so `sizeof (arr)` is for the `*` in the outer `a\u003c...\u003e[*]` and `sizeof (arr[])` is for the `*` in the inner `s[*]`  Then the destination variable is given after all information about the specifier has been derived.\n\nThe same applies to strings in enums:\n\n```pawn\nenum E_EXAMPLE\n{\n\tFloat:FLOAT,\n\tSTR_1[32],\n\tSTR_2[64],\n\tINT,\n}\n\nnew dest[E_EXAMPLE];\nsscanf(params, \"e\u003cfs[*]s[*]i\u003e\", _:STR_2 - _:STR_1, 64, dest);\n```\n\nAnd to arrays of users (see below):\n\n```pawn\nnew ids[3], i;\nsscanf(params, \"u[*]\", sizeof (ids), ids);\n```\n\nThis allows you to pass variable lengths if you don't want to use all of a string, and use the full power of the pre-processor to generate lengths at compile-time.  It also bypasses the compiler stringise bug with brackets in strings.  `extract` (see below) now uses `*` for all strings and arrays as well, so will similarly fully use the pre-processor.\n\n### Quiet\n\nThe two new specifiers `{` and `}` are used for what are known as `quiet` specifiers.  These are inputs which are read and checked, but not saved.  For example:\n\n```pawn\nsscanf(\"42 -100\", \"{i}i\", var);\n```\n\nClearly 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:\n\n```pawn\nsscanf(\"hi -100\", \"{i}i\", var);\n```\n\nAlthough 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:\n\n```pawn\nsscanf(\"1 2 3\", \"i{ii}\", var);\nsscanf(\"1 2 3\", \"{iii}\");\nsscanf(\"1 2 3\", \"i{a\u003ci\u003e[2]}\", var);\n```\n\nYou can also embed quiet sections inside enum specifications:\n\n```pawn\nsscanf(\"1 12.0 Bob 42 INFINITY c\", \"e\u003cifs[32]{ig}c\u003e\", var);\n```\n\nQuiet sections cannot contain other quiet sections, however they can include enums which contain quiet sections.\n\n### Searches\n\nSearches 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 `\\\\`:\n\n```pawn\nsscanf(\"10 11 woo 12\", \"i'woo'i\", var0, var1);\n```\n\nGives:\n\n```\n10\n12\n```\n\nYou could achieve the same effect with:\n\n```pawn\nsscanf(\"10 11 woo 12\", \"i{is[1000]}i\", var0, var1);\n```\n\nBut 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.\n\n### Enums\n\nThis is a feature similar to quiet sections, which allows you to skip overwriting certain parts of an enum:\n\n```\ne\u003cii-i-ii\u003e\n```\n\nHere the `-` is a `minus`, and tells sscanf that there is an enum element there, but not to do anything, so if you had:\n\n```pawn\nenum E\n{\n    E_A,\n    E_B,\n    E_C,\n    E_D,\n    E_E\n}\n```\n\nAnd 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:\n\n```\ne\u003cii-i-i{ii}i\u003e\n```\n\nThat 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:\n\n```\nE\u003cii-i-ii\u003e\n```\n\nYou should ONLY specify THREE defaults, not all five:\n\n```\nE\u003cii-i-ii\u003e(11, 22, 55)\n```\n\n### Delimiters\n\nThe 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:\n\n```pawn\nsscanf(\"1,2,3\", \"p,iii\", var0, var1, var2);\n```\n\nIs now:\n\n```pawn\nsscanf(\"1,2,3\", \"p\u003c,\u003eiii\", var0, var1, var2);\n```\n\nThe old version will still work, but it will give a warning.  Enum specifications can include delimiters, and is the only time `\u003c\u003e`s are contained in other `\u003c\u003e`s:\n\n```pawn\nsscanf(\"1 12.0 Bob,c\", \"e\u003cifp\u003c,\u003es[32]c\u003e\", var);\n```\n\nNote that the delimiter will remain in effect after the enum is complete.  You can even use `\u003e` as a specifier by doing `p\u003c\\\u003e\u003e` (or the older `p\u003e`).\n\nWhen used with strings, the collection behaviour is overruled.  Most specifiers are still space delimited, so for example this will work:\n\n```pawn\nsscanf(\"1 2 3\", \"p\u003c;\u003eiii\", var0, var1, var2);\n```\n\nDespite the fact that there are no `;`s.  However, strings will ONLY use the specified delimiters, so:\n\n```pawn\nsscanf(\"hello 1\", \"p\u003c-\u003es[32]i\", str, var);\n```\n\nWill NOT work - the variable `str` will contain `hello 1`.  On the other hand, the example from earlier, slightly modified:\n\n```pawn\nsscanf(\"hello there\u003e27\", \"p\u003c\u003e\u003es[32]i\", str, var);\n```\n\nWILL work and will give an output of:\n\n```\nhello there\n27\n```\n\nYou 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:\n\n```pawn\nsscanf(\"(4, 5, 6, 7)\", \"P\u003c(),\u003e{s[2]}iiii\", a, b, c, d);\n```\n\nThis uses a `quiet section` to ignore anything before the first `(`, and then uses multiple delimiters to end all the text.  Example:\n\n```pawn\nsscanf(\"42, 43; 44@\", \"P\u003c,;@\u003ea\u003ci\u003e[3]\", arr);\n```\n\n### Optional specifiers\n\nEVERY format specifier (that is, everything except `''`, `{}` and `p`) now has an optional equivalent - this is just their letter capitalised.  In addition to optional specifiers, there are also now default values:\n\n```pawn\nsscanf(\"\", \"I(12)\", var);\n```\n\nThe `()`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:\n\n```pawn\nsscanf(\"1 2\", \"A\u003ci\u003e(3)[4]\", arr);\n```\n\nNote 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:\n\n```\n1, 2, 3, 3\n```\n\nArray default values are clever, the final value of:\n\n```pawn\nsscanf(\"\", \"A\u003ci\u003e(3,6)[4]\", arr);\n```\n\nWill be:\n\n```\n3, 6, 9, 12\n```\n\nThe difference between `3` and `6` is `3`, so the values increase by that every index.  Note that it is not very clever, so:\n\n```pawn\nsscanf(\"\", \"A\u003ci\u003e(1,2,2)[4]\", arr);\n```\n\nWill produce:\n\n```\n1, 2, 2, 2\n```\n\nThe 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:\n\n```pawn\nsscanf(\"\", \"a\u003cI\u003e(1,2,2)[4]\", arr);\n```\n\nIs invalid syntax, the `A` must be the capital part.\n\nEnums can also be optional:\n\n```pawn\nsscanf(\"4\", \"E\u003cifs[32]c\u003e(1, 12.0, Bob, c)\", var);\n```\n\nIn that code all values except `4` will be default.  Also, again, you can escape commas with `\\\\` in default enum strings.  Some final examples:\n\n```pawn\nsscanf(\"1\", \"I(2)I(3)I(4)\", var0, var1, var2);\nsscanf(\"\", \"O(045)H(0xF4)B(0b0100)U(Y_Less)\", octnum, hexnum, binnum, user);\nsscanf(\"0xFF\", \"N(0b101)\");\n```\n\nThat 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.\n\n### Users\n\nThe `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`.\n\nAdditionally `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:\n\nPrevious:\n\n```pawn\nsscanf(params, \"U(Y_Less)\", id);\nif (id == INVALID_PLAYER_ID)\n{\n    // Y_Less or the entered player is not connected.\n}\n```\n\nNew:\n\n```pawn\nsscanf(params, \"U(-1)\", id);\nif (id == -1)\n{\n    // No player was entered.\n}\nelse if (id == INVALID_PLAYER_ID)\n    // Entered player is not connected.\n}\n```\n\nSee the section on options for more details.\n\nUsers 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:\n\n```\n0) Y_Less\n1) [CLAN]Y_Less\n2) Jake\n3) Alex\n4) Hass\n```\n\nThis code:\n\n```pawn\nnew ids[3], i;\nif (sscanf(\"Le\", \"?\u003cMATCH_NAME_PARTIAL=1\u003eu[3]\", ids)) printf(\"Error in input\");\nfor (i = 0; ids[i] != INVALID_PLAYER_ID; ++i)\n{\n    if (ids[i] == cellmin)\n    {\n        printf(\"Too many matches\");\n        break;\n    }\n    printf(\"id = %d\", ids[i]);\n}\nif (i == 0) printf(\"No matching players found.\");\n```\n\nWill output:\n\n```\nid = 0\nid = 1\nToo many matches\n```\n\nSearching `Les` instead will give:\n\n```\nid = 0\nid = 1\n```\n\nAnd searching without `MATCH_NAME_PARTIAL` will give:\n\n```\nNo matching players found.\n```\n\nBasically, 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.\n\nWhen 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`.\n\nNote also that user arrays can't be combined with normal arrays or enums, but normal single-return user specifiers still can be.\n\nJust like with array sizes you can use `*` to pass an additional variable for the default, so you can do:\n\n```pawn\nsscanf(params, \"I(*)\", INVALID_ID, output);\n```\n\nAs with arrays the default value comes *before* the output value in the parameter list.  When combined with `[*]` the order is *default*, then *size*, then *output*:\n\n```pawn\nsscanf(params, \"A\u003ci\u003e(*)[*]\", DEFAULT_VALUE, ARRAY_SIZE, output);\n```\n\nString defaults can use `\\` to escape `)` or `*` within them if you want a string to contain those values.\n\nHowever, currently neither arrays nor strings use the value from the `*` parameter, but both will still skip the parameter.\n\n### Custom (kustom) specifiers\n\nThe latest version of sscanf adds a new `k` specifier to allow you to define your own specifers in PAWN:\n\n```pawn\nsscanf(params, \"uk\u003cplayerstate\u003e\", playerid, state);\n```\n\n`k\u003cplayerstate\u003e` allows you to type a number or the textual name of a player state.  To convert this string name to a value we need a conversion function:\n\n```pawn\n@kustom() playerstate(string[])\n{\n    if ('0' \u003c= string[0] \u003c= '9')\n    {\n        new\n            ret = strval(string);\n        if (0 \u003c= ret \u003c= 9)\n        {\n            return ret;\n        }\n    }\n    else if (!strcmp(string, \"PLAYER_STATE_NONE\")) return 0;\n    else if (!strcmp(string, \"PLAYER_STATE_ONFOOT\")) return 1;\n    else if (!strcmp(string, \"PLAYER_STATE_DRIVER\")) return 2;\n    else if (!strcmp(string, \"PLAYER_STATE_PASSENGER\")) return 3;\n    else if (!strcmp(string, \"PLAYER_STATE_WASTED\")) return 7;\n    else if (!strcmp(string, \"PLAYER_STATE_SPAWNED\")) return 8;\n    else if (!strcmp(string, \"PLAYER_STATE_SPECTATING\")) return 9;\n}\n```\n\nThe code above, when added to the top level of your mode, will add the `playerstate` specifier used in `k\u003cplayerstate\u003e`.  With this a user can either type a number or the full name of a state and the function will convert the name or number to a number.\n\nThis system supports optional custom specifiers (`K`) with no additional PAWN code.  This optional kustom specifier takes a default value that is NOT (as of sscanf 2.8) parsed by the given callback:\n\n```pawn\nsscanf(params, \"uK\u003cplayerstate\u003e(0)\", playerid, state);\n```\n\nSo in this example `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:\n\n```\nK\u003cvehicle\u003e(999)\n```\n\nThe new version of `sscanf2.inc` includes functions for `k\u003cweapon\u003e` and `k\u003cvehicle\u003e` 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.\n\nNote 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.\n\nAlso as of sscanf 2.8, `k` can be used in both arrays and enums.  Prior to sscanf 2.13.3 `@kustom()` was `SSCANF:`; [this is bad for many reasons](https://github.com/pawn-lang/YSI-Includes/blob/5.x/annotations.md) but the old version still works of course (because backwards compatibility is important).\n\n### Colours\n\nsscanf 2.10.0 introduced colours in addition to normal hex numbers.  They are parsed almost identically, but have slightly more constraints on their forms.  Colours must be HEX values exactly 3, 6, or 8 digits long.  3 digit numbers are as in CSS - `#RGB` becomes `0xRRGGBBAA` with default alpha, 6 digit numbers are already `0xRRGGBBAA` with default alpha, 8 digit numbers are the full colour with alpha.  The default default alpha is `255` (`FF`), but this can be changed with the `SSCANF_ALPHA` option; for example setting the default alpha to `AA` would be `?\u003cSSCANF_ALPHA=170\u003e`.  Why do they use `m`, not some sane letter?  Simple - all the good descriptive letters were already used.\n\nThe different lengths have slightly different semantics in what is accepted, to reduce the changes of incorrect values being parsed.  You can also customise exactly which input types you accept with the `SSCANF_COLOUR_FORMS` option.\n\n#### 3 digits\n\nA 3-digit hex value MUST be prefixed with `#` as in CSS, and each component is multiplied by `0x11` to give the final component value.  `#FAB` would become `0xFFAABBFF`, `#123` would become `0x112233FF`, `000` would be rejected because there is no `#`.\n\n#### 6 digits\n\nA 6-digit hex colour MAY be prefixed by `#` as in CSS, but doesn't have to be; it can also be prefixed by `0x` or nothing at all.  `#123456`, `0x123456`, and `123456` are all the same value, all valid, and will all give an output of `0x123456FF` with the default alpha value.  Furthermore, a 6-digit hex value may be optionally enclosed in `{}`s - `{8800DD}` is valid, but no other length in `{}`s are valid.\n\nMore valid examples:\n\n* `FFFFFF`\n* `0x000000`\n* `0x010101`\n* `#EEEEEE`\n* `{000000}`\n\nMore invalid examples:\n\n* `FFFFFFF` - 7 digits\n* `0x00000` - 5 digits\n* `#EEEE` - 4 digits\n* `{}` - 0 digits\n* `{BBB}` - 3 digits, but not `#` prefix\n* `{12345678}` - 8 digits, but inside `{}`s`\n* `{123456` - 6 digits, but no closing `}`.\n\n#### 8 digits\n\n8-digit colours are the simplest - the alpha is specified explicitly and there are only two possible input forms - `0x` prefix and no prefix.  I.e. either `0x88995566` or `88995566`.\n\n### End Of Input\n\nThe `!` specifier must come at the end of a specifier and makes the match much stricter.  This will pass:\n\n```pawn\nsscanf(\"4 5 6 7 8\", \"iii\", a, b, c);\n```\n\nThis will not:\n\n```pawn\nsscanf(\"4 5 6 7 8\", \"iii!\", a, b, c);\n```\n\nThe `!` at the end checks that there is no input (except whitespace) remaining.\n\n## Options\n\nThe 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:\n\n```pawn\nSSCANF_Option(SSCANF_QUIET, 1);\n```\n\nThis sets the `SSCANF_QUIET` option globally.  Every time `sscanf` is called the option (see below) will be in effect.  Note that the use of `SSCANF_QUIET` instead of the string `\"SSCANF_QUIET\"` is entirely valid here - all the options are defined in the sscanf2 include already (but you can use the string if you want).\n\nAlternatively you can use `?` to specify an option locally - i.e. only for the current sscanf call:\n\n```pawn\nsscanf(params, \"si\", str, num);\nsscanf(params, \"?\u003cSSCANF_QUIET=1\u003esi\", str, num);\nsscanf(params, \"si\", str, num);\n```\n\n`s` without a length is wrong, and the first and last `sscanf` calls will give an error 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:\n\n```pawn\nSSCANF_Option(SSCANF_QUIET, 1);\nsscanf(params, \"si\", str, num);\nsscanf(params, \"?\u003cSSCANF_QUIET=0\u003esi\", str, num);\nsscanf(params, \"si\", str, num);\n```\n\nNote 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.\n\nTo specify multiple options requires multiple calls:\n\n```pawn\nSSCANF_Option(SSCANF_QUIET, 1);\nSSCANF_Option(MATCH_NAME_PARTIAL, 0);\nsscanf(params, \"?\u003cSSCANF_QUIET=1\u003e?\u003cMATCH_NAME_PARTIAL=0\u003es[10]i\", str, num);\n```\n\nYou can also read the current value of an option by ommitting the second parameter:\n\n```pawn\nnew quiet = SSCANF_Option(SSCANF_QUIET);\n```\n\nThe options are:\n\n### OLD_DEFAULT_NAME:\n\nThe 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.\n\n### MATCH_NAME_PARTIAL:\n\nCurrently 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.\n\n### CELLMIN_ON_MATCHES:\n\nWhatever 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:\n\n```pawn\nsscanf(params, \"?\u003cCELLMIN_ON_MATCHES=1\u003eU(-1)\", id);\nif (id == -1)\n{\n\t// No player was entered.\n}\nelse if (id == cellmin)\n{\n\t// Multiple matches found\n}\nelse if (id == INVALID_PLAYER_ID)\n{\n\t// Entered player is not connected.\n}\nelse\n{\n\t// Found just one player.\n}\n```\n\n### SSCANF_QUIET:\n\nDon't print any errors to the console.  REALLY not recommended unless you KNOW your code is stable and in production.\n\n### OLD_DEFAULT_KUSTOM:\n\nAs 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:\n\n```\nK\u003cvehicle\u003e(Veyron)\n```\n\nBecause that is not a valid vehicle name in GTA.  The new version now JUST takes a number and returns that regardless:\n\n```\nK\u003cvehicle\u003e(9999)\n```\n\nThis setting reverts to the old behaviour.\n\n### SSCANF_ALPHA:\n\nSpecify 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:\n\n```pawn\nSSCANF_Option(SSCANF_ALPHA, 128);\n```\n\n### SSCANF_COLOUR_FORMS:\n\nThere 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:\n\n* `1` - `#RGB`\n* `2` - `#RRGGBB`\n* `4` - `0xRRGGBB`\n* `8` - `RRGGBB`\n* `16` - `{RRGGBB}`\n* `32` - `0xRRGGBBAA`\n* `64` - `RRGGBBAA`\n\nSo to ONLY accept SA:MP `SendClientMessage` colours use:\n\n```pawn\nSSCANF_Option(SSCANF_COLOUR_FORMS, 16);\n```\n\nTo only accept 8-digit values use:\n\n```pawn\nSSCANF_Option(SSCANF_COLOUR_FORMS, 96);\n```\n\nDefault values (those specified between `()`s for `M`) ignore this setting - they can always use any form.\n\n### SSCANF_ARGB:\n\nSpecify whether the returned colour is `ARGB` or `RGBA`:\n\n```pawn\nSSCANF_Option(SSCANF_ARGB, 1); // Set 3- and 6-digit colour outputs to `AARRGGBB`.\nSSCANF_Option(SSCANF_ARGB, 0); // Set 3- and 6-digit colour outputs to `RRGGBBAA` (default).\n```\n\n### MATCH_NAME_SIMILARITY:\n\nUse the same text similarity metrics as in kustom matchers to find the best name match to a given input.  The value given is the cutoff threshold for matches.  A value of `-1` disables this setting:\n\n```pawn\nsscanf(\"Y_Lass\", \"?\u003cMATCH_NAME_SIMILARITY=0.3\u003eu\", id);\n```\n\nWill probably find `Y_Less` as the closest matching name.  A similarity of `1.0` would return only exact matches; a similarity of `0.0` will always return something, even if the input is total gibberish.  When set (i.e. not `-1`) this option overrides `MATCH_NAME_PARTIAL`, but works well with all the other name matching options.  This is the only float option, and so needs a tag override to read:\n\n```pawn\nnew Float:similarity = Float:SSCANF_Option(MATCH_NAME_SIMILARITY);\n```\n\n### ERROR_CODE_IN_RET:\n\nThe return value from `sscanf` is `0` for no error, or the index of the specifier that failed.  If you get a failure you can call `SSCANF_GetLastError` to get the exact error code, or you can set this option to `true` to have the return value also include this error value in the return, ORed with the index.\n\n### WARNINGS_AS_ERRORS:\n\nThis setting allows `sscanf` to fail when a warning is given, as well as an error.\n\n### ERROR_CATEGORY_ONLY:\n\nWhen `ERROR_CODE_IN_RET` is enabled returns the error category instead of the exact error code.\n\n## `extract`\n\nI've written some (extendable) macros so you can do:\n\n```pawn\nextract params -\u003e new a, string:b[32], Float:c; else\n{\n    return SendClientMessage(playerid, COLOUR_RED, \"FAIL!\");\n}\n```\n\nThis will compile as:\n\n```pawn\nnew a, string:b[32], Float:c;\nif (unformat(params, \"is[32]f\", a, b, c))\n{\n    return SendClientMessage(playerid, COLOUR_RED, \"FAIL!\");\n}\n```\n\nNote that `unformat` is the same as `sscanf`, also note that the `SendClientMessage` part is optional:\n\n```pawn\nextract params -\u003e new a, string:b[32], Float:c;\n```\n\nWill simply compile as:\n\n```pawn\nnew a, string:b[32], Float:c;\nunformat(params, \"is[32]f\", a, b, c);\n```\n\nBasically it just simplifies sscanf a little bit (IMHO).  I like new operators and syntax, hence this, examples:\n\n```pawn\n// An int and a float.\nextract params -\u003e new a, Float:b;\n// An int and an OPTIONAL float.\nextract params -\u003e new a, Float:b = 7.0;\n// An int and a string.\nextract params -\u003e new a, string:s[32];\n// An int and a playerid.\nextract params -\u003e new a, player:b;\n```\n\nAs I say, the syntax is extendable, so to add hex numbers you would do:\n\n```pawn\n#define hex_EXTRO:%0##%1,%2|||%3=%9|||%4,%5) EXTRY:%0##%1H\"(\"#%9\")\"#,%2,%3|||%4|||%5)\n#define hex_EXTRN:%0##%1,%2|||%3|||%4,%5) EXTRY:%0##%1h,%2,%3|||%4|||%5)\n#define hex_EXTRW:%0##%1,%2|||%3[%7]|||%4,%5) EXTRY:%0##%1a\u003ch\u003e[*],%2,(%7),%3|||%4|||%5)\n```\n\nThat 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\u003ch\u003e` in the examples above for `optional`, `required` and `required array` (no optional arrays yet besides strings)).\n\nNew examples (with `hex` added):\n\n```pawn\n// A hex number and a player.\nextract params -\u003e new hex:a, player:b;\n// 32 numbers then 32 players.\nextract params -\u003e new a[32], player:b[32];\n// 11 floats, an optional string, then an optional hex number.\nextract params -\u003e new Float:f[11], string:s[12] = \"optional\", hex:end = 0xFF;\n```\n\nThe 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:\n\n```pawn\n#define player:\n#define hex:\n```\n\nTo avoid tag mismatch warnings (to remove them AFTER the compiler has used them to determine the correct specifier).\n\nThe very first example had an `else`, this will turn:\n\n```pawn\nunformat(params, \"ii\", a, b);\n```\n\nIn to:\n\n```pawn\nif (unformat(params, \"ii\", a, b))\n```\n\nYou 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:\n\n```pawn\nelse return SendClientMessage(playerid, 0xFF0000AA, \"Usage: /cmd \u003cwhatever\u003e\");\n```\n\nThere is now the ability to split by things other than space (i.e. adds `P\u003c?\u003e` to the syntax - updated from using `p` to `P`):\n\n```pawn\nextract params\u003c|\u003e -\u003e new a, string:b[32], Float:c;\n```\n\nWill simply compile as:\n\n```pawn\nnew a, string:b[32], Float:c;\nunformat(params, \"P\u003c|\u003eis[32]f\", a, b, c);\n```\n\nNote that for technical reasons you can use `\u003c-\u003e` (because it looks like the arrow after the `extract` keyword).  You also can't use `\u003c;\u003e`, `\u003c,\u003e`, or `\u003c)\u003e` because of a bug with `#`, but you can use any other character (most notably `\u003c|\u003e`, 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.\n\n## Similarity\n\nA lot of the code uses a concept of \"similarity\" when comparing two strings.  This is less accurate, but thus more forgiving, than an exact string comparison.  For example if a player types `nrg` they probably mean the `NRG-500` bike, and if they type `mac10` they probably mean the `Mac 10` gun.  Using standard comparisons these inputs would never match, and using the common \"Levenshtein Distance\" (as old versions of the code did) can produce some strange results.  For example typing `NRG` as a vehicle input will return `TUG` using Levenshtein, because converting `NRG` to `TUG` only requires three replacements, whereas converting `NRG` to `NRG-500` requires four additions, thus is technically further away despite being more similar to a person.\n\nThus the new version of sscanf uses a \"similarity\" metric instead of a \"distance\" metric to work out which strings are probably the same.  This is used by the `k\u003cvehicle\u003e` and `k\u003cweapon\u003e` pre-defined custom specifiers, and for the `MATCH_NAME_SIMILARITY` option used with `u`, `r`, and `q` (i.e. for names).  The algorithm is roughly based on one of \"Bigram Similarity\":\n\n* Split the two input strings up in to letter pairs, ignoring punctuation and spaces.  For example `\"Shotty\"` becomes `\"sh\", \"ho\", \"ot\", \"tt\", \"ty\"` and `\"Combat Shotgun\"` becomes `\"co\", \"om\", \"mb\", \"ba\", \"at\", \"ts\", \"sh\", \"ho\", \"ot\", \"tg\", \"gu\", \"un\"`.\n\n* For each pair of letters from the first word, count how many of them appear in the second word.  For example `\"Combat Shotgun\"` contains `\"sh\", \"ho\", \"ot\"` from `\"Shotty\"`, 3/5 (60%) of the available letter pairs.\n\n* Repeat in the reverse, so `\"Shotty\"` contains `\"sh\", \"ho\", \"ot\"` from `\"Combat Shotgun\"`, 3/12 (25%) of the available letter pairs (the fact that the two result lists are the same here is pure fluke and irrelevant).\n\n* Invert the fractions to get the percentage of pairs not in the other word, multiply the two numbers, and subtract from `1.0` to give the final result.  So for the example above:\n\n```\nsimilarity = 1 - ((1 - 60%) * (1 - 25%))\nsimilarity = 1 - (0.4 * 0.75)\nsimilarity = 1 - 0.3\nsimilarity = 0.7\n```\n\nA similarity is always between `0.0` (totally different) and `1.0` (absolutely identical), and many functions will return the string from a list with the highest similarity.  So `k\u003cvehicle\u003e` will find the vehicle with the best similarity to the given input.  However, this doesn't always make sense.  If the input is `\"dkaoiingsjk\"`, that is obviously not any sane vehicle, but it still has a similarity rating to every vehicle name; the ratings will be very low, but one must be the highest (it is the `\"Sandking\"`, with a similarity rating of `0.228571`).  Just calling the similarity functions blindly will thus always return a result, even if that result is gibberish, thus the functions also optionally accept a *threshold*, a similarity value that the result must be better than.  So for the above example passing a threshold of `0.5` would result in no valid vehicle being returned.\n\n## Error Returns\n\nWhen everything is fine, `sscanf` returns `0` - meaning \"no error\".  When it isn't fine the return value will tell you exactly where and what the error was.  By default the return value is just the index (plus one) of the specifier that failed:\n\n```pawn\nindex = sscanf(\"bad input\", \"ii\", a, b); // Returns `1`\n```\n\nHere the return value is `1` because the first `i` failed to parse a number.  Normally this should be index `0`, but that would mean \"no error\", so we must increase the offset by one.  If the first specifier passes but the second one fails, then the return value will be `2`:\n\n```pawn\nindex = sscanf(\"45 input\", \"ii\", a, b); // Returns `2`\n```\n\nThe same is true for missing data instead of bad data:\n\n```pawn\nindex = sscanf(\"45\", \"ii\", a, b); // Returns `2`\n```\n\nIf you want more information about exactly what went wrong you can request this using `SSCANF_GetLastError`:\n\n```pawn\nindex = sscanf(\"45\", \"ii\", a, b); // Returns `2`\nif (index)\n{\n\terror = SSCANF_GetLastError(); // Returns `1004` (\"out of input\")\n}\n```\n\nThe error codes will persist internally as the \"last\" error until a new sscanf function is called (with the exception of `SSCANF_GetLastError`, hence why `SSCANF_ClearLastError` also exists to reset this value without any other side-effects).\n\nAlternatively you can set an option for `sscanf` to return both the index and error code together:\n\n```pawn\nSSCANF_Option(ERROR_CODE_IN_RET, 1);\n\nerror = sscanf(\"45 hello\", \"ii\", a, b); // Returns `SSCANF_ERROR(2, 1011)`\nerror = sscanf(\"45\", \"ii\", a, b); // Returns `SSCANF_ERROR(2, 1004)`\n```\n\nNow instead of returning just `2` to indicate the index of the specifier at which the error was encountered; the call also returns an error code representing exactly what the problem was.  `1004` means \"out of input\".  All the other possible codes are documented in this file in the sections [Errors/Warnings](#errorswarnings) and [Additional Codes](#additional-codes).\n\nIf you want slighty more information than just the index, but also not quite as much information as the exact error code you can use the error categories instead.  These categories group the (currently around eighty) error codes in to a few manageable subdivisions.  See [Error Categories](#error-categories) for the full list.  Use `SSCANF_GetErrorCategory` to get this reduced information:\n\n```pawn\nindex = sscanf(\"45\", \"ii\", a, b); // Returns `2`\nif (index)\n{\n\terror = SSCANF_GetLastError(); // Returns `1004` (\"out of input\")\n\tcategory = SSCANF_GetErrorCategory(error); // Returns `SSCANF_ERROR_MISSING`\n}\n```\n\nOr again, using options in the previous example:\n\n```pawn\nSSCANF_Option(ERROR_CODE_IN_RET, 1);\nSSCANF_Option(ERROR_CATEGORY_ONLY, 1);\n\nerror = sscanf(\"45 hello\", \"ii\", a, b); // Returns `SSCANF_ERROR(2, SSCANF_ERROR_INVALID)`\nerror = sscanf(\"45\", \"ii\", a, b); // Returns `SSCANF_ERROR(2, SSCANF_ERROR_MISSING)`\n```\n\nThe `SSCANF_ERROR` macro is slightly clever and allows you to avoid a tiny bit of repetition when using categories:\n\n```pawn\nSSCANF_Option(ERROR_CODE_IN_RET, 1);\nSSCANF_Option(ERROR_CATEGORY_ONLY, 1);\n\nerror = sscanf(\"45 hello\", \"ii\", a, b); // Returns `SSCANF_ERROR(2, INVALID)`\nerror = sscanf(\"45\", \"ii\", a, b); // Returns `SSCANF_ERROR(2, MISSING)`\n```\n\nsscanf has both \"errors\", which stop processing, and \"warnings\", which can be continued on from but may give strange results.  The distinction is currently a little blurred since some errors actually continue like warnings (so should be renamed).  The error system will not report warnings, including things like `sscanf warning: String buffer overflow.`.  By default.  This can be changed with yet a third setting:\n\n```pawn\nnew str[8]\nerror = sscanf(\"a very long string\", \"?\u003cERROR_CODE_IN_RET=1\u003e?\u003cERROR_CATEGORY_ONLY=1\u003e?\u003cWARNINGS_AS_ERRORS=1\u003e?\u003cSSCANF_QUIET=1\u003es[8]\", str);\n```\n\nThis code will return an error of `SSCANF_ERROR(5, OVERFLOW)` and will not (thanks to `SSCANF_QUIET`) print anything in the console.  This problem can finally be dealt with entirely in-code.  Note that the index here is `5`, not `1`, because EVERY specifier counts towards this offset.  There are four `?` specifiers in the string, hence the `s` is `5`.\n\nIt should be noted that `sscanf` and `SSCANF_GetLastError` will return the last error/warning generated by the call.  If there is more than one problem only the final one will be reported.\n\n### Additional Codes\n\nError codes under 1000 relate to printed messages, and are documented along-side their messages in [Section 14](#errorswarnings).  Error codes above 1000 are regular failures:\n\n* *1001* - A colour was given, but it doesn't match an allowed format:\n\n```pawn\nsscanf(input, \"?\u003cSSCANF_COLOUR_FORMS=12\u003em\", \"#112233\", colour);\n```\n\n* *1002* - `SSCANF_Join` was called with an invalid player ID:\n\n```pawn\nSSCANF_Join(134234);\n```\n\n* *1003* - The search string was not found in the input:\n\n```pawn\nsscanf(\"some input\", \"'hello'\");\n```\n\n* *1004* - Arguably the most basic failure - not enough input:\n\n```pawn\nsscanf(\"6\", \"ii\", a, b);\n```\n\n* *1005* - `SSCANF_Leave` was called with an invalid player ID:\n\n```pawn\nSSCANF_Leave(134234);\n```\n\n* *1006* - The matrix was not allocated correctly in `SSCANF_Levenshtein`.\n\n* *1007* - The text compared with `SSCANF_TextSimilarity` is too short:\n\n```pawn\nSSCANF_TextSimilarity(\"a\", \"b\");\n```\n\n* *1008* - `SSCANF_IsConnected` was called with an invalid player ID:\n\n```pawn\nSSCANF_IsConnected(134234);\n```\n\n* *1009* - Excess data seen, with a specifier ending `!`.\n\n```pawn\nsscanf(\"4 5 6\", \"ii!\", a, b);\n```\n\n* *1010* - No matching alternate found:\n\n```pawn\nsscanf(\"0x11 0x22\", \"ii|bb\", variant, a, b, c, d);\n```\n\n* *1011* - An int was wanted, but the input didn't match:\n\n```pawn\nsscanf(\"hello\", \"i\", output);\n```\n\n* *1012* - A number was wanted, but the input didn't match:\n\n```pawn\nsscanf(\"hello\", \"n\", output);\n```\n\n* *1013* - A hex was wanted, but the input didn't match:\n\n```pawn\nsscanf(\"hello\", \"h\", output);\n```\n\n* *1014* - A colour was wanted, but the input didn't match:\n\n```pawn\nsscanf(\"hello\", \"m\", output);\n```\n\n* *1015* - An octal was wanted, but the input didn't match:\n\n```pawn\nsscanf(\"0xFF\", \"o\", output);\n```\n\n* *1016* - A float was wanted, but the input didn't match:\n\n```pawn\nsscanf(\"NAN\", \"f\", output);\n```\n\n* *1017* - A character was wanted, but the input didn't match:\n\n```pawn\nsscanf(\"long\", \"c\", output);\n```\n\n* *1018* - A binary was wanted, but the input didn't match:\n\n```pawn\nsscanf(\"44\", \"b\", output);\n```\n\n* *1019* - An extended (IEEE 754) float was wanted, but the input didn't match:\n\n```pawn\nsscanf(\"IsANumber\", \"g\", output);\n```\n\n### Error Categories\n\nThere are almost 100 different unique error codes, but many of them can often be dealt with in the same way.  For example - \"***1011* - An int was wanted, but the input didn't match**\" and \"***1012* - A number was wanted, but the input didn't match**\" can probably usually use the same result processing (especially since they can never be returned by the same specifier).  Hence the errors are grouped together in to a few large groups for ease of processing.  These categories are:\n\n* `SSCANF_ERROR_NONE` - Not an error.\n* `SSCANF_ERROR_NATIVE` - A generic problem with calling a native, such as invalid parameters or out of memory.\n* `SSCANF_ERROR_SPECIFIER` - The specifier itself is wrong in some way.  This is always a coding error.\n* `SSCANF_ERROR_INVALID` - The parsed input was present, but invalid.  For example `hello` for an integer.\n* `SSCANF_ERROR_MISSING` - Required input data was missing.\n* `SSCANF_ERROR_EXCESS` - There was too much input data (with `!`).\n* `SSCANF_ERROR_COLOUR` - A colour was parsed, but was not one of the accepted formats.\n* `SSCANF_ERROR_OVERFLOW` - The infamous \"string buffer overflow\" warning.  Enable warnings as errors to get this in code.\n* `SSCANF_ERROR_NOT_FOUND` - The search string (`''`) wasn't found.\n* `SSCANF_ERROR_NO_ALTS` - None of the alternatives (`|`) matched.\n\nYou may notice that the last few \"groups\" are very small compared to the others, but they cover issues that are both unique and easy to rectify in code.\n\n## Alternates\n\nAlternates allow you to create two different specifier strings and find the best (or first) match.  Using this you could for example detect whether something is a boolean, a decimal, or a hexadecimal.  The `|` specifier splits the different options, and will report the branch taken (or `0` if it can't find one) in the first return parameter.  Each other specifier must then appear in the parameter list separately:\n\n```pawn\nnew alt, b, i, h;\nif (sscanf(\"45\", \"b|i|h\", alt, b, i, h)) == 0)\n{\n  switch (alt)\n\t{\n\t\tcase 1: printf(\"Typed a binary number.\");\n\t\tcase 2: printf(\"Typed a decimal number.\");\n\t\tcase 3: printf(\"Typed a hexadecimal number.\");\n\t}\n}\n```\n\n`45` is not a boolean number, therefore the first branch fails.  `45` is a decimal number, therefore the second branch succeedes, the variable `i` is set to `45`, and the variable `alt` is set to `2` because the second branch was successful.  `b` and `h` may be un-initialised after this call.  You could re-use the same variable for all three numeric outputs:\n\n```pawn\nnew alt, num;\nif (sscanf(\"45\", \"b|i|h\", alt, num, num, num)) == 0)\n{\n  switch (alt)\n\t{\n\t\tcase 1: printf(\"Typed a binary number.\");\n\t\tcase 2: printf(\"Typed a decimal number.\");\n\t\tcase 3: printf(\"Typed a hexadecimal number.\");\n\t}\n}\n```\n\nThe order here is important  - `45` is a valid integer, but it is also a valid hexadecimal number; `111` is valid for all three.  Thus we go most specific to least specific.  In the other order every single call will return *hexadecimal*:\n\n```pawn\nnew alt, num;\nif (sscanf(\"45\", \"h|i|b\", alt, num, num, num)) == 0)\n{\n  switch (alt)\n\t{\n\t\tcase 1: printf(\"Typed a hexadecimal number.\");\n\t\tcase 2: printf(\"Will never happen.\");\n\t\tcase 3: printf(\"Will never happen.\");\n\t}\n}\n```\n\nThe order also matters a lot if you have default values:\n\n```pawn\nsscanf(\"42\", \"iS[*](hello)|i\", alt, num, sizeof (str), str, num);\n```\n\nThat will return `alt == 1` and set `str` to `\"hello\"`, despite the fact that there is only one numeric input and the second branch is a better match.  Alternates do not look for the *best* match, they look for the *first* match.\n\nOne very good use for alternates is string matching.  See [A Basic Shop](#a-basic-shop) for an example of using `''` in multiple alternates.\n\nTemporary settings will NOT persist betwen alternate branches:\n\n```pawn\nSSCANF_SetOption(SSCANF_COLOUR_FORMS, 2);\nsscanf(\"#567890\", \"?\u003cSSCANF_COLOUR_FORMS=16\u003em|m\", alt, colour1, colour2);\n// `alt` is `2`.\n// `colour1` is undefined.\n// `colour2` is `0x56789000`\n```\n\nThe first branch will fail because the input is not in colour form `16` (`{RRGGBB}`), but after the `|` the local options are reset to the global options and the colour *is* in form `2` (`#RRGGBB`).  Weirdly this means that errors relating to the alternate itself (such as `SSCANF_ERROR(1, 1010)`) will always use the global settings, because no local settings can ever apply to `|`.\n\nNote that if you combine alternates and error returns you probably want to disable `ERROR_CODE_IN_RET` and just use `SSCANF_GetLastError`, and you absolutely want to disable `WARNINGS_AS_ERRORS`.  Enabling warnings as errors can make alternates that would normally pass, fail:\n\n```pawn\nsscanf(\"5 a-long-string\", \"is[8]|ii\", alt, int, string, a, b);\n```\n\nThat code will pass and set `alt` to `1` when `WARNINGS_AS_ERRORS` is disabled, but will fail with code `1010` when `WARNINGS_AS_ERRORS` is enabled because the first alternative will give a string buffer overflow warning, then turn that in to an error.  An alternate with an error can't possibly be correct.  However, `SSCANF_GetLastError` will still report the overflow while allowing the alternate to be found.\n\nThe errors will also be the last found error, probably in the final alternate section.  The specifier index is relative to the start of the format string, plus `1` for the alternates.  In the specifier `ii|x` the `x` has index `4` - `+1` for the eternal error base, `+1` for the alternate specifier (regardless of the number of `|`s) and `+2` for `ii`.  In `bb|ii|x` the `x` has index `6`, because despite there being two `|`s, alternates are just one thing.  They are also always specifier index `1` (despite the fact that they don't appear at the start of the string); so if `sscanf` returns `1`, you know it means \"no alternate match found\".\n\n## All Specifiers\n\nFor quick reference, here is a list of ALL the specifiers and their use:\n\n|                  Format                  |                   Use                  |\n| ---------------------------------------- | -------------------------------------- |\n|  `A\u003ctype\u003e(default)[length]`              |  Optional array of given type          |\n|  `a\u003ctype\u003e[length]`                       |  Array of given type                   |\n|  `B(binary)`                             |  Optional binary number                |\n|  `b`                                     |  Binary number                         |\n|  `C(character)`                          |  Optional character                    |\n|  `c`                                     |  Character                             |\n|  `D(integer)`                            |  Optional integer                      |\n|  `d`                                     |  Integer                               |\n|  `E\u003cspecification\u003e(default)`             |  Optional enumeration of given layout  |\n|  `e\u003cspecification\u003e`                      |  Enumeration of given layout           |\n|  `F(float)`                              |  Optional floating point number        |\n|  `f`                                     |  Floating point number                 |\n|  `G(float/INFINITY/-INFINITY/NAN/NAN_E)` |  Optional float with IEEE definitions  |\n|  `g`                                     |  Float with IEEE definitions           |\n|  `H(hex value)`                          |  Optional hex number                   |\n|  `h`                                     |  Hex number                            |\n|  `I(integer)`                            |  Optional integer                      |\n|  `i`                                     |  Integer                               |\n|  `K\u003ccallback\u003e(any format number)`        |  Optional custom operator              |\n|  `k\u003ccallback\u003e`                           |  Custom operator                       |\n|  `L(true/false)`                         |  Optional logical truthity             |\n|  `l`                                     |  Logical truthity                      |\n|  `M(hex value)`                          |  Optional colour                       |\n|  `m`                                     |  Colour                                |\n|  `N(any format number)`                  |  Optional number                       |\n|  `n`                                     |  Number                                |\n|  `O(octal value)`                        |  Optional octal value                  |\n|  `o`                                     |  Octal value                           |\n|  `P\u003cdelimiters\u003e`                         |  Multiple delimiters change            |\n|  `p\u003cdelimiter\u003e`                          |  Delimiter change                      |\n|  `Q(any format number)`                  |  Optional bot (bot)                    |\n|  `q`                                     |  Bot (bot)                             |\n|  `R(any format number)`                  |  Optional player (player)              |\n|  `r`                                     |  Player (player)                       |\n|  `S(string)[length]`                     |  Optional string                       |\n|  `s[length]`                             |  String                                |\n|  `U(any format number)`                  |  Optional user (bot/player)            |\n|  `u`                                     |  User (bot/player)                     |\n|  `X(hex value)`                          |  Optional hex number                   |\n|  `x`                                     |  Hex number                            |\n|  `Z(string)[length]`                     |  Optional packed string                |\n|  `z[length]`                             |  Packed string                         |\n|  `'string'`                              |  Search string                         |\n|  `{`                                     |  Open quiet section                    |\n|  `}`                                     |  Close quiet section                   |\n|  `%`                                     |  Deprecated optional specifier prefix  |\n|  `?`                                     |  Local options specifier               |\n|  `!`                                     |  Strict end of input check             |\n|  `|`                                     |  Alterates                             |\n\n## Full API\n\n### `sscanf(const data[], const format[], {Float, _}:...);`\n\nThe main sscanf function.\n\n### `unformat(const data[], const format[], {Float, _}:...);`\n\nAn alternate name for the main `sscanf` function, since the sscanf format specifiers do not conform\nto the C API of the same name.\n\n### `SSCANF_Option(const name[], value);`\n\nSet an option.\n\n### `SSCANF_Option(const name[]);`\n\nGet an option.\n\n### `SSCANF_SetOption(const name[], value);`\n\nSet an option explicitly (no overloaded `SSCANF_Option` call).\n\n### `SSCANF_GetOption(const name[], value);`\n\nGet an option explicitly (no overloaded `SSCANF_Option` call).\n\n### `SSCANF_Version(version[], size = sizeof (version));`\n\nGet the SSCANF plugin version as a string (e.g. `\"2.11.2\"`).  Compare to the macro `SSCANF_VERSION_STRING`.\n\n### `SSCANF_Version();`\n\nGet the SSCANF plugin version as binary coded decimal (BCD) number (e.g. `0x021003`).  Compare to the macro `SSCANF_VERSION_BCD`.\n\n### `SSCANF_VersionString(version[], size = sizeof (version));`\n\nGet the SSCANF plugin version as a string explicitly (no overloaded `SSCANF_Version` call).\n\n### `SSCANF_VersionBCD();`\n\nGet the SSCANF plugin version as BCD explicitly (no overloaded `SSCANF_Version` call).\n\n### `SSCANF_Levenshtein(const string1[], const string2[]);`\n\nComputes the [Levenshtein Distance](https://en.wikipedia.org/wiki/Levenshtein_distance) between the two input strings.  Useful in `k` callback functions to determine if the entered string is close to a possible string.\n\n### `Float:SSCANF_TextSimilarity(const string1[], const string2[]);`\n\nThis 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 uses various other (unfixed) algorithms; currently comparing all pairs of letters between the two strings to work out what percentage of each string is in the other string, then multiplying 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.\n\n### `SSCANF_GetClosestString(const input[], const candidates[][], threshold = cellmax, count = sizeof (candidates));`\n\nTakes 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.  Deprecated in favour of `SSCANF_GetSimilarString`.\n\n### `SSCANF_GetClosestValue(const input[], const candidates[][], const results[], fail = cellmin, threshold = cellmax, count = sizeof (candidates), check = sizeof (results));`\n\nSimilar to [`SSCANF_GetClosestString`](#sscanf_getcloseststringconst-input-const-candidates-threshold--cellmax-count--sizeof-candidates) 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.  Deprecated in favour of `SSCANF_GetSimilarValue`.\n\n### `SSCANF_GetSimilarString(const input[], const candidates[][], Float:threshold = 0.111111, count = sizeof (candidates));`\n\nLike `SSCANF_GetClosestString`, but using a more human-friendly text comparison function.\n\n### `SSCANF_GetSimilarValue(const input[], const candidates[][], const results[], fail = cellmin, Float:threshold = 0.111111, count = sizeof (candidates), check = sizeof (results));`\n\nLike `SSCANF_GetClosestValue`, but using a more human-friendly text comparison function.\n\n### `SSCANF_VERSION_STRING`\n\nThe SSCANF include version as a string.\n\n### `SSCANF_VERSION_BCD`\n\nThe SSCANF include version as BCD, as a `stock const` variable.\n\n### `SSCANF_VERSION`\n\nThe SSCANF include version as BCD, as a `const` to work at compile-time.\n\n### `SSCANF_NO_K_VEHICLE`\n\nExclude the default `k\u003cvehicle\u003e` kustom specifier from being compiled when this is defined before including the include file.\n\n### `SSCANF_NO_K_WEAPON`\n\nExclude the default `k\u003cweapon\u003e` kustom specifier from being compiled when this is defined before including the include file.\n\n### `SSCANF_NO_NICE_FEATURES`\n\nSeveral sscanf features, such as file and line numbers in errors, only work on the new compiler.  If you want to use the old compiler you'll get an error because those nice features won't work.  If you want to compile anyway without those features you need to define this symbol before inclusion.\n\n### `SSCANF_GetLastError();`\n\nGets the error code set by the most recent call to any other sscanf function.  Calling this function does \u003cb\u003enot\u003c/b\u003e clear the error, unlike some other implementations.  To do that call `SSCANF_ClearLastError`.\n\n### `SSCANF_ClearLastError();`\n\nResets the error code from any previous *sscanf* function call.  Note that calling any other function also resets this value, but with more side-effects.\n\n### `sscanf_error:SSCANF_GetErrorCategory(error);`\n\nThere are almost 100 different unique error codes, but many of them can often be dealt with in the same way.  For example - **`1011` - An int was wanted, but the input didn't match** and **`1012` - A number was wanted, but the input didn't match** can probably usually use the same result processing (especially since they can never be returned by the same specifier).  Hence the errors are grouped together in to a few large groups for ease of processing.\n\n### `SSCANF_GetErrorSpecifier();`\n\nGets the index of the specifier that generated the last error.  When the error was in an alternate this will return the final underlying specifier that had an error, while `sscanf` itself will just return an index of `1` for \"no matching alternative\".  This is the only place where the two may be different.\n\n### `SSCANF_Debug();`\n\nPrint a lot of useful debugging information.\n\n## Errors/Warnings\n\n### MSVRC100.dll not found\n\nIf you get this error, DO NOT just download the dll from a random website.  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:\n\nhttp://www.microsoft.com/download/en...s.aspx?id=5555\n\n### sscanf error: System not initialised\n\n*Error Code: 1*\n\nIf 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.\n\n### sscanf warning: String buffer overflow.\n\n*Error Code: 2*\n\nThis error comes up when people try and put too much data in to a string.  For example:\n\n```pawn\nnew str[10];\nsscanf(\"Hello there, how are you?\", \"s[10]\", str);\n```\n\nThat 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:\n\n```pawn\nnew str[10], num;\nsscanf(\"Hello there you|42\", \"p\u003c|\u003es[10]i\", str, num);\n```\n\nIn this case `num` is still correctly set to `42`, but the warning is given for lost data (`e you`).\n\nCurrently 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.\n\n### sscanf warning: Optional types invalid in array specifiers, consider using 'A'.\n\n*Error Code: 3*\n\nA specifier such as:\n\n```\na\u003cI(5)\u003e[10]\n```\n\nHas been written - here indicating an array of optional integers all with the default value `5`.  Instead you should use:\n\n```\nA\u003ci\u003e(5)[10]\n```\n\nThis is an optional array of integers all with the default value `5`, the advantage of this is that arrays can have multiple defaults:\n\n```\nA\u003ci\u003e(5, 6)[10]\n```\n\nThat will set the array to `5, 6, 7, 8, 9, 10, 11, 12, 13, 14` by default, incrementing by the found difference each time.\n\n### sscanf warning: Optional types invalid in enum specifiers, consider using 'E'.\n\n*Error Code: 4*\n\nSimilar to the previous warning, A specifier such as:\n\n```\ne\u003cI(5)f\u003e\n```\n\nIs invalid, instead use:\n\n```\nE\u003cif\u003e(42, 11.0)\n```\n\nThis forces ALL the parts of an enum to be optional - anything less is not possible.\n\n### sscanf error: Multi-dimensional arrays are not supported.\n\n*Error Code: 5*\n\nThis is not allowed:\n\n```pawn\nsscanf(params, \"a\u003ca\u003ci\u003e[5]\u003e[10]\", arr);\n```\n\nA work-around can be done using:\n\n```pawn\nsscanf(params, \"a\u003ci\u003e[50]\", arr[0]);\n```\n\nThat will correctly set up the pointers for the system.\n\n### sscanf error: Search strings are not supported in arrays.\n\n*Error Code: 6*\n\nThis is not allowed (see the section on search strings):\n\n```\na\u003c'hello'i\u003e[10]\n```\n\n### sscanf error: Delimiters are not supported in arrays.\n\n*Error Code: 7*\n\nThis is not allowed:\n\n```\na\u003cp\u003c,\u003ei\u003e[10]\n```\n\nInstead use:\n\n```\np\u003c,\u003ea\u003ci\u003e[10]\n```\n\n### sscanf error: Quiet sections are not supported in arrays.\n\n*Error Code: 8*\n\nThis is not allowed:\n\n```\na\u003c{i}\u003e[10]\n```\n\nInstead use:\n\n```\n{a\u003ci\u003e[10]}\n```\n\n### sscanf error: Unknown format specifier '?'.\n\n*Error Code: 9*\n\nThe given specifier is not known (this post contains a full list of all the specifiers near the bottom).\n\n### sscanf warning: Empty default values.\n\n*Error Code: 10*\n\nAn optional specifier has been set as (for example):\n\n```\nI()\n```\n\nInstead of:\n\n```\nI(42)\n```\n\nThis does not apply to strings as they can be legitimately empty.\n\n### sscanf warning: Unclosed default value.\n\n*Error Code: 11*\n\nYou have a default value on an optional specifier that looks like:\n\n```\nI(42\n```\n\nInstead of:\n\n```\nI(42)\n```\n\n### sscanf warning: No default value found.\n\n*Error Code: 12*\n\nYou have no default value on an optional specifier:\n\n```\nI\n```\n\nInstead of:\n\n```\nI(42)\n```\n\n### sscanf warning: Unenclosed specifier parameter.\n\n*Error Code: 13*\n\nYou are using the old style:\n\n```\np,\n```\n\nInstead of:\n\n```\np\u003c,\u003e\n```\n\nAlternatively a custom delimiter of:\n\n```\np\u003c\n```\n\nWas found with no matching `\u003e` after one character.  Instead use:\n\n```\np\u003c,\u003e\n```\n\nOr, if you really do want a delimiter of `\u003c` then use:\n\n```\np\u003c\u003c\u003e\n```\n\nNote that this does not need to be escaped; however, a delimiter of `\u003e` does:\n\n```\np\u003c\\\u003e\u003e\n```\n\nThe `\\` may also need to be escaped when writing actual PAWN strings, leading to:\n\n```\np\u003c\\\\\u003e\u003e\n```\n\nThis also applies to array types (`a\u003c` vs `a\u003ci\u003e`), and will result in an invalid array type.\n\n### sscanf warning: No specified parameter found.\n\n*Error Code: 14*\n\nThe format specifier just ends with:\n\n```\np\n```\n\nThis also applies to array types (`a` vs `a\u003ci\u003e`).\n\n### sscanf warning: Missing string length end.\n\n*Error Code: 15*\n\nSee below.\n\n### sscanf warning: Missing length end.\n\n*Error Code: 16*\n\nA string has been written as:\n\n```\ns[10\n```\n\nInstead of:\n\n```\ns[10]\n```\n\nI.e. the length has not been closed.\n\n### sscanf error: Invalid data length.\n\n*Error Code: 17*\n\nAn invalid array or string size has been specified (0, negative, or not a number).\n\n### sscanf error: Invalid character in data length.\n\n*Error Code: 18*\n\nA string or array has been given a length that is not a number.\n\n### sscanf error: String/array must include a length, please add a destination size.\n\n*Error Code: 19*\n\nArrays are newer than strings, so never had an implementation not requiring a length, so there is no compatibility problems in REQUIRING a length to be given.\n\n### sscanf warning: Can't have nestled quiet sections.\n\n*Error Code: 20*\n\nYou have tried writing something like this:\n\n```\n{i{x}}\n```\n\nThis has a quiet section (`{}`) inside another one, which makes no sense.\n\n### sscanf warning: Not in a quiet section.\n\n*Error Code: 21*\n\n`}` was found with no corresponding `{`:\n\n```\ni}\n```\n\n### sscanf warning: Can't remove quiet in enum.\n\n*Error Code: 22*\n\nThis is caused by specifiers such as:\n\n```\n{fe\u003ci}x\u003e\n```\n\nWhere the quiet section is started before the enum, but finishes part way through it rather than after it.  This can be emulated by:\n\n```\n{f}e\u003c{i}x\u003e\n```\n\n### sscanf error: Arrays are not supported in enums.\n\n*Error Code: 23*\n\nBasically, you can't do:\n\n```\ne\u003cfa\u003ci\u003e[5]f\u003e\n```\n\nYou can, however, still do:\n\n```\ne\u003cfiiiiif\u003e\n```\n\nThis is a little more awkward, but is actually more technically correct given how enums are compiled.\n\n### sscanf warning: Unclosed string literal.\n\n*Error Code: 24*\n\nA specifier starts a string with `'`, but doesn't close it:\n\n```\ni'hello\n```\n\n### sscanf warning: sscanf specifiers do not require '%' before them.\n\n*Error Code: 25*\n\n`format` uses code such as `%d`, sscanf only needs `d`, and confusingly the C equivalent function (also called `sscanf`) DOES require `%`.  Sorry.\n\n### sscanf error: Insufficient default values.\n\n*Error Code: 26*\n\nDefault values for arrays can be partially specified and the remainder will be inferred from the pattern of the last two:\n\n```\nA\u003ci\u003e(0, 1)[10]\n```\n\nThat 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:\n\n```\nE\u003ciiff\u003e(0, 1, 0.0, 1.0)\n```\n\nThis will not do:\n\n```\nE\u003ciiff\u003e(0, 1)\n```\n\n### sscanf error: Options are not supported in enums.\n\n*Error Code: 27*\n\nThe `?` specifier for local options must appear outside any other specifier.\n\n### sscanf error: Options are not supported in arrays.\n\n*Error Code: 28*\n\nThe `?` specifier for local options must appear outside any other specifier.\n\n### sscanf error: No option value.\n\n*Error Code: 29*\n\nAn option was specified with no value:\n\n```\n?\u003cOLD_DEFAULT_NAME\u003e\n```\n\n### sscanf error: Unknown option name.\n\n*Error Code: 30*\n\nThe given option was not recognised.  Check spelling and case:\n\n```\n?\u003cNOT_A_VALID_NAME=1\u003e\n```\n\n### sscanf warning: Could not find function SSCANF:?.\n\n*Error Code: 31*\n\nA `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.\n\n### sscanf error: SSCANF_Init has incorrect parameters.\n\n*Error Code: 32*\n\nYou edited something in the sscanf2 include - undo it or redownload it.\n\n### sscanf error: SSCANF_Join has incorrect parameters.\n\n*Error Code: 33*\n\nYou edited something in the sscanf2 include - undo it or redownload it.\n\n### sscanf error: SSCANF_Leave has incorrect parameters.\n\n*Error Code: 34*\n\nYou edited something in the sscanf2 include - undo it or redownload it.\n\n### sscanf error: SSCANF_IsConnected has incorrect parameters.\n\n*Error Code: 35*\n\nYou edited something in the sscanf2 include - undo it or redownload it.\n\n### sscanf error: SSCANF_Version has incorrect parameters.\n\n*Error Code: 36*\n\nYou edited something in the sscanf2 include - undo it or redownload it.\n\n### sscanf error: SSCANF_Option has incorrect parameters.\n\n*Error Code: 37*\n\nYou edited something in the sscanf2 include - undo it or redownload it.\n\n### sscanf error: SetPlayerName has incorrect parameters.\n\n*Error Code: 38*\n\nYou somehow managed to call `SetPlayerName` without passing all the parameters.  This can only happen by redefining the native declaration itself, so undo any edits to it.\n\n### sscanf error: Missing required parameters.\n\n*Error Code: 39*\n\n`sscanf` itself was called without sufficient parameters.  I.e. the input and specifier strings are missing.  This can also happen when you edit the include itself to mess with the file/line macros.\n\nYou somehow managed to call `SetPlayerName` without passing all the parameters.  This can only happen by redefining the native declaration itself, so undo any edits to it.\n\n### `fatal error 111: user error: sscanf already defined, or used before inclusion.`\n\nThere are two ways to trigger this:  The first is to have another copy of `sscanf` defined before you include the file.  This used to be the only known way to trigger this error, so the error said:\n\n\u003e `sscanf (possibly the PAWN version) already defined.`\n\nHowever, there is a second way to trigger it - using `sscanf` before including it.  This used to be possible, but isn't any more as `sscanf` is now a macro that inserts additional data in to the call.  So this will fail:\n\n```pawn\n#include \u003ca_samp\u003e\n\nmain()\n{\n\tsscanf(\"hi\", \"hi\");sscanf (possibly the PAWN version) already defined.\n}\n\n#include \u003csscanf2\u003e\n```\n\nTo fix this, just include `\u003csscanf2\u003e` before you use `sscanf`.\n\n### `error 004: function \"sscanf\" is not implemented`\n\nSee below.\n\n### `error 004: function \"sscanf\" is not implemented - include \u003csscanf2\u003e first.`\n\nThese are the same error, the only difference being compilers and settings.  Obviously the more useful (second) error which tells you how to solve this problem.  Similar to [the previous error](#fatal-error-111-user-error-sscanf-already-defined-or-used-before-inclusion) this happens when `sscanf` is used before being included, but in a slightly different way:\n\n```pawn\n#include \u003ca_samp\u003e\n\nmain()\n{\n\t#if defined sscanf\n\t\tsscanf(\"hi\", \"hi\");\n\t#endif\n}\n\n#include \u003csscanf2\u003e\n```\n\nThis code tries to be slightly clever, but fails.  The correct way to check for sscanf inclusion is:\n\n```pawn\n#if !defined _INC_SSCANF\n\t#error You need sscanf\n#endif\n```\n\nThere is a third version of this error which looks like:\n\n```\nerror 004: function \"sscanf\" is not implemented \u003clibrary\u003esscanf\u003c/library\u003e      \u003cremarks\u003e  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 \u003cc\u003eSSCANF__\u003c/c\u003e and passes the current file and line number as  well for improved error messages.  \u003c/remarks\u003e \n```\n\nFor more information on why, see [this compiler issue](https://github.com/pawn-lang/compiler/issues/705).\n\n### sscanf error: Pawn component not loaded.\n\n*Error Code: 40*\n\nWhen loading sscanf as a component on open.mp the Pawn component is also required.  Ensure `pawn.dll` or `pawn.so` is in the `components/` directory.\n\n### sscanf warning: Unknown `player-\u003esetName()` return.\n\n*Error Code: 41*\n\nThe open.mp sscanf component was probably built against an old version of the SDK.  Check for an updated version at https://www.github.com/Y-Less/sscanf/.\n\n### sscanf error: This script was built with the component version of the include.\n\n*Error Code: 42*\n\nWhen compiling a script with the open.mp official includes the sscanf2 include file compiles different code, which assumes that the natives will be loaded as a component.  This error comes when the natives are loaded as a plugin instead, as certain features like `SSCANF_Init` are no longer required in the component case.  Move `sscanf.dll` or `sscanf.so` from the `plugins/` directory to the `components/` directory and remove the legacy plugin name from `plugins` (in server.cfg) or `pawn.legacy_plugins` (in config.json).\n\n### sscanf error: Unable to allocate memory.\n\n*Error Code: 43*\n\nYou ran out of RAM.  Unfortunately there's not much that can be done about this error.  `sscanf` only allocates very small amounts of memory, and only temporarilly, so you must be *really* low to get this.\n\n### sscanf warning: User arrays are not supported in arrays.\n\n*Error Code: 44*\n\nThis is not allowed:\n\n```\na\u003cu[5]\u003e[10]\n```\n\nInstead use:\n\n```\nu[50]\n```\n\n### sscanf warning: Invalid values in array defaults.\n\n*Error Code: 45*\n\nThe optional array default values do not parse to the given type.  For example you've provided floats instead of integers:\n\n```\nA\u003ci\u003e(6.6, 7.7)[10]\n```\n\n### sscanf warning: Excess array defaults found.\n\n*Error Code: 46*\n\nMore optional array default values were given than needed:\n\n```\nA\u003ci\u003e(0, 1, 2, 3, 4, 5, 6)[3]\n```\n\n### sscanf warning: Format specifier does not match parameter count.\n\n*Error Code: 47*\n\nThere are more (or fewer) specifiers in the format string than there are destination parameters:\n\n```pawn\nsscanf(input, \"iiiiiiii\", a, b, c);\n```\n\n### sscanf warning: Unclosed quiet section.\n\n*Error Code: 48*\n\nA quiet section was opened but not closed:\n\n```\n{i\n```\n\n### sscanf warning: Include / plugin mismatch, please recompile your script for the latest features.\n\n*Error Code: 49*\n\nYour mode was compiled using a (possibly very) old version of `sscanf2.inc`, which doesn't have the new natives or features.  However, the plugin does have those and can give far better diagnostics (such as error line numbers) if you recompile.\n\n### sscanf warning: A minus minus makes no sense.\n\n*Error Code: 50*\n\n`-` in an `enum` declaration skips the next element in the `enum` so that you can parse partial data.  However, you've used two in a row:\n\n```\ne\u003ci--f\u003e\n```\n\n### sscanf warning: A minus option makes no sense.\n\n*Error Code: 51*\n\n`-` in an `enum` declaration skips the next element in the `enum` so that you can parse partial data.  However, you've used `-` on an optional specifier, which means you don't care if it is there, and won't save it - just skip it entirely:\n\n```\ne\u003ci-F(2.2)\u003e\n```\n\n### sscanf warning: A minus delimiter makes no sense.\n\n*Error Code: 52*\n\n`-` in an `enum` declaration skips the next element in the `enum` so that you can parse partial data.  However, `p` isn't a specifier for reading in data, so there's no type to skip:\n\n```\ne\u003ci-p\u003c,\u003e\u003e\n```\n\n### sscanf warning: A minus quiet section makes no sense.\n\n*Error Code: 53*\n\n`-` in an `enum` declaration skips the next element in the `enum` so that you can parse partial data.  A quiet section reads in input but doesn't save it.  I guess these do make sense together in retrospect, but instead of doing:\n\n```\ne\u003ci-{i}\u003e\n```\n\nDo:\n\n```\ne\u003ci-i{i}\u003e\n```\n\nTo skip data without writing it out.\n\n### sscanf warning: User arrays are not supported in enums.\n\n*Error Code: 54*\n\nYou can't do this:\n\n```\ne\u003cu[5]\u003e\n```\n\n### sscanf error: 'U(name)[len]' is incompatible with OLD_DEFAULT_NAME.\n\n*Error Code: 55*\n\nPlayer arrays end the list of results with `INVALID_PLAYER_ID`.  `OLD_DEFAULT_NAME` checks if the given player is connected by name.  Therefore if you try combine these two features you may end the list early:\n\n```\n?\u003cOLD_DEFAULT_NAME=1\u003eU(Y_Less)[5]\n```\n\n### sscanf error: 'U(num)[len]' length under 2.\n\n*Error Code: 56*\n\nUse arrays have a sentinel, i.e. they search for all matching users and end the list with `INVALID_PLAYER_ID`.  If the array size is smaller than `2` elements then the only value that can be returned is the sentinel, i.e. you won't get any useful results:\n\n```\nU(4)[1]\n```\n\n### sscanf error: 'u[len]' length under 2.\n\n*Error Code: 57*\n\nUse arrays have a sentinel, i.e. they search for all matching users and end the list with `INVALID_PLAYER_ID`.  If the array size is smaller than `2` elements then the only value that can be returned is the sentinel, i.e. you won't get any useful results:\n\n```\nu[1]\n```\n\n### sscanf error: 'Q(name)[len]' is incompatible with OLD_DEFAULT_NAME.\n\n*Error Code: 58*\n\nPlayer arrays end the list of results with `INVALID_PLAYER_ID`.  `OLD_DEFAULT_NAME` checks if the given player is connected by name.  Therefore if you try combine these two features you may end the list early:\n\n```\n?\u003cOLD_DEFAULT_NAME=1\u003eQ(Y_Less)[5]\n```\n\n### sscanf error: 'Q(num)[len]' length under 2.\n\n*Error Code: 59*\n\nUse arrays have a sentinel, i.e. they search for all matching users and end the list with `INVALID_PLAYER_ID`.  If the array size is smaller than `2` elements then the only value that can be returned is the sentinel, i.e. you won't get any useful results:\n\n```\nQ(4)[1]\n```\n\n### sscanf error: 'q[len]' length under 2.\n\n*Error Code: 60*\n\nUse arrays have a sentinel, i.e. they search for all matching users and end the list with `INVALID_PLAYER_ID`.  If the array size is smaller than `2` elements then the only value that can be returned is the sentinel, i.e. you won't get any useful results:\n\n```\nq[1]\n```\n\n### sscanf error: 'R(name)[len]' is incompatible with OLD_DEFAULT_NAME.\n\n*Error Code: 61*\n\nPlayer arrays end the list of results with `INVALID_PLAYER_ID`.  `OLD_DEFAULT_NAME` checks if the given player is connected by name.  Therefore if you try combine these two features you may end the list early:\n\n```\n?\u003cOLD_DEFAULT_NAME=1\u003eR(Y_Less)[5]\n```\n\n### sscanf error: 'R(num)[len]' length under 2.\n\n*Error Code: 62*\n\nUse arrays have a sentinel, i.e. they search for all matching users and end the list with `INVALID_PLAYER_ID`.  If the array size is smaller than `2` elements then the only value that can be returned is the sentinel, i.e. you won't get any useful results:\n\n```\nR(4)[1]\n```\n\n### sscanf error: 'r[len]' length under 2.\n\n*Error Code: 63*\n\nUse arrays have a sentinel, i.e. they search for all matching users and end the list with `INVALID_PLAYER_ID`.  If the array size is smaller than `2` elements then the only value that can be returned is the sentinel, i.e. you won't get any useful results:\n\n```\nr[1]\n```\n\n### sscanf error: (*) is not supported in strings/arrays yet.\n\n*Error Code: 64*\n\nYou can't use `*` to pass default string values in additional parameters:\n\n```pawn\nsscanf(input, \"A\u003cs\u003e(*)[5]\", \"default\", output)\n```\n\n### sscanf error: Unclosed specifier parameters.\n\n*Error Code: 65*\n\nSome parameter was not closed by `\u003e`:\n\n```\ne\u003ciiif\n```\n\n### sscanf error: No specified parameters found.\n\n*Error Code: 66*\n\nSome parameter was not opened by `\u003c`:\n\n```\neiiif\u003e\n```\n\n### sscanf error: Enums are not supported in enums.\n\n*Error Code: 67*\n\nBasically, you can't do:\n\n```\ne\u003cfe\u003ciii\u003ef\u003e\n```\n\nYou can, however, still do:\n\n```\ne\u003cfiiif\u003e\n```\n\nNested enums compile to the same as a single flat enum.  It may make less sense compared to the code, but is at least easier to write.\n\n### sscanf error: SSCANF_TextSimilarity has incorrect parameters.\n\n*Error Code: 68*\n\nYou edited something in the sscanf2 include - undo it or redownload it.\n\n### sscanf error: SSCANF_GetErrorCategory has incorrect parameters.\n\n*Error Code: 69*\n\nYou edited something in the sscanf2 include - undo it or redownload it.\n\n### sscanf error: End of text is not supported in arrays.\n\n*Error Code: 70*\n\nYou can't use `!` as the type of an array:\n\n```\na\u003c!\u003e[10]\n```\n\n### sscanf error: End of text is not supported in enums.\n\n*Error Code: 71*\n\nYou can't use `!` as a type in an enum:\n\n```\ne\u003cii!\u003e\n```\n\nJust put it after:\n\n```\ne\u003cii\u003e!\n```\n\n### sscanf warning: A minus end of text makes no sense.\n\n*Error Code: 72*\n\n`-` in an `enum` declaration skips the next element in the `enum` so that you can parse partial data.  However, `p` isn't a specifier for reading in data, so there's no type to skip:\n\n```\ne\u003ci-p\u003c,\u003e\u003e\n```\n\n### sscanf error: No alternate destination.\n\n*Error Code: 73*\n\n`-` in an `enum` declaration skips the next element in the `enum` so that you can parse partial data.  However, `p` isn't a specifier for reading in data, so there's no type to skip:\n\n```\ne\u003ci-p\u003c,\u003e\u003e\n```\n\n## Future Plans\n\n### Reserved Specifiers\n\nThe currently used specifiers are:\n\n```\nabcdefghiklmnopqrsuxz\n```\n\nThis leaves only the following specifiers:\n\n```\njtvwy\n```\n\n* `t` is for time - some sort of date/time processing that returns a unix timestamp.\n* `y` is for \"YID\" - the YSI user ID.  Don't use YSI?  Tough.\n* `v` I figure is for something to do with varargs, similar to `a` but for extra parameters.\n* `j` no idea yet.\n* `w` is the most important one to reserve - it is for ***w**ide* specifiers.  Since there are so few left it is important to establish future compatibility.  Thus `w` is a prefix that indicates that the following specifier has an alternate meaning.  So `i` is \"integer\" but `wi` is something else entirely (don't know what yet).  This scheme does recurse endlessly so `wwi` and `wwwwwi` are also different.  In this way we will never run out and can start adding support for more obscure items like iterators and jagged arrays (the original idea for `j`).  There's also a suggestion for this as `words` - `w\u003c5\u003e` for five words, but I think that makes sense as an extension to `s`.\n\n### Validity Ranges\n\nAdd a feature to simple specifiers (mainly numbers like `i`, `f`, `c`, etc) to specify what the valid values are.  So you could write an IP parser as:\n\n```\np\u003c.\u003ei\u003c0-256\u003ei\u003c0-256\u003ei\u003c0-256\u003ei\u003c0-256\u003e\n```\n\nObvioulys there is upper-bound is exclusive.  Multiple valid ranges, and open-ended ranges, can be given with `x\u003c0-10,20,22,50-90,1000-\u003e\n\n### Enums And Arrays\n\nMore of these: nested arrays in enums, 2d/3d arrays, strings in enums and arrays, etc.\n\n### Compilation\n\nBasically pre-defining specifier strings for use later:\n\n```pawn\nnew Specifier:spec = SSCANF_Compile(\"is[32]\");\n\nSSCANF_Run(input, spec, int, string);\n```\n\nYou could define the base `sscanf` as:\n\n```pawn\nsscanf(const input, const specifier, ...)\n{\n\treturn SSCANF_Run(input, SSCANF_Compile(specifier), ___(2));\n}\n```\n\n## Building\n\nIf you want to compile sscanf for yourself.\n\n### Getting The Code\n\nYou can clone the code using github.  Note that there are multiple submodules involved, so you are best using a recursive checkout:\n\n```\ngit clone --recursive https://github.com/Y-Less/sscanf/\n```\n\nNote the use of the `--recursive` argument, because this repository contains submodules.  A useful setting when cloning recursive repos is:\n\n```bash\ngit config --global url.\"git@github.com:\".insteadOf \"https://github.com/\"\n```\n\nWhich allows you to push `https://` repos you have permissions on.\n\n### Building On Windows\n\n```bash\nmkdir build\ncd build\ncmake .. -A Win32 -T ClangCL\n```\n\nOpen Visual Studio and build the solution.\n\n### Building On Linux\n\n```bash\nmkdir build\ncd build\n# May need to configure this line.\nexport CC=/usr/lib/llvm/13/bin/clang CXX=/usr/lib/llvm/13/bin/clang++\ncmake ..\nmake\n```\n\nChange `Debug` to `Release` for final versions.\n\n### Building With Docker\n\n```bash\ncd docker\n.\\build.sh\n```\n\nYou may need to set up some directories first:\n\n```bash\nmkdir build\nmkdir conan\nsudo chown 1000 build\nsudo chown 1000 conan\n```\n\nInstead you run the script as root, and target a specific distro:\n\n```bash\nUBUNTU_VERSION=18.04 sudo .\\build.sh\n```\n\nThe output is in `docker/build/`\n\n## License\n\n### Version: MPL 1.1\n\nThe contents of this file are subject to the Mozilla Public License Version\n1.1 (the \"License\"); you may not use this file except in compliance with\nthe License.  You may obtain a copy of the License at\nhttp://www.mozilla.org/MPL/\n\nSoftware distributed under the License is distributed on an \"AS IS\" basis,\nWITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License\nfor the specific language governing rights and limitations under the\nLicense.\n\nThe Original Code is the sscanf 2.0 SA:MP plugin.\n\nThe Initial Developer of the Original Code is Alex \"Y_Less\" Cole.\nPortions created by the Initial Developer are Copyright (c) 2022\nthe Initial Developer.  All Rights Reserved.\n\n### Contributor(s):\n\n* Cheaterman\n* DEntisT\n* Emmet_\n* karimcambridge\n* kalacsparty\n* Kirima\n* leHeix\n* maddinat0r\n* Southclaws\n* Y_Less\n* ziggi\n\n### Special Thanks to:\n\n* SA:MP Team past, present, and future.\n* maddinat0r, for hosting the repo for a very long time.\n* Emmet_, for his efforts in maintaining it for almost a year.\n\n## Changelog\n\n### sscanf 2.8.2 - 18/04/2015\n\n* Fixed a bug where `u` wasn't working correctly after a server restart.\n\n### sscanf 2.8.3 - 02/10/2018\n\n* Allow `k` in arrays.\n* Allow `k` to consume the rest of the line (like strings) when they are the last specifier.\n\n### sscanf 2.9.0 - 04/11/2019\n\n* Added `[*]` support.\n* Fixed bracketed lengths (`[(32)]`).\n* Ported readme to markdown.\n* Added `z` and `Z` for packed strings (thus officially removing their deprecated optional use).\n* Remove missing string length warnings - its now purely an error.\n* Remove `p,` warnings - its now purely an error.\n\n### sscanf 2.10.0 - 27/06/2020\n\n* Added `m` for colours (ran out of useful letters).\n* Added file and line details for errors.\n\n### sscanf 2.10.1 - 27/06/2020\n\n* Plugin backwards compatibility with older includes.\n\n### sscanf 2.10.2 - 28/06/2020\n\n* Fix bug in parameter counts.\n\n### sscanf 2.11.2 - 28/04/2021\n\n* Use prehooks in include.\n* Export `PawnSScanf` function from dll to other plugins.\n* `SSCANF_SetOption()` and `SSCANF_GetOption()` for more control of options.\n* `SSCANF_VERSION` and `SSCANF_Version()` to compare include and plugin versions.\n* Hide more internal functions.\n* Fix the license.\n\n### sscanf 2.10.4 - 17/01/2022\n\n* Fix trailing string literals, to allow `\"x'!'\"` for example.\n* Added `SSCANF_VERSION` for compile-time checks.\n\n### sscanf 2.11.1 - 25/01/2022\n\n* Re-added NPC mode support.\n\n### sscanf 2.11.2 - 04/02/2022\n\n* Minor Linux build fixes.\n\n### sscanf 2.11.3 - 05/02/2022\n\n* Added `SSCANF_Levenshtein` for better string candidate processing.\n* Added `SSCANF_GetClosestString` for better string candidate processing.\n* Added `SSCANF_GetClosestValue` for better string candidate processing.\n* Added `SSCANF_NO_K_VEHICLE` to disable the default `k\u003cvehicle\u003e` specifier code.\n* Added `SSCANF_NO_K_WEAPON` to disable the default `k\u003cweapon\u003e` specifier code.\n\n### sscanf 2.11.4 - 02/03/2022\n\n* Documentation comments on all functions via pawndoc.\n\n### sscanf 2.11.5 - 31/03/2022\n\n* Improve some errors caused by using `sscanf` before including it.\n\n### sscanf 2.12.1 - 05/05/2022\n\n* Integrate open.mp component support.\n\n### sscanf 2.12.2 - 11/05/2022\n\n* Switch to a different (semi made-up) word similarity function.\n* Added `SSCANF_TextSimilarity` for best string candidate processing.\n* Added `SSCANF_GetSimilarString` for best string candidate processing.\n* Added `SSCANF_GetSimilarValue` for best string candidate processing.\n* Use `OnPlayerNameChange` in the open.mp component code version.\n* Switch `SSCANF_Levenshtein` internally to use direct AMX access.\n\n### sscanf 2.13.1 - 25/06/2022\n\n* Enable similarity-based name comparisons (`MATCH_NAME_SIMILARITY`).\n* Return the best matching name by default.\n* Add `MATCH_NAME_FIRST` to revert best name match behaviour.\n* Improve `MATCH_NAME_PARTIAL`.\n* Internal cleanup and fixes.\n\n### sscanf 2.13.2 - 07/09/2022\n\n* Rebuild for open.mp beta 9 SDK changes.\n\n### sscanf 2.13.3 - 04/12/2022\n\n* Add `@kustom()` decorator for kustom specifiers.\n* Use *subhook* to hook `amx_Register` and find `SetPlayerName`.\n\n### sscanf 2.13.4 - 20/12/2022\n\n* Update open.mp SDK.\n* Added `(*)` for dynamic default values.\n\n### sscanf 2.13.5 - 28/12/2022\n\n* Move builds to CMake.\n* Add docker builds.\n* Fix `[*]` again.\n\n### sscanf 2.13.6 - 28/12/2022\n\n* Fixed a crash with `[*]` when there aren't enough parameters.\n\n### sscanf 2.13.7 - 02/01/2023\n\n* Improve GDK plugin compatibility.\n\n### sscanf 2.13.8 - 05/01/2023\n\n* \"Final\" SDK update.\n\n### sscanf 2.14.1 - 08/09/2023\n\n* Fixed a bug where setting `MATCH_NAME_FIRST` also disabled logging.\n* Re-introduced the return value of `sscanf` giving the index of the failed specifier.\n* `SSCANF_GetLastError` to get the error code in failure cases.\n* `SSCANF_ClearLastError` to reset the error code from previous failures.\n* `SSCANF_GetErrorCategory` to get the category of an error.\n* Added `WARNINGS_AS_ERRORS` option to treat warnings as errors.\n* Added `ERROR_CODE_IN_RET` option to return error codes along-side error indexes.\n* Added `ERROR_CATEGORY_ONLY` option to return error categories instead of error codes.\n* Stop defining `__PawnBuild` and use `__pawn_build` instead.\n* Add `sscanf_error` to define all error codes.\n* Add `SSCANF_ERROR` macro to combine specifier indexes and error codes for `ERROR_CODE_IN_RET`.\n* Add `!` specifier to check for strict end of input.\n\n### sscanf 2.15.1 - 10/09/2023\n\n* Added alternates via `|`.\n* `SSCANF_GetErrorSpecifier` to get the error position in failure cases.\n* `SSCANF_Debug` to dump a load of useful debugging information.\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fy-less%2Fsscanf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fy-less%2Fsscanf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fy-less%2Fsscanf/lists"}