PageRenderTime 95ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 2ms

/prvm_cmds.c

https://gitlab.com/xonotic/darkplaces
C | 7366 lines | 4891 code | 858 blank | 1617 comment | 1009 complexity | ac48622bb0bee129912c328ab7c76e96 MD5 | raw file
Possible License(s): GPL-2.0
  1. // AK
  2. // Basically every vm builtin cmd should be in here.
  3. // All 3 builtin and extension lists can be found here
  4. // cause large (I think they will) parts are from pr_cmds the same copyright like in pr_cmds
  5. // also applies here
  6. #include "quakedef.h"
  7. #include "prvm_cmds.h"
  8. #include "libcurl.h"
  9. #include <time.h>
  10. #include "cl_collision.h"
  11. #include "clvm_cmds.h"
  12. #include "csprogs.h"
  13. #include "ft2.h"
  14. #include "mdfour.h"
  15. extern cvar_t prvm_backtraceforwarnings;
  16. #ifdef USEODE
  17. extern dllhandle_t ode_dll;
  18. #endif
  19. // LordHavoc: changed this to NOT use a return statement, so that it can be used in functions that must return a value
  20. void VM_Warning(prvm_prog_t *prog, const char *fmt, ...)
  21. {
  22. va_list argptr;
  23. char msg[MAX_INPUTLINE];
  24. static double recursive = -1;
  25. va_start(argptr,fmt);
  26. dpvsnprintf(msg,sizeof(msg),fmt,argptr);
  27. va_end(argptr);
  28. Con_Print(msg);
  29. // TODO: either add a cvar/cmd to control the state dumping or replace some of the calls with Con_Printf [9/13/2006 Black]
  30. if(prvm_backtraceforwarnings.integer && recursive != realtime) // NOTE: this compares to the time, just in case if PRVM_PrintState causes a Host_Error and keeps recursive set
  31. {
  32. recursive = realtime;
  33. PRVM_PrintState(prog, 0);
  34. recursive = -1;
  35. }
  36. }
  37. //============================================================================
  38. // Common
  39. // TODO DONE: move vm_files and vm_fssearchlist to prvm_prog_t struct
  40. // TODO: move vm_files and vm_fssearchlist back [9/13/2006 Black]
  41. // TODO: (move vm_files and vm_fssearchlist to prvm_prog_t struct again) [2007-01-23 LordHavoc]
  42. // TODO: will this war ever end? [2007-01-23 LordHavoc]
  43. void VM_CheckEmptyString(prvm_prog_t *prog, const char *s)
  44. {
  45. if (ISWHITESPACE(s[0]))
  46. prog->error_cmd("%s: Bad string", prog->name);
  47. }
  48. void VM_GenerateFrameGroupBlend(prvm_prog_t *prog, framegroupblend_t *framegroupblend, const prvm_edict_t *ed)
  49. {
  50. // self.frame is the interpolation target (new frame)
  51. // self.frame1time is the animation base time for the interpolation target
  52. // self.frame2 is the interpolation start (previous frame)
  53. // self.frame2time is the animation base time for the interpolation start
  54. // self.lerpfrac is the interpolation strength for self.frame2
  55. // self.lerpfrac3 is the interpolation strength for self.frame3
  56. // self.lerpfrac4 is the interpolation strength for self.frame4
  57. // pitch angle on a player model where the animator set up 5 sets of
  58. // animations and the csqc simply lerps between sets)
  59. framegroupblend[0].frame = (int) PRVM_gameedictfloat(ed, frame );
  60. framegroupblend[1].frame = (int) PRVM_gameedictfloat(ed, frame2 );
  61. framegroupblend[2].frame = (int) PRVM_gameedictfloat(ed, frame3 );
  62. framegroupblend[3].frame = (int) PRVM_gameedictfloat(ed, frame4 );
  63. framegroupblend[0].start = PRVM_gameedictfloat(ed, frame1time);
  64. framegroupblend[1].start = PRVM_gameedictfloat(ed, frame2time);
  65. framegroupblend[2].start = PRVM_gameedictfloat(ed, frame3time);
  66. framegroupblend[3].start = PRVM_gameedictfloat(ed, frame4time);
  67. framegroupblend[1].lerp = PRVM_gameedictfloat(ed, lerpfrac );
  68. framegroupblend[2].lerp = PRVM_gameedictfloat(ed, lerpfrac3 );
  69. framegroupblend[3].lerp = PRVM_gameedictfloat(ed, lerpfrac4 );
  70. // assume that the (missing) lerpfrac1 is whatever remains after lerpfrac2+lerpfrac3+lerpfrac4 are summed
  71. framegroupblend[0].lerp = 1 - framegroupblend[1].lerp - framegroupblend[2].lerp - framegroupblend[3].lerp;
  72. }
  73. // LordHavoc: quite tempting to break apart this function to reuse the
  74. // duplicated code, but I suspect it is better for performance
  75. // this way
  76. void VM_FrameBlendFromFrameGroupBlend(frameblend_t *frameblend, const framegroupblend_t *framegroupblend, const dp_model_t *model, double curtime)
  77. {
  78. int sub2, numframes, f, i, k;
  79. int isfirstframegroup = true;
  80. int nolerp;
  81. double sublerp, lerp, d;
  82. const animscene_t *scene;
  83. const framegroupblend_t *g;
  84. frameblend_t *blend = frameblend;
  85. memset(blend, 0, MAX_FRAMEBLENDS * sizeof(*blend));
  86. // rpolzer: Not testing isanimated here - a model might have
  87. // "animations" that move no vertices (but only bones), thus rendering
  88. // may assume it's not animated while processing can't.
  89. if (!model)
  90. {
  91. blend[0].lerp = 1;
  92. return;
  93. }
  94. nolerp = (model->type == mod_sprite) ? !r_lerpsprites.integer : !r_lerpmodels.integer;
  95. numframes = model->numframes;
  96. for (k = 0, g = framegroupblend;k < MAX_FRAMEGROUPBLENDS;k++, g++)
  97. {
  98. f = g->frame;
  99. if ((unsigned int)f >= (unsigned int)numframes)
  100. {
  101. if (developer_extra.integer)
  102. Con_DPrintf("VM_FrameBlendFromFrameGroupBlend: no such frame %d in model %s\n", f, model->name);
  103. f = 0;
  104. }
  105. d = lerp = g->lerp;
  106. if (lerp <= 0)
  107. continue;
  108. if (nolerp)
  109. {
  110. if (isfirstframegroup)
  111. {
  112. d = lerp = 1;
  113. isfirstframegroup = false;
  114. }
  115. else
  116. continue;
  117. }
  118. if (model->animscenes)
  119. {
  120. scene = model->animscenes + f;
  121. f = scene->firstframe;
  122. if (scene->framecount > 1)
  123. {
  124. // this code path is only used on .zym models and torches
  125. sublerp = scene->framerate * (curtime - g->start);
  126. f = (int) floor(sublerp);
  127. sublerp -= f;
  128. sub2 = f + 1;
  129. if (sublerp < (1.0 / 65536.0f))
  130. sublerp = 0;
  131. if (sublerp > (65535.0f / 65536.0f))
  132. sublerp = 1;
  133. if (nolerp)
  134. sublerp = 0;
  135. if (scene->loop)
  136. {
  137. f = (f % scene->framecount);
  138. sub2 = (sub2 % scene->framecount);
  139. }
  140. f = bound(0, f, (scene->framecount - 1)) + scene->firstframe;
  141. sub2 = bound(0, sub2, (scene->framecount - 1)) + scene->firstframe;
  142. d = sublerp * lerp;
  143. // two framelerps produced from one animation
  144. if (d > 0)
  145. {
  146. for (i = 0;i < MAX_FRAMEBLENDS;i++)
  147. {
  148. if (blend[i].lerp <= 0 || blend[i].subframe == sub2)
  149. {
  150. blend[i].subframe = sub2;
  151. blend[i].lerp += d;
  152. break;
  153. }
  154. }
  155. }
  156. d = (1 - sublerp) * lerp;
  157. }
  158. }
  159. if (d > 0)
  160. {
  161. for (i = 0;i < MAX_FRAMEBLENDS;i++)
  162. {
  163. if (blend[i].lerp <= 0 || blend[i].subframe == f)
  164. {
  165. blend[i].subframe = f;
  166. blend[i].lerp += d;
  167. break;
  168. }
  169. }
  170. }
  171. }
  172. }
  173. void VM_UpdateEdictSkeleton(prvm_prog_t *prog, prvm_edict_t *ed, const dp_model_t *edmodel, const frameblend_t *frameblend)
  174. {
  175. if (ed->priv.server->skeleton.model != edmodel)
  176. {
  177. VM_RemoveEdictSkeleton(prog, ed);
  178. ed->priv.server->skeleton.model = edmodel;
  179. }
  180. if (!ed->priv.server->skeleton.model || !ed->priv.server->skeleton.model->num_bones)
  181. {
  182. if(ed->priv.server->skeleton.relativetransforms)
  183. Mem_Free(ed->priv.server->skeleton.relativetransforms);
  184. ed->priv.server->skeleton.relativetransforms = NULL;
  185. return;
  186. }
  187. {
  188. int skeletonindex = -1;
  189. skeleton_t *skeleton;
  190. skeletonindex = (int)PRVM_gameedictfloat(ed, skeletonindex) - 1;
  191. if (skeletonindex >= 0 && skeletonindex < MAX_EDICTS && (skeleton = prog->skeletons[skeletonindex]) && skeleton->model->num_bones == ed->priv.server->skeleton.model->num_bones)
  192. {
  193. // custom skeleton controlled by the game (FTE_CSQC_SKELETONOBJECTS)
  194. if (!ed->priv.server->skeleton.relativetransforms)
  195. ed->priv.server->skeleton.relativetransforms = (matrix4x4_t *)Mem_Alloc(prog->progs_mempool, ed->priv.server->skeleton.model->num_bones * sizeof(matrix4x4_t));
  196. memcpy(ed->priv.server->skeleton.relativetransforms, skeleton->relativetransforms, ed->priv.server->skeleton.model->num_bones * sizeof(matrix4x4_t));
  197. }
  198. else
  199. {
  200. if(ed->priv.server->skeleton.relativetransforms)
  201. Mem_Free(ed->priv.server->skeleton.relativetransforms);
  202. ed->priv.server->skeleton.relativetransforms = NULL;
  203. }
  204. }
  205. }
  206. void VM_RemoveEdictSkeleton(prvm_prog_t *prog, prvm_edict_t *ed)
  207. {
  208. if (ed->priv.server->skeleton.relativetransforms)
  209. Mem_Free(ed->priv.server->skeleton.relativetransforms);
  210. memset(&ed->priv.server->skeleton, 0, sizeof(ed->priv.server->skeleton));
  211. }
  212. //============================================================================
  213. //BUILT-IN FUNCTIONS
  214. void VM_VarString(prvm_prog_t *prog, int first, char *out, int outlength)
  215. {
  216. int i;
  217. const char *s;
  218. char *outend;
  219. outend = out + outlength - 1;
  220. for (i = first;i < prog->argc && out < outend;i++)
  221. {
  222. s = PRVM_G_STRING((OFS_PARM0+i*3));
  223. while (out < outend && *s)
  224. *out++ = *s++;
  225. }
  226. *out++ = 0;
  227. }
  228. /*
  229. =================
  230. VM_checkextension
  231. returns true if the extension is supported by the server
  232. checkextension(extensionname)
  233. =================
  234. */
  235. // kind of helper function
  236. static qboolean checkextension(prvm_prog_t *prog, const char *name)
  237. {
  238. int len;
  239. const char *e, *start;
  240. len = (int)strlen(name);
  241. for (e = prog->extensionstring;*e;e++)
  242. {
  243. while (*e == ' ')
  244. e++;
  245. if (!*e)
  246. break;
  247. start = e;
  248. while (*e && *e != ' ')
  249. e++;
  250. if ((e - start) == len && !strncasecmp(start, name, len))
  251. {
  252. #ifdef USEODE
  253. // special sheck for ODE
  254. if (!strncasecmp("DP_PHYSICS_ODE", name, 14))
  255. {
  256. #ifndef LINK_TO_LIBODE
  257. return ode_dll ? true : false;
  258. #else
  259. #ifdef LINK_TO_LIBODE
  260. return true;
  261. #else
  262. return false;
  263. #endif
  264. #endif
  265. }
  266. #endif
  267. // special sheck for d0_blind_id
  268. if (!strcasecmp("DP_CRYPTO", name))
  269. return Crypto_Available();
  270. if (!strcasecmp("DP_QC_DIGEST_SHA256", name))
  271. return Crypto_Available();
  272. return true;
  273. }
  274. }
  275. return false;
  276. }
  277. void VM_checkextension(prvm_prog_t *prog)
  278. {
  279. VM_SAFEPARMCOUNT(1,VM_checkextension);
  280. PRVM_G_FLOAT(OFS_RETURN) = checkextension(prog, PRVM_G_STRING(OFS_PARM0));
  281. }
  282. /*
  283. =================
  284. VM_error
  285. This is a TERMINAL error, which will kill off the entire prog.
  286. Dumps self.
  287. error(value)
  288. =================
  289. */
  290. void VM_error(prvm_prog_t *prog)
  291. {
  292. prvm_edict_t *ed;
  293. char string[VM_STRINGTEMP_LENGTH];
  294. VM_VarString(prog, 0, string, sizeof(string));
  295. Con_Printf("======%s ERROR in %s:\n%s\n", prog->name, PRVM_GetString(prog, prog->xfunction->s_name), string);
  296. ed = PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self));
  297. PRVM_ED_Print(prog, ed, NULL);
  298. prog->error_cmd("%s: Program error in function %s:\n%s\nTip: read above for entity information\n", prog->name, PRVM_GetString(prog, prog->xfunction->s_name), string);
  299. }
  300. /*
  301. =================
  302. VM_objerror
  303. Dumps out self, then an error message. The program is aborted and self is
  304. removed, but the level can continue.
  305. objerror(value)
  306. =================
  307. */
  308. void VM_objerror(prvm_prog_t *prog)
  309. {
  310. prvm_edict_t *ed;
  311. char string[VM_STRINGTEMP_LENGTH];
  312. VM_VarString(prog, 0, string, sizeof(string));
  313. Con_Printf("======OBJECT ERROR======\n"); // , prog->name, PRVM_GetString(prog->xfunction->s_name), string); // or include them? FIXME
  314. ed = PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self));
  315. PRVM_ED_Print(prog, ed, NULL);
  316. PRVM_ED_Free (prog, ed);
  317. Con_Printf("%s OBJECT ERROR in %s:\n%s\nTip: read above for entity information\n", prog->name, PRVM_GetString(prog, prog->xfunction->s_name), string);
  318. }
  319. /*
  320. =================
  321. VM_print
  322. print to console
  323. print(...[string])
  324. =================
  325. */
  326. void VM_print(prvm_prog_t *prog)
  327. {
  328. char string[VM_STRINGTEMP_LENGTH];
  329. VM_VarString(prog, 0, string, sizeof(string));
  330. Con_Print(string);
  331. }
  332. /*
  333. =================
  334. VM_bprint
  335. broadcast print to everyone on server
  336. bprint(...[string])
  337. =================
  338. */
  339. void VM_bprint(prvm_prog_t *prog)
  340. {
  341. char string[VM_STRINGTEMP_LENGTH];
  342. if(!sv.active)
  343. {
  344. VM_Warning(prog, "VM_bprint: game is not server(%s) !\n", prog->name);
  345. return;
  346. }
  347. VM_VarString(prog, 0, string, sizeof(string));
  348. SV_BroadcastPrint(string);
  349. }
  350. /*
  351. =================
  352. VM_sprint (menu & client but only if server.active == true)
  353. single print to a specific client
  354. sprint(float clientnum,...[string])
  355. =================
  356. */
  357. void VM_sprint(prvm_prog_t *prog)
  358. {
  359. client_t *client;
  360. int clientnum;
  361. char string[VM_STRINGTEMP_LENGTH];
  362. VM_SAFEPARMCOUNTRANGE(1, 8, VM_sprint);
  363. //find client for this entity
  364. clientnum = (int)PRVM_G_FLOAT(OFS_PARM0);
  365. if (!sv.active || clientnum < 0 || clientnum >= svs.maxclients || !svs.clients[clientnum].active)
  366. {
  367. VM_Warning(prog, "VM_sprint: %s: invalid client or server is not active !\n", prog->name);
  368. return;
  369. }
  370. client = svs.clients + clientnum;
  371. if (!client->netconnection)
  372. return;
  373. VM_VarString(prog, 1, string, sizeof(string));
  374. MSG_WriteChar(&client->netconnection->message,svc_print);
  375. MSG_WriteString(&client->netconnection->message, string);
  376. }
  377. /*
  378. =================
  379. VM_centerprint
  380. single print to the screen
  381. centerprint(value)
  382. =================
  383. */
  384. void VM_centerprint(prvm_prog_t *prog)
  385. {
  386. char string[VM_STRINGTEMP_LENGTH];
  387. VM_SAFEPARMCOUNTRANGE(1, 8, VM_centerprint);
  388. VM_VarString(prog, 0, string, sizeof(string));
  389. SCR_CenterPrint(string);
  390. }
  391. /*
  392. =================
  393. VM_normalize
  394. vector normalize(vector)
  395. =================
  396. */
  397. void VM_normalize(prvm_prog_t *prog)
  398. {
  399. prvm_vec_t *value1;
  400. vec3_t newvalue;
  401. double f;
  402. VM_SAFEPARMCOUNT(1,VM_normalize);
  403. value1 = PRVM_G_VECTOR(OFS_PARM0);
  404. f = VectorLength2(value1);
  405. if (f)
  406. {
  407. f = 1.0 / sqrt(f);
  408. VectorScale(value1, f, newvalue);
  409. }
  410. else
  411. VectorClear(newvalue);
  412. VectorCopy (newvalue, PRVM_G_VECTOR(OFS_RETURN));
  413. }
  414. /*
  415. =================
  416. VM_vlen
  417. scalar vlen(vector)
  418. =================
  419. */
  420. void VM_vlen(prvm_prog_t *prog)
  421. {
  422. VM_SAFEPARMCOUNT(1,VM_vlen);
  423. PRVM_G_FLOAT(OFS_RETURN) = VectorLength(PRVM_G_VECTOR(OFS_PARM0));
  424. }
  425. /*
  426. =================
  427. VM_vectoyaw
  428. float vectoyaw(vector)
  429. =================
  430. */
  431. void VM_vectoyaw(prvm_prog_t *prog)
  432. {
  433. prvm_vec_t *value1;
  434. prvm_vec_t yaw;
  435. VM_SAFEPARMCOUNT(1,VM_vectoyaw);
  436. value1 = PRVM_G_VECTOR(OFS_PARM0);
  437. if (value1[1] == 0 && value1[0] == 0)
  438. yaw = 0;
  439. else
  440. {
  441. yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
  442. if (yaw < 0)
  443. yaw += 360;
  444. }
  445. PRVM_G_FLOAT(OFS_RETURN) = yaw;
  446. }
  447. /*
  448. =================
  449. VM_vectoangles
  450. vector vectoangles(vector[, vector])
  451. =================
  452. */
  453. void VM_vectoangles(prvm_prog_t *prog)
  454. {
  455. vec3_t result, forward, up;
  456. VM_SAFEPARMCOUNTRANGE(1, 2,VM_vectoangles);
  457. VectorCopy(PRVM_G_VECTOR(OFS_PARM0), forward);
  458. if (prog->argc >= 2)
  459. {
  460. VectorCopy(PRVM_G_VECTOR(OFS_PARM1), up);
  461. AnglesFromVectors(result, forward, up, true);
  462. }
  463. else
  464. AnglesFromVectors(result, forward, NULL, true);
  465. VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
  466. }
  467. /*
  468. =================
  469. VM_random
  470. Returns a number from 0<= num < 1
  471. float random()
  472. =================
  473. */
  474. void VM_random(prvm_prog_t *prog)
  475. {
  476. VM_SAFEPARMCOUNT(0,VM_random);
  477. PRVM_G_FLOAT(OFS_RETURN) = lhrandom(0, 1);
  478. }
  479. /*
  480. =========
  481. VM_localsound
  482. localsound(string sample)
  483. =========
  484. */
  485. void VM_localsound(prvm_prog_t *prog)
  486. {
  487. const char *s;
  488. VM_SAFEPARMCOUNT(1,VM_localsound);
  489. s = PRVM_G_STRING(OFS_PARM0);
  490. if(!S_LocalSound (s))
  491. {
  492. PRVM_G_FLOAT(OFS_RETURN) = -4;
  493. VM_Warning(prog, "VM_localsound: Failed to play %s for %s !\n", s, prog->name);
  494. return;
  495. }
  496. PRVM_G_FLOAT(OFS_RETURN) = 1;
  497. }
  498. /*
  499. =================
  500. VM_break
  501. break()
  502. =================
  503. */
  504. void VM_break(prvm_prog_t *prog)
  505. {
  506. prog->error_cmd("%s: break statement", prog->name);
  507. }
  508. //============================================================================
  509. /*
  510. =================
  511. VM_localcmd
  512. Sends text over to the client's execution buffer
  513. [localcmd (string, ...) or]
  514. cmd (string, ...)
  515. =================
  516. */
  517. void VM_localcmd(prvm_prog_t *prog)
  518. {
  519. char string[VM_STRINGTEMP_LENGTH];
  520. VM_SAFEPARMCOUNTRANGE(1, 8, VM_localcmd);
  521. VM_VarString(prog, 0, string, sizeof(string));
  522. Cbuf_AddText(string);
  523. }
  524. static qboolean PRVM_Cvar_ReadOk(const char *string)
  525. {
  526. cvar_t *cvar;
  527. cvar = Cvar_FindVar(string);
  528. return ((cvar) && ((cvar->flags & CVAR_PRIVATE) == 0));
  529. }
  530. /*
  531. =================
  532. VM_cvar
  533. float cvar (string)
  534. =================
  535. */
  536. void VM_cvar(prvm_prog_t *prog)
  537. {
  538. char string[VM_STRINGTEMP_LENGTH];
  539. VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar);
  540. VM_VarString(prog, 0, string, sizeof(string));
  541. VM_CheckEmptyString(prog, string);
  542. PRVM_G_FLOAT(OFS_RETURN) = PRVM_Cvar_ReadOk(string) ? Cvar_VariableValue(string) : 0;
  543. }
  544. /*
  545. =================
  546. VM_cvar
  547. float cvar_type (string)
  548. float CVAR_TYPEFLAG_EXISTS = 1;
  549. float CVAR_TYPEFLAG_SAVED = 2;
  550. float CVAR_TYPEFLAG_PRIVATE = 4;
  551. float CVAR_TYPEFLAG_ENGINE = 8;
  552. float CVAR_TYPEFLAG_HASDESCRIPTION = 16;
  553. float CVAR_TYPEFLAG_READONLY = 32;
  554. =================
  555. */
  556. void VM_cvar_type(prvm_prog_t *prog)
  557. {
  558. char string[VM_STRINGTEMP_LENGTH];
  559. cvar_t *cvar;
  560. int ret;
  561. VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar);
  562. VM_VarString(prog, 0, string, sizeof(string));
  563. VM_CheckEmptyString(prog, string);
  564. cvar = Cvar_FindVar(string);
  565. if(!cvar)
  566. {
  567. PRVM_G_FLOAT(OFS_RETURN) = 0;
  568. return; // CVAR_TYPE_NONE
  569. }
  570. ret = 1; // CVAR_EXISTS
  571. if(cvar->flags & CVAR_SAVE)
  572. ret |= 2; // CVAR_TYPE_SAVED
  573. if(cvar->flags & CVAR_PRIVATE)
  574. ret |= 4; // CVAR_TYPE_PRIVATE
  575. if(!(cvar->flags & CVAR_ALLOCATED))
  576. ret |= 8; // CVAR_TYPE_ENGINE
  577. if(cvar->description != cvar_dummy_description)
  578. ret |= 16; // CVAR_TYPE_HASDESCRIPTION
  579. if(cvar->flags & CVAR_READONLY)
  580. ret |= 32; // CVAR_TYPE_READONLY
  581. PRVM_G_FLOAT(OFS_RETURN) = ret;
  582. }
  583. /*
  584. =================
  585. VM_cvar_string
  586. const string VM_cvar_string (string, ...)
  587. =================
  588. */
  589. void VM_cvar_string(prvm_prog_t *prog)
  590. {
  591. char string[VM_STRINGTEMP_LENGTH];
  592. VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar_string);
  593. VM_VarString(prog, 0, string, sizeof(string));
  594. VM_CheckEmptyString(prog, string);
  595. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, PRVM_Cvar_ReadOk(string) ? Cvar_VariableString(string) : "");
  596. }
  597. /*
  598. ========================
  599. VM_cvar_defstring
  600. const string VM_cvar_defstring (string, ...)
  601. ========================
  602. */
  603. void VM_cvar_defstring(prvm_prog_t *prog)
  604. {
  605. char string[VM_STRINGTEMP_LENGTH];
  606. VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar_defstring);
  607. VM_VarString(prog, 0, string, sizeof(string));
  608. VM_CheckEmptyString(prog, string);
  609. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, Cvar_VariableDefString(string));
  610. }
  611. /*
  612. ========================
  613. VM_cvar_defstring
  614. const string VM_cvar_description (string, ...)
  615. ========================
  616. */
  617. void VM_cvar_description(prvm_prog_t *prog)
  618. {
  619. char string[VM_STRINGTEMP_LENGTH];
  620. VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar_description);
  621. VM_VarString(prog, 0, string, sizeof(string));
  622. VM_CheckEmptyString(prog, string);
  623. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, Cvar_VariableDescription(string));
  624. }
  625. /*
  626. =================
  627. VM_cvar_set
  628. void cvar_set (string,string, ...)
  629. =================
  630. */
  631. void VM_cvar_set(prvm_prog_t *prog)
  632. {
  633. const char *name;
  634. char string[VM_STRINGTEMP_LENGTH];
  635. VM_SAFEPARMCOUNTRANGE(2,8,VM_cvar_set);
  636. VM_VarString(prog, 1, string, sizeof(string));
  637. name = PRVM_G_STRING(OFS_PARM0);
  638. VM_CheckEmptyString(prog, name);
  639. Cvar_Set(name, string);
  640. }
  641. /*
  642. =========
  643. VM_dprint
  644. dprint(...[string])
  645. =========
  646. */
  647. void VM_dprint(prvm_prog_t *prog)
  648. {
  649. char string[VM_STRINGTEMP_LENGTH];
  650. VM_SAFEPARMCOUNTRANGE(1, 8, VM_dprint);
  651. VM_VarString(prog, 0, string, sizeof(string));
  652. #if 1
  653. Con_DPrintf("%s", string);
  654. #else
  655. Con_DPrintf("%s: %s", prog->name, string);
  656. #endif
  657. }
  658. /*
  659. =========
  660. VM_ftos
  661. string ftos(float)
  662. =========
  663. */
  664. void VM_ftos(prvm_prog_t *prog)
  665. {
  666. prvm_vec_t v;
  667. char s[128];
  668. VM_SAFEPARMCOUNT(1, VM_ftos);
  669. v = PRVM_G_FLOAT(OFS_PARM0);
  670. if ((prvm_vec_t)((prvm_int_t)v) == v)
  671. dpsnprintf(s, sizeof(s), "%.0f", v);
  672. else
  673. dpsnprintf(s, sizeof(s), "%f", v);
  674. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s);
  675. }
  676. /*
  677. =========
  678. VM_fabs
  679. float fabs(float)
  680. =========
  681. */
  682. void VM_fabs(prvm_prog_t *prog)
  683. {
  684. prvm_vec_t v;
  685. VM_SAFEPARMCOUNT(1,VM_fabs);
  686. v = PRVM_G_FLOAT(OFS_PARM0);
  687. PRVM_G_FLOAT(OFS_RETURN) = fabs(v);
  688. }
  689. /*
  690. =========
  691. VM_vtos
  692. string vtos(vector)
  693. =========
  694. */
  695. void VM_vtos(prvm_prog_t *prog)
  696. {
  697. char s[512];
  698. VM_SAFEPARMCOUNT(1,VM_vtos);
  699. dpsnprintf (s, sizeof(s), "'%5.1f %5.1f %5.1f'", PRVM_G_VECTOR(OFS_PARM0)[0], PRVM_G_VECTOR(OFS_PARM0)[1], PRVM_G_VECTOR(OFS_PARM0)[2]);
  700. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s);
  701. }
  702. /*
  703. =========
  704. VM_etos
  705. string etos(entity)
  706. =========
  707. */
  708. void VM_etos(prvm_prog_t *prog)
  709. {
  710. char s[128];
  711. VM_SAFEPARMCOUNT(1, VM_etos);
  712. dpsnprintf (s, sizeof(s), "entity %i", PRVM_G_EDICTNUM(OFS_PARM0));
  713. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s);
  714. }
  715. /*
  716. =========
  717. VM_stof
  718. float stof(...[string])
  719. =========
  720. */
  721. void VM_stof(prvm_prog_t *prog)
  722. {
  723. char string[VM_STRINGTEMP_LENGTH];
  724. VM_SAFEPARMCOUNTRANGE(1, 8, VM_stof);
  725. VM_VarString(prog, 0, string, sizeof(string));
  726. PRVM_G_FLOAT(OFS_RETURN) = atof(string);
  727. }
  728. /*
  729. ========================
  730. VM_itof
  731. float itof(int ent)
  732. ========================
  733. */
  734. void VM_itof(prvm_prog_t *prog)
  735. {
  736. VM_SAFEPARMCOUNT(1, VM_itof);
  737. PRVM_G_FLOAT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
  738. }
  739. /*
  740. ========================
  741. VM_ftoe
  742. entity ftoe(float num)
  743. ========================
  744. */
  745. void VM_ftoe(prvm_prog_t *prog)
  746. {
  747. prvm_int_t ent;
  748. VM_SAFEPARMCOUNT(1, VM_ftoe);
  749. ent = (prvm_int_t)PRVM_G_FLOAT(OFS_PARM0);
  750. if (ent < 0 || ent >= prog->max_edicts || PRVM_PROG_TO_EDICT(ent)->priv.required->free)
  751. ent = 0; // return world instead of a free or invalid entity
  752. PRVM_G_INT(OFS_RETURN) = ent;
  753. }
  754. /*
  755. ========================
  756. VM_etof
  757. float etof(entity ent)
  758. ========================
  759. */
  760. void VM_etof(prvm_prog_t *prog)
  761. {
  762. VM_SAFEPARMCOUNT(1, VM_etof);
  763. PRVM_G_FLOAT(OFS_RETURN) = PRVM_G_EDICTNUM(OFS_PARM0);
  764. }
  765. /*
  766. =========
  767. VM_strftime
  768. string strftime(float uselocaltime, string[, string ...])
  769. =========
  770. */
  771. void VM_strftime(prvm_prog_t *prog)
  772. {
  773. time_t t;
  774. #if _MSC_VER >= 1400
  775. struct tm tm;
  776. int tmresult;
  777. #else
  778. struct tm *tm;
  779. #endif
  780. char fmt[VM_STRINGTEMP_LENGTH];
  781. char result[VM_STRINGTEMP_LENGTH];
  782. VM_SAFEPARMCOUNTRANGE(2, 8, VM_strftime);
  783. VM_VarString(prog, 1, fmt, sizeof(fmt));
  784. t = time(NULL);
  785. #if _MSC_VER >= 1400
  786. if (PRVM_G_FLOAT(OFS_PARM0))
  787. tmresult = localtime_s(&tm, &t);
  788. else
  789. tmresult = gmtime_s(&tm, &t);
  790. if (!tmresult)
  791. #else
  792. if (PRVM_G_FLOAT(OFS_PARM0))
  793. tm = localtime(&t);
  794. else
  795. tm = gmtime(&t);
  796. if (!tm)
  797. #endif
  798. {
  799. PRVM_G_INT(OFS_RETURN) = 0;
  800. return;
  801. }
  802. #if _MSC_VER >= 1400
  803. strftime(result, sizeof(result), fmt, &tm);
  804. #else
  805. strftime(result, sizeof(result), fmt, tm);
  806. #endif
  807. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, result);
  808. }
  809. /*
  810. =========
  811. VM_spawn
  812. entity spawn()
  813. =========
  814. */
  815. void VM_spawn(prvm_prog_t *prog)
  816. {
  817. prvm_edict_t *ed;
  818. VM_SAFEPARMCOUNT(0, VM_spawn);
  819. prog->xfunction->builtinsprofile += 20;
  820. ed = PRVM_ED_Alloc(prog);
  821. VM_RETURN_EDICT(ed);
  822. }
  823. /*
  824. =========
  825. VM_remove
  826. remove(entity e)
  827. =========
  828. */
  829. void VM_remove(prvm_prog_t *prog)
  830. {
  831. prvm_edict_t *ed;
  832. prog->xfunction->builtinsprofile += 20;
  833. VM_SAFEPARMCOUNT(1, VM_remove);
  834. ed = PRVM_G_EDICT(OFS_PARM0);
  835. if( PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
  836. {
  837. if (developer.integer > 0)
  838. VM_Warning(prog, "VM_remove: tried to remove the null entity or a reserved entity!\n" );
  839. }
  840. else if( ed->priv.required->free )
  841. {
  842. if (developer.integer > 0)
  843. VM_Warning(prog, "VM_remove: tried to remove an already freed entity!\n" );
  844. }
  845. else
  846. PRVM_ED_Free (prog, ed);
  847. }
  848. /*
  849. =========
  850. VM_find
  851. entity find(entity start, .string field, string match)
  852. =========
  853. */
  854. void VM_find(prvm_prog_t *prog)
  855. {
  856. int e;
  857. int f;
  858. const char *s, *t;
  859. prvm_edict_t *ed;
  860. VM_SAFEPARMCOUNT(3,VM_find);
  861. e = PRVM_G_EDICTNUM(OFS_PARM0);
  862. f = PRVM_G_INT(OFS_PARM1);
  863. s = PRVM_G_STRING(OFS_PARM2);
  864. // LordHavoc: apparently BloodMage does a find(world, weaponmodel, "") and
  865. // expects it to find all the monsters, so we must be careful to support
  866. // searching for ""
  867. for (e++ ; e < prog->num_edicts ; e++)
  868. {
  869. prog->xfunction->builtinsprofile++;
  870. ed = PRVM_EDICT_NUM(e);
  871. if (ed->priv.required->free)
  872. continue;
  873. t = PRVM_E_STRING(ed,f);
  874. if (!t)
  875. t = "";
  876. if (!strcmp(t,s))
  877. {
  878. VM_RETURN_EDICT(ed);
  879. return;
  880. }
  881. }
  882. VM_RETURN_EDICT(prog->edicts);
  883. }
  884. /*
  885. =========
  886. VM_findfloat
  887. entity findfloat(entity start, .float field, float match)
  888. entity findentity(entity start, .entity field, entity match)
  889. =========
  890. */
  891. // LordHavoc: added this for searching float, int, and entity reference fields
  892. void VM_findfloat(prvm_prog_t *prog)
  893. {
  894. int e;
  895. int f;
  896. float s;
  897. prvm_edict_t *ed;
  898. VM_SAFEPARMCOUNT(3,VM_findfloat);
  899. e = PRVM_G_EDICTNUM(OFS_PARM0);
  900. f = PRVM_G_INT(OFS_PARM1);
  901. s = PRVM_G_FLOAT(OFS_PARM2);
  902. for (e++ ; e < prog->num_edicts ; e++)
  903. {
  904. prog->xfunction->builtinsprofile++;
  905. ed = PRVM_EDICT_NUM(e);
  906. if (ed->priv.required->free)
  907. continue;
  908. if (PRVM_E_FLOAT(ed,f) == s)
  909. {
  910. VM_RETURN_EDICT(ed);
  911. return;
  912. }
  913. }
  914. VM_RETURN_EDICT(prog->edicts);
  915. }
  916. /*
  917. =========
  918. VM_findchain
  919. entity findchain(.string field, string match)
  920. =========
  921. */
  922. // chained search for strings in entity fields
  923. // entity(.string field, string match) findchain = #402;
  924. void VM_findchain(prvm_prog_t *prog)
  925. {
  926. int i;
  927. int f;
  928. const char *s, *t;
  929. prvm_edict_t *ent, *chain;
  930. int chainfield;
  931. VM_SAFEPARMCOUNTRANGE(2,3,VM_findchain);
  932. if(prog->argc == 3)
  933. chainfield = PRVM_G_INT(OFS_PARM2);
  934. else
  935. chainfield = prog->fieldoffsets.chain;
  936. if (chainfield < 0)
  937. prog->error_cmd("VM_findchain: %s doesnt have the specified chain field !", prog->name);
  938. chain = prog->edicts;
  939. f = PRVM_G_INT(OFS_PARM0);
  940. s = PRVM_G_STRING(OFS_PARM1);
  941. // LordHavoc: apparently BloodMage does a find(world, weaponmodel, "") and
  942. // expects it to find all the monsters, so we must be careful to support
  943. // searching for ""
  944. ent = PRVM_NEXT_EDICT(prog->edicts);
  945. for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
  946. {
  947. prog->xfunction->builtinsprofile++;
  948. if (ent->priv.required->free)
  949. continue;
  950. t = PRVM_E_STRING(ent,f);
  951. if (!t)
  952. t = "";
  953. if (strcmp(t,s))
  954. continue;
  955. PRVM_EDICTFIELDEDICT(ent,chainfield) = PRVM_NUM_FOR_EDICT(chain);
  956. chain = ent;
  957. }
  958. VM_RETURN_EDICT(chain);
  959. }
  960. /*
  961. =========
  962. VM_findchainfloat
  963. entity findchainfloat(.string field, float match)
  964. entity findchainentity(.string field, entity match)
  965. =========
  966. */
  967. // LordHavoc: chained search for float, int, and entity reference fields
  968. // entity(.string field, float match) findchainfloat = #403;
  969. void VM_findchainfloat(prvm_prog_t *prog)
  970. {
  971. int i;
  972. int f;
  973. float s;
  974. prvm_edict_t *ent, *chain;
  975. int chainfield;
  976. VM_SAFEPARMCOUNTRANGE(2, 3, VM_findchainfloat);
  977. if(prog->argc == 3)
  978. chainfield = PRVM_G_INT(OFS_PARM2);
  979. else
  980. chainfield = prog->fieldoffsets.chain;
  981. if (chainfield < 0)
  982. prog->error_cmd("VM_findchain: %s doesnt have the specified chain field !", prog->name);
  983. chain = (prvm_edict_t *)prog->edicts;
  984. f = PRVM_G_INT(OFS_PARM0);
  985. s = PRVM_G_FLOAT(OFS_PARM1);
  986. ent = PRVM_NEXT_EDICT(prog->edicts);
  987. for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
  988. {
  989. prog->xfunction->builtinsprofile++;
  990. if (ent->priv.required->free)
  991. continue;
  992. if (PRVM_E_FLOAT(ent,f) != s)
  993. continue;
  994. PRVM_EDICTFIELDEDICT(ent,chainfield) = PRVM_EDICT_TO_PROG(chain);
  995. chain = ent;
  996. }
  997. VM_RETURN_EDICT(chain);
  998. }
  999. /*
  1000. ========================
  1001. VM_findflags
  1002. entity findflags(entity start, .float field, float match)
  1003. ========================
  1004. */
  1005. // LordHavoc: search for flags in float fields
  1006. void VM_findflags(prvm_prog_t *prog)
  1007. {
  1008. prvm_int_t e;
  1009. prvm_int_t f;
  1010. prvm_int_t s;
  1011. prvm_edict_t *ed;
  1012. VM_SAFEPARMCOUNT(3, VM_findflags);
  1013. e = PRVM_G_EDICTNUM(OFS_PARM0);
  1014. f = PRVM_G_INT(OFS_PARM1);
  1015. s = (prvm_int_t)PRVM_G_FLOAT(OFS_PARM2);
  1016. for (e++ ; e < prog->num_edicts ; e++)
  1017. {
  1018. prog->xfunction->builtinsprofile++;
  1019. ed = PRVM_EDICT_NUM(e);
  1020. if (ed->priv.required->free)
  1021. continue;
  1022. if (!PRVM_E_FLOAT(ed,f))
  1023. continue;
  1024. if ((prvm_int_t)PRVM_E_FLOAT(ed,f) & s)
  1025. {
  1026. VM_RETURN_EDICT(ed);
  1027. return;
  1028. }
  1029. }
  1030. VM_RETURN_EDICT(prog->edicts);
  1031. }
  1032. /*
  1033. ========================
  1034. VM_findchainflags
  1035. entity findchainflags(.float field, float match)
  1036. ========================
  1037. */
  1038. // LordHavoc: chained search for flags in float fields
  1039. void VM_findchainflags(prvm_prog_t *prog)
  1040. {
  1041. prvm_int_t i;
  1042. prvm_int_t f;
  1043. prvm_int_t s;
  1044. prvm_edict_t *ent, *chain;
  1045. int chainfield;
  1046. VM_SAFEPARMCOUNTRANGE(2, 3, VM_findchainflags);
  1047. if(prog->argc == 3)
  1048. chainfield = PRVM_G_INT(OFS_PARM2);
  1049. else
  1050. chainfield = prog->fieldoffsets.chain;
  1051. if (chainfield < 0)
  1052. prog->error_cmd("VM_findchain: %s doesnt have the specified chain field !", prog->name);
  1053. chain = (prvm_edict_t *)prog->edicts;
  1054. f = PRVM_G_INT(OFS_PARM0);
  1055. s = (prvm_int_t)PRVM_G_FLOAT(OFS_PARM1);
  1056. ent = PRVM_NEXT_EDICT(prog->edicts);
  1057. for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
  1058. {
  1059. prog->xfunction->builtinsprofile++;
  1060. if (ent->priv.required->free)
  1061. continue;
  1062. if (!PRVM_E_FLOAT(ent,f))
  1063. continue;
  1064. if (!((prvm_int_t)PRVM_E_FLOAT(ent,f) & s))
  1065. continue;
  1066. PRVM_EDICTFIELDEDICT(ent,chainfield) = PRVM_EDICT_TO_PROG(chain);
  1067. chain = ent;
  1068. }
  1069. VM_RETURN_EDICT(chain);
  1070. }
  1071. /*
  1072. =========
  1073. VM_precache_sound
  1074. string precache_sound (string sample)
  1075. =========
  1076. */
  1077. void VM_precache_sound(prvm_prog_t *prog)
  1078. {
  1079. const char *s;
  1080. VM_SAFEPARMCOUNT(1, VM_precache_sound);
  1081. s = PRVM_G_STRING(OFS_PARM0);
  1082. PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
  1083. //VM_CheckEmptyString(prog, s);
  1084. if(snd_initialized.integer && !S_PrecacheSound(s, true, true))
  1085. {
  1086. VM_Warning(prog, "VM_precache_sound: Failed to load %s for %s\n", s, prog->name);
  1087. return;
  1088. }
  1089. }
  1090. /*
  1091. =================
  1092. VM_precache_file
  1093. returns the same string as output
  1094. does nothing, only used by qcc to build .pak archives
  1095. =================
  1096. */
  1097. void VM_precache_file(prvm_prog_t *prog)
  1098. {
  1099. VM_SAFEPARMCOUNT(1,VM_precache_file);
  1100. // precache_file is only used to copy files with qcc, it does nothing
  1101. PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
  1102. }
  1103. /*
  1104. =========
  1105. VM_coredump
  1106. coredump()
  1107. =========
  1108. */
  1109. void VM_coredump(prvm_prog_t *prog)
  1110. {
  1111. VM_SAFEPARMCOUNT(0,VM_coredump);
  1112. Cbuf_AddText("prvm_edicts ");
  1113. Cbuf_AddText(prog->name);
  1114. Cbuf_AddText("\n");
  1115. }
  1116. /*
  1117. =========
  1118. VM_stackdump
  1119. stackdump()
  1120. =========
  1121. */
  1122. void VM_stackdump(prvm_prog_t *prog)
  1123. {
  1124. VM_SAFEPARMCOUNT(0, VM_stackdump);
  1125. PRVM_StackTrace(prog);
  1126. }
  1127. /*
  1128. =========
  1129. VM_crash
  1130. crash()
  1131. =========
  1132. */
  1133. void VM_crash(prvm_prog_t *prog)
  1134. {
  1135. VM_SAFEPARMCOUNT(0, VM_crash);
  1136. prog->error_cmd("Crash called by %s",prog->name);
  1137. }
  1138. /*
  1139. =========
  1140. VM_traceon
  1141. traceon()
  1142. =========
  1143. */
  1144. void VM_traceon(prvm_prog_t *prog)
  1145. {
  1146. VM_SAFEPARMCOUNT(0,VM_traceon);
  1147. prog->trace = true;
  1148. }
  1149. /*
  1150. =========
  1151. VM_traceoff
  1152. traceoff()
  1153. =========
  1154. */
  1155. void VM_traceoff(prvm_prog_t *prog)
  1156. {
  1157. VM_SAFEPARMCOUNT(0,VM_traceoff);
  1158. prog->trace = false;
  1159. }
  1160. /*
  1161. =========
  1162. VM_eprint
  1163. eprint(entity e)
  1164. =========
  1165. */
  1166. void VM_eprint(prvm_prog_t *prog)
  1167. {
  1168. VM_SAFEPARMCOUNT(1,VM_eprint);
  1169. PRVM_ED_PrintNum (prog, PRVM_G_EDICTNUM(OFS_PARM0), NULL);
  1170. }
  1171. /*
  1172. =========
  1173. VM_rint
  1174. float rint(float)
  1175. =========
  1176. */
  1177. void VM_rint(prvm_prog_t *prog)
  1178. {
  1179. prvm_vec_t f;
  1180. VM_SAFEPARMCOUNT(1,VM_rint);
  1181. f = PRVM_G_FLOAT(OFS_PARM0);
  1182. if (f > 0)
  1183. PRVM_G_FLOAT(OFS_RETURN) = floor(f + 0.5);
  1184. else
  1185. PRVM_G_FLOAT(OFS_RETURN) = ceil(f - 0.5);
  1186. }
  1187. /*
  1188. =========
  1189. VM_floor
  1190. float floor(float)
  1191. =========
  1192. */
  1193. void VM_floor(prvm_prog_t *prog)
  1194. {
  1195. VM_SAFEPARMCOUNT(1,VM_floor);
  1196. PRVM_G_FLOAT(OFS_RETURN) = floor(PRVM_G_FLOAT(OFS_PARM0));
  1197. }
  1198. /*
  1199. =========
  1200. VM_ceil
  1201. float ceil(float)
  1202. =========
  1203. */
  1204. void VM_ceil(prvm_prog_t *prog)
  1205. {
  1206. VM_SAFEPARMCOUNT(1,VM_ceil);
  1207. PRVM_G_FLOAT(OFS_RETURN) = ceil(PRVM_G_FLOAT(OFS_PARM0));
  1208. }
  1209. /*
  1210. =============
  1211. VM_nextent
  1212. entity nextent(entity)
  1213. =============
  1214. */
  1215. void VM_nextent(prvm_prog_t *prog)
  1216. {
  1217. int i;
  1218. prvm_edict_t *ent;
  1219. VM_SAFEPARMCOUNT(1, VM_nextent);
  1220. i = PRVM_G_EDICTNUM(OFS_PARM0);
  1221. while (1)
  1222. {
  1223. prog->xfunction->builtinsprofile++;
  1224. i++;
  1225. if (i == prog->num_edicts)
  1226. {
  1227. VM_RETURN_EDICT(prog->edicts);
  1228. return;
  1229. }
  1230. ent = PRVM_EDICT_NUM(i);
  1231. if (!ent->priv.required->free)
  1232. {
  1233. VM_RETURN_EDICT(ent);
  1234. return;
  1235. }
  1236. }
  1237. }
  1238. //=============================================================================
  1239. /*
  1240. ==============
  1241. VM_changelevel
  1242. server and menu
  1243. changelevel(string map)
  1244. ==============
  1245. */
  1246. void VM_changelevel(prvm_prog_t *prog)
  1247. {
  1248. char vabuf[1024];
  1249. VM_SAFEPARMCOUNT(1, VM_changelevel);
  1250. if(!sv.active)
  1251. {
  1252. VM_Warning(prog, "VM_changelevel: game is not server (%s)\n", prog->name);
  1253. return;
  1254. }
  1255. // make sure we don't issue two changelevels
  1256. if (svs.changelevel_issued)
  1257. return;
  1258. svs.changelevel_issued = true;
  1259. Cbuf_AddText(va(vabuf, sizeof(vabuf), "changelevel %s\n",PRVM_G_STRING(OFS_PARM0)));
  1260. }
  1261. /*
  1262. =========
  1263. VM_sin
  1264. float sin(float)
  1265. =========
  1266. */
  1267. void VM_sin(prvm_prog_t *prog)
  1268. {
  1269. VM_SAFEPARMCOUNT(1,VM_sin);
  1270. PRVM_G_FLOAT(OFS_RETURN) = sin(PRVM_G_FLOAT(OFS_PARM0));
  1271. }
  1272. /*
  1273. =========
  1274. VM_cos
  1275. float cos(float)
  1276. =========
  1277. */
  1278. void VM_cos(prvm_prog_t *prog)
  1279. {
  1280. VM_SAFEPARMCOUNT(1,VM_cos);
  1281. PRVM_G_FLOAT(OFS_RETURN) = cos(PRVM_G_FLOAT(OFS_PARM0));
  1282. }
  1283. /*
  1284. =========
  1285. VM_sqrt
  1286. float sqrt(float)
  1287. =========
  1288. */
  1289. void VM_sqrt(prvm_prog_t *prog)
  1290. {
  1291. VM_SAFEPARMCOUNT(1,VM_sqrt);
  1292. PRVM_G_FLOAT(OFS_RETURN) = sqrt(PRVM_G_FLOAT(OFS_PARM0));
  1293. }
  1294. /*
  1295. =========
  1296. VM_asin
  1297. float asin(float)
  1298. =========
  1299. */
  1300. void VM_asin(prvm_prog_t *prog)
  1301. {
  1302. VM_SAFEPARMCOUNT(1,VM_asin);
  1303. PRVM_G_FLOAT(OFS_RETURN) = asin(PRVM_G_FLOAT(OFS_PARM0));
  1304. }
  1305. /*
  1306. =========
  1307. VM_acos
  1308. float acos(float)
  1309. =========
  1310. */
  1311. void VM_acos(prvm_prog_t *prog)
  1312. {
  1313. VM_SAFEPARMCOUNT(1,VM_acos);
  1314. PRVM_G_FLOAT(OFS_RETURN) = acos(PRVM_G_FLOAT(OFS_PARM0));
  1315. }
  1316. /*
  1317. =========
  1318. VM_atan
  1319. float atan(float)
  1320. =========
  1321. */
  1322. void VM_atan(prvm_prog_t *prog)
  1323. {
  1324. VM_SAFEPARMCOUNT(1,VM_atan);
  1325. PRVM_G_FLOAT(OFS_RETURN) = atan(PRVM_G_FLOAT(OFS_PARM0));
  1326. }
  1327. /*
  1328. =========
  1329. VM_atan2
  1330. float atan2(float,float)
  1331. =========
  1332. */
  1333. void VM_atan2(prvm_prog_t *prog)
  1334. {
  1335. VM_SAFEPARMCOUNT(2,VM_atan2);
  1336. PRVM_G_FLOAT(OFS_RETURN) = atan2(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
  1337. }
  1338. /*
  1339. =========
  1340. VM_tan
  1341. float tan(float)
  1342. =========
  1343. */
  1344. void VM_tan(prvm_prog_t *prog)
  1345. {
  1346. VM_SAFEPARMCOUNT(1,VM_tan);
  1347. PRVM_G_FLOAT(OFS_RETURN) = tan(PRVM_G_FLOAT(OFS_PARM0));
  1348. }
  1349. /*
  1350. =================
  1351. VM_randomvec
  1352. Returns a vector of length < 1 and > 0
  1353. vector randomvec()
  1354. =================
  1355. */
  1356. void VM_randomvec(prvm_prog_t *prog)
  1357. {
  1358. vec3_t temp;
  1359. VM_SAFEPARMCOUNT(0, VM_randomvec);
  1360. VectorRandom(temp);
  1361. VectorCopy(temp, PRVM_G_VECTOR(OFS_RETURN));
  1362. }
  1363. //=============================================================================
  1364. /*
  1365. =========
  1366. VM_registercvar
  1367. float registercvar (string name, string value[, float flags])
  1368. =========
  1369. */
  1370. void VM_registercvar(prvm_prog_t *prog)
  1371. {
  1372. const char *name, *value;
  1373. int flags;
  1374. VM_SAFEPARMCOUNTRANGE(2, 3, VM_registercvar);
  1375. name = PRVM_G_STRING(OFS_PARM0);
  1376. value = PRVM_G_STRING(OFS_PARM1);
  1377. flags = prog->argc >= 3 ? (int)PRVM_G_FLOAT(OFS_PARM2) : 0;
  1378. PRVM_G_FLOAT(OFS_RETURN) = 0;
  1379. if(flags > CVAR_MAXFLAGSVAL)
  1380. return;
  1381. // first check to see if it has already been defined
  1382. if (Cvar_FindVar (name))
  1383. return;
  1384. // check for overlap with a command
  1385. if (Cmd_Exists (name))
  1386. {
  1387. VM_Warning(prog, "VM_registercvar: %s is a command\n", name);
  1388. return;
  1389. }
  1390. Cvar_Get(name, value, flags, NULL);
  1391. PRVM_G_FLOAT(OFS_RETURN) = 1; // success
  1392. }
  1393. /*
  1394. =================
  1395. VM_min
  1396. returns the minimum of two supplied floats
  1397. float min(float a, float b, ...[float])
  1398. =================
  1399. */
  1400. void VM_min(prvm_prog_t *prog)
  1401. {
  1402. VM_SAFEPARMCOUNTRANGE(2, 8, VM_min);
  1403. // LordHavoc: 3+ argument enhancement suggested by FrikaC
  1404. if (prog->argc >= 3)
  1405. {
  1406. int i;
  1407. float f = PRVM_G_FLOAT(OFS_PARM0);
  1408. for (i = 1;i < prog->argc;i++)
  1409. if (f > PRVM_G_FLOAT((OFS_PARM0+i*3)))
  1410. f = PRVM_G_FLOAT((OFS_PARM0+i*3));
  1411. PRVM_G_FLOAT(OFS_RETURN) = f;
  1412. }
  1413. else
  1414. PRVM_G_FLOAT(OFS_RETURN) = min(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
  1415. }
  1416. /*
  1417. =================
  1418. VM_max
  1419. returns the maximum of two supplied floats
  1420. float max(float a, float b, ...[float])
  1421. =================
  1422. */
  1423. void VM_max(prvm_prog_t *prog)
  1424. {
  1425. VM_SAFEPARMCOUNTRANGE(2, 8, VM_max);
  1426. // LordHavoc: 3+ argument enhancement suggested by FrikaC
  1427. if (prog->argc >= 3)
  1428. {
  1429. int i;
  1430. float f = PRVM_G_FLOAT(OFS_PARM0);
  1431. for (i = 1;i < prog->argc;i++)
  1432. if (f < PRVM_G_FLOAT((OFS_PARM0+i*3)))
  1433. f = PRVM_G_FLOAT((OFS_PARM0+i*3));
  1434. PRVM_G_FLOAT(OFS_RETURN) = f;
  1435. }
  1436. else
  1437. PRVM_G_FLOAT(OFS_RETURN) = max(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
  1438. }
  1439. /*
  1440. =================
  1441. VM_bound
  1442. returns number bounded by supplied range
  1443. float bound(float min, float value, float max)
  1444. =================
  1445. */
  1446. void VM_bound(prvm_prog_t *prog)
  1447. {
  1448. VM_SAFEPARMCOUNT(3,VM_bound);
  1449. PRVM_G_FLOAT(OFS_RETURN) = bound(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1), PRVM_G_FLOAT(OFS_PARM2));
  1450. }
  1451. /*
  1452. =================
  1453. VM_pow
  1454. returns a raised to power b
  1455. float pow(float a, float b)
  1456. =================
  1457. */
  1458. void VM_pow(prvm_prog_t *prog)
  1459. {
  1460. VM_SAFEPARMCOUNT(2,VM_pow);
  1461. PRVM_G_FLOAT(OFS_RETURN) = pow(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
  1462. }
  1463. void VM_log(prvm_prog_t *prog)
  1464. {
  1465. VM_SAFEPARMCOUNT(1,VM_log);
  1466. PRVM_G_FLOAT(OFS_RETURN) = log(PRVM_G_FLOAT(OFS_PARM0));
  1467. }
  1468. void VM_Files_Init(prvm_prog_t *prog)
  1469. {
  1470. int i;
  1471. for (i = 0;i < PRVM_MAX_OPENFILES;i++)
  1472. prog->openfiles[i] = NULL;
  1473. }
  1474. void VM_Files_CloseAll(prvm_prog_t *prog)
  1475. {
  1476. int i;
  1477. for (i = 0;i < PRVM_MAX_OPENFILES;i++)
  1478. {
  1479. if (prog->openfiles[i])
  1480. FS_Close(prog->openfiles[i]);
  1481. prog->openfiles[i] = NULL;
  1482. }
  1483. }
  1484. static qfile_t *VM_GetFileHandle(prvm_prog_t *prog, int index)
  1485. {
  1486. if (index < 0 || index >= PRVM_MAX_OPENFILES)
  1487. {
  1488. Con_Printf("VM_GetFileHandle: invalid file handle %i used in %s\n", index, prog->name);
  1489. return NULL;
  1490. }
  1491. if (prog->openfiles[index] == NULL)
  1492. {
  1493. Con_Printf("VM_GetFileHandle: no such file handle %i (or file has been closed) in %s\n", index, prog->name);
  1494. return NULL;
  1495. }
  1496. return prog->openfiles[index];
  1497. }
  1498. /*
  1499. =========
  1500. VM_fopen
  1501. float fopen(string filename, float mode)
  1502. =========
  1503. */
  1504. // float(string filename, float mode) fopen = #110;
  1505. // opens a file inside quake/gamedir/data/ (mode is FILE_READ, FILE_APPEND, or FILE_WRITE),
  1506. // returns fhandle >= 0 if successful, or fhandle < 0 if unable to open file for any reason
  1507. void VM_fopen(prvm_prog_t *prog)
  1508. {
  1509. int filenum, mode;
  1510. const char *modestring, *filename;
  1511. char vabuf[1024];
  1512. VM_SAFEPARMCOUNT(2,VM_fopen);
  1513. for (filenum = 0;filenum < PRVM_MAX_OPENFILES;filenum++)
  1514. if (prog->openfiles[filenum] == NULL)
  1515. break;
  1516. if (filenum >= PRVM_MAX_OPENFILES)
  1517. {
  1518. PRVM_G_FLOAT(OFS_RETURN) = -2;
  1519. VM_Warning(prog, "VM_fopen: %s ran out of file handles (%i)\n", prog->name, PRVM_MAX_OPENFILES);
  1520. return;
  1521. }
  1522. filename = PRVM_G_STRING(OFS_PARM0);
  1523. mode = (int)PRVM_G_FLOAT(OFS_PARM1);
  1524. switch(mode)
  1525. {
  1526. case 0: // FILE_READ
  1527. modestring = "rb";
  1528. prog->openfiles[filenum] = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "data/%s", filename), false);
  1529. if (prog->openfiles[filenum] == NULL)
  1530. prog->openfiles[filenum] = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "%s", filename), false);
  1531. break;
  1532. case 1: // FILE_APPEND
  1533. modestring = "a";
  1534. prog->openfiles[filenum] = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "data/%s", filename), modestring, false);
  1535. break;
  1536. case 2: // FILE_WRITE
  1537. modestring = "w";
  1538. prog->openfiles[filenum] = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "data/%s", filename), modestring, false);
  1539. break;
  1540. default:
  1541. PRVM_G_FLOAT(OFS_RETURN) = -3;
  1542. VM_Warning(prog, "VM_fopen: %s: no such mode %i (valid: 0 = read, 1 = append, 2 = write)\n", prog->name, mode);
  1543. return;
  1544. }
  1545. if (prog->openfiles[filenum] == NULL)
  1546. {
  1547. PRVM_G_FLOAT(OFS_RETURN) = -1;
  1548. if (developer_extra.integer)
  1549. VM_Warning(prog, "VM_fopen: %s: %s mode %s failed\n", prog->name, filename, modestring);
  1550. }
  1551. else
  1552. {
  1553. PRVM_G_FLOAT(OFS_RETURN) = filenum;
  1554. if (developer_extra.integer)
  1555. Con_DPrintf("VM_fopen: %s: %s mode %s opened as #%i\n", prog->name, filename, modestring, filenum);
  1556. prog->openfiles_origin[filenum] = PRVM_AllocationOrigin(prog);
  1557. }
  1558. }
  1559. /*
  1560. =========
  1561. VM_fclose
  1562. fclose(float fhandle)
  1563. =========
  1564. */
  1565. //void(float fhandle) fclose = #111; // closes a file
  1566. void VM_fclose(prvm_prog_t *prog)
  1567. {
  1568. int filenum;
  1569. VM_SAFEPARMCOUNT(1,VM_fclose);
  1570. filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
  1571. if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
  1572. {
  1573. VM_Warning(prog, "VM_fclose: invalid file handle %i used in %s\n", filenum, prog->name);
  1574. return;
  1575. }
  1576. if (prog->openfiles[filenum] == NULL)
  1577. {
  1578. VM_Warning(prog, "VM_fclose: no such file handle %i (or file has been closed) in %s\n", filenum, prog->name);
  1579. return;
  1580. }
  1581. FS_Close(prog->openfiles[filenum]);
  1582. prog->openfiles[filenum] = NULL;
  1583. if(prog->openfiles_origin[filenum])
  1584. PRVM_Free((char *)prog->openfiles_origin[filenum]);
  1585. if (developer_extra.integer)
  1586. Con_DPrintf("VM_fclose: %s: #%i closed\n", prog->name, filenum);
  1587. }
  1588. /*
  1589. =========
  1590. VM_fgets
  1591. string fgets(float fhandle)
  1592. =========
  1593. */
  1594. //string(float fhandle) fgets = #112; // reads a line of text from the file and returns as a tempstring
  1595. void VM_fgets(prvm_prog_t *prog)
  1596. {
  1597. int c, end;
  1598. char string[VM_STRINGTEMP_LENGTH];
  1599. int filenum;
  1600. VM_SAFEPARMCOUNT(1,VM_fgets);
  1601. // set the return value regardless of any possible errors
  1602. PRVM_G_INT(OFS_RETURN) = OFS_NULL;
  1603. filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
  1604. if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
  1605. {
  1606. VM_Warning(prog, "VM_fgets: invalid file handle %i used in %s\n", filenum, prog->name);
  1607. return;
  1608. }
  1609. if (prog->openfiles[filenum] == NULL)
  1610. {
  1611. VM_Warning(prog, "VM_fgets: no such file handle %i (or file has been closed) in %s\n", filenum, prog->name);
  1612. return;
  1613. }
  1614. end = 0;
  1615. for (;;)
  1616. {
  1617. c = FS_Getc(prog->openfiles[filenum]);
  1618. if (c == '\r' || c == '\n' || c < 0)
  1619. break;
  1620. if (end < VM_STRINGTEMP_LENGTH - 1)
  1621. string[end++] = c;
  1622. }
  1623. string[end] = 0;
  1624. // remove \n following \r
  1625. if (c == '\r')
  1626. {
  1627. c = FS_Getc(prog->openfiles[filenum]);
  1628. if (c != '\n')
  1629. FS_UnGetc(prog->openfiles[filenum], (unsigned char)c);
  1630. }
  1631. if (developer_extra.integer)
  1632. Con_DPrintf("fgets: %s: %s\n", prog->name, string);
  1633. if (c >= 0 || end)
  1634. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
  1635. }
  1636. /*
  1637. =========
  1638. VM_fputs
  1639. fputs(float fhandle, string s)
  1640. =========
  1641. */
  1642. //void(float fhandle, string s) fputs = #113; // writes a line of text to the end of the file
  1643. void VM_fputs(prvm_prog_t *prog)
  1644. {
  1645. int stringlength;
  1646. char string[VM_STRINGTEMP_LENGTH];
  1647. int filenum;
  1648. VM_SAFEPARMCOUNT(2,VM_fputs);
  1649. filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
  1650. if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
  1651. {
  1652. VM_Warning(prog, "VM_fputs: invalid file handle %i used in %s\n", filenum, prog->name);
  1653. return;
  1654. }
  1655. if (prog->openfiles[filenum] == NULL)
  1656. {
  1657. VM_Warning(prog, "VM_fputs: no such file handle %i (or file has been closed) in %s\n", filenum, prog->name);
  1658. return;
  1659. }
  1660. VM_VarString(prog, 1, string, sizeof(string));
  1661. if ((stringlength = (int)strlen(string)))
  1662. FS_Write(prog->openfiles[filenum], string, stringlength);
  1663. if (developer_extra.integer)
  1664. Con_DPrintf("fputs: %s: %s\n", prog->name, string);
  1665. }
  1666. /*
  1667. =========
  1668. VM_writetofile
  1669. writetofile(float fhandle, entity ent)
  1670. =========
  1671. */
  1672. void VM_writetofile(prvm_prog_t *prog)
  1673. {
  1674. prvm_edict_t * ent;
  1675. qfile_t *file;
  1676. VM_SAFEPARMCOUNT(2, VM_writetofile);
  1677. file = VM_GetFileHandle(prog, (int)PRVM_G_FLOAT(OFS_PARM0));
  1678. if( !file )
  1679. {
  1680. VM_Warning(prog, "VM_writetofile: invalid or closed file handle\n");
  1681. return;
  1682. }
  1683. ent = PRVM_G_EDICT(OFS_PARM1);
  1684. if(ent->priv.required->free)
  1685. {
  1686. VM_Warning(prog, "VM_writetofile: %s: entity %i is free !\n", prog->name, PRVM_NUM_FOR_EDICT(ent));
  1687. return;
  1688. }
  1689. PRVM_ED_Write (prog, file, ent);
  1690. }
  1691. // KrimZon - DP_QC_ENTITYDATA
  1692. /*
  1693. =========
  1694. VM_numentityfields
  1695. float() numentityfields
  1696. Return the number of entity fields - NOT offsets
  1697. =========
  1698. */
  1699. void VM_numentityfields(prvm_prog_t *prog)
  1700. {
  1701. PRVM_G_FLOAT(OFS_RETURN) = prog->numfielddefs;
  1702. }
  1703. // KrimZon - DP_QC_ENTITYDATA
  1704. /*
  1705. =========
  1706. VM_entityfieldname
  1707. string(float fieldnum) entityfieldname
  1708. Return name of the specified field as a string, or empty if the field is invalid (warning)
  1709. =========
  1710. */
  1711. void VM_entityfieldname(prvm_prog_t *prog)
  1712. {
  1713. ddef_t *d;
  1714. int i = (int)PRVM_G_FLOAT(OFS_PARM0);
  1715. if (i < 0 || i >= prog->numfielddefs)
  1716. {
  1717. VM_Warning(prog, "VM_entityfieldname: %s: field index out of bounds\n", prog->name);
  1718. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
  1719. return;
  1720. }
  1721. d = &prog->fielddefs[i];
  1722. PRVM_G_INT(OFS_RETURN) = d->s_name; // presuming that s_name points to a string already
  1723. }
  1724. // KrimZon - DP_QC_ENTITYDATA
  1725. /*
  1726. =========
  1727. VM_entityfieldtype
  1728. float(float fieldnum) entityfieldtype
  1729. =========
  1730. */
  1731. void VM_entityfieldtype(prvm_prog_t *prog)
  1732. {
  1733. ddef_t *d;
  1734. int i = (int)PRVM_G_FLOAT(OFS_PARM0);
  1735. if (i < 0 || i >= prog->numfielddefs)
  1736. {
  1737. VM_Warning(prog, "VM_entityfieldtype: %s: field index out of bounds\n", prog->name);
  1738. PRVM_G_FLOAT(OFS_RETURN) = -1.0;
  1739. return;
  1740. }
  1741. d = &prog->fielddefs[i];
  1742. PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t)d->type;
  1743. }
  1744. // KrimZon - DP_QC_ENTITYDATA
  1745. /*
  1746. =========
  1747. VM_getentityfieldstring
  1748. string(float fieldnum, entity ent) getentityfieldstring
  1749. =========
  1750. */
  1751. void VM_getentityfieldstring(prvm_prog_t *prog)
  1752. {
  1753. // put the data into a string
  1754. ddef_t *d;
  1755. int type, j;
  1756. prvm_eval_t *val;
  1757. prvm_edict_t * ent;
  1758. int i = (int)PRVM_G_FLOAT(OFS_PARM0);
  1759. char valuebuf[MAX_INPUTLINE];
  1760. if (i < 0 || i >= prog->numfielddefs)
  1761. {
  1762. VM_Warning(prog, "VM_entityfielddata: %s: field index out of bounds\n", prog->name);
  1763. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
  1764. return;
  1765. }
  1766. d = &prog->fielddefs[i];
  1767. // get the entity
  1768. ent = PRVM_G_EDICT(OFS_PARM1);
  1769. if(ent->priv.required->free)
  1770. {
  1771. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
  1772. VM_Warning(prog, "VM_entityfielddata: %s: entity %i is free !\n", prog->name, PRVM_NUM_FOR_EDICT(ent));
  1773. return;
  1774. }
  1775. val = (prvm_eval_t *)(ent->fields.fp + d->ofs);
  1776. // if it's 0 or blank, return an empty string
  1777. type = d->type & ~DEF_SAVEGLOBAL;
  1778. for (j=0 ; j<prvm_type_size[type] ; j++)
  1779. if (val->ivector[j])
  1780. break;
  1781. if (j == prvm_type_size[type])
  1782. {
  1783. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
  1784. return;
  1785. }
  1786. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
  1787. }
  1788. // KrimZon - DP_QC_ENTITYDATA
  1789. /*
  1790. =========
  1791. VM_putentityfieldstring
  1792. float(float fieldnum, entity ent, string s) putentityfieldstring
  1793. =========
  1794. */
  1795. void VM_putentityfieldstring(prvm_prog_t *prog)
  1796. {
  1797. ddef_t *d;
  1798. prvm_edict_t * ent;
  1799. int i = (int)PRVM_G_FLOAT(OFS_PARM0);
  1800. if (i < 0 || i >= prog->numfielddefs)
  1801. {
  1802. VM_Warning(prog, "VM_entityfielddata: %s: field index out of bounds\n", prog->name);
  1803. PRVM_G_FLOAT(OFS_RETURN) = 0.0f;
  1804. return;
  1805. }
  1806. d = &prog->fielddefs[i];
  1807. // get the entity
  1808. ent = PRVM_G_EDICT(OFS_PARM1);
  1809. if(ent->priv.required->free)
  1810. {
  1811. VM_Warning(prog, "VM_entityfielddata: %s: entity %i is free !\n", prog->name, PRVM_NUM_FOR_EDICT(ent));
  1812. PRVM_G_FLOAT(OFS_RETURN) = 0.0f;
  1813. return;
  1814. }
  1815. // parse the string into the value
  1816. PRVM_G_FLOAT(OFS_RETURN) = ( PRVM_ED_ParseEpair(prog, ent, d, PRVM_G_STRING(OFS_PARM2), false) ) ? 1.0f : 0.0f;
  1817. }
  1818. /*
  1819. =========
  1820. VM_strlen
  1821. float strlen(string s)
  1822. =========
  1823. */
  1824. //float(string s) strlen = #114; // returns how many characters are in a string
  1825. void VM_strlen(prvm_prog_t *prog)
  1826. {
  1827. VM_SAFEPARMCOUNT(1,VM_strlen);
  1828. //PRVM_G_FLOAT(OFS_RETURN) = strlen(PRVM_G_STRING(OFS_PARM0));
  1829. PRVM_G_FLOAT(OFS_RETURN) = u8_strlen(PRVM_G_STRING(OFS_PARM0));
  1830. }
  1831. // DRESK - Decolorized String
  1832. /*
  1833. =========
  1834. VM_strdecolorize
  1835. string strdecolorize(string s)
  1836. =========
  1837. */
  1838. // string (string s) strdecolorize = #472; // returns the passed in string with color codes stripped
  1839. void VM_strdecolorize(prvm_prog_t *prog)
  1840. {
  1841. char szNewString[VM_STRINGTEMP_LENGTH];
  1842. const char *szString;
  1843. // Prepare Strings
  1844. VM_SAFEPARMCOUNT(1,VM_strdecolorize);
  1845. szString = PRVM_G_STRING(OFS_PARM0);
  1846. COM_StringDecolorize(szString, 0, szNewString, sizeof(szNewString), TRUE);
  1847. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString);
  1848. }
  1849. // DRESK - String Length (not counting color codes)
  1850. /*
  1851. =========
  1852. VM_strlennocol
  1853. float strlennocol(string s)
  1854. =========
  1855. */
  1856. // float(string s) strlennocol = #471; // returns how many characters are in a string not including color codes
  1857. // For example, ^2Dresk returns a length of 5
  1858. void VM_strlennocol(prvm_prog_t *prog)
  1859. {
  1860. const char *szString;
  1861. int nCnt;
  1862. VM_SAFEPARMCOUNT(1,VM_strlennocol);
  1863. szString = PRVM_G_STRING(OFS_PARM0);
  1864. //nCnt = (int)COM_StringLengthNoColors(szString, 0, NULL);
  1865. nCnt = (int)u8_COM_StringLengthNoColors(szString, 0, NULL);
  1866. PRVM_G_FLOAT(OFS_RETURN) = nCnt;
  1867. }
  1868. // DRESK - String to Uppercase and Lowercase
  1869. /*
  1870. =========
  1871. VM_strtolower
  1872. string strtolower(string s)
  1873. =========
  1874. */
  1875. // string (string s) strtolower = #480; // returns passed in string in lowercase form
  1876. void VM_strtolower(prvm_prog_t *prog)
  1877. {
  1878. char szNewString[VM_STRINGTEMP_LENGTH];
  1879. const char *szString;
  1880. // Prepare Strings
  1881. VM_SAFEPARMCOUNT(1,VM_strtolower);
  1882. szString = PRVM_G_STRING(OFS_PARM0);
  1883. COM_ToLowerString(szString, szNewString, sizeof(szNewString) );
  1884. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString);
  1885. }
  1886. /*
  1887. =========
  1888. VM_strtoupper
  1889. string strtoupper(string s)
  1890. =========
  1891. */
  1892. // string (string s) strtoupper = #481; // returns passed in string in uppercase form
  1893. void VM_strtoupper(prvm_prog_t *prog)
  1894. {
  1895. char szNewString[VM_STRINGTEMP_LENGTH];
  1896. const char *szString;
  1897. // Prepare Strings
  1898. VM_SAFEPARMCOUNT(1,VM_strtoupper);
  1899. szString = PRVM_G_STRING(OFS_PARM0);
  1900. COM_ToUpperString(szString, szNewString, sizeof(szNewString) );
  1901. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString);
  1902. }
  1903. /*
  1904. =========
  1905. VM_strcat
  1906. string strcat(string,string,...[string])
  1907. =========
  1908. */
  1909. //string(string s1, string s2) strcat = #115;
  1910. // concatenates two strings (for example "abc", "def" would return "abcdef")
  1911. // and returns as a tempstring
  1912. void VM_strcat(prvm_prog_t *prog)
  1913. {
  1914. char s[VM_STRINGTEMP_LENGTH];
  1915. VM_SAFEPARMCOUNTRANGE(1, 8, VM_strcat);
  1916. VM_VarString(prog, 0, s, sizeof(s));
  1917. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s);
  1918. }
  1919. /*
  1920. =========
  1921. VM_substring
  1922. string substring(string s, float start, float length)
  1923. =========
  1924. */
  1925. // string(string s, float start, float length) substring = #116;
  1926. // returns a section of a string as a tempstring
  1927. void VM_substring(prvm_prog_t *prog)
  1928. {
  1929. int start, length;
  1930. int u_slength = 0, u_start;
  1931. size_t u_length;
  1932. const char *s;
  1933. char string[VM_STRINGTEMP_LENGTH];
  1934. VM_SAFEPARMCOUNT(3,VM_substring);
  1935. /*
  1936. s = PRVM_G_STRING(OFS_PARM0);
  1937. start = (int)PRVM_G_FLOAT(OFS_PARM1);
  1938. length = (int)PRVM_G_FLOAT(OFS_PARM2);
  1939. slength = strlen(s);
  1940. if (start < 0) // FTE_STRINGS feature
  1941. start += slength;
  1942. start = bound(0, start, slength);
  1943. if (length < 0) // FTE_STRINGS feature
  1944. length += slength - start + 1;
  1945. maxlen = min((int)sizeof(string) - 1, slength - start);
  1946. length = bound(0, length, maxlen);
  1947. memcpy(string, s + start, length);
  1948. string[length] = 0;
  1949. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
  1950. */
  1951. s = PRVM_G_STRING(OFS_PARM0);
  1952. start = (int)PRVM_G_FLOAT(OFS_PARM1);
  1953. length = (int)PRVM_G_FLOAT(OFS_PARM2);
  1954. if (start < 0) // FTE_STRINGS feature
  1955. {
  1956. u_slength = (int)u8_strlen(s);
  1957. start += u_slength;
  1958. start = bound(0, start, u_slength);
  1959. }
  1960. if (length < 0) // FTE_STRINGS feature
  1961. {
  1962. if (!u_slength) // it's not calculated when it's not needed above
  1963. u_slength = (int)u8_strlen(s);
  1964. length += u_slength - start + 1;
  1965. }
  1966. // positive start, positive length
  1967. u_start = u8_byteofs(s, start, NULL);
  1968. if (u_start < 0)
  1969. {
  1970. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
  1971. return;
  1972. }
  1973. u_length = u8_bytelen(s + u_start, length);
  1974. if (u_length >= sizeof(string)-1)
  1975. u_length = sizeof(string)-1;
  1976. memcpy(string, s + u_start, u_length);
  1977. string[u_length] = 0;
  1978. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
  1979. }
  1980. /*
  1981. =========
  1982. VM_strreplace
  1983. string(string search, string replace, string subject) strreplace = #484;
  1984. =========
  1985. */
  1986. // replaces all occurrences of search with replace in the string subject, and returns the result
  1987. void VM_strreplace(prvm_prog_t *prog)
  1988. {
  1989. int i, j, si;
  1990. const char *search, *replace, *subject;
  1991. char string[VM_STRINGTEMP_LENGTH];
  1992. int search_len, replace_len, subject_len;
  1993. VM_SAFEPARMCOUNT(3,VM_strreplace);
  1994. search = PRVM_G_STRING(OFS_PARM0);
  1995. replace = PRVM_G_STRING(OFS_PARM1);
  1996. subject = PRVM_G_STRING(OFS_PARM2);
  1997. search_len = (int)strlen(search);
  1998. replace_len = (int)strlen(replace);
  1999. subject_len = (int)strlen(subject);
  2000. si = 0;
  2001. for (i = 0; i <= subject_len - search_len; i++)
  2002. {
  2003. for (j = 0; j < search_len; j++) // thus, i+j < subject_len
  2004. if (subject[i+j] != search[j])
  2005. break;
  2006. if (j == search_len)
  2007. {
  2008. // NOTE: if search_len == 0, we always hit THIS case, and never the other
  2009. // found it at offset 'i'
  2010. for (j = 0; j < replace_len && si < (int)sizeof(string) - 1; j++)
  2011. string[si++] = replace[j];
  2012. if(search_len > 0)
  2013. {
  2014. i += search_len - 1;
  2015. }
  2016. else
  2017. {
  2018. // the above would subtract 1 from i... so we
  2019. // don't do that, but instead output the next
  2020. // char
  2021. if (si < (int)sizeof(string) - 1)
  2022. string[si++] = subject[i];
  2023. }
  2024. }
  2025. else
  2026. {
  2027. // in THIS case, we know search_len > 0, thus i < subject_len
  2028. // not found
  2029. if (si < (int)sizeof(string) - 1)
  2030. string[si++] = subject[i];
  2031. }
  2032. }
  2033. // remaining chars (these cannot match)
  2034. for (; i < subject_len; i++)
  2035. if (si < (int)sizeof(string) - 1)
  2036. string[si++] = subject[i];
  2037. string[si] = '\0';
  2038. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
  2039. }
  2040. /*
  2041. =========
  2042. VM_strireplace
  2043. string(string search, string replace, string subject) strireplace = #485;
  2044. =========
  2045. */
  2046. // case-insensitive version of strreplace
  2047. void VM_strireplace(prvm_prog_t *prog)
  2048. {
  2049. int i, j, si;
  2050. const char *search, *replace, *subject;
  2051. char string[VM_STRINGTEMP_LENGTH];
  2052. int search_len, replace_len, subject_len;
  2053. VM_SAFEPARMCOUNT(3,VM_strreplace);
  2054. search = PRVM_G_STRING(OFS_PARM0);
  2055. replace = PRVM_G_STRING(OFS_PARM1);
  2056. subject = PRVM_G_STRING(OFS_PARM2);
  2057. search_len = (int)strlen(search);
  2058. replace_len = (int)strlen(replace);
  2059. subject_len = (int)strlen(subject);
  2060. si = 0;
  2061. for (i = 0; i <= subject_len - search_len; i++)
  2062. {
  2063. for (j = 0; j < search_len; j++) // thus, i+j < subject_len
  2064. if (tolower(subject[i+j]) != tolower(search[j]))
  2065. break;
  2066. if (j == search_len)
  2067. {
  2068. // NOTE: if search_len == 0, we always hit THIS case, and never the other
  2069. // found it at offset 'i'
  2070. for (j = 0; j < replace_len && si < (int)sizeof(string) - 1; j++)
  2071. string[si++] = replace[j];
  2072. if(search_len > 0)
  2073. {
  2074. i += search_len - 1;
  2075. }
  2076. else
  2077. {
  2078. // the above would subtract 1 from i... so we
  2079. // don't do that, but instead output the next
  2080. // char
  2081. if (si < (int)sizeof(string) - 1)
  2082. string[si++] = subject[i];
  2083. }
  2084. }
  2085. else
  2086. {
  2087. // in THIS case, we know search_len > 0, thus i < subject_len
  2088. // not found
  2089. if (si < (int)sizeof(string) - 1)
  2090. string[si++] = subject[i];
  2091. }
  2092. }
  2093. // remaining chars (these cannot match)
  2094. for (; i < subject_len; i++)
  2095. if (si < (int)sizeof(string) - 1)
  2096. string[si++] = subject[i];
  2097. string[si] = '\0';
  2098. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
  2099. }
  2100. /*
  2101. =========
  2102. VM_stov
  2103. vector stov(string s)
  2104. =========
  2105. */
  2106. //vector(string s) stov = #117; // returns vector value from a string
  2107. void VM_stov(prvm_prog_t *prog)
  2108. {
  2109. char string[VM_STRINGTEMP_LENGTH];
  2110. VM_SAFEPARMCOUNT(1,VM_stov);
  2111. VM_VarString(prog, 0, string, sizeof(string));
  2112. Math_atov(string, PRVM_G_VECTOR(OFS_RETURN));
  2113. }
  2114. /*
  2115. =========
  2116. VM_strzone
  2117. string strzone(string s)
  2118. =========
  2119. */
  2120. //string(string s, ...) strzone = #118; // makes a copy of a string into the string zone and returns it, this is often used to keep around a tempstring for longer periods of time (tempstrings are replaced often)
  2121. void VM_strzone(prvm_prog_t *prog)
  2122. {
  2123. char *out;
  2124. char string[VM_STRINGTEMP_LENGTH];
  2125. size_t alloclen;
  2126. VM_SAFEPARMCOUNT(1,VM_strzone);
  2127. VM_VarString(prog, 0, string, sizeof(string));
  2128. alloclen = strlen(string) + 1;
  2129. PRVM_G_INT(OFS_RETURN) = PRVM_AllocString(prog, alloclen, &out);
  2130. memcpy(out, string, alloclen);
  2131. }
  2132. /*
  2133. =========
  2134. VM_strunzone
  2135. strunzone(string s)
  2136. =========
  2137. */
  2138. //void(string s) strunzone = #119; // removes a copy of a string from the string zone (you can not use that string again or it may crash!!!)
  2139. void VM_strunzone(prvm_prog_t *prog)
  2140. {
  2141. VM_SAFEPARMCOUNT(1,VM_strunzone);
  2142. PRVM_FreeString(prog, PRVM_G_INT(OFS_PARM0));
  2143. }
  2144. /*
  2145. =========
  2146. VM_command (used by client and menu)
  2147. clientcommand(float client, string s) (for client and menu)
  2148. =========
  2149. */
  2150. //void(entity e, string s) clientcommand = #440; // executes a command string as if it came from the specified client
  2151. //this function originally written by KrimZon, made shorter by LordHavoc
  2152. void VM_clcommand (prvm_prog_t *prog)
  2153. {
  2154. client_t *temp_client;
  2155. int i;
  2156. VM_SAFEPARMCOUNT(2,VM_clcommand);
  2157. i = (int)PRVM_G_FLOAT(OFS_PARM0);
  2158. if (!sv.active || i < 0 || i >= svs.maxclients || !svs.clients[i].active)
  2159. {
  2160. VM_Warning(prog, "VM_clientcommand: %s: invalid client/server is not active !\n", prog->name);
  2161. return;
  2162. }
  2163. temp_client = host_client;
  2164. host_client = svs.clients + i;
  2165. Cmd_ExecuteString (PRVM_G_STRING(OFS_PARM1), src_client, true);
  2166. host_client = temp_client;
  2167. }
  2168. /*
  2169. =========
  2170. VM_tokenize
  2171. float tokenize(string s)
  2172. =========
  2173. */
  2174. //float(string s) tokenize = #441; // takes apart a string into individal words (access them with argv), returns how many
  2175. //this function originally written by KrimZon, made shorter by LordHavoc
  2176. //20040203: rewritten by LordHavoc (no longer uses allocations)
  2177. static int num_tokens = 0;
  2178. static int tokens[VM_STRINGTEMP_LENGTH / 2];
  2179. static int tokens_startpos[VM_STRINGTEMP_LENGTH / 2];
  2180. static int tokens_endpos[VM_STRINGTEMP_LENGTH / 2];
  2181. static char tokenize_string[VM_STRINGTEMP_LENGTH];
  2182. void VM_tokenize (prvm_prog_t *prog)
  2183. {
  2184. const char *p;
  2185. VM_SAFEPARMCOUNT(1,VM_tokenize);
  2186. strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
  2187. p = tokenize_string;
  2188. num_tokens = 0;
  2189. for(;;)
  2190. {
  2191. if (num_tokens >= (int)(sizeof(tokens)/sizeof(tokens[0])))
  2192. break;
  2193. // skip whitespace here to find token start pos
  2194. while(*p && ISWHITESPACE(*p))
  2195. ++p;
  2196. tokens_startpos[num_tokens] = p - tokenize_string;
  2197. if(!COM_ParseToken_VM_Tokenize(&p, false))
  2198. break;
  2199. tokens_endpos[num_tokens] = p - tokenize_string;
  2200. tokens[num_tokens] = PRVM_SetTempString(prog, com_token);
  2201. ++num_tokens;
  2202. }
  2203. PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
  2204. }
  2205. //float(string s) tokenize = #514; // takes apart a string into individal words (access them with argv), returns how many
  2206. void VM_tokenize_console (prvm_prog_t *prog)
  2207. {
  2208. const char *p;
  2209. VM_SAFEPARMCOUNT(1,VM_tokenize);
  2210. strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
  2211. p = tokenize_string;
  2212. num_tokens = 0;
  2213. for(;;)
  2214. {
  2215. if (num_tokens >= (int)(sizeof(tokens)/sizeof(tokens[0])))
  2216. break;
  2217. // skip whitespace here to find token start pos
  2218. while(*p && ISWHITESPACE(*p))
  2219. ++p;
  2220. tokens_startpos[num_tokens] = p - tokenize_string;
  2221. if(!COM_ParseToken_Console(&p))
  2222. break;
  2223. tokens_endpos[num_tokens] = p - tokenize_string;
  2224. tokens[num_tokens] = PRVM_SetTempString(prog, com_token);
  2225. ++num_tokens;
  2226. }
  2227. PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
  2228. }
  2229. /*
  2230. =========
  2231. VM_tokenizebyseparator
  2232. float tokenizebyseparator(string s, string separator1, ...)
  2233. =========
  2234. */
  2235. //float(string s, string separator1, ...) tokenizebyseparator = #479; // takes apart a string into individal words (access them with argv), returns how many
  2236. //this function returns the token preceding each instance of a separator (of
  2237. //which there can be multiple), and the text following the last separator
  2238. //useful for parsing certain kinds of data like IP addresses
  2239. //example:
  2240. //numnumbers = tokenizebyseparator("10.1.2.3", ".");
  2241. //returns 4 and the tokens "10" "1" "2" "3".
  2242. void VM_tokenizebyseparator (prvm_prog_t *prog)
  2243. {
  2244. int j, k;
  2245. int numseparators;
  2246. int separatorlen[7];
  2247. const char *separators[7];
  2248. const char *p, *p0;
  2249. const char *token;
  2250. char tokentext[MAX_INPUTLINE];
  2251. VM_SAFEPARMCOUNTRANGE(2, 8,VM_tokenizebyseparator);
  2252. strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
  2253. p = tokenize_string;
  2254. numseparators = 0;
  2255. for (j = 1;j < prog->argc;j++)
  2256. {
  2257. // skip any blank separator strings
  2258. const char *s = PRVM_G_STRING(OFS_PARM0+j*3);
  2259. if (!s[0])
  2260. continue;
  2261. separators[numseparators] = s;
  2262. separatorlen[numseparators] = (int)strlen(s);
  2263. numseparators++;
  2264. }
  2265. num_tokens = 0;
  2266. j = 0;
  2267. while (num_tokens < (int)(sizeof(tokens)/sizeof(tokens[0])))
  2268. {
  2269. token = tokentext + j;
  2270. tokens_startpos[num_tokens] = p - tokenize_string;
  2271. p0 = p;
  2272. while (*p)
  2273. {
  2274. for (k = 0;k < numseparators;k++)
  2275. {
  2276. if (!strncmp(p, separators[k], separatorlen[k]))
  2277. {
  2278. p += separatorlen[k];
  2279. break;
  2280. }
  2281. }
  2282. if (k < numseparators)
  2283. break;
  2284. if (j < (int)sizeof(tokentext)-1)
  2285. tokentext[j++] = *p;
  2286. p++;
  2287. p0 = p;
  2288. }
  2289. tokens_endpos[num_tokens] = p0 - tokenize_string;
  2290. if (j >= (int)sizeof(tokentext))
  2291. break;
  2292. tokentext[j++] = 0;
  2293. tokens[num_tokens++] = PRVM_SetTempString(prog, token);
  2294. if (!*p)
  2295. break;
  2296. }
  2297. PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
  2298. }
  2299. //string(float n) argv = #442; // returns a word from the tokenized string (returns nothing for an invalid index)
  2300. //this function originally written by KrimZon, made shorter by LordHavoc
  2301. void VM_argv (prvm_prog_t *prog)
  2302. {
  2303. int token_num;
  2304. VM_SAFEPARMCOUNT(1,VM_argv);
  2305. token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
  2306. if(token_num < 0)
  2307. token_num += num_tokens;
  2308. if (token_num >= 0 && token_num < num_tokens)
  2309. PRVM_G_INT(OFS_RETURN) = tokens[token_num];
  2310. else
  2311. PRVM_G_INT(OFS_RETURN) = OFS_NULL;
  2312. }
  2313. //float(float n) argv_start_index = #515; // returns the start index of a token
  2314. void VM_argv_start_index (prvm_prog_t *prog)
  2315. {
  2316. int token_num;
  2317. VM_SAFEPARMCOUNT(1,VM_argv);
  2318. token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
  2319. if(token_num < 0)
  2320. token_num += num_tokens;
  2321. if (token_num >= 0 && token_num < num_tokens)
  2322. PRVM_G_FLOAT(OFS_RETURN) = tokens_startpos[token_num];
  2323. else
  2324. PRVM_G_FLOAT(OFS_RETURN) = -1;
  2325. }
  2326. //float(float n) argv_end_index = #516; // returns the end index of a token
  2327. void VM_argv_end_index (prvm_prog_t *prog)
  2328. {
  2329. int token_num;
  2330. VM_SAFEPARMCOUNT(1,VM_argv);
  2331. token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
  2332. if(token_num < 0)
  2333. token_num += num_tokens;
  2334. if (token_num >= 0 && token_num < num_tokens)
  2335. PRVM_G_FLOAT(OFS_RETURN) = tokens_endpos[token_num];
  2336. else
  2337. PRVM_G_FLOAT(OFS_RETURN) = -1;
  2338. }
  2339. /*
  2340. =========
  2341. VM_isserver
  2342. float isserver()
  2343. =========
  2344. */
  2345. void VM_isserver(prvm_prog_t *prog)
  2346. {
  2347. VM_SAFEPARMCOUNT(0,VM_serverstate);
  2348. PRVM_G_FLOAT(OFS_RETURN) = sv.active;
  2349. }
  2350. /*
  2351. =========
  2352. VM_clientcount
  2353. float clientcount()
  2354. =========
  2355. */
  2356. void VM_clientcount(prvm_prog_t *prog)
  2357. {
  2358. VM_SAFEPARMCOUNT(0,VM_clientcount);
  2359. PRVM_G_FLOAT(OFS_RETURN) = svs.maxclients;
  2360. }
  2361. /*
  2362. =========
  2363. VM_clientstate
  2364. float clientstate()
  2365. =========
  2366. */
  2367. void VM_clientstate(prvm_prog_t *prog)
  2368. {
  2369. VM_SAFEPARMCOUNT(0,VM_clientstate);
  2370. switch( cls.state ) {
  2371. case ca_uninitialized:
  2372. case ca_dedicated:
  2373. PRVM_G_FLOAT(OFS_RETURN) = 0;
  2374. break;
  2375. case ca_disconnected:
  2376. PRVM_G_FLOAT(OFS_RETURN) = 1;
  2377. break;
  2378. case ca_connected:
  2379. PRVM_G_FLOAT(OFS_RETURN) = 2;
  2380. break;
  2381. default:
  2382. // should never be reached!
  2383. break;
  2384. }
  2385. }
  2386. /*
  2387. =========
  2388. VM_getostype
  2389. float getostype(prvm_prog_t *prog)
  2390. =========
  2391. */ // not used at the moment -> not included in the common list
  2392. void VM_getostype(prvm_prog_t *prog)
  2393. {
  2394. VM_SAFEPARMCOUNT(0,VM_getostype);
  2395. /*
  2396. OS_WINDOWS
  2397. OS_LINUX
  2398. OS_MAC - not supported
  2399. */
  2400. #ifdef WIN32
  2401. PRVM_G_FLOAT(OFS_RETURN) = 0;
  2402. #elif defined(MACOSX)
  2403. PRVM_G_FLOAT(OFS_RETURN) = 2;
  2404. #else
  2405. PRVM_G_FLOAT(OFS_RETURN) = 1;
  2406. #endif
  2407. }
  2408. /*
  2409. =========
  2410. VM_gettime
  2411. float gettime(prvm_prog_t *prog)
  2412. =========
  2413. */
  2414. #ifdef CONFIG_CD
  2415. float CDAudio_GetPosition(void);
  2416. #endif
  2417. void VM_gettime(prvm_prog_t *prog)
  2418. {
  2419. int timer_index;
  2420. VM_SAFEPARMCOUNTRANGE(0,1,VM_gettime);
  2421. if(prog->argc == 0)
  2422. {
  2423. PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t) realtime;
  2424. }
  2425. else
  2426. {
  2427. timer_index = (int) PRVM_G_FLOAT(OFS_PARM0);
  2428. switch(timer_index)
  2429. {
  2430. case 0: // GETTIME_FRAMESTART
  2431. PRVM_G_FLOAT(OFS_RETURN) = realtime;
  2432. break;
  2433. case 1: // GETTIME_REALTIME
  2434. PRVM_G_FLOAT(OFS_RETURN) = Sys_DirtyTime();
  2435. break;
  2436. case 2: // GETTIME_HIRES
  2437. PRVM_G_FLOAT(OFS_RETURN) = (Sys_DirtyTime() - host_dirtytime);
  2438. break;
  2439. case 3: // GETTIME_UPTIME
  2440. PRVM_G_FLOAT(OFS_RETURN) = realtime;
  2441. break;
  2442. #ifdef CONFIG_CD
  2443. case 4: // GETTIME_CDTRACK
  2444. PRVM_G_FLOAT(OFS_RETURN) = CDAudio_GetPosition();
  2445. break;
  2446. #endif
  2447. default:
  2448. VM_Warning(prog, "VM_gettime: %s: unsupported timer specified, returning realtime\n", prog->name);
  2449. PRVM_G_FLOAT(OFS_RETURN) = realtime;
  2450. break;
  2451. }
  2452. }
  2453. }
  2454. /*
  2455. =========
  2456. VM_getsoundtime
  2457. float getsoundtime(prvm_prog_t *prog)
  2458. =========
  2459. */
  2460. void VM_getsoundtime (prvm_prog_t *prog)
  2461. {
  2462. int entnum, entchannel;
  2463. VM_SAFEPARMCOUNT(2,VM_getsoundtime);
  2464. if (prog == SVVM_prog)
  2465. entnum = PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(OFS_PARM0));
  2466. else if (prog == CLVM_prog)
  2467. entnum = MAX_EDICTS + PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(OFS_PARM0));
  2468. else
  2469. {
  2470. VM_Warning(prog, "VM_getsoundtime: %s: not supported on this progs\n", prog->name);
  2471. PRVM_G_FLOAT(OFS_RETURN) = -1;
  2472. return;
  2473. }
  2474. entchannel = (int)PRVM_G_FLOAT(OFS_PARM1);
  2475. entchannel = CHAN_USER2ENGINE(entchannel);
  2476. if (!IS_CHAN(entchannel))
  2477. VM_Warning(prog, "VM_getsoundtime: %s: bad channel %i\n", prog->name, entchannel);
  2478. PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t)S_GetEntChannelPosition(entnum, entchannel);
  2479. }
  2480. /*
  2481. =========
  2482. VM_GetSoundLen
  2483. string soundlength (string sample)
  2484. =========
  2485. */
  2486. void VM_soundlength (prvm_prog_t *prog)
  2487. {
  2488. const char *s;
  2489. VM_SAFEPARMCOUNT(1, VM_soundlength);
  2490. s = PRVM_G_STRING(OFS_PARM0);
  2491. PRVM_G_FLOAT(OFS_RETURN) = S_SoundLength(s);
  2492. }
  2493. /*
  2494. =========
  2495. VM_loadfromdata
  2496. loadfromdata(string data)
  2497. =========
  2498. */
  2499. void VM_loadfromdata(prvm_prog_t *prog)
  2500. {
  2501. VM_SAFEPARMCOUNT(1,VM_loadentsfromfile);
  2502. PRVM_ED_LoadFromFile(prog, PRVM_G_STRING(OFS_PARM0));
  2503. }
  2504. /*
  2505. ========================
  2506. VM_parseentitydata
  2507. parseentitydata(entity ent, string data)
  2508. ========================
  2509. */
  2510. void VM_parseentitydata(prvm_prog_t *prog)
  2511. {
  2512. prvm_edict_t *ent;
  2513. const char *data;
  2514. VM_SAFEPARMCOUNT(2, VM_parseentitydata);
  2515. // get edict and test it
  2516. ent = PRVM_G_EDICT(OFS_PARM0);
  2517. if (ent->priv.required->free)
  2518. prog->error_cmd("VM_parseentitydata: %s: Can only set already spawned entities (entity %i is free)!", prog->name, PRVM_NUM_FOR_EDICT(ent));
  2519. data = PRVM_G_STRING(OFS_PARM1);
  2520. // parse the opening brace
  2521. if (!COM_ParseToken_Simple(&data, false, false, true) || com_token[0] != '{' )
  2522. prog->error_cmd("VM_parseentitydata: %s: Couldn't parse entity data:\n%s", prog->name, data );
  2523. PRVM_ED_ParseEdict (prog, data, ent);
  2524. }
  2525. /*
  2526. =========
  2527. VM_loadfromfile
  2528. loadfromfile(string file)
  2529. =========
  2530. */
  2531. void VM_loadfromfile(prvm_prog_t *prog)
  2532. {
  2533. const char *filename;
  2534. char *data;
  2535. VM_SAFEPARMCOUNT(1,VM_loadfromfile);
  2536. filename = PRVM_G_STRING(OFS_PARM0);
  2537. if (FS_CheckNastyPath(filename, false))
  2538. {
  2539. PRVM_G_FLOAT(OFS_RETURN) = -4;
  2540. VM_Warning(prog, "VM_loadfromfile: %s dangerous or non-portable filename \"%s\" not allowed. (contains : or \\ or begins with .. or /)\n", prog->name, filename);
  2541. return;
  2542. }
  2543. // not conform with VM_fopen
  2544. data = (char *)FS_LoadFile(filename, tempmempool, false, NULL);
  2545. if (data == NULL)
  2546. PRVM_G_FLOAT(OFS_RETURN) = -1;
  2547. PRVM_ED_LoadFromFile(prog, data);
  2548. if(data)
  2549. Mem_Free(data);
  2550. }
  2551. /*
  2552. =========
  2553. VM_modulo
  2554. float mod(float val, float m)
  2555. =========
  2556. */
  2557. void VM_modulo(prvm_prog_t *prog)
  2558. {
  2559. prvm_int_t val, m;
  2560. VM_SAFEPARMCOUNT(2,VM_module);
  2561. val = (prvm_int_t) PRVM_G_FLOAT(OFS_PARM0);
  2562. m = (prvm_int_t) PRVM_G_FLOAT(OFS_PARM1);
  2563. PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t) (val % m);
  2564. }
  2565. static void VM_Search_Init(prvm_prog_t *prog)
  2566. {
  2567. int i;
  2568. for (i = 0;i < PRVM_MAX_OPENSEARCHES;i++)
  2569. prog->opensearches[i] = NULL;
  2570. }
  2571. static void VM_Search_Reset(prvm_prog_t *prog)
  2572. {
  2573. int i;
  2574. // reset the fssearch list
  2575. for(i = 0; i < PRVM_MAX_OPENSEARCHES; i++)
  2576. {
  2577. if(prog->opensearches[i])
  2578. FS_FreeSearch(prog->opensearches[i]);
  2579. prog->opensearches[i] = NULL;
  2580. }
  2581. }
  2582. /*
  2583. =========
  2584. VM_search_begin
  2585. float search_begin(string pattern, float caseinsensitive, float quiet)
  2586. =========
  2587. */
  2588. void VM_search_begin(prvm_prog_t *prog)
  2589. {
  2590. int handle;
  2591. const char *pattern;
  2592. int caseinsens, quiet;
  2593. VM_SAFEPARMCOUNT(3, VM_search_begin);
  2594. pattern = PRVM_G_STRING(OFS_PARM0);
  2595. VM_CheckEmptyString(prog, pattern);
  2596. caseinsens = (int)PRVM_G_FLOAT(OFS_PARM1);
  2597. quiet = (int)PRVM_G_FLOAT(OFS_PARM2);
  2598. for(handle = 0; handle < PRVM_MAX_OPENSEARCHES; handle++)
  2599. if(!prog->opensearches[handle])
  2600. break;
  2601. if(handle >= PRVM_MAX_OPENSEARCHES)
  2602. {
  2603. PRVM_G_FLOAT(OFS_RETURN) = -2;
  2604. VM_Warning(prog, "VM_search_begin: %s ran out of search handles (%i)\n", prog->name, PRVM_MAX_OPENSEARCHES);
  2605. return;
  2606. }
  2607. if(!(prog->opensearches[handle] = FS_Search(pattern,caseinsens, quiet)))
  2608. PRVM_G_FLOAT(OFS_RETURN) = -1;
  2609. else
  2610. {
  2611. prog->opensearches_origin[handle] = PRVM_AllocationOrigin(prog);
  2612. PRVM_G_FLOAT(OFS_RETURN) = handle;
  2613. }
  2614. }
  2615. /*
  2616. =========
  2617. VM_search_end
  2618. void search_end(float handle)
  2619. =========
  2620. */
  2621. void VM_search_end(prvm_prog_t *prog)
  2622. {
  2623. int handle;
  2624. VM_SAFEPARMCOUNT(1, VM_search_end);
  2625. handle = (int)PRVM_G_FLOAT(OFS_PARM0);
  2626. if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
  2627. {
  2628. VM_Warning(prog, "VM_search_end: invalid handle %i used in %s\n", handle, prog->name);
  2629. return;
  2630. }
  2631. if(prog->opensearches[handle] == NULL)
  2632. {
  2633. VM_Warning(prog, "VM_search_end: no such handle %i in %s\n", handle, prog->name);
  2634. return;
  2635. }
  2636. FS_FreeSearch(prog->opensearches[handle]);
  2637. prog->opensearches[handle] = NULL;
  2638. if(prog->opensearches_origin[handle])
  2639. PRVM_Free((char *)prog->opensearches_origin[handle]);
  2640. }
  2641. /*
  2642. =========
  2643. VM_search_getsize
  2644. float search_getsize(float handle)
  2645. =========
  2646. */
  2647. void VM_search_getsize(prvm_prog_t *prog)
  2648. {
  2649. int handle;
  2650. VM_SAFEPARMCOUNT(1, VM_M_search_getsize);
  2651. handle = (int)PRVM_G_FLOAT(OFS_PARM0);
  2652. if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
  2653. {
  2654. VM_Warning(prog, "VM_search_getsize: invalid handle %i used in %s\n", handle, prog->name);
  2655. return;
  2656. }
  2657. if(prog->opensearches[handle] == NULL)
  2658. {
  2659. VM_Warning(prog, "VM_search_getsize: no such handle %i in %s\n", handle, prog->name);
  2660. return;
  2661. }
  2662. PRVM_G_FLOAT(OFS_RETURN) = prog->opensearches[handle]->numfilenames;
  2663. }
  2664. /*
  2665. =========
  2666. VM_search_getfilename
  2667. string search_getfilename(float handle, float num)
  2668. =========
  2669. */
  2670. void VM_search_getfilename(prvm_prog_t *prog)
  2671. {
  2672. int handle, filenum;
  2673. VM_SAFEPARMCOUNT(2, VM_search_getfilename);
  2674. handle = (int)PRVM_G_FLOAT(OFS_PARM0);
  2675. filenum = (int)PRVM_G_FLOAT(OFS_PARM1);
  2676. if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
  2677. {
  2678. VM_Warning(prog, "VM_search_getfilename: invalid handle %i used in %s\n", handle, prog->name);
  2679. return;
  2680. }
  2681. if(prog->opensearches[handle] == NULL)
  2682. {
  2683. VM_Warning(prog, "VM_search_getfilename: no such handle %i in %s\n", handle, prog->name);
  2684. return;
  2685. }
  2686. if(filenum < 0 || filenum >= prog->opensearches[handle]->numfilenames)
  2687. {
  2688. VM_Warning(prog, "VM_search_getfilename: invalid filenum %i in %s\n", filenum, prog->name);
  2689. return;
  2690. }
  2691. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, prog->opensearches[handle]->filenames[filenum]);
  2692. }
  2693. /*
  2694. =========
  2695. VM_chr
  2696. string chr(float ascii)
  2697. =========
  2698. */
  2699. void VM_chr(prvm_prog_t *prog)
  2700. {
  2701. /*
  2702. char tmp[2];
  2703. VM_SAFEPARMCOUNT(1, VM_chr);
  2704. tmp[0] = (unsigned char) PRVM_G_FLOAT(OFS_PARM0);
  2705. tmp[1] = 0;
  2706. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, tmp);
  2707. */
  2708. char tmp[8];
  2709. int len;
  2710. VM_SAFEPARMCOUNT(1, VM_chr);
  2711. len = u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0), tmp, sizeof(tmp));
  2712. tmp[len] = 0;
  2713. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, tmp);
  2714. }
  2715. //=============================================================================
  2716. // Draw builtins (client & menu)
  2717. /*
  2718. =========
  2719. VM_iscachedpic
  2720. float iscachedpic(string pic)
  2721. =========
  2722. */
  2723. void VM_iscachedpic(prvm_prog_t *prog)
  2724. {
  2725. VM_SAFEPARMCOUNT(1,VM_iscachedpic);
  2726. // drawq hasnt such a function, thus always return true
  2727. PRVM_G_FLOAT(OFS_RETURN) = false;
  2728. }
  2729. /*
  2730. =========
  2731. VM_precache_pic
  2732. string precache_pic(string pic)
  2733. =========
  2734. */
  2735. #define PRECACHE_PIC_FROMWAD 1 /* FTEQW, not supported here */
  2736. #define PRECACHE_PIC_NOTPERSISTENT 2
  2737. //#define PRECACHE_PIC_NOCLAMP 4
  2738. #define PRECACHE_PIC_MIPMAP 8
  2739. void VM_precache_pic(prvm_prog_t *prog)
  2740. {
  2741. const char *s;
  2742. int flags = 0;
  2743. VM_SAFEPARMCOUNTRANGE(1, 2, VM_precache_pic);
  2744. s = PRVM_G_STRING(OFS_PARM0);
  2745. PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
  2746. VM_CheckEmptyString(prog, s);
  2747. if(prog->argc >= 2)
  2748. {
  2749. int f = PRVM_G_FLOAT(OFS_PARM1);
  2750. if(f & PRECACHE_PIC_NOTPERSISTENT)
  2751. flags |= CACHEPICFLAG_NOTPERSISTENT;
  2752. //if(f & PRECACHE_PIC_NOCLAMP)
  2753. // flags |= CACHEPICFLAG_NOCLAMP;
  2754. if(f & PRECACHE_PIC_MIPMAP)
  2755. flags |= CACHEPICFLAG_MIPMAP;
  2756. }
  2757. // AK Draw_CachePic is supposed to always return a valid pointer
  2758. if( Draw_CachePic_Flags(s, flags)->tex == r_texture_notexture )
  2759. PRVM_G_INT(OFS_RETURN) = OFS_NULL;
  2760. }
  2761. /*
  2762. =========
  2763. VM_freepic
  2764. freepic(string s)
  2765. =========
  2766. */
  2767. void VM_freepic(prvm_prog_t *prog)
  2768. {
  2769. const char *s;
  2770. VM_SAFEPARMCOUNT(1,VM_freepic);
  2771. s = PRVM_G_STRING(OFS_PARM0);
  2772. VM_CheckEmptyString(prog, s);
  2773. Draw_FreePic(s);
  2774. }
  2775. static void getdrawfontscale(prvm_prog_t *prog, float *sx, float *sy)
  2776. {
  2777. vec3_t v;
  2778. *sx = *sy = 1;
  2779. VectorCopy(PRVM_drawglobalvector(drawfontscale), v);
  2780. if(VectorLength2(v) > 0)
  2781. {
  2782. *sx = v[0];
  2783. *sy = v[1];
  2784. }
  2785. }
  2786. static dp_font_t *getdrawfont(prvm_prog_t *prog)
  2787. {
  2788. int f = (int) PRVM_drawglobalfloat(drawfont);
  2789. if(f < 0 || f >= dp_fonts.maxsize)
  2790. return FONT_DEFAULT;
  2791. return &dp_fonts.f[f];
  2792. }
  2793. /*
  2794. =========
  2795. VM_drawcharacter
  2796. float drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag)
  2797. =========
  2798. */
  2799. void VM_drawcharacter(prvm_prog_t *prog)
  2800. {
  2801. prvm_vec_t *pos,*scale,*rgb;
  2802. char character;
  2803. int flag;
  2804. float sx, sy;
  2805. VM_SAFEPARMCOUNT(6,VM_drawcharacter);
  2806. character = (char) PRVM_G_FLOAT(OFS_PARM1);
  2807. if(character == 0)
  2808. {
  2809. PRVM_G_FLOAT(OFS_RETURN) = -1;
  2810. VM_Warning(prog, "VM_drawcharacter: %s passed null character !\n",prog->name);
  2811. return;
  2812. }
  2813. pos = PRVM_G_VECTOR(OFS_PARM0);
  2814. scale = PRVM_G_VECTOR(OFS_PARM2);
  2815. rgb = PRVM_G_VECTOR(OFS_PARM3);
  2816. flag = (int)PRVM_G_FLOAT(OFS_PARM5);
  2817. if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
  2818. {
  2819. PRVM_G_FLOAT(OFS_RETURN) = -2;
  2820. VM_Warning(prog, "VM_drawcharacter: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
  2821. return;
  2822. }
  2823. if(pos[2] || scale[2])
  2824. VM_Warning(prog, "VM_drawcharacter: z value%c from %s discarded\n",(pos[2] && scale[2]) ? 's' : 0,((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
  2825. if(!scale[0] || !scale[1])
  2826. {
  2827. PRVM_G_FLOAT(OFS_RETURN) = -3;
  2828. VM_Warning(prog, "VM_drawcharacter: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
  2829. return;
  2830. }
  2831. getdrawfontscale(prog, &sx, &sy);
  2832. DrawQ_String_Scale(pos[0], pos[1], &character, 1, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont(prog));
  2833. PRVM_G_FLOAT(OFS_RETURN) = 1;
  2834. }
  2835. /*
  2836. =========
  2837. VM_drawstring
  2838. float drawstring(vector position, string text, vector scale, vector rgb, float alpha[, float flag])
  2839. =========
  2840. */
  2841. void VM_drawstring(prvm_prog_t *prog)
  2842. {
  2843. prvm_vec_t *pos,*scale,*rgb;
  2844. const char *string;
  2845. int flag = 0;
  2846. float sx, sy;
  2847. VM_SAFEPARMCOUNTRANGE(5,6,VM_drawstring);
  2848. string = PRVM_G_STRING(OFS_PARM1);
  2849. pos = PRVM_G_VECTOR(OFS_PARM0);
  2850. scale = PRVM_G_VECTOR(OFS_PARM2);
  2851. rgb = PRVM_G_VECTOR(OFS_PARM3);
  2852. if (prog->argc >= 6)
  2853. flag = (int)PRVM_G_FLOAT(OFS_PARM5);
  2854. if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
  2855. {
  2856. PRVM_G_FLOAT(OFS_RETURN) = -2;
  2857. VM_Warning(prog, "VM_drawstring: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
  2858. return;
  2859. }
  2860. if(!scale[0] || !scale[1])
  2861. {
  2862. PRVM_G_FLOAT(OFS_RETURN) = -3;
  2863. VM_Warning(prog, "VM_drawstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
  2864. return;
  2865. }
  2866. if(pos[2] || scale[2])
  2867. VM_Warning(prog, "VM_drawstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
  2868. getdrawfontscale(prog, &sx, &sy);
  2869. DrawQ_String_Scale(pos[0], pos[1], string, 0, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont(prog));
  2870. //Font_DrawString(pos[0], pos[1], string, 0, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true);
  2871. PRVM_G_FLOAT(OFS_RETURN) = 1;
  2872. }
  2873. /*
  2874. =========
  2875. VM_drawcolorcodedstring
  2876. float drawcolorcodedstring(vector position, string text, vector scale, float alpha, float flag)
  2877. /
  2878. float drawcolorcodedstring(vector position, string text, vector scale, vector rgb, float alpha, float flag)
  2879. =========
  2880. */
  2881. void VM_drawcolorcodedstring(prvm_prog_t *prog)
  2882. {
  2883. prvm_vec_t *pos, *scale;
  2884. const char *string;
  2885. int flag;
  2886. vec3_t rgb;
  2887. float sx, sy, alpha;
  2888. VM_SAFEPARMCOUNTRANGE(5,6,VM_drawcolorcodedstring);
  2889. if (prog->argc == 6) // full 6 parms, like normal drawstring
  2890. {
  2891. pos = PRVM_G_VECTOR(OFS_PARM0);
  2892. string = PRVM_G_STRING(OFS_PARM1);
  2893. scale = PRVM_G_VECTOR(OFS_PARM2);
  2894. VectorCopy(PRVM_G_VECTOR(OFS_PARM3), rgb);
  2895. alpha = PRVM_G_FLOAT(OFS_PARM4);
  2896. flag = (int)PRVM_G_FLOAT(OFS_PARM5);
  2897. }
  2898. else
  2899. {
  2900. pos = PRVM_G_VECTOR(OFS_PARM0);
  2901. string = PRVM_G_STRING(OFS_PARM1);
  2902. scale = PRVM_G_VECTOR(OFS_PARM2);
  2903. rgb[0] = 1.0;
  2904. rgb[1] = 1.0;
  2905. rgb[2] = 1.0;
  2906. alpha = PRVM_G_FLOAT(OFS_PARM3);
  2907. flag = (int)PRVM_G_FLOAT(OFS_PARM4);
  2908. }
  2909. if(flag < DRAWFLAG_NORMAL || flag >= DRAWFLAG_NUMFLAGS)
  2910. {
  2911. PRVM_G_FLOAT(OFS_RETURN) = -2;
  2912. VM_Warning(prog, "VM_drawcolorcodedstring: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
  2913. return;
  2914. }
  2915. if(!scale[0] || !scale[1])
  2916. {
  2917. PRVM_G_FLOAT(OFS_RETURN) = -3;
  2918. VM_Warning(prog, "VM_drawcolorcodedstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
  2919. return;
  2920. }
  2921. if(pos[2] || scale[2])
  2922. VM_Warning(prog, "VM_drawcolorcodedstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
  2923. getdrawfontscale(prog, &sx, &sy);
  2924. DrawQ_String_Scale(pos[0], pos[1], string, 0, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], alpha, flag, NULL, false, getdrawfont(prog));
  2925. if (prog->argc == 6) // also return vector of last color
  2926. VectorCopy(DrawQ_Color, PRVM_G_VECTOR(OFS_RETURN));
  2927. else
  2928. PRVM_G_FLOAT(OFS_RETURN) = 1;
  2929. }
  2930. /*
  2931. =========
  2932. VM_stringwidth
  2933. float stringwidth(string text, float allowColorCodes, float size)
  2934. =========
  2935. */
  2936. void VM_stringwidth(prvm_prog_t *prog)
  2937. {
  2938. const char *string;
  2939. vec2_t szv;
  2940. float mult; // sz is intended font size so we can later add freetype support, mult is font size multiplier in pixels per character cell
  2941. int colors;
  2942. float sx, sy;
  2943. size_t maxlen = 0;
  2944. VM_SAFEPARMCOUNTRANGE(2,3,VM_drawstring);
  2945. getdrawfontscale(prog, &sx, &sy);
  2946. if(prog->argc == 3)
  2947. {
  2948. Vector2Copy(PRVM_G_VECTOR(OFS_PARM2), szv);
  2949. mult = 1;
  2950. }
  2951. else
  2952. {
  2953. // we want the width for 8x8 font size, divided by 8
  2954. Vector2Set(szv, 8, 8);
  2955. mult = 0.125;
  2956. // to make sure snapping is turned off, ALWAYS use a nontrivial scale in this case
  2957. if(sx >= 0.9 && sx <= 1.1)
  2958. {
  2959. mult *= 2;
  2960. sx /= 2;
  2961. sy /= 2;
  2962. }
  2963. }
  2964. string = PRVM_G_STRING(OFS_PARM0);
  2965. colors = (int)PRVM_G_FLOAT(OFS_PARM1);
  2966. PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth_UntilWidth_TrackColors_Scale(string, &maxlen, szv[0], szv[1], sx, sy, NULL, !colors, getdrawfont(prog), 1000000000) * mult;
  2967. /*
  2968. if(prog->argc == 3)
  2969. {
  2970. mult = sz = PRVM_G_FLOAT(OFS_PARM2);
  2971. }
  2972. else
  2973. {
  2974. sz = 8;
  2975. mult = 1;
  2976. }
  2977. string = PRVM_G_STRING(OFS_PARM0);
  2978. colors = (int)PRVM_G_FLOAT(OFS_PARM1);
  2979. PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth(string, 0, !colors, getdrawfont()) * mult; // 1x1 characters, don't actually draw
  2980. */
  2981. }
  2982. /*
  2983. =========
  2984. VM_findfont
  2985. float findfont(string s)
  2986. =========
  2987. */
  2988. static float getdrawfontnum(const char *fontname)
  2989. {
  2990. int i;
  2991. for(i = 0; i < dp_fonts.maxsize; ++i)
  2992. if(!strcmp(dp_fonts.f[i].title, fontname))
  2993. return i;
  2994. return -1;
  2995. }
  2996. void VM_findfont(prvm_prog_t *prog)
  2997. {
  2998. VM_SAFEPARMCOUNT(1,VM_findfont);
  2999. PRVM_G_FLOAT(OFS_RETURN) = getdrawfontnum(PRVM_G_STRING(OFS_PARM0));
  3000. }
  3001. /*
  3002. =========
  3003. VM_loadfont
  3004. float loadfont(string fontname, string fontmaps, string sizes, float slot)
  3005. =========
  3006. */
  3007. void VM_loadfont(prvm_prog_t *prog)
  3008. {
  3009. const char *fontname, *filelist, *sizes, *c, *cm;
  3010. char mainfont[MAX_QPATH];
  3011. int i, numsizes;
  3012. float sz, scale, voffset;
  3013. dp_font_t *f;
  3014. VM_SAFEPARMCOUNTRANGE(3,6,VM_loadfont);
  3015. fontname = PRVM_G_STRING(OFS_PARM0);
  3016. if (!fontname[0])
  3017. fontname = "default";
  3018. filelist = PRVM_G_STRING(OFS_PARM1);
  3019. if (!filelist[0])
  3020. filelist = "gfx/conchars";
  3021. sizes = PRVM_G_STRING(OFS_PARM2);
  3022. if (!sizes[0])
  3023. sizes = "10";
  3024. // find a font
  3025. f = NULL;
  3026. if (prog->argc >= 4)
  3027. {
  3028. i = PRVM_G_FLOAT(OFS_PARM3);
  3029. if (i >= 0 && i < dp_fonts.maxsize)
  3030. {
  3031. f = &dp_fonts.f[i];
  3032. strlcpy(f->title, fontname, sizeof(f->title)); // replace name
  3033. }
  3034. }
  3035. if (!f)
  3036. f = FindFont(fontname, true);
  3037. if (!f)
  3038. {
  3039. PRVM_G_FLOAT(OFS_RETURN) = -1;
  3040. return; // something go wrong
  3041. }
  3042. memset(f->fallbacks, 0, sizeof(f->fallbacks));
  3043. memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
  3044. // first font is handled "normally"
  3045. c = strchr(filelist, ':');
  3046. cm = strchr(filelist, ',');
  3047. if(c && (!cm || c < cm))
  3048. f->req_face = atoi(c+1);
  3049. else
  3050. {
  3051. f->req_face = 0;
  3052. c = cm;
  3053. }
  3054. if(!c || (c - filelist) > MAX_QPATH)
  3055. strlcpy(mainfont, filelist, sizeof(mainfont));
  3056. else
  3057. {
  3058. memcpy(mainfont, filelist, c - filelist);
  3059. mainfont[c - filelist] = 0;
  3060. }
  3061. // handle fallbacks
  3062. for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
  3063. {
  3064. c = strchr(filelist, ',');
  3065. if(!c)
  3066. break;
  3067. filelist = c + 1;
  3068. if(!*filelist)
  3069. break;
  3070. c = strchr(filelist, ':');
  3071. cm = strchr(filelist, ',');
  3072. if(c && (!cm || c < cm))
  3073. f->fallback_faces[i] = atoi(c+1);
  3074. else
  3075. {
  3076. f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
  3077. c = cm;
  3078. }
  3079. if(!c || (c-filelist) > MAX_QPATH)
  3080. {
  3081. strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
  3082. }
  3083. else
  3084. {
  3085. memcpy(f->fallbacks[i], filelist, c - filelist);
  3086. f->fallbacks[i][c - filelist] = 0;
  3087. }
  3088. }
  3089. // handle sizes
  3090. for(i = 0; i < MAX_FONT_SIZES; ++i)
  3091. f->req_sizes[i] = -1;
  3092. for (numsizes = 0,c = sizes;;)
  3093. {
  3094. if (!COM_ParseToken_VM_Tokenize(&c, 0))
  3095. break;
  3096. sz = atof(com_token);
  3097. // detect crap size
  3098. if (sz < 0.001f || sz > 1000.0f)
  3099. {
  3100. VM_Warning(prog, "VM_loadfont: crap size %s", com_token);
  3101. continue;
  3102. }
  3103. // check overflow
  3104. if (numsizes == MAX_FONT_SIZES)
  3105. {
  3106. VM_Warning(prog, "VM_loadfont: MAX_FONT_SIZES = %i exceeded", MAX_FONT_SIZES);
  3107. break;
  3108. }
  3109. f->req_sizes[numsizes] = sz;
  3110. numsizes++;
  3111. }
  3112. // additional scale/hoffset parms
  3113. scale = 1;
  3114. voffset = 0;
  3115. if (prog->argc >= 5)
  3116. {
  3117. scale = PRVM_G_FLOAT(OFS_PARM4);
  3118. if (scale <= 0)
  3119. scale = 1;
  3120. }
  3121. if (prog->argc >= 6)
  3122. voffset = PRVM_G_FLOAT(OFS_PARM5);
  3123. // load
  3124. LoadFont(true, mainfont, f, scale, voffset);
  3125. // return index of loaded font
  3126. PRVM_G_FLOAT(OFS_RETURN) = (f - dp_fonts.f);
  3127. }
  3128. /*
  3129. =========
  3130. VM_drawpic
  3131. float drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag)
  3132. =========
  3133. */
  3134. void VM_drawpic(prvm_prog_t *prog)
  3135. {
  3136. const char *picname;
  3137. prvm_vec_t *size, *pos, *rgb;
  3138. int flag = 0;
  3139. VM_SAFEPARMCOUNTRANGE(5,6,VM_drawpic);
  3140. picname = PRVM_G_STRING(OFS_PARM1);
  3141. VM_CheckEmptyString(prog, picname);
  3142. // is pic cached ? no function yet for that
  3143. if(!1)
  3144. {
  3145. PRVM_G_FLOAT(OFS_RETURN) = -4;
  3146. VM_Warning(prog, "VM_drawpic: %s: %s not cached !\n", prog->name, picname);
  3147. return;
  3148. }
  3149. pos = PRVM_G_VECTOR(OFS_PARM0);
  3150. size = PRVM_G_VECTOR(OFS_PARM2);
  3151. rgb = PRVM_G_VECTOR(OFS_PARM3);
  3152. if (prog->argc >= 6)
  3153. flag = (int) PRVM_G_FLOAT(OFS_PARM5);
  3154. if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
  3155. {
  3156. PRVM_G_FLOAT(OFS_RETURN) = -2;
  3157. VM_Warning(prog, "VM_drawpic: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
  3158. return;
  3159. }
  3160. if(pos[2] || size[2])
  3161. VM_Warning(prog, "VM_drawpic: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
  3162. DrawQ_Pic(pos[0], pos[1], Draw_CachePic_Flags (picname, CACHEPICFLAG_NOTPERSISTENT), size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag);
  3163. PRVM_G_FLOAT(OFS_RETURN) = 1;
  3164. }
  3165. /*
  3166. =========
  3167. VM_drawrotpic
  3168. float drawrotpic(vector position, string pic, vector size, vector org, float angle, vector rgb, float alpha, float flag)
  3169. =========
  3170. */
  3171. void VM_drawrotpic(prvm_prog_t *prog)
  3172. {
  3173. const char *picname;
  3174. prvm_vec_t *size, *pos, *org, *rgb;
  3175. int flag;
  3176. VM_SAFEPARMCOUNT(8,VM_drawrotpic);
  3177. picname = PRVM_G_STRING(OFS_PARM1);
  3178. VM_CheckEmptyString(prog, picname);
  3179. // is pic cached ? no function yet for that
  3180. if(!1)
  3181. {
  3182. PRVM_G_FLOAT(OFS_RETURN) = -4;
  3183. VM_Warning(prog, "VM_drawrotpic: %s: %s not cached !\n", prog->name, picname);
  3184. return;
  3185. }
  3186. pos = PRVM_G_VECTOR(OFS_PARM0);
  3187. size = PRVM_G_VECTOR(OFS_PARM2);
  3188. org = PRVM_G_VECTOR(OFS_PARM3);
  3189. rgb = PRVM_G_VECTOR(OFS_PARM5);
  3190. flag = (int) PRVM_G_FLOAT(OFS_PARM7);
  3191. if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
  3192. {
  3193. PRVM_G_FLOAT(OFS_RETURN) = -2;
  3194. VM_Warning(prog, "VM_drawrotpic: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
  3195. return;
  3196. }
  3197. if(pos[2] || size[2] || org[2])
  3198. VM_Warning(prog, "VM_drawrotpic: z value from pos/size/org discarded\n");
  3199. DrawQ_RotPic(pos[0], pos[1], Draw_CachePic_Flags(picname, CACHEPICFLAG_NOTPERSISTENT), size[0], size[1], org[0], org[1], PRVM_G_FLOAT(OFS_PARM4), rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM6), flag);
  3200. PRVM_G_FLOAT(OFS_RETURN) = 1;
  3201. }
  3202. /*
  3203. =========
  3204. VM_drawsubpic
  3205. float drawsubpic(vector position, vector size, string pic, vector srcPos, vector srcSize, vector rgb, float alpha, float flag)
  3206. =========
  3207. */
  3208. void VM_drawsubpic(prvm_prog_t *prog)
  3209. {
  3210. const char *picname;
  3211. prvm_vec_t *size, *pos, *rgb, *srcPos, *srcSize, alpha;
  3212. int flag;
  3213. VM_SAFEPARMCOUNT(8,VM_drawsubpic);
  3214. picname = PRVM_G_STRING(OFS_PARM2);
  3215. VM_CheckEmptyString(prog, picname);
  3216. // is pic cached ? no function yet for that
  3217. if(!1)
  3218. {
  3219. PRVM_G_FLOAT(OFS_RETURN) = -4;
  3220. VM_Warning(prog, "VM_drawsubpic: %s: %s not cached !\n", prog->name, picname);
  3221. return;
  3222. }
  3223. pos = PRVM_G_VECTOR(OFS_PARM0);
  3224. size = PRVM_G_VECTOR(OFS_PARM1);
  3225. srcPos = PRVM_G_VECTOR(OFS_PARM3);
  3226. srcSize = PRVM_G_VECTOR(OFS_PARM4);
  3227. rgb = PRVM_G_VECTOR(OFS_PARM5);
  3228. alpha = PRVM_G_FLOAT(OFS_PARM6);
  3229. flag = (int) PRVM_G_FLOAT(OFS_PARM7);
  3230. if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
  3231. {
  3232. PRVM_G_FLOAT(OFS_RETURN) = -2;
  3233. VM_Warning(prog, "VM_drawsubpic: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
  3234. return;
  3235. }
  3236. if(pos[2] || size[2])
  3237. VM_Warning(prog, "VM_drawsubpic: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
  3238. DrawQ_SuperPic(pos[0], pos[1], Draw_CachePic_Flags (picname, CACHEPICFLAG_NOTPERSISTENT),
  3239. size[0], size[1],
  3240. srcPos[0], srcPos[1], rgb[0], rgb[1], rgb[2], alpha,
  3241. srcPos[0] + srcSize[0], srcPos[1], rgb[0], rgb[1], rgb[2], alpha,
  3242. srcPos[0], srcPos[1] + srcSize[1], rgb[0], rgb[1], rgb[2], alpha,
  3243. srcPos[0] + srcSize[0], srcPos[1] + srcSize[1], rgb[0], rgb[1], rgb[2], alpha,
  3244. flag);
  3245. PRVM_G_FLOAT(OFS_RETURN) = 1;
  3246. }
  3247. /*
  3248. =========
  3249. VM_drawfill
  3250. float drawfill(vector position, vector size, vector rgb, float alpha, float flag)
  3251. =========
  3252. */
  3253. void VM_drawfill(prvm_prog_t *prog)
  3254. {
  3255. prvm_vec_t *size, *pos, *rgb;
  3256. int flag;
  3257. VM_SAFEPARMCOUNT(5,VM_drawfill);
  3258. pos = PRVM_G_VECTOR(OFS_PARM0);
  3259. size = PRVM_G_VECTOR(OFS_PARM1);
  3260. rgb = PRVM_G_VECTOR(OFS_PARM2);
  3261. flag = (int) PRVM_G_FLOAT(OFS_PARM4);
  3262. if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
  3263. {
  3264. PRVM_G_FLOAT(OFS_RETURN) = -2;
  3265. VM_Warning(prog, "VM_drawfill: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
  3266. return;
  3267. }
  3268. if(pos[2] || size[2])
  3269. VM_Warning(prog, "VM_drawfill: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
  3270. DrawQ_Fill(pos[0], pos[1], size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM3), flag);
  3271. PRVM_G_FLOAT(OFS_RETURN) = 1;
  3272. }
  3273. /*
  3274. =========
  3275. VM_drawsetcliparea
  3276. drawsetcliparea(float x, float y, float width, float height)
  3277. =========
  3278. */
  3279. void VM_drawsetcliparea(prvm_prog_t *prog)
  3280. {
  3281. float x,y,w,h;
  3282. VM_SAFEPARMCOUNT(4,VM_drawsetcliparea);
  3283. x = bound(0, PRVM_G_FLOAT(OFS_PARM0), vid_conwidth.integer);
  3284. y = bound(0, PRVM_G_FLOAT(OFS_PARM1), vid_conheight.integer);
  3285. w = bound(0, PRVM_G_FLOAT(OFS_PARM2) + PRVM_G_FLOAT(OFS_PARM0) - x, (vid_conwidth.integer - x));
  3286. h = bound(0, PRVM_G_FLOAT(OFS_PARM3) + PRVM_G_FLOAT(OFS_PARM1) - y, (vid_conheight.integer - y));
  3287. DrawQ_SetClipArea(x, y, w, h);
  3288. }
  3289. /*
  3290. =========
  3291. VM_drawresetcliparea
  3292. drawresetcliparea()
  3293. =========
  3294. */
  3295. void VM_drawresetcliparea(prvm_prog_t *prog)
  3296. {
  3297. VM_SAFEPARMCOUNT(0,VM_drawresetcliparea);
  3298. DrawQ_ResetClipArea();
  3299. }
  3300. /*
  3301. =========
  3302. VM_getimagesize
  3303. vector getimagesize(string pic)
  3304. =========
  3305. */
  3306. void VM_getimagesize(prvm_prog_t *prog)
  3307. {
  3308. const char *p;
  3309. cachepic_t *pic;
  3310. VM_SAFEPARMCOUNT(1,VM_getimagesize);
  3311. p = PRVM_G_STRING(OFS_PARM0);
  3312. VM_CheckEmptyString(prog, p);
  3313. pic = Draw_CachePic_Flags (p, CACHEPICFLAG_NOTPERSISTENT);
  3314. if( pic->tex == r_texture_notexture )
  3315. {
  3316. PRVM_G_VECTOR(OFS_RETURN)[0] = 0;
  3317. PRVM_G_VECTOR(OFS_RETURN)[1] = 0;
  3318. }
  3319. else
  3320. {
  3321. PRVM_G_VECTOR(OFS_RETURN)[0] = pic->width;
  3322. PRVM_G_VECTOR(OFS_RETURN)[1] = pic->height;
  3323. }
  3324. PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
  3325. }
  3326. /*
  3327. =========
  3328. VM_keynumtostring
  3329. string keynumtostring(float keynum)
  3330. =========
  3331. */
  3332. void VM_keynumtostring (prvm_prog_t *prog)
  3333. {
  3334. char tinystr[2];
  3335. VM_SAFEPARMCOUNT(1, VM_keynumtostring);
  3336. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, Key_KeynumToString((int)PRVM_G_FLOAT(OFS_PARM0), tinystr, sizeof(tinystr)));
  3337. }
  3338. /*
  3339. =========
  3340. VM_findkeysforcommand
  3341. string findkeysforcommand(string command, float bindmap)
  3342. the returned string is an altstring
  3343. =========
  3344. */
  3345. #define FKFC_NUMKEYS 5
  3346. void M_FindKeysForCommand(const char *command, int *keys);
  3347. void VM_findkeysforcommand(prvm_prog_t *prog)
  3348. {
  3349. const char *cmd;
  3350. char ret[VM_STRINGTEMP_LENGTH];
  3351. int keys[FKFC_NUMKEYS];
  3352. int i;
  3353. int bindmap;
  3354. char vabuf[1024];
  3355. VM_SAFEPARMCOUNTRANGE(1, 2, VM_findkeysforcommand);
  3356. cmd = PRVM_G_STRING(OFS_PARM0);
  3357. if(prog->argc == 2)
  3358. bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM1), MAX_BINDMAPS-1);
  3359. else
  3360. bindmap = 0; // consistent to "bind"
  3361. VM_CheckEmptyString(prog, cmd);
  3362. Key_FindKeysForCommand(cmd, keys, FKFC_NUMKEYS, bindmap);
  3363. ret[0] = 0;
  3364. for(i = 0; i < FKFC_NUMKEYS; i++)
  3365. strlcat(ret, va(vabuf, sizeof(vabuf), " \'%i\'", keys[i]), sizeof(ret));
  3366. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, ret);
  3367. }
  3368. /*
  3369. =========
  3370. VM_stringtokeynum
  3371. float stringtokeynum(string key)
  3372. =========
  3373. */
  3374. void VM_stringtokeynum (prvm_prog_t *prog)
  3375. {
  3376. VM_SAFEPARMCOUNT( 1, VM_keynumtostring );
  3377. PRVM_G_FLOAT(OFS_RETURN) = Key_StringToKeynum(PRVM_G_STRING(OFS_PARM0));
  3378. }
  3379. /*
  3380. =========
  3381. VM_getkeybind
  3382. string getkeybind(float key, float bindmap)
  3383. =========
  3384. */
  3385. void VM_getkeybind (prvm_prog_t *prog)
  3386. {
  3387. int bindmap;
  3388. VM_SAFEPARMCOUNTRANGE(1, 2, VM_CL_getkeybind);
  3389. if(prog->argc == 2)
  3390. bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM1), MAX_BINDMAPS-1);
  3391. else
  3392. bindmap = 0; // consistent to "bind"
  3393. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, Key_GetBind((int)PRVM_G_FLOAT(OFS_PARM0), bindmap));
  3394. }
  3395. /*
  3396. =========
  3397. VM_setkeybind
  3398. float setkeybind(float key, string cmd, float bindmap)
  3399. =========
  3400. */
  3401. void VM_setkeybind (prvm_prog_t *prog)
  3402. {
  3403. int bindmap;
  3404. VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_setkeybind);
  3405. if(prog->argc == 3)
  3406. bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM2), MAX_BINDMAPS-1);
  3407. else
  3408. bindmap = 0; // consistent to "bind"
  3409. PRVM_G_FLOAT(OFS_RETURN) = 0;
  3410. if(Key_SetBinding((int)PRVM_G_FLOAT(OFS_PARM0), bindmap, PRVM_G_STRING(OFS_PARM1)))
  3411. PRVM_G_FLOAT(OFS_RETURN) = 1;
  3412. }
  3413. /*
  3414. =========
  3415. VM_getbindmap
  3416. vector getbindmaps()
  3417. =========
  3418. */
  3419. void VM_getbindmaps (prvm_prog_t *prog)
  3420. {
  3421. int fg, bg;
  3422. VM_SAFEPARMCOUNT(0, VM_CL_getbindmap);
  3423. Key_GetBindMap(&fg, &bg);
  3424. PRVM_G_VECTOR(OFS_RETURN)[0] = fg;
  3425. PRVM_G_VECTOR(OFS_RETURN)[1] = bg;
  3426. PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
  3427. }
  3428. /*
  3429. =========
  3430. VM_setbindmap
  3431. float setbindmaps(vector bindmap)
  3432. =========
  3433. */
  3434. void VM_setbindmaps (prvm_prog_t *prog)
  3435. {
  3436. VM_SAFEPARMCOUNT(1, VM_CL_setbindmap);
  3437. PRVM_G_FLOAT(OFS_RETURN) = 0;
  3438. if(PRVM_G_VECTOR(OFS_PARM0)[2] == 0)
  3439. if(Key_SetBindMap((int)PRVM_G_VECTOR(OFS_PARM0)[0], (int)PRVM_G_VECTOR(OFS_PARM0)[1]))
  3440. PRVM_G_FLOAT(OFS_RETURN) = 1;
  3441. }
  3442. // CL_Video interface functions
  3443. /*
  3444. ========================
  3445. VM_cin_open
  3446. float cin_open(string file, string name)
  3447. ========================
  3448. */
  3449. void VM_cin_open(prvm_prog_t *prog)
  3450. {
  3451. const char *file;
  3452. const char *name;
  3453. VM_SAFEPARMCOUNT( 2, VM_cin_open );
  3454. file = PRVM_G_STRING( OFS_PARM0 );
  3455. name = PRVM_G_STRING( OFS_PARM1 );
  3456. VM_CheckEmptyString(prog, file );
  3457. VM_CheckEmptyString(prog, name );
  3458. if( CL_OpenVideo( file, name, MENUOWNER, "" ) )
  3459. PRVM_G_FLOAT( OFS_RETURN ) = 1;
  3460. else
  3461. PRVM_G_FLOAT( OFS_RETURN ) = 0;
  3462. }
  3463. /*
  3464. ========================
  3465. VM_cin_close
  3466. void cin_close(string name)
  3467. ========================
  3468. */
  3469. void VM_cin_close(prvm_prog_t *prog)
  3470. {
  3471. const char *name;
  3472. VM_SAFEPARMCOUNT( 1, VM_cin_close );
  3473. name = PRVM_G_STRING( OFS_PARM0 );
  3474. VM_CheckEmptyString(prog, name );
  3475. CL_CloseVideo( CL_GetVideoByName( name ) );
  3476. }
  3477. /*
  3478. ========================
  3479. VM_cin_setstate
  3480. void cin_setstate(string name, float type)
  3481. ========================
  3482. */
  3483. void VM_cin_setstate(prvm_prog_t *prog)
  3484. {
  3485. const char *name;
  3486. clvideostate_t state;
  3487. clvideo_t *video;
  3488. VM_SAFEPARMCOUNT( 2, VM_cin_netstate );
  3489. name = PRVM_G_STRING( OFS_PARM0 );
  3490. VM_CheckEmptyString(prog, name );
  3491. state = (clvideostate_t)((int)PRVM_G_FLOAT( OFS_PARM1 ));
  3492. video = CL_GetVideoByName( name );
  3493. if( video && state > CLVIDEO_UNUSED && state < CLVIDEO_STATECOUNT )
  3494. CL_SetVideoState( video, state );
  3495. }
  3496. /*
  3497. ========================
  3498. VM_cin_getstate
  3499. float cin_getstate(string name)
  3500. ========================
  3501. */
  3502. void VM_cin_getstate(prvm_prog_t *prog)
  3503. {
  3504. const char *name;
  3505. clvideo_t *video;
  3506. VM_SAFEPARMCOUNT( 1, VM_cin_getstate );
  3507. name = PRVM_G_STRING( OFS_PARM0 );
  3508. VM_CheckEmptyString(prog, name );
  3509. video = CL_GetVideoByName( name );
  3510. if( video )
  3511. PRVM_G_FLOAT( OFS_RETURN ) = (int)video->state;
  3512. else
  3513. PRVM_G_FLOAT( OFS_RETURN ) = 0;
  3514. }
  3515. /*
  3516. ========================
  3517. VM_cin_restart
  3518. void cin_restart(string name)
  3519. ========================
  3520. */
  3521. void VM_cin_restart(prvm_prog_t *prog)
  3522. {
  3523. const char *name;
  3524. clvideo_t *video;
  3525. VM_SAFEPARMCOUNT( 1, VM_cin_restart );
  3526. name = PRVM_G_STRING( OFS_PARM0 );
  3527. VM_CheckEmptyString(prog, name );
  3528. video = CL_GetVideoByName( name );
  3529. if( video )
  3530. CL_RestartVideo( video );
  3531. }
  3532. /*
  3533. ========================
  3534. VM_gecko_create
  3535. float[bool] gecko_create( string name )
  3536. ========================
  3537. */
  3538. void VM_gecko_create(prvm_prog_t *prog) {
  3539. // REMOVED
  3540. PRVM_G_FLOAT( OFS_RETURN ) = 0;
  3541. }
  3542. /*
  3543. ========================
  3544. VM_gecko_destroy
  3545. void gecko_destroy( string name )
  3546. ========================
  3547. */
  3548. void VM_gecko_destroy(prvm_prog_t *prog) {
  3549. // REMOVED
  3550. }
  3551. /*
  3552. ========================
  3553. VM_gecko_navigate
  3554. void gecko_navigate( string name, string URI )
  3555. ========================
  3556. */
  3557. void VM_gecko_navigate(prvm_prog_t *prog) {
  3558. // REMOVED
  3559. }
  3560. /*
  3561. ========================
  3562. VM_gecko_keyevent
  3563. float[bool] gecko_keyevent( string name, float key, float eventtype )
  3564. ========================
  3565. */
  3566. void VM_gecko_keyevent(prvm_prog_t *prog) {
  3567. // REMOVED
  3568. PRVM_G_FLOAT( OFS_RETURN ) = 0;
  3569. }
  3570. /*
  3571. ========================
  3572. VM_gecko_movemouse
  3573. void gecko_mousemove( string name, float x, float y )
  3574. ========================
  3575. */
  3576. void VM_gecko_movemouse(prvm_prog_t *prog) {
  3577. // REMOVED
  3578. }
  3579. /*
  3580. ========================
  3581. VM_gecko_resize
  3582. void gecko_resize( string name, float w, float h )
  3583. ========================
  3584. */
  3585. void VM_gecko_resize(prvm_prog_t *prog) {
  3586. // REMOVED
  3587. }
  3588. /*
  3589. ========================
  3590. VM_gecko_get_texture_extent
  3591. vector gecko_get_texture_extent( string name )
  3592. ========================
  3593. */
  3594. void VM_gecko_get_texture_extent(prvm_prog_t *prog) {
  3595. // REMOVED
  3596. PRVM_G_VECTOR(OFS_RETURN)[0] = 0;
  3597. PRVM_G_VECTOR(OFS_RETURN)[1] = 0;
  3598. }
  3599. /*
  3600. ==============
  3601. VM_makevectors
  3602. Writes new values for v_forward, v_up, and v_right based on angles
  3603. void makevectors(vector angle)
  3604. ==============
  3605. */
  3606. void VM_makevectors (prvm_prog_t *prog)
  3607. {
  3608. vec3_t angles, forward, right, up;
  3609. VM_SAFEPARMCOUNT(1, VM_makevectors);
  3610. VectorCopy(PRVM_G_VECTOR(OFS_PARM0), angles);
  3611. AngleVectors(angles, forward, right, up);
  3612. VectorCopy(forward, PRVM_gameglobalvector(v_forward));
  3613. VectorCopy(right, PRVM_gameglobalvector(v_right));
  3614. VectorCopy(up, PRVM_gameglobalvector(v_up));
  3615. }
  3616. /*
  3617. ==============
  3618. VM_vectorvectors
  3619. Writes new values for v_forward, v_up, and v_right based on the given forward vector
  3620. vectorvectors(vector)
  3621. ==============
  3622. */
  3623. void VM_vectorvectors (prvm_prog_t *prog)
  3624. {
  3625. vec3_t forward, right, up;
  3626. VM_SAFEPARMCOUNT(1, VM_vectorvectors);
  3627. VectorNormalize2(PRVM_G_VECTOR(OFS_PARM0), forward);
  3628. VectorVectors(forward, right, up);
  3629. VectorCopy(forward, PRVM_gameglobalvector(v_forward));
  3630. VectorCopy(right, PRVM_gameglobalvector(v_right));
  3631. VectorCopy(up, PRVM_gameglobalvector(v_up));
  3632. }
  3633. /*
  3634. ========================
  3635. VM_drawline
  3636. void drawline(float width, vector pos1, vector pos2, vector rgb, float alpha, float flags)
  3637. ========================
  3638. */
  3639. void VM_drawline (prvm_prog_t *prog)
  3640. {
  3641. prvm_vec_t *c1, *c2, *rgb;
  3642. float alpha, width;
  3643. unsigned char flags;
  3644. VM_SAFEPARMCOUNT(6, VM_drawline);
  3645. width = PRVM_G_FLOAT(OFS_PARM0);
  3646. c1 = PRVM_G_VECTOR(OFS_PARM1);
  3647. c2 = PRVM_G_VECTOR(OFS_PARM2);
  3648. rgb = PRVM_G_VECTOR(OFS_PARM3);
  3649. alpha = PRVM_G_FLOAT(OFS_PARM4);
  3650. flags = (int)PRVM_G_FLOAT(OFS_PARM5);
  3651. DrawQ_Line(width, c1[0], c1[1], c2[0], c2[1], rgb[0], rgb[1], rgb[2], alpha, flags);
  3652. }
  3653. // float(float number, float quantity) bitshift (EXT_BITSHIFT)
  3654. void VM_bitshift (prvm_prog_t *prog)
  3655. {
  3656. prvm_int_t n1, n2;
  3657. VM_SAFEPARMCOUNT(2, VM_bitshift);
  3658. n1 = (prvm_int_t)fabs((prvm_vec_t)((prvm_int_t)PRVM_G_FLOAT(OFS_PARM0)));
  3659. n2 = (prvm_int_t)PRVM_G_FLOAT(OFS_PARM1);
  3660. if(!n1)
  3661. PRVM_G_FLOAT(OFS_RETURN) = n1;
  3662. else
  3663. if(n2 < 0)
  3664. PRVM_G_FLOAT(OFS_RETURN) = (n1 >> -n2);
  3665. else
  3666. PRVM_G_FLOAT(OFS_RETURN) = (n1 << n2);
  3667. }
  3668. ////////////////////////////////////////
  3669. // AltString functions
  3670. ////////////////////////////////////////
  3671. /*
  3672. ========================
  3673. VM_altstr_count
  3674. float altstr_count(string)
  3675. ========================
  3676. */
  3677. void VM_altstr_count(prvm_prog_t *prog)
  3678. {
  3679. const char *altstr, *pos;
  3680. int count;
  3681. VM_SAFEPARMCOUNT( 1, VM_altstr_count );
  3682. altstr = PRVM_G_STRING( OFS_PARM0 );
  3683. //VM_CheckEmptyString(prog, altstr );
  3684. for( count = 0, pos = altstr ; *pos ; pos++ ) {
  3685. if( *pos == '\\' ) {
  3686. if( !*++pos ) {
  3687. break;
  3688. }
  3689. } else if( *pos == '\'' ) {
  3690. count++;
  3691. }
  3692. }
  3693. PRVM_G_FLOAT( OFS_RETURN ) = (prvm_vec_t) (count / 2);
  3694. }
  3695. /*
  3696. ========================
  3697. VM_altstr_prepare
  3698. string altstr_prepare(string)
  3699. ========================
  3700. */
  3701. void VM_altstr_prepare(prvm_prog_t *prog)
  3702. {
  3703. const char *instr, *in;
  3704. char outstr[VM_STRINGTEMP_LENGTH];
  3705. size_t outpos;
  3706. VM_SAFEPARMCOUNT( 1, VM_altstr_prepare );
  3707. instr = PRVM_G_STRING( OFS_PARM0 );
  3708. for (in = instr, outpos = 0; *in && outpos < sizeof(outstr) - 1; ++in)
  3709. {
  3710. if (*in == '\'' && outpos < sizeof(outstr) - 2)
  3711. {
  3712. outstr[outpos++] = '\\';
  3713. outstr[outpos++] = '\'';
  3714. }
  3715. else
  3716. outstr[outpos++] = *in;
  3717. }
  3718. outstr[outpos] = 0;
  3719. PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog, outstr );
  3720. }
  3721. /*
  3722. ========================
  3723. VM_altstr_get
  3724. string altstr_get(string, float)
  3725. ========================
  3726. */
  3727. void VM_altstr_get(prvm_prog_t *prog)
  3728. {
  3729. const char *altstr, *pos;
  3730. char *out;
  3731. int count, size;
  3732. char outstr[VM_STRINGTEMP_LENGTH];
  3733. VM_SAFEPARMCOUNT( 2, VM_altstr_get );
  3734. altstr = PRVM_G_STRING( OFS_PARM0 );
  3735. count = (int)PRVM_G_FLOAT( OFS_PARM1 );
  3736. count = count * 2 + 1;
  3737. for( pos = altstr ; *pos && count ; pos++ )
  3738. if( *pos == '\\' ) {
  3739. if( !*++pos )
  3740. break;
  3741. } else if( *pos == '\'' )
  3742. count--;
  3743. if( !*pos ) {
  3744. PRVM_G_INT( OFS_RETURN ) = 0;
  3745. return;
  3746. }
  3747. for( out = outstr, size = sizeof(outstr) - 1 ; size && *pos ; size--, pos++, out++ )
  3748. if( *pos == '\\' ) {
  3749. if( !*++pos )
  3750. break;
  3751. *out = *pos;
  3752. size--;
  3753. } else if( *pos == '\'' )
  3754. break;
  3755. else
  3756. *out = *pos;
  3757. *out = 0;
  3758. PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog, outstr );
  3759. }
  3760. /*
  3761. ========================
  3762. VM_altstr_set
  3763. string altstr_set(string altstr, float num, string set)
  3764. ========================
  3765. */
  3766. void VM_altstr_set(prvm_prog_t *prog)
  3767. {
  3768. int num;
  3769. const char *altstr, *str;
  3770. const char *in;
  3771. char *out;
  3772. char outstr[VM_STRINGTEMP_LENGTH];
  3773. VM_SAFEPARMCOUNT( 3, VM_altstr_set );
  3774. altstr = PRVM_G_STRING( OFS_PARM0 );
  3775. num = (int)PRVM_G_FLOAT( OFS_PARM1 );
  3776. str = PRVM_G_STRING( OFS_PARM2 );
  3777. out = outstr;
  3778. for( num = num * 2 + 1, in = altstr; *in && num; *out++ = *in++ )
  3779. if( *in == '\\' ) {
  3780. if( !*++in ) {
  3781. break;
  3782. }
  3783. } else if( *in == '\'' ) {
  3784. num--;
  3785. }
  3786. // copy set in
  3787. for( ; *str; *out++ = *str++ );
  3788. // now jump over the old content
  3789. for( ; *in ; in++ )
  3790. if( *in == '\'' || (*in == '\\' && !*++in) )
  3791. break;
  3792. strlcpy(out, in, outstr + sizeof(outstr) - out);
  3793. PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog, outstr );
  3794. }
  3795. /*
  3796. ========================
  3797. VM_altstr_ins
  3798. insert after num
  3799. string altstr_ins(string altstr, float num, string set)
  3800. ========================
  3801. */
  3802. void VM_altstr_ins(prvm_prog_t *prog)
  3803. {
  3804. int num;
  3805. const char *set;
  3806. const char *in;
  3807. char *out;
  3808. char outstr[VM_STRINGTEMP_LENGTH];
  3809. VM_SAFEPARMCOUNT(3, VM_altstr_ins);
  3810. in = PRVM_G_STRING( OFS_PARM0 );
  3811. num = (int)PRVM_G_FLOAT( OFS_PARM1 );
  3812. set = PRVM_G_STRING( OFS_PARM2 );
  3813. out = outstr;
  3814. for( num = num * 2 + 2 ; *in && num > 0 ; *out++ = *in++ )
  3815. if( *in == '\\' ) {
  3816. if( !*++in ) {
  3817. break;
  3818. }
  3819. } else if( *in == '\'' ) {
  3820. num--;
  3821. }
  3822. *out++ = '\'';
  3823. for( ; *set ; *out++ = *set++ );
  3824. *out++ = '\'';
  3825. strlcpy(out, in, outstr + sizeof(outstr) - out);
  3826. PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog, outstr );
  3827. }
  3828. ////////////////////////////////////////
  3829. // BufString functions
  3830. ////////////////////////////////////////
  3831. //[515]: string buffers support
  3832. static size_t stringbuffers_sortlength;
  3833. static void BufStr_Expand(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer, int strindex)
  3834. {
  3835. if (stringbuffer->max_strings <= strindex)
  3836. {
  3837. char **oldstrings = stringbuffer->strings;
  3838. stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
  3839. while (stringbuffer->max_strings <= strindex)
  3840. stringbuffer->max_strings *= 2;
  3841. stringbuffer->strings = (char **) Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
  3842. if (stringbuffer->num_strings > 0)
  3843. memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
  3844. if (oldstrings)
  3845. Mem_Free(oldstrings);
  3846. }
  3847. }
  3848. static void BufStr_Shrink(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer)
  3849. {
  3850. // reduce num_strings if there are empty string slots at the end
  3851. while (stringbuffer->num_strings > 0 && stringbuffer->strings[stringbuffer->num_strings - 1] == NULL)
  3852. stringbuffer->num_strings--;
  3853. // if empty, free the string pointer array
  3854. if (stringbuffer->num_strings == 0)
  3855. {
  3856. stringbuffer->max_strings = 0;
  3857. if (stringbuffer->strings)
  3858. Mem_Free(stringbuffer->strings);
  3859. stringbuffer->strings = NULL;
  3860. }
  3861. }
  3862. static int BufStr_SortStringsUP (const void *in1, const void *in2)
  3863. {
  3864. const char *a, *b;
  3865. a = *((const char **) in1);
  3866. b = *((const char **) in2);
  3867. if(!a || !a[0]) return 1;
  3868. if(!b || !b[0]) return -1;
  3869. return strncmp(a, b, stringbuffers_sortlength);
  3870. }
  3871. static int BufStr_SortStringsDOWN (const void *in1, const void *in2)
  3872. {
  3873. const char *a, *b;
  3874. a = *((const char **) in1);
  3875. b = *((const char **) in2);
  3876. if(!a || !a[0]) return 1;
  3877. if(!b || !b[0]) return -1;
  3878. return strncmp(b, a, stringbuffers_sortlength);
  3879. }
  3880. prvm_stringbuffer_t *BufStr_FindCreateReplace (prvm_prog_t *prog, int bufindex, int flags, const char *format)
  3881. {
  3882. prvm_stringbuffer_t *stringbuffer;
  3883. int i;
  3884. if (bufindex < 0)
  3885. return NULL;
  3886. // find buffer with wanted index
  3887. if (bufindex < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray))
  3888. {
  3889. if ( (stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, bufindex)) )
  3890. {
  3891. if (stringbuffer->flags & STRINGBUFFER_TEMP)
  3892. stringbuffer->flags = flags; // created but has not been used yet
  3893. return stringbuffer;
  3894. }
  3895. return NULL;
  3896. }
  3897. // allocate new buffer with wanted index
  3898. while(1)
  3899. {
  3900. stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecord(&prog->stringbuffersarray);
  3901. stringbuffer->flags = STRINGBUFFER_TEMP;
  3902. for (i = 0;stringbuffer != Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);i++);
  3903. if (i == bufindex)
  3904. {
  3905. stringbuffer->flags = flags; // mark as used
  3906. break;
  3907. }
  3908. }
  3909. return stringbuffer;
  3910. }
  3911. void BufStr_Set(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer, int strindex, const char *str)
  3912. {
  3913. size_t alloclen;
  3914. if (!stringbuffer || strindex < 0)
  3915. return;
  3916. BufStr_Expand(prog, stringbuffer, strindex);
  3917. stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
  3918. if (stringbuffer->strings[strindex])
  3919. Mem_Free(stringbuffer->strings[strindex]);
  3920. stringbuffer->strings[strindex] = NULL;
  3921. if (str)
  3922. {
  3923. // not the NULL string!
  3924. alloclen = strlen(str) + 1;
  3925. stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
  3926. memcpy(stringbuffer->strings[strindex], str, alloclen);
  3927. }
  3928. BufStr_Shrink(prog, stringbuffer);
  3929. }
  3930. void BufStr_Del(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer)
  3931. {
  3932. int i;
  3933. if (!stringbuffer)
  3934. return;
  3935. for (i = 0;i < stringbuffer->num_strings;i++)
  3936. if (stringbuffer->strings[i])
  3937. Mem_Free(stringbuffer->strings[i]);
  3938. if (stringbuffer->strings)
  3939. Mem_Free(stringbuffer->strings);
  3940. if(stringbuffer->origin)
  3941. PRVM_Free((char *)stringbuffer->origin);
  3942. Mem_ExpandableArray_FreeRecord(&prog->stringbuffersarray, stringbuffer);
  3943. }
  3944. void BufStr_Flush(prvm_prog_t *prog)
  3945. {
  3946. prvm_stringbuffer_t *stringbuffer;
  3947. int i, numbuffers;
  3948. numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
  3949. for (i = 0; i < numbuffers; i++)
  3950. if ( (stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i)) )
  3951. BufStr_Del(prog, stringbuffer);
  3952. Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
  3953. }
  3954. /*
  3955. ========================
  3956. VM_buf_create
  3957. creates new buffer, and returns it's index, returns -1 if failed
  3958. float buf_create(prvm_prog_t *prog) = #460;
  3959. float newbuf(string format, float flags) = #460;
  3960. ========================
  3961. */
  3962. void VM_buf_create (prvm_prog_t *prog)
  3963. {
  3964. prvm_stringbuffer_t *stringbuffer;
  3965. int i;
  3966. VM_SAFEPARMCOUNTRANGE(0, 2, VM_buf_create);
  3967. // VorteX: optional parm1 (buffer format) is unfinished, to keep intact with future databuffers extension must be set to "string"
  3968. if(prog->argc >= 1 && strcmp(PRVM_G_STRING(OFS_PARM0), "string"))
  3969. {
  3970. PRVM_G_FLOAT(OFS_RETURN) = -1;
  3971. return;
  3972. }
  3973. stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecord(&prog->stringbuffersarray);
  3974. for (i = 0;stringbuffer != Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);i++);
  3975. stringbuffer->origin = PRVM_AllocationOrigin(prog);
  3976. // optional flags parm
  3977. if (prog->argc >= 2)
  3978. stringbuffer->flags = (int)PRVM_G_FLOAT(OFS_PARM1) & STRINGBUFFER_QCFLAGS;
  3979. PRVM_G_FLOAT(OFS_RETURN) = i;
  3980. }
  3981. /*
  3982. ========================
  3983. VM_buf_del
  3984. deletes buffer and all strings in it
  3985. void buf_del(float bufhandle) = #461;
  3986. ========================
  3987. */
  3988. void VM_buf_del (prvm_prog_t *prog)
  3989. {
  3990. prvm_stringbuffer_t *stringbuffer;
  3991. VM_SAFEPARMCOUNT(1, VM_buf_del);
  3992. stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
  3993. if (stringbuffer)
  3994. BufStr_Del(prog, stringbuffer);
  3995. else
  3996. {
  3997. VM_Warning(prog, "VM_buf_del: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
  3998. return;
  3999. }
  4000. }
  4001. /*
  4002. ========================
  4003. VM_buf_getsize
  4004. how many strings are stored in buffer
  4005. float buf_getsize(float bufhandle) = #462;
  4006. ========================
  4007. */
  4008. void VM_buf_getsize (prvm_prog_t *prog)
  4009. {
  4010. prvm_stringbuffer_t *stringbuffer;
  4011. VM_SAFEPARMCOUNT(1, VM_buf_getsize);
  4012. stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
  4013. if(!stringbuffer)
  4014. {
  4015. PRVM_G_FLOAT(OFS_RETURN) = -1;
  4016. VM_Warning(prog, "VM_buf_getsize: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
  4017. return;
  4018. }
  4019. else
  4020. PRVM_G_FLOAT(OFS_RETURN) = stringbuffer->num_strings;
  4021. }
  4022. /*
  4023. ========================
  4024. VM_buf_copy
  4025. copy all content from one buffer to another, make sure it exists
  4026. void buf_copy(float bufhandle_from, float bufhandle_to) = #463;
  4027. ========================
  4028. */
  4029. void VM_buf_copy (prvm_prog_t *prog)
  4030. {
  4031. prvm_stringbuffer_t *srcstringbuffer, *dststringbuffer;
  4032. int i;
  4033. VM_SAFEPARMCOUNT(2, VM_buf_copy);
  4034. srcstringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
  4035. if(!srcstringbuffer)
  4036. {
  4037. VM_Warning(prog, "VM_buf_copy: invalid source buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
  4038. return;
  4039. }
  4040. i = (int)PRVM_G_FLOAT(OFS_PARM1);
  4041. if(i == (int)PRVM_G_FLOAT(OFS_PARM0))
  4042. {
  4043. VM_Warning(prog, "VM_buf_copy: source == destination (%i) in %s\n", i, prog->name);
  4044. return;
  4045. }
  4046. dststringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
  4047. if(!dststringbuffer)
  4048. {
  4049. VM_Warning(prog, "VM_buf_copy: invalid destination buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), prog->name);
  4050. return;
  4051. }
  4052. for (i = 0;i < dststringbuffer->num_strings;i++)
  4053. if (dststringbuffer->strings[i])
  4054. Mem_Free(dststringbuffer->strings[i]);
  4055. if (dststringbuffer->strings)
  4056. Mem_Free(dststringbuffer->strings);
  4057. *dststringbuffer = *srcstringbuffer;
  4058. if (dststringbuffer->max_strings)
  4059. dststringbuffer->strings = (char **)Mem_Alloc(prog->progs_mempool, sizeof(dststringbuffer->strings[0]) * dststringbuffer->max_strings);
  4060. for (i = 0;i < dststringbuffer->num_strings;i++)
  4061. {
  4062. if (srcstringbuffer->strings[i])
  4063. {
  4064. size_t stringlen;
  4065. stringlen = strlen(srcstringbuffer->strings[i]) + 1;
  4066. dststringbuffer->strings[i] = (char *)Mem_Alloc(prog->progs_mempool, stringlen);
  4067. memcpy(dststringbuffer->strings[i], srcstringbuffer->strings[i], stringlen);
  4068. }
  4069. }
  4070. }
  4071. /*
  4072. ========================
  4073. VM_buf_sort
  4074. sort buffer by beginnings of strings (cmplength defaults it's length)
  4075. "backward == TRUE" means that sorting goes upside-down
  4076. void buf_sort(float bufhandle, float cmplength, float backward) = #464;
  4077. ========================
  4078. */
  4079. void VM_buf_sort (prvm_prog_t *prog)
  4080. {
  4081. prvm_stringbuffer_t *stringbuffer;
  4082. VM_SAFEPARMCOUNT(3, VM_buf_sort);
  4083. stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
  4084. if(!stringbuffer)
  4085. {
  4086. VM_Warning(prog, "VM_buf_sort: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
  4087. return;
  4088. }
  4089. if(stringbuffer->num_strings <= 0)
  4090. {
  4091. VM_Warning(prog, "VM_buf_sort: tried to sort empty buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
  4092. return;
  4093. }
  4094. stringbuffers_sortlength = (int)PRVM_G_FLOAT(OFS_PARM1);
  4095. if(stringbuffers_sortlength <= 0)
  4096. stringbuffers_sortlength = 0x7FFFFFFF;
  4097. if(!PRVM_G_FLOAT(OFS_PARM2))
  4098. qsort(stringbuffer->strings, stringbuffer->num_strings, sizeof(char*), BufStr_SortStringsUP);
  4099. else
  4100. qsort(stringbuffer->strings, stringbuffer->num_strings, sizeof(char*), BufStr_SortStringsDOWN);
  4101. BufStr_Shrink(prog, stringbuffer);
  4102. }
  4103. /*
  4104. ========================
  4105. VM_buf_implode
  4106. concantenates all buffer string into one with "glue" separator and returns it as tempstring
  4107. string buf_implode(float bufhandle, string glue) = #465;
  4108. ========================
  4109. */
  4110. void VM_buf_implode (prvm_prog_t *prog)
  4111. {
  4112. prvm_stringbuffer_t *stringbuffer;
  4113. char k[VM_STRINGTEMP_LENGTH];
  4114. const char *sep;
  4115. int i;
  4116. size_t l;
  4117. VM_SAFEPARMCOUNT(2, VM_buf_implode);
  4118. stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
  4119. PRVM_G_INT(OFS_RETURN) = OFS_NULL;
  4120. if(!stringbuffer)
  4121. {
  4122. VM_Warning(prog, "VM_buf_implode: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
  4123. return;
  4124. }
  4125. if(!stringbuffer->num_strings)
  4126. return;
  4127. sep = PRVM_G_STRING(OFS_PARM1);
  4128. k[0] = 0;
  4129. for(l = i = 0;i < stringbuffer->num_strings;i++)
  4130. {
  4131. if(stringbuffer->strings[i])
  4132. {
  4133. l += (i > 0 ? strlen(sep) : 0) + strlen(stringbuffer->strings[i]);
  4134. if (l >= sizeof(k) - 1)
  4135. break;
  4136. strlcat(k, sep, sizeof(k));
  4137. strlcat(k, stringbuffer->strings[i], sizeof(k));
  4138. }
  4139. }
  4140. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, k);
  4141. }
  4142. /*
  4143. ========================
  4144. VM_bufstr_get
  4145. get a string from buffer, returns tempstring, dont str_unzone it!
  4146. string bufstr_get(float bufhandle, float string_index) = #465;
  4147. ========================
  4148. */
  4149. void VM_bufstr_get (prvm_prog_t *prog)
  4150. {
  4151. prvm_stringbuffer_t *stringbuffer;
  4152. int strindex;
  4153. VM_SAFEPARMCOUNT(2, VM_bufstr_get);
  4154. PRVM_G_INT(OFS_RETURN) = OFS_NULL;
  4155. stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
  4156. if(!stringbuffer)
  4157. {
  4158. VM_Warning(prog, "VM_bufstr_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
  4159. return;
  4160. }
  4161. strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
  4162. if (strindex < 0)
  4163. {
  4164. // VM_Warning(prog, "VM_bufstr_get: invalid string index %i used in %s\n", strindex, prog->name);
  4165. return;
  4166. }
  4167. if (strindex < stringbuffer->num_strings && stringbuffer->strings[strindex])
  4168. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, stringbuffer->strings[strindex]);
  4169. }
  4170. /*
  4171. ========================
  4172. VM_bufstr_set
  4173. copies a string into selected slot of buffer
  4174. void bufstr_set(float bufhandle, float string_index, string str) = #466;
  4175. ========================
  4176. */
  4177. void VM_bufstr_set (prvm_prog_t *prog)
  4178. {
  4179. int strindex;
  4180. prvm_stringbuffer_t *stringbuffer;
  4181. const char *news;
  4182. VM_SAFEPARMCOUNT(3, VM_bufstr_set);
  4183. stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
  4184. if(!stringbuffer)
  4185. {
  4186. VM_Warning(prog, "VM_bufstr_set: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
  4187. return;
  4188. }
  4189. strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
  4190. if(strindex < 0 || strindex >= 1000000) // huge number of strings
  4191. {
  4192. VM_Warning(prog, "VM_bufstr_set: invalid string index %i used in %s\n", strindex, prog->name);
  4193. return;
  4194. }
  4195. news = PRVM_G_STRING(OFS_PARM2);
  4196. BufStr_Set(prog, stringbuffer, strindex, news);
  4197. }
  4198. /*
  4199. ========================
  4200. VM_bufstr_add
  4201. adds string to buffer in first free slot and returns its index
  4202. "order == TRUE" means that string will be added after last "full" slot
  4203. float bufstr_add(float bufhandle, string str, float order) = #467;
  4204. ========================
  4205. */
  4206. void VM_bufstr_add (prvm_prog_t *prog)
  4207. {
  4208. int order, strindex;
  4209. prvm_stringbuffer_t *stringbuffer;
  4210. const char *string;
  4211. size_t alloclen;
  4212. VM_SAFEPARMCOUNT(3, VM_bufstr_add);
  4213. stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
  4214. PRVM_G_FLOAT(OFS_RETURN) = -1;
  4215. if(!stringbuffer)
  4216. {
  4217. VM_Warning(prog, "VM_bufstr_add: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
  4218. return;
  4219. }
  4220. if(!PRVM_G_INT(OFS_PARM1)) // NULL string
  4221. {
  4222. VM_Warning(prog, "VM_bufstr_add: can not add an empty string to buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
  4223. return;
  4224. }
  4225. string = PRVM_G_STRING(OFS_PARM1);
  4226. order = (int)PRVM_G_FLOAT(OFS_PARM2);
  4227. if(order)
  4228. strindex = stringbuffer->num_strings;
  4229. else
  4230. for (strindex = 0;strindex < stringbuffer->num_strings;strindex++)
  4231. if (stringbuffer->strings[strindex] == NULL)
  4232. break;
  4233. BufStr_Expand(prog, stringbuffer, strindex);
  4234. stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
  4235. alloclen = strlen(string) + 1;
  4236. stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
  4237. memcpy(stringbuffer->strings[strindex], string, alloclen);
  4238. PRVM_G_FLOAT(OFS_RETURN) = strindex;
  4239. }
  4240. /*
  4241. ========================
  4242. VM_bufstr_free
  4243. delete string from buffer
  4244. void bufstr_free(float bufhandle, float string_index) = #468;
  4245. ========================
  4246. */
  4247. void VM_bufstr_free (prvm_prog_t *prog)
  4248. {
  4249. int i;
  4250. prvm_stringbuffer_t *stringbuffer;
  4251. VM_SAFEPARMCOUNT(2, VM_bufstr_free);
  4252. stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
  4253. if(!stringbuffer)
  4254. {
  4255. VM_Warning(prog, "VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
  4256. return;
  4257. }
  4258. i = (int)PRVM_G_FLOAT(OFS_PARM1);
  4259. if(i < 0)
  4260. {
  4261. VM_Warning(prog, "VM_bufstr_free: invalid string index %i used in %s\n", i, prog->name);
  4262. return;
  4263. }
  4264. if (i < stringbuffer->num_strings)
  4265. {
  4266. if(stringbuffer->strings[i])
  4267. Mem_Free(stringbuffer->strings[i]);
  4268. stringbuffer->strings[i] = NULL;
  4269. }
  4270. BufStr_Shrink(prog, stringbuffer);
  4271. }
  4272. /*
  4273. ========================
  4274. VM_buf_loadfile
  4275. load a file into string buffer, return 0 or 1
  4276. float buf_loadfile(string filename, float bufhandle) = #535;
  4277. ========================
  4278. */
  4279. void VM_buf_loadfile(prvm_prog_t *prog)
  4280. {
  4281. size_t alloclen;
  4282. prvm_stringbuffer_t *stringbuffer;
  4283. char string[VM_STRINGTEMP_LENGTH];
  4284. int strindex, c, end;
  4285. const char *filename;
  4286. char vabuf[1024];
  4287. qfile_t *file;
  4288. VM_SAFEPARMCOUNT(2, VM_buf_loadfile);
  4289. // get file
  4290. filename = PRVM_G_STRING(OFS_PARM0);
  4291. file = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "data/%s", filename), false);
  4292. if (file == NULL)
  4293. file = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "%s", filename), false);
  4294. if (file == NULL)
  4295. {
  4296. if (developer_extra.integer)
  4297. VM_Warning(prog, "VM_buf_loadfile: failed to open file %s in %s\n", filename, prog->name);
  4298. PRVM_G_FLOAT(OFS_RETURN) = 0;
  4299. return;
  4300. }
  4301. // get string buffer
  4302. stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM1));
  4303. if(!stringbuffer)
  4304. {
  4305. VM_Warning(prog, "VM_buf_loadfile: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), prog->name);
  4306. PRVM_G_FLOAT(OFS_RETURN) = 0;
  4307. return;
  4308. }
  4309. // read file (append to the end of buffer)
  4310. strindex = stringbuffer->num_strings;
  4311. while(1)
  4312. {
  4313. // read line
  4314. end = 0;
  4315. for (;;)
  4316. {
  4317. c = FS_Getc(file);
  4318. if (c == '\r' || c == '\n' || c < 0)
  4319. break;
  4320. if (end < VM_STRINGTEMP_LENGTH - 1)
  4321. string[end++] = c;
  4322. }
  4323. string[end] = 0;
  4324. // remove \n following \r
  4325. if (c == '\r')
  4326. {
  4327. c = FS_Getc(file);
  4328. if (c != '\n')
  4329. FS_UnGetc(file, (unsigned char)c);
  4330. }
  4331. // add and continue
  4332. if (c >= 0 || end)
  4333. {
  4334. BufStr_Expand(prog, stringbuffer, strindex);
  4335. stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
  4336. alloclen = strlen(string) + 1;
  4337. stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
  4338. memcpy(stringbuffer->strings[strindex], string, alloclen);
  4339. strindex = stringbuffer->num_strings;
  4340. }
  4341. else
  4342. break;
  4343. }
  4344. // close file
  4345. FS_Close(file);
  4346. PRVM_G_FLOAT(OFS_RETURN) = 1;
  4347. }
  4348. /*
  4349. ========================
  4350. VM_buf_writefile
  4351. writes stringbuffer to a file, returns 0 or 1
  4352. float buf_writefile(float filehandle, float bufhandle, [, float startpos, float numstrings]) = #468;
  4353. ========================
  4354. */
  4355. void VM_buf_writefile(prvm_prog_t *prog)
  4356. {
  4357. int filenum, strindex, strnum, strlength;
  4358. prvm_stringbuffer_t *stringbuffer;
  4359. VM_SAFEPARMCOUNTRANGE(2, 4, VM_buf_writefile);
  4360. // get file
  4361. filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
  4362. if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
  4363. {
  4364. VM_Warning(prog, "VM_buf_writefile: invalid file handle %i used in %s\n", filenum, prog->name);
  4365. return;
  4366. }
  4367. if (prog->openfiles[filenum] == NULL)
  4368. {
  4369. VM_Warning(prog, "VM_buf_writefile: no such file handle %i (or file has been closed) in %s\n", filenum, prog->name);
  4370. return;
  4371. }
  4372. // get string buffer
  4373. stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM1));
  4374. if(!stringbuffer)
  4375. {
  4376. VM_Warning(prog, "VM_buf_writefile: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), prog->name);
  4377. PRVM_G_FLOAT(OFS_RETURN) = 0;
  4378. return;
  4379. }
  4380. // get start and end parms
  4381. if (prog->argc > 3)
  4382. {
  4383. strindex = (int)PRVM_G_FLOAT(OFS_PARM2);
  4384. strnum = (int)PRVM_G_FLOAT(OFS_PARM3);
  4385. }
  4386. else if (prog->argc > 2)
  4387. {
  4388. strindex = (int)PRVM_G_FLOAT(OFS_PARM2);
  4389. strnum = stringbuffer->num_strings - strindex;
  4390. }
  4391. else
  4392. {
  4393. strindex = 0;
  4394. strnum = stringbuffer->num_strings;
  4395. }
  4396. if (strindex < 0 || strindex >= stringbuffer->num_strings)
  4397. {
  4398. VM_Warning(prog, "VM_buf_writefile: wrong start string index %i used in %s\n", strindex, prog->name);
  4399. PRVM_G_FLOAT(OFS_RETURN) = 0;
  4400. return;
  4401. }
  4402. if (strnum < 0)
  4403. {
  4404. VM_Warning(prog, "VM_buf_writefile: wrong strings count %i used in %s\n", strnum, prog->name);
  4405. PRVM_G_FLOAT(OFS_RETURN) = 0;
  4406. return;
  4407. }
  4408. // write
  4409. while(strindex < stringbuffer->num_strings && strnum)
  4410. {
  4411. if (stringbuffer->strings[strindex])
  4412. {
  4413. if ((strlength = (int)strlen(stringbuffer->strings[strindex])))
  4414. FS_Write(prog->openfiles[filenum], stringbuffer->strings[strindex], strlength);
  4415. FS_Write(prog->openfiles[filenum], "\n", 1);
  4416. }
  4417. strindex++;
  4418. strnum--;
  4419. }
  4420. PRVM_G_FLOAT(OFS_RETURN) = 1;
  4421. }
  4422. #define MATCH_AUTO 0
  4423. #define MATCH_WHOLE 1
  4424. #define MATCH_LEFT 2
  4425. #define MATCH_RIGHT 3
  4426. #define MATCH_MIDDLE 4
  4427. #define MATCH_PATTERN 5
  4428. static const char *detect_match_rule(char *pattern, int *matchrule)
  4429. {
  4430. char *ppos, *qpos;
  4431. int patternlength;
  4432. patternlength = (int)strlen(pattern);
  4433. ppos = strchr(pattern, '*');
  4434. qpos = strchr(pattern, '?');
  4435. // has ? - pattern
  4436. if (qpos)
  4437. {
  4438. *matchrule = MATCH_PATTERN;
  4439. return pattern;
  4440. }
  4441. // has * - left, mid, right or pattern
  4442. if (ppos)
  4443. {
  4444. // starts with * - may be right/mid or pattern
  4445. if ((ppos - pattern) == 0)
  4446. {
  4447. ppos = strchr(pattern+1, '*');
  4448. // *something
  4449. if (!ppos)
  4450. {
  4451. *matchrule = MATCH_RIGHT;
  4452. return pattern+1;
  4453. }
  4454. // *something*
  4455. if ((ppos - pattern) == patternlength)
  4456. {
  4457. *matchrule = MATCH_MIDDLE;
  4458. *ppos = 0;
  4459. return pattern+1;
  4460. }
  4461. // *som*thing
  4462. *matchrule = MATCH_PATTERN;
  4463. return pattern;
  4464. }
  4465. // end with * - left
  4466. if ((ppos - pattern) == patternlength)
  4467. {
  4468. *matchrule = MATCH_LEFT;
  4469. *ppos = 0;
  4470. return pattern;
  4471. }
  4472. // som*thing
  4473. *matchrule = MATCH_PATTERN;
  4474. return pattern;
  4475. }
  4476. // have no wildcards - whole string
  4477. *matchrule = MATCH_WHOLE;
  4478. return pattern;
  4479. }
  4480. // todo: support UTF8
  4481. static qboolean match_rule(const char *string, int max_string, const char *pattern, int patternlength, int rule)
  4482. {
  4483. const char *mid;
  4484. if (rule == 1)
  4485. return !strncmp(string, pattern, max_string) ? true : false;
  4486. if (rule == 2)
  4487. return !strncmp(string, pattern, patternlength) ? true : false;
  4488. if (rule == 3)
  4489. {
  4490. mid = strstr(string, pattern);
  4491. return mid && !*(mid+patternlength);
  4492. }
  4493. if (rule == 4)
  4494. return strstr(string, pattern) ? true : false;
  4495. // pattern
  4496. return matchpattern_with_separator(string, pattern, false, "", false) ? true : false;
  4497. }
  4498. /*
  4499. ========================
  4500. VM_bufstr_find
  4501. find an index of bufstring matching rule
  4502. float bufstr_find(float bufhandle, string match, float matchrule, float startpos, float step) = #468;
  4503. ========================
  4504. */
  4505. void VM_bufstr_find(prvm_prog_t *prog)
  4506. {
  4507. prvm_stringbuffer_t *stringbuffer;
  4508. char string[VM_STRINGTEMP_LENGTH];
  4509. int matchrule, matchlen, i, step;
  4510. const char *match;
  4511. VM_SAFEPARMCOUNTRANGE(3, 5, VM_bufstr_find);
  4512. PRVM_G_FLOAT(OFS_RETURN) = -1;
  4513. // get string buffer
  4514. stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
  4515. if(!stringbuffer)
  4516. {
  4517. VM_Warning(prog, "VM_bufstr_find: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
  4518. return;
  4519. }
  4520. // get pattern/rule
  4521. matchrule = (int)PRVM_G_FLOAT(OFS_PARM2);
  4522. if (matchrule < 0 && matchrule > 5)
  4523. {
  4524. VM_Warning(prog, "VM_bufstr_find: invalid match rule %i in %s\n", matchrule, prog->name);
  4525. return;
  4526. }
  4527. if (matchrule)
  4528. match = PRVM_G_STRING(OFS_PARM1);
  4529. else
  4530. {
  4531. strlcpy(string, PRVM_G_STRING(OFS_PARM1), sizeof(string));
  4532. match = detect_match_rule(string, &matchrule);
  4533. }
  4534. matchlen = (int)strlen(match);
  4535. // find
  4536. i = (prog->argc > 3) ? (int)PRVM_G_FLOAT(OFS_PARM3) : 0;
  4537. step = (prog->argc > 4) ? (int)PRVM_G_FLOAT(OFS_PARM4) : 1;
  4538. while(i < stringbuffer->num_strings)
  4539. {
  4540. if (stringbuffer->strings[i] && match_rule(stringbuffer->strings[i], VM_STRINGTEMP_LENGTH, match, matchlen, matchrule))
  4541. {
  4542. PRVM_G_FLOAT(OFS_RETURN) = i;
  4543. break;
  4544. }
  4545. i += step;
  4546. }
  4547. }
  4548. /*
  4549. ========================
  4550. VM_matchpattern
  4551. float matchpattern(string s, string pattern, float matchrule, float startpos) = #468;
  4552. ========================
  4553. */
  4554. void VM_matchpattern(prvm_prog_t *prog)
  4555. {
  4556. const char *s, *match;
  4557. char string[VM_STRINGTEMP_LENGTH];
  4558. int matchrule, l;
  4559. VM_SAFEPARMCOUNTRANGE(2, 4, VM_matchpattern);
  4560. s = PRVM_G_STRING(OFS_PARM0);
  4561. // get pattern/rule
  4562. matchrule = (int)PRVM_G_FLOAT(OFS_PARM2);
  4563. if (matchrule < 0 && matchrule > 5)
  4564. {
  4565. VM_Warning(prog, "VM_bufstr_find: invalid match rule %i in %s\n", matchrule, prog->name);
  4566. return;
  4567. }
  4568. if (matchrule)
  4569. match = PRVM_G_STRING(OFS_PARM1);
  4570. else
  4571. {
  4572. strlcpy(string, PRVM_G_STRING(OFS_PARM1), sizeof(string));
  4573. match = detect_match_rule(string, &matchrule);
  4574. }
  4575. // offset
  4576. l = (int)strlen(match);
  4577. if (prog->argc > 3)
  4578. s += max(0, min((unsigned int)PRVM_G_FLOAT(OFS_PARM3), strlen(s)-1));
  4579. // match
  4580. PRVM_G_FLOAT(OFS_RETURN) = match_rule(s, VM_STRINGTEMP_LENGTH, match, l, matchrule);
  4581. }
  4582. /*
  4583. ========================
  4584. VM_buf_cvarlist
  4585. ========================
  4586. */
  4587. void VM_buf_cvarlist(prvm_prog_t *prog)
  4588. {
  4589. cvar_t *cvar;
  4590. const char *partial, *antipartial;
  4591. size_t len, antilen;
  4592. size_t alloclen;
  4593. qboolean ispattern, antiispattern;
  4594. int n;
  4595. prvm_stringbuffer_t *stringbuffer;
  4596. VM_SAFEPARMCOUNTRANGE(2, 3, VM_buf_cvarlist);
  4597. stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
  4598. if(!stringbuffer)
  4599. {
  4600. VM_Warning(prog, "VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
  4601. return;
  4602. }
  4603. partial = PRVM_G_STRING(OFS_PARM1);
  4604. if(!partial)
  4605. len = 0;
  4606. else
  4607. len = strlen(partial);
  4608. if(prog->argc == 3)
  4609. antipartial = PRVM_G_STRING(OFS_PARM2);
  4610. else
  4611. antipartial = NULL;
  4612. if(!antipartial)
  4613. antilen = 0;
  4614. else
  4615. antilen = strlen(antipartial);
  4616. for (n = 0;n < stringbuffer->num_strings;n++)
  4617. if (stringbuffer->strings[n])
  4618. Mem_Free(stringbuffer->strings[n]);
  4619. if (stringbuffer->strings)
  4620. Mem_Free(stringbuffer->strings);
  4621. stringbuffer->strings = NULL;
  4622. ispattern = partial && (strchr(partial, '*') || strchr(partial, '?'));
  4623. antiispattern = antipartial && (strchr(antipartial, '*') || strchr(antipartial, '?'));
  4624. n = 0;
  4625. for(cvar = cvar_vars; cvar; cvar = cvar->next)
  4626. {
  4627. if(len && (ispattern ? !matchpattern_with_separator(cvar->name, partial, false, "", false) : strncmp(partial, cvar->name, len)))
  4628. continue;
  4629. if(antilen && (antiispattern ? matchpattern_with_separator(cvar->name, antipartial, false, "", false) : !strncmp(antipartial, cvar->name, antilen)))
  4630. continue;
  4631. ++n;
  4632. }
  4633. stringbuffer->max_strings = stringbuffer->num_strings = n;
  4634. if (stringbuffer->max_strings)
  4635. stringbuffer->strings = (char **)Mem_Alloc(prog->progs_mempool, sizeof(stringbuffer->strings[0]) * stringbuffer->max_strings);
  4636. n = 0;
  4637. for(cvar = cvar_vars; cvar; cvar = cvar->next)
  4638. {
  4639. if(len && (ispattern ? !matchpattern_with_separator(cvar->name, partial, false, "", false) : strncmp(partial, cvar->name, len)))
  4640. continue;
  4641. if(antilen && (antiispattern ? matchpattern_with_separator(cvar->name, antipartial, false, "", false) : !strncmp(antipartial, cvar->name, antilen)))
  4642. continue;
  4643. alloclen = strlen(cvar->name) + 1;
  4644. stringbuffer->strings[n] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
  4645. memcpy(stringbuffer->strings[n], cvar->name, alloclen);
  4646. ++n;
  4647. }
  4648. }
  4649. //=============
  4650. /*
  4651. ==============
  4652. VM_changeyaw
  4653. This was a major timewaster in progs, so it was converted to C
  4654. ==============
  4655. */
  4656. void VM_changeyaw (prvm_prog_t *prog)
  4657. {
  4658. prvm_edict_t *ent;
  4659. float ideal, current, move, speed;
  4660. // this is called (VERY HACKISHLY) by VM_SV_MoveToGoal, so it can not use any
  4661. // parameters because they are the parameters to VM_SV_MoveToGoal, not this
  4662. //VM_SAFEPARMCOUNT(0, VM_changeyaw);
  4663. ent = PRVM_PROG_TO_EDICT(PRVM_gameglobaledict(self));
  4664. if (ent == prog->edicts)
  4665. {
  4666. VM_Warning(prog, "changeyaw: can not modify world entity\n");
  4667. return;
  4668. }
  4669. if (ent->priv.server->free)
  4670. {
  4671. VM_Warning(prog, "changeyaw: can not modify free entity\n");
  4672. return;
  4673. }
  4674. current = PRVM_gameedictvector(ent, angles)[1];
  4675. current = ANGLEMOD(current);
  4676. ideal = PRVM_gameedictfloat(ent, ideal_yaw);
  4677. speed = PRVM_gameedictfloat(ent, yaw_speed);
  4678. if (current == ideal)
  4679. return;
  4680. move = ideal - current;
  4681. if (ideal > current)
  4682. {
  4683. if (move >= 180)
  4684. move = move - 360;
  4685. }
  4686. else
  4687. {
  4688. if (move <= -180)
  4689. move = move + 360;
  4690. }
  4691. if (move > 0)
  4692. {
  4693. if (move > speed)
  4694. move = speed;
  4695. }
  4696. else
  4697. {
  4698. if (move < -speed)
  4699. move = -speed;
  4700. }
  4701. current += move;
  4702. PRVM_gameedictvector(ent, angles)[1] = ANGLEMOD(current);
  4703. }
  4704. /*
  4705. ==============
  4706. VM_changepitch
  4707. ==============
  4708. */
  4709. void VM_changepitch (prvm_prog_t *prog)
  4710. {
  4711. prvm_edict_t *ent;
  4712. float ideal, current, move, speed;
  4713. VM_SAFEPARMCOUNT(1, VM_changepitch);
  4714. ent = PRVM_G_EDICT(OFS_PARM0);
  4715. if (ent == prog->edicts)
  4716. {
  4717. VM_Warning(prog, "changepitch: can not modify world entity\n");
  4718. return;
  4719. }
  4720. if (ent->priv.server->free)
  4721. {
  4722. VM_Warning(prog, "changepitch: can not modify free entity\n");
  4723. return;
  4724. }
  4725. current = PRVM_gameedictvector(ent, angles)[0];
  4726. current = ANGLEMOD(current);
  4727. ideal = PRVM_gameedictfloat(ent, idealpitch);
  4728. speed = PRVM_gameedictfloat(ent, pitch_speed);
  4729. if (current == ideal)
  4730. return;
  4731. move = ideal - current;
  4732. if (ideal > current)
  4733. {
  4734. if (move >= 180)
  4735. move = move - 360;
  4736. }
  4737. else
  4738. {
  4739. if (move <= -180)
  4740. move = move + 360;
  4741. }
  4742. if (move > 0)
  4743. {
  4744. if (move > speed)
  4745. move = speed;
  4746. }
  4747. else
  4748. {
  4749. if (move < -speed)
  4750. move = -speed;
  4751. }
  4752. current += move;
  4753. PRVM_gameedictvector(ent, angles)[0] = ANGLEMOD(current);
  4754. }
  4755. void VM_uncolorstring (prvm_prog_t *prog)
  4756. {
  4757. char szNewString[VM_STRINGTEMP_LENGTH];
  4758. const char *szString;
  4759. // Prepare Strings
  4760. VM_SAFEPARMCOUNT(1, VM_uncolorstring);
  4761. szString = PRVM_G_STRING(OFS_PARM0);
  4762. COM_StringDecolorize(szString, 0, szNewString, sizeof(szNewString), TRUE);
  4763. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString);
  4764. }
  4765. // #221 float(string str, string sub[, float startpos]) strstrofs (FTE_STRINGS)
  4766. //strstr, without generating a new string. Use in conjunction with FRIK_FILE's substring for more similar strstr.
  4767. void VM_strstrofs (prvm_prog_t *prog)
  4768. {
  4769. const char *instr, *match;
  4770. int firstofs;
  4771. VM_SAFEPARMCOUNTRANGE(2, 3, VM_strstrofs);
  4772. instr = PRVM_G_STRING(OFS_PARM0);
  4773. match = PRVM_G_STRING(OFS_PARM1);
  4774. firstofs = (prog->argc > 2)?(int)PRVM_G_FLOAT(OFS_PARM2):0;
  4775. firstofs = (int)u8_bytelen(instr, firstofs);
  4776. if (firstofs && (firstofs < 0 || firstofs > (int)strlen(instr)))
  4777. {
  4778. PRVM_G_FLOAT(OFS_RETURN) = -1;
  4779. return;
  4780. }
  4781. match = strstr(instr+firstofs, match);
  4782. if (!match)
  4783. PRVM_G_FLOAT(OFS_RETURN) = -1;
  4784. else
  4785. PRVM_G_FLOAT(OFS_RETURN) = u8_strnlen(instr, match-instr);
  4786. }
  4787. //#222 string(string s, float index) str2chr (FTE_STRINGS)
  4788. void VM_str2chr (prvm_prog_t *prog)
  4789. {
  4790. const char *s;
  4791. Uchar ch;
  4792. int index;
  4793. VM_SAFEPARMCOUNT(2, VM_str2chr);
  4794. s = PRVM_G_STRING(OFS_PARM0);
  4795. index = (int)u8_bytelen(s, (int)PRVM_G_FLOAT(OFS_PARM1));
  4796. if((unsigned)index < strlen(s))
  4797. {
  4798. if (utf8_enable.integer)
  4799. ch = u8_getchar_noendptr(s + index);
  4800. else
  4801. ch = (unsigned char)s[index];
  4802. PRVM_G_FLOAT(OFS_RETURN) = ch;
  4803. }
  4804. else
  4805. PRVM_G_FLOAT(OFS_RETURN) = 0;
  4806. }
  4807. //#223 string(float c, ...) chr2str (FTE_STRINGS)
  4808. void VM_chr2str (prvm_prog_t *prog)
  4809. {
  4810. /*
  4811. char t[9];
  4812. int i;
  4813. VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
  4814. for(i = 0;i < prog->argc && i < (int)sizeof(t) - 1;i++)
  4815. t[i] = (unsigned char)PRVM_G_FLOAT(OFS_PARM0+i*3);
  4816. t[i] = 0;
  4817. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t);
  4818. */
  4819. char t[9 * 4 + 1];
  4820. int i;
  4821. size_t len = 0;
  4822. VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
  4823. for(i = 0; i < prog->argc && len < sizeof(t)-1; ++i)
  4824. len += u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0+i*3), t + len, sizeof(t)-1);
  4825. t[len] = 0;
  4826. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t);
  4827. }
  4828. static int chrconv_number(int i, int base, int conv)
  4829. {
  4830. i -= base;
  4831. switch (conv)
  4832. {
  4833. default:
  4834. case 5:
  4835. case 6:
  4836. case 0:
  4837. break;
  4838. case 1:
  4839. base = '0';
  4840. break;
  4841. case 2:
  4842. base = '0'+128;
  4843. break;
  4844. case 3:
  4845. base = '0'-30;
  4846. break;
  4847. case 4:
  4848. base = '0'+128-30;
  4849. break;
  4850. }
  4851. return i + base;
  4852. }
  4853. static int chrconv_punct(int i, int base, int conv)
  4854. {
  4855. i -= base;
  4856. switch (conv)
  4857. {
  4858. default:
  4859. case 0:
  4860. break;
  4861. case 1:
  4862. base = 0;
  4863. break;
  4864. case 2:
  4865. base = 128;
  4866. break;
  4867. }
  4868. return i + base;
  4869. }
  4870. static int chrchar_alpha(int i, int basec, int baset, int convc, int convt, int charnum)
  4871. {
  4872. //convert case and colour seperatly...
  4873. i -= baset + basec;
  4874. switch (convt)
  4875. {
  4876. default:
  4877. case 0:
  4878. break;
  4879. case 1:
  4880. baset = 0;
  4881. break;
  4882. case 2:
  4883. baset = 128;
  4884. break;
  4885. case 5:
  4886. case 6:
  4887. baset = 128*((charnum&1) == (convt-5));
  4888. break;
  4889. }
  4890. switch (convc)
  4891. {
  4892. default:
  4893. case 0:
  4894. break;
  4895. case 1:
  4896. basec = 'a';
  4897. break;
  4898. case 2:
  4899. basec = 'A';
  4900. break;
  4901. }
  4902. return i + basec + baset;
  4903. }
  4904. // #224 string(float ccase, float calpha, float cnum, string s, ...) strconv (FTE_STRINGS)
  4905. //bulk convert a string. change case or colouring.
  4906. void VM_strconv (prvm_prog_t *prog)
  4907. {
  4908. int ccase, redalpha, rednum, len, i;
  4909. unsigned char resbuf[VM_STRINGTEMP_LENGTH];
  4910. unsigned char *result = resbuf;
  4911. VM_SAFEPARMCOUNTRANGE(3, 8, VM_strconv);
  4912. ccase = (int) PRVM_G_FLOAT(OFS_PARM0); //0 same, 1 lower, 2 upper
  4913. redalpha = (int) PRVM_G_FLOAT(OFS_PARM1); //0 same, 1 white, 2 red, 5 alternate, 6 alternate-alternate
  4914. rednum = (int) PRVM_G_FLOAT(OFS_PARM2); //0 same, 1 white, 2 red, 3 redspecial, 4 whitespecial, 5 alternate, 6 alternate-alternate
  4915. VM_VarString(prog, 3, (char *) resbuf, sizeof(resbuf));
  4916. len = (int)strlen((char *) resbuf);
  4917. for (i = 0; i < len; i++, result++) //should this be done backwards?
  4918. {
  4919. if (*result >= '0' && *result <= '9') //normal numbers...
  4920. *result = chrconv_number(*result, '0', rednum);
  4921. else if (*result >= '0'+128 && *result <= '9'+128)
  4922. *result = chrconv_number(*result, '0'+128, rednum);
  4923. else if (*result >= '0'+128-30 && *result <= '9'+128-30)
  4924. *result = chrconv_number(*result, '0'+128-30, rednum);
  4925. else if (*result >= '0'-30 && *result <= '9'-30)
  4926. *result = chrconv_number(*result, '0'-30, rednum);
  4927. else if (*result >= 'a' && *result <= 'z') //normal numbers...
  4928. *result = chrchar_alpha(*result, 'a', 0, ccase, redalpha, i);
  4929. else if (*result >= 'A' && *result <= 'Z') //normal numbers...
  4930. *result = chrchar_alpha(*result, 'A', 0, ccase, redalpha, i);
  4931. else if (*result >= 'a'+128 && *result <= 'z'+128) //normal numbers...
  4932. *result = chrchar_alpha(*result, 'a', 128, ccase, redalpha, i);
  4933. else if (*result >= 'A'+128 && *result <= 'Z'+128) //normal numbers...
  4934. *result = chrchar_alpha(*result, 'A', 128, ccase, redalpha, i);
  4935. else if ((*result & 127) < 16 || !redalpha) //special chars..
  4936. *result = *result;
  4937. else if (*result < 128)
  4938. *result = chrconv_punct(*result, 0, redalpha);
  4939. else
  4940. *result = chrconv_punct(*result, 128, redalpha);
  4941. }
  4942. *result = '\0';
  4943. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, (char *) resbuf);
  4944. }
  4945. // #225 string(float chars, string s, ...) strpad (FTE_STRINGS)
  4946. void VM_strpad (prvm_prog_t *prog)
  4947. {
  4948. char src[VM_STRINGTEMP_LENGTH];
  4949. char destbuf[VM_STRINGTEMP_LENGTH];
  4950. int pad;
  4951. VM_SAFEPARMCOUNTRANGE(1, 8, VM_strpad);
  4952. pad = (int) PRVM_G_FLOAT(OFS_PARM0);
  4953. VM_VarString(prog, 1, src, sizeof(src));
  4954. // note: < 0 = left padding, > 0 = right padding,
  4955. // this is reverse logic of printf!
  4956. dpsnprintf(destbuf, sizeof(destbuf), "%*s", -pad, src);
  4957. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, destbuf);
  4958. }
  4959. // #226 string(string info, string key, string value, ...) infoadd (FTE_STRINGS)
  4960. //uses qw style \key\value strings
  4961. void VM_infoadd (prvm_prog_t *prog)
  4962. {
  4963. const char *info, *key;
  4964. char value[VM_STRINGTEMP_LENGTH];
  4965. char temp[VM_STRINGTEMP_LENGTH];
  4966. VM_SAFEPARMCOUNTRANGE(2, 8, VM_infoadd);
  4967. info = PRVM_G_STRING(OFS_PARM0);
  4968. key = PRVM_G_STRING(OFS_PARM1);
  4969. VM_VarString(prog, 2, value, sizeof(value));
  4970. strlcpy(temp, info, VM_STRINGTEMP_LENGTH);
  4971. InfoString_SetValue(temp, VM_STRINGTEMP_LENGTH, key, value);
  4972. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, temp);
  4973. }
  4974. // #227 string(string info, string key) infoget (FTE_STRINGS)
  4975. //uses qw style \key\value strings
  4976. void VM_infoget (prvm_prog_t *prog)
  4977. {
  4978. const char *info;
  4979. const char *key;
  4980. char value[VM_STRINGTEMP_LENGTH];
  4981. VM_SAFEPARMCOUNT(2, VM_infoget);
  4982. info = PRVM_G_STRING(OFS_PARM0);
  4983. key = PRVM_G_STRING(OFS_PARM1);
  4984. InfoString_GetValue(info, key, value, VM_STRINGTEMP_LENGTH);
  4985. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, value);
  4986. }
  4987. //#228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
  4988. // also float(string s1, string s2) strcmp (FRIK_FILE)
  4989. void VM_strncmp (prvm_prog_t *prog)
  4990. {
  4991. const char *s1, *s2;
  4992. VM_SAFEPARMCOUNTRANGE(2, 3, VM_strncmp);
  4993. s1 = PRVM_G_STRING(OFS_PARM0);
  4994. s2 = PRVM_G_STRING(OFS_PARM1);
  4995. if (prog->argc > 2)
  4996. {
  4997. PRVM_G_FLOAT(OFS_RETURN) = strncmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
  4998. }
  4999. else
  5000. {
  5001. PRVM_G_FLOAT(OFS_RETURN) = strcmp(s1, s2);
  5002. }
  5003. }
  5004. // #229 float(string s1, string s2) strcasecmp (FTE_STRINGS)
  5005. // #230 float(string s1, string s2, float len) strncasecmp (FTE_STRINGS)
  5006. void VM_strncasecmp (prvm_prog_t *prog)
  5007. {
  5008. const char *s1, *s2;
  5009. VM_SAFEPARMCOUNTRANGE(2, 3, VM_strncasecmp);
  5010. s1 = PRVM_G_STRING(OFS_PARM0);
  5011. s2 = PRVM_G_STRING(OFS_PARM1);
  5012. if (prog->argc > 2)
  5013. {
  5014. PRVM_G_FLOAT(OFS_RETURN) = strncasecmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
  5015. }
  5016. else
  5017. {
  5018. PRVM_G_FLOAT(OFS_RETURN) = strcasecmp(s1, s2);
  5019. }
  5020. }
  5021. // #494 float(float caseinsensitive, string s, ...) crc16
  5022. void VM_crc16(prvm_prog_t *prog)
  5023. {
  5024. float insensitive;
  5025. char s[VM_STRINGTEMP_LENGTH];
  5026. VM_SAFEPARMCOUNTRANGE(2, 8, VM_crc16);
  5027. insensitive = PRVM_G_FLOAT(OFS_PARM0);
  5028. VM_VarString(prog, 1, s, sizeof(s));
  5029. PRVM_G_FLOAT(OFS_RETURN) = (unsigned short) ((insensitive ? CRC_Block_CaseInsensitive : CRC_Block) ((unsigned char *) s, strlen(s)));
  5030. }
  5031. // #639 float(string digest, string data, ...) digest_hex
  5032. void VM_digest_hex(prvm_prog_t *prog)
  5033. {
  5034. const char *digest;
  5035. char out[32];
  5036. char outhex[65];
  5037. int outlen;
  5038. char s[VM_STRINGTEMP_LENGTH];
  5039. int len;
  5040. VM_SAFEPARMCOUNTRANGE(2, 8, VM_digest_hex);
  5041. digest = PRVM_G_STRING(OFS_PARM0);
  5042. if(!digest)
  5043. digest = "";
  5044. VM_VarString(prog, 1, s, sizeof(s));
  5045. len = (int)strlen(s);
  5046. outlen = 0;
  5047. if(!strcmp(digest, "MD4"))
  5048. {
  5049. outlen = 16;
  5050. mdfour((unsigned char *) out, (unsigned char *) s, len);
  5051. }
  5052. else if(!strcmp(digest, "SHA256") && Crypto_Available())
  5053. {
  5054. outlen = 32;
  5055. sha256((unsigned char *) out, (unsigned char *) s, len);
  5056. }
  5057. // no warning needed on mismatch - we return string_null to QC
  5058. if(outlen)
  5059. {
  5060. int i;
  5061. static const char *hexmap = "0123456789abcdef";
  5062. for(i = 0; i < outlen; ++i)
  5063. {
  5064. outhex[2*i] = hexmap[(out[i] >> 4) & 15];
  5065. outhex[2*i+1] = hexmap[(out[i] >> 0) & 15];
  5066. }
  5067. outhex[2*i] = 0;
  5068. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, outhex);
  5069. }
  5070. else
  5071. PRVM_G_INT(OFS_RETURN) = 0;
  5072. }
  5073. void VM_wasfreed (prvm_prog_t *prog)
  5074. {
  5075. VM_SAFEPARMCOUNT(1, VM_wasfreed);
  5076. PRVM_G_FLOAT(OFS_RETURN) = PRVM_G_EDICT(OFS_PARM0)->priv.required->free;
  5077. }
  5078. void VM_SetTraceGlobals(prvm_prog_t *prog, const trace_t *trace)
  5079. {
  5080. PRVM_gameglobalfloat(trace_allsolid) = trace->allsolid;
  5081. PRVM_gameglobalfloat(trace_startsolid) = trace->startsolid;
  5082. PRVM_gameglobalfloat(trace_fraction) = trace->fraction;
  5083. PRVM_gameglobalfloat(trace_inwater) = trace->inwater;
  5084. PRVM_gameglobalfloat(trace_inopen) = trace->inopen;
  5085. VectorCopy(trace->endpos, PRVM_gameglobalvector(trace_endpos));
  5086. VectorCopy(trace->plane.normal, PRVM_gameglobalvector(trace_plane_normal));
  5087. PRVM_gameglobalfloat(trace_plane_dist) = trace->plane.dist;
  5088. PRVM_gameglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(trace->ent ? trace->ent : prog->edicts);
  5089. PRVM_gameglobalfloat(trace_dpstartcontents) = trace->startsupercontents;
  5090. PRVM_gameglobalfloat(trace_dphitcontents) = trace->hitsupercontents;
  5091. PRVM_gameglobalfloat(trace_dphitq3surfaceflags) = trace->hitq3surfaceflags;
  5092. PRVM_gameglobalstring(trace_dphittexturename) = trace->hittexture ? PRVM_SetTempString(prog, trace->hittexture->name) : 0;
  5093. }
  5094. void VM_ClearTraceGlobals(prvm_prog_t *prog)
  5095. {
  5096. // clean up all trace globals when leaving the VM (anti-triggerbot safeguard)
  5097. PRVM_gameglobalfloat(trace_allsolid) = 0;
  5098. PRVM_gameglobalfloat(trace_startsolid) = 0;
  5099. PRVM_gameglobalfloat(trace_fraction) = 0;
  5100. PRVM_gameglobalfloat(trace_inwater) = 0;
  5101. PRVM_gameglobalfloat(trace_inopen) = 0;
  5102. VectorClear(PRVM_gameglobalvector(trace_endpos));
  5103. VectorClear(PRVM_gameglobalvector(trace_plane_normal));
  5104. PRVM_gameglobalfloat(trace_plane_dist) = 0;
  5105. PRVM_gameglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(prog->edicts);
  5106. PRVM_gameglobalfloat(trace_dpstartcontents) = 0;
  5107. PRVM_gameglobalfloat(trace_dphitcontents) = 0;
  5108. PRVM_gameglobalfloat(trace_dphitq3surfaceflags) = 0;
  5109. PRVM_gameglobalstring(trace_dphittexturename) = 0;
  5110. }
  5111. //=============
  5112. void VM_Cmd_Init(prvm_prog_t *prog)
  5113. {
  5114. // only init the stuff for the current prog
  5115. VM_Files_Init(prog);
  5116. VM_Search_Init(prog);
  5117. }
  5118. static void animatemodel_reset(prvm_prog_t *prog);
  5119. void VM_Cmd_Reset(prvm_prog_t *prog)
  5120. {
  5121. CL_PurgeOwner( MENUOWNER );
  5122. VM_Search_Reset(prog);
  5123. VM_Files_CloseAll(prog);
  5124. animatemodel_reset(prog);
  5125. }
  5126. // #510 string(string input, ...) uri_escape (DP_QC_URI_ESCAPE)
  5127. // does URI escaping on a string (replace evil stuff by %AB escapes)
  5128. void VM_uri_escape (prvm_prog_t *prog)
  5129. {
  5130. char src[VM_STRINGTEMP_LENGTH];
  5131. char dest[VM_STRINGTEMP_LENGTH];
  5132. char *p, *q;
  5133. static const char *hex = "0123456789ABCDEF";
  5134. VM_SAFEPARMCOUNTRANGE(1, 8, VM_uri_escape);
  5135. VM_VarString(prog, 0, src, sizeof(src));
  5136. for(p = src, q = dest; *p && q < dest + sizeof(dest) - 3; ++p)
  5137. {
  5138. if((*p >= 'A' && *p <= 'Z')
  5139. || (*p >= 'a' && *p <= 'z')
  5140. || (*p >= '0' && *p <= '9')
  5141. || (*p == '-') || (*p == '_') || (*p == '.')
  5142. || (*p == '!') || (*p == '~')
  5143. || (*p == '\'') || (*p == '(') || (*p == ')'))
  5144. *q++ = *p;
  5145. else
  5146. {
  5147. *q++ = '%';
  5148. *q++ = hex[(*(unsigned char *)p >> 4) & 0xF];
  5149. *q++ = hex[ *(unsigned char *)p & 0xF];
  5150. }
  5151. }
  5152. *q++ = 0;
  5153. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, dest);
  5154. }
  5155. // #510 string(string input, ...) uri_unescape (DP_QC_URI_ESCAPE)
  5156. // does URI unescaping on a string (get back the evil stuff)
  5157. void VM_uri_unescape (prvm_prog_t *prog)
  5158. {
  5159. char src[VM_STRINGTEMP_LENGTH];
  5160. char dest[VM_STRINGTEMP_LENGTH];
  5161. char *p, *q;
  5162. int hi, lo;
  5163. VM_SAFEPARMCOUNTRANGE(1, 8, VM_uri_unescape);
  5164. VM_VarString(prog, 0, src, sizeof(src));
  5165. for(p = src, q = dest; *p; ) // no need to check size, because unescape can't expand
  5166. {
  5167. if(*p == '%')
  5168. {
  5169. if(p[1] >= '0' && p[1] <= '9')
  5170. hi = p[1] - '0';
  5171. else if(p[1] >= 'a' && p[1] <= 'f')
  5172. hi = p[1] - 'a' + 10;
  5173. else if(p[1] >= 'A' && p[1] <= 'F')
  5174. hi = p[1] - 'A' + 10;
  5175. else
  5176. goto nohex;
  5177. if(p[2] >= '0' && p[2] <= '9')
  5178. lo = p[2] - '0';
  5179. else if(p[2] >= 'a' && p[2] <= 'f')
  5180. lo = p[2] - 'a' + 10;
  5181. else if(p[2] >= 'A' && p[2] <= 'F')
  5182. lo = p[2] - 'A' + 10;
  5183. else
  5184. goto nohex;
  5185. if(hi != 0 || lo != 0) // don't unescape NUL bytes
  5186. *q++ = (char) (hi * 0x10 + lo);
  5187. p += 3;
  5188. continue;
  5189. }
  5190. nohex:
  5191. // otherwise:
  5192. *q++ = *p++;
  5193. }
  5194. *q++ = 0;
  5195. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, dest);
  5196. }
  5197. // #502 string(string filename) whichpack (DP_QC_WHICHPACK)
  5198. // returns the name of the pack containing a file, or "" if it is not in any pack (but local or non-existant)
  5199. void VM_whichpack (prvm_prog_t *prog)
  5200. {
  5201. const char *fn, *pack;
  5202. VM_SAFEPARMCOUNT(1, VM_whichpack);
  5203. fn = PRVM_G_STRING(OFS_PARM0);
  5204. pack = FS_WhichPack(fn);
  5205. PRVM_G_INT(OFS_RETURN) = pack ? PRVM_SetTempString(prog, pack) : 0;
  5206. }
  5207. typedef struct
  5208. {
  5209. prvm_prog_t *prog;
  5210. double starttime;
  5211. float id;
  5212. char buffer[MAX_INPUTLINE];
  5213. char posttype[128];
  5214. unsigned char *postdata; // free when uri_to_prog_t is freed
  5215. size_t postlen;
  5216. char *sigdata; // free when uri_to_prog_t is freed
  5217. size_t siglen;
  5218. }
  5219. uri_to_prog_t;
  5220. static void uri_to_string_callback(int status, size_t length_received, unsigned char *buffer, void *cbdata)
  5221. {
  5222. prvm_prog_t *prog;
  5223. uri_to_prog_t *handle = (uri_to_prog_t *) cbdata;
  5224. prog = handle->prog;
  5225. if(!prog->loaded)
  5226. {
  5227. // curl reply came too late... so just drop it
  5228. if(handle->postdata)
  5229. Z_Free(handle->postdata);
  5230. if(handle->sigdata)
  5231. Z_Free(handle->sigdata);
  5232. Z_Free(handle);
  5233. return;
  5234. }
  5235. if((prog->starttime == handle->starttime) && (PRVM_allfunction(URI_Get_Callback)))
  5236. {
  5237. if(length_received >= sizeof(handle->buffer))
  5238. length_received = sizeof(handle->buffer) - 1;
  5239. handle->buffer[length_received] = 0;
  5240. PRVM_G_FLOAT(OFS_PARM0) = handle->id;
  5241. PRVM_G_FLOAT(OFS_PARM1) = status;
  5242. PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(prog, handle->buffer);
  5243. prog->ExecuteProgram(prog, PRVM_allfunction(URI_Get_Callback), "QC function URI_Get_Callback is missing");
  5244. }
  5245. if(handle->postdata)
  5246. Z_Free(handle->postdata);
  5247. if(handle->sigdata)
  5248. Z_Free(handle->sigdata);
  5249. Z_Free(handle);
  5250. }
  5251. // uri_get() gets content from an URL and calls a callback "uri_get_callback" with it set as string; an unique ID of the transfer is returned
  5252. // returns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string
  5253. void VM_uri_get (prvm_prog_t *prog)
  5254. {
  5255. const char *url;
  5256. float id;
  5257. qboolean ret;
  5258. uri_to_prog_t *handle;
  5259. const char *posttype = NULL;
  5260. const char *postseparator = NULL;
  5261. int poststringbuffer = -1;
  5262. int postkeyid = -1;
  5263. const char *query_string = NULL;
  5264. size_t lq;
  5265. if(!PRVM_allfunction(URI_Get_Callback))
  5266. prog->error_cmd("uri_get called by %s without URI_Get_Callback defined", prog->name);
  5267. VM_SAFEPARMCOUNTRANGE(2, 6, VM_uri_get);
  5268. url = PRVM_G_STRING(OFS_PARM0);
  5269. id = PRVM_G_FLOAT(OFS_PARM1);
  5270. if(prog->argc >= 3)
  5271. posttype = PRVM_G_STRING(OFS_PARM2);
  5272. if(prog->argc >= 4)
  5273. postseparator = PRVM_G_STRING(OFS_PARM3);
  5274. if(prog->argc >= 5)
  5275. poststringbuffer = PRVM_G_FLOAT(OFS_PARM4);
  5276. if(prog->argc >= 6)
  5277. postkeyid = PRVM_G_FLOAT(OFS_PARM5);
  5278. handle = (uri_to_prog_t *) Z_Malloc(sizeof(*handle)); // this can't be the prog's mem pool, as curl may call the callback later!
  5279. query_string = strchr(url, '?');
  5280. if(query_string)
  5281. ++query_string;
  5282. lq = query_string ? strlen(query_string) : 0;
  5283. handle->prog = prog;
  5284. handle->starttime = prog->starttime;
  5285. handle->id = id;
  5286. if(postseparator && posttype && *posttype)
  5287. {
  5288. size_t l = strlen(postseparator);
  5289. if(poststringbuffer >= 0)
  5290. {
  5291. size_t ltotal;
  5292. int i;
  5293. // "implode"
  5294. prvm_stringbuffer_t *stringbuffer;
  5295. stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, poststringbuffer);
  5296. if(!stringbuffer)
  5297. {
  5298. VM_Warning(prog, "uri_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
  5299. return;
  5300. }
  5301. ltotal = 0;
  5302. for(i = 0;i < stringbuffer->num_strings;i++)
  5303. {
  5304. if(i > 0)
  5305. ltotal += l;
  5306. if(stringbuffer->strings[i])
  5307. ltotal += strlen(stringbuffer->strings[i]);
  5308. }
  5309. handle->postdata = (unsigned char *)Z_Malloc(ltotal + 1 + lq);
  5310. handle->postlen = ltotal;
  5311. ltotal = 0;
  5312. for(i = 0;i < stringbuffer->num_strings;i++)
  5313. {
  5314. if(i > 0)
  5315. {
  5316. memcpy(handle->postdata + ltotal, postseparator, l);
  5317. ltotal += l;
  5318. }
  5319. if(stringbuffer->strings[i])
  5320. {
  5321. memcpy(handle->postdata + ltotal, stringbuffer->strings[i], strlen(stringbuffer->strings[i]));
  5322. ltotal += strlen(stringbuffer->strings[i]);
  5323. }
  5324. }
  5325. if(ltotal != handle->postlen)
  5326. prog->error_cmd("%s: string buffer content size mismatch, possible overrun", prog->name);
  5327. }
  5328. else
  5329. {
  5330. handle->postdata = (unsigned char *)Z_Malloc(l + 1 + lq);
  5331. handle->postlen = l;
  5332. memcpy(handle->postdata, postseparator, l);
  5333. }
  5334. handle->postdata[handle->postlen] = 0;
  5335. if(query_string)
  5336. memcpy(handle->postdata + handle->postlen + 1, query_string, lq);
  5337. if(postkeyid >= 0)
  5338. {
  5339. // POST: we sign postdata \0 query string
  5340. size_t ll;
  5341. handle->sigdata = (char *)Z_Malloc(8192);
  5342. strlcpy(handle->sigdata, "X-D0-Blind-ID-Detached-Signature: ", 8192);
  5343. l = strlen(handle->sigdata);
  5344. handle->siglen = Crypto_SignDataDetached(handle->postdata, handle->postlen + 1 + lq, postkeyid, handle->sigdata + l, 8192 - l);
  5345. if(!handle->siglen)
  5346. {
  5347. Z_Free(handle->sigdata);
  5348. handle->sigdata = NULL;
  5349. goto out1;
  5350. }
  5351. ll = base64_encode((unsigned char *) (handle->sigdata + l), handle->siglen, 8192 - l - 1);
  5352. if(!ll)
  5353. {
  5354. Z_Free(handle->sigdata);
  5355. handle->sigdata = NULL;
  5356. goto out1;
  5357. }
  5358. handle->siglen = l + ll;
  5359. handle->sigdata[handle->siglen] = 0;
  5360. }
  5361. out1:
  5362. strlcpy(handle->posttype, posttype, sizeof(handle->posttype));
  5363. ret = Curl_Begin_ToMemory_POST(url, handle->sigdata, 0, handle->posttype, handle->postdata, handle->postlen, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
  5364. }
  5365. else
  5366. {
  5367. if(postkeyid >= 0 && query_string)
  5368. {
  5369. // GET: we sign JUST the query string
  5370. size_t l, ll;
  5371. handle->sigdata = (char *)Z_Malloc(8192);
  5372. strlcpy(handle->sigdata, "X-D0-Blind-ID-Detached-Signature: ", 8192);
  5373. l = strlen(handle->sigdata);
  5374. handle->siglen = Crypto_SignDataDetached(query_string, lq, postkeyid, handle->sigdata + l, 8192 - l);
  5375. if(!handle->siglen)
  5376. {
  5377. Z_Free(handle->sigdata);
  5378. handle->sigdata = NULL;
  5379. goto out2;
  5380. }
  5381. ll = base64_encode((unsigned char *) (handle->sigdata + l), handle->siglen, 8192 - l - 1);
  5382. if(!ll)
  5383. {
  5384. Z_Free(handle->sigdata);
  5385. handle->sigdata = NULL;
  5386. goto out2;
  5387. }
  5388. handle->siglen = l + ll;
  5389. handle->sigdata[handle->siglen] = 0;
  5390. }
  5391. out2:
  5392. handle->postdata = NULL;
  5393. handle->postlen = 0;
  5394. ret = Curl_Begin_ToMemory_POST(url, handle->sigdata, 0, NULL, NULL, 0, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
  5395. }
  5396. if(ret)
  5397. {
  5398. PRVM_G_INT(OFS_RETURN) = 1;
  5399. }
  5400. else
  5401. {
  5402. if(handle->postdata)
  5403. Z_Free(handle->postdata);
  5404. if(handle->sigdata)
  5405. Z_Free(handle->sigdata);
  5406. Z_Free(handle);
  5407. PRVM_G_INT(OFS_RETURN) = 0;
  5408. }
  5409. }
  5410. void VM_netaddress_resolve (prvm_prog_t *prog)
  5411. {
  5412. const char *ip;
  5413. char normalized[128];
  5414. int port;
  5415. lhnetaddress_t addr;
  5416. VM_SAFEPARMCOUNTRANGE(1, 2, VM_netaddress_resolve);
  5417. ip = PRVM_G_STRING(OFS_PARM0);
  5418. port = 0;
  5419. if(prog->argc > 1)
  5420. port = (int) PRVM_G_FLOAT(OFS_PARM1);
  5421. if(LHNETADDRESS_FromString(&addr, ip, port) && LHNETADDRESS_ToString(&addr, normalized, sizeof(normalized), prog->argc > 1))
  5422. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, normalized);
  5423. else
  5424. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
  5425. }
  5426. //string(prvm_prog_t *prog) getextresponse = #624; // returns the next extResponse packet that was sent to this client
  5427. void VM_CL_getextresponse (prvm_prog_t *prog)
  5428. {
  5429. VM_SAFEPARMCOUNT(0,VM_argv);
  5430. if (cl_net_extresponse_count <= 0)
  5431. PRVM_G_INT(OFS_RETURN) = OFS_NULL;
  5432. else
  5433. {
  5434. int first;
  5435. --cl_net_extresponse_count;
  5436. first = (cl_net_extresponse_last + NET_EXTRESPONSE_MAX - cl_net_extresponse_count) % NET_EXTRESPONSE_MAX;
  5437. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cl_net_extresponse[first]);
  5438. }
  5439. }
  5440. void VM_SV_getextresponse (prvm_prog_t *prog)
  5441. {
  5442. VM_SAFEPARMCOUNT(0,VM_argv);
  5443. if (sv_net_extresponse_count <= 0)
  5444. PRVM_G_INT(OFS_RETURN) = OFS_NULL;
  5445. else
  5446. {
  5447. int first;
  5448. --sv_net_extresponse_count;
  5449. first = (sv_net_extresponse_last + NET_EXTRESPONSE_MAX - sv_net_extresponse_count) % NET_EXTRESPONSE_MAX;
  5450. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, sv_net_extresponse[first]);
  5451. }
  5452. }
  5453. /*
  5454. =========
  5455. Common functions between menu.dat and clsprogs
  5456. =========
  5457. */
  5458. //#349 float() isdemo
  5459. void VM_CL_isdemo (prvm_prog_t *prog)
  5460. {
  5461. VM_SAFEPARMCOUNT(0, VM_CL_isdemo);
  5462. PRVM_G_FLOAT(OFS_RETURN) = cls.demoplayback;
  5463. }
  5464. //#355 float() videoplaying
  5465. void VM_CL_videoplaying (prvm_prog_t *prog)
  5466. {
  5467. VM_SAFEPARMCOUNT(0, VM_CL_videoplaying);
  5468. PRVM_G_FLOAT(OFS_RETURN) = cl_videoplaying;
  5469. }
  5470. /*
  5471. =========
  5472. VM_M_callfunction
  5473. callfunction(...,string function_name)
  5474. Extension: pass
  5475. =========
  5476. */
  5477. void VM_callfunction(prvm_prog_t *prog)
  5478. {
  5479. mfunction_t *func;
  5480. const char *s;
  5481. VM_SAFEPARMCOUNTRANGE(1, 8, VM_callfunction);
  5482. s = PRVM_G_STRING(OFS_PARM0+(prog->argc - 1)*3);
  5483. VM_CheckEmptyString(prog, s);
  5484. func = PRVM_ED_FindFunction(prog, s);
  5485. if(!func)
  5486. prog->error_cmd("VM_callfunciton: function %s not found !", s);
  5487. else if (func->first_statement < 0)
  5488. {
  5489. // negative statements are built in functions
  5490. int builtinnumber = -func->first_statement;
  5491. prog->xfunction->builtinsprofile++;
  5492. if (builtinnumber < prog->numbuiltins && prog->builtins[builtinnumber])
  5493. prog->builtins[builtinnumber](prog);
  5494. else
  5495. prog->error_cmd("No such builtin #%i in %s; most likely cause: outdated engine build. Try updating!", builtinnumber, prog->name);
  5496. }
  5497. else if(func - prog->functions > 0)
  5498. {
  5499. prog->argc--;
  5500. prog->ExecuteProgram(prog, func - prog->functions,"");
  5501. prog->argc++;
  5502. }
  5503. }
  5504. /*
  5505. =========
  5506. VM_isfunction
  5507. float isfunction(string function_name)
  5508. =========
  5509. */
  5510. void VM_isfunction(prvm_prog_t *prog)
  5511. {
  5512. mfunction_t *func;
  5513. const char *s;
  5514. VM_SAFEPARMCOUNT(1, VM_isfunction);
  5515. s = PRVM_G_STRING(OFS_PARM0);
  5516. VM_CheckEmptyString(prog, s);
  5517. func = PRVM_ED_FindFunction(prog, s);
  5518. if(!func)
  5519. PRVM_G_FLOAT(OFS_RETURN) = false;
  5520. else
  5521. PRVM_G_FLOAT(OFS_RETURN) = true;
  5522. }
  5523. /*
  5524. =========
  5525. VM_sprintf
  5526. string sprintf(string format, ...)
  5527. =========
  5528. */
  5529. void VM_sprintf(prvm_prog_t *prog)
  5530. {
  5531. const char *s, *s0;
  5532. char outbuf[MAX_INPUTLINE];
  5533. char *o = outbuf, *end = outbuf + sizeof(outbuf), *err;
  5534. const char *p;
  5535. int argpos = 1;
  5536. int width, precision, thisarg, flags;
  5537. char formatbuf[16];
  5538. char *f;
  5539. int isfloat;
  5540. static prvm_int_t dummyivec[3] = {0, 0, 0};
  5541. static prvm_vec_t dummyvec[3] = {0, 0, 0};
  5542. char vabuf[1024];
  5543. #define PRINTF_ALTERNATE 1
  5544. #define PRINTF_ZEROPAD 2
  5545. #define PRINTF_LEFT 4
  5546. #define PRINTF_SPACEPOSITIVE 8
  5547. #define PRINTF_SIGNPOSITIVE 16
  5548. formatbuf[0] = '%';
  5549. s = PRVM_G_STRING(OFS_PARM0);
  5550. #define GETARG_FLOAT(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_FLOAT(OFS_PARM0 + 3 * (a))) : 0)
  5551. #define GETARG_VECTOR(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyvec)
  5552. #define GETARG_INT(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_INT(OFS_PARM0 + 3 * (a))) : 0)
  5553. #define GETARG_INTVECTOR(a) (((a)>=1 && (a)<prog->argc) ? ((prvm_int_t*) PRVM_G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyivec)
  5554. #define GETARG_STRING(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_STRING(OFS_PARM0 + 3 * (a))) : "")
  5555. for(;;)
  5556. {
  5557. s0 = s;
  5558. switch(*s)
  5559. {
  5560. case 0:
  5561. goto finished;
  5562. case '%':
  5563. ++s;
  5564. if(*s == '%')
  5565. goto verbatim;
  5566. // complete directive format:
  5567. // %3$*1$.*2$ld
  5568. width = -1;
  5569. precision = -1;
  5570. thisarg = -1;
  5571. flags = 0;
  5572. isfloat = -1;
  5573. // is number following?
  5574. if(*s >= '0' && *s <= '9')
  5575. {
  5576. width = strtol(s, &err, 10);
  5577. if(!err)
  5578. {
  5579. VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
  5580. goto finished;
  5581. }
  5582. if(*err == '$')
  5583. {
  5584. thisarg = width;
  5585. width = -1;
  5586. s = err + 1;
  5587. }
  5588. else
  5589. {
  5590. if(*s == '0')
  5591. {
  5592. flags |= PRINTF_ZEROPAD;
  5593. if(width == 0)
  5594. width = -1; // it was just a flag
  5595. }
  5596. s = err;
  5597. }
  5598. }
  5599. if(width < 0)
  5600. {
  5601. for(;;)
  5602. {
  5603. switch(*s)
  5604. {
  5605. case '#': flags |= PRINTF_ALTERNATE; break;
  5606. case '0': flags |= PRINTF_ZEROPAD; break;
  5607. case '-': flags |= PRINTF_LEFT; break;
  5608. case ' ': flags |= PRINTF_SPACEPOSITIVE; break;
  5609. case '+': flags |= PRINTF_SIGNPOSITIVE; break;
  5610. default:
  5611. goto noflags;
  5612. }
  5613. ++s;
  5614. }
  5615. noflags:
  5616. if(*s == '*')
  5617. {
  5618. ++s;
  5619. if(*s >= '0' && *s <= '9')
  5620. {
  5621. width = strtol(s, &err, 10);
  5622. if(!err || *err != '$')
  5623. {
  5624. VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
  5625. goto finished;
  5626. }
  5627. s = err + 1;
  5628. }
  5629. else
  5630. width = argpos++;
  5631. width = GETARG_FLOAT(width);
  5632. if(width < 0)
  5633. {
  5634. flags |= PRINTF_LEFT;
  5635. width = -width;
  5636. }
  5637. }
  5638. else if(*s >= '0' && *s <= '9')
  5639. {
  5640. width = strtol(s, &err, 10);
  5641. if(!err)
  5642. {
  5643. VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
  5644. goto finished;
  5645. }
  5646. s = err;
  5647. if(width < 0)
  5648. {
  5649. flags |= PRINTF_LEFT;
  5650. width = -width;
  5651. }
  5652. }
  5653. // otherwise width stays -1
  5654. }
  5655. if(*s == '.')
  5656. {
  5657. ++s;
  5658. if(*s == '*')
  5659. {
  5660. ++s;
  5661. if(*s >= '0' && *s <= '9')
  5662. {
  5663. precision = strtol(s, &err, 10);
  5664. if(!err || *err != '$')
  5665. {
  5666. VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
  5667. goto finished;
  5668. }
  5669. s = err + 1;
  5670. }
  5671. else
  5672. precision = argpos++;
  5673. precision = GETARG_FLOAT(precision);
  5674. }
  5675. else if(*s >= '0' && *s <= '9')
  5676. {
  5677. precision = strtol(s, &err, 10);
  5678. if(!err)
  5679. {
  5680. VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
  5681. goto finished;
  5682. }
  5683. s = err;
  5684. }
  5685. else
  5686. {
  5687. VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
  5688. goto finished;
  5689. }
  5690. }
  5691. for(;;)
  5692. {
  5693. switch(*s)
  5694. {
  5695. case 'h': isfloat = 1; break;
  5696. case 'l': isfloat = 0; break;
  5697. case 'L': isfloat = 0; break;
  5698. case 'j': break;
  5699. case 'z': break;
  5700. case 't': break;
  5701. default:
  5702. goto nolength;
  5703. }
  5704. ++s;
  5705. }
  5706. nolength:
  5707. // now s points to the final directive char and is no longer changed
  5708. if(isfloat < 0)
  5709. {
  5710. if(*s == 'i')
  5711. isfloat = 0;
  5712. else
  5713. isfloat = 1;
  5714. }
  5715. if(thisarg < 0)
  5716. thisarg = argpos++;
  5717. if(o < end - 1)
  5718. {
  5719. f = &formatbuf[1];
  5720. if(*s != 's' && *s != 'c')
  5721. if(flags & PRINTF_ALTERNATE) *f++ = '#';
  5722. if(flags & PRINTF_ZEROPAD) *f++ = '0';
  5723. if(flags & PRINTF_LEFT) *f++ = '-';
  5724. if(flags & PRINTF_SPACEPOSITIVE) *f++ = ' ';
  5725. if(flags & PRINTF_SIGNPOSITIVE) *f++ = '+';
  5726. *f++ = '*';
  5727. if(precision >= 0)
  5728. {
  5729. *f++ = '.';
  5730. *f++ = '*';
  5731. }
  5732. if(*s == 'd' || *s == 'i' || *s == 'o' || *s == 'u' || *s == 'x' || *s == 'X')
  5733. {
  5734. // make it use a good integer type
  5735. for(p = INT_LOSSLESS_FORMAT_SIZE; *p; )
  5736. *f++ = *p++;
  5737. }
  5738. *f++ = *s;
  5739. *f++ = 0;
  5740. if(width < 0) // not set
  5741. width = 0;
  5742. switch(*s)
  5743. {
  5744. case 'd': case 'i':
  5745. if(precision < 0) // not set
  5746. o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? INT_LOSSLESS_FORMAT_CONVERT_S(GETARG_FLOAT(thisarg)) : INT_LOSSLESS_FORMAT_CONVERT_S(GETARG_INT(thisarg))));
  5747. else
  5748. o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? INT_LOSSLESS_FORMAT_CONVERT_S(GETARG_FLOAT(thisarg)) : INT_LOSSLESS_FORMAT_CONVERT_S(GETARG_INT(thisarg))));
  5749. break;
  5750. case 'o': case 'u': case 'x': case 'X':
  5751. if(precision < 0) // not set
  5752. o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? INT_LOSSLESS_FORMAT_CONVERT_U(GETARG_FLOAT(thisarg)) : INT_LOSSLESS_FORMAT_CONVERT_U(GETARG_INT(thisarg))));
  5753. else
  5754. o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? INT_LOSSLESS_FORMAT_CONVERT_U(GETARG_FLOAT(thisarg)) : INT_LOSSLESS_FORMAT_CONVERT_U(GETARG_INT(thisarg))));
  5755. break;
  5756. case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
  5757. if(precision < 0) // not set
  5758. o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg)));
  5759. else
  5760. o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg)));
  5761. break;
  5762. case 'v': case 'V':
  5763. f[-2] += 'g' - 'v';
  5764. if(precision < 0) // not set
  5765. o += dpsnprintf(o, end - o, va(vabuf, sizeof(vabuf), "%s %s %s", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf),
  5766. width, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]),
  5767. width, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]),
  5768. width, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2])
  5769. );
  5770. else
  5771. o += dpsnprintf(o, end - o, va(vabuf, sizeof(vabuf), "%s %s %s", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf),
  5772. width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]),
  5773. width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]),
  5774. width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2])
  5775. );
  5776. break;
  5777. case 'c':
  5778. if(flags & PRINTF_ALTERNATE)
  5779. {
  5780. if(precision < 0) // not set
  5781. o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
  5782. else
  5783. o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
  5784. }
  5785. else
  5786. {
  5787. unsigned int c = (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg));
  5788. char charbuf16[16];
  5789. const char *buf = u8_encodech(c, NULL, charbuf16);
  5790. if(!buf)
  5791. buf = "";
  5792. if(precision < 0) // not set
  5793. precision = end - o - 1;
  5794. o += u8_strpad(o, end - o, buf, (flags & PRINTF_LEFT) != 0, width, precision);
  5795. }
  5796. break;
  5797. case 's':
  5798. if(flags & PRINTF_ALTERNATE)
  5799. {
  5800. if(precision < 0) // not set
  5801. o += dpsnprintf(o, end - o, formatbuf, width, GETARG_STRING(thisarg));
  5802. else
  5803. o += dpsnprintf(o, end - o, formatbuf, width, precision, GETARG_STRING(thisarg));
  5804. }
  5805. else
  5806. {
  5807. if(precision < 0) // not set
  5808. precision = end - o - 1;
  5809. if(flags & PRINTF_SIGNPOSITIVE)
  5810. o += u8_strpad(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision);
  5811. else
  5812. o += u8_strpad_colorcodes(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision);
  5813. }
  5814. break;
  5815. default:
  5816. VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
  5817. goto finished;
  5818. }
  5819. }
  5820. ++s;
  5821. break;
  5822. default:
  5823. verbatim:
  5824. if(o < end - 1)
  5825. *o++ = *s;
  5826. ++s;
  5827. break;
  5828. }
  5829. }
  5830. finished:
  5831. *o = 0;
  5832. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, outbuf);
  5833. }
  5834. // surface querying
  5835. static dp_model_t *getmodel(prvm_prog_t *prog, prvm_edict_t *ed)
  5836. {
  5837. if (prog == SVVM_prog)
  5838. return SV_GetModelFromEdict(ed);
  5839. else if (prog == CLVM_prog)
  5840. return CL_GetModelFromEdict(ed);
  5841. else
  5842. return NULL;
  5843. }
  5844. struct animatemodel_cache
  5845. {
  5846. dp_model_t *model;
  5847. frameblend_t frameblend[MAX_FRAMEBLENDS];
  5848. skeleton_t *skeleton_p;
  5849. skeleton_t skeleton;
  5850. float *data_vertex3f;
  5851. float *data_svector3f;
  5852. float *data_tvector3f;
  5853. float *data_normal3f;
  5854. int max_vertices;
  5855. float *buf_vertex3f;
  5856. float *buf_svector3f;
  5857. float *buf_tvector3f;
  5858. float *buf_normal3f;
  5859. };
  5860. static void animatemodel_reset(prvm_prog_t *prog)
  5861. {
  5862. if (!prog->animatemodel_cache)
  5863. return;
  5864. if(prog->animatemodel_cache->buf_vertex3f) Mem_Free(prog->animatemodel_cache->buf_vertex3f);
  5865. if(prog->animatemodel_cache->buf_svector3f) Mem_Free(prog->animatemodel_cache->buf_svector3f);
  5866. if(prog->animatemodel_cache->buf_tvector3f) Mem_Free(prog->animatemodel_cache->buf_tvector3f);
  5867. if(prog->animatemodel_cache->buf_normal3f) Mem_Free(prog->animatemodel_cache->buf_normal3f);
  5868. Mem_Free(prog->animatemodel_cache);
  5869. }
  5870. static void animatemodel(prvm_prog_t *prog, dp_model_t *model, prvm_edict_t *ed)
  5871. {
  5872. skeleton_t *skeleton;
  5873. int skeletonindex = -1;
  5874. qboolean need = false;
  5875. struct animatemodel_cache *animatemodel_cache;
  5876. if (!prog->animatemodel_cache)
  5877. {
  5878. prog->animatemodel_cache = (struct animatemodel_cache *)Mem_Alloc(prog->progs_mempool, sizeof(struct animatemodel_cache));
  5879. memset(prog->animatemodel_cache, 0, sizeof(struct animatemodel_cache));
  5880. }
  5881. animatemodel_cache = prog->animatemodel_cache;
  5882. if(!(model->surfmesh.isanimated && model->AnimateVertices))
  5883. {
  5884. animatemodel_cache->data_vertex3f = model->surfmesh.data_vertex3f;
  5885. animatemodel_cache->data_svector3f = model->surfmesh.data_svector3f;
  5886. animatemodel_cache->data_tvector3f = model->surfmesh.data_tvector3f;
  5887. animatemodel_cache->data_normal3f = model->surfmesh.data_normal3f;
  5888. return;
  5889. }
  5890. need |= (animatemodel_cache->model != model);
  5891. VM_GenerateFrameGroupBlend(prog, ed->priv.server->framegroupblend, ed);
  5892. VM_FrameBlendFromFrameGroupBlend(ed->priv.server->frameblend, ed->priv.server->framegroupblend, model, PRVM_serverglobalfloat(time));
  5893. need |= (memcmp(&animatemodel_cache->frameblend, &ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend))) != 0;
  5894. skeletonindex = (int)PRVM_gameedictfloat(ed, skeletonindex) - 1;
  5895. if (!(skeletonindex >= 0 && skeletonindex < MAX_EDICTS && (skeleton = prog->skeletons[skeletonindex]) && skeleton->model->num_bones == ed->priv.server->skeleton.model->num_bones))
  5896. skeleton = NULL;
  5897. need |= (animatemodel_cache->skeleton_p != skeleton);
  5898. if(skeleton)
  5899. need |= (memcmp(&animatemodel_cache->skeleton, skeleton, sizeof(ed->priv.server->skeleton))) != 0;
  5900. if(!need)
  5901. return;
  5902. if(model->surfmesh.num_vertices > animatemodel_cache->max_vertices)
  5903. {
  5904. animatemodel_cache->max_vertices = model->surfmesh.num_vertices * 2;
  5905. if(animatemodel_cache->buf_vertex3f) Mem_Free(animatemodel_cache->buf_vertex3f);
  5906. if(animatemodel_cache->buf_svector3f) Mem_Free(animatemodel_cache->buf_svector3f);
  5907. if(animatemodel_cache->buf_tvector3f) Mem_Free(animatemodel_cache->buf_tvector3f);
  5908. if(animatemodel_cache->buf_normal3f) Mem_Free(animatemodel_cache->buf_normal3f);
  5909. animatemodel_cache->buf_vertex3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
  5910. animatemodel_cache->buf_svector3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
  5911. animatemodel_cache->buf_tvector3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
  5912. animatemodel_cache->buf_normal3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
  5913. }
  5914. animatemodel_cache->data_vertex3f = animatemodel_cache->buf_vertex3f;
  5915. animatemodel_cache->data_svector3f = animatemodel_cache->buf_svector3f;
  5916. animatemodel_cache->data_tvector3f = animatemodel_cache->buf_tvector3f;
  5917. animatemodel_cache->data_normal3f = animatemodel_cache->buf_normal3f;
  5918. VM_UpdateEdictSkeleton(prog, ed, model, ed->priv.server->frameblend);
  5919. model->AnimateVertices(model, ed->priv.server->frameblend, &ed->priv.server->skeleton, animatemodel_cache->data_vertex3f, animatemodel_cache->data_normal3f, animatemodel_cache->data_svector3f, animatemodel_cache->data_tvector3f);
  5920. animatemodel_cache->model = model;
  5921. memcpy(&animatemodel_cache->frameblend, &ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend));
  5922. animatemodel_cache->skeleton_p = skeleton;
  5923. if(skeleton)
  5924. memcpy(&animatemodel_cache->skeleton, skeleton, sizeof(ed->priv.server->skeleton));
  5925. }
  5926. static void getmatrix(prvm_prog_t *prog, prvm_edict_t *ed, matrix4x4_t *out)
  5927. {
  5928. if (prog == SVVM_prog)
  5929. SV_GetEntityMatrix(prog, ed, out, false);
  5930. else if (prog == CLVM_prog)
  5931. CL_GetEntityMatrix(prog, ed, out, false);
  5932. else
  5933. *out = identitymatrix;
  5934. }
  5935. static void applytransform_forward(prvm_prog_t *prog, const vec3_t in, prvm_edict_t *ed, vec3_t out)
  5936. {
  5937. matrix4x4_t m;
  5938. getmatrix(prog, ed, &m);
  5939. Matrix4x4_Transform(&m, in, out);
  5940. }
  5941. static void applytransform_forward_direction(prvm_prog_t *prog, const vec3_t in, prvm_edict_t *ed, vec3_t out)
  5942. {
  5943. matrix4x4_t m;
  5944. getmatrix(prog, ed, &m);
  5945. Matrix4x4_Transform3x3(&m, in, out);
  5946. }
  5947. static void applytransform_inverted(prvm_prog_t *prog, const vec3_t in, prvm_edict_t *ed, vec3_t out)
  5948. {
  5949. matrix4x4_t m, n;
  5950. getmatrix(prog, ed, &m);
  5951. Matrix4x4_Invert_Full(&n, &m);
  5952. Matrix4x4_Transform3x3(&n, in, out);
  5953. }
  5954. static void applytransform_forward_normal(prvm_prog_t *prog, const vec3_t in, prvm_edict_t *ed, vec3_t out)
  5955. {
  5956. matrix4x4_t m;
  5957. float p[4];
  5958. getmatrix(prog, ed, &m);
  5959. Matrix4x4_TransformPositivePlane(&m, in[0], in[1], in[2], 0, p);
  5960. VectorCopy(p, out);
  5961. }
  5962. static void clippointtosurface(prvm_prog_t *prog, prvm_edict_t *ed, dp_model_t *model, msurface_t *surface, vec3_t p, vec3_t out)
  5963. {
  5964. int i, j, k;
  5965. float *v[3], facenormal[3], edgenormal[3], sidenormal[3], temp[3], offsetdist, dist, bestdist;
  5966. const int *e;
  5967. animatemodel(prog, model, ed);
  5968. bestdist = 1000000000;
  5969. VectorCopy(p, out);
  5970. for (i = 0, e = (model->surfmesh.data_element3i + 3 * surface->num_firsttriangle);i < surface->num_triangles;i++, e += 3)
  5971. {
  5972. // clip original point to each triangle of the surface and find the
  5973. // triangle that is closest
  5974. v[0] = prog->animatemodel_cache->data_vertex3f + e[0] * 3;
  5975. v[1] = prog->animatemodel_cache->data_vertex3f + e[1] * 3;
  5976. v[2] = prog->animatemodel_cache->data_vertex3f + e[2] * 3;
  5977. TriangleNormal(v[0], v[1], v[2], facenormal);
  5978. VectorNormalize(facenormal);
  5979. offsetdist = DotProduct(v[0], facenormal) - DotProduct(p, facenormal);
  5980. VectorMA(p, offsetdist, facenormal, temp);
  5981. for (j = 0, k = 2;j < 3;k = j, j++)
  5982. {
  5983. VectorSubtract(v[k], v[j], edgenormal);
  5984. CrossProduct(edgenormal, facenormal, sidenormal);
  5985. VectorNormalize(sidenormal);
  5986. offsetdist = DotProduct(v[k], sidenormal) - DotProduct(temp, sidenormal);
  5987. if (offsetdist < 0)
  5988. VectorMA(temp, offsetdist, sidenormal, temp);
  5989. }
  5990. dist = VectorDistance2(temp, p);
  5991. if (bestdist > dist)
  5992. {
  5993. bestdist = dist;
  5994. VectorCopy(temp, out);
  5995. }
  5996. }
  5997. }
  5998. static msurface_t *getsurface(dp_model_t *model, int surfacenum)
  5999. {
  6000. if (surfacenum < 0 || surfacenum >= model->nummodelsurfaces)
  6001. return NULL;
  6002. return model->data_surfaces + surfacenum + model->firstmodelsurface;
  6003. }
  6004. //PF_getsurfacenumpoints, // #434 float(entity e, float s) getsurfacenumpoints = #434;
  6005. void VM_getsurfacenumpoints(prvm_prog_t *prog)
  6006. {
  6007. dp_model_t *model;
  6008. msurface_t *surface;
  6009. VM_SAFEPARMCOUNT(2, VM_getsurfacenumpoints);
  6010. // return 0 if no such surface
  6011. if (!(model = getmodel(prog, PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
  6012. {
  6013. PRVM_G_FLOAT(OFS_RETURN) = 0;
  6014. return;
  6015. }
  6016. // note: this (incorrectly) assumes it is a simple polygon
  6017. PRVM_G_FLOAT(OFS_RETURN) = surface->num_vertices;
  6018. }
  6019. //PF_getsurfacepoint, // #435 vector(entity e, float s, float n) getsurfacepoint = #435;
  6020. void VM_getsurfacepoint(prvm_prog_t *prog)
  6021. {
  6022. prvm_edict_t *ed;
  6023. dp_model_t *model;
  6024. msurface_t *surface;
  6025. int pointnum;
  6026. vec3_t result;
  6027. VM_SAFEPARMCOUNT(3, VM_getsurfacepoint);
  6028. VectorClear(PRVM_G_VECTOR(OFS_RETURN));
  6029. ed = PRVM_G_EDICT(OFS_PARM0);
  6030. if (!(model = getmodel(prog, ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
  6031. return;
  6032. // note: this (incorrectly) assumes it is a simple polygon
  6033. pointnum = (int)PRVM_G_FLOAT(OFS_PARM2);
  6034. if (pointnum < 0 || pointnum >= surface->num_vertices)
  6035. return;
  6036. animatemodel(prog, model, ed);
  6037. applytransform_forward(prog, &(prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
  6038. VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
  6039. }
  6040. //PF_getsurfacepointattribute, // #486 vector(entity e, float s, float n, float a) getsurfacepointattribute = #486;
  6041. // float SPA_POSITION = 0;
  6042. // float SPA_S_AXIS = 1;
  6043. // float SPA_T_AXIS = 2;
  6044. // float SPA_R_AXIS = 3; // same as SPA_NORMAL
  6045. // float SPA_TEXCOORDS0 = 4;
  6046. // float SPA_LIGHTMAP0_TEXCOORDS = 5;
  6047. // float SPA_LIGHTMAP0_COLOR = 6;
  6048. void VM_getsurfacepointattribute(prvm_prog_t *prog)
  6049. {
  6050. prvm_edict_t *ed;
  6051. dp_model_t *model;
  6052. msurface_t *surface;
  6053. int pointnum;
  6054. int attributetype;
  6055. vec3_t result;
  6056. VM_SAFEPARMCOUNT(4, VM_getsurfacepoint);
  6057. VectorClear(PRVM_G_VECTOR(OFS_RETURN));
  6058. ed = PRVM_G_EDICT(OFS_PARM0);
  6059. if (!(model = getmodel(prog, ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
  6060. return;
  6061. pointnum = (int)PRVM_G_FLOAT(OFS_PARM2);
  6062. if (pointnum < 0 || pointnum >= surface->num_vertices)
  6063. return;
  6064. attributetype = (int) PRVM_G_FLOAT(OFS_PARM3);
  6065. animatemodel(prog, model, ed);
  6066. switch( attributetype ) {
  6067. // float SPA_POSITION = 0;
  6068. case 0:
  6069. applytransform_forward(prog, &(prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
  6070. VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
  6071. break;
  6072. // float SPA_S_AXIS = 1;
  6073. case 1:
  6074. applytransform_forward_direction(prog, &(prog->animatemodel_cache->data_svector3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
  6075. VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
  6076. break;
  6077. // float SPA_T_AXIS = 2;
  6078. case 2:
  6079. applytransform_forward_direction(prog, &(prog->animatemodel_cache->data_tvector3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
  6080. VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
  6081. break;
  6082. // float SPA_R_AXIS = 3; // same as SPA_NORMAL
  6083. case 3:
  6084. applytransform_forward_direction(prog, &(prog->animatemodel_cache->data_normal3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
  6085. VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
  6086. break;
  6087. // float SPA_TEXCOORDS0 = 4;
  6088. case 4: {
  6089. float *texcoord = &(model->surfmesh.data_texcoordtexture2f + 2 * surface->num_firstvertex)[pointnum * 2];
  6090. result[0] = texcoord[0];
  6091. result[1] = texcoord[1];
  6092. result[2] = 0.0f;
  6093. VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
  6094. break;
  6095. }
  6096. // float SPA_LIGHTMAP0_TEXCOORDS = 5;
  6097. case 5: {
  6098. float *texcoord = &(model->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[pointnum * 2];
  6099. result[0] = texcoord[0];
  6100. result[1] = texcoord[1];
  6101. result[2] = 0.0f;
  6102. VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
  6103. break;
  6104. }
  6105. // float SPA_LIGHTMAP0_COLOR = 6;
  6106. case 6:
  6107. // ignore alpha for now..
  6108. VectorCopy( &(model->surfmesh.data_lightmapcolor4f + 4 * surface->num_firstvertex)[pointnum * 4], PRVM_G_VECTOR(OFS_RETURN));
  6109. break;
  6110. default:
  6111. VectorSet( PRVM_G_VECTOR(OFS_RETURN), 0.0f, 0.0f, 0.0f );
  6112. break;
  6113. }
  6114. }
  6115. //PF_getsurfacenormal, // #436 vector(entity e, float s) getsurfacenormal = #436;
  6116. void VM_getsurfacenormal(prvm_prog_t *prog)
  6117. {
  6118. dp_model_t *model;
  6119. msurface_t *surface;
  6120. vec3_t normal;
  6121. vec3_t result;
  6122. VM_SAFEPARMCOUNT(2, VM_getsurfacenormal);
  6123. VectorClear(PRVM_G_VECTOR(OFS_RETURN));
  6124. if (!(model = getmodel(prog, PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
  6125. return;
  6126. // note: this only returns the first triangle, so it doesn't work very
  6127. // well for curved surfaces or arbitrary meshes
  6128. animatemodel(prog, model, PRVM_G_EDICT(OFS_PARM0));
  6129. TriangleNormal((prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex), (prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex) + 3, (prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex) + 6, normal);
  6130. applytransform_forward_normal(prog, normal, PRVM_G_EDICT(OFS_PARM0), result);
  6131. VectorNormalize(result);
  6132. VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
  6133. }
  6134. //PF_getsurfacetexture, // #437 string(entity e, float s) getsurfacetexture = #437;
  6135. void VM_getsurfacetexture(prvm_prog_t *prog)
  6136. {
  6137. dp_model_t *model;
  6138. msurface_t *surface;
  6139. VM_SAFEPARMCOUNT(2, VM_getsurfacetexture);
  6140. PRVM_G_INT(OFS_RETURN) = OFS_NULL;
  6141. if (!(model = getmodel(prog, PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
  6142. return;
  6143. PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, surface->texture->name);
  6144. }
  6145. //PF_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint = #438;
  6146. void VM_getsurfacenearpoint(prvm_prog_t *prog)
  6147. {
  6148. int surfacenum, best;
  6149. vec3_t clipped, p;
  6150. vec_t dist, bestdist;
  6151. prvm_edict_t *ed;
  6152. dp_model_t *model;
  6153. msurface_t *surface;
  6154. vec3_t point;
  6155. VM_SAFEPARMCOUNT(2, VM_getsurfacenearpoint);
  6156. PRVM_G_FLOAT(OFS_RETURN) = -1;
  6157. ed = PRVM_G_EDICT(OFS_PARM0);
  6158. VectorCopy(PRVM_G_VECTOR(OFS_PARM1), point);
  6159. if (!ed || ed->priv.server->free)
  6160. return;
  6161. model = getmodel(prog, ed);
  6162. if (!model || !model->num_surfaces)
  6163. return;
  6164. animatemodel(prog, model, ed);
  6165. applytransform_inverted(prog, point, ed, p);
  6166. best = -1;
  6167. bestdist = 1000000000;
  6168. for (surfacenum = 0;surfacenum < model->nummodelsurfaces;surfacenum++)
  6169. {
  6170. surface = model->data_surfaces + surfacenum + model->firstmodelsurface;
  6171. // first see if the nearest point on the surface's box is closer than the previous match
  6172. clipped[0] = bound(surface->mins[0], p[0], surface->maxs[0]) - p[0];
  6173. clipped[1] = bound(surface->mins[1], p[1], surface->maxs[1]) - p[1];
  6174. clipped[2] = bound(surface->mins[2], p[2], surface->maxs[2]) - p[2];
  6175. dist = VectorLength2(clipped);
  6176. if (dist < bestdist)
  6177. {
  6178. // it is, check the nearest point on the actual geometry
  6179. clippointtosurface(prog, ed, model, surface, p, clipped);
  6180. VectorSubtract(clipped, p, clipped);
  6181. dist += VectorLength2(clipped);
  6182. if (dist < bestdist)
  6183. {
  6184. // that's closer too, store it as the best match
  6185. best = surfacenum;
  6186. bestdist = dist;
  6187. }
  6188. }
  6189. }
  6190. PRVM_G_FLOAT(OFS_RETURN) = best;
  6191. }
  6192. //PF_getsurfaceclippedpoint, // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint = #439;
  6193. void VM_getsurfaceclippedpoint(prvm_prog_t *prog)
  6194. {
  6195. prvm_edict_t *ed;
  6196. dp_model_t *model;
  6197. msurface_t *surface;
  6198. vec3_t p, out, inp;
  6199. VM_SAFEPARMCOUNT(3, VM_te_getsurfaceclippedpoint);
  6200. VectorClear(PRVM_G_VECTOR(OFS_RETURN));
  6201. ed = PRVM_G_EDICT(OFS_PARM0);
  6202. if (!(model = getmodel(prog, ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
  6203. return;
  6204. animatemodel(prog, model, ed);
  6205. VectorCopy(PRVM_G_VECTOR(OFS_PARM2), inp);
  6206. applytransform_inverted(prog, inp, ed, p);
  6207. clippointtosurface(prog, ed, model, surface, p, out);
  6208. VectorAdd(out, PRVM_serveredictvector(ed, origin), PRVM_G_VECTOR(OFS_RETURN));
  6209. }
  6210. //PF_getsurfacenumtriangles, // #??? float(entity e, float s) getsurfacenumtriangles = #???;
  6211. void VM_getsurfacenumtriangles(prvm_prog_t *prog)
  6212. {
  6213. dp_model_t *model;
  6214. msurface_t *surface;
  6215. VM_SAFEPARMCOUNT(2, VM_SV_getsurfacenumtriangles);
  6216. // return 0 if no such surface
  6217. if (!(model = getmodel(prog, PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
  6218. {
  6219. PRVM_G_FLOAT(OFS_RETURN) = 0;
  6220. return;
  6221. }
  6222. PRVM_G_FLOAT(OFS_RETURN) = surface->num_triangles;
  6223. }
  6224. //PF_getsurfacetriangle, // #??? vector(entity e, float s, float n) getsurfacetriangle = #???;
  6225. void VM_getsurfacetriangle(prvm_prog_t *prog)
  6226. {
  6227. const vec3_t d = {-1, -1, -1};
  6228. prvm_edict_t *ed;
  6229. dp_model_t *model;
  6230. msurface_t *surface;
  6231. int trinum;
  6232. VM_SAFEPARMCOUNT(3, VM_SV_getsurfacetriangle);
  6233. VectorClear(PRVM_G_VECTOR(OFS_RETURN));
  6234. ed = PRVM_G_EDICT(OFS_PARM0);
  6235. if (!(model = getmodel(prog, ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
  6236. return;
  6237. trinum = (int)PRVM_G_FLOAT(OFS_PARM2);
  6238. if (trinum < 0 || trinum >= surface->num_triangles)
  6239. return;
  6240. // FIXME: implement rotation/scaling
  6241. VectorMA(&(model->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[trinum * 3], surface->num_firstvertex, d, PRVM_G_VECTOR(OFS_RETURN));
  6242. }
  6243. //
  6244. // physics builtins
  6245. //
  6246. #define VM_physics_ApplyCmd(ed,f) if (!ed->priv.server->ode_body) VM_physics_newstackfunction(prog, ed, f); else World_Physics_ApplyCmd(ed, f)
  6247. static edict_odefunc_t *VM_physics_newstackfunction(prvm_prog_t *prog, prvm_edict_t *ed, edict_odefunc_t *f)
  6248. {
  6249. edict_odefunc_t *newfunc, *func;
  6250. newfunc = (edict_odefunc_t *)Mem_Alloc(prog->progs_mempool, sizeof(edict_odefunc_t));
  6251. memcpy(newfunc, f, sizeof(edict_odefunc_t));
  6252. newfunc->next = NULL;
  6253. if (!ed->priv.server->ode_func)
  6254. ed->priv.server->ode_func = newfunc;
  6255. else
  6256. {
  6257. for (func = ed->priv.server->ode_func; func->next; func = func->next);
  6258. func->next = newfunc;
  6259. }
  6260. return newfunc;
  6261. }
  6262. // void(entity e, float physics_enabled) physics_enable = #;
  6263. void VM_physics_enable(prvm_prog_t *prog)
  6264. {
  6265. prvm_edict_t *ed;
  6266. edict_odefunc_t f;
  6267. VM_SAFEPARMCOUNT(2, VM_physics_enable);
  6268. ed = PRVM_G_EDICT(OFS_PARM0);
  6269. if (!ed)
  6270. {
  6271. if (developer.integer > 0)
  6272. VM_Warning(prog, "VM_physics_enable: null entity!\n");
  6273. return;
  6274. }
  6275. // entity should have MOVETYPE_PHYSICS already set, this can damage memory (making leaked allocation) so warn about this even if non-developer
  6276. if (PRVM_serveredictfloat(ed, movetype) != MOVETYPE_PHYSICS)
  6277. {
  6278. VM_Warning(prog, "VM_physics_enable: entity is not MOVETYPE_PHYSICS!\n");
  6279. return;
  6280. }
  6281. f.type = PRVM_G_FLOAT(OFS_PARM1) == 0 ? ODEFUNC_DISABLE : ODEFUNC_ENABLE;
  6282. VM_physics_ApplyCmd(ed, &f);
  6283. }
  6284. // void(entity e, vector force, vector relative_ofs) physics_addforce = #;
  6285. void VM_physics_addforce(prvm_prog_t *prog)
  6286. {
  6287. prvm_edict_t *ed;
  6288. edict_odefunc_t f;
  6289. VM_SAFEPARMCOUNT(3, VM_physics_addforce);
  6290. ed = PRVM_G_EDICT(OFS_PARM0);
  6291. if (!ed)
  6292. {
  6293. if (developer.integer > 0)
  6294. VM_Warning(prog, "VM_physics_addforce: null entity!\n");
  6295. return;
  6296. }
  6297. // entity should have MOVETYPE_PHYSICS already set, this can damage memory (making leaked allocation) so warn about this even if non-developer
  6298. if (PRVM_serveredictfloat(ed, movetype) != MOVETYPE_PHYSICS)
  6299. {
  6300. VM_Warning(prog, "VM_physics_addforce: entity is not MOVETYPE_PHYSICS!\n");
  6301. return;
  6302. }
  6303. f.type = ODEFUNC_FORCE;
  6304. VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f.v1);
  6305. VectorCopy(PRVM_G_VECTOR(OFS_PARM2), f.v2);
  6306. VM_physics_ApplyCmd(ed, &f);
  6307. }
  6308. // void(entity e, vector torque) physics_addtorque = #;
  6309. void VM_physics_addtorque(prvm_prog_t *prog)
  6310. {
  6311. prvm_edict_t *ed;
  6312. edict_odefunc_t f;
  6313. VM_SAFEPARMCOUNT(2, VM_physics_addtorque);
  6314. ed = PRVM_G_EDICT(OFS_PARM0);
  6315. if (!ed)
  6316. {
  6317. if (developer.integer > 0)
  6318. VM_Warning(prog, "VM_physics_addtorque: null entity!\n");
  6319. return;
  6320. }
  6321. // entity should have MOVETYPE_PHYSICS already set, this can damage memory (making leaked allocation) so warn about this even if non-developer
  6322. if (PRVM_serveredictfloat(ed, movetype) != MOVETYPE_PHYSICS)
  6323. {
  6324. VM_Warning(prog, "VM_physics_addtorque: entity is not MOVETYPE_PHYSICS!\n");
  6325. return;
  6326. }
  6327. f.type = ODEFUNC_TORQUE;
  6328. VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f.v1);
  6329. VM_physics_ApplyCmd(ed, &f);
  6330. }
  6331. extern cvar_t prvm_coverage;
  6332. void VM_coverage(prvm_prog_t *prog)
  6333. {
  6334. VM_SAFEPARMCOUNT(0, VM_coverage);
  6335. if (prog->explicit_profile[prog->xstatement]++ == 0 && (prvm_coverage.integer & 2))
  6336. PRVM_ExplicitCoverageEvent(prog, prog->xfunction, prog->xstatement);
  6337. }