introspect.inc 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. #include <a_samp>
  2. #if !defined AMX_NAME
  3. #error Please #define AMX_NAME "amx_name.amx"
  4. #endif
  5. #if debug < 2
  6. #error The debug level must be 2 or 3
  7. #endif
  8. #if !defined MAX_GLOBAL_VARIABLES
  9. #define MAX_GLOBAL_VARIABLES 4096
  10. #endif
  11. #if !defined MAX_FUNCTIONS
  12. #define MAX_FUNCTIONS 2048
  13. #endif
  14. #if !defined MAX_TAGS
  15. #define MAX_TAGS 512
  16. #endif
  17. #include "introspect-debug-info"
  18. #if defined INTROSPECT_NATIVES
  19. #include "introspect-natives"
  20. #tryinclude "..\amx\amx_header"
  21. #tryinclude "..\amx\dynamic_call"
  22. #if !defined CallNative || !defined SysreqCN
  23. #error amx_assembly is required. Get it here: github.com/Zeex/amx_assembly
  24. #endif
  25. #endif
  26. static stock const s_AmxName[] = AMX_NAME;
  27. stock IntrospectInit() {
  28. static s_initialized = false;
  29. if (s_initialized) {
  30. return true;
  31. } else {
  32. s_initialized = true;
  33. }
  34. new File:fp = fopen(s_AmxName, io_read);
  35. // Try some more versions based on Y_Less' tool: http://forum.sa-mp.com/showthread.php?t=458669
  36. if (!fp) {
  37. static const s_ServerDir[] = "DANGEROUS_SERVER_ROOT/";
  38. static const s_ModeDirs[][] = {
  39. "gamemodes/",
  40. "filterscripts/",
  41. "npcmodes/"
  42. };
  43. new path[64];
  44. for (new i = 0; i != 6; ++i) {
  45. format(path, sizeof (path), "%s%s%s", (i < 3) ? (s_ServerDir) : (""), s_ModeDirs[i % 3], s_AmxName);
  46. if ((fp = fopen(path))) break;
  47. }
  48. }
  49. if (!fp) {
  50. printf("(introspect.inc) Unable to open \"%s\"", s_AmxName);
  51. return false;
  52. }
  53. ReadAmxDebugData(fp);
  54. fclose(fp);
  55. return (s_initialized = true);
  56. }
  57. static stock DereferenceArray(addr, dimensions, ...) {
  58. new resolve = numargs() - 3;
  59. for (new i = 0; i <= resolve; i++) {
  60. new idx = getarg(2 + i);
  61. if (i + 1 >= dimensions) {
  62. #emit LOAD.S.pri idx
  63. #emit LOAD.S.alt addr
  64. #emit IDXADDR
  65. #emit STOR.S.pri addr
  66. } else {
  67. #emit LOAD.S.pri idx
  68. #emit LOAD.S.alt addr
  69. #emit IDXADDR
  70. #emit MOVE.alt
  71. #emit LOAD.I
  72. #emit ADD
  73. #emit STOR.S.pri addr
  74. }
  75. }
  76. return addr;
  77. }
  78. static stock ReadSymbol(const input[], &index, output[], maxlength = sizeof(output)) {
  79. new i = index, j = 0;
  80. if ('0' <= input[i] <= '9') {
  81. return false;
  82. }
  83. for (new c; (c = input[i]); i++) {
  84. if (j + 1 >= maxlength) {
  85. return false;
  86. }
  87. switch (c) {
  88. case 'a' .. 'z',
  89. 'A' .. 'Z',
  90. '0' .. '9',
  91. '@', '_': {
  92. output[j++] = c;
  93. continue;
  94. }
  95. default: {
  96. break;
  97. }
  98. }
  99. }
  100. if (!j) {
  101. return false;
  102. }
  103. output[j] = '\0';
  104. index = i;
  105. return j;
  106. }
  107. static stock ReadString(const input[], &index, output[], maxlength = sizeof(output)) {
  108. new in_esc = false, i = 0, c;
  109. for (index++; (c = input[index]); index++) {
  110. if (i + 1 >= maxlength) {
  111. break;
  112. }
  113. if (c == '\\') {
  114. if (in_esc) {
  115. in_esc = false;
  116. output[i++] = '\\';
  117. } else {
  118. in_esc = true;
  119. }
  120. } else if (in_esc) {
  121. switch (c) {
  122. case '"': output[i++] = c;
  123. case 'a': output[i++] = '\a';
  124. case 'b': output[i++] = '\b';
  125. case 'e': output[i++] = '\e';
  126. case 'r': output[i++] = '\r';
  127. case 'n': output[i++] = '\n';
  128. case 't': output[i++] = '\t';
  129. case 'v': output[i++] = '\v';
  130. }
  131. in_esc = false;
  132. } else {
  133. if ( c == '"') {
  134. output[i] = '\0';
  135. index++;
  136. return true;
  137. } else {
  138. output[i++] = c;
  139. }
  140. }
  141. }
  142. output[i] = '\0';
  143. return false;
  144. }
  145. static stock ReadNumber(const input[], &index, &output, &type) {
  146. new i, c, had_num = false;
  147. type = 'i';
  148. for (i = index; (c = input[i]); i++) {
  149. if (i == 0 && c == '-') {
  150. continue;
  151. }
  152. switch (c) {
  153. case '0' .. '9': {
  154. had_num = true;
  155. continue;
  156. }
  157. case '.': {
  158. if (!had_num) {
  159. return false;
  160. }
  161. type = 'f';
  162. }
  163. case '-', '+', 'e': {
  164. if (!had_num) {
  165. return false;
  166. }
  167. continue;
  168. }
  169. default: {
  170. break;
  171. }
  172. }
  173. }
  174. if (!had_num) {
  175. return false;
  176. }
  177. if (type == 'f') {
  178. output = _:floatstr(input[index]);
  179. } else {
  180. output = strval(input[index]);
  181. }
  182. index = i;
  183. return true;
  184. }
  185. static stock ReadArrayIndexes(info[E_VARIABLE], const input[], &index) {
  186. SkipSpaces(input, index);
  187. while (input[index] == '[') {
  188. index++, SkipSpaces(input, index);
  189. new idx, type;
  190. if (!ReadNumber(input, index, idx, type) || type != 'i') {
  191. return false;
  192. }
  193. info[Address] = DereferenceArray(info[Address], info[Dimensions], idx);
  194. info[Dimensions]--;
  195. info[DimensionSize][0] = info[DimensionSize][1];
  196. info[DimensionSize][1] = info[DimensionSize][2];
  197. info[DimensionSize][2] = 0;
  198. info[DimensionTag][0] = info[DimensionTag][1];
  199. info[DimensionTag][1] = info[DimensionTag][2];
  200. info[DimensionTag][2] = 0;
  201. SkipSpaces(input, index);
  202. if (input[index] == ']') {
  203. index++, SkipSpaces(input, index);
  204. } else {
  205. return false;
  206. }
  207. }
  208. return true;
  209. }
  210. static stock SkipSpaces(const buf[], &index) {
  211. while ('\0' < buf[index] <= ' ') {
  212. index++;
  213. }
  214. }
  215. static stock GetVariableAddress(...) {
  216. #emit LOAD.S.pri 12
  217. #emit RETN
  218. return 0;
  219. }
  220. stock RunSimpleStatement(const statement[], &output_type = 0, output[] = {0}, outlen = sizeof(output), error[] = "", errlen = sizeof(error)) {
  221. IntrospectInit();
  222. new sym[32], idx = 0, start_idx;
  223. #pragma unused outlen
  224. SkipSpaces(statement, idx);
  225. start_idx = idx;
  226. if (!ReadSymbol(statement, idx, sym)) {
  227. strunpack(error, "Invalid symbol given", errlen);
  228. return false;
  229. }
  230. SkipSpaces(statement, idx);
  231. if (statement[idx] == '=' || statement[idx] == '[') {
  232. new info[E_VARIABLE];
  233. if (!GetVariableInfo(sym, info)) {
  234. format(error, errlen, "Invalid variable: %s", sym);
  235. return false;
  236. }
  237. if (statement[idx] == '[') {
  238. if (!ReadArrayIndexes(info, statement, idx)) {
  239. format(error, errlen, "Failed to read array subscripts");
  240. return false;
  241. }
  242. SkipSpaces(statement, idx);
  243. }
  244. if (statement[idx] != '=') {
  245. format(error, errlen, "Expected \"=\"");
  246. return false;
  247. }
  248. idx++;
  249. SkipSpaces(statement, idx);
  250. switch (statement[idx]) {
  251. case '"': {
  252. new buf[512];
  253. if (!ReadString(statement, idx, buf)) {
  254. strunpack(error, "Invalid string literal", errlen);
  255. return false;
  256. }
  257. new addr = info[Address];
  258. new len = info[DimensionSize][0];
  259. if (!len) {
  260. len = sizeof(buf);
  261. }{}
  262. #emit PUSH.S len
  263. #emit PUSH.ADR buf
  264. #emit PUSH.S addr
  265. #emit PUSH.C 12
  266. #emit SYSREQ.C strunpack
  267. #emit STACK 16
  268. return true;
  269. }
  270. case '-', '0' .. '9': {
  271. new type, value;
  272. if (!ReadNumber(statement, idx, value, type)) {
  273. strunpack(error, "Invalid number", errlen);
  274. return false;
  275. }
  276. new addr = info[Address];
  277. #emit LOAD.S.pri value
  278. #emit SREF.S.pri addr
  279. return true;
  280. }
  281. default: {
  282. strunpack(error, "Invalid value in assignment (expects string or number)", errlen);
  283. return false;
  284. }
  285. }
  286. } else if (statement[idx] == '(') {
  287. idx++;
  288. new addr = 0, info[E_FUNCTION];
  289. #if defined INTROSPECT_NATIVES
  290. new is_native = false;
  291. if (-1 != (addr = GetNativeIndexFromName(sym))) {
  292. is_native = true;
  293. }
  294. #endif
  295. if (addr == -1 && GetFunctionInfo(sym, info)) {
  296. addr = info[Address];
  297. if (info[DimensionSize] != 0) {
  298. format(error, errlen, "Functions that return arrays are not yet supported (%s)", sym);
  299. }
  300. }
  301. if (addr == -1) {
  302. format(error, errlen, "Invalid function: %s", sym);
  303. return false;
  304. }
  305. new
  306. arg[16],
  307. arg_ref[16],
  308. arg_type[16],
  309. args = 0,
  310. buf[2048],
  311. buf_idx = 0,
  312. buf_adr,
  313. is_ref = false;
  314. #emit ADDR.pri buf
  315. #emit STOR.S.pri buf_adr
  316. while (statement[idx]) {
  317. SkipSpaces(statement, idx);
  318. if (statement[idx] == '&') {
  319. is_ref = true;
  320. idx++;
  321. } else {
  322. is_ref = false;
  323. }
  324. switch (statement[idx]) {
  325. case ')': {
  326. break;
  327. }
  328. case ',': {
  329. idx++;
  330. continue;
  331. }
  332. case '"': {
  333. if (!ReadString(statement, idx, buf[buf_idx], sizeof(buf) - buf_idx)) {
  334. strunpack(error, "Invalid string literal", errlen);
  335. return false;
  336. }
  337. arg_type[args] = 's';
  338. if (is_ref) {
  339. arg_ref[args] = buf_adr + buf_idx * 4;
  340. arg[args] = GetVariableAddress(arg_ref[args]);
  341. } else {
  342. arg[args] = buf_adr + buf_idx * 4;
  343. }
  344. args++;
  345. buf_idx += strlen(buf[buf_idx]) + 1;
  346. }
  347. case '-', '0' .. '9': {
  348. new type, value;
  349. if (!ReadNumber(statement, idx, value, type)) {
  350. strunpack(error, "Invalid number", errlen);
  351. return false;
  352. }
  353. arg_type[args] = 'i';
  354. if (is_ref) {
  355. arg_ref[args] = value;
  356. arg[args] = GetVariableAddress(arg_ref[args]);
  357. } else {
  358. arg[args] = value;
  359. }
  360. args++;
  361. }
  362. }
  363. }
  364. #if defined INTROSPECT_NATIVES
  365. if (is_native) {
  366. new arg_bytes = 4 + args * 4;
  367. while (--args >= 0) {
  368. #emit ADDR.pri arg
  369. #emit LOAD.S.alt args
  370. #emit SHL.C.alt 2
  371. #emit ADD
  372. #emit PUSH.pri
  373. }
  374. #emit PUSH.S addr
  375. #emit PUSH.S arg_bytes
  376. #emit LCTRL 6
  377. #emit ADD.C 28
  378. #emit PUSH.pri
  379. #emit CONST.pri CallNative
  380. #emit SCTRL 6
  381. return true;
  382. }
  383. #endif
  384. new value, arg_bytes = args * 4;
  385. new retval;
  386. while (--args >= 0) {
  387. value = arg[args];
  388. #emit PUSH.S value
  389. }
  390. #emit PUSH.S arg_bytes
  391. #emit LCTRL 6
  392. #emit ADD.C 28
  393. #emit PUSH.pri
  394. #emit LOAD.S.pri addr
  395. #emit SCTRL 6
  396. #emit STOR.S.pri retval
  397. output[0] = retval;
  398. if (!strcmp("Float", GetTagName(info[Tag]))) {
  399. output_type = 'f';
  400. } else {
  401. output_type = 'i';
  402. }
  403. return true;
  404. }
  405. strunpack(error, "Invalid simple statement", errlen);
  406. return false;
  407. }