/** * HACKHACK: The compiler will generate code for each case we need. * Don't remove this, otherwise files that use certain code generations * will have extern problems. For each case you need, add dummy code * here. */ void __WHOA_DONT_CALL_ME_PLZ_K_lol_o_O() { //acsprintf atcprintf((cell *)NULL, 0, (const char *)NULL, NULL, NULL, NULL); //accprintf atcprintf((cell *)NULL, 0, (cell *)NULL, NULL, NULL, NULL); //ascprintf atcprintf((char *)NULL, 0, (cell *)NULL, NULL, NULL, NULL); }
char* format_amxstring(AMX *amx, cell *params, int parm, int &len) { static char outbuf[4096]; cell *addr = get_amxaddr(amx, params[parm++]); len = atcprintf(outbuf, sizeof(outbuf)-1, addr, amx, params, &parm); return outbuf; }
static cell_t sm_WriteFileLine(IPluginContext *pContext, const cell_t *params) { Handle_t hndl = static_cast<Handle_t>(params[1]); HandleError herr; HandleSecurity sec; FILE *pFile; sec.pOwner = NULL; sec.pIdentity = g_pCoreIdent; if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile)) != HandleError_None) { return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr); } char *fmt; int err; if ((err=pContext->LocalToString(params[2], &fmt)) != SP_ERROR_NONE) { pContext->ThrowNativeErrorEx(err, NULL); return 0; } char buffer[2048]; int arg = 3; atcprintf(buffer, sizeof(buffer), fmt, pContext, params, &arg); fprintf(pFile, "%s\n", buffer); return 1; }
static cell AMX_NATIVE_CALL vdformat(AMX *amx, cell *params) { if (!g_pCurNative || (g_pCurNative->amx != amx)) { LogError(amx, AMX_ERR_NATIVE, "Not currently in a dynamic native"); return 0; } if (g_pCurNative->style) { LogError(amx, AMX_ERR_NATIVE, "Wrong style of dynamic native"); return 0; } int vargPos = static_cast<int>(params[4]); int fargPos = static_cast<int>(params[3]); cell max = g_Params[0] / sizeof(cell); if (vargPos > (int)max + 1) { LogError(amx, AMX_ERR_NATIVE, "Invalid vararg parameter passed: %d", vargPos); return 0; } if (fargPos > (int)max + 1) { LogError(amx, AMX_ERR_NATIVE, "Invalid fmtarg parameter passed: %d", fargPos); return 0; } /* get destination info */ cell *fmt; if (fargPos == 0) { if (params[0] / sizeof(cell) != 5) { LogError(amx, AMX_ERR_NATIVE, "Expected fmtarg as fifth parameter, found none"); return 0; } fmt = get_amxaddr(amx, params[5]); } else { fmt = get_amxaddr(g_pCaller, g_Params[fargPos]); } cell *realdest = get_amxaddr(amx, params[1]); size_t maxlen = static_cast<size_t>(params[2]); cell *dest = realdest; /* if this is necessary... */ static cell cpbuf[4096]; dest = cpbuf; /* perform format */ size_t total = atcprintf(dest, maxlen, fmt, g_pCaller, g_Params, &vargPos); /* copy back */ memcpy(realdest, dest, (total+1) * sizeof(cell)); return total; }
static cell AMX_NATIVE_CALL formatex(AMX *amx, cell *params) { cell *buf = get_amxaddr(amx, params[1]); size_t maxlen = static_cast<size_t>(params[2]); cell *fmt = get_amxaddr(amx, params[3]); int param = 4; size_t total = atcprintf(buf, maxlen, fmt, amx, params, ¶m); return static_cast<cell>(total); }
static cell AMX_NATIVE_CALL vformat(AMX *amx, cell *params) { int vargPos = static_cast<int>(params[4]); /** get the parent parameter array */ AMX_HEADER *hdr = (AMX_HEADER *)amx->base; cell *local_params = (cell *)( (char *)amx->base + (cell)hdr->dat + (cell)amx->frm + (2 * sizeof(cell)) ); cell max = local_params[0] / sizeof(cell); if (vargPos > (int)max + 1) { LogError(amx, AMX_ERR_NATIVE, "Invalid vararg parameter passed: %d", vargPos); return 0; } /** * check for bounds clipping */ cell addr_start = params[1]; cell addr_end = addr_start + params[2]; bool copy = false; for (int i = vargPos; i <= max; i++) { //does this clip the bounds? if ( (local_params[i] >= addr_start) && (local_params[i] <= addr_end) ) { copy = true; break; } } /* get destination info */ cell *fmt = get_amxaddr(amx, params[3]); cell *realdest = get_amxaddr(amx, params[1]); size_t maxlen = static_cast<size_t>(params[2]); cell *dest = realdest; /* if this is necessary... */ static cell cpbuf[4096]; if (copy) dest = cpbuf; /* perform format */ size_t total = atcprintf(dest, maxlen, fmt, amx, local_params, &vargPos); /* copy back */ if (copy) { memcpy(realdest, dest, (total+1) * sizeof(cell)); } return total; }
size_t SourceModBase::FormatString(char *buffer, size_t maxlength, IPluginContext *pContext, const cell_t *params, unsigned int param) { char *fmt; pContext->LocalToString(params[param], &fmt); int lparam = ++param; return atcprintf(buffer, maxlength, fmt, pContext, params, &lparam); }
char * CLangMngr::FormatAmxString(AMX *amx, cell *params, int parm, int &len) { //do an initial run through all this static char outbuf[4096]; cell *addr = get_amxaddr(amx, params[parm++]); len = atcprintf(outbuf, sizeof(outbuf)-1, addr, amx, params, &parm); return outbuf; }
static cell_t sm_BuildPath(IPluginContext *pContext, const cell_t *params) { char path[PLATFORM_MAX_PATH], *fmt, *buffer; int arg = 5; pContext->LocalToString(params[2], &buffer); pContext->LocalToString(params[4], &fmt); atcprintf(path, sizeof(path), fmt, pContext, params, &arg); return g_SourceMod.BuildPath(Path_SM_Rel, buffer, params[3], "%s", path); }
static cell_t sm_PrintToServer(IPluginContext *pCtx, const cell_t *params) { char buffer[1024]; char *fmt; int arg = 2; pCtx->LocalToString(params[1], &fmt); size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg); buffer[res++] = '\n'; buffer[res] = '\0'; META_CONPRINT(buffer); return 1; }
static cell_t sm_PrintToConsole(IPluginContext *pCtx, const cell_t *params) { int index = params[1]; if ((index < 0) || (index > g_Players.GetMaxClients())) { return pCtx->ThrowNativeError("Client index %d is invalid", index); } CPlayer *pPlayer = NULL; if (index != 0) { pPlayer = g_Players.GetPlayerByIndex(index); if (!pPlayer->IsInGame()) { return pCtx->ThrowNativeError("Client %d is not in game", index); } /* Silent fail on bots, engine will crash */ if (pPlayer->IsFakeClient()) { return 0; } } char buffer[1024]; char *fmt; int arg = 3; pCtx->LocalToString(params[2], &fmt); size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg); buffer[res++] = '\n'; buffer[res] = '\0'; if (index != 0) { #if SOURCE_ENGINE == SE_DOTA engine->ClientPrintf(pPlayer->GetIndex(), buffer); #else engine->ClientPrintf(pPlayer->GetEdict(), buffer); #endif } else { META_CONPRINT(buffer); } return 1; }
static cell AMX_NATIVE_CALL format(AMX *amx, cell *params) /* 3 param */ { cell *buf = get_amxaddr(amx, params[1]); cell *fmt = get_amxaddr(amx, params[3]); size_t maxlen = params[2]; /** * SPECIAL CASE - check if the buffers overlap. * some users, for whatever reason, do things like: * format(buf, 255, buf.... * this is considered "deprecated" but we have to support it. * we do this by checking to see if reading from buf will overlap */ cell addr_start = params[1]; cell addr_end = params[1] + maxlen * sizeof(cell); cell max = params[0] / sizeof(cell); bool copy = false; for (cell i = 3; i <= max; i++) { //does this clip the bounds?!?!? WELL, DOES IT!?!?! i am a loud dog if (params[i] >= addr_start && params[i] <= addr_end) { copy = true; break; } } if (copy) buf = g_cpbuf; int param = 4; size_t total = 0; total = atcprintf(buf, maxlen, fmt, amx, params, ¶m); if (copy) { /* copy back */ cell *old = get_amxaddr(amx, params[1]); memcpy(old, g_cpbuf, (total+1) * sizeof(cell)); } return total; }
size_t Translate(char *buffer, size_t maxlen, IPluginContext *pCtx, const char *key, cell_t target, const cell_t *params, int *arg, bool *error) { unsigned int langid; *error = false; Translation pTrans; CPlugin *pl = (CPlugin *)g_PluginSys.FindPluginByContext(pCtx->GetContext()); size_t langcount = pl->GetLangFileCount(); unsigned int max_params = 0; try_serverlang: if (target == LANG_SERVER) { langid = g_Translator.GetServerLanguage(); } else if ((target >= 1) && (target <= g_Players.GetMaxClients())) { langid = g_Translator.GetClientLanguage(target); } else { pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Translation failed: invalid client index %d", target); goto error_out; } if (!TryTranslation(pl, key, langid, langcount, &pTrans)) { if (target != LANG_SERVER && langid != g_Translator.GetServerLanguage()) { target = LANG_SERVER; goto try_serverlang; } else if (langid != LANGUAGE_ENGLISH) { if (!TryTranslation(pl, key, LANGUAGE_ENGLISH, langcount, &pTrans)) { pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key); goto error_out; } } else { pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key); goto error_out; } } max_params = pTrans.fmt_count; if (max_params) { cell_t new_params[MAX_TRANSLATE_PARAMS]; /* Check if we're going to over the limit */ if ((*arg) + (max_params - 1) > (size_t)params[0]) { pCtx->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX, "Translation string formatted incorrectly - missing at least %d parameters", ((*arg + (max_params - 1)) - params[0]) ); goto error_out; } /* If we need to re-order the parameters, do so with a temporary array. * Otherwise, we could run into trouble with continual formats, a la ShowActivity(). */ memcpy(new_params, params, sizeof(cell_t) * (params[0] + 1)); ReorderTranslationParams(&pTrans, &new_params[*arg]); return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, new_params, arg); } else { return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, params, arg); } error_out: *error = true; return 0; }
size_t atcprintf(D *buffer, size_t maxlen, const S *format, AMX *amx, cell *params, int *param) { int arg; int args = params[0] / sizeof(cell); D *buf_p; D ch; int flags; int width; int prec; int n; //char sign; const S *fmt; size_t llen = maxlen; buf_p = buffer; arg = *param; fmt = format; while (true) { // run through the format string until we hit a '%' or '\0' for (ch = static_cast<D>(*fmt); llen && ((ch = static_cast<D>(*fmt)) != '\0' && ch != '%'); fmt++) { *buf_p++ = static_cast<D>(ch); llen--; } if (ch == '\0' || llen <= 0) goto done; // skip over the '%' fmt++; // reset formatting state flags = 0; width = 0; prec = -1; //sign = '\0'; rflag: ch = static_cast<D>(*fmt++); reswitch: switch(ch) { case '-': flags |= LADJUST; goto rflag; case '.': n = 0; while( is_digit( ( ch = static_cast<D>(*fmt++)) ) ) n = 10 * n + ( ch - '0' ); prec = n < 0 ? -1 : n; goto reswitch; case '0': flags |= ZEROPAD; goto rflag; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; do { n = 10 * n + ( ch - '0' ); ch = static_cast<D>(*fmt++); } while( is_digit( ch ) ); width = n; goto reswitch; case 'c': CHECK_ARGS(0); *buf_p++ = static_cast<D>(*get_amxaddr(amx, params[arg])); llen--; arg++; break; case 'b': CHECK_ARGS(0); AddBinary(&buf_p, llen, *get_amxaddr(amx, params[arg]), width, flags); arg++; break; case 'd': case 'i': CHECK_ARGS(0); AddInt(&buf_p, llen, *get_amxaddr(amx, params[arg]), width, flags); arg++; break; case 'u': CHECK_ARGS(0); AddUInt(&buf_p, llen, static_cast<unsigned int>(*get_amxaddr(amx, params[arg])), width, flags); arg++; break; case 'f': CHECK_ARGS(0); AddFloat(&buf_p, llen, amx_ctof(*get_amxaddr(amx, params[arg])), width, prec, flags); arg++; break; case 'X': CHECK_ARGS(0); flags |= UPPERDIGITS; AddHex(&buf_p, llen, static_cast<unsigned int>(*get_amxaddr(amx, params[arg])), width, flags); arg++; break; case 'x': CHECK_ARGS(0); AddHex(&buf_p, llen, static_cast<unsigned int>(*get_amxaddr(amx, params[arg])), width, flags); arg++; break; case 'a': { CHECK_ARGS(0); // %a is passed a pointer directly to a cell string. cell* ptr=reinterpret_cast<cell*>(*get_amxaddr(amx, params[arg])); if (!ptr) { LogError(amx, AMX_ERR_NATIVE, "Invalid vector string handle provided (%d)", *get_amxaddr(amx, params[arg])); return 0; } AddString(&buf_p, llen, ptr, width, prec); arg++; break; } case 's': CHECK_ARGS(0); AddString(&buf_p, llen, get_amxaddr(amx, params[arg]), width, prec); arg++; break; case 'L': case 'l': { const char *lang; int len; if (ch == 'L') { CHECK_ARGS(1); auto currParam = params[arg++]; lang = playerlang(*get_amxaddr(amx, currParam)); if (!lang) lang = get_amxstring(amx, currParam, 2, len); } else { CHECK_ARGS(0); lang = playerlang(g_langMngr.GetDefLang()); } const char *key = get_amxstring(amx, params[arg++], 3, len); const char *def = translate(amx, lang, key); if (!def) { static char buf[255]; ke::SafeSprintf(buf, sizeof(buf), "ML_NOTFOUND: %s", key); def = buf; } size_t written = atcprintf(buf_p, llen, def, amx, params, &arg); buf_p += written; llen -= written; break; } case 'N': { CHECK_ARGS(0); cell *addr = get_amxaddr(amx, params[arg]); char buffer[255]; if (*addr) { CPlayer *player = NULL; if (*addr >= 1 && *addr <= gpGlobals->maxClients) { player = GET_PLAYER_POINTER_I(*addr); } if (!player || !player->initialized) { LogError(amx, AMX_ERR_NATIVE, "Client index %d is invalid", *addr); return 0; } const char *auth = GETPLAYERAUTHID(player->pEdict); if (!auth || auth[0] == '\0') { auth = "STEAM_ID_PENDING"; } int userid = GETPLAYERUSERID(player->pEdict); ke::SafeSprintf(buffer, sizeof(buffer), "%s<%d><%s><%s>", player->name.chars(), userid, auth, player->team.chars()); } else { ke::SafeSprintf(buffer, sizeof(buffer), "Console<0><Console><Console>"); } AddString(&buf_p, llen, buffer, width, prec); arg++; break; } case 'n': { CHECK_ARGS(0); cell *addr = get_amxaddr(amx, params[arg]); const char *name = "Console"; if (*addr) { CPlayer *player = NULL; if (*addr >= 1 && *addr <= gpGlobals->maxClients) { player = GET_PLAYER_POINTER_I(*addr); } if (!player || !player->initialized) { LogError(amx, AMX_ERR_NATIVE, "Client index %d is invalid", *addr); return 0; } name = player->name.chars(); } AddString(&buf_p, llen, name, width, prec); arg++; break; } case '%': *buf_p++ = static_cast<D>(ch); if (!llen) goto done; llen--; break; case '\0': *buf_p++ = static_cast<D>('%'); if (!llen) goto done; llen--; goto done; break; default: *buf_p++ = static_cast<D>(ch); if (!llen) goto done; llen--; break; } } done: *buf_p = static_cast<D>(0); *param = arg; /* if max buffer length consumed, make sure we don't truncate a multi-byte character */ if (llen <= 0 && *(buf_p - 1) & 1 << 7) { llen += UTIL_CheckValidChar(buf_p - 1); *(buf_p - llen) = static_cast<D>(0); } return maxlen-llen; }