tools.cpp 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057
  1. // implementation of generic tools
  2. #include "cube.h"
  3. #ifdef NO_POSIX_R
  4. char *strtok_r(char *s, const char *delim, char **b)
  5. {
  6. if(s) *b = s;
  7. *b += strspn(*b, delim);
  8. if(!**b) return NULL;
  9. s = *b;
  10. *b += strcspn(s, delim);
  11. if(**b) *(*b)++ = '\0';
  12. return s;
  13. }
  14. #endif
  15. string _timestringbuffer;
  16. const char *timestring(time_t t, bool local, const char *fmt, char *buf)
  17. {
  18. #ifdef NO_POSIX_R
  19. struct tm *timeinfo;
  20. timeinfo = local ? localtime(&t) : gmtime (&t);
  21. #else
  22. struct tm *timeinfo, b;
  23. timeinfo = local ? localtime_r(&t, &b) : gmtime_r(&t, &b);
  24. #endif
  25. strftime(buf, sizeof(string) - 1, fmt && *fmt ? fmt : "%Y%m%d_%H.%M.%S", timeinfo); // sortable time for filenames
  26. return buf;
  27. }
  28. const char *asctimestr()
  29. {
  30. return timestring(true, "%c");
  31. }
  32. const char *numtime()
  33. {
  34. static string numt;
  35. time_t t = time(NULL);
  36. int t1 = t / 1000000, t2 = t % 1000000;
  37. formatstring(numt)("%d%06d", t1, t2);
  38. return numt + strspn(numt, "0");
  39. }
  40. static const uchar transformenttab[] = {
  41. /* mapformat 1..5 6..7 */
  42. /* 0 */ /* NOTUSED */ NOTUSED, /* NOTUSED */ NOTUSED,
  43. /* 1 */ /* LIGHT */ LIGHT, /* LIGHT */ LIGHT,
  44. /* 2 */ /* PLAYERSTART */ PLAYERSTART, /* PLAYERSTART */ PLAYERSTART,
  45. /* 3 */ /* I_SHELLS */ I_AMMO, /* I_CLIPS */ I_CLIPS,
  46. /* 4 */ /* I_BULLETS */ I_AMMO, /* I_AMMO */ I_AMMO,
  47. /* 5 */ /* I_ROCKETS */ I_AMMO, /* I_GRENADE */ I_GRENADE,
  48. /* 6 */ /* I_ROUNDS */ I_AMMO, /* I_HEALTH */ I_HEALTH,
  49. /* 7 */ /* I_HEALTH */ I_HEALTH, /* I_ARMOUR */ I_ARMOUR,
  50. /* 8 */ /* I_BOOST */ I_HEALTH, /* I_AKIMBO */ I_AKIMBO,
  51. /* 9 */ /* I_GREENARMOUR */ I_HELMET, /* MAPMODEL */ MAPMODEL,
  52. /* 10 */ /* I_YELLOWARMOUR */ I_ARMOUR, /* CARROT */ CARROT,
  53. /* 11 */ /* I_QUAD */ I_AKIMBO, /* LADDER */ LADDER,
  54. /* 12 */ /* TELEPORT */ NOTUSED, /* CTF_FLAG */ CTF_FLAG,
  55. /* 13 */ /* TELEDEST */ NOTUSED, /* SOUND */ SOUND,
  56. /* 14 */ /* MAPMODEL */ MAPMODEL, /* CLIP */ CLIP,
  57. /* 15 */ /* MONSTER */ NOTUSED, /* PLCLIP */ PLCLIP,
  58. /* 16 */ /* CARROT */ NOTUSED, 16,
  59. /* 17 */ /* JUMPPAD */ NOTUSED, 17 };
  60. void transformoldentitytypes(int mapversion, uchar &enttype)
  61. {
  62. const uchar *usetab = transformenttab + (mapversion > 5 ? 1 : 0);
  63. if(mapversion < 8 && enttype < 18) enttype = usetab[enttype * 2];
  64. }
  65. int fixmapheadersize(int version, int headersize) // we can't trust hdr.headersize for file versions < 10 (thx flow)
  66. {
  67. if(version < 4) return sizeof(header) - sizeof(int) * 16;
  68. else if(version == 7 || version == 8) return sizeof(header) + sizeof(char) * 128; // mediareq
  69. else if(version < 10 || headersize < int(sizeof(header))) return sizeof(header);
  70. return headersize;
  71. }
  72. // map geometry statistics
  73. servsqr *createservworld(const sqr *s, int _cubicsize) // create a server-style floorplan on the client, to use same statistics functions on client and server
  74. {
  75. servsqr *res = new servsqr[_cubicsize], *d = res;
  76. loopirev(_cubicsize)
  77. {
  78. d->type = s->type == SOLID ? SOLID : ((s->type & TAGTRIGGERMASK) | (s->tag & ~TAGTRIGGERMASK)); // guaranteed to not have tagclips on SOLID cubes - so we can check for SOLID without masks
  79. d->ceil = s->ceil;
  80. d->floor = s->floor;
  81. d->vdelta = s->vdelta;
  82. d++;
  83. s++;
  84. }
  85. return res;
  86. }
  87. int calcmapdims(mapdim_s &md, const servsqr *s, int _ssize)
  88. {
  89. int res = 0;
  90. md.x1 = md.y1 = _ssize;
  91. md.x2 = md.y2 = 0;
  92. md.minfloor = 127; md.maxceil = -128;
  93. for(int y = 0; y < _ssize; y++) for(int x = 0; x < _ssize; x++)
  94. {
  95. if((s->type & TAGTRIGGERMASK) != SOLID)
  96. {
  97. if(x < md.x1) md.x1 = x;
  98. if(x > md.x2) md.x2 = x;
  99. if(y < md.y1) md.y1 = y;
  100. md.y2 = y;
  101. if(s->floor < md.minfloor) md.minfloor = s->floor;
  102. if(s->ceil > md.maxceil) md.maxceil = s->ceil;
  103. }
  104. s++;
  105. }
  106. if(md.x2 < md.x1 || md.y2 < md.y1)
  107. { // map is completely solid -> default to empty map values
  108. md.x1 = md.y1 = 2;
  109. md.x2 = md.y2 = _ssize - 3;
  110. md.minfloor = 0;
  111. md.maxceil = 16;
  112. res = -1; // reject map on servers
  113. }
  114. if(md.x1 < MINBORD || md.y1 < MINBORD || md.x2 >= _ssize - MINBORD || md.y2 >= _ssize - MINBORD) res = -2; // reject map because of world border violation
  115. md.xspan = md.x2 - md.x1 + 1;
  116. md.yspan = md.y2 - md.y1 + 1;
  117. md.xm = md.x1 + md.xspan / 2.0f;
  118. md.ym = md.y1 + md.yspan / 2.0f;
  119. return res;
  120. }
  121. int calcmapareastats(mapareastats_s &ms, servsqr *_servworld, int _ssize, const mapdim_s &md)
  122. {
  123. memset(&ms, 0, sizeof(ms));
  124. // count steep FHF and CHF
  125. int linegap = _ssize - md.xspan;
  126. #ifndef STANDALONE
  127. int totalmax = 0;
  128. #endif
  129. servsqr *bb = _servworld + _ssize * md.y1 + md.x1, *ss = bb;
  130. for(int j = md.yspan; j > 0; j--, ss += linegap) loopirev(md.xspan)
  131. {
  132. int type = ss->type & TAGTRIGGERMASK;
  133. if(type == FHF || type == CHF) // only CHF and FHF use vdelta
  134. {
  135. servsqr *r[3] = { ss + 1, ss + _ssize, ss + _ssize + 1 };
  136. int min = ss->vdelta, max = min;
  137. loopi(3)
  138. {
  139. if(r[i]->vdelta < min) min = r[i]->vdelta;
  140. else if(r[i]->vdelta > max) max = r[i]->vdelta;
  141. }
  142. max -= min;
  143. #ifndef STANDALONE
  144. if(max > totalmax)
  145. {
  146. ms.steepest = int(ss - _servworld);
  147. totalmax = max;
  148. }
  149. #endif
  150. int d = max / MAS_VDELTA_QUANT;
  151. ASSERT(d >= 0);
  152. if(d >= MAS_VDELTA_TABSIZE) d = MAS_VDELTA_TABSIZE - 1;
  153. ms.vdd[d]++;
  154. }
  155. ss++;
  156. }
  157. loopirev(MAS_VDELTA_TABSIZE) ms.vdds = (ms.vdds << 2) | ((ms.vdd[i] > MAS_VDELTA_THRES) << 1) | (ms.vdd[i] > 0);
  158. // check occlusion by SOLIDs (destroys vdelta values)
  159. ss = bb;
  160. for(int j = md.yspan; j > 0; j--, ss += linegap) loopirev(md.xspan) (ss++)->vdelta = 0; // reset all vdelta values
  161. int xc = (md.xspan + MAS_GRID / 2) / (MAS_GRID + 1), yc = (md.yspan + MAS_GRID / 2) / (MAS_GRID + 1);
  162. if(md.x1 + MAS_GRID * xc >= _ssize || md.y1 + MAS_GRID * yc >= _ssize) return -1; // malformed map
  163. int xb = min(md.x1 + xc, _ssize - MAS_GRID * xc - md.x1 - 1) / 2, yb = min(md.y1 + yc, _ssize - MAS_GRID * yc - md.y1 - 1) / 2; // make sure, all tp points are inside the map area
  164. int tgx = min(xb, max(3, xc / 3)), tgy = min(yb, max(3, yc / 3)) * _ssize, tp[8] = { tgx, -tgx, tgy, -tgy, 2 * tgx + 2 * tgy, -2 * tgx - 2 * tgy, -2 * tgx + 2 * tgy, 2 * tgx - 2 * tgy }; // 8 positions to try, if the stariing point is solid
  165. uchar epoch = 1;
  166. servsqr *s = bb + xc + yc * _ssize; ss = s;
  167. vector<threeint> tab;
  168. threeint pp;
  169. for(int y = 0; y < MAS_GRID; y++, ss = s += _ssize * yc) for(int x = 0; x < MAS_GRID; x++, ss += xc) // measure visible area in MAS_GRID x MAS_GRID probe points
  170. { // calculate MAS_GRID^2 probe points (with a low-res version of computeraytable()
  171. servsqr *r = ss, *rr;
  172. if(SOLID(r)) loopi(8)
  173. { // if initial position is SOLID, we try 8 points around that spot
  174. if(!SOLID(r + tp[i]))
  175. {
  176. r += tp[i];
  177. break;
  178. }
  179. }
  180. pp.val2 = int(r - _servworld);
  181. int area = 0, volume = 0, frac;
  182. loopk(MAS_RESOLUTION)
  183. {
  184. #define RAYS(da, db) \
  185. rr = r; frac = 0; \
  186. for(;;) \
  187. { \
  188. if((frac += k) >= MAS_RESOLUTION) rr += da, frac -= MAS_RESOLUTION; \
  189. rr += db; \
  190. if(SOLID(rr)) break; \
  191. if(rr->vdelta != epoch) { area += 1; volume += rr->ceil - rr->floor; } \
  192. rr->vdelta = epoch; \
  193. }
  194. RAYS(_ssize, 1);
  195. RAYS(_ssize, -1);
  196. RAYS(-_ssize, 1);
  197. RAYS(-_ssize, -1);
  198. RAYS(1, _ssize);
  199. RAYS(1, -_ssize);
  200. RAYS(-1, _ssize);
  201. RAYS(-1, -_ssize);
  202. #undef RAYS
  203. }
  204. pp.val1 = volume;
  205. pp.key = area;
  206. tab.add(pp);
  207. if(area) epoch++;
  208. if(!epoch) epoch++;
  209. }
  210. ss = bb;
  211. for(int j = md.yspan; j > 0; j--, ss += linegap) loopirev(md.xspan)
  212. {
  213. if(!SOLID(ss))
  214. {
  215. ms.total++;
  216. if(!ss->vdelta) ms.rest++; // count all cubes not in view of one of the probe points
  217. }
  218. ss++;
  219. }
  220. ASSERT(tab.length() == MAS_GRID2);
  221. tab.sort(cmpintdesc);
  222. loopv(tab)
  223. { // sort probe poiints in descending order of area
  224. ms.ppv[i] = tab[i].val1;
  225. ms.ppa[i] = tab[i].key;
  226. #ifndef STANDALONE
  227. ms.ppp[i] = tab[i].val2;
  228. #endif
  229. }
  230. return 0;
  231. }
  232. void calcentitystats(entitystats_s &es, const persistent_entity *pents, int pentsize)
  233. {
  234. #ifndef STANDALONE
  235. vector<persistent_entity> _pents;
  236. if(!pents)
  237. { // use regular ents list
  238. loopv(ents) _pents.add() = ents[i];
  239. pentsize = _pents.length();
  240. if(pentsize) pents = &_pents[0];
  241. }
  242. #endif
  243. memset(&es, 0, sizeof(es));
  244. loopi(MAXENTTYPES) es.first[i] = pentsize;
  245. vector<int> picks;
  246. loopi(pentsize)
  247. {
  248. const persistent_entity &e = pents[i];
  249. if(e.type < MAXENTTYPES)
  250. {
  251. es.entcnt[e.type]++;
  252. if(es.first[e.type] == pentsize) es.first[e.type] = i;
  253. es.last[e.type] = i;
  254. if(e.type == PLAYERSTART) switch(e.attr2)
  255. {
  256. case 0: es.spawns[0]++; break;
  257. case 1: es.spawns[1]++; break;
  258. case 100: es.spawns[2]++; break;
  259. default: es.unknownspawns++; break;
  260. }
  261. if(e.type == CTF_FLAG) switch(e.attr2)
  262. {
  263. case 0:
  264. case 1:
  265. es.flags[e.attr2]++;
  266. es.flagents[e.attr2] = i;
  267. break;
  268. default:
  269. es.unknownflags++;
  270. break;
  271. }
  272. if(isitem(e.type)) picks.add(i);
  273. }
  274. }
  275. es.hasffaspawns = es.spawns[2] >= MINSPAWNS;
  276. es.hasteamspawns = es.spawns[0] >= MINSPAWNS && es.spawns[1] >= MINSPAWNS;
  277. if(es.flags[0] == 1 && es.flags[1] == 1)
  278. {
  279. vec fd(pents[es.flagents[0]].x - pents[es.flagents[1]].x, pents[es.flagents[0]].y - pents[es.flagents[1]].y, 0);
  280. es.flagentdistance = fd.magnitude();
  281. es.hasflags = es.flagentdistance >= MINFLAGDISTANCE;
  282. }
  283. int r = 0;
  284. loopvjrev(picks) loopirev(j)
  285. { // calculate the distances between all pickups (takes n^2/2 calculations - which should be fine for any sane map)
  286. double d = pow2(pents[picks[i]].x - pents[picks[j]].x) + pow2(pents[picks[i]].y - pents[picks[j]].y); // deltaX^2 + deltaY^2
  287. frexp(d, &r);
  288. r /= 2; // sqrt ;)
  289. if(r > LARGEST_FACTOR) r = LARGEST_FACTOR;
  290. es.pickupdistance[r]++;
  291. }
  292. es.pickups = picks.length();
  293. }
  294. #if 0
  295. const char *rateentitystats(entitystats_s &es)
  296. {
  297. static char res[LARGEST_FACTOR + 1];
  298. }
  299. #endif
  300. int cmpintasc(const int *a, const int *b) { return *a - *b; } // leads to ascending sort order
  301. int cmpintdesc(const int *a, const int *b) { return *b - *a; } // leads to descending sort order
  302. int stringsort(const char **a, const char **b) { return strcmp(*a, *b); }
  303. int stringsortrev(const char **a, const char **b) { return strcmp(*b, *a); }
  304. int stringsortignorecase(const char **a, const char **b) { return strcasecmp(*a, *b); }
  305. int stringsortignorecaserev(const char **a, const char **b) { return strcasecmp(*b, *a); }
  306. extern char *maplayout, *testlayout;
  307. extern int maplayout_factor, testlayout_factor, Mvolume, Marea, Mopen, SHhits;
  308. extern float Mheight;
  309. extern int checkarea(int, char *);
  310. mapstats *loadmapstats(const char *filename, bool getlayout)
  311. {
  312. const int sizeof_header = sizeof(header), sizeof_baseheader = sizeof_header - sizeof(int) * 16;
  313. static mapstats s;
  314. static uchar *enttypes = NULL;
  315. static short *entposs = NULL;
  316. DELETEA(enttypes);
  317. loopi(MAXENTTYPES) s.entcnt[i] = 0;
  318. loopi(3) s.spawns[i] = 0;
  319. loopi(2) s.flags[i] = 0;
  320. stream *f = opengzfile(filename, "rb");
  321. if(!f) return NULL;
  322. memset(&s.hdr, 0, sizeof_header);
  323. if(f->read(&s.hdr, sizeof_baseheader) != sizeof_baseheader || (strncmp(s.hdr.head, "CUBE", 4) && strncmp(s.hdr.head, "ACMP",4))) { delete f; return NULL; }
  324. lilswap(&s.hdr.version, 4);
  325. s.hdr.headersize = fixmapheadersize(s.hdr.version, s.hdr.headersize);
  326. int restofhead = min(s.hdr.headersize, sizeof_header) - sizeof_baseheader;
  327. if(s.hdr.version > MAPVERSION || s.hdr.numents > MAXENTITIES ||
  328. f->read(&s.hdr.waterlevel, restofhead) != restofhead ||
  329. !f->seek(clamp(s.hdr.headersize - sizeof_header, 0, MAXHEADEREXTRA), SEEK_CUR)) { delete f; return NULL; }
  330. if(s.hdr.version>=4)
  331. {
  332. lilswap(&s.hdr.waterlevel, 1);
  333. lilswap(&s.hdr.maprevision, 2);
  334. }
  335. else s.hdr.waterlevel = -100000;
  336. entity e;
  337. enttypes = new uchar[s.hdr.numents];
  338. entposs = new short[s.hdr.numents * 3];
  339. loopi(s.hdr.numents)
  340. {
  341. f->read(&e, s.hdr.version < 10 ? 12 : sizeof(persistent_entity));
  342. lilswap((short *)&e, 4);
  343. transformoldentitytypes(s.hdr.version, e.type);
  344. if(e.type == PLAYERSTART && (e.attr2 == 0 || e.attr2 == 1 || e.attr2 == 100)) s.spawns[e.attr2 == 100 ? 2 : e.attr2]++;
  345. if(e.type == CTF_FLAG && (e.attr2 == 0 || e.attr2 == 1)) { s.flags[e.attr2]++; s.flagents[e.attr2] = i; }
  346. s.entcnt[e.type]++;
  347. enttypes[i] = e.type;
  348. entposs[i * 3] = e.x; entposs[i * 3 + 1] = e.y; entposs[i * 3 + 2] = e.z + e.attr1;
  349. }
  350. DELETEA(testlayout);
  351. int minfloor = 0;
  352. int maxceil = 0;
  353. if(s.hdr.sfactor <= LARGEST_FACTOR && s.hdr.sfactor >= SMALLEST_FACTOR)
  354. {
  355. testlayout_factor = s.hdr.sfactor;
  356. int layoutsize = 1 << (testlayout_factor * 2);
  357. bool fail = false;
  358. testlayout = new char[layoutsize + 256];
  359. memset(testlayout, 0, layoutsize * sizeof(char));
  360. char *t = NULL;
  361. char floor = 0, ceil;
  362. int diff = 0;
  363. Mvolume = Marea = SHhits = 0;
  364. loopk(layoutsize)
  365. {
  366. char *c = testlayout + k;
  367. int type = f->getchar();
  368. int n = 1;
  369. switch(type)
  370. {
  371. case 255:
  372. {
  373. if(!t || (n = f->getchar()) < 0) { fail = true; break; }
  374. memset(c, *t, n);
  375. k += n - 1;
  376. break;
  377. }
  378. case 254: // only in MAPVERSION<=2
  379. if(!t) { fail = true; break; }
  380. *c = *t;
  381. f->getchar(); f->getchar();
  382. break;
  383. default:
  384. if(type<0 || type>=MAXTYPE) { fail = true; break; }
  385. floor = f->getchar();
  386. ceil = f->getchar();
  387. if(floor >= ceil && ceil > -128) floor = ceil - 1; // for pre 12_13
  388. diff = ceil - floor;
  389. if(type == FHF) floor = -128;
  390. if(floor!=-128 && floor<minfloor) minfloor = floor;
  391. if(ceil>maxceil) maxceil = ceil;
  392. f->getchar(); f->getchar();
  393. if(s.hdr.version>=2) f->getchar();
  394. if(s.hdr.version>=5) f->getchar();
  395. case SOLID:
  396. *c = type == SOLID ? 127 : floor;
  397. f->getchar(); f->getchar();
  398. if(s.hdr.version<=2) { f->getchar(); f->getchar(); }
  399. break;
  400. }
  401. if ( type != SOLID && diff > 6 )
  402. {
  403. // Lucas (10mar2013): Removed "pow2" because it was too strict
  404. if (diff > MAXMHEIGHT) SHhits += /*pow2*/(diff-MAXMHEIGHT)*n;
  405. Marea += n;
  406. Mvolume += diff * n;
  407. }
  408. if(fail) break;
  409. t = c;
  410. }
  411. if(fail) { DELETEA(testlayout); }
  412. else
  413. {
  414. Mheight = Marea ? (float)Mvolume/Marea : 0;
  415. Mopen = checkarea(testlayout_factor, testlayout);
  416. }
  417. }
  418. if(getlayout)
  419. {
  420. DELETEA(maplayout);
  421. if (testlayout)
  422. {
  423. maplayout_factor = testlayout_factor;
  424. extern int maplayoutssize;
  425. maplayoutssize = 1 << testlayout_factor;
  426. int layoutsize = 1 << (testlayout_factor * 2);
  427. maplayout = new char[layoutsize + 256];
  428. memcpy(maplayout, testlayout, layoutsize * sizeof(char));
  429. /* memset(&mapdims, 0, sizeof(struct mapdim_s));
  430. mapdims.x1 = mapdims.y1 = maplayoutssize;
  431. loopk(layoutsize) if (testlayout[k] != 127)
  432. {
  433. int cwx = k%maplayoutssize,
  434. cwy = k/maplayoutssize;
  435. if(cwx < mapdims.x1) mapdims.x1 = cwx;
  436. if(cwy < mapdims.y1) mapdims.y1 = cwy;
  437. if(cwx > mapdims.x2) mapdims.x2 = cwx;
  438. if(cwy > mapdims.y2) mapdims.y2 = cwy;
  439. }
  440. mapdims.xspan = mapdims.x2 - mapdims.x1;
  441. mapdims.yspan = mapdims.y2 - mapdims.y1;
  442. mapdims.minfloor = minfloor;
  443. mapdims.maxceil = maxceil;
  444. */ }
  445. }
  446. delete f;
  447. s.hasffaspawns = s.spawns[2] > 0;
  448. s.hasteamspawns = s.spawns[0] > 0 && s.spawns[1] > 0;
  449. s.hasflags = s.flags[0] > 0 && s.flags[1] > 0;
  450. s.enttypes = enttypes;
  451. s.entposs = entposs;
  452. s.cgzsize = getfilesize(filename);
  453. return &s;
  454. }
  455. ///////////////////////// debugging ///////////////////////
  456. #if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__)
  457. void stackdumper(unsigned int type, EXCEPTION_POINTERS *ep)
  458. {
  459. if(!ep) fatal("unknown type");
  460. EXCEPTION_RECORD *er = ep->ExceptionRecord;
  461. CONTEXT *context = ep->ContextRecord;
  462. string out, t;
  463. formatstring(out)("Win32 Exception: 0x%x [0x%x]\n\n", er->ExceptionCode, er->ExceptionCode==EXCEPTION_ACCESS_VIOLATION ? er->ExceptionInformation[1] : -1);
  464. STACKFRAME sf = {{context->Eip, 0, AddrModeFlat}, {}, {context->Ebp, 0, AddrModeFlat}, {context->Esp, 0, AddrModeFlat}, 0};
  465. SymInitialize(GetCurrentProcess(), NULL, TRUE);
  466. while(::StackWalk(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), &sf, context, NULL, ::SymFunctionTableAccess, ::SymGetModuleBase, NULL))
  467. {
  468. struct { IMAGEHLP_SYMBOL sym; string n; } si = { { sizeof( IMAGEHLP_SYMBOL ), 0, 0, 0, sizeof(string) } };
  469. IMAGEHLP_LINE li = { sizeof( IMAGEHLP_LINE ) };
  470. DWORD off;
  471. if(SymGetSymFromAddr(GetCurrentProcess(), (DWORD)sf.AddrPC.Offset, &off, &si.sym) && SymGetLineFromAddr(GetCurrentProcess(), (DWORD)sf.AddrPC.Offset, &off, &li))
  472. {
  473. char *del = strrchr(li.FileName, '\\');
  474. formatstring(t)("%s - %s [%d]\n", si.sym.Name, del ? del + 1 : li.FileName, li.LineNumber);
  475. concatstring(out, t);
  476. }
  477. }
  478. #if !defined(STANDALONE)
  479. if(clientlogfile) clientlogfile->printf("%s\n", out);
  480. #endif
  481. fatal("%s", out);
  482. }
  483. #elif defined(__ANDROID__)
  484. // fixme ah
  485. #elif defined(linux) || defined(__linux) || defined(__linux__)
  486. #include <execinfo.h>
  487. // stack dumping on linux, inspired by Sachin Agrawal's sample code
  488. struct signalbinder
  489. {
  490. static void stackdumper(int sig)
  491. {
  492. printf("stacktrace:\n");
  493. #if !defined(STANDALONE)
  494. if(clientlogfile) clientlogfile->printf("stacktrace\n");
  495. #endif
  496. const int BTSIZE = 25;
  497. void *array[BTSIZE];
  498. int n = backtrace(array, BTSIZE);
  499. char **symbols = backtrace_symbols(array, n);
  500. for(int i = 0; i < n; i++)
  501. {
  502. printf("%s\n", symbols[i]);
  503. #if !defined(STANDALONE)
  504. if(clientlogfile) clientlogfile->printf("%s\n", symbols[i]);
  505. #endif
  506. }
  507. free(symbols);
  508. fatal("AssaultCube error (%d)", sig);
  509. }
  510. signalbinder()
  511. {
  512. // register signals to dump the stack if they are raised,
  513. // use constructor for early registering
  514. signal(SIGSEGV, stackdumper);
  515. signal(SIGFPE, stackdumper);
  516. signal(SIGILL, stackdumper);
  517. signal(SIGBUS, stackdumper);
  518. signal(SIGSYS, stackdumper);
  519. signal(SIGABRT, stackdumper);
  520. }
  521. };
  522. signalbinder sigbinder;
  523. #endif
  524. ///////////////////////// misc tools ///////////////////////
  525. bool cmpb(void *b, int n, enet_uint32 c)
  526. {
  527. ENetBuffer buf;
  528. buf.data = b;
  529. buf.dataLength = n;
  530. return enet_crc32(&buf, 1)==c;
  531. }
  532. bool cmpf(char *fn, enet_uint32 c)
  533. {
  534. int n = 0;
  535. char *b = loadfile(fn, &n);
  536. bool r = cmpb(b, n, c);
  537. delete[] b;
  538. return r;
  539. }
  540. enet_uint32 adler(unsigned char *data, size_t len)
  541. {
  542. enet_uint32 a = 1, b = 0;
  543. while (len--)
  544. {
  545. a += *data++;
  546. b += a;
  547. }
  548. return b;
  549. }
  550. bool isbigendian()
  551. {
  552. return !*(const uchar *)&islittleendian;
  553. }
  554. void strtoupper(char *t, const char *s)
  555. {
  556. if(!s) s = t;
  557. while(*s)
  558. {
  559. *t = toupper(*s);
  560. t++; s++;
  561. }
  562. *t = '\0';
  563. }
  564. const char *atoip(const char *s, enet_uint32 *ip)
  565. {
  566. unsigned int d[4];
  567. int n;
  568. if(!s || sscanf(s, "%u.%u.%u.%u%n", d, d + 1, d + 2, d + 3, &n) != 4) return NULL;
  569. *ip = 0;
  570. loopi(4)
  571. {
  572. if(d[i] > 255) return NULL;
  573. *ip = (*ip << 8) + d[i];
  574. }
  575. return s + n;
  576. }
  577. const char *atoipr(const char *s, iprange *ir)
  578. {
  579. if((s = atoip(s, &ir->lr)) == NULL) return NULL;
  580. ir->ur = ir->lr;
  581. s += strspn(s, " \t");
  582. if(*s == '-')
  583. {
  584. if(!(s = atoip(s + 1, &ir->ur)) || ir->lr > ir->ur) return NULL;
  585. }
  586. else if(*s == '/')
  587. {
  588. int m, n;
  589. if(sscanf(s + 1, "%d%n", &m, &n) != 1 || m < 0 || m > 32) return NULL;
  590. unsigned long bm = (1 << (32 - m)) - 1;
  591. ir->lr &= ~bm;
  592. ir->ur |= bm;
  593. s += 1 + n;
  594. }
  595. return s;
  596. }
  597. const char *iptoa(const enet_uint32 ip)
  598. {
  599. static string s[2];
  600. static int buf = 0;
  601. buf = (buf + 1) % 2;
  602. formatstring(s[buf])("%d.%d.%d.%d", (ip >> 24) & 255, (ip >> 16) & 255, (ip >> 8) & 255, ip & 255);
  603. return s[buf];
  604. }
  605. const char *iprtoa(const struct iprange &ipr)
  606. {
  607. static string s[2];
  608. static int buf = 0;
  609. buf = (buf + 1) % 2;
  610. if(ipr.lr == ipr.ur) copystring(s[buf], iptoa(ipr.lr));
  611. else formatstring(s[buf])("%s-%s", iptoa(ipr.lr), iptoa(ipr.ur));
  612. return s[buf];
  613. }
  614. int cmpiprange(const struct iprange *a, const struct iprange *b)
  615. {
  616. if(a->lr < b->lr) return -1;
  617. if(a->lr > b->lr) return 1;
  618. return 0;
  619. }
  620. int cmpipmatch(const struct iprange *a, const struct iprange *b)
  621. {
  622. return - (a->lr < b->lr) + (a->lr > b->ur);
  623. }
  624. char *concatformatstring(char *d, const char *s, ...)
  625. {
  626. static defvformatstring(temp, s, s);
  627. return concatstring(d, temp);
  628. }
  629. int cvecprintf(vector<char> &v, const char *s, ...)
  630. {
  631. defvformatstring(temp, s, s);
  632. int len = strlen(temp);
  633. if(len) v.put(temp, len);
  634. return len;
  635. }
  636. const char *hiddenpwd(const char *pwd, int showchars)
  637. {
  638. static int sc = 3;
  639. static string text;
  640. copystring(text, pwd);
  641. if(showchars > 0) sc = showchars;
  642. for(int i = (int)strlen(text) - 1; i >= sc; i--) text[i] = '*';
  643. return text;
  644. }
  645. int getlistindex(const char *key, const char *list[], bool acceptnumeric, int deflt)
  646. {
  647. int max = 0;
  648. while(list[max][0]) if(!strcasecmp(key, list[max])) return max; else max++;
  649. if(acceptnumeric && isdigit(key[0]))
  650. {
  651. int i = (int)strtol(key, NULL, 0);
  652. if(i >= 0 && i < max) return i;
  653. }
  654. #if !defined(STANDALONE) && defined(_DEBUG)
  655. char *opts = conc(list, -1, true);
  656. if(*key) clientlogf("warning: unknown token \"%s\" (not in list [%s])", key, opts);
  657. delstring(opts);
  658. #endif
  659. return deflt;
  660. }
  661. //////////////// geometry utils ////////////////
  662. static inline float det2x2(float a, float b, float c, float d) { return a*d - b*c; }
  663. static inline float det3x3(float a1, float a2, float a3,
  664. float b1, float b2, float b3,
  665. float c1, float c2, float c3)
  666. {
  667. return a1 * det2x2(b2, b3, c2, c3)
  668. - b1 * det2x2(a2, a3, c2, c3)
  669. + c1 * det2x2(a2, a3, b2, b3);
  670. }
  671. float glmatrixf::determinant() const
  672. {
  673. float a1 = v[0], a2 = v[1], a3 = v[2], a4 = v[3],
  674. b1 = v[4], b2 = v[5], b3 = v[6], b4 = v[7],
  675. c1 = v[8], c2 = v[9], c3 = v[10], c4 = v[11],
  676. d1 = v[12], d2 = v[13], d3 = v[14], d4 = v[15];
  677. return a1 * det3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4)
  678. - b1 * det3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4)
  679. + c1 * det3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4)
  680. - d1 * det3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
  681. }
  682. void glmatrixf::adjoint(const glmatrixf &m)
  683. {
  684. float a1 = m.v[0], a2 = m.v[1], a3 = m.v[2], a4 = m.v[3],
  685. b1 = m.v[4], b2 = m.v[5], b3 = m.v[6], b4 = m.v[7],
  686. c1 = m.v[8], c2 = m.v[9], c3 = m.v[10], c4 = m.v[11],
  687. d1 = m.v[12], d2 = m.v[13], d3 = m.v[14], d4 = m.v[15];
  688. v[0] = det3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4);
  689. v[1] = -det3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4);
  690. v[2] = det3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4);
  691. v[3] = -det3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
  692. v[4] = -det3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4);
  693. v[5] = det3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4);
  694. v[6] = -det3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4);
  695. v[7] = det3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4);
  696. v[8] = det3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4);
  697. v[9] = -det3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4);
  698. v[10] = det3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4);
  699. v[11] = -det3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4);
  700. v[12] = -det3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3);
  701. v[13] = det3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3);
  702. v[14] = -det3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3);
  703. v[15] = det3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3);
  704. }
  705. bool glmatrixf::invert(const glmatrixf &m, float mindet)
  706. {
  707. float a1 = m.v[0], b1 = m.v[4], c1 = m.v[8], d1 = m.v[12];
  708. adjoint(m);
  709. float det = a1*v[0] + b1*v[1] + c1*v[2] + d1*v[3]; // float det = m.determinant();
  710. if(fabs(det) < mindet) return false;
  711. float invdet = 1/det;
  712. loopi(16) v[i] *= invdet;
  713. return true;
  714. }
  715. // multithreading and ipc tools wrapper for the server
  716. // all embedded servers and all standalone servers except on linux use SDL
  717. // the standalone linux version uses native linux libraries - and also makes use of shared memory
  718. #ifdef AC_USE_SDL_THREADS
  719. #include "SDL_timer.h"
  720. #include "SDL_thread.h" // also fetches SDL_mutex.h
  721. #else
  722. #include <pthread.h>
  723. #include <semaphore.h>
  724. #include <sys/shm.h>
  725. #endif
  726. static int sl_sem_errorcountdummy = 0;
  727. #ifdef AC_USE_SDL_THREADS
  728. sl_semaphore::sl_semaphore(int init, int *ecnt)
  729. {
  730. data = (void *)SDL_CreateSemaphore(init);
  731. errorcount = ecnt ? ecnt : &sl_sem_errorcountdummy;
  732. if(data == NULL) (*errorcount)++;
  733. }
  734. sl_semaphore::~sl_semaphore()
  735. {
  736. if(data) SDL_DestroySemaphore((SDL_sem *) data);
  737. }
  738. void sl_semaphore::wait()
  739. {
  740. if(SDL_SemWait((SDL_sem *) data) != 0) (*errorcount)++;
  741. }
  742. int sl_semaphore::trywait()
  743. {
  744. return SDL_SemTryWait((SDL_sem *) data);
  745. }
  746. int sl_semaphore::timedwait(int howlongmillis)
  747. {
  748. return SDL_SemWaitTimeout((SDL_sem *) data, howlongmillis);
  749. }
  750. int sl_semaphore::getvalue()
  751. {
  752. return SDL_SemValue((SDL_sem *) data);
  753. }
  754. void sl_semaphore::post()
  755. {
  756. if(SDL_SemPost((SDL_sem *) data) != 0) (*errorcount)++;
  757. }
  758. #else
  759. sl_semaphore::sl_semaphore(int init, int *ecnt)
  760. {
  761. errorcount = ecnt ? ecnt : &sl_sem_errorcountdummy;
  762. data = (void *) new sem_t;
  763. if(data == NULL || sem_init((sem_t *) data, 0, init) != 0) (*errorcount)++;
  764. }
  765. sl_semaphore::~sl_semaphore()
  766. {
  767. if(data)
  768. {
  769. if(sem_destroy((sem_t *) data) != 0) (*errorcount)++;
  770. delete (sem_t *)data;
  771. }
  772. }
  773. void sl_semaphore::wait()
  774. {
  775. if(sem_wait((sem_t *) data) != 0) (*errorcount)++;
  776. }
  777. int sl_semaphore::trywait()
  778. {
  779. return sem_trywait((sem_t *) data);
  780. }
  781. int sl_semaphore::timedwait(int howlongmillis)
  782. {
  783. struct timespec t;
  784. if(clock_gettime(CLOCK_REALTIME, &t))
  785. {
  786. (*errorcount)++;
  787. return sem_trywait((sem_t *) data);
  788. }
  789. howlongmillis += t.tv_nsec / 1000000;
  790. t.tv_nsec = (howlongmillis % 1000) * 1000000;
  791. t.tv_sec += howlongmillis / 1000;
  792. return sem_timedwait((sem_t *) data, &t);
  793. }
  794. int sl_semaphore::getvalue()
  795. {
  796. int ret;
  797. if(sem_getvalue((sem_t *) data, &ret) != 0) (*errorcount)++;
  798. return ret;
  799. }
  800. void sl_semaphore::post()
  801. {
  802. if(sem_post((sem_t *) data) != 0) (*errorcount)++;
  803. }
  804. #endif
  805. // (wrapping threads is slightly ugly, since SDL threads use a different return value (int) than pthreads (void *) - and that can't be solved with a typecast)
  806. #ifdef AC_USE_SDL_THREADS
  807. struct sl_threadinfo { int (*fn)(void *); void *data; SDL_Thread *handle; volatile char done; };
  808. static int sl_thread_indir(void *info)
  809. {
  810. int res = (*((sl_threadinfo*)info)->fn)(((sl_threadinfo*)info)->data);
  811. ((sl_threadinfo*)info)->done = 1;
  812. return res;
  813. }
  814. void *sl_createthread(int (*fn)(void *), void *data)
  815. {
  816. sl_threadinfo *ti = new sl_threadinfo;
  817. ti->data = data;
  818. ti->fn = fn;
  819. ti->done = 0;
  820. ti->handle = SDL_CreateThread(sl_thread_indir, NULL, ti);
  821. return (void *) ti;
  822. }
  823. int sl_waitthread(void *ti)
  824. {
  825. int res;
  826. SDL_WaitThread(((sl_threadinfo *)ti)->handle, &res);
  827. delete (sl_threadinfo *) ti;
  828. return res;
  829. }
  830. static vector<sl_threadinfo *> oldthreads;
  831. static sl_semaphore sem_oldthreads(1, NULL);
  832. void sl_detachthread(void *ti) // SDL can't actually detach threads, so this is manual cleanup
  833. {
  834. if(ti && sl_pollthread(ti))
  835. {
  836. SDL_WaitThread(((sl_threadinfo *)ti)->handle, NULL);
  837. delete (sl_threadinfo *) ti;
  838. ti = NULL;
  839. }
  840. if(ti || sem_oldthreads.getvalue() > 0)
  841. {
  842. sem_oldthreads.wait();
  843. if(ti) oldthreads.add((sl_threadinfo *)ti);
  844. loopvrev(oldthreads)
  845. {
  846. if(oldthreads[i]->done)
  847. {
  848. SDL_WaitThread(oldthreads[i]->handle, NULL);
  849. delete oldthreads.remove(i);
  850. }
  851. }
  852. sem_oldthreads.post();
  853. }
  854. }
  855. static uint32_t mainthreadid = SDL_ThreadID();
  856. bool ismainthread() { return mainthreadid == SDL_ThreadID(); }
  857. #else
  858. struct sl_threadinfo { int (*fn)(void *); void *data; pthread_t handle; int res; volatile char done; };
  859. static void *sl_thread_indir(void *info)
  860. {
  861. sl_threadinfo *ti = (sl_threadinfo*) info;
  862. ti->res = (ti->fn)(ti->data);
  863. ti->done = 1;
  864. return &ti->res;
  865. }
  866. void *sl_createthread(int (*fn)(void *), void *data)
  867. {
  868. sl_threadinfo *ti = new sl_threadinfo;
  869. ti->data = data;
  870. ti->fn = fn;
  871. ti->done = 0;
  872. pthread_create(&(ti->handle), NULL, sl_thread_indir, ti);
  873. return (void *) ti;
  874. }
  875. int sl_waitthread(void *ti)
  876. {
  877. void *res;
  878. pthread_join(((sl_threadinfo *)ti)->handle, &res);
  879. int ires = *((int *)res);
  880. delete (sl_threadinfo *) ti;
  881. return ires;
  882. }
  883. static vector<sl_threadinfo *> oldthreads;
  884. static sl_semaphore sem_oldthreads(1, NULL);
  885. void sl_detachthread(void *ti)
  886. {
  887. if(ti) pthread_detach(((sl_threadinfo *)ti)->handle);
  888. if(ti || sem_oldthreads.getvalue() > 0)
  889. {
  890. sem_oldthreads.wait();
  891. if(ti) oldthreads.add((sl_threadinfo *)ti);
  892. loopvrev(oldthreads) if(oldthreads[i]->done) delete oldthreads.remove(i);
  893. sem_oldthreads.post();
  894. }
  895. }
  896. static pthread_t mainthreadid = pthread_self();
  897. bool ismainthread() { return pthread_equal(mainthreadid, pthread_self()) != 0; }
  898. #endif
  899. bool sl_pollthread(void *ti)
  900. {
  901. return ((sl_threadinfo*)ti)->done != 0;
  902. }
  903. // platform dependent stuff not covered by enet (use POSIX or, if possible, SDL)
  904. #ifdef AC_USE_SDL_THREADS
  905. void sl_sleep(int duration)
  906. {
  907. SDL_Delay(duration);
  908. }
  909. #else
  910. void sl_sleep(int duration)
  911. {
  912. struct timespec t = { duration / 1000, (duration % 1000) * 1000000 };
  913. nanosleep(&t, NULL);
  914. }
  915. #endif
  916. void parseupdatelist(hashtable<const char *, int> &ht, char *buf, const char *prefix, const char *suffix)
  917. {
  918. for(char *d = buf; *d; d++) if(!isalnum(*d) && !strchr("._-() /\n", *d)) *d = ' '; // allowed chars in media path strings (except ' ')
  919. char *p, *l = buf, *m;
  920. int rev, *revp, plen = prefix ? (int)strlen(prefix) : 0, slen = suffix ? (int)strlen(suffix) : 0;
  921. do
  922. {
  923. if((p = strchr(l, '\n'))) *p = '\0'; // break into single lines
  924. l += strspn(l, " "); // skip leading spaces
  925. if((m = strchr(l, ' ')) && (rev = atoi(m + 1))) // string has a space and a number != 0 after it
  926. {
  927. *m = '\0';
  928. if((!plen || !strncmp(l, prefix, plen)) && // prefix is either not required or present
  929. (!slen || (m - l > slen && !strcmp(m - slen, suffix)))) // suffix is either not required or present
  930. {
  931. l += plen; // skip prefix
  932. m[-slen] = '\0'; // cut suffix
  933. revp = ht.access(l);
  934. if(revp) *revp = rev;
  935. else ht.access(newstring(l), rev);
  936. }
  937. }
  938. l = p + 1;
  939. }
  940. while(p);
  941. }