| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- #include <a_samp>
- #if !defined AMX_NAME
- #error Please #define AMX_NAME "amx_name.amx"
- #endif
- #if debug < 2
- #error The debug level must be 2 or 3
- #endif
- #if !defined MAX_GLOBAL_VARIABLES
- #define MAX_GLOBAL_VARIABLES 4096
- #endif
- #if !defined MAX_FUNCTIONS
- #define MAX_FUNCTIONS 2048
- #endif
- #if !defined MAX_TAGS
- #define MAX_TAGS 512
- #endif
- #include "introspect-debug-info"
- #if defined INTROSPECT_NATIVES
- #include "introspect-natives"
-
- #tryinclude "..\amx\amx_header"
- #tryinclude "..\amx\dynamic_call"
-
- #if !defined CallNative || !defined SysreqCN
- #error amx_assembly is required. Get it here: github.com/Zeex/amx_assembly
- #endif
- #endif
- static stock const s_AmxName[] = AMX_NAME;
- stock IntrospectInit() {
- static s_initialized = false;
-
- if (s_initialized) {
- return true;
- } else {
- s_initialized = true;
- }
-
- new File:fp = fopen(s_AmxName, io_read);
-
- // Try some more versions based on Y_Less' tool: http://forum.sa-mp.com/showthread.php?t=458669
- if (!fp) {
- static const s_ServerDir[] = "DANGEROUS_SERVER_ROOT/";
- static const s_ModeDirs[][] = {
- "gamemodes/",
- "filterscripts/",
- "npcmodes/"
- };
- new path[64];
- for (new i = 0; i != 6; ++i) {
- format(path, sizeof (path), "%s%s%s", (i < 3) ? (s_ServerDir) : (""), s_ModeDirs[i % 3], s_AmxName);
- if ((fp = fopen(path))) break;
- }
- }
- if (!fp) {
- printf("(introspect.inc) Unable to open \"%s\"", s_AmxName);
- return false;
- }
-
- ReadAmxDebugData(fp);
-
- fclose(fp);
-
- return (s_initialized = true);
- }
- static stock DereferenceArray(addr, dimensions, ...) {
- new resolve = numargs() - 3;
-
- for (new i = 0; i <= resolve; i++) {
- new idx = getarg(2 + i);
-
- if (i + 1 >= dimensions) {
- #emit LOAD.S.pri idx
- #emit LOAD.S.alt addr
- #emit IDXADDR
- #emit STOR.S.pri addr
- } else {
- #emit LOAD.S.pri idx
- #emit LOAD.S.alt addr
- #emit IDXADDR
- #emit MOVE.alt
- #emit LOAD.I
- #emit ADD
- #emit STOR.S.pri addr
- }
- }
-
- return addr;
- }
- static stock ReadSymbol(const input[], &index, output[], maxlength = sizeof(output)) {
- new i = index, j = 0;
-
- if ('0' <= input[i] <= '9') {
- return false;
- }
-
- for (new c; (c = input[i]); i++) {
- if (j + 1 >= maxlength) {
- return false;
- }
-
- switch (c) {
- case 'a' .. 'z',
- 'A' .. 'Z',
- '0' .. '9',
- '@', '_': {
- output[j++] = c;
-
- continue;
- }
-
- default: {
- break;
- }
- }
- }
-
- if (!j) {
- return false;
- }
-
- output[j] = '\0';
-
- index = i;
-
- return j;
- }
- static stock ReadString(const input[], &index, output[], maxlength = sizeof(output)) {
- new in_esc = false, i = 0, c;
-
- for (index++; (c = input[index]); index++) {
- if (i + 1 >= maxlength) {
- break;
- }
-
- if (c == '\\') {
- if (in_esc) {
- in_esc = false;
-
- output[i++] = '\\';
- } else {
- in_esc = true;
- }
- } else if (in_esc) {
- switch (c) {
- case '"': output[i++] = c;
- case 'a': output[i++] = '\a';
- case 'b': output[i++] = '\b';
- case 'e': output[i++] = '\e';
- case 'r': output[i++] = '\r';
- case 'n': output[i++] = '\n';
- case 't': output[i++] = '\t';
- case 'v': output[i++] = '\v';
- }
-
- in_esc = false;
- } else {
- if ( c == '"') {
- output[i] = '\0';
- index++;
-
- return true;
- } else {
- output[i++] = c;
- }
- }
- }
-
- output[i] = '\0';
-
- return false;
- }
- static stock ReadNumber(const input[], &index, &output, &type) {
- new i, c, had_num = false;
-
- type = 'i';
-
- for (i = index; (c = input[i]); i++) {
- if (i == 0 && c == '-') {
- continue;
- }
-
- switch (c) {
- case '0' .. '9': {
- had_num = true;
-
- continue;
- }
-
- case '.': {
- if (!had_num) {
- return false;
- }
-
- type = 'f';
- }
-
- case '-', '+', 'e': {
- if (!had_num) {
- return false;
- }
-
- continue;
- }
-
- default: {
- break;
- }
- }
- }
-
- if (!had_num) {
- return false;
- }
-
- if (type == 'f') {
- output = _:floatstr(input[index]);
- } else {
- output = strval(input[index]);
- }
-
- index = i;
-
- return true;
- }
- static stock ReadArrayIndexes(info[E_VARIABLE], const input[], &index) {
- SkipSpaces(input, index);
-
- while (input[index] == '[') {
- index++, SkipSpaces(input, index);
-
- new idx, type;
-
- if (!ReadNumber(input, index, idx, type) || type != 'i') {
- return false;
- }
-
- info[Address] = DereferenceArray(info[Address], info[Dimensions], idx);
-
- info[Dimensions]--;
-
- info[DimensionSize][0] = info[DimensionSize][1];
- info[DimensionSize][1] = info[DimensionSize][2];
- info[DimensionSize][2] = 0;
-
- info[DimensionTag][0] = info[DimensionTag][1];
- info[DimensionTag][1] = info[DimensionTag][2];
- info[DimensionTag][2] = 0;
-
- SkipSpaces(input, index);
-
- if (input[index] == ']') {
- index++, SkipSpaces(input, index);
- } else {
- return false;
- }
- }
-
- return true;
- }
- static stock SkipSpaces(const buf[], &index) {
- while ('\0' < buf[index] <= ' ') {
- index++;
- }
- }
- static stock GetVariableAddress(...) {
- #emit LOAD.S.pri 12
- #emit RETN
-
- return 0;
- }
- stock RunSimpleStatement(const statement[], &output_type = 0, output[] = {0}, outlen = sizeof(output), error[] = "", errlen = sizeof(error)) {
- IntrospectInit();
-
- new sym[32], idx = 0, start_idx;
-
- #pragma unused outlen
-
- SkipSpaces(statement, idx);
-
- start_idx = idx;
-
- if (!ReadSymbol(statement, idx, sym)) {
- strunpack(error, "Invalid symbol given", errlen);
-
- return false;
- }
-
- SkipSpaces(statement, idx);
-
- if (statement[idx] == '=' || statement[idx] == '[') {
- new info[E_VARIABLE];
-
- if (!GetVariableInfo(sym, info)) {
- format(error, errlen, "Invalid variable: %s", sym);
-
- return false;
- }
-
- if (statement[idx] == '[') {
- if (!ReadArrayIndexes(info, statement, idx)) {
- format(error, errlen, "Failed to read array subscripts");
-
- return false;
- }
-
- SkipSpaces(statement, idx);
- }
-
- if (statement[idx] != '=') {
- format(error, errlen, "Expected \"=\"");
-
- return false;
- }
-
- idx++;
-
- SkipSpaces(statement, idx);
-
- switch (statement[idx]) {
- case '"': {
- new buf[512];
-
- if (!ReadString(statement, idx, buf)) {
- strunpack(error, "Invalid string literal", errlen);
-
- return false;
- }
-
- new addr = info[Address];
- new len = info[DimensionSize][0];
-
- if (!len) {
- len = sizeof(buf);
- }{}
-
- #emit PUSH.S len
- #emit PUSH.ADR buf
- #emit PUSH.S addr
- #emit PUSH.C 12
- #emit SYSREQ.C strunpack
- #emit STACK 16
-
- return true;
- }
-
- case '-', '0' .. '9': {
- new type, value;
-
- if (!ReadNumber(statement, idx, value, type)) {
- strunpack(error, "Invalid number", errlen);
-
- return false;
- }
-
- new addr = info[Address];
-
- #emit LOAD.S.pri value
- #emit SREF.S.pri addr
-
- return true;
- }
-
- default: {
- strunpack(error, "Invalid value in assignment (expects string or number)", errlen);
-
- return false;
- }
- }
- } else if (statement[idx] == '(') {
- idx++;
-
- new addr = 0, info[E_FUNCTION];
-
- #if defined INTROSPECT_NATIVES
- new is_native = false;
-
- if (-1 != (addr = GetNativeIndexFromName(sym))) {
- is_native = true;
- }
- #endif
-
- if (addr == -1 && GetFunctionInfo(sym, info)) {
- addr = info[Address];
-
- if (info[DimensionSize] != 0) {
- format(error, errlen, "Functions that return arrays are not yet supported (%s)", sym);
- }
- }
-
- if (addr == -1) {
- format(error, errlen, "Invalid function: %s", sym);
-
- return false;
- }
-
- new
- arg[16],
- arg_ref[16],
- arg_type[16],
- args = 0,
- buf[2048],
- buf_idx = 0,
- buf_adr,
- is_ref = false;
-
- #emit ADDR.pri buf
- #emit STOR.S.pri buf_adr
-
- while (statement[idx]) {
- SkipSpaces(statement, idx);
-
- if (statement[idx] == '&') {
- is_ref = true;
- idx++;
- } else {
- is_ref = false;
- }
-
- switch (statement[idx]) {
- case ')': {
- break;
- }
-
- case ',': {
- idx++;
- continue;
- }
-
- case '"': {
- if (!ReadString(statement, idx, buf[buf_idx], sizeof(buf) - buf_idx)) {
- strunpack(error, "Invalid string literal", errlen);
-
- return false;
- }
-
- arg_type[args] = 's';
-
- if (is_ref) {
- arg_ref[args] = buf_adr + buf_idx * 4;
- arg[args] = GetVariableAddress(arg_ref[args]);
- } else {
- arg[args] = buf_adr + buf_idx * 4;
- }
-
- args++;
-
- buf_idx += strlen(buf[buf_idx]) + 1;
- }
-
- case '-', '0' .. '9': {
- new type, value;
-
- if (!ReadNumber(statement, idx, value, type)) {
- strunpack(error, "Invalid number", errlen);
-
- return false;
- }
-
- arg_type[args] = 'i';
-
- if (is_ref) {
- arg_ref[args] = value;
- arg[args] = GetVariableAddress(arg_ref[args]);
- } else {
- arg[args] = value;
- }
-
- args++;
- }
- }
- }
-
- #if defined INTROSPECT_NATIVES
- if (is_native) {
- new arg_bytes = 4 + args * 4;
-
- while (--args >= 0) {
- #emit ADDR.pri arg
- #emit LOAD.S.alt args
- #emit SHL.C.alt 2
- #emit ADD
- #emit PUSH.pri
- }
-
- #emit PUSH.S addr
- #emit PUSH.S arg_bytes
-
- #emit LCTRL 6
- #emit ADD.C 28
- #emit PUSH.pri
- #emit CONST.pri CallNative
- #emit SCTRL 6
-
- return true;
- }
- #endif
-
- new value, arg_bytes = args * 4;
- new retval;
-
- while (--args >= 0) {
- value = arg[args];
- #emit PUSH.S value
- }
-
- #emit PUSH.S arg_bytes
-
- #emit LCTRL 6
- #emit ADD.C 28
- #emit PUSH.pri
- #emit LOAD.S.pri addr
- #emit SCTRL 6
- #emit STOR.S.pri retval
-
- output[0] = retval;
-
- if (!strcmp("Float", GetTagName(info[Tag]))) {
- output_type = 'f';
- } else {
- output_type = 'i';
- }
-
- return true;
- }
-
- strunpack(error, "Invalid simple statement", errlen);
-
- return false;
- }
|