| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012 |
- /*
- * Version: MPL 1.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the sscanf 2.0 SA:MP plugin.
- *
- * The Initial Developer of the Original Code is Alex "Y_Less" Cole.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Special Thanks to:
- *
- * SA:MP Team past, present and future
- */
- #include "SDK/amx/amx.h"
- #include "sscanf.h"
- #include "utils.h"
- extern unsigned int
- g_iTrueMax;
- extern logprintf_t
- logprintf,
- real_logprintf;
- void
- qlog(char *, ...);
- // Options:
- //
- // 1 = OLD_DEFAULT_NAME
- //
- // Parse values in "U(5)" as if they were a name that must be connected,
- // instead of just any number.
- //
- // 2 = MATCH_NAME_PARTIAL
- //
- // When searching for players, match any part of their name not just the
- // start.
- //
- // 4 = CELLMIN_ON_MATCHES
- //
- // If multiple player name matches are found, return 0x80000000.
- //
- // 8 = SSCANF_QUIET
- //
- // Disable all prints.
- //
- int
- gOptions = 0;
- void
- RestoreOpts(int opt)
- {
- gOptions = opt;
- if (gOptions & 8) logprintf = qlog;
- else logprintf = real_logprintf;
- }
- void
- DoOptions(char * name, cell value)
- {
- // Not the most flexible code I've ever written...
- if (!strincmp(name, "OLD_DEFAULT_NAME", 16))
- {
- switch (value)
- {
- case 1:
- gOptions |= 1;
- break;
- case 0:
- gOptions &= ~1;
- break;
- case -1:
- if (*(name + 16) == '=')
- {
- if (*(name + 17) == '0') gOptions &= ~1;
- else gOptions |= 1;
- }
- else
- {
- logprintf("sscanf error: No option value.");
- }
- }
- }
- else if (!strincmp(name, "MATCH_NAME_PARTIAL", 18))
- {
- switch (value)
- {
- case 1:
- gOptions |= 2;
- break;
- case 0:
- gOptions &= ~2;
- break;
- case -1:
- if (*(name + 18) == '=')
- {
- if (*(name + 19) == '0') gOptions &= ~2;
- else gOptions |= 2;
- }
- else
- {
- logprintf("sscanf error: No option value.");
- }
- }
- }
- else if (!strincmp(name, "CELLMIN_ON_MATCHES", 18))
- {
- switch (value)
- {
- case 1:
- gOptions |= 4;
- break;
- case 0:
- gOptions &= ~4;
- break;
- case -1:
- if (*(name + 18) == '=')
- {
- if (*(name + 19) == '0') gOptions &= ~4;
- else gOptions |= 4;
- }
- else
- {
- logprintf("sscanf error: No option value.");
- }
- }
- }
- else if (!strincmp(name, "SSCANF_QUIET", 12))
- {
- switch (value)
- {
- case 1:
- logprintf = qlog;
- gOptions |= 8;
- break;
- case 0:
- logprintf = real_logprintf;
- gOptions &= ~8;
- break;
- case -1:
- if (*(name + 12) == '=')
- {
- if (*(name + 13) == '0')
- {
- logprintf = real_logprintf;
- gOptions &= ~8;
- }
- else
- {
- logprintf = qlog;
- gOptions |= 8;
- }
- }
- else
- {
- logprintf("sscanf error: No option value.");
- }
- }
- }
- else if (!strincmp(name, "OLD_DEFAULT_KUSTOM", 18) || !strincmp(name, "OLD_DEFAULT_CUSTOM", 18))
- {
- switch (value)
- {
- case 1:
- gOptions |= 16;
- break;
- case 0:
- gOptions &= ~16;
- break;
- case -1:
- if (*(name + 18) == '=')
- {
- if (*(name + 19) == '0') gOptions &= ~16;
- else gOptions |= 16;
- }
- else
- {
- logprintf("sscanf error: No option value.");
- }
- }
- }
- else
- {
- logprintf("sscanf error: Unknown option name.");
- }
- }
- char
- GetSingleType(char ** format)
- {
- char
- * cur = *format,
- tmp = *cur;
- if (tmp == '<')
- {
- ++cur;
- char
- ret = *cur;
- if (ret)
- {
- ++cur;
- if (*cur == '>')
- {
- *format += 3;
- return ret;
- }
- else
- {
- logprintf("sscanf warning: Unclosed specifier parameter, assuming '<', consider using something like p<<>.");
- }
- }
- else
- {
- logprintf("sscanf warning: Unenclosed specifier parameters are deprecated, consider using something like p<<>.");
- }
- ++(*format);
- return '<';
- }
- else if (tmp)
- {
- // Legacy support.
- logprintf("sscanf warning: Unenclosed specifier parameters are deprecated, consider using something like p<%c>.", tmp);
- ++(*format);
- return tmp;
- }
- else
- {
- logprintf("sscanf warning: No specified parameter found.");
- return ' ';
- }
- }
- char *
- GetMultiType(char ** format)
- {
- char
- * cur = *format,
- * ret = cur;
- if (*cur == '<')
- {
- ++ret;
- bool
- reop = false,
- escape = false;
- do
- {
- if (*cur == '\\')
- {
- escape = !escape;
- }
- else
- {
- escape = false;
- }
- ++cur;
- if (!escape)
- {
- if (reop)
- {
- if (*cur == '>')
- {
- reop = false;
- ++cur;
- }
- }
- else
- {
- if (*cur == '<')
- {
- reop = true;
- ++cur;
- }
- }
- }
- }
- while (*cur && (reop || escape || *cur != '>'));
- if (*cur)
- {
- // Only gets here if there is a closing '>'.
- *cur = '\0';
- *format = cur + 1;
- return ret;
- }
- else
- {
- logprintf("sscanf error: Unclosed specifier parameters.");
- }
- }
- else
- {
- logprintf("sscanf error: No specified parameters found.");
- }
- *format = cur;
- return 0;
- }
- unsigned int
- GetUserString(char * string, char ** end)
- {
- unsigned int
- id = 0;
- char
- cur;
- // Get the full name, they can't contain spaces in this code. If a
- // player's name manages to contain spaces (e.g. via SetPlayerName), then
- // you can only do the part of the name up to the space.
- while ((cur = *string) && !IsSpacer(cur))
- {
- // Valid string item.
- ++string;
- if ((unsigned char)(cur - '0') >= 10)
- {
- // Invalid number, so not an ID.
- break;
- }
- // Still a valid ID, continue collecting.
- id = (id * 10) + (cur - '0');
- }
- *end = string;
- // Don't use extra checks every loop any more, just one will do.
- if (!IsSpacer(cur))
- {
- // Invalid ID, just find the end of the name.
- FindSpacer(end);
- // Save the ID as too large for detection later.
- id = g_iTrueMax;
- }
- return id;
- }
- int
- GetDecValue(char ** const input)
- {
- char *
- str = *input;
- int
- val = 0;
- unsigned char
- cur;
- // Convert to a number and test it.
- while ((cur = (unsigned char)(*str - '0')) < 10)
- {
- // Update the current value.
- val = (val * 10) + cur;
- // Update the current pointer.
- ++str;
- }
- // Save the pointer.
- *input = str;
- // Covert the sign and return without an if.
- return val;
- }
- int
- GetDec(char ** const input)
- {
- char *
- str = *input;
- int
- neg = 1;
- switch (*str)
- {
- case '-':
- neg = -1;
- // FALLTHROUGH
- case '+':
- // Check there is valid data after
- if (((unsigned char)(*(++str) - '0')) >= 10)
- {
- // Just return now, the caller will recognise this as bad.
- return 0;
- }
- }
- *input = str;
- return GetDecValue(input) * neg;
- }
- int
- GetOctValue(char ** const input)
- {
- char *
- str = *input;
- int
- val = 0;
- unsigned char
- cur;
- // Convert to a number and test it.
- while ((cur = (unsigned char)(*str - '0')) < 8)
- {
- // Update the current value.
- val = (val * 8) + cur;
- // Update the current pointer.
- ++str;
- }
- // Save the pointer.
- *input = str;
- // Covert the sign and return without an if.
- return val;
- }
- int
- GetOct(char ** const input)
- {
- char *
- str = *input;
- int
- neg = 1;
- switch (*str)
- {
- case '-':
- neg = -1;
- // FALLTHROUGH
- case '+':
- // Check there is valid data after
- if (((unsigned char)(*(++str) - '0')) >= 8)
- {
- // Just return now, the caller will recognise this as bad.
- return 0;
- }
- }
- *input = str;
- return GetOctValue(input) * neg;
- }
- int
- GetHexValue(char ** const input)
- {
- char *
- str = *input;
- int
- val = 0;
- // Rewrote it using a big switch.
- for ( ; ; )
- {
- switch (*str)
- {
- case '0':
- val = (val * 16) + 0x00;
- break;
- case '1':
- val = (val * 16) + 0x01;
- break;
- case '2':
- val = (val * 16) + 0x02;
- break;
- case '3':
- val = (val * 16) + 0x03;
- break;
- case '4':
- val = (val * 16) + 0x04;
- break;
- case '5':
- val = (val * 16) + 0x05;
- break;
- case '6':
- val = (val * 16) + 0x06;
- break;
- case '7':
- val = (val * 16) + 0x07;
- break;
- case '8':
- val = (val * 16) + 0x08;
- break;
- case '9':
- val = (val * 16) + 0x09;
- break;
- case 'a':
- case 'A':
- val = (val * 16) + 0x0A;
- break;
- case 'b':
- case 'B':
- val = (val * 16) + 0x0B;
- break;
- case 'c':
- case 'C':
- val = (val * 16) + 0x0C;
- break;
- case 'd':
- case 'D':
- val = (val * 16) + 0x0D;
- break;
- case 'e':
- case 'E':
- val = (val * 16) + 0x0E;
- break;
- case 'f':
- case 'F':
- val = (val * 16) + 0x0F;
- break;
- default:
- // UGLY UGLY UGLY!
- goto sscanf_hex_switch;
- }
- ++str;
- }
- // UGLY UGLY UGLY - Needed for the double level break, which isn't native
- // in C.
- sscanf_hex_switch:
- // Save the pointer.
- *input = str;
- // Covert the sign and return without an if.
- return val;
- }
- int
- GetHex(char ** const input)
- {
- char *
- str = *input;
- int
- neg = 1;
- switch (*str)
- {
- case '-':
- neg = -1;
- // FALLTHROUGH
- case '+':
- // Check there is valid data after
- if (((unsigned char)(*(++str) - '0')) >= 10)
- {
- // Just return now, the caller will recognise this as bad.
- return 0;
- }
- }
- if (*str == '0')
- {
- if (*(str + 1) == 'x' || *(str + 1) == 'X')
- {
- // Check there is real data, otherwise it's bad.
- str += 2;
- if ((*str < '0') || ((*str > '9') && ((*str | 0x20) < 'a')) || ((*str | 0x20) > 'f'))
- {
- *input = str - 1;
- return 0;
- }
- }
- }
- else if ((*str < '0') || ((*str > '9') && ((*str | 0x20) < 'a')) || ((*str | 0x20) > 'f'))
- {
- *input = str;
- return 0;
- }
- *input = str;
- return GetHexValue(input) * neg;
- // Convert to a number and test it. Horribly manually optimised - one of
- // these days I'll try write an ASM hex reader and see how well that works.
- // Actually I think I have written one before, but I don't know where it is
- // and I'm not sure how optimised it is. Anyway, this version works and
- // avoids loads of big switches (although a single large switch may be more
- // efficient thinking about it).
- /*while ((cur = (unsigned char)((*str | 0x20) - '0')))
- {
- if (cur < 10)
- {
- // 0 - 9
- val = (val * 16) + cur;
- }
- else
- {
- cur -= ('a' - '0');
- if (cur < 6)
- {
- // A - F, a - f
- val = (val * 16) + cur + 10;
- }
- else
- {
- // End of the number.
- // Save the pointer.
- *input = str;
- // Covert the sign and return without an if.
- return val * neg;
- }
- }
- }*/
- }
- unsigned int
- GetBoolValue(char ** const input)
- {
- char *
- str = *input;
- unsigned int
- val = 0;
- for ( ; ; ++str)
- {
- if (*str == '0')
- {
- val <<= 1;
- }
- else if (*str == '1')
- {
- val = (val << 1) | 1;
- }
- else
- {
- break;
- }
- }
- // Save the pointer.
- *input = str;
- return val;
- }
- int
- GetBool(char ** const input)
- {
- char *
- str = *input;
- if (*str == '0')
- {
- if (*(str + 1) == 'b' || *(str + 1) == 'B')
- {
- // Check there is real data, otherwise it's bad.
- str += 2;
- if (*str != '0' && *str != '1')
- {
- *input = str - 1;
- return 0;
- }
- }
- }
- else if (*str != '1')
- {
- *input = str;
- return 0;
- }
- *input = str;
- return (int)GetBoolValue(input);
- }
- int
- GetNumber(char ** const input)
- {
- char *
- str = *input;
- int
- neg = 1;
- switch (*str)
- {
- case '-':
- neg = -1;
- // FALLTHROUGH
- case '+':
- // Check there is valid data after
- if (((unsigned char)(*(++str) - '0')) >= 10)
- {
- // Just return now, the caller will recognise this as bad.
- return 0;
- }
- }
- if (*str == '0')
- {
- ++str;
- switch (*str)
- {
- case 'x':
- case 'X':
- // Hex.
- ++str;
- if (((*str >= '0') && (*str <= '9')) || (((*str | 0x20) >= 'a') && ((*str | 0x20) <= 'f')))
- {
- *input = str;
- return GetHexValue(input) * neg;
- }
- else
- {
- *input = str - 1;
- return 0;
- }
- case 'b':
- case 'B':
- // Bool.
- if (neg == -1)
- {
- // Can't have negative booleans.
- *input = str;
- return 0;
- }
- else
- {
- ++str;
- if ((*str == '0') || (*str == '1'))
- {
- *input = str;
- return (int)GetBoolValue(input);
- }
- else
- {
- *input = str - 1;
- return 0;
- }
- }
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- // Octal.
- *input = str;
- return GetOctValue(input) * neg;
- case '8':
- case '9':
- // Decimal.
- break;
- default:
- *input = str;
- return 0;
- }
- }
- else if ((*str < '0') || (*str > '9'))
- {
- *input = str;
- return 0;
- }
- *input = str;
- return GetDecValue(input) * neg;
- }
- bool
- GetLogical(char ** const input)
- {
- // Boolean values - if it's not '0' or 'false' then it's true. I could be
- // strict and make them enter '1' or 'true' for true, but I'm not going to.
- char *
- string = *input;
- bool
- ret;
- switch (*string++)
- {
- case '0':
- // Set the value to false till we decide otherwise.
- ret = false;
- while (!IsSpacer(*string))
- {
- if (*string++ != '0')
- {
- // Value is not false, thus is true.
- ret = true;
- break;
- }
- }
- // Let the cleanup code below handle excess chars.
- break;
- case 'f':
- case 'F':
- // Default to true unless we find "false" exactly.
- ret = true;
- if ((*string | 0x20) == 'a')
- {
- ++string;
- if ((*string | 0x20) == 'l')
- {
- ++string;
- if ((*string | 0x20) == 's')
- {
- ++string;
- if ((*string | 0x20) == 'e')
- {
- ++string;
- // We have the whole word, check that it's on its
- // own, i.e. is followed by a space or delimiter
- // Note that if your delimiter is in the word
- // "false", you may have a bug here but they seem
- // like odd delimiter characters tbh.
- if (IsSpacer(*string))
- {
- // Exact word found on its own so save the fact
- // that it's false.
- ret = false;
- }
- }
- }
- }
- }
- break;
- default:
- ret = true;
- break;
- }
- // Skip the rest of the data.
- *input = string;
- FindSpacer(input);
- return ret;
- }
- bool
- FindDefaultStart(char ** const str)
- {
- // Skip the default value passed for optional parameters.
- if (**str == '(')
- {
- ++(*str);
- SkipWhitespace(str);
- return true;
- }
- else
- {
- logprintf("sscanf warning: No default value found.");
- }
- return false;
- }
- void
- SkipDefault(char ** const str)
- {
- if (FindDefaultStart(str))
- {
- // Default value found - skip it.
- do
- {
- ++(*str);
- }
- while (**str && **str != ')');
- if (**str)
- {
- // Current pointer points to the close bracket, skip it.
- ++(*str);
- }
- else
- {
- logprintf("sscanf warning: Unclosed default value.");
- }
- }
- }
- void
- SkipDefaultEx(char ** const data)
- {
- // Skip the default value passed for optional parameters.
- char *
- str = *data;
- if (*str == '(')
- {
- // Default value found - skip it.
- bool
- escape = false;
- while (*str && (escape || *str != ')'))
- {
- if (*str == '\\')
- {
- // Invert the escape, they may do:
- // S(a\)
- // Where the default string is "a\", in which case we need a
- // way to tell the system not to include the close bracket,
- // which would be:
- // S(a\\)
- // Or, in real PAWN terms:
- // S(a\\\\)
- // Of course, they can also do:
- // S(\\\\\\\\\\))
- // To get a final string of "\\)". Yes, you need FOUR slashes
- // in a PAWN string to get just one in the output string.
- escape = !escape;
- }
- else
- {
- escape = false;
- }
- ++str;
- }
- if (*str)
- {
- // Current pointer points to the close bracket, skip it.
- ++str;
- }
- else
- {
- logprintf("sscanf warning: Unclosed default value.");
- }
- }
- else
- {
- logprintf("sscanf warning: No default value found.");
- }
- *data = str;
- }
- void
- SkipLength(char ** const input)
- {
- // Get an easy pointer to the data to manipulate.
- char *
- str = *input;
- if (*str == '[')
- {
- while (*(++str))
- {
- // Don't check the length is valid, that's effort and slow.
- if (*str == ']')
- {
- *input = str + 1;
- return;
- }
- }
- // If we get here then the end of the string was reached before the
- // valid end of the length.
- *input = str;
- logprintf("sscanf warning: Missing string length end.");
- }
- else
- {
- logprintf("sscanf warning: Arrays without a length are deprecated, please add a destination size.");
- }
- }
- int
- GetLength(char ** const input, bool error)
- {
- if (**input == '[')
- {
- ++(*input);
- int
- length = GetDec(input);
- char *
- str = *input;
- if (length <= 0)
- {
- if (error)
- {
- length = 0;
- logprintf("sscanf error: Invalid data length.");
- }
- else
- {
- length = SSCANF_MAX_LENGTH;
- logprintf("sscanf warning: Invalid data length.");
- }
- }
- if (*str == ']')
- {
- // Valid end: [number]
- *input = str + 1;
- return length;
- }
- else if (*str)
- {
- // Invalid character: [numberX]
- logprintf("sscanf warning: Invalid character in data length.");
- // Loop through the string till we find an end to the size.
- while (*(++str))
- {
- if (*str == ']')
- {
- // Valid end: [numberX]
- *input = str + 1;
- return length;
- }
- }
- }
- // Invalid end: [number
- logprintf("sscanf warning: Missing length end.");
- *input = str;
- return length;
- }
- else if (error)
- {
- logprintf("sscanf error: String/array must include a length, please add a destination size.");
- return 0;
- }
- else
- {
- logprintf("sscanf warning: Strings without a length are deprecated, please add a destination size.");
- return SSCANF_MAX_LENGTH;
- }
- }
- void
- GetJaggedSlot(cell * start, int count, int size, int slot, cell ** rs, int * rl)
- {
- // Get the start of the real data after the header.
- cell
- * cur,
- * dstart = start + count,
- * dend = dstart + (count * size),
- * sstart = (cell *)((char *)(start + slot) + *(start + slot));
- while (start < dstart)
- {
- cur = (cell *)((char *)start + *start);
- // Use "<" not "<=" to avoid assigning 0-length slots.
- if (sstart < cur && cur < dend)
- {
- dend = cur;
- }
- // else if (sstart == cur && slot--)
- //{
- // // Do something about the fact that we just found a 0 length slot.
- //}
- ++start;
- }
- *rs = sstart;
- *rl = (dend - sstart);
- }
|