// implementation of generic tools #include "cube.h" const char *timestring(bool local, const char *fmt) { static string asciitime; time_t t = time(NULL); struct tm * timeinfo; timeinfo = local ? localtime(&t) : gmtime (&t); strftime(asciitime, sizeof(string) - 1, fmt ? fmt : "%Y%m%d_%H.%M.%S", timeinfo); // sortable time for filenames return asciitime; } const char *asctime() { return timestring(true, "%c"); } const char *numtime() { static string numt; formatstring(numt)("%ld", (long long) time(NULL)); return numt; } extern char *maplayout; extern int maplayout_factor, Mvolume, Marea; extern float Mheight; extern int checkarea(); mapstats *loadmapstats(const char *filename, bool getlayout) { static mapstats s; static uchar *enttypes = NULL; static short *entposs = NULL; DELETEA(enttypes); loopi(MAXENTTYPES) s.entcnt[i] = 0; loopi(3) s.spawns[i] = 0; loopi(2) s.flags[i] = 0; stream *f = opengzfile(filename, "rb"); if(!f) return NULL; memset(&s.hdr, 0, sizeof(header)); if(f->read(&s.hdr, sizeof(header)-sizeof(int)*16-sizeof(char)*128)!=sizeof(header)-sizeof(int)*16-sizeof(char)*128 || (strncmp(s.hdr.head, "CUBE", 4) && strncmp(s.hdr.head, "ACMP",4))) { gzclose(f); return NULL; } lilswap(&s.hdr.version, 4); if(s.hdr.version>MAPVERSION || s.hdr.numents > MAXENTITIES || (s.hdr.version>=4 && f->read(&s.hdr.waterlevel, sizeof(int)*16)!=sizeof(int)*16)) { delete f; return NULL; } if(s.hdr.version>=7 && f->read(&s.hdr.mediareq, sizeof(char)*128)!=sizeof(char)*128) { delete f; return NULL; } if(s.hdr.version>=4) { lilswap(&s.hdr.waterlevel, 1); lilswap(&s.hdr.maprevision, 2); } else s.hdr.waterlevel = -100000; if(s.hdr.version<7) { copystring(s.hdr.mediareq, "", 128);//s.hdr.mediareq[0] = '\0'; } entity e; enttypes = new uchar[s.hdr.numents]; entposs = new short[s.hdr.numents * 3]; loopi(s.hdr.numents) { f->read(&e, sizeof(persistent_entity)); lilswap((short *)&e, 4); TRANSFORMOLDENTITIES(s.hdr) if(e.type == PLAYERSTART && (e.attr2 == 0 || e.attr2 == 1 || e.attr2 == 100)) s.spawns[e.attr2 == 100 ? 2 : e.attr2]++; if(e.type == CTF_FLAG && (e.attr2 == 0 || e.attr2 == 1)) { s.flags[e.attr2]++; s.flagents[e.attr2] = i; } s.entcnt[e.type]++; enttypes[i] = e.type; entposs[i * 3] = e.x; entposs[i * 3 + 1] = e.y; entposs[i * 3 + 2] = e.z; } if(getlayout) { DELETEA(maplayout); if(s.hdr.sfactor <= LARGEST_FACTOR && s.hdr.sfactor >= SMALLEST_FACTOR) { maplayout_factor = s.hdr.sfactor; int layoutsize = 1 << (maplayout_factor * 2); bool fail = false; maplayout = new char[layoutsize + 256]; memset(maplayout, 0, layoutsize * sizeof(char)); char *t = NULL; char floor = 0, ceil; int diff = 0; Mvolume = Marea = 0; loopk(layoutsize) { char *c = maplayout + k; int type = f->getchar(); switch(type) { case 255: { int n = f->getchar(); if(!t || n < 0) { fail = true; break; } memset(c, *t, n); k += n - 1; break; } case 254: // only in MAPVERSION<=2 if(!t) { fail = true; break; } *c = *t; f->getchar(); f->getchar(); break; default: if(type<0 || type>=MAXTYPE) { fail = true; break; } floor = f->getchar(); ceil = f->getchar(); if(floor >= ceil && ceil > -128) floor = ceil - 1; // for pre 12_13 diff = ceil - floor; if(type == FHF) floor = -128; f->getchar(); f->getchar(); if(s.hdr.version>=2) f->getchar(); if(s.hdr.version>=5) f->getchar(); case SOLID: *c = type == SOLID ? 127 : floor; f->getchar(); f->getchar(); if(s.hdr.version<=2) { f->getchar(); f->getchar(); } break; } if ( type != SOLID && diff > 6 ) { Marea++; Mvolume+=diff; } if(fail) break; t = c; } if(fail) { DELETEA(maplayout); } else Mheight = Marea ? (float)Mvolume/Marea : 0; } } delete f; s.hasffaspawns = s.spawns[2] > 0; s.hasteamspawns = s.spawns[0] > 0 && s.spawns[1] > 0; s.hasflags = s.flags[0] > 0 && s.flags[1] > 0; s.enttypes = enttypes; s.entposs = entposs; s.cgzsize = getfilesize(filename); return &s; } ///////////////////////// debugging /////////////////////// #if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__) void stackdumper(unsigned int type, EXCEPTION_POINTERS *ep) { if(!ep) fatal("unknown type"); EXCEPTION_RECORD *er = ep->ExceptionRecord; CONTEXT *context = ep->ContextRecord; string out, t; formatstring(out)("Win32 Exception: 0x%x [0x%x]\n\n", er->ExceptionCode, er->ExceptionCode==EXCEPTION_ACCESS_VIOLATION ? er->ExceptionInformation[1] : -1); STACKFRAME sf = {{context->Eip, 0, AddrModeFlat}, {}, {context->Ebp, 0, AddrModeFlat}, {context->Esp, 0, AddrModeFlat}, 0}; SymInitialize(GetCurrentProcess(), NULL, TRUE); while(::StackWalk(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), &sf, context, NULL, ::SymFunctionTableAccess, ::SymGetModuleBase, NULL)) { struct { IMAGEHLP_SYMBOL sym; string n; } si = { { sizeof( IMAGEHLP_SYMBOL ), 0, 0, 0, sizeof(string) } }; IMAGEHLP_LINE li = { sizeof( IMAGEHLP_LINE ) }; DWORD off; if(SymGetSymFromAddr(GetCurrentProcess(), (DWORD)sf.AddrPC.Offset, &off, &si.sym) && SymGetLineFromAddr(GetCurrentProcess(), (DWORD)sf.AddrPC.Offset, &off, &li)) { char *del = strrchr(li.FileName, '\\'); formatstring(t)("%s - %s [%d]\n", si.sym.Name, del ? del + 1 : li.FileName, li.LineNumber); concatstring(out, t); } } fatal("%s", out); } #elif defined(linux) || defined(__linux) || defined(__linux__) #include // stack dumping on linux, inspired by Sachin Agrawal's sample code struct signalbinder { static void stackdumper(int sig) { printf("stacktrace:\n"); const int BTSIZE = 25; void *array[BTSIZE]; int n = backtrace(array, BTSIZE); char **symbols = backtrace_symbols(array, n); for(int i = 0; i < n; i++) { printf("%s\n", symbols[i]); } free(symbols); fatal("AssaultCube error (%d)", sig); } signalbinder() { // register signals to dump the stack if they are raised, // use constructor for early registering signal(SIGSEGV, stackdumper); signal(SIGFPE, stackdumper); signal(SIGILL, stackdumper); signal(SIGBUS, stackdumper); signal(SIGSYS, stackdumper); signal(SIGABRT, stackdumper); } }; signalbinder sigbinder; #endif ///////////////////////// misc tools /////////////////////// bool cmpb(void *b, int n, enet_uint32 c) { ENetBuffer buf; buf.data = b; buf.dataLength = n; return enet_crc32(&buf, 1)==c; } bool cmpf(char *fn, enet_uint32 c) { int n = 0; char *b = loadfile(fn, &n); bool r = cmpb(b, n, c); delete[] b; return r; } enet_uint32 adler(unsigned char *data, size_t len) { enet_uint32 a = 1, b = 0; while (len--) { a += *data++; b += a; } return b; } bool isbigendian() { return !*(const uchar *)&islittleendian; } void strtoupper(char *t, const char *s) { if(!s) s = t; while(*s) { *t = toupper(*s); t++; s++; } *t = '\0'; } const char *atoip(const char *s, enet_uint32 *ip) { unsigned int d[4]; int n; if(!s || sscanf(s, "%u.%u.%u.%u%n", d, d + 1, d + 2, d + 3, &n) != 4) return NULL; *ip = 0; loopi(4) { if(d[i] > 255) return NULL; *ip = (*ip << 8) + d[i]; } return s + n; } const char *atoipr(const char *s, iprange *ir) { if((s = atoip(s, &ir->lr)) == NULL) return NULL; ir->ur = ir->lr; s += strspn(s, " \t"); if(*s == '-') { if(!(s = atoip(s + 1, &ir->ur)) || ir->lr > ir->ur) return NULL; } else if(*s == '/') { int m, n; if(sscanf(s + 1, "%d%n", &m, &n) != 1 || m < 0 || m > 32) return NULL; unsigned long bm = (1 << (32 - m)) - 1; ir->lr &= ~bm; ir->ur |= bm; s += 1 + n; } return s; } const char *iptoa(const enet_uint32 ip) { static string s[2]; static int buf = 0; buf = (buf + 1) % 2; formatstring(s[buf])("%d.%d.%d.%d", (ip >> 24) & 255, (ip >> 16) & 255, (ip >> 8) & 255, ip & 255); return s[buf]; } const char *iprtoa(const struct iprange &ipr) { static string s[2]; static int buf = 0; buf = (buf + 1) % 2; if(ipr.lr == ipr.ur) copystring(s[buf], iptoa(ipr.lr)); else formatstring(s[buf])("%s-%s", iptoa(ipr.lr), iptoa(ipr.ur)); return s[buf]; } int cmpiprange(const struct iprange *a, const struct iprange *b) { if(a->lr < b->lr) return -1; if(a->lr > b->lr) return 1; return 0; } int cmpipmatch(const struct iprange *a, const struct iprange *b) { return - (a->lr < b->lr) + (a->lr > b->ur); } char *concatformatstring(char *d, const char *s, ...) { static defvformatstring(temp, s, s); return concatstring(d, temp); } const char *hiddenpwd(const char *pwd, int showchars) { static int sc = 3; static string text; copystring(text, pwd); if(showchars > 0) sc = showchars; for(int i = (int)strlen(text) - 1; i >= sc; i--) text[i] = '*'; return text; } //////////////// geometry utils //////////////// static inline float det2x2(float a, float b, float c, float d) { return a*d - b*c; } static inline float det3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3) { return a1 * det2x2(b2, b3, c2, c3) - b1 * det2x2(a2, a3, c2, c3) + c1 * det2x2(a2, a3, b2, b3); } float glmatrixf::determinant() const { float a1 = v[0], a2 = v[1], a3 = v[2], a4 = v[3], b1 = v[4], b2 = v[5], b3 = v[6], b4 = v[7], c1 = v[8], c2 = v[9], c3 = v[10], c4 = v[11], d1 = v[12], d2 = v[13], d3 = v[14], d4 = v[15]; return a1 * det3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4) - b1 * det3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4) + c1 * det3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4) - d1 * det3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4); } void glmatrixf::adjoint(const glmatrixf &m) { float a1 = m.v[0], a2 = m.v[1], a3 = m.v[2], a4 = m.v[3], b1 = m.v[4], b2 = m.v[5], b3 = m.v[6], b4 = m.v[7], c1 = m.v[8], c2 = m.v[9], c3 = m.v[10], c4 = m.v[11], d1 = m.v[12], d2 = m.v[13], d3 = m.v[14], d4 = m.v[15]; v[0] = det3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4); v[1] = -det3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4); v[2] = det3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4); v[3] = -det3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4); v[4] = -det3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4); v[5] = det3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4); v[6] = -det3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4); v[7] = det3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4); v[8] = det3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4); v[9] = -det3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4); v[10] = det3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4); v[11] = -det3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4); v[12] = -det3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3); v[13] = det3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3); v[14] = -det3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3); v[15] = det3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3); } bool glmatrixf::invert(const glmatrixf &m, float mindet) { float a1 = m.v[0], b1 = m.v[4], c1 = m.v[8], d1 = m.v[12]; adjoint(m); float det = a1*v[0] + b1*v[1] + c1*v[2] + d1*v[3]; // float det = m.determinant(); if(fabs(det) < mindet) return false; float invdet = 1/det; loopi(16) v[i] *= invdet; return true; }