Files
assaultcube-/source/src/tools.h
T
2026-10-25 08:51:39 +00:00

1057 lines
32 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// generic useful stuff for any C++ program
#ifndef _TOOLS_H
#define _TOOLS_H
#ifdef NULL
#undef NULL
#endif
#define NULL 0
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
#ifdef _DEBUG
#ifdef __GNUC__
#define ASSERT(c) if(!(c)) { fflush(NULL); asm("int $3"); }
#else
#define ASSERT(c) if(!(c)) { fflush(NULL); { __asm int 3 } }
#endif
#include <iostream>
#define DEBUG(v) if(DEBUGCOND) { std::cout << behindpath(__FILE__) << ":" << __LINE__ << " " << __FUNCTION__ << "(..) " << v << std::endl; }
#define DEBUGVAR(v) if(DEBUGCOND) { std::cout << behindpath(__FILE__) << ":" << __LINE__ << " " << __FUNCTION__ << "(..) " << #v << " = " << v << std::endl; }
#define DEBUGCODE(v) v
#else
#define DEBUG(v) {}
#define DEBUGVAR(v) {}
#define DEBUGCODE(v)
#define ASSERT(c) if(c) {}
#endif
#ifdef NO_POSIX_R
extern char *strtok_r(char *s, const char *delim, char **b); // homebrew
#endif
#ifdef swap
#undef swap
#endif
template<class T>
static inline void swap(T &a, T &b)
{
T t = a;
a = b;
b = t;
}
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
template<class T>
static inline T max(T a, T b)
{
return a > b ? a : b;
}
template<class T>
static inline T min(T a, T b)
{
return a < b ? a : b;
}
template <typename T> inline T pow2(T x) { return x*x; }
inline int iabs(int n) { return labs(n); }
#define clamp(x,minval,maxval) (max(minval, min(x, maxval)))
#define rnd(x) ((int)(randomMT()&0xFFFFFF)%(x))
#define rndscale(x) (float((randomMT()&0xFFFFFF)*double(x)/double(0xFFFFFF)))
#define detrnd(s, x) ((int)(((((uint)(s))*1103515245+12345)>>16)%(x)))
#define constarraysize(x) ((int)(sizeof(x)/sizeof(*x)))
#define loop(v,m) for(int v = 0; v<int(m); v++)
#define loopi(m) loop(i,m)
#define loopj(m) loop(j,m)
#define loopk(m) loop(k,m)
#define loopl(m) loop(l,m)
#define loopirev(m) for(int i = int(m) - 1; i >= 0; i--)
#define DELETEP(p) if(p) { delete p; p = 0; }
#define DELETEA(p) if(p) { delete[] p; p = 0; }
#define PI (3.141592654f)
#define PI2 (6.283185307f) /* 2 * PI */
#define SQRT2 (1.414213562f)
#define SQRT3 (1.732050808f)
#define RAD (0.01745329252f) /* PI / 180 */
#define NEARZERO (0.000245f) /* epsilon for 1<<LARGEST_FACTOR, use to check if "zero enough" */
#ifdef WIN32
#ifndef __GNUC__
#pragma warning (3: 4189) // local variable is initialized but not referenced
#pragma warning (disable: 4244) // conversion from 'int' to 'float', possible loss of data
#pragma warning (disable: 4355) // 'this' : used in base member initializer list
#pragma warning (disable: 4996) // 'strncpy' was declared deprecated
#endif
#define strcasecmp(a,b) _stricmp(a,b)
#define strncasecmp(a,b,n) _strnicmp(a,b,n)
#define PATHDIV '\\'
#define PATHDIVS "\\"
#else
#define __cdecl
#define _vsnprintf vsnprintf
#define PATHDIV '/'
#define PATHDIVS "/"
#endif
#ifdef __GNUC__
#define PRINTFARGS(fmt, args) __attribute__((format(printf, fmt, args)))
//#pragma GCC diagnostic ignored "-Wformat-zero-length" // apparently doesn't work from precompiled header -> moved to makefile
#else
#define PRINTFARGS(fmt, args)
#endif
// easy safe strings
#define MAXSTRLEN 260
typedef char string[MAXSTRLEN];
#define vformatstring(d,last,fmt) { va_list ap; va_start(ap, last); vformatstring_(d, fmt, ap); va_end(ap); }
inline void vformatstring_(char* d, const char* fmt, va_list v, int len = MAXSTRLEN) { _vsnprintf(d, len, fmt, v); d[len - 1] = 0; }
//#pragma GCC diagnostic ignored "-Wstringop-truncation" // moved to makefile, because it doesn't work from precompiled header
inline char *copystring(char *d, const char *s, size_t len = MAXSTRLEN) { strncpy(d, s, len); d[len-1] = 0; return d; }
inline char *concatstring(char *d, const char *s, size_t len = MAXSTRLEN) { size_t used = strlen(d); return used < len ? copystring(d+used, s, len-used) : d; }
extern char *concatformatstring(char *d, const char *s, ...) PRINTFARGS(2, 3);
struct stringformatter
{
char *buf;
stringformatter(char *buf): buf((char *)buf) {}
void operator()(const char *fmt, ...) PRINTFARGS(2, 3)
{
vformatstring(buf, fmt, fmt);
}
};
#define formatstring(d) stringformatter((char *)d)
#define defformatstring(d) string d; formatstring(d)
#define defvformatstring(d,last,fmt) string d; vformatstring(d,last,fmt)
#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)
#define loopv(v) for(int i = 0; i<(v).length(); i++)
#define loopvj(v) for(int j = 0; j<(v).length(); j++)
#define loopvk(v) for(int k = 0; k<(v).length(); k++)
#define loopvrev(v) for(int i = (v).length()-1; i>=0; i--)
#define loopvjrev(v) for(int j = (v).length()-1; j>=0; j--)
template <class T>
struct databuf
{
enum
{
OVERREAD = 1<<0,
OVERWROTE = 1<<1
};
T *buf;
int len, maxlen;
uchar flags;
databuf() : buf(NULL), len(0), maxlen(0), flags(0) {}
template <class U>
databuf(T *buf, U maxlen) : buf(buf), len(0), maxlen((int)maxlen), flags(0) {}
const T &get()
{
static T overreadval = 0;
if(len<maxlen) return buf[len++];
flags |= OVERREAD;
return overreadval;
}
databuf subbuf(int sz)
{
sz = min(sz, maxlen-len);
len += sz;
return databuf(&buf[len-sz], sz);
}
void put(const T &val)
{
if(len<maxlen) buf[len++] = val;
else flags |= OVERWROTE;
}
void put(const T *vals, int numvals)
{
if(maxlen-len<numvals) flags |= OVERWROTE;
memcpy(&buf[len], vals, min(maxlen-len, numvals)*sizeof(T));
len += min(maxlen-len, numvals);
}
int get(T *vals, int numvals)
{
int read = min(maxlen-len, numvals);
if(read<numvals) flags |= OVERREAD;
memcpy(vals, &buf[len], read*sizeof(T));
len += read;
return read;
}
int length() const { return len; }
int remaining() const { return maxlen-len; }
bool overread() const { return (flags&OVERREAD)!=0; }
bool overwrote() const { return (flags&OVERWROTE)!=0; }
void forceoverread()
{
len = maxlen;
flags |= OVERREAD;
}
};
typedef databuf<char> charbuf;
typedef databuf<uchar> ucharbuf;
struct packetbuf : ucharbuf
{
ENetPacket *packet;
int growth;
packetbuf(ENetPacket *packet) : ucharbuf(packet->data, packet->dataLength), packet(packet), growth(0) {}
packetbuf(int growth, int pflags = 0) : growth(growth)
{
packet = enet_packet_create(NULL, growth, pflags);
buf = (uchar *)packet->data;
maxlen = packet->dataLength;
}
~packetbuf() { cleanup(); }
void reliable() { packet->flags |= ENET_PACKET_FLAG_RELIABLE; }
void resize(int n)
{
enet_packet_resize(packet, n);
buf = (uchar *)packet->data;
maxlen = packet->dataLength;
}
void checkspace(int n)
{
if(len + n > maxlen && packet && growth > 0) resize(max(len + n, maxlen + growth));
}
ucharbuf subbuf(int sz)
{
checkspace(sz);
return ucharbuf::subbuf(sz);
}
void put(const uchar &val)
{
checkspace(1);
ucharbuf::put(val);
}
void put(const uchar *vals, int numvals)
{
checkspace(numvals);
ucharbuf::put(vals, numvals);
}
bool overwrote() const { return false; }
ENetPacket *finalize()
{
resize(len);
return packet;
}
void cleanup()
{
if(growth > 0 && packet && !packet->referenceCount) { enet_packet_destroy(packet); packet = NULL; buf = NULL; len = maxlen = 0; }
}
};
template<class T>
struct bitbuf
{
T &q;
int blen;
bitbuf(T &oq) : q(oq), blen(0) {}
int getbits(int n)
{
int res = 0, p = 0;
while(n > 0)
{
if(!blen) q.get();
int r = 8 - blen;
if(r > n) r = n;
res |= ((q.buf[q.len - 1] >> blen) & ((1 << r) - 1)) << p;
n -= r; p += r;
blen = (blen + r) % 8;
}
return res;
}
void putbits(int n, int v)
{
while(n > 0)
{
if(!blen) { q.put(0); if(q.overwrote()) return; }
int r = 8 - blen;
if(r > n) r = n;
q.buf[q.len - 1] |= (v & ((1 << r) - 1)) << blen;
n -= r; v >>= r;
blen = (blen + r) % 8;
}
}
int rembits() { return (8 - blen) % 8; } // 0..7 bits remaining in current byte
};
template <class T> struct vector
{
static const int MINSIZE = 8;
T *buf;
int alen, ulen;
vector() : buf(NULL), alen(0), ulen(0)
{
}
vector(const vector &v) : buf(NULL), alen(0), ulen(0)
{
*this = v;
}
~vector() { shrink(0); if(buf) delete[] (uchar *)buf; }
vector<T> &operator=(const vector<T> &v)
{
shrink(0);
if(v.length() > alen) growbuf(v.length());
loopv(v) add(v[i]);
return *this;
}
T &add(const T &x)
{
if(ulen==alen) growbuf(ulen+1);
new (&buf[ulen]) T(x);
return buf[ulen++];
}
T &add()
{
if(ulen==alen) growbuf(ulen+1);
new (&buf[ulen]) T;
return buf[ulen++];
}
T &dup()
{
if(ulen==alen) growbuf(ulen+1);
new (&buf[ulen]) T(buf[ulen-1]);
return buf[ulen++];
}
bool inrange(size_t i) const { return i<size_t(ulen); }
bool inrange(int i) const { return i>=0 && i<ulen; }
T &pop() { ASSERT(ulen > 0); return buf[--ulen]; }
T &last() { ASSERT(ulen > 0); return buf[ulen-1]; }
void drop() { ASSERT(ulen > 0); buf[--ulen].~T(); }
bool empty() const { return ulen==0; }
int capacity() const { return alen; }
int length() const { return ulen; }
T &operator[](int i) { ASSERT(i>=0 && i<ulen); return buf[i]; }
const T &operator[](int i) const { ASSERT(i >= 0 && i<ulen); return buf[i]; }
void shrink(int i) { ASSERT(i>=0 && i<=ulen); while(ulen>i) drop(); }
void setsize(int i) { ASSERT(i>=0 && i<=ulen); ulen = i; }
void deletecontents() { while(!empty()) delete pop(); }
void deletearrays() { while(!empty()) delete[] pop(); }
T *getbuf() { return buf; }
const T *getbuf() const { return buf; }
bool inbuf(const T *e) const { return e >= buf && e < &buf[ulen]; }
template<class ST>
void sort(int (__cdecl *cf)(ST *, ST *), int i = 0, int n = -1)
{
qsort(&buf[i], n<0 ? ulen : n, sizeof(T), (int (__cdecl *)(const void *,const void *))cf);
}
template<class ST>
T *search(T *key, int (__cdecl *cf)(ST *, ST *), int i = 0, int n = -1)
{
return (T *) bsearch(key, &buf[i], n<0 ? ulen : n, sizeof(T), (int (__cdecl *)(const void *,const void *))cf);
}
void growbuf(int sz)
{
int olen = alen;
if(!alen) alen = max(MINSIZE, sz);
else while(alen < sz) alen *= 2;
if(alen <= olen) return;
uchar *newbuf = new uchar[alen*sizeof(T)];
if(olen > 0)
{
memcpy(newbuf, buf, olen*sizeof(T));
delete[] (uchar *)buf;
}
buf = (T *)newbuf;
}
databuf<T> reserve(int sz)
{
if(ulen+sz > alen) growbuf(ulen+sz);
return databuf<T>(&buf[ulen], sz);
}
void advance(int sz)
{
ulen += sz;
}
void addbuf(const databuf<T> &p)
{
advance(p.length());
}
T *pad(int n)
{
T *buf = reserve(n).buf;
advance(n);
return buf;
}
void put(const T &v) { add(v); }
void put(const T *v, int n)
{
databuf<T> buf = reserve(n);
buf.put(v, n);
addbuf(buf);
}
void remove(int i, int n)
{
for(int p = i+n; p<ulen; p++) buf[p-n] = buf[p];
ulen -= n;
}
T remove(int i)
{
T e = buf[i];
for(int p = i+1; p<ulen; p++) buf[p-1] = buf[p];
ulen--;
return e;
}
int find(const T &o)
{
loopi(ulen) if(buf[i]==o) return i;
return -1;
}
void removeobj(const T &o)
{
loopi(ulen) if(buf[i]==o) remove(i--);
}
void replacewithlast(const T &o)
{
if(!ulen) return;
loopi(ulen-1) if(buf[i]==o)
{
buf[i] = buf[ulen-1];
}
ulen--;
}
T &insert(int i, const T &e)
{
add(T());
for(int p = ulen-1; p>i; p--) buf[p] = buf[p-1];
buf[i] = e;
return buf[i];
}
T *insert(int i, const T *e, int n)
{
if(ulen+n>alen) growbuf(ulen+n);
loopj(n) add(T());
for(int p = ulen-1; p>=i+n; p--) buf[p] = buf[p-n];
loopj(n) buf[i+j] = e[j];
return &buf[i];
}
};
static inline uint hthash(const char *key)
{
uint h = 5381;
for(int i = 0, k; (k = key[i]); i++) h = ((h<<5)+h)^k; // bernstein k=33 xor
return h;
}
static inline bool htcmp(const char *x, const char *y)
{
return !strcmp(x, y);
}
struct uchar32 { uchar u[32]; };
static inline uint hthash(const uchar32 &key)
{
return *((uint *)&key);
}
static inline bool htcmp(const uchar32 &x, const uchar32 &y)
{
return !memcmp(&x, &y, 32);
}
static inline uint hthash(int key)
{
return key;
}
static inline bool htcmp(int x, int y)
{
return x==y;
}
static inline uint hthash(uint key)
{
return key;
}
static inline bool htcmp(uint x, uint y)
{
return x==y;
}
// UTF-8 helpers (strings in the whole engine are plain char[]; text that may contain non-ASCII is kept as UTF-8)
// decodes one UTF-8 sequence at s. Returns the codepoint, or -1 for an invalid/overlong/truncated sequence (len is 1 then).
// len receives the number of bytes consumed (0 at the terminating NUL).
static inline int utf8decode(const char *s, int &len)
{
const uchar *u = (const uchar *)s;
int c = u[0], n, cp, minv;
if(!c) { len = 0; return 0; }
len = 1;
if(c < 0x80) return c;
if((c & 0xE0) == 0xC0) { n = 1; cp = c & 0x1F; minv = 0x80; }
else if((c & 0xF0) == 0xE0) { n = 2; cp = c & 0x0F; minv = 0x800; }
else if((c & 0xF8) == 0xF0) { n = 3; cp = c & 0x07; minv = 0x10000; }
else return -1;
for(int i = 1; i <= n; i++)
{
if((u[i] & 0xC0) != 0x80) return -1;
cp = (cp << 6) | (u[i] & 0x3F);
}
if(cp < minv || cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF)) return -1;
len = n + 1;
return cp;
}
static inline int utf8charlen(const char *s) // bytes of the character starting at s (at least 1)
{
int l;
utf8decode(s, l);
return l > 0 ? l : 1;
}
static inline int utf8prev(const char *s, int i) // index of the start of the character before position i
{
if(i <= 0) return 0;
do { i--; } while(i > 0 && ((uchar)s[i] & 0xC0) == 0x80);
return i;
}
template <class K, class T> struct hashtable
{
typedef K key;
typedef const K const_key;
typedef T value;
typedef const T const_value;
enum { CHUNKSIZE = 16 };
struct chain { T data; K key; chain *next; };
struct chainchunk { chain chains[CHUNKSIZE]; chainchunk *next; };
int size;
int numelems;
chain **table;
chain *enumc;
chainchunk *chunks;
chain *unused;
hashtable(int size = 1<<10)
: size(size)
{
numelems = 0;
chunks = NULL;
unused = NULL;
table = new chain *[size];
loopi(size) table[i] = NULL;
}
~hashtable()
{
DELETEA(table);
deletechunks();
}
void insertchains(chainchunk *emptychunk)
{
loopi(CHUNKSIZE-1) emptychunk->chains[i].next = &emptychunk->chains[i+1];
emptychunk->chains[CHUNKSIZE-1].next = unused;
unused = emptychunk->chains;
}
chain *insert(const K &key, uint h)
{
if(!unused)
{
chainchunk *chunk = new chainchunk;
chunk->next = chunks;
chunks = chunk;
insertchains(chunk);
}
chain *c = unused;
unused = unused->next;
c->next = table[h];
c->key = key;
numelems++;
table[h] = c;
return c;
}
#define HTFIND(success, fail) \
uint h = hthash(key)&(size-1); \
for(chain *c = table[h]; c; c = c->next) \
{ \
if(htcmp(key, c->key)) return (success); \
} \
return (fail);
T *access(const K &key)
{
HTFIND(&c->data, NULL);
}
T &access(const K &key, const T &data)
{
HTFIND(c->data, insert(key, h)->data = data);
}
T &operator[](const K &key)
{
HTFIND(c->data, insert(key, h)->data);
}
#undef HTFIND
bool remove(const K &key)
{
uint h = hthash(key)&(size-1);
for(chain **p = &table[h], *c = table[h]; c; p = &c->next, c = c->next)
{
if(htcmp(key, c->key))
{
*p = c->next;
c->data.~T();
c->key.~K();
new (&c->data) T;
new (&c->key) K;
c->next = unused;
unused = c;
numelems--;
return true;
}
}
return false;
}
void deletechunks()
{
for(chainchunk *nextchunk; chunks; chunks = nextchunk)
{
nextchunk = chunks->next;
delete chunks;
}
}
void clear(bool del = true)
{
loopi(size) table[i] = NULL;
numelems = 0;
unused = NULL;
if(del) deletechunks();
else
{
for(chainchunk *chunk = chunks; chunk; chunk = chunk->next) insertchains(chunk);
}
}
};
template <class T, int SIZE> struct ringbuf
{
volatile int in, out;
T data[SIZE];
ringbuf() { clear(); }
void clear()
{
in = out = 0;
}
bool empty() const { return in == out; }
bool full() const { return length() >= SIZE - 1; }
int maxsize() const { return SIZE - 1; } // yes, only SIZE-1, sry
int length() const { return (SIZE + in - out) % SIZE; }
T &remove() // get one (crashes, if empty)
{
ASSERT(!empty());
T &res = data[out];
out = (out + 1) % SIZE;
return res;
}
T *remove(int *n) // get n (reduces n, if the data wraps around or isn't available)
{
if(*n > length()) *n = length();
T *res = data + out;
if(out + *n >= SIZE) *n = SIZE - out;
out = (out + *n) % SIZE;
return res;
}
T *peek(int *n) // look at n without removing (reduces n, if the data wraps around or isn't available)
{
if(*n > length()) *n = length();
if(out + *n >= SIZE) *n = SIZE - out;
return data + out;
}
void add(const T &e) // add one (crashes, if full) -- between threads, better use stage & commit
{
ASSERT(!full());
volatile int i;
data[(i = in)] = e;
in = (i + 1) % SIZE;
}
T &stage(const T &e) // add one without acknowledging it
{
return (data[in] = e);
}
T *stage() // add one without acknowledging it
{
return data + in;
}
void stage(const T *e, int n) // add n without acknowledging it
{
int n1 = in + n >= SIZE ? SIZE - in : n, n2 = n - n1;
memcpy(data + in, e, n1 * sizeof(T));
if(n2) memcpy(data, e + n1, n2 * sizeof(T));
}
void commit() // add staged one (crashes, if full)
{
ASSERT(!full());
in = (in + 1) % SIZE;
}
void commit(int n) // add staged n (crashes, if full)
{
ASSERT(length() + n < SIZE);
in = (in + n) % SIZE;
}
void add(const T *e, int n) // add n
{
stage(e, n);
commit(n);
}
T &operator[](int i) // [0] is the one, that remove() will get next (in other words, [0] is the oldest entry, [length()-1] was the last added one, loopv goes from old to new)
{
ASSERT(i >= 0 && i < length());
return data[(out + i) % SIZE];
}
const T &operator[](int i) const
{
ASSERT(i >= 0 && i < length());
return data[(out + i) % SIZE];
}
};
#define itoa(s, i) sprintf(s, "%d", i)
#define ftoa(s, f) sprintf(s, (f) == int(f) ? "%.1f" : "%.7g", f)
#define enumeratekt(ht,k,e,t,f,b) loopi((ht).size) for(hashtable<k,t>::chain *enumc = (ht).table[i]; enumc;) { hashtable<k,t>::const_key &e = enumc->key; t &f = enumc->data; enumc = enumc->next; b; }
#define enumeratek(ht,k,e,b) loopi((ht).size) for((ht).enumc = (ht).table[i]; (ht).enumc;) { k &e = (ht).enumc->key; (ht).enumc = (ht).enumc->next; b; }
#define enumerate(ht,t,e,b) loopi((ht).size) for((ht).enumc = (ht).table[i]; (ht).enumc;) { t &e = (ht).enumc->data; (ht).enumc = (ht).enumc->next; b; }
#ifndef STANDALONE
// ease time measurement
struct stopwatch
{
uint millis; // SDL_GetTicks() returns an uint value
stopwatch() : millis(0) {}
void start()
{
millis = SDL_GetTicks();
}
// returns elapsed time
int elapsed()
{
uint time = SDL_GetTicks() - millis; // subtraction also works in case of timer wraparounds
return time;
}
};
#endif
inline char *newstring(size_t l) { return new char[l+1]; }
inline char *newstring(const char *s, size_t l) { return copystring(newstring(l), s, l+1); }
inline char *newstring(const char *s) { size_t l = strlen(s); char *d = newstring(l); memcpy(d, s, l+1); return d; }
inline char *newstringbuf() { return newstring(MAXSTRLEN-1); }
inline char *newstringbuf(const char *s) { return newstring(s, MAXSTRLEN-1); }
inline void delstring(const char *s) { delete[] (char *)s; }
#define DELSTRING(s) if(s) { delstring(s); s = NULL; }
#ifndef INT_LEAST64_MIN
typedef unsigned long long int uint64_t;
#endif
const int islittleendian = 1;
#ifdef SDL_BYTEORDER
#define endianswap16 SDL_Swap16
#define endianswap32 SDL_Swap32
#define endianswap64 SDL_Swap64
#else
#if defined(__GNUC__) && /*!defined(__clang__) && */!defined(__ICL) && __GNUC__ >= 4 && !defined(WIN32) && !defined(__APPLE__)
inline ushort endianswap16(ushort n) { return __builtin_bswap16(n); }
inline uint endianswap32(uint n) { return __builtin_bswap32(n); }
inline uint64_t endianswap64(uint64_t n) { return __builtin_bswap64(n); }
#else
inline ushort endianswap16(ushort n) { return (n<<8) | (n>>8); }
inline uint endianswap32(uint n) { return (n<<24) | (n>>24) | ((n>>8)&0xFF00) | ((n<<8)&0xFF0000); }
inline uint64_t endianswap64(uint64_t n) { return ((uint64_t)endianswap32((uint)n) << 32) | ((uint64_t)endianswap32((uint)(n >> 32))); }
#endif
#endif
template<class T> inline T endianswap(T n) { union { T t; uchar u[sizeof(T)]; } s, d; s.t = n; loopi(sizeof(T)) d.u[i] = s.u[sizeof(T) - 1 - i]; return d.t; }
template<> inline ushort endianswap<ushort>(ushort n) { return endianswap16(n); }
template<> inline short endianswap<short>(short n) { return endianswap16(n); }
template<> inline uint endianswap<uint>(uint n) { return endianswap32(n); }
template<> inline int endianswap<int>(int n) { return endianswap32(n); }
template<> inline uint64_t endianswap<uint64_t>(uint64_t n) { return endianswap64(n); }
template<class T> inline void endianswap(T *buf, int len) { for(T *end = &buf[len]; buf < end; buf++) *buf = endianswap(*buf); }
template<class T> inline T endiansame(T n) { return n; }
template<class T> inline void endiansame(T *buf, int len) {}
#ifdef SDL_BYTEORDER
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
#define lilswap endiansame
#define bigswap endianswap
#else
#define lilswap endianswap
#define bigswap endiansame
#endif
#else
template<class T> inline T lilswap(T n) { return *(const uchar *)&islittleendian ? n : endianswap(n); }
template<class T> inline void lilswap(T *buf, int len) { if(!*(const uchar *)&islittleendian) endianswap(buf, len); }
template<class T> inline T bigswap(T n) { return *(const uchar *)&islittleendian ? endianswap(n) : n; }
template<class T> inline void bigswap(T *buf, int len) { if(*(const uchar *)&islittleendian) endianswap(buf, len); }
#endif
/* workaround for some C platforms that have these two functions as macros - not used anywhere */
#ifdef getchar
#undef getchar
#endif
#ifdef putchar
#undef putchar
#endif
struct stream
{
virtual ~stream() {}
virtual void close() = 0;
virtual bool end() = 0;
virtual long tell() { return -1; }
virtual bool seek(long offset, int whence = SEEK_SET) { return false; }
virtual long size();
virtual void fflush() {}
virtual int read(void *buf, int len) { return 0; }
virtual int write(const void *buf, int len) { return 0; }
virtual int getchar() { uchar c; return read(&c, 1) == 1 ? c : -1; }
virtual bool putchar(int n) { uchar c = n; return write(&c, 1) == 1; }
virtual bool getline(char *str, int len);
virtual bool putstring(const char *str) { int len = (int)strlen(str); return write(str, len) == len; }
virtual bool putline(const char *str) { return putstring(str) && putchar('\n'); }
virtual int printf(const char *fmt, ...) PRINTFARGS(2, 3) { return -1; }
virtual uint getcrc() { return 0; }
template<class T> bool put(T n) { return write(&n, sizeof(n)) == sizeof(n); }
template<class T> bool putlil(T n) { return put<T>(lilswap(n)); }
template<class T> bool putbig(T n) { return put<T>(bigswap(n)); }
template<class T> T get() { T n; return read(&n, sizeof(n)) == sizeof(n) ? n : 0; }
template<class T> T getlil() { return lilswap(get<T>()); }
template<class T> T getbig() { return bigswap(get<T>()); }
#ifndef STANDALONE
SDL_RWops *rwops();
#endif
};
extern string _timestringbuffer;
extern const char *timestring(time_t t, bool local = false, const char *fmt = NULL, char *buf = _timestringbuffer);
inline const char *timestring(bool local = false, const char *fmt = NULL, char *buf = _timestringbuffer) { return timestring(time(NULL), local, fmt, buf); }
extern const char *asctimestr();
extern const char *numtime();
extern void transformoldentitytypes(int mapversion, uchar &enttype);
extern int fixmapheadersize(int version, int headersize);
extern char *path(char *s);
extern char *path(const char *s, bool copy);
extern char *unixpath(char *s);
extern const char *behindpath(const char *s);
extern const char *parentdir(const char *directory);
extern bool fileexists(const char *path, const char *mode);
extern bool createdir(const char *path);
extern size_t fixpackagedir(char *dir);
extern void sethomedir(const char *dir);
extern bool havehomedir();
extern void addpackagedir(const char *dir);
extern int findfilelocation;
enum { FFL_WORKDIR = -2, FFL_HOME = -1, FFL_ZIP = 0 };
extern const char *findfile(const char *filename, const char *mode);
extern int getfilesize(const char *filename);
extern stream *openvecfile(vector<uchar> *s = NULL, bool autodelete = true);
extern stream *openmemfile(const uchar *buf, int size, int *refcnt);
extern bool findzipfile(const char *name);
extern stream *openzipfile(const char *filename, const char *mode);
extern stream *openfile(const char *filename, const char *mode);
extern stream *opentempfile(const char *filename, const char *mode);
extern stream *opengzfile(const char *filename, const char *mode, stream *file = NULL, int level = Z_BEST_COMPRESSION);
extern char *loadfile(const char *fn, int *size, const char *mode = NULL);
extern int streamcopy(stream *dest, stream *source, int maxlen = INT_MAX);
extern void filerotate(const char *basename, const char *ext, int keepold, const char *oldformat = NULL);
extern const char *stream_capabilities();
extern void listsubdirs(const char *dir, vector<char *> &subdirs, int (__cdecl *sf)(const char **, const char **));
extern bool listdir(const char *dir, const char *ext, vector<char *> &files);
extern void listfiles(const char *dir, const char *ext, vector<char *> &files, int (__cdecl *sf)(const char **, const char **) = NULL);
extern void listfilesrecursive(const char *dir, vector<char *> &files, int level = 0);
extern void listdirsrecursive(const char *dir, vector<char *> &subdirs, int level = 0);
extern void listzipfiles(const char *dir, const char *ext, vector<char *> &files);
extern void listzipdirs(const char *dir, vector<char *> &subdirs);
extern bool delfile(const char *path);
extern void backup(char *name, char *backupname);
extern bool validzipmodname(char *name);
extern void *zipmanualopen(stream *f, vector<const char *> &files);
extern stream *zipmanualstream(void *a, int n);
extern int zipmanualread(void *a, int n, stream *f, int maxlen = INT_MAX);
extern void zipmanualclose(void *a);
extern bool cmpb(void *b, int n, enet_uint32 c);
extern bool cmpf(char *fn, enet_uint32 c);
extern enet_uint32 adler(unsigned char *data, size_t len);
extern bool isbigendian();
extern void strtoupper(char *t, const char *s = NULL);
extern void seedMT(uint seed);
extern uint randomMT(void);
extern void popMT(void);
extern void entropy_init(uint seed);
extern void entropy_save();
extern void entropy_add_byte(uchar b);
extern void entropy_add_block(const uchar *s, int len);
extern void entropy_get(uchar *buf, int len);
struct iprange { enet_uint32 lr, ur; };
struct iprangecc : iprange { union { int ci; char cc[4]; enet_uint32 cu; }; };
extern const char *atoip(const char *s, enet_uint32 *ip);
extern const char *atoipr(const char *s, iprange *ir);
extern const char *iptoa(const enet_uint32 ip, char *b);
extern const char *iprtoa(const struct iprange &ipr, char *b);
extern char *formatdemofilename(const char *demoformat, const char *timestampformat, const char *map, int mode, int srvclock, int secondsplayed, int secondsremaining, enet_uint32 ip, char *buf);
extern int cmpiprange(const struct iprange *a, const struct iprange *b);
extern int cmpipmatch(const struct iprange *a, const struct iprange *b);
extern int cvecprintf(vector<char> &v, const char *s, ...) PRINTFARGS(2, 3);
extern int cvecconcat(vector<char> &v, const char *s);
extern const char *hiddenpwd(const char *pwd, int showchars = 0);
extern int getlistindex(const char *key, const char *list[], bool acceptnumeric = true, int deflt = -1);
extern void parseupdatelist(hashtable<const char *, int> &ht, char *buf, const char *prefix = NULL, const char *suffix = NULL);
struct twoint { int key, val; };
struct threeint { int key, val1, val2; };
extern int cmpintasc(const int *a, const int *b); // leads to ascending sort order
extern int cmpintdesc(const int *a, const int *b); // leads to descending sort order
extern int stringsort(const char **a, const char **b);
extern int stringsortrev(const char **a, const char **b);
extern int stringsortignorecase(const char **a, const char **b);
extern int stringsortignorecaserev(const char **a, const char **b);
#if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__)
extern void stackdumper(unsigned int type, EXCEPTION_POINTERS *ep);
#endif
struct sl_semaphore
{
void *data;
int *errorcount;
sl_semaphore(int init, int *errorcount); // init: initial semaphore value; errorcount: pointer to error counter for semaphore-related errors or NULL
~sl_semaphore();
void wait(); // blocks, until semaphore gets available
int trywait(); // returns 0, if semaphore was successfully locked (like wait(), but returns !=0 instead of blocking)
int timedwait(int howlongmillis); // like trywait(), but waits for a litte before returning failure
int getvalue(); // returns current semaphore value
void post(); // increments (unlocks) semaphore
};
extern void *sl_createthread(int (*fn)(void *), void *data, const char *name = NULL);
extern int sl_waitthread(void *ti);
extern bool sl_pollthread(void *ti);
extern void sl_detachthread(void *ti);
extern void sl_sleep(int duration);
extern bool ismainthread();
///////////////////////////////////////////////////////// FNV-1a ///////////////////////////////////////////////////////////////////
// Fowler–Noll–Vo is a non-cryptographic hash function created by Glenn Fowler, Landon Curt Noll, and Phong Vo. It is public domain.
// We use the 32-bit-version here.
inline void fnv1a_init(uint32_t &hash)
{
hash = 2166136261UL;
}
inline void fnv1a_add(uint32_t &hash, uchar byte)
{
hash ^= byte;
hash *= 16777619;
}
#endif