/* bool: procread(line[], size=sizeof line, bool:striplf=false, bool:packed=false) */ static cell AMX_NATIVE_CALL n_procread(AMX *amx, const cell *params) { TCHAR line[128]; cell *cptr; unsigned long num; int index; index=0; #if defined __WIN32__ || defined _WIN32 || defined WIN32 if (read_stdout==NULL) return 0; do { if (!ReadFile(read_stdout,line+index,1,&num,NULL)) break; index++; } while (index<sizeof(line)/sizeof(line[0])-1 && line[index-1]!=__T('\n')); #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ if (pipe_from[0]<0) return 0; do { if (read(pipe_from[0],line+index,1)<0) break; index++; } while (index<sizeof(line)/sizeof(line[0])-1 && line[index-1]!=__T('\n')); #endif if (params[3]) while (index>0 && (line[index-1]==__T('\r') || line[index-1]==__T('\n'))) index--; line[index]=__T('\0'); cptr=amx_Address(amx,params[1]); amx_SetString(cptr,line,params[4],sizeof(TCHAR)>1,params[2]); return 1; }
/* bool: argindex(index, value[], maxlength=sizeof value, bool:pack=false) * returns true if the option was found and false on error or if the parameter "index" is out of range */ static cell AMX_NATIVE_CALL n_argindex(AMX *amx, const cell *params) { const TCHAR *cmdline = rawcmdline(); const TCHAR *option; int length, max; TCHAR *str; cell *cptr; max = (int)params[3]; if (max <= 0) return 0; cptr = amx_Address(amx, params[2]); if ((option = tokenize(cmdline, params[1], &length)) == NULL) { /* option not found, return an empty string */ *cptr = 0; return 0; } /* if */ if (params[4]) max *= sizeof(cell); if (max > length + 1) max = length + 1; str = (TCHAR *)alloca(max*sizeof(TCHAR)); if (str == NULL) { amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ memcpy(str, option, (max - 1) * sizeof(TCHAR)); str[max - 1] = __T('\0'); amx_SetString(cptr, (char*)str, (int)params[4], sizeof(TCHAR)>1, max); return 1; }
// native mysql_fetch_field_string(resultId, field[], buffer[], bufferSize = sizeof(buffer)); static cell AMX_NATIVE_CALL n_mysql_fetch_field_string(AMX* amx, cell* params) { CHECK_PARAMS(4); ResultEntry* entry = resultController->at(static_cast<unsigned int>(params[1])); if (entry == nullptr) return 0; char* fieldName; amx_StrParam(amx, params[2], fieldName); if (!fieldName || !strlen(fieldName)) return 0; cell* bufferCell; amx_GetAddr(amx, params[3], &bufferCell); ColumnInfo::ColumnType fieldType = ColumnInfo::UnknownColumnType; const RowInfo::FieldValue* fieldValue = entry->fetch_field(fieldName, fieldType); if (fieldType != ColumnInfo::StringColumnType) { DebugPrinter::Print("[MySQL] Tried to get column \"%s\" with the wrong data-type, ignored.", fieldName); return 0; } amx_SetString(bufferCell, fieldValue->string_val, 0, 0, static_cast<size_t>(params[4])); return strlen(fieldValue->string_val); }
/** pawnLanguageString * native LanguageString(line, returnstring[], maxlength=sizeof returnstring); * */ static cell pawnLanguageString(AMX *amx, const cell *params) { std::string dialog_string; cell * cptr; cptr = amx_Address(amx, params[2]); dialog_string = lux::engine->GetString(params[1]); return amx_SetString(cptr, dialog_string.c_str(), 1, 0, params[3]); }
int main(int argc,char *argv[]) { extern int amx_ConsoleInit(AMX *amx); extern int amx_ConsoleCleanup(AMX *amx); extern int amx_CoreInit(AMX *amx); extern int amx_CoredCleanp(AMX *amx); size_t memsize; void *program; AMX amx; int index, err; cell amx_addr, *phys_addr; char output[128]; if (argc != 4) PrintUsage(argv[0]); if ((memsize = aux_ProgramSize(argv[1])) == 0) PrintUsage(argv[0]); program = malloc(memsize); if (program == NULL) ErrorExit(NULL, AMX_ERR_MEMORY); err = aux_LoadProgram(&amx, argv[1], program); if (err) ErrorExit(&amx, err); amx_ConsoleInit(amx); err = amx_CoreInit(amx); if (err) ErrorExit(&amx, err); err = amx_FindPublic(&amx, argv[2], &index); if (err) ErrorExit(&amx, err); err = amx_Allot(&amx, strlen(argv[3]) + 1, &amx_addr, &phys_addr); if (err) ErrorExit(&amx, err); amx_SetString(phys_addr, argv[3], 0); err = amx_Exec(&amx, NULL, index, 1, amx_addr); if (err) ErrorExit(&amx, err); amx_GetString(output, phys_addr); amx_Release(&amx, amx_addr); printf("%s returns %s\n", argv[1], output); amx_ConsoleCleanup(&amx); amx_CoreCleanup(&amx); amx_Cleanup(&amx); free(program); return 0; }
cell AMX_NATIVE_CALL Natives::GetDynamicVehicleNumberPlate(AMX *amx, cell *params) { CHECK_PARAMS(3, "GetDynamicVehicleNumberPlate"); boost::unordered_map<int, Item::SharedVehicle>::iterator v = core->getData()->vehicles.find(static_cast<int>(params[1])); if (v != core->getData()->vehicles.end()) { cell *text = NULL; amx_GetAddr(amx, params[2], &text); amx_SetString(text, v->second->numberplate.c_str(), 0, 0, static_cast<size_t>(params[3])); return 1; } return 0; }
error_t CellMemory:: SetNextString(char const * val, size_t idx, bool pack) { if (!idx) ++m_cur; FAIL(m_cur <= m_count, ERROR_OUT_OF_VARIABLES); // 1-indexed array. cell * addr; amx_GetAddr(m_amx, m_params[m_cur], &addr); amx_SetString(addr + idx, val, pack, false, strlen(val)); return OK; }
cell AMX_NATIVE_CALL Natives::GetDynamic3DTextLabelText(AMX *amx, cell *params) { CHECK_PARAMS(3, "GetDynamic3DTextLabelText"); boost::unordered_map<int, Item::SharedTextLabel>::iterator t = core->getData()->textLabels.find(static_cast<int>(params[1])); if (t != core->getData()->textLabels.end()) { cell *text = NULL; amx_GetAddr(amx, params[2], &text); amx_SetString(text, t->second->text.c_str(), 0, 0, static_cast<size_t>(params[3])); return 1; } return 0; }
cell AMX_NATIVE_CALL pawn_regex_replace(AMX* amx, cell* params) { regex_t* RegExpr; const char *rexp = NULL, *string = NULL, *replace = NULL; cell* addr = NULL; amx_GetAddr(amx, params[1], &addr); amx_StrParam(amx, params[1], string); amx_StrParam(amx, params[2], rexp); amx_StrParam(amx, params[3], replace); if(string && rexp) { int r=NULL; UChar* pattern = (UChar* )rexp; OnigErrorInfo einfo; r = onig_new(&RegExpr, pattern, pattern + strlen((char* )pattern), ONIG_OPTION_DEFAULT, ONIG_ENCODING_ASCII, ONIG_SYNTAX_PERL, &einfo); //logprintf("[REGEX DEBUG]: rexp %s",pattern); if(r != ONIG_NORMAL) { UChar s[ONIG_MAX_ERROR_MESSAGE_LEN]; onig_error_code_to_str(s, r, &einfo); logprintf("[REGEX ERROR]: %s", s); onig_free(RegExpr); return -1; } UChar* str = (UChar* )string; OnigRegion *region; region = onig_region_new(); r = onig_search(RegExpr, str, str+strlen((char*) str), str, str+strlen((char*) str), region, ONIG_OPTION_NONE); if(r>=0) { std::string asd = std::string((char*)str); asd.replace(asd.begin()+r, asd.begin()+region->end[region->num_regs-1], replace); amx_SetString(addr, asd.c_str(), 0, 0, params[1]); } else if(r<ONIG_MISMATCH) { UChar s[ONIG_MAX_ERROR_MESSAGE_LEN]; onig_error_code_to_str(s, r); logprintf("[REGEX ERROR]: %s\n", s); onig_region_free(region, 1); onig_free(RegExpr); return -1; } //logprintf("[REGEX DEBUG]: string %s",str); onig_region_free(region, 1); onig_free(RegExpr); //logprintf("[REGEX DEBUG]: return %d",r); return r; } return -1337; }
// native GetNativeBacktrace(string[], size = sizeof(string)); cell AMX_NATIVE_CALL GetNativeBacktrace(AMX *amx, cell *params) { cell string = params[1]; cell size = params[2]; cell *string_ptr; if (amx_GetAddr(amx, string, &string_ptr) == AMX_ERR_NONE) { std::stringstream stream; CrashDetect::PrintNativeBacktrace(stream, 0); return amx_SetString(string_ptr, stream.str().c_str(), 0, 0, size) == AMX_ERR_NONE; } return 0; }
cell AMX_NATIVE_CALL Dotnet_ReadStringFromPacket(AMX * amx, cell * params) { cell *pPack, *pStr;//, *pLen; amx_GetAddr(amx,params[1],&pPack); amx_GetAddr(amx,params[2],&pStr); //amx_GetAddr(amx,params[3],&pLen); Packet* pack = (Packet*)*pPack; if (pack == NULL) return 0; int strlen = pack->ReadUShort(); pack->pos -= 2; amx_SetString(pStr, (char*)pack->pos, 0, 0, strlen); pack->ReadString(NULL,0); return strlen; }
/* strtoarray(dest[], size = sizeof dest, String:source, bool:packed = false) */ static cell AMX_NATIVE_CALL n_bstrtoarray(AMX *amx,cell *params) { char *cstr = bstr2cstr((const bstring)params[3], '#'); int length = strlen(cstr) + 1; cell *cellptr; if (params[4]) length *= sizeof(cell); if (params[2] >= length) { amx_GetAddr(amx, params[1], &cellptr); amx_SetString(cellptr, cstr, params[4], 0); } /* if */ free(cstr); return 0; }
cell AMX_NATIVE_CALL Natives::CTime_ctime(AMX *pAMX, cell *iParams) { CHECK_PARAMS(3, "ctime"); cell *iPhysAddr ; time_t tTime = (time_t)iParams[1] ; amx_GetAddr(pAMX, iParams[2], &iPhysAddr); amx_SetString(iPhysAddr, ctime(&tTime), 0, 0, iParams[3]); return 1; }
void COrm::Variable::SetValue(const char *val) { switch (m_Type) { case COrm::Variable::Type::INT: ConvertStrToData(val, (*m_VariableAddr)); break; case COrm::Variable::Type::FLOAT: { float dest = 0.0f; if (ConvertStrToData(val, dest)) (*m_VariableAddr) = amx_ftoc(dest); } break; case COrm::Variable::Type::STRING: amx_SetString(m_VariableAddr, val != nullptr ? val : "NULL", 0, 0, m_VarMaxLen); break; } }
/* bool: argstr(index=0, const option[]="", value[]="", maxlength=sizeof value, bool:pack=false) * returns true if the option was found and false otherwise */ static cell AMX_NATIVE_CALL n_argstr(AMX *amx, const cell *params) { const TCHAR *option, *key; int length, max; TCHAR *str; cell *cptr; max = (int)params[4]; if (max <= 0) return 0; amx_StrParam(amx, params[2], key); amx_GetAddr(amx, params[3], &cptr); if (cptr == NULL) { amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ option = matcharg(key, (int)params[1], &length); if (option == NULL) return 0; /* option not found */ /* check whether we must write the value of the option at all; in case the * size is one cell and that cell is already zero, we do not write anything * back */ assert(params[4] > 0); if (params[4] > 1 || *cptr != 0) { if (params[5]) max *= sizeof(cell); if (max > length + 1) max = length + 1; str = (TCHAR *)alloca(max*sizeof(TCHAR)); if (str == NULL) { amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ memcpy(str, option, (max - 1) * sizeof(TCHAR)); str[max - 1] = __T('\0'); amx_SetString(cptr, (char*)str, (int)params[5], sizeof(TCHAR)>1, max); } /* if */ return 1; }
cell AMX_NATIVE_CALL Natives::CTime_asctime(AMX *pAMX, cell *iParams) { CHECK_PARAMS(3, "asctime"); cell *iPhysAddr ; amx_GetAddr(pAMX, iParams[1], &iPhysAddr); tm tmPtr ; g_CTime->SetTimePointer(&tmPtr, iPhysAddr); amx_GetAddr(pAMX, iParams[2], &iPhysAddr); amx_SetString(iPhysAddr, asctime(&tmPtr), 0, 0, iParams[3]); return 1; }
int sampgdk_fakeamx_push_string(const char *src, int *size, cell *address) { int src_size; int error; assert(address != NULL); assert(src != NULL); src_size = (int)strlen(src) + 1; if ((error = sampgdk_fakeamx_push(src_size, address)) < 0) { return error; } amx_SetString((cell *)sampgdk_array_get(&global.heap, *address / sizeof(cell)), src, 0, 0, src_size); if (size != NULL) { *size = src_size; } return 0; }
cell AMX_NATIVE_CALL pawn_regex_exreplace(AMX* amx, cell* params) { const char *string = NULL, *replace = NULL; cell* addr = NULL; amx_GetAddr(amx, params[1], &addr); amx_StrParam(amx, params[1], string); amx_StrParam(amx, params[3], replace); if(string) { int id=(int)params[2]; if(id>=0 && id<TotalExpressions) { int r=NULL; UChar* str = (UChar* )string; onig_region_clear(rexpression[id].zreg); r = onig_search(rexpression[id].RegExpr, str, str+strlen(string), str, str+strlen(string), rexpression[id].zreg, ONIG_OPTION_NONE); if(r>=0) { std::string asd = std::string(string); asd.replace(asd.begin()+r, asd.begin()+rexpression[id].zreg->end[rexpression[id].zreg->num_regs-1], replace); amx_SetString(addr, asd.c_str(), 0, 0, params[1]); } else if(r<ONIG_MISMATCH) { UChar s[ONIG_MAX_ERROR_MESSAGE_LEN]; onig_error_code_to_str(s, r); logprintf("[REGEX ERROR]: %s\n", s); return -1; } return r; } logprintf("[REGEX ERROR]: Call regex_exreplace with undefined parameter at index %d", id); return -1; } return -1337; }
cell AMX_NATIVE_CALL Natives::CTime_strftime(AMX *pAMX, cell *iParams) { CHECK_PARAMS(4, "strftime"); cell *iPhysAddr ; amx_GetAddr(pAMX, iParams[4], &iPhysAddr); tm tmPtr ; g_CTime->SetTimePointer(&tmPtr, iPhysAddr); int iSize = iParams[2] ; char *szFormat = new char[iSize] ; amx_GetAddr(pAMX, iParams[3], &iPhysAddr); amx_GetString(szFormat, iPhysAddr, 0, iSize); char *szBuf = new char[iSize] ; strftime(szBuf, iSize, szFormat, &tmPtr); amx_GetAddr(pAMX, iParams[1], &iPhysAddr); amx_SetString(iPhysAddr, szBuf, 0, 0, iParams[2]); delete szFormat; delete szBuf; return 1; }
uint32_t CScriptTimers::NewEx(char* _scriptName, uint32_t _interval, uint32_t _repeat, cell* _params, AMX* pAMX) { timerCount++; timerData* _timer = new timerData; strncpy(_timer->funcName, _scriptName, 255); _timer->totalTime = _interval; _timer->remainingTime = _interval; _timer->timerRepeat = _repeat; _timer->timerKilled = 0; _timer->timerAMX = pAMX; cell amx_addr[256]; char* szParamList; amx_StrParam(pAMX, _params[4], szParamList); int j, numstr, iOff = 5; if (szParamList == NULL) j = 0; else j = strlen(szParamList); numstr = 0; while (j) { j--; cell *paddr = NULL; if (*(szParamList + j) == 'a') { int numcells = *get_amxaddr(pAMX, _params[j + iOff + 1]); if (amx_Allot(pAMX, numcells, &amx_addr[numstr], &paddr) == AMX_ERR_NONE) { memcpy(paddr, get_amxaddr(pAMX, _params[j + iOff]), numcells * sizeof (cell)); numstr++; } } else if (*(szParamList + j) == 's') { char* szParamText; amx_StrParam(pAMX, _params[j + iOff], szParamText); if (szParamText != NULL && strlen(szParamText) > 0) { int numcells = strlen(szParamText) + 1; if (amx_Allot(pAMX, numcells, &amx_addr[numstr], &paddr) == AMX_ERR_NONE) { amx_SetString(paddr, szParamText, 0, 0, UNLIMITED); numstr++; } } else { *szParamText = 1; *(szParamText + 1) = 0; if (amx_Allot(pAMX, 1, &amx_addr[numstr], &paddr) == AMX_ERR_NONE) { amx_SetString(paddr, szParamText, 0, 0, UNLIMITED); numstr++; } } } else { amx_addr[numstr] = *get_amxaddr(pAMX, _params[j + iOff]); numstr++; } } void* mem = NULL; if (numstr) { mem = malloc(numstr * sizeof (cell)); memcpy(mem, &amx_addr, numstr * sizeof (cell)); _timer->timerParams = mem; } else { _timer->timerParams = NULL; } _timer->paramCount = numstr; timersMap.insert(std::pair<uint32_t, timerData*>(timerCount, _timer)); return timerCount; }
// native sscanf(const data[], const format[], (Float,_}:...); static cell AMX_NATIVE_CALL n_sscanf(AMX * amx, cell * params) { if (g_iTrueMax == 0) { logprintf("sscanf error: System not initialised."); return SSCANF_FAIL_RETURN; } // Friendly note, the most complex set of specifier additions is: // // A<i>(10, 11)[5] // // In that exact order - type, default, size. It's very opposite to how // it's done in code, where you would do the eqivalent to: // // <i>[5] = {10, 11} // // But this method is vastly simpler to parse in this context! Technically // you can, due to legacy support for 'p', do: // // Ai(10, 11)[5] // // But you will get an sscanf warning, and I may remove that ability from // the start - that will mean a new function, but an easy to write one. // In fact the most complex will probably be something like: // // E<ifs[32]s[8]d>(10, 12.3, Hello there, Hi, 42) // // Get the number of parameters passed. We add one as the indexes are out // by one (OBOE - Out By One Error) due to params[0] being the parameter // count, not an actual parameter. const int paramCount = ((int)params[0] / 4) + 1; // Could add a check for only 3 parameters here - I can't think of a time // when you would not want any return values at all, but that doesn't mean // they don't exist - you could just want to check but not save the format. // Update - that is now a possibility with the '{...}' specifiers. if (paramCount < (2 + 1)) { logprintf("sscanf error: Missing required parameters."); return SSCANF_FAIL_RETURN; } //else if (paramCount == (2 + 1)) //{ // Only have an input and a specifier - better hope the whole specifier // is quite (i.e. enclosed in '{...}'). //} // Set up function wide values. // Get and check the main data. // Pointer to the current input data. char * string; STR_PARAM(amx, params[1], string); // Pointer to the current format specifier. char * format; STR_PARAM(amx, params[2], format); // Check for CallRemoteFunction style null strings and correct. if (string[0] == '\1' && string[1] == '\0') { string[0] = '\0'; } // Current parameter to save data to. int paramPos = 3; cell * cptr; InitialiseDelimiter(); // Skip leading space. SkipWhitespace(&string); bool doSave; // Code for the rare cases where the WHOLE format is quiet. if (*format == '{') { ++format; doSave = false; } else { doSave = true; } // Now do the main loop as long as there are variables to store the data in // and input string remaining to get the data from. while (*string && (paramPos < paramCount || !doSave)) { if (!*format) { // End of the format string - if we're here we've got all the // parameters but there is extra string or variables, which may // indicate their code needs fixing, for example: // sscanf(data, "ii", var0, var1, var3, var4); // There is only two format specifiers, but four returns. This may // also be reached if there is too much input data, but that is // considered OK as that is likely a user's fault. if (paramPos < paramCount) { logprintf("sscanf warning: Format specifier does not match parameter count."); } if (!doSave) { // Started a quiet section but never explicitly ended it. logprintf("sscanf warning: Unclosed quiet section."); } return SSCANF_TRUE_RETURN; } else if (IsWhitespace(*format)) { ++format; } else { switch (*format++) { case 'L': DX(bool, L) // FALLTHROUGH case 'l': DOV(bool, L) break; case 'B': DX(int, B) // FALLTHROUGH case 'b': DO(int, B) case 'N': DX(int, N) // FALLTHROUGH case 'n': DO(int, N) case 'C': DX(char, C) // FALLTHROUGH case 'c': DO(char, C) case 'I': case 'D': DX(int, I) // FALLTHROUGH case 'i': case 'd': DO(int, I) case 'H': case 'X': DX(int, H) // FALLTHROUGH case 'h': case 'x': DO(int, H) case 'O': DX(int, O) // FALLTHROUGH case 'o': DO(int, O) case 'F': DXF(double, F) // FALLTHROUGH case 'f': DOF(double, F) case 'G': DXF(double, G) // FALLTHROUGH case 'g': DOF(double, G) case '{': if (doSave) { doSave = false; } else { // Already in a quiet section. logprintf("sscanf warning: Can't have nestled quiet sections."); } continue; case '}': if (doSave) { logprintf("sscanf warning: Not in a quiet section."); } else { doSave = true; } continue; case 'P': logprintf("sscanf warning: You can't have an optional delimiter."); // FALLTHROUGH case 'p': // 'P' doesn't exist. // Theoretically, for compatibility, this should be: // p<delimiter>, but that will break backwards // compatibility with anyone doing "p<" to use '<' as a // delimiter (doesn't matter how rare that may be). Also, // writing deprecation code and both the new and old code // is more trouble than it's worth, and it's slow. // UPDATE: I wrote the "GetSingleType" code for 'a' and // figured out a way to support legacy and new code, while // still maintaining support for the legacy "p<" separator, // so here it is: ResetDelimiter(); AddDelimiter(GetSingleType(&format)); continue; case 'Z': logprintf("sscanf warning: 'Z' doesn't exist - that would be an optional, deprecated optional string!."); // FALLTHROUGH case 'z': logprintf("sscanf warning: 'z' is deprecated, consider using 'S' instead."); // FALLTHROUGH case 'S': if (IsDelimiter(*string)) { char * dest; int length; if (DoSD(&format, &dest, &length)) { // Send the string to PAWN. if (doSave) { amx_GetAddr(amx, params[paramPos++], &cptr); amx_SetString(cptr, dest, 0, 0, length); } } break; } // Implicit "else". SkipDefaultEx(&format); // FALLTHROUGH case 's': { // Get the length. int length = GetLength(&format, false); char * dest; DoS(&string, &dest, length, IsEnd(*format)); // Send the string to PAWN. if (doSave) { amx_GetAddr(amx, params[paramPos++], &cptr); amx_SetString(cptr, dest, 0, 0, length); } } break; case 'U': DX(int, U) // FALLTHROUGH case 'u': DOV(int, U) break; case 'Q': DX(int, Q) // FALLTHROUGH case 'q': DOV(int, Q) break; case 'R': DX(int, R) // FALLTHROUGH case 'r': DOV(int, R) break; case 'A': // We need the default values here. if (doSave) { amx_GetAddr(amx, params[paramPos++], &cptr); if (DoA(&format, &string, cptr, true)) { break; } } else { // Pass a NULL pointer so data isn't saved anywhere. if (DoA(&format, &string, NULL, true)) { break; } } return SSCANF_FAIL_RETURN; case 'a': if (doSave) { amx_GetAddr(amx, params[paramPos++], &cptr); if (DoA(&format, &string, cptr, false)) { break; } } else { // Pass a NULL pointer so data isn't saved anywhere. if (DoA(&format, &string, NULL, false)) { break; } } return SSCANF_FAIL_RETURN; case 'E': // We need the default values here. if (doSave) { amx_GetAddr(amx, params[paramPos++], &cptr); if (DoE(&format, &string, cptr, true)) { break; } } else { // Pass a NULL pointer so data isn't saved anywhere. if (DoE(&format, &string, NULL, true)) { break; } } return SSCANF_FAIL_RETURN; case 'e': if (doSave) { amx_GetAddr(amx, params[paramPos++], &cptr); if (DoE(&format, &string, cptr, false)) { break; } } else { // Pass a NULL pointer so data isn't saved anywhere. if (DoE(&format, &string, NULL, false)) { break; } } return SSCANF_FAIL_RETURN; case 'K': // We need the default values here. if (doSave) { amx_GetAddr(amx, params[paramPos++], &cptr); if (DoK(amx, &format, &string, cptr, true)) { break; } } else { // Pass a NULL pointer so data isn't saved anywhere. if (DoK(amx, &format, &string, NULL, true)) { break; } } return SSCANF_FAIL_RETURN; case 'k': if (doSave) { amx_GetAddr(amx, params[paramPos++], &cptr); if (DoK(amx, &format, &string, cptr, false)) { break; } } else { // Pass a NULL pointer so data isn't saved anywhere. if (DoK(amx, &format, &string, NULL, false)) { break; } } return SSCANF_FAIL_RETURN; case '\'': // Find the end of the literal. { char * str = format, * write = format; bool escape = false; while (!IsEnd(*str) && (escape || *str != '\'')) { if (*str == '\\') { if (escape) { // "\\" - Go back a step to write this // character over the last character (which // just happens to be the same character). --write; } escape = !escape; } else { if (*str == '\'') { // Overwrite the escape character with the // quote character. Must have been // preceeded by a slash or it wouldn't have // got to here in the loop. --write; } escape = false; } // Copy the string over itself to get rid of excess // escape characters. // Not sure if it's faster in the average case to // always do the copy or check if it's needed. // This write is always safe as it makes the string // shorter, so we'll never run out of space. It // will also not overwrite the original string. *write++ = *str++; } if (*str == '\'') { // Correct end. Make a shorter string to search // for. *write = '\0'; // Find the current section of format in string. char * find = strstr(string, format); if (!find) { // Didn't find the string return SSCANF_FAIL_RETURN; } // Found the string. Update the current string // position to the length of the search term // further along from the start of the term. Use // "write" here as we want the escaped string // length. string = find + (write - format); // Move to after the end of the search string. Use // "str" here as we want the unescaped string // length. format = str + 1; } else { logprintf("sscanf warning: Unclosed string literal."); char * find = strstr(string, format); if (!find) { return SSCANF_FAIL_RETURN; } string = find + (write - format); format = str; } } break; case '%': logprintf("sscanf warning: sscanf specifiers do not require '%' before them."); continue; default: logprintf("sscanf warning: Unknown format specifier '%c', skipping.", *(format - 1)); continue; } // Loop cleanup - only skip one spacer so that we can detect // multiple explicit delimiters in a row, for example: // // hi there // // is NOT multiple explicit delimiters in a row (they're // whitespace). This however is: // // hi , , , there // SkipOneSpacer(&string); } } // Temporary to the end of the code. ResetDelimiter(); AddDelimiter(')'); // We don't need code here to handle the case where paramPos was reached, // but the end of the string wasn't - if that's the case there's no // problem as we just ignore excess string data. while (paramPos < paramCount || !doSave) { // Loop through if there's still parameters remaining. if (!*format) { logprintf("sscanf warning: Format specifier does not match parameter count."); if (!doSave) { // Started a quiet section but never explicitly ended it. logprintf("sscanf warning: Unclosed quiet section."); } return SSCANF_TRUE_RETURN; } else if (IsWhitespace(*format)) { ++format; } else { // Do the main switch again. switch (*format++) { case 'L': DE(bool, L) case 'B': DE(int, B) case 'N': DE(int, N) case 'C': DE(char, C) case 'I': case 'D': DE(int, I) case 'H': case 'X': DE(int, H) case 'O': DE(int, O) case 'F': DEF(double, F) case 'G': DEF(double, G) case 'U': DE(int, U) case 'Q': DE(int, Q) case 'R': DE(int, R) case 'A': if (doSave) { amx_GetAddr(amx, params[paramPos++], &cptr); if (DoA(&format, NULL, cptr, true)) { break; } } else { // Pass a NULL pointer so data isn't saved anywhere. // Also pass NULL data so it knows to only collect the // default values. if (DoA(&format, NULL, NULL, true)) { break; } } return SSCANF_FAIL_RETURN; case 'E': if (doSave) { amx_GetAddr(amx, params[paramPos++], &cptr); if (DoE(&format, NULL, cptr, true)) { break; } } else { // Pass a NULL pointer so data isn't saved anywhere. // Also pass NULL data so it knows to only collect the // default values. if (DoE(&format, NULL, NULL, true)) { break; } } return SSCANF_FAIL_RETURN; case 'K': if (doSave) { amx_GetAddr(amx, params[paramPos++], &cptr); if (DoK(amx, &format, NULL, cptr, true)) { break; } } else { // Pass a NULL pointer so data isn't saved anywhere. // Also pass NULL data so it knows to only collect the // default values. if (DoK(amx, &format, NULL, NULL, true)) { break; } } return SSCANF_FAIL_RETURN; case '{': if (doSave) { doSave = false; } else { // Already in a quiet section. logprintf("sscanf warning: Can't have nestled quiet sections."); } break; case '}': if (doSave) { logprintf("sscanf warning: Not in a quiet section."); } else { doSave = true; } break; case 'Z': logprintf("sscanf warning: 'Z' doesn't exist - that would be an optional, deprecated optional string!."); // FALLTHROUGH case 'z': logprintf("sscanf warning: 'z' is deprecated, consider using 'S' instead."); // FALLTHROUGH case 'S': { char * dest; int length; if (DoSD(&format, &dest, &length)) { // Send the string to PAWN. if (doSave) { amx_GetAddr(amx, params[paramPos++], &cptr); amx_SetString(cptr, dest, 0, 0, length); } } } break; case 'P': logprintf("sscanf warning: You can't have an optional delimiter."); // FALLTHROUGH case 'p': // Discard delimiter. This only matters when they have // real inputs, not the default ones used here. GetSingleType(&format); continue; case '\'': // Implicitly optional if the specifiers after it are // optional. { bool escape = false; while (!IsEnd(*format) && (escape || *format != '\'')) { if (*format == '\\') { escape = !escape; } else { escape = false; } ++format; } if (*format == '\'') { ++format; } else { logprintf("sscanf warning: Unclosed string literal."); } } break; // Large block of specifiers all together. case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'k': case 'l': case 'n': case 'o': case 'q': case 'r': case 's': case 'u': case 'x': // These are non optional items, but the input string // didn't include them, so we fail - this is in fact the // most basic definition of a fail (the original)! We // don't need any text warnings here - admittedly we don't // know if the format specifier is well formed (there may // not be enough return variables for example), but it // doesn't matter - the coder should have tested for those // things, and the more important thing is that the user // didn't enter the correct data. return SSCANF_FAIL_RETURN; case '%': logprintf("sscanf warning: sscanf specifiers do not require '%' before them."); break; default: logprintf("sscanf warning: Unknown format specifier '%c', skipping.", *(format - 1)); break; } // Don't need any cleanup here. } } if (*format) { do { if (!IsWhitespace(*format)) { // Only print this warning if the remaining characters are not // spaces - spaces are allowed, and sometimes required, on the // ends of formats (e.g. to stop the final 's' specifier // collecting all remaining characters and only get one word). // We could check that the remaining specifier is a valid one, // but this is only a guide - they shouldn't even have other // characters IN the specifier so it doesn't matter - it will // point to a bug, which is the important thing. if (doSave) { if (*format == '}') { logprintf("sscanf warning: Not in a quiet section."); } else if (*format != '{') { // Fix the bad display bug. logprintf("sscanf warning: Format specifier does not match parameter count."); } // Only display it once. break; } else { if (*format == '}') { doSave = true; } else { logprintf("sscanf warning: Format specifier does not match parameter count."); break; } } } ++format; } while (*format); } if (!doSave) { // Started a quiet section but never explicitly ended it. logprintf("sscanf warning: Unclosed quiet section."); } // No more parameters and no more format specifiers which could be read // from - this is a valid return! return SSCANF_TRUE_RETURN; }
/* libcall(const libname[], const funcname[], const typestring[], ...) * * Loads the DLL or shared library if not yet loaded (the name comparison is * case sensitive). * * typestring format: * Whitespace is permitted between the types, but not inside the type * specification. The string "ii[4]&u16s" is equivalent to "i i[4] &u16 s", * but the latter is easier on the eye. * * types: * i = signed integer, 16-bit in Windows 3.x, else 32-bit in Win32 and Linux * u = unsigned integer, 16-bit in Windows 3.x, else 32-bit in Win32 and Linux * f = IEEE floating point, 32-bit * p = packed string * s = unpacked string * The difference between packed and unpacked strings is only relevant when * the parameter is passed by reference (see below). * * pass-by-value and pass-by-reference: * By default, parameters are passed by value. To pass a parameter by * reference, prefix the type letter with an "&": * &i = signed integer passed by reference * i = signed integer passed by value * Same for '&u' versus 'u' and '&f' versus 'f'. * * Arrays are passed by "copy & copy-back". That is, libcall() allocates a * block of dynamic memory to copy the array into. On return from the foreign * function, libcall() copies the array back to the abstract machine. The * net effect is similar to pass by reference, but the foreign function does * not work in the AMX stack directly. During the copy and the copy-back * operations, libcall() may also transform the array elements, for example * between 16-bit and 32-bit elements. This is done because Pawn only * supports a single cell size, which may not fit the required integer size * of the foreign function. * * See "element ranges" for the syntax of passing an array. * * Strings may either be passed by copy, or by "copy & copy-back". When the * string is an output parameter (for the foreign function), the size of the * array that will hold the return string must be indicated between square * brackets behind the type letter (see "element ranges"). When the string * is "input only", this is not needed --libcall() will determine the length * of the input string itself. * * The tokens 'p' and 's' are equivalent, but 'p[10]' and 's[10]' are not * equivalent: the latter syntaxes determine whether the output from the * foreign function will be stored as a packed or an unpacked string. * * element sizes: * Add an integer behind the type letter; for example, 'i16' refers to a * 16-bit signed integer. Note that the value behind the type letter must * be either 8, 16 or 32. * * You should only use element size specifiers on the 'i' and 'u' types. That * is, do not use these specifiers on 'f', 's' and 'p'. * * element ranges: * For passing arrays, the size of the array may be given behind the type * letter and optional element size. The token 'u[4]' indicates an array of * four unsigned integers, which are typically 32-bit. The token 'i16[8]' * is an array of 8 signed 16-bit integers. Arrays are always passed by * "copy & copy-back" * * When compiled as Unicode, this library converts all strings to Unicode * strings. * * The calling convention for the foreign functions is assumed: * - "__stdcall" for Win32, * - "far pascal" for Win16 * - and the GCC default for Unix/Linux (_cdecl) * * C++ name mangling of the called function is not handled (there is no standard * convention for name mangling, so there is no portable way to convert C++ * function names to mangled names). Win32 name mangling (used by default by * Microsoft compilers on functions declared as __stdcall) is also not handled. * * Returns the value of the called function. */ static cell AMX_NATIVE_CALL n_libcall(AMX *amx, const cell *params) { const TCHAR *libname, *funcname, *typestring; MODLIST *item; int paramidx, typeidx, idx; PARAM ps[MAXPARAMS]; cell *cptr,result; LIBFUNC LibFunc; amx_StrParam(amx, params[1], libname); item = findlib(&ModRoot, amx, libname); if (item == NULL) item = addlib(&ModRoot, amx, libname); if (item == NULL) { amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ /* library is loaded, get the function */ amx_StrParam(amx, params[2], funcname); LibFunc=(LIBFUNC)SearchProcAddress(item->inst, funcname); if (LibFunc==NULL) { amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ #if defined HAVE_DYNCALL_H /* (re-)initialize the dyncall library */ if (dcVM==NULL) { dcVM=dcNewCallVM(4096); dcMode(dcVM,DC_CALL_C_X86_WIN32_STD); } /* if */ dcReset(dcVM); #endif /* decode the parameters */ paramidx=typeidx=0; amx_StrParam(amx, params[3], typestring); while (paramidx < MAXPARAMS && typestring[typeidx]!=__T('\0')) { /* skip white space */ while (typestring[typeidx]!=__T('\0') && typestring[typeidx]<=__T(' ')) typeidx++; if (typestring[typeidx]==__T('\0')) break; /* save "pass-by-reference" token */ ps[paramidx].type=0; if (typestring[typeidx]==__T('&')) { ps[paramidx].type=BYREF; typeidx++; } /* if */ /* store type character */ ps[paramidx].type |= (unsigned char)typestring[typeidx]; typeidx++; /* set default size, then check for an explicit size */ #if defined __WIN32__ || defined _WIN32 || defined WIN32 ps[paramidx].size=32; #elif defined _Windows ps[paramidx].size=16; #endif if (_istdigit(typestring[typeidx])) { ps[paramidx].size=(unsigned char)_tcstol(&typestring[typeidx],NULL,10); while (_istdigit(typestring[typeidx])) typeidx++; } /* if */ /* set default range, then check for an explicit range */ ps[paramidx].range=1; if (typestring[typeidx]=='[') { ps[paramidx].range=_tcstol(&typestring[typeidx+1],NULL,10); while (typestring[typeidx]!=']' && typestring[typeidx]!='\0') typeidx++; ps[paramidx].type |= BYREF; /* arrays are always passed by reference */ typeidx++; /* skip closing ']' too */ } /* if */ /* get pointer to parameter */ cptr=amx_Address(amx,params[paramidx+4]); switch (ps[paramidx].type) { case 'i': /* signed integer */ case 'u': /* unsigned integer */ case 'f': /* floating point */ assert(ps[paramidx].range==1); ps[paramidx].v.val=(int)*cptr; break; case 'i' | BYREF: case 'u' | BYREF: case 'f' | BYREF: ps[paramidx].v.ptr=cptr; if (ps[paramidx].range>1) { /* convert array and pass by address */ ps[paramidx].v.ptr = fillarray(amx, &ps[paramidx], cptr); } /* if */ break; case 'p': case 's': case 'p' | BYREF: case 's' | BYREF: if (ps[paramidx].type=='s' || ps[paramidx].type=='p') { int len; /* get length of input string */ amx_StrLen(cptr,&len); len++; /* include '\0' */ /* check max. size */ if (len<ps[paramidx].range) len=ps[paramidx].range; ps[paramidx].range=len; } /* if */ ps[paramidx].v.ptr=malloc(ps[paramidx].range*sizeof(TCHAR)); if (ps[paramidx].v.ptr==NULL) return amx_RaiseError(amx, AMX_ERR_NATIVE); amx_GetString((char *)ps[paramidx].v.ptr,cptr,sizeof(TCHAR)>1,UNLIMITED); break; default: /* invalid parameter type */ return amx_RaiseError(amx, AMX_ERR_NATIVE); } /* switch */ paramidx++; } /* while */ if ((params[0]/sizeof(cell)) - 3 != (size_t)paramidx) return amx_RaiseError(amx, AMX_ERR_NATIVE); /* format string does not match number of parameters */ #if defined HAVE_DYNCALL_H for (idx = 0; idx < paramidx; idx++) { if ((ps[idx].type=='i' || ps[idx].type=='u' || ps[idx].type=='f') && ps[idx].range==1) { switch (ps[idx].size) { case 8: dcArgChar(dcVM,(unsigned char)(ps[idx].v.val & 0xff)); break; case 16: dcArgShort(dcVM,(unsigned short)(ps[idx].v.val & 0xffff)); break; default: dcArgLong(dcVM,ps[idx].v.val); } /* switch */ } else { dcArgPointer(dcVM,ps[idx].v.ptr); } /* if */ } /* for */ result=(cell)dcCallPointer(dcVM,(void*)LibFunc); #else /* HAVE_DYNCALL_H */ /* push the parameters to the stack (left-to-right in 16-bit; right-to-left * in 32-bit) */ #if defined __WIN32__ || defined _WIN32 || defined WIN32 for (idx=paramidx-1; idx>=0; idx--) { #else for (idx=0; idx<paramidx; idx++) { #endif if ((ps[idx].type=='i' || ps[idx].type=='u' || ps[idx].type=='f') && ps[idx].range==1) { switch (ps[idx].size) { case 8: push((unsigned char)(ps[idx].v.val & 0xff)); break; case 16: push((unsigned short)(ps[idx].v.val & 0xffff)); break; default: push(ps[idx].v.val); } /* switch */ } else { push(ps[idx].v.ptr); } /* if */ } /* for */ /* call the function; all parameters are already pushed to the stack (the * function should remove the parameters from the stack) */ result=LibFunc(); #endif /* HAVE_DYNCALL_H */ /* store return values and free allocated memory */ for (idx=0; idx<paramidx; idx++) { switch (ps[idx].type) { case 'p': case 's': free(ps[idx].v.ptr); break; case 'p' | BYREF: case 's' | BYREF: cptr=amx_Address(amx,params[idx+4]); amx_SetString(cptr,(char *)ps[idx].v.ptr,ps[idx].type==('p'|BYREF),sizeof(TCHAR)>1,UNLIMITED); free(ps[idx].v.ptr); break; case 'i': case 'u': case 'f': assert(ps[idx].range==1); break; case 'i' | BYREF: case 'u' | BYREF: case 'f' | BYREF: cptr=amx_Address(amx,params[idx+4]); if (ps[idx].range==1) { /* modify directly in the AMX (no memory block was allocated */ switch (ps[idx].size) { case 8: *cptr= (ps[idx].type==('i' | BYREF)) ? (long)((signed char)*cptr) : (*cptr & 0xff); break; case 16: *cptr= (ps[idx].type==('i' | BYREF)) ? (long)((short)*cptr) : (*cptr & 0xffff); break; } /* switch */ } else { int i; for (i=0; i<ps[idx].range; i++) { switch (ps[idx].size) { case 8: *cptr= (ps[idx].type==('i' | BYREF)) ? ((signed char*)ps[idx].v.ptr)[i] : ((unsigned char*)ps[idx].v.ptr)[i]; break; case 16: *cptr= (ps[idx].type==('i' | BYREF)) ? ((short*)ps[idx].v.ptr)[i] : ((unsigned short*)ps[idx].v.ptr)[i]; break; default: *cptr= (ps[idx].type==('i' | BYREF)) ? ((long*)ps[idx].v.ptr)[i] : ((unsigned long*)ps[idx].v.ptr)[i]; } /* switch */ } /* for */ free((char *)ps[idx].v.ptr); } /* if */ break; default: assert(0); } /* switch */ } /* for */ return result; } /* bool: libfree(const libname[]="") * When the name is an empty string, this function frees all libraries (for this * abstract machine). The name comparison is case sensitive. * Returns true if one or more libraries were freed. */ static cell AMX_NATIVE_CALL n_libfree(AMX *amx, const cell *params) { const TCHAR *libname; amx_StrParam(amx,params[1],libname); return freelib(&ModRoot,amx,libname) > 0; } #else /* HAVE_DYNCALL_H || WIN32_FFI */ static cell AMX_NATIVE_CALL n_libcall(AMX *amx, const cell *params) { (void)amx; (void)params; return 0; }
int Invoke::callNative(const PAWN::Native * native, ...) { if (amx_list.empty() || amx_map.find(native->name) == amx_map.end()) { return 0; } unsigned int amx_addr = amx_map[native->name], count = strlen(native->data), variables = 0; cell * params = new cell[count + 1], * physAddr[6]; params[0] = count * sizeof(cell); va_list input; va_start(input, native); for (unsigned int i = 0; i < count; ++i) { switch (native->data[i]) { case 'd': case 'i': { params[i + 1] = va_arg(input, int); } break; case 'f': { float value = (float)va_arg(input, double); params[i + 1] = amx_ftoc(value); } break; case 's': { char * string = va_arg(input, char *); amx_Allot(amx_list.front(), strlen(string) + 1, ¶ms[i + 1], &physAddr[variables++]); amx_SetString(physAddr[variables - 1], string, 0, 0, strlen(string) + 1); } break; case 'v': { va_arg(input, void *); amx_Allot(amx_list.front(), 1, ¶ms[i + 1], &physAddr[variables++]); } break; case 'p': { va_arg(input, void *); int size = va_arg(input, int); amx_Allot(amx_list.front(), size, ¶ms[++i], &physAddr[variables++]); params[i + 1] = size; } break; } } va_end(input); amx_Function_t amx_Function = (amx_Function_t)amx_addr; int value = amx_Function(amx_list.front(), params); if (variables) { variables = 0; va_start(input, native); for (unsigned int i = 0; i < count; ++i) { switch (native->data[i]) { case 's': { amx_Release(amx_list.front(), params[i + 1]); } break; case 'v': { unsigned int * value = va_arg(input, unsigned int *), * returnValue = (unsigned int *)physAddr[variables++]; * value = * returnValue; amx_Release(amx_list.front(), params[i + 1]); } break; case 'p': { char * text = va_arg(input, char *); int size = va_arg(input, int); amx_GetString(text, physAddr[variables++], 0, size); amx_Release(amx_list.front(), params[++i]); } break; default: { va_arg(input, void *); } break; } } va_end(input); } delete [] params; return value; }
cell FakeAmxPush(const char *s) { cell address = FakeAmxPush(strlen(s)); amx_SetString(::fakeAmxData + address, s, 0, 0, strlen(s) + 1); return address; }