/*----------------------------------------------------------------------------*- =========================== YAdmin system - Format Core =========================== Based on the AMXMODX format function, used in SA:MP, modified by me for 0.2 Extends the Text core and is required by the Message2Format functions.Changelog: 14/04/07: Updated header documentation with more than changelog. Added %n. 31/03/07: First version.Functions: Public: - Core: - Stock: Format_SendFormattedText - Sends a formatted message to a player. Static: Format_AddNum - Adds a number to the output string. Format_AddFloat - Adds a float to the output string. Format_AddString - Adds a string to the output string. Format_AddCommand - Adds a command to the output string. Format_IsDigit - Checks a character is a number. Inline: Format_AddBin - Base 2 wrapper for Format_AddNum. Format_AddInt- Base 10 wrapper for Format_AddNum. Format_AddHex - Base 16 wrapper for Format_AddNum. API: -Definitions: FORMAT_LADJUST - Flag for padding left instead of right. FORMAT_ZEROPAD - Flag for padding with 0's instead of spaces. FORMAT_COMMAND - Adler32 of "Command" for properties.Enums: -Macros: -Variables: Global: - Static: -Commands: -Compile options: --*----------------------------------------------------------------------------*/#define FORMAT_LADJUST 1#define FORMAT_ZEROPAD 2#define FORMAT_COMMAND 179176128/*----------------------------------------------------------------------------*-Function: Format_AddBinParams: output[] - The string to write the data to. &maxlen - The maximum number of characters to write. val - The number to write. width - The minimum length of string to write. flags - FORMAT_LADJUST - pad left, FORMAT_ZEROPAD - pad with 0's.Return: Format_AddNumNotes: Wrapper for Format_AddNum in base 2. See Format_AddNum.-*----------------------------------------------------------------------------*/#define Format_AddBin(%1,%2,%3,%4,%5) Format_AddNum((%1), (%2), (%3), (%4), (%5), 0b10)/*----------------------------------------------------------------------------*-Function: Format_AddIntParams: output[] - The string to write the data to. &maxlen - The maximum number of characters to write. val - The number to write. width - The minimum length of string to write. flags - FORMAT_LADJUST - pad left, FORMAT_ZEROPAD - pad with 0's.Return: Format_AddNumNotes: Wrapper for Format_AddNum in base 10. See Format_AddNum.-*----------------------------------------------------------------------------*/#define Format_AddInt(%1,%2,%3,%4,%5) Format_AddNum((%1), (%2), (%3), (%4), (%5), 10)/*----------------------------------------------------------------------------*-Function: Format_AddBinParams: output[] - The string to write the data to. &maxlen - The maximum number of characters to write. val - The number to write. width - The minimum length of string to write. flags - FORMAT_LADJUST - pad left, FORMAT_ZEROPAD - pad with 0's.Return: Format_AddNumNotes: Wrapper for Format_AddNum in base 16. See Format_AddNum.-*----------------------------------------------------------------------------*/#define Format_AddHex(%1,%2,%3,%4,%5) Format_AddNum((%1), (%2), (%3), (%4), (%5), 0x10)/*----------------------------------------------------------------------------*-Function: Format_AddNumParams: output[] - The string to write the data to. &maxlen - The maximum number of characters to write. val - The number to write. width - The minimum length of string to write. flags - FORMAT_LADJUST - pad left, FORMAT_ZEROPAD - pad with 0's. base - The base to display the number in.Return: Length of the string added.Notes: Adds a number to the output string according to the passed parameters. The original had separate, almost identical functions for each base, this combines them all into one.-*----------------------------------------------------------------------------*/static Format_AddNum(output[], &maxlen, val, width, flags, base){ new text[33], digits, signedVal = val, pos; switch (base) { case 0b10: { do { text[digits++] = '0' + (val % 0b10); val >>>= 1; } while (val); } case 10: { if (val < 0) val = -val; do { text[digits++] = '0' + (val % 10); val /= 10; } while (val); if (signedVal < 0) { if (flags & FORMAT_ZEROPAD) output[pos++] = '-'; else text[digits++] = '-'; } } case 0x10: { do { text[digits++] = '0' + (val % 0x10); val >>>= 4; } while (val); } } if(!(flags & FORMAT_LADJUST)) { while (digits < width && maxlen) { output[pos++] = (flags & FORMAT_ZEROPAD) ? '0' : ' '; width--; maxlen--; } } while (digits-- && maxlen) { output[pos++] = text[digits]; width--; maxlen--; } if (flags & FORMAT_LADJUST) { while (width-- && maxlen) { output[pos++] = (flags & FORMAT_ZEROPAD) ? '0' : ' '; maxlen--; } } return pos;}/*----------------------------------------------------------------------------*-Function: Format_AddFloatParams: output[] - String to add to. &maxlen - Length of string remaining. Float:fval - value to add. width - Minimum length. prec - Number of DP.Return: Length of the string added.Notes: --*----------------------------------------------------------------------------*/static Format_AddFloat(output[], &maxlen, Float:fval, width, prec){ new text[32], digits, pos, Float:signedVal = fval, val; if (fval < 0) fval = -fval; digits = 0; val = floatround(fval, floatround_floor); do { text[digits++] = '0' + val % 10; val /= 10; } while (val); if (signedVal < 0.0) text[digits++] = '-'; while (digits < width && maxlen) { output[pos++] = ' '; width--; maxlen--; } while (digits-- && maxlen) { output[pos++] = text[digits]; maxlen--; } if (prec < 0) prec = 6; digits = 0; while (digits < prec) { fval -= val; fval *= 10.0; val = floatround(fval, floatround_floor); text[digits++] = '0' + val % 10; } if (digits > 0 && maxlen) { output[pos++] = '.'; maxlen--; for (prec = 0; maxlen && prec < digits; prec++) { output[pos++] = text[prec]; maxlen--; } } return pos;}/*----------------------------------------------------------------------------*-Function: Format_AddStringParams: output[] - String to add to. &maxlen - Length of string remaining. string[] - String to add. width - Minimum length. prec - Maximum length.Return: Length of the string added.Notes: --*----------------------------------------------------------------------------*/static Format_AddString(output[], &maxlen, string[], width, prec){ new size, pos, pos2; if (!string[0]) { prec = -1; } if (prec >= 0) { for (size = 0; size < prec; size++) { if (string[size] == '\0') break; } } else { while (string[size]) size++; } if (size > maxlen) size = maxlen; maxlen -= size; width -= size; while (size--) { output[pos++] = string[pos2++]; } while (width-- > 0 && maxlen) { output[pos++] = ' '; maxlen--; } return pos;}/*----------------------------------------------------------------------------*-Function: Format_AddCommandParams: output[] - String to add to. &maxlen - Length of string remaining. str[] - Function name to find. width - Minimum length. prec - Maximum length.Return: Length of the string added.Notes: --*----------------------------------------------------------------------------*/static Format_AddCommand(output[], &maxlen, str[], width, prec){ new size, pos, pos2, string[MAX_STRING] = "FAIL"; if (str[0] && CallRemoteFunction("Command_Name", "s", str)) getproperty(.value = FORMAT_COMMAND, .string = string); if (!string[0]) { prec = -1; } if (prec >= 0) { for (size = 0; size < prec; size++) { if (string[size] == '\0') break; } } else { while (string[size]) size++; } if (size > maxlen) size = maxlen; maxlen -= size; width -= size; while (size--) { output[pos++] = string[pos2++]; } while (width-- > 0 && maxlen) { output[pos++] = ' '; maxlen--; } return pos;}/*----------------------------------------------------------------------------*-Function: Format_IsDigitParams: n - The digit to test.Return: bool: Is n a digit?Notes: --*----------------------------------------------------------------------------*/static bool:Format_IsDigit(n) return (n <= '9' && n >= '0');/*----------------------------------------------------------------------------*-Function: Format_SendFormattedTextParams: playerid - The player to send it to or INVALID_PLAYER_ID for all players. type - The type of text (client/game and game type). colorTime - The color or time parameter for the text output. identifier[] - The string identifier of the text to output. ...Return: -Notes: This is a rewritten version of the SA:MP format function (taken from the HL AMXMODX mod). It is based on the 0.2 version including %b, %h and %x. The code is an almost direct port with minor fixes and changes due to the use of more generalised template code used in the original. This function is also designed to be as efficient as possible with multiple languages, ensuring each one is parsed only once if required then saved for later users of the same language. If playerid is INVALID_PLAYER_ID the function will loop through all players and format the message for them in their own language where available, otherwise it will format it for the required playerid and send it. This is also streamlined by only aquiring string parameters once and saving them for later use, unless more than 8 are used in which case the last array slot is used as a buffer, but this should rarely be the case.-*----------------------------------------------------------------------------*/stock Format_SendFormattedText(playerid, type, colorTime, identifier[], {Float,_}:...){ new messageid; if ((messageid = Text_FindTextPointers(identifier)) == TEXT_NO_POINTERS) return; new output[MAX_LANGUAGES][MAX_STRING]; for (new i = (playerid == INVALID_PLAYER_ID) ? (0) : (playerid); i < MAX_PLAYERS; i++) { if (IsPlayerConnected(i)) { new Language:playerLanguage = Text_GetPlayerLanguage(i); if (output[playerLanguage][0]) goto display; new pos, p, arg = 4, tempString[MAX_STRING], ch, llen = MAX_STRING, stringCount, bool:trueVar = true; tempString = Text_GetTextFromIndex(messageid, playerLanguage); while (trueVar) { while (llen-- && (ch = tempString[pos++]) != '\0' && ch != '%') output[playerLanguage][p++] = ch; if (ch == '\0' || llen <= 0) goto done; new argStrings[8][MAX_STRING], flags, width, prec = -1, stringPos, n; rflag: ch = tempString[pos++]; reswitch: switch (ch) { case '-': { flags |= FORMAT_LADJUST; goto rflag; } case '.': { n = 0; while (Format_IsDigit((ch = tempString[pos++]))) n = 10 * n + (ch - '0'); prec = n < 0 ? -1 : n; goto reswitch; } case '0': { flags |= FORMAT_ZEROPAD; } case '1', '2', '3', '4', '5', '6', '7', '8', '9': { do { n = 10 * n + (ch - '0'); } while (Format_IsDigit((ch = tempString[pos++]))); width = n; goto reswitch; } case 'b': { p += Format_AddBin(output[playerLanguage][p], llen, getarg(arg++), width, flags); } case 'c': { output[playerLanguage][p++] = getarg(arg++); if (!llen--) goto done; } case 'd', 'i': { p += Format_AddInt(output[playerLanguage][p], llen, getarg(arg++), width, flags); } case 'f': { p += Format_AddFloat(output[playerLanguage][p], llen, Float:getarg(arg++), width, prec); } case 's': { if (stringCount < pos) { for (new j = 0; j < MAX_STRING; j++) { if (!(argStrings[stringPos][j] = getarg(arg, j))) break; } } p += Format_AddString(output[playerLanguage][p], llen, argStrings[stringPos], width, prec); stringPos++; arg++; stringCount = pos; if (stringPos == sizeof (argStrings)) { stringCount--; stringPos--; } } case 'h', 'x': { p += Format_AddHex(output[playerLanguage][p], llen, getarg(arg++), width, flags); } case 'n': { if (stringCount < pos) { for (new j = 0; j < MAX_STRING; j++) { if (!(argStrings[stringPos][j] = getarg(arg, j))) break; } } p += Format_AddCommand(output[playerLanguage][p], llen, argStrings[stringPos], width, prec); stringPos++; arg++; stringCount = pos; if (stringPos == sizeof (argStrings)) { stringCount--; stringPos--; } } case '\0': { output[playerLanguage][p++] = '%'; if (!llen--) goto done; } default: { output[playerLanguage][p++] = ch; if (!llen--) goto done; } } } done: output[playerLanguage][p] = '\0'; display: if (type & TEXT_TYPE_CLIENT) { SendClientMessage(i, colorTime, output[playerLanguage]); } else if (type & TEXT_TYPE_GAME) { GameTextForPlayer(i, output[playerLanguage], colorTime, type & ~(TEXT_TYPE_CLIENT | TEXT_TYPE_GAME)); } } if (playerid != INVALID_PLAYER_ID) break; }}